From: Alex Lam S.L Date: Tue, 19 Mar 2019 18:53:04 +0000 (+0800) Subject: improve `compress` performance (#3348) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=b9615f7a62c8d9fb5721b1699d2f4f3f14bcc5be;p=UglifyJS.git improve `compress` performance (#3348) fixes #3174 --- diff --git a/lib/compress.js b/lib/compress.js index 69a900cd..4e276528 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -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; diff --git a/test/mocha/cli.js b/test/mocha/cli.js index af537f37..811c493f 100644 --- a/test/mocha/cli.js +++ b/test/mocha/cli.js @@ -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); + }); });