From a48f87abf2fb09cc8296444eb613021ef66492c3 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Mon, 30 Oct 2017 23:19:27 +0800 Subject: [PATCH 1/8] compress `new` `function` containing `this` (#2417) --- lib/compress.js | 27 ++++++++++++++------------- test/compress/hoist_props.js | 28 ++++++++++++++++++++++++++++ test/compress/properties.js | 19 +++++++++++++++++++ 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index a1db985c..99ab7b7a 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -592,7 +592,7 @@ merge(Compressor.prototype, { || !immutable && parent instanceof AST_Call && parent.expression === node - && (!(value instanceof AST_Function) || value.contains_this())) { + && (!(value instanceof AST_Function) || value.contains_this(parent))) { return true; } else if (parent instanceof AST_Array || parent instanceof AST_Object) { return is_modified(parent, parent, level + 1); @@ -4561,11 +4561,11 @@ merge(Compressor.prototype, { } } if (is_lhs(self, compressor.parent())) return self; - if (compressor.option("properties") && key !== prop) { - var node = self.flatten_object(property); - if (node) { - expr = self.expression = node.expression; - prop = self.property = node.property; + if (key !== prop) { + var sub = self.flatten_object(property, compressor); + if (sub) { + expr = self.expression = sub.expression; + prop = self.property = sub.property; } } if (compressor.option("properties") && compressor.option("side_effects") @@ -4611,7 +4611,8 @@ merge(Compressor.prototype, { 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 self = this; self.walk(new TreeWalker(function(node) { @@ -4622,7 +4623,8 @@ merge(Compressor.prototype, { return result; }); - AST_PropAccess.DEFMETHOD("flatten_object", function(key) { + AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) { + if (!compressor.option("properties")) return; var expr = this.expression; if (expr instanceof AST_Object) { var props = expr.properties; @@ -4633,7 +4635,8 @@ merge(Compressor.prototype, { return prop instanceof AST_ObjectKeyVal; })) break; var value = prop.value; - if (value instanceof AST_Function && value.contains_this()) break; + if (value instanceof AST_Function + && value.contains_this(compressor.parent())) break; return make_node(AST_Sub, this, { expression: make_node(AST_Array, expr, { elements: props.map(function(prop) { @@ -4677,10 +4680,8 @@ merge(Compressor.prototype, { } } if (is_lhs(self, compressor.parent())) return self; - if (compressor.option("properties")) { - var node = self.flatten_object(self.property); - if (node) return node.optimize(compressor); - } + var sub = self.flatten_object(self.property, compressor); + if (sub) return sub.optimize(compressor); var ev = self.evaluate(compressor); if (ev !== self) { ev = make_node_from_constant(ev, self).optimize(compressor); diff --git a/test/compress/hoist_props.js b/test/compress/hoist_props.js index 2e8343a6..ccfc76f8 100644 --- a/test/compress/hoist_props.js +++ b/test/compress/hoist_props.js @@ -369,3 +369,31 @@ contains_this_3: { } expect_stdout: "1 1 true" } + +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" +} diff --git a/test/compress/properties.js b/test/compress/properties.js index 1b5e7fc7..ab202525 100644 --- a/test/compress/properties.js +++ b/test/compress/properties.js @@ -1006,3 +1006,22 @@ array_hole: { } expect_stdout: "2 undefined 3" } + +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); + } +} From c8b6f4733d35db48b5b7e2373264db0d99eb299f Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sat, 4 Nov 2017 00:31:37 +0800 Subject: [PATCH 2/8] reduce `this` within functions (#2421) - only replace same-scope usages - augment `test/ufuzz.js` to test for `this` fixes #2420 --- lib/compress.js | 75 +++++++++++++++++++------------- test/compress/reduce_vars.js | 84 ++++++++++++++++++++++++++++++++++++ test/ufuzz.js | 1 + 3 files changed, 129 insertions(+), 31 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 99ab7b7a..f09f2b9b 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -285,7 +285,7 @@ merge(Compressor.prototype, { 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 unused = compressor.option("unused"); // Stack of look-up tables to keep track of whether a `SymbolDef` has been @@ -564,7 +564,10 @@ merge(Compressor.prototype, { } 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) { @@ -4211,39 +4214,49 @@ merge(Compressor.prototype, { var value = fixed.optimize(compressor); return value === fixed ? fixed.clone(true) : value; } - if (compressor.option("evaluate") && fixed) { - if (d.should_replace === undefined) { - var init = fixed.evaluate(compressor); - if (init !== fixed && (compressor.option("unsafe_regexp") || !(init instanceof RegExp))) { - init = make_node_from_constant(init, fixed); - var value_length = init.optimize(compressor).print_to_string().length; - var fn; - if (has_symbol_ref(fixed)) { - fn = function() { - var result = init.optimize(compressor); - return result === init ? result.clone(true) : result; - }; - } else { - value_length = Math.min(value_length, fixed.print_to_string().length); - fn = function() { - var result = best_of_expression(init.optimize(compressor), fixed); - return result === init || result === fixed ? result.clone(true) : result; - }; - } - var name_length = d.name.length; - var overhead = 0; - if (compressor.option("unused") && !compressor.exposed(d)) { - overhead = (name_length + 2 + value_length) / d.references.length; - } - d.should_replace = value_length <= name_length + overhead ? fn : false; - } else { - d.should_replace = false; + if (fixed && d.should_replace === undefined) { + var init; + if (fixed instanceof AST_This) { + 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 (d.should_replace) { - return d.should_replace(); + if (init) { + var value_length = init.optimize(compressor).print_to_string().length; + var fn; + if (has_symbol_ref(fixed)) { + fn = function() { + var result = init.optimize(compressor); + return result === init ? result.clone(true) : result; + }; + } else { + value_length = Math.min(value_length, fixed.print_to_string().length); + fn = function() { + var result = best_of_expression(init.optimize(compressor), fixed); + return result === init || result === fixed ? result.clone(true) : result; + }; + } + var name_length = d.name.length; + var overhead = 0; + if (compressor.option("unused") && !compressor.exposed(d)) { + overhead = (name_length + 2 + value_length) / d.references.length; + } + d.should_replace = value_length <= name_length + overhead ? fn : false; + } else { + d.should_replace = false; } } + if (d.should_replace) { + return d.should_replace(); + } } return self; diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index d7fdee18..e4d22e9b 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -3295,3 +3295,87 @@ escaped_prop: { } 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", + ] +} diff --git a/test/ufuzz.js b/test/ufuzz.js index e38ffa2f..d6a2a45b 100644 --- a/test/ufuzz.js +++ b/test/ufuzz.js @@ -162,6 +162,7 @@ var VALUES = [ '"object"', '"number"', '"function"', + 'this', ]; var BINARY_OPS_NO_COMMA = [ From 71e61153b1b3d5f2446b83a3507231509e11e90e Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sat, 4 Nov 2017 16:29:42 +0800 Subject: [PATCH 3/8] improve variations on call arguments for `ufuzz` (#2424) --- test/ufuzz.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/ufuzz.js b/test/ufuzz.js index d6a2a45b..702e2078 100644 --- a/test/ufuzz.js +++ b/test/ufuzz.js @@ -350,10 +350,10 @@ function createParams() { return params.join(', '); } -function createArgs() { +function createArgs(recurmax, stmtDepth, canThrow) { var args = []; for (var n = rng(4); --n >= 0;) { - args.push(createValue()); + args.push(createExpression(recurmax, COMMA_OK, stmtDepth, canThrow)); } return args.join(', '); } @@ -391,9 +391,10 @@ function createFunction(recurmax, inGlobal, noDecl, canThrow, stmtDepth) { 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) - 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; } @@ -627,6 +628,9 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) { case p++: case p++: return createValue(); + case p++: + case p++: + return getVarName(); case p++: return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow); case p++: From fe5a68f9d5a93557d44c48cd0a8ee533e2bd1a47 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sun, 5 Nov 2017 00:00:18 +0800 Subject: [PATCH 4/8] maintain call argument order in `collapse_vars` (#2426) fixes #2425 --- lib/compress.js | 22 +++++++++--- test/compress/collapse_vars.js | 66 ++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index f09f2b9b..1b4a1f77 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -808,6 +808,7 @@ merge(Compressor.prototype, { function collapse(statements, compressor) { var scope = compressor.find_parent(AST_Scope); if (scope.uses_eval || scope.uses_with) return statements; + var args; var candidates = []; var stat_index = statements.length; while (--stat_index >= 0) { @@ -828,7 +829,7 @@ merge(Compressor.prototype, { var one_off = lhs instanceof AST_Symbol && lhs.definition().references.length == 1; var side_effects = value_has_side_effects(candidate); 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) { if (abort) return node; // Skip nodes before `candidate` as quickly as possible @@ -853,7 +854,8 @@ merge(Compressor.prototype, { return node; } // Replace variable with assignment when found - if (!(node instanceof AST_SymbolDeclaration) + if (can_replace + && !(node instanceof AST_SymbolDeclaration) && !is_lhs(node, parent) && lhs.equivalent_to(node)) { CHANGED = replaced = abort = true; @@ -904,6 +906,12 @@ merge(Compressor.prototype, { // Skip (non-executed) functions and (leading) default case in switch statements if (node instanceof AST_Default || node instanceof AST_Scope) return node; }); + if (!can_replace) { + for (var j = compressor.self().argnames.lastIndexOf(candidate.name) + 1; j < args.length; j++) { + args[j].transform(tt); + } + can_replace = true; + } for (var i = stat_index; !abort && i < statements.length; i++) { statements[i].transform(tt); } @@ -921,12 +929,18 @@ merge(Compressor.prototype, { && iife.expression === fn) { var fn_strict = compressor.has_directive("use strict"); 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); - for (var i = fn.argnames.length; --i >= 0;) { + for (var i = len; --i >= 0;) { 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; names[sym.name] = true; - var arg = iife.args[i]; if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor); else { var tw = new TreeWalker(function(node) { diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index b5b97d24..e2c5f1be 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -2949,3 +2949,69 @@ conditional_2: { } 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" +} From a8aa28a7a6c0cb415965d055119956d4333de8fa Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sun, 5 Nov 2017 04:27:01 +0800 Subject: [PATCH 5/8] consolidate single-use `function` reduction (#2427) fixes #2423 --- lib/compress.js | 30 +++++++++------ test/compress/reduce_vars.js | 72 ++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 11 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 1b4a1f77..ba90f000 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -315,12 +315,18 @@ merge(Compressor.prototype, { d.fixed = false; } else if (d.fixed) { var value = node.fixed_value(); - if (unused) { - d.single_use = value - && d.references.length == 1 - && loop_ids[d.id] === in_loop - && d.scope === node.scope - && value.is_constant_expression(); + if (unused && value && d.references.length == 1) { + if (value instanceof AST_Lambda) { + d.single_use = d.scope === node.scope + && !(d.orig[0] instanceof AST_SymbolFunarg) + || value.is_constant_expression(); + } else { + d.single_use = d.scope === node.scope + && loop_ids[d.id] === in_loop + && value.is_constant_expression(); + } + } else { + d.single_use = false; } if (is_modified(node, value, 0, is_immutable(value))) { if (d.single_use) { @@ -377,6 +383,10 @@ merge(Compressor.prototype, { } else { d.fixed = node; mark(d, true); + if (unused && d.references.length == 1) { + d.single_use = d.scope === d.references[0].scope + || node.is_constant_expression(); + } } var save_ids = safe_ids; safe_ids = Object.create(null); @@ -527,6 +537,7 @@ merge(Compressor.prototype, { } return true; } + return def.fixed instanceof AST_Defun; } function safe_to_assign(def, value) { @@ -2165,7 +2176,7 @@ merge(Compressor.prototype, { } def(AST_Node, return_false); def(AST_Constant, return_true); - def(AST_Function, function(){ + def(AST_Lambda, function(){ var self = this; var result = true; self.walk(new TreeWalker(function(node) { @@ -4221,10 +4232,7 @@ merge(Compressor.prototype, { if (compressor.option("unused") && fixed && d.references.length == 1 - && (d.single_use || fixed instanceof AST_Function - && !(d.scope.uses_arguments && d.orig[0] instanceof AST_SymbolFunarg) - && !d.scope.uses_eval - && compressor.find_parent(AST_Scope) === fixed.parent_scope)) { + && d.single_use) { var value = fixed.optimize(compressor); return value === fixed ? fixed.clone(true) : value; } diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index e4d22e9b..a18d4256 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -3379,3 +3379,75 @@ issue_2420_2: { "false false true", ] } + +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(); + } +} + +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(); + } +} + +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; }()); })(); + } +} + +issue_2423_4: { + options = { + inline: true, + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function c() { return 1; } + function p() { console.log(c()); } + p(); + } + expect: { + void console.log(1); + } +} From 5b4b07e9a7d67e593c6ae8d54dc77d174afd25ac Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sun, 5 Nov 2017 06:18:45 +0800 Subject: [PATCH 6/8] extend function inlining safety checks (#2430) --- lib/compress.js | 46 ++++++++++++------ test/compress/drop-unused.js | 55 ++++++++++++++++----- test/compress/reduce_vars.js | 93 +++++++++++++++++++++++++++++++++++- 3 files changed, 167 insertions(+), 27 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index ba90f000..454c1666 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -319,7 +319,7 @@ merge(Compressor.prototype, { if (value instanceof AST_Lambda) { d.single_use = d.scope === node.scope && !(d.orig[0] instanceof AST_SymbolFunarg) - || value.is_constant_expression(); + || value.is_constant_expression(node.scope); } else { d.single_use = d.scope === node.scope && loop_ids[d.id] === in_loop @@ -385,7 +385,7 @@ merge(Compressor.prototype, { mark(d, true); if (unused && d.references.length == 1) { d.single_use = d.scope === d.references[0].scope - || node.is_constant_expression(); + || node.is_constant_expression(d.references[0].scope); } } var save_ids = safe_ids; @@ -2176,18 +2176,22 @@ merge(Compressor.prototype, { } def(AST_Node, return_false); def(AST_Constant, return_true); - def(AST_Lambda, function(){ + def(AST_Lambda, function(scope){ var self = this; var result = true; self.walk(new TreeWalker(function(node) { if (!result) return true; if (node instanceof AST_SymbolRef) { var def = node.definition(); - if (self.enclosed.indexOf(def) >= 0 - && self.variables.get(def.name) !== def) { + if (member(def, self.enclosed) + && !self.variables.has(def.name)) { + if (scope) { + var scope_def = scope.find_variable(node); + if (def.undeclared ? !scope_def : scope_def === def) return true; + } result = false; - return true; } + return true; } })); return result; @@ -2330,7 +2334,10 @@ merge(Compressor.prototype, { }); return true; } - if (assign_as_unused(node) instanceof AST_SymbolRef && scope === self) { + var sym; + if (scope === self + && (sym = assign_as_unused(node)) instanceof AST_SymbolRef + && self.variables.get(sym.name) === sym.definition()) { if (node instanceof AST_Assign) node.right.walk(tw); return true; } @@ -2403,8 +2410,10 @@ merge(Compressor.prototype, { } } if (drop_funcs && node instanceof AST_Defun && node !== self) { - if (!(node.name.definition().id in in_use_ids)) { + var def = node.name.definition(); + if (!(def.id in in_use_ids)) { compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name)); + drop_decl(def, node.name); return make_node(AST_EmptyStatement, node); } return node; @@ -2426,7 +2435,7 @@ merge(Compressor.prototype, { if (var_defs.length > 1 && !def.value) { compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name)); remove(var_defs, def); - remove(sym.orig, def.name); + drop_decl(sym, def.name); return; } } @@ -2459,7 +2468,7 @@ merge(Compressor.prototype, { } else { compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name)); } - remove(sym.orig, def.name); + drop_decl(sym, def.name); } }); if (head.length == 0 && tail.length == 1 && tail[0].name instanceof AST_SymbolVar) { @@ -2468,7 +2477,7 @@ merge(Compressor.prototype, { var def = tail.pop(); compressor.warn("Converting duplicated definition of variable {name} to assignment [{file}:{line},{col}]", template(def.name)); remove(var_defs, def); - remove(def.name.definition().orig, def.name); + drop_decl(def.name.definition(), def.name); side_effects.unshift(make_node(AST_Assign, def, { operator: "=", left: make_node(AST_SymbolRef, def.name, def.name), @@ -2497,10 +2506,9 @@ merge(Compressor.prototype, { } } if (drop_vars) { - var def = assign_as_unused(node); - if (def instanceof AST_SymbolRef - && !((def = def.definition()).id in in_use_ids) - && self.variables.get(def.name) === def) { + var sym = assign_as_unused(node); + if (sym instanceof AST_SymbolRef + && !(sym.definition().id in in_use_ids)) { if (node instanceof AST_Assign) { return maintain_this_binding(parent, node, node.right.transform(tt)); } @@ -2551,6 +2559,14 @@ merge(Compressor.prototype, { col : sym.start.col }; } + + function drop_decl(def, decl) { + remove(def.orig, decl); + if (!def.orig.length) { + def.scope.functions.del(def.name); + def.scope.variables.del(def.name); + } + } } ); self.transform(tt); diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index 4ce8d2eb..af6a7a47 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -1109,11 +1109,11 @@ var_catch_toplevel: { } } -issue_2105: { +issue_2105_1: { options = { collapse_vars: true, inline: true, - passes: 3, + passes: 2, reduce_vars: true, side_effects: true, unused: true, @@ -1139,17 +1139,50 @@ issue_2105: { }); } expect: { - (function() { - var quux = function() { + ({ + prop: function() { + console.log; console.log("PASS"); - }; - return { - prop: function() { - console.log; - quux(); + } + }).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(); + }; + return { prop: foo }; } - }; - })().prop(); + return bar; + } ); + }); + } + expect: { + console.log("PASS"); } expect_stdout: "PASS" } diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index a18d4256..7714ad5d 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -3397,6 +3397,10 @@ issue_2423_1: { p(); p(); } + expect_stdout: [ + "1", + "1", + ] } issue_2423_2: { @@ -3417,6 +3421,10 @@ issue_2423_2: { p(); p(); } + expect_stdout: [ + "1", + "1", + ] } issue_2423_3: { @@ -3433,12 +3441,14 @@ issue_2423_3: { 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, } @@ -3448,6 +3458,87 @@ issue_2423_4: { p(); } expect: { - void console.log(1); + 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", + ] } From 25a18883f5130ae809e1e38c226948576a2a4e90 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sun, 5 Nov 2017 12:49:14 +0800 Subject: [PATCH 7/8] tweak #2424 (#2432) --- test/ufuzz.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ufuzz.js b/test/ufuzz.js index 702e2078..578103e8 100644 --- a/test/ufuzz.js +++ b/test/ufuzz.js @@ -353,7 +353,7 @@ function createParams() { function createArgs(recurmax, stmtDepth, canThrow) { var args = []; for (var n = rng(4); --n >= 0;) { - args.push(createExpression(recurmax, COMMA_OK, stmtDepth, canThrow)); + args.push(rng(2) ? createValue() : createExpression(recurmax - 1, COMMA_OK, stmtDepth, canThrow)); } return args.join(', '); } From f46281e2b75a0cae0fbc591ba23c000d4106a07a Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sun, 5 Nov 2017 15:03:19 +0800 Subject: [PATCH 8/8] v3.1.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9f41493d..1f490865 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "homepage": "http://lisperator.net/uglifyjs", "author": "Mihai Bazon (http://lisperator.net/)", "license": "BSD-2-Clause", - "version": "3.1.6", + "version": "3.1.7", "engines": { "node": ">=0.8.0" },