fix corner case in `reduce_vars` (#4348)
authorAlex Lam S.L <alexlamsl@gmail.com>
Tue, 8 Dec 2020 00:52:14 +0000 (00:52 +0000)
committerGitHub <noreply@github.com>
Tue, 8 Dec 2020 00:52:14 +0000 (08:52 +0800)
fixes #4347

lib/compress.js
test/compress/async.js

index 1b5b8be..8936125 100644 (file)
@@ -340,8 +340,7 @@ merge(Compressor.prototype, {
             return !immutable
                 && parent.expression === node
                 && !parent.is_expr_pure(compressor)
-                && (!(value instanceof AST_Function)
-                    || !(parent instanceof AST_New) && value.contains_this());
+                && (!is_function(value) || !(parent instanceof AST_New) && value.contains_this());
         }
         if (parent instanceof AST_ForIn) return parent.init === node;
         if (parent instanceof AST_ObjectKeyVal) {
@@ -390,13 +389,13 @@ merge(Compressor.prototype, {
             def.fixed = !def.const_redefs
                 && !def.scope.pinned()
                 && !compressor.exposed(def)
-                && !(def.init instanceof AST_Function && def.init !== def.scope)
+                && !(is_function(def.init) && def.init !== def.scope)
                 && def.init;
-            if (def.fixed instanceof AST_Defun && !all(def.references, function(ref) {
+            if (is_defun(def.fixed) && !all(def.references, function(ref) {
                 var scope = ref.scope.resolve();
                 do {
                     if (def.scope === scope) return true;
-                } while (scope instanceof AST_Function && (scope = scope.parent_scope.resolve()));
+                } while (is_function(scope) && (scope = scope.parent_scope.resolve()));
             })) {
                 tw.defun_ids[def.id] = false;
             }
@@ -421,7 +420,7 @@ merge(Compressor.prototype, {
                 scope.may_call_this = noop;
                 if (!scope.contains_this()) return;
                 scope.functions.each(function(def) {
-                    if (def.init instanceof AST_Defun && !(def.id in tw.defun_ids)) {
+                    if (is_defun(def.init) && !(def.id in tw.defun_ids)) {
                         tw.defun_ids[def.id] = false;
                     }
                 });
@@ -458,7 +457,7 @@ merge(Compressor.prototype, {
 
         function walk_defuns(tw, scope) {
             scope.functions.each(function(def) {
-                if (def.init instanceof AST_Defun && !tw.defun_visited[def.id]) {
+                if (is_defun(def.init) && !tw.defun_visited[def.id]) {
                     tw.defun_ids[def.id] = tw.safe_ids;
                     def.init.walk(tw);
                 }
@@ -496,7 +495,7 @@ merge(Compressor.prototype, {
                 }
                 return !safe.assign || safe.assign === tw.safe_ids;
             }
-            return def.fixed instanceof AST_Defun;
+            return is_defun(def.fixed);
         }
 
         function safe_to_assign(tw, def, declare) {
@@ -651,6 +650,20 @@ merge(Compressor.prototype, {
             lhs.walk(scanner);
         }
 
+        function reduce_defun(tw, descend, compressor) {
+            var id = this.name.definition().id;
+            if (tw.defun_visited[id]) return true;
+            if (tw.defun_ids[id] !== tw.safe_ids) return true;
+            tw.defun_visited[id] = true;
+            this.inlined = false;
+            push(tw);
+            reset_variables(tw, compressor, this);
+            descend();
+            pop(tw);
+            walk_defuns(tw, this);
+            return true;
+        }
+
         def(AST_Assign, function(tw, descend, compressor) {
             var node = this;
             var left = node.left;
@@ -732,6 +745,7 @@ merge(Compressor.prototype, {
                 }
             }
         });
+        def(AST_AsyncDefun, reduce_defun);
         def(AST_Binary, function(tw) {
             if (!lazy_op[this.operator]) return;
             this.left.walk(tw);
@@ -748,31 +762,27 @@ merge(Compressor.prototype, {
         def(AST_Call, function(tw, descend) {
             tw.find_parent(AST_Scope).may_call_this();
             var exp = this.expression;
-            var tail = exp.tail_node();
-            if (tail instanceof AST_Function) {
-                if (exp !== tail) exp.expressions.slice(0, -1).forEach(function(node) {
-                    node.walk(tw);
-                });
+            if (is_function(exp)) {
                 this.args.forEach(function(arg) {
                     arg.walk(tw);
                 });
-                tail.walk(tw);
+                exp.walk(tw);
                 return true;
-            } else if (tail instanceof AST_SymbolRef) {
-                var def = tail.definition();
+            } else if (exp instanceof AST_SymbolRef) {
+                var def = exp.definition();
                 if (this.TYPE == "Call" && tw.in_boolean_context()) def.bool_fn++;
-                if (!(def.fixed instanceof AST_Defun)) return;
+                if (!is_defun(def.fixed)) return;
                 var defun = mark_defun(tw, def);
                 if (!defun) return;
                 descend();
                 defun.walk(tw);
                 return true;
             } else if (this.TYPE == "Call"
-                && tail instanceof AST_Assign
-                && tail.operator == "="
-                && tail.left instanceof AST_SymbolRef
+                && exp instanceof AST_Assign
+                && exp.operator == "="
+                && exp.left instanceof AST_SymbolRef
                 && tw.in_boolean_context()) {
-                tail.left.definition().bool_fn++;
+                exp.left.definition().bool_fn++;
             }
         });
         def(AST_Conditional, function(tw) {
@@ -785,19 +795,7 @@ merge(Compressor.prototype, {
             pop(tw);
             return true;
         });
-        def(AST_Defun, function(tw, descend, compressor) {
-            var id = this.name.definition().id;
-            if (tw.defun_visited[id]) return true;
-            if (tw.defun_ids[id] !== tw.safe_ids) return true;
-            tw.defun_visited[id] = true;
-            this.inlined = false;
-            push(tw);
-            reset_variables(tw, compressor, this);
-            descend();
-            pop(tw);
-            walk_defuns(tw, this);
-            return true;
-        });
+        def(AST_Defun, reduce_defun);
         def(AST_Do, function(tw) {
             var saved_loop = tw.in_loop;
             tw.in_loop = this;
@@ -1007,8 +1005,7 @@ merge(Compressor.prototype, {
             }
             if (!this.fixed) this.fixed = d.fixed;
             var parent;
-            if (d.fixed instanceof AST_Defun
-                && !((parent = tw.parent()) instanceof AST_Call && parent.expression === this)) {
+            if (is_defun(d.fixed) && !((parent = tw.parent()) instanceof AST_Call && parent.expression === this)) {
                 var defun = mark_defun(tw, d);
                 if (defun) defun.walk(tw);
             }
index ca60403..4f34289 100644 (file)
@@ -328,3 +328,62 @@ property_access_expression: {
     expect_stdout: "function"
     node_version: ">=8"
 }
+
+issue_4347_1: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        var a = "foo";
+        f();
+        a = "bar";
+        f();
+        async function f() {
+            console.log(a);
+        }
+    }
+    expect: {
+        var a = "foo";
+        f();
+        a = "bar";
+        f();
+        async function f() {
+            console.log(a);
+        }
+    }
+    expect_stdout: [
+        "foo",
+        "bar",
+    ]
+    node_version: ">=8"
+}
+
+issue_4347_2: {
+    options = {
+        evaluate: true,
+        reduce_vars: true,
+        toplevel: true,
+        unused: true,
+    }
+    input: {
+        var a = "PASS";
+        (async function() {
+            throw 42;
+            a = "FAIL";
+        })();
+        console.log(a);
+    }
+    expect: {
+        var a = "PASS";
+        (async function() {
+            throw 42;
+            a = "FAIL";
+        })();
+        console.log(a);
+    }
+    expect_stdout: "PASS"
+    node_version: ">=8"
+}