Skip to content

Commit

Permalink
enhance if_return (#5582)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexlamsl authored Jul 29, 2022
1 parent 937a672 commit 08c386f
Show file tree
Hide file tree
Showing 2 changed files with 240 additions and 25 deletions.
70 changes: 45 additions & 25 deletions lib/compress.js
Original file line number Diff line number Diff line change
Expand Up @@ -1965,6 +1965,12 @@ Compressor.prototype.compress = function(node) {
block = compressor.parent(level++);
} else if (block instanceof AST_LabeledStatement) {
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 {
stat = block;
Expand All @@ -1975,8 +1981,16 @@ Compressor.prototype.compress = function(node) {
&& (block instanceof AST_BlockStatement
|| block instanceof AST_Catch
|| block instanceof AST_Scope
|| block instanceof AST_SwitchBranch
|| block instanceof AST_Try)
&& 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() {
Expand Down Expand Up @@ -3437,7 +3451,7 @@ Compressor.prototype.compress = function(node) {
var changed = false;
var parent = compressor.parent();
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 chain_if_returns = in_lambda && compressor.option("conditionals") && compressor.option("sequences");
var multiple_if_returns = has_multiple_if_returns(statements);
Expand All @@ -3447,6 +3461,7 @@ Compressor.prototype.compress = function(node) {
var next = statements[j];

if (in_lambda && !next && stat instanceof AST_Return
&& !(self instanceof AST_SwitchBranch)
&& !(in_try && in_try.bfinally && in_async_generator(in_lambda))) {
var body = stat.value;
if (!body) {
Expand Down Expand Up @@ -3494,7 +3509,7 @@ Compressor.prototype.compress = function(node) {
stat.condition = cond;
statements[j] = stat.body;
stat.body = next;
if (next === exit) exit = null;
if (next === jump) jump = null;
statements[i] = stat;
statements[i] = stat.transform(compressor);
continue;
Expand Down Expand Up @@ -3538,7 +3553,7 @@ Compressor.prototype.compress = function(node) {
changed = true;
stat = stat.clone();
stat.alternative = next;
if (next === exit) exit = null;
if (next === jump) jump = null;
statements.splice(i, 1, stat.transform(compressor));
statements.splice(j, 1);
continue;
Expand Down Expand Up @@ -3583,12 +3598,12 @@ Compressor.prototype.compress = function(node) {
}
}

if (stat instanceof AST_Exit) {
exit = stat;
if (stat instanceof AST_Break || stat instanceof AST_Exit) {
jump = stat;
continue;
}

if (exit && exit === next) eliminate_returns(stat);
if (jump && jump === next) eliminate_returns(stat);
}
return changed;

Expand All @@ -3610,38 +3625,41 @@ Compressor.prototype.compress = function(node) {
}

function match_return(ab, exact) {
if (!exit) return false;
if (exit.TYPE != ab.TYPE) return false;
if (!jump) return false;
if (jump.TYPE != ab.TYPE) return false;
var value = ab.value;
if (!value) return false;
var equals = exit.equals(ab);
var equals = jump.equals(ab);
if (!equals && value instanceof AST_Sequence) {
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 (exit.value.tail_node().equals(value)) equals = 3;
if (!equals && !exact && jump.value instanceof AST_Sequence) {
if (jump.value.tail_node().equals(value)) equals = 3;
}
return equals;
}

function can_drop_abort(ab) {
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 (!(ab instanceof AST_Return)) return false;
if (is_undefined(ab.value)) return true;
return ab.value instanceof AST_Sequence && is_undefined(ab.value.tail_node());
var value = ab.value;
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;
var lct = compressor.loopcontrol_target(ab);
if (ab instanceof AST_Continue) return match_target(loop_body(lct));
if (lct instanceof AST_IterationStatement) return false;
if (jump) merge_jump = jump.equals(ab);
return match_target(lct);
}

function can_merge_flow(ab) {
merge_exit = false;
merge_jump = false;
if (!can_drop_abort(ab)) return false;
for (var j = statements.length; --j > i;) {
var stat = statements[j];
Expand All @@ -3663,12 +3681,12 @@ Compressor.prototype.compress = function(node) {
var lexical = false;
var start = i + 1;
var end;
if (merge_exit) {
end = statements.lastIndexOf(exit);
if (merge_jump) {
end = statements.lastIndexOf(jump);
if (end < 0) end = statements.length;
} else {
end = statements.length;
exit = null;
jump = null;
}
var tail = statements.splice(start, end - start).filter(function(stat) {
if (stat instanceof AST_LambdaDefinition) {
Expand All @@ -3678,18 +3696,20 @@ Compressor.prototype.compress = function(node) {
if (is_lexical_definition(stat)) lexical = true;
return true;
});
if (merge_exit === 3) {
tail.push(make_node(AST_SimpleStatement, exit.value, {
body: make_sequence(exit.value, exit.value.expressions.slice(0, -1)),
if (merge_jump === 3) {
tail.push(make_node(AST_SimpleStatement, jump.value, {
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);
return tail;
}

function trim_return(value, mode) {
if (value) switch (mode) {
case 4:
return value;
case 3:
if (!(value instanceof AST_Sequence)) break;
case 2:
Expand All @@ -3705,7 +3725,7 @@ Compressor.prototype.compress = function(node) {
}
block.pop();
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 }));
return body;
}
Expand Down Expand Up @@ -3757,7 +3777,7 @@ Compressor.prototype.compress = function(node) {
} else if (stat instanceof AST_LabeledStatement) {
stat.body = eliminate_returns(stat.body);
} 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);
var trimmed = eliminate_returns(stat.body.pop(), true);
if (trimmed) stat.body.push(trimmed);
Expand Down
195 changes: 195 additions & 0 deletions test/compress/if_return.js
Original file line number Diff line number Diff line change
Expand Up @@ -1393,3 +1393,198 @@ void_match: {
"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",
]
}

0 comments on commit 08c386f

Please sign in to comment.