aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Lam S.L <alexlamsl@gmail.com>2017-12-26 21:25:35 +0800
committerGitHub <noreply@github.com>2017-12-26 21:25:35 +0800
commit4832bc5d88e37ca35f1dd5f5d8ddd95cd8bbdd7d (patch)
tree925053e75bf5109861b7fdb54e30f0e5ae4b960f
parent7f342cb3e3abd3e39b18e62a2e9d6b8020d82773 (diff)
downloadtracifyjs-4832bc5d88e37ca35f1dd5f5d8ddd95cd8bbdd7d.tar.gz
tracifyjs-4832bc5d88e37ca35f1dd5f5d8ddd95cd8bbdd7d.zip
replace single-use recursive functions (#2659)
fixes #2628
-rw-r--r--lib/compress.js39
-rw-r--r--lib/scope.js4
-rw-r--r--test/compress/collapse_vars.js7
-rw-r--r--test/compress/functions.js27
-rw-r--r--test/compress/reduce_vars.js12
5 files changed, 63 insertions, 26 deletions
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();
+ }();
}
}
}