From: Alex Lam S.L Date: Wed, 1 Mar 2017 16:20:53 +0000 (+0800) Subject: fix corner cases in `reduce_vars` (#1524) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=7aa69117e1a38e4aeead13100ac952ca99dbb07f;p=UglifyJS.git fix corner cases in `reduce_vars` (#1524) Avoid variable substitution in the following cases: - use of variable before declaration - declaration within conditional code blocks - declaration within loop body fixes #1518 fixes #1525 --- diff --git a/lib/compress.js b/lib/compress.js index 32e7a649..24550973 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -179,38 +179,105 @@ merge(Compressor.prototype, { AST_Node.DEFMETHOD("reset_opt_flags", function(compressor, rescan){ var reduce_vars = rescan && compressor.option("reduce_vars"); - var unsafe = compressor.option("unsafe"); + var safe_ids = []; + push(); var tw = new TreeWalker(function(node){ + if (!(node instanceof AST_Directive || node instanceof AST_Constant)) { + node._squeezed = false; + node._optimized = false; + } 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.fixed && (d.orig.length > 1 || isModified(node, 0))) { + if (!d.fixed || isModified(node, 0) || !is_safe(d)) { + d.fixed = false; + } + } + if (node instanceof AST_VarDef) { + var d = node.name.definition(); + if (d.fixed === undefined) { + d.fixed = node.value || make_node(AST_Undefined, node); + mark_as_safe(d); + } else { d.fixed = false; } } - 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); + var iife; + if (node instanceof AST_Function + && (iife = tw.parent()) instanceof AST_Call + && iife.expression === node) { + node.argnames.forEach(function(arg, i) { + var d = arg.definition(); + d.fixed = iife.args[i] || make_node(AST_Undefined, iife); + mark_as_safe(d); }); } - } - if (!(node instanceof AST_Directive || node instanceof AST_Constant)) { - node._squeezed = false; - node._optimized = false; + if (node instanceof AST_If || node instanceof AST_DWLoop) { + node.condition.walk(tw); + push(); + node.body.walk(tw); + pop(); + if (node.alternative) { + push(); + node.alternative.walk(tw); + pop(); + } + return true; + } + if (node instanceof AST_LabeledStatement) { + push(); + node.body.walk(tw); + pop(); + return true; + } + if (node instanceof AST_For) { + if (node.init) node.init.walk(tw); + push(); + if (node.condition) node.condition.walk(tw); + node.body.walk(tw); + if (node.step) node.step.walk(tw); + pop(); + return true; + } + if (node instanceof AST_ForIn) { + if (node.init instanceof AST_SymbolRef) { + node.init.definition().fixed = false; + } + node.object.walk(tw); + push(); + node.body.walk(tw); + pop(); + return true; + } } }); this.walk(tw); + function mark_as_safe(def) { + safe_ids[safe_ids.length - 1][def.id] = true; + } + + function is_safe(def) { + for (var i = safe_ids.length, id = def.id; --i >= 0;) { + if (safe_ids[i][id]) return true; + } + } + + function push() { + safe_ids.push(Object.create(null)); + } + + function pop() { + safe_ids.pop(); + } + function reset_def(def) { - def.fixed = true; + def.fixed = undefined; def.references = []; def.should_replace = undefined; - if (unsafe && def.init) { - def.init._evaluated = undefined; - } } function isModified(node, level) { @@ -1263,14 +1330,14 @@ merge(Compressor.prototype, { this._evaluating = true; try { var d = this.definition(); - if (compressor.option("reduce_vars") && d.fixed && d.init) { + if (compressor.option("reduce_vars") && d.fixed) { if (compressor.option("unsafe")) { - if (d.init._evaluated === undefined) { - d.init._evaluated = ev(d.init, compressor); + if (!HOP(d.fixed, "_evaluated")) { + d.fixed._evaluated = ev(d.fixed, compressor); } - return d.init._evaluated; + return d.fixed._evaluated; } - return ev(d.init, compressor); + return ev(d.fixed, compressor); } } finally { this._evaluating = false; @@ -3046,9 +3113,9 @@ merge(Compressor.prototype, { } if (compressor.option("evaluate") && compressor.option("reduce_vars")) { var d = self.definition(); - if (d.fixed && d.init) { + if (d.fixed) { if (d.should_replace === undefined) { - var init = d.init.evaluate(compressor); + var init = d.fixed.evaluate(compressor); if (init.length > 1) { var value = init[0].print_to_string().length; var name = d.name.length; diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index 0ee201c0..e38c317b 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -470,3 +470,122 @@ multi_def_2: { var repeatLength = this.getBits(bitsLength) + bitsOffset; } } + +use_before_var: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + console.log(t); + var t = 1; + } + expect: { + console.log(t); + var t = 1; + } +} + +inner_var_if: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + function f(){ + return 0; + } + if (f()) + var t = 1; + if (!t) + console.log(t); + } + expect: { + function f(){ + return 0; + } + if (f()) + var t = 1; + if (!t) + console.log(t); + } +} + +inner_var_label: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + function f(){ + return 1; + } + l: { + if (f()) break l; + var t = 1; + } + console.log(t); + } + expect: { + function f(){ + return 1; + } + l: { + if (f()) break l; + var t = 1; + } + console.log(t); + } +} + +inner_var_for: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + var a = 1; + x(a, b, d); + for (var b = 2, c = 3; x(a, b, c, d); x(a, b, c, d)) { + var d = 4, e = 5; + x(a, b, c, d, e); + } + x(a, b, c, d, e) + } + expect: { + var a = 1; + x(1, b, d); + for (var b = 2, c = 3; x(1, b, 3, d); x(1, b, 3, d)) { + var d = 4, e = 5; + x(1, b, 3, d, e); + } + x(1, b, 3, d, e); + } +} + +inner_var_for_in: { + options = { + evaluate: true, + reduce_vars: true, + } + input: { + var a = 1, b = 2; + for (b in (function() { + return x(a, b, c); + })()) { + var c = 3, d = 4; + x(a, b, c, d); + } + x(a, b, c, d); + } + expect: { + var a = 1, b = 2; + for (b in (function() { + return x(1, b, c); + })()) { + var c = 3, d = 4; + x(1, b, c, d); + } + x(1, b, c, d); + } +}