fix corner cases in merge_vars (#4108)

fixes #4107
fixes #4109
fixes #4110
fixes #4111
This commit is contained in:
Alex Lam S.L
2020-09-15 21:43:01 +01:00
committed by GitHub
parent a62b086184
commit ad27c14202
2 changed files with 195 additions and 62 deletions

View File

@@ -4309,15 +4309,16 @@ merge(Compressor.prototype, {
AST_Scope.DEFMETHOD("merge_variables", function(compressor) { AST_Scope.DEFMETHOD("merge_variables", function(compressor) {
if (!compressor.option("merge_vars")) return; if (!compressor.option("merge_vars")) return;
var self = this, segment; var self = this, segment = null;
var first = [], last = [], index = 0; var first = [], last = [], index = 0;
var declarations = Object.create(null);
var references = Object.create(null); var references = Object.create(null);
var prev = Object.create(null); var prev = Object.create(null);
var tw = new TreeWalker(function(node, descend) { var tw = new TreeWalker(function(node, descend) {
if (node instanceof AST_Assign) { if (node instanceof AST_Assign) {
if (node.operator != "=") return;
var sym = node.left; var sym = node.left;
if (!(sym instanceof AST_SymbolRef)) return; if (!(sym instanceof AST_SymbolRef)) return;
if (node.operator != "=") mark(sym);
node.right.walk(tw); node.right.walk(tw);
mark(sym, true); mark(sym, true);
return true; return true;
@@ -4325,58 +4326,54 @@ merge(Compressor.prototype, {
if (node instanceof AST_Binary) { if (node instanceof AST_Binary) {
if (!lazy_op[node.operator]) return; if (!lazy_op[node.operator]) return;
node.left.walk(tw); node.left.walk(tw);
var save = segment; push();
segment = node;
node.right.walk(tw); node.right.walk(tw);
segment = save; pop();
return true; return true;
} }
if (node instanceof AST_Conditional) { if (node instanceof AST_Conditional) {
node.condition.walk(tw); node.condition.walk(tw);
var save = segment; push();
segment = node.consequent;
node.consequent.walk(tw); node.consequent.walk(tw);
segment = node.alternative; pop();
push();
node.alternative.walk(tw); node.alternative.walk(tw);
segment = save; pop();
return true; return true;
} }
if (node instanceof AST_For) { if (node instanceof AST_For) {
if (node.init) node.init.walk(tw); if (node.init) node.init.walk(tw);
var save = segment; push();
segment = node;
if (node.condition) node.condition.walk(tw); if (node.condition) node.condition.walk(tw);
node.body.walk(tw); node.body.walk(tw);
if (node.step) node.step.walk(tw); if (node.step) node.step.walk(tw);
segment = save; pop();
return true; return true;
} }
if (node instanceof AST_ForIn) { if (node instanceof AST_ForIn) {
node.object.walk(tw); node.object.walk(tw);
var save = segment; push();
segment = node;
node.init.walk(tw); node.init.walk(tw);
node.body.walk(tw); node.body.walk(tw);
segment = save; pop();
return true; return true;
} }
if (node instanceof AST_If) { if (node instanceof AST_If) {
node.condition.walk(tw); node.condition.walk(tw);
var save = segment; push();
segment = node;
node.body.walk(tw); node.body.walk(tw);
pop();
if (node.alternative) { if (node.alternative) {
segment = node.alternative; push();
node.alternative.walk(tw); node.alternative.walk(tw);
pop();
} }
segment = save;
return true; return true;
} }
if (node instanceof AST_IterationStatement) { if (node instanceof AST_IterationStatement) {
var save = segment; push();
segment = node;
descend(); descend();
segment = save; pop();
return true; return true;
} }
if (node instanceof AST_Scope) { if (node instanceof AST_Scope) {
@@ -4384,35 +4381,32 @@ merge(Compressor.prototype, {
references[node.variables.get("arguments").id] = false; references[node.variables.get("arguments").id] = false;
if (node.name) references[node.name.definition().id] = false; if (node.name) references[node.name.definition().id] = false;
} }
var save = segment; push();
segment = node;
descend(); descend();
segment = save; pop();
return true; return true;
} }
if (node instanceof AST_Switch) { if (node instanceof AST_Switch) {
node.expression.walk(tw); node.expression.walk(tw);
var save = segment, first = true; var first = true;
node.body.forEach(function(branch) { node.body.forEach(function(branch) {
if (branch instanceof AST_Default) return; if (branch instanceof AST_Default) return;
if (first) { if (!first) push();
first = false;
} else {
segment = branch.expression;
}
branch.expression.walk(tw); branch.expression.walk(tw);
if (!first) pop();
first = false;
}); });
node.body.forEach(function(branch) { node.body.forEach(function(branch) {
segment = branch; push();
branch.body.forEach(function(stat) { branch.body.forEach(function(stat) {
stat.walk(tw); stat.walk(tw);
}); });
pop();
}); });
segment = save;
return true; return true;
} }
if (node instanceof AST_SymbolFunarg) { if (node instanceof AST_SymbolFunarg) {
mark(node, true); if (!node.__unused) mark(node, true);
return true; return true;
} }
if (node instanceof AST_SymbolRef) { if (node instanceof AST_SymbolRef) {
@@ -4420,16 +4414,19 @@ merge(Compressor.prototype, {
return true; return true;
} }
if (node instanceof AST_Try) { if (node instanceof AST_Try) {
var save = segment; push();
segment = node; node.body.forEach(function(stat) {
node.body.forEach(function(branch) { stat.walk(tw);
branch.walk(tw);
}); });
pop();
if (node.bcatch) { if (node.bcatch) {
segment = node.bcatch; references[node.bcatch.argname.definition().id] = false;
node.bcatch.walk(tw); push();
node.bcatch.body.forEach(function(stat) {
stat.walk(tw);
});
pop();
} }
segment = save;
if (node.bfinally) node.bfinally.walk(tw); if (node.bfinally) node.bfinally.walk(tw);
return true; return true;
} }
@@ -4441,9 +4438,14 @@ merge(Compressor.prototype, {
return true; return true;
} }
if (node instanceof AST_VarDef) { if (node instanceof AST_VarDef) {
if (!node.value) return true; if (node.value) {
node.value.walk(tw); node.value.walk(tw);
mark(node.name, true); mark(node.name, true);
} else {
var id = node.name.definition().id;
if (!(id in declarations)) declarations[id] = [];
declarations[id].push(node.name);
}
return true; return true;
} }
}); });
@@ -4460,28 +4462,41 @@ merge(Compressor.prototype, {
var tail = last.pop(); var tail = last.pop();
if (!tail) continue; if (!tail) continue;
if (tail.index > head.index) continue; if (tail.index > head.index) continue;
if (!references[tail.definition.id]) continue; var id = tail.definition.id;
if (references[def.id].segment !== references[tail.definition.id].segment) { if (!references[id]) continue;
if (references[def.id].segment !== references[id].segment) {
skipped.unshift(tail); skipped.unshift(tail);
continue; continue;
} }
var orig = [], refs = []; if (id in declarations) declarations[id].forEach(function(sym) {
references[tail.definition.id].forEach(function(sym) {
push(sym);
sym.thedef = def; sym.thedef = def;
sym.name = def.name; 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; def.fixed = tail.definition.fixed && def.fixed;
merged[tail.definition.id] = def; merged[id] = def;
break; break;
} while (last.length); } while (last.length);
if (skipped.length) last = last.concat(skipped); if (skipped.length) last = last.concat(skipped);
} }
function push() {
segment = Object.create(segment);
}
function pop() {
segment = Object.getPrototypeOf(segment);
}
function read(def) { function read(def) {
prev[def.id] = last.length; prev[def.id] = last.length;
last.push({ 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) { AST_Scope.DEFMETHOD("drop_unused", function(compressor) {

View File

@@ -324,3 +324,129 @@ issue_4103: {
"NaN", "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"
}