aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2020-12-26 05:40:31 +0000
committerGitHub <noreply@github.com>2020-12-26 13:40:31 +0800
commitbe1f5199f422080a42e962670758bd68f34b5bb6 (patch)
treead7d12505b13199bfdc32b4f6c5edf000433ea28
parent95aea0e33ce0288d7c31b11b044432f2043cf3d1 (diff)
downloadtracifyjs-be1f5199f422080a42e962670758bd68f34b5bb6.tar.gz
tracifyjs-be1f5199f422080a42e962670758bd68f34b5bb6.zip
fix corner cases in `collapse_vars` (#4462)
fixes #4460 fixes #4461
-rw-r--r--lib/compress.js84
-rw-r--r--test/compress/default-values.js60
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"
+}