support asynchronous arrow functions (#4530)

This commit is contained in:
Alex Lam S.L
2021-01-10 03:34:26 +00:00
committed by GitHub
parent 0818d396c5
commit ba54d074d8
10 changed files with 159 additions and 56 deletions

View File

@@ -567,8 +567,20 @@ var AST_Accessor = DEFNODE("Accessor", null, {
}, },
}, AST_Lambda); }, AST_Lambda);
function is_arrow(node) {
return node instanceof AST_AsyncArrow || node instanceof AST_Arrow;
}
function is_function(node) { function is_function(node) {
return node instanceof AST_Arrow || node instanceof AST_AsyncFunction || node instanceof AST_Function; return is_arrow(node) || node instanceof AST_AsyncFunction || node instanceof AST_Function;
}
function walk_lambda(node, tw) {
if (is_arrow(node) && node.value) {
node.value.walk(tw);
} else {
walk_body(node, tw);
}
} }
var AST_Arrow = DEFNODE("Arrow", "inlined value", { var AST_Arrow = DEFNODE("Arrow", "inlined value", {
@@ -601,9 +613,38 @@ var AST_Arrow = DEFNODE("Arrow", "inlined value", {
}, AST_Lambda); }, AST_Lambda);
function is_async(node) { function is_async(node) {
return node instanceof AST_AsyncDefun || node instanceof AST_AsyncFunction; return node instanceof AST_AsyncArrow || node instanceof AST_AsyncDefun || node instanceof AST_AsyncFunction;
} }
var AST_AsyncArrow = DEFNODE("AsyncArrow", "inlined value", {
$documentation: "An asynchronous arrow function expression",
$propdoc: {
value: "[AST_Node?] simple return expression, or null if using function body.",
},
walk: function(visitor) {
var node = this;
visitor.visit(node, function() {
node.argnames.forEach(function(argname) {
argname.walk(visitor);
});
if (node.rest) node.rest.walk(visitor);
if (node.value) {
node.value.walk(visitor);
} else {
walk_body(node, visitor);
}
});
},
_validate: function() {
if (this.name != null) throw new Error("name must be null");
if (this.uses_arguments) throw new Error("uses_arguments must be false");
if (this.value != null) {
must_be_expression(this, "value");
if (this.body.length) throw new Error("body must be empty if value exists");
}
},
}, AST_Lambda);
var AST_AsyncFunction = DEFNODE("AsyncFunction", "inlined name", { var AST_AsyncFunction = DEFNODE("AsyncFunction", "inlined name", {
$documentation: "An asynchronous function expression", $documentation: "An asynchronous function expression",
$propdoc: { $propdoc: {

View File

@@ -749,11 +749,7 @@ merge(Compressor.prototype, {
} }
}); });
}); });
if (fn instanceof AST_Arrow && fn.value) { walk_lambda(fn, tw);
fn.value.walk(tw);
} else {
walk_body(fn, tw);
}
var safe_ids = tw.safe_ids; var safe_ids = tw.safe_ids;
pop(tw); pop(tw);
walk_defuns(tw, fn); walk_defuns(tw, fn);
@@ -1893,7 +1889,7 @@ merge(Compressor.prototype, {
return !(argname instanceof AST_Destructured); return !(argname instanceof AST_Destructured);
})) { })) {
abort = true; abort = true;
} else if (fn instanceof AST_Arrow && fn.value) { } else if (is_arrow(fn) && fn.value) {
fn.value.transform(scanner); fn.value.transform(scanner);
} else for (var i = 0; !abort && i < fn.body.length; i++) { } else for (var i = 0; !abort && i < fn.body.length; i++) {
var stat = fn.body[i]; var stat = fn.body[i];
@@ -3765,12 +3761,15 @@ merge(Compressor.prototype, {
if (!(stat instanceof AST_Directive)) return stat; if (!(stat instanceof AST_Directive)) return stat;
} }
} }
AST_Arrow.DEFMETHOD("first_statement", function() {
function arrow_first_statement() {
if (this.value) return make_node(AST_Return, this.value, { if (this.value) return make_node(AST_Return, this.value, {
value: this.value value: this.value
}); });
return skip_directives(this.body); return skip_directives(this.body);
}); }
AST_Arrow.DEFMETHOD("first_statement", arrow_first_statement);
AST_AsyncArrow.DEFMETHOD("first_statement", arrow_first_statement);
AST_Lambda.DEFMETHOD("first_statement", function() { AST_Lambda.DEFMETHOD("first_statement", function() {
return skip_directives(this.body); return skip_directives(this.body);
}); });
@@ -4384,6 +4383,9 @@ merge(Compressor.prototype, {
def(AST_Arrow, function() { def(AST_Arrow, function() {
return basic_negation(this); return basic_negation(this);
}); });
def(AST_AsyncArrow, function() {
return basic_negation(this);
});
def(AST_AsyncFunction, function() { def(AST_AsyncFunction, function() {
return basic_negation(this); return basic_negation(this);
}); });
@@ -4783,7 +4785,7 @@ merge(Compressor.prototype, {
return true; return true;
} }
if (node instanceof AST_This) { if (node instanceof AST_This) {
if (scopes.length == 0 && self instanceof AST_Arrow) result = false; if (scopes.length == 0 && is_arrow(self)) result = false;
return true; return true;
} }
})); }));
@@ -4869,7 +4871,7 @@ merge(Compressor.prototype, {
return trim_block(self); return trim_block(self);
}); });
OPT(AST_Arrow, function(self, compressor) { function opt_arrow(self, compressor) {
if (!compressor.option("arrows")) return self; if (!compressor.option("arrows")) return self;
var body = tighten_body(self.value ? [ self.first_statement() ] : self.body, compressor); var body = tighten_body(self.value ? [ self.first_statement() ] : self.body, compressor);
switch (body.length) { switch (body.length) {
@@ -4886,7 +4888,9 @@ merge(Compressor.prototype, {
break; break;
} }
return self; return self;
}); }
OPT(AST_Arrow, opt_arrow);
OPT(AST_AsyncArrow, opt_arrow);
OPT(AST_Function, function(self, compressor) { OPT(AST_Function, function(self, compressor) {
self.body = tighten_body(self.body, compressor); self.body = tighten_body(self.body, compressor);
@@ -5116,11 +5120,7 @@ merge(Compressor.prototype, {
}); });
if (node.rest) node.rest.mark_symbol(marker, scanner); if (node.rest) node.rest.mark_symbol(marker, scanner);
} }
if (node instanceof AST_Arrow && node.value) { walk_lambda(node, tw);
node.value.walk(tw);
} else {
walk_body(node, tw);
}
pop(); pop();
return true; return true;
} }
@@ -6735,6 +6735,7 @@ merge(Compressor.prototype, {
} }
return this; return this;
}); });
def(AST_AsyncArrow, return_null);
def(AST_AsyncFunction, return_null); def(AST_AsyncFunction, return_null);
def(AST_Await, function(compressor) { def(AST_Await, function(compressor) {
if (!compressor.option("awaits")) return this; if (!compressor.option("awaits")) return this;
@@ -8226,7 +8227,7 @@ merge(Compressor.prototype, {
&& can_drop && can_drop
&& all(fn.body, is_empty) && all(fn.body, is_empty)
&& (fn !== exp || fn_name_unused(fn, compressor)) && (fn !== exp || fn_name_unused(fn, compressor))
&& !(fn instanceof AST_Arrow && fn.value)) { && !(is_arrow(fn) && fn.value)) {
return make_sequence(self, convert_args()).optimize(compressor); return make_sequence(self, convert_args()).optimize(compressor);
} }
} }
@@ -8334,7 +8335,7 @@ merge(Compressor.prototype, {
if (!safe) return true; if (!safe) return true;
if (node instanceof AST_Scope) { if (node instanceof AST_Scope) {
if (node === fn) return; if (node === fn) return;
if (node instanceof AST_Arrow) { if (is_arrow(node)) {
for (var i = 0; safe && i < node.argnames.length; i++) node.argnames[i].walk(tw); for (var i = 0; safe && i < node.argnames.length; i++) node.argnames[i].walk(tw);
} else if (is_defun(node) && node.name.name == "await") { } else if (is_defun(node) && node.name.name == "await") {
safe = false; safe = false;
@@ -10562,7 +10563,7 @@ merge(Compressor.prototype, {
while (p = compressor.parent(i++)) { while (p = compressor.parent(i++)) {
if (p instanceof AST_Lambda) { if (p instanceof AST_Lambda) {
if (p instanceof AST_Accessor) return; if (p instanceof AST_Accessor) return;
if (p instanceof AST_Arrow) continue; if (is_arrow(p)) continue;
fn_parent = compressor.parent(i); fn_parent = compressor.parent(i);
return p; return p;
} }
@@ -10571,13 +10572,14 @@ merge(Compressor.prototype, {
}); });
AST_Arrow.DEFMETHOD("contains_this", return_false); AST_Arrow.DEFMETHOD("contains_this", return_false);
AST_AsyncArrow.DEFMETHOD("contains_this", return_false);
AST_Scope.DEFMETHOD("contains_this", function() { AST_Scope.DEFMETHOD("contains_this", function() {
var result; var result;
var self = this; var self = this;
self.walk(new TreeWalker(function(node) { self.walk(new TreeWalker(function(node) {
if (result) return true; if (result) return true;
if (node instanceof AST_This) return result = true; if (node instanceof AST_This) return result = true;
if (node !== self && node instanceof AST_Scope && !(node instanceof AST_Arrow)) return true; if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true;
})); }));
return result; return result;
}); });

View File

@@ -692,7 +692,7 @@ function OutputStream(options) {
// [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ] // [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
return p instanceof AST_Array return p instanceof AST_Array
// () => (foo, bar) // () => (foo, bar)
|| p instanceof AST_Arrow && p.value === this || is_arrow(p) && p.value === this
// await (foo, bar) // await (foo, bar)
|| p instanceof AST_Await || p instanceof AST_Await
// 1 + (2, 3) + 4 ==> 8 // 1 + (2, 3) + 4 ==> 8
@@ -813,6 +813,9 @@ function OutputStream(options) {
// ({ p: a } = o); // ({ p: a } = o);
if (this.left instanceof AST_DestructuredObject) return needs_parens_obj(output); if (this.left instanceof AST_DestructuredObject) return needs_parens_obj(output);
}); });
PARENS(AST_AsyncArrow, function(output) {
return needs_parens_assign_cond(this, output);
});
PARENS(AST_Conditional, function(output) { PARENS(AST_Conditional, function(output) {
return needs_parens_assign_cond(this, output); return needs_parens_assign_cond(this, output);
}); });
@@ -1005,8 +1008,7 @@ function OutputStream(options) {
} }
}); });
} }
DEFPRINT(AST_Arrow, function(output) { function print_arrow(self, output) {
var self = this;
if (self.argnames.length == 1 && self.argnames[0] instanceof AST_SymbolFunarg && !self.rest) { if (self.argnames.length == 1 && self.argnames[0] instanceof AST_SymbolFunarg && !self.rest) {
self.argnames[0].print(output); self.argnames[0].print(output);
} else { } else {
@@ -1020,6 +1022,14 @@ function OutputStream(options) {
} else { } else {
print_braced(self, output, true); print_braced(self, output, true);
} }
}
DEFPRINT(AST_Arrow, function(output) {
print_arrow(this, output);
});
DEFPRINT(AST_AsyncArrow, function(output) {
output.print("async");
output.space();
print_arrow(this, output);
}); });
function print_lambda(self, output) { function print_lambda(self, output) {
if (self.name) { if (self.name) {
@@ -1207,7 +1217,7 @@ function OutputStream(options) {
if (noin) node.walk(new TreeWalker(function(node) { if (noin) node.walk(new TreeWalker(function(node) {
if (parens) return true; if (parens) return true;
if (node instanceof AST_Binary && node.operator == "in") return parens = true; if (node instanceof AST_Binary && node.operator == "in") return parens = true;
if (node instanceof AST_Scope && !(node instanceof AST_Arrow && node.value)) return true; if (node instanceof AST_Scope && !(is_arrow(node) && node.value)) return true;
})); }));
node.print(output, parens); node.print(output, parens);
} }

View File

@@ -796,13 +796,14 @@ function parse($TEXT, options) {
next(); next();
return function_(AST_AsyncDefun); return function_(AST_AsyncDefun);
} }
break;
case "await": case "await":
if (S.in_async) return simple_statement(); if (S.in_async) return simple_statement();
default: break;
return is_token(peek(), "punc", ":")
? labeled_statement()
: simple_statement();
} }
return is_token(peek(), "punc", ":")
? labeled_statement()
: simple_statement();
case "punc": case "punc":
switch (S.token.value) { switch (S.token.value) {
@@ -1094,16 +1095,19 @@ function parse($TEXT, options) {
end: node.end, end: node.end,
}); });
} }
if (node instanceof AST_SymbolFunarg) return node;
if (node instanceof AST_SymbolRef) return new AST_SymbolFunarg(node); if (node instanceof AST_SymbolRef) return new AST_SymbolFunarg(node);
token_error(node.start, "Invalid arrow parameter"); token_error(node.start, "Invalid arrow parameter");
} }
function arrow(exprs, start) { function arrow(exprs, start, async) {
var was_async = S.in_async; var was_async = S.in_async;
S.in_async = false; S.in_async = async;
var was_funarg = S.in_funarg; var was_funarg = S.in_funarg;
S.in_funarg = S.in_function; S.in_funarg = S.in_function;
var argnames = exprs.map(to_funarg); var argnames = exprs.map(to_funarg);
var rest = exprs.rest || null;
if (rest) rest = to_funarg(rest);
S.in_funarg = was_funarg; S.in_funarg = was_funarg;
expect("=>"); expect("=>");
var body, value; var body, value;
@@ -1129,10 +1133,10 @@ function parse($TEXT, options) {
S.in_loop = loop; S.in_loop = loop;
S.labels = labels; S.labels = labels;
S.in_async = was_async; S.in_async = was_async;
return new AST_Arrow({ return new (async ? AST_AsyncArrow : AST_Arrow)({
start: start, start: start,
argnames: argnames, argnames: argnames,
rest: exprs.rest || null, rest: rest,
body: body, body: body,
value: value, value: value,
end: prev(), end: prev(),
@@ -1431,16 +1435,9 @@ function parse($TEXT, options) {
} }
unexpected(); unexpected();
} }
var ctor; if (is("keyword", "function")) {
if (is("name", "async") && is_token(peek(), "keyword", "function")) {
next(); next();
ctor = AST_AsyncFunction; var func = function_(AST_Function);
} else if (is("keyword", "function")) {
ctor = AST_Function;
}
if (ctor) {
next();
var func = function_(ctor);
func.start = start; func.start = start;
func.end = prev(); func.end = prev();
return subscripts(func, allow_calls); return subscripts(func, allow_calls);
@@ -1448,6 +1445,30 @@ function parse($TEXT, options) {
if (is("name")) { if (is("name")) {
var sym = _make_symbol(AST_SymbolRef, start); var sym = _make_symbol(AST_SymbolRef, start);
next(); next();
if (sym.name == "async") {
if (is("keyword", "function")) {
next();
var func = function_(AST_AsyncFunction);
func.start = start;
func.end = prev();
return subscripts(func, allow_calls);
}
if (is("name")) {
start = S.token;
sym = _make_symbol(AST_SymbolRef, start);
next();
return arrow([ sym ], start, true);
}
if (is("punc", "(")) {
var call = subscripts(sym, allow_calls);
if (!is("punc", "=>")) return call;
var args = call.args;
if (args[args.length - 1] instanceof AST_Spread) {
args.rest = args.pop().expression;
}
return arrow(args, start, true);
}
}
return is("punc", "=>") ? arrow([ sym ], start) : subscripts(sym, allow_calls); return is("punc", "=>") ? arrow([ sym ], start) : subscripts(sym, allow_calls);
} }
if (ATOMIC_START_TOKEN[S.token.type]) { if (ATOMIC_START_TOKEN[S.token.type]) {

View File

@@ -223,11 +223,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
}); });
if (node.rest) node.rest.walk(tw); if (node.rest) node.rest.walk(tw);
in_arg.pop(); in_arg.pop();
if (node instanceof AST_Arrow && node.value) { walk_lambda(node, tw);
node.value.walk(tw);
} else {
walk_body(node, tw);
}
return true; return true;
} }
if (node instanceof AST_LoopControl) { if (node instanceof AST_LoopControl) {
@@ -328,7 +324,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
function is_arguments(sym) { function is_arguments(sym) {
return sym.orig[0] instanceof AST_SymbolFunarg return sym.orig[0] instanceof AST_SymbolFunarg
&& !(sym.orig[1] instanceof AST_SymbolFunarg || sym.orig[2] instanceof AST_SymbolFunarg) && !(sym.orig[1] instanceof AST_SymbolFunarg || sym.orig[2] instanceof AST_SymbolFunarg)
&& !(sym.scope instanceof AST_Arrow); && !is_arrow(sym.scope);
} }
function redefine(node, scope) { function redefine(node, scope) {
@@ -395,6 +391,9 @@ AST_Scope.DEFMETHOD("init_vars", function(parent_scope) {
AST_Arrow.DEFMETHOD("init_vars", function(parent_scope) { AST_Arrow.DEFMETHOD("init_vars", function(parent_scope) {
init_scope_vars(this, parent_scope); init_scope_vars(this, parent_scope);
}); });
AST_AsyncArrow.DEFMETHOD("init_vars", function(parent_scope) {
init_scope_vars(this, parent_scope);
});
AST_Lambda.DEFMETHOD("init_vars", function(parent_scope) { AST_Lambda.DEFMETHOD("init_vars", function(parent_scope) {
init_scope_vars(this, parent_scope); init_scope_vars(this, parent_scope);
this.uses_arguments = false; this.uses_arguments = false;

View File

@@ -136,7 +136,7 @@ TreeTransformer.prototype = new TreeWalker;
if (self.rest) self.rest = self.rest.transform(tw); if (self.rest) self.rest = self.rest.transform(tw);
self.body = do_list(self.body, tw); self.body = do_list(self.body, tw);
}); });
DEF(AST_Arrow, function(self, tw) { function transform_arrow(self, tw) {
self.argnames = do_list(self.argnames, tw); self.argnames = do_list(self.argnames, tw);
if (self.rest) self.rest = self.rest.transform(tw); if (self.rest) self.rest = self.rest.transform(tw);
if (self.value) { if (self.value) {
@@ -144,7 +144,9 @@ TreeTransformer.prototype = new TreeWalker;
} else { } else {
self.body = do_list(self.body, tw); self.body = do_list(self.body, tw);
} }
}); }
DEF(AST_Arrow, transform_arrow);
DEF(AST_AsyncArrow, transform_arrow);
DEF(AST_Call, function(self, tw) { DEF(AST_Call, function(self, tw) {
self.expression = self.expression.transform(tw); self.expression = self.expression.transform(tw);
self.args = do_list(self.args, tw); self.args = do_list(self.args, tw);

View File

@@ -241,7 +241,7 @@ function HOP(obj, prop) {
function first_in_statement(stack, arrow) { function first_in_statement(stack, arrow) {
var node = stack.parent(-1); var node = stack.parent(-1);
for (var i = 0, p; p = stack.parent(i++); node = p) { for (var i = 0, p; p = stack.parent(i++); node = p) {
if (p instanceof AST_Arrow) { if (is_arrow(p)) {
return arrow && p.value === node; return arrow && p.value === node;
} else if (p instanceof AST_Binary) { } else if (p instanceof AST_Binary) {
if (p.left === node) continue; if (p.left === node) continue;

View File

@@ -1,3 +1,27 @@
async_arrow: {
input: {
(async a => console.log(a))("PASS");
console.log(typeof (async () => 42)());
}
expect_exact: '(async a=>console.log(a))("PASS");console.log(typeof(async()=>42)());'
expect_stdout: [
"PASS",
"object",
]
node_version: ">=8"
}
async_label: {
input: {
(async function() {
async: console.log("PASS");
})();
}
expect_exact: '(async function(){async:console.log("PASS")})();'
expect_stdout: "PASS"
node_version: ">=8"
}
await_await: { await_await: {
input: { input: {
(async function() { (async function() {

View File

@@ -667,7 +667,10 @@ function is_timed_out(result) {
function is_statement(node) { function is_statement(node) {
return node instanceof U.AST_Statement return node instanceof U.AST_Statement
&& !(node instanceof U.AST_Arrow || node instanceof U.AST_AsyncFunction || node instanceof U.AST_Function); && !(node instanceof U.AST_Arrow
|| node instanceof U.AST_AsyncArrow
|| node instanceof U.AST_AsyncFunction
|| node instanceof U.AST_Function);
} }
function merge_sequence(array, node) { function merge_sequence(array, node) {

View File

@@ -1053,7 +1053,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
var s = []; var s = [];
switch (rng(5)) { switch (rng(5)) {
case 0: case 0:
if (SUPPORT.arrow && !async && !name && rng(2)) { if (SUPPORT.arrow && !name && rng(2)) {
var args, suffix; var args, suffix;
(rng(2) ? createBlockVariables : function() { (rng(2) ? createBlockVariables : function() {
arguments[3](); arguments[3]();
@@ -1067,16 +1067,17 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
} else { } else {
params = createParams(save_async, NO_DUPLICATE); params = createParams(save_async, NO_DUPLICATE);
} }
params = (async ? "async (" : "(") + params + ") => ";
if (defns) { if (defns) {
s.push( s.push(
"((" + params + ") => {", "(" + params + "{",
strictMode(), strictMode(),
defns(), defns(),
_createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth) _createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth)
); );
suffix = "})"; suffix = "})";
} else { } else {
s.push("((" + params + ") => "); s.push("(" + params);
switch (rng(10)) { switch (rng(10)) {
case 0: case 0:
s.push('(typeof arguments != "undefined" && arguments && arguments[' + rng(3) + "])"); s.push('(typeof arguments != "undefined" && arguments && arguments[' + rng(3) + "])");