From c6e287331d8bf547e1d8dc578847224b137e866b Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 10 Feb 2021 12:40:57 +0000 Subject: [PATCH] fix corner cases in `inline` (#4640) fixes #4639 --- lib/compress.js | 28 +++++++++++++-------- lib/parse.js | 3 +++ test/compress/yields.js | 56 +++++++++++++++++++++++++++++++++++++++++ test/ufuzz/index.js | 1 + 4 files changed, 77 insertions(+), 11 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 5a2a0543..94719afa 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -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) { diff --git a/lib/parse.js b/lib/parse.js index ef0fc49a..27803f2e 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -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, diff --git a/test/compress/yields.js b/test/compress/yields.js index 7ba19bfd..78a5389a 100644 --- a/test/compress/yields.js +++ b/test/compress/yields.js @@ -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" +} diff --git a/test/ufuzz/index.js b/test/ufuzz/index.js index b4a26272..7f7da390 100644 --- a/test/ufuzz/index.js +++ b/test/ufuzz/index.js @@ -333,6 +333,7 @@ var VAR_NAMES = [ "arguments", "async", "await", + "yield", ]; var INITIAL_NAMES_LEN = VAR_NAMES.length; -- 2.34.1