From 2cc073f753254ce58d11676f834ed540983cddcd Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Thu, 12 Aug 2021 23:10:17 -0400 Subject: [PATCH] Convert simple switch statements into if statements --- lib/compress/index.js | 154 ++++++++++++++++++++++++++++------------ test/compress/switch.js | 118 ++++++++++++++++++++++-------- 2 files changed, 197 insertions(+), 75 deletions(-) diff --git a/lib/compress/index.js b/lib/compress/index.js index a6a93fe67..0a5f99c99 100644 --- a/lib/compress/index.js +++ b/lib/compress/index.js @@ -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; @@ -1684,6 +1649,33 @@ 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, { @@ -1691,7 +1683,7 @@ def_optimize(AST_Switch, function(self, compressor) { })) }).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 @@ -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; @@ -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) { diff --git a/test/compress/switch.js b/test/compress/switch.js index 8e889d94f..2a6d52fe1 100644 --- a/test/compress/switch.js +++ b/test/compress/switch.js @@ -266,9 +266,7 @@ drop_default_1: { } } expect: { - switch (foo) { - case 'bar': baz(); - } + if ("bar" === foo) baz(); } } @@ -285,9 +283,7 @@ drop_default_2: { } } expect: { - switch (foo) { - case 'bar': baz(); - } + if ("bar" === foo) baz(); } } @@ -305,11 +301,8 @@ keep_default: { } } expect: { - switch (foo) { - case 'bar': baz(); - default: - something(); - } + if ('bar' === foo) baz(); + something(); } } @@ -540,10 +533,7 @@ collapse_into_default_8: { } expect: { console.log(function (foo) { - switch (foo) { - case 1: - return 1; - } + if (1 === foo) return 1; }(1)); } } @@ -595,9 +585,7 @@ drop_case: { } } expect: { - switch (foo) { - case 'bar': baz(); - } + if ('bar' === foo) baz(); } } @@ -621,6 +609,84 @@ keep_case: { } } +if_else: { + options = { + dead_code: true, + switches: true, + } + input: { + switch (foo) { + case 'bar': + bar(); + break; + default: + other(); + } + } + expect: { + if ('bar' === foo) bar(); + else other(); + } +} + +if_else2: { + options = { + dead_code: true, + switches: true, + } + input: { + switch (foo) { + case 'bar': + bar(); + default: + other(); + } + } + expect: { + if ('bar' === foo) bar(); + other(); + } +} + +if_else3: { + options = { + dead_code: true, + switches: true, + } + input: { + switch (foo) { + default: + other(); + break; + case 'bar': + bar(); + } + } + expect: { + if ('bar' === foo) bar(); + else other(); + } +} + +if_else4: { + options = { + dead_code: true, + switches: true, + } + input: { + switch (foo) { + default: + other(); + case 'bar': + bar(); + } + } + expect: { + if ('bar' !== foo) other(); + bar(); + } +} + issue_376: { options = { dead_code: true, @@ -638,10 +704,7 @@ issue_376: { } } expect: { - switch (true) { - case boolCondition: - console.log(1); - } + if (true === boolCondition) console.log(1); } } @@ -801,6 +864,8 @@ issue_1679: { dead_code: true, evaluate: true, switches: true, + conditionals: true, + side_effects: true, } input: { var a = 100, b = 10; @@ -830,7 +895,6 @@ issue_1679: { case !function x() {}: break; case b--: - 0; a--; case (a++): } @@ -838,7 +902,7 @@ issue_1679: { f(); console.log(a, b); } - expect_stdout: true + expect_stdout: ["99 8"] } issue_1680_1: { @@ -998,11 +1062,7 @@ issue_1705_1: { } expect: { var a = 0; - switch (a) { - default: - console.log("FAIL"); - case 0: - } + if (0 !== a) console.log("FAIL"); } expect_stdout: true }