compress arithmetic expressions further (#3529)
authorAlex Lam S.L <alexlamsl@gmail.com>
Sat, 26 Oct 2019 19:07:07 +0000 (03:07 +0800)
committerGitHub <noreply@github.com>
Sat, 26 Oct 2019 19:07:07 +0000 (03:07 +0800)
lib/compress.js
test/compress/asm.js
test/compress/numbers.js

index 03c1f59..6d8c1f8 100644 (file)
@@ -2437,9 +2437,10 @@ merge(Compressor.prototype, {
                 || this.operator == "=" && this.right.is_number(compressor);
         });
         def(AST_Binary, function(compressor) {
-            return binary[this.operator] || this.operator == "+"
-                && this.left.is_number(compressor)
-                && this.right.is_number(compressor);
+            if (binary[this.operator]) return true;
+            if (this.operator != "+") return false;
+            return (this.left.is_boolean(compressor) || this.left.is_number(compressor))
+                && (this.right.is_boolean(compressor) || this.right.is_number(compressor));
         });
         var fn = makePredicate([
             "charCodeAt",
@@ -5777,6 +5778,7 @@ merge(Compressor.prototype, {
             }
         }
         if (compressor.option("evaluate")) {
+            var associative = true;
             switch (self.operator) {
               case "&&":
                 var ll = fuzzy_eval(self.left);
@@ -5845,9 +5847,6 @@ merge(Compressor.prototype, {
                     }).optimize(compressor);
                 }
                 break;
-            }
-            var associative = true;
-            switch (self.operator) {
               case "+":
                 // "foo" + ("bar" + x) => "foobar" + x
                 if (self.left instanceof AST_Constant
@@ -5927,13 +5926,38 @@ merge(Compressor.prototype, {
                     });
                     break;
                 }
+              case "-":
+                // a - -b => a + b
+                if (self.right instanceof AST_UnaryPrefix
+                    && self.right.operator == "-"
+                    && self.left.is_number(compressor)) {
+                    self = make_node(AST_Binary, self, {
+                        operator: "+",
+                        left: self.left,
+                        right: self.right.expression
+                    });
+                    break;
+                }
               case "*":
+              case "/":
                 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;
+                    }
+                }
               case "&":
               case "|":
               case "^":
                 // a + +b => +b + a
-                if (self.left.is_number(compressor)
+                if (self.operator != "-"
+                    && self.operator != "/"
+                    && self.left.is_number(compressor)
                     && self.right.is_number(compressor)
                     && reversible()
                     && !(self.left instanceof AST_Binary
@@ -5951,77 +5975,56 @@ merge(Compressor.prototype, {
                         self = best_of(compressor, self, reversed);
                     }
                 }
-                if (associative && self.is_number(compressor)) {
-                    // a + (b + c) => (a + b) + c
-                    if (self.right instanceof AST_Binary
-                        && self.right.operator == self.operator) {
-                        self = make_node(AST_Binary, self, {
+                if (!associative || !self.is_number(compressor)) break;
+                // a + (b + c) => (a + b) + c
+                if (self.right instanceof AST_Binary
+                    && self.right.operator != "%"
+                    && PRECEDENCE[self.right.operator] == PRECEDENCE[self.operator]) {
+                    self = make_node(AST_Binary, self, {
+                        operator: align(self.operator, self.right.operator),
+                        left: make_node(AST_Binary, self.left, {
                             operator: self.operator,
+                            left: self.left,
+                            right: self.right.left,
+                            start: self.left.start,
+                            end: self.right.left.end
+                        }),
+                        right: self.right.right
+                    });
+                }
+                // (2 * n) * 3 => 6 * n
+                // (n + 2) + 3 => n + 5
+                if (self.right instanceof AST_Constant
+                    && self.left instanceof AST_Binary
+                    && self.left.operator != "%"
+                    && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]) {
+                    if (self.left.left instanceof AST_Constant) {
+                        self = make_node(AST_Binary, self, {
+                            operator: self.left.operator,
                             left: make_node(AST_Binary, self.left, {
                                 operator: self.operator,
-                                left: self.left,
-                                right: self.right.left,
-                                start: self.left.start,
-                                end: self.right.left.end
+                                left: self.left.left,
+                                right: self.right,
+                                start: self.left.left.start,
+                                end: self.right.end
                             }),
-                            right: self.right.right
+                            right: self.left.right
                         });
-                    }
-                    // (n + 2) + 3 => 5 + n
-                    // (2 * n) * 3 => 6 + n
-                    if (self.right instanceof AST_Constant
-                        && self.left instanceof AST_Binary
-                        && self.left.operator == self.operator) {
-                        if (self.left.left instanceof AST_Constant) {
-                            self = make_node(AST_Binary, self, {
-                                operator: self.operator,
-                                left: make_node(AST_Binary, self.left, {
-                                    operator: self.operator,
-                                    left: self.left.left,
-                                    right: self.right,
-                                    start: self.left.left.start,
-                                    end: self.right.end
-                                }),
-                                right: self.left.right
-                            });
-                        } else if (self.left.right instanceof AST_Constant) {
-                            self = make_node(AST_Binary, self, {
-                                operator: self.operator,
-                                left: make_node(AST_Binary, self.left, {
-                                    operator: self.operator,
-                                    left: self.left.right,
-                                    right: self.right,
-                                    start: self.left.right.start,
-                                    end: self.right.end
-                                }),
-                                right: self.left.left
-                            });
-                        }
-                    }
-                    // (a | 1) | (2 | d) => (3 | a) | b
-                    if (self.left instanceof AST_Binary
-                        && self.left.operator == self.operator
-                        && self.left.right instanceof AST_Constant
-                        && self.right instanceof AST_Binary
-                        && self.right.operator == self.operator
-                        && self.right.left instanceof AST_Constant) {
+                    } else if (self.left.right instanceof AST_Constant) {
                         self = make_node(AST_Binary, self, {
-                            operator: self.operator,
-                            left: make_node(AST_Binary, self.left, {
-                                operator: self.operator,
-                                left: make_node(AST_Binary, self.left.left, {
-                                    operator: self.operator,
-                                    left: self.left.right,
-                                    right: self.right.left,
-                                    start: self.left.right.start,
-                                    end: self.right.left.end
-                                }),
-                                right: self.left.left
-                            }),
-                            right: self.right.right
+                            operator: self.left.operator,
+                            left: self.left.left,
+                            right: make_node(AST_Binary, self.left, {
+                                operator: align(self.left.operator, self.operator),
+                                left: self.left.right,
+                                right: self.right,
+                                start: self.left.right.start,
+                                end: self.right.end
+                            })
                         });
                     }
                 }
+                break;
             }
         }
         if (compressor.option("unsafe")) {
@@ -6086,6 +6089,17 @@ merge(Compressor.prototype, {
         }
         return self;
 
+        function align(ref, op) {
+            switch (ref) {
+              case "-":
+                return op == "+" ? "-" : "+";
+              case "/":
+                return op == "*" ? "/" : "*";
+              default:
+                return op;
+            }
+        }
+
         function fuzzy_eval(node) {
             if (node.truthy) return true;
             if (node.falsy) return false;
index cebe683..1f00f8f 100644 (file)
@@ -96,7 +96,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 000b9ab..3f57978 100644 (file)
@@ -51,17 +51,20 @@ comparisons: {
         comparisons: true,
     }
     input: {
+        var x = "42", y = "0x30";
         console.log(
             ~x === 42,
-            x % n === 42
+            x % y === 42
         );
     }
     expect: {
+        var x = "42", y = "0x30";
         console.log(
             42 == ~x,
-            x % n == 42
+            x % y == 42
         );
     }
+    expect_stdout: "false true"
 }
 
 evaluate_1: {
@@ -103,12 +106,94 @@ evaluate_1: {
     }
 }
 
+evaluate_1_unsafe_math: {
+    options = {
+        evaluate: true,
+        unsafe_math: true,
+    }
+    input: {
+        console.log(
+            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)
+        );
+    }
+    expect: {
+        console.log(
+            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 - y + ~x,
+            0 & x,
+            6 + (x |= 0)
+        );
+    }
+}
+
 evaluate_2: {
+    options = {
+        evaluate: true,
+        unsafe_math: false,
+    }
+    input: {
+        var x = "42", y = null;
+        console.log(
+            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)
+        );
+    }
+    expect: {
+        var x = "42", y = null;
+        console.log(
+            x + 1 + 2,
+            1 * x * 2,
+            +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
+        );
+    }
+    expect_stdout: "4212 84 45 14223 43 48 6 47 -36 -37 0 47"
+}
+
+evaluate_2_unsafe_math: {
     options = {
         evaluate: true,
         unsafe_math: true,
     }
     input: {
+        var x = "42", y = null;
         console.log(
             x + 1 + 2,
             x * 1 * 2,
@@ -118,24 +203,30 @@ evaluate_2: {
             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)
         );
     }
     expect: {
+        var x = "42", y = null;
         console.log(
             x + 1 + 2,
             2 * x,
-            3 + +x,
+            +x + 3,
             1 + x + 2 + 3,
             3 | x,
             6 + x--,
-            6 + x*y,
+            x*y + 6,
             1 + (2 + x + 3),
+            6 + ~x,
+            5 + ~x - y,
             0 & x,
             6 + (x |= 0)
         );
     }
+    expect_stdout: "4212 84 45 14223 43 48 6 47 -36 -37 0 47"
 }
 
 evaluate_3: {
@@ -148,7 +239,7 @@ evaluate_3: {
         console.log(1 + Number(x) + 2);
     }
     expect: {
-        console.log(3 + +x);
+        console.log(+x + 3);
     }
 }
 
@@ -182,6 +273,258 @@ evaluate_4: {
     }
 }
 
+evaluate_5: {
+    options = {
+        evaluate: true,
+        unsafe_math: false,
+    }
+    input: {
+        var a = true;
+        console.log(
+            +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
+        );
+    }
+    expect: {
+        var a = true;
+        console.log(
+            +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
+        );
+    }
+    expect_stdout: "6 0 2 -4 6 0 4 -2 6 4 0 -2"
+}
+
+evaluate_5_unsafe_math: {
+    options = {
+        evaluate: true,
+        unsafe_math: true,
+    }
+    input: {
+        var a = true;
+        console.log(
+            +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
+        );
+    }
+    expect: {
+        var a = true;
+        console.log(
+            +a + 5,
+            +a + -1,
+            a - -1,
+            a - 5,
+            +a + 5,
+            +a + -1,
+            5 - a,
+            -1 - a,
+            +a + 5,
+            5 - a,
+            +a - 1,
+            -1 - a
+        );
+    }
+    expect_stdout: "6 0 2 -4 6 0 4 -2 6 4 0 -2"
+}
+
+evaluate_6: {
+    options = {
+        evaluate: true,
+        unsafe_math: false,
+    }
+    input: {
+        var a = true;
+        console.log(
+            -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
+        );
+    }
+    expect: {
+        var a = true;
+        console.log(
+            2 - a + 3,
+            2 - a - 3,
+            -a - 2 + 3,
+            -a - 2 - 3,
+            2 - a + 3,
+            2 - a - 3,
+            2 + a + 3,
+            2 + a - 3,
+            5 - a,
+            5 + a,
+            -1 - a,
+            -1 + a
+        );
+    }
+    expect_stdout: "4 -2 0 -6 4 -2 6 0 4 6 -2 0"
+}
+
+evaluate_6_unsafe_math: {
+    options = {
+        evaluate: true,
+        unsafe_math: true,
+    }
+    input: {
+        var a = true;
+        console.log(
+            -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
+        );
+    }
+    expect: {
+        var a = true;
+        console.log(
+            5 - a,
+            -1 - a,
+            -a - -1,
+            -a - 5,
+            5 - a,
+            -1 - a,
+            2 + a + 3,
+            -1 + a,
+            5 - a,
+            5 + a,
+            -1 - a,
+            -1 + a
+        );
+    }
+    expect_stdout: "4 -2 0 -6 4 -2 6 0 4 6 -2 0"
+}
+
+evaluate_7: {
+    options = {
+        evaluate: true,
+        unsafe_math: false,
+    }
+    input: {
+        var x = "42", y;
+        console.log(
+            +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)
+        );
+    }
+    expect: {
+        var x = "42", y;
+        console.log(
+            +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)
+        );
+    }
+    expect_stdout: "48 46 40 42 44 42 36 38"
+}
+
+evaluate_7_unsafe_math: {
+    options = {
+        evaluate: true,
+        unsafe_math: true,
+    }
+    input: {
+        var x = "42", y;
+        console.log(
+            +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)
+        );
+    }
+    expect: {
+        var x = "42", y;
+        console.log(
+            +x + 5 + !y,
+            +x + 5 - !y,
+            +x + -1 - !y,
+            +x + -1 + !y,
+            x - -1 + !y,
+            x - -1 - !y,
+            x - 5 - !y,
+            x - 5 + !y
+        );
+    }
+    expect_stdout: "48 46 40 42 44 42 36 38"
+}
+
+NaN_redefined: {
+    options = {
+        evaluate: true,
+        unsafe_math: true,
+    }
+    input: {
+        var NaN;
+        console.log(1 / (0 / 0));
+    }
+    expect: {
+        var NaN;
+        console.log(0 / 0);
+    }
+    expect_stdout: "NaN"
+}
+
 issue_1710: {
     options = {
         evaluate: true,