enhance `conditionals` (#4181)
authorAlex Lam S.L <alexlamsl@gmail.com>
Mon, 5 Oct 2020 07:55:37 +0000 (08:55 +0100)
committerGitHub <noreply@github.com>
Mon, 5 Oct 2020 07:55:37 +0000 (15:55 +0800)
lib/compress.js
test/compress/conditionals.js

index 4534d9c..74c0feb 100644 (file)
@@ -7463,6 +7463,15 @@ merge(Compressor.prototype, {
             || node instanceof AST_Object;
     }
 
+    function repeatable(compressor, node) {
+        if (node instanceof AST_Dot) return repeatable(compressor, node.expression);
+        if (node instanceof AST_Sub) {
+            return repeatable(compressor, node.expression) && repeatable(compressor, node.property);
+        }
+        if (node instanceof AST_Symbol) return true;
+        return !node.has_side_effects(compressor);
+    }
+
     OPT(AST_Binary, function(self, compressor) {
         function reversible() {
             return self.left.is_constant()
@@ -7531,7 +7540,7 @@ merge(Compressor.prototype, {
             if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
                 (self.left.is_number(compressor) && self.right.is_number(compressor)) ||
                 (self.left.is_boolean(compressor) && self.right.is_boolean(compressor)) ||
-                can_self_compare(self.left) && self.left.equivalent_to(self.right)) {
+                repeatable(compressor, self.left) && self.left.equivalent_to(self.right)) {
                 self.operator = self.operator.slice(0, 2);
             }
             // XXX: intentionally falling down to the next case
@@ -8036,13 +8045,6 @@ merge(Compressor.prototype, {
         }
         return try_evaluate(compressor, self);
 
-        function can_self_compare(node) {
-            if (node instanceof AST_Dot) return can_self_compare(node.expression);
-            if (node instanceof AST_Sub) return can_self_compare(node.expression) && can_self_compare(node.property);
-            if (node instanceof AST_Symbol) return true;
-            return !node.has_side_effects(compressor);
-        }
-
         function align(ref, op) {
             switch (ref) {
               case "-":
@@ -8504,14 +8506,13 @@ merge(Compressor.prototype, {
     });
 
     OPT(AST_Conditional, function(self, compressor) {
-        if (!compressor.option("conditionals")) return self;
-        // This looks like lift_sequences(), should probably be under "sequences"
-        if (self.condition instanceof AST_Sequence) {
+        if (compressor.option("sequences") && self.condition instanceof AST_Sequence) {
             var expressions = self.condition.expressions.slice();
             self.condition = expressions.pop();
             expressions.push(self);
             return make_sequence(self, expressions);
         }
+        if (!compressor.option("conditionals")) return self;
         var condition = self.condition.is_truthy() || self.condition.evaluate(compressor, true);
         if (!condition) {
             AST_Node.warn("Condition always false [{file}:{line},{col}]", self.start);
@@ -8532,15 +8533,19 @@ merge(Compressor.prototype, {
         }
         var consequent = self.consequent;
         var alternative = self.alternative;
-        // x ? x : y => x || y
-        if (condition instanceof AST_SymbolRef
-            && consequent instanceof AST_SymbolRef
-            && condition.definition() === consequent.definition()) {
-            return make_node(AST_Binary, self, {
+        if (repeatable(compressor, condition)) {
+            // x ? x : y => x || y
+            if (condition.equivalent_to(consequent)) return make_node(AST_Binary, self, {
                 operator: "||",
                 left: condition,
-                right: alternative
-            });
+                right: alternative,
+            }).optimize(compressor);
+            // x ? y : x => x && y
+            if (condition.equivalent_to(alternative)) return make_node(AST_Binary, self, {
+                operator: "&&",
+                left: condition,
+                right: consequent,
+            }).optimize(compressor);
         }
         // if (foo) exp = something; else exp = something_else;
         //                   |
index 4d54b83..5dfd17b 100644 (file)
@@ -1159,7 +1159,7 @@ issue_1645_2: {
     expect_stdout: true
 }
 
-condition_symbol_matches_consequent: {
+condition_matches_consequent: {
     options = {
         conditionals: true,
     }
@@ -1188,6 +1188,35 @@ condition_symbol_matches_consequent: {
     expect_stdout: "3 7 true 4"
 }
 
+condition_matches_alternative: {
+    options = {
+        conditionals: true,
+    }
+    input: {
+        function foo(x, y) {
+            return x.p ? y[0] : x.p;
+        }
+        function bar() {
+            return g ? h : g;
+        }
+        var g = 4;
+        var h = 5;
+        console.log(foo({ p: 3 }, [ null ]), foo({ p: 0 }, [ 7 ]), foo({ p: true } , [ false ]), bar());
+    }
+    expect: {
+        function foo(x, y) {
+            return x.p && y[0];
+        }
+        function bar() {
+            return g && h;
+        }
+        var g = 4;
+        var h = 5;
+        console.log(foo({ p: 3 }, [ null ]), foo({ p: 0 }, [ 7 ]), foo({ p: true } , [ false ]), bar());
+    }
+    expect_stdout: "null 0 false 5"
+}
+
 delete_conditional_1: {
     options = {
         booleans: true,