Ignore:
Timestamp:
Oct 9, 2017, 10:53:12 PM (8 years ago)
Author:
Yusuke Suzuki
Message:

async should be able to be used as an imported binding name
https://bugs.webkit.org/show_bug.cgi?id=176573

Reviewed by Saam Barati.

JSTests:

  • modules/import-default-async.js: Added.
  • modules/import-named-async-as.js: Added.
  • modules/import-named-async.js: Added.
  • modules/import-named-async/target.js: Added.
  • modules/import-namespace-async.js: Added.
  • test262.yaml:

Source/JavaScriptCore:

Previously, we have ASYNC keyword in the parser. This is introduced only for performance,
and ECMA262 spec does not categorize "async" to keyword. This makes parser code complicated,
since ASYNC should be handled as IDENT. If we missed this ASYNC keyword, we cause a bug.
For example, import declaration failed to bind imported binding to the name "async" because
the parser considered ASYNC as keyword.

This patch removes ASYNC keyword from the parser. By carefully handling ASYNC, we can keep
the current performance without using this ASYNC keyword.

We also add escaped field to token data since contextual keyword is valid only if it does
not contain any escape sequences. We fix bunch of contextual keyword use with this fix too
e.g. of in for-of. This improves test262 score.

  • parser/Keywords.table:
  • parser/Lexer.cpp:

(JSC::Lexer<LChar>::parseIdentifier):
(JSC::Lexer<UChar>::parseIdentifier):
(JSC::Lexer<CharacterType>::parseIdentifierSlowCase):

  • parser/Parser.cpp:

(JSC::Parser<LexerType>::parseStatementListItem):
(JSC::Parser<LexerType>::parseForStatement):
(JSC::Parser<LexerType>::parseStatement):
(JSC::Parser<LexerType>::maybeParseAsyncFunctionDeclarationStatement):
(JSC::Parser<LexerType>::parseClass):
(JSC::Parser<LexerType>::parseExportDeclaration):
(JSC::Parser<LexerType>::parseAssignmentExpression):
(JSC::Parser<LexerType>::parseProperty):
(JSC::Parser<LexerType>::parsePrimaryExpression):
(JSC::Parser<LexerType>::parseMemberExpression):
(JSC::Parser<LexerType>::printUnexpectedTokenText):

  • parser/Parser.h:

(JSC::Parser::matchContextualKeyword):

  • parser/ParserTokens.h:
  • runtime/CommonIdentifiers.h:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/parser/Parser.cpp

    r223062 r223124  
    658658        result = parseFunctionDeclaration(context);
    659659        break;
    660     case ASYNC: {
    661         // Eagerly parse as AsyncFunctionDeclaration. This is the uncommon case,
    662         // but could be mistakenly parsed as an AsyncFunctionExpression.
    663         SavePoint savePoint = createSavePoint();
    664         next();
    665         if (UNLIKELY(match(FUNCTION) && !m_lexer->prevTerminator())) {
    666             result = parseAsyncFunctionDeclaration(context);
    667             break;
    668         }
    669         restoreSavePoint(savePoint);
     660    case IDENT:
     661        if (UNLIKELY(*m_token.m_data.ident == m_vm->propertyNames->async && !m_token.m_data.escaped)) {
     662            // Eagerly parse as AsyncFunctionDeclaration. This is the uncommon case,
     663            // but could be mistakenly parsed as an AsyncFunctionExpression.
     664            SavePoint savePoint = createSavePoint();
     665            next();
     666            if (UNLIKELY(match(FUNCTION) && !m_lexer->prevTerminator())) {
     667                result = parseAsyncFunctionDeclaration(context);
     668                break;
     669            }
     670            restoreSavePoint(savePoint);
     671        }
    670672        FALLTHROUGH;
    671     }
    672     case IDENT:
    673673    case AWAIT:
    674674    case YIELD: {
     
    13501350        bool isOfEnumeration = false;
    13511351        if (!match(INTOKEN)) {
    1352             failIfFalse(match(IDENT) && *m_token.m_data.ident == m_vm->propertyNames->of, "Expected either 'in' or 'of' in enumeration syntax");
     1352            failIfFalse(matchContextualKeyword(m_vm->propertyNames->of), "Expected either 'in' or 'of' in enumeration syntax");
    13531353            isOfEnumeration = true;
    13541354            next();
     
    14001400            pattern = tryParseDestructuringPatternExpression(context, AssignmentContext::DeclarationStatement);
    14011401            declsEnd = lastTokenEndPosition();
    1402             if (pattern && (match(INTOKEN) || (match(IDENT) && *m_token.m_data.ident == m_vm->propertyNames->of)))
     1402            if (pattern && (match(INTOKEN) || matchContextualKeyword(m_vm->propertyNames->of)))
    14031403                goto enumerationLoop;
    14041404            pattern = TreeDestructuringPattern(0);
     
    14551455    bool isOfEnumeration = false;
    14561456    if (!match(INTOKEN)) {
    1457         failIfFalse(match(IDENT) && *m_token.m_data.ident == m_vm->propertyNames->of, "Expected either 'in' or 'of' in enumeration syntax");
     1457        failIfFalse(matchContextualKeyword(m_vm->propertyNames->of), "Expected either 'in' or 'of' in enumeration syntax");
    14581458        isOfEnumeration = true;
    14591459        next();
     
    19001900        goto defaultCase;
    19011901    }
    1902     case ASYNC:
    1903         if (maybeParseAsyncFunctionDeclarationStatement(context, result, parentAllowsFunctionDeclarationAsStatement))
    1904             break;
     1902    case IDENT:
     1903        if (UNLIKELY(*m_token.m_data.ident == m_vm->propertyNames->async && !m_token.m_data.escaped)) {
     1904            if (maybeParseAsyncFunctionDeclarationStatement(context, result, parentAllowsFunctionDeclarationAsStatement))
     1905                break;
     1906        }
    19051907        FALLTHROUGH;
    1906     case IDENT:
    19071908    case AWAIT:
    19081909    case YIELD: {
     
    19891990template <class TreeBuilder> bool Parser<LexerType>::maybeParseAsyncFunctionDeclarationStatement(TreeBuilder& context, TreeStatement& result, bool parentAllowsFunctionDeclarationAsStatement)
    19901991{
    1991     ASSERT(match(ASYNC));
     1992    ASSERT(matchContextualKeyword(m_vm->propertyNames->async));
    19921993    SavePoint savePoint = createSavePoint();
    19931994    next();
     
    28362837            next();
    28372838            break;
    2838         case ASYNC:
    2839             if (!isGeneratorMethodParseMode(parseMode) && !isAsyncMethodParseMode(parseMode)) {
    2840                 ident = m_token.m_data.ident;
    2841                 next();
    2842                 if (match(OPENPAREN) || match(COLON) || match(EQUAL) || m_lexer->prevTerminator())
    2843                     break;
    2844                 if (Options::useAsyncIterator() && UNLIKELY(consume(TIMES)))
    2845                     parseMode = SourceParseMode::AsyncGeneratorWrapperMethodMode;
    2846                 else
    2847                     parseMode = SourceParseMode::AsyncMethodMode;
    2848                 goto parseMethod;
     2839        case IDENT:
     2840            if (UNLIKELY(*m_token.m_data.ident == m_vm->propertyNames->async && !m_token.m_data.escaped)) {
     2841                if (!isGeneratorMethodParseMode(parseMode) && !isAsyncMethodParseMode(parseMode)) {
     2842                    ident = m_token.m_data.ident;
     2843                    next();
     2844                    if (match(OPENPAREN) || match(COLON) || match(EQUAL) || m_lexer->prevTerminator())
     2845                        break;
     2846                    if (Options::useAsyncIterator() && UNLIKELY(consume(TIMES)))
     2847                        parseMode = SourceParseMode::AsyncGeneratorWrapperMethodMode;
     2848                    else
     2849                        parseMode = SourceParseMode::AsyncMethodMode;
     2850                    goto parseMethod;
     2851                }
    28492852            }
    28502853            FALLTHROUGH;
    2851         case IDENT:
    28522854        case AWAIT:
    28532855            ident = m_token.m_data.ident;
     
    33703372                localName = m_token.m_data.ident;
    33713373            restoreSavePoint(savePoint);
    3372         } else if (match(ASYNC)) {
     3374        } else if (matchContextualKeyword(m_vm->propertyNames->async)) {
    33733375            SavePoint savePoint = createSavePoint();
    33743376            next();
     
    33943396                result = parseClassDeclaration(context, ExportType::NotExported, DeclarationDefaultContext::ExportDefault);
    33953397            } else {
    3396                 ASSERT(match(ASYNC));
     3398                ASSERT(matchContextualKeyword(m_vm->propertyNames->async));
    33973399                next();
    33983400                DepthManager statementDepth(&m_statementDepth);
     
    35153517            break;
    35163518
    3517         case ASYNC: {
    3518             next();
    3519             semanticFailIfFalse(match(FUNCTION) && !m_lexer->prevTerminator(), "Expected 'function' keyword following 'async' keyword with no preceding line terminator");
    3520             DepthManager statementDepth(&m_statementDepth);
    3521             m_statementDepth = 1;
    3522             result = parseAsyncFunctionDeclaration(context, ExportType::Exported);
    3523             break;
    3524         }
    3525 
     3519        case IDENT:
     3520            if (*m_token.m_data.ident == m_vm->propertyNames->async && !m_token.m_data.escaped) {
     3521                next();
     3522                semanticFailIfFalse(match(FUNCTION) && !m_lexer->prevTerminator(), "Expected 'function' keyword following 'async' keyword with no preceding line terminator");
     3523                DepthManager statementDepth(&m_statementDepth);
     3524                m_statementDepth = 1;
     3525                result = parseAsyncFunctionDeclaration(context, ExportType::Exported);
     3526                break;
     3527            }
     3528            FALLTHROUGH;
    35263529        default:
    35273530            failWithMessage("Expected either a declaration or a variable statement");
     
    36213624            bool isAsyncArrow = false;
    36223625            if (UNLIKELY(classifier.indicatesPossibleAsyncArrowFunction())) {
    3623                 ASSERT(match(ASYNC));
     3626                ASSERT(matchContextualKeyword(m_vm->propertyNames->async));
    36243627                next();
    36253628                isAsyncArrow = !m_lexer->prevTerminator();
     
    38893892parseProperty:
    38903893    switch (m_token.m_type) {
    3891     case ASYNC:
    3892         if (parseMode == SourceParseMode::MethodMode) {
    3893             SavePoint savePoint = createSavePoint();
    3894             next();
    3895 
    3896             if (match(COLON) || match(OPENPAREN) || match(COMMA) || match(CLOSEBRACE)) {
    3897                 restoreSavePoint(savePoint);
    3898                 wasIdent = true;
    3899                 goto namedProperty;
     3894    case IDENT:
     3895        if (UNLIKELY(*m_token.m_data.ident == m_vm->propertyNames->async && !m_token.m_data.escaped)) {
     3896            if (parseMode == SourceParseMode::MethodMode) {
     3897                SavePoint savePoint = createSavePoint();
     3898                next();
     3899
     3900                if (match(COLON) || match(OPENPAREN) || match(COMMA) || match(CLOSEBRACE)) {
     3901                    restoreSavePoint(savePoint);
     3902                    wasIdent = true;
     3903                    goto namedProperty;
     3904                }
     3905
     3906                failIfTrue(m_lexer->prevTerminator(), "Expected a property name following keyword 'async'");
     3907                if (Options::useAsyncIterator() && UNLIKELY(consume(TIMES)))
     3908                    parseMode = SourceParseMode::AsyncGeneratorWrapperMethodMode;
     3909                else
     3910                    parseMode = SourceParseMode::AsyncMethodMode;
     3911                goto parseProperty;
    39003912            }
    3901 
    3902             failIfTrue(m_lexer->prevTerminator(), "Expected a property name following keyword 'async'");
    3903             if (Options::useAsyncIterator() && UNLIKELY(consume(TIMES)))
    3904                 parseMode = SourceParseMode::AsyncGeneratorWrapperMethodMode;
    3905             else
    3906                 parseMode = SourceParseMode::AsyncMethodMode;
    3907             goto parseProperty;
    39083913        }
    39093914        FALLTHROUGH;
    3910     case IDENT:
    39113915    case YIELD:
    39123916    case AWAIT:
     
    44494453
    44504454        goto identifierExpression;
    4451     case ASYNC: {
    4452         JSTextPosition start = tokenStartPosition();
    4453         const Identifier* ident = m_token.m_data.ident;
    4454         JSTokenLocation location(tokenLocation());
    4455         next();
    4456         if (match(FUNCTION) && !m_lexer->prevTerminator())
    4457             return parseAsyncFunctionExpression(context);
    4458 
    4459         // Avoid using variable if it is an arrow function parameter
    4460         if (UNLIKELY(match(ARROWFUNCTION)))
    4461             return 0;
    4462 
    4463         const bool isEval = false;
    4464         return createResolveAndUseVariable(context, ident, isEval, start, location);
    4465     }
    44664455    case IDENT: {
     4456        if (UNLIKELY(*m_token.m_data.ident == m_vm->propertyNames->async && !m_token.m_data.escaped)) {
     4457            JSTextPosition start = tokenStartPosition();
     4458            const Identifier* ident = m_token.m_data.ident;
     4459            JSTokenLocation location(tokenLocation());
     4460            next();
     4461            if (match(FUNCTION) && !m_lexer->prevTerminator())
     4462                return parseAsyncFunctionExpression(context);
     4463
     4464            // Avoid using variable if it is an arrow function parameter
     4465            if (UNLIKELY(match(ARROWFUNCTION)))
     4466                return 0;
     4467
     4468            const bool isEval = false;
     4469            return createResolveAndUseVariable(context, ident, isEval, start, location);
     4470        }
    44674471    identifierExpression:
    44684472        JSTextPosition start = tokenStartPosition();
     
    46504654    if (newCount && match(DOT)) {
    46514655        next();
    4652         if (match(IDENT)) {
    4653             const Identifier* ident = m_token.m_data.ident;
    4654             if (m_vm->propertyNames->target == *ident) {
    4655                 ScopeRef closestOrdinaryFunctionScope = closestParentOrdinaryFunctionNonLexicalScope();
    4656                 semanticFailIfFalse(currentScope()->isFunction() || closestOrdinaryFunctionScope->evalContextType() == EvalContextType::FunctionEvalContext, "new.target is only valid inside functions");
    4657                 baseIsNewTarget = true;
    4658                 if (currentScope()->isArrowFunction()) {
    4659                     semanticFailIfFalse(!closestOrdinaryFunctionScope->isGlobalCodeScope() || closestOrdinaryFunctionScope->evalContextType() == EvalContextType::FunctionEvalContext, "new.target is not valid inside arrow functions in global code");
    4660                     currentScope()->setInnerArrowFunctionUsesNewTarget();
    4661                 }
    4662                 base = context.createNewTargetExpr(location);
    4663                 newCount--;
    4664                 next();
    4665             } else
    4666                 failWithMessage("\"new.\" can only followed with target");
    4667         } else
     4656        if (matchContextualKeyword(m_vm->propertyNames->target)) {
     4657            ScopeRef closestOrdinaryFunctionScope = closestParentOrdinaryFunctionNonLexicalScope();
     4658            semanticFailIfFalse(currentScope()->isFunction() || closestOrdinaryFunctionScope->evalContextType() == EvalContextType::FunctionEvalContext, "new.target is only valid inside functions");
     4659            baseIsNewTarget = true;
     4660            if (currentScope()->isArrowFunction()) {
     4661                semanticFailIfFalse(!closestOrdinaryFunctionScope->isGlobalCodeScope() || closestOrdinaryFunctionScope->evalContextType() == EvalContextType::FunctionEvalContext, "new.target is not valid inside arrow functions in global code");
     4662                currentScope()->setInnerArrowFunctionUsesNewTarget();
     4663            }
     4664            base = context.createNewTargetExpr(location);
     4665            newCount--;
     4666            next();
     4667        } else {
     4668            failIfTrue(match(IDENT), "\"new.\" can only followed with target");
    46684669            failDueToUnexpectedToken();
     4670        }
    46694671    }
    46704672
     
    46924694        JSTextPosition expressionEnd = lastTokenEndPosition();
    46934695        if (consume(DOT)) {
    4694             if (!match(IDENT))
     4696            if (matchContextualKeyword(m_vm->propertyNames->builtinNames().metaPublicName())) {
     4697                semanticFailIfFalse(m_scriptMode == JSParserScriptMode::Module, "import.meta is only valid inside modules");
     4698
     4699                JSTokenLocation location(tokenLocation());
     4700                base = createResolveAndUseVariable(context, &m_vm->propertyNames->builtinNames().metaPrivateName(), false, expressionStart, location);
     4701                next();
     4702            } else {
     4703                failIfTrue(match(IDENT), "\"import.\" can only followed with meta");
    46954704                failDueToUnexpectedToken();
    4696             const Identifier* ident = m_token.m_data.ident;
    4697             if (m_vm->propertyNames->builtinNames().metaPublicName() != *ident)
    4698                 failWithMessage("\"import.\" can only followed with meta");
    4699 
    4700             semanticFailIfFalse(m_scriptMode == JSParserScriptMode::Module, "import.meta is only valid inside modules");
    4701 
    4702             JSTokenLocation location(tokenLocation());
    4703             base = createResolveAndUseVariable(context, &m_vm->propertyNames->builtinNames().metaPrivateName(), false, expressionStart, location);
    4704             next();
     4705            }
    47054706        } else {
    47064707            consumeOrFail(OPENPAREN, "import call expects exactly one argument");
     
    47114712        }
    47124713    } else if (!baseIsNewTarget) {
    4713         const bool isAsync = match(ASYNC);
     4714        const bool isAsync = matchContextualKeyword(m_vm->propertyNames->async);
    47144715
    47154716        base = parsePrimaryExpression(context);
     
    50685069        return;
    50695070           
    5070     case ASYNC:
    50715071    case AWAIT:
    50725072    case IDENT:
Note: See TracChangeset for help on using the changeset viewer.