enhance `inline` (#5243)
authorAlex Lam S.L <alexlamsl@gmail.com>
Mon, 27 Dec 2021 16:53:12 +0000 (16:53 +0000)
committerGitHub <noreply@github.com>
Mon, 27 Dec 2021 16:53:12 +0000 (00:53 +0800)
lib/compress.js
test/compress/awaits.js
test/compress/yields.js

index 22cbdd1..7a4c53e 100644 (file)
@@ -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);
     });
index 577474b..170bfd1 100644 (file)
@@ -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,
index 306ec6e..6e40f3e 100644 (file)
@@ -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,