enhance if_return (#5582)

This commit is contained in:
Alex Lam S.L
2022-07-29 22:25:17 +01:00
committed by GitHub
parent 937a672879
commit 08c386f363
2 changed files with 240 additions and 25 deletions

View File

@@ -1965,6 +1965,12 @@ Compressor.prototype.compress = function(node) {
block = compressor.parent(level++); block = compressor.parent(level++);
} else if (block instanceof AST_LabeledStatement) { } else if (block instanceof AST_LabeledStatement) {
block = block.body; block = block.body;
} else if (block instanceof AST_SwitchBranch) {
var branches = compressor.parent(level);
if (branches.body[branches.body.length - 1] === block || has_break(block.body)) {
level++;
block = branches;
}
} }
do { do {
stat = block; stat = block;
@@ -1975,8 +1981,16 @@ Compressor.prototype.compress = function(node) {
&& (block instanceof AST_BlockStatement && (block instanceof AST_BlockStatement
|| block instanceof AST_Catch || block instanceof AST_Catch
|| block instanceof AST_Scope || block instanceof AST_Scope
|| block instanceof AST_SwitchBranch
|| block instanceof AST_Try) || block instanceof AST_Try)
&& is_last_statement(block.body, stat)); && is_last_statement(block.body, stat));
function has_break(stats) {
for (var i = stats.length; --i >= 0;) {
if (stats[i] instanceof AST_Break) return true;
}
return false;
}
} }
function find_loop_scope_try() { function find_loop_scope_try() {
@@ -3437,7 +3451,7 @@ Compressor.prototype.compress = function(node) {
var changed = false; var changed = false;
var parent = compressor.parent(); var parent = compressor.parent();
var self = compressor.self(); var self = compressor.self();
var exit, merge_exit; var jump, merge_jump;
var in_iife = in_lambda && parent && parent.TYPE == "Call" && parent.expression === self; var in_iife = in_lambda && parent && parent.TYPE == "Call" && parent.expression === self;
var chain_if_returns = in_lambda && compressor.option("conditionals") && compressor.option("sequences"); var chain_if_returns = in_lambda && compressor.option("conditionals") && compressor.option("sequences");
var multiple_if_returns = has_multiple_if_returns(statements); var multiple_if_returns = has_multiple_if_returns(statements);
@@ -3447,6 +3461,7 @@ Compressor.prototype.compress = function(node) {
var next = statements[j]; var next = statements[j];
if (in_lambda && !next && stat instanceof AST_Return if (in_lambda && !next && stat instanceof AST_Return
&& !(self instanceof AST_SwitchBranch)
&& !(in_try && in_try.bfinally && in_async_generator(in_lambda))) { && !(in_try && in_try.bfinally && in_async_generator(in_lambda))) {
var body = stat.value; var body = stat.value;
if (!body) { if (!body) {
@@ -3494,7 +3509,7 @@ Compressor.prototype.compress = function(node) {
stat.condition = cond; stat.condition = cond;
statements[j] = stat.body; statements[j] = stat.body;
stat.body = next; stat.body = next;
if (next === exit) exit = null; if (next === jump) jump = null;
statements[i] = stat; statements[i] = stat;
statements[i] = stat.transform(compressor); statements[i] = stat.transform(compressor);
continue; continue;
@@ -3538,7 +3553,7 @@ Compressor.prototype.compress = function(node) {
changed = true; changed = true;
stat = stat.clone(); stat = stat.clone();
stat.alternative = next; stat.alternative = next;
if (next === exit) exit = null; if (next === jump) jump = null;
statements.splice(i, 1, stat.transform(compressor)); statements.splice(i, 1, stat.transform(compressor));
statements.splice(j, 1); statements.splice(j, 1);
continue; continue;
@@ -3583,12 +3598,12 @@ Compressor.prototype.compress = function(node) {
} }
} }
if (stat instanceof AST_Exit) { if (stat instanceof AST_Break || stat instanceof AST_Exit) {
exit = stat; jump = stat;
continue; continue;
} }
if (exit && exit === next) eliminate_returns(stat); if (jump && jump === next) eliminate_returns(stat);
} }
return changed; return changed;
@@ -3610,38 +3625,41 @@ Compressor.prototype.compress = function(node) {
} }
function match_return(ab, exact) { function match_return(ab, exact) {
if (!exit) return false; if (!jump) return false;
if (exit.TYPE != ab.TYPE) return false; if (jump.TYPE != ab.TYPE) return false;
var value = ab.value; var value = ab.value;
if (!value) return false; if (!value) return false;
var equals = exit.equals(ab); var equals = jump.equals(ab);
if (!equals && value instanceof AST_Sequence) { if (!equals && value instanceof AST_Sequence) {
value = value.tail_node(); value = value.tail_node();
if (exit.value && exit.value.equals(value)) equals = 2; if (jump.value && jump.value.equals(value)) equals = 2;
} }
if (!equals && !exact && exit.value instanceof AST_Sequence) { if (!equals && !exact && jump.value instanceof AST_Sequence) {
if (exit.value.tail_node().equals(value)) equals = 3; if (jump.value.tail_node().equals(value)) equals = 3;
} }
return equals; return equals;
} }
function can_drop_abort(ab) { function can_drop_abort(ab) {
if (ab instanceof AST_Exit) { if (ab instanceof AST_Exit) {
if (merge_exit = match_return(ab)) return true; if (merge_jump = match_return(ab)) return true;
if (!in_lambda) return false; if (!in_lambda) return false;
if (!(ab instanceof AST_Return)) return false; if (!(ab instanceof AST_Return)) return false;
if (is_undefined(ab.value)) return true; var value = ab.value;
return ab.value instanceof AST_Sequence && is_undefined(ab.value.tail_node()); if (value && !is_undefined(value.tail_node())) return false;
if (self instanceof AST_SwitchBranch) merge_jump = 4;
return true;
} }
if (!(ab instanceof AST_LoopControl)) return false; if (!(ab instanceof AST_LoopControl)) return false;
var lct = compressor.loopcontrol_target(ab); var lct = compressor.loopcontrol_target(ab);
if (ab instanceof AST_Continue) return match_target(loop_body(lct)); if (ab instanceof AST_Continue) return match_target(loop_body(lct));
if (lct instanceof AST_IterationStatement) return false; if (lct instanceof AST_IterationStatement) return false;
if (jump) merge_jump = jump.equals(ab);
return match_target(lct); return match_target(lct);
} }
function can_merge_flow(ab) { function can_merge_flow(ab) {
merge_exit = false; merge_jump = false;
if (!can_drop_abort(ab)) return false; if (!can_drop_abort(ab)) return false;
for (var j = statements.length; --j > i;) { for (var j = statements.length; --j > i;) {
var stat = statements[j]; var stat = statements[j];
@@ -3663,12 +3681,12 @@ Compressor.prototype.compress = function(node) {
var lexical = false; var lexical = false;
var start = i + 1; var start = i + 1;
var end; var end;
if (merge_exit) { if (merge_jump) {
end = statements.lastIndexOf(exit); end = statements.lastIndexOf(jump);
if (end < 0) end = statements.length; if (end < 0) end = statements.length;
} else { } else {
end = statements.length; end = statements.length;
exit = null; jump = null;
} }
var tail = statements.splice(start, end - start).filter(function(stat) { var tail = statements.splice(start, end - start).filter(function(stat) {
if (stat instanceof AST_LambdaDefinition) { if (stat instanceof AST_LambdaDefinition) {
@@ -3678,11 +3696,11 @@ Compressor.prototype.compress = function(node) {
if (is_lexical_definition(stat)) lexical = true; if (is_lexical_definition(stat)) lexical = true;
return true; return true;
}); });
if (merge_exit === 3) { if (merge_jump === 3) {
tail.push(make_node(AST_SimpleStatement, exit.value, { tail.push(make_node(AST_SimpleStatement, jump.value, {
body: make_sequence(exit.value, exit.value.expressions.slice(0, -1)), body: make_sequence(jump.value, jump.value.expressions.slice(0, -1)),
})); }));
exit.value = exit.value.tail_node(); jump.value = jump.value.tail_node();
} }
[].push.apply(lexical ? tail : statements, defuns); [].push.apply(lexical ? tail : statements, defuns);
return tail; return tail;
@@ -3690,6 +3708,8 @@ Compressor.prototype.compress = function(node) {
function trim_return(value, mode) { function trim_return(value, mode) {
if (value) switch (mode) { if (value) switch (mode) {
case 4:
return value;
case 3: case 3:
if (!(value instanceof AST_Sequence)) break; if (!(value instanceof AST_Sequence)) break;
case 2: case 2:
@@ -3705,7 +3725,7 @@ Compressor.prototype.compress = function(node) {
} }
block.pop(); block.pop();
var value = ab.value; var value = ab.value;
if (merge_exit) value = trim_return(value, merge_exit); if (merge_jump) value = trim_return(value, merge_jump);
if (value) block.push(make_node(AST_SimpleStatement, value, { body: value })); if (value) block.push(make_node(AST_SimpleStatement, value, { body: value }));
return body; return body;
} }
@@ -3757,7 +3777,7 @@ Compressor.prototype.compress = function(node) {
} else if (stat instanceof AST_LabeledStatement) { } else if (stat instanceof AST_LabeledStatement) {
stat.body = eliminate_returns(stat.body); stat.body = eliminate_returns(stat.body);
} else if (stat instanceof AST_Try) { } else if (stat instanceof AST_Try) {
if (!stat.bfinally || !exit.value || exit.value.is_constant()) { if (!stat.bfinally || !jump.value || jump.value.is_constant()) {
if (stat.bcatch) eliminate_returns(stat.bcatch); if (stat.bcatch) eliminate_returns(stat.bcatch);
var trimmed = eliminate_returns(stat.body.pop(), true); var trimmed = eliminate_returns(stat.body.pop(), true);
if (trimmed) stat.body.push(trimmed); if (trimmed) stat.body.push(trimmed);

View File

@@ -1393,3 +1393,198 @@ void_match: {
"foo", "foo",
] ]
} }
switch_break: {
options = {
conditionals: true,
if_return: true,
}
input: {
function f(a) {
switch (a) {
default:
if (console.log("foo"))
break;
while (console.log("bar"));
case 42:
if (console.log("baz"))
break;
while (console.log("moo"));
break;
case null:
if (console.log("moz"))
break;
}
}
f();
f(42);
f(null);
}
expect: {
function f(a) {
switch (a) {
default:
if (console.log("foo"))
break;
while (console.log("bar"));
case 42:
if (!console.log("baz"))
while (console.log("moo"));
break;
case null:
console.log("moz");
}
}
f();
f(42);
f(null);
}
expect_stdout: [
"foo",
"bar",
"baz",
"moo",
"baz",
"moo",
"moz",
]
}
switch_return_1: {
options = {
dead_code: true,
if_return: true,
}
input: {
function f(a) {
switch (a) {
case console.log("PASS"):
return;
break;
case 42:
FAIL;
}
}
f();
}
expect: {
function f(a) {
switch (a) {
case console.log("PASS"):
return;
case 42:
FAIL;
}
}
f();
}
expect_stdout: "PASS"
}
switch_return_2: {
options = {
if_return: true,
}
input: {
function f(a) {
switch (a) {
case console.log("PASS"):
if (console)
return;
break;
case 42:
FAIL;
}
}
f();
}
expect: {
function f(a) {
switch (a) {
case console.log("PASS"):
if (console);
break;
case 42:
FAIL;
}
}
f();
}
expect_stdout: "PASS"
}
switch_return_3: {
options = {
if_return: true,
side_effects: true,
}
input: {
function f(a) {
switch (a) {
case console.log("foo"):
if (console)
return void console.log("bar");
break;
case 42:
FAIL;
}
}
f();
}
expect: {
function f(a) {
switch (a) {
case console.log("foo"):
if (console)
console.log("bar");
break;
case 42:
FAIL;
}
}
f();
}
expect_stdout: [
"foo",
"bar",
]
}
switch_return_4: {
options = {
conditionals: true,
if_return: true,
side_effects: true,
}
input: {
function f(a) {
switch (a) {
case console.log("foo"):
if (console) {
console.log("bar");
return;
}
break;
case 42:
FAIL;
}
}
f();
}
expect: {
function f(a) {
switch (a) {
case console.log("foo"):
console && console.log("bar");
break;
case 42:
FAIL;
}
}
f();
}
expect_stdout: [
"foo",
"bar",
]
}