enhance inline & side_effects (#4506)

This commit is contained in:
Alex Lam S.L
2021-01-05 07:02:49 +00:00
committed by GitHub
parent fc5aee662d
commit 6eceac0966
4 changed files with 214 additions and 53 deletions

View File

@@ -521,6 +521,10 @@ var AST_Lambda = DEFNODE("Lambda", "argnames length_read uses_arguments", {
}, },
each_argname: function(visit) { each_argname: function(visit) {
var tw = new TreeWalker(function(node) { var tw = new TreeWalker(function(node) {
if (node instanceof AST_DefaultValue) {
node.name.walk(tw);
return true;
}
if (node instanceof AST_DestructuredKeyVal) { if (node instanceof AST_DestructuredKeyVal) {
node.value.walk(tw); node.value.walk(tw);
return true; return true;

View File

@@ -1210,7 +1210,7 @@ merge(Compressor.prototype, {
}); });
AST_Node.DEFMETHOD("convert_symbol", noop); AST_Node.DEFMETHOD("convert_symbol", noop);
AST_Destructured.DEFMETHOD("convert_symbol", function(type, process) { function convert_destructured(type, process) {
return this.transform(new TreeTransformer(function(node, descend) { return this.transform(new TreeTransformer(function(node, descend) {
if (node instanceof AST_DefaultValue) { if (node instanceof AST_DefaultValue) {
node = node.clone(); node = node.clone();
@@ -1229,7 +1229,9 @@ merge(Compressor.prototype, {
} }
return node.convert_symbol(type, process); return node.convert_symbol(type, process);
})); }));
}); }
AST_DefaultValue.DEFMETHOD("convert_symbol", convert_destructured);
AST_Destructured.DEFMETHOD("convert_symbol", convert_destructured);
function convert_symbol(type, process) { function convert_symbol(type, process) {
var node = make_node(type, this, this); var node = make_node(type, this, this);
process(node, this); process(node, this);
@@ -5314,6 +5316,12 @@ merge(Compressor.prototype, {
} }
}); });
function fill_holes(orig, elements) {
for (var i = elements.length; --i >= 0;) {
if (!elements[i]) elements[i] = make_node(AST_Hole, orig);
}
}
AST_Scope.DEFMETHOD("drop_unused", function(compressor) { AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
if (!compressor.option("unused")) return; if (!compressor.option("unused")) return;
var self = this; var self = this;
@@ -6143,9 +6151,7 @@ merge(Compressor.prototype, {
}); });
value = save; value = save;
if (values && elements.length == 0) return null; if (values && elements.length == 0) return null;
for (var i = elements.length; --i >= 0;) { fill_holes(node, elements);
if (!elements[i]) elements[i] = make_node(AST_Hole, node.elements[i] || node);
}
node.elements = elements; node.elements = elements;
return node; return node;
} }
@@ -8052,24 +8058,32 @@ merge(Compressor.prototype, {
var is_func = fn instanceof AST_Lambda && (!is_async(fn) var is_func = fn instanceof AST_Lambda && (!is_async(fn)
|| compressor.option("awaits") && compressor.parent() instanceof AST_Await); || compressor.option("awaits") && compressor.parent() instanceof AST_Await);
var stat = is_func && fn.first_statement(); var stat = is_func && fn.first_statement();
var has_default = false; var has_default = 0, has_destructured = false;
var has_spread = !all(self.args, function(arg) {
return !(arg instanceof AST_Spread);
});
var can_drop = is_func && all(fn.argnames, function(argname, index) { var can_drop = is_func && all(fn.argnames, function(argname, index) {
if (has_default && self.args[index] instanceof AST_Spread) return false; if (has_default == 1 && self.args[index] instanceof AST_Spread) has_default = 2;
if (argname instanceof AST_DefaultValue) { if (argname instanceof AST_DefaultValue) {
has_default = true; if (!has_default) has_default = 1;
var arg = self.args[index]; var arg = has_default == 1 && self.args[index];
if (arg && !is_undefined(arg)) return false; if (arg && !is_undefined(arg)) has_default = 2;
if (has_arg_refs(argname.value)) return false;
argname = argname.name;
}
if (argname instanceof AST_Destructured) {
has_destructured = true;
var abort = false; var abort = false;
argname.value.walk(new TreeWalker(function(node) { argname.walk(new TreeWalker(function(node) {
if (abort) return true; if (abort) return true;
if (node instanceof AST_SymbolRef && fn.variables.get(node.name) === node.definition()) { if (node instanceof AST_DestructuredKeyVal) {
return abort = true; var key = node.key;
if (key instanceof AST_Node && has_arg_refs(key)) return abort = true;
} }
})); }));
if (abort) return false; if (abort) return false;
argname = argname.name;
} }
return !(argname instanceof AST_Destructured); return true;
}); });
var can_inline = can_drop && compressor.option("inline") && !self.is_expr_pure(compressor); var can_inline = can_drop && compressor.option("inline") && !self.is_expr_pure(compressor);
if (can_inline && stat instanceof AST_Return) { if (can_inline && stat instanceof AST_Return) {
@@ -8086,9 +8100,7 @@ merge(Compressor.prototype, {
&& !(fn.name && fn instanceof AST_Function) && !(fn.name && fn instanceof AST_Function)
&& (exp === fn || !recursive_ref(compressor, def = exp.definition()) && (exp === fn || !recursive_ref(compressor, def = exp.definition())
&& fn.is_constant_expression(find_scope(compressor))) && fn.is_constant_expression(find_scope(compressor)))
&& all(self.args, function(arg) { && !has_spread
return !(arg instanceof AST_Spread);
})
&& (value = can_flatten_body(stat)) && (value = can_flatten_body(stat))
&& !fn.contains_this()) { && !fn.contains_this()) {
var replacing = exp === fn || def.single_use && def.references.length - def.replaced == 1; var replacing = exp === fn || def.single_use && def.references.length - def.replaced == 1;
@@ -8161,13 +8173,79 @@ merge(Compressor.prototype, {
} }
return try_evaluate(compressor, self); return try_evaluate(compressor, self);
function convert_args(value) { function has_arg_refs(node) {
var args = self.args.map(function(arg) { var found = false;
return arg instanceof AST_Spread ? make_node(AST_Array, arg, { node.walk(new TreeWalker(function(node) {
elements: [ arg ], if (found) return true;
}) : arg; if (node instanceof AST_SymbolRef && fn.variables.get(node.name) === node.definition()) {
return found = true;
}
}));
return found;
}
function make_void_lhs(orig) {
return make_node(AST_Sub, orig, {
expression: make_node(AST_Number, orig, { value: 0 }),
property: make_node(AST_Number, orig, { value: 0 }),
}); });
fn.argnames.forEach(function(argname, index) { }
function convert_args(value) {
var args = self.args.slice();
var destructured = has_default > 1 || has_destructured;
if (destructured || has_spread) args = [ make_node(AST_Array, self, { elements: args }) ];
if (destructured) {
var tt = new TreeTransformer(function(node, descend) {
if (node instanceof AST_DefaultValue) return make_node(AST_DefaultValue, node, {
name: node.name.transform(tt) || make_void_lhs(node),
value: node.value,
});
if (node instanceof AST_DestructuredArray) {
var elements = [];
node.elements.forEach(function(node, index) {
node = node.transform(tt);
if (node) elements[index] = node;
});
fill_holes(node, elements);
return make_node(AST_DestructuredArray, node, { elements: elements });
}
if (node instanceof AST_DestructuredObject) {
var properties = [], side_effects = [];
node.properties.forEach(function(prop) {
var key = prop.key;
var value = prop.value.transform(tt);
if (value) {
side_effects.push(key instanceof AST_Node ? key : make_node_from_constant(key, prop));
properties.push(make_node(AST_DestructuredKeyVal, prop, {
key: make_sequence(node, side_effects),
value: value,
}));
side_effects = [];
} else if (key instanceof AST_Node) {
side_effects.push(key);
}
});
if (side_effects.length) properties.push(make_node(AST_DestructuredKeyVal, node, {
key: make_sequence(node, side_effects),
value: make_void_lhs(node),
}));
return make_node(AST_DestructuredObject, node, { properties: properties });
}
if (node instanceof AST_SymbolFunarg) return null;
});
var lhs = [];
fn.argnames.forEach(function(argname, index) {
argname = argname.transform(tt);
if (argname) lhs[index] = argname;
});
fill_holes(fn, lhs);
args[0] = make_node(AST_Assign, self, {
operator: "=",
left: make_node(AST_DestructuredArray, fn, { elements: lhs }),
right: args[0],
});
} else fn.argnames.forEach(function(argname) {
if (argname instanceof AST_DefaultValue) args.push(argname.value); if (argname instanceof AST_DefaultValue) args.push(argname.value);
}); });
args.push(value || make_node(AST_Undefined, self)); args.push(value || make_node(AST_Undefined, self));
@@ -8241,8 +8319,7 @@ merge(Compressor.prototype, {
} }
function can_substitute_directly() { function can_substitute_directly() {
if (has_default) return; if (has_default || has_destructured || var_assigned) return;
if (var_assigned) return;
if (compressor.option("inline") < 2 && fn.argnames.length) return; if (compressor.option("inline") < 2 && fn.argnames.length) return;
if (!fn.variables.all(function(def) { if (!fn.variables.all(function(def) {
return def.references.length - def.replaced < 2 && def.orig[0] instanceof AST_SymbolFunarg; return def.references.length - def.replaced < 2 && def.orig[0] instanceof AST_SymbolFunarg;
@@ -8308,15 +8385,16 @@ merge(Compressor.prototype, {
} }
function can_inject_args(defined, used, safe_to_inject) { function can_inject_args(defined, used, safe_to_inject) {
for (var i = 0; i < fn.argnames.length; i++) { var abort = false;
var arg = fn.argnames[i]; fn.each_argname(function(arg) {
if (arg.__unused) continue; if (abort) return;
if (arg.__unused) return;
if (arg instanceof AST_DefaultValue) arg = arg.name; if (arg instanceof AST_DefaultValue) arg = arg.name;
if (!safe_to_inject || var_exists(defined, arg.name)) return false; if (!safe_to_inject || var_exists(defined, arg.name)) return abort = true;
used[arg.name] = true; used[arg.name] = true;
if (in_loop) in_loop.push(arg.definition()); if (in_loop) in_loop.push(arg.definition());
} });
return true; return !abort;
} }
function can_inject_vars(defined, used, safe_to_inject) { function can_inject_vars(defined, used, safe_to_inject) {
@@ -8450,6 +8528,22 @@ merge(Compressor.prototype, {
} }
} }
function flatten_destructured(decls, expressions) {
expressions.push(make_node(AST_Assign, self, {
operator: "=",
left: make_node(AST_DestructuredArray, self, {
elements: fn.argnames.map(function(argname) {
return argname.convert_symbol(AST_SymbolRef, function(ref, name) {
var symbol = make_node(AST_SymbolVar, name, name);
name.definition().orig.push(symbol);
append_var(decls, expressions, symbol);
});
}),
}),
right: make_node(AST_Array, self, { elements: self.args.slice() }),
}));
}
function flatten_vars(decls, expressions) { function flatten_vars(decls, expressions) {
var pos = expressions.length; var pos = expressions.length;
for (var i = 0; i < fn.body.length; i++) { for (var i = 0; i < fn.body.length; i++) {
@@ -8484,7 +8578,11 @@ merge(Compressor.prototype, {
function flatten_fn() { function flatten_fn() {
var decls = []; var decls = [];
var expressions = []; var expressions = [];
flatten_args(decls, expressions); if (has_default > 1 || has_destructured) {
flatten_destructured(decls, expressions);
} else {
flatten_args(decls, expressions);
}
flatten_vars(decls, expressions); flatten_vars(decls, expressions);
expressions.push(value); expressions.push(value);
var args = fn.body.filter(function(stat) { var args = fn.body.filter(function(stat) {

View File

@@ -354,6 +354,22 @@ inline_constant: {
node_version: ">=6" node_version: ">=6"
} }
inline_destructured: {
options = {
inline: true,
}
input: {
console.log(function([ a ] = []) {
return "PASS";
}());
}
expect: {
console.log(([ [] = [] ] = [], "PASS"));
}
expect_stdout: "PASS"
node_version: ">=6"
}
inline_function: { inline_function: {
options = { options = {
default_values: true, default_values: true,
@@ -423,6 +439,45 @@ inline_loop_2: {
node_version: ">=6" node_version: ">=6"
} }
inline_side_effects_1: {
options = {
inline: true,
toplevel: true,
}
input: {
var a = 42;
(function(b = --a) {})(console);
console.log(a);
}
expect: {
var a = 42;
[ b = --a ] = [ console ],
void 0;
var b;
console.log(a);
}
expect_stdout: "42"
node_version: ">=6"
}
inline_side_effects_2: {
options = {
side_effects: true,
}
input: {
var a = 42;
(function(b = --a) {})(console);
console.log(a);
}
expect: {
var a = 42;
[ 0[0] = --a ] = [ console ];
console.log(a);
}
expect_stdout: "42"
node_version: ">=6"
}
drop_empty_iife: { drop_empty_iife: {
options = { options = {
side_effects: true, side_effects: true,
@@ -1419,7 +1474,7 @@ issue_4502_4: {
(function(a, b = console.log("FAIL")) {})(..."" + console.log(42)); (function(a, b = console.log("FAIL")) {})(..."" + console.log(42));
} }
expect: { expect: {
(function(a, b = console.log("FAIL")) {})(..."" + console.log(42)); [ , 0[0] = console.log("FAIL") ] = [ ..."" + console.log(42) ];
} }
expect_stdout: "42" expect_stdout: "42"
node_version: ">=6" node_version: ">=6"

View File

@@ -188,7 +188,7 @@ funarg_side_effects_1: {
} }
expect: { expect: {
try { try {
(function({}) {})(); [ {} ] = [];
} catch (e) { } catch (e) {
console.log("PASS"); console.log("PASS");
} }
@@ -682,7 +682,7 @@ funarg_inline: {
} }
expect: { expect: {
try { try {
(function({}) {})(); [ {} ] = [];
} catch (e) { } catch (e) {
console.log("PASS"); console.log("PASS");
} }
@@ -1718,10 +1718,14 @@ issue_4312: {
expect: { expect: {
var a; var a;
b = "PASS", b = "PASS",
(function({ c = "FAIL",
[a = b]: d, [
}){})((c = "FAIL") && c); {
var b, c; [a = b]: d,
},
] = [ c && c ],
void 0;
var b, c, d;
console.log(a); console.log(a);
} }
expect_stdout: "PASS" expect_stdout: "PASS"
@@ -1783,9 +1787,7 @@ issue_4319: {
function f(a) { function f(a) {
while (!a); while (!a);
} }
console.log(function({}) { console.log(([ {} ] = [ 0 ], f(console)));
return f(console);
}(0));
} }
expect_stdout: "undefined" expect_stdout: "undefined"
node_version: ">=6" node_version: ">=6"
@@ -1809,11 +1811,9 @@ issue_4321: {
} }
expect: { expect: {
try { try {
console.log(function({}) { console.log(([ {} ] = [], function() {
return function() { while (!console);
while (!console); }()));
}();
}());
} catch (e) { } catch (e) {
console.log("PASS"); console.log("PASS");
} }
@@ -1844,11 +1844,15 @@ issue_4323: {
} }
expect: { expect: {
var a = 0; var a = 0;
(function({ [
[function a() { {
console.log(typeof a); [function a() {
}()]: d, console.log(typeof a);
}) {})(0); }()]: d,
},
] = [ 0 ],
void 0;
var d;
e = 1, e = 1,
console.log, console.log,
void e.p; void e.p;