From ad27c1420226032459157444cf7ac17bf95bd5e6 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Tue, 15 Sep 2020 21:43:01 +0100 Subject: fix corner cases in `merge_vars` (#4108) fixes #4107 fixes #4109 fixes #4110 fixes #4111 --- lib/compress.js | 131 +++++++++++++++++++++++--------------------- test/compress/merge_vars.js | 126 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+), 62 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 51f65268..52ed0e61 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -4309,15 +4309,16 @@ merge(Compressor.prototype, { AST_Scope.DEFMETHOD("merge_variables", function(compressor) { if (!compressor.option("merge_vars")) return; - var self = this, segment; + var self = this, segment = null; var first = [], last = [], index = 0; + var declarations = Object.create(null); var references = Object.create(null); var prev = Object.create(null); var tw = new TreeWalker(function(node, descend) { if (node instanceof AST_Assign) { - if (node.operator != "=") return; var sym = node.left; if (!(sym instanceof AST_SymbolRef)) return; + if (node.operator != "=") mark(sym); node.right.walk(tw); mark(sym, true); return true; @@ -4325,58 +4326,54 @@ merge(Compressor.prototype, { if (node instanceof AST_Binary) { if (!lazy_op[node.operator]) return; node.left.walk(tw); - var save = segment; - segment = node; + push(); node.right.walk(tw); - segment = save; + pop(); return true; } if (node instanceof AST_Conditional) { node.condition.walk(tw); - var save = segment; - segment = node.consequent; + push(); node.consequent.walk(tw); - segment = node.alternative; + pop(); + push(); node.alternative.walk(tw); - segment = save; + pop(); return true; } if (node instanceof AST_For) { if (node.init) node.init.walk(tw); - var save = segment; - segment = node; + push(); if (node.condition) node.condition.walk(tw); node.body.walk(tw); if (node.step) node.step.walk(tw); - segment = save; + pop(); return true; } if (node instanceof AST_ForIn) { node.object.walk(tw); - var save = segment; - segment = node; + push(); node.init.walk(tw); node.body.walk(tw); - segment = save; + pop(); return true; } if (node instanceof AST_If) { node.condition.walk(tw); - var save = segment; - segment = node; + push(); node.body.walk(tw); + pop(); if (node.alternative) { - segment = node.alternative; + push(); node.alternative.walk(tw); + pop(); } - segment = save; return true; } if (node instanceof AST_IterationStatement) { - var save = segment; - segment = node; + push(); descend(); - segment = save; + pop(); return true; } if (node instanceof AST_Scope) { @@ -4384,35 +4381,32 @@ merge(Compressor.prototype, { references[node.variables.get("arguments").id] = false; if (node.name) references[node.name.definition().id] = false; } - var save = segment; - segment = node; + push(); descend(); - segment = save; + pop(); return true; } if (node instanceof AST_Switch) { node.expression.walk(tw); - var save = segment, first = true; + var first = true; node.body.forEach(function(branch) { if (branch instanceof AST_Default) return; - if (first) { - first = false; - } else { - segment = branch.expression; - } + if (!first) push(); branch.expression.walk(tw); + if (!first) pop(); + first = false; }); node.body.forEach(function(branch) { - segment = branch; + push(); branch.body.forEach(function(stat) { stat.walk(tw); }); + pop(); }); - segment = save; return true; } if (node instanceof AST_SymbolFunarg) { - mark(node, true); + if (!node.__unused) mark(node, true); return true; } if (node instanceof AST_SymbolRef) { @@ -4420,16 +4414,19 @@ merge(Compressor.prototype, { return true; } if (node instanceof AST_Try) { - var save = segment; - segment = node; - node.body.forEach(function(branch) { - branch.walk(tw); + push(); + node.body.forEach(function(stat) { + stat.walk(tw); }); + pop(); if (node.bcatch) { - segment = node.bcatch; - node.bcatch.walk(tw); + references[node.bcatch.argname.definition().id] = false; + push(); + node.bcatch.body.forEach(function(stat) { + stat.walk(tw); + }); + pop(); } - segment = save; if (node.bfinally) node.bfinally.walk(tw); return true; } @@ -4441,9 +4438,14 @@ merge(Compressor.prototype, { return true; } if (node instanceof AST_VarDef) { - if (!node.value) return true; - node.value.walk(tw); - mark(node.name, true); + if (node.value) { + node.value.walk(tw); + mark(node.name, true); + } else { + var id = node.name.definition().id; + if (!(id in declarations)) declarations[id] = []; + declarations[id].push(node.name); + } return true; } }); @@ -4460,28 +4462,41 @@ merge(Compressor.prototype, { var tail = last.pop(); if (!tail) continue; if (tail.index > head.index) continue; - if (!references[tail.definition.id]) continue; - if (references[def.id].segment !== references[tail.definition.id].segment) { + var id = tail.definition.id; + if (!references[id]) continue; + if (references[def.id].segment !== references[id].segment) { skipped.unshift(tail); continue; } - var orig = [], refs = []; - references[tail.definition.id].forEach(function(sym) { - push(sym); + if (id in declarations) declarations[id].forEach(function(sym) { + sym.thedef = def; + sym.name = def.name; + def.orig.push(sym); + }); + references[id].forEach(function(sym) { sym.thedef = def; sym.name = def.name; + if (sym instanceof AST_SymbolRef) { + def.references.push(sym); + } else { + def.orig.push(sym); + } }); - references[def.id].forEach(push); - def.orig = orig; - def.refs = refs; - def.eliminated = def.replaced = 0; def.fixed = tail.definition.fixed && def.fixed; - merged[tail.definition.id] = def; + merged[id] = def; break; } while (last.length); if (skipped.length) last = last.concat(skipped); } + function push() { + segment = Object.create(segment); + } + + function pop() { + segment = Object.getPrototypeOf(segment); + } + function read(def) { prev[def.id] = last.length; last.push({ @@ -4516,14 +4531,6 @@ merge(Compressor.prototype, { } } } - - function push(sym) { - if (sym instanceof AST_SymbolRef) { - refs.push(sym); - } else { - orig.push(sym); - } - } }); AST_Scope.DEFMETHOD("drop_unused", function(compressor) { diff --git a/test/compress/merge_vars.js b/test/compress/merge_vars.js index 4c5f6dac..f83643e3 100644 --- a/test/compress/merge_vars.js +++ b/test/compress/merge_vars.js @@ -324,3 +324,129 @@ issue_4103: { "NaN", ] } + +issue_4107: { + options = { + keep_fargs: "strict", + merge_vars: true, + reduce_vars: true, + unused: true, + } + input: { + (function() { + function f(b, b, c) { + var d = 1 && a, a = console || c; + console.log(typeof a); + } + f(); + })(); + console.log(typeof a); + } + expect: { + (function() { + (function(c) { + var a = console || c; + console.log(typeof a); + })(); + })(); + console.log(typeof a); + } + expect_stdout: [ + "object", + "undefined", + ] +} + +issue_4109: { + options = { + ie8: true, + merge_vars: true, + toplevel: true, + } + input: { + var a = "foo"; + try { + throw "bar"; + } catch (e) { + console.log(e); + } finally { + var o = a; + for (var k in o); + (function() { + a++; + }); + } + console.log(a); + } + expect: { + var a = "foo"; + try { + throw "bar"; + } catch (e) { + console.log(e); + } finally { + var o = a; + for (var k in o); + (function() { + a++; + }); + } + console.log(a); + } + expect_stdout: [ + "bar", + "foo", + ] +} + +issue_4110: { + options = { + merge_vars: true, + toplevel: true, + } + input: { + while (a) + var c; + var b, a = c += b = a; + console.log(b); + } + expect: { + while (a) + var c; + var b, a = c += b = a; + console.log(b); + } + expect_stdout: "undefined" +} + +issue_4111: { + options = { + join_vars: true, + loops: true, + merge_vars: true, + toplevel: true, + } + input: { + var a = 0; + if (a) + a = 0; + else + for (var b = 0; --b && ++a < 2;) { + var o = console, k; + for (k in o); + } + console.log(a); + } + expect: { + var a = 0; + if (a) + a = 0; + else + for (var b = 0; --b && ++a < 2;) { + var o = console, k; + for (k in o); + } + console.log(a); + } + expect_stdout: "2" +} -- cgit v1.2.3