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) {
var tw = new TreeWalker(function(node) {
if (node instanceof AST_DefaultValue) {
node.name.walk(tw);
return true;
}
if (node instanceof AST_DestructuredKeyVal) {
node.value.walk(tw);
return true;

View File

@@ -1210,7 +1210,7 @@ merge(Compressor.prototype, {
});
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) {
if (node instanceof AST_DefaultValue) {
node = node.clone();
@@ -1229,7 +1229,9 @@ merge(Compressor.prototype, {
}
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) {
var node = make_node(type, this, 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) {
if (!compressor.option("unused")) return;
var self = this;
@@ -6143,9 +6151,7 @@ merge(Compressor.prototype, {
});
value = save;
if (values && elements.length == 0) return null;
for (var i = elements.length; --i >= 0;) {
if (!elements[i]) elements[i] = make_node(AST_Hole, node.elements[i] || node);
}
fill_holes(node, elements);
node.elements = elements;
return node;
}
@@ -8052,24 +8058,32 @@ merge(Compressor.prototype, {
var is_func = fn instanceof AST_Lambda && (!is_async(fn)
|| compressor.option("awaits") && compressor.parent() instanceof AST_Await);
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) {
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) {
has_default = true;
var arg = self.args[index];
if (arg && !is_undefined(arg)) return false;
if (!has_default) has_default = 1;
var arg = has_default == 1 && self.args[index];
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;
argname.value.walk(new TreeWalker(function(node) {
argname.walk(new TreeWalker(function(node) {
if (abort) return true;
if (node instanceof AST_SymbolRef && fn.variables.get(node.name) === node.definition()) {
return abort = true;
if (node instanceof AST_DestructuredKeyVal) {
var key = node.key;
if (key instanceof AST_Node && has_arg_refs(key)) return abort = true;
}
}));
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);
if (can_inline && stat instanceof AST_Return) {
@@ -8086,9 +8100,7 @@ merge(Compressor.prototype, {
&& !(fn.name && fn instanceof AST_Function)
&& (exp === fn || !recursive_ref(compressor, def = exp.definition())
&& fn.is_constant_expression(find_scope(compressor)))
&& all(self.args, function(arg) {
return !(arg instanceof AST_Spread);
})
&& !has_spread
&& (value = can_flatten_body(stat))
&& !fn.contains_this()) {
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);
function convert_args(value) {
var args = self.args.map(function(arg) {
return arg instanceof AST_Spread ? make_node(AST_Array, arg, {
elements: [ arg ],
}) : arg;
function has_arg_refs(node) {
var found = false;
node.walk(new TreeWalker(function(node) {
if (found) return true;
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);
});
args.push(value || make_node(AST_Undefined, self));
@@ -8241,8 +8319,7 @@ merge(Compressor.prototype, {
}
function can_substitute_directly() {
if (has_default) return;
if (var_assigned) return;
if (has_default || has_destructured || var_assigned) return;
if (compressor.option("inline") < 2 && fn.argnames.length) return;
if (!fn.variables.all(function(def) {
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) {
for (var i = 0; i < fn.argnames.length; i++) {
var arg = fn.argnames[i];
if (arg.__unused) continue;
var abort = false;
fn.each_argname(function(arg) {
if (abort) return;
if (arg.__unused) return;
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;
if (in_loop) in_loop.push(arg.definition());
}
return true;
});
return !abort;
}
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) {
var pos = expressions.length;
for (var i = 0; i < fn.body.length; i++) {
@@ -8484,7 +8578,11 @@ merge(Compressor.prototype, {
function flatten_fn() {
var decls = [];
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);
expressions.push(value);
var args = fn.body.filter(function(stat) {

View File

@@ -354,6 +354,22 @@ inline_constant: {
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: {
options = {
default_values: true,
@@ -423,6 +439,45 @@ inline_loop_2: {
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: {
options = {
side_effects: true,
@@ -1419,7 +1474,7 @@ issue_4502_4: {
(function(a, b = console.log("FAIL")) {})(..."" + console.log(42));
}
expect: {
(function(a, b = console.log("FAIL")) {})(..."" + console.log(42));
[ , 0[0] = console.log("FAIL") ] = [ ..."" + console.log(42) ];
}
expect_stdout: "42"
node_version: ">=6"

View File

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