From d51caaf3586036d5b62779acb0bc0acbd21930e5 Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Mon, 27 Dec 2021 16:53:12 +0000 Subject: [PATCH] enhance `inline` (#5243) --- lib/compress.js | 43 ++++++- test/compress/awaits.js | 247 ++++++++++++++++++++++++++++++++++++++++ test/compress/yields.js | 90 ++++++++++++++- 3 files changed, 374 insertions(+), 6 deletions(-) diff --git a/lib/compress.js b/lib/compress.js index 22cbdd19..7a4c53eb 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -12778,14 +12778,19 @@ Compressor.prototype.compress = function(node) { (function(def) { def(AST_Node, noop); + def(AST_Await, function(compressor, scope) { + return this.expression.try_inline(compressor, scope); + }); def(AST_Call, function(compressor, scope) { if (compressor.option("inline") < 4) return; var call = this; if (call.is_expr_pure(compressor)) return; var fn = call.expression; - if (!(fn instanceof AST_Function)) return; + if (!(fn instanceof AST_LambdaExpression)) return; if (fn.name) return; if (fn.uses_arguments) return; + if (is_arrow(fn) && fn.value) return; + if (is_generator(fn)) return; if (fn.contains_this()) return; if (!scope) scope = find_scope(compressor); var defined = new Dictionary(); @@ -12795,6 +12800,7 @@ Compressor.prototype.compress = function(node) { }); scope = scope.parent_scope; } + if (is_async(fn) && !(compressor.option("awaits") && is_async(scope))) return; var names = scope.var_names(); if (!fn.variables.all(function(def, name) { if (!defined.has(name) && !names.has(name)) return true; @@ -12864,9 +12870,16 @@ Compressor.prototype.compress = function(node) { syms.each(function(orig, id) { [].unshift.apply(defs[id].orig, orig); }); - return make_node(AST_BlockStatement, call, { + var inlined = make_node(AST_BlockStatement, call, { body: body.concat(fn.body, make_node(AST_Return, call, { value: null })), }); + if (is_async(fn)) scan_local_returns(inlined, function(node) { + var value = node.value; + if (!value) return; + if (is_undefined(value)) return; + node.value = make_node(AST_Await, call, { expression: value }); + }); + return inlined; function process(sym, argname) { var def = argname.definition(); @@ -12901,11 +12914,31 @@ Compressor.prototype.compress = function(node) { } node.value = make_node(AST_UnaryPrefix, self, { operator: op, - expression: value || make_node(AST_Undefined, node), - }).optimize(compressor); + expression: value || make_node(AST_Undefined, node).transform(compressor), + }); }); return inlined; - }) + }); + def(AST_Yield, function(compressor, scope) { + if (!compressor.option("yields")) return; + if (!this.nested) return; + var call = this.expression; + if (call.TYPE != "Call") return; + var fn = call.expression; + switch (fn.CTOR) { + case AST_AsyncGeneratorFunction: + fn = make_node(AST_AsyncFunction, fn, fn); + break; + case AST_GeneratorFunction: + fn = make_node(AST_Function, fn, fn); + break; + default: + return; + } + call = call.clone(); + call.expression = fn; + return call.try_inline(compressor, scope); + }); })(function(node, func) { node.DEFMETHOD("try_inline", func); }); diff --git a/test/compress/awaits.js b/test/compress/awaits.js index 577474b6..170bfd15 100644 --- a/test/compress/awaits.js +++ b/test/compress/awaits.js @@ -395,6 +395,253 @@ inline_await_this: { node_version: ">=8" } +inline_block: { + options = { + awaits: true, + if_return: true, + inline: true, + } + input: { + console.log("foo"); + (async function() { + console.log("bar"); + (async function() { + for (var a of [ "baz" ]) + return a; + })(); + })().then(console.log); + console.log("moo"); + } + expect: { + console.log("foo"); + (async function() { + console.log("bar"); + for (var a of [ "baz" ]) + return void await a; + })().then(console.log); + console.log("moo"); + } + expect_stdout: [ + "foo", + "bar", + "moo", + "undefined", + ] + node_version: ">=8" +} + +inline_block_async: { + options = { + awaits: true, + if_return: true, + inline: true, + } + input: { + console.log("foo"); + (async function() { + console.log("bar"); + (async function() { + for (var a of [ "baz" ]) + return { + then(r) { + console.log("moo"); + r(a); + }, + }; + })(); + })().then(console.log); + console.log("moz"); + } + expect: { + console.log("foo"); + (async function() { + console.log("bar"); + for (var a of [ "baz" ]) + return void await { + then(r) { + console.log("moo"); + r(a); + }, + }; + })().then(console.log); + console.log("moz"); + } + expect_stdout: [ + "foo", + "bar", + "moz", + "moo", + "undefined", + ] + node_version: ">=8" +} + +inline_block_await: { + options = { + awaits: true, + if_return: true, + inline: true, + } + input: { + console.log("foo"); + (async function() { + console.log("bar"); + await async function() { + for (var a of [ "baz" ]) + return a; + }(); + })().then(console.log); + console.log("moo"); + } + expect: { + console.log("foo"); + (async function() { + console.log("bar"); + for (var a of [ "baz" ]) + return void await a; + })().then(console.log); + console.log("moo"); + } + expect_stdout: [ + "foo", + "bar", + "moo", + "undefined", + ] + node_version: ">=8" +} + +inline_block_await_async: { + options = { + awaits: true, + if_return: true, + inline: true, + } + input: { + console.log("foo"); + (async function() { + console.log("bar"); + await async function() { + for (var a of [ "baz" ]) + return { + then(r) { + console.log("moo"); + r(a); + }, + }; + }(); + })().then(console.log); + console.log("moz"); + } + expect: { + console.log("foo"); + (async function() { + console.log("bar"); + for (var a of [ "baz" ]) + return void await { + then(r) { + console.log("moo"); + r(a); + }, + };; + })().then(console.log); + console.log("moz"); + } + expect_stdout: [ + "foo", + "bar", + "moz", + "moo", + "undefined", + ] + node_version: ">=8" +} + +inline_block_return: { + options = { + awaits: true, + if_return: true, + inline: true, + passes: 2, + side_effects: true, + } + input: { + console.log("foo"); + (async function() { + console.log("bar"); + return async function() { + for (var a of [ "baz" ]) + return a; + }(); + })().then(console.log); + console.log("moo"); + } + expect: { + console.log("foo"); + (async function() { + console.log("bar"); + for (var a of [ "baz" ]) + return a; + })().then(console.log); + console.log("moo"); + } + expect_stdout: [ + "foo", + "bar", + "moo", + "baz", + ] + node_version: ">=8" +} + +inline_block_return_async: { + options = { + awaits: true, + if_return: true, + inline: true, + passes: 2, + side_effects: true, + } + input: { + console.log("foo"); + (async function() { + console.log("bar"); + return async function() { + for (var a of [ "baz" ]) + return { + then(r) { + console.log("moo"); + r(a); + }, + }; + }(); + })().then(console.log); + console.log("moz"); + } + expect: { + console.log("foo"); + (async function() { + console.log("bar"); + for (var a of [ "baz" ]) + return { + then(r) { + console.log("moo"); + r(a); + }, + }; + })().then(console.log); + console.log("moz"); + } + expect_stdout: [ + "foo", + "bar", + "moz", + "moo", + "baz", + ] + node_version: ">=8" +} + await_unary: { options = { awaits: true, diff --git a/test/compress/yields.js b/test/compress/yields.js index 306ec6e4..6e40f3e0 100644 --- a/test/compress/yields.js +++ b/test/compress/yields.js @@ -749,7 +749,7 @@ lift_sequence: { node_version: ">=4" } -inline_nested_yield: { +inline_nested: { options = { inline: true, sequences: true, @@ -784,6 +784,94 @@ inline_nested_yield: { node_version: ">=4" } +inline_nested_async: { + options = { + awaits: true, + inline: true, + sequences: true, + side_effects: true, + yields: true, + } + input: { + console.log("foo"); + var a = async function*() { + console.log(await(yield* async function*() { + yield { + then: r => r("bar"), + }; + return "baz"; + }())); + }(); + console.log("moo"); + a.next().then(function f(b) { + console.log(b.value); + b.done || a.next().then(f); + }); + console.log("moz"); + } + expect: { + console.log("foo"); + var a = async function*() { + console.log((yield { + then: r => r("bar"), + }, await "baz")); + }(); + console.log("moo"), + a.next().then(function f(b) { + console.log(b.value), + b.done || a.next().then(f); + }), + console.log("moz"); + } + expect_stdout: [ + "foo", + "moo", + "moz", + "bar", + "baz", + "undefined", + ] + node_version: ">=10" +} + +inline_nested_block: { + options = { + if_return: true, + inline: true, + yields: true, + } + input: { + var a = function*() { + yield* function*() { + for (var a of [ "foo", "bar" ]) + yield a; + return "FAIL"; + }(); + }(), b; + do { + b = a.next(); + console.log(b.value); + } while (!b.done); + } + expect: { + var a = function*() { + for (var a of [ "foo", "bar" ]) + yield a; + "FAIL"; + }(), b; + do { + b = a.next(); + console.log(b.value); + } while (!b.done); + } + expect_stdout: [ + "foo", + "bar", + "undefined", + ] + node_version: ">=4" +} + dont_inline_nested: { options = { inline: true,