improve `compress` efficiency (#5220)
authorAlex Lam S.L <alexlamsl@gmail.com>
Wed, 15 Dec 2021 00:08:24 +0000 (00:08 +0000)
committerGitHub <noreply@github.com>
Wed, 15 Dec 2021 00:08:24 +0000 (08:08 +0800)
lib/compress.js
test/compress/arrows.js
test/compress/classes.js
test/compress/drop-unused.js
test/compress/functions.js
test/compress/negate-iife.js
test/compress/rests.js

index 3102d76..ef84a75 100644 (file)
@@ -7929,12 +7929,17 @@ Compressor.prototype.compress = function(node) {
         function drop_returns(compressor, exp) {
             var arrow = is_arrow(exp);
             var async = is_async(exp);
+            var changed = false;
             var drop_body = false;
             if (arrow && compressor.option("arrows")) {
                 if (!exp.value) {
                     drop_body = true;
                 } else if (!async || is_primitive(compressor, exp.value)) {
-                    exp.value = exp.value.drop_side_effect_free(compressor);
+                    var dropped = exp.value.drop_side_effect_free(compressor);
+                    if (dropped !== exp.value) {
+                        changed = true;
+                        exp.value = dropped;
+                    }
                 }
             } else if (exp instanceof AST_AsyncFunction || exp instanceof AST_Function) {
                 if (exp.name) {
@@ -7951,6 +7956,7 @@ Compressor.prototype.compress = function(node) {
                         if (async && !is_primitive(compressor, value)) return node;
                         value = value.drop_side_effect_free(compressor, true);
                     }
+                    changed = true;
                     if (!value) return make_node(AST_EmptyStatement, node);
                     return make_node(AST_SimpleStatement, node, { body: value });
                 });
@@ -7958,7 +7964,11 @@ Compressor.prototype.compress = function(node) {
                     var value = node.value;
                     if (value) {
                         if (async && !is_primitive(compressor, value)) return;
-                        node.value = value.drop_side_effect_free(compressor);
+                        var dropped = value.drop_side_effect_free(compressor);
+                        if (dropped !== value) {
+                            changed = true;
+                            node.value = dropped;
+                        }
                     }
                 });
             }
@@ -7967,6 +7977,7 @@ Compressor.prototype.compress = function(node) {
                     var body = node.body;
                     if (body instanceof AST_Await) {
                         if (is_primitive(compressor, body.expression)) {
+                            changed = true;
                             body = body.expression.drop_side_effect_free(compressor, true);
                             if (!body) return make_node(AST_EmptyStatement, node);
                             node.body = body;
@@ -7976,8 +7987,10 @@ Compressor.prototype.compress = function(node) {
                         for (var i = exprs.length; --i >= 0;) {
                             var tail = exprs[i];
                             if (!(tail instanceof AST_Await)) break;
-                            if (!is_primitive(compressor, tail.expression)) break;
-                            if (exprs[i] = tail.expression.drop_side_effect_free(compressor)) break;
+                            var value = tail.expression;
+                            if (!is_primitive(compressor, value)) break;
+                            changed = true;
+                            if (exprs[i] = value.drop_side_effect_free(compressor)) break;
                         }
                         switch (i) {
                           case -1:
@@ -8021,7 +8034,7 @@ Compressor.prototype.compress = function(node) {
                     return make_node(ctor, exp, exp);
                 }
             }
-            return drop_body && exp.clone();
+            return changed && exp.clone();
         }
         def(AST_Call, function(compressor, first_in_statement) {
             var self = this;
@@ -10369,42 +10382,46 @@ Compressor.prototype.compress = function(node) {
     OPT(AST_UnaryPrefix, function(self, compressor) {
         var op = self.operator;
         var exp = self.expression;
-        if (compressor.option("evaluate") && op == "delete" && !may_not_delete(exp)) {
-            return make_sequence(self, [ exp, make_node(AST_True, self) ]).optimize(compressor);
-        }
         if (compressor.option("sequences") && can_lift()) {
             var seq = lift_sequence_in_expression(self, compressor);
             if (seq !== self) return seq.optimize(compressor);
         }
-        if (compressor.option("side_effects") && op == "void") {
-            exp = exp.drop_side_effect_free(compressor);
-            if (!exp) return make_node(AST_Undefined, self).optimize(compressor);
-            self.expression = exp;
-            return self;
-        }
-        if (compressor.option("booleans")) {
-            if (op == "!" && exp.is_truthy()) {
-                return make_sequence(self, [ exp, make_node(AST_False, self) ]).optimize(compressor);
-            } else if (compressor.in_boolean_context()) switch (op) {
-              case "!":
-                if (exp instanceof AST_UnaryPrefix && exp.operator == "!") {
-                    // !!foo ---> foo, if we're in boolean context
-                    return exp.expression;
-                }
+        switch (op) {
+          case "-":
+            if (exp instanceof AST_Infinity) exp = exp.transform(compressor);
+            // avoids infinite recursion of numerals
+            if (exp instanceof AST_Number || exp instanceof AST_Infinity) return self;
+            break;
+          case "!":
+            if (!compressor.option("booleans")) break;
+            if (exp.is_truthy()) return make_sequence(self, [ exp, make_node(AST_False, self) ]).optimize(compressor);
+            if (compressor.in_boolean_context()) {
+                // !!foo ---> foo, if we're in boolean context
+                if (exp instanceof AST_UnaryPrefix && exp.operator == "!") return exp.expression;
                 if (exp instanceof AST_Binary) {
                     self = best_of(compressor, self, exp.negate(compressor, first_in_statement(compressor)));
                 }
-                break;
-              case "typeof":
-                // typeof always returns a non-empty string, thus it's
-                // always true in booleans
-                AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
-                var exprs = [ make_node(AST_True, self) ];
-                if (!(exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor))) exprs.unshift(exp);
-                return make_sequence(self, exprs).optimize(compressor);
             }
+            break;
+          case "delete":
+            if (!compressor.option("evaluate")) break;
+            if (may_not_delete(exp)) break;
+            return make_sequence(self, [ exp, make_node(AST_True, self) ]).optimize(compressor);
+          case "typeof":
+            if (!compressor.option("booleans")) break;
+            if (!compressor.in_boolean_context()) break;
+            // typeof always returns a non-empty string, thus always truthy
+            AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
+            var exprs = [ make_node(AST_True, self) ];
+            if (!(exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor))) exprs.unshift(exp);
+            return make_sequence(self, exprs).optimize(compressor);
+          case "void":
+            if (!compressor.option("side_effects")) break;
+            exp = exp.drop_side_effect_free(compressor);
+            if (!exp) return make_node(AST_Undefined, self).optimize(compressor);
+            self.expression = exp;
+            return self;
         }
-        if (op == "-" && exp instanceof AST_Infinity) exp = exp.transform(compressor);
         if (compressor.option("evaluate")
             && exp instanceof AST_Binary
             && SIGN_OPS[op]
@@ -10414,14 +10431,12 @@ Compressor.prototype.compress = function(node) {
                 operator: exp.operator,
                 left: make_node(AST_UnaryPrefix, exp.left, {
                     operator: op,
-                    expression: exp.left
+                    expression: exp.left,
                 }),
-                right: exp.right
+                right: exp.right,
             });
         }
-        // avoids infinite recursion of numerals
-        return op == "-" && (exp instanceof AST_Number || exp instanceof AST_Infinity)
-            ? self : try_evaluate(compressor, self);
+        return try_evaluate(compressor, self);
 
         function may_not_delete(node) {
             return node instanceof AST_Infinity
@@ -10582,32 +10597,6 @@ Compressor.prototype.compress = function(node) {
     }
 
     OPT(AST_Binary, function(self, compressor) {
-        function reversible() {
-            return self.left.is_constant()
-                || self.right.is_constant()
-                || !self.left.has_side_effects(compressor)
-                    && !self.right.has_side_effects(compressor);
-        }
-        function reverse(op) {
-            if (reversible()) {
-                if (op) self.operator = op;
-                var tmp = self.left;
-                self.left = self.right;
-                self.right = tmp;
-            }
-        }
-        function swap_chain() {
-            var rhs = self.right;
-            self.left = make_node(AST_Binary, self, {
-                operator: self.operator,
-                left: self.left,
-                right: rhs.left,
-                start: self.left.start,
-                end: rhs.left.end
-            });
-            self.right = rhs.right;
-            self.left = self.left.transform(compressor);
-        }
         if (commutativeOperators[self.operator]
             && self.right.is_constant()
             && !self.left.is_constant()
@@ -11260,6 +11249,35 @@ Compressor.prototype.compress = function(node) {
                         && self.left.expression instanceof AST_Number && self.left.expression.value == 1;
             }
         }
+
+        function reversible() {
+            return self.left.is_constant()
+                || self.right.is_constant()
+                || !self.left.has_side_effects(compressor)
+                    && !self.right.has_side_effects(compressor);
+        }
+
+        function reverse(op) {
+            if (reversible()) {
+                if (op) self.operator = op;
+                var tmp = self.left;
+                self.left = self.right;
+                self.right = tmp;
+            }
+        }
+
+        function swap_chain() {
+            var rhs = self.right;
+            self.left = make_node(AST_Binary, self, {
+                operator: self.operator,
+                left: self.left,
+                right: rhs.left,
+                start: self.left.start,
+                end: rhs.left.end
+            });
+            self.right = rhs.right;
+            self.left = self.left.transform(compressor);
+        }
     });
 
     OPT(AST_SymbolExport, function(self) {
@@ -11593,9 +11611,7 @@ Compressor.prototype.compress = function(node) {
         if (lhs && is_atomic(lhs, self)) return self;
         return make_node(AST_UnaryPrefix, self, {
             operator: "void",
-            expression: make_node(AST_Number, self, {
-                value: 0
-            })
+            expression: make_node(AST_Number, self, { value: 0 }),
         });
     });
 
@@ -11607,12 +11623,8 @@ Compressor.prototype.compress = function(node) {
         }
         return make_node(AST_Binary, self, {
             operator: "/",
-            left: make_node(AST_Number, self, {
-                value: 1
-            }),
-            right: make_node(AST_Number, self, {
-                value: 0
-            })
+            left: make_node(AST_Number, self, { value: 1 }),
+            right: make_node(AST_Number, self, { value: 0 }),
         });
     });
 
@@ -11622,12 +11634,8 @@ Compressor.prototype.compress = function(node) {
         if (!lhs && !find_scope(compressor).find_variable("NaN")) return self;
         return make_node(AST_Binary, self, {
             operator: "/",
-            left: make_node(AST_Number, self, {
-                value: 0
-            }),
-            right: make_node(AST_Number, self, {
-                value: 0
-            })
+            left: make_node(AST_Number, self, { value: 0 }),
+            right: make_node(AST_Number, self, { value: 0 }),
         });
     });
 
@@ -12199,9 +12207,7 @@ Compressor.prototype.compress = function(node) {
 
     OPT(AST_Boolean, function(self, compressor) {
         if (!compressor.option("booleans")) return self;
-        if (compressor.in_boolean_context()) return make_node(AST_Number, self, {
-            value: +self.value
-        });
+        if (compressor.in_boolean_context()) return make_node(AST_Number, self, { value: +self.value });
         var p = compressor.parent();
         if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) {
             AST_Node.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
@@ -12211,15 +12217,11 @@ Compressor.prototype.compress = function(node) {
                 line     : p.start.line,
                 col      : p.start.col,
             });
-            return make_node(AST_Number, self, {
-                value: +self.value
-            });
+            return make_node(AST_Number, self, { value: +self.value });
         }
         return make_node(AST_UnaryPrefix, self, {
             operator: "!",
-            expression: make_node(AST_Number, self, {
-                value: 1 - self.value
-            })
+            expression: make_node(AST_Number, self, { value: 1 - self.value }),
         });
     });
 
index 54886d5..709fc63 100644 (file)
@@ -508,7 +508,7 @@ drop_value: {
         ((a, b) => a + b)(console.log(42));
     }
     expect: {
-        ((a, b) => {})(console.log(42));
+        void console.log(42);
     }
     expect_stdout: "42"
     node_version: ">=4"
index 2ac97b0..1b1aea1 100644 (file)
@@ -890,7 +890,7 @@ keep_fnames: {
 issue_805_1: {
     options = {
         inline: true,
-        passes: 2,
+        passes: 3,
         pure_getters: "strict",
         reduce_vars: true,
         sequences: true,
@@ -926,7 +926,7 @@ issue_805_1: {
 issue_805_2: {
     options = {
         inline: true,
-        passes: 2,
+        passes: 3,
         pure_getters: "strict",
         reduce_vars: true,
         sequences: true,
index 7873f4f..be28ebe 100644 (file)
@@ -1765,7 +1765,7 @@ issue_2846: {
 issue_805_1: {
     options = {
         inline: true,
-        passes: 2,
+        passes: 3,
         pure_getters: "strict",
         reduce_vars: true,
         sequences: true,
@@ -1798,7 +1798,7 @@ issue_805_1: {
 issue_805_2: {
     options = {
         inline: true,
-        passes: 2,
+        passes: 3,
         pure_getters: "strict",
         reduce_vars: true,
         sequences: true,
index 8cf776f..2841f9e 100644 (file)
@@ -3862,6 +3862,7 @@ issue_3679_1: {
     options = {
         collapse_vars: true,
         inline: true,
+        passes: 2,
         pure_getters: "strict",
         reduce_vars: true,
         side_effects: true,
index 620c098..813b906 100644 (file)
@@ -389,6 +389,7 @@ issue_1288_side_effects: {
     options = {
         conditionals: true,
         negate_iife: true,
+        sequences: true,
         side_effects: true,
     }
     input: {
@@ -409,10 +410,10 @@ issue_1288_side_effects: {
             })(0);
     }
     expect: {
-        w;
+        w,
         x || function() {
             x = {};
-        }();
+        }(),
         y;
     }
 }
index f377f63..ce27b6e 100644 (file)
@@ -866,9 +866,9 @@ issue_4575: {
     }
     expect: {
         (function(a) {
-            (function a(...d) {
+            (function(d) {
                 console.log(d.length);
-            })();
+            })([]);
         })();
     }
     expect_stdout: "0"