fix corner cases in `collapse_vars` (#4462)
authorAlex Lam S.L <alexlamsl@gmail.com>
Sat, 26 Dec 2020 05:40:31 +0000 (05:40 +0000)
committerGitHub <noreply@github.com>
Sat, 26 Dec 2020 05:40:31 +0000 (13:40 +0800)
fixes #4460
fixes #4461

lib/compress.js
test/compress/default-values.js

index e4506f7..ae66d14 100644 (file)
@@ -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);
             }
         }
index eefa6e0..0e05d37 100644 (file)
@@ -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"
+}