Compare commits

...

20 Commits

Author SHA1 Message Date
Alex Lam S.L
94b19a9c46 harmony-v3.1.8 2017-11-07 06:05:40 +08:00
alexlamsl
bcf95ac02c update tests 2017-11-07 04:49:57 +08:00
alexlamsl
e11cec1ab8 Merge branch 'master' into harmony 2017-11-07 04:30:40 +08:00
Alex Lam S.L
38bfb73f06 v3.1.8 2017-11-07 03:55:16 +08:00
Alex Lam S.L
bbedbf4ea0 handle circular function reference gracefully (#2446)
fixes #2442
2017-11-07 02:37:23 +08:00
Alex Lam S.L
2cfb5aa7da account for eval & with in reduce_vars (#2441)
fixes #2440
2017-11-06 16:10:57 +08:00
Alex Lam S.L
6c45101870 consolidate & enhance unused (#2439)
- defer declaration removal in `collapse_vars`
- account for `AST_SymbolFunarg` in deduplication
- private accounting for `collapse_vars`
- avoid issues with identity reference due to deep cloning

fixes #2437
2017-11-06 14:25:10 +08:00
Alex Lam S.L
2c2fd89e34 inline single-use functions that are not constant expressions (#2434)
fixes #2428
2017-11-05 22:14:11 +08:00
Alex Lam S.L
0c43519097 harmony-v3.1.7 2017-11-05 17:25:57 +08:00
alexlamsl
352a7de204 update tests 2017-11-05 16:48:00 +08:00
alexlamsl
df9c8dfd72 Merge branch 'master' into harmony-v3.1.7 2017-11-05 15:47:40 +08:00
Alex Lam S.L
f46281e2b7 v3.1.7 2017-11-05 15:03:19 +08:00
Alex Lam S.L
25a18883f5 tweak #2424 (#2432) 2017-11-05 12:49:14 +08:00
Alex Lam S.L
5b4b07e9a7 extend function inlining safety checks (#2430) 2017-11-05 06:18:45 +08:00
Alex Lam S.L
a8aa28a7a6 consolidate single-use function reduction (#2427)
fixes #2423
2017-11-05 04:27:01 +08:00
Alex Lam S.L
fe5a68f9d5 maintain call argument order in collapse_vars (#2426)
fixes #2425
2017-11-05 00:00:18 +08:00
Alex Lam S.L
71e61153b1 improve variations on call arguments for ufuzz (#2424) 2017-11-04 16:29:42 +08:00
Alex Lam S.L
c8b6f4733d reduce this within functions (#2421)
- only replace same-scope usages
- augment `test/ufuzz.js` to test for `this`


fixes #2420
2017-11-04 00:31:37 +08:00
kzc
29bbc41dfe hoist_props: implement limited hoisting of class expressions (#2415) 2017-10-30 23:20:54 +08:00
Alex Lam S.L
a48f87abf2 compress new function containing this (#2417) 2017-10-30 23:19:27 +08:00
13 changed files with 1232 additions and 218 deletions

View File

@@ -156,7 +156,9 @@ merge(Compressor.prototype, {
} }
var passes = +this.options.passes || 1; var passes = +this.options.passes || 1;
var last_count = 1 / 0; var last_count = 1 / 0;
var mangle = { ie8: this.option("ie8") };
for (var pass = 0; pass < passes; pass++) { for (var pass = 0; pass < passes; pass++) {
node.figure_out_scope(mangle);
if (pass > 0 || this.option("reduce_vars")) if (pass > 0 || this.option("reduce_vars"))
node.reset_opt_flags(this); node.reset_opt_flags(this);
node = node.transform(this); node = node.transform(this);
@@ -291,7 +293,7 @@ merge(Compressor.prototype, {
self.transform(tt); self.transform(tt);
}); });
AST_Node.DEFMETHOD("reset_opt_flags", function(compressor) { AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
var reduce_vars = compressor.option("reduce_vars"); var reduce_vars = compressor.option("reduce_vars");
var unused = compressor.option("unused"); var unused = compressor.option("unused");
// Stack of look-up tables to keep track of whether a `SymbolDef` has been // Stack of look-up tables to keep track of whether a `SymbolDef` has been
@@ -322,13 +324,19 @@ merge(Compressor.prototype, {
d.fixed = false; d.fixed = false;
} else if (d.fixed) { } else if (d.fixed) {
var value = node.fixed_value(); var value = node.fixed_value();
if (unused && !compressor.exposed(d)) { if (value && ref_once(d) && !compressor.exposed(d)) {
d.single_use = value if (value instanceof AST_Lambda) {
&& d.references.length == 1 d.single_use = d.scope === node.scope
&& !(d.orig[0] instanceof AST_SymbolFunarg)
|| value.is_constant_expression(node.scope);
} else {
d.single_use = d.scope === node.scope
&& loop_ids[d.id] === in_loop && loop_ids[d.id] === in_loop
&& d.scope === node.scope
&& value.is_constant_expression(); && value.is_constant_expression();
} }
} else {
d.single_use = false;
}
if (is_modified(node, value, 0, is_immutable(value))) { if (is_modified(node, value, 0, is_immutable(value))) {
if (d.single_use) { if (d.single_use) {
d.single_use = "m"; d.single_use = "m";
@@ -390,6 +398,10 @@ merge(Compressor.prototype, {
} else { } else {
d.fixed = node; d.fixed = node;
mark(d, true); mark(d, true);
if (ref_once(d)) {
d.single_use = d.scope === d.references[0].scope
|| node.is_constant_expression(d.references[0].scope);
}
} }
var save_ids = safe_ids; var save_ids = safe_ids;
safe_ids = Object.create(null); safe_ids = Object.create(null);
@@ -542,6 +554,7 @@ merge(Compressor.prototype, {
} }
return true; return true;
} }
return def.fixed instanceof AST_Defun;
} }
function safe_to_assign(def, value) { function safe_to_assign(def, value) {
@@ -567,7 +580,7 @@ merge(Compressor.prototype, {
function reset_def(def) { function reset_def(def) {
def.direct_access = false; def.direct_access = false;
def.escaped = false; def.escaped = false;
if (def.scope.uses_eval) { if (def.scope.uses_eval || def.scope.uses_with) {
def.fixed = false; def.fixed = false;
} else if (def.orig[0] instanceof AST_SymbolConst || !compressor.exposed(def)) { } else if (def.orig[0] instanceof AST_SymbolConst || !compressor.exposed(def)) {
def.fixed = undefined; def.fixed = undefined;
@@ -579,8 +592,15 @@ merge(Compressor.prototype, {
def.single_use = undefined; def.single_use = undefined;
} }
function ref_once(def) {
return unused && !def.scope.uses_eval && !def.scope.uses_with && def.references.length == 1;
}
function is_immutable(value) { function is_immutable(value) {
return value && (value.is_constant() || value instanceof AST_Lambda); if (!value) return false;
return value.is_constant()
|| value instanceof AST_Lambda
|| value instanceof AST_This;
} }
function read_property(obj, key) { function read_property(obj, key) {
@@ -608,7 +628,7 @@ merge(Compressor.prototype, {
|| !immutable || !immutable
&& parent instanceof AST_Call && parent instanceof AST_Call
&& parent.expression === node && parent.expression === node
&& (!(value instanceof AST_Function) || value.contains_this())) { && (!(value instanceof AST_Function) || value.contains_this(parent))) {
return true; return true;
} else if (parent instanceof AST_Array || parent instanceof AST_Object) { } else if (parent instanceof AST_Array || parent instanceof AST_Object) {
return is_modified(parent, parent, level + 1); return is_modified(parent, parent, level + 1);
@@ -619,7 +639,11 @@ merge(Compressor.prototype, {
function mark_escaped(d, node, value, level) { function mark_escaped(d, node, value, level) {
var parent = tw.parent(level); var parent = tw.parent(level);
if (value instanceof AST_Constant || value instanceof AST_Function) return; if (value instanceof AST_Constant
|| value instanceof AST_Function
|| value instanceof AST_ClassExpression) {
return;
}
if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right
|| parent instanceof AST_Call && node !== parent.expression || parent instanceof AST_Call && node !== parent.expression
|| parent instanceof AST_Return && node === parent.value && node.scope !== d.scope || parent instanceof AST_Return && node === parent.value && node.scope !== d.scope
@@ -811,6 +835,14 @@ merge(Compressor.prototype, {
|| compressor.option("unsafe") && global_names(this.name); || compressor.option("unsafe") && global_names(this.name);
}); });
function drop_decl(def) {
def._eliminiated = (def._eliminiated || 0) + 1;
if (def.orig.length == def._eliminiated) {
def.scope.functions.del(def.name);
def.scope.variables.del(def.name);
}
}
function tighten_body(statements, compressor) { function tighten_body(statements, compressor) {
var CHANGED, max_iter = 10; var CHANGED, max_iter = 10;
do { do {
@@ -844,6 +876,7 @@ merge(Compressor.prototype, {
function collapse(statements, compressor) { function collapse(statements, compressor) {
var scope = compressor.find_parent(AST_Scope).get_defun_scope(); var scope = compressor.find_parent(AST_Scope).get_defun_scope();
if (scope.uses_eval || scope.uses_with) return statements; if (scope.uses_eval || scope.uses_with) return statements;
var args;
var candidates = []; var candidates = [];
var stat_index = statements.length; var stat_index = statements.length;
while (--stat_index >= 0) { while (--stat_index >= 0) {
@@ -864,7 +897,7 @@ merge(Compressor.prototype, {
var one_off = lhs instanceof AST_Symbol && lhs.definition().references.length == 1; var one_off = lhs instanceof AST_Symbol && lhs.definition().references.length == 1;
var side_effects = value_has_side_effects(candidate); var side_effects = value_has_side_effects(candidate);
var hit = candidate.name instanceof AST_SymbolFunarg; var hit = candidate.name instanceof AST_SymbolFunarg;
var abort = false, replaced = false; var abort = false, replaced = false, can_replace = !args || !hit;
var tt = new TreeTransformer(function(node, descend) { var tt = new TreeTransformer(function(node, descend) {
if (abort) return node; if (abort) return node;
// Skip nodes before `candidate` as quickly as possible // Skip nodes before `candidate` as quickly as possible
@@ -891,7 +924,8 @@ merge(Compressor.prototype, {
return node; return node;
} }
// Replace variable with assignment when found // Replace variable with assignment when found
if (!(node instanceof AST_SymbolDeclaration) if (can_replace
&& !(node instanceof AST_SymbolDeclaration)
&& !is_lhs(node, parent) && !is_lhs(node, parent)
&& lhs.equivalent_to(node)) { && lhs.equivalent_to(node)) {
CHANGED = replaced = abort = true; CHANGED = replaced = abort = true;
@@ -942,6 +976,12 @@ merge(Compressor.prototype, {
// Skip (non-executed) functions and (leading) default case in switch statements // Skip (non-executed) functions and (leading) default case in switch statements
if (node instanceof AST_Default || node instanceof AST_Scope) return node; if (node instanceof AST_Default || node instanceof AST_Scope) return node;
}); });
if (!can_replace) {
for (var j = compressor.self().argnames.lastIndexOf(candidate.__name || candidate.name) + 1; j < args.length; j++) {
args[j].transform(tt);
}
can_replace = true;
}
for (var i = stat_index; !abort && i < statements.length; i++) { for (var i = stat_index; !abort && i < statements.length; i++) {
statements[i].transform(tt); statements[i].transform(tt);
} }
@@ -987,9 +1027,16 @@ merge(Compressor.prototype, {
})) { })) {
var fn_strict = compressor.has_directive("use strict"); var fn_strict = compressor.has_directive("use strict");
if (fn_strict && fn.body.indexOf(fn_strict) < 0) fn_strict = false; if (fn_strict && fn.body.indexOf(fn_strict) < 0) fn_strict = false;
var len = fn.argnames.length;
args = iife.args.slice(len);
var names = Object.create(null); var names = Object.create(null);
for (var i = fn.argnames.length; --i >= 0;) { for (var i = len; --i >= 0;) {
var sym = fn.argnames[i]; var sym = fn.argnames[i];
var arg = iife.args[i];
args.unshift(make_node(AST_VarDef, sym, {
name: sym,
value: arg
}));
if (sym.name in names) continue; if (sym.name in names) continue;
names[sym.name] = true; names[sym.name] = true;
if (sym instanceof AST_Expansion) { if (sym instanceof AST_Expansion) {
@@ -1003,9 +1050,9 @@ merge(Compressor.prototype, {
elements: elements elements: elements
}) })
})); }));
candidates[0].__name = sym;
} }
} else { } else {
var arg = iife.args[i];
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor); if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null; else if (has_overlapping_symbol(fn, arg, fn_strict)) arg = null;
if (arg) candidates.unshift(make_node(AST_VarDef, sym, { if (arg) candidates.unshift(make_node(AST_VarDef, sym, {
@@ -1037,7 +1084,8 @@ merge(Compressor.prototype, {
function get_lhs(expr) { function get_lhs(expr) {
if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) { if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
var def = expr.name.definition(); var def = expr.name.definition();
if (def.orig.length > 1 && !(expr.name instanceof AST_SymbolFunarg) if (def.orig.length - (def._eliminiated || 0) > 1
&& !(expr.name instanceof AST_SymbolFunarg)
|| def.references.length == 1 && !compressor.exposed(def)) { || def.references.length == 1 && !compressor.exposed(def)) {
return make_node(AST_SymbolRef, expr.name, expr.name); return make_node(AST_SymbolRef, expr.name, expr.name);
} }
@@ -1047,6 +1095,10 @@ merge(Compressor.prototype, {
} }
} }
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function get_lvalues(expr) { function get_lvalues(expr) {
var lvalues = Object.create(null); var lvalues = Object.create(null);
if (expr instanceof AST_Unary) return lvalues; if (expr instanceof AST_Unary) return lvalues;
@@ -1057,7 +1109,7 @@ merge(Compressor.prototype, {
lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent()); lvalues[sym.name] = lvalues[sym.name] || is_lhs(node, tw.parent());
} }
}); });
expr[expr instanceof AST_Assign ? "right" : "value"].walk(tw); get_rvalue(expr).walk(tw);
return lvalues; return lvalues;
} }
@@ -1086,7 +1138,9 @@ merge(Compressor.prototype, {
if (node === expr) { if (node === expr) {
found = true; found = true;
if (node instanceof AST_VarDef) { if (node instanceof AST_VarDef) {
remove(node.name.definition().orig, node.name); drop_decl(node.name.definition());
node.value = null;
return node;
} }
return in_list ? MAP.skip : null; return in_list ? MAP.skip : null;
} }
@@ -1095,16 +1149,13 @@ merge(Compressor.prototype, {
case 0: return null; case 0: return null;
case 1: return node.expressions[0]; case 1: return node.expressions[0];
} }
if (node instanceof AST_Definitions && node.definitions.length == 0 if (node instanceof AST_SimpleStatement && !node.body) return null;
|| node instanceof AST_SimpleStatement && !node.body) {
return null;
}
})); }));
} }
function value_has_side_effects(expr) { function value_has_side_effects(expr) {
if (expr instanceof AST_Unary) return false; if (expr instanceof AST_Unary) return false;
return expr[expr instanceof AST_Assign ? "right" : "value"].has_side_effects(compressor); return get_rvalue(expr).has_side_effects(compressor);
} }
function references_in_scope(def) { function references_in_scope(def) {
@@ -2261,18 +2312,22 @@ merge(Compressor.prototype, {
} }
def(AST_Node, return_false); def(AST_Node, return_false);
def(AST_Constant, return_true); def(AST_Constant, return_true);
def(AST_Function, function(){ def(AST_Lambda, function(scope){
var self = this; var self = this;
var result = true; var result = true;
self.walk(new TreeWalker(function(node) { self.walk(new TreeWalker(function(node) {
if (!result) return true; if (!result) return true;
if (node instanceof AST_SymbolRef) { if (node instanceof AST_SymbolRef) {
var def = node.definition(); var def = node.definition();
if (self.enclosed.indexOf(def) >= 0 if (member(def, self.enclosed)
&& self.variables.get(def.name) !== def) { && !self.variables.has(def.name)) {
result = false; if (scope) {
return true; var scope_def = scope.find_variable(node);
if (def.undeclared ? !scope_def : scope_def === def) return true;
} }
result = false;
}
return true;
} }
})); }));
return result; return result;
@@ -2389,7 +2444,7 @@ merge(Compressor.prototype, {
// this scope (not in nested scopes). // this scope (not in nested scopes).
var scope = this; var scope = this;
var tw = new TreeWalker(function(node, descend){ var tw = new TreeWalker(function(node, descend){
if (node !== self) { if (node === self) return;
if (node instanceof AST_Defun || node instanceof AST_DefClass) { if (node instanceof AST_Defun || node instanceof AST_DefClass) {
var in_export = tw.parent() instanceof AST_Export; var in_export = tw.parent() instanceof AST_Export;
if (in_export || !drop_funcs && scope === self) { if (in_export || !drop_funcs && scope === self) {
@@ -2402,6 +2457,9 @@ merge(Compressor.prototype, {
initializations.add(node.name.name, node); initializations.add(node.name.name, node);
return true; // don't go in nested scopes return true; // don't go in nested scopes
} }
if (node instanceof AST_SymbolFunarg && scope === self) {
var_defs_by_id.add(node.definition().id, node);
}
if (node instanceof AST_Definitions && scope === self) { if (node instanceof AST_Definitions && scope === self) {
var in_export = tw.parent() instanceof AST_Export; var in_export = tw.parent() instanceof AST_Export;
node.definitions.forEach(function(def){ node.definitions.forEach(function(def){
@@ -2435,8 +2493,11 @@ merge(Compressor.prototype, {
}); });
return true; return true;
} }
if (assign_as_unused(node) instanceof AST_SymbolRef && scope === self var sym;
&& !is_ref_of(node.left, AST_SymbolBlockDeclaration)) { if (scope === self
&& (sym = assign_as_unused(node)) instanceof AST_SymbolRef
&& !is_ref_of(node.left, AST_SymbolBlockDeclaration)
&& self.variables.get(sym.name) === sym.definition()) {
if (node instanceof AST_Assign) node.right.walk(tw); if (node instanceof AST_Assign) node.right.walk(tw);
return true; return true;
} }
@@ -2458,7 +2519,6 @@ merge(Compressor.prototype, {
if (node.destructuring && destructuring_value) { if (node.destructuring && destructuring_value) {
initializations.add(node.name, destructuring_value); initializations.add(node.name, destructuring_value);
} }
}
}); });
self.walk(tw); self.walk(tw);
// pass 2: for every used symbol we need to walk its // pass 2: for every used symbol we need to walk its
@@ -2522,9 +2582,11 @@ merge(Compressor.prototype, {
} }
} }
if ((node instanceof AST_Defun || node instanceof AST_DefClass) && 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; var def = node.name.definition();
var keep = (def.id in in_use_ids) || !drop_funcs && def.global;
if (!keep) { if (!keep) {
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name)); compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
drop_decl(def);
return make_node(AST_EmptyStatement, node); return make_node(AST_EmptyStatement, node);
} }
return node; return node;
@@ -2549,7 +2611,7 @@ merge(Compressor.prototype, {
if (var_defs.length > 1 && !def.value) { if (var_defs.length > 1 && !def.value) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name)); compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
remove(var_defs, def); remove(var_defs, def);
remove(sym.orig, def.name); drop_decl(sym);
return; return;
} }
} }
@@ -2582,7 +2644,7 @@ merge(Compressor.prototype, {
} else { } else {
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name)); compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
} }
remove(sym.orig, def.name); drop_decl(sym);
} }
}); });
if (head.length == 0 && tail.length == 1 && tail[0].name instanceof AST_SymbolVar) { if (head.length == 0 && tail.length == 1 && tail[0].name instanceof AST_SymbolVar) {
@@ -2591,7 +2653,7 @@ merge(Compressor.prototype, {
var def = tail.pop(); var def = tail.pop();
compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", template(def.name)); compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", template(def.name));
remove(var_defs, def); remove(var_defs, def);
remove(def.name.definition().orig, def.name); drop_decl(def.name.definition());
side_effects.unshift(make_node(AST_Assign, def, { side_effects.unshift(make_node(AST_Assign, def, {
operator: "=", operator: "=",
left: make_node(AST_SymbolRef, def.name, def.name), left: make_node(AST_SymbolRef, def.name, def.name),
@@ -2623,8 +2685,7 @@ merge(Compressor.prototype, {
var def = assign_as_unused(node); var def = assign_as_unused(node);
if (def instanceof AST_SymbolRef if (def instanceof AST_SymbolRef
&& !((def = def.definition()).id in in_use_ids) && !((def = def.definition()).id in in_use_ids)
&& (drop_vars || !def.global) && (drop_vars || !def.global)) {
&& self.variables.get(def.name) === def) {
if (node instanceof AST_Assign) { if (node instanceof AST_Assign) {
return maintain_this_binding(parent, node, node.right.transform(tt)); return maintain_this_binding(parent, node, node.right.transform(tt));
} }
@@ -3440,7 +3501,7 @@ merge(Compressor.prototype, {
}); });
a.push(var_); a.push(var_);
} }
remove(def.name.definition().orig, def.name); drop_decl(def.name.definition());
return a; return a;
}, []); }, []);
if (assignments.length == 0) return null; if (assignments.length == 0) return null;
@@ -3723,6 +3784,7 @@ merge(Compressor.prototype, {
&& !exp.uses_arguments && !exp.uses_arguments
&& !exp.uses_eval && !exp.uses_eval
&& (exp.body instanceof AST_Node || exp.body.length == 1) && (exp.body instanceof AST_Node || exp.body.length == 1)
&& !exp.contains_this()
&& all(exp.argnames, function(arg) { && all(exp.argnames, function(arg) {
if (arg instanceof AST_Expansion) return arg.expression.__unused; if (arg instanceof AST_Expansion) return arg.expression.__unused;
return arg.__unused; return arg.__unused;
@@ -3737,23 +3799,6 @@ merge(Compressor.prototype, {
expression: stat.body expression: stat.body
}); });
} }
if (value) {
var tw = new TreeWalker(function(node) {
if (!value) return true;
if (node instanceof AST_SymbolRef) {
var ref = node.scope.find_variable(node);
if (ref && ref.scope.parent_scope === fn.parent_scope) {
value = null;
return true;
}
}
if (node instanceof AST_This && !tw.find_parent(AST_Scope)) {
value = null;
return true;
}
});
value.walk(tw);
}
if (value) { if (value) {
var args = self.args.concat(value); var args = self.args.concat(value);
return make_sequence(self, args).optimize(compressor); return make_sequence(self, args).optimize(compressor);
@@ -4043,7 +4088,8 @@ merge(Compressor.prototype, {
function is_object(node) { function is_object(node) {
return node instanceof AST_Array return node instanceof AST_Array
|| node instanceof AST_Lambda || node instanceof AST_Lambda
|| node instanceof AST_Object; || node instanceof AST_Object
|| node instanceof AST_Class;
} }
OPT(AST_Binary, function(self, compressor){ OPT(AST_Binary, function(self, compressor){
@@ -4448,21 +4494,37 @@ merge(Compressor.prototype, {
if (fixed instanceof AST_Defun) { if (fixed instanceof AST_Defun) {
d.fixed = fixed = make_node(AST_Function, fixed, fixed); d.fixed = fixed = make_node(AST_Function, fixed, fixed);
} }
if (compressor.option("unused") if (fixed && d.single_use) {
&& fixed var recurse;
&& d.references.length == 1 if (fixed instanceof AST_Function) {
&& (d.single_use || is_func_expr(fixed) for (var i = 0; recurse = compressor.parent(i); i++) {
&& !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg) if (recurse instanceof AST_Lambda) {
&& !d.scope.uses_eval var name = recurse.name;
&& compressor.find_parent(AST_Scope) === fixed.parent_scope)) { if (name && name.definition() === d) break;
}
}
}
if (!recurse) {
var value = fixed.optimize(compressor); var value = fixed.optimize(compressor);
return value === fixed ? fixed.clone(true) : value; return value === fixed ? fixed.clone(true) : value;
} }
if (compressor.option("evaluate") && fixed) { }
if (d.should_replace === undefined) { if (fixed && d.should_replace === undefined) {
var init = fixed.evaluate(compressor); var init;
if (init !== fixed && (compressor.option("unsafe_regexp") || !(init instanceof RegExp))) { if (fixed instanceof AST_This) {
init = make_node_from_constant(init, fixed); if (!(d.orig[0] instanceof AST_SymbolFunarg)
&& all(d.references, function(ref) {
return d.scope === ref.scope;
})) {
init = fixed;
}
} else {
var ev = fixed.evaluate(compressor);
if (ev !== fixed && (compressor.option("unsafe_regexp") || !(ev instanceof RegExp))) {
init = make_node_from_constant(ev, fixed);
}
}
if (init) {
var value_length = init.optimize(compressor).print_to_string().length; var value_length = init.optimize(compressor).print_to_string().length;
var fn; var fn;
if (has_symbol_ref(fixed)) { if (has_symbol_ref(fixed)) {
@@ -4491,7 +4553,6 @@ merge(Compressor.prototype, {
return d.should_replace(); return d.should_replace();
} }
} }
}
return self; return self;
function has_symbol_ref(value) { function has_symbol_ref(value) {
@@ -4827,11 +4888,11 @@ merge(Compressor.prototype, {
} }
} }
if (is_lhs(self, compressor.parent())) return self; if (is_lhs(self, compressor.parent())) return self;
if (compressor.option("properties") && key !== prop) { if (key !== prop) {
var node = self.flatten_object(property, compressor); var sub = self.flatten_object(property, compressor);
if (node) { if (sub) {
expr = self.expression = node.expression; expr = self.expression = sub.expression;
prop = self.property = node.property; prop = self.property = sub.property;
} }
} }
if (compressor.option("properties") && compressor.option("side_effects") if (compressor.option("properties") && compressor.option("side_effects")
@@ -4877,7 +4938,8 @@ merge(Compressor.prototype, {
return self; return self;
}); });
AST_Lambda.DEFMETHOD("contains_this", function() { AST_Lambda.DEFMETHOD("contains_this", function(grandparent) {
if (grandparent instanceof AST_New) return false;
var result; var result;
var self = this; var self = this;
self.walk(new TreeWalker(function(node) { self.walk(new TreeWalker(function(node) {
@@ -4889,6 +4951,7 @@ merge(Compressor.prototype, {
}); });
AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) { AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
if (!compressor.option("properties")) return;
var arrows = compressor.option("unsafe_arrows") && compressor.option("ecma") >= 6; var arrows = compressor.option("unsafe_arrows") && compressor.option("ecma") >= 6;
var expr = this.expression; var expr = this.expression;
if (expr instanceof AST_Object) { if (expr instanceof AST_Object) {
@@ -4902,7 +4965,7 @@ merge(Compressor.prototype, {
})) break; })) break;
var value = prop.value; var value = prop.value;
if ((value instanceof AST_Accessor || value instanceof AST_Function) if ((value instanceof AST_Accessor || value instanceof AST_Function)
&& value.contains_this()) break; && value.contains_this(compressor.parent())) break;
return make_node(AST_Sub, this, { return make_node(AST_Sub, this, {
expression: make_node(AST_Array, expr, { expression: make_node(AST_Array, expr, {
elements: props.map(function(prop) { elements: props.map(function(prop) {
@@ -4952,10 +5015,8 @@ merge(Compressor.prototype, {
} }
} }
if (is_lhs(self, compressor.parent())) return self; if (is_lhs(self, compressor.parent())) return self;
if (compressor.option("properties")) { var sub = self.flatten_object(self.property, compressor);
var node = self.flatten_object(self.property, compressor); if (sub) return sub.optimize(compressor);
if (node) return node.optimize(compressor);
}
var ev = self.evaluate(compressor); var ev = self.evaluate(compressor);
if (ev !== self) { if (ev !== self) {
ev = make_node_from_constant(ev, self).optimize(compressor); ev = make_node_from_constant(ev, self).optimize(compressor);

View File

@@ -141,11 +141,9 @@ function minify(files, options) {
if (options.wrap) { if (options.wrap) {
toplevel = toplevel.wrap_commonjs(options.wrap); toplevel = toplevel.wrap_commonjs(options.wrap);
} }
if (timings) timings.scope1 = Date.now();
if (options.compress) toplevel.figure_out_scope(options.mangle);
if (timings) timings.compress = Date.now(); if (timings) timings.compress = Date.now();
if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel); if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel);
if (timings) timings.scope2 = Date.now(); if (timings) timings.scope = Date.now();
if (options.mangle) toplevel.figure_out_scope(options.mangle); if (options.mangle) toplevel.figure_out_scope(options.mangle);
if (timings) timings.mangle = Date.now(); if (timings) timings.mangle = Date.now();
if (options.mangle) { if (options.mangle) {
@@ -203,9 +201,9 @@ function minify(files, options) {
if (timings) { if (timings) {
timings.end = Date.now(); timings.end = Date.now();
result.timings = { result.timings = {
parse: 1e-3 * (timings.scope1 - timings.parse), parse: 1e-3 * (timings.compress - timings.parse),
scope: 1e-3 * (timings.compress - timings.scope1 + timings.mangle - timings.scope2), compress: 1e-3 * (timings.scope - timings.compress),
compress: 1e-3 * (timings.scope2 - timings.compress), scope: 1e-3 * (timings.mangle - timings.scope),
mangle: 1e-3 * (timings.properties - timings.mangle), mangle: 1e-3 * (timings.properties - timings.mangle),
properties: 1e-3 * (timings.output - timings.properties), properties: 1e-3 * (timings.output - timings.properties),
output: 1e-3 * (timings.end - timings.output), output: 1e-3 * (timings.end - timings.output),

View File

@@ -4,7 +4,7 @@
"homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony", "homepage": "https://github.com/mishoo/UglifyJS2/tree/harmony",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"version": "3.1.6", "version": "3.1.8",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },

View File

@@ -314,17 +314,12 @@ issue_2105_1: {
}); });
} }
expect: { expect: {
(() => { ({
var quux = () => {
console.log("PASS");
};
return {
prop() { prop() {
console.log; console.log;
quux(); console.log("PASS");
} }
}; }).prop();
})().prop();
} }
expect_stdout: "PASS" expect_stdout: "PASS"
node_version: ">=4" node_version: ">=4"
@@ -360,17 +355,12 @@ issue_2105_2: {
}); });
} }
expect: { expect: {
(() => { ({
var quux = () => {
console.log("PASS");
};
return {
prop: () => { prop: () => {
console.log; console.log;
quux(); console.log("PASS");
} }
}; }).prop();
})().prop();
} }
expect_stdout: "PASS" expect_stdout: "PASS"
node_version: ">=6" node_version: ">=6"

View File

@@ -1487,6 +1487,7 @@ issue_1605_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
toplevel: false, toplevel: false,
unused: true,
} }
input: { input: {
function foo(x) { function foo(x) {
@@ -1509,6 +1510,7 @@ issue_1605_2: {
options = { options = {
collapse_vars: true, collapse_vars: true,
toplevel: "vars", toplevel: "vars",
unused: true,
} }
input: { input: {
function foo(x) { function foo(x) {
@@ -1636,6 +1638,7 @@ issue_1631_3: {
var_side_effects_1: { var_side_effects_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
unused: true,
} }
input: { input: {
var print = console.log.bind(console); var print = console.log.bind(console);
@@ -1658,6 +1661,7 @@ var_side_effects_1: {
var_side_effects_2: { var_side_effects_2: {
options = { options = {
collapse_vars: true, collapse_vars: true,
unused: true,
} }
input: { input: {
var print = console.log.bind(console); var print = console.log.bind(console);
@@ -1683,6 +1687,7 @@ var_side_effects_3: {
collapse_vars: true, collapse_vars: true,
pure_getters: true, pure_getters: true,
unsafe: true, unsafe: true,
unused: true,
} }
input: { input: {
var print = console.log.bind(console); var print = console.log.bind(console);
@@ -1758,6 +1763,7 @@ iife_2: {
}(foo); }(foo);
} }
expect: { expect: {
var foo;
!function(x) { !function(x) {
console.log(x); console.log(x);
}(bar()); }(bar());
@@ -2044,6 +2050,7 @@ ref_scope: {
chained_1: { chained_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
unused: true,
} }
input: { input: {
var a = 2; var a = 2;
@@ -2060,6 +2067,7 @@ chained_1: {
chained_2: { chained_2: {
options = { options = {
collapse_vars: true, collapse_vars: true,
unused: true,
} }
input: { input: {
var a; var a;
@@ -2160,6 +2168,7 @@ inner_lvalues: {
double_def: { double_def: {
options = { options = {
collapse_vars: true, collapse_vars: true,
unused: true,
} }
input: { input: {
var a = x, a = a && y; var a = x, a = a && y;
@@ -2174,6 +2183,7 @@ double_def: {
toplevel_single_reference: { toplevel_single_reference: {
options = { options = {
collapse_vars: true, collapse_vars: true,
unused: true,
} }
input: { input: {
var a; var a;
@@ -2183,11 +2193,12 @@ toplevel_single_reference: {
} }
} }
expect: { expect: {
for (var b in x) {
var a; var a;
for (var b in x)
b(a = b); b(a = b);
} }
} }
}
unused_orig: { unused_orig: {
options = { options = {
@@ -2215,10 +2226,11 @@ unused_orig: {
expect: { expect: {
var a = 1; var a = 1;
console.log(function(b) { console.log(function(b) {
var a;
var c = b; var c = b;
for (var d in c) for (var d in c) {
var a;
return --b + (a = c[0]); return --b + (a = c[0]);
}
a && a.NaN; a && a.NaN;
}([2]), a); }([2]), a);
} }
@@ -2565,7 +2577,9 @@ issue_2250_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
conditionals: true, conditionals: true,
passes: 2,
reduce_vars: true, reduce_vars: true,
unused: true,
} }
input: { input: {
function f(x) { function f(x) {
@@ -2607,6 +2621,7 @@ issue_2250_2: {
reduce_vars: true, reduce_vars: true,
side_effects: true, side_effects: true,
toplevel: true, toplevel: true,
unused: true,
} }
input: { input: {
{ {
@@ -3163,6 +3178,7 @@ pure_getters_chain: {
options = { options = {
collapse_vars: true, collapse_vars: true,
pure_getters: true, pure_getters: true,
unused: true,
} }
input: { input: {
function o(t, r) { function o(t, r) {
@@ -3183,6 +3199,7 @@ pure_getters_chain: {
conditional_1: { conditional_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
unused: true,
} }
input: { input: {
function f(a, b) { function f(a, b) {
@@ -3207,6 +3224,7 @@ conditional_1: {
conditional_2: { conditional_2: {
options = { options = {
collapse_vars: true, collapse_vars: true,
unused: true,
} }
input: { input: {
function f(a, b) { function f(a, b) {
@@ -3223,3 +3241,117 @@ conditional_2: {
} }
expect_stdout: "5 5" expect_stdout: "5 5"
} }
issue_2425_1: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a = 8;
(function(b) {
b.toString();
})(--a, a |= 10);
console.log(a);
}
expect: {
var a = 8;
(function(b) {
b.toString();
})(--a, a |= 10);
console.log(a);
}
expect_stdout: "15"
}
issue_2425_2: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a = 8;
(function(b, c) {
b.toString();
})(--a, a |= 10);
console.log(a);
}
expect: {
var a = 8;
(function(b, c) {
b.toString();
})(--a, a |= 10);
console.log(a);
}
expect_stdout: "15"
}
issue_2425_3: {
options = {
collapse_vars: true,
unused: true,
}
input: {
var a = 8;
(function(b, b) {
b.toString();
})(--a, a |= 10);
console.log(a);
}
expect: {
var a = 8;
(function(b, b) {
(a |= 10).toString();
})(--a);
console.log(a);
}
expect_stdout: "15"
}
issue_2437: {
options = {
collapse_vars: true,
conditionals: true,
inline: true,
join_vars: true,
reduce_vars: true,
side_effects: true,
sequences: true,
toplevel: true,
unused: true,
}
input: {
function foo() {
bar();
}
function bar() {
if (xhrDesc) {
var req = new XMLHttpRequest();
var result = !!req.onreadystatechange;
Object.defineProperty(XMLHttpRequest.prototype, 'onreadystatechange', xhrDesc || {});
return result;
}
else {
var req = new XMLHttpRequest();
var detectFunc = function () { };
req.onreadystatechange = detectFunc;
var result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc;
req.onreadystatechange = null;
return result;
}
}
foo();
}
expect: {
!function() {
if (xhrDesc)
return result = !!(req = new XMLHttpRequest()).onreadystatechange,
Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}),
result;
var req = new XMLHttpRequest(), detectFunc = function() {};
req.onreadystatechange = detectFunc;
var result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc;
req.onreadystatechange = null;
}();
}
}

View File

@@ -1294,11 +1294,11 @@ issue_2063: {
} }
} }
issue_2105: { issue_2105_1: {
options = { options = {
collapse_vars: true, collapse_vars: true,
inline: true, inline: true,
passes: 3, passes: 2,
reduce_vars: true, reduce_vars: true,
side_effects: true, side_effects: true,
unused: true, unused: true,
@@ -1324,17 +1324,50 @@ issue_2105: {
}); });
} }
expect: { expect: {
(function() { ({
var quux = function() {
console.log("PASS");
};
return {
prop: function() { prop: function() {
console.log; console.log;
quux(); console.log("PASS");
} }
}).prop();
}
expect_stdout: "PASS"
}
issue_2105_2: {
options = {
collapse_vars: true,
inline: true,
passes: 2,
properties: true,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
unsafe: 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();
}; };
})().prop(); return { prop: foo };
}
return bar;
} );
});
}
expect: {
console.log("PASS");
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }

View File

@@ -508,3 +508,41 @@ issue_2114_2: {
} }
expect_stdout: "2" expect_stdout: "2"
} }
issue_2428: {
options = {
collapse_vars: true,
inline: true,
passes: 2,
pure_getters: "strict",
reduce_vars: true,
side_effects: true,
toplevel: true,
unsafe: true,
unused: true,
}
input: {
function bar(k) {
console.log(k);
}
function foo(x) {
return bar(x);
}
function baz(a) {
foo(a);
}
baz(42);
baz("PASS");
}
expect: {
function baz(a) {
console.log(a);
}
baz(42);
baz("PASS");
}
expect_stdout: [
"42",
"PASS",
]
}

View File

@@ -369,3 +369,145 @@ contains_this_3: {
} }
expect_stdout: "1 1 true" expect_stdout: "1 1 true"
} }
hoist_class: {
options = {
comparisons: true,
evaluate: true,
hoist_props: true,
inline: true,
keep_fnames: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function run(c, v) {
return new c(v).value;
}
var o = {
p: class Foo {
constructor(value) {
this.value = value * 10;
}
},
x: 1,
y: 2,
};
console.log(o.p.name, o.p === o.p, run(o.p, o.x), run(o.p, o.y));
}
expect: {
function run(c, v) {
return new c(v).value;
}
var o_p = class Foo {
constructor(value) {
this.value = 10 * value;
}
};
console.log(o_p.name, true, run(o_p, 1), run(o_p, 2));
}
node_version: ">=6"
expect_stdout: "Foo true 10 20"
}
hoist_class_with_new: {
options = {
comparisons: true,
evaluate: true,
hoist_props: true,
inline: true,
keep_fnames: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
p: class Foo {
constructor(value) {
this.value = value * 10;
}
},
x: 1,
y: 2,
};
console.log(o.p.name, o.p === o.p, new o.p(o.x).value, new o.p(o.y).value);
}
expect: {
// FIXME: class `o.p` not hoisted due to `new`
var o = {
p: class Foo {
constructor(value) {
this.value = 10 * value;
}
},
x: 1,
y: 2
};
console.log(o.p.name, o.p == o.p, new o.p(o.x).value, new o.p(o.y).value);
}
node_version: ">=6"
expect_stdout: "Foo true 10 20"
}
hoist_function_with_call: {
options = {
comparisons: true,
evaluate: true,
hoist_props: true,
inline: true,
keep_fnames: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
p: function Foo(value) {
return 10 * value;
},
x: 1,
y: 2
};
console.log(o.p.name, o.p === o.p, o.p(o.x), o.p(o.y));
}
expect: {
var o_p = function Foo(value){
return 10 * value
};
console.log(o_p.name, true, o_p(1), o_p(2));
}
expect_stdout: "Foo true 10 20"
}
new_this: {
options = {
evaluate: true,
hoist_props: true,
inline: true,
passes: 2,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var o = {
a: 1,
b: 2,
f: function(a) {
this.b = a;
}
};
console.log(new o.f(o.a).b, o.b);
}
expect: {
console.log(new function(a) {
this.b = a;
}(1).b, 2);
}
expect_stdout: "1 2"
}

View File

@@ -177,6 +177,7 @@ export_mangle_2: {
export_mangle_3: { export_mangle_3: {
options = { options = {
collapse_vars: true, collapse_vars: true,
unused: true,
} }
mangle = { mangle = {
toplevel: true, toplevel: true,
@@ -195,6 +196,7 @@ export_mangle_3: {
export_mangle_4: { export_mangle_4: {
options = { options = {
collapse_vars: true, collapse_vars: true,
unused: true,
} }
mangle = { mangle = {
toplevel: true, toplevel: true,

View File

@@ -51,6 +51,7 @@ this_binding_collapse_vars: {
options = { options = {
collapse_vars: true, collapse_vars: true,
toplevel: true, toplevel: true,
unused: true,
}; };
input: { input: {
var c = a; c(); var c = a; c();

View File

@@ -1282,3 +1282,22 @@ computed_property: {
] ]
node_version: ">=4" node_version: ">=4"
} }
new_this: {
options = {
properties: true,
side_effects: true,
}
input: {
new {
f: function(a) {
this.a = a;
}
}.f(42);
}
expect: {
new function(a) {
this.a = a;
}(42);
}
}

View File

@@ -2131,14 +2131,13 @@ redefine_farg_1: {
} }
expect: { expect: {
function f(a) { function f(a) {
var a;
return typeof a; return typeof a;
} }
function g() { function g() {
return "number"; return "number";
} }
function h(a, b) { function h(a, b) {
var a = b; a = b;
return typeof a; return typeof a;
} }
console.log(f([]), g([]), h([])); console.log(f([]), g([]), h([]));
@@ -2173,10 +2172,9 @@ redefine_farg_2: {
} }
expect: { expect: {
console.log(function(a) { console.log(function(a) {
var a;
return typeof a; return typeof a;
}([]), "number",function(a, b) { }([]), "number",function(a, b) {
var a = b; a = b;
return typeof a; return typeof a;
}([])); }([]));
} }
@@ -2185,11 +2183,13 @@ redefine_farg_2: {
redefine_farg_3: { redefine_farg_3: {
options = { options = {
cascade: true,
evaluate: true, evaluate: true,
inline: true, inline: true,
keep_fargs: false, keep_fargs: false,
passes: 3, passes: 2,
reduce_vars: true, reduce_vars: true,
sequences: true,
side_effects: true, side_effects: true,
toplevel: true, toplevel: true,
unused: true, unused: true,
@@ -3537,3 +3537,596 @@ escaped_prop: {
} }
expect_stdout: "2" expect_stdout: "2"
} }
issue_2420_1: {
options = {
reduce_vars: true,
unused: true,
}
input: {
function run() {
var self = this;
if (self.count++)
self.foo();
else
self.bar();
}
var o = {
count: 0,
foo: function() { console.log("foo"); },
bar: function() { console.log("bar"); },
};
run.call(o);
run.call(o);
}
expect: {
function run() {
if (this.count++)
this.foo();
else
this.bar();
}
var o = {
count: 0,
foo: function() { console.log("foo"); },
bar: function() { console.log("bar"); },
};
run.call(o);
run.call(o);
}
expect_stdout: [
"bar",
"foo",
]
}
issue_2420_2: {
options = {
reduce_vars: true,
unused: true,
}
input: {
function f() {
var that = this;
if (that.bar)
that.foo();
else
!function(that, self) {
console.log(this === that, self === this, that === self);
}(that, this);
}
f.call({
bar: 1,
foo: function() { console.log("foo", this.bar); },
});
f.call({});
}
expect: {
function f() {
if (this.bar)
this.foo();
else
!function(that, self) {
console.log(this === that, self === this, that === self);
}(this, this);
}
f.call({
bar: 1,
foo: function() { console.log("foo", this.bar); },
});
f.call({});
}
expect_stdout: [
"foo 1",
"false false true",
]
}
issue_2420_3: {
options = {
reduce_vars: true,
unused: true,
}
input: {
function f() {
var that = this;
if (that.bar)
that.foo();
else
((that, self) => {
console.log(this === that, self === this, that === self);
})(that, this);
}
f.call({
bar: 1,
foo: function() { console.log("foo", this.bar); },
});
f.call({});
}
expect: {
function f() {
if (this.bar)
this.foo();
else
((that, self) => {
console.log(this === that, self === this, that === self);
})(this, this);
}
f.call({
bar: 1,
foo: function() { console.log("foo", this.bar); },
});
f.call({});
}
expect_stdout: [
"foo 1",
"true true true",
]
node_version: ">=4"
}
issue_2423_1: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function c() { return 1; }
function p() { console.log(c()); }
p();
p();
}
expect: {
function p() { console.log(function() { return 1; }()); }
p();
p();
}
expect_stdout: [
"1",
"1",
]
}
issue_2423_2: {
options = {
inline: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function c() { return 1; }
function p() { console.log(c()); }
p();
p();
}
expect: {
function p() { console.log(1); }
p();
p();
}
expect_stdout: [
"1",
"1",
]
}
issue_2423_3: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function c() { return 1; }
function p() { console.log(c()); }
p();
}
expect: {
(function() { console.log(function() { return 1; }()); })();
}
expect_stdout: "1"
}
issue_2423_4: {
options = {
inline: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function c() { return 1; }
function p() { console.log(c()); }
p();
}
expect: {
console.log(1);
}
expect_stdout: "1"
}
issue_2423_5: {
options = {
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function x() {
y();
}
function y() {
console.log(1);
}
function z() {
function y() {
console.log(2);
}
x();
}
z();
z();
}
expect: {
function z() {
console.log(1);
}
z();
z();
}
expect_stdout: [
"1",
"1",
]
}
issue_2423_6: {
options = {
inline: true,
passes: 2,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function x() {
y();
}
function y() {
console.log(1);
}
function z() {
function y() {
console.log(2);
}
x();
y();
}
z();
z();
}
expect: {
function z(){
console.log(1);
console.log(2);
}
z();
z();
}
expect_stdout: [
"1",
"2",
"1",
"2",
]
}
issue_2440_eval_1: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function foo() {
return bar();
}
baz = {
quux: foo
};
exec = function() {
return eval("foo()");
};
}
expect: {
function foo() {
return bar();
}
baz = {
quux: foo
};
exec = function() {
return eval("foo()");
};
}
}
issue_2440_eval_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
baz = {
quux: foo
};
exec = function() {
return eval("foo()");
};
function foo() {
return bar();
}
}
expect: {
baz = {
quux: foo
};
exec = function() {
return eval("foo()");
};
function foo() {
return bar();
}
}
}
issue_2440_with_1: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function foo() {
return bar();
}
baz = {
quux: foo
};
with (o) whatever();
}
expect: {
function foo() {
return bar();
}
baz = {
quux: foo
};
with (o) whatever();
}
}
issue_2440_with_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
baz = {
quux: foo
};
with (o) whatever();
function foo() {
return bar();
}
}
expect: {
baz = {
quux: foo
};
with (o) whatever();
function foo() {
return bar();
}
}
}
issue_2442: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function foo() {
foo();
}
}
expect: {}
}
recursive_inlining_1: {
options = {
reduce_vars: true,
unused: true,
}
input: {
!function() {
function foo() { bar(); }
function bar() { foo(); }
console.log("PASS");
}();
}
expect: {
!function() {
console.log("PASS");
}();
}
expect_stdout: "PASS"
}
recursive_inlining_2: {
options = {
reduce_vars: true,
unused: true,
}
input: {
!function() {
function foo() { qux(); }
function bar() { foo(); }
function qux() { bar(); }
console.log("PASS");
}();
}
expect: {
!function() {
console.log("PASS");
}();
}
expect_stdout: "PASS"
}
recursive_inlining_3: {
options = {
reduce_vars: true,
unused: true,
}
input: {
!function() {
function foo(x) { console.log("foo", x); if (x) bar(x-1); }
function bar(x) { console.log("bar", x); if (x) qux(x-1); }
function qux(x) { console.log("qux", x); if (x) foo(x-1); }
qux(4);
}();
}
expect: {
!function() {
function qux(x) {
console.log("qux", x);
if (x) (function(x) {
console.log("foo", x);
if (x) (function(x) {
console.log("bar", x);
if (x) qux(x - 1);
})(x - 1);
})(x - 1);
}
qux(4);
}();
}
expect_stdout: [
"qux 4",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
]
}
recursive_inlining_4: {
options = {
reduce_vars: true,
unused: true,
}
input: {
!function() {
function foo(x) { console.log("foo", x); if (x) bar(x-1); }
function bar(x) { console.log("bar", x); if (x) qux(x-1); }
function qux(x) { console.log("qux", x); if (x) foo(x-1); }
qux(4);
bar(5);
}();
}
expect: {
!function() {
function bar(x) {
console.log("bar", x);
if (x) qux(x - 1);
}
function qux(x) {
console.log("qux", x);
if (x) (function(x) {
console.log("foo", x);
if (x) bar(x - 1);
})(x - 1);
}
qux(4);
bar(5);
}();
}
expect_stdout: [
"qux 4",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
"bar 5",
"qux 4",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
]
}
recursive_inlining_5: {
options = {
reduce_vars: true,
unused: true,
}
input: {
!function() {
function foo(x) { console.log("foo", x); if (x) bar(x-1); }
function bar(x) { console.log("bar", x); if (x) qux(x-1); }
function qux(x) { console.log("qux", x); if (x) foo(x-1); }
qux(4);
bar(5);
foo(3);
}();
}
expect: {
!function() {
function foo(x) {
console.log("foo", x);
if (x) bar(x - 1);
}
function bar(x) {
console.log("bar", x);
if (x) qux(x - 1);
}
function qux(x) {
console.log("qux", x);
if (x) foo(x - 1);
}
qux(4);
bar(5);
foo(3);
}();
}
expect_stdout: [
"qux 4",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
"bar 5",
"qux 4",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
"foo 3",
"bar 2",
"qux 1",
"foo 0",
]
}

View File

@@ -162,6 +162,7 @@ var VALUES = [
'"object"', '"object"',
'"number"', '"number"',
'"function"', '"function"',
'this',
]; ];
var BINARY_OPS_NO_COMMA = [ var BINARY_OPS_NO_COMMA = [
@@ -349,10 +350,10 @@ function createParams() {
return params.join(', '); return params.join(', ');
} }
function createArgs() { function createArgs(recurmax, stmtDepth, canThrow) {
var args = []; var args = [];
for (var n = rng(4); --n >= 0;) { for (var n = rng(4); --n >= 0;) {
args.push(createValue()); args.push(rng(2) ? createValue() : createExpression(recurmax - 1, COMMA_OK, stmtDepth, canThrow));
} }
return args.join(', '); return args.join(', ');
} }
@@ -390,9 +391,10 @@ function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) {
VAR_NAMES.length = namesLenBefore; VAR_NAMES.length = namesLenBefore;
if (noDecl) s = 'var ' + createVarName(MANDATORY) + ' = ' + s + '(' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ');'; if (noDecl) s = 'var ' + createVarName(MANDATORY) + ' = ' + s;
// avoid "function statements" (decl inside statements) // avoid "function statements" (decl inside statements)
else if (inGlobal || rng(10) > 0) s += 'var ' + createVarName(MANDATORY) + ' = ' + name + '(' + createArgs() + ');'; else if (inGlobal || rng(10) > 0) s += 'var ' + createVarName(MANDATORY) + ' = ' + name;
s += '(' + createArgs(recurmax, stmtDepth, canThrow) + ');';
return s; return s;
} }
@@ -626,6 +628,9 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++: case p++:
case p++: case p++:
return createValue(); return createValue();
case p++:
case p++:
return getVarName();
case p++: case p++:
return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow); return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
case p++: case p++: