From a14555a39e7074b2161b183fe890a0449da8a064 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Sat, 12 Feb 2022 15:18:07 +0000 Subject: [PATCH] enhance `conditionals`, `if_return` & `side_effects` (#5348) --- lib/compress.js | 96 +++++++++++++++++------------------ test/compress/comparisons.js | 2 +- test/compress/const.js | 7 ++- test/compress/functions.js | 10 ++-- test/compress/if_return.js | 15 +++--- test/compress/issue-1052.js | 89 +++++++++++++++++--------------- test/compress/issue-1447.js | 7 ++- test/compress/issue-979.js | 53 ++++++++----------- test/compress/let.js | 11 ++-- test/compress/side_effects.js | 2 +- test/compress/typeof.js | 34 ++++++++----- 11 files changed, 162 insertions(+), 164 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index c92a774818..7eb228559c 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -3336,23 +3336,24 @@ Compressor.prototype.compress = function(node) { if (ab.label) remove(ab.label.thedef.references, ab); changed = true; stat = stat.clone(); - stat.condition = stat.condition.negate(compressor); - var body = as_statement_array_with_return(stat.body, ab); stat.body = make_node(AST_BlockStatement, stat, { + body: as_statement_array_with_return(stat.body, ab), + }); + stat.alternative = make_node(AST_BlockStatement, stat, { body: as_statement_array(stat.alternative).concat(extract_functions()), }); - stat.alternative = make_node(AST_BlockStatement, stat, { body: body }); statements[i] = stat; statements[i] = stat.transform(compressor); continue; } - if (ab && !stat.alternative && stat.body instanceof AST_BlockStatement && next instanceof AST_Jump) { - var negated = stat.condition.negate(compressor); - if (negated.print_to_string().length <= stat.condition.print_to_string().length) { + if (ab && !stat.alternative && next instanceof AST_Jump) { + var cond = stat.condition; + cond = best_of_expression(cond, cond.negate(compressor), stat.body instanceof AST_BlockStatement); + if (cond !== stat.condition) { changed = true; stat = stat.clone(); - stat.condition = negated; + stat.condition = cond; statements[j] = stat.body; stat.body = next; statements[i] = stat; @@ -3369,8 +3370,9 @@ Compressor.prototype.compress = function(node) { stat.body = make_node(AST_BlockStatement, stat.body, { body: as_statement_array(stat.body).concat(extract_functions()), }); - var body = as_statement_array_with_return(stat.alternative, alt); - stat.alternative = make_node(AST_BlockStatement, stat.alternative, { body: body }); + stat.alternative = make_node(AST_BlockStatement, stat.alternative, { + body: as_statement_array_with_return(stat.alternative, alt), + }); statements[i] = stat; statements[i] = stat.transform(compressor); continue; @@ -3392,15 +3394,6 @@ Compressor.prototype.compress = function(node) { var value = stat.body.value; var in_bool = stat.body.in_bool || next instanceof AST_Return && next.in_bool; //--- - // pretty silly case, but: - // if (foo()) return; return; ---> foo(); return; - if (!value && !stat.alternative - && (in_lambda && !next || next instanceof AST_Return && !next.value)) { - changed = true; - statements[i] = make_node(AST_SimpleStatement, stat.condition, { body: stat.condition }); - continue; - } - //--- // if (foo()) return x; return y; ---> return foo() ? x : y; if (!stat.alternative && next instanceof AST_Return) { changed = true; @@ -8211,12 +8204,12 @@ Compressor.prototype.compress = function(node) { node.right = rhs.drop_side_effect_free(compressor); } if (op == "??") return node; - var negated = make_node(AST_Binary, this, { - operator: op == "&&" ? "||" : "&&", - left: left.negate(compressor, first_in_statement), - right: node.right, - }); - return first_in_statement ? best_of_statement(node, negated) : best_of_expression(node, negated); + var negated = node.clone(); + negated.operator = op == "&&" ? "||" : "&&"; + negated.left = left.negate(compressor, first_in_statement); + if (negated.operator == negated.right.operator) swap_chain(negated); + var best = first_in_statement ? best_of_statement : best_of_expression; + return op == "&&" ? best(node, negated) : best(negated, node); } var lhs = left.drop_side_effect_free(compressor, first_in_statement); if (!lhs) return rhs; @@ -9082,8 +9075,8 @@ Compressor.prototype.compress = function(node) { // here because they are only used in an equality comparison later on. self.condition = negated; var tmp = self.body; - self.body = self.alternative || make_node(AST_EmptyStatement, self); - self.alternative = tmp; + self.body = self.alternative; + self.alternative = is_empty(tmp) ? null : tmp; } var body_defuns = []; var body_var_defs = []; @@ -9220,10 +9213,12 @@ Compressor.prototype.compress = function(node) { var line = stat.body[i]; if (line instanceof AST_EmptyStatement) continue; if (line instanceof AST_Exit) { - if (exprs.length == 0) return; - line = line.clone(); - exprs.push(line.value || make_node(AST_Undefined, line).transform(compressor)); - line.value = make_sequence(stat, exprs); + if (i == 0) return; + if (exprs.length > 0) { + line = line.clone(); + exprs.push(line.value || make_node(AST_Undefined, line).transform(compressor)); + line.value = make_sequence(stat, exprs); + } var block = stat.clone(); block.body = block.body.slice(i + 1); block.body.unshift(line); @@ -10962,6 +10957,23 @@ Compressor.prototype.compress = function(node) { return !node.has_side_effects(compressor); } + function swap_chain(self, compressor) { + var rhs = self.right; + self.left = make_node(AST_Binary, self, { + operator: self.operator, + left: self.left, + right: rhs.left, + start: self.left.start, + end: rhs.left.end + }); + self.right = rhs.right; + if (compressor) { + self.left = self.left.transform(compressor); + } else if (self.operator == rhs.left.operator) { + swap_chain(self.left); + } + } + OPT(AST_Binary, function(self, compressor) { if (commutativeOperators[self.operator] && self.right.is_constant() @@ -11132,7 +11144,7 @@ Compressor.prototype.compress = function(node) { && lazy_op[self.operator] && self.right instanceof AST_Binary && self.operator == self.right.operator) { - swap_chain(); + swap_chain(self, compressor); } if (compressor.option("strings") && self.operator == "+") { // "foo" + 42 + "" ---> "foo" + 42 @@ -11164,7 +11176,7 @@ Compressor.prototype.compress = function(node) { && (self.left.is_string(compressor) && self.right.is_string(compressor) || self.right.left.is_string(compressor) && (self.left.is_constant() || !self.right.right.has_side_effects(compressor)))) { - swap_chain(); + swap_chain(self, compressor); } } if (compressor.option("evaluate")) { @@ -11631,19 +11643,6 @@ Compressor.prototype.compress = function(node) { self.right = tmp; } } - - function swap_chain() { - var rhs = self.right; - self.left = make_node(AST_Binary, self, { - operator: self.operator, - left: self.left, - right: rhs.left, - start: self.left.start, - end: rhs.left.end - }); - self.right = rhs.right; - self.left = self.left.transform(compressor); - } }); OPT(AST_SymbolExport, function(self) { @@ -12747,7 +12746,7 @@ Compressor.prototype.compress = function(node) { if (flatten && value.has_side_effects(compressor)) flatten = false; } } - if (!flatten) values.unshift(retValue); + values.unshift(retValue); while (--i >= 0) { var value = elements[i].drop_side_effect_free(compressor); if (value) { @@ -12758,10 +12757,7 @@ Compressor.prototype.compress = function(node) { index--; } } - if (flatten) { - values.push(retValue); - return make_sequence(self, values).optimize(compressor); - } else return make_node(AST_Sub, self, { + return flatten ? make_sequence(self, values).optimize(compressor) : make_node(AST_Sub, self, { expression: make_node(AST_Array, expr, { elements: values }), property: make_node(AST_Number, prop, { value: index }), }); diff --git a/test/compress/comparisons.js b/test/compress/comparisons.js index a04498ee11..3a728a3a76 100644 --- a/test/compress/comparisons.js +++ b/test/compress/comparisons.js @@ -489,7 +489,7 @@ issue_3413: { } expect: { var b; - void 0 !== ("" < b || void 0) || console.log("PASS"); + void 0 === ("" < b || void 0) && console.log("PASS"); } expect_stdout: "PASS" } diff --git a/test/compress/const.js b/test/compress/const.js index d79c80334e..8178747d62 100644 --- a/test/compress/const.js +++ b/test/compress/const.js @@ -597,8 +597,7 @@ do_if_continue_1: { } expect: { do { - if (!console); - else { + if (console) { console.log("PASS"); { const a = 0; @@ -628,8 +627,7 @@ do_if_continue_2: { } expect: { do { - if (!console); - else { + if (console) { console.log("PASS"); { const a = 0; @@ -1518,6 +1516,7 @@ issue_4689: { issue_4691: { options = { + conditionals: true, if_return: true, toplevel: true, } diff --git a/test/compress/functions.js b/test/compress/functions.js index 20be426df0..3cbb2a3c12 100644 --- a/test/compress/functions.js +++ b/test/compress/functions.js @@ -7981,6 +7981,7 @@ issue_5264_2: { issue_5283: { options = { + conditionals: true, if_return: true, inline: true, pure_getters: "strict", @@ -8005,11 +8006,10 @@ issue_5283: { var a = "FAIL 1"; (function() { a = "PASS"; - if (!console) - (function(a) { - console.log("FAIL 2"); - a.p; - })(); + console || function(a) { + console.log("FAIL 2"); + a.p; + }(); })(); console.log(a); } diff --git a/test/compress/if_return.js b/test/compress/if_return.js index 5302065ca2..155d912e93 100644 --- a/test/compress/if_return.js +++ b/test/compress/if_return.js @@ -698,7 +698,9 @@ iife_if_return_simple: { nested_if_break: { options = { + conditionals: true, if_return: true, + side_effects: true, } input: { for (var i = 0; i < 3; i++) @@ -709,8 +711,7 @@ nested_if_break: { } expect: { for (var i = 0; i < 3; i++) - L1: if ("number" == typeof i) - if (0 !== i) console.log(i); + L1: "number" == typeof i && 0 !== i && console.log(i); } expect_stdout: [ "1", @@ -749,11 +750,11 @@ nested_if_continue: { function f(n) { for (var i = 0; "number" == typeof n - && (0 !== n - ? 1 !== n - ? i++ - : console.log("odd", i) - : console.log("even", i)), + && (0 === n + ? console.log("even", i) + : 1 === n + ? console.log("odd", i) + : i++), 0 <= (n -= 2);); } f(37); diff --git a/test/compress/issue-1052.js b/test/compress/issue-1052.js index cbdd1cb7bb..8861f7fe80 100644 --- a/test/compress/issue-1052.js +++ b/test/compress/issue-1052.js @@ -4,22 +4,21 @@ multiple_functions: { if_return: true, } input: { - ( function() { - if ( !window ) { + (function() { + if (!window) return; - } function f() {} function g() {} - } )(); + })(); } expect: { - ( function() { + (function() { // NOTE: other compression steps will reduce this // down to just `window`. - if ( !window ); + if (!window); function f() {} function g() {} - } )(); + })(); } } @@ -29,18 +28,17 @@ single_function: { if_return: true, } input: { - ( function() { - if ( !window ) { + (function() { + if (!window) return; - } function f() {} - } )(); + })(); } expect: { - ( function() { - if ( !window ); + (function() { + if (!window); function f() {} - } )(); + })(); } } @@ -50,28 +48,26 @@ deeply_nested: { if_return: true, } input: { - ( function() { - if ( !window ) { + (function() { + if (!window) return; - } function f() {} function g() {} - if ( !document ) { + if (!document) return; - } function h() {} - } )(); + })(); } expect: { - ( function() { + (function() { // NOTE: other compression steps will reduce this // down to just `window`. - if ( window ) - if ( !document ); + if (!window); + else if (!document); function f() {} function g() {} function h() {} - } )(); + })(); } } @@ -81,18 +77,18 @@ not_hoisted_when_already_nested: { if_return: true, } input: { - ( function() { - if ( !window ) { + (function() { + if (!window) return; - } - if ( foo ) function f() {} - } )(); + if (foo) function f() {} + })(); } expect: { - ( function() { - if ( window ) - if ( foo ) function f() {} - } )(); + (function() { + if (!window); + else if (foo) + function f() {} + })(); } } @@ -104,15 +100,19 @@ defun_if_return: { input: { function e() { function f() {} - if (!window) return; - else function g() {} + if (!window) + return; + else + function g() {} function h() {} } } expect: { function e() { function f() {} - if (window) function g() {} + if (!window); + else + function g() {} function h() {} } } @@ -126,8 +126,10 @@ defun_hoist_funs: { input: { function e() { function f() {} - if (!window) return; - else function g() {} + if (!window) + return; + else + function g() {} function h() {} } } @@ -136,7 +138,7 @@ defun_hoist_funs: { function f() {} function g() {} function h() {} - if (window); + if (!window); } } } @@ -149,15 +151,18 @@ defun_else_if_return: { input: { function e() { function f() {} - if (window) function g() {} - else return; + if (window) + function g() {} + else + return; function h() {} } } expect: { function e() { function f() {} - if (window) function g() {} + if (window) + function g() {} function h() {} } } diff --git a/test/compress/issue-1447.js b/test/compress/issue-1447.js index 3c881058fe..b5dbf035dd 100644 --- a/test/compress/issue-1447.js +++ b/test/compress/issue-1447.js @@ -40,6 +40,9 @@ conditional_false_stray_else_in_loop: { console.log(i); } } - expect_exact: "for(var i=1;i<=4;++i)if(!(i<=2))console.log(i);" - expect_stdout: true + expect_exact: "for(var i=1;i<=4;++i)if(i<=2);else console.log(i);" + expect_stdout: [ + "3", + "4", + ] } diff --git a/test/compress/issue-979.js b/test/compress/issue-979.js index 8f01760216..d752ff9fdb 100644 --- a/test/compress/issue-979.js +++ b/test/compress/issue-979.js @@ -1,4 +1,4 @@ -issue979_reported: { +reported: { options = { booleans: true, comparisons: true, @@ -17,29 +17,26 @@ issue979_reported: { } input: { function f1() { - if (a == 1 || b == 2) { + if (a == 1 || b == 2) foo(); - } } function f2() { - if (!(a == 1 || b == 2)) { - } - else { + if (!(a == 1 || b == 2)); + else foo(); - } } } expect: { function f1() { - 1!=a&&2!=b||foo(); + 1 != a && 2 != b || foo(); } function f2() { - 1!=a&&2!=b||foo(); + 1 != a && 2 != b || foo(); } } } -issue979_test_negated_is_best: { +test_negated_is_best: { options = { booleans: true, comparisons: true, @@ -58,53 +55,47 @@ issue979_test_negated_is_best: { } input: { function f3() { - if (a == 1 | b == 2) { + if (a == 1 | b == 2) foo(); - } } function f4() { - if (!(a == 1 | b == 2)) { - } - else { + if (!(a == 1 | b == 2)); + else foo(); - } } function f5() { - if (a == 1 && b == 2) { + if (a == 1 && b == 2) foo(); - } } function f6() { - if (!(a == 1 && b == 2)) { - } - else { + if (!(a == 1 && b == 2)); + else foo(); - } } function f7() { - if (a == 1 || b == 2) { + if (a == 1 || b == 2) foo(); - } - else { + else return bar(); - } } } expect: { function f3() { - 1==a|2==b&&foo(); + 1 == a | 2 == b && foo(); } function f4() { - 1==a|2==b&&foo(); + 1 == a | 2 == b && foo(); } function f5() { - 1==a&&2==b&&foo(); + 1 == a && 2 == b && foo(); } function f6() { - 1!=a||2!=b||foo(); + 1 == a && 2 == b && foo(); } function f7() { - if(1!=a&&2!=b)return bar();foo() + if (1 != a && 2 != b) + return bar(); + foo(); } } } diff --git a/test/compress/let.js b/test/compress/let.js index b7caedf0bb..d0261fc4fa 100644 --- a/test/compress/let.js +++ b/test/compress/let.js @@ -912,8 +912,7 @@ do_if_continue_1: { expect: { "use strict"; do { - if (!console); - else { + if (console) { console.log("PASS"); { let a = 0; @@ -946,8 +945,7 @@ do_if_continue_2: { expect: { "use strict"; do { - if (!console); - else { + if (console) { console.log("FAIL"); { let a = 0; @@ -1667,9 +1665,7 @@ issue_4438: { expect: { "use strict"; function f() { - if (!console) - ; - else { + if (console) { let a = console.log; void a("PASS"); } @@ -1757,6 +1753,7 @@ issue_4689: { issue_4691: { options = { + conditionals: true, if_return: true, toplevel: true, } diff --git a/test/compress/side_effects.js b/test/compress/side_effects.js index 63a6270367..ca929153c2 100644 --- a/test/compress/side_effects.js +++ b/test/compress/side_effects.js @@ -617,7 +617,7 @@ issue_4730_2: { } expect: { var a; - !console.log("PASS") || a && a[a.p]; + console.log("PASS") && a && a[a.p]; } expect_stdout: "PASS" } diff --git a/test/compress/typeof.js b/test/compress/typeof.js index 67226565ee..5faa72e93d 100644 --- a/test/compress/typeof.js +++ b/test/compress/typeof.js @@ -307,7 +307,7 @@ typeof_defined_1: { } expect: { "undefined" == typeof A && A; - "undefined" != typeof A || A; + "undefined" == typeof A && A; } } @@ -324,7 +324,7 @@ typeof_defined_2: { } expect: { "function" != typeof A && A; - "function" == typeof A || A; + "function" != typeof A && A; } } @@ -355,16 +355,19 @@ typeof_defined_3: { "undefined" == typeof A && "undefined" == typeof B && (A, B); "undefined" == typeof A && "undefined" != typeof B && A; "undefined" != typeof A && "undefined" == typeof B && B; + // dropped "undefined" == typeof A && "undefined" == typeof B || (A, B); "undefined" == typeof A && "undefined" != typeof B || (A, B); "undefined" != typeof A && "undefined" == typeof B || (A, B); "undefined" != typeof A && "undefined" != typeof B || (A, B); - "undefined" == typeof A || "undefined" == typeof B && B; - "undefined" != typeof A || "undefined" == typeof B && (A, B); - "undefined" != typeof A || "undefined" != typeof B && A; - "undefined" == typeof A || "undefined" != typeof B || B; - "undefined" != typeof A || "undefined" == typeof B || A; - "undefined" != typeof A || "undefined" != typeof B || (A, B); + "undefined" != typeof A && "undefined" == typeof B && B; + // dropped + "undefined" == typeof A && "undefined" == typeof B && (A, B); + "undefined" == typeof A && "undefined" != typeof B && A; + // dropped + "undefined" != typeof A && "undefined" == typeof B && B; + "undefined" == typeof A && "undefined" != typeof B && A; + "undefined" == typeof A && "undefined" == typeof B && (A, B); } } @@ -392,6 +395,7 @@ typeof_defined_4: { "object" != typeof A || "object" != typeof B || (A, B); } expect: { + // dropped "object" == typeof A && "object" != typeof B && B; "object" != typeof A && "object" == typeof B && A; "object" != typeof A && "object" != typeof B && (A, B); @@ -399,12 +403,14 @@ typeof_defined_4: { "object" == typeof A && "object" != typeof B || (A, B); "object" != typeof A && "object" == typeof B || (A, B); "object" != typeof A && "object" != typeof B || (A, B); - "object" == typeof A || "object" == typeof B && A; - "object" == typeof A || "object" != typeof B && (A, B); - "object" != typeof A || "object" != typeof B && B; - "object" == typeof A || "object" == typeof B || (A, B); - "object" == typeof A || "object" != typeof B || A; - "object" != typeof A || "object" == typeof B || B; + "object" != typeof A && "object" == typeof B && A; + "object" != typeof A && "object" != typeof B && (A, B); + // dropped + "object" == typeof A && "object" != typeof B && B; + "object" != typeof A && "object" != typeof B && (A, B); + "object" != typeof A && "object" == typeof B && A; + "object" == typeof A && "object" != typeof B && B; + // dropped } }