From 16cd5d57a5cf7f5750104df0e5af246708fd493f Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sun, 26 Feb 2017 00:40:33 +0800 Subject: [PATCH] consolidate `evaluate` & `reduce_vars` (#1505) - improve marking efficiency - apply smarter `const` replacement to `var` fixes #1501 --- lib/compress.js | 81 +++++++++++++++------------ lib/scope.js | 2 - test/compress/evaluate.js | 2 +- test/compress/reduce_vars.js | 16 +++--- test/mocha/comment_before_constant.js | 6 +- 5 files changed, 57 insertions(+), 50 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 2bc1c5a5..ccd6e23c 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -118,7 +118,7 @@ merge(Compressor.prototype, { var passes = +this.options.passes || 1; for (var pass = 0; pass < passes && pass < 3; ++pass) { if (pass > 0 || this.option("reduce_vars")) - node.reset_opt_flags(this); + node.reset_opt_flags(this, true); node = node.transform(this); } return node; @@ -177,28 +177,26 @@ merge(Compressor.prototype, { return this.print_to_string() == node.print_to_string(); }); - AST_Node.DEFMETHOD("reset_opt_flags", function(compressor){ - var reduce_vars = compressor.option("reduce_vars"); + AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan){ + var reduce_vars = rescan && compressor.option("reduce_vars"); + var unsafe = compressor.option("unsafe"); var tw = new TreeWalker(function(node){ - if (reduce_vars && node instanceof AST_Scope) { - node.variables.each(function(def) { - delete def.modified; - }); - } - if (node instanceof AST_SymbolRef) { - var d = node.definition(); - if (d.init) { - delete d.init._evaluated; + if (reduce_vars) { + if (node instanceof AST_Toplevel) node.globals.each(reset_def); + if (node instanceof AST_Scope) node.variables.each(reset_def); + if (node instanceof AST_SymbolRef) { + var d = node.definition(); + d.references.push(node); + if (!d.modified && (d.orig.length > 1 || isModified(node, 0))) { + d.modified = true; + } } - if (reduce_vars && (d.orig.length > 1 || isModified(node, 0))) { - d.modified = true; + if (node instanceof AST_Call && node.expression instanceof AST_Function) { + node.expression.argnames.forEach(function(arg, i) { + arg.definition().init = node.args[i] || make_node(AST_Undefined, node); + }); } } - if (reduce_vars && node instanceof AST_Call && node.expression instanceof AST_Function) { - node.expression.argnames.forEach(function(arg, i) { - arg.definition().init = node.args[i] || make_node(AST_Undefined, node); - }); - } if (!(node instanceof AST_Directive || node instanceof AST_Constant)) { node._squeezed = false; node._optimized = false; @@ -206,10 +204,18 @@ merge(Compressor.prototype, { }); this.walk(tw); + function reset_def(def) { + def.modified = false; + def.references = []; + def.should_replace = undefined; + if (unsafe && def.init) { + def.init._evaluated = undefined; + } + } + function isModified(node, level) { var parent = tw.parent(level); - if (parent instanceof AST_Unary && (parent.operator === "++" || parent.operator === "--") - || parent instanceof AST_Assign && parent.left === node + if (isLHS(node, parent) || parent instanceof AST_Call && parent.expression === node) { return true; } else if (parent instanceof AST_PropAccess && parent.expression === node) { @@ -1254,7 +1260,7 @@ merge(Compressor.prototype, { var d = this.definition(); if (compressor.option("reduce_vars") && !d.modified && d.init) { if (compressor.option("unsafe")) { - if (!HOP(d.init, '_evaluated')) { + if (d.init._evaluated === undefined) { d.init._evaluated = ev(d.init, compressor); } return d.init._evaluated; @@ -2403,9 +2409,6 @@ merge(Compressor.prototype, { }); OPT(AST_Call, function(self, compressor){ - self.args = self.args.map(function(arg) { - return arg.evaluate(compressor)[0]; - }); if (compressor.option("unsafe")) { var exp = self.expression; if (exp instanceof AST_SymbolRef && exp.undeclared()) { @@ -3025,18 +3028,24 @@ merge(Compressor.prototype, { return make_node(AST_Infinity, self).transform(compressor); } } - if (compressor.option("evaluate") - && compressor.option("reduce_vars") - && !isLHS(self, compressor.parent())) { + if (compressor.option("evaluate") && compressor.option("reduce_vars")) { var d = self.definition(); - if (d.constant && !d.modified && d.init && d.init.is_constant(compressor)) { - var original_as_string = self.print_to_string(); - var const_node = make_node_from_constant(compressor, d.init.constant_value(compressor), self); - var const_node_as_string = const_node.print_to_string(); - var per_const_overhead = d.global || !d.references.length ? 0 - : (d.name.length + 2 + const_node_as_string.length) / d.references.length; - if (const_node_as_string.length <= original_as_string.length + per_const_overhead) - return const_node; + if (!d.modified && d.init) { + if (d.should_replace === undefined) { + var init = d.init.evaluate(compressor); + if (init.length > 1) { + var value = init[0].print_to_string().length; + var name = d.name.length; + var freq = d.references.length; + var overhead = d.global || !freq ? 0 : (name + 2 + value) / freq; + d.should_replace = value <= name + overhead ? init[0] : false; + } else { + d.should_replace = false; + } + } + if (d.should_replace) { + return d.should_replace; + } } } return self; diff --git a/lib/scope.js b/lib/scope.js index 29e4103a..ae0c5777 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -51,7 +51,6 @@ function SymbolDef(scope, index, orig) { this.global = false; this.mangled_name = null; this.undeclared = false; - this.constant = false; this.index = index; this.id = SymbolDef.next_id++; }; @@ -156,7 +155,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){ else if (node instanceof AST_SymbolVar || node instanceof AST_SymbolConst) { var def = defun.def_variable(node); - def.constant = node instanceof AST_SymbolConst; def.init = tw.parent().value; } else if (node instanceof AST_SymbolCatch) { diff --git a/test/compress/evaluate.js b/test/compress/evaluate.js index ae5e58d6..5cefadc8 100644 --- a/test/compress/evaluate.js +++ b/test/compress/evaluate.js @@ -615,7 +615,7 @@ call_args: { const a = 1; console.log(1); +function(a) { - return a; + return 1; }(1); } } diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index d9d02efa..0ee201c0 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -134,8 +134,8 @@ modified: { } function f2() { - var b = 2, c = 3; - b = c; + var b = 2; + b = 3; console.log(1 + b); console.log(b + 3); console.log(4); @@ -143,8 +143,8 @@ modified: { } function f3() { - var b = 2, c = 3; - b *= c; + var b = 2; + b *= 3; console.log(1 + b); console.log(b + 3); console.log(4); @@ -236,7 +236,7 @@ unsafe_evaluate_object: { function f0(){ var a = 1; var b = {}; - b[a] = 2; + b[1] = 2; console.log(4); } @@ -280,7 +280,7 @@ unsafe_evaluate_array: { function f0(){ var a = 1; var b = []; - b[a] = 2; + b[1] = 2; console.log(4); } @@ -373,8 +373,8 @@ passes: { } expect: { function f() { - var b = 2, c = 3; - b = c; + var b = 2; + b = 3; console.log(1 + b); console.log(b + 3); console.log(4); diff --git a/test/mocha/comment_before_constant.js b/test/mocha/comment_before_constant.js index cfdb6da1..eaa8691c 100644 --- a/test/mocha/comment_before_constant.js +++ b/test/mocha/comment_before_constant.js @@ -7,7 +7,7 @@ describe("comment before constant", function() { it("Should test comment before constant is retained and output after mangle.", function() { var result = Uglify.minify(js, { fromString: true, - compress: { collapse_vars: false }, + compress: { collapse_vars: false, reduce_vars: false }, mangle: {}, output: { comments: true }, }); @@ -17,9 +17,9 @@ describe("comment before constant", function() { it("Should test code works when comments disabled.", function() { var result = Uglify.minify(js, { fromString: true, - compress: { collapse_vars: false }, + compress: { collapse_vars: false, reduce_vars: false }, mangle: {}, - output: {}, + output: { comments: false }, }); assert.strictEqual(result.code, 'function f(){var n=!1;return n}'); }); -- 2.34.1