enhance `collapse_vars` & `reduce_vars` (#5275)
authorAlex Lam S.L <alexlamsl@gmail.com>
Fri, 7 Jan 2022 21:26:49 +0000 (21:26 +0000)
committerGitHub <noreply@github.com>
Fri, 7 Jan 2022 21:26:49 +0000 (05:26 +0800)
fixes #5276

lib/compress.js
test/compress/collapse_vars.js

index 2784bbe..01b1845 100644 (file)
@@ -674,6 +674,16 @@ Compressor.prototype.compress = function(node) {
             return node;
         }
 
+        function replace_ref(ref, fixed) {
+            return function() {
+                var node = make_ref(ref, fixed);
+                var def = ref.definition();
+                def.references.push(node);
+                def.replaced++;
+                return node;
+            };
+        }
+
         function ref_once(compressor, def) {
             return compressor.option("unused")
                 && !def.scope.pinned()
@@ -954,6 +964,7 @@ Compressor.prototype.compress = function(node) {
                     };
                     left.fixed.assigns = !fixed || !fixed.assigns ? [] : fixed.assigns.slice();
                     left.fixed.assigns.push(node);
+                    left.fixed.to_binary = replace_ref(left, fixed);
                 } else {
                     left.walk(tw);
                     ld.fixed = false;
@@ -1397,7 +1408,7 @@ Compressor.prototype.compress = function(node) {
                         operator: node.operator.slice(0, -1),
                         left: make_node(AST_UnaryPrefix, node, {
                             operator: "+",
-                            expression: make_ref(exp, fixed)
+                            expression: make_ref(exp, fixed),
                         }),
                         right: make_node(AST_Number, node, { value: 1 }),
                     });
@@ -1414,6 +1425,7 @@ Compressor.prototype.compress = function(node) {
                         });
                     };
                     exp.fixed.assigns = fixed && fixed.assigns;
+                    exp.fixed.to_prefix = replace_ref(exp, d.fixed);
                 }
             } else {
                 exp.walk(tw);
@@ -1960,6 +1972,7 @@ Compressor.prototype.compress = function(node) {
                         col: node.start.col,
                     });
                     can_replace = false;
+                    lvalues = get_lvalues(lhs);
                     node.right.transform(scanner);
                     clear_write_only(candidate);
                     var folded;
@@ -1967,10 +1980,9 @@ Compressor.prototype.compress = function(node) {
                         folded = candidate;
                     } else {
                         abort = true;
-                        lhs.definition().fixed = false;
                         folded = make_node(AST_Binary, candidate, {
                             operator: compound,
-                            left: lhs,
+                            left: lhs.fixed ? lhs.fixed.to_binary() : lhs,
                             right: rvalue,
                         });
                     }
@@ -2026,10 +2038,10 @@ Compressor.prototype.compress = function(node) {
                             alternative: node,
                         }),
                     });
-                    if (candidate instanceof AST_UnaryPostfix) {
-                        if (lhs instanceof AST_SymbolRef) lhs.definition().fixed = false;
-                        return make_node(AST_UnaryPrefix, candidate, candidate);
-                    }
+                    if (candidate instanceof AST_UnaryPostfix) return make_node(AST_UnaryPrefix, candidate, {
+                        operator: candidate.operator,
+                        expression: lhs.fixed ? lhs.fixed.to_prefix() : lhs,
+                    });
                     if (candidate instanceof AST_VarDef) {
                         var def = candidate.name.definition();
                         if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
@@ -3086,16 +3098,13 @@ Compressor.prototype.compress = function(node) {
                 var value = rvalue === rhs_value ? null : make_sequence(rhs_value, rhs_value.expressions.slice(0, -1));
                 var index = expr.name_index;
                 if (index >= 0) {
-                    var argname = scope.argnames[index];
+                    var args, argname = scope.argnames[index];
                     if (argname instanceof AST_DefaultValue) {
+                        scope.argnames[index] = argname = argname.clone();
                         argname.value = value || make_node(AST_Number, argname, { value: 0 });
-                        argname.name.definition().fixed = false;
-                    } else {
-                        var args = compressor.parent().args;
-                        if (args[index]) {
-                            args[index] = value || make_node(AST_Number, args[index], { value: 0 });
-                            argname.definition().fixed = false;
-                        }
+                    } else if ((args = compressor.parent().args)[index]) {
+                        scope.argnames[index] = argname.clone();
+                        args[index] = value || make_node(AST_Number, args[index], { value: 0 });
                     }
                     return;
                 }
@@ -6677,7 +6686,7 @@ Compressor.prototype.compress = function(node) {
                     && indexOf_assign(node.expression.definition(), node) < 0) {
                     return make_node(AST_UnaryPrefix, node, {
                         operator: "+",
-                        expression: node.expression
+                        expression: node.expression,
                     });
                 }
             }
@@ -11946,22 +11955,29 @@ Compressor.prototype.compress = function(node) {
         }
         if (compressor.option("assignments")) {
             if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) {
+                var ref;
                 // x = expr1 OP expr2
-                if (self.right.left instanceof AST_SymbolRef
-                    && self.right.left.name == self.left.name
+                if ((ref = self.right.left) instanceof AST_SymbolRef
+                    && ref.name == self.left.name
                     && ASSIGN_OPS[self.right.operator]) {
                     // x = x - 2 ---> x -= 2
+                    if (self.left.fixed) self.left.fixed.to_binary = function() {
+                        return ref;
+                    };
                     return make_node(AST_Assign, self, {
                         operator: self.right.operator + "=",
                         left: self.left,
                         right: self.right.right,
                     });
                 }
-                if (self.right.right instanceof AST_SymbolRef
-                    && self.right.right.name == self.left.name
+                if ((ref = self.right.right) instanceof AST_SymbolRef
+                    && ref.name == self.left.name
                     && ASSIGN_OPS_COMMUTATIVE[self.right.operator]
                     && !self.right.left.has_side_effects(compressor)) {
                     // x = 2 & x ---> x &= 2
+                    if (self.left.fixed) self.left.fixed.to_binary = function() {
+                        return ref;
+                    };
                     return make_node(AST_Assign, self, {
                         operator: self.right.operator + "=",
                         left: self.left,
index e3c6468..40ba3bb 100644 (file)
@@ -3032,6 +3032,54 @@ compound_assignment_6: {
     expect_stdout: "42"
 }
 
+compound_assignment_7: {
+    options = {
+        assignments: true,
+        collapse_vars: true,
+        reduce_vars: true,
+        toplevel: true,
+    }
+    input: {
+        var a = "FA";
+        a = a + "I";
+        a = a + "L";
+        if (console)
+            a = "PASS";
+        console.log(a);
+    }
+    expect: {
+        var a = "FA";
+        a = a + "I" + "L";
+        if (console)
+            a = "PASS";
+        console.log(a);
+    }
+    expect_stdout: "PASS"
+}
+
+compound_assignment_8: {
+    options = {
+        assignments: true,
+        collapse_vars: true,
+        reduce_vars: true,
+        toplevel: true,
+    }
+    input: {
+        var a = 2;
+        a = 3 * a;
+        a = 7 * a;
+        console || (a = "FAIL");
+        console.log(a);
+    }
+    expect: {
+        var a = 2;
+        a = a * 3 * 7;
+        console || (a = "FAIL");
+        console.log(a);
+    }
+    expect_stdout: "42"
+}
+
 issue_2187_1: {
     options = {
         collapse_vars: true,
@@ -8285,9 +8333,9 @@ issue_3884_1: {
         console.log(a, b);
     }
     expect: {
-        var a = 100, b = 1;
-        b <<= ++a;
-        console.log(a, b);
+        var a = 100;
+        ++a;
+        console.log(a, 32);
     }
     expect_stdout: "101 32"
 }
@@ -9715,9 +9763,30 @@ issue_5273: {
         function f(c, d) {
             return d;
         }
-        b = (b + a) * a,
+        b = 1100,
         f,
         console.log(b);
     }
     expect_stdout: "1100"
 }
+
+issue_5276: {
+    options = {
+        collapse_vars: true,
+        pure_getters: "strict",
+        reduce_vars: true,
+        toplevel: true,
+    }
+    input: {
+        var a = A = "PASS";
+        a.p += null;
+        a.p -= 42;
+        console.log(a);
+    }
+    expect: {
+        var a = A = "PASS";
+        a.p = a.p + null - 42;
+        console.log(a);
+    }
+    expect_stdout: "PASS"
+}