From 4c0b0177b6fa556c31f0099c6d52a4ad4f670ba3 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 8 Nov 2017 03:28:46 +0800 Subject: [PATCH] preserve function identity in `reduce_vars` (#2451) fixes #2450 --- lib/compress.js | 45 ++++++---- test/compress/reduce_vars.js | 154 +++++++++++++++++++++++++++++++++-- 2 files changed, 176 insertions(+), 23 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 274ab604..6354197d 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -324,9 +324,8 @@ merge(Compressor.prototype, { || value.is_constant_expression(node.scope); } else { d.single_use = d.scope === node.scope - && loop_ids[d.id] === in_loop && value.is_constant_expression(); - } + } } else { d.single_use = false; } @@ -384,6 +383,7 @@ merge(Compressor.prototype, { d.fixed = false; } else { d.fixed = node; + loop_ids[d.id] = in_loop; mark(d, true); if (ref_once(d)) { d.single_use = d.scope === d.references[0].scope @@ -577,7 +577,11 @@ merge(Compressor.prototype, { } 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) { @@ -623,7 +627,8 @@ merge(Compressor.prototype, { function mark_escaped(d, node, value, 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 || parent instanceof AST_Call && node !== parent.expression || parent instanceof AST_Return && node === parent.value && node.scope !== d.scope @@ -4216,6 +4221,17 @@ merge(Compressor.prototype, { 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){ var def = self.resolve_defines(compressor); if (def) { @@ -4241,20 +4257,13 @@ merge(Compressor.prototype, { if (fixed instanceof AST_Defun) { d.fixed = fixed = make_node(AST_Function, fixed, fixed); } - if (fixed && d.single_use) { - var recurse; - if (fixed instanceof AST_Function) { - for (var i = 0; recurse = compressor.parent(i); i++) { - if (recurse instanceof AST_Lambda) { - var name = recurse.name; - if (name && name.definition() === d) break; - } - } - } - if (!recurse) { - var value = fixed.optimize(compressor); - return value === fixed ? fixed.clone(true) : value; - } + if (fixed + && d.single_use + && !(fixed instanceof AST_Function + && (d.escaped && d.scope !== self.scope + || recursive_ref(compressor, d)))) { + var value = fixed.optimize(compressor); + return value === fixed ? fixed.clone(true) : value; } if (fixed && d.should_replace === undefined) { var init; diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index f1a27ff9..8afea850 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -1123,11 +1123,12 @@ toplevel_on_loops_1: { while (x); } expect: { + function bar() { + console.log("bar:", --x); + } var x = 3; do - (function() { - console.log("bar:", --x); - })(); + bar(); while (x); } expect_stdout: true @@ -1180,9 +1181,10 @@ toplevel_on_loops_2: { while (x); } expect: { - for (;;) (function() { + function bar() { console.log("bar:"); - })(); + } + for (;;) bar(); } } @@ -3845,3 +3847,145 @@ recursive_inlining_5: { "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", + ] +} -- 2.34.1