From: Alex Lam S.L Date: Wed, 5 Apr 2017 13:06:42 +0000 (+0800) Subject: implement delayed resolution for `reduce_vars` (#1788) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=ff289b90a92739641dcb7fc7f6c8ecf8ee74d15f;p=UglifyJS.git implement delayed resolution for `reduce_vars` (#1788) Although it would be nice to enforce `AST_Node` cloning during transformation, that ship has sailed a long time ago. We now get the assigned value when resolving `AST_SymbolRef` instead of `reset_opt_flags()`, which has the added advantage of improved compressor efficiency. fixes #1787 --- diff --git a/lib/compress.js b/lib/compress.js index 14083fe8..ef7f0441 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -273,7 +273,7 @@ merge(Compressor.prototype, { var d = node.definition(); d.references.push(node); if (d.fixed === undefined || !is_safe(d) - || is_modified(node, 0, d.fixed instanceof AST_Lambda)) { + || is_modified(node, 0, node.fixed_value() instanceof AST_Lambda)) { d.fixed = false; } } @@ -283,7 +283,9 @@ merge(Compressor.prototype, { if (node instanceof AST_VarDef) { var d = node.name.definition(); if (d.fixed == null) { - d.fixed = node.value; + d.fixed = node.value && function() { + return node.value; + }; mark_as_safe(d); } else if (node.value) { d.fixed = false; @@ -314,7 +316,9 @@ merge(Compressor.prototype, { // So existing transformation rules can work on them. node.argnames.forEach(function(arg, i) { var d = arg.definition(); - d.fixed = iife.args[i] || make_node(AST_Undefined, iife); + d.fixed = function() { + return iife.args[i] || make_node(AST_Undefined, iife); + }; mark_as_safe(d); }); } @@ -409,6 +413,12 @@ merge(Compressor.prototype, { } }); + AST_SymbolRef.DEFMETHOD("fixed_value", function() { + var fixed = this.definition().fixed; + if (!fixed || fixed instanceof AST_Node) return fixed; + return fixed(); + }); + function find_variable(compressor, name) { var scope, i = 0; while (scope = compressor.parent(i++)) { @@ -1487,15 +1497,15 @@ merge(Compressor.prototype, { if (this._evaluating) throw def; this._evaluating = true; try { - var d = this.definition(); - if (compressor.option("reduce_vars") && d.fixed) { + var fixed = this.fixed_value(); + if (compressor.option("reduce_vars") && fixed) { if (compressor.option("unsafe")) { - if (!HOP(d.fixed, "_evaluated")) { - d.fixed._evaluated = ev(d.fixed, compressor); + if (!HOP(fixed, "_evaluated")) { + fixed._evaluated = ev(fixed, compressor); } - return d.fixed._evaluated; + return fixed._evaluated; } - return ev(d.fixed, compressor); + return ev(fixed, compressor); } } finally { this._evaluating = false; @@ -2689,11 +2699,12 @@ merge(Compressor.prototype, { if (compressor.option("reduce_vars") && exp instanceof AST_SymbolRef) { var def = exp.definition(); - if (def.fixed instanceof AST_Defun) { - def.fixed = make_node(AST_Function, def.fixed, def.fixed).clone(true); + var fixed = exp.fixed_value(); + if (fixed instanceof AST_Defun) { + def.fixed = fixed = make_node(AST_Function, fixed, fixed).clone(true); } - if (def.fixed instanceof AST_Function) { - exp = def.fixed; + if (fixed instanceof AST_Function) { + exp = fixed; if (compressor.option("unused") && def.references.length == 1 && !(def.scope.uses_arguments @@ -3080,7 +3091,7 @@ merge(Compressor.prototype, { && (e.operator == "*" || e.operator == "/" || e.operator == "%")) { self.expression = e.left; e.left = self; - return e.optimize(compressor); + return e; } // avoids infinite recursion of numerals if (self.operator != "-" @@ -3511,12 +3522,13 @@ merge(Compressor.prototype, { } if (compressor.option("evaluate") && compressor.option("reduce_vars")) { var d = self.definition(); - if (d.fixed) { + var fixed = self.fixed_value(); + if (fixed) { if (d.should_replace === undefined) { - var init = d.fixed.evaluate(compressor); - if (init !== d.fixed) { - init = make_node_from_constant(init, d.fixed).optimize(compressor); - init = best_of_expression(init, d.fixed); + var init = fixed.evaluate(compressor); + if (init !== fixed) { + init = make_node_from_constant(init, fixed).optimize(compressor); + init = best_of_expression(init, fixed); var value = init.print_to_string().length; var name = d.name.length; var freq = d.references.length; diff --git a/test/compress/issue-1609.js b/test/compress/issue-1609.js index 577a3ee1..da4b54a2 100644 --- a/test/compress/issue-1609.js +++ b/test/compress/issue-1609.js @@ -45,11 +45,10 @@ chained_evaluation_2: { } expect: { (function() { - var a = "long piece of string"; (function() { - var c; - c = f(a); - c.bar = a; + var c, b = "long piece of string"; + c = f(b); + c.bar = b; })(); })(); } diff --git a/test/compress/issue-1787.js b/test/compress/issue-1787.js new file mode 100644 index 00000000..43d1f1be --- /dev/null +++ b/test/compress/issue-1787.js @@ -0,0 +1,19 @@ +unary_prefix: { + options = { + evaluate: true, + reduce_vars: true, + unused: true, + } + input: { + console.log(function() { + var x = -(2 / 3); + return x; + }()); + } + expect: { + console.log(function() { + return -2 / 3; + }()); + } + expect_stdout: true +}