Skip to content

Commit 08c386f

Browse files
authored
enhance if_return (#5582)
1 parent 937a672 commit 08c386f

File tree

2 files changed

+240
-25
lines changed

2 files changed

+240
-25
lines changed

lib/compress.js

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1965,6 +1965,12 @@ Compressor.prototype.compress = function(node) {
19651965
block = compressor.parent(level++);
19661966
} else if (block instanceof AST_LabeledStatement) {
19671967
block = block.body;
1968+
} else if (block instanceof AST_SwitchBranch) {
1969+
var branches = compressor.parent(level);
1970+
if (branches.body[branches.body.length - 1] === block || has_break(block.body)) {
1971+
level++;
1972+
block = branches;
1973+
}
19681974
}
19691975
do {
19701976
stat = block;
@@ -1975,8 +1981,16 @@ Compressor.prototype.compress = function(node) {
19751981
&& (block instanceof AST_BlockStatement
19761982
|| block instanceof AST_Catch
19771983
|| block instanceof AST_Scope
1984+
|| block instanceof AST_SwitchBranch
19781985
|| block instanceof AST_Try)
19791986
&& is_last_statement(block.body, stat));
1987+
1988+
function has_break(stats) {
1989+
for (var i = stats.length; --i >= 0;) {
1990+
if (stats[i] instanceof AST_Break) return true;
1991+
}
1992+
return false;
1993+
}
19801994
}
19811995

19821996
function find_loop_scope_try() {
@@ -3437,7 +3451,7 @@ Compressor.prototype.compress = function(node) {
34373451
var changed = false;
34383452
var parent = compressor.parent();
34393453
var self = compressor.self();
3440-
var exit, merge_exit;
3454+
var jump, merge_jump;
34413455
var in_iife = in_lambda && parent && parent.TYPE == "Call" && parent.expression === self;
34423456
var chain_if_returns = in_lambda && compressor.option("conditionals") && compressor.option("sequences");
34433457
var multiple_if_returns = has_multiple_if_returns(statements);
@@ -3447,6 +3461,7 @@ Compressor.prototype.compress = function(node) {
34473461
var next = statements[j];
34483462

34493463
if (in_lambda && !next && stat instanceof AST_Return
3464+
&& !(self instanceof AST_SwitchBranch)
34503465
&& !(in_try && in_try.bfinally && in_async_generator(in_lambda))) {
34513466
var body = stat.value;
34523467
if (!body) {
@@ -3494,7 +3509,7 @@ Compressor.prototype.compress = function(node) {
34943509
stat.condition = cond;
34953510
statements[j] = stat.body;
34963511
stat.body = next;
3497-
if (next === exit) exit = null;
3512+
if (next === jump) jump = null;
34983513
statements[i] = stat;
34993514
statements[i] = stat.transform(compressor);
35003515
continue;
@@ -3538,7 +3553,7 @@ Compressor.prototype.compress = function(node) {
35383553
changed = true;
35393554
stat = stat.clone();
35403555
stat.alternative = next;
3541-
if (next === exit) exit = null;
3556+
if (next === jump) jump = null;
35423557
statements.splice(i, 1, stat.transform(compressor));
35433558
statements.splice(j, 1);
35443559
continue;
@@ -3583,12 +3598,12 @@ Compressor.prototype.compress = function(node) {
35833598
}
35843599
}
35853600

3586-
if (stat instanceof AST_Exit) {
3587-
exit = stat;
3601+
if (stat instanceof AST_Break || stat instanceof AST_Exit) {
3602+
jump = stat;
35883603
continue;
35893604
}
35903605

3591-
if (exit && exit === next) eliminate_returns(stat);
3606+
if (jump && jump === next) eliminate_returns(stat);
35923607
}
35933608
return changed;
35943609

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

36123627
function match_return(ab, exact) {
3613-
if (!exit) return false;
3614-
if (exit.TYPE != ab.TYPE) return false;
3628+
if (!jump) return false;
3629+
if (jump.TYPE != ab.TYPE) return false;
36153630
var value = ab.value;
36163631
if (!value) return false;
3617-
var equals = exit.equals(ab);
3632+
var equals = jump.equals(ab);
36183633
if (!equals && value instanceof AST_Sequence) {
36193634
value = value.tail_node();
3620-
if (exit.value && exit.value.equals(value)) equals = 2;
3635+
if (jump.value && jump.value.equals(value)) equals = 2;
36213636
}
3622-
if (!equals && !exact && exit.value instanceof AST_Sequence) {
3623-
if (exit.value.tail_node().equals(value)) equals = 3;
3637+
if (!equals && !exact && jump.value instanceof AST_Sequence) {
3638+
if (jump.value.tail_node().equals(value)) equals = 3;
36243639
}
36253640
return equals;
36263641
}
36273642

36283643
function can_drop_abort(ab) {
36293644
if (ab instanceof AST_Exit) {
3630-
if (merge_exit = match_return(ab)) return true;
3645+
if (merge_jump = match_return(ab)) return true;
36313646
if (!in_lambda) return false;
36323647
if (!(ab instanceof AST_Return)) return false;
3633-
if (is_undefined(ab.value)) return true;
3634-
return ab.value instanceof AST_Sequence && is_undefined(ab.value.tail_node());
3648+
var value = ab.value;
3649+
if (value && !is_undefined(value.tail_node())) return false;
3650+
if (self instanceof AST_SwitchBranch) merge_jump = 4;
3651+
return true;
36353652
}
36363653
if (!(ab instanceof AST_LoopControl)) return false;
36373654
var lct = compressor.loopcontrol_target(ab);
36383655
if (ab instanceof AST_Continue) return match_target(loop_body(lct));
36393656
if (lct instanceof AST_IterationStatement) return false;
3657+
if (jump) merge_jump = jump.equals(ab);
36403658
return match_target(lct);
36413659
}
36423660

36433661
function can_merge_flow(ab) {
3644-
merge_exit = false;
3662+
merge_jump = false;
36453663
if (!can_drop_abort(ab)) return false;
36463664
for (var j = statements.length; --j > i;) {
36473665
var stat = statements[j];
@@ -3663,12 +3681,12 @@ Compressor.prototype.compress = function(node) {
36633681
var lexical = false;
36643682
var start = i + 1;
36653683
var end;
3666-
if (merge_exit) {
3667-
end = statements.lastIndexOf(exit);
3684+
if (merge_jump) {
3685+
end = statements.lastIndexOf(jump);
36683686
if (end < 0) end = statements.length;
36693687
} else {
36703688
end = statements.length;
3671-
exit = null;
3689+
jump = null;
36723690
}
36733691
var tail = statements.splice(start, end - start).filter(function(stat) {
36743692
if (stat instanceof AST_LambdaDefinition) {
@@ -3678,18 +3696,20 @@ Compressor.prototype.compress = function(node) {
36783696
if (is_lexical_definition(stat)) lexical = true;
36793697
return true;
36803698
});
3681-
if (merge_exit === 3) {
3682-
tail.push(make_node(AST_SimpleStatement, exit.value, {
3683-
body: make_sequence(exit.value, exit.value.expressions.slice(0, -1)),
3699+
if (merge_jump === 3) {
3700+
tail.push(make_node(AST_SimpleStatement, jump.value, {
3701+
body: make_sequence(jump.value, jump.value.expressions.slice(0, -1)),
36843702
}));
3685-
exit.value = exit.value.tail_node();
3703+
jump.value = jump.value.tail_node();
36863704
}
36873705
[].push.apply(lexical ? tail : statements, defuns);
36883706
return tail;
36893707
}
36903708

36913709
function trim_return(value, mode) {
36923710
if (value) switch (mode) {
3711+
case 4:
3712+
return value;
36933713
case 3:
36943714
if (!(value instanceof AST_Sequence)) break;
36953715
case 2:
@@ -3705,7 +3725,7 @@ Compressor.prototype.compress = function(node) {
37053725
}
37063726
block.pop();
37073727
var value = ab.value;
3708-
if (merge_exit) value = trim_return(value, merge_exit);
3728+
if (merge_jump) value = trim_return(value, merge_jump);
37093729
if (value) block.push(make_node(AST_SimpleStatement, value, { body: value }));
37103730
return body;
37113731
}
@@ -3757,7 +3777,7 @@ Compressor.prototype.compress = function(node) {
37573777
} else if (stat instanceof AST_LabeledStatement) {
37583778
stat.body = eliminate_returns(stat.body);
37593779
} else if (stat instanceof AST_Try) {
3760-
if (!stat.bfinally || !exit.value || exit.value.is_constant()) {
3780+
if (!stat.bfinally || !jump.value || jump.value.is_constant()) {
37613781
if (stat.bcatch) eliminate_returns(stat.bcatch);
37623782
var trimmed = eliminate_returns(stat.body.pop(), true);
37633783
if (trimmed) stat.body.push(trimmed);

test/compress/if_return.js

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,3 +1393,198 @@ void_match: {
13931393
"foo",
13941394
]
13951395
}
1396+
1397+
switch_break: {
1398+
options = {
1399+
conditionals: true,
1400+
if_return: true,
1401+
}
1402+
input: {
1403+
function f(a) {
1404+
switch (a) {
1405+
default:
1406+
if (console.log("foo"))
1407+
break;
1408+
while (console.log("bar"));
1409+
case 42:
1410+
if (console.log("baz"))
1411+
break;
1412+
while (console.log("moo"));
1413+
break;
1414+
case null:
1415+
if (console.log("moz"))
1416+
break;
1417+
}
1418+
}
1419+
f();
1420+
f(42);
1421+
f(null);
1422+
}
1423+
expect: {
1424+
function f(a) {
1425+
switch (a) {
1426+
default:
1427+
if (console.log("foo"))
1428+
break;
1429+
while (console.log("bar"));
1430+
case 42:
1431+
if (!console.log("baz"))
1432+
while (console.log("moo"));
1433+
break;
1434+
case null:
1435+
console.log("moz");
1436+
}
1437+
}
1438+
f();
1439+
f(42);
1440+
f(null);
1441+
}
1442+
expect_stdout: [
1443+
"foo",
1444+
"bar",
1445+
"baz",
1446+
"moo",
1447+
"baz",
1448+
"moo",
1449+
"moz",
1450+
]
1451+
}
1452+
1453+
switch_return_1: {
1454+
options = {
1455+
dead_code: true,
1456+
if_return: true,
1457+
}
1458+
input: {
1459+
function f(a) {
1460+
switch (a) {
1461+
case console.log("PASS"):
1462+
return;
1463+
break;
1464+
case 42:
1465+
FAIL;
1466+
}
1467+
}
1468+
f();
1469+
}
1470+
expect: {
1471+
function f(a) {
1472+
switch (a) {
1473+
case console.log("PASS"):
1474+
return;
1475+
case 42:
1476+
FAIL;
1477+
}
1478+
}
1479+
f();
1480+
}
1481+
expect_stdout: "PASS"
1482+
}
1483+
1484+
switch_return_2: {
1485+
options = {
1486+
if_return: true,
1487+
}
1488+
input: {
1489+
function f(a) {
1490+
switch (a) {
1491+
case console.log("PASS"):
1492+
if (console)
1493+
return;
1494+
break;
1495+
case 42:
1496+
FAIL;
1497+
}
1498+
}
1499+
f();
1500+
}
1501+
expect: {
1502+
function f(a) {
1503+
switch (a) {
1504+
case console.log("PASS"):
1505+
if (console);
1506+
break;
1507+
case 42:
1508+
FAIL;
1509+
}
1510+
}
1511+
f();
1512+
}
1513+
expect_stdout: "PASS"
1514+
}
1515+
1516+
switch_return_3: {
1517+
options = {
1518+
if_return: true,
1519+
side_effects: true,
1520+
}
1521+
input: {
1522+
function f(a) {
1523+
switch (a) {
1524+
case console.log("foo"):
1525+
if (console)
1526+
return void console.log("bar");
1527+
break;
1528+
case 42:
1529+
FAIL;
1530+
}
1531+
}
1532+
f();
1533+
}
1534+
expect: {
1535+
function f(a) {
1536+
switch (a) {
1537+
case console.log("foo"):
1538+
if (console)
1539+
console.log("bar");
1540+
break;
1541+
case 42:
1542+
FAIL;
1543+
}
1544+
}
1545+
f();
1546+
}
1547+
expect_stdout: [
1548+
"foo",
1549+
"bar",
1550+
]
1551+
}
1552+
1553+
switch_return_4: {
1554+
options = {
1555+
conditionals: true,
1556+
if_return: true,
1557+
side_effects: true,
1558+
}
1559+
input: {
1560+
function f(a) {
1561+
switch (a) {
1562+
case console.log("foo"):
1563+
if (console) {
1564+
console.log("bar");
1565+
return;
1566+
}
1567+
break;
1568+
case 42:
1569+
FAIL;
1570+
}
1571+
}
1572+
f();
1573+
}
1574+
expect: {
1575+
function f(a) {
1576+
switch (a) {
1577+
case console.log("foo"):
1578+
console && console.log("bar");
1579+
break;
1580+
case 42:
1581+
FAIL;
1582+
}
1583+
}
1584+
f();
1585+
}
1586+
expect_stdout: [
1587+
"foo",
1588+
"bar",
1589+
]
1590+
}

0 commit comments

Comments
 (0)