See More

/* * Copyright (C) 1999-2001 Harri Porten ([email protected]) * Copyright (C) 2001 Peter Kelly ([email protected]) * Copyright (C) 2003-2021 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 (!isContextualKeyword(token)) \ semanticFail("Cannot use the keyword '", getToken(token), "' as a ", __VA_ARGS__); \ if (token.m_type == LET && strictMode())\ semanticFail("Cannot use 'let' as a ", __VA_ARGS__, " ", disallowedIdentifierLetReason()); \ if (token.m_type == AWAIT && !canUseIdentifierAwait()) \ semanticFail("Cannot use 'await' as a ", __VA_ARGS__, " ", disallowedIdentifierAwaitReason()); \ if (token.m_type == YIELD && !canUseIdentifierYield()) \ 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_parseMode(parseMode) , 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() { } void JSToken::dump(PrintStream& out) const { out.print(*m_data.cooked); } static ALWAYS_INLINE bool isPrivateFieldName(UniquedStringImpl* uid) { return uid->length() && uid->at(0) == '#'; } template Expected::ParseInnerResult, String> Parser::parseInner(const Identifier& calleeName, ParsingContext parsingContext, std::optional functionConstructorParametersEndPosition, const FixedVector* classFieldLocations, const PrivateNameEnvironment* parentScopePrivateNames) { ASTBuilder context(const_cast(m_vm), m_parserArena, const_cast(m_source)); SourceParseMode parseMode = sourceParseMode(); ScopeRef scope = currentScope(); scope->setIsLexicalScope(); bool hasPrivateNames = scope->isEvalContext() && parentScopePrivateNames && parentScopePrivateNames->size(); if (hasPrivateNames) { scope->setIsPrivateNameScope(); scope->lexicalVariables().addPrivateNamesFrom(parentScopePrivateNames); } 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, 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, isArrowFunctionBodyExpression, CheckForStrictMode); else if (isArrowFunctionBodyExpression) sourceElements = parseArrowFunctionSingleExpressionBodySourceElements(context); else if (isModuleParseMode(parseMode)) sourceElements = parseModuleSourceElements(context); else if (isGeneratorWrapperParseMode(parseMode)) sourceElements = parseGeneratorFunctionSourceElements(context, calleeName, CheckForStrictMode); else if (isAsyncGeneratorWrapperParseMode(parseMode)) sourceElements = parseAsyncGeneratorFunctionSourceElements(context, 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); if (!m_lexer->isReparsingFunction() && m_seenPrivateNameUseInNonReparsingFunctionMode) { String errorMessage; scope->forEachUsedVariable([&] (UniquedStringImpl* impl) { if (!isPrivateFieldName(impl)) return IterationStatus::Continue; if (parentScopePrivateNames && parentScopePrivateNames->contains(impl)) return IterationStatus::Continue; if (scope->lexicalVariables().contains(impl)) return IterationStatus::Continue; errorMessage = makeString("Cannot reference undeclared private names: \"", String(impl), "\""); return IterationStatus::Done; }); if (!errorMessage.isNull()) return makeUnexpected(errorMessage); } IdentifierSet capturedVariables; UniquedStringImplPtrSet sloppyModeHoistedFunctions; scope->getSloppyModeHoistedFunctions(sloppyModeHoistedFunctions); scope->getCapturedVars(capturedVariables); VariableEnvironment& varDeclarations = scope->declaredVariables(); for (auto& entry : capturedVariables) varDeclarations.markVariableAsCaptured(entry); scope->finalizeLexicalEnvironment(); if (isGeneratorWrapperParseMode(parseMode) || isAsyncFunctionOrAsyncGeneratorWrapperParseMode(parseMode)) { if (scope->usedVariablesContains(m_vm.propertyNames->arguments.impl())) context.propagateArgumentsUse(); } CodeFeatures features = context.features(); if (scope->shadowsArguments()) features |= ShadowsArgumentsFeature; if (m_seenTaggedTemplateInNonReparsingFunctionMode) 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(), scope->takeDeclaredVariables(), scope->takeLexicalEnvironment(), 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(isDisallowedIdentifierAwait(m_token), "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) { 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 (sourceParseMode() == 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, lexicalScopeFeatures(), 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, bool isArrowFunctionBodyExpression, SourceElementsMode mode) { ASSERT(isAsyncFunctionOrAsyncGeneratorWrapperParseMode(sourceParseMode())); 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 parseMode = getAsynFunctionBodyParseMode(sourceParseMode()); SetForScope innerParseMode(m_parseMode, parseMode); { AutoPopScopeRef asyncFunctionBodyScope(this, pushScope()); asyncFunctionBodyScope->setSourceParseMode(sourceParseMode()); 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, lexicalScopeFeatures(), ConstructorKind::None, m_superBinding, info.parameterCount, sourceParseMode(), isArrowFunctionBodyExpression); info.endLine = tokenLine(); info.endOffset = isArrowFunctionBodyExpression ? tokenLocation().endOffset : m_token.m_data.offset; info.parametersStartColumn = startColumn; auto functionExpr = context.createAsyncFunctionBody(startLocation, info, parseMode); auto statement = context.createExprStatement(startLocation, functionExpr, start, m_lastTokenEndPosition.line); context.appendStatement(sourceElements, statement); return sourceElements; } template template TreeSourceElements Parser::parseAsyncGeneratorFunctionSourceElements(TreeBuilder& context, bool isArrowFunctionBodyExpression, SourceElementsMode mode) { ASSERT(isAsyncGeneratorWrapperParseMode(sourceParseMode())); 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 parseMode = SourceParseMode::AsyncGeneratorBodyMode; SetForScope innerParseMode(m_parseMode, parseMode); { AutoPopScopeRef asyncFunctionBodyScope(this, pushScope()); asyncFunctionBodyScope->setSourceParseMode(sourceParseMode()); 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, lexicalScopeFeatures(), ConstructorKind::None, m_superBinding, info.parameterCount, parseMode, isArrowFunctionBodyExpression); info.endLine = tokenLine(); info.endOffset = isArrowFunctionBodyExpression ? tokenLocation().endOffset : m_token.m_data.offset; info.parametersStartColumn = startColumn; auto functionExpr = context.createAsyncFunctionBody(startLocation, info, parseMode); auto statement = context.createExprStatement(startLocation, functionExpr, start, m_lastTokenEndPosition.line); context.appendStatement(sourceElements, statement); return sourceElements; } template template TreeSourceElements Parser::parseSingleFunction(TreeBuilder& context, std::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 `matchIdentifierOrPossiblyEscapedContextualKeyword()` and not `matchSpecIdentifier()`. // We would like contextual keywords to fall under parseVariableDeclaration even when not used as identifiers. // 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 (!matchIdentifierOrPossiblyEscapedContextualKeyword() && !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 ESCAPED_KEYWORD: if (!matchAllowedEscapedContextualKeyword()) { failDueToUnexpectedToken(); break; } FALLTHROUGH; 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(isPossiblyEscapedLet(m_token) && (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() || isModuleParseMode(sourceParseMode()), "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; AutoCleanupLexicalScope lexicalScope; auto popLexicalScopeIfNecessary = [&]() -> VariableEnvironment { if (isLetDeclaration || isConstDeclaration) { auto [lexicalVariables, functionDeclarations] = popScope(lexicalScope, TreeBuilder::NeedsFreeVariableInfo); return lexicalVariables; } return { }; }; 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"); VariableEnvironment lexicalVariables = popLexicalScopeIfNecessary(); if (isOfEnumeration) return context.createForOfLoop(isAwaitFor, location, forInTarget, expr, statement, declLocation, declsStart, inLocation, exprEnd, startLine, endLine, WTFMove(lexicalVariables)); ASSERT(!isAwaitFor); if (isVarDeclaration && forInInitializer) return context.createForInLoop(location, decls, expr, statement, declLocation, declsStart, inLocation, exprEnd, startLine, endLine, WTFMove(lexicalVariables)); return context.createForInLoop(location, forInTarget, expr, statement, declLocation, declsStart, inLocation, exprEnd, startLine, endLine, WTFMove(lexicalVariables)); } 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"); VariableEnvironment lexicalVariables = popLexicalScopeIfNecessary(); return context.createForLoop(location, decls, condition, increment, statement, startLine, endLine, WTFMove(lexicalVariables)); } // 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"); if (pattern) { ASSERT(!decls); VariableEnvironment lexicalVariables = popLexicalScopeIfNecessary(); if (isOfEnumeration) return context.createForOfLoop(isAwaitFor, location, pattern, expr, statement, declLocation, declsStart, declsEnd, exprEnd, startLine, endLine, WTFMove(lexicalVariables)); ASSERT(!isAwaitFor); return context.createForInLoop(location, pattern, expr, statement, declLocation, declsStart, declsEnd, exprEnd, startLine, endLine, WTFMove(lexicalVariables)); } semanticFailIfFalse(isSimpleAssignmentTarget(context, decls), "Left side of assignment is not a reference"); VariableEnvironment lexicalVariables = popLexicalScopeIfNecessary(); if (isOfEnumeration) return context.createForOfLoop(isAwaitFor, location, decls, expr, statement, declLocation, declsStart, declsEnd, exprEnd, startLine, endLine, WTFMove(lexicalVariables)); ASSERT(!isAwaitFor); return context.createForInLoop(location, decls, expr, statement, declLocation, declsStart, declsEnd, exprEnd, startLine, endLine, WTFMove(lexicalVariables)); } 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'"); auto [lexicalEnvironment, functionDeclarations] = popScope(lexicalScope, TreeBuilder::NeedsFreeVariableInfo); return context.createSwitchStatement(location, expr, firstClauses, defaultClause, secondClauses, startLine, endLine, WTFMove(lexicalEnvironment), WTFMove(functionDeclarations)); } 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; DeclarationStacks::FunctionStack functionStack; 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"); std::tie(catchEnvironment, functionStack) = popScope(catchScope, TreeBuilder::NeedsFreeVariableInfo); ASSERT(functionStack.isEmpty()); RELEASE_ASSERT(!ident || (catchEnvironment.size() == 1 && catchEnvironment.contains(ident->impl()))); } } 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, WTFMove(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 lexicalEnvironment; DeclarationStacks::FunctionStack functionStack; next(); if (match(CLOSEBRACE)) { int endOffset = m_token.m_data.offset; next(); if (shouldPushLexicalScope) std::tie(lexicalEnvironment, functionStack) = popScope(lexicalScope, TreeBuilder::NeedsFreeVariableInfo); TreeStatement result = context.createBlockStatement(location, 0, start, m_lastTokenEndPosition.line, WTFMove(lexicalEnvironment), WTFMove(functionStack)); context.setStartOffset(result, startOffset); context.setEndOffset(result, endOffset); 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(); if (shouldPushLexicalScope) std::tie(lexicalEnvironment, functionStack) = popScope(lexicalScope, TreeBuilder::NeedsFreeVariableInfo); TreeStatement result = context.createBlockStatement(location, subtree, start, m_lastTokenEndPosition.line, WTFMove(lexicalEnvironment), WTFMove(functionStack)); context.setStartOffset(result, startOffset); context.setEndOffset(result, endOffset); 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); auto [lexicalEnvironment, functionDeclarations] = popScope(blockScope, TreeBuilder::NeedsFreeVariableInfo); return context.createBlockStatement(location, sourceElements, start, m_lastTokenEndPosition.line, WTFMove(lexicalEnvironment), WTFMove(functionDeclarations)); } 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(isDisallowedIdentifierAwait(m_token), "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) { 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, lexicalScopeFeatures(), constructorKind, functionSuperBinding, parameterCount, sourceParseMode(), 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, lexicalScopeFeatures(), constructorKind, functionSuperBinding, parameterCount, sourceParseMode(), 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, FunctionInfoType& functionInfo) { auto mode = sourceParseMode(); 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, bool nameIsInContainingScope, ConstructorKind constructorKind, SuperBinding expectedSuperBinding, int functionKeywordStart, ParserFunctionInfo& functionInfo, FunctionDefinitionType functionDefinitionType, std::optional functionConstructorParametersEndPosition) { auto mode = sourceParseMode(); RELEASE_ASSERT(isFunctionParseMode(mode)); ScopeRef parentScope = currentScope(); bool functionNameIsAwait = isPossiblyEscapedAwait(m_token); const char* isDisallowedAwaitFunctionNameReason = functionNameIsAwait && !canUseIdentifierAwait() ? 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->lexicalScopeFeatures() & StrictModeLexicalFeature)); 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->lexicalScopeFeatures(), 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, 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 canUseYield = !strictMode(); if (!(functionDefinitionType == FunctionDefinitionType::Expression && SourceParseModeSet(SourceParseMode::NormalFunctionMode, SourceParseMode::AsyncFunctionMode).contains(mode))) canUseYield &= !parentScope->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(canUseYield, functionNameIsAwait)) { functionInfo.name = m_token.m_data.ident; m_parserState.lastFunctionName = functionInfo.name; if (UNLIKELY(isDisallowedAwaitFunctionNameReason)) 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, 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); } return IterationStatus::Continue; }); auto performParsingFunctionBody = [&] { return parseFunctionBody(context, syntaxChecker, startLocation, startColumn, functionKeywordStart, functionNameStart, parametersStart, constructorKind, expectedSuperBinding, functionBodyType, functionInfo.parameterCount); }; 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, std::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; } SetForScope innerParseMode(m_parseMode, parseMode); 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, 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, std::optional functionConstructorParametersEndPosition) { ASSERT(match(FUNCTION)); JSTokenLocation location(tokenLocation()); unsigned functionKeywordStart = tokenStart(); next(); ParserFunctionInfo functionInfo; SourceParseMode parseMode = SourceParseMode::AsyncFunctionMode; if (consume(TIMES)) parseMode = SourceParseMode::AsyncGeneratorWrapperFunctionMode; SetForScope innerParseMode(m_parseMode, parseMode); 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, 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)); JSTextPosition start = tokenStartPosition(); JSTokenLocation location(tokenLocation()); info.startLine = location.line; info.startColumn = tokenColumn(); info.startOffset = location.startOffset; // We have a subtle problem here. Class heritage evaluation should find class declaration's constructor name, but should not find private name evaluation. // For example, // // class A extends ( // class { // constructor() { // print(A); // This is OK. // print(A.#test); // This is SyntaxError. // } // }) { // static #test = 42; // } // // We need to create two scopes here since private name lookup will traverse scope at linking time in CodeBlock. // This classHeadScope is similar to functionScope in FunctionExpression with name. AutoPopScopeRef classHeadScope(this, pushScope()); classHeadScope->setIsLexicalScope(); classHeadScope->preventVarDeclarations(); classHeadScope->setStrictMode(); next(); 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) || isAllowedIdentifierAwait(m_token)) { info.className = m_token.m_data.ident; next(); failIfTrue(classHeadScope->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); JSTextPosition divot = start; TreeExpression parentClass = 0; if (consume(EXTENDS)) { divot = tokenStartPosition(); parentClass = parseMemberExpression(context); failIfFalse(parentClass, "Cannot parse the parent class name"); } const ConstructorKind constructorKind = parentClass ? ConstructorKind::Extends : ConstructorKind::Base; JSTextPosition classHeadEnd = lastTokenEndPosition(); consumeOrFail(OPENBRACE, "Expected opening '{' at the start of a class body"); AutoPopScopeRef classScope(this, pushScope()); classScope->setIsLexicalScope(); classScope->preventVarDeclarations(); classScope->setStrictMode(); classScope->setIsClassScope(); bool declaresPrivateMethod = false; bool declaresPrivateAccessor = false; bool declaresStaticPrivateMethod = false; bool declaresStaticPrivateAccessor = false; 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) || match(PRIVATENAME))) { 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>(type | PropertyNode::Computed); failIfFalse(computedPropertyName, "Cannot parse computed property name"); handleProductionOrFail(CLOSEBRACKET, "]", "end", "computed property name"); break; case PRIVATENAME: { ident = m_token.m_data.ident; failIfTrue(isGetter || isSetter, "Cannot parse class method with private name"); ASSERT(ident); next(); if (match(OPENPAREN)) { semanticFailIfTrue(classScope->declarePrivateMethod(*ident, tag) & DeclarationResult::InvalidDuplicateDeclaration, "Cannot declare private method twice"); semanticFailIfTrue(*ident == propertyNames.constructorPrivateField, "Cannot declare a private method named '#constructor'"); if (tag == ClassElementTag::Static) declaresStaticPrivateMethod = true; else declaresPrivateMethod = true; type = static_cast<:type>(type | PropertyNode::PrivateMethod); break; } failIfTrue(match(OPENPAREN), "Cannot parse class method with private name"); semanticFailIfTrue(classScope->declarePrivateField(*ident) & DeclarationResult::InvalidDuplicateDeclaration, "Cannot declare private field twice"); type = static_cast<:type>(type | PropertyNode::PrivateField); break; } default: if (m_token.m_type & KeywordTokenFlag) goto namedKeyword; failDueToUnexpectedToken(); } TreeProperty property; if (isGetter || isSetter) { if (match(PRIVATENAME)) { ident = m_token.m_data.ident; auto declarationResult = isSetter ? classScope->declarePrivateSetter(*ident, tag) : classScope->declarePrivateGetter(*ident, tag); semanticFailIfTrue(declarationResult & DeclarationResult::InvalidDuplicateDeclaration, "Declared private setter with an already used name"); if (tag == ClassElementTag::Static) { semanticFailIfTrue(declarationResult & DeclarationResult::InvalidPrivateStaticNonStatic, "Cannot declare a private static ", (isSetter ? "setter" : "getter") , " if there is a non-static private ", (isSetter ? "getter" : "setter"), " with used name"); declaresStaticPrivateAccessor = true; } else { semanticFailIfTrue(declarationResult & DeclarationResult::InvalidPrivateStaticNonStatic, "Cannot declare a private non-static ", (isSetter ? "setter" : "getter"), " if there is a static private ", (isSetter ? "getter" : "setter"), " with used name"); declaresPrivateAccessor = true; } if (isSetter) type = static_cast<:type>(type | PropertyNode::PrivateSetter); else type = static_cast<:type>(type | PropertyNode::PrivateGetter); } else { type = static_cast<:type>(type & ~PropertyNode::Constant); type = static_cast<:type>(type | (isGetter ? PropertyNode::Getter : PropertyNode::Setter)); } property = parseGetterSetter(context, type, methodStart, ConstructorKind::None, tag); failIfFalse(property, "Cannot parse this method"); } else if (!match(OPENPAREN) && 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; SetForScope innerParseMode(m_parseMode, parseMode); failIfFalse((parseFunctionInfo(context, FunctionNameRequirements::Unnamed, 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 (declaresPrivateMethod || declaresPrivateAccessor || declaresStaticPrivateMethod || declaresStaticPrivateAccessor) { { Identifier privateBrandIdentifier = m_vm.propertyNames->builtinNames().privateBrandPrivateName(); DeclarationResultMask declarationResult = classScope->declareLexicalVariable(&privateBrandIdentifier, true); ASSERT_UNUSED(declarationResult, declarationResult == DeclarationResult::Valid); classScope->useVariable(&privateBrandIdentifier, false); classScope->addClosedVariableCandidateUnconditionally(privateBrandIdentifier.impl()); } { Identifier privateClassBrandIdentifier = m_vm.propertyNames->builtinNames().privateClassBrandPrivateName(); DeclarationResultMask declarationResult = classScope->declareLexicalVariable(&privateClassBrandIdentifier, true); ASSERT_UNUSED(declarationResult, declarationResult == DeclarationResult::Valid); classScope->useVariable(&privateClassBrandIdentifier, false); classScope->addClosedVariableCandidateUnconditionally(privateClassBrandIdentifier.impl()); } } if constexpr (std::is_same_v) { if (classElements) classElements->setHasPrivateAccessors(declaresPrivateAccessor || declaresStaticPrivateAccessor); } auto [lexicalEnvironment, functionDeclarations] = popScope(classScope, TreeBuilder::NeedsFreeVariableInfo); auto [classHeadEnvironment, classHeadFunctionDeclarations] = popScope(classHeadScope, TreeBuilder::NeedsFreeVariableInfo); ASSERT(functionDeclarations.isEmpty()); ASSERT(classHeadFunctionDeclarations.isEmpty()); return context.createClassExpr(location, info, WTFMove(classHeadEnvironment), WTFMove(lexicalEnvironment), constructor, parentClass, classElements, start, divot, classHeadEnd); } template template TreeSourceElements Parser::parseClassFieldInitializerSourceElements(TreeBuilder& context, const FixedVector& 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<:pair int>> 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 typename TreeBuilder::ImportAssertionList Parser::parseImportAssertions(TreeBuilder& context) { auto assertionList = context.createImportAssertionList(); consumeOrFail(OPENBRACE, "Expected opening '{' at the start of import assertion"); while (!match(CLOSEBRACE)) { failIfFalse(matchIdentifierOrKeyword() || match(STRING), "Expected an assertion key"); auto key = m_token.m_data.ident; next(); consumeOrFail(COLON, "Expected ':' after assertion key"); failIfFalse(match(STRING), "Expected an assertion value"); auto value = m_token.m_data.ident; next(); context.appendImportAssertion(assertionList, *key, *value); if (!consume(COMMA)) break; } handleProductionOrFail2(CLOSEBRACE, "}", "end", "import assertion"); return assertionList; } template