From ad2efee6fed227c32aa90c5b1f937348915f9d5d Mon Sep 17 00:00:00 2001 From: alexlamsl Date: Sat, 30 Jul 2022 07:47:43 +0800 Subject: [PATCH] fix corner cases in `if_return` fixes #5583 fixes #5584 fixes #5586 --- lib/compress.js | 49 ++++++-- test/compress/if_return.js | 239 +++++++++++++++++++++++++++++++++++++ test/reduce.js | 2 +- 3 files changed, 277 insertions(+), 13 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 327b359a5aa..3280049ec97 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -3547,7 +3547,6 @@ Compressor.prototype.compress = function(node) { if (stat instanceof AST_If && stat.body instanceof AST_Return) { var value = stat.body.value; var in_bool = stat.body.in_bool || next instanceof AST_Return && next.in_bool; - //--- // if (foo()) return x; return y; ---> return foo() ? x : y; if (!stat.alternative && next instanceof AST_Return) { changed = true; @@ -3558,16 +3557,33 @@ Compressor.prototype.compress = function(node) { statements.splice(j, 1); continue; } - //--- - // if (foo()) return x; [ return ; ] ---> return foo() ? x : undefined; - if (!stat.alternative && !next && in_lambda && (in_bool || value && multiple_if_returns)) { - changed = true; - stat = stat.clone(); - stat.alternative = make_node(AST_Return, stat, { value: null }); - statements.splice(i, 1, stat.transform(compressor)); - continue; + if (!stat.alternative && !next && in_lambda) { + // if (foo()) return x; [ return ; ] ---> return foo() ? x : undefined; + if (in_bool || value && multiple_if_returns) { + changed = true; + stat = stat.clone(); + stat.alternative = make_node(AST_Return, stat, { value: null }); + statements.splice(i, 1, stat.transform(compressor)); + continue; + } + // if (foo()) return bar() ? x : void 0; ---> return foo() && bar() ? x : void 0; + // if (foo()) return bar() ? void 0 : x; ---> return foo() || bar() ? void 0 : x; + var or; + if (value instanceof AST_Conditional + && ((or = is_undefined(value.consequent, compressor)) + || is_undefined(value.alternative, compressor))) { + changed = true; + var ret = stat.body.clone(); + ret.value = value.clone(); + ret.value.condition = make_node(AST_Binary, stat, { + operator: or ? "||" : "&&", + left: stat.condition, + right: value.condition, + }); + statements.splice(i, 1, ret.transform(compressor)); + continue; + } } - //--- // if (a) return b; if (c) return d; e; ---> return a ? b : c ? d : void e; // // if sequences is not enabled, this can lead to an endless loop (issue #866). @@ -3647,14 +3663,23 @@ Compressor.prototype.compress = function(node) { if (!(ab instanceof AST_Return)) return false; var value = ab.value; if (value && !is_undefined(value.tail_node())) return false; - if (self instanceof AST_SwitchBranch) merge_jump = 4; + if (!(self instanceof AST_SwitchBranch)) return true; + if (jump instanceof AST_Break) { + merge_jump = 4; + } else if (jump instanceof AST_Exit && !jump.value) { + merge_jump = true; + } return true; } if (!(ab instanceof AST_LoopControl)) return false; var lct = compressor.loopcontrol_target(ab); + if (jump && self instanceof AST_SwitchBranch) { + if (jump instanceof AST_Exit && jump.value) return false; + if (compressor.loopcontrol_target(jump) instanceof AST_IterationStatement) return false; + merge_jump = true; + } 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); } diff --git a/test/compress/if_return.js b/test/compress/if_return.js index ac7cb5cd714..2b41e951951 100644 --- a/test/compress/if_return.js +++ b/test/compress/if_return.js @@ -218,6 +218,79 @@ if_return_8: { } } +if_return_cond_void_1: { + options = { + if_return: true, + } + input: { + function f(a) { + if (a) + return console.log("foo") ? console.log("bar") : void 0; + } + f(); + f(42); + } + expect: { + function f(a) { + return a && console.log("foo") ? console.log("bar") : void 0; + } + f(); + f(42); + } + expect_stdout: "foo" +} + +if_return_cond_void_2: { + options = { + if_return: true, + } + input: { + function f(a) { + if (a) + return console.log("foo") ? void 0 : console.log("bar"); + } + f(); + f(42); + } + expect: { + function f(a) { + return a || console.log("foo") ? void 0 : console.log("bar"); + } + f(); + f(42); + } + expect_stdout: [ + "foo", + "bar", + ] +} + +if_return_cond_void_3: { + options = { + if_return: true, + } + input: { + function f(a) { + if (a) + return console.log("foo") ? void console.log("bar") : void console.log("baz"); + } + f(); + f(42); + } + expect: { + function f(a) { + if (a) + return console.log("foo") ? void console.log("bar") : void console.log("baz"); + } + f(); + f(42); + } + expect_stdout: [ + "foo", + "baz", + ] +} + issue_1089: { options = { booleans: true, @@ -1588,3 +1661,169 @@ switch_return_4: { "bar", ] } + +switch_return_5: { + options = { + dead_code: true, + if_return: true, + } + input: { + function f(a) { + switch (console.log("foo")) { + case console.log("bar"): + if (a) + return; + return; + break; + case null: + FAIL; + } + } + f(); + f(42); + } + expect: { + function f(a) { + switch (console.log("foo")) { + case console.log("bar"): + if (a); + return; + case null: + FAIL; + } + } + f(); + f(42); + } + expect_stdout: [ + "foo", + "bar", + "foo", + "bar", + ] +} + +issue_5583: { + options = { + conditionals: true, + if_return: true, + side_effects: true, + } + input: { + do { + switch (console) { + default: + if (!console.log("foo")) + continue; + break; + case console.log("bar"): + FAIL; + } + } while (console.log("baz")); + } + expect: { + do { + switch (console) { + default: + console.log("foo"); + break; + case console.log("bar"): + FAIL; + } + } while (console.log("baz")); + } + expect_stdout: [ + "bar", + "foo", + "baz", + ] +} + +issue_5584_1: { + options = { + conditionals: true, + if_return: true, + } + input: { + function f(a) { + switch (a) { + case 42: + if (!console.log("PASS")) + return; + return FAIL; + } + } + f(42); + } + expect: { + function f(a) { + switch (a) { + case 42: + if (console.log("PASS")) + return FAIL; + } + } + f(42); + } + expect_stdout: "PASS" +} + +issue_5584_2: { + options = { + if_return: true, + } + input: { + function f(a) { + switch (a) { + case console.log("PASS"): + if (console) + break; + return FAIL; + } + } + f(); + } + expect: { + function f(a) { + switch (a) { + case console.log("PASS"): + if (console) + break; + return FAIL; + } + } + f(); + } + expect_stdout: "PASS" +} + +issue_5586: { + options = { + if_return: true, + } + input: { + L: do { + switch (console.log("foo")) { + case console.log("bar"): + if (console) + break; + break L; + } + } while (console.log("baz")); + } + expect: { + L: do { + switch (console.log("foo")) { + case console.log("bar"): + if (console) + break; + break L; + } + } while (console.log("baz")); + } + expect_stdout: [ + "foo", + "bar", + "baz", + ] +} diff --git a/test/reduce.js b/test/reduce.js index 82670842a1b..696914bd8ee 100644 --- a/test/reduce.js +++ b/test/reduce.js @@ -466,7 +466,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options) } } else if (node instanceof U.AST_VarDef) { - if (node.value) { + if (node.value && !(parent instanceof U.AST_Const)) { node.start._permute++; CHANGED = true; return new U.AST_VarDef({