introduce `assignments` (#3345)
authorAlex Lam S.L <alexlamsl@gmail.com>
Mon, 18 Mar 2019 13:28:41 +0000 (21:28 +0800)
committerGitHub <noreply@github.com>
Mon, 18 Mar 2019 13:28:41 +0000 (21:28 +0800)
README.md
lib/compress.js
test/compress/asm.js
test/compress/assignment.js
test/compress/collapse_vars.js
test/compress/conditionals.js
test/compress/evaluate.js
test/compress/functions.js

index 5d6b89b..1be2d2e 100644 (file)
--- a/README.md
+++ b/README.md
@@ -605,6 +605,8 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
 - `arguments` (default: `true`) -- replace `arguments[index]` with function
   parameter name whenever possible.
 
+- `assignments` (default: `true`) -- apply optimizations to assignment expressions.
+
 - `booleans` (default: `true`) -- various optimizations for boolean context,
   for example `!!a ? b : c → a ? b : c`
 
index 707982d..a31ce86 100644 (file)
@@ -49,6 +49,7 @@ function Compressor(options, false_by_default) {
     TreeTransformer.call(this, this.before, this.after);
     this.options = defaults(options, {
         arguments       : !false_by_default,
+        assignments     : !false_by_default,
         booleans        : !false_by_default,
         collapse_vars   : !false_by_default,
         comparisons     : !false_by_default,
@@ -2415,6 +2416,10 @@ merge(Compressor.prototype, {
         def(AST_Sequence, function(compressor) {
             return this.tail_node().is_number(compressor);
         });
+        def(AST_SymbolRef, function(compressor) {
+            var fixed = this.fixed_value();
+            return fixed && fixed.is_number(compressor);
+        });
         var unary = makePredicate("+ - ~ ++ --");
         def(AST_Unary, function() {
             return unary[this.operator];
@@ -2426,22 +2431,26 @@ merge(Compressor.prototype, {
     // methods to determine if an expression has a string result type
     (function(def) {
         def(AST_Node, return_false);
-        def(AST_String, return_true);
-        def(AST_UnaryPrefix, function() {
-            return this.operator == "typeof";
+        def(AST_Assign, function(compressor) {
+            return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
         });
         def(AST_Binary, function(compressor) {
             return this.operator == "+" &&
                 (this.left.is_string(compressor) || this.right.is_string(compressor));
         });
-        def(AST_Assign, function(compressor) {
-            return (this.operator == "=" || this.operator == "+=") && this.right.is_string(compressor);
+        def(AST_Conditional, function(compressor) {
+            return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
         });
         def(AST_Sequence, function(compressor) {
             return this.tail_node().is_string(compressor);
         });
-        def(AST_Conditional, function(compressor) {
-            return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
+        def(AST_String, return_true);
+        def(AST_SymbolRef, function(compressor) {
+            var fixed = this.fixed_value();
+            return fixed && fixed.is_string(compressor);
+        });
+        def(AST_UnaryPrefix, function() {
+            return this.operator == "typeof";
         });
     })(function(node, func) {
         node.DEFMETHOD("is_string", func);
@@ -6010,6 +6019,7 @@ merge(Compressor.prototype, {
                 || parent instanceof AST_Sequence && parent.tail_node() === node);
         }
         self = self.lift_sequences(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
@@ -6028,6 +6038,16 @@ merge(Compressor.prototype, {
                 self.right = self.right.left;
             }
         }
+        if ((self.operator == "+=" || self.operator == "-=")
+            && self.left.is_number(compressor)
+            && self.right instanceof AST_Number
+            && self.right.getValue() === 1) {
+            var op = self.operator.slice(0, -1);
+            return make_node(AST_UnaryPrefix, self, {
+                operator: op + op,
+                expression: self.left
+            });
+        }
         return self;
 
         function in_try(level, node) {
index d7b1b62..cebe683 100644 (file)
@@ -1,5 +1,6 @@
 asm_mixed: {
     options = {
+        assignments: true,
         booleans: true,
         comparisons: true,
         conditionals: true,
index 903380a..ba41258 100644 (file)
@@ -1,5 +1,6 @@
 op_equals_left_local_var: {
     options = {
+        assignments: true,
         evaluate: true,
     }
     input: {
@@ -60,6 +61,7 @@ op_equals_left_local_var: {
 
 op_equals_right_local_var: {
     options = {
+        assignments: true,
         evaluate: true,
     }
     input: {
@@ -123,6 +125,7 @@ op_equals_right_local_var: {
 }
 op_equals_left_global_var: {
     options = {
+        assignments: true,
         evaluate: true,
     }
     input: {
@@ -179,6 +182,7 @@ op_equals_left_global_var: {
 
 op_equals_right_global_var: {
     options = {
+        assignments: true,
         evaluate: true,
     }
     input: {
@@ -236,3 +240,52 @@ op_equals_right_global_var: {
         x = g() &   x;
     }
 }
+
+increment_decrement_1: {
+    options = {
+        assignments: true,
+        reduce_vars: true,
+    }
+    input: {
+        console.log(function(a) {
+            a += 1;
+            a -= 1;
+            return a;
+        }(42));
+    }
+    expect: {
+        console.log(function(a){
+            ++a;
+            --a;
+            return a;
+        }(42));
+    }
+    expect_stdout: "42"
+}
+
+increment_decrement_2: {
+    options = {
+        assignments: true,
+        passes: 2,
+        reduce_vars: true,
+    }
+    input: {
+        console.log(function(a) {
+            a = a + 1;
+            a = a - 1;
+            a += 1;
+            a -= 1;
+            return a;
+        }(42));
+    }
+    expect: {
+        console.log(function(a){
+            ++a;
+            --a;
+            ++a;
+            --a;
+            return a;
+        }(42));
+    }
+    expect_stdout: "42"
+}
index 6c86712..a858e00 100644 (file)
@@ -944,7 +944,7 @@ collapse_vars_misc1: {
         function f5(x) { var z = foo(); return (5 - window.x) / z }
         function f6() { return window.a * window.z && zap() }
         function f7() { var b = window.a * window.z; return b + b }
-        function f8() { var b = window.a * window.z; return b + (b + 5) }
+        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; }
index d6d47c4..78c0ca2 100644 (file)
@@ -1367,6 +1367,7 @@ cond_seq_assign_2: {
 
 cond_seq_assign_3: {
     options = {
+        assignments: true,
         conditionals: true,
     }
     input: {
index 2378528..a740cd8 100644 (file)
@@ -1540,7 +1540,7 @@ issue_2926_2: {
     expect_stdout: "function"
 }
 
-issue_2968: {
+issue_2968_1: {
     options = {
         collapse_vars: true,
         evaluate: true,
@@ -1571,6 +1571,39 @@ issue_2968: {
     expect_stdout: "PASS"
 }
 
+issue_2968_2: {
+    options = {
+        assignments: true,
+        collapse_vars: true,
+        evaluate: true,
+        inline: true,
+        passes: 2,
+        reduce_vars: true,
+        side_effects: true,
+        unused: true,
+    }
+    input: {
+        var c = "FAIL";
+        (function() {
+            (function(a, b) {
+                a <<= 0;
+                a && (a[(c = "PASS", 0 >>> (b += 1))] = 0);
+            })(42, -42);
+        })();
+        console.log(c);
+    }
+    expect: {
+        var c = "FAIL";
+        (function() {
+            a = 42,
+            ((a <<= 0) && (a[(c = "PASS", 0)] = 0));
+            var a;
+        })();
+        console.log(c);
+    }
+    expect_stdout: "PASS"
+}
+
 truthy_conditionals: {
     options = {
         conditionals: true,
index 4d243a8..a52796c 100644 (file)
@@ -358,6 +358,7 @@ inner_ref: {
 
 issue_2107: {
     options = {
+        assignments: true,
         collapse_vars: true,
         inline: true,
         passes: 3,
@@ -387,6 +388,7 @@ issue_2107: {
 
 issue_2114_1: {
     options = {
+        assignments: true,
         collapse_vars: true,
         if_return: true,
         inline: true,
@@ -419,6 +421,7 @@ issue_2114_1: {
 
 issue_2114_2: {
     options = {
+        assignments: true,
         collapse_vars: true,
         if_return: true,
         inline: true,
@@ -1223,6 +1226,7 @@ issue_2630_1: {
 
 issue_2630_2: {
     options = {
+        assignments: true,
         collapse_vars: true,
         inline: true,
         passes: 2,
@@ -1320,6 +1324,7 @@ issue_2630_4: {
 
 issue_2630_5: {
     options = {
+        assignments: true,
         collapse_vars: true,
         inline: true,
         reduce_vars: true,