fix corner case in `evaluate` (#3934)
authorAlex Lam S.L <alexlamsl@gmail.com>
Fri, 29 May 2020 09:48:26 +0000 (10:48 +0100)
committerGitHub <noreply@github.com>
Fri, 29 May 2020 09:48:26 +0000 (17:48 +0800)
fixes #3933

lib/compress.js
test/compress/collapse_vars.js
test/compress/dead-code.js
test/compress/evaluate.js

index daf60ee..b82ac81 100644 (file)
@@ -3288,15 +3288,18 @@ merge(Compressor.prototype, {
             var lhs = this.left;
             if (!ignore_side_effects) {
                 if (!(lhs instanceof AST_SymbolRef)) return this;
-                if (!HOP(lhs, "_eval") && !lhs.fixed) return this;
-                var def = lhs.definition();
-                if (def.undeclared) return this;
-                if (def.last_ref !== lhs) return this;
-                if (def.single_use == "m") return this;
+                if (!HOP(lhs, "_eval")) {
+                    if (!lhs.fixed) return this;
+                    var def = lhs.definition();
+                    if (!def.fixed) return this;
+                    if (def.undeclared) return this;
+                    if (def.last_ref !== lhs) return this;
+                    if (def.single_use == "m") return this;
+                }
             }
             var op = this.operator;
             var node;
-            if (HOP(lhs, "_eval") || !lhs.fixed) {
+            if (HOP(lhs, "_eval") || !(lhs instanceof AST_SymbolRef) || !lhs.fixed_value()) {
                 node = op == "=" ? this.right : make_node(AST_Binary, this, {
                     operator: op.slice(0, -1),
                     left: lhs,
@@ -3356,7 +3359,7 @@ merge(Compressor.prototype, {
                         key = key._eval(compressor, ignore_side_effects, cached, depth);
                         if (key === prop.key) return this;
                     }
-                    if (typeof Object.prototype[key] === 'function') {
+                    if (typeof Object.prototype[key] === "function") {
                         return this;
                     }
                     if (prop.value instanceof AST_Function) continue;
@@ -3372,7 +3375,7 @@ merge(Compressor.prototype, {
             var e = this.expression;
             var op = this.operator;
             // Function would be evaluated to an array and so typeof would
-            // incorrectly return 'object'. Hence making is a special case.
+            // incorrectly return "object". Hence making is a special case.
             if (compressor.option("typeofs")
                 && op == "typeof"
                 && (e instanceof AST_Lambda
@@ -3400,7 +3403,11 @@ merge(Compressor.prototype, {
               case "++":
               case "--":
                 if (!(e instanceof AST_SymbolRef)) return this;
-                if (!ignore_side_effects && e.definition().last_ref !== e) return this;
+                if (!ignore_side_effects) {
+                    var def = e.definition();
+                    if (def.undeclared) return this;
+                    if (def.last_ref !== e) return this;
+                }
                 if (HOP(e, "_eval")) v = +(op[0] + 1) + +v;
                 modified(e);
                 return v;
@@ -3409,8 +3416,17 @@ merge(Compressor.prototype, {
         });
         def(AST_UnaryPostfix, function(compressor, ignore_side_effects, cached, depth) {
             var e = this.expression;
-            if (!e.fixed) return this;
-            if (!ignore_side_effects && e.definition().last_ref !== e) return this;
+            if (!(e instanceof AST_SymbolRef)) {
+                if (!ignore_side_effects) return this;
+            } else if (!HOP(e, "_eval")) {
+                if (!e.fixed) return this;
+                if (!ignore_side_effects) {
+                    var def = e.definition();
+                    if (!def.fixed) return this;
+                    if (def.undeclared) return this;
+                    if (def.last_ref !== e) return this;
+                }
+            }
             var v = e._eval(compressor, ignore_side_effects, cached, depth + 1);
             if (v === e) return this;
             modified(e);
@@ -7835,34 +7851,35 @@ merge(Compressor.prototype, {
             var seq = self.lift_sequences(compressor);
             if (seq !== self) return seq.optimize(compressor);
         }
-        if (!compressor.option("assignments")) return self;
-        if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) {
-            // x = expr1 OP expr2
-            if (self.right.left instanceof AST_SymbolRef
-                && self.right.left.name == self.left.name
-                && ASSIGN_OPS[self.right.operator]) {
-                // x = x - 2 => x -= 2
-                self.operator = self.right.operator + "=";
-                self.right = self.right.right;
-            }
-            else if (self.right.right instanceof AST_SymbolRef
-                && self.right.right.name == self.left.name
-                && ASSIGN_OPS_COMMUTATIVE[self.right.operator]
-                && !self.right.left.has_side_effects(compressor)) {
-                // x = 2 & x => x &= 2
-                self.operator = self.right.operator + "=";
-                self.right = self.right.left;
-            }
-        }
-        if ((self.operator == "-=" || self.operator == "+="
-                && (self.left.is_boolean(compressor) || self.left.is_number(compressor)))
-            && self.right instanceof AST_Number
-            && self.right.value == 1) {
-            var op = self.operator.slice(0, -1);
-            return make_node(AST_UnaryPrefix, self, {
-                operator: op + op,
-                expression: self.left
-            });
+        if (compressor.option("assignments")) {
+            if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) {
+                // x = expr1 OP expr2
+                if (self.right.left instanceof AST_SymbolRef
+                    && self.right.left.name == self.left.name
+                    && ASSIGN_OPS[self.right.operator]) {
+                    // x = x - 2 => x -= 2
+                    self.operator = self.right.operator + "=";
+                    self.right = self.right.right;
+                }
+                else if (self.right.right instanceof AST_SymbolRef
+                    && self.right.right.name == self.left.name
+                    && ASSIGN_OPS_COMMUTATIVE[self.right.operator]
+                    && !self.right.left.has_side_effects(compressor)) {
+                    // x = 2 & x => x &= 2
+                    self.operator = self.right.operator + "=";
+                    self.right = self.right.left;
+                }
+            }
+            if ((self.operator == "-=" || self.operator == "+="
+                    && (self.left.is_boolean(compressor) || self.left.is_number(compressor)))
+                && self.right instanceof AST_Number
+                && self.right.value == 1) {
+                var op = self.operator.slice(0, -1);
+                return make_node(AST_UnaryPrefix, self, {
+                    operator: op + op,
+                    expression: self.left
+                });
+            }
         }
         return try_evaluate(compressor, self);
 
index 82fb057..6244e03 100644 (file)
@@ -917,7 +917,7 @@ collapse_vars_lvalues_drop_assign: {
     }
 }
 
-collapse_vars_misc1: {
+collapse_vars_misc: {
     options = {
         booleans: true,
         collapse_vars: true,
@@ -971,8 +971,8 @@ collapse_vars_misc1: {
         function f7() { var b = window.a * window.z; return b + b }
         function f8() { var b = window.a * window.z; return b + (5 + b) }
         function f9() { var b = window.a * window.z; return bar() || b }
-        function f10(x) { var a = 5; return a += 3; }
-        function f11(x) { var a = 5; return a += 2; }
+        function f10(x) { return 8; }
+        function f11(x) { return 7; }
     }
 }
 
index 795d1f7..22eb0d9 100644 (file)
@@ -236,7 +236,7 @@ collapse_vars_lvalues_drop_assign: {
     }
 }
 
-collapse_vars_misc1: {
+collapse_vars_misc: {
     options = {
         collapse_vars: true,
         dead_code: true,
index 868ad15..a7b5a4a 100644 (file)
@@ -2503,7 +2503,7 @@ inlined_increment_prefix: {
     expect: {
         var a = 0;
         void ++a;
-        console.log(a += 0);
+        console.log(1);
     }
     expect_stdout: "1"
 }
@@ -2525,7 +2525,7 @@ inlined_increment_postfix: {
     expect: {
         var a = 0;
         void a++;
-        console.log(a += 0);
+        console.log(1);
     }
     expect_stdout: "1"
 }
@@ -2544,3 +2544,113 @@ compound_assignment_to_property: {
     }
     expect_stdout: "PASS"
 }
+
+issue_2208_assign: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+        toplevel: true,
+    }
+    input: {
+        a = 42;
+        console.log({
+            p: function() {
+                return function() {
+                    return this.a;
+                }();
+            }
+        }.p());
+    }
+    expect: {
+        a = 42;
+        console.log({
+            p: function() {
+                return function() {
+                    return this.a;
+                }();
+            }
+        }.p());
+    }
+    expect_stdout: "42"
+}
+
+issue_2208_postfix: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+        toplevel: true,
+    }
+    input: {
+        a = 41;
+        a++;
+        console.log({
+            p: function() {
+                return function() {
+                    return this.a;
+                }();
+            }
+        }.p());
+    }
+    expect: {
+        a = 41;
+        a++;
+        console.log({
+            p: function() {
+                return function() {
+                    return this.a;
+                }();
+            }
+        }.p());
+    }
+    expect_stdout: "42"
+}
+
+issue_2208_prefix: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+        toplevel: true,
+    }
+    input: {
+        a = 43;
+        --a;
+        console.log({
+            p: function() {
+                return function() {
+                    return this.a;
+                }();
+            }
+        }.p());
+    }
+    expect: {
+        a = 43;
+        --a;
+        console.log({
+            p: function() {
+                return function() {
+                    return this.a;
+                }();
+            }
+        }.p());
+    }
+    expect_stdout: "42"
+}
+
+issue_3933: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+        unused: true,
+    }
+    input: {
+        (function(a, b) {
+            a && (b ^= 1) && console.log("PASS");
+        })(1);
+    }
+    expect: {
+        (function(a, b) {
+            1, (b ^= 1), console.log("PASS");
+        })();
+    }
+    expect_stdout: "PASS"
+}