Compare commits
21 Commits
harmony-v3
...
harmony-v3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
402954bdf3 | ||
|
|
f5931866e0 | ||
|
|
f67a6b0e43 | ||
|
|
471db8a717 | ||
|
|
8ba9e4e0da | ||
|
|
71556d00b5 | ||
|
|
8709753bfb | ||
|
|
db877e8729 | ||
|
|
11923e3ae8 | ||
|
|
62d1fbf645 | ||
|
|
343ea326c2 | ||
|
|
849ba79dee | ||
|
|
a298bcce02 | ||
|
|
daaf1273fa | ||
|
|
1c150c632f | ||
|
|
0a0f4f5591 | ||
|
|
931daa85bf | ||
|
|
00e4f7b3c1 | ||
|
|
11e63bc335 | ||
|
|
3fa862ce19 | ||
|
|
33405bb24b |
32
lib/ast.js
32
lib/ast.js
@@ -629,11 +629,11 @@ var AST_Const = DEFNODE("Const", null, {
|
||||
$documentation: "A `const` statement"
|
||||
}, AST_Definitions);
|
||||
|
||||
var AST_NameImport = DEFNODE("NameImport", "foreign_name name", {
|
||||
$documentation: "The part of the import statement that imports names from a module.",
|
||||
var AST_NameMapping = DEFNODE("NameMapping", "foreign_name name", {
|
||||
$documentation: "The part of the export/import statement that declare names from a module.",
|
||||
$propdoc: {
|
||||
foreign_name: "[AST_SymbolImportForeign] The name being imported (as specified in the module)",
|
||||
name: "[AST_SymbolImport] The name as it becomes available to this module."
|
||||
foreign_name: "[AST_SymbolExportForeign|AST_SymbolImportForeign] The name being exported/imported (as specified in the module)",
|
||||
name: "[AST_SymbolExport|AST_SymbolImport] The name as it is visible to this module."
|
||||
},
|
||||
_walk: function (visitor) {
|
||||
return visitor._visit(this, function() {
|
||||
@@ -647,7 +647,7 @@ var AST_Import = DEFNODE("Import", "imported_name imported_names module_name", {
|
||||
$documentation: "An `import` statement",
|
||||
$propdoc: {
|
||||
imported_name: "[AST_SymbolImport] The name of the variable holding the module's default export.",
|
||||
imported_names: "[AST_NameImport*] The names of non-default imported variables",
|
||||
imported_names: "[AST_NameMapping*] The names of non-default imported variables",
|
||||
module_name: "[AST_String] String literal describing where this module came from",
|
||||
},
|
||||
_walk: function(visitor) {
|
||||
@@ -656,7 +656,7 @@ var AST_Import = DEFNODE("Import", "imported_name imported_names module_name", {
|
||||
this.imported_name._walk(visitor);
|
||||
}
|
||||
if (this.imported_names) {
|
||||
this.imported_names.forEach(function (name_import) {
|
||||
this.imported_names.forEach(function(name_import) {
|
||||
name_import._walk(visitor);
|
||||
});
|
||||
}
|
||||
@@ -670,7 +670,7 @@ var AST_Export = DEFNODE("Export", "exported_definition exported_value is_defaul
|
||||
$propdoc: {
|
||||
exported_definition: "[AST_Defun|AST_Definitions|AST_DefClass?] An exported definition",
|
||||
exported_value: "[AST_Node?] An exported value",
|
||||
exported_names: "[AST_NameImport*?] List of exported names",
|
||||
exported_names: "[AST_NameMapping*?] List of exported names",
|
||||
module_name: "[AST_String?] Name of the file to load exports from",
|
||||
is_default: "[Boolean] Whether this is the default exported value of this module"
|
||||
},
|
||||
@@ -682,6 +682,14 @@ var AST_Export = DEFNODE("Export", "exported_definition exported_value is_defaul
|
||||
if (this.exported_value) {
|
||||
this.exported_value._walk(visitor);
|
||||
}
|
||||
if (this.exported_names) {
|
||||
this.exported_names.forEach(function(name_export) {
|
||||
name_export._walk(visitor);
|
||||
});
|
||||
}
|
||||
if (this.module_name) {
|
||||
this.module_name._walk(visitor);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, AST_Statement);
|
||||
@@ -996,7 +1004,7 @@ var AST_SymbolCatch = DEFNODE("SymbolCatch", null, {
|
||||
}, AST_SymbolBlockDeclaration);
|
||||
|
||||
var AST_SymbolImport = DEFNODE("SymbolImport", null, {
|
||||
$documentation: "Symbol refering to an imported name",
|
||||
$documentation: "Symbol referring to an imported name",
|
||||
}, AST_SymbolBlockDeclaration);
|
||||
|
||||
var AST_SymbolImportForeign = DEFNODE("SymbolImportForeign", null, {
|
||||
@@ -1018,6 +1026,14 @@ var AST_SymbolRef = DEFNODE("SymbolRef", null, {
|
||||
$documentation: "Reference to some symbol (not definition/declaration)",
|
||||
}, AST_Symbol);
|
||||
|
||||
var AST_SymbolExport = DEFNODE("SymbolExport", null, {
|
||||
$documentation: "Symbol referring to a name to export",
|
||||
}, AST_SymbolRef);
|
||||
|
||||
var AST_SymbolExportForeign = DEFNODE("SymbolExportForeign", null, {
|
||||
$documentation: "A symbol exported from this module, but it is used in the other module, and its real name is irrelevant for this module's purposes",
|
||||
}, AST_Symbol);
|
||||
|
||||
var AST_LabelRef = DEFNODE("LabelRef", null, {
|
||||
$documentation: "Reference to a label symbol",
|
||||
}, AST_Symbol);
|
||||
|
||||
446
lib/compress.js
446
lib/compress.js
@@ -129,7 +129,9 @@ function Compressor(options, false_by_default) {
|
||||
this.toplevel.funcs = /funcs/.test(toplevel);
|
||||
this.toplevel.vars = /vars/.test(toplevel);
|
||||
} else {
|
||||
this.toplevel = toplevel ? return_true : return_false;
|
||||
this.toplevel = toplevel ? function(def) {
|
||||
return !def.export;
|
||||
} : return_false;
|
||||
this.toplevel.funcs = this.toplevel.vars = toplevel;
|
||||
}
|
||||
var sequences = this.options["sequences"];
|
||||
@@ -141,6 +143,7 @@ Compressor.prototype = new TreeTransformer;
|
||||
merge(Compressor.prototype, {
|
||||
option: function(key) { return this.options[key] },
|
||||
toplevel: function(def) {
|
||||
if (def.export) return false;
|
||||
for (var i = 0, len = def.orig.length; i < len; i++)
|
||||
if (!this.toplevel[def.orig[i] instanceof AST_SymbolDefun ? "funcs" : "vars"])
|
||||
return false;
|
||||
@@ -672,7 +675,9 @@ merge(Compressor.prototype, {
|
||||
node instanceof AST_DefClass ||
|
||||
node instanceof AST_Defun ||
|
||||
node instanceof AST_Let ||
|
||||
node instanceof AST_Const
|
||||
node instanceof AST_Const ||
|
||||
node instanceof AST_Export ||
|
||||
node instanceof AST_Import
|
||||
);
|
||||
}
|
||||
|
||||
@@ -695,26 +700,24 @@ merge(Compressor.prototype, {
|
||||
var CHANGED, max_iter = 10;
|
||||
do {
|
||||
CHANGED = false;
|
||||
statements = eliminate_spurious_blocks(statements);
|
||||
eliminate_spurious_blocks(statements);
|
||||
if (compressor.option("dead_code")) {
|
||||
statements = eliminate_dead_code(statements, compressor);
|
||||
eliminate_dead_code(statements, compressor);
|
||||
}
|
||||
if (compressor.option("if_return")) {
|
||||
statements = handle_if_return(statements, compressor);
|
||||
handle_if_return(statements, compressor);
|
||||
}
|
||||
if (compressor.sequences_limit > 0) {
|
||||
statements = sequencesize(statements, compressor);
|
||||
sequencesize(statements, compressor);
|
||||
}
|
||||
if (compressor.option("join_vars")) {
|
||||
statements = join_consecutive_vars(statements, compressor);
|
||||
join_consecutive_vars(statements, compressor);
|
||||
}
|
||||
if (compressor.option("collapse_vars")) {
|
||||
statements = collapse(statements, compressor);
|
||||
collapse(statements, compressor);
|
||||
}
|
||||
} while (CHANGED && max_iter-- > 0);
|
||||
|
||||
return statements;
|
||||
|
||||
// Search from right to left for assignment-like expressions:
|
||||
// - `var a = x;`
|
||||
// - `a = x;`
|
||||
@@ -752,6 +755,7 @@ merge(Compressor.prototype, {
|
||||
var parent = tt.parent();
|
||||
if (node instanceof AST_Assign && node.operator != "=" && lhs.equivalent_to(node.left)
|
||||
|| node instanceof AST_Await
|
||||
|| node instanceof AST_Call && lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression)
|
||||
|| node instanceof AST_Debugger
|
||||
|| node instanceof AST_Destructuring
|
||||
|| node instanceof AST_IterationStatement && !(node instanceof AST_For)
|
||||
@@ -818,7 +822,6 @@ merge(Compressor.prototype, {
|
||||
if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
|
||||
}
|
||||
}
|
||||
return statements;
|
||||
|
||||
function extract_candidates(expr) {
|
||||
if (expr instanceof AST_Assign && !expr.left.has_side_effects(compressor)
|
||||
@@ -921,59 +924,60 @@ merge(Compressor.prototype, {
|
||||
|
||||
function eliminate_spurious_blocks(statements) {
|
||||
var seen_dirs = [];
|
||||
return statements.reduce(function(a, stat){
|
||||
for (var i = 0; i < statements.length;) {
|
||||
var stat = statements[i];
|
||||
if (stat instanceof AST_BlockStatement && all(stat.body, can_be_evicted_from_block)) {
|
||||
CHANGED = true;
|
||||
a.push.apply(a, eliminate_spurious_blocks(stat.body));
|
||||
eliminate_spurious_blocks(stat.body);
|
||||
[].splice.apply(statements, [i, 1].concat(stat.body));
|
||||
i += stat.body.length;
|
||||
} else if (stat instanceof AST_EmptyStatement) {
|
||||
CHANGED = true;
|
||||
statements.splice(i, 1);
|
||||
} else if (stat instanceof AST_Directive) {
|
||||
if (seen_dirs.indexOf(stat.value) < 0) {
|
||||
a.push(stat);
|
||||
i++;
|
||||
seen_dirs.push(stat.value);
|
||||
} else {
|
||||
CHANGED = true;
|
||||
statements.splice(i, 1);
|
||||
}
|
||||
} else {
|
||||
a.push(stat);
|
||||
}
|
||||
return a;
|
||||
}, []);
|
||||
};
|
||||
} else i++;
|
||||
}
|
||||
}
|
||||
|
||||
function handle_if_return(statements, compressor) {
|
||||
var self = compressor.self();
|
||||
var multiple_if_returns = has_multiple_if_returns(statements);
|
||||
var in_lambda = self instanceof AST_Lambda;
|
||||
var ret = []; // Optimized statements, build from tail to front
|
||||
loop: for (var i = statements.length; --i >= 0;) {
|
||||
for (var i = statements.length; --i >= 0;) {
|
||||
var stat = statements[i];
|
||||
switch (true) {
|
||||
case (in_lambda && stat instanceof AST_Return && !stat.value && ret.length == 0):
|
||||
var next = statements[i + 1];
|
||||
|
||||
if (in_lambda && stat instanceof AST_Return && !stat.value && !next) {
|
||||
CHANGED = true;
|
||||
// note, ret.length is probably always zero
|
||||
// because we drop unreachable code before this
|
||||
// step. nevertheless, it's good to check.
|
||||
continue loop;
|
||||
case stat instanceof AST_If:
|
||||
statements.length--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stat instanceof AST_If) {
|
||||
var ab = aborts(stat.body);
|
||||
if (can_merge_flow(ab)) {
|
||||
if (ab.label) {
|
||||
remove(ab.label.thedef.references, ab);
|
||||
}
|
||||
CHANGED = true;
|
||||
var funs = extract_functions_from_statement_array(ret);
|
||||
var body = as_statement_array_with_return(stat.body, ab);
|
||||
stat = stat.clone();
|
||||
stat.condition = stat.condition.negate(compressor);
|
||||
var body = as_statement_array_with_return(stat.body, ab);
|
||||
stat.body = make_node(AST_BlockStatement, stat, {
|
||||
body: as_statement_array(stat.alternative).concat(ret)
|
||||
body: as_statement_array(stat.alternative).concat(extract_functions())
|
||||
});
|
||||
stat.alternative = make_node(AST_BlockStatement, stat, {
|
||||
body: body
|
||||
});
|
||||
ret = [ stat.transform(compressor) ].concat(funs);
|
||||
continue loop;
|
||||
statements[i] = stat.transform(compressor);
|
||||
continue;
|
||||
}
|
||||
|
||||
var ab = aborts(stat.alternative);
|
||||
@@ -982,81 +986,71 @@ merge(Compressor.prototype, {
|
||||
remove(ab.label.thedef.references, ab);
|
||||
}
|
||||
CHANGED = true;
|
||||
var funs = extract_functions_from_statement_array(ret);
|
||||
stat = stat.clone();
|
||||
stat.body = make_node(AST_BlockStatement, stat.body, {
|
||||
body: as_statement_array(stat.body).concat(ret)
|
||||
body: as_statement_array(stat.body).concat(extract_functions())
|
||||
});
|
||||
var body = as_statement_array_with_return(stat.alternative, ab);
|
||||
stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
|
||||
body: body
|
||||
});
|
||||
ret = [ stat.transform(compressor) ].concat(funs);
|
||||
continue loop;
|
||||
statements[i] = stat.transform(compressor);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (stat.body instanceof AST_Return) {
|
||||
var value = stat.body.value;
|
||||
//---
|
||||
// pretty silly case, but:
|
||||
// if (foo()) return; return; ==> foo(); return;
|
||||
if ((in_lambda && ret.length == 0 || ret[0] instanceof AST_Return && !ret[0].value)
|
||||
&& !value && !stat.alternative) {
|
||||
CHANGED = true;
|
||||
var cond = make_node(AST_SimpleStatement, stat.condition, {
|
||||
body: stat.condition
|
||||
});
|
||||
ret.unshift(cond);
|
||||
continue loop;
|
||||
}
|
||||
//---
|
||||
// if (foo()) return x; return y; ==> return foo() ? x : y;
|
||||
if (ret[0] instanceof AST_Return && value && ret[0].value && !stat.alternative) {
|
||||
CHANGED = true;
|
||||
stat = stat.clone();
|
||||
stat.alternative = ret[0];
|
||||
ret[0] = stat.transform(compressor);
|
||||
continue loop;
|
||||
}
|
||||
//---
|
||||
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
|
||||
if (multiple_if_returns && (ret.length == 0 || ret[0] instanceof AST_Return)
|
||||
&& value && !stat.alternative && in_lambda) {
|
||||
CHANGED = true;
|
||||
stat = stat.clone();
|
||||
stat.alternative = ret[0] || make_node(AST_Return, stat, {
|
||||
value: null
|
||||
});
|
||||
ret[0] = stat.transform(compressor);
|
||||
continue loop;
|
||||
}
|
||||
//---
|
||||
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
|
||||
//
|
||||
// if sequences is not enabled, this can lead to an endless loop (issue #866).
|
||||
// however, with sequences on this helps producing slightly better output for
|
||||
// the example code.
|
||||
if (compressor.option("sequences")
|
||||
&& i > 0 && statements[i - 1] instanceof AST_If && statements[i - 1].body instanceof AST_Return
|
||||
&& ret.length == 1 && in_lambda && ret[0] instanceof AST_SimpleStatement
|
||||
&& !stat.alternative) {
|
||||
CHANGED = true;
|
||||
ret.push(make_node(AST_Return, ret[0], {
|
||||
value: null
|
||||
}).transform(compressor));
|
||||
ret.unshift(stat);
|
||||
continue loop;
|
||||
}
|
||||
if (stat instanceof AST_If && stat.body instanceof AST_Return) {
|
||||
var value = stat.body.value;
|
||||
//---
|
||||
// pretty silly case, but:
|
||||
// if (foo()) return; return; ==> foo(); return;
|
||||
if (!value && !stat.alternative
|
||||
&& (in_lambda && !next || next instanceof AST_Return && !next.value)) {
|
||||
CHANGED = true;
|
||||
statements[i] = make_node(AST_SimpleStatement, stat.condition, {
|
||||
body: stat.condition
|
||||
});
|
||||
continue;
|
||||
}
|
||||
//---
|
||||
// if (foo()) return x; return y; ==> return foo() ? x : y;
|
||||
if (value && !stat.alternative && next instanceof AST_Return && next.value) {
|
||||
CHANGED = true;
|
||||
stat = stat.clone();
|
||||
stat.alternative = next;
|
||||
statements.splice(i, 2, stat.transform(compressor));
|
||||
continue;
|
||||
}
|
||||
//---
|
||||
// if (foo()) return x; [ return ; ] ==> return foo() ? x : undefined;
|
||||
if (multiple_if_returns && in_lambda && value && !stat.alternative
|
||||
&& (!next || next instanceof AST_Return)) {
|
||||
CHANGED = true;
|
||||
stat = stat.clone();
|
||||
stat.alternative = next || make_node(AST_Return, stat, {
|
||||
value: null
|
||||
});
|
||||
statements.splice(i, next ? 2 : 1, stat.transform(compressor));
|
||||
continue;
|
||||
}
|
||||
//---
|
||||
// if (a) return b; if (c) return d; e; ==> return a ? b : c ? d : void e;
|
||||
//
|
||||
// if sequences is not enabled, this can lead to an endless loop (issue #866).
|
||||
// however, with sequences on this helps producing slightly better output for
|
||||
// the example code.
|
||||
var prev = statements[i - 1];
|
||||
if (compressor.option("sequences") && in_lambda && !stat.alternative
|
||||
&& prev instanceof AST_If && prev.body instanceof AST_Return
|
||||
&& i + 2 == statements.length && next instanceof AST_SimpleStatement) {
|
||||
CHANGED = true;
|
||||
statements.push(make_node(AST_Return, next, {
|
||||
value: null
|
||||
}).transform(compressor));
|
||||
continue;
|
||||
}
|
||||
|
||||
ret.unshift(stat);
|
||||
break;
|
||||
default:
|
||||
ret.unshift(stat);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
|
||||
function has_multiple_if_returns(statements) {
|
||||
var n = 0;
|
||||
@@ -1074,15 +1068,29 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
function can_merge_flow(ab) {
|
||||
if (!ab || !all(ret, function(stat) {
|
||||
return !(stat instanceof AST_Const || stat instanceof AST_Let);
|
||||
})) return false;
|
||||
if (!ab) return false;
|
||||
for (var j = i + 1, len = statements.length; j < len; j++) {
|
||||
var stat = statements[j];
|
||||
if (stat instanceof AST_Const || stat instanceof AST_Let) return false;
|
||||
}
|
||||
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab) : null;
|
||||
return ab instanceof AST_Return && in_lambda && is_return_void(ab.value)
|
||||
|| ab instanceof AST_Continue && self === loop_body(lct)
|
||||
|| ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct;
|
||||
}
|
||||
|
||||
function extract_functions() {
|
||||
var tail = statements.slice(i + 1);
|
||||
statements.length = i + 1;
|
||||
return tail.filter(function(stat) {
|
||||
if (stat instanceof AST_Defun) {
|
||||
statements.push(stat);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function as_statement_array_with_return(node, ab) {
|
||||
var body = as_statement_array(node).slice(0, -1);
|
||||
if (ab.value) {
|
||||
@@ -1092,49 +1100,52 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
return body;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function eliminate_dead_code(statements, compressor) {
|
||||
var has_quit = false;
|
||||
var orig = statements.length;
|
||||
var has_quit;
|
||||
var self = compressor.self();
|
||||
statements = statements.reduce(function(a, stat){
|
||||
if (has_quit) {
|
||||
extract_declarations_from_unreachable_code(compressor, stat, a);
|
||||
} else {
|
||||
if (stat instanceof AST_LoopControl) {
|
||||
var lct = compressor.loopcontrol_target(stat);
|
||||
if ((stat instanceof AST_Break
|
||||
&& !(lct instanceof AST_IterationStatement)
|
||||
&& loop_body(lct) === self) || (stat instanceof AST_Continue
|
||||
&& loop_body(lct) === self)) {
|
||||
if (stat.label) {
|
||||
remove(stat.label.thedef.references, stat);
|
||||
}
|
||||
} else {
|
||||
a.push(stat);
|
||||
for (var i = 0, n = 0, len = statements.length; i < len; i++) {
|
||||
var stat = statements[i];
|
||||
if (stat instanceof AST_LoopControl) {
|
||||
var lct = compressor.loopcontrol_target(stat);
|
||||
if (stat instanceof AST_Break
|
||||
&& !(lct instanceof AST_IterationStatement)
|
||||
&& loop_body(lct) === self
|
||||
|| stat instanceof AST_Continue
|
||||
&& loop_body(lct) === self) {
|
||||
if (stat.label) {
|
||||
remove(stat.label.thedef.references, stat);
|
||||
}
|
||||
} else {
|
||||
a.push(stat);
|
||||
statements[n++] = stat;
|
||||
}
|
||||
if (aborts(stat)) has_quit = true;
|
||||
} else {
|
||||
statements[n++] = stat;
|
||||
}
|
||||
return a;
|
||||
}, []);
|
||||
CHANGED = statements.length != orig;
|
||||
return statements;
|
||||
};
|
||||
if (aborts(stat)) {
|
||||
has_quit = statements.slice(i + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
statements.length = n;
|
||||
CHANGED = n != len;
|
||||
if (has_quit) has_quit.forEach(function(stat) {
|
||||
extract_declarations_from_unreachable_code(compressor, stat, statements);
|
||||
});
|
||||
}
|
||||
|
||||
function sequencesize(statements, compressor) {
|
||||
if (statements.length < 2) return statements;
|
||||
var seq = [], ret = [];
|
||||
if (statements.length < 2) return;
|
||||
var seq = [], n = 0;
|
||||
function push_seq() {
|
||||
if (!seq.length) return;
|
||||
var body = make_sequence(seq[0], seq);
|
||||
ret.push(make_node(AST_SimpleStatement, body, { body: body }));
|
||||
statements[n++] = make_node(AST_SimpleStatement, body, { body: body });
|
||||
seq = [];
|
||||
};
|
||||
statements.forEach(function(stat){
|
||||
}
|
||||
for (var i = 0, len = statements.length; i < len; i++) {
|
||||
var stat = statements[i];
|
||||
if (stat instanceof AST_SimpleStatement) {
|
||||
if (seq.length >= compressor.sequences_limit) push_seq();
|
||||
var body = stat.body;
|
||||
@@ -1142,18 +1153,18 @@ merge(Compressor.prototype, {
|
||||
if (body) merge_sequence(seq, body);
|
||||
} else {
|
||||
push_seq();
|
||||
ret.push(stat);
|
||||
statements[n++] = stat;
|
||||
}
|
||||
});
|
||||
}
|
||||
push_seq();
|
||||
ret = sequencesize_2(ret, compressor);
|
||||
CHANGED = ret.length != statements.length;
|
||||
return ret;
|
||||
};
|
||||
statements.length = n;
|
||||
sequencesize_2(statements, compressor);
|
||||
CHANGED = statements.length != len;
|
||||
}
|
||||
|
||||
function sequencesize_2(statements, compressor) {
|
||||
function cons_seq(right) {
|
||||
ret.pop();
|
||||
n--;
|
||||
var left = prev.body;
|
||||
if (!(left instanceof AST_Sequence)) {
|
||||
left = make_node(AST_Sequence, left, {
|
||||
@@ -1163,8 +1174,9 @@ merge(Compressor.prototype, {
|
||||
merge_sequence(left.expressions, right);
|
||||
return left.transform(compressor);
|
||||
};
|
||||
var ret = [], prev = null;
|
||||
statements.forEach(function(stat){
|
||||
var n = 0, prev;
|
||||
for (var i = 0, len = statements.length; i < len; i++) {
|
||||
var stat = statements[i];
|
||||
if (prev) {
|
||||
if (stat instanceof AST_For) {
|
||||
try {
|
||||
@@ -1177,7 +1189,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
else if (!stat.init) {
|
||||
stat.init = prev.body.drop_side_effect_free(compressor);
|
||||
ret.pop();
|
||||
n--;
|
||||
}
|
||||
} catch(ex) {
|
||||
if (ex !== cons_seq) throw ex;
|
||||
@@ -1199,15 +1211,16 @@ merge(Compressor.prototype, {
|
||||
stat.expression = cons_seq(stat.expression);
|
||||
}
|
||||
}
|
||||
ret.push(stat);
|
||||
statements[n++] = stat;
|
||||
prev = stat instanceof AST_SimpleStatement ? stat : null;
|
||||
});
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
statements.length = n;
|
||||
}
|
||||
|
||||
function join_consecutive_vars(statements, compressor) {
|
||||
var prev = null;
|
||||
return statements.reduce(function(a, stat){
|
||||
for (var i = 0, j = -1, len = statements.length; i < len; i++) {
|
||||
var stat = statements[i];
|
||||
var prev = statements[j];
|
||||
if (stat instanceof AST_Definitions && prev && prev.TYPE == stat.TYPE) {
|
||||
prev.definitions = prev.definitions.concat(stat.definitions);
|
||||
CHANGED = true;
|
||||
@@ -1216,35 +1229,19 @@ merge(Compressor.prototype, {
|
||||
&& prev instanceof AST_Var
|
||||
&& (!stat.init || stat.init.TYPE == prev.TYPE)) {
|
||||
CHANGED = true;
|
||||
a.pop();
|
||||
if (stat.init) {
|
||||
stat.init.definitions = prev.definitions.concat(stat.init.definitions);
|
||||
} else {
|
||||
stat.init = prev;
|
||||
}
|
||||
a.push(stat);
|
||||
prev = stat;
|
||||
statements[j] = stat;
|
||||
}
|
||||
else {
|
||||
prev = stat;
|
||||
a.push(stat);
|
||||
statements[++j] = stat;
|
||||
}
|
||||
return a;
|
||||
}, []);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
function extract_functions_from_statement_array(statements) {
|
||||
var funs = [];
|
||||
for (var i = statements.length - 1; i >= 0; --i) {
|
||||
var stat = statements[i];
|
||||
if (stat instanceof AST_Defun) {
|
||||
statements.splice(i, 1);
|
||||
funs.unshift(stat);
|
||||
}
|
||||
}
|
||||
return funs;
|
||||
statements.length = j + 1;
|
||||
};
|
||||
}
|
||||
|
||||
function extract_declarations_from_unreachable_code(compressor, stat, target) {
|
||||
@@ -2050,13 +2047,12 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
OPT(AST_Block, function(self, compressor){
|
||||
if (self.body instanceof AST_Node) { return self; }
|
||||
self.body = tighten_body(self.body, compressor);
|
||||
if (!(self.body instanceof AST_Node)) tighten_body(self.body, compressor);
|
||||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_BlockStatement, function(self, compressor){
|
||||
self.body = tighten_body(self.body, compressor);
|
||||
tighten_body(self.body, compressor);
|
||||
switch (self.body.length) {
|
||||
case 1:
|
||||
if (!compressor.has_directive("use strict") && compressor.parent() instanceof AST_If
|
||||
@@ -2097,7 +2093,8 @@ merge(Compressor.prototype, {
|
||||
var tw = new TreeWalker(function(node, descend){
|
||||
if (node !== self) {
|
||||
if (node instanceof AST_Defun || node instanceof AST_DefClass) {
|
||||
if (!drop_funcs && scope === self) {
|
||||
var in_export = tw.parent() instanceof AST_Export;
|
||||
if (in_export || !drop_funcs && scope === self) {
|
||||
var node_def = node.name.definition();
|
||||
if (node_def.global && !(node_def.id in in_use_ids)) {
|
||||
in_use_ids[node_def.id] = true;
|
||||
@@ -2108,15 +2105,16 @@ merge(Compressor.prototype, {
|
||||
return true; // don't go in nested scopes
|
||||
}
|
||||
if (node instanceof AST_Definitions && scope === self) {
|
||||
var in_export = tw.parent() instanceof AST_Export;
|
||||
node.definitions.forEach(function(def){
|
||||
if (def.name instanceof AST_SymbolVar) {
|
||||
var_defs_by_id.add(def.name.definition().id, def);
|
||||
}
|
||||
if (!drop_vars) {
|
||||
if (in_export || !drop_vars) {
|
||||
def.name.walk(new TreeWalker(function(node) {
|
||||
if (node instanceof AST_SymbolDeclaration) {
|
||||
var def = node.definition();
|
||||
if (def.global && !(def.id in in_use_ids)) {
|
||||
if ((in_export || def.global) && !(def.id in in_use_ids)) {
|
||||
in_use_ids[def.id] = true;
|
||||
in_use.push(def);
|
||||
}
|
||||
@@ -2233,11 +2231,8 @@ merge(Compressor.prototype, {
|
||||
// pass 3: we should drop declarations not in_use
|
||||
var tt = new TreeTransformer(
|
||||
function before(node, descend, in_list) {
|
||||
var parent = tt.parent();
|
||||
if (!compressor.option("keep_fnames")
|
||||
&& ((node instanceof AST_Function || node instanceof AST_ClassExpression) && node.name
|
||||
|| (node instanceof AST_Defun || node instanceof AST_DefClass)
|
||||
&& parent instanceof AST_Export && parent.is_default)) {
|
||||
&& node.name && (node instanceof AST_Function || node instanceof AST_ClassExpression)) {
|
||||
var def = node.name.definition();
|
||||
// any declarations with same name will overshadow
|
||||
// name of this anonymous function and can therefore
|
||||
@@ -2272,7 +2267,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((node instanceof AST_Defun || node instanceof AST_DefClass) && !(parent instanceof AST_Export) && node !== self) {
|
||||
if ((node instanceof AST_Defun || node instanceof AST_DefClass) && node !== self) {
|
||||
var keep = (node.name.definition().id in in_use_ids) || !drop_funcs && node.name.definition().global;
|
||||
if (!keep) {
|
||||
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
|
||||
@@ -2280,6 +2275,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
return node;
|
||||
}
|
||||
var parent = tt.parent();
|
||||
if (node instanceof AST_Definitions
|
||||
&& !(parent instanceof AST_ForIn && parent.init === node)
|
||||
&& (drop_vars || !(parent instanceof AST_Toplevel) && !(node instanceof AST_Var))) {
|
||||
@@ -3057,7 +3053,7 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
OPT(AST_Try, function(self, compressor){
|
||||
self.body = tighten_body(self.body, compressor);
|
||||
tighten_body(self.body, compressor);
|
||||
if (self.bcatch && self.bfinally && all(self.bfinally.body, is_empty)) self.bfinally = null;
|
||||
if (all(self.body, is_empty)) {
|
||||
var body = [];
|
||||
@@ -3114,18 +3110,34 @@ merge(Compressor.prototype, {
|
||||
|
||||
OPT(AST_Call, function(self, compressor){
|
||||
var exp = self.expression;
|
||||
if (compressor.option("reduce_vars") && exp instanceof AST_SymbolRef) {
|
||||
var fixed = exp.fixed_value();
|
||||
if (fixed instanceof AST_Function) exp = fixed;
|
||||
}
|
||||
var fn = exp;
|
||||
var simple_args = all(self.args, function(arg) {
|
||||
return !(arg instanceof AST_Expansion);
|
||||
});
|
||||
if (compressor.option("unused")
|
||||
&& exp instanceof AST_Function
|
||||
&& !exp.uses_arguments
|
||||
&& !exp.uses_eval) {
|
||||
&& simple_args
|
||||
&& (fn instanceof AST_Function
|
||||
|| compressor.option("reduce_vars")
|
||||
&& fn instanceof AST_SymbolRef
|
||||
&& (fn = fn.fixed_value()) instanceof AST_Function)
|
||||
&& !fn.uses_arguments
|
||||
&& !fn.uses_eval) {
|
||||
var pos = 0, last = 0;
|
||||
for (var i = 0, len = self.args.length; i < len; i++) {
|
||||
var trim = i >= exp.argnames.length;
|
||||
if (trim || exp.argnames[i].__unused) {
|
||||
if (fn.argnames[i] instanceof AST_Expansion) {
|
||||
if (fn.argnames[i].__unused) while (i < len) {
|
||||
var node = self.args[i++].drop_side_effect_free(compressor);
|
||||
if (node) {
|
||||
self.args[pos++] = node;
|
||||
}
|
||||
} else while (i < len) {
|
||||
self.args[pos++] = self.args[i++];
|
||||
}
|
||||
last = pos;
|
||||
break;
|
||||
}
|
||||
var trim = i >= fn.argnames.length;
|
||||
if (trim || fn.argnames[i].__unused) {
|
||||
var node = self.args[i].drop_side_effect_free(compressor);
|
||||
if (node) {
|
||||
self.args[pos++] = node;
|
||||
@@ -3330,20 +3342,21 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (exp instanceof AST_Function && !self.expression.is_generator && !self.expression.async) {
|
||||
var stat = exp.body[0];
|
||||
if (compressor.option("inline") && stat instanceof AST_Return) {
|
||||
var value = stat && stat.value;
|
||||
if (!value || value.is_constant_expression()) {
|
||||
var args = self.args.concat(value || make_node(AST_Undefined, self));
|
||||
return make_sequence(self, args).transform(compressor);
|
||||
}
|
||||
var stat = fn instanceof AST_Function && fn.body[0];
|
||||
if (compressor.option("inline") && stat instanceof AST_Return) {
|
||||
var value = stat.value;
|
||||
if (!value || value.is_constant_expression()) {
|
||||
var args = self.args.concat(value || make_node(AST_Undefined, self));
|
||||
return make_sequence(self, args).transform(compressor);
|
||||
}
|
||||
}
|
||||
if (exp instanceof AST_Function && !exp.is_generator && !exp.async) {
|
||||
if (compressor.option("inline")
|
||||
&& !exp.name
|
||||
&& exp.body.length == 1
|
||||
&& !exp.uses_arguments
|
||||
&& !exp.uses_eval
|
||||
&& simple_args
|
||||
&& !self.has_pure_annotation(compressor)) {
|
||||
var value;
|
||||
if (stat instanceof AST_Return) {
|
||||
@@ -3361,16 +3374,29 @@ merge(Compressor.prototype, {
|
||||
if (exp.argnames.length > 0) {
|
||||
fn.body.push(make_node(AST_Var, self, {
|
||||
definitions: exp.argnames.map(function(sym, i) {
|
||||
if (sym instanceof AST_Expansion) {
|
||||
return make_node(AST_VarDef, sym, {
|
||||
name: sym.expression,
|
||||
value: make_node(AST_Array, self, {
|
||||
elements: self.args.slice(i).map(function(arg) {
|
||||
return arg.clone(true);
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
var arg = self.args[i];
|
||||
return make_node(AST_VarDef, sym, {
|
||||
name: sym,
|
||||
value: self.args[i] || make_node(AST_Undefined, self)
|
||||
value: arg ? arg.clone(true) : make_node(AST_Undefined, self)
|
||||
});
|
||||
})
|
||||
}));
|
||||
}
|
||||
if (self.args.length > exp.argnames.length) {
|
||||
if (self.args.length > exp.argnames.length && !(exp.argnames[exp.argnames.length - 1] instanceof AST_Expansion)) {
|
||||
fn.body.push(make_node(AST_SimpleStatement, self, {
|
||||
body: make_sequence(self, self.args.slice(exp.argnames.length))
|
||||
body: make_sequence(self, self.args.slice(exp.argnames.length).map(function(node) {
|
||||
return node.clone(true);
|
||||
}))
|
||||
}));
|
||||
}
|
||||
fn.body.push(make_node(AST_Return, self, {
|
||||
@@ -3381,18 +3407,21 @@ merge(Compressor.prototype, {
|
||||
if (body.length == 1 && body[0] instanceof AST_Return) {
|
||||
value = body[0].value;
|
||||
if (!value) return make_node(AST_Undefined, self);
|
||||
value.walk(new TreeWalker(function(node) {
|
||||
var tw = new TreeWalker(function(node) {
|
||||
if (value === self) return true;
|
||||
if (node instanceof AST_SymbolRef && matches(node.scope.find_variable(node))
|
||||
|| node instanceof AST_This && matches(node)) {
|
||||
if (node instanceof AST_SymbolRef) {
|
||||
var ref = node.scope.find_variable(node);
|
||||
if (ref && ref.scope.parent_scope === fn.parent_scope) {
|
||||
value = self;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (node instanceof AST_This && !tw.find_parent(AST_Scope)) {
|
||||
value = self;
|
||||
return true;
|
||||
}
|
||||
|
||||
function matches(ref) {
|
||||
return ref && ref.scope.parent_scope === fn.parent_scope;
|
||||
}
|
||||
}));
|
||||
});
|
||||
value.walk(tw);
|
||||
if (value !== self) value = best_of(compressor, value, self);
|
||||
} else {
|
||||
value = self;
|
||||
@@ -3531,6 +3560,7 @@ merge(Compressor.prototype, {
|
||||
field = "left";
|
||||
}
|
||||
} else if (cdr instanceof AST_Call
|
||||
&& !(left instanceof AST_PropAccess && cdr.expression.equivalent_to(left))
|
||||
|| cdr instanceof AST_PropAccess
|
||||
|| cdr instanceof AST_Unary && !unary_side_effects(cdr.operator)) {
|
||||
field = "expression";
|
||||
@@ -4041,6 +4071,10 @@ merge(Compressor.prototype, {
|
||||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_SymbolExport, function(self, compressor){
|
||||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_SymbolRef, function(self, compressor){
|
||||
var def = self.resolve_defines(compressor);
|
||||
if (def) {
|
||||
|
||||
@@ -1280,7 +1280,6 @@ function OutputStream(options) {
|
||||
name_import.print(output);
|
||||
if (i < self.imported_names.length - 1) {
|
||||
output.print(",");
|
||||
output.space();
|
||||
}
|
||||
});
|
||||
output.space();
|
||||
@@ -1296,17 +1295,26 @@ function OutputStream(options) {
|
||||
output.semicolon();
|
||||
});
|
||||
|
||||
DEFPRINT(AST_NameImport, function(self, output) {
|
||||
DEFPRINT(AST_NameMapping, function(self, output) {
|
||||
var is_import = output.parent() instanceof AST_Import;
|
||||
var definition = self.name.definition();
|
||||
var names_are_different =
|
||||
(definition && definition.mangled_name || self.name.name) !==
|
||||
self.foreign_name.name;
|
||||
if (names_are_different) {
|
||||
output.print(self.foreign_name.name);
|
||||
if (is_import) {
|
||||
output.print(self.foreign_name.name);
|
||||
} else {
|
||||
self.name.print(output);
|
||||
}
|
||||
output.space();
|
||||
output.print("as");
|
||||
output.space();
|
||||
self.name.print(output);
|
||||
if (is_import) {
|
||||
self.name.print(output);
|
||||
} else {
|
||||
output.print(self.foreign_name.name);
|
||||
}
|
||||
} else {
|
||||
self.name.print(output);
|
||||
}
|
||||
@@ -1320,24 +1328,20 @@ function OutputStream(options) {
|
||||
output.space();
|
||||
}
|
||||
if (self.exported_names) {
|
||||
output.space();
|
||||
|
||||
if (self.exported_names.length === 1 && self.exported_names[0].name.name === "*") {
|
||||
self.exported_names[0].print(output);
|
||||
} else {
|
||||
output.print("{");
|
||||
self.exported_names.forEach(function (name_import, i) {
|
||||
self.exported_names.forEach(function(name_export, i) {
|
||||
output.space();
|
||||
name_import.print(output);
|
||||
name_export.print(output);
|
||||
if (i < self.exported_names.length - 1) {
|
||||
output.print(",");
|
||||
output.space();
|
||||
}
|
||||
});
|
||||
output.space();
|
||||
output.print("}");
|
||||
}
|
||||
output.space();
|
||||
}
|
||||
else if (self.exported_value) {
|
||||
self.exported_value.print(output);
|
||||
|
||||
156
lib/parse.js
156
lib/parse.js
@@ -2012,7 +2012,7 @@ function parse($TEXT, options) {
|
||||
case "[":
|
||||
return subscripts(array_(), allow_calls);
|
||||
case "{":
|
||||
return subscripts(object_or_object_destructuring_(), allow_calls);
|
||||
return subscripts(object_or_destructuring_(), allow_calls);
|
||||
}
|
||||
unexpected();
|
||||
}
|
||||
@@ -2109,7 +2109,7 @@ function parse($TEXT, options) {
|
||||
return function_(AST_Accessor, is_generator, is_async);
|
||||
});
|
||||
|
||||
var object_or_object_destructuring_ = embed_tokens(function() {
|
||||
var object_or_destructuring_ = embed_tokens(function object_or_destructuring_() {
|
||||
var start = S.token, first = true, a = [];
|
||||
expect("{");
|
||||
while (!is("punc", "}")) {
|
||||
@@ -2231,7 +2231,7 @@ function parse($TEXT, options) {
|
||||
property_token = S.token;
|
||||
name = as_property_name();
|
||||
}
|
||||
if (name === "async" && !is("punc", "(")) {
|
||||
if (name === "async" && !is("punc", "(") && !is("punc", ",") && !is("punc", "}")) {
|
||||
is_async = true;
|
||||
property_token = S.token;
|
||||
name = as_property_name();
|
||||
@@ -2302,7 +2302,7 @@ function parse($TEXT, options) {
|
||||
next();
|
||||
}
|
||||
|
||||
imported_names = import_names(true);
|
||||
imported_names = map_names(true);
|
||||
|
||||
if (imported_names || imported_name) {
|
||||
expect_token("name", "from");
|
||||
@@ -2326,26 +2326,32 @@ function parse($TEXT, options) {
|
||||
});
|
||||
}
|
||||
|
||||
function import_name() {
|
||||
function map_name(is_import) {
|
||||
var foreign_type = is_import ? AST_SymbolImportForeign : AST_SymbolExportForeign;
|
||||
var type = is_import ? AST_SymbolImport : AST_SymbolExport;
|
||||
var start = S.token;
|
||||
var foreign_name;
|
||||
var name;
|
||||
|
||||
if (peek().value === "as" && peek().type === "name") {
|
||||
foreign_name = as_symbol(AST_SymbolImportForeign);
|
||||
if (is_import) {
|
||||
foreign_name = as_symbol(foreign_type);
|
||||
} else {
|
||||
name = as_symbol(type);
|
||||
}
|
||||
if (is("name", "as")) {
|
||||
next(); // The "as" word
|
||||
}
|
||||
name = as_symbol(AST_SymbolImport);
|
||||
|
||||
if (foreign_name === undefined) {
|
||||
foreign_name = new AST_SymbolImportForeign({
|
||||
name: name.name,
|
||||
start: name.start,
|
||||
end: name.end,
|
||||
});
|
||||
if (is_import) {
|
||||
name = as_symbol(type);
|
||||
} else {
|
||||
foreign_name = as_symbol(foreign_type);
|
||||
}
|
||||
} else if (is_import) {
|
||||
name = new type(foreign_name);
|
||||
} else {
|
||||
foreign_name = new foreign_type(name);
|
||||
}
|
||||
|
||||
return new AST_NameImport({
|
||||
return new AST_NameMapping({
|
||||
start: start,
|
||||
foreign_name: foreign_name,
|
||||
name: name,
|
||||
@@ -2353,26 +2359,26 @@ function parse($TEXT, options) {
|
||||
})
|
||||
}
|
||||
|
||||
function import_nameAsterisk(name) {
|
||||
function map_nameAsterisk(is_import, name) {
|
||||
var foreign_type = is_import ? AST_SymbolImportForeign : AST_SymbolExportForeign;
|
||||
var type = is_import ? AST_SymbolImport : AST_SymbolExport;
|
||||
var start = S.token;
|
||||
var foreign_name;
|
||||
|
||||
|
||||
var end = prev();
|
||||
|
||||
name = name || new AST_SymbolImport({
|
||||
name = name || new type({
|
||||
name: '*',
|
||||
start: start,
|
||||
end: end,
|
||||
});
|
||||
|
||||
foreign_name = new AST_SymbolImportForeign({
|
||||
foreign_name = new foreign_type({
|
||||
name: '*',
|
||||
start: start,
|
||||
end: end,
|
||||
});
|
||||
|
||||
return new AST_NameImport({
|
||||
return new AST_NameMapping({
|
||||
start: start,
|
||||
foreign_name: foreign_name,
|
||||
name: name,
|
||||
@@ -2380,13 +2386,13 @@ function parse($TEXT, options) {
|
||||
})
|
||||
}
|
||||
|
||||
function import_names(allow_as) {
|
||||
function map_names(is_import) {
|
||||
var names;
|
||||
if (is("punc", "{")) {
|
||||
next();
|
||||
names = [];
|
||||
while (!is("punc", "}")) {
|
||||
names.push(import_name());
|
||||
names.push(map_name(is_import));
|
||||
if (is("punc", ",")) {
|
||||
next();
|
||||
}
|
||||
@@ -2395,11 +2401,11 @@ function parse($TEXT, options) {
|
||||
} else if (is("operator", "*")) {
|
||||
var name;
|
||||
next();
|
||||
if (allow_as && is("name", "as")) {
|
||||
if (is_import && is("name", "as")) {
|
||||
next(); // The "as" word
|
||||
name = as_symbol(AST_SymbolImportForeign);
|
||||
}
|
||||
names = [import_nameAsterisk(name)];
|
||||
names = [map_nameAsterisk(is_import, name)];
|
||||
}
|
||||
return names;
|
||||
}
|
||||
@@ -2407,72 +2413,60 @@ function parse($TEXT, options) {
|
||||
function export_() {
|
||||
var start = S.token;
|
||||
var is_default;
|
||||
var exported_value;
|
||||
var exported_definition;
|
||||
var exported_names;
|
||||
|
||||
if (is("keyword", "default")) {
|
||||
is_default = true;
|
||||
next();
|
||||
} else {
|
||||
exported_names = import_names(false);
|
||||
} else if (exported_names = map_names(false)) {
|
||||
if (is("name", "from")) {
|
||||
next();
|
||||
|
||||
if (exported_names) {
|
||||
if (is("name", "from")) {
|
||||
next();
|
||||
|
||||
var mod_str = S.token;
|
||||
if (mod_str.type !== 'string') {
|
||||
unexpected();
|
||||
}
|
||||
next();
|
||||
|
||||
return new AST_Export({
|
||||
start: start,
|
||||
is_default: is_default,
|
||||
exported_names: exported_names,
|
||||
module_name: new AST_String({
|
||||
start: mod_str,
|
||||
value: mod_str.value,
|
||||
quote: mod_str.quote,
|
||||
end: mod_str,
|
||||
}),
|
||||
end: prev(),
|
||||
});
|
||||
} else {
|
||||
return new AST_Export({
|
||||
start: start,
|
||||
is_default: is_default,
|
||||
exported_names: exported_names,
|
||||
end: prev(),
|
||||
});
|
||||
var mod_str = S.token;
|
||||
if (mod_str.type !== 'string') {
|
||||
unexpected();
|
||||
}
|
||||
next();
|
||||
|
||||
return new AST_Export({
|
||||
start: start,
|
||||
is_default: is_default,
|
||||
exported_names: exported_names,
|
||||
module_name: new AST_String({
|
||||
start: mod_str,
|
||||
value: mod_str.value,
|
||||
quote: mod_str.quote,
|
||||
end: mod_str,
|
||||
}),
|
||||
end: prev(),
|
||||
});
|
||||
} else {
|
||||
return new AST_Export({
|
||||
start: start,
|
||||
is_default: is_default,
|
||||
exported_names: exported_names,
|
||||
end: prev(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var is_definition = is("keyword", "var") || is("keyword", "let") || is("keyword", "const");
|
||||
if (is_definition) {
|
||||
if (is_default) unexpected();
|
||||
exported_definition = statement();
|
||||
} else if (is("keyword", "class")) {
|
||||
var cls = expr_atom(false);
|
||||
if (cls.name) {
|
||||
cls.name = new AST_SymbolDefClass(cls.name);
|
||||
exported_definition = new AST_DefClass(cls);
|
||||
} else {
|
||||
exported_value = cls;
|
||||
}
|
||||
} else if (is("keyword", "function")) {
|
||||
var func = expr_atom(false);
|
||||
if (func.name) {
|
||||
func.name = new AST_SymbolDefun(func.name);
|
||||
exported_definition = new AST_Defun(func);
|
||||
} else {
|
||||
exported_value = func;
|
||||
}
|
||||
} else {
|
||||
var node;
|
||||
var exported_value;
|
||||
var exported_definition;
|
||||
if (is("punc", "{")
|
||||
|| is_default
|
||||
&& (is("keyword", "class") || is("keyword", "function"))
|
||||
&& is_token(peek(), "punc")) {
|
||||
exported_value = expression(false);
|
||||
semicolon();
|
||||
} else if ((node = statement()) instanceof AST_Definitions && is_default) {
|
||||
unexpected(node.start);
|
||||
} else if (node instanceof AST_Definitions || node instanceof AST_Defun || node instanceof AST_DefClass) {
|
||||
exported_definition = node;
|
||||
} else if (node instanceof AST_SimpleStatement) {
|
||||
exported_value = node.body;
|
||||
} else {
|
||||
unexpected(node.start);
|
||||
}
|
||||
|
||||
return new AST_Export({
|
||||
|
||||
32
lib/scope.js
32
lib/scope.js
@@ -85,7 +85,7 @@ SymbolDef.prototype = {
|
||||
if (options.ie8 && sym instanceof AST_SymbolLambda)
|
||||
s = s.parent_scope;
|
||||
var def;
|
||||
if (this.defun && (def = this.defun.variables.get(this.name))) {
|
||||
if (def = this.redefined()) {
|
||||
this.mangled_name = def.mangled_name || def.name;
|
||||
} else
|
||||
this.mangled_name = s.next_mangled(options, this);
|
||||
@@ -93,6 +93,9 @@ SymbolDef.prototype = {
|
||||
cache.set(this.name, this.mangled_name);
|
||||
}
|
||||
}
|
||||
},
|
||||
redefined: function() {
|
||||
return this.defun && this.defun.variables.get(this.name);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -223,10 +226,25 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
}));
|
||||
node.thedef = sym;
|
||||
}
|
||||
if (!(scope instanceof AST_Toplevel) && (node instanceof AST_Export || node instanceof AST_Import)) {
|
||||
js_error(
|
||||
node.TYPE + " statement may only appear at top level",
|
||||
node.start.file,
|
||||
node.start.line,
|
||||
node.start.col,
|
||||
node.start.pos
|
||||
);
|
||||
}
|
||||
|
||||
function mark_export(def, level) {
|
||||
if (in_destructuring) {
|
||||
var i = 0;
|
||||
do {
|
||||
level++;
|
||||
} while (tw.parent(i++) !== in_destructuring);
|
||||
}
|
||||
var node = tw.parent(level);
|
||||
def.export = node instanceof AST_Export && !node.is_default;
|
||||
def.export = node instanceof AST_Export;
|
||||
}
|
||||
});
|
||||
self.walk(tw);
|
||||
@@ -255,6 +273,16 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
|
||||
node.reference(options);
|
||||
return true;
|
||||
}
|
||||
// ensure mangling works if catch reuses a scope variable
|
||||
var def;
|
||||
if (node instanceof AST_SymbolCatch && (def = node.definition().redefined())) {
|
||||
var s = node.scope;
|
||||
while (s) {
|
||||
push_uniq(s.enclosed, def);
|
||||
if (s === def.scope) break;
|
||||
s = s.parent_scope;
|
||||
}
|
||||
}
|
||||
});
|
||||
self.walk(tw);
|
||||
|
||||
|
||||
@@ -243,9 +243,22 @@ TreeTransformer.prototype = new TreeWalker;
|
||||
self.expression = self.expression.transform(tw);
|
||||
});
|
||||
|
||||
_(AST_Export, function(self, tw){
|
||||
_(AST_NameMapping, function(self, tw) {
|
||||
self.foreign_name = self.foreign_name.transform(tw);
|
||||
self.name = self.name.transform(tw);
|
||||
});
|
||||
|
||||
_(AST_Import, function(self, tw) {
|
||||
if (self.imported_name) self.imported_name = self.imported_name.transform(tw);
|
||||
if (self.imported_names) do_list(self.imported_names, tw);
|
||||
self.module_name = self.module_name.transform(tw);
|
||||
});
|
||||
|
||||
_(AST_Export, function(self, tw) {
|
||||
if (self.exported_definition) self.exported_definition = self.exported_definition.transform(tw);
|
||||
if (self.exported_value) self.exported_value = self.exported_value.transform(tw);
|
||||
if (self.exported_names) do_list(self.exported_names, tw);
|
||||
if (self.module_name) self.module_name = self.module_name.transform(tw);
|
||||
});
|
||||
|
||||
_(AST_TemplateString, function(self, tw) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony",
|
||||
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
||||
"license": "BSD-2-Clause",
|
||||
"version": "3.0.17",
|
||||
"version": "3.0.19",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
|
||||
@@ -185,6 +185,51 @@ async_identifiers: {
|
||||
node_version: ">=8"
|
||||
}
|
||||
|
||||
async_shorthand_property: {
|
||||
mangle = {
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
function print(o) { console.log(o.async + " " + o.await); }
|
||||
var async = "Async", await = "Await";
|
||||
|
||||
print({ async });
|
||||
print({ await });
|
||||
print({ async, await });
|
||||
print({ await, async });
|
||||
|
||||
print({ async: async });
|
||||
print({ await: await });
|
||||
print({ async: async, await: await });
|
||||
print({ await: await, async: async });
|
||||
}
|
||||
expect: {
|
||||
function a(a) { console.log(a.async + " " + a.await); }
|
||||
var n = "Async", c = "Await";
|
||||
|
||||
a({ async: n });
|
||||
a({ await: c });
|
||||
a({ async: n, await: c });
|
||||
a({ await: c, async: n });
|
||||
|
||||
a({ async: n });
|
||||
a({ await: c });
|
||||
a({ async: n, await: c });
|
||||
a({ await: c, async: n });
|
||||
}
|
||||
expect_stdout: [
|
||||
"Async undefined",
|
||||
"undefined Await",
|
||||
"Async Await",
|
||||
"Async Await",
|
||||
"Async undefined",
|
||||
"undefined Await",
|
||||
"Async Await",
|
||||
"Async Await",
|
||||
]
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
/* FIXME: add test when supported by parser
|
||||
async_arrow: {
|
||||
input: {
|
||||
|
||||
@@ -1294,3 +1294,118 @@ issue_2063: {
|
||||
var a;
|
||||
}
|
||||
}
|
||||
|
||||
issue_2105: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
inline: true,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
!function(factory) {
|
||||
factory();
|
||||
}( function() {
|
||||
return function(fn) {
|
||||
fn()().prop();
|
||||
}( function() {
|
||||
function bar() {
|
||||
var quux = function() {
|
||||
console.log("PASS");
|
||||
}, foo = function() {
|
||||
console.log;
|
||||
quux();
|
||||
};
|
||||
return { prop: foo };
|
||||
}
|
||||
return bar;
|
||||
} );
|
||||
});
|
||||
}
|
||||
expect: {
|
||||
!void function() {
|
||||
var quux = function() {
|
||||
console.log("PASS");
|
||||
};
|
||||
return {
|
||||
prop: function() {
|
||||
console.log;
|
||||
quux();
|
||||
}
|
||||
};
|
||||
}().prop();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_2136_1: {
|
||||
options = {
|
||||
inline: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
!function(a, ...b) {
|
||||
console.log(b);
|
||||
}();
|
||||
}
|
||||
expect: {
|
||||
!function(a, ...b) {
|
||||
console.log(b);
|
||||
}();
|
||||
}
|
||||
expect_stdout: "[]"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
issue_2136_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
inline: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f(x) {
|
||||
console.log(x);
|
||||
}
|
||||
!function(a, ...b) {
|
||||
f(b[0]);
|
||||
}(1, 2, 3);
|
||||
}
|
||||
expect: {
|
||||
function f(x) {
|
||||
console.log(x);
|
||||
}
|
||||
f([2,3][0]);
|
||||
}
|
||||
expect_stdout: "2"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
issue_2136_3: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
passes: 3,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
toplevel: true,
|
||||
unsafe: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f(x) {
|
||||
console.log(x);
|
||||
}
|
||||
!function(a, ...b) {
|
||||
f(b[0]);
|
||||
}(1, 2, 3);
|
||||
}
|
||||
expect: {
|
||||
console.log(2);
|
||||
}
|
||||
expect_stdout: "2"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
156
test/compress/export.js
Normal file
156
test/compress/export.js
Normal file
@@ -0,0 +1,156 @@
|
||||
issue_2038_1: {
|
||||
options = {
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
mangle = {
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
export var V = 1;
|
||||
export let L = 2;
|
||||
export const C = 3;
|
||||
}
|
||||
expect: {
|
||||
export var V = 1;
|
||||
export let L = 2;
|
||||
export const C = 3;
|
||||
}
|
||||
}
|
||||
|
||||
issue_2038_2: {
|
||||
options = {
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
mangle = {
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
let LET = 1;
|
||||
const CONST = 2;
|
||||
var VAR = 3;
|
||||
export { LET, CONST, VAR };
|
||||
}
|
||||
expect: {
|
||||
let a = 1;
|
||||
const c = 2;
|
||||
var n = 3;
|
||||
export { a as LET, c as CONST, n as VAR };
|
||||
}
|
||||
}
|
||||
|
||||
issue_2126: {
|
||||
mangle = {
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
import { foo as bar, cat as dog } from "stuff";
|
||||
console.log(bar, dog);
|
||||
export { bar as qux };
|
||||
export { dog };
|
||||
}
|
||||
expect: {
|
||||
import { foo as o, cat as f } from "stuff";
|
||||
console.log(o, f);
|
||||
export { o as qux };
|
||||
export { f as dog };
|
||||
}
|
||||
}
|
||||
|
||||
beautify: {
|
||||
beautify = {
|
||||
beautify: true,
|
||||
}
|
||||
input: {
|
||||
export { A as B, C as D };
|
||||
}
|
||||
expect_exact: "export { A as B, C as D };"
|
||||
}
|
||||
|
||||
issue_2131: {
|
||||
options = {
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function no() {
|
||||
console.log(42);
|
||||
}
|
||||
function go() {
|
||||
console.log(42);
|
||||
}
|
||||
var X = 1, Y = 2;
|
||||
export function main() {
|
||||
go(X);
|
||||
};
|
||||
}
|
||||
expect: {
|
||||
function go() {
|
||||
console.log(42);
|
||||
}
|
||||
var X = 1;
|
||||
export function main() {
|
||||
go(X);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
issue_2129: {
|
||||
mangle = {
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
export const { keys } = Object;
|
||||
}
|
||||
expect: {
|
||||
export const { keys } = Object;
|
||||
}
|
||||
}
|
||||
|
||||
async_func: {
|
||||
options = {
|
||||
keep_fargs: false,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
export async function Foo(x){};
|
||||
}
|
||||
expect: {
|
||||
export async function Foo(){};
|
||||
}
|
||||
}
|
||||
|
||||
issue_2134_1: {
|
||||
options = {
|
||||
keep_fargs: false,
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
export function Foo(x){};
|
||||
Foo.prototype = {};
|
||||
}
|
||||
expect: {
|
||||
export function Foo(){};
|
||||
Foo.prototype = {};
|
||||
}
|
||||
}
|
||||
|
||||
issue_2134_2: {
|
||||
options = {
|
||||
keep_fargs: false,
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
export async function Foo(x){};
|
||||
Foo.prototype = {};
|
||||
}
|
||||
expect: {
|
||||
export async function Foo(){};
|
||||
Foo.prototype = {};
|
||||
}
|
||||
}
|
||||
@@ -414,3 +414,99 @@ inner_ref: {
|
||||
}
|
||||
expect_stdout: "1 undefined"
|
||||
}
|
||||
|
||||
issue_2107: {
|
||||
options = {
|
||||
cascade: true,
|
||||
collapse_vars: true,
|
||||
inline: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var c = 0;
|
||||
!function() {
|
||||
c++;
|
||||
}(c++ + new function() {
|
||||
this.a = 0;
|
||||
var a = (c = c + 1) + (c = 1 + c);
|
||||
return c++ + a;
|
||||
}());
|
||||
console.log(c);
|
||||
}
|
||||
expect: {
|
||||
var c = 0;
|
||||
c++, new function() {
|
||||
this.a = 0, c = 1 + (c += 1), c++;
|
||||
}(), c++, console.log(c);
|
||||
}
|
||||
expect_stdout: "5"
|
||||
}
|
||||
|
||||
issue_2114_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
if_return: true,
|
||||
inline: true,
|
||||
keep_fargs: false,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var c = 0;
|
||||
!function(a) {
|
||||
a = 0;
|
||||
}([ {
|
||||
0: c = c + 1,
|
||||
length: c = 1 + c
|
||||
}, typeof void function a() {
|
||||
var b = function f1(a) {
|
||||
}(b && (b.b += (c = c + 1, 0)));
|
||||
}() ]);
|
||||
console.log(c);
|
||||
}
|
||||
expect: {
|
||||
var c = 0;
|
||||
!function() {
|
||||
0;
|
||||
}((c += 1, c = 1 + c, function() {
|
||||
var b = void (b && (b.b += (c += 1, 0)));
|
||||
}()));
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "2"
|
||||
}
|
||||
|
||||
issue_2114_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
if_return: true,
|
||||
inline: true,
|
||||
keep_fargs: false,
|
||||
passes: 2,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var c = 0;
|
||||
!function(a) {
|
||||
a = 0;
|
||||
}([ {
|
||||
0: c = c + 1,
|
||||
length: c = 1 + c
|
||||
}, typeof void function a() {
|
||||
var b = function f1(a) {
|
||||
}(b && (b.b += (c = c + 1, 0)));
|
||||
}() ]);
|
||||
console.log(c);
|
||||
}
|
||||
expect: {
|
||||
var c = 0;
|
||||
c = 1 + (c += 1), function() {
|
||||
var b = void (b && (b.b += (c += 1, 0)));
|
||||
}();
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "2"
|
||||
}
|
||||
|
||||
@@ -664,3 +664,31 @@ class_expression_statement_unused_toplevel: {
|
||||
}
|
||||
expect_exact: ""
|
||||
}
|
||||
|
||||
export_default_function_decl: {
|
||||
options = {
|
||||
toplevel: true,
|
||||
passes: 3,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
// do not drop "unused" exports
|
||||
export default function Foo() {};
|
||||
export function Far() {};
|
||||
}
|
||||
expect_exact: "export default function Foo(){};export function Far(){};"
|
||||
}
|
||||
|
||||
export_default_class_decl: {
|
||||
options = {
|
||||
toplevel: true,
|
||||
passes: 3,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
// do not drop "unused" exports
|
||||
export default class Car {};
|
||||
export class Cab {};
|
||||
}
|
||||
expect_exact: "export default class Car{};export class Cab{};"
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ export_default_func_1: {
|
||||
input: {
|
||||
export default function f(){};
|
||||
}
|
||||
expect_exact: "export default function(){};"
|
||||
expect_exact: "export default function f(){};"
|
||||
}
|
||||
|
||||
export_default_func_2: {
|
||||
@@ -58,7 +58,7 @@ export_default_func_2: {
|
||||
input: {
|
||||
export default function f(){}(1);
|
||||
}
|
||||
expect_exact: "export default function(){};1;"
|
||||
expect_exact: "export default function f(){};1;"
|
||||
}
|
||||
|
||||
export_default_func_3: {
|
||||
@@ -71,7 +71,7 @@ export_default_func_3: {
|
||||
input: {
|
||||
export default function f(){}(1);
|
||||
}
|
||||
expect_exact: "export default function(){};"
|
||||
expect_exact: "export default function f(){};"
|
||||
}
|
||||
|
||||
export_class_1: {
|
||||
@@ -121,7 +121,7 @@ export_default_class_1: {
|
||||
input: {
|
||||
export default class C {};
|
||||
}
|
||||
expect_exact: "export default class{};"
|
||||
expect_exact: "export default class C{};"
|
||||
}
|
||||
|
||||
export_default_class_2: {
|
||||
@@ -134,7 +134,7 @@ export_default_class_2: {
|
||||
input: {
|
||||
export default class C {}(1);
|
||||
}
|
||||
expect_exact: "export default class{};1;"
|
||||
expect_exact: "export default class C{};1;"
|
||||
}
|
||||
|
||||
export_default_class_3: {
|
||||
@@ -147,7 +147,7 @@ export_default_class_3: {
|
||||
input: {
|
||||
export default class C {}(1);
|
||||
}
|
||||
expect_exact: "export default class{};"
|
||||
expect_exact: "export default class C{};"
|
||||
}
|
||||
|
||||
export_mangle_1: {
|
||||
@@ -171,7 +171,7 @@ export_mangle_2: {
|
||||
return one - two;
|
||||
};
|
||||
}
|
||||
expect_exact: "export default function n(n,r){return n-r};"
|
||||
expect_exact: "export default function foo(n,o){return n-o};"
|
||||
}
|
||||
|
||||
export_mangle_3: {
|
||||
@@ -207,7 +207,7 @@ export_mangle_4: {
|
||||
}
|
||||
};
|
||||
}
|
||||
expect_exact: "export default class n{go(n,r){return n-r+n}};"
|
||||
expect_exact: "export default class C{go(n,r){return n-r+n}};"
|
||||
}
|
||||
|
||||
export_mangle_5: {
|
||||
@@ -247,7 +247,7 @@ export_toplevel_1: {
|
||||
}
|
||||
expect: {
|
||||
export function g(){};
|
||||
export default function(){};
|
||||
export default function h(){};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ export_toplevel_2: {
|
||||
}
|
||||
expect: {
|
||||
export class B {};
|
||||
export default class {};
|
||||
export default class C {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -178,3 +178,66 @@ impure_getter_2: {
|
||||
}
|
||||
expect: {}
|
||||
}
|
||||
|
||||
issue_2110_1: {
|
||||
options = {
|
||||
cascade: true,
|
||||
pure_getters: "strict",
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f() {
|
||||
function f() {}
|
||||
function g() {
|
||||
return this;
|
||||
}
|
||||
f.g = g;
|
||||
return f.g();
|
||||
}
|
||||
console.log(typeof f());
|
||||
}
|
||||
expect: {
|
||||
function f() {
|
||||
function f() {}
|
||||
return f.g = function() {
|
||||
return this;
|
||||
}, f.g();
|
||||
}
|
||||
console.log(typeof f());
|
||||
}
|
||||
expect_stdout: "function"
|
||||
}
|
||||
|
||||
issue_2110_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f() {
|
||||
function f() {}
|
||||
function g() {
|
||||
return this;
|
||||
}
|
||||
f.g = g;
|
||||
return f.g();
|
||||
}
|
||||
console.log(typeof f());
|
||||
}
|
||||
expect: {
|
||||
function f() {
|
||||
function f() {}
|
||||
f.g = function() {
|
||||
return this;
|
||||
};
|
||||
return f.g();
|
||||
}
|
||||
console.log(typeof f());
|
||||
}
|
||||
expect_stdout: "function"
|
||||
}
|
||||
|
||||
@@ -255,3 +255,73 @@ issue_1586_2: {
|
||||
}
|
||||
expect_exact: "function f(){try{x()}catch(c){console.log(c.message)}}"
|
||||
}
|
||||
|
||||
issue_2120_1: {
|
||||
mangle = {
|
||||
ie8: false,
|
||||
}
|
||||
input: {
|
||||
"aaaaaaaa";
|
||||
var a = 1, b = "FAIL";
|
||||
try {
|
||||
throw 1;
|
||||
} catch (c) {
|
||||
try {
|
||||
throw 0;
|
||||
} catch (a) {
|
||||
if (c) b = "PASS";
|
||||
}
|
||||
}
|
||||
console.log(b);
|
||||
}
|
||||
expect: {
|
||||
"aaaaaaaa";
|
||||
var a = 1, b = "FAIL";
|
||||
try {
|
||||
throw 1;
|
||||
} catch (t) {
|
||||
try {
|
||||
throw 0;
|
||||
} catch (a) {
|
||||
if (t) b = "PASS";
|
||||
}
|
||||
}
|
||||
console.log(b);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_2120_2: {
|
||||
mangle = {
|
||||
ie8: true,
|
||||
}
|
||||
input: {
|
||||
"aaaaaaaa";
|
||||
var a = 1, b = "FAIL";
|
||||
try {
|
||||
throw 1;
|
||||
} catch (c) {
|
||||
try {
|
||||
throw 0;
|
||||
} catch (a) {
|
||||
if (c) b = "PASS";
|
||||
}
|
||||
}
|
||||
console.log(b);
|
||||
}
|
||||
expect: {
|
||||
"aaaaaaaa";
|
||||
var a = 1, b = "FAIL";
|
||||
try {
|
||||
throw 1;
|
||||
} catch (c) {
|
||||
try {
|
||||
throw 0;
|
||||
} catch (a) {
|
||||
if (c) b = "PASS";
|
||||
}
|
||||
}
|
||||
console.log(b);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
3
test/input/invalid/export_1.js
Normal file
3
test/input/invalid/export_1.js
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
export var V = 1;
|
||||
}
|
||||
1
test/input/invalid/export_2.js
Normal file
1
test/input/invalid/export_2.js
Normal file
@@ -0,0 +1 @@
|
||||
export class{};
|
||||
1
test/input/invalid/export_3.js
Normal file
1
test/input/invalid/export_3.js
Normal file
@@ -0,0 +1 @@
|
||||
export function(){};
|
||||
3
test/input/invalid/import.js
Normal file
3
test/input/invalid/import.js
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
import A from "B";
|
||||
}
|
||||
@@ -533,6 +533,66 @@ describe("bin/uglifyjs", function () {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should throw syntax error (block-level export)", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/invalid/export_1.js -m';
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(stdout, "");
|
||||
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||
"Parse error at test/input/invalid/export_1.js:2,4",
|
||||
" export var V = 1;",
|
||||
" ^",
|
||||
"ERROR: Export statement may only appear at top level"
|
||||
].join("\n"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should throw syntax error (block-level import)", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/invalid/import.js -m';
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(stdout, "");
|
||||
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||
"Parse error at test/input/invalid/import.js:2,4",
|
||||
' import A from "B";',
|
||||
" ^",
|
||||
"ERROR: Import statement may only appear at top level"
|
||||
].join("\n"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should throw syntax error (anonymous class)", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/invalid/export_2.js';
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(stdout, "");
|
||||
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||
"Parse error at test/input/invalid/export_2.js:1,12",
|
||||
"export class{};",
|
||||
" ^",
|
||||
"ERROR: Unexpected token: punc ({)"
|
||||
].join("\n"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should throw syntax error (anonymous function)", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/invalid/export_3.js';
|
||||
|
||||
exec(command, function (err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(stdout, "");
|
||||
assert.strictEqual(stderr.split(/\n/).slice(0, 4).join("\n"), [
|
||||
"Parse error at test/input/invalid/export_3.js:1,15",
|
||||
"export function(){};",
|
||||
" ^",
|
||||
"ERROR: Unexpected token: punc (()"
|
||||
].join("\n"));
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should handle literal string as source map input", function(done) {
|
||||
var command = [
|
||||
uglifyjscmd,
|
||||
|
||||
@@ -2,8 +2,7 @@ var assert = require("assert");
|
||||
var uglify = require("../node");
|
||||
|
||||
describe("Export", function() {
|
||||
it ("Should parse export directives", function() {
|
||||
|
||||
it("Should parse export directives", function() {
|
||||
var inputs = [
|
||||
['export * from "a.js"', ['*'], "a.js"],
|
||||
['export {A} from "a.js"', ['A'], "a.js"],
|
||||
@@ -12,17 +11,17 @@ describe("Export", function() {
|
||||
['export {A, B} from "a.js"', ['A', 'B'], "a.js"],
|
||||
];
|
||||
|
||||
var test = function(code) {
|
||||
function test(code) {
|
||||
return uglify.parse(code);
|
||||
};
|
||||
}
|
||||
|
||||
var extractNames = function(symbols) {
|
||||
function extractNames(symbols) {
|
||||
var ret = [];
|
||||
for (var i = 0; i < symbols.length; i++) {
|
||||
ret.push(symbols[i].name.name)
|
||||
ret.push(symbols[i].foreign_name.name);
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
var ast = test(inputs[i][0]);
|
||||
@@ -34,7 +33,7 @@ describe("Export", function() {
|
||||
assert(st instanceof uglify.AST_Export);
|
||||
var actualNames = extractNames(st.exported_names);
|
||||
assert.deepEqual(actualNames, names);
|
||||
assert.equal(st.module_name.value, filename)
|
||||
assert.equal(st.module_name.value, filename);
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user