fix corner cases in merge_vars (#4108)
fixes #4107 fixes #4109 fixes #4110 fixes #4111
This commit is contained in:
131
lib/compress.js
131
lib/compress.js
@@ -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) {
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user