From e1b03d0235c207a40c35d27d94bda372a253efd1 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Wed, 29 Jun 2022 00:37:58 +0100 Subject: [PATCH] fix corner case in `inline` (#5529) fixes #5528 --- lib/compress.js | 101 +++++++++++++------------- test/compress/awaits.js | 154 ++++++++++++++++++++++++++++++++++++++++ test/reduce.js | 4 +- test/ufuzz/index.js | 4 +- 4 files changed, 210 insertions(+), 53 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index d8160222227..9b28bc7d7ce 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -13475,7 +13475,7 @@ Compressor.prototype.compress = function(node) { def(AST_Assign, noop); def(AST_Await, function(compressor, scope, no_return, in_loop) { var self = this; - var inlined = sync(self.expression).try_inline(compressor, scope, no_return, in_loop); + var inlined = self.expression.try_inline(compressor, scope, no_return, in_loop, true); if (!inlined) return; if (!no_return) scan_local_returns(inlined, function(node) { node.in_bool = false; @@ -13490,35 +13490,13 @@ Compressor.prototype.compress = function(node) { body: make_node(AST_Await, self, { expression: make_node(AST_Number, self, { value: 0 })}), }) ], }); - - function sync(node) { - if (!no_return) return node; - if (node.TYPE != "Call") return node; - var fn = node.expression; - switch (fn.CTOR) { - case AST_AsyncArrow: - fn = make_node(AST_Arrow, fn, fn); - break; - case AST_AsyncFunction: - fn = make_node(AST_Function, fn, fn); - break; - case AST_AsyncGeneratorFunction: - fn = make_node(AST_GeneratorFunction, fn, fn); - break; - default: - return node; - } - node = node.clone(); - node.expression = fn; - return node; - } }); - def(AST_Binary, function(compressor, scope, no_return, in_loop) { + def(AST_Binary, function(compressor, scope, no_return, in_loop, in_await) { if (no_return === undefined) return; var self = this; var op = self.operator; if (!lazy_op[op]) return; - var inlined = self.right.try_inline(compressor, scope, no_return, in_loop); + var inlined = self.right.try_inline(compressor, scope, no_return, in_loop, in_await); if (!inlined) return; return make_node(AST_If, self, { condition: make_condition(self.left), @@ -13554,7 +13532,7 @@ Compressor.prototype.compress = function(node) { body[last] = inlined; return this; }); - def(AST_Call, function(compressor, scope, no_return, in_loop) { + def(AST_Call, function(compressor, scope, no_return, in_loop, in_await) { if (compressor.option("inline") < 4) return; var call = this; if (call.is_expr_pure(compressor)) return; @@ -13587,7 +13565,7 @@ Compressor.prototype.compress = function(node) { } defined.set("arguments", true); } - var async = is_async(fn); + var async = !in_await && is_async(fn); if (async) { if (!compressor.option("awaits")) return; if (!is_async(scope)) return; @@ -13629,15 +13607,40 @@ Compressor.prototype.compress = function(node) { if (has_arg_refs(fn, fn.rest)) return; simple_argnames = false; } - if (no_return && !all(fn.body, function(stat) { - var abort = false; - stat.walk(new TreeWalker(function(node) { - if (abort) return true; - if (async && node instanceof AST_Await || node instanceof AST_Return) return abort = true; - if (node instanceof AST_Scope && node !== fn) return true; - })); - return !abort; - })) return; + var verify_body; + if (no_return) { + verify_body = function(stat) { + var abort = false; + stat.walk(new TreeWalker(function(node) { + if (abort) return true; + if (async && node instanceof AST_Await || node instanceof AST_Return) return abort = true; + if (node instanceof AST_Scope) return true; + })); + return !abort; + }; + } else if (in_await && !is_async(fn)) { + verify_body = function(stat) { + var abort = false; + var find_return = new TreeWalker(function(node) { + if (abort) return true; + if (node instanceof AST_Return) return abort = true; + if (node instanceof AST_Scope) return true; + }); + stat.walk(new TreeWalker(function(node) { + if (abort) return true; + if (node instanceof AST_Try) { + if (node.bfinally && all(node.body, function(stat) { + stat.walk(find_return); + return !abort; + }) && node.bcatch) node.bcatch.walk(find_return); + return true; + } + if (node instanceof AST_Scope) return true; + })); + return !abort; + }; + } + if (verify_body && !all(fn.body, verify_body)) return; if (!safe_from_await_yield(fn, avoid_await_yield(compressor, scope))) return; fn.functions.each(function(def, name) { scope.functions.set(name, def); @@ -13737,10 +13740,10 @@ Compressor.prototype.compress = function(node) { syms.add(def.id, sym); } }); - def(AST_Conditional, function(compressor, scope, no_return, in_loop) { + def(AST_Conditional, function(compressor, scope, no_return, in_loop, in_await) { var self = this; - var body = self.consequent.try_inline(compressor, scope, no_return, in_loop); - var alt = self.alternative.try_inline(compressor, scope, no_return, in_loop); + var body = self.consequent.try_inline(compressor, scope, no_return, in_loop, in_await); + var alt = self.alternative.try_inline(compressor, scope, no_return, in_loop, in_await); if (!body && !alt) return; return make_node(AST_If, self, { condition: self.condition, @@ -13775,7 +13778,7 @@ Compressor.prototype.compress = function(node) { if (body) this.body = body; var obj = this.object; if (obj instanceof AST_Sequence) { - var inlined = inline_sequence(compressor, scope, true, in_loop, obj, 1); + var inlined = inline_sequence(compressor, scope, true, in_loop, false, obj, 1); if (inlined) { this.object = obj.tail_node(); inlined.body.push(this); @@ -13794,7 +13797,7 @@ Compressor.prototype.compress = function(node) { } var cond = this.condition; if (cond instanceof AST_Sequence) { - var inlined = inline_sequence(compressor, scope, true, in_loop, cond, 1); + var inlined = inline_sequence(compressor, scope, true, in_loop, false, cond, 1); if (inlined) { this.condition = cond.tail_node(); inlined.body.push(this); @@ -13826,10 +13829,10 @@ Compressor.prototype.compress = function(node) { var value = this.value; return value && value.try_inline(compressor, scope, undefined, in_loop === "try"); }); - function inline_sequence(compressor, scope, no_return, in_loop, node, skip) { + function inline_sequence(compressor, scope, no_return, in_loop, in_await, node, skip) { var body = [], exprs = node.expressions, no_ret = no_return; - for (var i = exprs.length - (skip || 0), j = i; --i >= 0; no_ret = true) { - var inlined = exprs[i].try_inline(compressor, scope, no_ret, in_loop); + for (var i = exprs.length - (skip || 0), j = i; --i >= 0; no_ret = true, in_await = false) { + var inlined = exprs[i].try_inline(compressor, scope, no_ret, in_loop, in_await); if (!inlined) continue; flush(); body.push(inlined); @@ -13848,8 +13851,8 @@ Compressor.prototype.compress = function(node) { j = i; } } - def(AST_Sequence, function(compressor, scope, no_return, in_loop) { - return inline_sequence(compressor, scope, no_return, in_loop, this); + def(AST_Sequence, function(compressor, scope, no_return, in_loop, in_await) { + return inline_sequence(compressor, scope, no_return, in_loop, in_await, this); }); def(AST_SimpleStatement, function(compressor, scope, no_return, in_loop) { var body = this.body; @@ -13865,12 +13868,12 @@ Compressor.prototype.compress = function(node) { }); return body.try_inline(compressor, scope, no_return || false, in_loop); }); - def(AST_UnaryPrefix, function(compressor, scope, no_return, in_loop) { + def(AST_UnaryPrefix, function(compressor, scope, no_return, in_loop, in_await) { var self = this; var op = self.operator; if (unary_side_effects[op]) return; if (!no_return && op == "void") no_return = false; - var inlined = self.expression.try_inline(compressor, scope, no_return, in_loop); + var inlined = self.expression.try_inline(compressor, scope, no_return, in_loop, in_await); if (!inlined) return; if (!no_return) scan_local_returns(inlined, function(node) { node.in_bool = false; @@ -13888,7 +13891,7 @@ Compressor.prototype.compress = function(node) { if (body) this.body = body; var exp = this.expression; if (exp instanceof AST_Sequence) { - var inlined = inline_sequence(compressor, scope, true, in_loop, exp, 1); + var inlined = inline_sequence(compressor, scope, true, in_loop, false, exp, 1); if (inlined) { this.expression = exp.tail_node(); inlined.body.push(this); diff --git a/test/compress/awaits.js b/test/compress/awaits.js index 1e1b482b8d1..04016c9cf15 100644 --- a/test/compress/awaits.js +++ b/test/compress/awaits.js @@ -3023,3 +3023,157 @@ issue_5506: { expect_stdout: "PASS" node_version: ">=8" } + +issue_5528_1: { + options = { + inline: true, + } + input: { + (async function() { + await function() { + try { + return; + } finally { + console.log("foo"); + } + }(); + })(); + console.log("bar"); + } + expect: { + (async function() { + await function() { + try { + return; + } finally { + console.log("foo"); + } + }(); + })(); + console.log("bar"); + } + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=8" +} + +issue_5528_2: { + options = { + inline: true, + } + input: { + (async function() { + await function() { + try { + return 42; + } finally { + console.log("foo"); + } + }(); + })(); + console.log("bar"); + } + expect: { + (async function() { + await function() { + try { + return 42; + } finally { + console.log("foo"); + } + }(); + })(); + console.log("bar"); + } + expect_stdout: [ + "foo", + "bar", + ] + node_version: ">=8" +} + +issue_5528_3: { + options = { + inline: true, + } + input: { + (async function() { + await function() { + try { + FAIL; + } catch (e) { + return console.log("foo"); + } finally { + console.log("bar"); + } + }(); + })(); + console.log("baz"); + } + expect: { + (async function() { + await function() { + try { + FAIL; + } catch (e) { + return console.log("foo"); + } finally { + console.log("bar"); + } + }(); + })(); + console.log("baz"); + } + expect_stdout: [ + "foo", + "bar", + "baz", + ] + node_version: ">=8" +} + +issue_5528_4: { + options = { + inline: true, + } + input: { + (async function() { + await function() { + try { + return { + then() { + console.log("foo"); + }, + }; + } finally { + console.log("bar"); + } + }(); + })(); + console.log("baz"); + } + expect: { + (async function() { + await function() { + try { + return { + then() { + console.log("foo"); + }, + }; + } finally { + console.log("bar"); + } + }(); + })(); + console.log("baz"); + } + expect_stdout: [ + "bar", + "baz", + "foo", + ] + node_version: ">=8" +} diff --git a/test/reduce.js b/test/reduce.js index ed76beed3fe..ffb1c2f5ee6 100644 --- a/test/reduce.js +++ b/test/reduce.js @@ -778,9 +778,9 @@ function compare_run_code(code, minify_options, result_cache, max_timeout) { function run(code, timeout) { if (minify_options.module) code = [ '"use strict";', - "(async function(){", + "(async ()=>{", code, - "})();" + "})().catch(e=>console.log(e));", ].join("\n"); return run_code(code, toplevel, result_cache, timeout); } diff --git a/test/ufuzz/index.js b/test/ufuzz/index.js index c8ee64dc69c..415ed45ba2e 100644 --- a/test/ufuzz/index.js +++ b/test/ufuzz/index.js @@ -2104,9 +2104,9 @@ if (require.main !== module) { function run_code(code, toplevel, timeout) { if (async && has_await) code = [ '"use strict";', - "(async function(){", + "(async ()=>{", code, - "})();" + "})().catch(e=>console.log(e));", ].join("\n"); return sandbox.run_code(sandbox.patch_module_statements(code), toplevel, timeout); }