fix corner case in `evaluate` (#4138)
authorAlex Lam S.L <alexlamsl@gmail.com>
Mon, 21 Sep 2020 22:49:32 +0000 (23:49 +0100)
committerGitHub <noreply@github.com>
Mon, 21 Sep 2020 22:49:32 +0000 (06:49 +0800)
fixes #4137

lib/compress.js
test/compress/asm.js
test/compress/numbers.js

index 82bbbf1..adc894f 100644 (file)
@@ -3106,7 +3106,12 @@ merge(Compressor.prototype, {
     (function(def) {
         def(AST_Node, return_false);
         def(AST_Assign, function(compressor) {
-            return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
+            switch (this.operator) {
+              case "+=":
+                if (this.left.is_string(compressor)) return true;
+              case "=":
+                return this.right.is_string(compressor);
+            }
         });
         def(AST_Binary, function(compressor) {
             return this.operator == "+" &&
@@ -7365,10 +7370,9 @@ merge(Compressor.prototype, {
     var indexFns = makePredicate("indexOf lastIndexOf");
     var commutativeOperators = makePredicate("== === != !== * & | ^");
     function is_object(node) {
-        while ((node = node.tail_node()) instanceof AST_SymbolRef) {
-            node = node.fixed_value();
-            if (!node) return false;
-        }
+        if (node instanceof AST_Assign) return node.operator == "=" && is_object(node.right);
+        if (node instanceof AST_Sequence) return is_object(node.tail_node());
+        if (node instanceof AST_SymbolRef) return is_object(node.fixed_value());
         return node instanceof AST_Array
             || node instanceof AST_Lambda
             || node instanceof AST_New
@@ -7760,14 +7764,16 @@ merge(Compressor.prototype, {
                 associative = compressor.option("unsafe_math");
                 // +a - b => a - b
                 // a - +b => a - b
-                if (self.operator != "+") {
-                    if (self.left instanceof AST_UnaryPrefix && self.left.operator == "+") {
-                        self.left = self.left.expression;
-                    }
-                    if (self.right instanceof AST_UnaryPrefix && self.right.operator == "+") {
-                        self.right = self.right.expression;
+                [ "left", "right" ].forEach(function(operand) {
+                    var node = self[operand];
+                    if (node instanceof AST_UnaryPrefix && node.operator == "+") {
+                        var exp = node.expression;
+                        if (exp.is_boolean(compressor) || exp.is_number(compressor)
+                            || self.operator != "+" && exp.is_string(compressor)) {
+                            self[operand] = exp;
+                        }
                     }
-                }
+                });
               case "&":
               case "|":
               case "^":
index 54cf90f..65d20b9 100644 (file)
@@ -95,7 +95,7 @@ asm_mixed: {
                 return +sum;
             }
             function geometricMean(start, end) {
-                return start |= 0, end |= 0, +exp(logSum(start, end) / (end - start | 0));
+                return start |= 0, end |= 0, +exp(+logSum(start, end) / (end - start | 0));
             }
             var exp = stdlib.Math.exp, log = stdlib.Math.log, values = new stdlib.Float64Array(buffer);
             return { geometricMean: geometricMean };
index 32987a6..7f355a1 100644 (file)
@@ -91,7 +91,7 @@ evaluate_1: {
     expect: {
         console.log(
             x + 1 + 2,
-            2 * x,
+            2 * +x,
             +x + 1 + 2,
             1 + x + 2 + 3,
             3 | x,
@@ -130,7 +130,7 @@ evaluate_1_unsafe_math: {
     expect: {
         console.log(
             x + 1 + 2,
-            2 * x,
+            2 * +x,
             +x + 3,
             1 + x + 2 + 3,
             3 | x,
@@ -148,45 +148,52 @@ evaluate_1_unsafe_math: {
 evaluate_2: {
     options = {
         evaluate: true,
+        reduce_vars: true,
         unsafe_math: false,
     }
     input: {
-        var x = "42", y = null;
-        [
-            x + 1 + 2,
-            x * 1 * 2,
-            +x + 1 + 2,
-            1 + x + 2 + 3,
-            1 | x | 2 | 3,
-            1 + x-- + 2 + 3,
-            1 + (x*y + 2) + 3,
-            1 + (2 + x + 3),
-            1 + (2 + ~x + 3),
-            -y + (2 + ~x + 3),
-            1 & (2 & x & 3),
-            1 + (2 + (x |= 0) + 3),
-        ].forEach(function(n) {
-            console.log(typeof n, n);
-        });
+        function f(num) {
+            var x = "" + num, y = null;
+            [
+                x + 1 + 2,
+                x * 1 * 2,
+                +x + 1 + 2,
+                1 + x + 2 + 3,
+                1 | x | 2 | 3,
+                1 + x-- + 2 + 3,
+                1 + (x*y + 2) + 3,
+                1 + (2 + x + 3),
+                1 + (2 + ~x + 3),
+                -y + (2 + ~x + 3),
+                1 & (2 & x & 3),
+                1 + (2 + (x |= 0) + 3),
+            ].forEach(function(n) {
+                console.log(typeof n, n);
+            });
+        }
+        f(42);
     }
     expect: {
-        var x = "42", y = null;
-        [
-            x + 1 + 2,
-            2 * x,
-            +x + 1 + 2,
-            1 + x + 2 + 3,
-            3 | x,
-            1 + x-- + 2 + 3,
-            x*y + 2 + 1 + 3,
-            1 + (2 + x + 3),
-            2 + ~x + 3 + 1,
-            2 + ~x + 3 - y,
-            0 & x,
-            2 + (x |= 0) + 3 + 1,
-        ].forEach(function(n) {
-            console.log(typeof n, n);
-        });
+        function f(num) {
+            var x = "" + num, y = null;
+            [
+                x + "12",
+                2 * x,
+                +x + 1 + 2,
+                1 + x + "23",
+                3 | x,
+                1 + x-- + 2 + 3,
+                x*y + 2 + 1 + 3,
+                2 + x + 3 + 1,
+                2 + ~x + 3 + 1,
+                2 + ~x + 3,
+                0 & x,
+                2 + (x |= 0) + 3 + 1,
+            ].forEach(function(n) {
+                console.log(typeof n, n);
+            });
+        }
+        f(42);
     }
     expect_stdout: [
         "string 4212",
@@ -207,45 +214,52 @@ evaluate_2: {
 evaluate_2_unsafe_math: {
     options = {
         evaluate: true,
+        reduce_vars: true,
         unsafe_math: true,
     }
     input: {
-        var x = "42", y = null;
-        [
-            x + 1 + 2,
-            x * 1 * 2,
-            +x + 1 + 2,
-            1 + x + 2 + 3,
-            1 | x | 2 | 3,
-            1 + x-- + 2 + 3,
-            1 + (x*y + 2) + 3,
-            1 + (2 + x + 3),
-            1 + (2 + ~x + 3),
-            -y + (2 + ~x + 3),
-            1 & (2 & x & 3),
-            1 + (2 + (x |= 0) + 3),
-        ].forEach(function(n) {
-            console.log(typeof n, n);
-        });
+        function f(num) {
+            var x = "" + num, y = null;
+            [
+                x + 1 + 2,
+                x * 1 * 2,
+                +x + 1 + 2,
+                1 + x + 2 + 3,
+                1 | x | 2 | 3,
+                1 + x-- + 2 + 3,
+                1 + (x*y + 2) + 3,
+                1 + (2 + x + 3),
+                1 + (2 + ~x + 3),
+                -y + (2 + ~x + 3),
+                1 & (2 & x & 3),
+                1 + (2 + (x |= 0) + 3),
+            ].forEach(function(n) {
+                console.log(typeof n, n);
+            });
+        }
+        f(42);
     }
     expect: {
-        var x = "42", y = null;
-        [
-            x + 1 + 2,
-            2 * x,
-            +x + 3,
-            1 + x + 2 + 3,
-            3 | x,
-            6 + x--,
-            x*y + 6,
-            1 + (2 + x + 3),
-            6 + ~x,
-            5 + ~x - y,
-            0 & x,
-            6 + (x |= 0),
-        ].forEach(function(n) {
-            console.log(typeof n, n);
-        });
+        function f(num) {
+            var x = "" + num, y = null;
+            [
+                x + "12",
+                2 * x,
+                +x + 3,
+                1 + x + "23",
+                3 | x,
+                6 + x--,
+                x*y + 6,
+                6 + x,
+                6 + ~x,
+                5 + ~x,
+                0 & x,
+                6 + (x |= 0),
+            ].forEach(function(n) {
+                console.log(typeof n, n);
+            });
+        }
+        f(42);
     }
     expect_stdout: [
         "string 4212",
@@ -310,45 +324,52 @@ evaluate_4: {
 evaluate_5: {
     options = {
         evaluate: true,
+        reduce_vars: true,
         unsafe_math: false,
     }
     input: {
-        var a = "1";
-        [
-            +a + 2 + 3,
-            +a + 2 - 3,
-            +a - 2 + 3,
-            +a - 2 - 3,
-            2 + +a + 3,
-            2 + +a - 3,
-            2 - +a + 3,
-            2 - +a - 3,
-            2 + 3 + +a,
-            2 + 3 - +a,
-            2 - 3 + +a,
-            2 - 3 - +a,
-        ].forEach(function(n) {
-            console.log(typeof n, n);
-        });
+        function f(num) {
+            var a = "" + num;
+            [
+                +a + 2 + 3,
+                +a + 2 - 3,
+                +a - 2 + 3,
+                +a - 2 - 3,
+                2 + +a + 3,
+                2 + +a - 3,
+                2 - +a + 3,
+                2 - +a - 3,
+                2 + 3 + +a,
+                2 + 3 - +a,
+                2 - 3 + +a,
+                2 - 3 - +a,
+            ].forEach(function(n) {
+                console.log(typeof n, n);
+            });
+        }
+        f(1);
     }
     expect: {
-        var a = "1";
-        [
-            +a + 2 + 3,
-            +a + 2 - 3,
-            a - 2 + 3,
-            a - 2 - 3,
-            +a + 2 + 3,
-            +a + 2 - 3,
-            2 - a + 3,
-            2 - a - 3,
-            +a + 5,
-            5 - a,
-            +a - 1,
-            -1 - a,
-        ].forEach(function(n) {
-            console.log(typeof n, n);
-        });
+        function f(num) {
+            var a = "" + num;
+            [
+                +a + 2 + 3,
+                +a + 2 - 3,
+                a - 2 + 3,
+                a - 2 - 3,
+                +a + 2 + 3,
+                +a + 2 - 3,
+                2 - a + 3,
+                2 - a - 3,
+                +a + 5,
+                5 - a,
+                +a - 1,
+                -1 - a,
+            ].forEach(function(n) {
+                console.log(typeof n, n);
+            });
+        }
+        f(1);
     }
     expect_stdout: [
         "number 6",
@@ -369,45 +390,52 @@ evaluate_5: {
 evaluate_5_unsafe_math: {
     options = {
         evaluate: true,
+        reduce_vars: true,
         unsafe_math: true,
     }
     input: {
-        var a = "1";
-        [
-            +a + 2 + 3,
-            +a + 2 - 3,
-            +a - 2 + 3,
-            +a - 2 - 3,
-            2 + +a + 3,
-            2 + +a - 3,
-            2 - +a + 3,
-            2 - +a - 3,
-            2 + 3 + +a,
-            2 + 3 - +a,
-            2 - 3 + +a,
-            2 - 3 - +a,
-        ].forEach(function(n) {
-            console.log(typeof n, n);
-        });
+        function f(num) {
+            var a = "" + num;
+            [
+                +a + 2 + 3,
+                +a + 2 - 3,
+                +a - 2 + 3,
+                +a - 2 - 3,
+                2 + +a + 3,
+                2 + +a - 3,
+                2 - +a + 3,
+                2 - +a - 3,
+                2 + 3 + +a,
+                2 + 3 - +a,
+                2 - 3 + +a,
+                2 - 3 - +a,
+            ].forEach(function(n) {
+                console.log(typeof n, n);
+            });
+        }
+        f(1);
     }
     expect: {
-        var a = "1";
-        [
-            +a + 5,
-            +a + -1,
-            a - -1,
-            a - 5,
-            +a + 5,
-            +a + -1,
-            5 - a,
-            -1 - a,
-            +a + 5,
-            5 - a,
-            +a - 1,
-            -1 - a,
-        ].forEach(function(n) {
-            console.log(typeof n, n);
-        });
+        function f(num) {
+            var a = "" + num;
+            [
+                +a + 5,
+                +a + -1,
+                a - -1,
+                a - 5,
+                +a + 5,
+                +a + -1,
+                5 - a,
+                -1 - a,
+                +a + 5,
+                5 - a,
+                +a - 1,
+                -1 - a,
+            ].forEach(function(n) {
+                console.log(typeof n, n);
+            });
+        }
+        f(1);
     }
     expect_stdout: [
         "number 6",
@@ -546,37 +574,44 @@ evaluate_6_unsafe_math: {
 evaluate_7: {
     options = {
         evaluate: true,
+        reduce_vars: true,
         unsafe_math: false,
     }
     input: {
-        var x = "42", y;
-        [
-            +x + 2 + (3 + !y),
-            +x + 2 + (3 - !y),
-            +x + 2 - (3 + !y),
-            +x + 2 - (3 - !y),
-            +x - 2 + (3 + !y),
-            +x - 2 + (3 - !y),
-            +x - 2 - (3 + !y),
-            +x - 2 - (3 - !y),
-        ].forEach(function(n) {
-            console.log(typeof n, n);
-        });
+        function f(num, y) {
+            var x = "" + num;
+            [
+                +x + 2 + (3 + !y),
+                +x + 2 + (3 - !y),
+                +x + 2 - (3 + !y),
+                +x + 2 - (3 - !y),
+                +x - 2 + (3 + !y),
+                +x - 2 + (3 - !y),
+                +x - 2 - (3 + !y),
+                +x - 2 - (3 - !y),
+            ].forEach(function(n) {
+                console.log(typeof n, n);
+            });
+        }
+        f(42);
     }
     expect: {
-        var x = "42", y;
-        [
-            +x + 2 + (3 + !y),
-            +x + 2 + (3 - !y),
-            +x + 2 - (3 + !y),
-            +x + 2 - (3 - !y),
-            x - 2 + (3 + !y),
-            x - 2 + (3 - !y),
-            x - 2 - (3 + !y),
-            x - 2 - (3 - !y),
-        ].forEach(function(n) {
-            console.log(typeof n, n);
-        });
+        function f(num, y) {
+            var x = "" + num;
+            [
+                +x + 2 + (3 + !y),
+                +x + 2 + (3 - !y),
+                +x + 2 - (3 + !y),
+                +x + 2 - (3 - !y),
+                x - 2 + (3 + !y),
+                x - 2 + (3 - !y),
+                x - 2 - (3 + !y),
+                x - 2 - (3 - !y),
+            ].forEach(function(n) {
+                console.log(typeof n, n);
+            });
+        }
+        f(42);
     }
     expect_stdout: [
         "number 48",
@@ -593,37 +628,44 @@ evaluate_7: {
 evaluate_7_unsafe_math: {
     options = {
         evaluate: true,
+        reduce_vars: true,
         unsafe_math: true,
     }
     input: {
-        var x = "42", y;
-        [
-            +x + 2 + (3 + !y),
-            +x + 2 + (3 - !y),
-            +x + 2 - (3 + !y),
-            +x + 2 - (3 - !y),
-            +x - 2 + (3 + !y),
-            +x - 2 + (3 - !y),
-            +x - 2 - (3 + !y),
-            +x - 2 - (3 - !y),
-        ].forEach(function(n) {
-            console.log(typeof n, n);
-        });
+        function f(num, y) {
+            var x = "" + num;
+            [
+                +x + 2 + (3 + !y),
+                +x + 2 + (3 - !y),
+                +x + 2 - (3 + !y),
+                +x + 2 - (3 - !y),
+                +x - 2 + (3 + !y),
+                +x - 2 + (3 - !y),
+                +x - 2 - (3 + !y),
+                +x - 2 - (3 - !y),
+            ].forEach(function(n) {
+                console.log(typeof n, n);
+            });
+        }
+        f(42);
     }
     expect: {
-        var x = "42", y;
-        [
-            +x + 5 + !y,
-            +x + 5 - !y,
-            +x + -1 - !y,
-            +x + -1 + !y,
-            x - -1 + !y,
-            x - -1 - !y,
-            x - 5 - !y,
-            x - 5 + !y,
-        ].forEach(function(n) {
-            console.log(typeof n, n);
-        });
+        function f(num, y) {
+            var x = "" + num;
+            [
+                +x + 5 + !y,
+                +x + 5 - !y,
+                +x + -1 - !y,
+                +x + -1 + !y,
+                x - -1 + !y,
+                x - -1 - !y,
+                x - 5 - !y,
+                x - 5 + !y,
+            ].forEach(function(n) {
+                console.log(typeof n, n);
+            });
+        }
+        f(42);
     }
     expect_stdout: [
         "number 48",
@@ -1267,3 +1309,16 @@ issue_3695: {
     }
     expect_stdout: "NaN"
 }
+
+issue_4137: {
+    options = {
+        evaluate: true,
+    }
+    input: {
+        console.log(+(A = []) * (A[0] = 1));
+    }
+    expect: {
+        console.log(+(A = []) * (A[0] = 1));
+    }
+    expect_stdout: "0"
+}