/* * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) * Copyright (C) 2003-2019 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "config.h" #include "Parser.h" #include "ASTBuilder.h" #include "BuiltinNames.h" #include "DebuggerParseData.h" #include "JSCJSValueInlines.h" #include "VM.h" #include #include #include #include #define updateErrorMessage(shouldPrintToken, ...) do {\ propagateError(); \ logError(shouldPrintToken, __VA_ARGS__); \ } while (0) #define propagateError() do { if (UNLIKELY(hasError())) return 0; } while (0) #define internalFailWithMessage(shouldPrintToken, ...) do { updateErrorMessage(shouldPrintToken, __VA_ARGS__); return 0; } while (0) #define handleErrorToken() do { if (m_token.m_type == EOFTOK || m_token.m_type & CanBeErrorTokenFlag) { failDueToUnexpectedToken(); } } while (0) #define failWithMessage(...) do { { handleErrorToken(); updateErrorMessage(true, __VA_ARGS__); } return 0; } while (0) #define failWithStackOverflow() do { updateErrorMessage(false, "Stack exhausted"); m_hasStackOverflow = true; return 0; } while (0) #define failIfFalse(cond, ...) do { if (!(cond)) { handleErrorToken(); internalFailWithMessage(true, __VA_ARGS__); } } while (0) #define failIfTrue(cond, ...) do { if (cond) { handleErrorToken(); internalFailWithMessage(true, __VA_ARGS__); } } while (0) #define failIfTrueIfStrict(cond, ...) do { if ((cond) && strictMode()) internalFailWithMessage(false, __VA_ARGS__); } while (0) #define failIfFalseIfStrict(cond, ...) do { if ((!(cond)) && strictMode()) internalFailWithMessage(false, __VA_ARGS__); } while (0) #define consumeOrFail(tokenType, ...) do { if (!consume(tokenType)) { handleErrorToken(); internalFailWithMessage(true, __VA_ARGS__); } } while (0) #define consumeOrFailWithFlags(tokenType, flags, ...) do { if (!consume(tokenType, flags)) { handleErrorToken(); internalFailWithMessage(true, __VA_ARGS__); } } while (0) #define matchOrFail(tokenType, ...) do { if (!match(tokenType)) { handleErrorToken(); internalFailWithMessage(true, __VA_ARGS__); } } while (0) #define failIfStackOverflow() do { if (UNLIKELY(!canRecurse())) failWithStackOverflow(); } while (0) #define semanticFail(...) do { internalFailWithMessage(false, __VA_ARGS__); } while (0) #define semanticFailIfTrue(cond, ...) do { if (UNLIKELY(cond)) internalFailWithMessage(false, __VA_ARGS__); } while (0) #define semanticFailIfFalse(cond, ...) do { if (UNLIKELY(!(cond))) internalFailWithMessage(false, __VA_ARGS__); } while (0) #define regexFail(failure) do { setErrorMessage(failure); return 0; } while (0) #define failDueToUnexpectedToken() do {\ logError(true);\ return 0;\ } while (0) #define handleProductionOrFail(token, tokenString, operation, production) do {\ consumeOrFail(token, "Expected '", tokenString, "' to ", operation, " a ", production);\ } while (0) #define handleProductionOrFail2(token, tokenString, operation, production) do {\ consumeOrFail(token, "Expected '", tokenString, "' to ", operation, " an ", production);\ } while (0) #define semanticFailureDueToKeywordCheckingToken(token, ...) do { \ if (strictMode() && token.m_type == RESERVED_IF_STRICT) \ semanticFail("Cannot use the reserved word '", getToken(token), "' as a ", __VA_ARGS__, " in strict mode"); \ if (token.m_type == RESERVED || token.m_type == RESERVED_IF_STRICT) \ semanticFail("Cannot use the reserved word '", getToken(token), "' as a ", __VA_ARGS__); \ if (token.m_type & KeywordTokenFlag) { \ if (!isAnyContextualKeyword(token)) \ semanticFail("Cannot use the keyword '", getToken(token), "' as a ", __VA_ARGS__); \ if (isDisallowedIdentifierLet(token)) \ semanticFail("Cannot use 'let' as a ", __VA_ARGS__, " ", disallowedIdentifierLetReason()); \ if (isDisallowedIdentifierAwait(token)) \ semanticFail("Cannot use 'await' as a ", __VA_ARGS__, " ", disallowedIdentifierAwaitReason()); \ if (isDisallowedIdentifierYield(token)) \ semanticFail("Cannot use 'yield' as a ", __VA_ARGS__, " ", disallowedIdentifierYieldReason()); \ } \ } while (0) #define semanticFailureDueToKeyword(...) semanticFailureDueToKeywordCheckingToken(m_token, __VA_ARGS__); namespace JSC { std::atomic globalParseCount { 0 }; ALWAYS_INLINE static SourceParseMode getAsynFunctionBodyParseMode(SourceParseMode parseMode) { if (isAsyncGeneratorWrapperParseMode(parseMode)) return SourceParseMode::AsyncGeneratorBodyMode; if (parseMode == SourceParseMode::AsyncArrowFunctionMode) return SourceParseMode::AsyncArrowFunctionBodyMode; return SourceParseMode::AsyncFunctionBodyMode; } template void Parser::logError(bool) { if (hasError()) return; StringPrintStream stream; printUnexpectedTokenText(stream); setErrorMessage(stream.toStringWithLatin1Fallback()); } template template void Parser::logError(bool shouldPrintToken, Args&&... args) { if (hasError()) return; StringPrintStream stream; if (shouldPrintToken) { printUnexpectedTokenText(stream); stream.print(". "); } stream.print(std::forward(args)..., "."); setErrorMessage(stream.toStringWithLatin1Fallback()); } template Parser::Parser(VM& vm, const SourceCode& source, JSParserBuiltinMode builtinMode, JSParserStrictMode strictMode, JSParserScriptMode scriptMode, SourceParseMode parseMode, SuperBinding superBinding, ConstructorKind defaultConstructorKindForTopLevelFunction, DerivedContextType derivedContextType, bool isEvalContext, EvalContextType evalContextType, DebuggerParseData* debuggerParseData, bool isInsideOrdinaryFunction) : m_vm(vm) , m_source(&source) , m_hasStackOverflow(false) , m_allowsIn(true) , m_statementDepth(0) , m_parsingBuiltin(builtinMode == JSParserBuiltinMode::Builtin) , m_scriptMode(scriptMode) , m_superBinding(superBinding) , m_defaultConstructorKindForTopLevelFunction(defaultConstructorKindForTopLevelFunction) , m_immediateParentAllowsFunctionDeclarationInStatement(false) , m_debuggerParseData(debuggerParseData) , m_isInsideOrdinaryFunction(isInsideOrdinaryFunction) { m_lexer = makeUnique(vm, builtinMode, scriptMode); m_lexer->setCode(source, &m_parserArena); m_token.m_location.line = source.firstLine().oneBasedInt(); m_token.m_location.startOffset = source.startOffset(); m_token.m_location.endOffset = source.startOffset(); m_token.m_location.lineStartOffset = source.startOffset(); m_functionCache = vm.addSourceProviderCache(source.provider()); m_expressionErrorClassifier = nullptr; ScopeRef scope = pushScope(); scope->setSourceParseMode(parseMode); scope->setIsEvalContext(isEvalContext); if (isEvalContext) scope->setEvalContextType(evalContextType); if (derivedContextType == DerivedContextType::DerivedConstructorContext) { scope->setConstructorKind(ConstructorKind::Extends); scope->setExpectedSuperBinding(SuperBinding::Needed); } if (derivedContextType == DerivedContextType::DerivedMethodContext) scope->setExpectedSuperBinding(SuperBinding::Needed); if (strictMode == JSParserStrictMode::Strict) scope->setStrictMode(); if (isModuleParseMode(parseMode)) m_moduleScopeData = ModuleScopeData::create(); if (isProgramOrModuleParseMode(parseMode)) scope->setIsGlobalCodeScope(); next(); } class Scope::MaybeParseAsGeneratorForScope { public: MaybeParseAsGeneratorForScope(ScopeRef& scope, bool shouldParseAsGenerator) : m_scope(scope) , m_oldValue(scope->m_isGenerator) { m_scope->m_isGenerator = shouldParseAsGenerator; } ~MaybeParseAsGeneratorForScope() { m_scope->m_isGenerator = m_oldValue; } private: ScopeRef m_scope; bool m_oldValue; }; struct DepthManager : private SetForScope { public: DepthManager(int* depth) : SetForScope(*depth, *depth) { } }; template Parser::~Parser() { } template Expected::ParseInnerResult, String> Parser::parseInner(const Identifier& calleeName, SourceParseMode parseMode, ParsingContext parsingContext, Optional functionConstructorParametersEndPosition, const Vector* classFieldLocations) { ASTBuilder context(const_cast(m_vm), m_parserArena, const_cast(m_source)); ScopeRef scope = currentScope(); scope->setIsLexicalScope(); SetForScope functionParsePhasePoisoner(m_parserState.functionParsePhase, FunctionParsePhase::Body); FunctionParameters* parameters = nullptr; bool isArrowFunctionBodyExpression = parseMode == SourceParseMode::AsyncArrowFunctionBodyMode && !match(OPENBRACE); if (m_lexer->isReparsingFunction()) { ParserFunctionInfo functionInfo; if (isGeneratorOrAsyncFunctionBodyParseMode(parseMode)) parameters = createGeneratorParameters(context, functionInfo.parameterCount); else if (parseMode == SourceParseMode::ClassFieldInitializerMode) parameters = context.createFormalParameterList(); else parameters = parseFunctionParameters(context, parseMode, functionInfo); if (SourceParseModeSet(SourceParseMode::ArrowFunctionMode, SourceParseMode::AsyncArrowFunctionMode).contains(parseMode) && !hasError()) { // FIXME: // Logically, this should be an assert, since we already successfully parsed the arrow // function when syntax checking. So logically, we should see the arrow token here. // But we're seeing crashes in the wild when making this an assert. Instead, we'll just // handle it as an error in release builds, and an assert on debug builds, with the hopes // of fixing it in the future. // https://bugs.webkit.org/show_bug.cgi?id=221633 if (UNLIKELY(!match(ARROWFUNCTION))) { ASSERT_NOT_REACHED(); return makeUnexpected("Parser error"_s); } next(); isArrowFunctionBodyExpression = !match(OPENBRACE); } } if (!calleeName.isNull()) scope->declareCallee(&calleeName); if (m_lexer->isReparsingFunction()) m_statementDepth--; SourceElements* sourceElements = nullptr; // The only way we can error this early is if we reparse a function and we run out of stack space. if (!hasError()) { if (isAsyncFunctionWrapperParseMode(parseMode)) sourceElements = parseAsyncFunctionSourceElements(context, parseMode, isArrowFunctionBodyExpression, CheckForStrictMode); else if (isArrowFunctionBodyExpression) sourceElements = parseArrowFunctionSingleExpressionBodySourceElements(context); else if (isModuleParseMode(parseMode)) sourceElements = parseModuleSourceElements(context, parseMode); else if (isGeneratorWrapperParseMode(parseMode)) sourceElements = parseGeneratorFunctionSourceElements(context, calleeName, CheckForStrictMode); else if (isAsyncGeneratorWrapperParseMode(parseMode)) sourceElements = parseAsyncGeneratorFunctionSourceElements(context, parseMode, isArrowFunctionBodyExpression, CheckForStrictMode); else if (parsingContext == ParsingContext::FunctionConstructor) sourceElements = parseSingleFunction(context, functionConstructorParametersEndPosition); else if (parseMode == SourceParseMode::ClassFieldInitializerMode) { ASSERT(classFieldLocations && !classFieldLocations->isEmpty()); sourceElements = parseClassFieldInitializerSourceElements(context, *classFieldLocations); } else sourceElements = parseSourceElements(context, CheckForStrictMode); } bool validEnding = consume(EOFTOK); if (!sourceElements || !validEnding) return makeUnexpected(hasError() ? m_errorMessage : "Parser error"_s); IdentifierSet capturedVariables; UniquedStringImplPtrSet sloppyModeHoistedFunctions; scope->getSloppyModeHoistedFunctions(sloppyModeHoistedFunctions); scope->getCapturedVars(capturedVariables); VariableEnvironment& varDeclarations = scope->declaredVariables(); for (auto& entry : capturedVariables) varDeclarations.markVariableAsCaptured(entry); if (isGeneratorWrapperParseMode(parseMode) || isAsyncFunctionOrAsyncGeneratorWrapperParseMode(parseMode)) { if (scope->usedVariablesContains(m_vm.propertyNames->arguments.impl())) context.propagateArgumentsUse(); } CodeFeatures features = context.features(); if (scope->strictMode()) features |= StrictModeFeature; if (scope->shadowsArguments()) features |= ShadowsArgumentsFeature; if (m_seenTaggedTemplate) features |= NoEvalCacheFeature; if (scope->hasNonSimpleParameterList()) features |= NonSimpleParameterListFeature; #if ASSERT_ENABLED if (m_parsingBuiltin && isProgramParseMode(parseMode)) { VariableEnvironment& lexicalVariables = scope->lexicalVariables(); const HashSet& closedVariableCandidates = scope->closedVariableCandidates(); for (UniquedStringImpl* candidate : closedVariableCandidates) { // FIXME: We allow async to leak because it appearing as a closed variable is a side effect of trying to parse async arrow functions. if (!lexicalVariables.contains(candidate) && !varDeclarations.contains(candidate) && !candidate->isSymbol() && candidate != m_vm.propertyNames->async.impl()) { dataLog("Bad global capture in builtin: '", candidate, "'\n"); dataLog(m_source->view()); CRASH(); } } } #endif // ASSERT_ENABLED return ParseInnerResult { parameters, sourceElements, scope->takeFunctionDeclarations(), WTFMove(varDeclarations), WTFMove(sloppyModeHoistedFunctions), features, context.numConstants() }; } template template bool Parser::isArrowFunctionParameters(TreeBuilder& context) { if (match(OPENPAREN)) { SavePoint saveArrowFunctionPoint = createSavePoint(context); next(); bool isArrowFunction = false; if (match(CLOSEPAREN)) { next(); isArrowFunction = match(ARROWFUNCTION); } else { SyntaxChecker syntaxChecker(const_cast(m_vm), m_lexer.get()); // We make fake scope, otherwise parseFormalParameters will add variable to current scope that lead to errors AutoPopScopeRef fakeScope(this, pushScope()); fakeScope->setSourceParseMode(SourceParseMode::ArrowFunctionMode); unsigned parametersCount = 0; bool isArrowFunctionParameterList = true; bool isMethod = false; isArrowFunction = parseFormalParameters(syntaxChecker, syntaxChecker.createFormalParameterList(), isArrowFunctionParameterList, isMethod, parametersCount) && consume(CLOSEPAREN) && match(ARROWFUNCTION); propagateError(); popScope(fakeScope, syntaxChecker.NeedsFreeVariableInfo); } restoreSavePoint(context, saveArrowFunctionPoint); return isArrowFunction; } if (matchSpecIdentifier()) { semanticFailIfTrue(!m_parserState.allowAwait && match(AWAIT), "Cannot use 'await' as a parameter name in an async function"); SavePoint saveArrowFunctionPoint = createSavePoint(context); next(); bool isArrowFunction = match(ARROWFUNCTION); restoreSavePoint(context, saveArrowFunctionPoint); return isArrowFunction; } return false; } template bool Parser::allowAutomaticSemicolon() { return match(CLOSEBRACE) || match(EOFTOK) || m_lexer->hasLineTerminatorBeforeToken(); } template template TreeSourceElements Parser::parseSourceElements(TreeBuilder& context, SourceElementsMode mode) { const unsigned lengthOfUseStrictLiteral = 12; // "use strict".length TreeSourceElements sourceElements = context.createSourceElements(); const Identifier* directive = nullptr; unsigned directiveLiteralLength = 0; auto savePoint = createSavePoint(context); bool shouldCheckForUseStrict = mode == CheckForStrictMode; while (TreeStatement statement = parseStatementListItem(context, directive, &directiveLiteralLength)) { if (shouldCheckForUseStrict) { if (directive) { // "use strict" must be the exact literal without escape sequences or line continuation. if (directiveLiteralLength == lengthOfUseStrictLiteral && m_vm.propertyNames->useStrictIdentifier == *directive) { setStrictMode(); shouldCheckForUseStrict = false; // We saw "use strict", there is no need to keep checking for it. if (!isValidStrictMode()) { if (m_parserState.lastFunctionName) { if (m_vm.propertyNames->arguments == *m_parserState.lastFunctionName) semanticFail("Cannot name a function 'arguments' in strict mode"); if (m_vm.propertyNames->eval == *m_parserState.lastFunctionName) semanticFail("Cannot name a function 'eval' in strict mode"); } if (hasDeclaredVariable(m_vm.propertyNames->arguments)) semanticFail("Cannot declare a variable named 'arguments' in strict mode"); if (hasDeclaredVariable(m_vm.propertyNames->eval)) semanticFail("Cannot declare a variable named 'eval' in strict mode"); semanticFailIfTrue(currentScope()->hasNonSimpleParameterList(), "'use strict' directive not allowed inside a function with a non-simple parameter list"); semanticFailIfFalse(isValidStrictMode(), "Invalid parameters or function name in strict mode"); } // Since strict mode is changed, restoring lexer state by calling next() may cause errors. restoreSavePoint(context, savePoint); propagateError(); continue; } // We saw a directive, but it wasn't "use strict". We reset our state to // see if the next statement we parse is also a directive. directive = nullptr; } else { // We saw a statement that wasn't in the form of a directive. The spec says that "use strict" // is only allowed as the first statement, or after a sequence of directives before it, but // not after non-directive statements. shouldCheckForUseStrict = false; } } context.appendStatement(sourceElements, statement); } propagateError(); return sourceElements; } template template TreeSourceElements Parser::parseModuleSourceElements(TreeBuilder& context, SourceParseMode parseMode) { TreeSourceElements sourceElements = context.createSourceElements(); SyntaxChecker syntaxChecker(const_cast(m_vm), m_lexer.get()); while (true) { TreeStatement statement = 0; switch (m_token.m_type) { case EXPORT_: statement = parseExportDeclaration(context); if (statement) recordPauseLocation(context.breakpointLocation(statement)); break; case IMPORT: { SavePoint savePoint = createSavePoint(context); next(); bool isImportDeclaration = !match(OPENPAREN) && !match(DOT); restoreSavePoint(context, savePoint); if (isImportDeclaration) { statement = parseImportDeclaration(context); if (statement) recordPauseLocation(context.breakpointLocation(statement)); break; } // This is `import("...")` call or `import.meta` meta property case. FALLTHROUGH; } default: { const Identifier* directive = nullptr; unsigned directiveLiteralLength = 0; if (parseMode == SourceParseMode::ModuleAnalyzeMode) { if (!parseStatementListItem(syntaxChecker, directive, &directiveLiteralLength)) goto end; continue; } statement = parseStatementListItem(context, directive, &directiveLiteralLength); break; } } if (!statement) goto end; context.appendStatement(sourceElements, statement); } end: propagateError(); for (const auto& pair : m_moduleScopeData->exportedBindings()) { const auto& uid = pair.key; if (currentScope()->hasDeclaredVariable(uid)) { currentScope()->declaredVariables().markVariableAsExported(uid); continue; } if (currentScope()->hasLexicallyDeclaredVariable(uid)) { currentScope()->lexicalVariables().markVariableAsExported(uid); continue; } semanticFail("Exported binding '", uid.get(), "' needs to refer to a top-level declared variable"); } return sourceElements; } template template TreeSourceElements Parser::parseGeneratorFunctionSourceElements(TreeBuilder& context, const Identifier& name, SourceElementsMode mode) { auto sourceElements = context.createSourceElements(); unsigned functionKeywordStart = tokenStart(); JSTokenLocation startLocation(tokenLocation()); JSTextPosition start = tokenStartPosition(); unsigned startColumn = tokenColumn(); int functionNameStart = m_token.m_location.startOffset; int parametersStart = m_token.m_location.startOffset; ParserFunctionInfo info; info.name = &m_vm.propertyNames->nullIdentifier; createGeneratorParameters(context, info.parameterCount); info.startOffset = parametersStart; info.startLine = tokenLine(); { AutoPopScopeRef generatorBodyScope(this, pushScope()); generatorBodyScope->setSourceParseMode(SourceParseMode::GeneratorBodyMode); generatorBodyScope->setConstructorKind(ConstructorKind::None); generatorBodyScope->setExpectedSuperBinding(m_superBinding); SyntaxChecker generatorFunctionContext(const_cast(m_vm), m_lexer.get()); failIfFalse(parseSourceElements(generatorFunctionContext, mode), "Cannot parse the body of a generator"); popScope(generatorBodyScope, TreeBuilder::NeedsFreeVariableInfo); } info.body = context.createFunctionMetadata(startLocation, tokenLocation(), startColumn, tokenColumn(), functionKeywordStart, functionNameStart, parametersStart, strictMode(), ConstructorKind::None, m_superBinding, info.parameterCount, SourceParseMode::GeneratorBodyMode, false); info.endLine = tokenLine(); info.endOffset = m_token.m_data.offset; info.parametersStartColumn = startColumn; auto functionExpr = context.createGeneratorFunctionBody(startLocation, info, name); auto statement = context.createExprStatement(startLocation, functionExpr, start, m_lastTokenEndPosition.line); context.appendStatement(sourceElements, statement); return sourceElements; } template template TreeSourceElements Parser::parseAsyncFunctionSourceElements(TreeBuilder& context, SourceParseMode parseMode, bool isArrowFunctionBodyExpression, SourceElementsMode mode) { ASSERT(isAsyncFunctionOrAsyncGeneratorWrapperParseMode(parseMode)); auto sourceElements = context.createSourceElements(); unsigned functionKeywordStart = tokenStart(); JSTokenLocation startLocation(tokenLocation()); JSTextPosition start = tokenStartPosition(); unsigned startColumn = tokenColumn(); int functionNameStart = m_token.m_location.startOffset; int parametersStart = m_token.m_location.startOffset; ParserFunctionInfo info; info.name = &m_vm.propertyNames->nullIdentifier; createGeneratorParameters(context, info.parameterCount); info.startOffset = parametersStart; info.startLine = tokenLine(); SourceParseMode innerParseMode = getAsynFunctionBodyParseMode(parseMode); { AutoPopScopeRef asyncFunctionBodyScope(this, pushScope()); asyncFunctionBodyScope->setSourceParseMode(innerParseMode); SyntaxChecker syntaxChecker(const_cast(m_vm), m_lexer.get()); if (isArrowFunctionBodyExpression) { if (m_debuggerParseData) failIfFalse(parseArrowFunctionSingleExpressionBodySourceElements(context), "Cannot parse the body of async arrow function"); else failIfFalse(parseArrowFunctionSingleExpressionBodySourceElements(syntaxChecker), "Cannot parse the body of async arrow function"); } else { if (m_debuggerParseData) failIfFalse(parseSourceElements(context, mode), "Cannot parse the body of async function"); else failIfFalse(parseSourceElements(syntaxChecker, mode), "Cannot parse the body of async function"); } popScope(asyncFunctionBodyScope, TreeBuilder::NeedsFreeVariableInfo); } info.body = context.createFunctionMetadata(startLocation, tokenLocation(), startColumn, tokenColumn(), functionKeywordStart, functionNameStart, parametersStart, strictMode(), ConstructorKind::None, m_superBinding, info.parameterCount, innerParseMode, isArrowFunctionBodyExpression); info.endLine = tokenLine(); info.endOffset = isArrowFunctionBodyExpression ? tokenLocation().endOffset : m_token.m_data.offset; info.parametersStartColumn = startColumn; auto functionExpr = context.createAsyncFunctionBody(startLocation, info, innerParseMode); auto statement = context.createExprStatement(startLocation, functionExpr, start, m_lastTokenEndPosition.line); context.appendStatement(sourceElements, statement); return sourceElements; } template template TreeSourceElements Parser::parseAsyncGeneratorFunctionSourceElements(TreeBuilder& context, SourceParseMode parseMode, bool isArrowFunctionBodyExpression, SourceElementsMode mode) { ASSERT_UNUSED(parseMode, isAsyncGeneratorWrapperParseMode(parseMode)); auto sourceElements = context.createSourceElements(); unsigned functionKeywordStart = tokenStart(); JSTokenLocation startLocation(tokenLocation()); JSTextPosition start = tokenStartPosition(); unsigned startColumn = tokenColumn(); int functionNameStart = m_token.m_location.startOffset; int parametersStart = m_token.m_location.startOffset; ParserFunctionInfo info; info.name = &m_vm.propertyNames->nullIdentifier; createGeneratorParameters(context, info.parameterCount); info.startOffset = parametersStart; info.startLine = tokenLine(); SourceParseMode innerParseMode = SourceParseMode::AsyncGeneratorBodyMode; { AutoPopScopeRef asyncFunctionBodyScope(this, pushScope()); asyncFunctionBodyScope->setSourceParseMode(innerParseMode); SyntaxChecker syntaxChecker(const_cast(m_vm), m_lexer.get()); if (isArrowFunctionBodyExpression) { if (m_debuggerParseData) failIfFalse(parseArrowFunctionSingleExpressionBodySourceElements(context), "Cannot parse the body of async arrow function"); else failIfFalse(parseArrowFunctionSingleExpressionBodySourceElements(syntaxChecker), "Cannot parse the body of async arrow function"); } else { if (m_debuggerParseData) failIfFalse(parseSourceElements(context, mode), "Cannot parse the body of async function"); else failIfFalse(parseSourceElements(syntaxChecker, mode), "Cannot parse the body of async function"); } popScope(asyncFunctionBodyScope, TreeBuilder::NeedsFreeVariableInfo); } info.body = context.createFunctionMetadata(startLocation, tokenLocation(), startColumn, tokenColumn(), functionKeywordStart, functionNameStart, parametersStart, strictMode(), ConstructorKind::None, m_superBinding, info.parameterCount, innerParseMode, isArrowFunctionBodyExpression); info.endLine = tokenLine(); info.endOffset = isArrowFunctionBodyExpression ? tokenLocation().endOffset : m_token.m_data.offset; info.parametersStartColumn = startColumn; auto functionExpr = context.createAsyncFunctionBody(startLocation, info, innerParseMode); auto statement = context.createExprStatement(startLocation, functionExpr, start, m_lastTokenEndPosition.line); context.appendStatement(sourceElements, statement); return sourceElements; } template template TreeSourceElements Parser::parseSingleFunction(TreeBuilder& context, Optional functionConstructorParametersEndPosition) { TreeSourceElements sourceElements = context.createSourceElements(); TreeStatement statement = 0; switch (m_token.m_type) { case FUNCTION: statement = parseFunctionDeclaration(context, FunctionDeclarationType::Declaration, ExportType::NotExported, DeclarationDefaultContext::Standard, functionConstructorParametersEndPosition); break; case IDENT: if (*m_token.m_data.ident == m_vm.propertyNames->async && !m_token.m_data.escaped) { next(); failIfFalse(match(FUNCTION) && !m_lexer->hasLineTerminatorBeforeToken(), "Cannot parse the async function"); statement = parseAsyncFunctionDeclaration(context, ExportType::NotExported, DeclarationDefaultContext::Standard, functionConstructorParametersEndPosition); break; } FALLTHROUGH; default: failDueToUnexpectedToken(); break; } if (statement) { context.setEndOffset(statement, m_lastTokenEndPosition.offset); context.appendStatement(sourceElements, statement); } propagateError(); return sourceElements; } template template TreeStatement Parser::parseStatementListItem(TreeBuilder& context, const Identifier*& directive, unsigned* directiveLiteralLength) { // The grammar is documented here: // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-statements DepthManager statementDepth(&m_statementDepth); m_statementDepth++; failIfStackOverflow(); TreeStatement result = 0; bool shouldSetEndOffset = true; bool shouldSetPauseLocation = false; switch (m_token.m_type) { case CONSTTOKEN: result = parseVariableDeclaration(context, DeclarationType::ConstDeclaration); shouldSetPauseLocation = true; break; case LET: { bool shouldParseVariableDeclaration = true; if (!strictMode()) { SavePoint savePoint = createSavePoint(context); next(); // Intentionally use `isIdentifierOrAnyContextualKeyword(m_token)` and don't use `matchSpecIdentifier()`. // We would like to fall into parseVariableDeclaration path even if "yield" is not treated as an Identifier. // For example, under a generator context, matchSpecIdentifier() for "yield" returns `false`. // But we would like to enter parseVariableDeclaration and raise an error under the context of parseVariableDeclaration // to raise consistent errors between "var", "const" and "let". if (!isIdentifierOrAnyContextualKeyword(m_token) && !match(OPENBRACE) && !match(OPENBRACKET)) shouldParseVariableDeclaration = false; restoreSavePoint(context, savePoint); } if (shouldParseVariableDeclaration) result = parseVariableDeclaration(context, DeclarationType::LetDeclaration); else { bool allowFunctionDeclarationAsStatement = true; result = parseExpressionOrLabelStatement(context, allowFunctionDeclarationAsStatement); } shouldSetPauseLocation = !context.shouldSkipPauseLocation(result); break; } case CLASSTOKEN: result = parseClassDeclaration(context); break; case FUNCTION: result = parseFunctionDeclaration(context); break; case IDENT: if (UNLIKELY(*m_token.m_data.ident == m_vm.propertyNames->async && !m_token.m_data.escaped)) { // Eagerly parse as AsyncFunctionDeclaration. This is the uncommon case, // but could be mistakenly parsed as an AsyncFunctionExpression. SavePoint savePoint = createSavePoint(context); next(); if (UNLIKELY(match(FUNCTION) && !m_lexer->hasLineTerminatorBeforeToken())) { result = parseAsyncFunctionDeclaration(context); break; } restoreSavePoint(context, savePoint); } FALLTHROUGH; case AWAIT: case YIELD: { // This is a convenient place to notice labeled statements // (even though we also parse them as normal statements) // because we allow the following type of code in sloppy mode: // ``` function foo() { label: function bar() { } } ``` bool allowFunctionDeclarationAsStatement = true; result = parseExpressionOrLabelStatement(context, allowFunctionDeclarationAsStatement); shouldSetPauseLocation = !context.shouldSkipPauseLocation(result); break; } default: m_statementDepth--; // parseStatement() increments the depth. result = parseStatement(context, directive, directiveLiteralLength); shouldSetEndOffset = false; break; } if (result) { if (shouldSetEndOffset) context.setEndOffset(result, m_lastTokenEndPosition.offset); if (shouldSetPauseLocation) recordPauseLocation(context.breakpointLocation(result)); } return result; } template template TreeStatement Parser::parseVariableDeclaration(TreeBuilder& context, DeclarationType declarationType, ExportType exportType) { ASSERT(match(VAR) || match(LET) || match(CONSTTOKEN)); JSTokenLocation location(tokenLocation()); int start = tokenLine(); int end = 0; int scratch; TreeDestructuringPattern scratch1 = 0; TreeExpression scratch2 = 0; JSTextPosition scratch3; bool scratchBool; TreeExpression variableDecls = parseVariableDeclarationList(context, scratch, scratch1, scratch2, scratch3, scratch3, scratch3, VarDeclarationContext, declarationType, exportType, scratchBool); propagateError(); failIfFalse(autoSemiColon(), "Expected ';' after variable declaration"); return context.createDeclarationStatement(location, variableDecls, start, end); } template template TreeStatement Parser::parseDoWhileStatement(TreeBuilder& context) { ASSERT(match(DO)); int startLine = tokenLine(); next(); const Identifier* unused = nullptr; startLoop(); TreeStatement statement = parseStatement(context, unused); endLoop(); failIfFalse(statement, "Expected a statement following 'do'"); int endLine = tokenLine(); JSTokenLocation location(tokenLocation()); handleProductionOrFail(WHILE, "while", "end", "do-while loop"); handleProductionOrFail(OPENPAREN, "(", "start", "do-while loop condition"); semanticFailIfTrue(match(CLOSEPAREN), "Must provide an expression as a do-while loop condition"); TreeExpression expr = parseExpression(context); failIfFalse(expr, "Unable to parse do-while loop condition"); recordPauseLocation(context.breakpointLocation(expr)); handleProductionOrFail(CLOSEPAREN, ")", "end", "do-while loop condition"); if (match(SEMICOLON)) next(); // Always performs automatic semicolon insertion. return context.createDoWhileStatement(location, statement, expr, startLine, endLine); } template template TreeStatement Parser::parseWhileStatement(TreeBuilder& context) { ASSERT(match(WHILE)); JSTokenLocation location(tokenLocation()); int startLine = tokenLine(); next(); handleProductionOrFail(OPENPAREN, "(", "start", "while loop condition"); semanticFailIfTrue(match(CLOSEPAREN), "Must provide an expression as a while loop condition"); TreeExpression expr = parseExpression(context); failIfFalse(expr, "Unable to parse while loop condition"); recordPauseLocation(context.breakpointLocation(expr)); int endLine = tokenLine(); handleProductionOrFail(CLOSEPAREN, ")", "end", "while loop condition"); const Identifier* unused = nullptr; startLoop(); TreeStatement statement = parseStatement(context, unused); endLoop(); failIfFalse(statement, "Expected a statement as the body of a while loop"); return context.createWhileStatement(location, expr, statement, startLine, endLine); } template template TreeExpression Parser::parseVariableDeclarationList(TreeBuilder& context, int& declarations, TreeDestructuringPattern& lastPattern, TreeExpression& lastInitializer, JSTextPosition& identStart, JSTextPosition& initStart, JSTextPosition& initEnd, VarDeclarationListContext declarationListContext, DeclarationType declarationType, ExportType exportType, bool& forLoopConstDoesNotHaveInitializer) { ASSERT(declarationType == DeclarationType::LetDeclaration || declarationType == DeclarationType::VarDeclaration || declarationType == DeclarationType::ConstDeclaration); TreeExpression head = 0; JSTokenLocation headLocation; TreeExpression tail = 0; const Identifier* lastIdent; JSToken lastIdentToken; AssignmentContext assignmentContext = assignmentContextFromDeclarationType(declarationType); do { lastPattern = TreeDestructuringPattern(0); lastIdent = nullptr; JSTokenLocation location(tokenLocation()); next(); if (head) { // Move the location of subsequent declarations after the comma. location = tokenLocation(); } TreeExpression node = 0; declarations++; bool hasInitializer = false; failIfTrue(match(PRIVATENAME), "Cannot use a private name to declare a variable"); if (matchSpecIdentifier()) { failIfTrue(match(LET) && (declarationType == DeclarationType::LetDeclaration || declarationType == DeclarationType::ConstDeclaration), "Cannot use 'let' as an identifier name for a LexicalDeclaration"); semanticFailIfTrue(isDisallowedIdentifierAwait(m_token), "Cannot use 'await' as a ", declarationTypeToVariableKind(declarationType), " ", disallowedIdentifierAwaitReason()); JSTextPosition varStart = tokenStartPosition(); JSTokenLocation varStartLocation(tokenLocation()); identStart = varStart; const Identifier* name = m_token.m_data.ident; lastIdent = name; lastIdentToken = m_token; next(); hasInitializer = match(EQUAL); DeclarationResultMask declarationResult = declareVariable(name, declarationType); if (declarationResult != DeclarationResult::Valid) { failIfTrueIfStrict(declarationResult & DeclarationResult::InvalidStrictMode, "Cannot declare a variable named ", name->impl(), " in strict mode"); if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration) { if (declarationType == DeclarationType::LetDeclaration) internalFailWithMessage(false, "Cannot declare a let variable twice: '", name->impl(), "'"); if (declarationType == DeclarationType::ConstDeclaration) internalFailWithMessage(false, "Cannot declare a const variable twice: '", name->impl(), "'"); ASSERT(declarationType == DeclarationType::VarDeclaration); internalFailWithMessage(false, "Cannot declare a var variable that shadows a let/const/class variable: '", name->impl(), "'"); } } if (exportType == ExportType::Exported) { semanticFailIfFalse(exportName(*name), "Cannot export a duplicate name '", name->impl(), "'"); m_moduleScopeData->exportBinding(*name); } if (hasInitializer) { JSTextPosition varDivot = tokenStartPosition() + 1; initStart = tokenStartPosition(); next(TreeBuilder::DontBuildStrings); // consume '=' propagateError(); TreeExpression initializer = parseAssignmentExpression(context); initEnd = lastTokenEndPosition(); lastInitializer = initializer; failIfFalse(initializer, "Expected expression as the intializer for the variable '", name->impl(), "'"); node = context.createAssignResolve(location, *name, initializer, varStart, varDivot, lastTokenEndPosition(), assignmentContext); } else { if (declarationListContext == ForLoopContext && declarationType == DeclarationType::ConstDeclaration) forLoopConstDoesNotHaveInitializer = true; failIfTrue(declarationListContext != ForLoopContext && declarationType == DeclarationType::ConstDeclaration, "const declared variable '", name->impl(), "'", " must have an initializer"); if (declarationType == DeclarationType::VarDeclaration) node = context.createEmptyVarExpression(varStartLocation, *name); else node = context.createEmptyLetExpression(varStartLocation, *name); } } else { lastIdent = nullptr; auto pattern = parseDestructuringPattern(context, destructuringKindFromDeclarationType(declarationType), exportType, nullptr, nullptr, assignmentContext); failIfFalse(pattern, "Cannot parse this destructuring pattern"); hasInitializer = match(EQUAL); failIfTrue(declarationListContext == VarDeclarationContext && !hasInitializer, "Expected an initializer in destructuring variable declaration"); lastPattern = pattern; if (hasInitializer) { next(TreeBuilder::DontBuildStrings); // consume '=' TreeExpression rhs = parseAssignmentExpression(context); propagateError(); ASSERT(rhs); node = context.createDestructuringAssignment(location, pattern, rhs); lastInitializer = rhs; } } if (node) { if (!head) { head = node; headLocation = location; } else { if (!tail) { head = tail = context.createCommaExpr(headLocation, head); recordPauseLocation(context.breakpointLocation(head)); } tail = context.appendToCommaExpr(location, head, tail, node); recordPauseLocation(context.breakpointLocation(tail)); } } } while (match(COMMA)); if (lastIdent) lastPattern = context.createBindingLocation(lastIdentToken.m_location, *lastIdent, lastIdentToken.m_startPosition, lastIdentToken.m_endPosition, assignmentContext); return head; } template bool Parser::declareRestOrNormalParameter(const Identifier& name, const Identifier** duplicateIdentifier) { DeclarationResultMask declarationResult = declareParameter(&name); if ((declarationResult & DeclarationResult::InvalidStrictMode) && strictMode()) { semanticFailIfTrue(isEvalOrArguments(&name), "Cannot destructure to a parameter name '", name.impl(), "' in strict mode"); if (m_parserState.lastFunctionName && name == *m_parserState.lastFunctionName) semanticFail("Cannot declare a parameter named '", name.impl(), "' as it shadows the name of a strict mode function"); semanticFailureDueToKeyword("parameter name"); if (!m_lexer->isReparsingFunction() && hasDeclaredParameter(name)) semanticFail("Cannot declare a parameter named '", name.impl(), "' in strict mode as it has already been declared"); semanticFail("Cannot declare a parameter named '", name.impl(), "' in strict mode"); } if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration) { // It's not always an error to define a duplicate parameter. // It's only an error when there are default parameter values or destructuring parameters. // We note this value now so we can check it later. if (duplicateIdentifier) *duplicateIdentifier = &name; } return true; } template template TreeDestructuringPattern Parser::createBindingPattern(TreeBuilder& context, DestructuringKind kind, ExportType exportType, const Identifier& name, const JSToken& token, AssignmentContext bindingContext, const Identifier** duplicateIdentifier) { ASSERT(!name.isNull()); ASSERT(name.impl()->isAtom() || name.impl()->isSymbol()); switch (kind) { case DestructuringKind::DestructureToVariables: { DeclarationResultMask declarationResult = declareVariable(&name); failIfTrueIfStrict(declarationResult & DeclarationResult::InvalidStrictMode, "Cannot declare a variable named '", name.impl(), "' in strict mode"); if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration) internalFailWithMessage(false, "Cannot declare a var variable that shadows a let/const/class variable: '", name.impl(), "'"); break; } case DestructuringKind::DestructureToLet: case DestructuringKind::DestructureToConst: case DestructuringKind::DestructureToCatchParameters: { DeclarationResultMask declarationResult = declareVariable(&name, kind == DestructuringKind::DestructureToConst ? DeclarationType::ConstDeclaration : DeclarationType::LetDeclaration); if (declarationResult != DeclarationResult::Valid) { failIfTrueIfStrict(declarationResult & DeclarationResult::InvalidStrictMode, "Cannot destructure to a variable named '", name.impl(), "' in strict mode"); failIfTrue(declarationResult & DeclarationResult::InvalidDuplicateDeclaration, "Cannot declare a lexical variable twice: '", name.impl(), "'"); } break; } case DestructuringKind::DestructureToParameters: { declareRestOrNormalParameter(name, duplicateIdentifier); propagateError(); break; } case DestructuringKind::DestructureToExpressions: { break; } } if (exportType == ExportType::Exported) { semanticFailIfFalse(exportName(name), "Cannot export a duplicate name '", name.impl(), "'"); m_moduleScopeData->exportBinding(name); } return context.createBindingLocation(token.m_location, name, token.m_startPosition, token.m_endPosition, bindingContext); } template template NEVER_INLINE TreeDestructuringPattern Parser::createAssignmentElement(TreeBuilder& context, TreeExpression& assignmentTarget, const JSTextPosition& startPosition, const JSTextPosition& endPosition) { return context.createAssignmentElement(assignmentTarget, startPosition, endPosition); } template template TreeSourceElements Parser::parseArrowFunctionSingleExpressionBodySourceElements(TreeBuilder& context) { ASSERT(!match(OPENBRACE)); JSTokenLocation location(tokenLocation()); JSTextPosition start = tokenStartPosition(); failIfStackOverflow(); TreeExpression expr = parseAssignmentExpression(context); failIfFalse(expr, "Cannot parse the arrow function expression"); context.setEndOffset(expr, m_lastTokenEndPosition.offset); JSTextPosition end = tokenEndPosition(); TreeSourceElements sourceElements = context.createSourceElements(); TreeStatement body = context.createReturnStatement(location, expr, start, end); context.setEndOffset(body, m_lastTokenEndPosition.offset); recordPauseLocation(context.breakpointLocation(body)); context.appendStatement(sourceElements, body); return sourceElements; } template template TreeDestructuringPattern Parser::tryParseDestructuringPatternExpression(TreeBuilder& context, AssignmentContext bindingContext) { return parseDestructuringPattern(context, DestructuringKind::DestructureToExpressions, ExportType::NotExported, nullptr, nullptr, bindingContext); } template template TreeDestructuringPattern Parser::parseBindingOrAssignmentElement(TreeBuilder& context, DestructuringKind kind, ExportType exportType, const Identifier** duplicateIdentifier, bool* hasDestructuringPattern, AssignmentContext bindingContext, int depth) { if (kind == DestructuringKind::DestructureToExpressions) return parseAssignmentElement(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth); return parseDestructuringPattern(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth); } template template TreeDestructuringPattern Parser::parseObjectRestAssignmentElement(TreeBuilder& context) { JSTextPosition startPosition = tokenStartPosition(); auto element = parseMemberExpression(context); if (!element || !context.isAssignmentLocation(element)) { reclassifyExpressionError(ErrorIndicatesPattern, ErrorIndicatesNothing); semanticFail("Invalid destructuring assignment target"); } if (strictMode() && m_parserState.lastIdentifier && context.isResolve(element)) { bool isEvalOrArguments = m_vm.propertyNames->eval == *m_parserState.lastIdentifier || m_vm.propertyNames->arguments == *m_parserState.lastIdentifier; if (isEvalOrArguments && strictMode()) reclassifyExpressionError(ErrorIndicatesPattern, ErrorIndicatesNothing); failIfTrueIfStrict(isEvalOrArguments, "Cannot modify '", m_parserState.lastIdentifier->impl(), "' in strict mode"); } return createAssignmentElement(context, element, startPosition, lastTokenEndPosition()); } template template TreeDestructuringPattern Parser::parseAssignmentElement(TreeBuilder& context, DestructuringKind kind, ExportType exportType, const Identifier** duplicateIdentifier, bool* hasDestructuringPattern, AssignmentContext bindingContext, int depth) { TreeDestructuringPattern assignmentTarget = 0; if (match(OPENBRACE) || match(OPENBRACKET)) { SavePoint savePoint = createSavePoint(context); assignmentTarget = parseDestructuringPattern(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth); if (assignmentTarget && !match(DOT) && !match(OPENBRACKET) && !match(OPENPAREN) && !match(BACKQUOTE)) return assignmentTarget; restoreSavePoint(context, savePoint); } JSTextPosition startPosition = tokenStartPosition(); auto element = parseMemberExpression(context); semanticFailIfFalse(element && context.isAssignmentLocation(element), "Invalid destructuring assignment target"); if (strictMode() && m_parserState.lastIdentifier && context.isResolve(element)) { bool isEvalOrArguments = m_vm.propertyNames->eval == *m_parserState.lastIdentifier || m_vm.propertyNames->arguments == *m_parserState.lastIdentifier; failIfTrueIfStrict(isEvalOrArguments, "Cannot modify '", m_parserState.lastIdentifier->impl(), "' in strict mode"); } return createAssignmentElement(context, element, startPosition, lastTokenEndPosition()); } static const char* destructuringKindToVariableKindName(DestructuringKind kind) { switch (kind) { case DestructuringKind::DestructureToLet: case DestructuringKind::DestructureToConst: return "lexical variable name"; case DestructuringKind::DestructureToVariables: return "variable name"; case DestructuringKind::DestructureToParameters: return "parameter name"; case DestructuringKind::DestructureToCatchParameters: return "catch parameter name"; case DestructuringKind::DestructureToExpressions: return "expression name"; } RELEASE_ASSERT_NOT_REACHED(); return "invalid"; } template template TreeDestructuringPattern Parser::parseObjectRestElement(TreeBuilder& context, DestructuringKind kind, ExportType exportType, const Identifier** duplicateIdentifier, AssignmentContext bindingContext) { ASSERT(kind != DestructuringKind::DestructureToExpressions); failIfStackOverflow(); TreeDestructuringPattern pattern; if (!matchSpecIdentifier()) { semanticFailureDueToKeyword(destructuringKindToVariableKindName(kind)); failWithMessage("Expected a binding element"); } failIfTrue(match(LET) && (kind == DestructuringKind::DestructureToLet || kind == DestructuringKind::DestructureToConst), "Cannot use 'let' as an identifier name for a LexicalDeclaration"); semanticFailIfTrue(isDisallowedIdentifierAwait(m_token), "Cannot use 'await' as a ", destructuringKindToVariableKindName(kind), " ", disallowedIdentifierAwaitReason()); pattern = createBindingPattern(context, kind, exportType, *m_token.m_data.ident, m_token, bindingContext, duplicateIdentifier); next(); return pattern; } template template TreeDestructuringPattern Parser::parseObjectRestBindingOrAssignmentElement(TreeBuilder& context, DestructuringKind kind, ExportType exportType, const Identifier** duplicateIdentifier, AssignmentContext bindingContext) { if (kind == DestructuringKind::DestructureToExpressions) return parseObjectRestAssignmentElement(context); return parseObjectRestElement(context, kind, exportType, duplicateIdentifier, bindingContext); } template template TreeDestructuringPattern Parser::parseDestructuringPattern(TreeBuilder& context, DestructuringKind kind, ExportType exportType, const Identifier** duplicateIdentifier, bool* hasDestructuringPattern, AssignmentContext bindingContext, int depth) { failIfStackOverflow(); m_parserState.assignmentCount++; int nonLHSCount = m_parserState.nonLHSCount; TreeDestructuringPattern pattern; switch (m_token.m_type) { case OPENBRACKET: { JSTextPosition divotStart = tokenStartPosition(); auto arrayPattern = context.createArrayPattern(m_token.m_location); next(); if (hasDestructuringPattern) *hasDestructuringPattern = true; bool restElementWasFound = false; do { while (match(COMMA)) { context.appendArrayPatternSkipEntry(arrayPattern, m_token.m_location); next(); } propagateError(); if (match(CLOSEBRACKET)) break; if (UNLIKELY(match(DOTDOTDOT))) { JSTokenLocation location = m_token.m_location; next(); auto innerPattern = parseBindingOrAssignmentElement(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1); if (kind == DestructuringKind::DestructureToExpressions && !innerPattern) return 0; failIfFalse(innerPattern, "Cannot parse this destructuring pattern"); context.appendArrayPatternRestEntry(arrayPattern, location, innerPattern); restElementWasFound = true; break; } JSTokenLocation location = m_token.m_location; auto innerPattern = parseBindingOrAssignmentElement(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1); if (kind == DestructuringKind::DestructureToExpressions && !innerPattern) return 0; failIfFalse(innerPattern, "Cannot parse this destructuring pattern"); TreeExpression defaultValue = parseDefaultValueForDestructuringPattern(context); propagateError(); context.appendArrayPatternEntry(arrayPattern, location, innerPattern, defaultValue); } while (consume(COMMA)); consumeOrFail(CLOSEBRACKET, restElementWasFound ? "Expected a closing ']' following a rest element destructuring pattern" : "Expected either a closing ']' or a ',' following an element destructuring pattern"); context.finishArrayPattern(arrayPattern, divotStart, divotStart, lastTokenEndPosition()); pattern = arrayPattern; break; } case OPENBRACE: { auto objectPattern = context.createObjectPattern(m_token.m_location); next(); if (hasDestructuringPattern) *hasDestructuringPattern = true; bool restElementWasFound = false; do { bool wasString = false; if (match(CLOSEBRACE)) break; if (match(DOTDOTDOT)) { JSTokenLocation location = m_token.m_location; next(); auto innerPattern = parseObjectRestBindingOrAssignmentElement(context, kind, exportType, duplicateIdentifier, bindingContext); propagateError(); if (!innerPattern) return 0; context.appendObjectPatternRestEntry(m_vm, objectPattern, location, innerPattern); restElementWasFound = true; context.setContainsObjectRestElement(objectPattern, restElementWasFound); break; } const Identifier* propertyName = nullptr; TreeExpression propertyExpression = 0; TreeDestructuringPattern innerPattern = 0; JSTokenLocation location = m_token.m_location; bool escapedKeyword = match(ESCAPED_KEYWORD); if (escapedKeyword || matchSpecIdentifier()) { failIfTrue(match(LET) && (kind == DestructuringKind::DestructureToLet || kind == DestructuringKind::DestructureToConst), "Cannot use 'let' as an identifier name for a LexicalDeclaration"); propertyName = m_token.m_data.ident; JSToken identifierToken = m_token; next(); if (consume(COLON)) innerPattern = parseBindingOrAssignmentElement(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1); else { semanticFailIfTrue(escapedKeyword, "Cannot use abbreviated destructuring syntax for keyword '", propertyName->impl(), "'"); if (kind == DestructuringKind::DestructureToExpressions) { bool isEvalOrArguments = m_vm.propertyNames->eval == *propertyName || m_vm.propertyNames->arguments == *propertyName; if (isEvalOrArguments && strictMode()) reclassifyExpressionError(ErrorIndicatesPattern, ErrorIndicatesNothing); failIfTrueIfStrict(isEvalOrArguments, "Cannot modify '", propertyName->impl(), "' in strict mode"); } semanticFailIfTrue(isDisallowedIdentifierAwait(identifierToken), "Cannot use 'await' as a ", destructuringKindToVariableKindName(kind), " ", disallowedIdentifierAwaitReason()); innerPattern = createBindingPattern(context, kind, exportType, *propertyName, identifierToken, bindingContext, duplicateIdentifier); } } else { JSTokenType tokenType = m_token.m_type; switch (m_token.m_type) { case DOUBLE: case INTEGER: propertyName = &m_parserArena.identifierArena().makeNumericIdentifier(const_cast(m_vm), m_token.m_data.doubleValue); break; case STRING: propertyName = m_token.m_data.ident; wasString = true; break; case BIGINT: propertyName = &m_parserArena.identifierArena().makeBigIntDecimalIdentifier(const_cast(m_vm), *m_token.m_data.bigIntString, m_token.m_data.radix); break; case OPENBRACKET: next(); propertyExpression = parseAssignmentExpression(context); failIfFalse(propertyExpression, "Cannot parse computed property name"); matchOrFail(CLOSEBRACKET, "Expected ']' to end end a computed property name"); break; default: if (m_token.m_type != RESERVED && m_token.m_type != RESERVED_IF_STRICT && !(m_token.m_type & KeywordTokenFlag)) { if (kind == DestructuringKind::DestructureToExpressions) return 0; failWithMessage("Expected a property name"); } propertyName = m_token.m_data.ident; break; } next(); if (!consume(COLON)) { if (kind == DestructuringKind::DestructureToExpressions) return 0; semanticFailIfTrue(tokenType == RESERVED, "Cannot use abbreviated destructuring syntax for reserved name '", propertyName->impl(), "'"); semanticFailIfTrue(tokenType == RESERVED_IF_STRICT, "Cannot use abbreviated destructuring syntax for reserved name '", propertyName->impl(), "' in strict mode"); semanticFailIfTrue(tokenType & KeywordTokenFlag, "Cannot use abbreviated destructuring syntax for keyword '", propertyName->impl(), "'"); failWithMessage("Expected a ':' prior to a named destructuring property"); } innerPattern = parseBindingOrAssignmentElement(context, kind, exportType, duplicateIdentifier, hasDestructuringPattern, bindingContext, depth + 1); } if (kind == DestructuringKind::DestructureToExpressions && !innerPattern) return 0; failIfFalse(innerPattern, "Cannot parse this destructuring pattern"); TreeExpression defaultValue = parseDefaultValueForDestructuringPattern(context); propagateError(); if (propertyExpression) { context.appendObjectPatternEntry(m_vm, objectPattern, location, propertyExpression, innerPattern, defaultValue); context.setContainsComputedProperty(objectPattern, true); } else { ASSERT(propertyName); context.appendObjectPatternEntry(objectPattern, location, wasString, *propertyName, innerPattern, defaultValue); } } while (consume(COMMA)); if (kind == DestructuringKind::DestructureToExpressions && !match(CLOSEBRACE)) return 0; consumeOrFail(CLOSEBRACE, restElementWasFound ? "Expected a closing '}' following a rest element destructuring pattern" : "Expected either a closing '}' or an ',' after a property destructuring pattern"); pattern = objectPattern; break; } default: { if (!matchSpecIdentifier()) { if (kind == DestructuringKind::DestructureToExpressions) return 0; semanticFailureDueToKeyword(destructuringKindToVariableKindName(kind)); if (kind != DestructuringKind::DestructureToParameters) failIfTrue(match(PRIVATENAME), "Cannot use a private name as a ", destructuringKindToVariableKindName(kind)); failWithMessage("Expected a parameter pattern or a ')' in parameter list"); } failIfTrue(match(LET) && (kind == DestructuringKind::DestructureToLet || kind == DestructuringKind::DestructureToConst), "Cannot use 'let' as an identifier name for a LexicalDeclaration"); semanticFailIfTrue(isDisallowedIdentifierAwait(m_token), "Cannot use 'await' as a ", destructuringKindToVariableKindName(kind), " ", disallowedIdentifierAwaitReason()); pattern = createBindingPattern(context, kind, exportType, *m_token.m_data.ident, m_token, bindingContext, duplicateIdentifier); next(); break; } } m_parserState.nonLHSCount = nonLHSCount; return pattern; } template template TreeExpression Parser::parseDefaultValueForDestructuringPattern(TreeBuilder& context) { if (!match(EQUAL)) return 0; next(TreeBuilder::DontBuildStrings); // consume '=' return parseAssignmentExpression(context); } template template TreeStatement Parser::parseForStatement(TreeBuilder& context) { ASSERT(match(FOR)); JSTokenLocation location(tokenLocation()); int startLine = tokenLine(); bool isAwaitFor = false; next(); DepthManager statementDepth(&m_statementDepth); m_statementDepth++; if (match(AWAIT)) { semanticFailIfFalse(currentScope()->isAsyncFunction(), "for-await-of can only be used in an async function or async generator"); isAwaitFor = true; next(); } handleProductionOrFail(OPENPAREN, "(", "start", "for-loop header"); int nonLHSCount = m_parserState.nonLHSCount; int declarations = 0; JSTokenLocation declLocation(tokenLocation()); JSTextPosition declsStart; JSTextPosition declsEnd; TreeExpression decls = 0; TreeDestructuringPattern pattern = 0; bool isVarDeclaration = match(VAR); bool isLetDeclaration = match(LET); bool isConstDeclaration = match(CONSTTOKEN); bool forLoopConstDoesNotHaveInitializer = false; VariableEnvironment dummySet; VariableEnvironment* lexicalVariables = nullptr; AutoCleanupLexicalScope lexicalScope; auto gatherLexicalVariablesIfNecessary = [&] { if (isLetDeclaration || isConstDeclaration) { ScopeRef scope = lexicalScope.scope(); lexicalVariables = &scope->finalizeLexicalEnvironment(); } else lexicalVariables = &dummySet; }; auto popLexicalScopeIfNecessary = [&] { if (isLetDeclaration || isConstDeclaration) popScope(lexicalScope, TreeBuilder::NeedsFreeVariableInfo); }; if (isVarDeclaration || isLetDeclaration || isConstDeclaration) { /* for (var/let/const IDENT in/of expression) statement for (var/let/const varDeclarationList; expressionOpt; expressionOpt) */ if (isLetDeclaration || isConstDeclaration) { ScopeRef newScope = pushScope(); newScope->setIsLexicalScope(); newScope->preventVarDeclarations(); lexicalScope.setIsValid(newScope, this); } TreeDestructuringPattern forInTarget = 0; TreeExpression forInInitializer = 0; m_allowsIn = false; JSTextPosition initStart; JSTextPosition initEnd; DeclarationType declarationType; if (isVarDeclaration) declarationType = DeclarationType::VarDeclaration; else if (isLetDeclaration) declarationType = DeclarationType::LetDeclaration; else if (isConstDeclaration) declarationType = DeclarationType::ConstDeclaration; else RELEASE_ASSERT_NOT_REACHED(); decls = parseVariableDeclarationList(context, declarations, forInTarget, forInInitializer, declsStart, initStart, initEnd, ForLoopContext, declarationType, ExportType::NotExported, forLoopConstDoesNotHaveInitializer); m_allowsIn = true; propagateError(); // Remainder of a standard for loop is handled identically if (match(SEMICOLON)) goto standardForLoop; failIfFalse(declarations == 1, "can only declare a single variable in an enumeration"); // Handle for-in with var declaration JSTextPosition inLocation = tokenStartPosition(); bool isOfEnumeration = false; if (!match(INTOKEN)) { failIfFalse(matchContextualKeyword(m_vm.propertyNames->of), "Expected either 'in' or 'of' in enumeration syntax"); isOfEnumeration = true; next(); } else { failIfFalse(!isAwaitFor, "Expected 'of' in for-await syntax"); next(); } bool hasAnyAssignments = !!forInInitializer; if (hasAnyAssignments) { if (isOfEnumeration) internalFailWithMessage(false, "Cannot assign to the loop variable inside a for-of loop header"); if (strictMode() || (isLetDeclaration || isConstDeclaration) || !context.isBindingNode(forInTarget)) internalFailWithMessage(false, "Cannot assign to the loop variable inside a for-in loop header"); } // While for-in uses Expression, for-of / for-await-of use AssignmentExpression. // https://tc39.es/ecma262/#sec-for-in-and-for-of-statements TreeExpression expr = 0; if (isOfEnumeration) expr = parseAssignmentExpression(context); else expr = parseExpression(context); failIfFalse(expr, "Expected expression to enumerate"); recordPauseLocation(context.breakpointLocation(expr)); JSTextPosition exprEnd = lastTokenEndPosition(); int endLine = tokenLine(); handleProductionOrFail(CLOSEPAREN, ")", "end", (isOfEnumeration ? "for-of header" : "for-in header")); const Identifier* unused = nullptr; startLoop(); TreeStatement statement = parseStatement(context, unused); endLoop(); failIfFalse(statement, "Expected statement as body of for-", isOfEnumeration ? "of" : "in", " statement"); gatherLexicalVariablesIfNecessary(); TreeStatement result; if (isOfEnumeration) result = context.createForOfLoop(isAwaitFor, location, forInTarget, expr, statement, declLocation, declsStart, inLocation, exprEnd, startLine, endLine, *lexicalVariables); else { ASSERT(!isAwaitFor); if (isVarDeclaration && forInInitializer) result = context.createForInLoop(location, decls, expr, statement, declLocation, declsStart, inLocation, exprEnd, startLine, endLine, *lexicalVariables); else result = context.createForInLoop(location, forInTarget, expr, statement, declLocation, declsStart, inLocation, exprEnd, startLine, endLine, *lexicalVariables); } popLexicalScopeIfNecessary(); return result; } if (!match(SEMICOLON)) { if (match(OPENBRACE) || match(OPENBRACKET)) { SavePoint savePoint = createSavePoint(context); declsStart = tokenStartPosition(); pattern = tryParseDestructuringPatternExpression(context, AssignmentContext::DeclarationStatement); declsEnd = lastTokenEndPosition(); if (pattern && (match(INTOKEN) || matchContextualKeyword(m_vm.propertyNames->of))) goto enumerationLoop; pattern = TreeDestructuringPattern(0); restoreSavePoint(context, savePoint); } m_allowsIn = false; declsStart = tokenStartPosition(); decls = parseExpression(context); declsEnd = lastTokenEndPosition(); m_allowsIn = true; failIfFalse(decls, "Cannot parse for loop declarations"); recordPauseLocation(context.breakpointLocation(decls)); } if (match(SEMICOLON)) { standardForLoop: failIfFalse(!isAwaitFor, "Unexpected a ';' in for-await-of header"); // Standard for loop if (decls) recordPauseLocation(context.breakpointLocation(decls)); next(); TreeExpression condition = 0; failIfTrue(forLoopConstDoesNotHaveInitializer && isConstDeclaration, "const variables in for loops must have initializers"); if (!match(SEMICOLON)) { condition = parseExpression(context); failIfFalse(condition, "Cannot parse for loop condition expression"); recordPauseLocation(context.breakpointLocation(condition)); } consumeOrFail(SEMICOLON, "Expected a ';' after the for loop condition expression"); TreeExpression increment = 0; if (!match(CLOSEPAREN)) { increment = parseExpression(context); failIfFalse(increment, "Cannot parse for loop iteration expression"); recordPauseLocation(context.breakpointLocation(increment)); } int endLine = tokenLine(); handleProductionOrFail(CLOSEPAREN, ")", "end", "for-loop header"); const Identifier* unused = nullptr; startLoop(); TreeStatement statement = parseStatement(context, unused); endLoop(); failIfFalse(statement, "Expected a statement as the body of a for loop"); gatherLexicalVariablesIfNecessary(); TreeStatement result = context.createForLoop(location, decls, condition, increment, statement, startLine, endLine, *lexicalVariables); popLexicalScopeIfNecessary(); return result; } // For-in and For-of loop enumerationLoop: failIfFalse(nonLHSCount == m_parserState.nonLHSCount, "Expected a reference on the left hand side of an enumeration statement"); bool isOfEnumeration = false; if (!match(INTOKEN)) { failIfFalse(matchContextualKeyword(m_vm.propertyNames->of), "Expected either 'in' or 'of' in enumeration syntax"); isOfEnumeration = true; next(); } else { failIfFalse(!isAwaitFor, "Expected 'of' in for-await syntax"); next(); } // While for-in uses Expression, for-of / for-await-of use AssignmentExpression. // https://tc39.es/ecma262/#sec-for-in-and-for-of-statements TreeExpression expr = 0; if (isOfEnumeration) expr = parseAssignmentExpression(context); else expr = parseExpression(context); failIfFalse(expr, "Cannot parse subject for-", isOfEnumeration ? "of" : "in", " statement"); recordPauseLocation(context.breakpointLocation(expr)); JSTextPosition exprEnd = lastTokenEndPosition(); int endLine = tokenLine(); handleProductionOrFail(CLOSEPAREN, ")", "end", (isOfEnumeration ? "for-of header" : "for-in header")); const Identifier* unused = nullptr; startLoop(); TreeStatement statement = parseStatement(context, unused); endLoop(); failIfFalse(statement, "Expected a statement as the body of a for-", isOfEnumeration ? "of" : "in", "loop"); gatherLexicalVariablesIfNecessary(); TreeStatement result; if (pattern) { ASSERT(!decls); if (isOfEnumeration) result = context.createForOfLoop(isAwaitFor, location, pattern, expr, statement, declLocation, declsStart, declsEnd, exprEnd, startLine, endLine, *lexicalVariables); else { ASSERT(!isAwaitFor); result = context.createForInLoop(location, pattern, expr, statement, declLocation, declsStart, declsEnd, exprEnd, startLine, endLine, *lexicalVariables); } popLexicalScopeIfNecessary(); return result; } semanticFailIfFalse(isSimpleAssignmentTarget(context, decls), "Left side of assignment is not a reference"); if (isOfEnumeration) result = context.createForOfLoop(isAwaitFor, location, decls, expr, statement, declLocation, declsStart, declsEnd, exprEnd, startLine, endLine, *lexicalVariables); else { ASSERT(!isAwaitFor); result = context.createForInLoop(location, decls, expr, statement, declLocation, declsStart, declsEnd, exprEnd, startLine, endLine, *lexicalVariables); } popLexicalScopeIfNecessary(); return result; } template template TreeStatement Parser::parseBreakStatement(TreeBuilder& context) { ASSERT(match(BREAK)); JSTokenLocation location(tokenLocation()); JSTextPosition start = tokenStartPosition(); JSTextPosition end = tokenEndPosition(); next(); if (autoSemiColon()) { semanticFailIfFalse(breakIsValid(), "'break' is only valid inside a switch or loop statement"); return context.createBreakStatement(location, &m_vm.propertyNames->nullIdentifier, start, end); } failIfFalse(matchSpecIdentifier(), "Expected an identifier as the target for a break statement"); const Identifier* ident = m_token.m_data.ident; semanticFailIfFalse(getLabel(ident), "Cannot use the undeclared label '", ident->impl(), "'"); end = tokenEndPosition(); next(); failIfFalse(autoSemiColon(), "Expected a ';' following a targeted break statement"); return context.createBreakStatement(location, ident, start, end); } template template TreeStatement Parser::parseContinueStatement(TreeBuilder& context) { ASSERT(match(CONTINUE)); JSTokenLocation location(tokenLocation()); JSTextPosition start = tokenStartPosition(); JSTextPosition end = tokenEndPosition(); next(); if (autoSemiColon()) { semanticFailIfFalse(continueIsValid(), "'continue' is only valid inside a loop statement"); return context.createContinueStatement(location, &m_vm.propertyNames->nullIdentifier, start, end); } failIfFalse(matchSpecIdentifier(), "Expected an identifier as the target for a continue statement"); const Identifier* ident = m_token.m_data.ident; ScopeLabelInfo* label = getLabel(ident); semanticFailIfFalse(label, "Cannot use the undeclared label '", ident->impl(), "'"); semanticFailIfFalse(label->isLoop, "Cannot continue to the label '", ident->impl(), "' as it is not targeting a loop"); end = tokenEndPosition(); next(); failIfFalse(autoSemiColon(), "Expected a ';' following a targeted continue statement"); return context.createContinueStatement(location, ident, start, end); } template template TreeStatement Parser::parseReturnStatement(TreeBuilder& context) { ASSERT(match(RETURN)); JSTokenLocation location(tokenLocation()); semanticFailIfFalse(currentScope()->isFunction(), "Return statements are only valid inside functions"); JSTextPosition start = tokenStartPosition(); JSTextPosition end = tokenEndPosition(); next(); // We do the auto semicolon check before attempting to parse expression // as we need to ensure the a line break after the return correctly terminates // the statement if (match(SEMICOLON)) end = tokenEndPosition(); if (autoSemiColon()) return context.createReturnStatement(location, 0, start, end); TreeExpression expr = parseExpression(context, IsOnlyChildOfStatement::Yes); failIfFalse(expr, "Cannot parse the return expression"); end = lastTokenEndPosition(); if (match(SEMICOLON)) end = tokenEndPosition(); if (!autoSemiColon()) failWithMessage("Expected a ';' following a return statement"); return context.createReturnStatement(location, expr, start, end); } template template TreeStatement Parser::parseThrowStatement(TreeBuilder& context) { ASSERT(match(THROW)); JSTokenLocation location(tokenLocation()); JSTextPosition start = tokenStartPosition(); next(); failIfTrue(match(SEMICOLON), "Expected expression after 'throw'"); semanticFailIfTrue(autoSemiColon(), "Cannot have a newline after 'throw'"); TreeExpression expr = parseExpression(context, IsOnlyChildOfStatement::Yes); failIfFalse(expr, "Cannot parse expression for throw statement"); JSTextPosition end = lastTokenEndPosition(); failIfFalse(autoSemiColon(), "Expected a ';' after a throw statement"); return context.createThrowStatement(location, expr, start, end); } template template TreeStatement Parser::parseWithStatement(TreeBuilder& context) { ASSERT(match(WITH)); JSTokenLocation location(tokenLocation()); semanticFailIfTrue(strictMode(), "'with' statements are not valid in strict mode"); currentScope()->setNeedsFullActivation(); int startLine = tokenLine(); next(); handleProductionOrFail(OPENPAREN, "(", "start", "subject of a 'with' statement"); int start = tokenStart(); TreeExpression expr = parseExpression(context); failIfFalse(expr, "Cannot parse 'with' subject expression"); recordPauseLocation(context.breakpointLocation(expr)); JSTextPosition end = lastTokenEndPosition(); int endLine = tokenLine(); handleProductionOrFail(CLOSEPAREN, ")", "start", "subject of a 'with' statement"); const Identifier* unused = nullptr; TreeStatement statement = parseStatement(context, unused); failIfFalse(statement, "A 'with' statement must have a body"); return context.createWithStatement(location, expr, statement, start, end, startLine, endLine); } template template TreeStatement Parser::parseSwitchStatement(TreeBuilder& context) { ASSERT(match(SWITCH)); JSTokenLocation location(tokenLocation()); int startLine = tokenLine(); next(); handleProductionOrFail(OPENPAREN, "(", "start", "subject of a 'switch'"); TreeExpression expr = parseExpression(context); failIfFalse(expr, "Cannot parse switch subject expression"); recordPauseLocation(context.breakpointLocation(expr)); int endLine = tokenLine(); handleProductionOrFail(CLOSEPAREN, ")", "end", "subject of a 'switch'"); handleProductionOrFail(OPENBRACE, "{", "start", "body of a 'switch'"); AutoPopScopeRef lexicalScope(this, pushScope()); lexicalScope->setIsLexicalScope(); lexicalScope->preventVarDeclarations(); startSwitch(); TreeClauseList firstClauses = parseSwitchClauses(context); propagateError(); TreeClause defaultClause = parseSwitchDefaultClause(context); propagateError(); TreeClauseList secondClauses = parseSwitchClauses(context); propagateError(); endSwitch(); handleProductionOrFail(CLOSEBRACE, "}", "end", "body of a 'switch'"); TreeStatement result = context.createSwitchStatement(location, expr, firstClauses, defaultClause, secondClauses, startLine, endLine, lexicalScope->finalizeLexicalEnvironment(), lexicalScope->takeFunctionDeclarations()); popScope(lexicalScope, TreeBuilder::NeedsFreeVariableInfo); return result; } template template TreeClauseList Parser::parseSwitchClauses(TreeBuilder& context) { if (!match(CASE)) return 0; unsigned startOffset = tokenStart(); next(); TreeExpression condition = parseExpression(context); failIfFalse(condition, "Cannot parse switch clause"); consumeOrFail(COLON, "Expected a ':' after switch clause expression"); TreeSourceElements statements = parseSourceElements(context, DontCheckForStrictMode); failIfFalse(statements, "Cannot parse the body of a switch clause"); TreeClause clause = context.createClause(condition, statements); context.setStartOffset(clause, startOffset); TreeClauseList clauseList = context.createClauseList(clause); TreeClauseList tail = clauseList; while (match(CASE)) { startOffset = tokenStart(); next(); TreeExpression condition = parseExpression(context); failIfFalse(condition, "Cannot parse switch case expression"); consumeOrFail(COLON, "Expected a ':' after switch clause expression"); TreeSourceElements statements = parseSourceElements(context, DontCheckForStrictMode); failIfFalse(statements, "Cannot parse the body of a switch clause"); clause = context.createClause(condition, statements); context.setStartOffset(clause, startOffset); tail = context.createClauseList(tail, clause); } return clauseList; } template template TreeClause Parser::parseSwitchDefaultClause(TreeBuilder& context) { if (!match(DEFAULT)) return 0; unsigned startOffset = tokenStart(); next(); consumeOrFail(COLON, "Expected a ':' after switch default clause"); TreeSourceElements statements = parseSourceElements(context, DontCheckForStrictMode); failIfFalse(statements, "Cannot parse the body of a switch default clause"); TreeClause result = context.createClause(0, statements); context.setStartOffset(result, startOffset); return result; } template template TreeStatement Parser::parseTryStatement(TreeBuilder& context) { ASSERT(match(TRY)); JSTokenLocation location(tokenLocation()); TreeStatement tryBlock = 0; TreeDestructuringPattern catchPattern = 0; TreeStatement catchBlock = 0; TreeStatement finallyBlock = 0; int firstLine = tokenLine(); next(); matchOrFail(OPENBRACE, "Expected a block statement as body of a try statement"); tryBlock = parseBlockStatement(context); failIfFalse(tryBlock, "Cannot parse the body of try block"); int lastLine = m_lastTokenEndPosition.line; VariableEnvironment catchEnvironment; if (match(CATCH)) { next(); if (match(OPENBRACE)) { catchBlock = parseBlockStatement(context); failIfFalse(catchBlock, "Unable to parse 'catch' block"); } else { handleProductionOrFail(OPENPAREN, "(", "start", "'catch' target"); DepthManager statementDepth(&m_statementDepth); m_statementDepth++; AutoPopScopeRef catchScope(this, pushScope()); catchScope->setIsLexicalScope(); catchScope->preventVarDeclarations(); const Identifier* ident = nullptr; if (matchSpecIdentifier()) { catchScope->setIsSimpleCatchParameterScope(); ident = m_token.m_data.ident; catchPattern = context.createBindingLocation(m_token.m_location, *ident, m_token.m_startPosition, m_token.m_endPosition, AssignmentContext::DeclarationStatement); next(); failIfTrueIfStrict(catchScope->declareLexicalVariable(ident, false) & DeclarationResult::InvalidStrictMode, "Cannot declare a catch variable named '", ident->impl(), "' in strict mode"); } else { catchPattern = parseDestructuringPattern(context, DestructuringKind::DestructureToCatchParameters, ExportType::NotExported); failIfFalse(catchPattern, "Cannot parse this destructuring pattern"); } handleProductionOrFail(CLOSEPAREN, ")", "end", "'catch' target"); matchOrFail(OPENBRACE, "Expected exception handler to be a block statement"); constexpr bool isCatchBlock = true; catchBlock = parseBlockStatement(context, isCatchBlock); failIfFalse(catchBlock, "Unable to parse 'catch' block"); catchEnvironment = catchScope->finalizeLexicalEnvironment(); RELEASE_ASSERT(!ident || (catchEnvironment.size() == 1 && catchEnvironment.contains(ident->impl()))); popScope(catchScope, TreeBuilder::NeedsFreeVariableInfo); } } if (match(FINALLY)) { next(); matchOrFail(OPENBRACE, "Expected block statement for finally body"); finallyBlock = parseBlockStatement(context); failIfFalse(finallyBlock, "Cannot parse finally body"); } failIfFalse(catchBlock || finallyBlock, "Try statements must have at least a catch or finally block"); return context.createTryStatement(location, tryBlock, catchPattern, catchBlock, finallyBlock, firstLine, lastLine, catchEnvironment); } template template TreeStatement Parser::parseDebuggerStatement(TreeBuilder& context) { ASSERT(match(DEBUGGER)); JSTokenLocation location(tokenLocation()); int startLine = tokenLine(); int endLine = startLine; next(); if (match(SEMICOLON)) startLine = tokenLine(); failIfFalse(autoSemiColon(), "Debugger keyword must be followed by a ';'"); return context.createDebugger(location, startLine, endLine); } template template TreeStatement Parser::parseBlockStatement(TreeBuilder& context, bool isCatchBlock) { ASSERT(match(OPENBRACE)); // We should treat the first block statement of the function (the body of the function) as the lexical // scope of the function itself, and not the lexical scope of a 'block' statement within the function. AutoCleanupLexicalScope lexicalScope; bool shouldPushLexicalScope = m_statementDepth > 0; if (shouldPushLexicalScope) { ScopeRef newScope = pushScope(); newScope->setIsLexicalScope(); newScope->preventVarDeclarations(); if (isCatchBlock) newScope->setIsCatchBlockScope(); lexicalScope.setIsValid(newScope, this); } JSTokenLocation location(tokenLocation()); int startOffset = m_token.m_data.offset; int start = tokenLine(); VariableEnvironment emptyEnvironment; DeclarationStacks::FunctionStack emptyFunctionStack; next(); if (match(CLOSEBRACE)) { int endOffset = m_token.m_data.offset; next(); TreeStatement result = context.createBlockStatement(location, 0, start, m_lastTokenEndPosition.line, shouldPushLexicalScope ? currentScope()->finalizeLexicalEnvironment() : emptyEnvironment, shouldPushLexicalScope ? currentScope()->takeFunctionDeclarations() : WTFMove(emptyFunctionStack)); context.setStartOffset(result, startOffset); context.setEndOffset(result, endOffset); if (shouldPushLexicalScope) popScope(lexicalScope, TreeBuilder::NeedsFreeVariableInfo); return result; } TreeSourceElements subtree = parseSourceElements(context, DontCheckForStrictMode); failIfFalse(subtree, "Cannot parse the body of the block statement"); matchOrFail(CLOSEBRACE, "Expected a closing '}' at the end of a block statement"); int endOffset = m_token.m_data.offset; next(); TreeStatement result = context.createBlockStatement(location, subtree, start, m_lastTokenEndPosition.line, shouldPushLexicalScope ? currentScope()->finalizeLexicalEnvironment() : emptyEnvironment, shouldPushLexicalScope ? currentScope()->takeFunctionDeclarations() : WTFMove(emptyFunctionStack)); context.setStartOffset(result, startOffset); context.setEndOffset(result, endOffset); if (shouldPushLexicalScope) popScope(lexicalScope, TreeBuilder::NeedsFreeVariableInfo); return result; } template template TreeStatement Parser::parseStatement(TreeBuilder& context, const Identifier*& directive, unsigned* directiveLiteralLength) { DepthManager statementDepth(&m_statementDepth); m_statementDepth++; int nonTrivialExpressionCount = 0; failIfStackOverflow(); TreeStatement result = 0; bool shouldSetEndOffset = true; bool shouldSetPauseLocation = false; bool parentAllowsFunctionDeclarationAsStatement = m_immediateParentAllowsFunctionDeclarationInStatement; m_immediateParentAllowsFunctionDeclarationInStatement = false; switch (m_token.m_type) { case OPENBRACE: result = parseBlockStatement(context); shouldSetEndOffset = false; break; case VAR: result = parseVariableDeclaration(context, DeclarationType::VarDeclaration); shouldSetPauseLocation = true; break; case FUNCTION: { result = parseFunctionDeclarationStatement(context, parentAllowsFunctionDeclarationAsStatement); break; } case SEMICOLON: { JSTokenLocation location(tokenLocation()); next(); result = context.createEmptyStatement(location); shouldSetPauseLocation = true; break; } case IF: result = parseIfStatement(context); break; case DO: result = parseDoWhileStatement(context); break; case WHILE: result = parseWhileStatement(context); break; case FOR: result = parseForStatement(context); break; case CONTINUE: result = parseContinueStatement(context); shouldSetPauseLocation = true; break; case BREAK: result = parseBreakStatement(context); shouldSetPauseLocation = true; break; case RETURN: result = parseReturnStatement(context); shouldSetPauseLocation = true; break; case WITH: result = parseWithStatement(context); break; case SWITCH: result = parseSwitchStatement(context); break; case THROW: result = parseThrowStatement(context); shouldSetPauseLocation = true; break; case TRY: result = parseTryStatement(context); break; case DEBUGGER: result = parseDebuggerStatement(context); shouldSetPauseLocation = true; break; case EOFTOK: case CASE: case CLOSEBRACE: case DEFAULT: // These tokens imply the end of a set of source elements return 0; case LET: { // https://tc39.es/ecma262/#sec-expression-statement // ExpressionStatement's lookahead includes `let [` sequence. SavePoint savePoint = createSavePoint(context); next(); failIfTrue(match(OPENBRACKET), "Cannot use lexical declaration in single-statement context"); restoreSavePoint(context, savePoint); if (!strictMode()) goto identcase; goto defaultCase; } case IDENT: if (UNLIKELY(*m_token.m_data.ident == m_vm.propertyNames->async && !m_token.m_data.escaped)) { SavePoint savePoint = createSavePoint(context); next(); failIfTrue(match(FUNCTION) && !m_lexer->hasLineTerminatorBeforeToken(), "Cannot use async function declaration in single-statement context"); restoreSavePoint(context, savePoint); } FALLTHROUGH; case AWAIT: case YIELD: { identcase: bool allowFunctionDeclarationAsStatement = false; result = parseExpressionOrLabelStatement(context, allowFunctionDeclarationAsStatement); shouldSetPauseLocation = !context.shouldSkipPauseLocation(result); break; } case STRING: directive = m_token.m_data.ident; if (directiveLiteralLength) *directiveLiteralLength = m_token.m_location.endOffset - m_token.m_location.startOffset; nonTrivialExpressionCount = m_parserState.nonTrivialExpressionCount; FALLTHROUGH; default: defaultCase: TreeStatement exprStatement = parseExpressionStatement(context); if (directive && nonTrivialExpressionCount != m_parserState.nonTrivialExpressionCount) directive = nullptr; result = exprStatement; shouldSetPauseLocation = true; break; } if (result) { if (shouldSetEndOffset) context.setEndOffset(result, m_lastTokenEndPosition.offset); if (shouldSetPauseLocation) recordPauseLocation(context.breakpointLocation(result)); } return result; } template template TreeStatement Parser::parseFunctionDeclarationStatement(TreeBuilder& context, bool parentAllowsFunctionDeclarationAsStatement) { semanticFailIfTrue(strictMode(), "Function declarations are only allowed inside blocks or switch statements in strict mode"); failIfFalse(parentAllowsFunctionDeclarationAsStatement, "Function declarations are only allowed inside block statements or at the top level of a program"); if (!currentScope()->isFunction() && !closestParentOrdinaryFunctionNonLexicalScope()->isEvalContext()) { // We only implement annex B.3.3 if we're in function mode or eval mode. Otherwise, we fall back // to hoisting behavior. // FIXME: https://bugs.webkit.org/show_bug.cgi?id=155813 DepthManager statementDepth(&m_statementDepth); m_statementDepth = 1; return parseFunctionDeclaration(context, FunctionDeclarationType::Statement); } // Any function declaration that isn't in a block is a syntax error unless it's // in an if/else statement. If it's in an if/else statement, we will magically // treat it as if the if/else statement is inside a block statement. // to the very top like a "var". For example: // function a() { // if (cond) function foo() { } // } // will be rewritten as: // function a() { // if (cond) { function foo() { } } // } AutoPopScopeRef blockScope(this, pushScope()); blockScope->setIsLexicalScope(); blockScope->preventVarDeclarations(); JSTokenLocation location(tokenLocation()); int start = tokenLine(); TreeStatement function = parseFunctionDeclaration(context, FunctionDeclarationType::Statement); propagateError(); failIfFalse(function, "Expected valid function statement after 'function' keyword"); TreeSourceElements sourceElements = context.createSourceElements(); context.appendStatement(sourceElements, function); TreeStatement result = context.createBlockStatement(location, sourceElements, start, m_lastTokenEndPosition.line, currentScope()->finalizeLexicalEnvironment(), currentScope()->takeFunctionDeclarations()); popScope(blockScope, TreeBuilder::NeedsFreeVariableInfo); return result; } template template bool Parser::parseFormalParameters(TreeBuilder& context, TreeFormalParameterList list, bool isArrowFunction, bool isMethod, unsigned& parameterCount) { #define failIfDuplicateIfViolation() \ if (duplicateParameter) {\ semanticFailIfTrue(hasDefaultParameterValues, "Duplicate parameter '", duplicateParameter->impl(), "' not allowed in function with default parameter values");\ semanticFailIfTrue(hasDestructuringPattern, "Duplicate parameter '", duplicateParameter->impl(), "' not allowed in function with destructuring parameters");\ semanticFailIfTrue(isRestParameter, "Duplicate parameter '", duplicateParameter->impl(), "' not allowed in function with a rest parameter");\ semanticFailIfTrue(isArrowFunction, "Duplicate parameter '", duplicateParameter->impl(), "' not allowed in an arrow function");\ semanticFailIfTrue(isMethod, "Duplicate parameter '", duplicateParameter->impl(), "' not allowed in a method");\ } bool hasDefaultParameterValues = false; bool hasDestructuringPattern = false; bool isRestParameter = false; const Identifier* duplicateParameter = nullptr; unsigned restParameterStart = 0; do { TreeDestructuringPattern parameter = 0; TreeExpression defaultValue = 0; if (UNLIKELY(match(CLOSEPAREN))) break; if (match(DOTDOTDOT)) { next(); semanticFailIfTrue(!m_parserState.allowAwait && match(AWAIT), "Cannot use 'await' as a parameter name in an async function"); TreeDestructuringPattern destructuringPattern = parseDestructuringPattern(context, DestructuringKind::DestructureToParameters, ExportType::NotExported, &duplicateParameter, &hasDestructuringPattern); propagateError(); parameter = context.createRestParameter(destructuringPattern, restParameterStart); failIfTrue(match(COMMA), "Rest parameter should be the last parameter in a function declaration"); // Let's have a good error message for this common case. isRestParameter = true; } else parameter = parseDestructuringPattern(context, DestructuringKind::DestructureToParameters, ExportType::NotExported, &duplicateParameter, &hasDestructuringPattern); failIfFalse(parameter, "Cannot parse parameter pattern"); if (!isRestParameter) { defaultValue = parseDefaultValueForDestructuringPattern(context); if (defaultValue) hasDefaultParameterValues = true; } propagateError(); failIfDuplicateIfViolation(); if (isRestParameter || defaultValue || hasDestructuringPattern) currentScope()->setHasNonSimpleParameterList(); context.appendParameter(list, parameter, defaultValue); if (!isRestParameter) { restParameterStart++; if (!hasDefaultParameterValues) parameterCount++; } } while (!isRestParameter && consume(COMMA)); return true; #undef failIfDuplicateIfViolation } template template TreeFunctionBody Parser::parseFunctionBody( TreeBuilder& context, SyntaxChecker& syntaxChecker, const JSTokenLocation& startLocation, int startColumn, int functionKeywordStart, int functionNameStart, int parametersStart, ConstructorKind constructorKind, SuperBinding superBinding, FunctionBodyType bodyType, unsigned parameterCount, SourceParseMode parseMode) { SetForScope overrideParsingClassFieldInitializer(m_parserState.isParsingClassFieldInitializer, bodyType == StandardFunctionBodyBlock ? false : m_parserState.isParsingClassFieldInitializer); bool isArrowFunctionBodyExpression = bodyType == ArrowFunctionBodyExpression; if (!isArrowFunctionBodyExpression) { next(); if (match(CLOSEBRACE)) { unsigned endColumn = tokenColumn(); SuperBinding functionSuperBinding = adjustSuperBindingForBaseConstructor(constructorKind, superBinding, currentScope()); return context.createFunctionMetadata(startLocation, tokenLocation(), startColumn, endColumn, functionKeywordStart, functionNameStart, parametersStart, strictMode(), constructorKind, functionSuperBinding, parameterCount, parseMode, isArrowFunctionBodyExpression); } } DepthManager statementDepth(&m_statementDepth); m_statementDepth = 0; if (bodyType == ArrowFunctionBodyExpression) { if (m_debuggerParseData) failIfFalse(parseArrowFunctionSingleExpressionBodySourceElements(context), "Cannot parse body of this arrow function"); else failIfFalse(parseArrowFunctionSingleExpressionBodySourceElements(syntaxChecker), "Cannot parse body of this arrow function"); } else { if (m_debuggerParseData) failIfFalse(parseSourceElements(context, CheckForStrictMode), bodyType == StandardFunctionBodyBlock ? "Cannot parse body of this function" : "Cannot parse body of this arrow function"); else failIfFalse(parseSourceElements(syntaxChecker, CheckForStrictMode), bodyType == StandardFunctionBodyBlock ? "Cannot parse body of this function" : "Cannot parse body of this arrow function"); } unsigned endColumn = tokenColumn(); SuperBinding functionSuperBinding = adjustSuperBindingForBaseConstructor(constructorKind, superBinding, currentScope()); return context.createFunctionMetadata(startLocation, tokenLocation(), startColumn, endColumn, functionKeywordStart, functionNameStart, parametersStart, strictMode(), constructorKind, functionSuperBinding, parameterCount, parseMode, isArrowFunctionBodyExpression); } static const char* stringArticleForFunctionMode(SourceParseMode mode) { switch (mode) { case SourceParseMode::GetterMode: case SourceParseMode::SetterMode: case SourceParseMode::NormalFunctionMode: case SourceParseMode::MethodMode: case SourceParseMode::GeneratorBodyMode: case SourceParseMode::GeneratorWrapperFunctionMode: case SourceParseMode::GeneratorWrapperMethodMode: return "a "; case SourceParseMode::ArrowFunctionMode: case SourceParseMode::AsyncFunctionMode: case SourceParseMode::AsyncFunctionBodyMode: case SourceParseMode::AsyncMethodMode: case SourceParseMode::AsyncArrowFunctionBodyMode: case SourceParseMode::AsyncArrowFunctionMode: case SourceParseMode::AsyncGeneratorWrapperFunctionMode: case SourceParseMode::AsyncGeneratorBodyMode: case SourceParseMode::AsyncGeneratorWrapperMethodMode: return "an "; case SourceParseMode::ProgramMode: case SourceParseMode::ModuleAnalyzeMode: case SourceParseMode::ModuleEvaluateMode: case SourceParseMode::ClassFieldInitializerMode: RELEASE_ASSERT_NOT_REACHED(); return ""; } RELEASE_ASSERT_NOT_REACHED(); return nullptr; } static const char* stringForFunctionMode(SourceParseMode mode) { switch (mode) { case SourceParseMode::GetterMode: return "getter"; case SourceParseMode::SetterMode: return "setter"; case SourceParseMode::NormalFunctionMode: return "function"; case SourceParseMode::MethodMode: return "method"; case SourceParseMode::GeneratorBodyMode: return "generator"; case SourceParseMode::GeneratorWrapperFunctionMode: case SourceParseMode::GeneratorWrapperMethodMode: return "generator function"; case SourceParseMode::ArrowFunctionMode: return "arrow function"; case SourceParseMode::AsyncFunctionMode: case SourceParseMode::AsyncFunctionBodyMode: return "async function"; case SourceParseMode::AsyncMethodMode: return "async method"; case SourceParseMode::AsyncArrowFunctionBodyMode: case SourceParseMode::AsyncArrowFunctionMode: return "async arrow function"; case SourceParseMode::AsyncGeneratorWrapperFunctionMode: case SourceParseMode::AsyncGeneratorBodyMode: return "async generator function"; case SourceParseMode::AsyncGeneratorWrapperMethodMode: return "async generator method"; case SourceParseMode::ProgramMode: case SourceParseMode::ModuleAnalyzeMode: case SourceParseMode::ModuleEvaluateMode: case SourceParseMode::ClassFieldInitializerMode: RELEASE_ASSERT_NOT_REACHED(); return ""; } RELEASE_ASSERT_NOT_REACHED(); return nullptr; } template template typename TreeBuilder::FormalParameterList Parser::parseFunctionParameters(TreeBuilder& context, SourceParseMode mode, FunctionInfoType& functionInfo) { RELEASE_ASSERT(!(SourceParseModeSet(SourceParseMode::ProgramMode, SourceParseMode::ModuleAnalyzeMode, SourceParseMode::ModuleEvaluateMode).contains(mode))); TreeFormalParameterList parameterList = context.createFormalParameterList(); SetForScope functionParsePhasePoisoner(m_parserState.functionParsePhase, FunctionParsePhase::Parameters); if (UNLIKELY((SourceParseModeSet(SourceParseMode::ArrowFunctionMode, SourceParseMode::AsyncArrowFunctionMode).contains(mode)))) { if (!matchSpecIdentifier() && !match(OPENPAREN)) { semanticFailureDueToKeyword(stringForFunctionMode(mode), " name"); failWithMessage("Expected an arrow function input parameter"); } else { if (match(OPENPAREN)) { next(); if (match(CLOSEPAREN)) { functionInfo.parameterCount = 0; } else { bool isArrowFunction = true; bool isMethod = false; failIfFalse(parseFormalParameters(context, parameterList, isArrowFunction, isMethod, functionInfo.parameterCount), "Cannot parse parameters for this ", stringForFunctionMode(mode)); } consumeOrFail(CLOSEPAREN, "Expected a ')' or a ',' after a parameter declaration"); } else { functionInfo.parameterCount = 1; auto parameter = parseDestructuringPattern(context, DestructuringKind::DestructureToParameters, ExportType::NotExported); failIfFalse(parameter, "Cannot parse parameter pattern"); context.appendParameter(parameterList, parameter, 0); } } return parameterList; } if (!consume(OPENPAREN)) { semanticFailureDueToKeyword(stringForFunctionMode(mode), " name"); failWithMessage("Expected an opening '(' before a ", stringForFunctionMode(mode), "'s parameter list"); } if (mode == SourceParseMode::GetterMode) { consumeOrFail(CLOSEPAREN, "getter functions must have no parameters"); functionInfo.parameterCount = 0; } else if (mode == SourceParseMode::SetterMode) { failIfTrue(match(CLOSEPAREN), "setter functions must have one parameter"); const Identifier* duplicateParameter = nullptr; bool hasDestructuringPattern = false; auto parameter = parseDestructuringPattern(context, DestructuringKind::DestructureToParameters, ExportType::NotExported, &duplicateParameter, &hasDestructuringPattern); failIfFalse(parameter, "setter functions must have one parameter"); auto defaultValue = parseDefaultValueForDestructuringPattern(context); propagateError(); if (defaultValue || hasDestructuringPattern) { semanticFailIfTrue(duplicateParameter, "Duplicate parameter '", duplicateParameter->impl(), "' not allowed in function with non-simple parameter list"); currentScope()->setHasNonSimpleParameterList(); } context.appendParameter(parameterList, parameter, defaultValue); functionInfo.parameterCount = defaultValue ? 0 : 1; failIfTrue(match(COMMA), "setter functions must have one parameter"); consumeOrFail(CLOSEPAREN, "Expected a ')' after a parameter declaration"); } else { if (match(CLOSEPAREN)) { functionInfo.parameterCount = 0; } else { bool isArrowFunction = false; bool isMethod = isMethodParseMode(mode); failIfFalse(parseFormalParameters(context, parameterList, isArrowFunction, isMethod, functionInfo.parameterCount), "Cannot parse parameters for this ", stringForFunctionMode(mode)); } consumeOrFail(CLOSEPAREN, "Expected a ')' or a ',' after a parameter declaration"); } return parameterList; } template template typename TreeBuilder::FormalParameterList Parser::createGeneratorParameters(TreeBuilder& context, unsigned& parameterCount) { auto parameters = context.createFormalParameterList(); JSTokenLocation location(tokenLocation()); JSTextPosition position = tokenStartPosition(); auto addParameter = [&](const Identifier& name) { declareParameter(&name); auto binding = context.createBindingLocation(location, name, position, position, AssignmentContext::DeclarationStatement); context.appendParameter(parameters, binding, 0); ++parameterCount; }; // @generator addParameter(m_vm.propertyNames->generatorPrivateName); // @generatorState addParameter(m_vm.propertyNames->generatorStatePrivateName); // @generatorValue addParameter(m_vm.propertyNames->generatorValuePrivateName); // @generatorResumeMode addParameter(m_vm.propertyNames->generatorResumeModePrivateName); // @generatorFrame addParameter(m_vm.propertyNames->generatorFramePrivateName); return parameters; } template template bool Parser::parseFunctionInfo(TreeBuilder& context, FunctionNameRequirements requirements, SourceParseMode mode, bool nameIsInContainingScope, ConstructorKind constructorKind, SuperBinding expectedSuperBinding, int functionKeywordStart, ParserFunctionInfo& functionInfo, FunctionDefinitionType functionDefinitionType, Optional functionConstructorParametersEndPosition) { RELEASE_ASSERT(isFunctionParseMode(mode)); ScopeRef parentScope = currentScope(); bool isDisallowedAwaitFunctionName = isDisallowedIdentifierAwait(m_token); const char* isDisallowedAwaitFunctionNameReason = isDisallowedAwaitFunctionName ? disallowedIdentifierAwaitReason() : nullptr; AutoPopScopeRef functionScope(this, pushScope()); functionScope->setSourceParseMode(mode); functionScope->setExpectedSuperBinding(expectedSuperBinding); functionScope->setConstructorKind(constructorKind); SetForScope functionParsePhasePoisoner(m_parserState.functionParsePhase, FunctionParsePhase::Body); int functionNameStart = m_token.m_location.startOffset; const Identifier* lastFunctionName = m_parserState.lastFunctionName; m_parserState.lastFunctionName = nullptr; int parametersStart = -1; JSTokenLocation startLocation; int startColumn = -1; FunctionBodyType functionBodyType; auto loadCachedFunction = [&] () -> bool { if (UNLIKELY(!Options::useSourceProviderCache())) return false; if (UNLIKELY(m_debuggerParseData)) return false; ASSERT(parametersStart != -1); ASSERT(startColumn != -1); // If we know about this function already, we can use the cached info and skip the parser to the end of the function. if (const SourceProviderCacheItem* cachedInfo = TreeBuilder::CanUseFunctionCache ? findCachedFunctionInfo(parametersStart) : nullptr) { // If we're in a strict context, the cached function info must say it was strict too. ASSERT(!strictMode() || cachedInfo->strictMode); JSTokenLocation endLocation; ConstructorKind constructorKind = static_cast(cachedInfo->constructorKind); SuperBinding expectedSuperBinding = static_cast(cachedInfo->expectedSuperBinding); endLocation.line = cachedInfo->lastTokenLine; endLocation.startOffset = cachedInfo->lastTokenStartOffset; endLocation.lineStartOffset = cachedInfo->lastTokenLineStartOffset; ASSERT(endLocation.startOffset >= endLocation.lineStartOffset); bool endColumnIsOnStartLine = endLocation.line == functionInfo.startLine; unsigned currentLineStartOffset = m_lexer->currentLineStartOffset(); unsigned bodyEndColumn = endColumnIsOnStartLine ? endLocation.startOffset - currentLineStartOffset : endLocation.startOffset - endLocation.lineStartOffset; ASSERT(endLocation.startOffset >= endLocation.lineStartOffset); FunctionBodyType functionBodyType; if (UNLIKELY(SourceParseModeSet(SourceParseMode::ArrowFunctionMode, SourceParseMode::AsyncArrowFunctionMode).contains(mode))) functionBodyType = cachedInfo->isBodyArrowExpression ? ArrowFunctionBodyExpression : ArrowFunctionBodyBlock; else functionBodyType = StandardFunctionBodyBlock; SuperBinding functionSuperBinding = adjustSuperBindingForBaseConstructor(constructorKind, expectedSuperBinding, cachedInfo->needsSuperBinding, cachedInfo->usesEval, cachedInfo->innerArrowFunctionFeatures); functionInfo.body = context.createFunctionMetadata( startLocation, endLocation, startColumn, bodyEndColumn, functionKeywordStart, functionNameStart, parametersStart, cachedInfo->strictMode, constructorKind, functionSuperBinding, cachedInfo->parameterCount, mode, functionBodyType == ArrowFunctionBodyExpression); functionInfo.endOffset = cachedInfo->endFunctionOffset; functionInfo.parameterCount = cachedInfo->parameterCount; functionScope->restoreFromSourceProviderCache(cachedInfo); popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo); m_token = cachedInfo->endFunctionToken(); if (endColumnIsOnStartLine) m_token.m_location.lineStartOffset = currentLineStartOffset; m_lexer->setOffset(m_token.m_location.endOffset, m_token.m_location.lineStartOffset); m_lexer->setLineNumber(m_token.m_location.line); switch (functionBodyType) { case ArrowFunctionBodyExpression: next(); context.setEndOffset(functionInfo.body, m_lexer->currentOffset()); break; case ArrowFunctionBodyBlock: case StandardFunctionBodyBlock: context.setEndOffset(functionInfo.body, m_lexer->currentOffset()); next(); break; } functionInfo.endLine = m_lastTokenEndPosition.line; return true; } return false; }; SyntaxChecker syntaxChecker(const_cast(m_vm), m_lexer.get()); if (UNLIKELY((SourceParseModeSet(SourceParseMode::ArrowFunctionMode, SourceParseMode::AsyncArrowFunctionMode).contains(mode)))) { startLocation = tokenLocation(); functionInfo.startLine = tokenLine(); startColumn = tokenColumn(); parametersStart = m_token.m_location.startOffset; functionInfo.startOffset = parametersStart; functionInfo.parametersStartColumn = startColumn; if (loadCachedFunction()) return true; { // Parse formal parameters with [+Yield] parameterization, in order to ban YieldExpressions // in ArrowFormalParameters, per ES6 #sec-arrow-function-definitions-static-semantics-early-errors. Scope::MaybeParseAsGeneratorForScope parseAsGenerator(functionScope, parentScope->isGenerator()); SetForScope overrideAllowAwait(m_parserState.allowAwait, !parentScope->isAsyncFunction() && !isAsyncFunctionParseMode(mode)); parseFunctionParameters(syntaxChecker, mode, functionInfo); propagateError(); } matchOrFail(ARROWFUNCTION, "Expected a '=>' after arrow function parameter declaration"); if (m_lexer->hasLineTerminatorBeforeToken()) failDueToUnexpectedToken(); ASSERT(constructorKind == ConstructorKind::None); // Check if arrow body start with {. If it true it mean that arrow function is Fat arrow function // and we need use common approach to parse function body next(); functionBodyType = match(OPENBRACE) ? ArrowFunctionBodyBlock : ArrowFunctionBodyExpression; } else { // http://ecma-international.org/ecma-262/6.0/#sec-function-definitions // FunctionExpression : // function BindingIdentifieropt ( FormalParameters ) { FunctionBody } // // FunctionDeclaration[Yield, Default] : // function BindingIdentifier[?Yield] ( FormalParameters ) { FunctionBody } // [+Default] function ( FormalParameters ) { FunctionBody } // // GeneratorDeclaration[Yield, Default] : // function * BindingIdentifier[?Yield] ( FormalParameters[Yield] ) { GeneratorBody } // [+Default] function * ( FormalParameters[Yield] ) { GeneratorBody } // // GeneratorExpression : // function * BindingIdentifier[Yield]opt ( FormalParameters[Yield] ) { GeneratorBody } // // The name of FunctionExpression and AsyncFunctionExpression can accept "yield" even in the context of generator. bool upperScopeIsGenerator = false; if (!(functionDefinitionType == FunctionDefinitionType::Expression && SourceParseModeSet(SourceParseMode::NormalFunctionMode, SourceParseMode::AsyncFunctionMode).contains(mode))) upperScopeIsGenerator = upperScope(1)->isGenerator(); if (requirements != FunctionNameRequirements::Unnamed) { ASSERT_WITH_MESSAGE(!(requirements == FunctionNameRequirements::None && !functionInfo.name), "When specifying FunctionNameRequirements::None, we need to initialize functionInfo.name with the default value in the caller side."); if (matchSpecIdentifier(upperScopeIsGenerator)) { functionInfo.name = m_token.m_data.ident; m_parserState.lastFunctionName = functionInfo.name; if (UNLIKELY(isDisallowedAwaitFunctionName)) semanticFailIfTrue(functionDefinitionType == FunctionDefinitionType::Declaration || isAsyncFunctionOrAsyncGeneratorWrapperParseMode(mode), "Cannot declare function named 'await' ", isDisallowedAwaitFunctionNameReason); else if (isAsyncFunctionOrAsyncGeneratorWrapperParseMode(mode) && match(AWAIT) && functionDefinitionType == FunctionDefinitionType::Expression) semanticFail("Cannot declare ", stringForFunctionMode(mode), " named 'await'"); else if (isGeneratorWrapperParseMode(mode) && match(YIELD) && functionDefinitionType == FunctionDefinitionType::Expression) semanticFail("Cannot declare generator function named 'yield'"); next(); if (!nameIsInContainingScope) failIfTrueIfStrict(functionScope->declareCallee(functionInfo.name) & DeclarationResult::InvalidStrictMode, "'", functionInfo.name->impl(), "' is not a valid ", stringForFunctionMode(mode), " name in strict mode"); } else if (requirements == FunctionNameRequirements::Named) { if (match(OPENPAREN)) { semanticFailIfTrue(mode == SourceParseMode::NormalFunctionMode, "Function statements must have a name"); semanticFailIfTrue(mode == SourceParseMode::AsyncFunctionMode, "Async function statements must have a name"); } semanticFailureDueToKeyword(stringForFunctionMode(mode), " name"); failDueToUnexpectedToken(); return false; } ASSERT(functionInfo.name); } startLocation = tokenLocation(); functionInfo.startLine = tokenLine(); startColumn = tokenColumn(); functionInfo.parametersStartColumn = startColumn; parametersStart = m_token.m_location.startOffset; functionInfo.startOffset = parametersStart; if (loadCachedFunction()) return true; { SetForScope overrideAllowAwait(m_parserState.allowAwait, !isAsyncFunctionParseMode(mode)); parseFunctionParameters(syntaxChecker, mode, functionInfo); propagateError(); } matchOrFail(OPENBRACE, "Expected an opening '{' at the start of a ", stringForFunctionMode(mode), " body"); // If the code is invoked from function constructor, we need to ensure that parameters are only composed by the string offered as parameters. if (UNLIKELY(functionConstructorParametersEndPosition)) semanticFailIfFalse(lastTokenEndPosition().offset == *functionConstructorParametersEndPosition, "Parameters should match arguments offered as parameters in Function constructor"); // BytecodeGenerator emits code to throw TypeError when a class constructor is "call"ed. // Set ConstructorKind to None for non-constructor methods of classes. if (parentScope->isGlobalCodeScope() && m_defaultConstructorKindForTopLevelFunction != ConstructorKind::None) { constructorKind = m_defaultConstructorKindForTopLevelFunction; expectedSuperBinding = m_defaultConstructorKindForTopLevelFunction == ConstructorKind::Extends ? SuperBinding::Needed : SuperBinding::NotNeeded; } functionBodyType = StandardFunctionBodyBlock; } functionScope->setConstructorKind(constructorKind); functionScope->setExpectedSuperBinding(expectedSuperBinding); m_parserState.lastFunctionName = lastFunctionName; ParserState oldState = internalSaveParserState(context); // FIXME: https://bugs.webkit.org/show_bug.cgi?id=156962 // This loop collects the set of capture candidates that aren't // part of the set of this function's declared parameters. We will // figure out which parameters are captured for this function when // we actually generate code for it. For now, we just propagate to // our parent scopes which variables we might have closed over that // belong to them. This is necessary for correctness when using // the source provider cache because we can't close over a variable // that we don't claim to close over. The source provider cache must // know this information to properly cache this function. // This might work itself out nicer if we declared a different // Scope struct for the parameters (because they are indeed implemented // as their own scope). UniquedStringImplPtrSet nonLocalCapturesFromParameterExpressions; functionScope->forEachUsedVariable([&] (UniquedStringImpl* impl) { if (!functionScope->hasDeclaredParameter(impl)) { nonLocalCapturesFromParameterExpressions.add(impl); if (TreeBuilder::NeedsFreeVariableInfo) parentScope->addClosedVariableCandidateUnconditionally(impl); } }); auto performParsingFunctionBody = [&] { return parseFunctionBody(context, syntaxChecker, startLocation, startColumn, functionKeywordStart, functionNameStart, parametersStart, constructorKind, expectedSuperBinding, functionBodyType, functionInfo.parameterCount, mode); }; if (isGeneratorOrAsyncFunctionWrapperParseMode(mode)) { AutoPopScopeRef generatorBodyScope(this, pushScope()); SourceParseMode innerParseMode = SourceParseMode::GeneratorBodyMode; if (isAsyncFunctionOrAsyncGeneratorWrapperParseMode(mode)) innerParseMode = getAsynFunctionBodyParseMode(mode); generatorBodyScope->setSourceParseMode(innerParseMode); generatorBodyScope->setConstructorKind(ConstructorKind::None); generatorBodyScope->setExpectedSuperBinding(expectedSuperBinding); // Disallow 'use strict' directives in the implicit inner function if // needed. if (functionScope->hasNonSimpleParameterList()) generatorBodyScope->setHasNonSimpleParameterList(); functionInfo.body = performParsingFunctionBody(); // When a generator has a "use strict" directive, a generator function wrapping it should be strict mode. if (generatorBodyScope->strictMode()) functionScope->setStrictMode(); popScope(generatorBodyScope, TreeBuilder::NeedsFreeVariableInfo); } else functionInfo.body = performParsingFunctionBody(); restoreParserState(context, oldState); failIfFalse(functionInfo.body, "Cannot parse the body of this ", stringForFunctionMode(mode)); context.setEndOffset(functionInfo.body, m_lexer->currentOffset()); if (functionScope->strictMode() && requirements != FunctionNameRequirements::Unnamed) { ASSERT(functionInfo.name); RELEASE_ASSERT(SourceParseModeSet(SourceParseMode::NormalFunctionMode, SourceParseMode::MethodMode, SourceParseMode::ArrowFunctionMode, SourceParseMode::GeneratorBodyMode, SourceParseMode::GeneratorWrapperFunctionMode).contains(mode) || isAsyncFunctionOrAsyncGeneratorWrapperParseMode(mode)); semanticFailIfTrue(m_vm.propertyNames->arguments == *functionInfo.name, "'", functionInfo.name->impl(), "' is not a valid function name in strict mode"); semanticFailIfTrue(m_vm.propertyNames->eval == *functionInfo.name, "'", functionInfo.name->impl(), "' is not a valid function name in strict mode"); } JSTokenLocation location = JSTokenLocation(m_token.m_location); functionInfo.endOffset = m_token.m_data.offset; if (functionBodyType == ArrowFunctionBodyExpression) { location = locationBeforeLastToken(); functionInfo.endOffset = location.endOffset; } else { recordFunctionEntryLocation(JSTextPosition(startLocation.line, startLocation.startOffset, startLocation.lineStartOffset)); recordFunctionLeaveLocation(JSTextPosition(location.line, location.startOffset, location.lineStartOffset)); } // Cache the tokenizer state and the function scope the first time the function is parsed. // Any future reparsing can then skip the function. // For arrow function is 8 = x=>x + 4 symbols; // For ordinary function is 16 = function(){} + 4 symbols const int minimumSourceLengthToCache = functionBodyType == StandardFunctionBodyBlock ? 16 : 8; std::unique_ptr newInfo; int sourceLength = functionInfo.endOffset - functionInfo.startOffset; if (TreeBuilder::CanUseFunctionCache && m_functionCache && sourceLength > minimumSourceLengthToCache) { SourceProviderCacheItemCreationParameters parameters; parameters.endFunctionOffset = functionInfo.endOffset; parameters.lastTokenLine = location.line; parameters.lastTokenStartOffset = location.startOffset; parameters.lastTokenEndOffset = location.endOffset; parameters.lastTokenLineStartOffset = location.lineStartOffset; parameters.parameterCount = functionInfo.parameterCount; parameters.constructorKind = constructorKind; parameters.expectedSuperBinding = expectedSuperBinding; if (functionBodyType == ArrowFunctionBodyExpression) { parameters.isBodyArrowExpression = true; parameters.tokenType = m_token.m_type; } functionScope->fillParametersForSourceProviderCache(parameters, nonLocalCapturesFromParameterExpressions); newInfo = SourceProviderCacheItem::create(parameters); } bool functionScopeWasStrictMode = functionScope->strictMode(); popScope(functionScope, TreeBuilder::NeedsFreeVariableInfo); if (functionBodyType != ArrowFunctionBodyExpression) { matchOrFail(CLOSEBRACE, "Expected a closing '}' after a ", stringForFunctionMode(mode), " body"); next(); } else { // We need to lex the last token again because the last token is lexed under the different context because of the following possibilities. // 1. which may have different strict mode. // 2. which may not build strings for tokens. // But (1) is not possible because we do not recognize the string literal in ArrowFunctionBodyExpression as directive and this is correct in terms of the spec (`value => "use strict"`). // So we only check TreeBuilder's type here. ASSERT_UNUSED(functionScopeWasStrictMode, functionScopeWasStrictMode == currentScope()->strictMode()); if (!std::is_same::value) lexCurrentTokenAgainUnderCurrentContext(context); } if (newInfo) m_functionCache->add(functionInfo.startOffset, WTFMove(newInfo)); functionInfo.endLine = m_lastTokenEndPosition.line; return true; } static NO_RETURN_DUE_TO_CRASH FunctionMetadataNode* getMetadata(ParserFunctionInfo&) { RELEASE_ASSERT_NOT_REACHED(); } static FunctionMetadataNode* getMetadata(ParserFunctionInfo& info) { return info.body; } template template TreeStatement Parser::parseFunctionDeclaration(TreeBuilder& context, FunctionDeclarationType declarationType, ExportType exportType, DeclarationDefaultContext declarationDefaultContext, Optional functionConstructorParametersEndPosition) { ASSERT(match(FUNCTION)); JSTokenLocation location(tokenLocation()); unsigned functionKeywordStart = tokenStart(); next(); SourceParseMode parseMode = SourceParseMode::NormalFunctionMode; if (match(TIMES)) { failIfTrue(declarationType == FunctionDeclarationType::Statement, "Cannot use generator function declaration in single-statement context"); next(); parseMode = SourceParseMode::GeneratorWrapperFunctionMode; } ParserFunctionInfo functionInfo; FunctionNameRequirements requirements = FunctionNameRequirements::Named; if (declarationDefaultContext == DeclarationDefaultContext::ExportDefault) { // Under the "export default" context, function declaration does not require the function name. // // ExportDeclaration: // ... // export default HoistableDeclaration[~Yield, +Default] // ... // // HoistableDeclaration[Yield, Default]: // FunctionDeclaration[?Yield, ?Default] // GeneratorDeclaration[?Yield, ?Default] // // FunctionDeclaration[Yield, Default]: // ... // [+Default] function ( FormalParameters[~Yield] ) { FunctionBody[~Yield] } // // GeneratorDeclaration[Yield, Default]: // ... // [+Default] function * ( FormalParameters[+Yield] ) { GeneratorBody } // // In this case, we use "*default*" as this function declaration's name. requirements = FunctionNameRequirements::None; functionInfo.name = &m_vm.propertyNames->starDefaultPrivateName; } failIfFalse((parseFunctionInfo(context, requirements, parseMode, true, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Declaration, functionConstructorParametersEndPosition)), "Cannot parse this function"); ASSERT(functionInfo.name); std::pair functionDeclaration = declareFunction(functionInfo.name); DeclarationResultMask declarationResult = functionDeclaration.first; failIfTrueIfStrict(declarationResult & DeclarationResult::InvalidStrictMode, "Cannot declare a function named '", functionInfo.name->impl(), "' in strict mode"); if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration) internalFailWithMessage(false, "Cannot declare a function that shadows a let/const/class/function variable '", functionInfo.name->impl(), "' in strict mode"); if (exportType == ExportType::Exported) { ASSERT_WITH_MESSAGE(declarationDefaultContext != DeclarationDefaultContext::ExportDefault, "Export default case will export the name and binding in the caller."); semanticFailIfFalse(exportName(*functionInfo.name), "Cannot export a duplicate function name: '", functionInfo.name->impl(), "'"); m_moduleScopeData->exportBinding(*functionInfo.name); } TreeStatement result = context.createFuncDeclStatement(location, functionInfo); if (TreeBuilder::CreatesAST) functionDeclaration.second->appendFunction(getMetadata(functionInfo)); return result; } template template TreeStatement Parser::parseAsyncFunctionDeclaration(TreeBuilder& context, ExportType exportType, DeclarationDefaultContext declarationDefaultContext, Optional functionConstructorParametersEndPosition) { ASSERT(match(FUNCTION)); JSTokenLocation location(tokenLocation()); unsigned functionKeywordStart = tokenStart(); next(); ParserFunctionInfo functionInfo; SourceParseMode parseMode = SourceParseMode::AsyncFunctionMode; if (consume(TIMES)) parseMode = SourceParseMode::AsyncGeneratorWrapperFunctionMode; FunctionNameRequirements requirements = FunctionNameRequirements::Named; if (declarationDefaultContext == DeclarationDefaultContext::ExportDefault) { // Under the "export default" context, function declaration does not require the function name. // // ExportDeclaration: // ... // export default HoistableDeclaration[~Yield, +Default] // ... // // HoistableDeclaration[Yield, Default]: // FunctionDeclaration[?Yield, ?Default] // GeneratorDeclaration[?Yield, ?Default] // // FunctionDeclaration[Yield, Default]: // ... // [+Default] function ( FormalParameters[~Yield] ) { FunctionBody[~Yield] } // // GeneratorDeclaration[Yield, Default]: // ... // [+Default] function * ( FormalParameters[+Yield] ) { GeneratorBody } // // In this case, we use "*default*" as this function declaration's name. requirements = FunctionNameRequirements::None; functionInfo.name = &m_vm.propertyNames->starDefaultPrivateName; } failIfFalse((parseFunctionInfo(context, requirements, parseMode, true, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Declaration, functionConstructorParametersEndPosition)), "Cannot parse this async function"); failIfFalse(functionInfo.name, "Async function statements must have a name"); std::pair functionDeclaration = declareFunction(functionInfo.name); DeclarationResultMask declarationResult = functionDeclaration.first; failIfTrueIfStrict(declarationResult & DeclarationResult::InvalidStrictMode, "Cannot declare an async function named '", functionInfo.name->impl(), "' in strict mode"); if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration) internalFailWithMessage(false, "Cannot declare an async function that shadows a let/const/class/function variable '", functionInfo.name->impl(), "' in strict mode"); if (exportType == ExportType::Exported) { semanticFailIfFalse(exportName(*functionInfo.name), "Cannot export a duplicate function name: '", functionInfo.name->impl(), "'"); m_moduleScopeData->exportBinding(*functionInfo.name); } TreeStatement result = context.createFuncDeclStatement(location, functionInfo); if (TreeBuilder::CreatesAST) functionDeclaration.second->appendFunction(getMetadata(functionInfo)); return result; } template template TreeStatement Parser::parseClassDeclaration(TreeBuilder& context, ExportType exportType, DeclarationDefaultContext declarationDefaultContext) { ASSERT(match(CLASSTOKEN)); JSTokenLocation location(tokenLocation()); JSTextPosition classStart = tokenStartPosition(); unsigned classStartLine = tokenLine(); ParserClassInfo info; FunctionNameRequirements requirements = FunctionNameRequirements::Named; if (declarationDefaultContext == DeclarationDefaultContext::ExportDefault) { // Under the "export default" context, class declaration does not require the class name. // // ExportDeclaration: // ... // export default ClassDeclaration[~Yield, +Default] // ... // // ClassDeclaration[Yield, Default]: // ... // [+Default] class ClassTail[?Yield] // // In this case, we use "*default*" as this class declaration's name. requirements = FunctionNameRequirements::None; info.className = &m_vm.propertyNames->starDefaultPrivateName; } TreeClassExpression classExpr = parseClass(context, requirements, info); failIfFalse(classExpr, "Failed to parse class"); ASSERT(info.className); DeclarationResultMask declarationResult = declareVariable(info.className, DeclarationType::LetDeclaration); if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration) internalFailWithMessage(false, "Cannot declare a class twice: '", info.className->impl(), "'"); if (exportType == ExportType::Exported) { ASSERT_WITH_MESSAGE(declarationDefaultContext != DeclarationDefaultContext::ExportDefault, "Export default case will export the name and binding in the caller."); semanticFailIfFalse(exportName(*info.className), "Cannot export a duplicate class name: '", info.className->impl(), "'"); m_moduleScopeData->exportBinding(*info.className); } JSTextPosition classEnd = lastTokenEndPosition(); unsigned classEndLine = tokenLine(); return context.createClassDeclStatement(location, classExpr, classStart, classEnd, classStartLine, classEndLine); } static constexpr ASCIILiteral instanceComputedNamePrefix { "instanceComputedName"_s }; static constexpr ASCIILiteral staticComputedNamePrefix { "staticComputedName"_s }; template template TreeClassExpression Parser::parseClass(TreeBuilder& context, FunctionNameRequirements requirements, ParserClassInfo& info) { ASSERT(match(CLASSTOKEN)); JSTokenLocation location(tokenLocation()); info.startLine = location.line; info.startColumn = tokenColumn(); info.startOffset = location.startOffset; next(); AutoPopScopeRef classScope(this, pushScope()); classScope->setIsLexicalScope(); classScope->preventVarDeclarations(); classScope->setStrictMode(); ASSERT_WITH_MESSAGE(requirements != FunctionNameRequirements::Unnamed, "Currently, there is no caller that uses FunctionNameRequirements::Unnamed for class syntax."); ASSERT_WITH_MESSAGE(!(requirements == FunctionNameRequirements::None && !info.className), "When specifying FunctionNameRequirements::None, we need to initialize info.className with the default value in the caller side."); if (match(IDENT)) { info.className = m_token.m_data.ident; next(); failIfTrue(classScope->declareLexicalVariable(info.className, true) & DeclarationResult::InvalidStrictMode, "'", info.className->impl(), "' is not a valid class name"); } else if (requirements == FunctionNameRequirements::Named) { if (match(OPENBRACE)) semanticFail("Class statements must have a name"); semanticFailureDueToKeyword("class name"); failDueToUnexpectedToken(); } ASSERT(info.className); TreeExpression parentClass = 0; if (consume(EXTENDS)) { parentClass = parseMemberExpression(context); failIfFalse(parentClass, "Cannot parse the parent class name"); } classScope->setIsClassScope(); const ConstructorKind constructorKind = parentClass ? ConstructorKind::Extends : ConstructorKind::Base; consumeOrFail(OPENBRACE, "Expected opening '{' at the start of a class body"); TreeExpression constructor = 0; TreePropertyList classElements = 0; TreePropertyList classElementsTail = 0; unsigned nextInstanceComputedFieldID = 0; unsigned nextStaticComputedFieldID = 0; while (!match(CLOSEBRACE)) { if (match(SEMICOLON)) { next(); continue; } JSTokenLocation methodLocation(tokenLocation()); unsigned methodStart = tokenStart(); // For backwards compatibility, "static" is a non-reserved keyword in non-strict mode. ClassElementTag tag = ClassElementTag::Instance; auto type = PropertyNode::Constant; if (match(RESERVED_IF_STRICT) && *m_token.m_data.ident == m_vm.propertyNames->staticKeyword) { SavePoint savePoint = createSavePoint(context); next(); if (match(OPENPAREN) || match(SEMICOLON) || match(EQUAL)) { // Reparse "static()" as a method or "static" as a class field. restoreSavePoint(context, savePoint); } else tag = ClassElementTag::Static; } // FIXME: Figure out a way to share more code with parseProperty. const CommonIdentifiers& propertyNames = *m_vm.propertyNames; const Identifier* ident = &propertyNames.nullIdentifier; TreeExpression computedPropertyName = 0; bool isGetter = false; bool isSetter = false; SourceParseMode parseMode = SourceParseMode::MethodMode; if (consume(TIMES)) parseMode = SourceParseMode::GeneratorWrapperMethodMode; parseMethod: switch (m_token.m_type) { namedKeyword: case STRING: ident = m_token.m_data.ident; ASSERT(ident); next(); break; case BIGINT: ident = &m_parserArena.identifierArena().makeBigIntDecimalIdentifier(const_cast(m_vm), *m_token.m_data.bigIntString, m_token.m_data.radix); ASSERT(ident); next(); break; case ESCAPED_KEYWORD: case IDENT: if (UNLIKELY(*m_token.m_data.ident == m_vm.propertyNames->async && !m_token.m_data.escaped)) { if (!isGeneratorMethodParseMode(parseMode) && !isAsyncMethodParseMode(parseMode)) { ident = m_token.m_data.ident; next(); // We match SEMICOLON as a special case for a field called 'async' without initializer. if (match(OPENPAREN) || match(COLON) || match(SEMICOLON) || match(EQUAL) || m_lexer->hasLineTerminatorBeforeToken()) break; if (UNLIKELY(consume(TIMES))) parseMode = SourceParseMode::AsyncGeneratorWrapperMethodMode; else parseMode = SourceParseMode::AsyncMethodMode; goto parseMethod; } } FALLTHROUGH; case AWAIT: { ident = m_token.m_data.ident; bool escaped = m_token.m_data.escaped; ASSERT(ident); next(); if (parseMode == SourceParseMode::MethodMode && !escaped && (matchIdentifierOrKeyword() || match(STRING) || match(DOUBLE) || match(INTEGER) || match(BIGINT) || match(OPENBRACKET))) { isGetter = *ident == propertyNames.get; isSetter = *ident == propertyNames.set; } break; } case DOUBLE: case INTEGER: ident = &m_parserArena.identifierArena().makeNumericIdentifier(const_cast(m_vm), m_token.m_data.doubleValue); ASSERT(ident); next(); break; case OPENBRACKET: next(); computedPropertyName = parseAssignmentExpression(context); type = static_cast(type | PropertyNode::Computed); failIfFalse(computedPropertyName, "Cannot parse computed property name"); handleProductionOrFail(CLOSEBRACKET, "]", "end", "computed property name"); break; case PRIVATENAME: { ASSERT(Options::usePrivateClassFields()); JSToken token = m_token; ident = m_token.m_data.ident; if (!Options::usePrivateStaticClassFields()) failIfTrue(tag == ClassElementTag::Static, "Static class element cannot be private"); failIfTrue(isGetter || isSetter, "Cannot parse class method with private name"); ASSERT(ident); next(); failIfTrue(matchAndUpdate(OPENPAREN, token), "Cannot parse class method with private name"); semanticFailIfTrue(classScope->declarePrivateName(*ident) & DeclarationResult::InvalidDuplicateDeclaration, "Cannot declare private field twice"); type = static_cast(type | PropertyNode::Private); break; } default: if (m_token.m_type & KeywordTokenFlag) goto namedKeyword; failDueToUnexpectedToken(); } TreeProperty property; if (isGetter || isSetter) { type = static_cast(type & ~PropertyNode::Constant); type = static_cast(type | (isGetter ? PropertyNode::Getter : PropertyNode::Setter)); property = parseGetterSetter(context, type, methodStart, ConstructorKind::None, tag); failIfFalse(property, "Cannot parse this method"); } else if (!match(OPENPAREN) && (tag == ClassElementTag::Instance || Options::usePublicStaticClassFields()) && parseMode == SourceParseMode::MethodMode) { ASSERT(!isGetter && !isSetter); if (ident) { semanticFailIfTrue(*ident == propertyNames.constructor, "Cannot declare class field named 'constructor'"); semanticFailIfTrue(*ident == propertyNames.constructorPrivateField, "Cannot declare private class field named '#constructor'"); if (tag == ClassElementTag::Static) semanticFailIfTrue(*ident == propertyNames.prototype, "Cannot declare a static field named 'prototype'"); } if (computedPropertyName) { if (tag == ClassElementTag::Instance) ident = &m_parserArena.identifierArena().makePrivateIdentifier(m_vm, instanceComputedNamePrefix, nextInstanceComputedFieldID++); else ident = &m_parserArena.identifierArena().makePrivateIdentifier(m_vm, staticComputedNamePrefix, nextStaticComputedFieldID++); DeclarationResultMask declarationResult = classScope->declareLexicalVariable(ident, true); ASSERT_UNUSED(declarationResult, declarationResult == DeclarationResult::Valid); classScope->useVariable(ident, false); classScope->addClosedVariableCandidateUnconditionally(ident->impl()); } TreeExpression initializer = 0; if (consume(EQUAL)) { SetForScope overrideParsingClassFieldInitializer(m_parserState.isParsingClassFieldInitializer, true); classScope->setExpectedSuperBinding(SuperBinding::Needed); initializer = parseAssignmentExpression(context); classScope->setExpectedSuperBinding(SuperBinding::NotNeeded); failIfFalse(initializer, "Cannot parse initializer for class field"); classScope->markLastUsedVariablesSetAsCaptured(); } failIfFalse(autoSemiColon(), "Expected a ';' following a class field"); auto inferName = initializer ? InferName::Allowed : InferName::Disallowed; if (computedPropertyName) property = context.createProperty(ident, computedPropertyName, initializer, type, SuperBinding::NotNeeded, tag); else property = context.createProperty(ident, initializer, type, SuperBinding::NotNeeded, inferName, tag); } else { ParserFunctionInfo methodInfo; bool isConstructor = tag == ClassElementTag::Instance && *ident == propertyNames.constructor; semanticFailIfTrue(isConstructor && parseMode != SourceParseMode::MethodMode, "Cannot declare ", stringArticleForFunctionMode(parseMode), stringForFunctionMode(parseMode), " named 'constructor'"); methodInfo.name = isConstructor ? info.className : ident; failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, parseMode, false, isConstructor ? constructorKind : ConstructorKind::None, SuperBinding::Needed, methodStart, methodInfo, FunctionDefinitionType::Method)), "Cannot parse this method"); TreeExpression method = context.createMethodDefinition(methodLocation, methodInfo); if (isConstructor) { semanticFailIfTrue(constructor, "Cannot declare multiple constructors in a single class"); constructor = method; continue; } semanticFailIfTrue(tag == ClassElementTag::Static && methodInfo.name && *methodInfo.name == propertyNames.prototype, "Cannot declare a static method named 'prototype'"); if (computedPropertyName) { property = context.createProperty(computedPropertyName, method, type, SuperBinding::Needed, tag); } else { property = context.createProperty(methodInfo.name, method, type, SuperBinding::Needed, InferName::Allowed, tag); } } if (classElementsTail) classElementsTail = context.createPropertyList(methodLocation, property, classElementsTail); else classElements = classElementsTail = context.createPropertyList(methodLocation, property); } info.endOffset = tokenLocation().endOffset - 1; consumeOrFail(CLOSEBRACE, "Expected a closing '}' after a class body"); if (Options::usePrivateClassFields()) { // Fail if there are no parent private name scopes and any used-but-undeclared private names. semanticFailIfFalse(copyUndeclaredPrivateNamesToOuterScope(), "Cannot reference undeclared private names"); } auto classExpression = context.createClassExpr(location, info, classScope->finalizeLexicalEnvironment(), constructor, parentClass, classElements); popScope(classScope, TreeBuilder::NeedsFreeVariableInfo); return classExpression; } template template TreeSourceElements Parser::parseClassFieldInitializerSourceElements(TreeBuilder& context, const Vector& classFieldLocations) { TreeSourceElements sourceElements = context.createSourceElements(); currentScope()->setIsClassScope(); unsigned numComputedFields = 0; for (auto location : classFieldLocations) { // This loop will either parse only static fields or only // instance fields, but never a mix; we could make it slightly // smarter about parsing given that fact, but it's probably // not worth the hassle, so begin each iteration without // knowing which kind the next field will be. bool isStaticField = false; // We don't need to worry about hasLineTerminatorBeforeToken // on class fields, so we set this value to false. LexerState lexerState { location.offset, static_cast(location.lineStartOffset), static_cast(location.line), static_cast(location.line), false }; restoreLexerState(lexerState); JSTokenLocation fieldLocation = tokenLocation(); const Identifier* ident = nullptr; DefineFieldNode::Type type = DefineFieldNode::Type::Name; if (match(RESERVED_IF_STRICT) && *m_token.m_data.ident == m_vm.propertyNames->staticKeyword) { auto* staticIdentifier = m_token.m_data.ident; ASSERT(staticIdentifier); next(); if (match(SEMICOLON) || match (EQUAL) || match(CLOSEBRACE) || m_lexer->hasLineTerminatorBeforeToken()) ident = staticIdentifier; else isStaticField = true; } if (!ident) { switch (m_token.m_type) { case PRIVATENAME: type = DefineFieldNode::Type::PrivateName; FALLTHROUGH; case STRING: case IDENT: namedKeyword: ident = m_token.m_data.ident; ASSERT(ident); next(); break; case BIGINT: ident = &m_parserArena.identifierArena().makeBigIntDecimalIdentifier(const_cast(m_vm), *m_token.m_data.bigIntString, m_token.m_data.radix); ASSERT(ident); next(); break; case DOUBLE: case INTEGER: ident = &m_parserArena.identifierArena().makeNumericIdentifier(const_cast(m_vm), m_token.m_data.doubleValue); ASSERT(ident); next(); break; case OPENBRACKET: { next(); TreeExpression computedPropertyName = parseAssignmentExpression(context); failIfFalse(computedPropertyName, "Cannot parse computed property name"); handleProductionOrFail(CLOSEBRACKET, "]", "end", "computed property name"); ident = &m_parserArena.identifierArena().makePrivateIdentifier(m_vm, isStaticField ? staticComputedNamePrefix : instanceComputedNamePrefix, numComputedFields++); type = DefineFieldNode::Type::ComputedName; break; } default: if (m_token.m_type & KeywordTokenFlag) goto namedKeyword; failDueToUnexpectedToken(); } } // Only valid class fields are handled in this function. ASSERT(match(EQUAL) || match(SEMICOLON) || match(CLOSEBRACE) || m_lexer->hasLineTerminatorBeforeToken()); TreeExpression initializer = 0; if (consume(EQUAL)) initializer = parseAssignmentExpression(context); if (type == DefineFieldNode::Type::PrivateName) currentScope()->useVariable(ident, false); TreeStatement defineField = context.createDefineField(fieldLocation, ident, initializer, type); context.appendStatement(sourceElements, defineField); } ASSERT(!hasError()); // Trick parseInner() into believing we've parsed the entire SourceCode, in order to prevent it from producing an error. m_token.m_type = EOFTOK; return sourceElements; } struct LabelInfo { LabelInfo(const Identifier* ident, const JSTextPosition& start, const JSTextPosition& end) : m_ident(ident) , m_start(start) , m_end(end) { } const Identifier* m_ident; JSTextPosition m_start; JSTextPosition m_end; }; template template TreeStatement Parser::parseExpressionOrLabelStatement(TreeBuilder& context, bool allowFunctionDeclarationAsStatement) { /* Expression and Label statements are ambiguous at LL(1), so we have a * special case that looks for a colon as the next character in the input. */ Vector labels; JSTokenLocation location; do { JSTextPosition start = tokenStartPosition(); location = tokenLocation(); if (!nextTokenIsColon()) { // If we hit this path we're making a expression statement, which // by definition can't make use of continue/break so we can just // ignore any labels we might have accumulated. TreeExpression expression = parseExpression(context, IsOnlyChildOfStatement::Yes); failIfFalse(expression, "Cannot parse expression statement"); if (!autoSemiColon()) failDueToUnexpectedToken(); return context.createExprStatement(location, expression, start, m_lastTokenEndPosition.line); } semanticFailIfTrue(isDisallowedIdentifierAwait(m_token), "Cannot use 'await' as a label ", disallowedIdentifierAwaitReason()); semanticFailIfTrue(isDisallowedIdentifierYield(m_token), "Cannot use 'yield' as a label ", disallowedIdentifierYieldReason()); const Identifier* ident = m_token.m_data.ident; JSTextPosition end = tokenEndPosition(); next(); consumeOrFail(COLON, "Labels must be followed by a ':'"); // This is O(N^2) over the current list of consecutive labels, but I // have never seen more than one label in a row in the real world. for (size_t i = 0; i < labels.size(); i++) failIfTrue(ident->impl() == labels[i].m_ident->impl(), "Attempted to redeclare the label '", ident->impl(), "'"); failIfTrue(getLabel(ident), "Cannot find scope for the label '", ident->impl(), "'"); labels.append(LabelInfo(ident, start, end)); } while (matchSpecIdentifier()); bool isLoop = false; switch (m_token.m_type) { case FOR: case WHILE: case DO: isLoop = true; break; default: break; } const Identifier* unused = nullptr; ScopeRef labelScope = currentScope(); for (size_t i = 0; i < labels.size(); i++) pushLabel(labels[i].m_ident, isLoop); m_immediateParentAllowsFunctionDeclarationInStatement = allowFunctionDeclarationAsStatement; TreeStatement statement = parseStatement(context, unused); for (size_t i = 0; i < labels.size(); i++) popLabel(labelScope); failIfFalse(statement, "Cannot parse statement"); for (size_t i = 0; i < labels.size(); i++) { const LabelInfo& info = labels[labels.size() - i - 1]; statement = context.createLabelStatement(location, info.m_ident, statement, info.m_start, info.m_end); } return statement; } template template TreeStatement Parser::parseExpressionStatement(TreeBuilder& context) { switch (m_token.m_type) { // Consult: http://www.ecma-international.org/ecma-262/6.0/index.html#sec-expression-statement // The ES6 spec mandates that we should fail from FUNCTION token here. We handle this case // in parseStatement() which is the only caller of parseExpressionStatement(). // We actually allow FUNCTION in situations where it should not be allowed unless we're in strict mode. case CLASSTOKEN: failWithMessage("'class' declaration is not directly within a block statement"); break; default: // FIXME: when implementing 'let' we should fail when we see the token sequence "let [". // https://bugs.webkit.org/show_bug.cgi?id=142944 break; } JSTextPosition start = tokenStartPosition(); JSTokenLocation location(tokenLocation()); TreeExpression expression = parseExpression(context, IsOnlyChildOfStatement::Yes); failIfFalse(expression, "Cannot parse expression statement"); failIfFalse(autoSemiColon(), "Parse error"); return context.createExprStatement(location, expression, start, m_lastTokenEndPosition.line); } template template TreeStatement Parser::parseIfStatement(TreeBuilder& context) { ASSERT(match(IF)); JSTokenLocation ifLocation(tokenLocation()); int start = tokenLine(); next(); handleProductionOrFail2(OPENPAREN, "(", "start", "'if' condition"); TreeExpression condition = parseExpression(context); failIfFalse(condition, "Expected an expression as the condition for an if statement"); recordPauseLocation(context.breakpointLocation(condition)); int end = tokenLine(); handleProductionOrFail2(CLOSEPAREN, ")", "end", "'if' condition"); const Identifier* unused = nullptr; m_immediateParentAllowsFunctionDeclarationInStatement = true; TreeStatement trueBlock = parseStatement(context, unused); failIfFalse(trueBlock, "Expected a statement as the body of an if block"); if (!match(ELSE)) return context.createIfStatement(ifLocation, condition, trueBlock, 0, start, end); Vector exprStack; Vector> posStack; Vector tokenLocationStack; Vector statementStack; bool trailingElse = false; do { JSTokenLocation tempLocation = tokenLocation(); next(); if (!match(IF)) { const Identifier* unused = nullptr; m_immediateParentAllowsFunctionDeclarationInStatement = true; TreeStatement block = parseStatement(context, unused); failIfFalse(block, "Expected a statement as the body of an else block"); statementStack.append(block); trailingElse = true; break; } int innerStart = tokenLine(); next(); handleProductionOrFail2(OPENPAREN, "(", "start", "'if' condition"); TreeExpression innerCondition = parseExpression(context); failIfFalse(innerCondition, "Expected an expression as the condition for an if statement"); recordPauseLocation(context.breakpointLocation(innerCondition)); int innerEnd = tokenLine(); handleProductionOrFail2(CLOSEPAREN, ")", "end", "'if' condition"); const Identifier* unused = nullptr; m_immediateParentAllowsFunctionDeclarationInStatement = true; TreeStatement innerTrueBlock = parseStatement(context, unused); failIfFalse(innerTrueBlock, "Expected a statement as the body of an if block"); tokenLocationStack.append(tempLocation); exprStack.append(innerCondition); posStack.append(std::make_pair(innerStart, innerEnd)); statementStack.append(innerTrueBlock); } while (match(ELSE)); if (!trailingElse) { TreeExpression condition = exprStack.last(); exprStack.removeLast(); TreeStatement trueBlock = statementStack.last(); statementStack.removeLast(); std::pair pos = posStack.last(); posStack.removeLast(); JSTokenLocation elseLocation = tokenLocationStack.last(); tokenLocationStack.removeLast(); TreeStatement ifStatement = context.createIfStatement(elseLocation, condition, trueBlock, 0, pos.first, pos.second); context.setEndOffset(ifStatement, context.endOffset(trueBlock)); statementStack.append(ifStatement); } while (!exprStack.isEmpty()) { TreeExpression condition = exprStack.last(); exprStack.removeLast(); TreeStatement falseBlock = statementStack.last(); statementStack.removeLast(); TreeStatement trueBlock = statementStack.last(); statementStack.removeLast(); std::pair pos = posStack.last(); posStack.removeLast(); JSTokenLocation elseLocation = tokenLocationStack.last(); tokenLocationStack.removeLast(); TreeStatement ifStatement = context.createIfStatement(elseLocation, condition, trueBlock, falseBlock, pos.first, pos.second); context.setEndOffset(ifStatement, context.endOffset(falseBlock)); statementStack.append(ifStatement); } return context.createIfStatement(ifLocation, condition, trueBlock, statementStack.last(), start, end); } template template typename TreeBuilder::ModuleName Parser::parseModuleName(TreeBuilder& context) { // ModuleName (ModuleSpecifier in the spec) represents the module name imported by the script. // http://www.ecma-international.org/ecma-262/6.0/#sec-imports // http://www.ecma-international.org/ecma-262/6.0/#sec-exports JSTokenLocation specifierLocation(tokenLocation()); failIfFalse(match(STRING), "Imported modules names must be string literals"); const Identifier* moduleName = m_token.m_data.ident; next(); return context.createModuleName(specifierLocation, *moduleName); } template template typename TreeBuilder::ImportSpecifier Parser::parseImportClauseItem(TreeBuilder& context, ImportSpecifierType specifierType) { // Produced node is the item of the ImportClause. // That is the ImportSpecifier, ImportedDefaultBinding or NameSpaceImport. // http://www.ecma-international.org/ecma-262/6.0/#sec-imports JSTokenLocation specifierLocation(tokenLocation()); JSToken localNameToken; const Identifier* importedName = nullptr; const Identifier* localName = nullptr; switch (specifierType) { case ImportSpecifierType::NamespaceImport: { // NameSpaceImport : // * as ImportedBinding // e.g. // * as namespace ASSERT(match(TIMES)); importedName = &m_vm.propertyNames->timesIdentifier; next(); failIfFalse(matchContextualKeyword(m_vm.propertyNames->as), "Expected 'as' before imported binding name"); next(); failIfFalse(matchSpecIdentifier(), "Expected a variable name for the import declaration"); localNameToken = m_token; localName = m_token.m_data.ident; next(); break; } case ImportSpecifierType::NamedImport: { // ImportSpecifier : // ImportedBinding // IdentifierName as ImportedBinding // ModuleExportName as ImportedBinding // e.g. // A // A as B ASSERT(matchIdentifierOrKeyword() || match(STRING)); bool isModuleExportName = match(STRING); localName = m_token.m_data.ident; importedName = localName; localNameToken = m_token; if (isModuleExportName) failIfTrue(hasUnpairedSurrogate(localName->string()), "Expected a well-formed-unicode string for the module export name"); next(); bool useAs = matchContextualKeyword(m_vm.propertyNames->as); if (isModuleExportName) failIfFalse(useAs, "Expected 'as' after the module export name string"); if (useAs) { next(); failIfFalse(matchSpecIdentifier(), "Expected a variable name for the import declaration"); localNameToken = m_token; localName = m_token.m_data.ident; next(); } break; } case ImportSpecifierType::DefaultImport: { // ImportedDefaultBinding : // ImportedBinding ASSERT(matchSpecIdentifier()); localNameToken = m_token; localName = m_token.m_data.ident; importedName = &m_vm.propertyNames->defaultKeyword; next(); break; } } semanticFailIfTrue(localNameToken.m_type == AWAIT, "Cannot use 'await' as an imported binding name"); semanticFailIfTrue(localNameToken.m_type & KeywordTokenFlag, "Cannot use keyword as imported binding name"); DeclarationResultMask declarationResult = declareVariable(localName, DeclarationType::ConstDeclaration, (specifierType == ImportSpecifierType::NamespaceImport) ? DeclarationImportType::ImportedNamespace : DeclarationImportType::Imported); if (declarationResult != DeclarationResult::Valid) { failIfTrueIfStrict(declarationResult & DeclarationResult::InvalidStrictMode, "Cannot declare an imported binding named ", localName->impl(), " in strict mode"); if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration) internalFailWithMessage(false, "Cannot declare an imported binding name twice: '", localName->impl(), "'"); } return context.createImportSpecifier(specifierLocation, *importedName, *localName); } template template TreeStatement Parser::parseImportDeclaration(TreeBuilder& context) { // http://www.ecma-international.org/ecma-262/6.0/#sec-imports ASSERT(match(IMPORT)); JSTokenLocation importLocation(tokenLocation()); next(); auto specifierList = context.createImportSpecifierList(); if (match(STRING)) { // import ModuleSpecifier ; auto moduleName = parseModuleName(context); failIfFalse(moduleName, "Cannot parse the module name"); failIfFalse(autoSemiColon(), "Expected a ';' following a targeted import declaration"); return context.createImportDeclaration(importLocation, specifierList, moduleName); } bool isFinishedParsingImport = false; if (matchSpecIdentifier()) { // ImportedDefaultBinding : // ImportedBinding auto specifier = parseImportClauseItem(context, ImportSpecifierType::DefaultImport); failIfFalse(specifier, "Cannot parse the default import"); context.appendImportSpecifier(specifierList, specifier); if (match(COMMA)) next(); else isFinishedParsingImport = true; } if (!isFinishedParsingImport) { if (match(TIMES)) { // import NameSpaceImport FromClause ; auto specifier = parseImportClauseItem(context, ImportSpecifierType::NamespaceImport); failIfFalse(specifier, "Cannot parse the namespace import"); context.appendImportSpecifier(specifierList, specifier); } else if (match(OPENBRACE)) { // NamedImports : // { } // { ImportsList } // { ImportsList , } next(); while (!match(CLOSEBRACE)) { failIfFalse(matchIdentifierOrKeyword() || match(STRING), "Expected an imported name or a module export name string for the import declaration"); auto specifier = parseImportClauseItem(context, ImportSpecifierType::NamedImport); failIfFalse(specifier, "Cannot parse the named import"); context.appendImportSpecifier(specifierList, specifier); if (!consume(COMMA)) break; } handleProductionOrFail2(CLOSEBRACE, "}", "end", "import list"); } else failWithMessage("Expected namespace import or import list"); } // FromClause : // from ModuleSpecifier failIfFalse(matchContextualKeyword(m_vm.propertyNames->from), "Expected 'from' before imported module name"); next(); auto moduleName = parseModuleName(context); failIfFalse(moduleName, "Cannot parse the module name"); failIfFalse(autoSemiColon(), "Expected a ';' following a targeted import declaration"); return context.createImportDeclaration(importLocation, specifierList, moduleName); } template template typename TreeBuilder::ExportSpecifier Parser::parseExportSpecifier(TreeBuilder& context, Vector>& maybeExportedLocalNames, bool& hasKeywordForLocalBindings, bool& hasReferencedModuleExportNames) { // ExportSpecifier : // IdentifierName // IdentifierName as IdentifierName // IdentifierName as ModuleExportName // ModuleExportName // ModuleExportName as IdentifierName // ModuleExportName as ModuleExportName // http://www.ecma-international.org/ecma-262/6.0/#sec-exports ASSERT(matchIdentifierOrKeyword() || match(STRING)); JSTokenLocation specifierLocation(tokenLocation()); const Identifier* localName = m_token.m_data.ident; const Identifier* exportedName = localName; if (match(STRING)) { hasReferencedModuleExportNames = true; failIfTrue(hasUnpairedSurrogate(exportedName->string()), "Expected a well-formed-unicode string for the module export name"); } else { if (m_token.m_type & KeywordTokenFlag) hasKeywordForLocalBindings = true; } next(); if (matchContextualKeyword(m_vm.propertyNames->as)) { next(); failIfFalse(matchIdentifierOrKeyword() || match(STRING), "Expected an exported name or a module export name string for the export declaration"); exportedName = m_token.m_data.ident; if (match(STRING)) failIfTrue(hasUnpairedSurrogate(exportedName->string()), "Expected a well-formed-unicode string for the module export name"); next(); } semanticFailIfFalse(exportName(*exportedName), "Cannot export a duplicate name '", exportedName->impl(), "'"); maybeExportedLocalNames.append(std::make_pair(localName, exportedName)); return context.createExportSpecifier(specifierLocation, *localName, *exportedName); } template template TreeStatement Parser::parseExportDeclaration(TreeBuilder& context) { // http://www.ecma-international.org/ecma-262/6.0/#sec-exports ASSERT(match(EXPORT_)); JSTokenLocation exportLocation(tokenLocation()); next(); switch (m_token.m_type) { case TIMES: { // export * FromClause ; // export * as IdentifierName FromClause ; // export * as ModuleExportName FromClause ; next(); const Identifier* exportedName = nullptr; JSTokenLocation specifierLocation; if (matchContextualKeyword(m_vm.propertyNames->as)) { next(); specifierLocation = JSTokenLocation(tokenLocation()); failIfFalse(matchIdentifierOrKeyword() || match(STRING), "Expected an exported name or a module export name string for the export declaration"); exportedName = m_token.m_data.ident; if (match(STRING)) failIfTrue(hasUnpairedSurrogate(exportedName->string()), "Expected a well-formed-unicode string for the module export name"); next(); } failIfFalse(matchContextualKeyword(m_vm.propertyNames->from), "Expected 'from' before exported module name"); next(); auto moduleName = parseModuleName(context); failIfFalse(moduleName, "Cannot parse the 'from' clause"); failIfFalse(autoSemiColon(), "Expected a ';' following a targeted export declaration"); if (exportedName) { semanticFailIfFalse(exportName(*exportedName), "Cannot export a duplicate name '", exportedName->impl(), "'"); auto specifierList = context.createExportSpecifierList(); auto localName = &m_vm.propertyNames->starNamespacePrivateName; auto specifier = context.createExportSpecifier(specifierLocation, *localName, *exportedName); context.appendExportSpecifier(specifierList, specifier); return context.createExportNamedDeclaration(exportLocation, specifierList, moduleName); } return context.createExportAllDeclaration(exportLocation, moduleName); } case DEFAULT: { // export default HoistableDeclaration[~Yield, ~Await, +Default] // export default ClassDeclaration[~Yield, ~Await, +Default] // export default [lookahead not-in { function, async [no LineTerminator here] function, class }] AssignmentExpression[+In, ~Yield, ~Await] next(); TreeStatement result = 0; bool isFunctionOrClassDeclaration = false; const Identifier* localName = nullptr; bool startsWithFunction = match(FUNCTION); if (startsWithFunction || match(CLASSTOKEN)) { SavePoint savePoint = createSavePoint(context); isFunctionOrClassDeclaration = true; next(); // ES6 Generators if (startsWithFunction && match(TIMES)) next(); if (match(IDENT)) localName = m_token.m_data.ident; restoreSavePoint(context, savePoint); } else if (matchContextualKeyword(m_vm.propertyNames->async)) { // export default async function xxx() { } // export default async function * yyy() { } SavePoint savePoint = createSavePoint(context); next(); if (match(FUNCTION) && !m_lexer->hasLineTerminatorBeforeToken()) { next(); // Async Generators if (match(TIMES)) next(); if (match(IDENT)) localName = m_token.m_data.ident; isFunctionOrClassDeclaration = true; } restoreSavePoint(context, savePoint); } if (!localName) localName = &m_vm.propertyNames->starDefaultPrivateName; if (isFunctionOrClassDeclaration) { if (startsWithFunction) { ASSERT(match(FUNCTION)); DepthManager statementDepth(&m_statementDepth); m_statementDepth = 1; result = parseFunctionDeclaration(context, FunctionDeclarationType::Declaration, ExportType::NotExported, DeclarationDefaultContext::ExportDefault); } else if (match(CLASSTOKEN)) { result = parseClassDeclaration(context, ExportType::NotExported, DeclarationDefaultContext::ExportDefault); } else { ASSERT(matchContextualKeyword(m_vm.propertyNames->async)); next(); DepthManager statementDepth(&m_statementDepth); m_statementDepth = 1; result = parseAsyncFunctionDeclaration(context, ExportType::NotExported, DeclarationDefaultContext::ExportDefault); } } else { // export default expr; // // It should be treated as the same to the following. // // const *default* = expr; // export { *default* as default } // // In the above example, *default* is the invisible variable to the users. // We use the private symbol to represent the name of this variable. JSTokenLocation location(tokenLocation()); JSTextPosition start = tokenStartPosition(); TreeExpression expression = parseAssignmentExpression(context); failIfFalse(expression, "Cannot parse expression"); DeclarationResultMask declarationResult = declareVariable(&m_vm.propertyNames->starDefaultPrivateName, DeclarationType::ConstDeclaration); if (declarationResult & DeclarationResult::InvalidDuplicateDeclaration) internalFailWithMessage(false, "Only one 'default' export is allowed"); TreeExpression assignment = context.createAssignResolve(location, m_vm.propertyNames->starDefaultPrivateName, expression, start, start, tokenEndPosition(), AssignmentContext::ConstDeclarationStatement); result = context.createExprStatement(location, assignment, start, tokenEndPosition()); failIfFalse(autoSemiColon(), "Expected a ';' following a targeted export declaration"); } failIfFalse(result, "Cannot parse the declaration"); semanticFailIfFalse(exportName(m_vm.propertyNames->defaultKeyword), "Only one 'default' export is allowed"); m_moduleScopeData->exportBinding(*localName, m_vm.propertyNames->defaultKeyword); return context.createExportDefaultDeclaration(exportLocation, result, *localName); } case OPENBRACE: { // export ExportClause FromClause ; // export ExportClause ; // // ExportClause : // { } // { ExportsList } // { ExportsList , } // // ExportsList : // ExportSpecifier // ExportsList , ExportSpecifier next(); auto specifierList = context.createExportSpecifierList(); Vector> maybeExportedLocalNames; bool hasKeywordForLocalBindings = false; bool hasReferencedModuleExportNames = false; while (!match(CLOSEBRACE)) { failIfFalse(matchIdentifierOrKeyword() || match(STRING), "Expected a variable name or a module export name string for the export declaration"); auto specifier = parseExportSpecifier(context, maybeExportedLocalNames, hasKeywordForLocalBindings, hasReferencedModuleExportNames); failIfFalse(specifier, "Cannot parse the named export"); context.appendExportSpecifier(specifierList, specifier); if (!consume(COMMA)) break; } handleProductionOrFail2(CLOSEBRACE, "}", "end", "export list"); typename TreeBuilder::ModuleName moduleName = 0; if (matchContextualKeyword(m_vm.propertyNames->from)) { next(); moduleName = parseModuleName(context); failIfFalse(moduleName, "Cannot parse the 'from' clause"); } else semanticFailIfTrue(hasReferencedModuleExportNames, "Cannot use module export names if they reference variable names in the current module"); failIfFalse(autoSemiColon(), "Expected a ';' following a targeted export declaration"); if (!moduleName) { semanticFailIfTrue(hasKeywordForLocalBindings, "Cannot use keyword as exported variable name"); // Since this export declaration does not have module specifier part, it exports the local bindings. // While the export declaration with module specifier does not have any effect on the current module's scope, // the export named declaration without module specifier references the local binding names. // For example, // export { A, B, C as D } from "mod" // does not have effect on the current module's scope. But, // export { A, B, C as D } // will reference the current module's bindings. for (const auto& pair : maybeExportedLocalNames) { const Identifier* localName = pair.first; const Identifier* exportedName = pair.second; m_moduleScopeData->exportBinding(*localName, *exportedName); } } return context.createExportNamedDeclaration(exportLocation, specifierList, moduleName); } default: { // export VariableStatement // export Declaration TreeStatement result = 0; switch (m_token.m_type) { case VAR: result = parseVariableDeclaration(context, DeclarationType::VarDeclaration, ExportType::Exported); break; case CONSTTOKEN: result = parseVariableDeclaration(context, DeclarationType::ConstDeclaration, ExportType::Exported); break; case LET: result = parseVariableDeclaration(context, DeclarationType::LetDeclaration, ExportType::Exported); break; case FUNCTION: { DepthManager statementDepth(&m_statementDepth); m_statementDepth = 1; result = parseFunctionDeclaration(context, FunctionDeclarationType::Declaration, ExportType::Exported); break; } case CLASSTOKEN: result = parseClassDeclaration(context, ExportType::Exported); break; case IDENT: if (*m_token.m_data.ident == m_vm.propertyNames->async && !m_token.m_data.escaped) { next(); semanticFailIfFalse(match(FUNCTION) && !m_lexer->hasLineTerminatorBeforeToken(), "Expected 'function' keyword following 'async' keyword with no preceding line terminator"); DepthManager statementDepth(&m_statementDepth); m_statementDepth = 1; result = parseAsyncFunctionDeclaration(context, ExportType::Exported); break; } FALLTHROUGH; default: failWithMessage("Expected either a declaration or a variable statement"); break; } failIfFalse(result, "Cannot parse the declaration"); return context.createExportLocalDeclaration(exportLocation, result); } } RELEASE_ASSERT_NOT_REACHED(); return 0; } template template TreeExpression Parser::parseExpression(TreeBuilder& context, IsOnlyChildOfStatement isStatement) { failIfStackOverflow(); JSTokenLocation headLocation(tokenLocation()); TreeExpression node = parseAssignmentExpression(context); failIfFalse(node, "Cannot parse expression"); context.setEndOffset(node, m_lastTokenEndPosition.offset); if (!match(COMMA)) return node; next(); m_parserState.nonTrivialExpressionCount++; m_parserState.nonLHSCount++; JSTokenLocation tailLocation(tokenLocation()); TreeExpression right = parseAssignmentExpression(context); failIfFalse(right, "Cannot parse expression in a comma expression"); context.setEndOffset(right, m_lastTokenEndPosition.offset); typename TreeBuilder::Comma head = context.createCommaExpr(headLocation, node); if (isStatement == IsOnlyChildOfStatement::Yes) recordPauseLocation(context.breakpointLocation(head)); typename TreeBuilder::Comma tail = context.appendToCommaExpr(tailLocation, head, head, right); if (isStatement == IsOnlyChildOfStatement::Yes) recordPauseLocation(context.breakpointLocation(tail)); while (match(COMMA)) { next(TreeBuilder::DontBuildStrings); tailLocation = tokenLocation(); right = parseAssignmentExpression(context); failIfFalse(right, "Cannot parse expression in a comma expression"); context.setEndOffset(right, m_lastTokenEndPosition.offset); tail = context.appendToCommaExpr(tailLocation, head, tail, right); if (isStatement == IsOnlyChildOfStatement::Yes) recordPauseLocation(context.breakpointLocation(tail)); } context.setEndOffset(head, m_lastTokenEndPosition.offset); return head; } template template TreeExpression Parser::parseAssignmentExpressionOrPropagateErrorClass(TreeBuilder& context) { ExpressionErrorClassifier classifier(this); auto assignment = parseAssignmentExpression(context, classifier); if (!assignment) classifier.propagateExpressionErrorClass(); return assignment; } template template TreeExpression Parser::parseAssignmentExpression(TreeBuilder& context) { ExpressionErrorClassifier classifier(this); return parseAssignmentExpression(context, classifier); } template template NEVER_INLINE const char* Parser::metaPropertyName(TreeBuilder& context, TreeExpression expr) { if (context.isNewTarget(expr)) return "new.target"; if (context.isImportMeta(expr)) return "import.meta"; RELEASE_ASSERT_NOT_REACHED(); return "error"; } template template bool Parser::isSimpleAssignmentTarget(TreeBuilder& context, TreeExpression expr) { // Web compatibility concerns prevent us from handling a function call LHS as an early error in sloppy mode. // This behavior is currently unspecified, but see: https://github.com/tc39/ecma262/issues/257#issuecomment-195106880 return context.isLocation(expr) || (!strictMode() && context.isFunctionCall(expr)); } template template TreeExpression Parser::parseAssignmentExpression(TreeBuilder& context, ExpressionErrorClassifier& classifier) { ASSERT(!hasError()); failIfStackOverflow(); if (match(YIELD) && !isYIELDMaskedAsIDENT(currentScope()->isGenerator())) return parseYieldExpression(context); JSTextPosition start = tokenStartPosition(); JSTokenLocation location(tokenLocation()); int initialAssignmentCount = m_parserState.assignmentCount; int initialNonLHSCount = m_parserState.nonLHSCount; bool maybeAssignmentPattern = match(OPENBRACE) || match(OPENBRACKET); bool wasOpenParen = match(OPENPAREN); // Do not use matchSpecIdentifier() here since it is slower than isIdentifierOrKeyword. // Whether spec identifier is will be validated by isArrowFunctionParameters(). bool wasIdentifierOrKeyword = isIdentifierOrKeyword(m_token); bool maybeValidArrowFunctionStart = wasOpenParen || wasIdentifierOrKeyword; SavePoint savePoint = createSavePoint(context); size_t usedVariablesSize = 0; if (wasOpenParen) { usedVariablesSize = currentScope()->currentUsedVariablesSize(); currentScope()->pushUsedVariableSet(); } TreeExpression lhs = parseConditionalExpression(context); if (maybeValidArrowFunctionStart && !match(EOFTOK)) { bool isArrowFunctionToken = match(ARROWFUNCTION); if (!lhs || isArrowFunctionToken) { SavePointWithError errorRestorationSavePoint = swapSavePointForError(context, savePoint); bool isAsyncArrow = false; if (UNLIKELY(classifier.indicatesPossibleAsyncArrowFunction())) { if (matchContextualKeyword(m_vm.propertyNames->async)) { next(); isAsyncArrow = !m_lexer->hasLineTerminatorBeforeToken(); } } if (isArrowFunctionParameters(context)) { if (wasOpenParen) currentScope()->revertToPreviousUsedVariables(usedVariablesSize); return parseArrowFunctionExpression(context, isAsyncArrow); } if (isArrowFunctionToken) propagateError(); restoreSavePointWithError(context, errorRestorationSavePoint); if (isArrowFunctionToken) failDueToUnexpectedToken(); } } if (!lhs && (!maybeAssignmentPattern || !classifier.indicatesPossiblePattern())) propagateError(); if (maybeAssignmentPattern && (!lhs || (context.isObjectOrArrayLiteral(lhs) && match(EQUAL)))) { SavePointWithError expressionErrorLocation = swapSavePointForError(context, savePoint); auto pattern = tryParseDestructuringPatternExpression(context, AssignmentContext::AssignmentExpression); if (classifier.indicatesPossiblePattern() && (!pattern || !match(EQUAL))) { restoreSavePointWithError(context, expressionErrorLocation); return 0; } failIfFalse(pattern, "Cannot parse assignment pattern"); consumeOrFail(EQUAL, "Expected '=' following assignment pattern"); auto rhs = parseAssignmentExpression(context); if (!rhs) propagateError(); return context.createDestructuringAssignment(location, pattern, rhs); } failIfFalse(lhs, "Cannot parse expression"); if (initialNonLHSCount != m_parserState.nonLHSCount) { if (m_token.m_type >= EQUAL && m_token.m_type <= ANDEQUAL) semanticFail("Left hand side of operator '", getToken(), "' must be a reference"); return lhs; } int assignmentStack = 0; Operator op; bool hadAssignment = false; while (true) { switch (m_token.m_type) { case EQUAL: op = Operator::Equal; break; case PLUSEQUAL: op = Operator::PlusEq; break; case MINUSEQUAL: op = Operator::MinusEq; break; case MULTEQUAL: op = Operator::MultEq; break; case DIVEQUAL: op = Operator::DivEq; break; case LSHIFTEQUAL: op = Operator::LShift; break; case RSHIFTEQUAL: op = Operator::RShift; break; case URSHIFTEQUAL: op = Operator::URShift; break; case BITANDEQUAL: op = Operator::BitAndEq; break; case BITXOREQUAL: op = Operator::BitXOrEq; break; case BITOREQUAL: op = Operator::BitOrEq; break; case MODEQUAL: op = Operator::ModEq; break; case POWEQUAL: op = Operator::PowEq; break; case COALESCEEQUAL: op = Operator::CoalesceEq; break; case OREQUAL: op = Operator::OrEq; break; case ANDEQUAL: op = Operator::AndEq; break; default: goto end; } m_parserState.nonTrivialExpressionCount++; hadAssignment = true; semanticFailIfTrue(context.isMetaProperty(lhs), metaPropertyName(context, lhs), " can't be the left hand side of an assignment expression"); semanticFailIfFalse(isSimpleAssignmentTarget(context, lhs), "Left side of assignment is not a reference"); context.assignmentStackAppend(assignmentStack, lhs, start, tokenStartPosition(), m_parserState.assignmentCount, op); start = tokenStartPosition(); m_parserState.assignmentCount++; next(TreeBuilder::DontBuildStrings); if (strictMode() && m_parserState.lastIdentifier && context.isResolve(lhs)) { failIfTrueIfStrict(m_vm.propertyNames->eval == *m_parserState.lastIdentifier, "Cannot modify 'eval' in strict mode"); failIfTrueIfStrict(m_vm.propertyNames->arguments == *m_parserState.lastIdentifier, "Cannot modify 'arguments' in strict mode"); m_parserState.lastIdentifier = nullptr; } lhs = parseAssignmentExpression(context); failIfFalse(lhs, "Cannot parse the right hand side of an assignment expression"); if (initialNonLHSCount != m_parserState.nonLHSCount) { if (m_token.m_type >= EQUAL && m_token.m_type <= ANDEQUAL) semanticFail("Left hand side of operator '", getToken(), "' must be a reference"); break; } } end: if (hadAssignment) m_parserState.nonLHSCount++; while (assignmentStack) lhs = context.createAssignment(location, assignmentStack, lhs, initialAssignmentCount, m_parserState.assignmentCount, lastTokenEndPosition()); return lhs; } template template TreeExpression Parser::parseYieldExpression(TreeBuilder& context) { // YieldExpression[In] : // yield // yield [no LineTerminator here] AssignmentExpression[?In, Yield] // yield [no LineTerminator here] * AssignmentExpression[?In, Yield] // http://ecma-international.org/ecma-262/6.0/#sec-generator-function-definitions failIfFalse(currentScope()->isGenerator() && !currentScope()->isArrowFunctionBoundary(), "Cannot use yield expression out of generator"); // http://ecma-international.org/ecma-262/6.0/#sec-generator-function-definitions-static-semantics-early-errors failIfTrue(m_parserState.functionParsePhase == FunctionParsePhase::Parameters, "Cannot use yield expression within parameters"); JSTokenLocation location(tokenLocation()); JSTextPosition divotStart = tokenStartPosition(); ASSERT(match(YIELD)); SavePoint savePoint = createSavePoint(context); next(); if (m_lexer->hasLineTerminatorBeforeToken()) return context.createYield(location); bool delegate = consume(TIMES); JSTextPosition argumentStart = tokenStartPosition(); TreeExpression argument = parseAssignmentExpression(context); if (!argument) { restoreSavePoint(context, savePoint); next(); return context.createYield(location); } return context.createYield(location, argument, delegate, divotStart, argumentStart, lastTokenEndPosition()); } template template TreeExpression Parser::parseAwaitExpression(TreeBuilder& context) { ASSERT(match(AWAIT)); ASSERT(currentScope()->isAsyncFunction()); ASSERT(m_parserState.functionParsePhase != FunctionParsePhase::Parameters); JSTokenLocation location(tokenLocation()); JSTextPosition divotStart = tokenStartPosition(); next(); JSTextPosition argumentStart = tokenStartPosition(); ExpressionErrorClassifier classifier(this); TreeExpression argument = parseUnaryExpression(context); failIfFalse(argument, "Failed to parse await expression"); return context.createAwait(location, argument, divotStart, argumentStart, lastTokenEndPosition()); } template template TreeExpression Parser::parseConditionalExpression(TreeBuilder& context) { JSTokenLocation location(tokenLocation()); TreeExpression cond = parseBinaryExpression(context); failIfFalse(cond, "Cannot parse expression"); if (!match(QUESTION)) return cond; m_parserState.nonTrivialExpressionCount++; m_parserState.nonLHSCount++; next(TreeBuilder::DontBuildStrings); TreeExpression lhs = 0; { // this block is necessary so that we don't leave `in` enabled for the rhs AllowInOverride allowInOverride(this); lhs = parseAssignmentExpression(context); } failIfFalse(lhs, "Cannot parse left hand side of ternary operator"); context.setEndOffset(lhs, m_lastTokenEndPosition.offset); consumeOrFailWithFlags(COLON, TreeBuilder::DontBuildStrings, "Expected ':' in ternary operator"); TreeExpression rhs = parseAssignmentExpression(context); failIfFalse(rhs, "Cannot parse right hand side of ternary operator"); context.setEndOffset(rhs, m_lastTokenEndPosition.offset); return context.createConditionalExpr(location, cond, lhs, rhs); } ALWAYS_INLINE static bool isUnaryOpExcludingUpdateOp(JSTokenType token) { if (isUpdateOp(token)) return false; return isUnaryOp(token); } template int Parser::isBinaryOperator(JSTokenType token) { if (m_allowsIn) return token & (BinaryOpTokenPrecedenceMask << BinaryOpTokenAllowsInPrecedenceAdditionalShift); return token & BinaryOpTokenPrecedenceMask; } template template TreeExpression Parser::parseBinaryExpression(TreeBuilder& context) { int operandStackDepth = 0; int operatorStackDepth = 0; typename TreeBuilder::BinaryExprContext binaryExprContext(context); JSTokenLocation location(tokenLocation()); bool hasLogicalOperator = false; bool hasCoalesceOperator = false; while (true) { JSTextPosition exprStart = tokenStartPosition(); int initialAssignments = m_parserState.assignmentCount; JSTokenType leadingTokenTypeForUnaryExpression = m_token.m_type; TreeExpression current = parseUnaryExpression(context); failIfFalse(current, "Cannot parse expression"); context.appendBinaryExpressionInfo(operandStackDepth, current, exprStart, lastTokenEndPosition(), lastTokenEndPosition(), initialAssignments != m_parserState.assignmentCount); int precedence = isBinaryOperator(m_token.m_type); if (!precedence) break; // 12.6 https://tc39.github.io/ecma262/#sec-exp-operator // ExponentiationExpresion is described as follows. // // ExponentiationExpression[Yield]: // UnaryExpression[?Yield] // UpdateExpression[?Yield] ** ExponentiationExpression[?Yield] // // As we can see, the left hand side of the ExponentiationExpression is UpdateExpression, not UnaryExpression. // So placing UnaryExpression not included in UpdateExpression here is a syntax error. // This is intentional. For example, if UnaryExpression is allowed, we can have the code like `-x**y`. // But this is confusing: `-(x**y)` OR `(-x)**y`, which interpretation is correct? // To avoid this problem, ECMA262 makes unparenthesized exponentiation expression as operand of unary operators an early error. // More rationale: https://mail.mozilla.org/pipermail/es-discuss/2015-September/044232.html // // Here, we guarantee that the left hand side of this expression is not unary expression by checking the leading operator of the parseUnaryExpression. // This check just works. Let's consider the example, // y <> -x ** z // ^ // Check this. // If the binary operator <> has higher precedence than one of "**", this check does not work. // But it's OK for ** because the operator "**" has the highest operator precedence in the binary operators. failIfTrue(match(POW) && isUnaryOpExcludingUpdateOp(leadingTokenTypeForUnaryExpression), "Ambiguous unary expression in the left hand side of the exponentiation expression; parentheses must be used to disambiguate the expression"); // Mixing ?? with || or && is currently specified as an early error. // Since ?? is the lowest-precedence binary operator, it suffices to check whether these ever coexist in the operator stack. if (match(AND) || match(OR)) hasLogicalOperator = true; else if (match(COALESCE)) hasCoalesceOperator = true; failIfTrue(hasLogicalOperator && hasCoalesceOperator, "Coalescing and logical operators used together in the same expression; parentheses must be used to disambiguate"); m_parserState.nonTrivialExpressionCount++; m_parserState.nonLHSCount++; int operatorToken = m_token.m_type; next(TreeBuilder::DontBuildStrings); while (operatorStackDepth && context.operatorStackShouldReduce(precedence)) { ASSERT(operandStackDepth > 1); typename TreeBuilder::BinaryOperand rhs = context.getFromOperandStack(-1); typename TreeBuilder::BinaryOperand lhs = context.getFromOperandStack(-2); context.shrinkOperandStackBy(operandStackDepth, 2); context.appendBinaryOperation(location, operandStackDepth, operatorStackDepth, lhs, rhs); context.operatorStackPop(operatorStackDepth); } context.operatorStackAppend(operatorStackDepth, operatorToken, precedence); } while (operatorStackDepth) { ASSERT(operandStackDepth > 1); typename TreeBuilder::BinaryOperand rhs = context.getFromOperandStack(-1); typename TreeBuilder::BinaryOperand lhs = context.getFromOperandStack(-2); context.shrinkOperandStackBy(operandStackDepth, 2); context.appendBinaryOperation(location, operandStackDepth, operatorStackDepth, lhs, rhs); context.operatorStackPop(operatorStackDepth); } return context.popOperandStack(operandStackDepth); } template template TreeProperty Parser::parseProperty(TreeBuilder& context) { SourceParseMode parseMode = SourceParseMode::MethodMode; bool wasIdent = false; if (consume(TIMES)) parseMode = SourceParseMode::GeneratorWrapperMethodMode; parseProperty: switch (m_token.m_type) { case ESCAPED_KEYWORD: case IDENT: if (UNLIKELY(*m_token.m_data.ident == m_vm.propertyNames->async && !m_token.m_data.escaped)) { if (parseMode == SourceParseMode::MethodMode) { SavePoint savePoint = createSavePoint(context); next(); if (match(COLON) || match(OPENPAREN) || match(COMMA) || match(CLOSEBRACE)) { restoreSavePoint(context, savePoint); wasIdent = true; goto namedProperty; } failIfTrue(m_lexer->hasLineTerminatorBeforeToken(), "Expected a property name following keyword 'async'"); if (UNLIKELY(consume(TIMES))) parseMode = SourceParseMode::AsyncGeneratorWrapperMethodMode; else parseMode = SourceParseMode::AsyncMethodMode; goto parseProperty; } } FALLTHROUGH; case YIELD: case AWAIT: wasIdent = true; FALLTHROUGH; case STRING: { namedProperty: const Identifier* ident = m_token.m_data.ident; bool escaped = m_token.m_data.escaped; unsigned getterOrSetterStartOffset = tokenStart(); JSToken identToken = m_token; if (wasIdent && !isGeneratorMethodParseMode(parseMode) && (!escaped && (*ident == m_vm.propertyNames->get || *ident == m_vm.propertyNames->set))) nextExpectIdentifier(LexerFlags::IgnoreReservedWords); else nextExpectIdentifier(TreeBuilder::DontBuildKeywords | LexerFlags::IgnoreReservedWords); if (!isGeneratorMethodParseMode(parseMode) && !isAsyncMethodParseMode(parseMode) && match(COLON)) { next(); TreeExpression node = parseAssignmentExpressionOrPropagateErrorClass(context); failIfFalse(node, "Cannot parse expression for property declaration"); context.setEndOffset(node, m_lexer->currentOffset()); InferName inferName = ident && *ident == m_vm.propertyNames->underscoreProto ? InferName::Disallowed : InferName::Allowed; return context.createProperty(ident, node, PropertyNode::Constant, SuperBinding::NotNeeded, inferName, ClassElementTag::No); } if (match(OPENPAREN)) { auto method = parsePropertyMethod(context, ident, parseMode); propagateError(); return context.createProperty(ident, method, PropertyNode::Constant, SuperBinding::Needed, InferName::Allowed, ClassElementTag::No); } failIfTrue(parseMode != SourceParseMode::MethodMode, "Expected a parenthesis for argument list"); failIfFalse(wasIdent, "Expected an identifier as property name"); if (match(COMMA) || match(CLOSEBRACE)) { semanticFailureDueToKeywordCheckingToken(identToken, "shorthand property name"); JSTextPosition start = tokenStartPosition(); JSTokenLocation location(tokenLocation()); currentScope()->useVariable(ident, m_vm.propertyNames->eval == *ident); if (currentScope()->isArrowFunction()) currentScope()->setInnerArrowFunctionUsesEval(); TreeExpression node = context.createResolve(location, *ident, start, lastTokenEndPosition()); return context.createProperty(ident, node, static_cast(PropertyNode::Constant | PropertyNode::Shorthand), SuperBinding::NotNeeded, InferName::Allowed, ClassElementTag::No); } if (match(EQUAL)) // CoverInitializedName is exclusive to BindingPattern and AssignmentPattern classifyExpressionError(ErrorIndicatesPattern); Optional type; if (!escaped) { if (*ident == m_vm.propertyNames->get) type = PropertyNode::Getter; else if (*ident == m_vm.propertyNames->set) type = PropertyNode::Setter; } if (!type) failWithMessage("Expected a ':' following the property name '", ident->impl(), "'"); return parseGetterSetter(context, type.value(), getterOrSetterStartOffset, ConstructorKind::None, ClassElementTag::No); } case DOUBLE: case INTEGER: { double propertyName = m_token.m_data.doubleValue; next(); if (match(OPENPAREN)) { const Identifier& ident = m_parserArena.identifierArena().makeNumericIdentifier(const_cast(m_vm), propertyName); auto method = parsePropertyMethod(context, &ident, parseMode); propagateError(); return context.createProperty(&ident, method, PropertyNode::Constant, SuperBinding::Needed, InferName::Allowed, ClassElementTag::No); } failIfTrue(parseMode != SourceParseMode::MethodMode, "Expected a parenthesis for argument list"); consumeOrFail(COLON, "Expected ':' after property name"); TreeExpression node = parseAssignmentExpression(context); failIfFalse(node, "Cannot parse expression for property declaration"); context.setEndOffset(node, m_lexer->currentOffset()); return context.createProperty(const_cast(m_vm), m_parserArena, propertyName, node, PropertyNode::Constant, SuperBinding::NotNeeded, ClassElementTag::No); } case BIGINT: { const Identifier* ident = &m_parserArena.identifierArena().makeBigIntDecimalIdentifier(const_cast(m_vm), *m_token.m_data.bigIntString, m_token.m_data.radix); next(); if (match(OPENPAREN)) { auto method = parsePropertyMethod(context, ident, parseMode); propagateError(); return context.createProperty(ident, method, PropertyNode::Constant, SuperBinding::Needed, InferName::Allowed, ClassElementTag::No); } failIfTrue(parseMode != SourceParseMode::MethodMode, "Expected a parenthesis for argument list"); consumeOrFail(COLON, "Expected ':' after property name"); TreeExpression node = parseAssignmentExpression(context); failIfFalse(node, "Cannot parse expression for property declaration"); context.setEndOffset(node, m_lexer->currentOffset()); return context.createProperty(ident, node, PropertyNode::Constant, SuperBinding::NotNeeded, InferName::Allowed, ClassElementTag::No); } case OPENBRACKET: { next(); auto propertyName = parseAssignmentExpression(context); failIfFalse(propertyName, "Cannot parse computed property name"); handleProductionOrFail(CLOSEBRACKET, "]", "end", "computed property name"); if (match(OPENPAREN)) { auto method = parsePropertyMethod(context, &m_vm.propertyNames->nullIdentifier, parseMode); propagateError(); return context.createProperty(propertyName, method, static_cast(PropertyNode::Constant | PropertyNode::Computed), SuperBinding::Needed, ClassElementTag::No); } failIfTrue(parseMode != SourceParseMode::MethodMode, "Expected a parenthesis for argument list"); consumeOrFail(COLON, "Expected ':' after property name"); TreeExpression node = parseAssignmentExpression(context); failIfFalse(node, "Cannot parse expression for property declaration"); context.setEndOffset(node, m_lexer->currentOffset()); return context.createProperty(propertyName, node, static_cast(PropertyNode::Constant | PropertyNode::Computed), SuperBinding::NotNeeded, ClassElementTag::No); } case DOTDOTDOT: { auto spreadLocation = m_token.m_location; auto start = m_token.m_startPosition; auto divot = m_token.m_endPosition; next(); TreeExpression elem = parseAssignmentExpressionOrPropagateErrorClass(context); failIfFalse(elem, "Cannot parse subject of a spread operation"); auto node = context.createObjectSpreadExpression(spreadLocation, elem, start, divot, m_lastTokenEndPosition); return context.createProperty(node, PropertyNode::Spread, SuperBinding::NotNeeded, ClassElementTag::No); } default: failIfFalse(m_token.m_type & KeywordTokenFlag, "Expected a property name"); wasIdent = true; // Treat keyword token as an identifier goto namedProperty; } } template template TreeExpression Parser::parsePropertyMethod(TreeBuilder& context, const Identifier* methodName, SourceParseMode parseMode) { ASSERT(isMethodParseMode(parseMode)); JSTokenLocation methodLocation(tokenLocation()); unsigned methodStart = tokenStart(); ParserFunctionInfo methodInfo; methodInfo.name = methodName; failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, parseMode, false, ConstructorKind::None, SuperBinding::Needed, methodStart, methodInfo, FunctionDefinitionType::Method)), "Cannot parse this method"); return context.createMethodDefinition(methodLocation, methodInfo); } template template TreeProperty Parser::parseGetterSetter(TreeBuilder& context, PropertyNode::Type type, unsigned getterOrSetterStartOffset, ConstructorKind constructorKind, ClassElementTag tag) { const Identifier* stringPropertyName = nullptr; double numericPropertyName = 0; TreeExpression computedPropertyName = 0; JSTokenLocation location(tokenLocation()); if (matchSpecIdentifier() || match(STRING) || m_token.m_type & KeywordTokenFlag) { stringPropertyName = m_token.m_data.ident; semanticFailIfTrue(tag == ClassElementTag::Static && *stringPropertyName == m_vm.propertyNames->prototype, "Cannot declare a static method named 'prototype'"); semanticFailIfTrue(tag == ClassElementTag::Instance && *stringPropertyName == m_vm.propertyNames->constructor, "Cannot declare a getter or setter named 'constructor'"); next(); } else if (match(DOUBLE) || match(INTEGER)) { numericPropertyName = m_token.m_data.doubleValue; next(); } else if (match(BIGINT)) { stringPropertyName = &m_parserArena.identifierArena().makeBigIntDecimalIdentifier(const_cast(m_vm), *m_token.m_data.bigIntString, m_token.m_data.radix); next(); } else if (match(OPENBRACKET)) { next(); computedPropertyName = parseAssignmentExpression(context); failIfFalse(computedPropertyName, "Cannot parse computed property name"); handleProductionOrFail(CLOSEBRACKET, "]", "end", "computed property name"); } else failDueToUnexpectedToken(); ParserFunctionInfo info; if (type & PropertyNode::Getter) { failIfFalse(match(OPENPAREN), "Expected a parameter list for getter definition"); failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, SourceParseMode::GetterMode, false, constructorKind, SuperBinding::Needed, getterOrSetterStartOffset, info, FunctionDefinitionType::Method)), "Cannot parse getter definition"); } else { failIfFalse(match(OPENPAREN), "Expected a parameter list for setter definition"); failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, SourceParseMode::SetterMode, false, constructorKind, SuperBinding::Needed, getterOrSetterStartOffset, info, FunctionDefinitionType::Method)), "Cannot parse setter definition"); } if (stringPropertyName) return context.createGetterOrSetterProperty(location, type, stringPropertyName, info, tag); if (computedPropertyName) return context.createGetterOrSetterProperty(location, static_cast(type | PropertyNode::Computed), computedPropertyName, info, tag); return context.createGetterOrSetterProperty(const_cast(m_vm), m_parserArena, location, type, numericPropertyName, info, tag); } template void Parser::recordPauseLocation(const JSTextPosition& position) { if (LIKELY(!m_debuggerParseData)) return; if (position.line < 0) return; m_debuggerParseData->pausePositions.appendPause(position); } template void Parser::recordFunctionEntryLocation(const JSTextPosition& position) { if (LIKELY(!m_debuggerParseData)) return; m_debuggerParseData->pausePositions.appendEntry(position); } template void Parser::recordFunctionLeaveLocation(const JSTextPosition& position) { if (LIKELY(!m_debuggerParseData)) return; m_debuggerParseData->pausePositions.appendLeave(position); } template template TreeExpression Parser::parseObjectLiteral(TreeBuilder& context) { consumeOrFail(OPENBRACE, "Expected opening '{' at the start of an object literal"); int oldNonLHSCount = m_parserState.nonLHSCount; JSTokenLocation location(tokenLocation()); if (match(CLOSEBRACE)) { next(); return context.createObjectLiteral(location); } TreeProperty property = parseProperty(context); failIfFalse(property, "Cannot parse object literal property"); bool seenProtoSetter = context.isUnderscoreProtoSetter(property); TreePropertyList propertyList = context.createPropertyList(location, property); TreePropertyList tail = propertyList; while (match(COMMA)) { next(); if (match(CLOSEBRACE)) break; JSTokenLocation propertyLocation(tokenLocation()); property = parseProperty(context); failIfFalse(property, "Cannot parse object literal property"); if (context.isUnderscoreProtoSetter(property)) { // https://tc39.es/ecma262/#sec-__proto__-property-names-in-object-initializers semanticFailIfTrue(seenProtoSetter, "Attempted to redefine __proto__ property"); seenProtoSetter = true; } tail = context.createPropertyList(propertyLocation, property, tail); } location = tokenLocation(); handleProductionOrFail2(CLOSEBRACE, "}", "end", "object literal"); m_parserState.nonLHSCount = oldNonLHSCount; return context.createObjectLiteral(location, propertyList); } template template TreeExpression Parser::parseArrayLiteral(TreeBuilder& context) { consumeOrFailWithFlags(OPENBRACKET, TreeBuilder::DontBuildStrings, "Expected an opening '[' at the beginning of an array literal"); int oldNonLHSCount = m_parserState.nonLHSCount; int elisions = 0; while (match(COMMA)) { next(TreeBuilder::DontBuildStrings); elisions++; } if (match(CLOSEBRACKET)) { JSTokenLocation location(tokenLocation()); next(TreeBuilder::DontBuildStrings); return context.createArray(location, elisions); } TreeExpression elem; if (UNLIKELY(match(DOTDOTDOT))) { auto spreadLocation = m_token.m_location; auto start = m_token.m_startPosition; auto divot = m_token.m_endPosition; next(); auto spreadExpr = parseAssignmentExpressionOrPropagateErrorClass(context); failIfFalse(spreadExpr, "Cannot parse subject of a spread operation"); elem = context.createSpreadExpression(spreadLocation, spreadExpr, start, divot, m_lastTokenEndPosition); } else elem = parseAssignmentExpressionOrPropagateErrorClass(context); failIfFalse(elem, "Cannot parse array literal element"); typename TreeBuilder::ElementList elementList = context.createElementList(elisions, elem); typename TreeBuilder::ElementList tail = elementList; elisions = 0; while (match(COMMA)) { next(TreeBuilder::DontBuildStrings); elisions = 0; while (match(COMMA)) { next(); elisions++; } if (match(CLOSEBRACKET)) { JSTokenLocation location(tokenLocation()); next(TreeBuilder::DontBuildStrings); return context.createArray(location, elisions, elementList); } if (UNLIKELY(match(DOTDOTDOT))) { auto spreadLocation = m_token.m_location; auto start = m_token.m_startPosition; auto divot = m_token.m_endPosition; next(); TreeExpression elem = parseAssignmentExpressionOrPropagateErrorClass(context); failIfFalse(elem, "Cannot parse subject of a spread operation"); auto spread = context.createSpreadExpression(spreadLocation, elem, start, divot, m_lastTokenEndPosition); tail = context.createElementList(tail, elisions, spread); continue; } TreeExpression elem = parseAssignmentExpressionOrPropagateErrorClass(context); failIfFalse(elem, "Cannot parse array literal element"); tail = context.createElementList(tail, elisions, elem); } JSTokenLocation location(tokenLocation()); if (!consume(CLOSEBRACKET)) { failIfFalse(match(DOTDOTDOT), "Expected either a closing ']' or a ',' following an array element"); semanticFail("The '...' operator should come before a target expression"); } m_parserState.nonLHSCount = oldNonLHSCount; return context.createArray(location, elementList); } template template TreeClassExpression Parser::parseClassExpression(TreeBuilder& context) { ASSERT(match(CLASSTOKEN)); ParserClassInfo info; info.className = &m_vm.propertyNames->nullIdentifier; return parseClass(context, FunctionNameRequirements::None, info); } template template TreeExpression Parser::parseFunctionExpression(TreeBuilder& context) { ASSERT(match(FUNCTION)); JSTokenLocation location(tokenLocation()); unsigned functionKeywordStart = tokenStart(); next(); ParserFunctionInfo functionInfo; functionInfo.name = &m_vm.propertyNames->nullIdentifier; SourceParseMode parseMode = SourceParseMode::NormalFunctionMode; if (consume(TIMES)) parseMode = SourceParseMode::GeneratorWrapperFunctionMode; failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::None, parseMode, false, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Expression)), "Cannot parse function expression"); return context.createFunctionExpr(location, functionInfo); } template template TreeExpression Parser::parseAsyncFunctionExpression(TreeBuilder& context) { ASSERT(match(FUNCTION)); JSTokenLocation location(tokenLocation()); unsigned functionKeywordStart = tokenStart(); next(); SourceParseMode parseMode = SourceParseMode::AsyncFunctionMode; if (consume(TIMES)) parseMode = SourceParseMode::AsyncGeneratorWrapperFunctionMode; ParserFunctionInfo functionInfo; functionInfo.name = &m_vm.propertyNames->nullIdentifier; failIfFalse(parseFunctionInfo(context, FunctionNameRequirements::None, parseMode, false, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, functionInfo, FunctionDefinitionType::Expression), parseMode == SourceParseMode::AsyncFunctionMode ? "Cannot parse async function expression" : "Cannot parse async generator function expression"); return context.createFunctionExpr(location, functionInfo); } template template typename TreeBuilder::TemplateString Parser::parseTemplateString(TreeBuilder& context, bool isTemplateHead, typename LexerType::RawStringsBuildMode rawStringsBuildMode, bool& elementIsTail) { if (isTemplateHead) ASSERT(match(BACKQUOTE)); else matchOrFail(CLOSEBRACE, "Expected a closing '}' following an expression in template literal"); // Re-scan the token to recognize it as Template Element. m_token.m_type = m_lexer->scanTemplateString(&m_token, rawStringsBuildMode); matchOrFail(TEMPLATE, "Expected an template element"); const Identifier* cooked = m_token.m_data.cooked; const Identifier* raw = m_token.m_data.raw; elementIsTail = m_token.m_data.isTail; JSTokenLocation location(tokenLocation()); next(); return context.createTemplateString(location, cooked, raw); } template template typename TreeBuilder::TemplateLiteral Parser::parseTemplateLiteral(TreeBuilder& context, typename LexerType::RawStringsBuildMode rawStringsBuildMode) { ASSERT(match(BACKQUOTE)); JSTokenLocation location(tokenLocation()); bool elementIsTail = false; auto headTemplateString = parseTemplateString(context, true, rawStringsBuildMode, elementIsTail); failIfFalse(headTemplateString, "Cannot parse head template element"); typename TreeBuilder::TemplateStringList templateStringList = context.createTemplateStringList(headTemplateString); typename TreeBuilder::TemplateStringList templateStringTail = templateStringList; if (elementIsTail) return context.createTemplateLiteral(location, templateStringList); failIfTrue(match(CLOSEBRACE), "Template literal expression cannot be empty"); TreeExpression expression = parseExpression(context); failIfFalse(expression, "Cannot parse expression in template literal"); typename TreeBuilder::TemplateExpressionList templateExpressionList = context.createTemplateExpressionList(expression); typename TreeBuilder::TemplateExpressionList templateExpressionTail = templateExpressionList; auto templateString = parseTemplateString(context, false, rawStringsBuildMode, elementIsTail); failIfFalse(templateString, "Cannot parse template element"); templateStringTail = context.createTemplateStringList(templateStringTail, templateString); while (!elementIsTail) { failIfTrue(match(CLOSEBRACE), "Template literal expression cannot be empty"); TreeExpression expression = parseExpression(context); failIfFalse(expression, "Cannot parse expression in template literal"); templateExpressionTail = context.createTemplateExpressionList(templateExpressionTail, expression); auto templateString = parseTemplateString(context, false, rawStringsBuildMode, elementIsTail); failIfFalse(templateString, "Cannot parse template element"); templateStringTail = context.createTemplateStringList(templateStringTail, templateString); } return context.createTemplateLiteral(location, templateStringList, templateExpressionList); } template template TreeExpression Parser::createResolveAndUseVariable(TreeBuilder& context, const Identifier* ident, bool isEval, const JSTextPosition& start, const JSTokenLocation& location) { currentScope()->useVariable(ident, isEval); m_parserState.lastIdentifier = ident; return context.createResolve(location, *ident, start, lastTokenEndPosition()); } template template TreeExpression Parser::parsePrimaryExpression(TreeBuilder& context) { failIfStackOverflow(); switch (m_token.m_type) { case FUNCTION: return parseFunctionExpression(context); case CLASSTOKEN: return parseClassExpression(context); case OPENBRACE: return parseObjectLiteral(context); case OPENBRACKET: return parseArrayLiteral(context); case OPENPAREN: { next(); int oldNonLHSCount = m_parserState.nonLHSCount; TreeExpression result = parseExpression(context); m_parserState.nonLHSCount = oldNonLHSCount; handleProductionOrFail(CLOSEPAREN, ")", "end", "compound expression"); return result; } case THISTOKEN: { JSTokenLocation location(tokenLocation()); next(); if (currentScope()->isArrowFunction()) currentScope()->setInnerArrowFunctionUsesThis(); return context.createThisExpr(location); } case AWAIT: if (m_parserState.functionParsePhase == FunctionParsePhase::Parameters) semanticFailIfFalse(m_parserState.allowAwait, "Cannot use 'await' within a parameter default expression"); else if (currentFunctionScope()->isAsyncFunctionBoundary()) return parseAwaitExpression(context); goto identifierExpression; case IDENT: { if (UNLIKELY(*m_token.m_data.ident == m_vm.propertyNames->async && !m_token.m_data.escaped)) { JSTextPosition start = tokenStartPosition(); const Identifier* ident = m_token.m_data.ident; JSTokenLocation location(tokenLocation()); next(); if (match(FUNCTION) && !m_lexer->hasLineTerminatorBeforeToken()) return parseAsyncFunctionExpression(context); // Avoid using variable if it is an arrow function parameter if (UNLIKELY(match(ARROWFUNCTION))) return 0; const bool isEval = false; return createResolveAndUseVariable(context, ident, isEval, start, location); } if (UNLIKELY(m_parserState.isParsingClassFieldInitializer)) failIfTrue(*m_token.m_data.ident == m_vm.propertyNames->arguments, "Cannot reference 'arguments' in class field initializer"); identifierExpression: JSTextPosition start = tokenStartPosition(); const Identifier* ident = m_token.m_data.ident; if (UNLIKELY(currentScope()->evalContextType() == EvalContextType::InstanceFieldEvalContext)) failIfTrue(*ident == m_vm.propertyNames->arguments, "arguments is not valid in this context"); JSTokenLocation location(tokenLocation()); next(); // Avoid using variable if it is an arrow function parameter if (UNLIKELY(match(ARROWFUNCTION))) return 0; return createResolveAndUseVariable(context, ident, *ident == m_vm.propertyNames->eval, start, location); } case BIGINT: { const Identifier* ident = m_token.m_data.bigIntString; uint8_t radix = m_token.m_data.radix; JSTokenLocation location(tokenLocation()); next(); return context.createBigInt(location, ident, radix); } case STRING: { const Identifier* ident = m_token.m_data.ident; JSTokenLocation location(tokenLocation()); next(); return context.createString(location, ident); } case DOUBLE: { double d = m_token.m_data.doubleValue; JSTokenLocation location(tokenLocation()); next(); return context.createDoubleExpr(location, d); } case INTEGER: { double d = m_token.m_data.doubleValue; JSTokenLocation location(tokenLocation()); next(); return context.createIntegerExpr(location, d); } case NULLTOKEN: { JSTokenLocation location(tokenLocation()); next(); return context.createNull(location); } case TRUETOKEN: { JSTokenLocation location(tokenLocation()); next(); return context.createBoolean(location, true); } case FALSETOKEN: { JSTokenLocation location(tokenLocation()); next(); return context.createBoolean(location, false); } case DIVEQUAL: case DIVIDE: { /* regexp */ if (match(DIVEQUAL)) m_token.m_type = m_lexer->scanRegExp(&m_token, '='); else m_token.m_type = m_lexer->scanRegExp(&m_token); matchOrFail(REGEXP, "Invalid regular expression"); const Identifier* pattern = m_token.m_data.pattern; const Identifier* flags = m_token.m_data.flags; JSTextPosition start = tokenStartPosition(); JSTokenLocation location(tokenLocation()); next(); TreeExpression re = context.createRegExp(location, *pattern, *flags, start); if (!re) { Yarr::ErrorCode errorCode = Yarr::checkSyntax(pattern->string(), flags->string()); regexFail(Yarr::errorMessage(errorCode)); } return re; } case BACKQUOTE: return parseTemplateLiteral(context, LexerType::RawStringsBuildMode::DontBuildRawStrings); case YIELD: if (!strictMode() && !currentScope()->isGenerator()) goto identifierExpression; failDueToUnexpectedToken(); case LET: if (!strictMode()) goto identifierExpression; FALLTHROUGH; default: failDueToUnexpectedToken(); } } template template TreeArguments Parser::parseArguments(TreeBuilder& context) { consumeOrFailWithFlags(OPENPAREN, TreeBuilder::DontBuildStrings, "Expected opening '(' at start of argument list"); JSTokenLocation location(tokenLocation()); if (match(CLOSEPAREN)) { next(TreeBuilder::DontBuildStrings); return context.createArguments(); } auto argumentsStart = m_token.m_startPosition; auto argumentsDivot = m_token.m_endPosition; int initialAssignments = m_parserState.assignmentCount; ArgumentType argType = ArgumentType::Normal; TreeExpression firstArg = parseArgument(context, argType); failIfFalse(firstArg, "Cannot parse function argument"); semanticFailIfTrue(match(DOTDOTDOT), "The '...' operator should come before the target expression"); bool hasSpread = false; if (argType == ArgumentType::Spread) hasSpread = true; TreeArgumentsList argList = context.createArgumentsList(location, firstArg); TreeArgumentsList tail = argList; while (match(COMMA)) { JSTokenLocation argumentLocation(tokenLocation()); next(TreeBuilder::DontBuildStrings); if (UNLIKELY(match(CLOSEPAREN))) break; TreeExpression arg = parseArgument(context, argType); propagateError(); semanticFailIfTrue(match(DOTDOTDOT), "The '...' operator should come before the target expression"); if (argType == ArgumentType::Spread) hasSpread = true; tail = context.createArgumentsList(argumentLocation, tail, arg); } handleProductionOrFail2(CLOSEPAREN, ")", "end", "argument list"); if (hasSpread) { TreeExpression spreadArray = context.createSpreadExpression(location, context.createArray(location, context.createElementList(argList)), argumentsStart, argumentsDivot, m_lastTokenEndPosition); return context.createArguments(context.createArgumentsList(location, spreadArray), initialAssignments != m_parserState.assignmentCount); } return context.createArguments(argList, initialAssignments != m_parserState.assignmentCount); } template template TreeExpression Parser::parseArgument(TreeBuilder& context, ArgumentType& type) { if (UNLIKELY(match(DOTDOTDOT))) { JSTokenLocation spreadLocation(tokenLocation()); auto start = m_token.m_startPosition; auto divot = m_token.m_endPosition; next(); TreeExpression spreadExpr = parseAssignmentExpression(context); propagateError(); auto end = m_lastTokenEndPosition; type = ArgumentType::Spread; return context.createSpreadExpression(spreadLocation, spreadExpr, start, divot, end); } type = ArgumentType::Normal; return parseAssignmentExpression(context); } template ::value>::type> static inline void recordCallOrApplyDepth(ParserType* parser, VM& vm, Optional& callOrApplyDepthScope, ExpressionNode* expression) { if (expression->isDotAccessorNode()) { DotAccessorNode* dot = static_cast(expression); bool isCallOrApply = dot->identifier() == vm.propertyNames->builtinNames().callPublicName() || dot->identifier() == vm.propertyNames->builtinNames().applyPublicName(); if (isCallOrApply) callOrApplyDepthScope.emplace(parser); } } template ::value>::type> static inline void recordCallOrApplyDepth(ParserType*, VM&, Optional&, SyntaxChecker::Expression) { } template bool Parser::usePrivateName(const Identifier* ident) { if (m_lexer->isReparsingFunction()) return true; if (auto maybeCurrent = findPrivateNameScope()) { ScopeRef current = maybeCurrent.value(); if (!current->hasPrivateName(*ident)) current->usePrivateName(*ident); return true; } return false; } template template TreeExpression Parser::parseMemberExpression(TreeBuilder& context) { TreeExpression base = 0; JSTextPosition expressionStart = tokenStartPosition(); int newCount = 0; JSTokenLocation startLocation = tokenLocation(); JSTokenLocation lastNewTokenLocation; while (match(NEW)) { lastNewTokenLocation = tokenLocation(); next(); newCount++; } JSTokenLocation location = tokenLocation(); bool baseIsSuper = match(SUPER); bool previousBaseWasSuper = false; bool baseIsImport = match(IMPORT); bool baseIsNewTarget = false; if (newCount && match(DOT)) { next(); if (matchContextualKeyword(m_vm.propertyNames->target)) { ScopeRef closestOrdinaryFunctionScope = closestParentOrdinaryFunctionNonLexicalScope(); bool isClassFieldInitializer = m_parserState.isParsingClassFieldInitializer; bool isFunctionEvalContextType = m_isInsideOrdinaryFunction && (closestOrdinaryFunctionScope->evalContextType() == EvalContextType::FunctionEvalContext || closestOrdinaryFunctionScope->evalContextType() == EvalContextType::InstanceFieldEvalContext); semanticFailIfFalse(currentScope()->isFunction() || isFunctionEvalContextType || isClassFieldInitializer, "new.target is only valid inside functions"); baseIsNewTarget = true; if (currentScope()->isArrowFunction()) { semanticFailIfFalse(!closestOrdinaryFunctionScope->isGlobalCodeScope() || isFunctionEvalContextType || isClassFieldInitializer, "new.target is not valid inside arrow functions in global code"); currentScope()->setInnerArrowFunctionUsesNewTarget(); } ASSERT(lastNewTokenLocation.line); base = context.createNewTargetExpr(lastNewTokenLocation); newCount--; next(); } else { failIfTrue(match(IDENT), "\"new.\" can only followed with target"); failDueToUnexpectedToken(); } } bool baseIsAsyncKeyword = false; if (baseIsSuper) { ScopeRef closestOrdinaryFunctionScope = closestParentOrdinaryFunctionNonLexicalScope(); ScopeRef classScope = closestClassScopeOrTopLevelScope(); bool isClassFieldInitializer = classScope.index() > closestOrdinaryFunctionScope.index(); semanticFailIfFalse(currentScope()->isFunction() || isClassFieldInitializer || (closestOrdinaryFunctionScope->isEvalContext() && closestOrdinaryFunctionScope->expectedSuperBinding() == SuperBinding::Needed), "super is not valid in this context"); base = context.createSuperExpr(location); next(); failIfTrue(match(OPENPAREN) && currentScope()->evalContextType() == EvalContextType::InstanceFieldEvalContext, "super call is not valid in this context"); ScopeRef functionScope = currentFunctionScope(); if (!functionScope->setNeedsSuperBinding()) { // It unnecessary to check of using super during reparsing one more time. Also it can lead to syntax error // in case of arrow function because during reparsing we don't know whether we currently parse the arrow function // inside of the constructor or method. if (!m_lexer->isReparsingFunction()) { SuperBinding functionSuperBinding = !functionScope->isArrowFunction() && !closestOrdinaryFunctionScope->isEvalContext() ? functionScope->expectedSuperBinding() : closestOrdinaryFunctionScope->expectedSuperBinding(); semanticFailIfTrue(functionSuperBinding == SuperBinding::NotNeeded && !isClassFieldInitializer, "super is not valid in this context"); } } } else if (baseIsImport) { next(); JSTextPosition expressionEnd = lastTokenEndPosition(); if (consume(DOT)) { if (matchContextualKeyword(m_vm.propertyNames->builtinNames().metaPublicName())) { semanticFailIfFalse(m_scriptMode == JSParserScriptMode::Module, "import.meta is only valid inside modules"); base = context.createImportMetaExpr(location, createResolveAndUseVariable(context, &m_vm.propertyNames->metaPrivateName, false, expressionStart, location)); next(); } else { failIfTrue(match(IDENT), "\"import.\" can only followed with meta"); failDueToUnexpectedToken(); } } else { semanticFailIfTrue(newCount, "Cannot use new with import"); consumeOrFail(OPENPAREN, "import call expects exactly one argument"); TreeExpression expr = parseAssignmentExpression(context); failIfFalse(expr, "Cannot parse expression"); consumeOrFail(CLOSEPAREN, "import call expects exactly one argument"); base = context.createImportExpr(location, expr, expressionStart, expressionEnd, lastTokenEndPosition()); } } else if (!baseIsNewTarget) { const bool isAsync = matchContextualKeyword(m_vm.propertyNames->async); base = parsePrimaryExpression(context); failIfFalse(base, "Cannot parse base expression"); if (UNLIKELY(isAsync && context.isResolve(base) && !m_lexer->hasLineTerminatorBeforeToken())) { if (matchSpecIdentifier()) { // AsyncArrowFunction forceClassifyExpressionError(ErrorIndicatesAsyncArrowFunction); failDueToUnexpectedToken(); } baseIsAsyncKeyword = true; } } failIfFalse(base, "Cannot parse base expression"); do { TreeExpression optionalChainBase = 0; JSTokenLocation optionalChainLocation; JSTokenType type = m_token.m_type; if (match(QUESTIONDOT)) { semanticFailIfTrue(newCount, "Cannot call constructor in an optional chain"); semanticFailIfTrue(baseIsSuper, "Cannot use super as the base of an optional chain"); optionalChainBase = base; optionalChainLocation = tokenLocation(); SavePoint savePoint = createSavePoint(context); next(); if (match(OPENBRACKET) || match(OPENPAREN) || match(BACKQUOTE)) type = m_token.m_type; else { type = DOT; restoreSavePoint(context, savePoint); } } while (true) { location = tokenLocation(); switch (type) { case OPENBRACKET: { m_parserState.nonTrivialExpressionCount++; JSTextPosition expressionEnd = lastTokenEndPosition(); next(); int nonLHSCount = m_parserState.nonLHSCount; int initialAssignments = m_parserState.assignmentCount; TreeExpression property = parseExpression(context); failIfFalse(property, "Cannot parse subscript expression"); base = context.createBracketAccess(startLocation, base, property, initialAssignments != m_parserState.assignmentCount, expressionStart, expressionEnd, tokenEndPosition()); if (UNLIKELY(baseIsSuper && currentScope()->isArrowFunction())) currentFunctionScope()->setInnerArrowFunctionUsesSuperProperty(); handleProductionOrFail(CLOSEBRACKET, "]", "end", "subscript expression"); m_parserState.nonLHSCount = nonLHSCount; break; } case OPENPAREN: { if (baseIsSuper) failIfTrue(m_parserState.isParsingClassFieldInitializer, "super call is not valid in class field initializer context"); m_parserState.nonTrivialExpressionCount++; int nonLHSCount = m_parserState.nonLHSCount; if (newCount) { newCount--; semanticFailIfTrue(baseIsSuper, "Cannot use new with super call"); JSTextPosition expressionEnd = lastTokenEndPosition(); TreeArguments arguments = parseArguments(context); failIfFalse(arguments, "Cannot parse call arguments"); base = context.createNewExpr(location, base, arguments, expressionStart, expressionEnd, lastTokenEndPosition()); } else { size_t usedVariablesSize = currentScope()->currentUsedVariablesSize(); JSTextPosition expressionEnd = lastTokenEndPosition(); Optional callOrApplyDepthScope; recordCallOrApplyDepth(this, m_vm, callOrApplyDepthScope, base); TreeArguments arguments = parseArguments(context); if (baseIsAsyncKeyword && (!arguments || match(ARROWFUNCTION))) { currentScope()->revertToPreviousUsedVariables(usedVariablesSize); forceClassifyExpressionError(ErrorIndicatesAsyncArrowFunction); failDueToUnexpectedToken(); } failIfFalse(arguments, "Cannot parse call arguments"); if (baseIsSuper) { ScopeRef functionScope = currentFunctionScope(); if (!functionScope->setHasDirectSuper()) { // It unnecessary to check of using super during reparsing one more time. Also it can lead to syntax error // in case of arrow function because during reparsing we don't know whether we currently parse the arrow function // inside of the constructor or method. if (!m_lexer->isReparsingFunction()) { ScopeRef closestOrdinaryFunctionScope = closestParentOrdinaryFunctionNonLexicalScope(); ConstructorKind functionConstructorKind = !functionScope->isArrowFunction() && !closestOrdinaryFunctionScope->isEvalContext() ? functionScope->constructorKind() : closestOrdinaryFunctionScope->constructorKind(); semanticFailIfTrue(functionConstructorKind == ConstructorKind::None, "super is not valid in this context"); semanticFailIfTrue(functionConstructorKind != ConstructorKind::Extends, "super is not valid in this context"); } } if (currentScope()->isArrowFunction()) functionScope->setInnerArrowFunctionUsesSuperCall(); } bool isOptionalCall = optionalChainLocation.endOffset == static_cast(expressionEnd.offset); base = context.makeFunctionCallNode(startLocation, base, previousBaseWasSuper, arguments, expressionStart, expressionEnd, lastTokenEndPosition(), callOrApplyDepthScope ? callOrApplyDepthScope->distanceToInnermostChild() : 0, isOptionalCall); if (isOptionalCall) optionalChainBase = base; } m_parserState.nonLHSCount = nonLHSCount; break; } case DOT: { m_parserState.nonTrivialExpressionCount++; JSTextPosition expressionEnd = lastTokenEndPosition(); nextExpectIdentifier(TreeBuilder::DontBuildKeywords | LexerFlags::IgnoreReservedWords); const Identifier* ident = m_token.m_data.ident; auto type = DotType::Name; if (match(PRIVATENAME)) { ASSERT(ident); failIfTrue(baseIsSuper, "Cannot access private names from super"); if (UNLIKELY(currentScope()->evalContextType() == EvalContextType::InstanceFieldEvalContext)) semanticFailIfFalse(currentScope()->hasPrivateName(*ident), "Cannot reference undeclared private field '", ident->impl(), "'"); semanticFailIfFalse(usePrivateName(ident), "Cannot reference private names outside of class"); m_parserState.lastPrivateName = ident; currentScope()->useVariable(ident, false); type = DotType::PrivateField; m_token.m_type = IDENT; } matchOrFail(IDENT, "Expected a property name after ", optionalChainBase ? "'?.'" : "'.'"); base = context.createDotAccess(startLocation, base, ident, type, expressionStart, expressionEnd, tokenEndPosition()); if (UNLIKELY(baseIsSuper && currentScope()->isArrowFunction())) currentFunctionScope()->setInnerArrowFunctionUsesSuperProperty(); next(); break; } case BACKQUOTE: { semanticFailIfTrue(optionalChainBase, "Cannot use tagged templates in an optional chain"); semanticFailIfTrue(baseIsSuper, "Cannot use super as tag for tagged templates"); JSTextPosition expressionEnd = lastTokenEndPosition(); int nonLHSCount = m_parserState.nonLHSCount; typename TreeBuilder::TemplateLiteral templateLiteral = parseTemplateLiteral(context, LexerType::RawStringsBuildMode::BuildRawStrings); failIfFalse(templateLiteral, "Cannot parse template literal"); base = context.createTaggedTemplate(startLocation, base, templateLiteral, expressionStart, expressionEnd, lastTokenEndPosition()); m_parserState.nonLHSCount = nonLHSCount; m_seenTaggedTemplate = true; break; } default: goto endOfChain; } previousBaseWasSuper = baseIsSuper; baseIsSuper = false; type = m_token.m_type; } endOfChain: if (optionalChainBase) base = context.createOptionalChain(optionalChainLocation, optionalChainBase, base, !match(QUESTIONDOT)); } while (match(QUESTIONDOT)); semanticFailIfTrue(baseIsSuper, newCount ? "Cannot use new with super call" : "super is not valid in this context"); while (newCount--) base = context.createNewExpr(location, base, expressionStart, lastTokenEndPosition()); return base; } template template TreeExpression Parser::parseArrowFunctionExpression(TreeBuilder& context, bool isAsync) { JSTokenLocation location; unsigned functionKeywordStart = tokenStart(); location = tokenLocation(); ParserFunctionInfo info; info.name = &m_vm.propertyNames->nullIdentifier; SourceParseMode parseMode = isAsync ? SourceParseMode::AsyncArrowFunctionMode : SourceParseMode::ArrowFunctionMode; failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, parseMode, true, ConstructorKind::None, SuperBinding::NotNeeded, functionKeywordStart, info, FunctionDefinitionType::Expression)), "Cannot parse arrow function expression"); return context.createArrowFunctionExpr(location, info); } static const char* operatorString(bool prefix, unsigned tok) { switch (tok) { case MINUSMINUS: case AUTOMINUSMINUS: return prefix ? "prefix-decrement" : "decrement"; case PLUSPLUS: case AUTOPLUSPLUS: return prefix ? "prefix-increment" : "increment"; case EXCLAMATION: return "logical-not"; case TILDE: return "bitwise-not"; case TYPEOF: return "typeof"; case VOIDTOKEN: return "void"; case DELETETOKEN: return "delete"; } RELEASE_ASSERT_NOT_REACHED(); return "error"; } template template TreeExpression Parser::parseUnaryExpression(TreeBuilder& context) { typename TreeBuilder::UnaryExprContext unaryExprContext(context); AllowInOverride allowInOverride(this); int tokenStackDepth = 0; bool hasPrefixUpdateOp = false; unsigned lastOperator = 0; if (UNLIKELY(match(AWAIT) && currentFunctionScope()->isAsyncFunctionBoundary())) return parseAwaitExpression(context); JSTokenLocation location(tokenLocation()); int oldTokenStackDepth = context.unaryTokenStackDepth(); auto scopeExit = makeScopeExit([&] { ASSERT_UNUSED(oldTokenStackDepth, oldTokenStackDepth <= context.unaryTokenStackDepth()); }); while (isUnaryOp(m_token.m_type)) { semanticFailIfTrue(hasPrefixUpdateOp, "The ", operatorString(true, lastOperator), " operator requires a reference expression"); if (isUpdateOp(m_token.m_type)) hasPrefixUpdateOp = true; lastOperator = m_token.m_type; m_parserState.nonLHSCount++; context.appendUnaryToken(tokenStackDepth, m_token.m_type, tokenStartPosition()); next(); m_parserState.nonTrivialExpressionCount++; } JSTextPosition subExprStart = tokenStartPosition(); ASSERT(subExprStart.offset >= subExprStart.lineStartOffset); TreeExpression expr = parseMemberExpression(context); if (!expr) { if (lastOperator) failWithMessage("Cannot parse subexpression of ", operatorString(true, lastOperator), "operator"); failWithMessage("Cannot parse member expression"); } if constexpr (std::is_same_v) ASSERT(oldTokenStackDepth + tokenStackDepth == context.unaryTokenStackDepth()); if (isUpdateOp(static_cast(lastOperator))) { semanticFailIfTrue(context.isMetaProperty(expr), metaPropertyName(context, expr), " can't come after a prefix operator"); semanticFailIfFalse(isSimpleAssignmentTarget(context, expr), "Prefix ", lastOperator == PLUSPLUS ? "++" : "--", " operator applied to value that is not a reference"); } bool isEvalOrArguments = false; if (strictMode()) { if (context.isResolve(expr)) isEvalOrArguments = *m_parserState.lastIdentifier == m_vm.propertyNames->eval || *m_parserState.lastIdentifier == m_vm.propertyNames->arguments; } failIfTrueIfStrict(isEvalOrArguments && hasPrefixUpdateOp, "Cannot modify '", m_parserState.lastIdentifier->impl(), "' in strict mode"); switch (m_token.m_type) { case PLUSPLUS: semanticFailIfTrue(context.isMetaProperty(expr), metaPropertyName(context, expr), " can't come before a postfix operator"); semanticFailIfFalse(isSimpleAssignmentTarget(context, expr), "Postfix ++ operator applied to value that is not a reference"); m_parserState.nonTrivialExpressionCount++; m_parserState.nonLHSCount++; expr = context.makePostfixNode(location, expr, Operator::PlusPlus, subExprStart, lastTokenEndPosition(), tokenEndPosition()); m_parserState.assignmentCount++; failIfTrueIfStrict(isEvalOrArguments, "Cannot modify '", m_parserState.lastIdentifier->impl(), "' in strict mode"); semanticFailIfTrue(hasPrefixUpdateOp, "The ", operatorString(false, lastOperator), " operator requires a reference expression"); next(); break; case MINUSMINUS: semanticFailIfTrue(context.isMetaProperty(expr), metaPropertyName(context, expr), " can't come before a postfix operator"); semanticFailIfFalse(isSimpleAssignmentTarget(context, expr), "Postfix -- operator applied to value that is not a reference"); m_parserState.nonTrivialExpressionCount++; m_parserState.nonLHSCount++; expr = context.makePostfixNode(location, expr, Operator::MinusMinus, subExprStart, lastTokenEndPosition(), tokenEndPosition()); m_parserState.assignmentCount++; failIfTrueIfStrict(isEvalOrArguments, "'", m_parserState.lastIdentifier->impl(), "' cannot be modified in strict mode"); semanticFailIfTrue(hasPrefixUpdateOp, "The ", operatorString(false, lastOperator), " operator requires a reference expression"); next(); break; default: break; } JSTextPosition end = lastTokenEndPosition(); while (tokenStackDepth) { subExprStart = context.unaryTokenStackLastStart(tokenStackDepth); auto tokenType = context.unaryTokenStackLastType(tokenStackDepth); switch (tokenType) { case EXCLAMATION: expr = context.createLogicalNot(location, expr); break; case TILDE: expr = context.makeBitwiseNotNode(location, expr); break; case MINUS: expr = context.makeNegateNode(location, expr); break; case PLUS: expr = context.createUnaryPlus(location, expr); break; case PLUSPLUS: case AUTOPLUSPLUS: ASSERT(isSimpleAssignmentTarget(context, expr)); expr = context.makePrefixNode(location, expr, Operator::PlusPlus, subExprStart, subExprStart + 2, end); m_parserState.assignmentCount++; break; case MINUSMINUS: case AUTOMINUSMINUS: ASSERT(isSimpleAssignmentTarget(context, expr)); expr = context.makePrefixNode(location, expr, Operator::MinusMinus, subExprStart, subExprStart + 2, end); m_parserState.assignmentCount++; break; case TYPEOF: expr = context.makeTypeOfNode(location, expr); break; case VOIDTOKEN: expr = context.createVoid(location, expr); break; case DELETETOKEN: failIfTrueIfStrict(context.isResolve(expr), "Cannot delete unqualified property '", m_parserState.lastIdentifier->impl(), "' in strict mode"); semanticFailIfTrue(context.isPrivateLocation(expr), "Cannot delete private field ", m_parserState.lastPrivateName->impl()); expr = context.makeDeleteNode(location, expr, context.unaryTokenStackLastStart(tokenStackDepth), end, end); break; default: // If we get here something has gone horribly horribly wrong CRASH(); } context.unaryTokenStackRemoveLast(tokenStackDepth); } return expr; } template void Parser::printUnexpectedTokenText(WTF::PrintStream& out) { switch (m_token.m_type) { case EOFTOK: out.print("Unexpected end of script"); return; case UNTERMINATED_IDENTIFIER_ESCAPE_ERRORTOK: case UNTERMINATED_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK: out.print("Incomplete unicode escape in identifier: '", getToken(), "'"); return; case UNTERMINATED_MULTILINE_COMMENT_ERRORTOK: out.print("Unterminated multiline comment"); return; case UNTERMINATED_NUMERIC_LITERAL_ERRORTOK: out.print("Unterminated numeric literal '", getToken(), "'"); return; case UNTERMINATED_STRING_LITERAL_ERRORTOK: out.print("Unterminated string literal '", getToken(), "'"); return; case INVALID_IDENTIFIER_ESCAPE_ERRORTOK: out.print("Invalid escape in identifier: '", getToken(), "'"); return; case ESCAPED_KEYWORD: out.print("Unexpected escaped characters in keyword token: '", getToken(), "'"); return; case INVALID_IDENTIFIER_UNICODE_ESCAPE_ERRORTOK: out.print("Invalid unicode escape in identifier: '", getToken(), "'"); return; case INVALID_NUMERIC_LITERAL_ERRORTOK: out.print("Invalid numeric literal: '", getToken(), "'"); return; case UNTERMINATED_OCTAL_NUMBER_ERRORTOK: out.print("Invalid use of octal: '", getToken(), "'"); return; case INVALID_STRING_LITERAL_ERRORTOK: out.print("Invalid string literal: '", getToken(), "'"); return; case INVALID_UNICODE_ENCODING_ERRORTOK: out.print("Invalid unicode encoding: '", getToken(), "'"); return; case INVALID_IDENTIFIER_UNICODE_ERRORTOK: out.print("Invalid unicode code point in identifier: '", getToken(), "'"); return; case ERRORTOK: out.print("Unrecognized token '", getToken(), "'"); return; case STRING: out.print("Unexpected string literal ", getToken()); return; case INTEGER: case DOUBLE: out.print("Unexpected number '", getToken(), "'"); return; case RESERVED_IF_STRICT: out.print("Unexpected use of reserved word '", getToken(), "' in strict mode"); return; case RESERVED: out.print("Unexpected use of reserved word '", getToken(), "'"); return; case INVALID_PRIVATE_NAME_ERRORTOK: out.print("Invalid private name '", getToken(), "'"); return; case PRIVATENAME: out.print("Unexpected private name ", getToken()); return; case AWAIT: case IDENT: out.print("Unexpected identifier '", getToken(), "'"); return; default: break; } if (m_token.m_type & KeywordTokenFlag) { out.print("Unexpected keyword '", getToken(), "'"); return; } out.print("Unexpected token '", getToken(), "'"); } // Instantiate the two flavors of Parser we need instead of putting most of this file in Parser.h template class Parser>; template class Parser>; } // namespace JSC