preserve function identity in reduce_vars (#2451)

fixes #2450
This commit is contained in:
Alex Lam S.L
2017-11-08 03:28:46 +08:00
committed by GitHub
parent 38bfb73f06
commit 4c0b0177b6
2 changed files with 176 additions and 23 deletions

View File

@@ -324,9 +324,8 @@ merge(Compressor.prototype, {
|| value.is_constant_expression(node.scope); || value.is_constant_expression(node.scope);
} else { } else {
d.single_use = d.scope === node.scope d.single_use = d.scope === node.scope
&& loop_ids[d.id] === in_loop
&& value.is_constant_expression(); && value.is_constant_expression();
} }
} else { } else {
d.single_use = false; d.single_use = false;
} }
@@ -384,6 +383,7 @@ merge(Compressor.prototype, {
d.fixed = false; d.fixed = false;
} else { } else {
d.fixed = node; d.fixed = node;
loop_ids[d.id] = in_loop;
mark(d, true); mark(d, true);
if (ref_once(d)) { if (ref_once(d)) {
d.single_use = d.scope === d.references[0].scope d.single_use = d.scope === d.references[0].scope
@@ -577,7 +577,11 @@ merge(Compressor.prototype, {
} }
function ref_once(def) { function ref_once(def) {
return unused && !def.scope.uses_eval && !def.scope.uses_with && def.references.length == 1; return unused
&& !def.scope.uses_eval
&& !def.scope.uses_with
&& def.references.length == 1
&& loop_ids[def.id] === in_loop;
} }
function is_immutable(value) { function is_immutable(value) {
@@ -623,7 +627,8 @@ merge(Compressor.prototype, {
function mark_escaped(d, node, value, level) { function mark_escaped(d, node, value, level) {
var parent = tw.parent(level); var parent = tw.parent(level);
if (value instanceof AST_Constant || value instanceof AST_Function) return; if (value instanceof AST_Constant) return;
if (level > 0 && value instanceof AST_Function) return;
if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right
|| parent instanceof AST_Call && node !== parent.expression || parent instanceof AST_Call && node !== parent.expression
|| parent instanceof AST_Return && node === parent.value && node.scope !== d.scope || parent instanceof AST_Return && node === parent.value && node.scope !== d.scope
@@ -4216,6 +4221,17 @@ merge(Compressor.prototype, {
return self; return self;
}); });
function recursive_ref(compressor, def) {
var node;
for (var i = 0; node = compressor.parent(i); i++) {
if (node instanceof AST_Lambda) {
var name = node.name;
if (name && name.definition() === def) break;
}
}
return node;
}
OPT(AST_SymbolRef, function(self, compressor){ OPT(AST_SymbolRef, function(self, compressor){
var def = self.resolve_defines(compressor); var def = self.resolve_defines(compressor);
if (def) { if (def) {
@@ -4241,20 +4257,13 @@ merge(Compressor.prototype, {
if (fixed instanceof AST_Defun) { if (fixed instanceof AST_Defun) {
d.fixed = fixed = make_node(AST_Function, fixed, fixed); d.fixed = fixed = make_node(AST_Function, fixed, fixed);
} }
if (fixed && d.single_use) { if (fixed
var recurse; && d.single_use
if (fixed instanceof AST_Function) { && !(fixed instanceof AST_Function
for (var i = 0; recurse = compressor.parent(i); i++) { && (d.escaped && d.scope !== self.scope
if (recurse instanceof AST_Lambda) { || recursive_ref(compressor, d)))) {
var name = recurse.name; var value = fixed.optimize(compressor);
if (name && name.definition() === d) break; return value === fixed ? fixed.clone(true) : value;
}
}
}
if (!recurse) {
var value = fixed.optimize(compressor);
return value === fixed ? fixed.clone(true) : value;
}
} }
if (fixed && d.should_replace === undefined) { if (fixed && d.should_replace === undefined) {
var init; var init;

View File

@@ -1123,11 +1123,12 @@ toplevel_on_loops_1: {
while (x); while (x);
} }
expect: { expect: {
function bar() {
console.log("bar:", --x);
}
var x = 3; var x = 3;
do do
(function() { bar();
console.log("bar:", --x);
})();
while (x); while (x);
} }
expect_stdout: true expect_stdout: true
@@ -1180,9 +1181,10 @@ toplevel_on_loops_2: {
while (x); while (x);
} }
expect: { expect: {
for (;;) (function() { function bar() {
console.log("bar:"); console.log("bar:");
})(); }
for (;;) bar();
} }
} }
@@ -3845,3 +3847,145 @@ recursive_inlining_5: {
"foo 0", "foo 0",
] ]
} }
issue_2450_1: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {}
function g() {
return f;
}
console.log(g() === g());
}
expect: {
function f() {}
function g() {
return f;
}
console.log(g() === g());
}
expect_stdout: "true"
}
issue_2450_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function g() {
function f() {}
return f;
}
console.log(g() === g());
}
expect: {
function g() {
return function() {};
}
console.log(g() === g());
}
expect_stdout: "false"
}
issue_2450_3: {
options = {
reduce_vars: true,
unused: true,
}
input: {
var x = (function() {
function test() {
return "foo";
}
return function b() {
return [1, test];
}
})();
console.log(x()[1] === x()[1]);
}
expect: {
var x = (function() {
function test() {
return "foo";
}
return function() {
return [1, test];
}
})();
console.log(x()[1] === x()[1]);
}
expect_stdout: "true"
}
issue_2450_4: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a;
function f(b) {
console.log(a === b);
a = b;
}
function g() {}
for (var i = 3; --i >= 0;)
f(g);
}
expect: {
var a;
function f(b) {
console.log(a === b);
a = b;
}
function g() {}
for (var i = 3; --i >= 0;)
f(g);
}
expect_stdout: [
"false",
"true",
"true",
]
}
issue_2450_5: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a;
function f(b) {
console.log(a === b);
a = b;
}
function g() {}
[1, 2, 3].forEach(function() {
f(g);
});
}
expect: {
var a;
function g() {}
[1, 2, 3].forEach(function() {
(function(b) {
console.log(a === b);
a = b;
})(g);
});
}
expect_stdout: [
"false",
"true",
"true",
]
}