enhance reduce_vars (#4392)

This commit is contained in:
Alex Lam S.L
2020-12-17 22:18:47 +00:00
committed by GitHub
parent ab82be82b2
commit 4fa54b075c
3 changed files with 212 additions and 59 deletions

View File

@@ -672,6 +672,53 @@ merge(Compressor.prototype, {
return true;
}
function reduce_iife(tw, descend, compressor) {
var fn = this;
fn.inlined = false;
var iife = tw.parent();
var hit = fn instanceof AST_AsyncFunction;
var aborts = false;
fn.walk(new TreeWalker(function(node) {
if (hit) return aborts = true;
if (node instanceof AST_Return) return hit = true;
if (node instanceof AST_Scope && node !== fn) return true;
}));
if (aborts) push(tw);
reset_variables(tw, compressor, fn);
// Virtually turn IIFE parameters into variable definitions:
// (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
// So existing transformation rules can work on them.
var safe = !fn.uses_arguments || tw.has_directive("use strict");
fn.argnames.forEach(function(arg, i) {
var value = iife.args[i];
scan_declaration(tw, arg, function() {
var j = fn.argnames.indexOf(arg);
return (j < 0 ? value : iife.args[j]) || make_node(AST_Undefined, iife);
}, function(node, fixed) {
var d = node.definition();
if (safe && d.fixed === undefined) {
mark(tw, d);
tw.loop_ids[d.id] = tw.in_loop;
var value = iife.args[i];
d.fixed = fixed;
d.fixed.assigns = [ arg ];
} else {
d.fixed = false;
}
});
});
if (fn instanceof AST_Arrow && fn.value) {
fn.value.walk(tw);
} else {
walk_body(fn, tw);
}
var safe_ids = tw.safe_ids;
pop(tw);
walk_defuns(tw, fn);
if (!aborts) tw.safe_ids = safe_ids;
return true;
}
def(AST_Assign, function(tw, descend, compressor) {
var node = this;
var left = node.left;
@@ -771,10 +818,14 @@ merge(Compressor.prototype, {
tw.find_parent(AST_Scope).may_call_this();
var exp = this.expression;
if (is_function(exp)) {
var iife = !exp.name;
this.args.forEach(function(arg) {
arg.walk(tw);
if (arg instanceof AST_Spread) iife = false;
});
if (iife) exp.reduce_vars = reduce_iife;
exp.walk(tw);
if (iife) delete exp.reduce_vars;
return true;
} else if (exp instanceof AST_SymbolRef) {
var def = exp.definition();
@@ -861,62 +912,6 @@ merge(Compressor.prototype, {
tw.in_loop = saved_loop;
return true;
});
def(AST_Function, function(tw, descend, compressor) {
var fn = this;
fn.inlined = false;
var iife;
if (!fn.name
&& (iife = tw.parent()) instanceof AST_Call
&& iife.expression === fn
&& all(iife.args, function(arg) {
return !(arg instanceof AST_Spread);
})) {
var hit = false;
var aborts = false;
fn.walk(new TreeWalker(function(node) {
if (hit) return aborts = true;
if (node instanceof AST_Return) return hit = true;
if (node instanceof AST_Scope && node !== fn) return true;
}));
if (aborts) push(tw);
reset_variables(tw, compressor, fn);
// Virtually turn IIFE parameters into variable definitions:
// (function(a,b) {...})(c,d) => (function() {var a=c,b=d; ...})()
// So existing transformation rules can work on them.
var safe = !fn.uses_arguments || tw.has_directive("use strict");
fn.argnames.forEach(function(arg, i) {
var value = iife.args[i];
scan_declaration(tw, arg, function() {
var j = fn.argnames.indexOf(arg);
return (j < 0 ? value : iife.args[j]) || make_node(AST_Undefined, iife);
}, function(node, fixed) {
var d = node.definition();
if (safe && d.fixed === undefined) {
mark(tw, d);
tw.loop_ids[d.id] = tw.in_loop;
var value = iife.args[i];
d.fixed = fixed;
d.fixed.assigns = [ arg ];
} else {
d.fixed = false;
}
});
});
walk_body(fn, tw);
var safe_ids = tw.safe_ids;
pop(tw);
walk_defuns(tw, fn);
if (!aborts) tw.safe_ids = safe_ids;
} else {
push(tw);
reset_variables(tw, compressor, fn);
descend();
pop(tw);
if (fn.name) mark_escaped(tw, fn.name.definition(), fn, fn.name, fn, 0, 1);
walk_defuns(tw, fn);
}
return true;
});
def(AST_If, function(tw) {
this.condition.walk(tw);
push(tw);
@@ -936,12 +931,14 @@ merge(Compressor.prototype, {
return true;
});
def(AST_Lambda, function(tw, descend, compressor) {
this.inlined = false;
var fn = this;
fn.inlined = false;
push(tw);
reset_variables(tw, compressor, this);
reset_variables(tw, compressor, fn);
descend();
pop(tw);
walk_defuns(tw, this);
if (fn.name) mark_escaped(tw, fn.name.definition(), fn, fn.name, fn, 0, 1);
walk_defuns(tw, fn);
return true;
});
def(AST_Switch, function(tw, descend, compressor) {

View File

@@ -337,6 +337,91 @@ trim_body: {
node_version: ">=4"
}
reduce_iife_1: {
options = {
evaluate: true,
keep_fargs: "strict",
reduce_vars: true,
unused: true,
}
input: {
(a => console.log(a + a))(21);
}
expect: {
(() => console.log(42))();
}
expect_stdout: "42"
node_version: ">=4"
}
reduce_iife_2: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 21;
(() => console.log(a + a))();
}
expect: {
(() => console.log(42))();
}
expect_stdout: "42"
node_version: ">=4"
}
reduce_iife_3: {
options = {
evaluate: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = "foo";
(() => {
console.log(a);
console.log(a);
})();
a = "bar";
}
expect: {
(() => {
console.log("foo");
console.log("foo");
})();
}
expect_stdout: [
"foo",
"foo",
]
node_version: ">=4"
}
single_use_recursive: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
return (() => f)();
}
console.log(typeof f());
}
expect: {
console.log(typeof function f() {
return (() => f)();
}());
}
expect_stdout: "function"
node_version: ">=4"
}
issue_4388: {
options = {
inline: true,

View File

@@ -329,6 +329,77 @@ property_access_expression: {
node_version: ">=8"
}
reduce_iife_1: {
options = {
evaluate: true,
keep_fargs: "strict",
reduce_vars: true,
unused: true,
}
input: {
(async function(a) {
console.log(a + a);
})(21);
}
expect: {
(async function() {
console.log(42);
})();
}
expect_stdout: "42"
node_version: ">=8"
}
reduce_iife_2: {
options = {
evaluate: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
var a = 21;
(async function() {
console.log(a + a);
})();
}
expect: {
(async function() {
console.log(42);
})();
}
expect_stdout: "42"
node_version: ">=8"
}
reduce_iife_3: {
options = {
evaluate: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = "foo";
(async function() {
console.log(a);
console.log(await a);
})();
a = "bar";
}
expect: {
var a = "foo";
(async function() {
console.log(a);
console.log(await a);
})();
a = "bar";
}
expect_stdout: "foo"
node_version: ">=8"
}
issue_4347_1: {
options = {
evaluate: true,