Source/JavaScriptCore/ChangeLog

 12016-11-20 Caitlin Potter <caitp@igalia.com>
 2
 3 [JSC] speed up parsing of async functions
 4 https://bugs.webkit.org/show_bug.cgi?id=164808
 5
 6 Reviewed by Yusuke Suzuki.
 7
 8 Minor adjustments to Parser in order to mitigate slowdown with async
 9 function parsing enabled:
 10
 11 - Tokenize "async" as a keyword
 12 - Perform less branching in various areas of the Parser
 13
 14 * parser/Keywords.table:
 15 * parser/Parser.cpp:
 16 (JSC::Parser<LexerType>::parseStatementListItem):
 17 (JSC::Parser<LexerType>::parseStatement):
 18 (JSC::Parser<LexerType>::maybeParseAsyncFunctionDeclarationStatement):
 19 (JSC::Parser<LexerType>::parseClass):
 20 (JSC::Parser<LexerType>::parseExportDeclaration):
 21 (JSC::Parser<LexerType>::parseAssignmentExpression):
 22 (JSC::Parser<LexerType>::parseProperty):
 23 (JSC::Parser<LexerType>::createResolveAndUseVariable):
 24 (JSC::Parser<LexerType>::parsePrimaryExpression):
 25 (JSC::Parser<LexerType>::parseMemberExpression):
 26 (JSC::Parser<LexerType>::printUnexpectedTokenText):
 27 * parser/Parser.h:
 28 (JSC::isAnyContextualKeyword):
 29 (JSC::isIdentifierOrAnyContextualKeyword):
 30 (JSC::isSafeContextualKeyword):
 31 (JSC::Parser::matchSpecIdentifier):
 32 * parser/ParserTokens.h:
 33 * runtime/CommonIdentifiers.h:
 34
1352016-11-19 Mark Lam <mark.lam@apple.com>
236
337 Add --timeoutMultiplier option to allow some tests more time to run.

Source/JavaScriptCore/parser/Keywords.table

@@true TRUETOKEN
77false FALSETOKEN
88
99# Keywords.
 10async ASYNC
1011await AWAIT
1112break BREAK
1213case CASE

Source/JavaScriptCore/parser/Parser.cpp

@@template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatementList
616616 if (!strictMode()) {
617617 SavePoint savePoint = createSavePoint();
618618 next();
619  // Intentionally use `match(IDENT) || match(LET) || match(YIELD)` and don't use `matchSpecIdentifier()`.
 619 // Intentionally use `isIdentifierOrAnyContextualKeyword(m_token)` and don't use `matchSpecIdentifier()`.
620620 // We would like to fall into parseVariableDeclaration path even if "yield" is not treated as an Identifier.
621621 // For example, under a generator context, matchSpecIdentifier() for "yield" returns `false`.
622622 // But we would like to enter parseVariableDeclaration and raise an error under the context of parseVariableDeclaration
623623 // to raise consistent errors between "var", "const" and "let".
624  if (!(match(IDENT) || match(LET) || match(YIELD) || match(AWAIT)) && !match(OPENBRACE) && !match(OPENBRACKET))
 624 if (!isIdentifierOrAnyContextualKeyword(m_token) && !match(OPENBRACE) && !match(OPENBRACKET))
625625 shouldParseVariableDeclaration = false;
626626 restoreSavePoint(savePoint);
627627 }

@@template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatementList
640640 case FUNCTION:
641641 result = parseFunctionDeclaration(context);
642642 break;
643  case IDENT:
644  if (UNLIKELY(*m_token.m_data.ident == m_vm->propertyNames->async)) {
645  // Eagerly parse as AsyncFunctionDeclaration. This is the uncommon case,
646  // but could be mistakenly parsed as an AsyncFunctionExpression.
647  SavePoint savePoint = createSavePoint();
648  next();
649  if (UNLIKELY(match(FUNCTION) && !m_lexer->prevTerminator())) {
650  result = parseAsyncFunctionDeclaration(context);
651  break;
652  }
653  restoreSavePoint(savePoint);
 643 case ASYNC: {
 644 // Eagerly parse as AsyncFunctionDeclaration. This is the uncommon case,
 645 // but could be mistakenly parsed as an AsyncFunctionExpression.
 646 SavePoint savePoint = createSavePoint();
 647 next();
 648 if (UNLIKELY(match(FUNCTION) && !m_lexer->prevTerminator())) {
 649 result = parseAsyncFunctionDeclaration(context);
 650 break;
654651 }
 652 restoreSavePoint(savePoint);
655653 FALLTHROUGH;
 654 }
 655 case IDENT:
656656 case AWAIT:
657657 case YIELD: {
658658 // This is a convenient place to notice labeled statements

@@template <class TreeBuilder> TreeStatement Parser<LexerType>::parseStatement(Tre
17841784 case DEFAULT:
17851785 // These tokens imply the end of a set of source elements
17861786 return 0;
1787  case IDENT:
1788  if (UNLIKELY(*m_token.m_data.ident == m_vm->propertyNames->async && maybeParseAsyncFunctionDeclarationStatement(context, result, parentAllowsFunctionDeclarationAsStatement)))
 1787 case ASYNC:
 1788 if (maybeParseAsyncFunctionDeclarationStatement(context, result, parentAllowsFunctionDeclarationAsStatement))
17891789 break;
17901790 FALLTHROUGH;
 1791 case IDENT:
17911792 case AWAIT:
17921793 case YIELD: {
17931794 bool allowFunctionDeclarationAsStatement = false;

@@template <class TreeBuilder> TreeStatement Parser<LexerType>::parseFunctionDecla
18701871template <typename LexerType>
18711872template <class TreeBuilder> bool Parser<LexerType>::maybeParseAsyncFunctionDeclarationStatement(TreeBuilder& context, TreeStatement& result, bool parentAllowsFunctionDeclarationAsStatement)
18721873{
1873  ASSERT(*m_token.m_data.ident == m_vm->propertyNames->async);
 1874 ASSERT(match(ASYNC));
18741875 SavePoint savePoint = createSavePoint();
18751876 next();
18761877 if (match(FUNCTION) && !m_lexer->prevTerminator()) {

@@template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(T
26652666 bool isGetter = false;
26662667 bool isSetter = false;
26672668 bool isGenerator = false;
 2669 bool isAsync = false;
26682670 bool isAsyncMethod = false;
26692671 if (consume(TIMES))
26702672 isGenerator = true;

@@parseMethod:
26772679 ASSERT(ident);
26782680 next();
26792681 break;
 2682 case ASYNC:
 2683 isAsync = !isGenerator && !isAsyncMethod;
 2684 FALLTHROUGH;
26802685 case IDENT:
26812686 case AWAIT:
26822687 ident = m_token.m_data.ident;

@@parseMethod:
26862691 isGetter = *ident == propertyNames.get;
26872692 isSetter = *ident == propertyNames.set;
26882693
2689  if (UNLIKELY(*ident == propertyNames.async && !m_lexer->prevTerminator() && !isAsyncMethod)) {
 2694 if (UNLIKELY(isAsync && !m_lexer->prevTerminator())) {
26902695 isAsyncMethod = true;
26912696 goto parseMethod;
26922697 }

@@template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExportDeclara
32073212 if (match(IDENT))
32083213 localName = m_token.m_data.ident;
32093214 restoreSavePoint(savePoint);
3210  } else if (UNLIKELY(isIdentifierOrKeyword(m_token) && *m_token.m_data.ident == m_vm->propertyNames->async)) {
 3215 } else if (match(ASYNC)) {
32113216 SavePoint savePoint = createSavePoint();
32123217 next();
32133218 if (match(FUNCTION) && !m_lexer->prevTerminator()) {

@@template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExportDeclara
32313236 } else if (match(CLASSTOKEN)) {
32323237 result = parseClassDeclaration(context, ExportType::NotExported, DeclarationDefaultContext::ExportDefault);
32333238 } else {
3234  ASSERT(match(IDENT) && *m_token.m_data.ident == m_vm->propertyNames->async);
 3239 ASSERT(match(ASYNC));
32353240 next();
32363241 DepthManager statementDepth(&m_statementDepth);
32373242 m_statementDepth = 1;

@@template <class TreeBuilder> TreeStatement Parser<LexerType>::parseExportDeclara
33523357 result = parseClassDeclaration(context, ExportType::Exported);
33533358 break;
33543359
 3360 case ASYNC:
 3361 next();
 3362 semanticFailIfFalse(match(FUNCTION) && !m_lexer->prevTerminator(), "Expected 'function' keyword following 'async' keyword with no preceding line terminator");
 3363 result = parseAsyncFunctionDeclaration(context, ExportType::Exported);
 3364 break;
 3365
33553366 default:
3356  if (UNLIKELY(isIdentifierOrKeyword(m_token) && *m_token.m_data.ident == m_vm->propertyNames->async)) {
3357  next();
3358  semanticFailIfFalse(match(FUNCTION) && !m_lexer->prevTerminator(), "Expected 'function' keyword following 'async' keyword with no preceding line terminator");
3359  result = parseAsyncFunctionDeclaration(context, ExportType::Exported);
3360  break;
3361  }
33623367 failWithMessage("Expected either a declaration or a variable statement");
33633368 break;
33643369 }

@@template <typename TreeBuilder> TreeExpression Parser<LexerType>::parseAssignmen
34413446 SavePoint savePoint = createSavePoint();
34423447 size_t usedVariablesSize = 0;
34433448
3444  if (wasOpenParen || (wasIdentifierOrKeyword && *m_token.m_data.ident == m_vm->propertyNames->async)) {
 3449 if (wasOpenParen) {
34453450 usedVariablesSize = currentScope()->currentUsedVariablesSize();
34463451 currentScope()->pushUsedVariableSet();
34473452 }

@@template <typename TreeBuilder> TreeExpression Parser<LexerType>::parseAssignmen
34553460 restoreSavePoint(savePoint);
34563461 bool isAsyncArrow = false;
34573462 if (UNLIKELY(classifier.indicatesPossibleAsyncArrowFunction())) {
3458  ASSERT(matchContextualKeyword(m_vm->propertyNames->async));
 3463 ASSERT(match(ASYNC));
34593464 next();
34603465 isAsyncArrow = !m_lexer->prevTerminator();
34613466 }
34623467 if (isArrowFunctionParameters()) {
3463  if (wasOpenParen || isAsyncArrow)
 3468 if (wasOpenParen)
34643469 currentScope()->revertToPreviousUsedVariables(usedVariablesSize);
34653470 return parseArrowFunctionExpression(context, isAsyncArrow);
34663471 }

@@template <typename LexerType>
37133718template <class TreeBuilder> TreeProperty Parser<LexerType>::parseProperty(TreeBuilder& context, bool complete)
37143719{
37153720 bool wasIdent = false;
 3721 bool isAsync = false;
37163722 bool isGenerator = false;
37173723 bool isClassProperty = false;
37183724 bool isAsyncMethod = false;
37193725 if (consume(TIMES))
37203726 isGenerator = true;
37213727
3722 UNUSED_LABEL(parseProperty);
37233728parseProperty:
37243729 switch (m_token.m_type) {
3725  namedProperty:
 3730 case ASYNC:
 3731 isAsync = !isGenerator && !isAsyncMethod;
 3732 FALLTHROUGH;
37263733 case IDENT:
37273734 case AWAIT:
37283735 wasIdent = true;
37293736 FALLTHROUGH;
37303737 case STRING: {
 3738namedProperty:
37313739 const Identifier* ident = m_token.m_data.ident;
37323740 unsigned getterOrSetterStartOffset = tokenStart();
3733  if (complete || (wasIdent && !isGenerator && (*ident == m_vm->propertyNames->get || *ident == m_vm->propertyNames->set)))
3734  nextExpectIdentifier(LexerFlagsIgnoreReservedWords);
3735  else if (wasIdent && !isGenerator && *ident == m_vm->propertyNames->async)
 3741
 3742 if (complete || (wasIdent && !isGenerator && (*ident == m_vm->propertyNames->get || *ident == m_vm->propertyNames->set)) || isAsync)
37363743 nextExpectIdentifier(LexerFlagsIgnoreReservedWords);
37373744 else
37383745 nextExpectIdentifier(LexerFlagsIgnoreReservedWords | TreeBuilder::DontBuildKeywords);

@@parseProperty:
37723779 type = PropertyNode::Getter;
37733780 else if (*ident == m_vm->propertyNames->set)
37743781 type = PropertyNode::Setter;
3775  else if (UNLIKELY(*ident == m_vm->propertyNames->async && !isGenerator && !isAsyncMethod)) {
 3782 else if (UNLIKELY(isAsync && !isAsyncMethod)) {
37763783 isAsyncMethod = true;
37773784 failIfTrue(m_lexer->prevTerminator(), "Expected a property name following keyword 'async'");
37783785 goto parseProperty;

@@parseProperty:
38203827 }
38213828 default:
38223829 failIfFalse(m_token.m_type & KeywordTokenFlag, "Expected a property name");
 3830 wasIdent = true; // Treat keyword token as an identifier
38233831 goto namedProperty;
38243832 }
38253833}

@@template <class TreeBuilder> typename TreeBuilder::TemplateLiteral Parser<LexerT
41984206 return context.createTemplateLiteral(location, templateStringList, templateExpressionList);
41994207}
42004208
 4209template <class LexerType>
 4210template <class TreeBuilder> TreeExpression Parser<LexerType>::createResolveAndUseVariable(TreeBuilder& context, const Identifier* ident, bool isEval, const JSTextPosition& start, const JSTokenLocation& location)
 4211{
 4212 currentScope()->useVariable(ident, isEval);
 4213 m_parserState.lastIdentifier = ident;
 4214 return context.createResolve(location, *ident, start, lastTokenEndPosition());
 4215}
 4216
42014217template <typename LexerType>
42024218template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpression(TreeBuilder& context)
42034219{

@@template <class TreeBuilder> TreeExpression Parser<LexerType>::parsePrimaryExpre
42314247 case AWAIT:
42324248 if (m_parserState.functionParsePhase == FunctionParsePhase::Parameters)
42334249 failIfFalse(m_parserState.allowAwait, "Cannot use await expression within parameters");
4234  FALLTHROUGH;
 4250 goto identifierExpression;
 4251 case ASYNC: {
 4252 JSTextPosition start = tokenStartPosition();
 4253 const Identifier* ident = m_token.m_data.ident;
 4254 JSTokenLocation location(tokenLocation());
 4255 next();
 4256 if (match(FUNCTION) && !m_lexer->prevTerminator())
 4257 return parseAsyncFunctionExpression(context);
 4258
 4259 // Avoid using variable if it is an arrow function parameter
 4260 if (UNLIKELY(match(ARROWFUNCTION)))
 4261 return 0;
 4262
 4263 const bool isEval = false;
 4264 return createResolveAndUseVariable(context, ident, isEval, start, location);
 4265 }
42354266 case IDENT: {
42364267 identifierExpression:
42374268 JSTextPosition start = tokenStartPosition();
42384269 const Identifier* ident = m_token.m_data.ident;
42394270 JSTokenLocation location(tokenLocation());
42404271 next();
4241  if (match(ARROWFUNCTION))
4242  return 0;
42434272
4244  if (UNLIKELY(*ident == m_vm->propertyNames->async && match(FUNCTION) && !m_lexer->prevTerminator()))
4245  return parseAsyncFunctionExpression(context);
 4273 // Avoid using variable if it is an arrow function parameter
 4274 if (UNLIKELY(match(ARROWFUNCTION)))
 4275 return 0;
42464276
4247  currentScope()->useVariable(ident, m_vm->propertyNames->eval == *ident);
4248  m_parserState.lastIdentifier = ident;
4249  return context.createResolve(location, *ident, start, lastTokenEndPosition());
 4277 return createResolveAndUseVariable(context, ident, *ident == m_vm->propertyNames->eval, start, location);
42504278 }
42514279 case STRING: {
42524280 const Identifier* ident = m_token.m_data.ident;

@@template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres
44404468 }
44414469 }
44424470 } else if (!baseIsNewTarget) {
4443  const bool isAsync = isIdentifierOrKeyword(m_token) && *m_token.m_data.ident == m_vm->propertyNames->async;
 4471 const bool isAsync = match(ASYNC);
 4472
44444473 base = parsePrimaryExpression(context);
44454474 failIfFalse(base, "Cannot parse base expression");
4446  if (isAsync && context.isResolve(base) && !m_lexer->prevTerminator()) {
 4475 if (UNLIKELY(isAsync && context.isResolve(base) && !m_lexer->prevTerminator())) {
44474476 if (matchSpecIdentifier()) {
44484477 // AsyncArrowFunction
44494478 forceClassifyExpressionError(ErrorIndicatesAsyncArrowFunction);

@@template <class TreeBuilder> TreeExpression Parser<LexerType>::parseMemberExpres
44844513 failIfFalse(arguments, "Cannot parse call arguments");
44854514 base = context.createNewExpr(location, base, arguments, expressionStart, expressionEnd, lastTokenEndPosition());
44864515 } else {
 4516 size_t usedVariablesSize = currentScope()->currentUsedVariablesSize();
44874517 JSTextPosition expressionEnd = lastTokenEndPosition();
44884518 TreeArguments arguments = parseArguments(context);
44894519
44904520 if (baseIsAsyncKeyword && (!arguments || match(ARROWFUNCTION))) {
 4521 currentScope()->revertToPreviousUsedVariables(usedVariablesSize);
44914522 forceClassifyExpressionError(ErrorIndicatesAsyncArrowFunction);
44924523 failDueToUnexpectedToken();
44934524 }

@@template <typename LexerType> void Parser<LexerType>::printUnexpectedTokenText(W
47874818 out.print("Invalid private name '", getToken(), "'");
47884819 return;
47894820
 4821 case ASYNC:
47904822 case AWAIT:
47914823 case IDENT:
47924824 out.print("Unexpected identifier '", getToken(), "'");

Source/JavaScriptCore/parser/Parser.h

@@ALWAYS_INLINE static bool isIdentifierOrKeyword(const JSToken& token)
128128{
129129 return token.m_type == IDENT || token.m_type & KeywordTokenFlag;
130130}
 131// _Any_ContextualKeyword includes keywords such as "let" or "yield", which have a specific meaning depending on the current parse mode
 132// or strict mode. These helpers allow to treat all contextual keywords as identifiers as required.
 133ALWAYS_INLINE static bool isAnyContextualKeyword(const JSToken& token)
 134{
 135 return token.m_type >= FirstContextualKeywordToken && token.m_type <= LastContextualKeywordToken;
 136}
 137ALWAYS_INLINE static bool isIdentifierOrAnyContextualKeyword(const JSToken& token)
 138{
 139 return token.m_type == IDENT || isAnyContextualKeyword(token);
 140}
 141// _Safe_ContextualKeyword includes only contextual keywords which can be treated as identifiers independently from parse mode. The exeption
 142// to this rule is `await`, but matchSpecIdentifier() always treats it as an identifier regardless.
 143ALWAYS_INLINE static bool isSafeContextualKeyword(const JSToken& token)
 144{
 145 return token.m_type >= FirstSafeContextualKeywordToken && token.m_type <= LastSafeContextualKeywordToken;
 146}
131147
132148struct Scope {
133149 WTF_MAKE_NONCOPYABLE(Scope);

@@private:
14591475 // http://ecma-international.org/ecma-262/6.0/#sec-generator-function-definitions-static-semantics-early-errors
14601476 ALWAYS_INLINE bool matchSpecIdentifier(bool inGenerator)
14611477 {
1462  return match(IDENT) || match(AWAIT) || isLETMaskedAsIDENT() || isYIELDMaskedAsIDENT(inGenerator);
 1478 return match(IDENT) || isLETMaskedAsIDENT() || isYIELDMaskedAsIDENT(inGenerator) || isSafeContextualKeyword(m_token);
14631479 }
14641480
14651481 ALWAYS_INLINE bool matchSpecIdentifier()
14661482 {
1467  return match(IDENT) || match(AWAIT) || isLETMaskedAsIDENT() || isYIELDMaskedAsIDENT(currentScope()->isGenerator());
 1483 return match(IDENT) || isLETMaskedAsIDENT() || isYIELDMaskedAsIDENT(currentScope()->isGenerator()) || isSafeContextualKeyword(m_token);
14681484 }
14691485
14701486 template <class TreeBuilder> TreeSourceElements parseSourceElements(TreeBuilder&, SourceElementsMode);

@@private:
15391555 template <class TreeBuilder> typename TreeBuilder::ExportSpecifier parseExportSpecifier(TreeBuilder& context, Vector<std::pair<const Identifier*, const Identifier*>>& maybeExportedLocalNames, bool& hasKeywordForLocalBindings);
15401556 template <class TreeBuilder> TreeStatement parseExportDeclaration(TreeBuilder&);
15411557
 1558 template <class TreeBuilder> ALWAYS_INLINE TreeExpression createResolveAndUseVariable(TreeBuilder&, const Identifier*, bool isEval, const JSTextPosition&, const JSTokenLocation&);
 1559
15421560 enum class FunctionDefinitionType { Expression, Declaration, Method };
15431561 template <class TreeBuilder> NEVER_INLINE bool parseFunctionInfo(TreeBuilder&, FunctionNameRequirements, SourceParseMode, bool nameIsInContainingScope, ConstructorKind, SuperBinding, int functionKeywordStart, ParserFunctionInfo<TreeBuilder>&, FunctionDefinitionType);
15441562

Source/JavaScriptCore/parser/ParserTokens.h

@@enum JSTokenType {
5757 FOR,
5858 NEW,
5959 VAR,
60  LET,
6160 CONSTTOKEN,
6261 CONTINUE,
6362 FUNCTION,

@@enum JSTokenType {
7877 ELSE,
7978 IMPORT,
8079 EXPORT,
81  YIELD,
8280 CLASSTOKEN,
8381 EXTENDS,
8482 SUPER,
 83
 84 // Contextual keywords
 85 LET,
 86 YIELD,
8587 AWAIT,
 88 ASYNC,
 89
 90 FirstContextualKeywordToken = LET,
 91 LastContextualKeywordToken = ASYNC,
 92 FirstSafeContextualKeywordToken = AWAIT,
 93 LastSafeContextualKeywordToken = LastContextualKeywordToken,
 94
8695 OPENBRACE = 0,
8796 CLOSEBRACE,
8897 OPENPAREN,

Source/JavaScriptCore/runtime/CommonIdentifiers.h

112112 macro(arguments) \
113113 macro(as) \
114114 macro(assign) \
115  macro(async) \
116115 macro(back) \
117116 macro(bind) \
118117 macro(blur) \

276275 macro(year)
277276
278277#define JSC_COMMON_IDENTIFIERS_EACH_KEYWORD(macro) \
 278 macro(async) \
279279 macro(await) \
280280 macro(break) \
281281 macro(case) \

JSTests/ChangeLog

 12016-11-20 Caitlin Potter <caitp@igalia.com>
 2
 3 [JSC] speed up parsing of async functions
 4 https://bugs.webkit.org/show_bug.cgi?id=164808
 5
 6 Reviewed by Yusuke Suzuki.
 7
 8 Add tests for line terminator following "async" keyword in async
 9 function syntax.
 10
 11 * stress/async-await-syntax.js:
 12 (shouldBe):
 13
1142016-11-19 Mark Lam <mark.lam@apple.com>
215
316 op_mod-* JSC tests needs a longer timeout too.

JSTests/stress/async-await-syntax.js

11// Copyright (C) 2016 the V8 project authors. All rights reserved.
22// This code is governed by the BSD license found in the LICENSE file.
33
 4function shouldBe(expected, actual, msg = "") {
 5 if (msg)
 6 msg = " for " + msg;
 7 if (actual !== expected)
 8 throw new Error("bad value" + msg + ": " + actual + ". Expected " + expected);
 9}
 10
411function testSyntax(script) {
512 try {
613 eval(script);

@@async function fn(b) {
388395 const b = 1;
389396}
390397`, `SyntaxError: Cannot declare a const variable twice: 'b'.`);
 398
 399(function testMethodDefinition() {
 400 testSyntax("({ async [foo]() {} })");
 401 testSyntax("({ async [Symbol.asyncIterator]() {} })");
 402 testSyntax("({ async 0() {} })");
 403 testSyntax("({ async 'string'() {} })");
 404 testSyntax("({ async ident() {} })");
 405
 406 testSyntax("(class { async [foo]() {} })");
 407 testSyntax("(class { async [Symbol.asyncIterator]() {} })");
 408 testSyntax("(class { async 0() {} })");
 409 testSyntax("(class { async 'string'() {} })");
 410 testSyntax("(class { async ident() {} })");
 411
 412 testSyntax("(class { static async [foo]() {} })");
 413 testSyntax("(class { static async [Symbol.asyncIterator]() {} })");
 414 testSyntax("(class { static async 0() {} })");
 415 testSyntax("(class { static async 'string'() {} })");
 416 testSyntax("(class { static async ident() {} })");
 417})();
 418
 419(function testLineTerminator() {
 420 let testLineFeedErrors = (prefix, suffix) => {
 421 testSyntaxError(`${prefix}// comment
 422 ${suffix}`);
 423 testSyntaxError(`${prefix}/* comment
 424 */ ${suffix}`);
 425 testSyntaxError(`${prefix}
 426 ${suffix}`);
 427 testSyntaxError(`"use strict";${prefix}// comment
 428 ${suffix}`);
 429 testSyntaxError(`"use strict";${prefix}/* comment
 430 */ ${suffix}`);
 431 testSyntaxError(`"use strict";${prefix}
 432 ${suffix}`);
 433 };
 434
 435 let testLineFeeds = (prefix, suffix) => {
 436 testSyntax(`${prefix}// comment
 437 ${suffix}`);
 438 testSyntax(`${prefix}/* comment
 439 */${suffix}`);
 440 testSyntax(`${prefix}
 441 ${suffix}`);
 442 testSyntax(`"use strict";${prefix}// comment
 443 ${suffix}`);
 444 testSyntax(`"use strict";${prefix}/* comment
 445 */${suffix}`);
 446 testSyntax(`"use strict";${prefix}
 447 ${suffix}`);
 448 };
 449
 450 let tests = [
 451 // ObjectLiteral AsyncMethodDefinition
 452 { prefix: "({ async", suffix: "method() {} }).method" },
 453
 454 // ClassLiteral AsyncMethodDefinition
 455 { prefix: "(class { async", suffix: "method() {} }).prototype.method" },
 456
 457 // AsyncArrowFunctions
 458 { prefix: "(async", suffix: "param => 1)" },
 459 { prefix: "(async", suffix: "(param) => 1)" },
 460 { prefix: "(async", suffix: "param => {})" },
 461 { prefix: "(async", suffix: "(param) => {})" },
 462 ];
 463
 464 for (let { prefix, suffix } of tests) {
 465 testSyntax(`${prefix} ${suffix}`);
 466 testSyntax(`"use strict";${prefix} ${suffix}`);
 467 shouldBe("function", typeof eval(`${prefix} ${suffix}`));
 468 shouldBe("function", typeof eval(`"use strict";${prefix} ${suffix}`));
 469 testLineFeedErrors(prefix, suffix);
 470 }
 471
 472 // AsyncFunctionDeclaration
 473 testSyntax("async function foo() {}");
 474 testLineFeeds("async", "function foo() {}");
 475
 476 // AsyncFunctionExpression
 477 testSyntax("var x = async function foo() {}");
 478 testSyntax("'use strict';var x = async function foo() {}");
 479 testLineFeeds("var x = async", "function foo() {}");
 480 testLineFeedErrors("var x = async", "function() {}");
 481})();