fix corner cases in `reduce_vars` & `unused` (#3955)
authorAlex Lam S.L <alexlamsl@gmail.com>
Fri, 5 Jun 2020 10:51:21 +0000 (11:51 +0100)
committerGitHub <noreply@github.com>
Fri, 5 Jun 2020 10:51:21 +0000 (18:51 +0800)
fixes #3953
fixes #3956
fixes #3957

lib/compress.js
test/compress/drop-unused.js
test/compress/evaluate.js
test/compress/reduce_vars.js

index 23a9f92..db445bb 100644 (file)
@@ -495,6 +495,12 @@ merge(Compressor.prototype, {
             });
         }
 
+        function to_value(def, sym, fixed) {
+            if (fixed instanceof AST_Node) return fixed;
+            if (typeof fixed == "function") return fixed();
+            return make_node(AST_Undefined, sym);
+        }
+
         function ref_once(compressor, def) {
             return compressor.option("unused")
                 && !def.scope.pinned()
@@ -576,8 +582,9 @@ merge(Compressor.prototype, {
             var eq = node.operator == "=";
             var value = eq ? node.right : node;
             if (is_modified(compressor, tw, node, value, 0)) return;
+            var safe = eq || safe_to_read(tw, d);
             node.right.walk(tw);
-            if ((eq || safe_to_read(tw, d)) && safe_to_assign(tw, d)) {
+            if (safe && safe_to_assign(tw, d)) {
                 push_ref(d, sym);
                 mark(tw, d);
                 if (eq) {
@@ -587,9 +594,8 @@ merge(Compressor.prototype, {
                         return node.right;
                     };
                 } else {
-                    if (fixed == null) fixed = make_node(AST_Undefined, d.orig[0]);
                     sym.fixed = d.fixed = function() {
-                        var value = fixed instanceof AST_Node ? fixed : fixed();
+                        var value = to_value(d, sym, fixed);
                         return value && make_node(AST_Binary, node, {
                             operator: node.operator.slice(0, -1),
                             left: value,
@@ -597,7 +603,7 @@ merge(Compressor.prototype, {
                         });
                     };
                 }
-                sym.fixed.assigns = eq || !fixed.assigns ? [] : fixed.assigns.slice();
+                sym.fixed.assigns = eq || !fixed || !fixed.assigns ? [] : fixed.assigns.slice();
                 sym.fixed.assigns.push(node);
             } else {
                 sym.walk(tw);
@@ -740,8 +746,8 @@ merge(Compressor.prototype, {
                 fn.argnames.forEach(function(arg, i) {
                     var d = arg.definition();
                     if (d.fixed === undefined && (!fn.uses_arguments || tw.has_directive("use strict"))) {
-                        tw.loop_ids[d.id] = tw.in_loop;
                         mark(tw, d);
+                        tw.loop_ids[d.id] = tw.in_loop;
                         var value = iife.args[i];
                         d.fixed = function() {
                             var j = fn.argnames.indexOf(arg);
@@ -870,9 +876,8 @@ merge(Compressor.prototype, {
             if (safe_to_read(tw, d) && safe_to_assign(tw, d)) {
                 push_ref(d, exp);
                 mark(tw, d);
-                if (fixed == null) fixed = make_node(AST_Undefined, d.orig[0]);
                 d.fixed = function() {
-                    var value = fixed instanceof AST_Node ? fixed : fixed();
+                    var value = to_value(d, exp, fixed);
                     return value && make_node(AST_Binary, node, {
                         operator: node.operator.slice(0, -1),
                         left: make_node(AST_UnaryPrefix, node, {
@@ -884,19 +889,19 @@ merge(Compressor.prototype, {
                         })
                     });
                 };
-                d.fixed.assigns = fixed.assigns ? fixed.assigns.slice() : [];
+                d.fixed.assigns = fixed && fixed.assigns ? fixed.assigns.slice() : [];
                 d.fixed.assigns.push(node);
                 if (node instanceof AST_UnaryPrefix) {
                     exp.fixed = d.fixed;
                 } else {
                     exp.fixed = function() {
-                        var value = fixed instanceof AST_Node ? fixed : fixed();
+                        var value = to_value(d, exp, fixed);
                         return value && make_node(AST_UnaryPrefix, node, {
                             operator: "+",
                             expression: value
                         });
                     };
-                    exp.fixed.assigns = fixed.assigns;
+                    exp.fixed.assigns = fixed && fixed.assigns;
                 }
             } else {
                 exp.walk(tw);
@@ -4429,6 +4434,7 @@ merge(Compressor.prototype, {
                     if (!drop_vars || sym.id in in_use_ids) {
                         if (def.value && indexOf_assign(sym, def) < 0) {
                             def.value = def.value.drop_side_effect_free(compressor);
+                            if (def.value) def.value.tail_node().write_only = false;
                         }
                         var old_def, var_defs = var_defs_by_id.get(sym.id);
                         if (!def.value) {
index f3aeab2..a4a8595 100644 (file)
@@ -2652,3 +2652,41 @@ issue_3951: {
         "0",
     ]
 }
+
+issue_3956: {
+    options = {
+        collapse_vars: true,
+        evaluate: true,
+        inline: true,
+        passes: 2,
+        reduce_vars: true,
+        sequences: true,
+        side_effects: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        (function(a) {
+            function f(b) {
+                console.log(b);
+                a = 1;
+            }
+            var c = f(c += 0);
+            (function(d) {
+                console.log(d);
+            })(console.log(a) ^ 1, c);
+        })();
+    }
+    expect: {
+        var c, d;
+        c += 0,
+        console.log(NaN),
+        d = 1 ^ console.log(1),
+        console.log(d);
+    }
+    expect_stdout: [
+        "NaN",
+        "1",
+        "1",
+    ]
+}
index fd8deab..0f3a9fd 100644 (file)
@@ -2715,3 +2715,23 @@ issue_3944: {
     }
     expect_stdout: "false"
 }
+
+issue_3953: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+    }
+    input: {
+        function f(a) {
+            (a += 0 * (a = 0)) && console.log("PASS");
+        }
+        f(1);
+    }
+    expect: {
+        function f(a) {
+            (a += 0 * (a = 0)) && console.log("PASS");
+        }
+        f(1);
+    }
+    expect_stdout: "PASS"
+}
index 3368cc6..1a782b4 100644 (file)
@@ -7273,3 +7273,53 @@ local_assignment_loop: {
     }
     expect_stdout: "PASS"
 }
+
+issue_3957_1: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+    }
+    input: {
+        function f(a) {
+            while (a += console.log(a = 0))
+                a = 0;
+        }
+        f("FAIL");
+    }
+    expect: {
+        function f(a) {
+            while (a += console.log(a = 0))
+                a = 0;
+        }
+        f("FAIL");
+    }
+    expect_stdout: [
+        "0",
+        "0",
+    ]
+}
+
+issue_3957_2: {
+    options = {
+        reduce_vars: true,
+        unused: true,
+    }
+    input: {
+        function f(a) {
+            while (a += console.log(a = 0))
+                a = 0;
+        }
+        f("FAIL");
+    }
+    expect: {
+        function f(a) {
+            while (a += console.log(a = 0))
+                a = 0;
+        }
+        f("FAIL");
+    }
+    expect_stdout: [
+        "0",
+        "0",
+    ]
+}