improve `compress` performance (#3348)
authorAlex Lam S.L <alexlamsl@gmail.com>
Tue, 19 Mar 2019 18:53:04 +0000 (02:53 +0800)
committerGitHub <noreply@github.com>
Tue, 19 Mar 2019 18:53:04 +0000 (02:53 +0800)
fixes #3174

lib/compress.js
test/mocha/cli.js

index 69a900c..4e27652 100644 (file)
@@ -1101,10 +1101,10 @@ merge(Compressor.prototype, {
                     stop_if_hit = parent;
                 }
                 // Replace variable with assignment when found
-                var hit_lhs, hit_rhs;
+                var hit_rhs;
                 if (can_replace
                     && !(node instanceof AST_SymbolDeclaration)
-                    && (scan_lhs && (hit_lhs = lhs.equivalent_to(node))
+                    && (scan_lhs && lhs.equivalent_to(node)
                         || scan_rhs && (hit_rhs = scan_rhs(node, this)))) {
                     if (stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) {
                         abort = true;
@@ -1194,10 +1194,9 @@ merge(Compressor.prototype, {
                     var stop_after = null;
                     var stop_if_hit = null;
                     var lhs = get_lhs(candidate);
-                    var rhs = get_rhs(candidate);
                     var side_effects = lhs && lhs.has_side_effects(compressor);
                     var scan_lhs = lhs && !side_effects && !is_lhs_read_only(lhs);
-                    var scan_rhs = rhs && foldable(rhs);
+                    var scan_rhs = foldable(get_rhs(candidate));
                     if (!scan_lhs && !scan_rhs) continue;
                     // Locate symbols which may execute code outside of scanning range
                     var lvalues = get_lvalues(candidate);
@@ -1524,6 +1523,7 @@ merge(Compressor.prototype, {
             }
 
             function foldable(expr) {
+                if (!expr) return false;
                 if (expr instanceof AST_SymbolRef) {
                     var value = expr.evaluate(compressor);
                     if (value === expr) return rhs_exact_match;
@@ -1545,10 +1545,10 @@ merge(Compressor.prototype, {
                     }
                 }));
                 return !circular && rhs_exact_match;
-            }
 
-            function rhs_exact_match(node) {
-                return rhs.equivalent_to(node);
+                function rhs_exact_match(node) {
+                    return expr.equivalent_to(node);
+                }
             }
 
             function rhs_fuzzy_match(value, fallback) {
@@ -4073,19 +4073,6 @@ merge(Compressor.prototype, {
         def(AST_Function, function(compressor) {
             return this.name && compressor.option("ie8") ? this : null;
         });
-        def(AST_Unary, function(compressor, first_in_statement) {
-            if (unary_side_effects[this.operator]) {
-                this.write_only = !this.expression.has_side_effects(compressor);
-                return this;
-            }
-            if (this.operator == "typeof" && this.expression instanceof AST_SymbolRef) return null;
-            var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
-            if (first_in_statement && expression && is_iife_call(expression)) {
-                if (expression === this.expression && this.operator == "!") return this;
-                return expression.negate(compressor, first_in_statement);
-            }
-            return expression;
-        });
         def(AST_Object, function(compressor, first_in_statement) {
             var values = trim(this.properties, compressor, first_in_statement);
             return values && make_sequence(this, values);
@@ -4110,9 +4097,27 @@ merge(Compressor.prototype, {
             return make_sequence(this, [ expression, property ]);
         });
         def(AST_SymbolRef, function(compressor) {
-            return this.is_declared(compressor) ? null : this;
+            if (!this.is_declared(compressor)) return this;
+            this.definition().replaced++;
+            return null;
         });
         def(AST_This, return_null);
+        def(AST_Unary, function(compressor, first_in_statement) {
+            if (unary_side_effects[this.operator]) {
+                this.write_only = !this.expression.has_side_effects(compressor);
+                return this;
+            }
+            if (this.operator == "typeof" && this.expression instanceof AST_SymbolRef) {
+                this.expression.definition().replaced++;
+                return null;
+            }
+            var expression = this.expression.drop_side_effect_free(compressor, first_in_statement);
+            if (first_in_statement && expression && is_iife_call(expression)) {
+                if (expression === this.expression && this.operator == "!") return this;
+                return expression.negate(compressor, first_in_statement);
+            }
+            return expression;
+        });
     })(function(node, func) {
         node.DEFMETHOD("drop_side_effect_free", func);
     });
@@ -4607,9 +4612,7 @@ merge(Compressor.prototype, {
     });
 
     OPT(AST_Definitions, function(self, compressor) {
-        if (self.definitions.length == 0)
-            return make_node(AST_EmptyStatement, self);
-        return self;
+        return self.definitions.length ? self : make_node(AST_EmptyStatement, self);
     });
 
     AST_Call.DEFMETHOD("lift_sequences", function(compressor) {
@@ -5855,6 +5858,7 @@ merge(Compressor.prototype, {
                     value = fixed.optimize(compressor);
                     if (value === fixed) value = fixed.clone(true);
                 }
+                def.replaced++;
                 return value;
             }
             if (fixed && def.should_replace === undefined) {
@@ -5897,7 +5901,9 @@ merge(Compressor.prototype, {
                 }
             }
             if (def.should_replace) {
-                return def.should_replace();
+                var value = def.should_replace();
+                def.replaced++;
+                return value;
             }
         }
         return self;
index af537f3..811c493 100644 (file)
@@ -1,6 +1,7 @@
 var assert = require("assert");
 var exec = require("child_process").exec;
 var fs = require("fs");
+var run_code = require("../sandbox").run_code;
 
 function read(path) {
     return fs.readFileSync(path, "utf8");
@@ -714,4 +715,32 @@ describe("bin/uglifyjs", function() {
             done();
         });
     });
+    it("Should compress swarm of unused variables with reasonable performance", function(done) {
+        var code = [
+            "console.log(function() {",
+        ];
+        for (var i = 0; i < 10000; i++) {
+            code.push("var obj" + i + " = {p: " + i + "};");
+        }
+        code.push("var map = {");
+        for (var i = 0; i < 10000; i++) {
+            code.push("obj" + i + ": obj" + i + ",");
+        }
+        code = code.concat([
+            "};",
+            "return obj25.p + obj121.p + obj1024.p;",
+            "}());",
+        ]).join("\n");
+        exec(uglifyjscmd + " -mc", function(err, stdout) {
+            if (err) throw err;
+            assert.strictEqual(stdout, [
+                "console.log(function(){",
+                "var p={p:25},n={p:121},o={p:1024};",
+                "return p.p+n.p+o.p",
+                "}());\n",
+            ].join(""));
+            assert.strictEqual(run_code(stdout), run_code(code));
+            done();
+        }).stdin.end(code);
+    });
 });