--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2404,23 +2404,38 @@ Parser<ParseHandler>::functionBody(InHan
// the invalid parameter name at the correct source location.
pc->newDirectives->setStrict();
return null();
}
}
} else {
MOZ_ASSERT(type == ExpressionBody);
+ // Async functions are implemented as star generators, and star
+ // generators are assumed to be statement lists, to prepend initial
+ // `yield`.
+ Node stmtList = null();
+ if (pc->isAsync()) {
+ stmtList = handler.newStatementList(pos());
+ if (!stmtList)
+ return null();
+ }
+
Node kid = assignExpr(inHandling, yieldHandling, TripledotProhibited);
if (!kid)
return null();
pn = handler.newReturnStatement(kid, handler.getPosition(kid));
if (!pn)
return null();
+
+ if (pc->isAsync()) {
+ handler.addStatementToList(stmtList, pn);
+ pn = stmtList;
+ }
}
switch (pc->generatorKind()) {
case NotGenerator:
MOZ_ASSERT(pc->lastYieldOffset == startYieldOffset);
break;
case LegacyGenerator:
@@ -2431,23 +2446,23 @@ Parser<ParseHandler>::functionBody(InHan
MOZ_ASSERT(!IsGetterKind(kind));
MOZ_ASSERT(!IsSetterKind(kind));
MOZ_ASSERT(!IsConstructorKind(kind));
MOZ_ASSERT(kind != Method);
MOZ_ASSERT(type != ExpressionBody);
break;
case StarGenerator:
- MOZ_ASSERT(kind != Arrow);
- MOZ_ASSERT(type == StatementListBody);
+ MOZ_ASSERT_IF(!pc->isAsync(), kind != Arrow);
+ MOZ_ASSERT_IF(!pc->isAsync(), type == StatementListBody);
break;
}
if (pc->isGenerator()) {
- MOZ_ASSERT(type == StatementListBody);
+ MOZ_ASSERT_IF(!pc->isAsync(), type == StatementListBody);
if (!declareDotGeneratorName())
return null();
Node generator = newDotGeneratorName();
if (!generator)
return null();
if (!handler.prependInitialYield(pn, generator))
return null();
}
@@ -2629,29 +2644,57 @@ Parser<ParseHandler>::prefixAccessorName
template <typename ParseHandler>
bool
Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind,
Node funcpn)
{
FunctionBox* funbox = pc->functionBox();
bool parenFreeArrow = false;
- TokenStream::Modifier modifier = TokenStream::None;
+ // Modifier for the following tokens.
+ // TokenStream::None for the following cases:
+ // async a => 1
+ // ^
+ //
+ // (a) => 1
+ // ^
+ //
+ // async (a) => 1
+ // ^
+ //
+ // function f(a) {}
+ // ^
+ //
+ // TokenStream::Operand for the following case:
+ // a => 1
+ // ^
+ TokenStream::Modifier firstTokenModifier = TokenStream::None;
+
+ // Modifier for the the first token in each argument.
+ // can be changed to TokenStream::None for the following case:
+ // async a => 1
+ // ^
+ TokenStream::Modifier argModifier = TokenStream::Operand;
if (kind == Arrow) {
TokenKind tt;
- if (!tokenStream.peekToken(&tt, TokenStream::Operand))
+ // In async function, the first token after `async` is already gotten
+ // with TokenStream::None.
+ // In sync function, the first token is already gotten with
+ // TokenStream::Operand.
+ firstTokenModifier = funbox->isAsync() ? TokenStream::None : TokenStream::Operand;
+ if (!tokenStream.peekToken(&tt, firstTokenModifier))
return false;
- if (tt == TOK_NAME || tt == TOK_YIELD)
+ if (tt == TOK_NAME || tt == TOK_YIELD) {
parenFreeArrow = true;
- else
- modifier = TokenStream::Operand;
+ argModifier = firstTokenModifier;
+ }
}
if (!parenFreeArrow) {
TokenKind tt;
- if (!tokenStream.getToken(&tt, modifier))
+ if (!tokenStream.getToken(&tt, firstTokenModifier))
return false;
if (tt != TOK_LP) {
report(ParseError, false, null(),
kind == Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL);
return false;
}
// Record the start of function source (for FunctionToString). If we
@@ -2688,18 +2731,19 @@ Parser<ParseHandler>::functionArguments(
while (true) {
if (hasRest) {
report(ParseError, false, null(), JSMSG_PARAMETER_AFTER_REST);
return false;
}
TokenKind tt;
- if (!tokenStream.getToken(&tt, TokenStream::Operand))
+ if (!tokenStream.getToken(&tt, argModifier))
return false;
+ argModifier = TokenStream::Operand;
MOZ_ASSERT_IF(parenFreeArrow, tt == TOK_NAME || tt == TOK_YIELD);
if (tt == TOK_TRIPLEDOT) {
if (IsSetterKind(kind)) {
report(ParseError, false, null(),
JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", "");
return false;
}
@@ -2747,16 +2791,25 @@ Parser<ParseHandler>::functionArguments(
break;
}
case TOK_NAME:
case TOK_YIELD: {
if (parenFreeArrow)
funbox->setStart(tokenStream);
+ if (funbox->isAsync() && tokenStream.currentName() == context->names().await) {
+ // `await` is already gotten as TOK_NAME for the following
+ // case:
+ //
+ // async await => 1
+ report(ParseError, false, null(), JSMSG_RESERVED_ID, "await");
+ return false;
+ }
+
RootedPropertyName name(context, bindingIdentifier(yieldHandling));
if (!name)
return false;
if (!notePositionalFormalParameter(funcpn, name, disallowDuplicateParams,
&duplicatedParam))
{
return false;
@@ -3375,17 +3428,17 @@ Parser<ParseHandler>::functionFormalPara
}
// Parse the function body.
FunctionBodyType bodyType = StatementListBody;
TokenKind tt;
if (!tokenStream.getToken(&tt, TokenStream::Operand))
return false;
if (tt != TOK_LC) {
- if (funbox->isStarGenerator() || kind == Method ||
+ if ((funbox->isStarGenerator() && !funbox->isAsync()) || kind == Method ||
kind == GetterNoExpressionClosure || kind == SetterNoExpressionClosure ||
IsConstructorKind(kind)) {
report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY);
return false;
}
if (kind != Arrow) {
#if JS_HAS_EXPR_CLOSURES
@@ -7509,27 +7562,63 @@ Parser<ParseHandler>::assignExpr(InHandl
return null();
if (endsExpr)
return stringLiteral();
}
if (tt == TOK_YIELD && yieldExpressionsSupported())
return yieldExpression(inHandling);
+ bool maybeAsyncArrow = false;
+ if (tt == TOK_NAME && tokenStream.currentName() == context->names().async) {
+ TokenKind nextSameLine = TOK_EOF;
+ if (!tokenStream.peekTokenSameLine(&nextSameLine))
+ return null();
+
+ if (nextSameLine == TOK_NAME || nextSameLine == TOK_YIELD)
+ maybeAsyncArrow = true;
+ }
+
tokenStream.ungetToken();
// Save the tokenizer state in case we find an arrow function and have to
// rewind.
TokenStream::Position start(keepAtoms);
tokenStream.tell(&start);
PossibleError possibleErrorInner(*this);
- Node lhs = condExpr1(inHandling, yieldHandling, tripledotHandling, &possibleErrorInner, invoked);
- if (!lhs)
- return null();
+ Node lhs;
+ if (maybeAsyncArrow) {
+ tokenStream.consumeKnownToken(TOK_NAME, TokenStream::Operand);
+ MOZ_ASSERT(tokenStream.currentName() == context->names().async);
+
+ TokenKind tt;
+ if (!tokenStream.getToken(&tt))
+ return null();
+ MOZ_ASSERT(tt == TOK_NAME || tt == TOK_YIELD);
+
+ // Check yield validity here.
+ RootedPropertyName name(context, bindingIdentifier(yieldHandling));
+ if (!name)
+ return null();
+
+ if (!tokenStream.getToken(&tt))
+ return null();
+ if (tt != TOK_ARROW) {
+ report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
+ "'=>' after argument list", TokenKindToDesc(tt));
+
+ return null();
+ }
+ } else {
+ lhs = condExpr1(inHandling, yieldHandling, tripledotHandling, &possibleErrorInner, invoked);
+ if (!lhs) {
+ return null();
+ }
+ }
ParseNodeKind kind;
JSOp op;
switch (tokenStream.currentToken().type) {
case TOK_ASSIGN: kind = PNK_ASSIGN; op = JSOP_NOP; break;
case TOK_ADDASSIGN: kind = PNK_ADDASSIGN; op = JSOP_ADD; break;
case TOK_SUBASSIGN: kind = PNK_SUBASSIGN; op = JSOP_SUB; break;
case TOK_BITORASSIGN: kind = PNK_BITORASSIGN; op = JSOP_BITOR; break;
@@ -7561,22 +7650,43 @@ Parser<ParseHandler>::assignExpr(InHandl
bool isBlock = false;
if (!tokenStream.peekToken(&next, TokenStream::Operand))
return null();
if (next == TOK_LC)
isBlock = true;
tokenStream.seek(start);
- TokenKind ignored;
- if (!tokenStream.peekToken(&ignored, TokenStream::Operand))
- return null();
+ if (!tokenStream.peekToken(&next, TokenStream::Operand))
+ return null();
+
+ GeneratorKind generatorKind = NotGenerator;
+ FunctionAsyncKind asyncKind = SyncFunction;
+
+ if (next == TOK_NAME) {
+ tokenStream.consumeKnownToken(next, TokenStream::Operand);
+
+ if (tokenStream.currentName() == context->names().async) {
+ TokenKind nextSameLine = TOK_EOF;
+ if (!tokenStream.peekTokenSameLine(&nextSameLine))
+ return null();
+
+ if (nextSameLine == TOK_ARROW) {
+ tokenStream.ungetToken();
+ } else {
+ generatorKind = StarGenerator;
+ asyncKind = AsyncFunction;
+ }
+ } else {
+ tokenStream.ungetToken();
+ }
+ }
Node arrowFunc = functionDefinition(inHandling, yieldHandling, nullptr,
- Arrow, NotGenerator, SyncFunction);
+ Arrow, generatorKind, asyncKind);
if (!arrowFunc)
return null();
if (isBlock) {
// This arrow function could be a non-trailing member of a comma
// expression or a semicolon terminating a full expression. If so,
// the next token is that comma/semicolon, gotten with None:
//
@@ -7595,16 +7705,17 @@ Parser<ParseHandler>::assignExpr(InHandl
// before Parser::expr() looks for a comma. Do so here, then
// immediately add the modifier exception needed for the first
// case.
//
// Note that the second case occurs *only* if the arrow function
// has block body. An arrow function not ending in such, ends in
// another AssignmentExpression that we can inductively assume was
// peeked consistently.
+ TokenKind ignored;
if (!tokenStream.peekToken(&ignored, TokenStream::Operand))
return null();
tokenStream.addModifierException(TokenStream::NoneIsOperand);
}
return arrowFunc;
}
default: