enhance switches (#5005)

closes #5004
This commit is contained in:
Alex Lam S.L 2021-06-15 03:32:39 +01:00 committed by GitHub
parent 6fc7a2ab6a
commit 498ac83541
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 321 additions and 89 deletions

View File

@ -8424,7 +8424,6 @@ merge(Compressor.prototype, {
OPT(AST_Switch, function(self, compressor) { OPT(AST_Switch, function(self, compressor) {
if (!compressor.option("switches")) return self; if (!compressor.option("switches")) return self;
var branch;
var value = self.expression.evaluate(compressor); var value = self.expression.evaluate(compressor);
if (!(value instanceof AST_Node)) { if (!(value instanceof AST_Node)) {
var orig = self.expression; var orig = self.expression;
@ -8435,10 +8434,12 @@ merge(Compressor.prototype, {
if (value instanceof AST_Node) { if (value instanceof AST_Node) {
value = self.expression.evaluate(compressor, true); value = self.expression.evaluate(compressor, true);
} }
var decl = [];
var body = []; var body = [];
var branch;
var decl = [];
var default_branch; var default_branch;
var exact_match; var exact_match;
var side_effects = [];
for (var i = 0, len = self.body.length; i < len && !exact_match; i++) { for (var i = 0, len = self.body.length; i < len && !exact_match; i++) {
branch = self.body[i]; branch = self.body[i];
if (branch instanceof AST_Default) { if (branch instanceof AST_Default) {
@ -8450,13 +8451,9 @@ merge(Compressor.prototype, {
default_branch = branch; default_branch = branch;
} }
} else if (!(value instanceof AST_Node)) { } else if (!(value instanceof AST_Node)) {
var exp = branch.expression.evaluate(compressor); var exp = branch.expression;
if (!(exp instanceof AST_Node) && exp !== value) { var val = exp.evaluate(compressor, true);
eliminate_branch(branch, body[body.length - 1]); if (val === value) {
continue;
}
if (exp instanceof AST_Node) exp = branch.expression.evaluate(compressor, true);
if (exp === value) {
exact_match = branch; exact_match = branch;
if (default_branch) { if (default_branch) {
var default_index = body.indexOf(default_branch); var default_index = body.indexOf(default_branch);
@ -8464,18 +8461,41 @@ merge(Compressor.prototype, {
eliminate_branch(default_branch, body[default_index - 1]); eliminate_branch(default_branch, body[default_index - 1]);
default_branch = null; default_branch = null;
} }
} else if (!(val instanceof AST_Node)) {
if (exp.has_side_effects(compressor)) side_effects.push(exp);
eliminate_branch(branch, body[body.length - 1]);
continue;
} }
} }
if (aborts(branch)) { if (exact_match || i == len - 1 || aborts(branch)) {
var prev = body[body.length - 1]; var prev = body[body.length - 1];
if (aborts(prev) && prev.body.length == branch.body.length var statements = branch.body;
&& make_node(AST_BlockStatement, prev, prev).equivalent_to(make_node(AST_BlockStatement, branch, branch))) { if (aborts(prev)) switch (prev.body.length - statements.length) {
prev.body = []; case 1:
var stat = prev.body[prev.body.length - 1];
if (!is_break(stat, compressor)) break;
statements = statements.concat(stat);
case 0:
var prev_block = make_node(AST_BlockStatement, prev, prev);
var next_block = make_node(AST_BlockStatement, branch, { body: statements });
if (prev_block.equivalent_to(next_block)) prev.body = [];
} }
} }
if (side_effects.length) {
if (branch instanceof AST_Default) {
body.push(make_node(AST_Case, self, { expression: make_sequence(self, side_effects), body: [] }));
} else {
side_effects.push(branch.expression);
branch.expression = make_sequence(self, side_effects);
}
side_effects = [];
}
body.push(branch); body.push(branch);
} }
while (i < len) eliminate_branch(self.body[i++], body[body.length - 1]); while (i < len) eliminate_branch(self.body[i++], body[body.length - 1]);
if (side_effects.length && !exact_match) {
body.push(make_node(AST_Case, self, { expression: make_sequence(self, side_effects), body: [] }));
}
while (branch = body[body.length - 1]) { while (branch = body[body.length - 1]) {
var stat = branch.body[branch.body.length - 1]; var stat = branch.body[branch.body.length - 1];
if (is_break(stat, compressor)) branch.body.pop(); if (is_break(stat, compressor)) branch.body.pop();
@ -8492,12 +8512,30 @@ merge(Compressor.prototype, {
eliminate_branch(branch); eliminate_branch(branch);
if (body.pop() === default_branch) default_branch = null; if (body.pop() === default_branch) default_branch = null;
} }
if (body.length == 0) { if (!branch) {
return make_node(AST_BlockStatement, self, { decl.push(make_node(AST_SimpleStatement, self.expression, { body: self.expression }));
body: decl.concat(make_node(AST_SimpleStatement, self.expression, { if (side_effects.length) decl.push(make_node(AST_SimpleStatement, self, {
body: self.expression body: make_sequence(self, side_effects),
})) }));
}).optimize(compressor); return make_node(AST_BlockStatement, self, { body: decl }).optimize(compressor);
}
if (exact_match ? !branch.expression.has_side_effects(compressor) : branch === default_branch) {
while (branch = body[body.length - 2]) {
if (branch instanceof AST_Default) break;
if (!has_declarations_only(branch)) break;
var exp = branch.expression;
if (exp.has_side_effects(compressor)) {
var prev = body[body.length - 3];
if (prev && !aborts(prev)) break;
if (exact_match) {
exact_match.expression = make_sequence(self, [ exp, exact_match.expression ]);
} else {
default_branch.body.unshift(make_node(AST_SimpleStatement, self, { body: exp }));
}
}
eliminate_branch(branch);
body.splice(-2, 1);
}
} }
body[0].body = decl.concat(body[0].body); body[0].body = decl.concat(body[0].body);
self.body = body; self.body = body;

View File

@ -2071,72 +2071,6 @@ issue_1670_2: {
} }
issue_1670_3: { issue_1670_3: {
options = {
comparisons: true,
conditionals: true,
dead_code: true,
evaluate: true,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
switches: true,
typeofs: true,
unused: true,
}
input: {
(function f() {
switch (1) {
case 0:
var a = true;
break;
case 1:
if (typeof a === "undefined") console.log("PASS");
else console.log("FAIL");
}
})();
}
expect: {
(function() {
var a;
void 0 === a ? console.log("PASS") : console.log("FAIL");
})();
}
expect_stdout: "PASS"
}
issue_1670_4: {
options = {
conditionals: true,
dead_code: true,
evaluate: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
switches: true,
unused: true,
}
input: {
(function f() {
switch (1) {
case 0:
var a = true;
break;
case 1:
if (typeof a === "undefined") console.log("PASS");
else console.log("FAIL");
}
})();
}
expect: {
(function() {
console.log("PASS");
})();
}
expect_stdout: "PASS"
}
issue_1670_5: {
options = { options = {
conditionals: true, conditionals: true,
dead_code: true, dead_code: true,
@ -2168,7 +2102,7 @@ issue_1670_5: {
expect_stdout: "1" expect_stdout: "1"
} }
issue_1670_6: { issue_1670_4: {
options = { options = {
conditionals: true, conditionals: true,
dead_code: true, dead_code: true,

View File

@ -300,6 +300,37 @@ drop_default_2: {
} }
} }
drop_default_3: {
options = {
dead_code: true,
evaluate: true,
switches: true,
}
input: {
function f() {
console.log("PASS");
return 42;
}
switch (42) {
case f():
break;
case void console.log("FAIL"):
default:
}
}
expect: {
function f() {
console.log("PASS");
return 42;
}
switch (42) {
case f():
case void console.log("FAIL"):
}
}
expect_stdout: "PASS"
}
keep_default: { keep_default: {
options = { options = {
dead_code: true, dead_code: true,
@ -423,7 +454,6 @@ drop_case_3: {
switch ({}.p) { switch ({}.p) {
default: default:
case void 0: case void 0:
break;
case c = "FAIL": case c = "FAIL":
} }
console.log(c); console.log(c);
@ -454,7 +484,166 @@ drop_case_4: {
expect_stdout: "PASS" expect_stdout: "PASS"
} }
keep_case: { drop_case_5: {
options = {
dead_code: true,
evaluate: true,
switches: true,
}
input: {
switch (42) {
case void console.log("PASS 1"):
console.log("FAIL 1");
case 42:
case console.log("FAIL 2"):
console.log("PASS 2");
}
}
expect: {
switch (42) {
case (void console.log("PASS 1"), 42):
console.log("PASS 2");
}
}
expect_stdout: [
"PASS 1",
"PASS 2",
]
}
drop_case_6: {
options = {
dead_code: true,
evaluate: true,
switches: true,
}
input: {
switch (console.log("PASS 1"), 2) {
case 0:
console.log("FAIL 1");
case (console.log("PASS 2"), 1):
console.log("FAIL 2");
}
}
expect: {
switch (console.log("PASS 1"), 2) {
case (console.log("PASS 2"), 1):
}
}
expect_stdout: [
"PASS 1",
"PASS 2",
]
}
drop_case_7: {
options = {
dead_code: true,
evaluate: true,
switches: true,
}
input: {
switch (2) {
case 0:
console.log("FAIL 1");
case (console.log("PASS 1"), 1):
console.log("FAIL 2");
case 2:
console.log("PASS 2");
}
}
expect: {
switch (2) {
case (console.log("PASS 1"), 1, 2):
console.log("PASS 2");
}
}
expect_stdout: [
"PASS 1",
"PASS 2",
]
}
drop_case_8: {
options = {
dead_code: true,
switches: true,
}
input: {
function log(msg) {
console.log(msg);
return msg;
}
switch (log("foo")) {
case "bar":
log("moo");
break;
case log("baz"):
log("moo");
break;
default:
log("moo");
}
}
expect: {
function log(msg) {
console.log(msg);
return msg;
}
switch (log("foo")) {
case "bar":
case log("baz"):
default:
log("moo");
}
}
expect_stdout: [
"foo",
"baz",
"moo",
]
}
drop_case_9: {
options = {
dead_code: true,
switches: true,
}
input: {
function log(msg) {
console.log(msg);
return msg;
}
switch (log("foo")) {
case log("bar"):
log("moo");
break;
case "baz":
log("moo");
break;
default:
log("moo");
}
}
expect: {
function log(msg) {
console.log(msg);
return msg;
}
switch (log("foo")) {
default:
log("bar");
log("moo");
}
}
expect_stdout: [
"foo",
"bar",
"moo",
]
}
keep_case_1: {
options = { options = {
dead_code: true, dead_code: true,
switches: true, switches: true,
@ -474,6 +663,76 @@ keep_case: {
} }
} }
keep_case_2: {
options = {
dead_code: true,
evaluate: true,
switches: true,
}
input: {
switch ("foo") {
case console.log("bar"):
case console.log("baz"), "moo":
}
}
expect: {
switch ("foo") {
case console.log("bar"):
case console.log("baz"), "moo":
}
}
expect_stdout: [
"bar",
"baz",
]
}
keep_case_3: {
options = {
dead_code: true,
evaluate: true,
switches: true,
}
input: {
var a;
switch (void console.log("PASS")) {
case a:
case console.log("FAIL"), 42:
}
}
expect: {
var a;
switch (void console.log("PASS")) {
case a:
case console.log("FAIL"), 42:
}
}
expect_stdout: "PASS"
}
keep_case_4: {
options = {
dead_code: true,
evaluate: true,
switches: true,
}
input: {
var a;
switch (void console.log("PASS")) {
case a:
case void console.log("FAIL"):
}
}
expect: {
var a;
switch (void console.log("PASS")) {
case a:
case void console.log("FAIL"):
}
}
expect_stdout: "PASS"
}
issue_376: { issue_376: {
options = { options = {
dead_code: true, dead_code: true,
@ -1088,7 +1347,8 @@ drop_switch_6: {
} }
} }
expect: { expect: {
A === B; A;
B;
x(); x();
C !== D; C !== D;
y(); y();