replace single-use recursive functions (#2659)

fixes #2628
This commit is contained in:
Alex Lam S.L
2017-12-26 21:25:35 +08:00
committed by GitHub
parent 7f342cb3e3
commit 4832bc5d88
5 changed files with 63 additions and 26 deletions

View File

@@ -319,6 +319,7 @@ merge(Compressor.prototype, {
} else { } else {
def.fixed = false; def.fixed = false;
} }
def.recursive_refs = 0;
def.references = []; def.references = [];
def.should_replace = undefined; def.should_replace = undefined;
def.single_use = undefined; def.single_use = undefined;
@@ -369,7 +370,7 @@ merge(Compressor.prototype, {
return compressor.option("unused") return compressor.option("unused")
&& !def.scope.uses_eval && !def.scope.uses_eval
&& !def.scope.uses_with && !def.scope.uses_with
&& def.references.length == 1 && def.references.length - def.recursive_refs == 1
&& tw.loop_ids[def.id] === tw.in_loop; && tw.loop_ids[def.id] === tw.in_loop;
} }
@@ -621,7 +622,9 @@ merge(Compressor.prototype, {
d.fixed = false; d.fixed = false;
} else if (d.fixed) { } else if (d.fixed) {
value = this.fixed_value(); 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.single_use = value instanceof AST_Lambda
|| d.scope === this.scope && value.is_constant_expression(); || d.scope === this.scope && value.is_constant_expression();
} else { } else {
@@ -4670,17 +4673,18 @@ 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 (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 if (d.scope !== self.scope
&& (!compressor.option("reduce_funcs") && (!compressor.option("reduce_funcs")
|| d.escaped == 1 || d.escaped == 1
|| fixed.inlined)) { || fixed.inlined)) {
d.single_use = false; single_use = false;
} else if (recursive_ref(compressor, d)) { } 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) { } else if (d.scope !== self.scope || d.orig[0] instanceof AST_SymbolFunarg) {
d.single_use = fixed.is_constant_expression(self.scope); single_use = fixed.is_constant_expression(self.scope);
if (d.single_use == "f") { if (single_use == "f") {
var scope = self.scope; var scope = self.scope;
do { do {
if (scope instanceof AST_Defun || scope instanceof AST_Function) { if (scope instanceof AST_Defun || scope instanceof AST_Function) {
@@ -4690,9 +4694,24 @@ merge(Compressor.prototype, {
} }
} }
} }
if (d.single_use && fixed) { if (single_use && fixed) {
var value = fixed.optimize(compressor); var value;
return value === fixed ? fixed.clone(true) : 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) { if (fixed && d.should_replace === undefined) {
var init; var init;

View File

@@ -307,7 +307,9 @@ AST_Scope.DEFMETHOD("find_variable", function(name){
}); });
AST_Scope.DEFMETHOD("def_function", function(symbol){ 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){ AST_Scope.DEFMETHOD("def_variable", function(symbol){

View File

@@ -3878,10 +3878,9 @@ recursive_function_replacement: {
console.log(f(c)); console.log(f(c));
} }
expect: { expect: {
function f(n) { console.log(function n(o) {
return x(y(f(n))); return x(y(n(o)));
} }(c));
console.log(f(c));
} }
} }

View File

@@ -1345,11 +1345,10 @@ issue_2630_3: {
expect: { expect: {
var x = 2, a = 1; var x = 2, a = 1;
(function() { (function() {
function f1(a) { (function f1(a) {
f2(); f2();
--x >= 0 && f1({}); --x >= 0 && f1({});
} })(a++);
f1(a++);
function f2() { function f2() {
a++; a++;
} }
@@ -1424,7 +1423,7 @@ issue_2630_5: {
expect_stdout: "155" expect_stdout: "155"
} }
recursive_inline: { recursive_inline_1: {
options = { options = {
inline: true, inline: true,
reduce_funcs: true, reduce_funcs: true,
@@ -1448,6 +1447,26 @@ recursive_inline: {
expect: {} 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: { issue_2657: {
options = { options = {
inline: true, inline: true,

View File

@@ -1355,10 +1355,9 @@ defun_inline_1: {
function f() { function f() {
return function(b) { return function(b) {
return b; return b;
}(2) + h(); }(2) + function h() {
function h() {
return h(); return h();
} }();
} }
} }
} }
@@ -1382,12 +1381,11 @@ defun_inline_2: {
} }
expect: { expect: {
function f() { function f() {
function h() {
return h();
}
return function(b) { return function(b) {
return b; return b;
}(2) + h(); }(2) + function h() {
return h();
}();
} }
} }
} }