Skip to content

Commit

Permalink
Convert simple switch statements into if statements
Browse files Browse the repository at this point in the history
  • Loading branch information
jridgewell committed Aug 13, 2021
1 parent 34c1858 commit ae63f4f
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 91 deletions.
154 changes: 108 additions & 46 deletions lib/compress/index.js
Expand Up @@ -1615,41 +1615,6 @@ def_optimize(AST_Switch, function(self, compressor) {
}
}

// If all cases fall through into the final branch, and a default is present anywhere,
// we can collapse all branches.
if (default_branch) {
let i = 0;
for (; i < body.length; i++) {
let branch = body[i];
if (branch.body.length > 0) break;
if (branch === default_branch) continue;
// It's actually ok for the last branch's expression to have a side-effect
if (branch.expression.has_side_effects(compressor)) break;
}
// 3 cases:
// 1. i < body.length - 1, there's a side-effect/non-empty branch, and nothing to optimize.
// 2. i === body.length - 1, the last branch has a side-effect/non-empty, but that can be collapsed.
// 3. i === body.length, all branches are empty, and we can pretend only an empty default branch exists.
if (i >= body.length - 1) {
if (i < body.length) {
let last = body[i];
if (last !== default_branch) {
default_branch.body = make_node(AST_BlockStatement, last, {
body: [
make_node(AST_SimpleStatement, last.expression, { body: last.expression })
].concat(last.body),
}).optimize(compressor);
}
}
body = [default_branch];
}
}

if (body.length > 0) {
body[0].body = decl.concat(body[0].body);
}
self.body = body;

// Try to prune any empty branches at the end of the switch statement.
{
let i = body.length - 1;
Expand Down Expand Up @@ -1684,14 +1649,41 @@ def_optimize(AST_Switch, function(self, compressor) {
body.length = pointer;
}
}

// Collapse branches into the default.
if (default_branch) {
let i = find_significant_branch_index(body, 0, 1);
// 3 cases:
if (i >= body.length - 1) {
// 1. i === body.length - 1, the last branch has a side-effect/non-empty, but that can be collapsed.
// 2. i === body.length, all branches are empty, and we can pretend only an empty default branch exists.
if (i < body.length) {
let last = body[i];
if (last !== default_branch) {
default_branch.body = make_node(AST_BlockStatement, last, {
body: [
make_node(AST_SimpleStatement, last.expression, { body: last.expression })
].concat(last.body),
}).optimize(compressor);
}
}
body = [default_branch];
}
}

if (body.length > 0) {
body[0].body = decl.concat(body[0].body);
}
self.body = body;

if (body.length == 0) {
return make_node(AST_BlockStatement, self, {
body: decl.concat(make_node(AST_SimpleStatement, self.expression, {
body: self.expression
}))
}).optimize(compressor);
}
if (body.length == 1 && (body[0] === exact_match || body[0] === default_branch)) {
if (body.length == 1) {
var has_break = false;
var tw = new TreeWalker(function(node) {
if (has_break
Expand All @@ -1702,18 +1694,76 @@ def_optimize(AST_Switch, function(self, compressor) {
});
self.walk(tw);
if (!has_break) {
var statements = body[0].body.slice();
var exp = body[0].expression;
if (exp) statements.unshift(make_node(AST_SimpleStatement, exp, {
body: exp
}));
statements.unshift(make_node(AST_SimpleStatement, self.expression, {
body:self.expression
}));
return make_node(AST_BlockStatement, self, {
body: statements
let branch = body[0];
if (branch === exact_match || branch === default_branch) {
let statements = [make_node(AST_SimpleStatement, self.expression, {
body: self.expression
})];
if (branch.expression) {
statements.push(make_node(AST_SimpleStatement, branch.expression, {
body: branch.expression
}));
}
return make_node(AST_BlockStatement, branch, {
body: statements.concat(branch.body)
}).optimize(compressor);
} else {
return make_node(AST_If, self, {
condition: make_node(AST_Binary, self, {
operator: "===",
left: self.expression,
right: branch.expression,
}),
body: make_node(AST_BlockStatement, branch, {
body: branch.body
}),
alternative: null
}).optimize(compressor);
}
}
}
if (body.length === 2 && default_branch) {
let branch = body[0] === default_branch ? body[1] : body[0];
if (aborts(body[0])) {
body[0].body.pop();
return make_node(AST_If, self, {
condition: make_node(AST_Binary, self, {
operator: "===",
left: self.expression,
right: branch.expression,
}),
body: make_node(AST_BlockStatement, branch, {
body: branch.body
}),
alternative: make_node(AST_BlockStatement, default_branch, {
body: default_branch.body
})
}).optimize(compressor);
}
let operator = "===";
let consequent = branch;
let always = default_branch;
if (body[0] === default_branch) {
operator = "!==";
consequent = default_branch;
always = branch;
}
return make_node(AST_BlockStatement, self, {
body: [
make_node(AST_If, self, {
condition: make_node(AST_Binary, self, {
operator: operator,
left: self.expression,
right: branch.expression,
}),
body: make_node(AST_BlockStatement, consequent, {
body: consequent.body,
}),
alternative: null
})
].concat(always.body)
}).optimize(compressor);

}
return self;

Expand All @@ -1735,6 +1785,18 @@ def_optimize(AST_Switch, function(self, compressor) {
let pblock = make_node(AST_BlockStatement, prev, { body: pbody });
return bblock.equivalent_to(pblock);
}
function find_significant_branch_index(body, start_index, direction) {
let i = start_index;
for (; i < body.length && i >= 0; i += direction) {
if (is_significant_branch(body[i])) return i;
}
return i;
}
function is_significant_branch(branch) {
if (branch.body.length > 0) return true;
if (branch instanceof AST_Default) return false;
return branch.expression.has_side_effects(compressor);
}
});

def_optimize(AST_Try, function(self, compressor) {
Expand Down
4 changes: 1 addition & 3 deletions test/compress/functions.js
Expand Up @@ -1328,9 +1328,7 @@ issue_2620_4: {
expect: {
var c = "FAIL";
!function() {
switch (NaN) {
case void (c = "PASS"):
}
if (NaN === void (c = "PASS"));
}();
console.log(c);
}
Expand Down
10 changes: 2 additions & 8 deletions test/compress/issue-1750.js
Expand Up @@ -16,10 +16,7 @@ case_1: {
}
expect: {
var a = 0, b = 1;
switch (true) {
case a || true:
b = 2;
}
if (true === (a || true)) b = 2;
console.log(a, b);
}
expect_stdout: "0 2"
Expand All @@ -44,10 +41,7 @@ case_2: {
}
expect: {
var a = 0, b = 1;
switch (0) {
case a:
a = 3;
}
if (0 === a) a = 3;
console.log(a, b);
}
expect_stdout: "3 1"
Expand Down
9 changes: 2 additions & 7 deletions test/compress/reduce_vars.js
Expand Up @@ -1947,13 +1947,8 @@ issue_1670_6: {
}
expect: {
(function(a) {
switch (1) {
case a = 1:
console.log(a);
break;
default:
console.log(2);
}
if (1 === (a = 1)) console.log(a);
else console.log(2);
})(1);
}
expect_stdout: "1"
Expand Down

0 comments on commit ae63f4f

Please sign in to comment.