From b499e03f82987d59814c81fb9f211fee5e3a7ba4 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Tue, 28 Jan 2020 12:33:21 +0800 Subject: [PATCH] enhance `conditionals` (#3694) --- lib/compress.js | 85 +++++++++++++++++++++++++++++++---- test/compress/conditionals.js | 57 +++++++++++++++++++++++ 2 files changed, 134 insertions(+), 8 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 2a338ba16d..f2dfb64c60 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -7194,23 +7194,24 @@ merge(Compressor.prototype, { expressions.push(self); return make_sequence(self, expressions); } - var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor); - if (!cond) { + var condition = self.condition.is_truthy() || self.condition.evaluate(compressor); + if (!condition) { AST_Node.warn("Condition always false [{file}:{line},{col}]", self.start); return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor); - } else if (!(cond instanceof AST_Node)) { + } else if (!(condition instanceof AST_Node)) { AST_Node.warn("Condition always true [{file}:{line},{col}]", self.start); return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor); } - var negated = cond.negate(compressor, first_in_statement(compressor)); - if (best_of(compressor, cond, negated) === negated) { + var negated = condition.negate(compressor, first_in_statement(compressor)); + if (best_of(compressor, condition, negated) === negated) { self = make_node(AST_Conditional, self, { condition: negated, consequent: self.alternative, alternative: self.consequent }); + negated = condition; + condition = self.condition; } - var condition = self.condition; var consequent = self.consequent; var alternative = self.alternative; // x ? x : y => x || y @@ -7298,6 +7299,19 @@ merge(Compressor.prototype, { alternative: alternative }); } + // x ? (y ? a : b) : a => !x || y ? a : b + if (consequent instanceof AST_Conditional + && consequent.consequent.equivalent_to(alternative)) { + return make_node(AST_Conditional, self, { + condition: make_node(AST_Binary, self, { + left: negated, + operator: "||", + right: consequent.condition + }), + consequent: alternative, + alternative: consequent.alternative + }); + } // x ? a : (y ? a : b) => x || y ? a : b if (alternative instanceof AST_Conditional && consequent.equivalent_to(alternative.consequent)) { @@ -7311,7 +7325,20 @@ merge(Compressor.prototype, { alternative: alternative.alternative }); } - // x ? (y, w) : (z, w) => x ? y : z, w + // x ? b : (y ? a : b) => !x && y ? a : b + if (alternative instanceof AST_Conditional + && consequent.equivalent_to(alternative.alternative)) { + return make_node(AST_Conditional, self, { + condition: make_node(AST_Binary, self, { + left: negated, + operator: "&&", + right: alternative.condition + }), + consequent: alternative.consequent, + alternative: consequent + }); + } + // x ? (a, c) : (b, c) => x ? a : b, c if ((consequent instanceof AST_Sequence || alternative instanceof AST_Sequence) && consequent.tail_node().equivalent_to(alternative.tail_node())) { return make_sequence(self, [ @@ -7323,7 +7350,21 @@ merge(Compressor.prototype, { consequent.tail_node() ]).optimize(compressor); } - // x ? y || z : z => x && y || z + // x ? y && a : a => (!x || y) && a + if (consequent instanceof AST_Binary + && consequent.operator == "&&" + && consequent.right.equivalent_to(alternative)) { + return make_node(AST_Binary, self, { + operator: "&&", + left: make_node(AST_Binary, self, { + operator: "||", + left: negated, + right: consequent.left + }), + right: alternative + }).optimize(compressor); + } + // x ? y || a : a => x && y || a if (consequent instanceof AST_Binary && consequent.operator == "||" && consequent.right.equivalent_to(alternative)) { @@ -7337,6 +7378,34 @@ merge(Compressor.prototype, { right: alternative }).optimize(compressor); } + // x ? a : y && a => (x || y) && a + if (alternative instanceof AST_Binary + && alternative.operator == "&&" + && alternative.right.equivalent_to(consequent)) { + return make_node(AST_Binary, self, { + operator: "&&", + left: make_node(AST_Binary, self, { + operator: "||", + left: condition, + right: alternative.left + }), + right: consequent + }).optimize(compressor); + } + // x ? a : y || a => !x && y || a + if (alternative instanceof AST_Binary + && alternative.operator == "||" + && alternative.right.equivalent_to(consequent)) { + return make_node(AST_Binary, self, { + operator: "||", + left: make_node(AST_Binary, self, { + operator: "&&", + left: negated, + right: alternative.left + }), + right: consequent + }).optimize(compressor); + } var in_bool = compressor.option("booleans") && compressor.in_boolean_context(); if (is_true(consequent)) { if (is_false(alternative)) { diff --git a/test/compress/conditionals.js b/test/compress/conditionals.js index 2049b40137..8044489ef1 100644 --- a/test/compress/conditionals.js +++ b/test/compress/conditionals.js @@ -294,6 +294,45 @@ cond_5: { } } +cond_6: { + options = { + booleans: true, + conditionals: true, + } + input: { + x ? a : b; + x ? a : a; + + x ? y ? a : b : c; + x ? y ? a : a : b; + x ? y ? a : b : b; + x ? y ? a : b : a; + x ? y ? a : a : a; + + x ? a : y ? b : c; + x ? a : y ? a : b; + x ? a : y ? b : b; + x ? a : y ? b : a; + x ? a : y ? a : a; + } + expect: { + x ? a : b; + x, a; + + x ? y ? a : b : c; + x ? (y, a) : b; + x && y ? a : b; + !x || y ? a : b; + x && y, a; + + x ? a : y ? b : c; + x || y ? a : b; + x ? a : (y, b); + !x && y ? b : a; + !x && y, a; + } +} + cond_7: { options = { conditionals: true, @@ -726,6 +765,24 @@ cond_11: { expect_stdout: "foo bar" } +cond_12: { + options = { + conditionals: true, + } + input: { + x ? y && a : a; + x ? y || a : a; + x ? a : y && a; + x ? a : y || a; + } + expect: { + (!x || y) && a; + x && y || a; + (x || y) && a; + !x && y || a; + } +} + ternary_boolean_consequent: { options = { booleans: true,