fix corner cases in `inline` (#4640)
authorAlex Lam S.L <alexlamsl@gmail.com>
Wed, 10 Feb 2021 12:40:57 +0000 (12:40 +0000)
committerGitHub <noreply@github.com>
Wed, 10 Feb 2021 12:40:57 +0000 (20:40 +0800)
fixes #4639

lib/compress.js
lib/parse.js
test/compress/yields.js
test/ufuzz/index.js

index 5a2a054..94719af 100644 (file)
@@ -8336,7 +8336,7 @@ merge(Compressor.prototype, {
         var can_inline = can_drop && compressor.option("inline") && !self.is_expr_pure(compressor);
         if (can_inline && stat instanceof AST_Return) {
             var value = stat.value;
-            if (exp === fn && (!value || value.is_constant_expression() && safe_from_await(value))) {
+            if (exp === fn && (!value || value.is_constant_expression() && safe_from_await_yield(value))) {
                 return make_sequence(self, convert_args(value)).optimize(compressor);
             }
         }
@@ -8506,8 +8506,17 @@ merge(Compressor.prototype, {
             return args;
         }
 
-        function safe_from_await(node) {
-            if (!is_async(scope || compressor.find_parent(AST_Scope))) return true;
+        function avoid_await_yield() {
+            var avoid = [];
+            var parent_scope = scope || compressor.find_parent(AST_Scope);
+            if (is_async(parent_scope)) avoid.push("await");
+            if (is_generator(parent_scope)) avoid.push("yield");
+            return avoid.length && makePredicate(avoid);
+        }
+
+        function safe_from_await_yield(node) {
+            var avoid = avoid_await_yield();
+            if (!avoid) return true;
             var safe = true;
             var tw = new TreeWalker(function(node) {
                 if (!safe) return true;
@@ -8515,12 +8524,12 @@ merge(Compressor.prototype, {
                     if (node === fn) return;
                     if (is_arrow(node)) {
                         for (var i = 0; safe && i < node.argnames.length; i++) node.argnames[i].walk(tw);
-                    } else if (node instanceof AST_LambdaDefinition && node.name.name == "await") {
+                    } else if (node instanceof AST_LambdaDefinition && avoid[node.name.name]) {
                         safe = false;
                     }
                     return true;
                 }
-                if (node instanceof AST_Symbol && node.name == "await" && node !== fn.name) safe = false;
+                if (node instanceof AST_Symbol && avoid[node.name] && node !== fn.name) safe = false;
             });
             node.walk(tw);
             return safe;
@@ -8579,10 +8588,10 @@ merge(Compressor.prototype, {
                 return def.references.length - def.replaced < 2 && def.orig[0] instanceof AST_SymbolFunarg;
             })) return;
             var abort = false;
+            var avoid = avoid_await_yield();
             var begin;
             var in_order = [];
             var side_effects = false;
-            var verify_await = true;
             value.walk(new TreeWalker(function(node, descend) {
                 if (abort) return true;
                 if (node instanceof AST_Binary && lazy_op[node.operator]
@@ -8591,10 +8600,7 @@ merge(Compressor.prototype, {
                     return;
                 }
                 if (node instanceof AST_Scope) return abort = true;
-                if (verify_await && node instanceof AST_Symbol && node.name == "await") {
-                    if (is_async(compressor.find_parent(AST_Scope))) return abort = true;
-                    verify_await = false;
-                }
+                if (avoid && node instanceof AST_Symbol && avoid[node.name]) return abort = true;
                 if (node instanceof AST_SymbolRef) {
                     var def = node.definition();
                     if (fn.variables.get(node.name) !== def) {
@@ -8697,7 +8703,7 @@ merge(Compressor.prototype, {
             } while (!(scope instanceof AST_Scope));
             insert = scope.body.indexOf(child) + 1;
             if (!insert) return false;
-            if (!safe_from_await(fn)) return false;
+            if (!safe_from_await_yield(fn)) return false;
             var safe_to_inject = exp !== fn || fn.parent_scope.resolve() === scope;
             if (scope instanceof AST_Toplevel) {
                 if (compressor.toplevel.vars) {
index ef0fc49..27803f2 100644 (file)
@@ -1166,7 +1166,9 @@ function parse($TEXT, options) {
 
     function arrow(exprs, start, async) {
         var was_async = S.in_async;
+        var was_gen = S.in_generator;
         S.in_async = async;
+        S.in_generator = false;
         var was_funarg = S.in_funarg;
         S.in_funarg = S.in_function;
         var argnames = exprs.map(to_funarg);
@@ -1196,6 +1198,7 @@ function parse($TEXT, options) {
         --S.in_function;
         S.in_loop = loop;
         S.labels = labels;
+        S.in_generator = was_gen;
         S.in_async = was_async;
         return new (async ? AST_AsyncArrow : AST_Arrow)({
             start: start,
index 7ba19bf..78a5389 100644 (file)
@@ -96,6 +96,18 @@ pause_resume: {
     node_version: ">=4"
 }
 
+arrow_yield: {
+    input: {
+        yield = "PASS";
+        console.log(function*() {
+            return () => yield || "FAIL";
+        }().next().value());
+    }
+    expect_exact: 'yield="PASS";console.log(function*(){return()=>yield||"FAIL"}().next().value());'
+    expect_stdout: "PASS"
+    node_version: ">=4"
+}
+
 for_of: {
     input: {
         function* f() {
@@ -833,3 +845,47 @@ issue_4633: {
     ]
     node_version: ">=4"
 }
+
+issue_4639_1: {
+    options = {
+        inline: true,
+    }
+    input: {
+        console.log(function*() {
+            return function() {
+                return yield => "PASS";
+            }();
+        }().next().value());
+    }
+    expect: {
+        console.log(function*() {
+            return function() {
+                return yield => "PASS";
+            }();
+        }().next().value());
+    }
+    expect_stdout: "PASS"
+    node_version: ">=4"
+}
+
+issue_4639_2: {
+    options = {
+        inline: true,
+    }
+    input: {
+        (function*() {
+            console.log(function() {
+                return typeof yield;
+            }());
+        })().next();
+    }
+    expect: {
+        (function*() {
+            console.log(function() {
+                return typeof yield;
+            }());
+        })().next();
+    }
+    expect_stdout: "undefined"
+    node_version: ">=4"
+}
index b4a2627..7f7da39 100644 (file)
@@ -333,6 +333,7 @@ var VAR_NAMES = [
     "arguments",
     "async",
     "await",
+    "yield",
 ];
 var INITIAL_NAMES_LEN = VAR_NAMES.length;