enhance `evaluate` & `reduce_vars` (#3873)
authorAlex Lam S.L <alexlamsl@gmail.com>
Sun, 10 May 2020 19:08:05 +0000 (20:08 +0100)
committerGitHub <noreply@github.com>
Sun, 10 May 2020 19:08:05 +0000 (03:08 +0800)
lib/compress.js
test/compress/evaluate.js
test/compress/functions.js
test/compress/reduce_vars.js

index 6a444e9..fcbbffb 100644 (file)
@@ -492,8 +492,7 @@ merge(Compressor.prototype, {
         function ref_once(tw, compressor, def) {
             return compressor.option("unused")
                 && !def.scope.pinned()
-                && def.references.length - def.recursive_refs == 1
-                && tw.loop_ids[def.id] === tw.in_loop;
+                && def.references.length - def.recursive_refs == 1;
         }
 
         function is_immutable(value) {
@@ -797,8 +796,9 @@ merge(Compressor.prototype, {
                 if (recursive) {
                     d.recursive_refs++;
                 } else if (value && ref_once(tw, compressor, d)) {
+                    d.in_loop = tw.loop_ids[d.id] !== tw.in_loop;
                     d.single_use = value instanceof AST_Lambda && !value.pinned()
-                        || d.scope === this.scope && value.is_constant_expression();
+                        || !d.in_loop && d.scope === this.scope && value.is_constant_expression();
                 } else {
                     d.single_use = false;
                 }
@@ -3501,7 +3501,21 @@ merge(Compressor.prototype, {
                 if (fn.name && fn.name.definition().recursive_refs > 0) return this;
                 if (this.is_expr_pure(compressor)) return this;
                 var stat = fn.first_statement();
-                if (!(stat instanceof AST_Return)) return this;
+                if (!(stat instanceof AST_Return)) {
+                    if (ignore_side_effects) {
+                        var found = false;
+                        fn.walk(new TreeWalker(function(node) {
+                            if (found) return true;
+                            if (node instanceof AST_Return) {
+                                if (node.value && node.value.evaluate(compressor, true) !== undefined) found = true;
+                                return true;
+                            }
+                            if (node instanceof AST_Scope && node !== fn) return true;
+                        }));
+                        if (!found) return void 0;
+                    }
+                    return this;
+                }
                 var args = eval_args(this.args);
                 if (!args) return this;
                 if (!stat.value) return undefined;
@@ -7427,7 +7441,7 @@ merge(Compressor.prototype, {
             var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
             if (single_use) {
                 if (fixed instanceof AST_Lambda) {
-                    if (def.scope !== self.scope
+                    if ((def.scope !== self.scope || def.in_loop)
                         && (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) {
                         single_use = false;
                     } else if (recursive_ref(compressor, def)) {
@@ -7771,7 +7785,7 @@ merge(Compressor.prototype, {
             expressions.push(self);
             return make_sequence(self, expressions);
         }
-        var condition = self.condition.is_truthy() || self.condition.evaluate(compressor);
+        var condition = self.condition.is_truthy() || self.condition.evaluate(compressor, true);
         if (!condition) {
             AST_Node.warn("Condition always false [{file}:{line},{col}]", self.start);
             return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor);
index e7f6f7e..a46d38a 100644 (file)
@@ -2190,3 +2190,139 @@ issue_3755: {
     }
     expect_stdout: "undefined"
 }
+
+void_side_effects: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        var a = void console.log("PASS");
+        console.log(a);
+    }
+    expect: {
+        console.log("PASS");
+        console.log(void 0);
+    }
+    expect_stdout: [
+        "PASS",
+        "undefined",
+    ]
+}
+
+no_returns: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        var a = function() {
+            console.log("PASS");
+        }();
+        console.log(a);
+    }
+    expect: {
+        (function() {
+            console.log("PASS");
+        })();
+        console.log(void 0);
+    }
+    expect_stdout: [
+        "PASS",
+        "undefined",
+    ]
+}
+
+void_returns: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        var a = function f() {
+            function g(b) {
+                if (b) console.log("FAIL");
+            }
+            while (1) {
+                console.log("PASS");
+                try {
+                    if (console) return;
+                } catch (e) {
+                    return g(e);
+                }
+            }
+        }();
+        console.log(a);
+    }
+    expect: {
+        (function() {
+            function g(b) {
+                if (b) console.log("FAIL");
+            }
+            while (1) {
+                console.log("PASS");
+                try {
+                    if (console) return;
+                } catch (e) {
+                    return g(e);
+                }
+            }
+        })();
+        console.log(void 0);
+    }
+    expect_stdout: [
+        "PASS",
+        "undefined",
+    ]
+}
+
+void_returns_recursive: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        var a = function f() {
+            function g(b) {
+                return f();
+            }
+            while (1) {
+                console.log("PASS");
+                try {
+                    if (console) return;
+                } catch (e) {
+                    return g(e);
+                }
+            }
+        }();
+        console.log(a);
+    }
+    expect: {
+        var a = function f() {
+            function g(b) {
+                return f();
+            }
+            while (1) {
+                console.log("PASS");
+                try {
+                    if (console) return;
+                } catch (e) {
+                    return g();
+                }
+            }
+        }();
+        console.log(a);
+    }
+    expect_stdout: [
+        "PASS",
+        "undefined",
+    ]
+}
index ae113c8..db5466e 100644 (file)
@@ -3514,10 +3514,9 @@ hoisted_single_use: {
     }
     expect: {
         function f(a) {
-            for (var r in a) g(r);
-        }
-        function g(a) {
-            console.log(a);
+            for (var r in a) (function(a) {
+                console.log(a);
+            })(r);
         }
         (function(a) {
             var g = a.bar;
index ee1e471..00cebcd 100644 (file)
@@ -1197,11 +1197,10 @@ toplevel_on_loops_1: {
         while (x);
     }
     expect: {
-        function bar() {
-            console.log("bar:", --x);
-        }
         var x = 3;
-        for (;bar(), x;);
+        for (;function() {
+            console.log("bar:", --x);
+        }(), x;);
     }
     expect_stdout: true
 }
@@ -1254,10 +1253,9 @@ toplevel_on_loops_2: {
         while (x);
     }
     expect: {
-        function bar() {
+        for (;;) (function() {
             console.log("bar:");
-        }
-        for (;;) bar();
+        })();
     }
 }
 
@@ -4231,13 +4229,12 @@ issue_2450_4: {
     }
     expect: {
         var a;
-        function f(b) {
-            console.log(a === b);
-            a = b;
-        }
         function g() {}
         for (var i = 3; --i >= 0;)
-            f(g);
+            (function(b) {
+                console.log(a === b);
+                a = b;
+            })(g);
     }
     expect_stdout: [
         "false",
@@ -4338,14 +4335,13 @@ perf_1: {
         console.log(sum);
     }
     expect: {
-        function indirect_foo(x, y, z) {
-            return function(x, y, z) {
-                return x < y ? x * y + z : x * z - y;
-            }(x, y, z);
-        }
         var sum = 0;
         for (var i = 0; i < 100; ++i)
-            sum += indirect_foo(i, i + 1, 3 * i);
+            sum += function(x, y, z) {
+                return function(x, y, z) {
+                    return x < y ? x * y + z : x * z - y;
+                }(x, y, z);
+            }(i, i + 1, 3 * i);
         console.log(sum);
     }
     expect_stdout: "348150"
@@ -4406,14 +4402,13 @@ perf_3: {
         console.log(sum);
     }
     expect: {
-        var indirect_foo = function(x, y, z) {
-            return function(x, y, z) {
-                return x < y ? x * y + z : x * z - y;
-            }(x, y, z);
-        }
         var sum = 0;
         for (var i = 0; i < 100; ++i)
-            sum += indirect_foo(i, i + 1, 3 * i);
+            sum += function(x, y, z) {
+                return function(x, y, z) {
+                    return x < y ? x * y + z : x * z - y;
+                }(x, y, z);
+            }(i, i + 1, 3 * i);
         console.log(sum);
     }
     expect_stdout: "348150"
@@ -4475,14 +4470,13 @@ perf_5: {
         console.log(sum);
     }
     expect: {
-        function indirect_foo(x, y, z) {
-            return function(x, y, z) {
-                return x < y ? x * y + z : x * z - y;
-            }(x, y, z);
-        }
         var sum = 0;
         for (var i = 0; i < 100; ++i)
-            sum += indirect_foo(i, i + 1, 3 * i);
+            sum += function(x, y, z) {
+                return function(x, y, z) {
+                    return x < y ? x * y + z : x * z - y;
+                }(x, y, z);
+            }(i, i + 1, 3 * i);
         console.log(sum);
     }
     expect_stdout: "348150"
@@ -4543,14 +4537,13 @@ perf_7: {
         console.log(sum);
     }
     expect: {
-        var indirect_foo = function(x, y, z) {
-            return function(x, y, z) {
-                return x < y ? x * y + z : x * z - y;
-            }(x, y, z);
-        }
         var sum = 0;
         for (var i = 0; i < 100; ++i)
-            sum += indirect_foo(i, i + 1, 3 * i);
+            sum += function(x, y, z) {
+                return function(x, y, z) {
+                    return x < y ? x * y + z : x * z - y;
+                }(x, y, z);
+            }(i, i + 1, 3 * i);
         console.log(sum);
     }
     expect_stdout: "348150"
@@ -7054,24 +7047,3 @@ issue_3866: {
     }
     expect_stdout: "PASS"
 }
-
-void_side_effects: {
-    options = {
-        evaluate: true,
-        reduce_vars: true,
-        toplevel: true,
-        unused: true,
-    }
-    input: {
-        var a = void console.log("PASS");
-        console.log(a);
-    }
-    expect: {
-        console.log("PASS");
-        console.log(void 0);
-    }
-    expect_stdout: [
-        "PASS",
-        "undefined",
-    ]
-}