account for side-effects in conditional call inversion (#2562)
authorAlex Lam S.L <alexlamsl@gmail.com>
Thu, 7 Dec 2017 17:15:31 +0000 (01:15 +0800)
committerGitHub <noreply@github.com>
Thu, 7 Dec 2017 17:15:31 +0000 (01:15 +0800)
fixes #2560

lib/compress.js
test/compress/conditionals.js

index 4195ab2..e59dce6 100644 (file)
@@ -4652,18 +4652,22 @@ merge(Compressor.prototype, {
             });
         }
         // x ? y(a) : y(b) --> y(x ? a : b)
+        var arg_index;
         if (consequent instanceof AST_Call
             && alternative.TYPE === consequent.TYPE
-            && consequent.args.length == 1
-            && alternative.args.length == 1
+            && consequent.args.length > 0
+            && consequent.args.length == alternative.args.length
             && consequent.expression.equivalent_to(alternative.expression)
-            && !consequent.expression.has_side_effects(compressor)) {
-            consequent.args[0] = make_node(AST_Conditional, self, {
+            && !self.condition.has_side_effects(compressor)
+            && !consequent.expression.has_side_effects(compressor)
+            && typeof (arg_index = single_arg_diff()) == "number") {
+            var node = consequent.clone();
+            node.args[arg_index] = make_node(AST_Conditional, self, {
                 condition: self.condition,
-                consequent: consequent.args[0],
-                alternative: alternative.args[0]
+                consequent: consequent.args[arg_index],
+                alternative: alternative.args[arg_index]
             });
-            return consequent;
+            return node;
         }
         // x?y?z:a:a --> x&&y?z:a
         if (consequent instanceof AST_Conditional
@@ -4760,6 +4764,19 @@ merge(Compressor.prototype, {
                     && node.expression instanceof AST_Constant
                     && node.expression.getValue());
         }
+
+        function single_arg_diff() {
+            var a = consequent.args;
+            var b = alternative.args;
+            for (var i = 0, len = a.length; i < len; i++) {
+                if (!a[i].equivalent_to(b[i])) {
+                    for (var j = i + 1; j < len; j++) {
+                        if (!a[j].equivalent_to(b[j])) return;
+                    }
+                    return i;
+                }
+            }
+        }
     });
 
     OPT(AST_Boolean, function(self, compressor){
index 89c0526..143ece4 100644 (file)
@@ -166,22 +166,24 @@ cond_1: {
         conditionals: true
     };
     input: {
-        var do_something; // if undeclared it's assumed to have side-effects
-        if (some_condition()) {
-            do_something(x);
-        } else {
-            do_something(y);
-        }
-        if (some_condition()) {
-            side_effects(x);
-        } else {
-            side_effects(y);
+        function foo(do_something, some_condition) {
+            if (some_condition) {
+                do_something(x);
+            } else {
+                do_something(y);
+            }
+            if (some_condition) {
+                side_effects(x);
+            } else {
+                side_effects(y);
+            }
         }
     }
     expect: {
-        var do_something;
-        do_something(some_condition() ? x : y);
-        some_condition() ? side_effects(x) : side_effects(y);
+        function foo(do_something, some_condition) {
+            do_something(some_condition ? x : y);
+            some_condition ? side_effects(x) : side_effects(y);
+        }
     }
 }
 
@@ -190,16 +192,18 @@ cond_2: {
         conditionals: true
     };
     input: {
-        var x, FooBar;
-        if (some_condition()) {
-            x = new FooBar(1);
-        } else {
-            x = new FooBar(2);
+        function foo(x, FooBar, some_condition) {
+            if (some_condition) {
+                x = new FooBar(1);
+            } else {
+                x = new FooBar(2);
+            }
         }
     }
     expect: {
-        var x, FooBar;
-        x = new FooBar(some_condition() ? 1 : 2);
+        function foo(x, FooBar, some_condition) {
+            x = new FooBar(some_condition ? 1 : 2);
+        }
     }
 }
 
@@ -605,6 +609,42 @@ cond_8c: {
     }
 }
 
+cond_9: {
+    options = {
+        conditionals: true,
+    }
+    input: {
+        function f(x, y) {
+            g() ? x(1) : x(2);
+            x ? (y || x)() : (y || x)();
+            x ? y(a, b) : y(d, b, c);
+            x ? y(a, b, c) : y(a, b, c);
+            x ? y(a, b, c) : y(a, b, f);
+            x ? y(a, b, c) : y(a, e, c);
+            x ? y(a, b, c) : y(a, e, f);
+            x ? y(a, b, c) : y(d, b, c);
+            x ? y(a, b, c) : y(d, b, f);
+            x ? y(a, b, c) : y(d, e, c);
+            x ? y(a, b, c) : y(d, e, f);
+        }
+    }
+    expect: {
+        function f(x, y) {
+            g() ? x(1) : x(2);
+            x, (y || x)();
+            x ? y(a, b) : y(d, b, c);
+            x, y(a, b, c);
+            y(a, b, x ? c : f);
+            y(a, x ? b : e, c);
+            x ? y(a, b, c) : y(a, e, f);
+            y(x ? a : d, b, c);
+            x ? y(a, b, c) : y(d, b, f);
+            x ? y(a, b, c) : y(d, e, c);
+            x ? y(a, b, c) : y(d, e, f);
+        }
+    }
+}
+
 ternary_boolean_consequent: {
     options = {
         collapse_vars:true, sequences:true, properties:true, dead_code:true, conditionals:true,
@@ -1115,3 +1155,51 @@ issue_2535_2: {
         "false",
     ]
 }
+
+issue_2560: {
+    options = {
+        conditionals: true,
+        inline: true,
+        reduce_funcs: true,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        function log(x) {
+            console.log(x);
+        }
+        function foo() {
+            return log;
+        }
+        function bar() {
+            if (x !== (x = foo())) {
+                x(1);
+            } else {
+                x(2);
+            }
+        }
+        var x = function() {
+            console.log("init");
+        };
+        bar();
+        bar();
+    }
+    expect: {
+        function log(x) {
+            console.log(x);
+        }
+        function bar() {
+            x !== (x = log) ? x(1) : x(2);
+        }
+        var x = function() {
+            console.log("init");
+        };
+        bar();
+        bar();
+    }
+    expect_stdout: [
+        "1",
+        "2",
+    ]
+}