From: Alex Lam S.L Date: Tue, 26 Dec 2017 13:25:35 +0000 (+0800) Subject: replace single-use recursive functions (#2659) X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=4832bc5d88e37ca35f1dd5f5d8ddd95cd8bbdd7d;p=UglifyJS.git replace single-use recursive functions (#2659) fixes #2628 --- diff --git a/lib/compress.js b/lib/compress.js index bb355423..d890caf7 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -319,6 +319,7 @@ merge(Compressor.prototype, { } else { def.fixed = false; } + def.recursive_refs = 0; def.references = []; def.should_replace = undefined; def.single_use = undefined; @@ -369,7 +370,7 @@ merge(Compressor.prototype, { return compressor.option("unused") && !def.scope.uses_eval && !def.scope.uses_with - && def.references.length == 1 + && def.references.length - def.recursive_refs == 1 && tw.loop_ids[def.id] === tw.in_loop; } @@ -621,7 +622,9 @@ merge(Compressor.prototype, { d.fixed = false; } else if (d.fixed) { value = this.fixed_value(); - if (value && ref_once(tw, compressor, d)) { + if (value instanceof AST_Lambda && recursive_ref(tw, d)) { + d.recursive_refs++; + } else if (value && ref_once(tw, compressor, d)) { d.single_use = value instanceof AST_Lambda || d.scope === this.scope && value.is_constant_expression(); } else { @@ -4670,17 +4673,18 @@ merge(Compressor.prototype, { if (fixed instanceof AST_Defun) { d.fixed = fixed = make_node(AST_Function, fixed, fixed); } - if (d.single_use && fixed instanceof AST_Function) { + var single_use = d.single_use; + if (single_use && fixed instanceof AST_Function) { if (d.scope !== self.scope && (!compressor.option("reduce_funcs") || d.escaped == 1 || fixed.inlined)) { - d.single_use = false; + single_use = false; } else if (recursive_ref(compressor, d)) { - d.single_use = false; + single_use = false; } else if (d.scope !== self.scope || d.orig[0] instanceof AST_SymbolFunarg) { - d.single_use = fixed.is_constant_expression(self.scope); - if (d.single_use == "f") { + single_use = fixed.is_constant_expression(self.scope); + if (single_use == "f") { var scope = self.scope; do { if (scope instanceof AST_Defun || scope instanceof AST_Function) { @@ -4690,9 +4694,24 @@ merge(Compressor.prototype, { } } } - if (d.single_use && fixed) { - var value = fixed.optimize(compressor); - return value === fixed ? fixed.clone(true) : value; + if (single_use && fixed) { + var value; + if (d.recursive_refs > 0 && fixed.name instanceof AST_SymbolDefun) { + value = fixed.clone(true); + var defun_def = value.name.definition(); + value.name = make_node(AST_SymbolLambda, value.name, value.name); + value.name.scope = value; + var lambda_def = value.def_function(value.name); + value.walk(new TreeWalker(function(node) { + if (node instanceof AST_SymbolRef && node.definition() === defun_def) { + node.thedef = lambda_def; + } + })); + } else { + value = fixed.optimize(compressor); + if (value === fixed) value = fixed.clone(true); + } + return value; } if (fixed && d.should_replace === undefined) { var init; diff --git a/lib/scope.js b/lib/scope.js index bbfa037c..bceec289 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -307,7 +307,9 @@ AST_Scope.DEFMETHOD("find_variable", function(name){ }); AST_Scope.DEFMETHOD("def_function", function(symbol){ - this.functions.set(symbol.name, this.def_variable(symbol)); + var def = this.def_variable(symbol); + this.functions.set(symbol.name, def); + return def; }); AST_Scope.DEFMETHOD("def_variable", function(symbol){ diff --git a/test/compress/collapse_vars.js b/test/compress/collapse_vars.js index 9a20c559..0bad06a4 100644 --- a/test/compress/collapse_vars.js +++ b/test/compress/collapse_vars.js @@ -3878,10 +3878,9 @@ recursive_function_replacement: { console.log(f(c)); } expect: { - function f(n) { - return x(y(f(n))); - } - console.log(f(c)); + console.log(function n(o) { + return x(y(n(o))); + }(c)); } } diff --git a/test/compress/functions.js b/test/compress/functions.js index 7f35de76..83a27a06 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -1345,11 +1345,10 @@ issue_2630_3: { expect: { var x = 2, a = 1; (function() { - function f1(a) { + (function f1(a) { f2(); --x >= 0 && f1({}); - } - f1(a++); + })(a++); function f2() { a++; } @@ -1424,7 +1423,7 @@ issue_2630_5: { expect_stdout: "155" } -recursive_inline: { +recursive_inline_1: { options = { inline: true, reduce_funcs: true, @@ -1448,6 +1447,26 @@ recursive_inline: { expect: {} } +recursive_inline_2: { + options = { + reduce_vars: true, + toplevel: true, + unused: true, + } + input: { + function f(n) { + return n ? n * f(n - 1) : 1; + } + console.log(f(5)); + } + expect: { + console.log(function f(n) { + return n ? n * f(n - 1) : 1; + }(5)); + } + expect_stdout: "120" +} + issue_2657: { options = { inline: true, diff --git a/test/compress/reduce_vars.js b/test/compress/reduce_vars.js index 2b2c77da..6f302ecd 100644 --- a/test/compress/reduce_vars.js +++ b/test/compress/reduce_vars.js @@ -1355,10 +1355,9 @@ defun_inline_1: { function f() { return function(b) { return b; - }(2) + h(); - function h() { + }(2) + function h() { return h(); - } + }(); } } } @@ -1382,12 +1381,11 @@ defun_inline_2: { } expect: { function f() { - function h() { - return h(); - } return function(b) { return b; - }(2) + h(); + }(2) + function h() { + return h(); + }(); } } }