From be1f5199f422080a42e962670758bd68f34b5bb6 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sat, 26 Dec 2020 05:40:31 +0000 Subject: [PATCH] fix corner cases in `collapse_vars` (#4462) fixes #4460 fixes #4461 --- lib/compress.js | 84 ++++++++++++++++++++------------- test/compress/default-values.js | 60 +++++++++++++++++++++++ 2 files changed, 111 insertions(+), 33 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index e4506f75..ae66d140 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -931,19 +931,25 @@ merge(Compressor.prototype, { tw.in_loop = this; push(tw); var init = this.init; - init.walk(tw); if (init instanceof AST_Definitions) { - init.definitions[0].name.match_symbol(function(node) { + init.definitions[0].name.mark_symbol(function(node) { if (node instanceof AST_SymbolDeclaration) { var def = node.definition(); def.assignments++; def.fixed = false; } - }, true); - } else if (init instanceof AST_SymbolRef) { - var def = init.definition(); - def.assignments++; - if (!init.is_immutable()) def.fixed = false; + }, tw); + } else if (init instanceof AST_Destructured || init instanceof AST_SymbolRef) { + init.mark_symbol(function(node) { + if (node instanceof AST_SymbolRef) { + var def = node.definition(); + push_ref(def, node); + def.assignments++; + if (!node.is_immutable()) def.fixed = false; + } + }, tw); + } else { + init.walk(tw); } this.body.walk(tw); pop(tw); @@ -1259,16 +1265,17 @@ merge(Compressor.prototype, { AST_Node.DEFMETHOD("match_symbol", function(predicate) { return predicate(this); }); - AST_Destructured.DEFMETHOD("match_symbol", function(predicate, allow_computed_keys) { + AST_Destructured.DEFMETHOD("match_symbol", function(predicate, ignore_side_effects) { var found = false; var tw = new TreeWalker(function(node) { if (found) return true; if (node instanceof AST_DefaultValue) { + if (!ignore_side_effects) return found = true; node.name.walk(tw); return true; } if (node instanceof AST_DestructuredKeyVal) { - if (!allow_computed_keys && node.key instanceof AST_Node) return found = true; + if (!ignore_side_effects && node.key instanceof AST_Node) return found = true; node.value.walk(tw); return true; } @@ -1605,13 +1612,12 @@ merge(Compressor.prototype, { can_replace = replace; return signal_abort(node); } - // Scan but don't replace inside destructuring LHS - if (node instanceof AST_Assign && node.left instanceof AST_Destructured) { + // Scan but don't replace inside destructuring expression + if (node instanceof AST_Destructured) { var replace = can_replace; can_replace = false; - node.left = node.left.transform(scanner); + descend(node, scanner); can_replace = replace; - node.right = node.right.transform(scanner); return signal_abort(node); } // Scan but don't replace inside default value @@ -1710,8 +1716,8 @@ merge(Compressor.prototype, { var assign_used = false; var can_replace = !args || !hit; if (!can_replace) { - for (var j = candidate.index + 1; !abort && j < args.length; j++) { - args[j].transform(scanner); + for (var j = candidate.arg_index + 1; !abort && j < args.length; j++) { + if (args[j]) args[j].transform(scanner); } can_replace = true; } @@ -1911,7 +1917,7 @@ merge(Compressor.prototype, { return (node.value || parent instanceof AST_Let) && node.name.match_symbol(function(node) { return node instanceof AST_SymbolDeclaration && (lvalues.has(node.name) || side_effects && may_modify(node)); - }); + }, true); } var sym = is_lhs(node.left, node); if (!sym) return false; @@ -1920,7 +1926,7 @@ merge(Compressor.prototype, { return sym.match_symbol(function(node) { return node instanceof AST_SymbolRef && (lvalues.has(node.name) || read_toplevel && compressor.exposed(node.definition())); - }); + }, true); } function extract_args() { @@ -1959,21 +1965,17 @@ merge(Compressor.prototype, { return true; } }); - var len = fn.argnames.length; - args = iife.args.slice(len); + args = iife.args.slice(); var names = Object.create(null); - for (var i = len; --i >= 0;) { + for (var i = fn.argnames.length; --i >= 0;) { var sym = fn.argnames[i]; var arg = iife.args[i]; var value; if (sym instanceof AST_DefaultValue) { value = sym.value; sym = sym.name; + args[iife.args.length + i] = value; } - args.unshift(make_node(AST_VarDef, sym, { - name: sym, - value: value ? arg ? make_sequence(iife, [ arg, value ]) : value : arg, - })); if (sym instanceof AST_Destructured) { if (!sym.match_symbol(return_false)) continue; candidates.length = 0; @@ -1994,7 +1996,8 @@ merge(Compressor.prototype, { name: sym, value: arg }); - candidate.index = i; + candidate.name_index = i; + candidate.arg_index = value ? iife.args.length + i : i; candidates.unshift([ candidate ]); } } @@ -2198,9 +2201,7 @@ merge(Compressor.prototype, { if (is_last_node(node, parent)) return node; if (in_conditional(node, parent)) return node; if (parent instanceof AST_Array) return find_stop_unused(parent, level + 1); - if (parent instanceof AST_Assign) { - return may_throw(parent) ? node : find_stop_unused(parent, level + 1); - } + if (parent instanceof AST_Assign) return check_assignment(parent.left); if (parent instanceof AST_Await) return node; if (parent instanceof AST_Binary) return find_stop_unused(parent, level + 1); if (parent instanceof AST_Call) return find_stop_unused(parent, level + 1); @@ -2231,10 +2232,27 @@ merge(Compressor.prototype, { if (parent instanceof AST_Spread) return node; if (parent instanceof AST_Switch) return find_stop_unused(parent, level + 1); if (parent instanceof AST_Unary) return find_stop_unused(parent, level + 1); - if (parent instanceof AST_VarDef) { - return may_throw(parent) ? node : find_stop_unused(parent, level + 1); - } + if (parent instanceof AST_VarDef) return check_assignment(parent.name); return null; + + function check_assignment(lhs) { + if (may_throw(parent)) return node; + if (lhs !== node && lhs instanceof AST_Destructured) { + var replace = can_replace; + can_replace = false; + var after = stop_after; + var if_hit = stop_if_hit; + lhs.transform(scanner); + stop_if_hit = if_hit; + stop_after = after; + can_replace = replace; + if (abort) { + abort = false; + return node; + } + } + return find_stop_unused(parent, level + 1); + } } function mangleable_var(value) { @@ -2389,7 +2407,7 @@ merge(Compressor.prototype, { } function remove_candidate(expr) { - var index = expr.index; + var index = expr.name_index; if (index >= 0) { var argname = scope.argnames[index]; if (argname instanceof AST_DefaultValue) { @@ -7018,7 +7036,7 @@ merge(Compressor.prototype, { if ((name instanceof AST_Destructured || name instanceof AST_SymbolLet) && !name.match_symbol(function(node) { if (node instanceof AST_SymbolDeclaration) return may_overlap(compressor, node.definition()); - })) { + }, true)) { self.init = to_var(self.init); } } diff --git a/test/compress/default-values.js b/test/compress/default-values.js index eefa6e0e..0e05d37d 100644 --- a/test/compress/default-values.js +++ b/test/compress/default-values.js @@ -1100,3 +1100,63 @@ issue_4458: { expect_stdout: "PASS 42" node_version: ">=6" } + +issue_4460: { + options = { + collapse_vars: true, + } + input: { + var log = console.log, a = "FAIL"; + var [ b = a ] = (a = "PASS", []); + log(a, b); + } + expect: { + var log = console.log, a = "FAIL"; + var [ b = a ] = (a = "PASS", []); + log(a, b); + } + expect_stdout: "PASS PASS" + node_version: ">=6" +} + +issue_4461_1: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a = 0; + (function(b = a && console.log("PASS"), c) { + return c; + })(void 0, a++); + } + expect: { + var a = 0; + (function(b = a && console.log("PASS"), c) { + return c; + })(void 0, a++); + } + expect_stdout: "PASS" + node_version: ">=6" +} + +issue_4461_2: { + options = { + collapse_vars: true, + unused: true, + } + input: { + var a = 0; + (function([ b = a && console.log("PASS") ], c) { + return c; + })([], a++); + } + expect: { + var a = 0; + (function([ b = a && console.log("PASS") ], c) { + return c; + })([], a++); + } + expect_stdout: "PASS" + node_version: ">=6" +} -- 2.34.1