/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: set ts=8 sts=2 et sw=2 tw=80: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * JS parser. * * This is a recursive-descent parser for the JavaScript language specified by * "The ECMAScript Language Specification" (Standard ECMA-262). It uses * lexical and semantic feedback to disambiguate non-LL(1) structures. It * generates trees of nodes induced by the recursive parsing (not precise * syntax trees, see Parser.h). After tree construction, it rewrites trees to * fold constants and evaluate compile-time expressions. * * This parser attempts no error recovery. */ #include "frontend/Parser.h" #include "mozilla/ArrayUtils.h" #include "mozilla/Assertions.h" #include "mozilla/Casting.h" #include "mozilla/Range.h" #include "mozilla/Sprintf.h" #include "mozilla/Try.h" // MOZ_TRY* #include "mozilla/Utf8.h" #include "mozilla/Variant.h" #include #include #include #include "jsnum.h" #include "jstypes.h" #include "frontend/FoldConstants.h" #include "frontend/FunctionSyntaxKind.h" // FunctionSyntaxKind #include "frontend/ModuleSharedContext.h" #include "frontend/ParseNode.h" #include "frontend/ParseNodeVerify.h" #include "frontend/Parser-macros.h" // MOZ_TRY_VAR_OR_RETURN #include "frontend/ParserAtom.h" // TaggedParserAtomIndex, ParserAtomsTable, ParserAtom #include "frontend/ScriptIndex.h" // ScriptIndex #include "frontend/TokenStream.h" // IsKeyword, ReservedWordTokenKind, ReservedWordToCharZ, DeprecatedContent, *TokenStream*, CharBuffer, TokenKindToDesc #include "irregexp/RegExpAPI.h" #include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin, JS::ColumnNumberOneOrigin #include "js/ErrorReport.h" // JSErrorBase #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* #include "js/HashTable.h" #include "js/RegExpFlags.h" // JS::RegExpFlags #include "js/Stack.h" // JS::NativeStackLimit #include "util/StringBuffer.h" // StringBuffer #include "vm/BytecodeUtil.h" #include "vm/FunctionFlags.h" // js::FunctionFlags #include "vm/GeneratorAndAsyncKind.h" // js::GeneratorKind, js::FunctionAsyncKind #include "vm/JSContext.h" #include "vm/JSScript.h" #include "vm/ModuleBuilder.h" // js::ModuleBuilder #include "vm/Scope.h" // GetScopeDataTrailingNames #include "wasm/AsmJS.h" #include "frontend/ParseContext-inl.h" #include "frontend/SharedContext-inl.h" using namespace js; using mozilla::AssertedCast; using mozilla::AsVariant; using mozilla::Maybe; using mozilla::Nothing; using mozilla::PointerRangeSize; using mozilla::Some; using mozilla::Utf8Unit; using JS::ReadOnlyCompileOptions; using JS::RegExpFlags; namespace js::frontend { using DeclaredNamePtr = ParseContext::Scope::DeclaredNamePtr; using AddDeclaredNamePtr = ParseContext::Scope::AddDeclaredNamePtr; using BindingIter = ParseContext::Scope::BindingIter; using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr; using ParserBindingNameVector = Vector; static inline void PropagateTransitiveParseFlags(const FunctionBox* inner, SharedContext* outer) { if (inner->bindingsAccessedDynamically()) { outer->setBindingsAccessedDynamically(); } if (inner->hasDirectEval()) { outer->setHasDirectEval(); } } static bool StatementKindIsBraced(StatementKind kind) { return kind == StatementKind::Block || kind == StatementKind::Switch || kind == StatementKind::Try || kind == StatementKind::Catch || kind == StatementKind::Finally; } template inline typename GeneralParser::FinalParser* GeneralParser::asFinalParser() { static_assert( std::is_base_of_v, FinalParser>, "inheritance relationship required by the static_cast<> below"); return static_cast(this); } template inline const typename GeneralParser::FinalParser* GeneralParser::asFinalParser() const { static_assert( std::is_base_of_v, FinalParser>, "inheritance relationship required by the static_cast<> below"); return static_cast(this); } template template bool GeneralParser::mustMatchTokenInternal( ConditionT condition, ErrorReportT errorReport) { MOZ_ASSERT(condition(TokenKind::Div) == false); MOZ_ASSERT(condition(TokenKind::DivAssign) == false); MOZ_ASSERT(condition(TokenKind::RegExp) == false); TokenKind actual; if (!tokenStream.getToken(&actual, TokenStream::SlashIsInvalid)) { return false; } if (!condition(actual)) { errorReport(actual); return false; } return true; } ParserSharedBase::ParserSharedBase(FrontendContext* fc, CompilationState& compilationState, Kind kind) : fc_(fc), alloc_(compilationState.parserAllocScope.alloc()), compilationState_(compilationState), pc_(nullptr), usedNames_(compilationState.usedNames) { fc_->nameCollectionPool().addActiveCompilation(); } ParserSharedBase::~ParserSharedBase() { fc_->nameCollectionPool().removeActiveCompilation(); } #if defined(DEBUG) || defined(JS_JITSPEW) void ParserSharedBase::dumpAtom(TaggedParserAtomIndex index) const { parserAtoms().dump(index); } #endif ParserBase::ParserBase(FrontendContext* fc, const ReadOnlyCompileOptions& options, bool foldConstants, CompilationState& compilationState) : ParserSharedBase(fc, compilationState, ParserSharedBase::Kind::Parser), anyChars(fc, options, this), ss(nullptr), foldConstants_(foldConstants), #ifdef DEBUG checkOptionsCalled_(false), #endif isUnexpectedEOF_(false), awaitHandling_(AwaitIsName), inParametersOfAsyncFunction_(false) { } bool ParserBase::checkOptions() { #ifdef DEBUG checkOptionsCalled_ = true; #endif return anyChars.checkOptions(); } ParserBase::~ParserBase() { MOZ_ASSERT(checkOptionsCalled_); } JSAtom* ParserBase::liftParserAtomToJSAtom(TaggedParserAtomIndex index) { JSContext* cx = fc_->maybeCurrentJSContext(); MOZ_ASSERT(cx); return parserAtoms().toJSAtom(cx, fc_, index, compilationState_.input.atomCache); } template PerHandlerParser::PerHandlerParser( FrontendContext* fc, const ReadOnlyCompileOptions& options, bool foldConstants, CompilationState& compilationState, void* internalSyntaxParser) : ParserBase(fc, options, foldConstants, compilationState), handler_(fc, compilationState), internalSyntaxParser_(internalSyntaxParser) { MOZ_ASSERT(compilationState.isInitialStencil() == compilationState.input.isInitialStencil()); } template GeneralParser::GeneralParser( FrontendContext* fc, const ReadOnlyCompileOptions& options, const Unit* units, size_t length, bool foldConstants, CompilationState& compilationState, SyntaxParser* syntaxParser) : Base(fc, options, foldConstants, compilationState, syntaxParser), tokenStream(fc, &compilationState.parserAtoms, options, units, length) {} template void Parser::setAwaitHandling( AwaitHandling awaitHandling) { this->awaitHandling_ = awaitHandling; } template void Parser::setAwaitHandling( AwaitHandling awaitHandling) { this->awaitHandling_ = awaitHandling; if (SyntaxParser* syntaxParser = getSyntaxParser()) { syntaxParser->setAwaitHandling(awaitHandling); } } template inline void GeneralParser::setAwaitHandling( AwaitHandling awaitHandling) { asFinalParser()->setAwaitHandling(awaitHandling); } template void Parser::setInParametersOfAsyncFunction( bool inParameters) { this->inParametersOfAsyncFunction_ = inParameters; } template void Parser::setInParametersOfAsyncFunction( bool inParameters) { this->inParametersOfAsyncFunction_ = inParameters; if (SyntaxParser* syntaxParser = getSyntaxParser()) { syntaxParser->setInParametersOfAsyncFunction(inParameters); } } template inline void GeneralParser::setInParametersOfAsyncFunction( bool inParameters) { asFinalParser()->setInParametersOfAsyncFunction(inParameters); } template FunctionBox* PerHandlerParser::newFunctionBox( FunctionNodeType funNode, TaggedParserAtomIndex explicitName, FunctionFlags flags, uint32_t toStringStart, Directives inheritedDirectives, GeneratorKind generatorKind, FunctionAsyncKind asyncKind) { MOZ_ASSERT(funNode); ScriptIndex index = ScriptIndex(compilationState_.scriptData.length()); if (uint32_t(index) >= TaggedScriptThingIndex::IndexLimit) { ReportAllocationOverflow(fc_); return nullptr; } if (!compilationState_.appendScriptStencilAndData(fc_)) { return nullptr; } bool isInitialStencil = compilationState_.isInitialStencil(); // This source extent will be further filled in during the remainder of parse. SourceExtent extent; extent.toStringStart = toStringStart; FunctionBox* funbox = alloc_.new_( fc_, extent, compilationState_, inheritedDirectives, generatorKind, asyncKind, isInitialStencil, explicitName, flags, index); if (!funbox) { ReportOutOfMemory(fc_); return nullptr; } handler_.setFunctionBox(funNode, funbox); return funbox; } template FunctionBox* PerHandlerParser::newFunctionBox( FunctionNodeType funNode, const ScriptStencil& cachedScriptData, const ScriptStencilExtra& cachedScriptExtra) { MOZ_ASSERT(funNode); ScriptIndex index = ScriptIndex(compilationState_.scriptData.length()); if (uint32_t(index) >= TaggedScriptThingIndex::IndexLimit) { ReportAllocationOverflow(fc_); return nullptr; } if (!compilationState_.appendScriptStencilAndData(fc_)) { return nullptr; } FunctionBox* funbox = alloc_.new_( fc_, cachedScriptExtra.extent, compilationState_, Directives(/* strict = */ false), cachedScriptExtra.generatorKind(), cachedScriptExtra.asyncKind(), compilationState_.isInitialStencil(), cachedScriptData.functionAtom, cachedScriptData.functionFlags, index); if (!funbox) { ReportOutOfMemory(fc_); return nullptr; } handler_.setFunctionBox(funNode, funbox); funbox->initFromScriptStencilExtra(cachedScriptExtra); return funbox; } bool ParserBase::setSourceMapInfo() { // If support for source pragmas have been fully disabled, we can skip // processing of all of these values. if (!options().sourcePragmas()) { return true; } // Not all clients initialize ss. Can't update info to an object that isn't // there. if (!ss) { return true; } if (anyChars.hasDisplayURL()) { if (!ss->setDisplayURL(fc_, anyChars.displayURL())) { return false; } } if (anyChars.hasSourceMapURL()) { MOZ_ASSERT(!ss->hasSourceMapURL()); if (!ss->setSourceMapURL(fc_, anyChars.sourceMapURL())) { return false; } } /* * Source map URLs passed as a compile option (usually via a HTTP source map * header) override any source map urls passed as comment pragmas. */ if (options().sourceMapURL()) { // Warn about the replacement, but use the new one. if (ss->hasSourceMapURL()) { if (!warningNoOffset(JSMSG_ALREADY_HAS_PRAGMA, ss->filename(), "//# sourceMappingURL")) { return false; } } if (!ss->setSourceMapURL(fc_, options().sourceMapURL())) { return false; } } return true; } /* * Parse a top-level JS script. */ template typename ParseHandler::ListNodeResult GeneralParser::parse() { MOZ_ASSERT(checkOptionsCalled_); SourceExtent extent = SourceExtent::makeGlobalExtent( /* len = */ 0, options().lineno, JS::LimitedColumnNumberOneOrigin::fromUnlimited( JS::ColumnNumberOneOrigin(options().column))); Directives directives(options().forceStrictMode()); GlobalSharedContext globalsc(this->fc_, ScopeKind::Global, options(), directives, extent); SourceParseContext globalpc(this, &globalsc, /* newDirectives = */ nullptr); if (!globalpc.init()) { return errorResult(); } ParseContext::VarScope varScope(this); if (!varScope.init(pc_)) { return errorResult(); } ListNodeType stmtList; MOZ_TRY_VAR(stmtList, statementList(YieldIsName)); TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } if (tt != TokenKind::Eof) { error(JSMSG_GARBAGE_AFTER_INPUT, "script", TokenKindToDesc(tt)); return errorResult(); } if (!CheckParseTree(this->fc_, alloc_, stmtList)) { return errorResult(); } if (foldConstants_) { Node node = stmtList; // Don't constant-fold inside "use asm" code, as this could create a parse // tree that doesn't type-check as asm.js. if (!pc_->useAsmOrInsideUseAsm()) { if (!FoldConstants(this->fc_, this->parserAtoms(), &node, &handler_)) { return errorResult(); } } stmtList = handler_.asListNode(node); } return stmtList; } /* * Strict mode forbids introducing new definitions for 'eval', 'arguments', * 'let', 'static', 'yield', or for any strict mode reserved word. */ bool ParserBase::isValidStrictBinding(TaggedParserAtomIndex name) { TokenKind tt = ReservedWordTokenKind(name); if (tt == TokenKind::Limit) { return name != TaggedParserAtomIndex::WellKnown::eval() && name != TaggedParserAtomIndex::WellKnown::arguments(); } return tt != TokenKind::Let && tt != TokenKind::Static && tt != TokenKind::Yield && !TokenKindIsStrictReservedWord(tt); } /* * Returns true if all parameter names are valid strict mode binding names and * no duplicate parameter names are present. */ bool ParserBase::hasValidSimpleStrictParameterNames() { MOZ_ASSERT(pc_->isFunctionBox() && pc_->functionBox()->hasSimpleParameterList()); if (pc_->functionBox()->hasDuplicateParameters) { return false; } for (auto name : pc_->positionalFormalParameterNames()) { MOZ_ASSERT(name); if (!isValidStrictBinding(name)) { return false; } } return true; } template void GeneralParser::reportMissingClosing( unsigned errorNumber, unsigned noteNumber, uint32_t openedPos) { auto notes = MakeUnique(); if (!notes) { ReportOutOfMemory(this->fc_); return; } uint32_t line; JS::LimitedColumnNumberOneOrigin column; tokenStream.computeLineAndColumn(openedPos, &line, &column); const size_t MaxWidth = sizeof("4294967295"); char columnNumber[MaxWidth]; SprintfLiteral(columnNumber, "%" PRIu32, column.oneOriginValue()); char lineNumber[MaxWidth]; SprintfLiteral(lineNumber, "%" PRIu32, line); if (!notes->addNoteASCII(this->fc_, getFilename().c_str(), 0, line, JS::ColumnNumberOneOrigin(column), GetErrorMessage, nullptr, noteNumber, lineNumber, columnNumber)) { return; } errorWithNotes(std::move(notes), errorNumber); } template void GeneralParser::reportRedeclarationHelper( TaggedParserAtomIndex& name, DeclarationKind& prevKind, TokenPos& pos, uint32_t& prevPos, const unsigned& errorNumber, const unsigned& noteErrorNumber) { UniqueChars bytes = this->parserAtoms().toPrintableString(name); if (!bytes) { ReportOutOfMemory(this->fc_); return; } if (prevPos == DeclaredNameInfo::npos) { errorAt(pos.begin, errorNumber, DeclarationKindString(prevKind), bytes.get()); return; } auto notes = MakeUnique(); if (!notes) { ReportOutOfMemory(this->fc_); return; } uint32_t line; JS::LimitedColumnNumberOneOrigin column; tokenStream.computeLineAndColumn(prevPos, &line, &column); const size_t MaxWidth = sizeof("4294967295"); char columnNumber[MaxWidth]; SprintfLiteral(columnNumber, "%" PRIu32, column.oneOriginValue()); char lineNumber[MaxWidth]; SprintfLiteral(lineNumber, "%" PRIu32, line); if (!notes->addNoteASCII(this->fc_, getFilename().c_str(), 0, line, JS::ColumnNumberOneOrigin(column), GetErrorMessage, nullptr, noteErrorNumber, lineNumber, columnNumber)) { return; } errorWithNotesAt(std::move(notes), pos.begin, errorNumber, DeclarationKindString(prevKind), bytes.get()); } template void GeneralParser::reportRedeclaration( TaggedParserAtomIndex name, DeclarationKind prevKind, TokenPos pos, uint32_t prevPos) { reportRedeclarationHelper(name, prevKind, pos, prevPos, JSMSG_REDECLARED_VAR, JSMSG_PREV_DECLARATION); } template void GeneralParser::reportMismatchedPlacement( TaggedParserAtomIndex name, DeclarationKind prevKind, TokenPos pos, uint32_t prevPos) { reportRedeclarationHelper(name, prevKind, pos, prevPos, JSMSG_MISMATCHED_PLACEMENT, JSMSG_PREV_DECLARATION); } // notePositionalFormalParameter is called for both the arguments of a regular // function definition and the arguments specified by the Function // constructor. // // The 'disallowDuplicateParams' bool indicates whether the use of another // feature (destructuring or default arguments) disables duplicate arguments. // (ECMA-262 requires us to support duplicate parameter names, but, for newer // features, we consider the code to have "opted in" to higher standards and // forbid duplicates.) template bool GeneralParser::notePositionalFormalParameter( FunctionNodeType funNode, TaggedParserAtomIndex name, uint32_t beginPos, bool disallowDuplicateParams, bool* duplicatedParam) { if (AddDeclaredNamePtr p = pc_->functionScope().lookupDeclaredNameForAdd(name)) { if (disallowDuplicateParams) { error(JSMSG_BAD_DUP_ARGS); return false; } // Strict-mode disallows duplicate args. We may not know whether we are // in strict mode or not (since the function body hasn't been parsed). // In such cases, report will queue up the potential error and return // 'true'. if (pc_->sc()->strict()) { UniqueChars bytes = this->parserAtoms().toPrintableString(name); if (!bytes) { ReportOutOfMemory(this->fc_); return false; } if (!strictModeError(JSMSG_DUPLICATE_FORMAL, bytes.get())) { return false; } } *duplicatedParam = true; } else { DeclarationKind kind = DeclarationKind::PositionalFormalParameter; if (!pc_->functionScope().addDeclaredName(pc_, p, name, kind, beginPos)) { return false; } } if (!pc_->positionalFormalParameterNames().append( TrivialTaggedParserAtomIndex::from(name))) { ReportOutOfMemory(this->fc_); return false; } NameNodeType paramNode; MOZ_TRY_VAR_OR_RETURN(paramNode, newName(name), false); handler_.addFunctionFormalParameter(funNode, paramNode); return true; } template bool PerHandlerParser::noteDestructuredPositionalFormalParameter( FunctionNodeType funNode, Node destruct) { // Append an empty name to the positional formals vector to keep track of // argument slots when making FunctionScope::ParserData. if (!pc_->positionalFormalParameterNames().append( TrivialTaggedParserAtomIndex::null())) { ReportOutOfMemory(fc_); return false; } handler_.addFunctionFormalParameter(funNode, destruct); return true; } template bool GeneralParser::noteDeclaredName( TaggedParserAtomIndex name, DeclarationKind kind, TokenPos pos, ClosedOver isClosedOver) { // The asm.js validator does all its own symbol-table management so, as an // optimization, avoid doing any work here. if (pc_->useAsmOrInsideUseAsm()) { return true; } switch (kind) { case DeclarationKind::Var: case DeclarationKind::BodyLevelFunction: { Maybe redeclaredKind; uint32_t prevPos; if (!pc_->tryDeclareVar(name, this, kind, pos.begin, &redeclaredKind, &prevPos)) { return false; } if (redeclaredKind) { reportRedeclaration(name, *redeclaredKind, pos, prevPos); return false; } break; } case DeclarationKind::ModuleBodyLevelFunction: { MOZ_ASSERT(pc_->atModuleLevel()); AddDeclaredNamePtr p = pc_->varScope().lookupDeclaredNameForAdd(name); if (p) { reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos()); return false; } if (!pc_->varScope().addDeclaredName(pc_, p, name, kind, pos.begin, isClosedOver)) { return false; } // Body-level functions in modules are always closed over. pc_->varScope().lookupDeclaredName(name)->value()->setClosedOver(); break; } case DeclarationKind::FormalParameter: { // It is an early error if any non-positional formal parameter name // (e.g., destructuring formal parameter) is duplicated. AddDeclaredNamePtr p = pc_->functionScope().lookupDeclaredNameForAdd(name); if (p) { error(JSMSG_BAD_DUP_ARGS); return false; } if (!pc_->functionScope().addDeclaredName(pc_, p, name, kind, pos.begin, isClosedOver)) { return false; } break; } case DeclarationKind::LexicalFunction: case DeclarationKind::PrivateName: case DeclarationKind::Synthetic: case DeclarationKind::PrivateMethod: { ParseContext::Scope* scope = pc_->innermostScope(); AddDeclaredNamePtr p = scope->lookupDeclaredNameForAdd(name); if (p) { reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos()); return false; } if (!scope->addDeclaredName(pc_, p, name, kind, pos.begin, isClosedOver)) { return false; } break; } case DeclarationKind::SloppyLexicalFunction: { // Functions in block have complex allowances in sloppy mode for being // labelled that other lexical declarations do not have. Those checks // are done in functionStmt. ParseContext::Scope* scope = pc_->innermostScope(); if (AddDeclaredNamePtr p = scope->lookupDeclaredNameForAdd(name)) { // It is usually an early error if there is another declaration // with the same name in the same scope. // // Sloppy lexical functions may redeclare other sloppy lexical // functions for web compatibility reasons. if (p->value()->kind() != DeclarationKind::SloppyLexicalFunction) { reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos()); return false; } } else { if (!scope->addDeclaredName(pc_, p, name, kind, pos.begin, isClosedOver)) { return false; } } break; } case DeclarationKind::Let: case DeclarationKind::Const: case DeclarationKind::Class: // The BoundNames of LexicalDeclaration and ForDeclaration must not // contain 'let'. (CatchParameter is the only lexical binding form // without this restriction.) if (name == TaggedParserAtomIndex::WellKnown::let()) { errorAt(pos.begin, JSMSG_LEXICAL_DECL_DEFINES_LET); return false; } // For body-level lexically declared names in a function, it is an // early error if there is a formal parameter of the same name. This // needs a special check if there is an extra var scope due to // parameter expressions. if (pc_->isFunctionExtraBodyVarScopeInnermost()) { DeclaredNamePtr p = pc_->functionScope().lookupDeclaredName(name); if (p && DeclarationKindIsParameter(p->value()->kind())) { reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos()); return false; } } [[fallthrough]]; case DeclarationKind::Import: // Module code is always strict, so 'let' is always a keyword and never a // name. MOZ_ASSERT(name != TaggedParserAtomIndex::WellKnown::let()); [[fallthrough]]; case DeclarationKind::SimpleCatchParameter: case DeclarationKind::CatchParameter: { ParseContext::Scope* scope = pc_->innermostScope(); // It is an early error if there is another declaration with the same // name in the same scope. AddDeclaredNamePtr p = scope->lookupDeclaredNameForAdd(name); if (p) { reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos()); return false; } if (!scope->addDeclaredName(pc_, p, name, kind, pos.begin, isClosedOver)) { return false; } break; } case DeclarationKind::CoverArrowParameter: // CoverArrowParameter is only used as a placeholder declaration kind. break; case DeclarationKind::PositionalFormalParameter: MOZ_CRASH( "Positional formal parameter names should use " "notePositionalFormalParameter"); break; case DeclarationKind::VarForAnnexBLexicalFunction: MOZ_CRASH( "Synthesized Annex B vars should go through " "addPossibleAnnexBFunctionBox, and " "propagateAndMarkAnnexBFunctionBoxes"); break; } return true; } template bool GeneralParser::noteDeclaredPrivateName( Node nameNode, TaggedParserAtomIndex name, PropertyType propType, FieldPlacement placement, TokenPos pos) { ParseContext::Scope* scope = pc_->innermostScope(); AddDeclaredNamePtr p = scope->lookupDeclaredNameForAdd(name); DeclarationKind declKind = DeclarationKind::PrivateName; ClosedOver closedOver = ClosedOver::No; PrivateNameKind kind; switch (propType) { case PropertyType::Field: kind = PrivateNameKind::Field; break; case PropertyType::FieldWithAccessor: // In this case, we create a new private field for the underlying storage, // and use the current name for the getter and setter. kind = PrivateNameKind::GetterSetter; break; case PropertyType::Method: case PropertyType::GeneratorMethod: case PropertyType::AsyncMethod: case PropertyType::AsyncGeneratorMethod: if (placement == FieldPlacement::Instance) { // Optimized private method. Non-optimized paths still get // DeclarationKind::Synthetic. declKind = DeclarationKind::PrivateMethod; } // Methods must be marked closed-over so that // EmitterScope::lookupPrivate() works even if the method is used, but not // within any method (from a computed property name, or debugger frame) closedOver = ClosedOver::Yes; kind = PrivateNameKind::Method; break; case PropertyType::Getter: kind = PrivateNameKind::Getter; break; case PropertyType::Setter: kind = PrivateNameKind::Setter; break; default: kind = PrivateNameKind::None; } if (p) { PrivateNameKind prevKind = p->value()->privateNameKind(); if ((prevKind == PrivateNameKind::Getter && kind == PrivateNameKind::Setter) || (prevKind == PrivateNameKind::Setter && kind == PrivateNameKind::Getter)) { // Private methods demands that // // class A { // static set #x(_) {} // get #x() { } // } // // Report a SyntaxError. if (placement == p->value()->placement()) { p->value()->setPrivateNameKind(PrivateNameKind::GetterSetter); handler_.setPrivateNameKind(nameNode, PrivateNameKind::GetterSetter); return true; } } reportMismatchedPlacement(name, p->value()->kind(), pos, p->value()->pos()); return false; } if (!scope->addDeclaredName(pc_, p, name, declKind, pos.begin, closedOver)) { return false; } DeclaredNamePtr declared = scope->lookupDeclaredName(name); declared->value()->setPrivateNameKind(kind); declared->value()->setFieldPlacement(placement); handler_.setPrivateNameKind(nameNode, kind); return true; } bool ParserBase::noteUsedNameInternal(TaggedParserAtomIndex name, NameVisibility visibility, mozilla::Maybe tokenPosition) { // The asm.js validator does all its own symbol-table management so, as an // optimization, avoid doing any work here. if (pc_->useAsmOrInsideUseAsm()) { return true; } // Global bindings are properties and not actual bindings; we don't need // to know if they are closed over. So no need to track used name at the // global scope. It is not incorrect to track them, this is an // optimization. // // Exceptions: // (a) Track private name references, as the used names tracker is used to // provide early errors for undeclared private name references // (b) If the script has extra bindings, track all references to detect // references to extra bindings ParseContext::Scope* scope = pc_->innermostScope(); if (pc_->sc()->isGlobalContext() && scope == &pc_->varScope() && visibility == NameVisibility::Public && !this->compilationState_.input.hasExtraBindings()) { return true; } return usedNames_.noteUse(fc_, name, visibility, pc_->scriptId(), scope->id(), tokenPosition); } template bool PerHandlerParser:: propagateFreeNamesAndMarkClosedOverBindings(ParseContext::Scope& scope) { // Now that we have all the declared names in the scope, check which // functions should exhibit Annex B semantics. if (!scope.propagateAndMarkAnnexBFunctionBoxes(pc_, this)) { return false; } if (handler_.reuseClosedOverBindings()) { MOZ_ASSERT(pc_->isOutermostOfCurrentCompile()); // Closed over bindings for all scopes are stored in a contiguous array, in // the same order as the order in which scopes are visited, and seprated by // TaggedParserAtomIndex::null(). uint32_t slotCount = scope.declaredCount(); while (auto parserAtom = handler_.nextLazyClosedOverBinding()) { scope.lookupDeclaredName(parserAtom)->value()->setClosedOver(); MOZ_ASSERT(slotCount > 0); slotCount--; } if (pc_->isGeneratorOrAsync()) { scope.setOwnStackSlotCount(slotCount); } return true; } constexpr bool isSyntaxParser = std::is_same_v; uint32_t scriptId = pc_->scriptId(); uint32_t scopeId = scope.id(); uint32_t slotCount = 0; for (BindingIter bi = scope.bindings(pc_); bi; bi++) { bool closedOver = false; if (UsedNamePtr p = usedNames_.lookup(bi.name())) { p->value().noteBoundInScope(scriptId, scopeId, &closedOver); if (closedOver) { bi.setClosedOver(); if constexpr (isSyntaxParser) { if (!pc_->closedOverBindingsForLazy().append( TrivialTaggedParserAtomIndex::from(bi.name()))) { ReportOutOfMemory(fc_); return false; } } } } if constexpr (!isSyntaxParser) { if (!closedOver) { slotCount++; } } } if constexpr (!isSyntaxParser) { if (pc_->isGeneratorOrAsync()) { scope.setOwnStackSlotCount(slotCount); } } // Append a nullptr to denote end-of-scope. if constexpr (isSyntaxParser) { if (!pc_->closedOverBindingsForLazy().append( TrivialTaggedParserAtomIndex::null())) { ReportOutOfMemory(fc_); return false; } } return true; } template bool Parser::checkStatementsEOF() { // This is designed to be paired with parsing a statement list at the top // level. // // The statementList() call breaks on TokenKind::RightCurly, so make sure // we've reached EOF here. TokenKind tt; if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) { return false; } if (tt != TokenKind::Eof) { error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt)); return false; } return true; } template typename ScopeT::ParserData* NewEmptyBindingData(FrontendContext* fc, LifoAlloc& alloc, uint32_t numBindings) { using Data = typename ScopeT::ParserData; size_t allocSize = SizeOfScopeData(numBindings); auto* bindings = alloc.newWithSize(allocSize, numBindings); if (!bindings) { ReportOutOfMemory(fc); } return bindings; } GlobalScope::ParserData* NewEmptyGlobalScopeData(FrontendContext* fc, LifoAlloc& alloc, uint32_t numBindings) { return NewEmptyBindingData(fc, alloc, numBindings); } LexicalScope::ParserData* NewEmptyLexicalScopeData(FrontendContext* fc, LifoAlloc& alloc, uint32_t numBindings) { return NewEmptyBindingData(fc, alloc, numBindings); } FunctionScope::ParserData* NewEmptyFunctionScopeData(FrontendContext* fc, LifoAlloc& alloc, uint32_t numBindings) { return NewEmptyBindingData(fc, alloc, numBindings); } namespace detail { template static MOZ_ALWAYS_INLINE ParserBindingName* InitializeIndexedBindings( SlotInfo& slotInfo, ParserBindingName* start, ParserBindingName* cursor) { return cursor; } template static MOZ_ALWAYS_INLINE ParserBindingName* InitializeIndexedBindings( SlotInfo& slotInfo, ParserBindingName* start, ParserBindingName* cursor, UnsignedInteger SlotInfo::*field, const ParserBindingNameVector& bindings, Step&&... step) { slotInfo.*field = AssertedCast(PointerRangeSize(start, cursor)); ParserBindingName* newCursor = std::uninitialized_copy(bindings.begin(), bindings.end(), cursor); return InitializeIndexedBindings(slotInfo, start, newCursor, std::forward(step)...); } } // namespace detail // Initialize the trailing name bindings of |data|, then set |data->length| to // the count of bindings added (which must equal |count|). // // First, |firstBindings| are added to the trailing names. Then any // "steps" present are performed first to last. Each step is 1) a pointer to a // member of |data| to be set to the current number of bindings added, and 2) a // vector of |ParserBindingName|s to then copy into |data->trailingNames|. // (Thus each |data| member field indicates where the corresponding vector's // names start.) template static MOZ_ALWAYS_INLINE void InitializeBindingData( Data* data, uint32_t count, const ParserBindingNameVector& firstBindings, Step&&... step) { MOZ_ASSERT(data->length == 0, "data shouldn't be filled yet"); ParserBindingName* start = GetScopeDataTrailingNamesPointer(data); ParserBindingName* cursor = std::uninitialized_copy( firstBindings.begin(), firstBindings.end(), start); #ifdef DEBUG ParserBindingName* end = #endif detail::InitializeIndexedBindings(data->slotInfo, start, cursor, std::forward(step)...); MOZ_ASSERT(PointerRangeSize(start, end) == count); data->length = count; } static Maybe NewGlobalScopeData( FrontendContext* fc, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc) { ParserBindingNameVector vars(fc); ParserBindingNameVector lets(fc); ParserBindingNameVector consts(fc); bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver(); for (BindingIter bi = scope.bindings(pc); bi; bi++) { bool closedOver = allBindingsClosedOver || bi.closedOver(); switch (bi.kind()) { case BindingKind::Var: { bool isTopLevelFunction = bi.declarationKind() == DeclarationKind::BodyLevelFunction; ParserBindingName binding(bi.name(), closedOver, isTopLevelFunction); if (!vars.append(binding)) { return Nothing(); } break; } case BindingKind::Let: { ParserBindingName binding(bi.name(), closedOver); if (!lets.append(binding)) { return Nothing(); } break; } case BindingKind::Const: { ParserBindingName binding(bi.name(), closedOver); if (!consts.append(binding)) { return Nothing(); } break; } default: MOZ_CRASH("Bad global scope BindingKind"); } } GlobalScope::ParserData* bindings = nullptr; uint32_t numBindings = vars.length() + lets.length() + consts.length(); if (numBindings > 0) { bindings = NewEmptyBindingData(fc, alloc, numBindings); if (!bindings) { return Nothing(); } // The ordering here is important. See comments in GlobalScope. InitializeBindingData(bindings, numBindings, vars, &ParserGlobalScopeSlotInfo::letStart, lets, &ParserGlobalScopeSlotInfo::constStart, consts); } return Some(bindings); } Maybe ParserBase::newGlobalScopeData( ParseContext::Scope& scope) { return NewGlobalScopeData(fc_, scope, stencilAlloc(), pc_); } static Maybe NewModuleScopeData( FrontendContext* fc, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc) { ParserBindingNameVector imports(fc); ParserBindingNameVector vars(fc); ParserBindingNameVector lets(fc); ParserBindingNameVector consts(fc); bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver() || scope.tooBigToOptimize(); for (BindingIter bi = scope.bindings(pc); bi; bi++) { // Imports are indirect bindings and must not be given known slots. ParserBindingName binding(bi.name(), (allBindingsClosedOver || bi.closedOver()) && bi.kind() != BindingKind::Import); switch (bi.kind()) { case BindingKind::Import: if (!imports.append(binding)) { return Nothing(); } break; case BindingKind::Var: if (!vars.append(binding)) { return Nothing(); } break; case BindingKind::Let: if (!lets.append(binding)) { return Nothing(); } break; case BindingKind::Const: if (!consts.append(binding)) { return Nothing(); } break; default: MOZ_CRASH("Bad module scope BindingKind"); } } ModuleScope::ParserData* bindings = nullptr; uint32_t numBindings = imports.length() + vars.length() + lets.length() + consts.length(); if (numBindings > 0) { bindings = NewEmptyBindingData(fc, alloc, numBindings); if (!bindings) { return Nothing(); } // The ordering here is important. See comments in ModuleScope. InitializeBindingData(bindings, numBindings, imports, &ParserModuleScopeSlotInfo::varStart, vars, &ParserModuleScopeSlotInfo::letStart, lets, &ParserModuleScopeSlotInfo::constStart, consts); } return Some(bindings); } Maybe ParserBase::newModuleScopeData( ParseContext::Scope& scope) { return NewModuleScopeData(fc_, scope, stencilAlloc(), pc_); } static Maybe NewEvalScopeData( FrontendContext* fc, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc) { ParserBindingNameVector vars(fc); // Treat all bindings as closed over in non-strict eval. bool allBindingsClosedOver = !pc->sc()->strict() || pc->sc()->allBindingsClosedOver(); for (BindingIter bi = scope.bindings(pc); bi; bi++) { // Eval scopes only contain 'var' bindings. MOZ_ASSERT(bi.kind() == BindingKind::Var); bool isTopLevelFunction = bi.declarationKind() == DeclarationKind::BodyLevelFunction; bool closedOver = allBindingsClosedOver || bi.closedOver(); ParserBindingName binding(bi.name(), closedOver, isTopLevelFunction); if (!vars.append(binding)) { return Nothing(); } } EvalScope::ParserData* bindings = nullptr; uint32_t numBindings = vars.length(); if (numBindings > 0) { bindings = NewEmptyBindingData(fc, alloc, numBindings); if (!bindings) { return Nothing(); } InitializeBindingData(bindings, numBindings, vars); } return Some(bindings); } Maybe ParserBase::newEvalScopeData( ParseContext::Scope& scope) { return NewEvalScopeData(fc_, scope, stencilAlloc(), pc_); } static Maybe NewFunctionScopeData( FrontendContext* fc, ParseContext::Scope& scope, bool hasParameterExprs, LifoAlloc& alloc, ParseContext* pc) { ParserBindingNameVector positionalFormals(fc); ParserBindingNameVector formals(fc); ParserBindingNameVector vars(fc); bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver() || scope.tooBigToOptimize(); bool argumentBindingsClosedOver = allBindingsClosedOver || pc->isGeneratorOrAsync(); bool hasDuplicateParams = pc->functionBox()->hasDuplicateParameters; // Positional parameter names must be added in order of appearance as they are // referenced using argument slots. for (size_t i = 0; i < pc->positionalFormalParameterNames().length(); i++) { TaggedParserAtomIndex name = pc->positionalFormalParameterNames()[i]; ParserBindingName bindName; if (name) { DeclaredNamePtr p = scope.lookupDeclaredName(name); // Do not consider any positional formal parameters closed over if // there are parameter defaults. It is the binding in the defaults // scope that is closed over instead. bool closedOver = argumentBindingsClosedOver || (p && p->value()->closedOver()); // If the parameter name has duplicates, only the final parameter // name should be on the environment, as otherwise the environment // object would have multiple, same-named properties. if (hasDuplicateParams) { for (size_t j = pc->positionalFormalParameterNames().length() - 1; j > i; j--) { if (TaggedParserAtomIndex(pc->positionalFormalParameterNames()[j]) == name) { closedOver = false; break; } } } bindName = ParserBindingName(name, closedOver); } if (!positionalFormals.append(bindName)) { return Nothing(); } } for (BindingIter bi = scope.bindings(pc); bi; bi++) { ParserBindingName binding(bi.name(), allBindingsClosedOver || bi.closedOver()); switch (bi.kind()) { case BindingKind::FormalParameter: // Positional parameter names are already handled above. if (bi.declarationKind() == DeclarationKind::FormalParameter) { if (!formals.append(binding)) { return Nothing(); } } break; case BindingKind::Var: // The only vars in the function scope when there are parameter // exprs, which induces a separate var environment, should be the // special bindings. MOZ_ASSERT_IF(hasParameterExprs, FunctionScope::isSpecialName(bi.name())); if (!vars.append(binding)) { return Nothing(); } break; case BindingKind::Let: case BindingKind::Const: break; default: MOZ_CRASH("bad function scope BindingKind"); break; } } FunctionScope::ParserData* bindings = nullptr; uint32_t numBindings = positionalFormals.length() + formals.length() + vars.length(); if (numBindings > 0) { bindings = NewEmptyBindingData(fc, alloc, numBindings); if (!bindings) { return Nothing(); } // The ordering here is important. See comments in FunctionScope. InitializeBindingData( bindings, numBindings, positionalFormals, &ParserFunctionScopeSlotInfo::nonPositionalFormalStart, formals, &ParserFunctionScopeSlotInfo::varStart, vars); } return Some(bindings); } // Compute if `NewFunctionScopeData` would return any binding list with any // entry marked as closed-over. This is done without the need to allocate the // binding list. If true, an EnvironmentObject will be needed at runtime. bool FunctionScopeHasClosedOverBindings(ParseContext* pc) { bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver() || pc->functionScope().tooBigToOptimize(); for (BindingIter bi = pc->functionScope().bindings(pc); bi; bi++) { switch (bi.kind()) { case BindingKind::FormalParameter: case BindingKind::Var: if (allBindingsClosedOver || bi.closedOver()) { return true; } break; default: break; } } return false; } Maybe ParserBase::newFunctionScopeData( ParseContext::Scope& scope, bool hasParameterExprs) { return NewFunctionScopeData(fc_, scope, hasParameterExprs, stencilAlloc(), pc_); } VarScope::ParserData* NewEmptyVarScopeData(FrontendContext* fc, LifoAlloc& alloc, uint32_t numBindings) { return NewEmptyBindingData(fc, alloc, numBindings); } static Maybe NewVarScopeData(FrontendContext* fc, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc) { ParserBindingNameVector vars(fc); bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver() || scope.tooBigToOptimize(); for (BindingIter bi = scope.bindings(pc); bi; bi++) { if (bi.kind() == BindingKind::Var) { ParserBindingName binding(bi.name(), allBindingsClosedOver || bi.closedOver()); if (!vars.append(binding)) { return Nothing(); } } else { MOZ_ASSERT( bi.kind() == BindingKind::Let || bi.kind() == BindingKind::Const, "bad var scope BindingKind"); } } VarScope::ParserData* bindings = nullptr; uint32_t numBindings = vars.length(); if (numBindings > 0) { bindings = NewEmptyBindingData(fc, alloc, numBindings); if (!bindings) { return Nothing(); } InitializeBindingData(bindings, numBindings, vars); } return Some(bindings); } // Compute if `NewVarScopeData` would return any binding list. This is done // without allocate the binding list. static bool VarScopeHasBindings(ParseContext* pc) { for (BindingIter bi = pc->varScope().bindings(pc); bi; bi++) { if (bi.kind() == BindingKind::Var) { return true; } } return false; } Maybe ParserBase::newVarScopeData( ParseContext::Scope& scope) { return NewVarScopeData(fc_, scope, stencilAlloc(), pc_); } static Maybe NewLexicalScopeData( FrontendContext* fc, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc) { ParserBindingNameVector lets(fc); ParserBindingNameVector consts(fc); bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver() || scope.tooBigToOptimize(); for (BindingIter bi = scope.bindings(pc); bi; bi++) { ParserBindingName binding(bi.name(), allBindingsClosedOver || bi.closedOver()); switch (bi.kind()) { case BindingKind::Let: if (!lets.append(binding)) { return Nothing(); } break; case BindingKind::Const: if (!consts.append(binding)) { return Nothing(); } break; case BindingKind::Var: case BindingKind::FormalParameter: break; default: MOZ_CRASH("Bad lexical scope BindingKind"); break; } } LexicalScope::ParserData* bindings = nullptr; uint32_t numBindings = lets.length() + consts.length(); if (numBindings > 0) { bindings = NewEmptyBindingData(fc, alloc, numBindings); if (!bindings) { return Nothing(); } // The ordering here is important. See comments in LexicalScope. InitializeBindingData(bindings, numBindings, lets, &ParserLexicalScopeSlotInfo::constStart, consts); } return Some(bindings); } // Compute if `NewLexicalScopeData` would return any binding list with any entry // marked as closed-over. This is done without the need to allocate the binding // list. If true, an EnvironmentObject will be needed at runtime. bool LexicalScopeHasClosedOverBindings(ParseContext* pc, ParseContext::Scope& scope) { bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver() || scope.tooBigToOptimize(); for (BindingIter bi = scope.bindings(pc); bi; bi++) { switch (bi.kind()) { case BindingKind::Let: case BindingKind::Const: if (allBindingsClosedOver || bi.closedOver()) { return true; } break; default: break; } } return false; } Maybe ParserBase::newLexicalScopeData( ParseContext::Scope& scope) { return NewLexicalScopeData(fc_, scope, stencilAlloc(), pc_); } static Maybe NewClassBodyScopeData( FrontendContext* fc, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc) { ParserBindingNameVector privateBrand(fc); ParserBindingNameVector synthetics(fc); ParserBindingNameVector privateMethods(fc); bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver() || scope.tooBigToOptimize(); for (BindingIter bi = scope.bindings(pc); bi; bi++) { ParserBindingName binding(bi.name(), allBindingsClosedOver || bi.closedOver()); switch (bi.kind()) { case BindingKind::Synthetic: if (bi.name() == TaggedParserAtomIndex::WellKnown::dot_privateBrand_()) { MOZ_ASSERT(privateBrand.empty()); if (!privateBrand.append(binding)) { return Nothing(); } } else { if (!synthetics.append(binding)) { return Nothing(); } } break; case BindingKind::PrivateMethod: if (!privateMethods.append(binding)) { return Nothing(); } break; default: MOZ_CRASH("bad class body scope BindingKind"); break; } } // We should have zero or one private brands. MOZ_ASSERT(privateBrand.length() == 0 || privateBrand.length() == 1); ClassBodyScope::ParserData* bindings = nullptr; uint32_t numBindings = privateBrand.length() + synthetics.length() + privateMethods.length(); if (numBindings > 0) { bindings = NewEmptyBindingData(fc, alloc, numBindings); if (!bindings) { return Nothing(); } // To simplify initialization of the bindings, we concatenate the // synthetics+privateBrand vector such that the private brand is always the // first element, as ordering is important. See comments in ClassBodyScope. ParserBindingNameVector brandAndSynthetics(fc); if (!brandAndSynthetics.appendAll(privateBrand)) { return Nothing(); } if (!brandAndSynthetics.appendAll(synthetics)) { return Nothing(); } // The ordering here is important. See comments in ClassBodyScope. InitializeBindingData(bindings, numBindings, brandAndSynthetics, &ParserClassBodyScopeSlotInfo::privateMethodStart, privateMethods); } // `EmitterScope::lookupPrivate()` requires `.privateBrand` to be stored in a // predictable slot: the first slot available in the environment object, // `ClassBodyLexicalEnvironmentObject::privateBrandSlot()`. We assume that // if `.privateBrand` is first in the scope, it will be stored there. MOZ_ASSERT_IF(!privateBrand.empty(), GetScopeDataTrailingNames(bindings)[0].name() == TaggedParserAtomIndex::WellKnown::dot_privateBrand_()); return Some(bindings); } Maybe ParserBase::newClassBodyScopeData( ParseContext::Scope& scope) { return NewClassBodyScopeData(fc_, scope, stencilAlloc(), pc_); } template <> SyntaxParseHandler::LexicalScopeNodeResult PerHandlerParser::finishLexicalScope( ParseContext::Scope& scope, Node body, ScopeKind kind) { if (!propagateFreeNamesAndMarkClosedOverBindings(scope)) { return errorResult(); } return handler_.newLexicalScope(body); } template <> FullParseHandler::LexicalScopeNodeResult PerHandlerParser::finishLexicalScope( ParseContext::Scope& scope, ParseNode* body, ScopeKind kind) { if (!propagateFreeNamesAndMarkClosedOverBindings(scope)) { return errorResult(); } Maybe bindings = newLexicalScopeData(scope); if (!bindings) { return errorResult(); } return handler_.newLexicalScope(*bindings, body, kind); } template <> SyntaxParseHandler::ClassBodyScopeNodeResult PerHandlerParser::finishClassBodyScope( ParseContext::Scope& scope, ListNodeType body) { if (!propagateFreeNamesAndMarkClosedOverBindings(scope)) { return errorResult(); } return handler_.newClassBodyScope(body); } template <> FullParseHandler::ClassBodyScopeNodeResult PerHandlerParser::finishClassBodyScope( ParseContext::Scope& scope, ListNode* body) { if (!propagateFreeNamesAndMarkClosedOverBindings(scope)) { return errorResult(); } Maybe bindings = newClassBodyScopeData(scope); if (!bindings) { return errorResult(); } return handler_.newClassBodyScope(*bindings, body); } template bool PerHandlerParser::checkForUndefinedPrivateFields( EvalSharedContext* evalSc) { if (!this->compilationState_.isInitialStencil()) { // We're delazifying -- so we already checked private names during first // parse. return true; } Vector unboundPrivateNames(fc_); if (!usedNames_.getUnboundPrivateNames(unboundPrivateNames)) { return false; } // No unbound names, let's get out of here! if (unboundPrivateNames.empty()) { return true; } // It is an early error if there's private name references unbound, // unless it's an eval, in which case we need to check the scope // chain. if (!evalSc) { // The unbound private names are sorted, so just grab the first one. UnboundPrivateName minimum = unboundPrivateNames[0]; UniqueChars str = this->parserAtoms().toPrintableString(minimum.atom); if (!str) { ReportOutOfMemory(this->fc_); return false; } errorAt(minimum.position.begin, JSMSG_MISSING_PRIVATE_DECL, str.get()); return false; } // It's important that the unbound private names are sorted, as we // want our errors to always be issued to the first textually. for (UnboundPrivateName unboundName : unboundPrivateNames) { // If the enclosingScope is non-syntactic, then we are in a // Debugger.Frame.prototype.eval call. In order to find the declared private // names, we must use the effective scope that was determined when creating // the scopeContext. if (!this->compilationState_.scopeContext .effectiveScopePrivateFieldCacheHas(unboundName.atom)) { UniqueChars str = this->parserAtoms().toPrintableString(unboundName.atom); if (!str) { ReportOutOfMemory(this->fc_); return false; } errorAt(unboundName.position.begin, JSMSG_MISSING_PRIVATE_DECL, str.get()); return false; } } return true; } template FullParseHandler::LexicalScopeNodeResult Parser::evalBody(EvalSharedContext* evalsc) { SourceParseContext evalpc(this, evalsc, /* newDirectives = */ nullptr); if (!evalpc.init()) { return errorResult(); } ParseContext::VarScope varScope(this); if (!varScope.init(pc_)) { return errorResult(); } LexicalScopeNode* body; { // All evals have an implicit non-extensible lexical scope. ParseContext::Scope lexicalScope(this); if (!lexicalScope.init(pc_)) { return errorResult(); } ListNode* list; MOZ_TRY_VAR(list, statementList(YieldIsName)); if (!checkStatementsEOF()) { return errorResult(); } // Private names not lexically defined must trigger a syntax error. if (!checkForUndefinedPrivateFields(evalsc)) { return errorResult(); } MOZ_TRY_VAR(body, finishLexicalScope(lexicalScope, list)); } #ifdef DEBUG if (evalpc.superScopeNeedsHomeObject() && !this->compilationState_.input.enclosingScope.isNull()) { // If superScopeNeedsHomeObject_ is set and we are an entry-point // ParseContext, then we must be emitting an eval script, and the // outer function must already be marked as needing a home object // since it contains an eval. MOZ_ASSERT( this->compilationState_.scopeContext.hasFunctionNeedsHomeObjectOnChain, "Eval must have found an enclosing function box scope that " "allows super.property"); } #endif if (!CheckParseTree(this->fc_, alloc_, body)) { return errorResult(); } ParseNode* node = body; // Don't constant-fold inside "use asm" code, as this could create a parse // tree that doesn't type-check as asm.js. if (!pc_->useAsmOrInsideUseAsm()) { if (!FoldConstants(this->fc_, this->parserAtoms(), &node, &handler_)) { return errorResult(); } } body = handler_.asLexicalScopeNode(node); if (!this->setSourceMapInfo()) { return errorResult(); } if (pc_->sc()->strict()) { if (!propagateFreeNamesAndMarkClosedOverBindings(varScope)) { return errorResult(); } } else { // For non-strict eval scripts, since all bindings are automatically // considered closed over, we don't need to call propagateFreeNames- // AndMarkClosedOverBindings. However, Annex B.3.3 functions still need to // be marked. if (!varScope.propagateAndMarkAnnexBFunctionBoxes(pc_, this)) { return errorResult(); } } Maybe bindings = newEvalScopeData(pc_->varScope()); if (!bindings) { return errorResult(); } evalsc->bindings = *bindings; return body; } template FullParseHandler::ListNodeResult Parser::globalBody( GlobalSharedContext* globalsc) { SourceParseContext globalpc(this, globalsc, /* newDirectives = */ nullptr); if (!globalpc.init()) { return errorResult(); } ParseContext::VarScope varScope(this); if (!varScope.init(pc_)) { return errorResult(); } ListNode* body; MOZ_TRY_VAR(body, statementList(YieldIsName)); if (!checkStatementsEOF()) { return errorResult(); } if (!CheckParseTree(this->fc_, alloc_, body)) { return errorResult(); } if (!checkForUndefinedPrivateFields()) { return errorResult(); } ParseNode* node = body; // Don't constant-fold inside "use asm" code, as this could create a parse // tree that doesn't type-check as asm.js. if (!pc_->useAsmOrInsideUseAsm()) { if (!FoldConstants(this->fc_, this->parserAtoms(), &node, &handler_)) { return errorResult(); } } body = &node->as(); if (!this->setSourceMapInfo()) { return errorResult(); } // For global scripts, whether bindings are closed over or not doesn't // matter, so no need to call propagateFreeNamesAndMarkClosedOver- // Bindings. However, Annex B.3.3 functions still need to be marked. if (!varScope.propagateAndMarkAnnexBFunctionBoxes(pc_, this)) { return errorResult(); } Maybe bindings = newGlobalScopeData(pc_->varScope()); if (!bindings) { return errorResult(); } globalsc->bindings = *bindings; return body; } template FullParseHandler::ModuleNodeResult Parser::moduleBody( ModuleSharedContext* modulesc) { MOZ_ASSERT(checkOptionsCalled_); this->compilationState_.moduleMetadata = fc_->getAllocator()->template new_(); if (!this->compilationState_.moduleMetadata) { return errorResult(); } SourceParseContext modulepc(this, modulesc, nullptr); if (!modulepc.init()) { return errorResult(); } ParseContext::VarScope varScope(this); if (!varScope.init(pc_)) { return errorResult(); } ModuleNodeType moduleNode; MOZ_TRY_VAR(moduleNode, handler_.newModule(pos())); AutoAwaitIsKeyword awaitIsKeyword( this, AwaitIsModuleKeyword); ListNode* stmtList; MOZ_TRY_VAR(stmtList, statementList(YieldIsName)); MOZ_ASSERT(stmtList->isKind(ParseNodeKind::StatementList)); moduleNode->setBody(&stmtList->template as()); if (pc_->isAsync()) { if (!noteUsedName(TaggedParserAtomIndex::WellKnown::dot_generator_())) { return errorResult(); } if (!pc_->declareTopLevelDotGeneratorName()) { return errorResult(); } } TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } if (tt != TokenKind::Eof) { error(JSMSG_GARBAGE_AFTER_INPUT, "module", TokenKindToDesc(tt)); return errorResult(); } // Set the module to async if an await keyword was found at the top level. if (pc_->isAsync()) { pc_->sc()->asModuleContext()->builder.noteAsync( *this->compilationState_.moduleMetadata); } // Generate the Import/Export tables and store in CompilationState. if (!modulesc->builder.buildTables(*this->compilationState_.moduleMetadata)) { return errorResult(); } // Check exported local bindings exist and mark them as closed over. StencilModuleMetadata& moduleMetadata = *this->compilationState_.moduleMetadata; for (auto entry : moduleMetadata.localExportEntries) { DeclaredNamePtr p = modulepc.varScope().lookupDeclaredName(entry.localName); if (!p) { UniqueChars str = this->parserAtoms().toPrintableString(entry.localName); if (!str) { ReportOutOfMemory(this->fc_); return errorResult(); } errorNoOffset(JSMSG_MISSING_EXPORT, str.get()); return errorResult(); } p->value()->setClosedOver(); } // Reserve an environment slot for a "*namespace*" psuedo-binding and mark as // closed-over. We do not know until module linking if this will be used. if (!noteDeclaredName( TaggedParserAtomIndex::WellKnown::star_namespace_star_(), DeclarationKind::Const, pos())) { return errorResult(); } modulepc.varScope() .lookupDeclaredName( TaggedParserAtomIndex::WellKnown::star_namespace_star_()) ->value() ->setClosedOver(); if (options().deoptimizeModuleGlobalVars) { for (BindingIter bi = modulepc.varScope().bindings(pc_); bi; bi++) { bi.setClosedOver(); } } if (!CheckParseTree(this->fc_, alloc_, stmtList)) { return errorResult(); } ParseNode* node = stmtList; // Don't constant-fold inside "use asm" code, as this could create a parse // tree that doesn't type-check as asm.js. if (!pc_->useAsmOrInsideUseAsm()) { if (!FoldConstants(this->fc_, this->parserAtoms(), &node, &handler_)) { return errorResult(); } } stmtList = &node->as(); if (!this->setSourceMapInfo()) { return errorResult(); } // Private names not lexically defined must trigger a syntax error. if (!checkForUndefinedPrivateFields()) { return errorResult(); } if (!propagateFreeNamesAndMarkClosedOverBindings(modulepc.varScope())) { return errorResult(); } Maybe bindings = newModuleScopeData(modulepc.varScope()); if (!bindings) { return errorResult(); } modulesc->bindings = *bindings; return moduleNode; } template SyntaxParseHandler::ModuleNodeResult Parser::moduleBody(ModuleSharedContext* modulesc) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return errorResult(); } template typename ParseHandler::NameNodeResult PerHandlerParser::newInternalDotName(TaggedParserAtomIndex name) { NameNodeType nameNode; MOZ_TRY_VAR(nameNode, newName(name)); if (!noteUsedName(name)) { return errorResult(); } return nameNode; } template typename ParseHandler::NameNodeResult PerHandlerParser::newThisName() { return newInternalDotName(TaggedParserAtomIndex::WellKnown::dot_this_()); } template typename ParseHandler::NameNodeResult PerHandlerParser::newNewTargetName() { return newInternalDotName(TaggedParserAtomIndex::WellKnown::dot_newTarget_()); } template typename ParseHandler::NameNodeResult PerHandlerParser::newDotGeneratorName() { return newInternalDotName(TaggedParserAtomIndex::WellKnown::dot_generator_()); } template bool PerHandlerParser::finishFunctionScopes( bool isStandaloneFunction) { FunctionBox* funbox = pc_->functionBox(); if (funbox->hasParameterExprs) { if (!propagateFreeNamesAndMarkClosedOverBindings(pc_->functionScope())) { return false; } // Functions with parameter expressions utilize the FunctionScope for vars // generated by sloppy-direct-evals, as well as arguments (which are // lexicals bindings). If the function body has var bindings (or has a // sloppy-direct-eval that might), then an extra VarScope must be created // for them. if (VarScopeHasBindings(pc_) || funbox->needsExtraBodyVarEnvironmentRegardlessOfBindings()) { funbox->setFunctionHasExtraBodyVarScope(); } } // See: JSFunction::needsCallObject() if (FunctionScopeHasClosedOverBindings(pc_) || funbox->needsCallObjectRegardlessOfBindings()) { funbox->setNeedsFunctionEnvironmentObjects(); } if (funbox->isNamedLambda() && !isStandaloneFunction) { if (!propagateFreeNamesAndMarkClosedOverBindings(pc_->namedLambdaScope())) { return false; } // See: JSFunction::needsNamedLambdaEnvironment() if (LexicalScopeHasClosedOverBindings(pc_, pc_->namedLambdaScope())) { funbox->setNeedsFunctionEnvironmentObjects(); } } return true; } template <> bool PerHandlerParser::finishFunction( bool isStandaloneFunction /* = false */) { if (!finishFunctionScopes(isStandaloneFunction)) { return false; } FunctionBox* funbox = pc_->functionBox(); ScriptStencil& script = funbox->functionStencil(); if (funbox->isInterpreted()) { // BCE will need to generate bytecode for this. funbox->emitBytecode = true; this->compilationState_.nonLazyFunctionCount++; } bool hasParameterExprs = funbox->hasParameterExprs; if (hasParameterExprs) { Maybe bindings = newVarScopeData(pc_->varScope()); if (!bindings) { return false; } funbox->setExtraVarScopeBindings(*bindings); MOZ_ASSERT(bool(*bindings) == VarScopeHasBindings(pc_)); MOZ_ASSERT_IF(!funbox->needsExtraBodyVarEnvironmentRegardlessOfBindings(), bool(*bindings) == funbox->functionHasExtraBodyVarScope()); } { Maybe bindings = newFunctionScopeData(pc_->functionScope(), hasParameterExprs); if (!bindings) { return false; } funbox->setFunctionScopeBindings(*bindings); } if (funbox->isNamedLambda() && !isStandaloneFunction) { Maybe bindings = newLexicalScopeData(pc_->namedLambdaScope()); if (!bindings) { return false; } funbox->setNamedLambdaBindings(*bindings); } funbox->finishScriptFlags(); funbox->copyFunctionFields(script); if (this->compilationState_.isInitialStencil()) { ScriptStencilExtra& scriptExtra = funbox->functionExtraStencil(); funbox->copyFunctionExtraFields(scriptExtra); funbox->copyScriptExtraFields(scriptExtra); } return true; } template <> bool PerHandlerParser::finishFunction( bool isStandaloneFunction /* = false */) { // The BaseScript for a lazily parsed function needs to know its set of // free variables and inner functions so that when it is fully parsed, we // can skip over any already syntax parsed inner functions and still // retain correct scope information. if (!finishFunctionScopes(isStandaloneFunction)) { return false; } FunctionBox* funbox = pc_->functionBox(); ScriptStencil& script = funbox->functionStencil(); funbox->finishScriptFlags(); funbox->copyFunctionFields(script); ScriptStencilExtra& scriptExtra = funbox->functionExtraStencil(); funbox->copyFunctionExtraFields(scriptExtra); funbox->copyScriptExtraFields(scriptExtra); // Elide nullptr sentinels from end of binding list. These are inserted for // each scope regardless of if any bindings are actually closed over. { AtomVector& closedOver = pc_->closedOverBindingsForLazy(); while (!closedOver.empty() && !closedOver.back()) { closedOver.popBack(); } } // Check if we will overflow the `ngcthings` field later. mozilla::CheckedUint32 ngcthings = mozilla::CheckedUint32(pc_->innerFunctionIndexesForLazy.length()) + mozilla::CheckedUint32(pc_->closedOverBindingsForLazy().length()); if (!ngcthings.isValid()) { ReportAllocationOverflow(fc_); return false; } // If there are no script-things, we can return early without allocating. if (ngcthings.value() == 0) { MOZ_ASSERT(!script.hasGCThings()); return true; } TaggedScriptThingIndex* cursor = nullptr; if (!this->compilationState_.allocateGCThingsUninitialized( fc_, funbox->index(), ngcthings.value(), &cursor)) { return false; } // Copy inner-function and closed-over-binding info for the stencil. The order // is important here. We emit functions first, followed by the bindings info. // The bindings list uses nullptr as delimiter to separates the bindings per // scope. // // See: FullParseHandler::nextLazyInnerFunction(), // FullParseHandler::nextLazyClosedOverBinding() for (const ScriptIndex& index : pc_->innerFunctionIndexesForLazy) { void* raw = &(*cursor++); new (raw) TaggedScriptThingIndex(index); } for (auto binding : pc_->closedOverBindingsForLazy()) { void* raw = &(*cursor++); if (binding) { this->parserAtoms().markUsedByStencil(binding, ParserAtom::Atomize::Yes); new (raw) TaggedScriptThingIndex(binding); } else { new (raw) TaggedScriptThingIndex(); } } return true; } static YieldHandling GetYieldHandling(GeneratorKind generatorKind) { if (generatorKind == GeneratorKind::NotGenerator) { return YieldIsName; } return YieldIsKeyword; } static AwaitHandling GetAwaitHandling(FunctionAsyncKind asyncKind) { if (asyncKind == FunctionAsyncKind::SyncFunction) { return AwaitIsName; } return AwaitIsKeyword; } static FunctionFlags InitialFunctionFlags(FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool isSelfHosting) { FunctionFlags flags = {}; switch (kind) { case FunctionSyntaxKind::Expression: flags = (generatorKind == GeneratorKind::NotGenerator && asyncKind == FunctionAsyncKind::SyncFunction ? FunctionFlags::INTERPRETED_LAMBDA : FunctionFlags::INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC); break; case FunctionSyntaxKind::Arrow: flags = FunctionFlags::INTERPRETED_LAMBDA_ARROW; break; case FunctionSyntaxKind::Method: case FunctionSyntaxKind::FieldInitializer: case FunctionSyntaxKind::StaticClassBlock: flags = FunctionFlags::INTERPRETED_METHOD; break; case FunctionSyntaxKind::ClassConstructor: case FunctionSyntaxKind::DerivedClassConstructor: flags = FunctionFlags::INTERPRETED_CLASS_CTOR; break; case FunctionSyntaxKind::Getter: flags = FunctionFlags::INTERPRETED_GETTER; break; case FunctionSyntaxKind::Setter: flags = FunctionFlags::INTERPRETED_SETTER; break; default: MOZ_ASSERT(kind == FunctionSyntaxKind::Statement); flags = (generatorKind == GeneratorKind::NotGenerator && asyncKind == FunctionAsyncKind::SyncFunction ? FunctionFlags::INTERPRETED_NORMAL : FunctionFlags::INTERPRETED_GENERATOR_OR_ASYNC); } if (isSelfHosting) { flags.setIsSelfHostedBuiltin(); } return flags; } template FullParseHandler::FunctionNodeResult Parser::standaloneFunction( const Maybe& parameterListEnd, FunctionSyntaxKind syntaxKind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, Directives inheritedDirectives, Directives* newDirectives) { MOZ_ASSERT(checkOptionsCalled_); // Skip prelude. TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } if (asyncKind == FunctionAsyncKind::AsyncFunction) { MOZ_ASSERT(tt == TokenKind::Async); if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } } MOZ_ASSERT(tt == TokenKind::Function); if (!tokenStream.getToken(&tt)) { return errorResult(); } if (generatorKind == GeneratorKind::Generator) { MOZ_ASSERT(tt == TokenKind::Mul); if (!tokenStream.getToken(&tt)) { return errorResult(); } } // Skip function name, if present. TaggedParserAtomIndex explicitName; if (TokenKindIsPossibleIdentifierName(tt)) { explicitName = anyChars.currentName(); } else { anyChars.ungetToken(); } FunctionNodeType funNode; MOZ_TRY_VAR(funNode, handler_.newFunction(syntaxKind, pos())); ParamsBodyNodeType argsbody; MOZ_TRY_VAR(argsbody, handler_.newParamsBody(pos())); funNode->setBody(argsbody); bool isSelfHosting = options().selfHostingMode; FunctionFlags flags = InitialFunctionFlags(syntaxKind, generatorKind, asyncKind, isSelfHosting); FunctionBox* funbox = newFunctionBox(funNode, explicitName, flags, /* toStringStart = */ 0, inheritedDirectives, generatorKind, asyncKind); if (!funbox) { return errorResult(); } // Function is not syntactically part of another script. MOZ_ASSERT(funbox->index() == CompilationStencil::TopLevelIndex); funbox->initStandalone(this->compilationState_.scopeContext, syntaxKind); SourceParseContext funpc(this, funbox, newDirectives); if (!funpc.init()) { return errorResult(); } YieldHandling yieldHandling = GetYieldHandling(generatorKind); AwaitHandling awaitHandling = GetAwaitHandling(asyncKind); AutoAwaitIsKeyword awaitIsKeyword(this, awaitHandling); if (!functionFormalParametersAndBody(InAllowed, yieldHandling, &funNode, syntaxKind, parameterListEnd, /* isStandaloneFunction = */ true)) { return errorResult(); } if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } if (tt != TokenKind::Eof) { error(JSMSG_GARBAGE_AFTER_INPUT, "function body", TokenKindToDesc(tt)); return errorResult(); } if (!CheckParseTree(this->fc_, alloc_, funNode)) { return errorResult(); } ParseNode* node = funNode; // Don't constant-fold inside "use asm" code, as this could create a parse // tree that doesn't type-check as asm.js. if (!pc_->useAsmOrInsideUseAsm()) { if (!FoldConstants(this->fc_, this->parserAtoms(), &node, &handler_)) { return errorResult(); } } funNode = &node->as(); if (!checkForUndefinedPrivateFields(nullptr)) { return errorResult(); } if (!this->setSourceMapInfo()) { return errorResult(); } return funNode; } template typename ParseHandler::LexicalScopeNodeResult GeneralParser::functionBody(InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, FunctionBodyType type) { MOZ_ASSERT(pc_->isFunctionBox()); #ifdef DEBUG uint32_t startYieldOffset = pc_->lastYieldOffset; #endif Node body; if (type == StatementListBody) { bool inheritedStrict = pc_->sc()->strict(); MOZ_TRY_VAR(body, statementList(yieldHandling)); // When we transitioned from non-strict to strict mode, we need to // validate that all parameter names are valid strict mode names. if (!inheritedStrict && pc_->sc()->strict()) { MOZ_ASSERT(pc_->sc()->hasExplicitUseStrict(), "strict mode should only change when a 'use strict' directive " "is present"); if (!hasValidSimpleStrictParameterNames()) { // Request that this function be reparsed as strict to report // the invalid parameter name at the correct source location. pc_->newDirectives->setStrict(); return errorResult(); } } } else { MOZ_ASSERT(type == ExpressionBody); // Async functions are implemented as generators, and generators are // assumed to be statement lists, to prepend initial `yield`. ListNodeType stmtList = null(); if (pc_->isAsync()) { MOZ_TRY_VAR(stmtList, handler_.newStatementList(pos())); } Node kid; MOZ_TRY_VAR(kid, assignExpr(inHandling, yieldHandling, TripledotProhibited)); MOZ_TRY_VAR(body, handler_.newExpressionBody(kid)); if (pc_->isAsync()) { handler_.addStatementToList(stmtList, body); body = stmtList; } } MOZ_ASSERT_IF(!pc_->isGenerator() && !pc_->isAsync(), pc_->lastYieldOffset == startYieldOffset); MOZ_ASSERT_IF(pc_->isGenerator(), kind != FunctionSyntaxKind::Arrow); MOZ_ASSERT_IF(pc_->isGenerator(), type == StatementListBody); if (pc_->needsDotGeneratorName()) { MOZ_ASSERT_IF(!pc_->isAsync(), type == StatementListBody); if (!pc_->declareDotGeneratorName()) { return errorResult(); } if (pc_->isGenerator()) { NameNodeType generator; MOZ_TRY_VAR(generator, newDotGeneratorName()); if (!handler_.prependInitialYield(handler_.asListNode(body), generator)) { return errorResult(); } } } if (pc_->numberOfArgumentsNames > 0 || kind == FunctionSyntaxKind::Arrow) { MOZ_ASSERT(pc_->isFunctionBox()); pc_->sc()->setIneligibleForArgumentsLength(); } // Declare the 'arguments', 'this', and 'new.target' bindings if necessary // before finishing up the scope so these special bindings get marked as // closed over if necessary. Arrow functions don't have these bindings. if (kind != FunctionSyntaxKind::Arrow) { bool canSkipLazyClosedOverBindings = handler_.reuseClosedOverBindings(); if (!pc_->declareFunctionArgumentsObject(usedNames_, canSkipLazyClosedOverBindings)) { return errorResult(); } if (!pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings)) { return errorResult(); } if (!pc_->declareNewTarget(usedNames_, canSkipLazyClosedOverBindings)) { return errorResult(); } } return finishLexicalScope(pc_->varScope(), body, ScopeKind::FunctionLexical); } template bool GeneralParser::matchOrInsertSemicolon( Modifier modifier /* = TokenStream::SlashIsRegExp */) { TokenKind tt = TokenKind::Eof; if (!tokenStream.peekTokenSameLine(&tt, modifier)) { return false; } if (tt != TokenKind::Eof && tt != TokenKind::Eol && tt != TokenKind::Semi && tt != TokenKind::RightCurly) { /* * When current token is `await` and it's outside of async function, * it's possibly intended to be an await expression. * * await f(); * ^ * | * tried to insert semicolon here * * Detect this situation and throw an understandable error. Otherwise * we'd throw a confusing "unexpected token: (unexpected token)" error. */ if (!pc_->isAsync() && anyChars.currentToken().type == TokenKind::Await) { error(JSMSG_AWAIT_OUTSIDE_ASYNC_OR_MODULE); return false; } if (!yieldExpressionsSupported() && anyChars.currentToken().type == TokenKind::Yield) { error(JSMSG_YIELD_OUTSIDE_GENERATOR); return false; } /* Advance the scanner for proper error location reporting. */ tokenStream.consumeKnownToken(tt, modifier); error(JSMSG_UNEXPECTED_TOKEN_NO_EXPECT, TokenKindToDesc(tt)); return false; } bool matched; return tokenStream.matchToken(&matched, TokenKind::Semi, modifier); } bool ParserBase::leaveInnerFunction(ParseContext* outerpc) { MOZ_ASSERT(pc_ != outerpc); MOZ_ASSERT_IF(outerpc->isFunctionBox(), outerpc->functionBox()->index() < pc_->functionBox()->index()); // If the current function allows super.property but cannot have a home // object, i.e., it is an arrow function, we need to propagate the flag to // the outer ParseContext. if (pc_->superScopeNeedsHomeObject()) { if (!pc_->isArrowFunction()) { MOZ_ASSERT(pc_->functionBox()->needsHomeObject()); } else { outerpc->setSuperScopeNeedsHomeObject(); } } // Lazy functions inner to another lazy function need to be remembered by // the inner function so that if the outer function is eventually parsed // we do not need any further parsing or processing of the inner function. // // Append the inner function index here unconditionally; the vector is only // used if the Parser using outerpc is a syntax parsing. See // GeneralParser::finishFunction. if (!outerpc->innerFunctionIndexesForLazy.append( pc_->functionBox()->index())) { return false; } PropagateTransitiveParseFlags(pc_->functionBox(), outerpc->sc()); return true; } TaggedParserAtomIndex ParserBase::prefixAccessorName( PropertyType propType, TaggedParserAtomIndex propAtom) { StringBuffer prefixed(fc_); if (propType == PropertyType::Setter) { if (!prefixed.append("set ")) { return TaggedParserAtomIndex::null(); } } else { if (!prefixed.append("get ")) { return TaggedParserAtomIndex::null(); } } if (!prefixed.append(this->parserAtoms(), propAtom)) { return TaggedParserAtomIndex::null(); } return prefixed.finishParserAtom(this->parserAtoms(), fc_); } template void GeneralParser::setFunctionStartAtPosition( FunctionBox* funbox, TokenPos pos) const { uint32_t startLine; JS::LimitedColumnNumberOneOrigin startColumn; tokenStream.computeLineAndColumn(pos.begin, &startLine, &startColumn); // NOTE: `Debugger::CallData::findScripts` relies on sourceStart and // lineno/column referring to the same location. funbox->setStart(pos.begin, startLine, startColumn); } template void GeneralParser::setFunctionStartAtCurrentToken( FunctionBox* funbox) const { setFunctionStartAtPosition(funbox, anyChars.currentToken().pos); } template bool GeneralParser::functionArguments( YieldHandling yieldHandling, FunctionSyntaxKind kind, FunctionNodeType funNode) { FunctionBox* funbox = pc_->functionBox(); // Modifier for the following tokens. // TokenStream::SlashIsDiv for the following cases: // async a => 1 // ^ // // (a) => 1 // ^ // // async (a) => 1 // ^ // // function f(a) {} // ^ // // TokenStream::SlashIsRegExp for the following case: // a => 1 // ^ Modifier firstTokenModifier = kind != FunctionSyntaxKind::Arrow || funbox->isAsync() ? TokenStream::SlashIsDiv : TokenStream::SlashIsRegExp; TokenKind tt; if (!tokenStream.getToken(&tt, firstTokenModifier)) { return false; } if (kind == FunctionSyntaxKind::Arrow && TokenKindIsPossibleIdentifier(tt)) { // Record the start of function source (for FunctionToString). setFunctionStartAtCurrentToken(funbox); ParamsBodyNodeType argsbody; MOZ_TRY_VAR_OR_RETURN(argsbody, handler_.newParamsBody(pos()), false); handler_.setFunctionFormalParametersAndBody(funNode, argsbody); TaggedParserAtomIndex name = bindingIdentifier(yieldHandling); if (!name) { return false; } constexpr bool disallowDuplicateParams = true; bool duplicatedParam = false; if (!notePositionalFormalParameter(funNode, name, pos().begin, disallowDuplicateParams, &duplicatedParam)) { return false; } MOZ_ASSERT(!duplicatedParam); MOZ_ASSERT(pc_->positionalFormalParameterNames().length() == 1); funbox->setLength(1); funbox->setArgCount(1); return true; } if (tt != TokenKind::LeftParen) { error(kind == FunctionSyntaxKind::Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL); return false; } // Record the start of function source (for FunctionToString). setFunctionStartAtCurrentToken(funbox); ParamsBodyNodeType argsbody; MOZ_TRY_VAR_OR_RETURN(argsbody, handler_.newParamsBody(pos()), false); handler_.setFunctionFormalParametersAndBody(funNode, argsbody); bool matched; if (!tokenStream.matchToken(&matched, TokenKind::RightParen, TokenStream::SlashIsRegExp)) { return false; } if (!matched) { bool hasRest = false; bool hasDefault = false; bool duplicatedParam = false; bool disallowDuplicateParams = kind == FunctionSyntaxKind::Arrow || kind == FunctionSyntaxKind::Method || kind == FunctionSyntaxKind::FieldInitializer || kind == FunctionSyntaxKind::ClassConstructor; AtomVector& positionalFormals = pc_->positionalFormalParameterNames(); if (kind == FunctionSyntaxKind::Getter) { error(JSMSG_ACCESSOR_WRONG_ARGS, "getter", "no", "s"); return false; } while (true) { if (hasRest) { error(JSMSG_PARAMETER_AFTER_REST); return false; } TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return false; } if (tt == TokenKind::TripleDot) { if (kind == FunctionSyntaxKind::Setter) { error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", ""); return false; } disallowDuplicateParams = true; if (duplicatedParam) { // Has duplicated args before the rest parameter. error(JSMSG_BAD_DUP_ARGS); return false; } hasRest = true; funbox->setHasRest(); if (!tokenStream.getToken(&tt)) { return false; } if (!TokenKindIsPossibleIdentifier(tt) && tt != TokenKind::LeftBracket && tt != TokenKind::LeftCurly) { error(JSMSG_NO_REST_NAME); return false; } } switch (tt) { case TokenKind::LeftBracket: case TokenKind::LeftCurly: { disallowDuplicateParams = true; if (duplicatedParam) { // Has duplicated args before the destructuring parameter. error(JSMSG_BAD_DUP_ARGS); return false; } funbox->hasDestructuringArgs = true; Node destruct; MOZ_TRY_VAR_OR_RETURN( destruct, destructuringDeclarationWithoutYieldOrAwait( DeclarationKind::FormalParameter, yieldHandling, tt), false); if (!noteDestructuredPositionalFormalParameter(funNode, destruct)) { return false; } break; } default: { if (!TokenKindIsPossibleIdentifier(tt)) { error(JSMSG_MISSING_FORMAL); return false; } TaggedParserAtomIndex name = bindingIdentifier(yieldHandling); if (!name) { return false; } if (!notePositionalFormalParameter(funNode, name, pos().begin, disallowDuplicateParams, &duplicatedParam)) { return false; } if (duplicatedParam) { funbox->hasDuplicateParameters = true; } break; } } if (positionalFormals.length() >= ARGNO_LIMIT) { error(JSMSG_TOO_MANY_FUN_ARGS); return false; } bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Assign, TokenStream::SlashIsRegExp)) { return false; } if (matched) { if (hasRest) { error(JSMSG_REST_WITH_DEFAULT); return false; } disallowDuplicateParams = true; if (duplicatedParam) { error(JSMSG_BAD_DUP_ARGS); return false; } if (!hasDefault) { hasDefault = true; // The Function.length property is the number of formals // before the first default argument. funbox->setLength(positionalFormals.length() - 1); } funbox->hasParameterExprs = true; Node def_expr; MOZ_TRY_VAR_OR_RETURN( def_expr, assignExprWithoutYieldOrAwait(yieldHandling), false); if (!handler_.setLastFunctionFormalParameterDefault(funNode, def_expr)) { return false; } } // Setter syntax uniquely requires exactly one argument. if (kind == FunctionSyntaxKind::Setter) { break; } if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::SlashIsRegExp)) { return false; } if (!matched) { break; } if (!hasRest) { if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) { return false; } if (tt == TokenKind::RightParen) { break; } } } TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return false; } if (tt != TokenKind::RightParen) { if (kind == FunctionSyntaxKind::Setter) { error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", ""); return false; } error(JSMSG_PAREN_AFTER_FORMAL); return false; } if (!hasDefault) { funbox->setLength(positionalFormals.length() - hasRest); } funbox->setArgCount(positionalFormals.length()); } else if (kind == FunctionSyntaxKind::Setter) { error(JSMSG_ACCESSOR_WRONG_ARGS, "setter", "one", ""); return false; } return true; } template bool Parser::skipLazyInnerFunction( FunctionNode* funNode, uint32_t toStringStart, bool tryAnnexB) { // When a lazily-parsed function is called, we only fully parse (and emit) // that function, not any of its nested children. The initial syntax-only // parse recorded the free variables of nested functions and their extents, // so we can skip over them after accounting for their free variables. MOZ_ASSERT(pc_->isOutermostOfCurrentCompile()); handler_.nextLazyInnerFunction(); const ScriptStencil& cachedData = handler_.cachedScriptData(); const ScriptStencilExtra& cachedExtra = handler_.cachedScriptExtra(); MOZ_ASSERT(toStringStart == cachedExtra.extent.toStringStart); FunctionBox* funbox = newFunctionBox(funNode, cachedData, cachedExtra); if (!funbox) { return false; } ScriptStencil& script = funbox->functionStencil(); funbox->copyFunctionFields(script); // If the inner lazy function is class constructor, connect it to the class // statement/expression we are parsing. if (funbox->isClassConstructor()) { auto classStmt = pc_->template findInnermostStatement(); MOZ_ASSERT(!classStmt->constructorBox); classStmt->constructorBox = funbox; } MOZ_ASSERT_IF(pc_->isFunctionBox(), pc_->functionBox()->index() < funbox->index()); PropagateTransitiveParseFlags(funbox, pc_->sc()); if (!tokenStream.advance(funbox->extent().sourceEnd)) { return false; } // Append possible Annex B function box only upon successfully parsing. if (tryAnnexB && !pc_->innermostScope()->addPossibleAnnexBFunctionBox(pc_, funbox)) { return false; } return true; } template bool Parser::skipLazyInnerFunction( FunctionNodeType funNode, uint32_t toStringStart, bool tryAnnexB) { MOZ_CRASH("Cannot skip lazy inner functions when syntax parsing"); } template bool GeneralParser::skipLazyInnerFunction( FunctionNodeType funNode, uint32_t toStringStart, bool tryAnnexB) { return asFinalParser()->skipLazyInnerFunction(funNode, toStringStart, tryAnnexB); } template bool GeneralParser::addExprAndGetNextTemplStrToken( YieldHandling yieldHandling, ListNodeType nodeList, TokenKind* ttp) { Node pn; MOZ_TRY_VAR_OR_RETURN(pn, expr(InAllowed, yieldHandling, TripledotProhibited), false); handler_.addList(nodeList, pn); TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return false; } if (tt != TokenKind::RightCurly) { error(JSMSG_TEMPLSTR_UNTERM_EXPR); return false; } return tokenStream.getTemplateToken(ttp); } template bool GeneralParser::taggedTemplate( YieldHandling yieldHandling, ListNodeType tagArgsList, TokenKind tt) { CallSiteNodeType callSiteObjNode; MOZ_TRY_VAR_OR_RETURN(callSiteObjNode, handler_.newCallSiteObject(pos().begin), false); handler_.addList(tagArgsList, callSiteObjNode); pc_->sc()->setHasCallSiteObj(); while (true) { if (!appendToCallSiteObj(callSiteObjNode)) { return false; } if (tt != TokenKind::TemplateHead) { break; } if (!addExprAndGetNextTemplStrToken(yieldHandling, tagArgsList, &tt)) { return false; } } handler_.setEndPosition(tagArgsList, callSiteObjNode); return true; } template typename ParseHandler::ListNodeResult GeneralParser::templateLiteral( YieldHandling yieldHandling) { NameNodeType literal; MOZ_TRY_VAR(literal, noSubstitutionUntaggedTemplate()); ListNodeType nodeList; MOZ_TRY_VAR(nodeList, handler_.newList(ParseNodeKind::TemplateStringListExpr, literal)); TokenKind tt; do { if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt)) { return errorResult(); } MOZ_TRY_VAR(literal, noSubstitutionUntaggedTemplate()); handler_.addList(nodeList, literal); } while (tt == TokenKind::TemplateHead); return nodeList; } template typename ParseHandler::FunctionNodeResult GeneralParser::functionDefinition( FunctionNodeType funNode, uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling, TaggedParserAtomIndex funName, FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB /* = false */) { MOZ_ASSERT_IF(kind == FunctionSyntaxKind::Statement, funName); // If we see any inner function, note it on our current context. The bytecode // emitter may eliminate the function later, but we use a conservative // definition for consistency between lazy and full parsing. pc_->sc()->setHasInnerFunctions(); // When fully parsing a lazy script, we do not fully reparse its inner // functions, which are also lazy. Instead, their free variables and source // extents are recorded and may be skipped. if (handler_.reuseLazyInnerFunctions()) { if (!skipLazyInnerFunction(funNode, toStringStart, tryAnnexB)) { return errorResult(); } return funNode; } bool isSelfHosting = options().selfHostingMode; FunctionFlags flags = InitialFunctionFlags(kind, generatorKind, asyncKind, isSelfHosting); // Self-hosted functions with special function names require extended slots // for various purposes. bool forceExtended = isSelfHosting && funName && this->parserAtoms().isExtendedUnclonedSelfHostedFunctionName(funName); if (forceExtended) { flags.setIsExtended(); } // Speculatively parse using the directives of the parent parsing context. // If a directive is encountered (e.g., "use strict") that changes how the // function should have been parsed, we backup and reparse with the new set // of directives. Directives directives(pc_); Directives newDirectives = directives; Position start(tokenStream); auto startObj = this->compilationState_.getPosition(); // Parse the inner function. The following is a loop as we may attempt to // reparse a function due to failed syntax parsing and encountering new // "use foo" directives. while (true) { if (trySyntaxParseInnerFunction(&funNode, funName, flags, toStringStart, inHandling, yieldHandling, kind, generatorKind, asyncKind, tryAnnexB, directives, &newDirectives)) { break; } // Return on error. if (anyChars.hadError() || directives == newDirectives) { return errorResult(); } // Assignment must be monotonic to prevent infinitely attempting to // reparse. MOZ_ASSERT_IF(directives.strict(), newDirectives.strict()); MOZ_ASSERT_IF(directives.asmJS(), newDirectives.asmJS()); directives = newDirectives; // Rewind to retry parsing with new directives applied. tokenStream.rewind(start); this->compilationState_.rewind(startObj); // functionFormalParametersAndBody may have already set body before failing. handler_.setFunctionFormalParametersAndBody(funNode, null()); } return funNode; } template bool Parser::advancePastSyntaxParsedFunction( SyntaxParser* syntaxParser) { MOZ_ASSERT(getSyntaxParser() == syntaxParser); // Advance this parser over tokens processed by the syntax parser. Position currentSyntaxPosition(syntaxParser->tokenStream); if (!tokenStream.fastForward(currentSyntaxPosition, syntaxParser->anyChars)) { return false; } anyChars.adoptState(syntaxParser->anyChars); tokenStream.adoptState(syntaxParser->tokenStream); return true; } template bool Parser::trySyntaxParseInnerFunction( FunctionNode** funNode, TaggedParserAtomIndex explicitName, FunctionFlags flags, uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB, Directives inheritedDirectives, Directives* newDirectives) { // Try a syntax parse for this inner function. do { // If we're assuming this function is an IIFE, always perform a full // parse to avoid the overhead of a lazy syntax-only parse. Although // the prediction may be incorrect, IIFEs are common enough that it // pays off for lots of code. if ((*funNode)->isLikelyIIFE() && generatorKind == GeneratorKind::NotGenerator && asyncKind == FunctionAsyncKind::SyncFunction) { break; } SyntaxParser* syntaxParser = getSyntaxParser(); if (!syntaxParser) { break; } UsedNameTracker::RewindToken token = usedNames_.getRewindToken(); auto statePosition = this->compilationState_.getPosition(); // Move the syntax parser to the current position in the stream. In the // common case this seeks forward, but it'll also seek backward *at least* // when arrow functions appear inside arrow function argument defaults // (because we rewind to reparse arrow functions once we're certain they're // arrow functions): // // var x = (y = z => 2) => q; // // ^ we first seek to here to syntax-parse this function // // ^ then we seek back to here to syntax-parse the outer function Position currentPosition(tokenStream); if (!syntaxParser->tokenStream.seekTo(currentPosition, anyChars)) { return false; } // Make a FunctionBox before we enter the syntax parser, because |pn| // still expects a FunctionBox to be attached to it during BCE, and // the syntax parser cannot attach one to it. FunctionBox* funbox = newFunctionBox(*funNode, explicitName, flags, toStringStart, inheritedDirectives, generatorKind, asyncKind); if (!funbox) { return false; } funbox->initWithEnclosingParseContext(pc_, kind); auto syntaxNodeResult = syntaxParser->innerFunctionForFunctionBox( SyntaxParseHandler::Node::NodeGeneric, pc_, funbox, inHandling, yieldHandling, kind, newDirectives); if (syntaxNodeResult.isErr()) { if (syntaxParser->hadAbortedSyntaxParse()) { // Try again with a full parse. UsedNameTracker needs to be // rewound to just before we tried the syntax parse for // correctness. syntaxParser->clearAbortedSyntaxParse(); usedNames_.rewind(token); this->compilationState_.rewind(statePosition); MOZ_ASSERT(!fc_->hadErrors()); break; } return false; } if (!advancePastSyntaxParsedFunction(syntaxParser)) { return false; } // Update the end position of the parse node. (*funNode)->pn_pos.end = anyChars.currentToken().pos.end; // Append possible Annex B function box only upon successfully parsing. if (tryAnnexB) { if (!pc_->innermostScope()->addPossibleAnnexBFunctionBox(pc_, funbox)) { return false; } } return true; } while (false); // We failed to do a syntax parse above, so do the full parse. FunctionNodeType innerFunc; MOZ_TRY_VAR_OR_RETURN( innerFunc, innerFunction(*funNode, pc_, explicitName, flags, toStringStart, inHandling, yieldHandling, kind, generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives), false); *funNode = innerFunc; return true; } template bool Parser::trySyntaxParseInnerFunction( FunctionNodeType* funNode, TaggedParserAtomIndex explicitName, FunctionFlags flags, uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB, Directives inheritedDirectives, Directives* newDirectives) { // This is already a syntax parser, so just parse the inner function. FunctionNodeType innerFunc; MOZ_TRY_VAR_OR_RETURN( innerFunc, innerFunction(*funNode, pc_, explicitName, flags, toStringStart, inHandling, yieldHandling, kind, generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives), false); *funNode = innerFunc; return true; } template inline bool GeneralParser::trySyntaxParseInnerFunction( FunctionNodeType* funNode, TaggedParserAtomIndex explicitName, FunctionFlags flags, uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB, Directives inheritedDirectives, Directives* newDirectives) { return asFinalParser()->trySyntaxParseInnerFunction( funNode, explicitName, flags, toStringStart, inHandling, yieldHandling, kind, generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives); } template typename ParseHandler::FunctionNodeResult GeneralParser::innerFunctionForFunctionBox( FunctionNodeType funNode, ParseContext* outerpc, FunctionBox* funbox, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, Directives* newDirectives) { // Note that it is possible for outerpc != this->pc_, as we may be // attempting to syntax parse an inner function from an outer full // parser. In that case, outerpc is a SourceParseContext from the full parser // instead of the current top of the stack of the syntax parser. // Push a new ParseContext. SourceParseContext funpc(this, funbox, newDirectives); if (!funpc.init()) { return errorResult(); } if (!functionFormalParametersAndBody(inHandling, yieldHandling, &funNode, kind)) { return errorResult(); } if (!leaveInnerFunction(outerpc)) { return errorResult(); } return funNode; } template typename ParseHandler::FunctionNodeResult GeneralParser::innerFunction( FunctionNodeType funNode, ParseContext* outerpc, TaggedParserAtomIndex explicitName, FunctionFlags flags, uint32_t toStringStart, InHandling inHandling, YieldHandling yieldHandling, FunctionSyntaxKind kind, GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB, Directives inheritedDirectives, Directives* newDirectives) { // Note that it is possible for outerpc != this->pc_, as we may be // attempting to syntax parse an inner function from an outer full // parser. In that case, outerpc is a SourceParseContext from the full parser // instead of the current top of the stack of the syntax parser. FunctionBox* funbox = newFunctionBox(funNode, explicitName, flags, toStringStart, inheritedDirectives, generatorKind, asyncKind); if (!funbox) { return errorResult(); } funbox->initWithEnclosingParseContext(outerpc, kind); FunctionNodeType innerFunc; MOZ_TRY_VAR(innerFunc, innerFunctionForFunctionBox(funNode, outerpc, funbox, inHandling, yieldHandling, kind, newDirectives)); // Append possible Annex B function box only upon successfully parsing. if (tryAnnexB) { if (!pc_->innermostScope()->addPossibleAnnexBFunctionBox(pc_, funbox)) { return errorResult(); } } return innerFunc; } template bool GeneralParser::appendToCallSiteObj( CallSiteNodeType callSiteObj) { Node cookedNode; MOZ_TRY_VAR_OR_RETURN(cookedNode, noSubstitutionTaggedTemplate(), false); auto atom = tokenStream.getRawTemplateStringAtom(); if (!atom) { return false; } NameNodeType rawNode; MOZ_TRY_VAR_OR_RETURN(rawNode, handler_.newTemplateStringLiteral(atom, pos()), false); handler_.addToCallSiteObject(callSiteObj, rawNode, cookedNode); return true; } template FullParseHandler::FunctionNodeResult Parser::standaloneLazyFunction( CompilationInput& input, uint32_t toStringStart, bool strict, GeneratorKind generatorKind, FunctionAsyncKind asyncKind) { MOZ_ASSERT(checkOptionsCalled_); FunctionSyntaxKind syntaxKind = input.functionSyntaxKind(); FunctionNodeType funNode; MOZ_TRY_VAR(funNode, handler_.newFunction(syntaxKind, pos())); TaggedParserAtomIndex displayAtom = this->getCompilationState().previousParseCache.displayAtom(); Directives directives(strict); FunctionBox* funbox = newFunctionBox(funNode, displayAtom, input.functionFlags(), toStringStart, directives, generatorKind, asyncKind); if (!funbox) { return errorResult(); } const ScriptStencilExtra& funExtra = this->getCompilationState().previousParseCache.funExtra(); funbox->initFromLazyFunction( funExtra, this->getCompilationState().scopeContext, syntaxKind); if (funbox->useMemberInitializers()) { funbox->setMemberInitializers(funExtra.memberInitializers()); } Directives newDirectives = directives; SourceParseContext funpc(this, funbox, &newDirectives); if (!funpc.init()) { return errorResult(); } // Our tokenStream has no current token, so funNode's position is garbage. // Substitute the position of the first token in our source. If the // function is a not-async arrow, use TokenStream::SlashIsRegExp to keep // verifyConsistentModifier from complaining (we will use // TokenStream::SlashIsRegExp in functionArguments). Modifier modifier = (input.functionFlags().isArrow() && asyncKind == FunctionAsyncKind::SyncFunction) ? TokenStream::SlashIsRegExp : TokenStream::SlashIsDiv; if (!tokenStream.peekTokenPos(&funNode->pn_pos, modifier)) { return errorResult(); } YieldHandling yieldHandling = GetYieldHandling(generatorKind); if (funbox->isSyntheticFunction()) { // Currently default class constructors are the only synthetic function that // supports delazification. MOZ_ASSERT(funbox->isClassConstructor()); MOZ_ASSERT(funbox->extent().toStringStart == funbox->extent().sourceStart); HasHeritage hasHeritage = funbox->isDerivedClassConstructor() ? HasHeritage::Yes : HasHeritage::No; TokenPos synthesizedBodyPos(funbox->extent().toStringStart, funbox->extent().toStringEnd); // Reset pos() to the `class` keyword for predictable results. tokenStream.consumeKnownToken(TokenKind::Class); if (!this->synthesizeConstructorBody(synthesizedBodyPos, hasHeritage, funNode, funbox)) { return errorResult(); } } else { if (!functionFormalParametersAndBody(InAllowed, yieldHandling, &funNode, syntaxKind)) { MOZ_ASSERT(directives == newDirectives); return errorResult(); } } if (!CheckParseTree(this->fc_, alloc_, funNode)) { return errorResult(); } ParseNode* node = funNode; // Don't constant-fold inside "use asm" code, as this could create a parse // tree that doesn't type-check as asm.js. if (!pc_->useAsmOrInsideUseAsm()) { if (!FoldConstants(this->fc_, this->parserAtoms(), &node, &handler_)) { return errorResult(); } } funNode = &node->as(); return funNode; } void ParserBase::setFunctionEndFromCurrentToken(FunctionBox* funbox) const { if (compilationState_.isInitialStencil()) { MOZ_ASSERT(anyChars.currentToken().type != TokenKind::Eof); MOZ_ASSERT(anyChars.currentToken().type < TokenKind::Limit); funbox->setEnd(anyChars.currentToken().pos.end); } else { // If we're delazifying an arrow function with expression body and // the expression is also a function, we arrive here immediately after // skipping the function by Parser::skipLazyInnerFunction. // // a => b => c // ^ // | // we're here // // In that case, the current token's type field is either Limit or // poisoned. // We shouldn't read the value if it's poisoned. // See TokenStreamSpecific::advance and // mfbt/MemoryChecking.h for more details. // // Also, in delazification, the FunctionBox should already have the // correct extent, and we shouldn't overwrite it here. // See ScriptStencil variant of PerHandlerParser::newFunctionBox. #if !defined(MOZ_ASAN) && !defined(MOZ_MSAN) && !defined(MOZ_VALGRIND) MOZ_ASSERT(anyChars.currentToken().type != TokenKind::Eof); #endif MOZ_ASSERT(funbox->extent().sourceEnd == anyChars.currentToken().pos.end); } } template bool GeneralParser::functionFormalParametersAndBody( InHandling inHandling, YieldHandling yieldHandling, FunctionNodeType* funNode, FunctionSyntaxKind kind, const Maybe& parameterListEnd /* = Nothing() */, bool isStandaloneFunction /* = false */) { // Given a properly initialized parse context, try to parse an actual // function without concern for conversion to strict mode, use of lazy // parsing and such. FunctionBox* funbox = pc_->functionBox(); if (kind == FunctionSyntaxKind::ClassConstructor || kind == FunctionSyntaxKind::DerivedClassConstructor) { if (!noteUsedName(TaggedParserAtomIndex::WellKnown::dot_initializers_())) { return false; } #ifdef ENABLE_DECORATORS if (!noteUsedName(TaggedParserAtomIndex::WellKnown:: dot_instanceExtraInitializers_())) { return false; } #endif } // See below for an explanation why arrow function parameters and arrow // function bodies are parsed with different yield/await settings. { AwaitHandling awaitHandling = kind == FunctionSyntaxKind::StaticClassBlock ? AwaitIsDisallowed : (funbox->isAsync() || (kind == FunctionSyntaxKind::Arrow && awaitIsKeyword())) ? AwaitIsKeyword : AwaitIsName; AutoAwaitIsKeyword awaitIsKeyword(this, awaitHandling); AutoInParametersOfAsyncFunction inParameters( this, funbox->isAsync()); if (!functionArguments(yieldHandling, kind, *funNode)) { return false; } } Maybe varScope; if (funbox->hasParameterExprs) { varScope.emplace(this); if (!varScope->init(pc_)) { return false; } } else { pc_->functionScope().useAsVarScope(pc_); } if (kind == FunctionSyntaxKind::Arrow) { TokenKind tt; if (!tokenStream.peekTokenSameLine(&tt)) { return false; } if (tt == TokenKind::Eol) { error(JSMSG_UNEXPECTED_TOKEN, "'=>' on the same line after an argument list", TokenKindToDesc(tt)); return false; } if (tt != TokenKind::Arrow) { error(JSMSG_BAD_ARROW_ARGS); return false; } tokenStream.consumeKnownToken(TokenKind::Arrow); } // When parsing something for new Function() we have to make sure to // only treat a certain part of the source as a parameter list. if (parameterListEnd.isSome() && parameterListEnd.value() != pos().begin) { error(JSMSG_UNEXPECTED_PARAMLIST_END); return false; } // Parse the function body. FunctionBodyType bodyType = StatementListBody; TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return false; } uint32_t openedPos = 0; if (tt != TokenKind::LeftCurly) { if (kind != FunctionSyntaxKind::Arrow) { error(JSMSG_CURLY_BEFORE_BODY); return false; } anyChars.ungetToken(); bodyType = ExpressionBody; funbox->setHasExprBody(); } else { openedPos = pos().begin; } // Arrow function parameters inherit yieldHandling from the enclosing // context, but the arrow body doesn't. E.g. in |(a = yield) => yield|, // |yield| in the parameters is either a name or keyword, depending on // whether the arrow function is enclosed in a generator function or not. // Whereas the |yield| in the function body is always parsed as a name. // The same goes when parsing |await| in arrow functions. YieldHandling bodyYieldHandling = GetYieldHandling(pc_->generatorKind()); AwaitHandling bodyAwaitHandling = GetAwaitHandling(pc_->asyncKind()); bool inheritedStrict = pc_->sc()->strict(); LexicalScopeNodeType body; { AutoAwaitIsKeyword awaitIsKeyword(this, bodyAwaitHandling); AutoInParametersOfAsyncFunction inParameters(this, false); MOZ_TRY_VAR_OR_RETURN( body, functionBody(inHandling, bodyYieldHandling, kind, bodyType), false); } // Revalidate the function name when we transitioned to strict mode. if ((kind == FunctionSyntaxKind::Statement || kind == FunctionSyntaxKind::Expression) && funbox->explicitName() && !inheritedStrict && pc_->sc()->strict()) { MOZ_ASSERT(pc_->sc()->hasExplicitUseStrict(), "strict mode should only change when a 'use strict' directive " "is present"); auto propertyName = funbox->explicitName(); YieldHandling nameYieldHandling; if (kind == FunctionSyntaxKind::Expression) { // Named lambda has binding inside it. nameYieldHandling = bodyYieldHandling; } else { // Otherwise YieldHandling cannot be checked at this point // because of different context. // It should already be checked before this point. nameYieldHandling = YieldIsName; } // We already use the correct await-handling at this point, therefore // we don't need call AutoAwaitIsKeyword here. uint32_t nameOffset = handler_.getFunctionNameOffset(*funNode, anyChars); if (!checkBindingIdentifier(propertyName, nameOffset, nameYieldHandling)) { return false; } } if (bodyType == StatementListBody) { // Cannot use mustMatchToken here because of internal compiler error on // gcc 6.4.0, with linux 64 SM hazard build. TokenKind actual; if (!tokenStream.getToken(&actual, TokenStream::SlashIsRegExp)) { return false; } if (actual != TokenKind::RightCurly) { reportMissingClosing(JSMSG_CURLY_AFTER_BODY, JSMSG_CURLY_OPENED, openedPos); return false; } setFunctionEndFromCurrentToken(funbox); } else { MOZ_ASSERT(kind == FunctionSyntaxKind::Arrow); if (anyChars.hadError()) { return false; } setFunctionEndFromCurrentToken(funbox); if (kind == FunctionSyntaxKind::Statement) { if (!matchOrInsertSemicolon()) { return false; } } } if (IsMethodDefinitionKind(kind) && pc_->superScopeNeedsHomeObject()) { funbox->setNeedsHomeObject(); } if (!finishFunction(isStandaloneFunction)) { return false; } handler_.setEndPosition(body, pos().begin); handler_.setEndPosition(*funNode, pos().end); handler_.setFunctionBody(*funNode, body); return true; } template typename ParseHandler::FunctionNodeResult GeneralParser::functionStmt(uint32_t toStringStart, YieldHandling yieldHandling, DefaultHandling defaultHandling, FunctionAsyncKind asyncKind) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Function)); // In sloppy mode, Annex B.3.2 allows labelled function declarations. // Otherwise it's a parse error. ParseContext::Statement* declaredInStmt = pc_->innermostStatement(); if (declaredInStmt && declaredInStmt->kind() == StatementKind::Label) { MOZ_ASSERT(!pc_->sc()->strict(), "labeled functions shouldn't be parsed in strict mode"); // Find the innermost non-label statement. Report an error if it's // unbraced: functions can't appear in it. Otherwise the statement // (or its absence) determines the scope the function's bound in. while (declaredInStmt && declaredInStmt->kind() == StatementKind::Label) { declaredInStmt = declaredInStmt->enclosing(); } if (declaredInStmt && !StatementKindIsBraced(declaredInStmt->kind())) { error(JSMSG_SLOPPY_FUNCTION_LABEL); return errorResult(); } } TokenKind tt; if (!tokenStream.getToken(&tt)) { return errorResult(); } GeneratorKind generatorKind = GeneratorKind::NotGenerator; if (tt == TokenKind::Mul) { generatorKind = GeneratorKind::Generator; if (!tokenStream.getToken(&tt)) { return errorResult(); } } TaggedParserAtomIndex name; if (TokenKindIsPossibleIdentifier(tt)) { name = bindingIdentifier(yieldHandling); if (!name) { return errorResult(); } } else if (defaultHandling == AllowDefaultName) { name = TaggedParserAtomIndex::WellKnown::default_(); anyChars.ungetToken(); } else { /* Unnamed function expressions are forbidden in statement context. */ error(JSMSG_UNNAMED_FUNCTION_STMT); return errorResult(); } // Note the declared name and check for early errors. DeclarationKind kind; if (declaredInStmt) { MOZ_ASSERT(declaredInStmt->kind() != StatementKind::Label); MOZ_ASSERT(StatementKindIsBraced(declaredInStmt->kind())); kind = (!pc_->sc()->strict() && generatorKind == GeneratorKind::NotGenerator && asyncKind == FunctionAsyncKind::SyncFunction) ? DeclarationKind::SloppyLexicalFunction : DeclarationKind::LexicalFunction; } else { kind = pc_->atModuleLevel() ? DeclarationKind::ModuleBodyLevelFunction : DeclarationKind::BodyLevelFunction; } if (!noteDeclaredName(name, kind, pos())) { return errorResult(); } FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::Statement; FunctionNodeType funNode; MOZ_TRY_VAR(funNode, handler_.newFunction(syntaxKind, pos())); // Under sloppy mode, try Annex B.3.3 semantics. If making an additional // 'var' binding of the same name does not throw an early error, do so. // This 'var' binding would be assigned the function object when its // declaration is reached, not at the start of the block. // // This semantics is implemented upon Scope exit in // Scope::propagateAndMarkAnnexBFunctionBoxes. bool tryAnnexB = kind == DeclarationKind::SloppyLexicalFunction; YieldHandling newYieldHandling = GetYieldHandling(generatorKind); return functionDefinition(funNode, toStringStart, InAllowed, newYieldHandling, name, syntaxKind, generatorKind, asyncKind, tryAnnexB); } template typename ParseHandler::FunctionNodeResult GeneralParser::functionExpr(uint32_t toStringStart, InvokedPrediction invoked, FunctionAsyncKind asyncKind) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Function)); AutoAwaitIsKeyword awaitIsKeyword( this, GetAwaitHandling(asyncKind)); GeneratorKind generatorKind = GeneratorKind::NotGenerator; TokenKind tt; if (!tokenStream.getToken(&tt)) { return errorResult(); } if (tt == TokenKind::Mul) { generatorKind = GeneratorKind::Generator; if (!tokenStream.getToken(&tt)) { return errorResult(); } } YieldHandling yieldHandling = GetYieldHandling(generatorKind); TaggedParserAtomIndex name; if (TokenKindIsPossibleIdentifier(tt)) { name = bindingIdentifier(yieldHandling); if (!name) { return errorResult(); } } else { anyChars.ungetToken(); } FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::Expression; FunctionNodeType funNode; MOZ_TRY_VAR(funNode, handler_.newFunction(syntaxKind, pos())); if (invoked) { funNode = handler_.setLikelyIIFE(funNode); } return functionDefinition(funNode, toStringStart, InAllowed, yieldHandling, name, syntaxKind, generatorKind, asyncKind); } /* * Return true if this node, known to be an unparenthesized string literal * that never contain escape sequences, could be the string of a directive in a * Directive Prologue. Directive strings never contain escape sequences or line * continuations. */ static inline bool IsUseStrictDirective(const TokenPos& pos, TaggedParserAtomIndex atom) { // the length of "use strict", including quotation. static constexpr size_t useStrictLength = 12; return atom == TaggedParserAtomIndex::WellKnown::use_strict_() && pos.begin + useStrictLength == pos.end; } static inline bool IsUseAsmDirective(const TokenPos& pos, TaggedParserAtomIndex atom) { // the length of "use asm", including quotation. static constexpr size_t useAsmLength = 9; return atom == TaggedParserAtomIndex::WellKnown::use_asm_() && pos.begin + useAsmLength == pos.end; } template bool Parser::asmJS(ListNodeType list) { // While asm.js could technically be validated and compiled during syntax // parsing, we have no guarantee that some later JS wouldn't abort the // syntax parse and cause us to re-parse (and re-compile) the asm.js module. // For simplicity, unconditionally abort the syntax parse when "use asm" is // encountered so that asm.js is always validated/compiled exactly once // during a full parse. MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return false; } template bool Parser::asmJS(ListNodeType list) { // Disable syntax parsing in anything nested inside the asm.js module. disableSyntaxParser(); // We should be encountering the "use asm" directive for the first time; if // the directive is already, we must have failed asm.js validation and we're // reparsing. In that case, don't try to validate again. A non-null // newDirectives means we're not in a normal function. if (!pc_->newDirectives || pc_->newDirectives->asmJS()) { return true; } // If there is no ScriptSource, then we are doing a non-compiling parse and // so we shouldn't (and can't, without a ScriptSource) compile. if (ss == nullptr) { return true; } pc_->functionBox()->useAsm = true; // Attempt to validate and compile this asm.js module. On success, the // tokenStream has been advanced to the closing }. On failure, the // tokenStream is in an indeterminate state and we must reparse the // function from the beginning. Reparsing is triggered by marking that a // new directive has been encountered and returning 'false'. bool validated; if (!CompileAsmJS(this->fc_, this->parserAtoms(), *this, list, &validated)) { return false; } if (!validated) { pc_->newDirectives->setAsmJS(); return false; } return true; } template inline bool GeneralParser::asmJS(ListNodeType list) { return asFinalParser()->asmJS(list); } /* * Recognize Directive Prologue members and directives. Assuming |pn| is a * candidate for membership in a directive prologue, recognize directives and * set |pc_|'s flags accordingly. If |pn| is indeed part of a prologue, set its * |prologue| flag. * * Note that the following is a strict mode function: * * function foo() { * "blah" // inserted semi colon * "blurgh" * "use\x20loose" * "use strict" * } * * That is, even though "use\x20loose" can never be a directive, now or in the * future (because of the hex escape), the Directive Prologue extends through it * to the "use strict" statement, which is indeed a directive. */ template bool GeneralParser::maybeParseDirective( ListNodeType list, Node possibleDirective, bool* cont) { TokenPos directivePos; TaggedParserAtomIndex directive = handler_.isStringExprStatement(possibleDirective, &directivePos); *cont = !!directive; if (!*cont) { return true; } if (IsUseStrictDirective(directivePos, directive)) { // Functions with non-simple parameter lists (destructuring, // default or rest parameters) must not contain a "use strict" // directive. if (pc_->isFunctionBox()) { FunctionBox* funbox = pc_->functionBox(); if (!funbox->hasSimpleParameterList()) { const char* parameterKind = funbox->hasDestructuringArgs ? "destructuring" : funbox->hasParameterExprs ? "default" : "rest"; errorAt(directivePos.begin, JSMSG_STRICT_NON_SIMPLE_PARAMS, parameterKind); return false; } } // We're going to be in strict mode. Note that this scope explicitly // had "use strict"; pc_->sc()->setExplicitUseStrict(); if (!pc_->sc()->strict()) { // Some strict mode violations can appear before a Use Strict Directive // is applied. (See the |DeprecatedContent| enum initializers.) These // violations can manifest in two ways. // // First, the violation can appear *before* the Use Strict Directive. // Numeric literals (and therefore octal literals) can only precede a // Use Strict Directive if this function's parameter list is not simple, // but we reported an error for non-simple parameter lists above, so // octal literals present no issue. But octal escapes and \8 and \9 can // appear in the directive prologue before a Use Strict Directive: // // function f() // { // "hell\157 world"; // octal escape // "\8"; "\9"; // NonOctalDecimalEscape // "use strict"; // retroactively makes all the above errors // } // // Second, the violation can appear *after* the Use Strict Directive but // *before* the directive is recognized as terminated. This only // happens when a directive is terminated by ASI, and the next token // contains a violation: // // function a() // { // "use strict" // ASI // 0755; // } // function b() // { // "use strict" // ASI // "hell\157 world"; // } // function c() // { // "use strict" // ASI // "\8"; // } // // We note such violations when tokenizing. Then, if a violation has // been observed at the time a "use strict" is applied, we report the // error. switch (anyChars.sawDeprecatedContent()) { case DeprecatedContent::None: break; case DeprecatedContent::OctalLiteral: error(JSMSG_DEPRECATED_OCTAL_LITERAL); return false; case DeprecatedContent::OctalEscape: error(JSMSG_DEPRECATED_OCTAL_ESCAPE); return false; case DeprecatedContent::EightOrNineEscape: error(JSMSG_DEPRECATED_EIGHT_OR_NINE_ESCAPE); return false; } pc_->sc()->setStrictScript(); } } else if (IsUseAsmDirective(directivePos, directive)) { if (pc_->isFunctionBox()) { return asmJS(list); } return warningAt(directivePos.begin, JSMSG_USE_ASM_DIRECTIVE_FAIL); } return true; } template typename ParseHandler::ListNodeResult GeneralParser::statementList(YieldHandling yieldHandling) { AutoCheckRecursionLimit recursion(this->fc_); if (!recursion.check(this->fc_)) { return errorResult(); } ListNodeType stmtList; MOZ_TRY_VAR(stmtList, handler_.newStatementList(pos())); bool canHaveDirectives = pc_->atBodyLevel(); if (canHaveDirectives) { // Clear flags for deprecated content that might have been seen in an // enclosing context. anyChars.clearSawDeprecatedContent(); } bool canHaveHashbangComment = pc_->atTopLevel(); if (canHaveHashbangComment) { tokenStream.consumeOptionalHashbangComment(); } bool afterReturn = false; bool warnedAboutStatementsAfterReturn = false; uint32_t statementBegin = 0; for (;;) { TokenKind tt = TokenKind::Eof; if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) { if (anyChars.isEOF()) { isUnexpectedEOF_ = true; } return errorResult(); } if (tt == TokenKind::Eof || tt == TokenKind::RightCurly) { TokenPos pos; if (!tokenStream.peekTokenPos(&pos, TokenStream::SlashIsRegExp)) { return errorResult(); } handler_.setListEndPosition(stmtList, pos); break; } if (afterReturn) { if (!tokenStream.peekOffset(&statementBegin, TokenStream::SlashIsRegExp)) { return errorResult(); } } auto nextResult = statementListItem(yieldHandling, canHaveDirectives); if (nextResult.isErr()) { if (anyChars.isEOF()) { isUnexpectedEOF_ = true; } return errorResult(); } Node next = nextResult.unwrap(); if (!warnedAboutStatementsAfterReturn) { if (afterReturn) { if (!handler_.isStatementPermittedAfterReturnStatement(next)) { if (!warningAt(statementBegin, JSMSG_STMT_AFTER_RETURN)) { return errorResult(); } warnedAboutStatementsAfterReturn = true; } } else if (handler_.isReturnStatement(next)) { afterReturn = true; } } if (canHaveDirectives) { if (!maybeParseDirective(stmtList, next, &canHaveDirectives)) { return errorResult(); } } handler_.addStatementToList(stmtList, next); } return stmtList; } template typename ParseHandler::NodeResult GeneralParser::condition( InHandling inHandling, YieldHandling yieldHandling) { if (!mustMatchToken(TokenKind::LeftParen, JSMSG_PAREN_BEFORE_COND)) { return errorResult(); } Node pn; MOZ_TRY_VAR(pn, exprInParens(inHandling, yieldHandling, TripledotProhibited)); if (!mustMatchToken(TokenKind::RightParen, JSMSG_PAREN_AFTER_COND)) { return errorResult(); } return pn; } template bool GeneralParser::matchLabel( YieldHandling yieldHandling, TaggedParserAtomIndex* labelOut) { MOZ_ASSERT(labelOut != nullptr); TokenKind tt = TokenKind::Eof; if (!tokenStream.peekTokenSameLine(&tt, TokenStream::SlashIsRegExp)) { return false; } if (TokenKindIsPossibleIdentifier(tt)) { tokenStream.consumeKnownToken(tt, TokenStream::SlashIsRegExp); *labelOut = labelIdentifier(yieldHandling); if (!*labelOut) { return false; } } else { *labelOut = TaggedParserAtomIndex::null(); } return true; } template GeneralParser::PossibleError::PossibleError( GeneralParser& parser) : parser_(parser) {} template typename GeneralParser::PossibleError::Error& GeneralParser::PossibleError::error(ErrorKind kind) { if (kind == ErrorKind::Expression) { return exprError_; } if (kind == ErrorKind::Destructuring) { return destructuringError_; } MOZ_ASSERT(kind == ErrorKind::DestructuringWarning); return destructuringWarning_; } template void GeneralParser::PossibleError::setResolved( ErrorKind kind) { error(kind).state_ = ErrorState::None; } template bool GeneralParser::PossibleError::hasError( ErrorKind kind) { return error(kind).state_ == ErrorState::Pending; } template bool GeneralParser::PossibleError::hasPendingDestructuringError() { return hasError(ErrorKind::Destructuring); } template void GeneralParser::PossibleError::setPending( ErrorKind kind, const TokenPos& pos, unsigned errorNumber) { // Don't overwrite a previously recorded error. if (hasError(kind)) { return; } // If we report an error later, we'll do it from the position where we set // the state to pending. Error& err = error(kind); err.offset_ = pos.begin; err.errorNumber_ = errorNumber; err.state_ = ErrorState::Pending; } template void GeneralParser::PossibleError:: setPendingDestructuringErrorAt(const TokenPos& pos, unsigned errorNumber) { setPending(ErrorKind::Destructuring, pos, errorNumber); } template void GeneralParser::PossibleError:: setPendingDestructuringWarningAt(const TokenPos& pos, unsigned errorNumber) { setPending(ErrorKind::DestructuringWarning, pos, errorNumber); } template void GeneralParser::PossibleError:: setPendingExpressionErrorAt(const TokenPos& pos, unsigned errorNumber) { setPending(ErrorKind::Expression, pos, errorNumber); } template bool GeneralParser::PossibleError::checkForError( ErrorKind kind) { if (!hasError(kind)) { return true; } Error& err = error(kind); parser_.errorAt(err.offset_, err.errorNumber_); return false; } template bool GeneralParser::PossibleError::checkForDestructuringErrorOrWarning() { // Clear pending expression error, because we're definitely not in an // expression context. setResolved(ErrorKind::Expression); // Report any pending destructuring error. return checkForError(ErrorKind::Destructuring); } template bool GeneralParser::PossibleError::checkForExpressionError() { // Clear pending destructuring error, because we're definitely not // in a destructuring context. setResolved(ErrorKind::Destructuring); setResolved(ErrorKind::DestructuringWarning); // Report any pending expression error. return checkForError(ErrorKind::Expression); } template void GeneralParser::PossibleError::transferErrorTo( ErrorKind kind, PossibleError* other) { if (hasError(kind) && !other->hasError(kind)) { Error& err = error(kind); Error& otherErr = other->error(kind); otherErr.offset_ = err.offset_; otherErr.errorNumber_ = err.errorNumber_; otherErr.state_ = err.state_; } } template void GeneralParser::PossibleError::transferErrorsTo( PossibleError* other) { MOZ_ASSERT(other); MOZ_ASSERT(this != other); MOZ_ASSERT(&parser_ == &other->parser_, "Can't transfer fields to an instance which belongs to a " "different parser"); transferErrorTo(ErrorKind::Destructuring, other); transferErrorTo(ErrorKind::Expression, other); } template typename ParseHandler::BinaryNodeResult GeneralParser::bindingInitializer( Node lhs, DeclarationKind kind, YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Assign)); if (kind == DeclarationKind::FormalParameter) { pc_->functionBox()->hasParameterExprs = true; } Node rhs; MOZ_TRY_VAR(rhs, assignExpr(InAllowed, yieldHandling, TripledotProhibited)); BinaryNodeType assign; MOZ_TRY_VAR(assign, handler_.newAssignment(ParseNodeKind::AssignExpr, lhs, rhs)); return assign; } template typename ParseHandler::NameNodeResult GeneralParser::bindingIdentifier( DeclarationKind kind, YieldHandling yieldHandling) { TaggedParserAtomIndex name = bindingIdentifier(yieldHandling); if (!name) { return errorResult(); } NameNodeType binding; MOZ_TRY_VAR(binding, newName(name)); if (!noteDeclaredName(name, kind, pos())) { return errorResult(); } return binding; } template typename ParseHandler::NodeResult GeneralParser::bindingIdentifierOrPattern( DeclarationKind kind, YieldHandling yieldHandling, TokenKind tt) { if (tt == TokenKind::LeftBracket) { return arrayBindingPattern(kind, yieldHandling); } if (tt == TokenKind::LeftCurly) { return objectBindingPattern(kind, yieldHandling); } if (!TokenKindIsPossibleIdentifierName(tt)) { error(JSMSG_NO_VARIABLE_NAME); return errorResult(); } return bindingIdentifier(kind, yieldHandling); } template typename ParseHandler::ListNodeResult GeneralParser::objectBindingPattern( DeclarationKind kind, YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftCurly)); AutoCheckRecursionLimit recursion(this->fc_); if (!recursion.check(this->fc_)) { return errorResult(); } uint32_t begin = pos().begin; ListNodeType literal; MOZ_TRY_VAR(literal, handler_.newObjectLiteral(begin)); Maybe declKind = Some(kind); TaggedParserAtomIndex propAtom; for (;;) { TokenKind tt; if (!tokenStream.peekToken(&tt)) { return errorResult(); } if (tt == TokenKind::RightCurly) { break; } if (tt == TokenKind::TripleDot) { tokenStream.consumeKnownToken(TokenKind::TripleDot); uint32_t begin = pos().begin; TokenKind tt; if (!tokenStream.getToken(&tt)) { return errorResult(); } if (!TokenKindIsPossibleIdentifierName(tt)) { error(JSMSG_NO_VARIABLE_NAME); return errorResult(); } NameNodeType inner; MOZ_TRY_VAR(inner, bindingIdentifier(kind, yieldHandling)); if (!handler_.addSpreadProperty(literal, begin, inner)) { return errorResult(); } } else { TokenPos namePos = anyChars.nextToken().pos; PropertyType propType; Node propName; MOZ_TRY_VAR(propName, propertyOrMethodName( yieldHandling, PropertyNameInPattern, declKind, literal, &propType, &propAtom)); if (propType == PropertyType::Normal) { // Handle e.g., |var {p: x} = o| and |var {p: x=0} = o|. if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } Node binding; MOZ_TRY_VAR(binding, bindingIdentifierOrPattern(kind, yieldHandling, tt)); bool hasInitializer; if (!tokenStream.matchToken(&hasInitializer, TokenKind::Assign, TokenStream::SlashIsRegExp)) { return errorResult(); } Node bindingExpr; if (hasInitializer) { MOZ_TRY_VAR(bindingExpr, bindingInitializer(binding, kind, yieldHandling)); } else { bindingExpr = binding; } if (!handler_.addPropertyDefinition(literal, propName, bindingExpr)) { return errorResult(); } } else if (propType == PropertyType::Shorthand) { // Handle e.g., |var {x, y} = o| as destructuring shorthand // for |var {x: x, y: y} = o|. MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt)); NameNodeType binding; MOZ_TRY_VAR(binding, bindingIdentifier(kind, yieldHandling)); if (!handler_.addShorthand(literal, handler_.asNameNode(propName), binding)) { return errorResult(); } } else if (propType == PropertyType::CoverInitializedName) { // Handle e.g., |var {x=1, y=2} = o| as destructuring // shorthand with default values. MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt)); NameNodeType binding; MOZ_TRY_VAR(binding, bindingIdentifier(kind, yieldHandling)); tokenStream.consumeKnownToken(TokenKind::Assign); BinaryNodeType bindingExpr; MOZ_TRY_VAR(bindingExpr, bindingInitializer(binding, kind, yieldHandling)); if (!handler_.addPropertyDefinition(literal, propName, bindingExpr)) { return errorResult(); } } else { errorAt(namePos.begin, JSMSG_NO_VARIABLE_NAME); return errorResult(); } } bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::SlashIsInvalid)) { return errorResult(); } if (!matched) { break; } if (tt == TokenKind::TripleDot) { error(JSMSG_REST_WITH_COMMA); return errorResult(); } } if (!mustMatchToken(TokenKind::RightCurly, [this, begin](TokenKind actual) { this->reportMissingClosing(JSMSG_CURLY_AFTER_LIST, JSMSG_CURLY_OPENED, begin); })) { return errorResult(); } handler_.setEndPosition(literal, pos().end); return literal; } template typename ParseHandler::ListNodeResult GeneralParser::arrayBindingPattern( DeclarationKind kind, YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftBracket)); AutoCheckRecursionLimit recursion(this->fc_); if (!recursion.check(this->fc_)) { return errorResult(); } uint32_t begin = pos().begin; ListNodeType literal; MOZ_TRY_VAR(literal, handler_.newArrayLiteral(begin)); uint32_t index = 0; for (;; index++) { if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) { error(JSMSG_ARRAY_INIT_TOO_BIG); return errorResult(); } TokenKind tt; if (!tokenStream.getToken(&tt)) { return errorResult(); } if (tt == TokenKind::RightBracket) { anyChars.ungetToken(); break; } if (tt == TokenKind::Comma) { if (!handler_.addElision(literal, pos())) { return errorResult(); } } else if (tt == TokenKind::TripleDot) { uint32_t begin = pos().begin; TokenKind tt; if (!tokenStream.getToken(&tt)) { return errorResult(); } Node inner; MOZ_TRY_VAR(inner, bindingIdentifierOrPattern(kind, yieldHandling, tt)); if (!handler_.addSpreadElement(literal, begin, inner)) { return errorResult(); } } else { Node binding; MOZ_TRY_VAR(binding, bindingIdentifierOrPattern(kind, yieldHandling, tt)); bool hasInitializer; if (!tokenStream.matchToken(&hasInitializer, TokenKind::Assign, TokenStream::SlashIsRegExp)) { return errorResult(); } Node element; if (hasInitializer) { MOZ_TRY_VAR(element, bindingInitializer(binding, kind, yieldHandling)); } else { element = binding; } handler_.addArrayElement(literal, element); } if (tt != TokenKind::Comma) { // If we didn't already match TokenKind::Comma in above case. bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::SlashIsRegExp)) { return errorResult(); } if (!matched) { break; } if (tt == TokenKind::TripleDot) { error(JSMSG_REST_WITH_COMMA); return errorResult(); } } } if (!mustMatchToken(TokenKind::RightBracket, [this, begin](TokenKind actual) { this->reportMissingClosing(JSMSG_BRACKET_AFTER_LIST, JSMSG_BRACKET_OPENED, begin); })) { return errorResult(); } handler_.setEndPosition(literal, pos().end); return literal; } template typename ParseHandler::NodeResult GeneralParser::destructuringDeclaration( DeclarationKind kind, YieldHandling yieldHandling, TokenKind tt) { MOZ_ASSERT(anyChars.isCurrentTokenType(tt)); MOZ_ASSERT(tt == TokenKind::LeftBracket || tt == TokenKind::LeftCurly); if (tt == TokenKind::LeftBracket) { return arrayBindingPattern(kind, yieldHandling); } return objectBindingPattern(kind, yieldHandling); } template typename ParseHandler::NodeResult GeneralParser::destructuringDeclarationWithoutYieldOrAwait( DeclarationKind kind, YieldHandling yieldHandling, TokenKind tt) { uint32_t startYieldOffset = pc_->lastYieldOffset; uint32_t startAwaitOffset = pc_->lastAwaitOffset; Node res; MOZ_TRY_VAR(res, destructuringDeclaration(kind, yieldHandling, tt)); if (pc_->lastYieldOffset != startYieldOffset) { errorAt(pc_->lastYieldOffset, JSMSG_YIELD_IN_PARAMETER); return errorResult(); } if (pc_->lastAwaitOffset != startAwaitOffset) { errorAt(pc_->lastAwaitOffset, JSMSG_AWAIT_IN_PARAMETER); return errorResult(); } return res; } template typename ParseHandler::LexicalScopeNodeResult GeneralParser::blockStatement(YieldHandling yieldHandling, unsigned errorNumber) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftCurly)); uint32_t openedPos = pos().begin; ParseContext::Statement stmt(pc_, StatementKind::Block); ParseContext::Scope scope(this); if (!scope.init(pc_)) { return errorResult(); } ListNodeType list; MOZ_TRY_VAR(list, statementList(yieldHandling)); if (!mustMatchToken(TokenKind::RightCurly, [this, errorNumber, openedPos](TokenKind actual) { this->reportMissingClosing(errorNumber, JSMSG_CURLY_OPENED, openedPos); })) { return errorResult(); } return finishLexicalScope(scope, list); } template typename ParseHandler::NodeResult GeneralParser::expressionAfterForInOrOf( ParseNodeKind forHeadKind, YieldHandling yieldHandling) { MOZ_ASSERT(forHeadKind == ParseNodeKind::ForIn || forHeadKind == ParseNodeKind::ForOf); if (forHeadKind == ParseNodeKind::ForOf) { return assignExpr(InAllowed, yieldHandling, TripledotProhibited); } return expr(InAllowed, yieldHandling, TripledotProhibited); } template typename ParseHandler::NodeResult GeneralParser::declarationPattern( DeclarationKind declKind, TokenKind tt, bool initialDeclaration, YieldHandling yieldHandling, ParseNodeKind* forHeadKind, Node* forInOrOfExpression) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftBracket) || anyChars.isCurrentTokenType(TokenKind::LeftCurly)); Node pattern; MOZ_TRY_VAR(pattern, destructuringDeclaration(declKind, yieldHandling, tt)); if (initialDeclaration && forHeadKind) { bool isForIn, isForOf; if (!matchInOrOf(&isForIn, &isForOf)) { return errorResult(); } if (isForIn) { *forHeadKind = ParseNodeKind::ForIn; } else if (isForOf) { *forHeadKind = ParseNodeKind::ForOf; } else { *forHeadKind = ParseNodeKind::ForHead; } if (*forHeadKind != ParseNodeKind::ForHead) { MOZ_TRY_VAR(*forInOrOfExpression, expressionAfterForInOrOf(*forHeadKind, yieldHandling)); return pattern; } } if (!mustMatchToken(TokenKind::Assign, JSMSG_BAD_DESTRUCT_DECL)) { return errorResult(); } Node init; MOZ_TRY_VAR(init, assignExpr(forHeadKind ? InProhibited : InAllowed, yieldHandling, TripledotProhibited)); return handler_.newAssignment(ParseNodeKind::AssignExpr, pattern, init); } template typename ParseHandler::AssignmentNodeResult GeneralParser::initializerInNameDeclaration( NameNodeType binding, DeclarationKind declKind, bool initialDeclaration, YieldHandling yieldHandling, ParseNodeKind* forHeadKind, Node* forInOrOfExpression) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Assign)); uint32_t initializerOffset; if (!tokenStream.peekOffset(&initializerOffset, TokenStream::SlashIsRegExp)) { return errorResult(); } Node initializer; MOZ_TRY_VAR(initializer, assignExpr(forHeadKind ? InProhibited : InAllowed, yieldHandling, TripledotProhibited)); if (forHeadKind && initialDeclaration) { bool isForIn, isForOf; if (!matchInOrOf(&isForIn, &isForOf)) { return errorResult(); } // An initialized declaration can't appear in a for-of: // // for (var/let/const x = ... of ...); // BAD if (isForOf) { errorAt(initializerOffset, JSMSG_OF_AFTER_FOR_LOOP_DECL); return errorResult(); } if (isForIn) { // Lexical declarations in for-in loops can't be initialized: // // for (let/const x = ... in ...); // BAD if (DeclarationKindIsLexical(declKind)) { errorAt(initializerOffset, JSMSG_IN_AFTER_LEXICAL_FOR_DECL); return errorResult(); } // This leaves only initialized for-in |var| declarations. ES6 // forbids these; later ES un-forbids in non-strict mode code. *forHeadKind = ParseNodeKind::ForIn; if (!strictModeErrorAt(initializerOffset, JSMSG_INVALID_FOR_IN_DECL_WITH_INIT)) { return errorResult(); } MOZ_TRY_VAR( *forInOrOfExpression, expressionAfterForInOrOf(ParseNodeKind::ForIn, yieldHandling)); } else { *forHeadKind = ParseNodeKind::ForHead; } } return handler_.finishInitializerAssignment(binding, initializer); } template typename ParseHandler::NodeResult GeneralParser::declarationName(DeclarationKind declKind, TokenKind tt, bool initialDeclaration, YieldHandling yieldHandling, ParseNodeKind* forHeadKind, Node* forInOrOfExpression) { // Anything other than possible identifier is an error. if (!TokenKindIsPossibleIdentifier(tt)) { error(JSMSG_NO_VARIABLE_NAME); return errorResult(); } TaggedParserAtomIndex name = bindingIdentifier(yieldHandling); if (!name) { return errorResult(); } NameNodeType binding; MOZ_TRY_VAR(binding, newName(name)); TokenPos namePos = pos(); // The '=' context after a variable name in a declaration is an opportunity // for ASI, and thus for the next token to start an ExpressionStatement: // // var foo // VariableDeclaration // /bar/g; // ExpressionStatement // // Therefore get the token here with SlashIsRegExp. bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Assign, TokenStream::SlashIsRegExp)) { return errorResult(); } Node declaration; if (matched) { MOZ_TRY_VAR(declaration, initializerInNameDeclaration(binding, declKind, initialDeclaration, yieldHandling, forHeadKind, forInOrOfExpression)); } else { declaration = binding; if (initialDeclaration && forHeadKind) { bool isForIn, isForOf; if (!matchInOrOf(&isForIn, &isForOf)) { return errorResult(); } if (isForIn) { *forHeadKind = ParseNodeKind::ForIn; } else if (isForOf) { *forHeadKind = ParseNodeKind::ForOf; } else { *forHeadKind = ParseNodeKind::ForHead; } } if (forHeadKind && *forHeadKind != ParseNodeKind::ForHead) { MOZ_TRY_VAR(*forInOrOfExpression, expressionAfterForInOrOf(*forHeadKind, yieldHandling)); } else { // Normal const declarations, and const declarations in for(;;) // heads, must be initialized. if (declKind == DeclarationKind::Const) { errorAt(namePos.begin, JSMSG_BAD_CONST_DECL); return errorResult(); } } } // Note the declared name after knowing whether or not we are in a for-of // loop, due to special early error semantics in Annex B.3.5. if (!noteDeclaredName(name, declKind, namePos)) { return errorResult(); } return declaration; } template typename ParseHandler::DeclarationListNodeResult GeneralParser::declarationList( YieldHandling yieldHandling, ParseNodeKind kind, ParseNodeKind* forHeadKind /* = nullptr */, Node* forInOrOfExpression /* = nullptr */) { MOZ_ASSERT(kind == ParseNodeKind::VarStmt || kind == ParseNodeKind::LetDecl || kind == ParseNodeKind::ConstDecl); DeclarationKind declKind; switch (kind) { case ParseNodeKind::VarStmt: declKind = DeclarationKind::Var; break; case ParseNodeKind::ConstDecl: declKind = DeclarationKind::Const; break; case ParseNodeKind::LetDecl: declKind = DeclarationKind::Let; break; default: MOZ_CRASH("Unknown declaration kind"); } DeclarationListNodeType decl; MOZ_TRY_VAR(decl, handler_.newDeclarationList(kind, pos())); bool moreDeclarations; bool initialDeclaration = true; do { MOZ_ASSERT_IF(!initialDeclaration && forHeadKind, *forHeadKind == ParseNodeKind::ForHead); TokenKind tt; if (!tokenStream.getToken(&tt)) { return errorResult(); } Node binding; if (tt == TokenKind::LeftBracket || tt == TokenKind::LeftCurly) { MOZ_TRY_VAR(binding, declarationPattern(declKind, tt, initialDeclaration, yieldHandling, forHeadKind, forInOrOfExpression)); } else { MOZ_TRY_VAR(binding, declarationName(declKind, tt, initialDeclaration, yieldHandling, forHeadKind, forInOrOfExpression)); } handler_.addList(decl, binding); // If we have a for-in/of loop, the above call matches the entirety // of the loop head (up to the closing parenthesis). if (forHeadKind && *forHeadKind != ParseNodeKind::ForHead) { break; } initialDeclaration = false; if (!tokenStream.matchToken(&moreDeclarations, TokenKind::Comma, TokenStream::SlashIsRegExp)) { return errorResult(); } } while (moreDeclarations); return decl; } template typename ParseHandler::DeclarationListNodeResult GeneralParser::lexicalDeclaration( YieldHandling yieldHandling, DeclarationKind kind) { MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let); if (options().selfHostingMode) { error(JSMSG_SELFHOSTED_LEXICAL); return errorResult(); } /* * Parse body-level lets without a new block object. ES6 specs * that an execution environment's initial lexical environment * is the VariableEnvironment, i.e., body-level lets are in * the same environment record as vars. * * However, they cannot be parsed exactly as vars, as ES6 * requires that uninitialized lets throw ReferenceError on use. * * See 8.1.1.1.6 and the note in 13.2.1. */ DeclarationListNodeType decl; MOZ_TRY_VAR(decl, declarationList(yieldHandling, kind == DeclarationKind::Const ? ParseNodeKind::ConstDecl : ParseNodeKind::LetDecl)); if (!matchOrInsertSemicolon()) { return errorResult(); } return decl; } template typename ParseHandler::NameNodeResult GeneralParser::moduleExportName() { MOZ_ASSERT(anyChars.currentToken().type == TokenKind::String); TaggedParserAtomIndex name = anyChars.currentToken().atom(); if (!this->parserAtoms().isModuleExportName(name)) { error(JSMSG_UNPAIRED_SURROGATE_EXPORT); return errorResult(); } return handler_.newStringLiteral(name, pos()); } template bool GeneralParser::withClause(ListNodeType attributesSet) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Assert) || anyChars.isCurrentTokenType(TokenKind::With)); if (!options().importAttributes()) { error(JSMSG_IMPORT_ASSERTIONS_NOT_SUPPORTED); return false; } if (!abortIfSyntaxParser()) { return false; } if (!mustMatchToken(TokenKind::LeftCurly, JSMSG_CURLY_AFTER_ASSERT)) { return false; } // Handle the form |... assert {}| TokenKind token; if (!tokenStream.getToken(&token)) { return false; } if (token == TokenKind::RightCurly) { return true; } js::HashSet usedAssertionKeys; for (;;) { TaggedParserAtomIndex keyName; if (TokenKindIsPossibleIdentifierName(token)) { keyName = anyChars.currentName(); } else if (token == TokenKind::String) { keyName = anyChars.currentToken().atom(); } else { error(JSMSG_ASSERT_KEY_EXPECTED); return false; } auto p = usedAssertionKeys.lookupForAdd(keyName); if (p) { UniqueChars str = this->parserAtoms().toPrintableString(keyName); if (!str) { ReportOutOfMemory(this->fc_); return false; } error(JSMSG_DUPLICATE_ASSERT_KEY, str.get()); return false; } if (!usedAssertionKeys.add(p, keyName)) { ReportOutOfMemory(this->fc_); return false; } NameNodeType keyNode; MOZ_TRY_VAR_OR_RETURN(keyNode, newName(keyName), false); if (!mustMatchToken(TokenKind::Colon, JSMSG_COLON_AFTER_ASSERT_KEY)) { return false; } if (!mustMatchToken(TokenKind::String, JSMSG_ASSERT_STRING_LITERAL)) { return false; } NameNodeType valueNode; MOZ_TRY_VAR_OR_RETURN(valueNode, stringLiteral(), false); BinaryNodeType importAttributeNode; MOZ_TRY_VAR_OR_RETURN(importAttributeNode, handler_.newImportAttribute(keyNode, valueNode), false); handler_.addList(attributesSet, importAttributeNode); if (!tokenStream.getToken(&token)) { return false; } if (token == TokenKind::Comma) { if (!tokenStream.getToken(&token)) { return false; } } if (token == TokenKind::RightCurly) { break; } } return true; } template bool GeneralParser::namedImports( ListNodeType importSpecSet) { if (!abortIfSyntaxParser()) { return false; } while (true) { // Handle the forms |import {} from 'a'| and // |import { ..., } from 'a'| (where ... is non empty), by // escaping the loop early if the next token is }. TokenKind tt; if (!tokenStream.getToken(&tt)) { return false; } if (tt == TokenKind::RightCurly) { break; } TaggedParserAtomIndex importName; NameNodeType importNameNode = null(); if (TokenKindIsPossibleIdentifierName(tt)) { importName = anyChars.currentName(); MOZ_TRY_VAR_OR_RETURN(importNameNode, newName(importName), false); } else if (tt == TokenKind::String) { MOZ_TRY_VAR_OR_RETURN(importNameNode, moduleExportName(), false); } else { error(JSMSG_NO_IMPORT_NAME); return false; } bool matched; if (!tokenStream.matchToken(&matched, TokenKind::As)) { return false; } if (matched) { TokenKind afterAs; if (!tokenStream.getToken(&afterAs)) { return false; } if (!TokenKindIsPossibleIdentifierName(afterAs)) { error(JSMSG_NO_BINDING_NAME); return false; } } else { // String export names can't refer to local bindings. if (tt == TokenKind::String) { error(JSMSG_AS_AFTER_STRING); return false; } // Keywords cannot be bound to themselves, so an import name // that is a keyword is a syntax error if it is not followed // by the keyword 'as'. // See the ImportSpecifier production in ES6 section 15.2.2. MOZ_ASSERT(importName); if (IsKeyword(importName)) { error(JSMSG_AS_AFTER_RESERVED_WORD, ReservedWordToCharZ(importName)); return false; } } TaggedParserAtomIndex bindingAtom = importedBinding(); if (!bindingAtom) { return false; } NameNodeType bindingName; MOZ_TRY_VAR_OR_RETURN(bindingName, newName(bindingAtom), false); if (!noteDeclaredName(bindingAtom, DeclarationKind::Import, pos())) { return false; } BinaryNodeType importSpec; MOZ_TRY_VAR_OR_RETURN( importSpec, handler_.newImportSpec(importNameNode, bindingName), false); handler_.addList(importSpecSet, importSpec); TokenKind next; if (!tokenStream.getToken(&next)) { return false; } if (next == TokenKind::RightCurly) { break; } if (next != TokenKind::Comma) { error(JSMSG_RC_AFTER_IMPORT_SPEC_LIST); return false; } } return true; } template bool GeneralParser::namespaceImport( ListNodeType importSpecSet) { if (!abortIfSyntaxParser()) { return false; } if (!mustMatchToken(TokenKind::As, JSMSG_AS_AFTER_IMPORT_STAR)) { return false; } uint32_t begin = pos().begin; if (!mustMatchToken(TokenKindIsPossibleIdentifierName, JSMSG_NO_BINDING_NAME)) { return false; } // Namespace imports are not indirect bindings but lexical // definitions that hold a module namespace object. They are treated // as const variables which are initialized during the // ModuleInstantiate step. TaggedParserAtomIndex bindingName = importedBinding(); if (!bindingName) { return false; } NameNodeType bindingNameNode; MOZ_TRY_VAR_OR_RETURN(bindingNameNode, newName(bindingName), false); if (!noteDeclaredName(bindingName, DeclarationKind::Const, pos())) { return false; } // The namespace import name is currently required to live on the // environment. pc_->varScope().lookupDeclaredName(bindingName)->value()->setClosedOver(); UnaryNodeType importSpec; MOZ_TRY_VAR_OR_RETURN(importSpec, handler_.newImportNamespaceSpec(begin, bindingNameNode), false); handler_.addList(importSpecSet, importSpec); return true; } template typename ParseHandler::BinaryNodeResult GeneralParser::importDeclaration() { if (!abortIfSyntaxParser()) { return errorResult(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Import)); if (!pc_->atModuleLevel()) { error(JSMSG_IMPORT_DECL_AT_TOP_LEVEL); return errorResult(); } uint32_t begin = pos().begin; TokenKind tt; if (!tokenStream.getToken(&tt)) { return errorResult(); } ListNodeType importSpecSet; MOZ_TRY_VAR(importSpecSet, handler_.newList(ParseNodeKind::ImportSpecList, pos())); if (tt == TokenKind::String) { // Handle the form |import 'a'| by leaving the list empty. This is // equivalent to |import {} from 'a'|. handler_.setEndPosition(importSpecSet, pos().begin); } else { if (tt == TokenKind::LeftCurly) { if (!namedImports(importSpecSet)) { return errorResult(); } } else if (tt == TokenKind::Mul) { if (!namespaceImport(importSpecSet)) { return errorResult(); } } else if (TokenKindIsPossibleIdentifierName(tt)) { // Handle the form |import a from 'b'|, by adding a single import // specifier to the list, with 'default' as the import name and // 'a' as the binding name. This is equivalent to // |import { default as a } from 'b'|. NameNodeType importName; MOZ_TRY_VAR(importName, newName(TaggedParserAtomIndex::WellKnown::default_())); TaggedParserAtomIndex bindingAtom = importedBinding(); if (!bindingAtom) { return errorResult(); } NameNodeType bindingName; MOZ_TRY_VAR(bindingName, newName(bindingAtom)); if (!noteDeclaredName(bindingAtom, DeclarationKind::Import, pos())) { return errorResult(); } BinaryNodeType importSpec; MOZ_TRY_VAR(importSpec, handler_.newImportSpec(importName, bindingName)); handler_.addList(importSpecSet, importSpec); if (!tokenStream.peekToken(&tt)) { return errorResult(); } if (tt == TokenKind::Comma) { tokenStream.consumeKnownToken(tt); if (!tokenStream.getToken(&tt)) { return errorResult(); } if (tt == TokenKind::LeftCurly) { if (!namedImports(importSpecSet)) { return errorResult(); } } else if (tt == TokenKind::Mul) { if (!namespaceImport(importSpecSet)) { return errorResult(); } } else { error(JSMSG_NAMED_IMPORTS_OR_NAMESPACE_IMPORT); return errorResult(); } } } else { error(JSMSG_DECLARATION_AFTER_IMPORT); return errorResult(); } if (!mustMatchToken(TokenKind::From, JSMSG_FROM_AFTER_IMPORT_CLAUSE)) { return errorResult(); } if (!mustMatchToken(TokenKind::String, JSMSG_MODULE_SPEC_AFTER_FROM)) { return errorResult(); } } NameNodeType moduleSpec; MOZ_TRY_VAR(moduleSpec, stringLiteral()); // The `assert` keyword has a [no LineTerminator here] production before it in // the grammar -- `with` does not. We need to handle this distinction. if (!tokenStream.peekTokenSameLine(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } // `with` may have an EOL prior, so peek the next token and replace // EOL if the next token is `with`. if (tt == TokenKind::Eol) { // Doing a regular peek won't produce Eol, but the actual next token. TokenKind peekedToken; if (!tokenStream.peekToken(&peekedToken, TokenStream::SlashIsRegExp)) { return errorResult(); } if (peekedToken == TokenKind::With) { tt = TokenKind::With; } } ListNodeType importAttributeList; MOZ_TRY_VAR(importAttributeList, handler_.newList(ParseNodeKind::ImportAttributeList, pos())); if (tt == TokenKind::With || (tt == TokenKind::Assert && options().importAttributesAssertSyntax())) { tokenStream.consumeKnownToken(tt, TokenStream::SlashIsRegExp); if (!withClause(importAttributeList)) { return errorResult(); } } if (!matchOrInsertSemicolon(TokenStream::SlashIsRegExp)) { return errorResult(); } BinaryNodeType moduleRequest; MOZ_TRY_VAR(moduleRequest, handler_.newModuleRequest(moduleSpec, importAttributeList, TokenPos(begin, pos().end))); BinaryNodeType node; MOZ_TRY_VAR(node, handler_.newImportDeclaration(importSpecSet, moduleRequest, TokenPos(begin, pos().end))); if (!processImport(node)) { return errorResult(); } return node; } template inline typename ParseHandler::NodeResult GeneralParser::importDeclarationOrImportExpr( YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Import)); TokenKind tt; if (!tokenStream.peekToken(&tt)) { return errorResult(); } if (tt == TokenKind::Dot || tt == TokenKind::LeftParen) { return expressionStatement(yieldHandling); } return importDeclaration(); } template bool Parser::checkExportedName( TaggedParserAtomIndex exportName) { if (!pc_->sc()->asModuleContext()->builder.hasExportedName(exportName)) { return true; } UniqueChars str = this->parserAtoms().toPrintableString(exportName); if (!str) { ReportOutOfMemory(this->fc_); return false; } error(JSMSG_DUPLICATE_EXPORT_NAME, str.get()); return false; } template inline bool Parser::checkExportedName( TaggedParserAtomIndex exportName) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return false; } template inline bool GeneralParser::checkExportedName( TaggedParserAtomIndex exportName) { return asFinalParser()->checkExportedName(exportName); } template bool Parser::checkExportedNamesForArrayBinding( ListNode* array) { MOZ_ASSERT(array->isKind(ParseNodeKind::ArrayExpr)); for (ParseNode* node : array->contents()) { if (node->isKind(ParseNodeKind::Elision)) { continue; } ParseNode* binding; if (node->isKind(ParseNodeKind::Spread)) { binding = node->as().kid(); } else if (node->isKind(ParseNodeKind::AssignExpr)) { binding = node->as().left(); } else { binding = node; } if (!checkExportedNamesForDeclaration(binding)) { return false; } } return true; } template inline bool Parser::checkExportedNamesForArrayBinding( ListNodeType array) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return false; } template inline bool GeneralParser::checkExportedNamesForArrayBinding( ListNodeType array) { return asFinalParser()->checkExportedNamesForArrayBinding(array); } template bool Parser::checkExportedNamesForObjectBinding( ListNode* obj) { MOZ_ASSERT(obj->isKind(ParseNodeKind::ObjectExpr)); for (ParseNode* node : obj->contents()) { MOZ_ASSERT(node->isKind(ParseNodeKind::MutateProto) || node->isKind(ParseNodeKind::PropertyDefinition) || node->isKind(ParseNodeKind::Shorthand) || node->isKind(ParseNodeKind::Spread)); ParseNode* target; if (node->isKind(ParseNodeKind::Spread)) { target = node->as().kid(); } else { if (node->isKind(ParseNodeKind::MutateProto)) { target = node->as().kid(); } else { target = node->as().right(); } if (target->isKind(ParseNodeKind::AssignExpr)) { target = target->as().left(); } } if (!checkExportedNamesForDeclaration(target)) { return false; } } return true; } template inline bool Parser::checkExportedNamesForObjectBinding(ListNodeType obj) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return false; } template inline bool GeneralParser::checkExportedNamesForObjectBinding( ListNodeType obj) { return asFinalParser()->checkExportedNamesForObjectBinding(obj); } template bool Parser::checkExportedNamesForDeclaration( ParseNode* node) { if (node->isKind(ParseNodeKind::Name)) { if (!checkExportedName(node->as().atom())) { return false; } } else if (node->isKind(ParseNodeKind::ArrayExpr)) { if (!checkExportedNamesForArrayBinding(&node->as())) { return false; } } else { MOZ_ASSERT(node->isKind(ParseNodeKind::ObjectExpr)); if (!checkExportedNamesForObjectBinding(&node->as())) { return false; } } return true; } template inline bool Parser::checkExportedNamesForDeclaration( Node node) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return false; } template inline bool GeneralParser::checkExportedNamesForDeclaration( Node node) { return asFinalParser()->checkExportedNamesForDeclaration(node); } template bool Parser::checkExportedNamesForDeclarationList( DeclarationListNodeType node) { for (ParseNode* binding : node->contents()) { if (binding->isKind(ParseNodeKind::AssignExpr)) { binding = binding->as().left(); } else { MOZ_ASSERT(binding->isKind(ParseNodeKind::Name)); } if (!checkExportedNamesForDeclaration(binding)) { return false; } } return true; } template inline bool Parser::checkExportedNamesForDeclarationList( DeclarationListNodeType node) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return false; } template inline bool GeneralParser::checkExportedNamesForDeclarationList( DeclarationListNodeType node) { return asFinalParser()->checkExportedNamesForDeclarationList(node); } template inline bool Parser::checkExportedNameForClause( NameNode* nameNode) { return checkExportedName(nameNode->atom()); } template inline bool Parser::checkExportedNameForClause( NameNodeType nameNode) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return false; } template inline bool GeneralParser::checkExportedNameForClause( NameNodeType nameNode) { return asFinalParser()->checkExportedNameForClause(nameNode); } template bool Parser::checkExportedNameForFunction( FunctionNode* funNode) { return checkExportedName(funNode->funbox()->explicitName()); } template inline bool Parser::checkExportedNameForFunction( FunctionNodeType funNode) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return false; } template inline bool GeneralParser::checkExportedNameForFunction( FunctionNodeType funNode) { return asFinalParser()->checkExportedNameForFunction(funNode); } template bool Parser::checkExportedNameForClass( ClassNode* classNode) { MOZ_ASSERT(classNode->names()); return checkExportedName(classNode->names()->innerBinding()->atom()); } template inline bool Parser::checkExportedNameForClass( ClassNodeType classNode) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return false; } template inline bool GeneralParser::checkExportedNameForClass( ClassNodeType classNode) { return asFinalParser()->checkExportedNameForClass(classNode); } template <> inline bool PerHandlerParser::processExport(ParseNode* node) { return pc_->sc()->asModuleContext()->builder.processExport(node); } template <> inline bool PerHandlerParser::processExport(Node node) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return false; } template <> inline bool PerHandlerParser::processExportFrom( BinaryNodeType node) { return pc_->sc()->asModuleContext()->builder.processExportFrom(node); } template <> inline bool PerHandlerParser::processExportFrom( BinaryNodeType node) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return false; } template <> inline bool PerHandlerParser::processImport( BinaryNodeType node) { return pc_->sc()->asModuleContext()->builder.processImport(node); } template <> inline bool PerHandlerParser::processImport( BinaryNodeType node) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return false; } template typename ParseHandler::BinaryNodeResult GeneralParser::exportFrom(uint32_t begin, Node specList) { if (!abortIfSyntaxParser()) { return errorResult(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::From)); if (!mustMatchToken(TokenKind::String, JSMSG_MODULE_SPEC_AFTER_FROM)) { return errorResult(); } NameNodeType moduleSpec; MOZ_TRY_VAR(moduleSpec, stringLiteral()); TokenKind tt; // The `assert` keyword has a [no LineTerminator here] production before it in // the grammar -- `with` does not. We need to handle this distinction. if (!tokenStream.peekTokenSameLine(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } // `with` may have an EOL prior, so peek the next token and replace // EOL if the next token is `with`. if (tt == TokenKind::Eol) { // Doing a regular peek won't produce Eol, but the actual next token. TokenKind peekedToken; if (!tokenStream.peekToken(&peekedToken, TokenStream::SlashIsRegExp)) { return errorResult(); } if (peekedToken == TokenKind::With) { tt = TokenKind::With; } } uint32_t moduleSpecPos = pos().begin; ListNodeType importAttributeList; MOZ_TRY_VAR(importAttributeList, handler_.newList(ParseNodeKind::ImportAttributeList, pos())); if (tt == TokenKind::With || (tt == TokenKind::Assert && options().importAttributesAssertSyntax())) { tokenStream.consumeKnownToken(tt, TokenStream::SlashIsRegExp); if (!withClause(importAttributeList)) { return errorResult(); } } if (!matchOrInsertSemicolon(TokenStream::SlashIsRegExp)) { return errorResult(); } BinaryNodeType moduleRequest; MOZ_TRY_VAR(moduleRequest, handler_.newModuleRequest(moduleSpec, importAttributeList, TokenPos(moduleSpecPos, pos().end))); BinaryNodeType node; MOZ_TRY_VAR( node, handler_.newExportFromDeclaration(begin, specList, moduleRequest)); if (!processExportFrom(node)) { return errorResult(); } return node; } template typename ParseHandler::BinaryNodeResult GeneralParser::exportBatch(uint32_t begin) { if (!abortIfSyntaxParser()) { return errorResult(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Mul)); uint32_t beginExportSpec = pos().begin; ListNodeType kid; MOZ_TRY_VAR(kid, handler_.newList(ParseNodeKind::ExportSpecList, pos())); bool foundAs; if (!tokenStream.matchToken(&foundAs, TokenKind::As)) { return errorResult(); } if (foundAs) { TokenKind tt; if (!tokenStream.getToken(&tt)) { return errorResult(); } NameNodeType exportName = null(); if (TokenKindIsPossibleIdentifierName(tt)) { MOZ_TRY_VAR(exportName, newName(anyChars.currentName())); } else if (tt == TokenKind::String) { MOZ_TRY_VAR(exportName, moduleExportName()); } else { error(JSMSG_NO_EXPORT_NAME); return errorResult(); } if (!checkExportedNameForClause(exportName)) { return errorResult(); } UnaryNodeType exportSpec; MOZ_TRY_VAR(exportSpec, handler_.newExportNamespaceSpec(beginExportSpec, exportName)); handler_.addList(kid, exportSpec); } else { // Handle the form |export *| by adding a special export batch // specifier to the list. NullaryNodeType exportSpec; MOZ_TRY_VAR(exportSpec, handler_.newExportBatchSpec(pos())); handler_.addList(kid, exportSpec); } if (!mustMatchToken(TokenKind::From, JSMSG_FROM_AFTER_EXPORT_STAR)) { return errorResult(); } return exportFrom(begin, kid); } template bool Parser::checkLocalExportNames(ListNode* node) { // ES 2017 draft 15.2.3.1. for (ParseNode* next : node->contents()) { ParseNode* name = next->as().left(); if (name->isKind(ParseNodeKind::StringExpr)) { errorAt(name->pn_pos.begin, JSMSG_BAD_LOCAL_STRING_EXPORT); return false; } MOZ_ASSERT(name->isKind(ParseNodeKind::Name)); TaggedParserAtomIndex ident = name->as().atom(); if (!checkLocalExportName(ident, name->pn_pos.begin)) { return false; } } return true; } template bool Parser::checkLocalExportNames( ListNodeType node) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return false; } template inline bool GeneralParser::checkLocalExportNames( ListNodeType node) { return asFinalParser()->checkLocalExportNames(node); } template typename ParseHandler::NodeResult GeneralParser::exportClause(uint32_t begin) { if (!abortIfSyntaxParser()) { return errorResult(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftCurly)); ListNodeType kid; MOZ_TRY_VAR(kid, handler_.newList(ParseNodeKind::ExportSpecList, pos())); TokenKind tt; while (true) { // Handle the forms |export {}| and |export { ..., }| (where ... is non // empty), by escaping the loop early if the next token is }. if (!tokenStream.getToken(&tt)) { return errorResult(); } if (tt == TokenKind::RightCurly) { break; } NameNodeType bindingName = null(); if (TokenKindIsPossibleIdentifierName(tt)) { MOZ_TRY_VAR(bindingName, newName(anyChars.currentName())); } else if (tt == TokenKind::String) { MOZ_TRY_VAR(bindingName, moduleExportName()); } else { error(JSMSG_NO_BINDING_NAME); return errorResult(); } bool foundAs; if (!tokenStream.matchToken(&foundAs, TokenKind::As)) { return errorResult(); } NameNodeType exportName = null(); if (foundAs) { TokenKind tt; if (!tokenStream.getToken(&tt)) { return errorResult(); } if (TokenKindIsPossibleIdentifierName(tt)) { MOZ_TRY_VAR(exportName, newName(anyChars.currentName())); } else if (tt == TokenKind::String) { MOZ_TRY_VAR(exportName, moduleExportName()); } else { error(JSMSG_NO_EXPORT_NAME); return errorResult(); } } else { if (tt != TokenKind::String) { MOZ_TRY_VAR(exportName, newName(anyChars.currentName())); } else { MOZ_TRY_VAR(exportName, moduleExportName()); } } if (!checkExportedNameForClause(exportName)) { return errorResult(); } BinaryNodeType exportSpec; MOZ_TRY_VAR(exportSpec, handler_.newExportSpec(bindingName, exportName)); handler_.addList(kid, exportSpec); TokenKind next; if (!tokenStream.getToken(&next)) { return errorResult(); } if (next == TokenKind::RightCurly) { break; } if (next != TokenKind::Comma) { error(JSMSG_RC_AFTER_EXPORT_SPEC_LIST); return errorResult(); } } // Careful! If |from| follows, even on a new line, it must start a // FromClause: // // export { x } // from "foo"; // a single ExportDeclaration // // But if it doesn't, we might have an ASI opportunity in SlashIsRegExp // context: // // export { x } // ExportDeclaration, terminated by ASI // fro\u006D // ExpressionStatement, the name "from" // // In that case let matchOrInsertSemicolon sort out ASI or any necessary // error. bool matched; if (!tokenStream.matchToken(&matched, TokenKind::From, TokenStream::SlashIsRegExp)) { return errorResult(); } if (matched) { return exportFrom(begin, kid); } if (!matchOrInsertSemicolon()) { return errorResult(); } if (!checkLocalExportNames(kid)) { return errorResult(); } UnaryNodeType node; MOZ_TRY_VAR(node, handler_.newExportDeclaration(kid, TokenPos(begin, pos().end))); if (!processExport(node)) { return errorResult(); } return node; } template typename ParseHandler::UnaryNodeResult GeneralParser::exportVariableStatement(uint32_t begin) { if (!abortIfSyntaxParser()) { return errorResult(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Var)); DeclarationListNodeType kid; MOZ_TRY_VAR(kid, declarationList(YieldIsName, ParseNodeKind::VarStmt)); if (!matchOrInsertSemicolon()) { return errorResult(); } if (!checkExportedNamesForDeclarationList(kid)) { return errorResult(); } UnaryNodeType node; MOZ_TRY_VAR(node, handler_.newExportDeclaration(kid, TokenPos(begin, pos().end))); if (!processExport(node)) { return errorResult(); } return node; } template typename ParseHandler::UnaryNodeResult GeneralParser::exportFunctionDeclaration( uint32_t begin, uint32_t toStringStart, FunctionAsyncKind asyncKind /* = SyncFunction */) { if (!abortIfSyntaxParser()) { return errorResult(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Function)); Node kid; MOZ_TRY_VAR( kid, functionStmt(toStringStart, YieldIsName, NameRequired, asyncKind)); if (!checkExportedNameForFunction(handler_.asFunctionNode(kid))) { return errorResult(); } UnaryNodeType node; MOZ_TRY_VAR(node, handler_.newExportDeclaration(kid, TokenPos(begin, pos().end))); if (!processExport(node)) { return errorResult(); } return node; } template typename ParseHandler::UnaryNodeResult GeneralParser::exportClassDeclaration(uint32_t begin) { if (!abortIfSyntaxParser()) { return errorResult(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Class)); ClassNodeType kid; MOZ_TRY_VAR(kid, classDefinition(YieldIsName, ClassStatement, NameRequired)); if (!checkExportedNameForClass(kid)) { return errorResult(); } UnaryNodeType node; MOZ_TRY_VAR(node, handler_.newExportDeclaration(kid, TokenPos(begin, pos().end))); if (!processExport(node)) { return errorResult(); } return node; } template typename ParseHandler::UnaryNodeResult GeneralParser::exportLexicalDeclaration( uint32_t begin, DeclarationKind kind) { if (!abortIfSyntaxParser()) { return errorResult(); } MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let); MOZ_ASSERT_IF(kind == DeclarationKind::Const, anyChars.isCurrentTokenType(TokenKind::Const)); MOZ_ASSERT_IF(kind == DeclarationKind::Let, anyChars.isCurrentTokenType(TokenKind::Let)); DeclarationListNodeType kid; MOZ_TRY_VAR(kid, lexicalDeclaration(YieldIsName, kind)); if (!checkExportedNamesForDeclarationList(kid)) { return errorResult(); } UnaryNodeType node; MOZ_TRY_VAR(node, handler_.newExportDeclaration(kid, TokenPos(begin, pos().end))); if (!processExport(node)) { return errorResult(); } return node; } template typename ParseHandler::BinaryNodeResult GeneralParser::exportDefaultFunctionDeclaration( uint32_t begin, uint32_t toStringStart, FunctionAsyncKind asyncKind /* = SyncFunction */) { if (!abortIfSyntaxParser()) { return errorResult(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Function)); Node kid; MOZ_TRY_VAR(kid, functionStmt(toStringStart, YieldIsName, AllowDefaultName, asyncKind)); BinaryNodeType node; MOZ_TRY_VAR(node, handler_.newExportDefaultDeclaration( kid, null(), TokenPos(begin, pos().end))); if (!processExport(node)) { return errorResult(); } return node; } template typename ParseHandler::BinaryNodeResult GeneralParser::exportDefaultClassDeclaration( uint32_t begin) { if (!abortIfSyntaxParser()) { return errorResult(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Class)); ClassNodeType kid; MOZ_TRY_VAR(kid, classDefinition(YieldIsName, ClassStatement, AllowDefaultName)); BinaryNodeType node; MOZ_TRY_VAR(node, handler_.newExportDefaultDeclaration( kid, null(), TokenPos(begin, pos().end))); if (!processExport(node)) { return errorResult(); } return node; } template typename ParseHandler::BinaryNodeResult GeneralParser::exportDefaultAssignExpr(uint32_t begin) { if (!abortIfSyntaxParser()) { return errorResult(); } TaggedParserAtomIndex name = TaggedParserAtomIndex::WellKnown::default_(); NameNodeType nameNode; MOZ_TRY_VAR(nameNode, newName(name)); if (!noteDeclaredName(name, DeclarationKind::Const, pos())) { return errorResult(); } Node kid; MOZ_TRY_VAR(kid, assignExpr(InAllowed, YieldIsName, TripledotProhibited)); if (!matchOrInsertSemicolon()) { return errorResult(); } BinaryNodeType node; MOZ_TRY_VAR(node, handler_.newExportDefaultDeclaration( kid, nameNode, TokenPos(begin, pos().end))); if (!processExport(node)) { return errorResult(); } return node; } template typename ParseHandler::BinaryNodeResult GeneralParser::exportDefault(uint32_t begin) { if (!abortIfSyntaxParser()) { return errorResult(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Default)); TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } if (!checkExportedName(TaggedParserAtomIndex::WellKnown::default_())) { return errorResult(); } switch (tt) { case TokenKind::Function: return exportDefaultFunctionDeclaration(begin, pos().begin); case TokenKind::Async: { TokenKind nextSameLine = TokenKind::Eof; if (!tokenStream.peekTokenSameLine(&nextSameLine)) { return errorResult(); } if (nextSameLine == TokenKind::Function) { uint32_t toStringStart = pos().begin; tokenStream.consumeKnownToken(TokenKind::Function); return exportDefaultFunctionDeclaration( begin, toStringStart, FunctionAsyncKind::AsyncFunction); } anyChars.ungetToken(); return exportDefaultAssignExpr(begin); } case TokenKind::Class: return exportDefaultClassDeclaration(begin); default: anyChars.ungetToken(); return exportDefaultAssignExpr(begin); } } template typename ParseHandler::NodeResult GeneralParser::exportDeclaration() { if (!abortIfSyntaxParser()) { return errorResult(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Export)); if (!pc_->atModuleLevel()) { error(JSMSG_EXPORT_DECL_AT_TOP_LEVEL); return errorResult(); } uint32_t begin = pos().begin; TokenKind tt; if (!tokenStream.getToken(&tt)) { return errorResult(); } switch (tt) { case TokenKind::Mul: return exportBatch(begin); case TokenKind::LeftCurly: return exportClause(begin); case TokenKind::Var: return exportVariableStatement(begin); case TokenKind::Function: return exportFunctionDeclaration(begin, pos().begin); case TokenKind::Async: { TokenKind nextSameLine = TokenKind::Eof; if (!tokenStream.peekTokenSameLine(&nextSameLine)) { return errorResult(); } if (nextSameLine == TokenKind::Function) { uint32_t toStringStart = pos().begin; tokenStream.consumeKnownToken(TokenKind::Function); return exportFunctionDeclaration(begin, toStringStart, FunctionAsyncKind::AsyncFunction); } error(JSMSG_DECLARATION_AFTER_EXPORT); return errorResult(); } case TokenKind::Class: return exportClassDeclaration(begin); case TokenKind::Const: return exportLexicalDeclaration(begin, DeclarationKind::Const); case TokenKind::Let: return exportLexicalDeclaration(begin, DeclarationKind::Let); case TokenKind::Default: return exportDefault(begin); default: error(JSMSG_DECLARATION_AFTER_EXPORT); return errorResult(); } } template typename ParseHandler::UnaryNodeResult GeneralParser::expressionStatement( YieldHandling yieldHandling, InvokedPrediction invoked) { anyChars.ungetToken(); Node pnexpr; MOZ_TRY_VAR(pnexpr, expr(InAllowed, yieldHandling, TripledotProhibited, /* possibleError = */ nullptr, invoked)); if (!matchOrInsertSemicolon()) { return errorResult(); } return handler_.newExprStatement(pnexpr, pos().end); } template typename ParseHandler::NodeResult GeneralParser::consequentOrAlternative( YieldHandling yieldHandling) { TokenKind next; if (!tokenStream.peekToken(&next, TokenStream::SlashIsRegExp)) { return errorResult(); } // Annex B.3.4 says that unbraced FunctionDeclarations under if/else in // non-strict code act as if they were braced: |if (x) function f() {}| // parses as |if (x) { function f() {} }|. // // Careful! FunctionDeclaration doesn't include generators or async // functions. if (next == TokenKind::Function) { tokenStream.consumeKnownToken(next, TokenStream::SlashIsRegExp); // Parser::statement would handle this, but as this function handles // every other error case, it seems best to handle this. if (pc_->sc()->strict()) { error(JSMSG_FORBIDDEN_AS_STATEMENT, "function declarations"); return errorResult(); } TokenKind maybeStar; if (!tokenStream.peekToken(&maybeStar)) { return errorResult(); } if (maybeStar == TokenKind::Mul) { error(JSMSG_FORBIDDEN_AS_STATEMENT, "generator declarations"); return errorResult(); } ParseContext::Statement stmt(pc_, StatementKind::Block); ParseContext::Scope scope(this); if (!scope.init(pc_)) { return errorResult(); } TokenPos funcPos = pos(); Node fun; MOZ_TRY_VAR(fun, functionStmt(pos().begin, yieldHandling, NameRequired)); ListNodeType block; MOZ_TRY_VAR(block, handler_.newStatementList(funcPos)); handler_.addStatementToList(block, fun); return finishLexicalScope(scope, block); } return statement(yieldHandling); } template typename ParseHandler::TernaryNodeResult GeneralParser::ifStatement(YieldHandling yieldHandling) { Vector condList(fc_), thenList(fc_); Vector posList(fc_); Node elseBranch; ParseContext::Statement stmt(pc_, StatementKind::If); while (true) { uint32_t begin = pos().begin; /* An IF node has three kids: condition, then, and optional else. */ Node cond; MOZ_TRY_VAR(cond, condition(InAllowed, yieldHandling)); TokenKind tt; if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } Node thenBranch; MOZ_TRY_VAR(thenBranch, consequentOrAlternative(yieldHandling)); if (!condList.append(cond) || !thenList.append(thenBranch) || !posList.append(begin)) { return errorResult(); } bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Else, TokenStream::SlashIsRegExp)) { return errorResult(); } if (matched) { if (!tokenStream.matchToken(&matched, TokenKind::If, TokenStream::SlashIsRegExp)) { return errorResult(); } if (matched) { continue; } MOZ_TRY_VAR(elseBranch, consequentOrAlternative(yieldHandling)); } else { elseBranch = null(); } break; } TernaryNodeType ifNode; for (int i = condList.length() - 1; i >= 0; i--) { MOZ_TRY_VAR(ifNode, handler_.newIfStatement(posList[i], condList[i], thenList[i], elseBranch)); elseBranch = ifNode; } return ifNode; } template typename ParseHandler::BinaryNodeResult GeneralParser::doWhileStatement( YieldHandling yieldHandling) { uint32_t begin = pos().begin; ParseContext::Statement stmt(pc_, StatementKind::DoLoop); Node body; MOZ_TRY_VAR(body, statement(yieldHandling)); if (!mustMatchToken(TokenKind::While, JSMSG_WHILE_AFTER_DO)) { return errorResult(); } Node cond; MOZ_TRY_VAR(cond, condition(InAllowed, yieldHandling)); // The semicolon after do-while is even more optional than most // semicolons in JS. Web compat required this by 2004: // http://bugzilla.mozilla.org/show_bug.cgi?id=238945 // ES3 and ES5 disagreed, but ES6 conforms to Web reality: // https://bugs.ecmascript.org/show_bug.cgi?id=157 // To parse |do {} while (true) false| correctly, use SlashIsRegExp. bool ignored; if (!tokenStream.matchToken(&ignored, TokenKind::Semi, TokenStream::SlashIsRegExp)) { return errorResult(); } return handler_.newDoWhileStatement(body, cond, TokenPos(begin, pos().end)); } template typename ParseHandler::BinaryNodeResult GeneralParser::whileStatement(YieldHandling yieldHandling) { uint32_t begin = pos().begin; ParseContext::Statement stmt(pc_, StatementKind::WhileLoop); Node cond; MOZ_TRY_VAR(cond, condition(InAllowed, yieldHandling)); Node body; MOZ_TRY_VAR(body, statement(yieldHandling)); return handler_.newWhileStatement(begin, cond, body); } template bool GeneralParser::matchInOrOf(bool* isForInp, bool* isForOfp) { TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return false; } *isForInp = tt == TokenKind::In; *isForOfp = tt == TokenKind::Of; if (!*isForInp && !*isForOfp) { anyChars.ungetToken(); } MOZ_ASSERT_IF(*isForInp || *isForOfp, *isForInp != *isForOfp); return true; } template bool GeneralParser::forHeadStart( YieldHandling yieldHandling, IteratorKind iterKind, ParseNodeKind* forHeadKind, Node* forInitialPart, Maybe& forLoopLexicalScope, Node* forInOrOfExpression) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftParen)); TokenKind tt; if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) { return false; } // Super-duper easy case: |for (;| is a C-style for-loop with no init // component. if (tt == TokenKind::Semi) { *forInitialPart = null(); *forHeadKind = ParseNodeKind::ForHead; return true; } // Parsing after |for (var| is also relatively simple (from this method's // point of view). No block-related work complicates matters, so delegate // to Parser::declaration. if (tt == TokenKind::Var) { tokenStream.consumeKnownToken(tt, TokenStream::SlashIsRegExp); // Pass null for block object because |var| declarations don't use one. MOZ_TRY_VAR_OR_RETURN(*forInitialPart, declarationList(yieldHandling, ParseNodeKind::VarStmt, forHeadKind, forInOrOfExpression), false); return true; } // Otherwise we have a lexical declaration or an expression. // For-in loop backwards compatibility requires that |let| starting a // for-loop that's not a (new to ES6) for-of loop, in non-strict mode code, // parse as an identifier. (|let| in for-of is always a declaration.) // // For-of loops can't start with the token sequence "async of", because that // leads to a shift-reduce conflict when parsing |for (async of => {};;)| or // |for (async of [])|. bool parsingLexicalDeclaration = false; bool letIsIdentifier = false; bool startsWithForOf = false; if (tt == TokenKind::Const) { parsingLexicalDeclaration = true; tokenStream.consumeKnownToken(tt, TokenStream::SlashIsRegExp); } else if (tt == TokenKind::Let) { // We could have a {For,Lexical}Declaration, or we could have a // LeftHandSideExpression with lookahead restrictions so it's not // ambiguous with the former. Check for a continuation of the former // to decide which we have. tokenStream.consumeKnownToken(TokenKind::Let, TokenStream::SlashIsRegExp); TokenKind next; if (!tokenStream.peekToken(&next)) { return false; } parsingLexicalDeclaration = nextTokenContinuesLetDeclaration(next); if (!parsingLexicalDeclaration) { // If we end up here, we may have `for (let of/in ...`, // which is not valid. if (next != TokenKind::In && next != TokenKind::Of && TokenKindIsReservedWord(next)) { tokenStream.consumeKnownToken(next); error(JSMSG_UNEXPECTED_TOKEN_NO_EXPECT, TokenKindToDesc(next)); return false; } anyChars.ungetToken(); letIsIdentifier = true; } } else if (tt == TokenKind::Async && iterKind == IteratorKind::Sync) { tokenStream.consumeKnownToken(TokenKind::Async, TokenStream::SlashIsRegExp); TokenKind next; if (!tokenStream.peekToken(&next)) { return false; } if (next == TokenKind::Of) { startsWithForOf = true; } anyChars.ungetToken(); } if (parsingLexicalDeclaration) { if (options().selfHostingMode) { error(JSMSG_SELFHOSTED_LEXICAL); return false; } forLoopLexicalScope.emplace(this); if (!forLoopLexicalScope->init(pc_)) { return false; } // Push a temporary ForLoopLexicalHead Statement that allows for // lexical declarations, as they are usually allowed only in braced // statements. ParseContext::Statement forHeadStmt(pc_, StatementKind::ForLoopLexicalHead); MOZ_TRY_VAR_OR_RETURN( *forInitialPart, declarationList(yieldHandling, tt == TokenKind::Const ? ParseNodeKind::ConstDecl : ParseNodeKind::LetDecl, forHeadKind, forInOrOfExpression), false); return true; } uint32_t exprOffset; if (!tokenStream.peekOffset(&exprOffset, TokenStream::SlashIsRegExp)) { return false; } // Finally, handle for-loops that start with expressions. Pass // |InProhibited| so that |in| isn't parsed in a RelationalExpression as a // binary operator. |in| makes it a for-in loop, *not* an |in| expression. PossibleError possibleError(*this); MOZ_TRY_VAR_OR_RETURN( *forInitialPart, expr(InProhibited, yieldHandling, TripledotProhibited, &possibleError), false); bool isForIn, isForOf; if (!matchInOrOf(&isForIn, &isForOf)) { return false; } // If we don't encounter 'in'/'of', we have a for(;;) loop. We've handled // the init expression; the caller handles the rest. if (!isForIn && !isForOf) { if (!possibleError.checkForExpressionError()) { return false; } *forHeadKind = ParseNodeKind::ForHead; return true; } MOZ_ASSERT(isForIn != isForOf); // In a for-of loop, 'let' that starts the loop head is a |let| keyword, // per the [lookahead ≠ let] restriction on the LeftHandSideExpression // variant of such loops. Expressions that start with |let| can't be used // here. // // var let = {}; // for (let.prop of [1]) // BAD // break; // // See ES6 13.7. if (isForOf && letIsIdentifier) { errorAt(exprOffset, JSMSG_BAD_STARTING_FOROF_LHS, "let"); return false; } // In a for-of loop, the LeftHandSideExpression isn't allowed to be an // identifier named "async" per the [lookahead ≠ async of] restriction. if (isForOf && startsWithForOf) { errorAt(exprOffset, JSMSG_BAD_STARTING_FOROF_LHS, "async of"); return false; } *forHeadKind = isForIn ? ParseNodeKind::ForIn : ParseNodeKind::ForOf; // Verify the left-hand side expression doesn't have a forbidden form. if (handler_.isUnparenthesizedDestructuringPattern(*forInitialPart)) { if (!possibleError.checkForDestructuringErrorOrWarning()) { return false; } } else if (handler_.isName(*forInitialPart)) { if (const char* chars = nameIsArgumentsOrEval(*forInitialPart)) { // |chars| is "arguments" or "eval" here. if (!strictModeErrorAt(exprOffset, JSMSG_BAD_STRICT_ASSIGN, chars)) { return false; } } } else if (handler_.isArgumentsLength(*forInitialPart)) { pc_->sc()->setIneligibleForArgumentsLength(); } else if (handler_.isPropertyOrPrivateMemberAccess(*forInitialPart)) { // Permitted: no additional testing/fixup needed. } else if (handler_.isFunctionCall(*forInitialPart)) { if (!strictModeErrorAt(exprOffset, JSMSG_BAD_FOR_LEFTSIDE)) { return false; } } else { errorAt(exprOffset, JSMSG_BAD_FOR_LEFTSIDE); return false; } if (!possibleError.checkForExpressionError()) { return false; } // Finally, parse the iterated expression, making the for-loop's closing // ')' the next token. MOZ_TRY_VAR_OR_RETURN(*forInOrOfExpression, expressionAfterForInOrOf(*forHeadKind, yieldHandling), false); return true; } template typename ParseHandler::NodeResult GeneralParser::forStatement(YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::For)); uint32_t begin = pos().begin; ParseContext::Statement stmt(pc_, StatementKind::ForLoop); IteratorKind iterKind = IteratorKind::Sync; unsigned iflags = 0; if (pc_->isAsync() || pc_->sc()->isModuleContext()) { bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Await)) { return errorResult(); } // If we come across a top level await here, mark the module as async. if (matched && pc_->sc()->isModuleContext() && !pc_->isAsync()) { if (!options().topLevelAwait) { error(JSMSG_TOP_LEVEL_AWAIT_NOT_SUPPORTED); return errorResult(); } pc_->sc()->asModuleContext()->setIsAsync(); MOZ_ASSERT(pc_->isAsync()); } if (matched) { iflags |= JSITER_FORAWAITOF; iterKind = IteratorKind::Async; } } if (!mustMatchToken(TokenKind::LeftParen, [this](TokenKind actual) { this->error((actual == TokenKind::Await && !this->pc_->isAsync()) ? JSMSG_FOR_AWAIT_OUTSIDE_ASYNC : JSMSG_PAREN_AFTER_FOR); })) { return errorResult(); } // ParseNodeKind::ForHead, ParseNodeKind::ForIn, or // ParseNodeKind::ForOf depending on the loop type. ParseNodeKind headKind; // |x| in either |for (x; ...; ...)| or |for (x in/of ...)|. Node startNode; // The next two variables are used to implement `for (let/const ...)`. // // We generate an implicit block, wrapping the whole loop, to store loop // variables declared this way. Note that if the loop uses `for (var...)` // instead, those variables go on some existing enclosing scope, so no // implicit block scope is created. // // Both variables remain null/none if the loop is any other form. // The static block scope for the implicit block scope. Maybe forLoopLexicalScope; // The expression being iterated over, for for-in/of loops only. Unused // for for(;;) loops. Node iteratedExpr; // Parse the entirety of the loop-head for a for-in/of loop (so the next // token is the closing ')'): // // for (... in/of ...) ... // ^next token // // ...OR, parse up to the first ';' in a C-style for-loop: // // for (...; ...; ...) ... // ^next token // // In either case the subsequent token can be consistently accessed using // TokenStream::SlashIsDiv semantics. if (!forHeadStart(yieldHandling, iterKind, &headKind, &startNode, forLoopLexicalScope, &iteratedExpr)) { return errorResult(); } MOZ_ASSERT(headKind == ParseNodeKind::ForIn || headKind == ParseNodeKind::ForOf || headKind == ParseNodeKind::ForHead); if (iterKind == IteratorKind::Async && headKind != ParseNodeKind::ForOf) { errorAt(begin, JSMSG_FOR_AWAIT_NOT_OF); return errorResult(); } TernaryNodeType forHead; if (headKind == ParseNodeKind::ForHead) { Node init = startNode; // Look for an operand: |for (;| means we might have already examined // this semicolon with that modifier. if (!mustMatchToken(TokenKind::Semi, JSMSG_SEMI_AFTER_FOR_INIT)) { return errorResult(); } TokenKind tt; if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } Node test; if (tt == TokenKind::Semi) { test = null(); } else { MOZ_TRY_VAR(test, expr(InAllowed, yieldHandling, TripledotProhibited)); } if (!mustMatchToken(TokenKind::Semi, JSMSG_SEMI_AFTER_FOR_COND)) { return errorResult(); } if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } Node update; if (tt == TokenKind::RightParen) { update = null(); } else { MOZ_TRY_VAR(update, expr(InAllowed, yieldHandling, TripledotProhibited)); } if (!mustMatchToken(TokenKind::RightParen, JSMSG_PAREN_AFTER_FOR_CTRL)) { return errorResult(); } TokenPos headPos(begin, pos().end); MOZ_TRY_VAR(forHead, handler_.newForHead(init, test, update, headPos)); } else { MOZ_ASSERT(headKind == ParseNodeKind::ForIn || headKind == ParseNodeKind::ForOf); // |target| is the LeftHandSideExpression or declaration to which the // per-iteration value (an arbitrary value exposed by the iteration // protocol, or a string naming a property) is assigned. Node target = startNode; // Parse the rest of the for-in/of head. if (headKind == ParseNodeKind::ForIn) { stmt.refineForKind(StatementKind::ForInLoop); } else { stmt.refineForKind(StatementKind::ForOfLoop); } // Parser::declaration consumed everything up to the closing ')'. That // token follows an {Assignment,}Expression and so must be interpreted // as an operand to be consistent with normal expression tokenizing. if (!mustMatchToken(TokenKind::RightParen, JSMSG_PAREN_AFTER_FOR_CTRL)) { return errorResult(); } TokenPos headPos(begin, pos().end); MOZ_TRY_VAR(forHead, handler_.newForInOrOfHead(headKind, target, iteratedExpr, headPos)); } Node body; MOZ_TRY_VAR(body, statement(yieldHandling)); ForNodeType forLoop; MOZ_TRY_VAR(forLoop, handler_.newForStatement(begin, forHead, body, iflags)); if (forLoopLexicalScope) { return finishLexicalScope(*forLoopLexicalScope, forLoop); } return forLoop; } template typename ParseHandler::SwitchStatementResult GeneralParser::switchStatement( YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Switch)); uint32_t begin = pos().begin; if (!mustMatchToken(TokenKind::LeftParen, JSMSG_PAREN_BEFORE_SWITCH)) { return errorResult(); } Node discriminant; MOZ_TRY_VAR(discriminant, exprInParens(InAllowed, yieldHandling, TripledotProhibited)); if (!mustMatchToken(TokenKind::RightParen, JSMSG_PAREN_AFTER_SWITCH)) { return errorResult(); } if (!mustMatchToken(TokenKind::LeftCurly, JSMSG_CURLY_BEFORE_SWITCH)) { return errorResult(); } ParseContext::Statement stmt(pc_, StatementKind::Switch); ParseContext::Scope scope(this); if (!scope.init(pc_)) { return errorResult(); } ListNodeType caseList; MOZ_TRY_VAR(caseList, handler_.newStatementList(pos())); bool seenDefault = false; TokenKind tt; while (true) { if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } if (tt == TokenKind::RightCurly) { break; } uint32_t caseBegin = pos().begin; Node caseExpr; switch (tt) { case TokenKind::Default: if (seenDefault) { error(JSMSG_TOO_MANY_DEFAULTS); return errorResult(); } seenDefault = true; caseExpr = null(); // The default case has pn_left == nullptr. break; case TokenKind::Case: MOZ_TRY_VAR(caseExpr, expr(InAllowed, yieldHandling, TripledotProhibited)); break; default: error(JSMSG_BAD_SWITCH); return errorResult(); } if (!mustMatchToken(TokenKind::Colon, JSMSG_COLON_AFTER_CASE)) { return errorResult(); } ListNodeType body; MOZ_TRY_VAR(body, handler_.newStatementList(pos())); bool afterReturn = false; bool warnedAboutStatementsAfterReturn = false; uint32_t statementBegin = 0; while (true) { if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } if (tt == TokenKind::RightCurly || tt == TokenKind::Case || tt == TokenKind::Default) { break; } if (afterReturn) { if (!tokenStream.peekOffset(&statementBegin, TokenStream::SlashIsRegExp)) { return errorResult(); } } Node stmt; MOZ_TRY_VAR(stmt, statementListItem(yieldHandling)); if (!warnedAboutStatementsAfterReturn) { if (afterReturn) { if (!handler_.isStatementPermittedAfterReturnStatement(stmt)) { if (!warningAt(statementBegin, JSMSG_STMT_AFTER_RETURN)) { return errorResult(); } warnedAboutStatementsAfterReturn = true; } } else if (handler_.isReturnStatement(stmt)) { afterReturn = true; } } handler_.addStatementToList(body, stmt); } CaseClauseType caseClause; MOZ_TRY_VAR(caseClause, handler_.newCaseOrDefault(caseBegin, caseExpr, body)); handler_.addCaseStatementToList(caseList, caseClause); } LexicalScopeNodeType lexicalForCaseList; MOZ_TRY_VAR(lexicalForCaseList, finishLexicalScope(scope, caseList)); handler_.setEndPosition(lexicalForCaseList, pos().end); return handler_.newSwitchStatement(begin, discriminant, lexicalForCaseList, seenDefault); } template typename ParseHandler::ContinueStatementResult GeneralParser::continueStatement( YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Continue)); uint32_t begin = pos().begin; TaggedParserAtomIndex label; if (!matchLabel(yieldHandling, &label)) { return errorResult(); } auto validity = pc_->checkContinueStatement(label); if (validity.isErr()) { switch (validity.unwrapErr()) { case ParseContext::ContinueStatementError::NotInALoop: errorAt(begin, JSMSG_BAD_CONTINUE); break; case ParseContext::ContinueStatementError::LabelNotFound: error(JSMSG_LABEL_NOT_FOUND); break; } return errorResult(); } if (!matchOrInsertSemicolon()) { return errorResult(); } return handler_.newContinueStatement(label, TokenPos(begin, pos().end)); } template typename ParseHandler::BreakStatementResult GeneralParser::breakStatement(YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Break)); uint32_t begin = pos().begin; TaggedParserAtomIndex label; if (!matchLabel(yieldHandling, &label)) { return errorResult(); } auto validity = pc_->checkBreakStatement(label); if (validity.isErr()) { switch (validity.unwrapErr()) { case ParseContext::BreakStatementError::ToughBreak: errorAt(begin, JSMSG_TOUGH_BREAK); return errorResult(); case ParseContext::BreakStatementError::LabelNotFound: error(JSMSG_LABEL_NOT_FOUND); return errorResult(); } } if (!matchOrInsertSemicolon()) { return errorResult(); } return handler_.newBreakStatement(label, TokenPos(begin, pos().end)); } template typename ParseHandler::UnaryNodeResult GeneralParser::returnStatement( YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Return)); uint32_t begin = pos().begin; MOZ_ASSERT(pc_->isFunctionBox()); // Parse an optional operand. // // This is ugly, but we don't want to require a semicolon. Node exprNode; TokenKind tt = TokenKind::Eof; if (!tokenStream.peekTokenSameLine(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } switch (tt) { case TokenKind::Eol: case TokenKind::Eof: case TokenKind::Semi: case TokenKind::RightCurly: exprNode = null(); break; default: { MOZ_TRY_VAR(exprNode, expr(InAllowed, yieldHandling, TripledotProhibited)); } } if (!matchOrInsertSemicolon()) { return errorResult(); } return handler_.newReturnStatement(exprNode, TokenPos(begin, pos().end)); } template typename ParseHandler::UnaryNodeResult GeneralParser::yieldExpression(InHandling inHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Yield)); uint32_t begin = pos().begin; MOZ_ASSERT(pc_->isGenerator()); MOZ_ASSERT(pc_->isFunctionBox()); pc_->lastYieldOffset = begin; Node exprNode; ParseNodeKind kind = ParseNodeKind::YieldExpr; TokenKind tt = TokenKind::Eof; if (!tokenStream.peekTokenSameLine(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } switch (tt) { // TokenKind::Eol is special; it implements the [no LineTerminator here] // quirk in the grammar. case TokenKind::Eol: // The rest of these make up the complete set of tokens that can // appear after any of the places where AssignmentExpression is used // throughout the grammar. Conveniently, none of them can also be the // start an expression. case TokenKind::Eof: case TokenKind::Semi: case TokenKind::RightCurly: case TokenKind::RightBracket: case TokenKind::RightParen: case TokenKind::Colon: case TokenKind::Comma: case TokenKind::In: // Annex B.3.6 `for (x = yield in y) ;` // No value. exprNode = null(); break; case TokenKind::Mul: kind = ParseNodeKind::YieldStarExpr; tokenStream.consumeKnownToken(TokenKind::Mul, TokenStream::SlashIsRegExp); [[fallthrough]]; default: MOZ_TRY_VAR(exprNode, assignExpr(inHandling, YieldIsKeyword, TripledotProhibited)); } if (kind == ParseNodeKind::YieldStarExpr) { return handler_.newYieldStarExpression(begin, exprNode); } return handler_.newYieldExpression(begin, exprNode); } template typename ParseHandler::BinaryNodeResult GeneralParser::withStatement(YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::With)); uint32_t begin = pos().begin; if (pc_->sc()->strict()) { if (!strictModeError(JSMSG_STRICT_CODE_WITH)) { return errorResult(); } } if (!mustMatchToken(TokenKind::LeftParen, JSMSG_PAREN_BEFORE_WITH)) { return errorResult(); } Node objectExpr; MOZ_TRY_VAR(objectExpr, exprInParens(InAllowed, yieldHandling, TripledotProhibited)); if (!mustMatchToken(TokenKind::RightParen, JSMSG_PAREN_AFTER_WITH)) { return errorResult(); } Node innerBlock; { ParseContext::Statement stmt(pc_, StatementKind::With); MOZ_TRY_VAR(innerBlock, statement(yieldHandling)); } pc_->sc()->setBindingsAccessedDynamically(); return handler_.newWithStatement(begin, objectExpr, innerBlock); } template typename ParseHandler::NodeResult GeneralParser::labeledItem(YieldHandling yieldHandling) { TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } if (tt == TokenKind::Function) { TokenKind next; if (!tokenStream.peekToken(&next)) { return errorResult(); } // GeneratorDeclaration is only matched by HoistableDeclaration in // StatementListItem, so generators can't be inside labels. if (next == TokenKind::Mul) { error(JSMSG_GENERATOR_LABEL); return errorResult(); } // Per 13.13.1 it's a syntax error if LabelledItem: FunctionDeclaration // is ever matched. Per Annex B.3.2 that modifies this text, this // applies only to strict mode code. if (pc_->sc()->strict()) { error(JSMSG_FUNCTION_LABEL); return errorResult(); } return functionStmt(pos().begin, yieldHandling, NameRequired); } anyChars.ungetToken(); return statement(yieldHandling); } template typename ParseHandler::LabeledStatementResult GeneralParser::labeledStatement( YieldHandling yieldHandling) { TaggedParserAtomIndex label = labelIdentifier(yieldHandling); if (!label) { return errorResult(); } auto hasSameLabel = [&label](ParseContext::LabelStatement* stmt) { return stmt->label() == label; }; uint32_t begin = pos().begin; if (pc_->template findInnermostStatement( hasSameLabel)) { errorAt(begin, JSMSG_DUPLICATE_LABEL); return errorResult(); } tokenStream.consumeKnownToken(TokenKind::Colon); /* Push a label struct and parse the statement. */ ParseContext::LabelStatement stmt(pc_, label); Node pn; MOZ_TRY_VAR(pn, labeledItem(yieldHandling)); return handler_.newLabeledStatement(label, pn, begin); } template typename ParseHandler::UnaryNodeResult GeneralParser::throwStatement(YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Throw)); uint32_t begin = pos().begin; /* ECMA-262 Edition 3 says 'throw [no LineTerminator here] Expr'. */ TokenKind tt = TokenKind::Eof; if (!tokenStream.peekTokenSameLine(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } if (tt == TokenKind::Eof || tt == TokenKind::Semi || tt == TokenKind::RightCurly) { error(JSMSG_MISSING_EXPR_AFTER_THROW); return errorResult(); } if (tt == TokenKind::Eol) { error(JSMSG_LINE_BREAK_AFTER_THROW); return errorResult(); } Node throwExpr; MOZ_TRY_VAR(throwExpr, expr(InAllowed, yieldHandling, TripledotProhibited)); if (!matchOrInsertSemicolon()) { return errorResult(); } return handler_.newThrowStatement(throwExpr, TokenPos(begin, pos().end)); } template typename ParseHandler::TernaryNodeResult GeneralParser::tryStatement(YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Try)); uint32_t begin = pos().begin; /* * try nodes are ternary. * kid1 is the try statement * kid2 is the catch node list or null * kid3 is the finally statement * * catch nodes are binary. * left is the catch-name/pattern or null * right is the catch block * * catch lvalue nodes are either: * a single identifier * TokenKind::RightBracket for a destructuring left-hand side * TokenKind::RightCurly for a destructuring left-hand side * * finally nodes are TokenKind::LeftCurly statement lists. */ Node innerBlock; { if (!mustMatchToken(TokenKind::LeftCurly, JSMSG_CURLY_BEFORE_TRY)) { return errorResult(); } uint32_t openedPos = pos().begin; ParseContext::Statement stmt(pc_, StatementKind::Try); ParseContext::Scope scope(this); if (!scope.init(pc_)) { return errorResult(); } MOZ_TRY_VAR(innerBlock, statementList(yieldHandling)); MOZ_TRY_VAR(innerBlock, finishLexicalScope(scope, innerBlock)); if (!mustMatchToken( TokenKind::RightCurly, [this, openedPos](TokenKind actual) { this->reportMissingClosing(JSMSG_CURLY_AFTER_TRY, JSMSG_CURLY_OPENED, openedPos); })) { return errorResult(); } } LexicalScopeNodeType catchScope = null(); TokenKind tt; if (!tokenStream.getToken(&tt)) { return errorResult(); } if (tt == TokenKind::Catch) { /* * Create a lexical scope node around the whole catch clause, * including the head. */ ParseContext::Statement stmt(pc_, StatementKind::Catch); ParseContext::Scope scope(this); if (!scope.init(pc_)) { return errorResult(); } /* * Legal catch forms are: * catch (lhs) { * catch { * where lhs is a name or a destructuring left-hand side. */ bool omittedBinding; if (!tokenStream.matchToken(&omittedBinding, TokenKind::LeftCurly)) { return errorResult(); } Node catchName; if (omittedBinding) { catchName = null(); } else { if (!mustMatchToken(TokenKind::LeftParen, JSMSG_PAREN_BEFORE_CATCH)) { return errorResult(); } if (!tokenStream.getToken(&tt)) { return errorResult(); } switch (tt) { case TokenKind::LeftBracket: case TokenKind::LeftCurly: MOZ_TRY_VAR(catchName, destructuringDeclaration(DeclarationKind::CatchParameter, yieldHandling, tt)); break; default: { if (!TokenKindIsPossibleIdentifierName(tt)) { error(JSMSG_CATCH_IDENTIFIER); return errorResult(); } MOZ_TRY_VAR(catchName, bindingIdentifier(DeclarationKind::SimpleCatchParameter, yieldHandling)); break; } } if (!mustMatchToken(TokenKind::RightParen, JSMSG_PAREN_AFTER_CATCH)) { return errorResult(); } if (!mustMatchToken(TokenKind::LeftCurly, JSMSG_CURLY_BEFORE_CATCH)) { return errorResult(); } } LexicalScopeNodeType catchBody; MOZ_TRY_VAR(catchBody, catchBlockStatement(yieldHandling, scope)); MOZ_TRY_VAR(catchScope, finishLexicalScope(scope, catchBody)); if (!handler_.setupCatchScope(catchScope, catchName, catchBody)) { return errorResult(); } handler_.setEndPosition(catchScope, pos().end); if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } } Node finallyBlock = null(); if (tt == TokenKind::Finally) { if (!mustMatchToken(TokenKind::LeftCurly, JSMSG_CURLY_BEFORE_FINALLY)) { return errorResult(); } uint32_t openedPos = pos().begin; ParseContext::Statement stmt(pc_, StatementKind::Finally); ParseContext::Scope scope(this); if (!scope.init(pc_)) { return errorResult(); } MOZ_TRY_VAR(finallyBlock, statementList(yieldHandling)); MOZ_TRY_VAR(finallyBlock, finishLexicalScope(scope, finallyBlock)); if (!mustMatchToken( TokenKind::RightCurly, [this, openedPos](TokenKind actual) { this->reportMissingClosing(JSMSG_CURLY_AFTER_FINALLY, JSMSG_CURLY_OPENED, openedPos); })) { return errorResult(); } } else { anyChars.ungetToken(); } if (!catchScope && !finallyBlock) { error(JSMSG_CATCH_OR_FINALLY); return errorResult(); } return handler_.newTryStatement(begin, innerBlock, catchScope, finallyBlock); } template typename ParseHandler::LexicalScopeNodeResult GeneralParser::catchBlockStatement( YieldHandling yieldHandling, ParseContext::Scope& catchParamScope) { uint32_t openedPos = pos().begin; ParseContext::Statement stmt(pc_, StatementKind::Block); // ES 13.15.7 CatchClauseEvaluation // // Step 8 means that the body of a catch block always has an additional // lexical scope. ParseContext::Scope scope(this); if (!scope.init(pc_)) { return errorResult(); } // The catch parameter names cannot be redeclared inside the catch // block, so declare the name in the inner scope. if (!scope.addCatchParameters(pc_, catchParamScope)) { return errorResult(); } ListNodeType list; MOZ_TRY_VAR(list, statementList(yieldHandling)); if (!mustMatchToken( TokenKind::RightCurly, [this, openedPos](TokenKind actual) { this->reportMissingClosing(JSMSG_CURLY_AFTER_CATCH, JSMSG_CURLY_OPENED, openedPos); })) { return errorResult(); } // The catch parameter names are not bound in the body scope, so remove // them before generating bindings. scope.removeCatchParameters(pc_, catchParamScope); return finishLexicalScope(scope, list); } template typename ParseHandler::DebuggerStatementResult GeneralParser::debuggerStatement() { TokenPos p; p.begin = pos().begin; if (!matchOrInsertSemicolon()) { return errorResult(); } p.end = pos().end; return handler_.newDebuggerStatement(p); } static AccessorType ToAccessorType(PropertyType propType) { switch (propType) { case PropertyType::Getter: return AccessorType::Getter; case PropertyType::Setter: return AccessorType::Setter; case PropertyType::Normal: case PropertyType::Method: case PropertyType::GeneratorMethod: case PropertyType::AsyncMethod: case PropertyType::AsyncGeneratorMethod: case PropertyType::Constructor: case PropertyType::DerivedConstructor: return AccessorType::None; default: MOZ_CRASH("unexpected property type"); } } #ifdef ENABLE_DECORATORS template typename ParseHandler::ListNodeResult GeneralParser::decoratorList(YieldHandling yieldHandling) { ListNodeType decorators; MOZ_TRY_VAR(decorators, handler_.newList(ParseNodeKind::DecoratorList, pos())); // Build a decorator list element. At each entry point to this loop we have // already consumed the |@| token TokenKind tt; for (;;) { if (!tokenStream.getToken(&tt, TokenStream::SlashIsInvalid)) { return errorResult(); } Node decorator; MOZ_TRY_VAR(decorator, decoratorExpr(yieldHandling, tt)); handler_.addList(decorators, decorator); if (!tokenStream.getToken(&tt)) { return errorResult(); } if (tt != TokenKind::At) { anyChars.ungetToken(); break; } } return decorators; } #endif template bool GeneralParser::classMember( YieldHandling yieldHandling, const ParseContext::ClassStatement& classStmt, TaggedParserAtomIndex className, uint32_t classStartOffset, HasHeritage hasHeritage, ClassInitializedMembers& classInitializedMembers, ListNodeType& classMembers, bool* done) { *done = false; TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsInvalid)) { return false; } if (tt == TokenKind::RightCurly) { *done = true; return true; } if (tt == TokenKind::Semi) { return true; } #ifdef ENABLE_DECORATORS ListNodeType decorators = null(); if (tt == TokenKind::At) { MOZ_TRY_VAR_OR_RETURN(decorators, decoratorList(yieldHandling), false); if (!tokenStream.getToken(&tt, TokenStream::SlashIsInvalid)) { return false; } } #endif bool isStatic = false; if (tt == TokenKind::Static) { if (!tokenStream.peekToken(&tt)) { return false; } if (tt == TokenKind::LeftCurly) { /* Parsing static class block: static { ... } */ FunctionNodeType staticBlockBody; MOZ_TRY_VAR_OR_RETURN(staticBlockBody, staticClassBlock(classInitializedMembers), false); StaticClassBlockType classBlock; MOZ_TRY_VAR_OR_RETURN( classBlock, handler_.newStaticClassBlock(staticBlockBody), false); return handler_.addClassMemberDefinition(classMembers, classBlock); } if (tt != TokenKind::LeftParen && tt != TokenKind::Assign && tt != TokenKind::Semi && tt != TokenKind::RightCurly) { isStatic = true; } else { anyChars.ungetToken(); } } else { anyChars.ungetToken(); } uint32_t propNameOffset; if (!tokenStream.peekOffset(&propNameOffset, TokenStream::SlashIsInvalid)) { return false; } TaggedParserAtomIndex propAtom; PropertyType propType; Node propName; MOZ_TRY_VAR_OR_RETURN( propName, propertyOrMethodName(yieldHandling, PropertyNameInClass, /* maybeDecl = */ Nothing(), classMembers, &propType, &propAtom), false); if (propType == PropertyType::Field || propType == PropertyType::FieldWithAccessor) { if (isStatic) { if (propAtom == TaggedParserAtomIndex::WellKnown::prototype()) { errorAt(propNameOffset, JSMSG_BAD_METHOD_DEF); return false; } } if (propAtom == TaggedParserAtomIndex::WellKnown::constructor()) { errorAt(propNameOffset, JSMSG_BAD_METHOD_DEF); return false; } if (handler_.isPrivateName(propName)) { if (propAtom == TaggedParserAtomIndex::WellKnown::hash_constructor_()) { errorAt(propNameOffset, JSMSG_BAD_METHOD_DEF); return false; } auto privateName = propAtom; if (!noteDeclaredPrivateName( propName, privateName, propType, isStatic ? FieldPlacement::Static : FieldPlacement::Instance, pos())) { return false; } } #ifdef ENABLE_DECORATORS ClassMethodType accessorGetterNode = null(); ClassMethodType accessorSetterNode = null(); if (propType == PropertyType::FieldWithAccessor) { // Decorators Proposal // https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-runtime-semantics-classfielddefinitionevaluation // // FieldDefinition : accessor ClassElementName Initializeropt // // Step 1. Let name be the result of evaluating ClassElementName. // ... // Step 3. Let privateStateDesc be the string-concatenation of name // and " accessor storage". StringBuffer privateStateDesc(fc_); if (!privateStateDesc.append(this->parserAtoms(), propAtom)) { return false; } if (!privateStateDesc.append(" accessor storage")) { return false; } // Step 4. Let privateStateName be a new Private Name whose // [[Description]] value is privateStateDesc. TokenPos propNamePos(propNameOffset, pos().end); auto privateStateName = privateStateDesc.finishParserAtom(this->parserAtoms(), fc_); if (!noteDeclaredPrivateName( propName, privateStateName, propType, isStatic ? FieldPlacement::Static : FieldPlacement::Instance, propNamePos)) { return false; } // Step 5. Let getter be MakeAutoAccessorGetter(homeObject, name, // privateStateName). MOZ_TRY_VAR_OR_RETURN( accessorGetterNode, synthesizeAccessor(propName, propNamePos, propAtom, privateStateName, isStatic, FunctionSyntaxKind::Getter, classInitializedMembers), false); // If the accessor is not decorated or is a non-static private field, // add it to the class here. Otherwise, we'll handle this when the // decorators are called. We don't need to keep a reference to the node // after this except for non-static private accessors. Please see the // comment in the definition of ClassField for details. bool addAccessorImmediately = !decorators || (!isStatic && handler_.isPrivateName(propName)); if (addAccessorImmediately) { if (!handler_.addClassMemberDefinition(classMembers, accessorGetterNode)) { return false; } if (!handler_.isPrivateName(propName)) { accessorGetterNode = null(); } } // Step 6. Let setter be MakeAutoAccessorSetter(homeObject, name, // privateStateName). MOZ_TRY_VAR_OR_RETURN( accessorSetterNode, synthesizeAccessor(propName, propNamePos, propAtom, privateStateName, isStatic, FunctionSyntaxKind::Setter, classInitializedMembers), false); if (addAccessorImmediately) { if (!handler_.addClassMemberDefinition(classMembers, accessorSetterNode)) { return false; } if (!handler_.isPrivateName(propName)) { accessorSetterNode = null(); } } // Step 10. Return ClassElementDefinition Record { [[Key]]: name, // [[Kind]]: accessor, [[Get]]: getter, [[Set]]: setter, // [[BackingStorageKey]]: privateStateName, [[Initializers]]: // initializers, [[Decorators]]: empty }. MOZ_TRY_VAR_OR_RETURN( propName, handler_.newPrivateName(privateStateName, pos()), false); propAtom = privateStateName; // We maintain `decorators` here to perform this step at the same time: // https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-static-semantics-classelementevaluation // 4. Set fieldDefinition.[[Decorators]] to decorators. } #endif if (isStatic) { classInitializedMembers.staticFields++; } else { classInitializedMembers.instanceFields++; #ifdef ENABLE_DECORATORS if (decorators) { classInitializedMembers.hasInstanceDecorators = true; } #endif } TokenPos propNamePos(propNameOffset, pos().end); FunctionNodeType initializer; MOZ_TRY_VAR_OR_RETURN( initializer, fieldInitializerOpt(propNamePos, propName, propAtom, classInitializedMembers, isStatic, hasHeritage), false); if (!matchOrInsertSemicolon(TokenStream::SlashIsInvalid)) { return false; } ClassFieldType field; MOZ_TRY_VAR_OR_RETURN(field, handler_.newClassFieldDefinition( propName, initializer, isStatic #ifdef ENABLE_DECORATORS , decorators, accessorGetterNode, accessorSetterNode #endif ), false); return handler_.addClassMemberDefinition(classMembers, field); } if (propType != PropertyType::Getter && propType != PropertyType::Setter && propType != PropertyType::Method && propType != PropertyType::GeneratorMethod && propType != PropertyType::AsyncMethod && propType != PropertyType::AsyncGeneratorMethod) { errorAt(propNameOffset, JSMSG_BAD_METHOD_DEF); return false; } bool isConstructor = !isStatic && propAtom == TaggedParserAtomIndex::WellKnown::constructor(); if (isConstructor) { if (propType != PropertyType::Method) { errorAt(propNameOffset, JSMSG_BAD_METHOD_DEF); return false; } if (classStmt.constructorBox) { errorAt(propNameOffset, JSMSG_DUPLICATE_PROPERTY, "constructor"); return false; } propType = hasHeritage == HasHeritage::Yes ? PropertyType::DerivedConstructor : PropertyType::Constructor; } else if (isStatic && propAtom == TaggedParserAtomIndex::WellKnown::prototype()) { errorAt(propNameOffset, JSMSG_BAD_METHOD_DEF); return false; } TaggedParserAtomIndex funName; switch (propType) { case PropertyType::Getter: case PropertyType::Setter: { bool hasStaticName = !anyChars.isCurrentTokenType(TokenKind::RightBracket) && propAtom; if (hasStaticName) { funName = prefixAccessorName(propType, propAtom); if (!funName) { return false; } } break; } case PropertyType::Constructor: case PropertyType::DerivedConstructor: funName = className; break; default: if (!anyChars.isCurrentTokenType(TokenKind::RightBracket)) { funName = propAtom; } } // When |super()| is invoked, we search for the nearest scope containing // |.initializers| to initialize the class fields. This set-up precludes // declaring |.initializers| in the class scope, because in some syntactic // contexts |super()| can appear nested in a class, while actually belonging // to an outer class definition. // // Example: // class Outer extends Base { // field = 1; // constructor() { // class Inner { // field = 2; // // // The super() call in the computed property name mustn't access // // Inner's |.initializers| array, but instead Outer's. // [super()]() {} // } // } // } Maybe dotInitializersScope; if (isConstructor && !options().selfHostingMode) { dotInitializersScope.emplace(this); if (!dotInitializersScope->init(pc_)) { return false; } if (!noteDeclaredName(TaggedParserAtomIndex::WellKnown::dot_initializers_(), DeclarationKind::Let, pos())) { return false; } #ifdef ENABLE_DECORATORS if (!noteDeclaredName( TaggedParserAtomIndex::WellKnown::dot_instanceExtraInitializers_(), DeclarationKind::Let, pos())) { return false; } #endif } // Calling toString on constructors need to return the source text for // the entire class. The end offset is unknown at this point in // parsing and will be amended when class parsing finishes below. FunctionNodeType funNode; MOZ_TRY_VAR_OR_RETURN( funNode, methodDefinition(isConstructor ? classStartOffset : propNameOffset, propType, funName), false); AccessorType atype = ToAccessorType(propType); Maybe initializerIfPrivate = Nothing(); if (handler_.isPrivateName(propName)) { if (propAtom == TaggedParserAtomIndex::WellKnown::hash_constructor_()) { // #constructor is an invalid private name. errorAt(propNameOffset, JSMSG_BAD_METHOD_DEF); return false; } TaggedParserAtomIndex privateName = propAtom; if (!noteDeclaredPrivateName( propName, privateName, propType, isStatic ? FieldPlacement::Static : FieldPlacement::Instance, pos())) { return false; } // Private non-static methods are stored in the class body environment. // Private non-static accessors are stamped onto every instance using // initializers. Private static methods are stamped onto the constructor // during class evaluation; see BytecodeEmitter::emitPropertyList. if (!isStatic) { if (atype == AccessorType::Getter || atype == AccessorType::Setter) { classInitializedMembers.privateAccessors++; TokenPos propNamePos(propNameOffset, pos().end); FunctionNodeType initializerNode; MOZ_TRY_VAR_OR_RETURN( initializerNode, synthesizePrivateMethodInitializer(propAtom, atype, propNamePos), false); initializerIfPrivate = Some(initializerNode); } else { MOZ_ASSERT(atype == AccessorType::None); classInitializedMembers.privateMethods++; } } } #ifdef ENABLE_DECORATORS if (decorators) { classInitializedMembers.hasInstanceDecorators = true; } #endif Node method; MOZ_TRY_VAR_OR_RETURN( method, handler_.newClassMethodDefinition(propName, funNode, atype, isStatic, initializerIfPrivate #ifdef ENABLE_DECORATORS , decorators #endif ), false); if (dotInitializersScope.isSome()) { MOZ_TRY_VAR_OR_RETURN( method, finishLexicalScope(*dotInitializersScope, method), false); dotInitializersScope.reset(); } return handler_.addClassMemberDefinition(classMembers, method); } template bool GeneralParser::finishClassConstructor( const ParseContext::ClassStatement& classStmt, TaggedParserAtomIndex className, HasHeritage hasHeritage, uint32_t classStartOffset, uint32_t classEndOffset, const ClassInitializedMembers& classInitializedMembers, ListNodeType& classMembers) { if (classStmt.constructorBox == nullptr) { MOZ_ASSERT(!options().selfHostingMode); // Unconditionally create the scope here, because it's always the // constructor. ParseContext::Scope dotInitializersScope(this); if (!dotInitializersScope.init(pc_)) { return false; } if (!noteDeclaredName(TaggedParserAtomIndex::WellKnown::dot_initializers_(), DeclarationKind::Let, pos())) { return false; } #ifdef ENABLE_DECORATORS if (!noteDeclaredName( TaggedParserAtomIndex::WellKnown::dot_instanceExtraInitializers_(), DeclarationKind::Let, pos(), ClosedOver::Yes)) { return false; } #endif // synthesizeConstructor assigns to classStmt.constructorBox TokenPos synthesizedBodyPos(classStartOffset, classEndOffset); FunctionNodeType synthesizedCtor; MOZ_TRY_VAR_OR_RETURN( synthesizedCtor, synthesizeConstructor(className, synthesizedBodyPos, hasHeritage), false); // Note: the *function* has the name of the class, but the *property* // containing the function has the name "constructor" Node constructorNameNode; MOZ_TRY_VAR_OR_RETURN( constructorNameNode, handler_.newObjectLiteralPropertyName( TaggedParserAtomIndex::WellKnown::constructor(), pos()), false); ClassMethodType method; MOZ_TRY_VAR_OR_RETURN(method, handler_.newDefaultClassConstructor( constructorNameNode, synthesizedCtor), false); LexicalScopeNodeType scope; MOZ_TRY_VAR_OR_RETURN( scope, finishLexicalScope(dotInitializersScope, method), false); if (!handler_.addClassMemberDefinition(classMembers, scope)) { return false; } } MOZ_ASSERT(classStmt.constructorBox); FunctionBox* ctorbox = classStmt.constructorBox; // Amend the toStringEnd offset for the constructor now that we've // finished parsing the class. ctorbox->setCtorToStringEnd(classEndOffset); size_t numMemberInitializers = classInitializedMembers.privateAccessors + classInitializedMembers.instanceFields; bool hasPrivateBrand = classInitializedMembers.hasPrivateBrand(); if (hasPrivateBrand || numMemberInitializers > 0) { // Now that we have full set of initializers, update the constructor. MemberInitializers initializers( hasPrivateBrand, #ifdef ENABLE_DECORATORS classInitializedMembers.hasInstanceDecorators, #endif numMemberInitializers); ctorbox->setMemberInitializers(initializers); // Field initialization need access to `this`. ctorbox->setCtorFunctionHasThisBinding(); } return true; } template typename ParseHandler::ClassNodeResult GeneralParser::classDefinition( YieldHandling yieldHandling, ClassContext classContext, DefaultHandling defaultHandling) { #ifdef ENABLE_DECORATORS MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::At) || anyChars.isCurrentTokenType(TokenKind::Class)); ListNodeType decorators = null(); FunctionNodeType addInitializerFunction = null(); if (anyChars.isCurrentTokenType(TokenKind::At)) { MOZ_TRY_VAR(decorators, decoratorList(yieldHandling)); TokenKind next; if (!tokenStream.getToken(&next)) { return errorResult(); } if (next != TokenKind::Class) { error(JSMSG_CLASS_EXPECTED); return errorResult(); } } #else MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Class)); #endif uint32_t classStartOffset = pos().begin; bool savedStrictness = setLocalStrictMode(true); // Classes are quite broken in self-hosted code. if (options().selfHostingMode) { error(JSMSG_SELFHOSTED_CLASS); return errorResult(); } TokenKind tt; if (!tokenStream.getToken(&tt)) { return errorResult(); } TaggedParserAtomIndex className; if (TokenKindIsPossibleIdentifier(tt)) { className = bindingIdentifier(yieldHandling); if (!className) { return errorResult(); } } else if (classContext == ClassStatement) { if (defaultHandling == AllowDefaultName) { className = TaggedParserAtomIndex::WellKnown::default_(); anyChars.ungetToken(); } else { // Class statements must have a bound name error(JSMSG_UNNAMED_CLASS_STMT); return errorResult(); } } else { // Make sure to put it back, whatever it was anyChars.ungetToken(); } // Because the binding definitions keep track of their blockId, we need to // create at least the inner binding later. Keep track of the name's // position in order to provide it for the nodes created later. TokenPos namePos = pos(); bool isInClass = pc_->sc()->inClass(); // Push a ParseContext::ClassStatement to keep track of the constructor // funbox. ParseContext::ClassStatement classStmt(pc_); NameNodeType innerName; Node nameNode = null(); Node classHeritage = null(); LexicalScopeNodeType classBlock = null(); ClassBodyScopeNodeType classBodyBlock = null(); uint32_t classEndOffset; { // A named class creates a new lexical scope with a const binding of the // class name for the "inner name". ParseContext::Statement innerScopeStmt(pc_, StatementKind::Block); ParseContext::Scope innerScope(this); if (!innerScope.init(pc_)) { return errorResult(); } bool hasHeritageBool; if (!tokenStream.matchToken(&hasHeritageBool, TokenKind::Extends)) { return errorResult(); } HasHeritage hasHeritage = hasHeritageBool ? HasHeritage::Yes : HasHeritage::No; if (hasHeritage == HasHeritage::Yes) { if (!tokenStream.getToken(&tt)) { return errorResult(); } MOZ_TRY_VAR(classHeritage, optionalExpr(yieldHandling, TripledotProhibited, tt)); } if (!mustMatchToken(TokenKind::LeftCurly, JSMSG_CURLY_BEFORE_CLASS)) { return errorResult(); } { ParseContext::Statement bodyScopeStmt(pc_, StatementKind::Block); ParseContext::Scope bodyScope(this); if (!bodyScope.init(pc_)) { return errorResult(); } ListNodeType classMembers; MOZ_TRY_VAR(classMembers, handler_.newClassMemberList(pos().begin)); ClassInitializedMembers classInitializedMembers{}; for (;;) { bool done; if (!classMember(yieldHandling, classStmt, className, classStartOffset, hasHeritage, classInitializedMembers, classMembers, &done)) { return errorResult(); } if (done) { break; } } #ifdef ENABLE_DECORATORS if (classInitializedMembers.hasInstanceDecorators) { MOZ_TRY_VAR(addInitializerFunction, synthesizeAddInitializerFunction( TaggedParserAtomIndex::WellKnown:: dot_instanceExtraInitializers_(), yieldHandling)); } #endif if (classInitializedMembers.privateMethods + classInitializedMembers.privateAccessors > 0) { // We declare `.privateBrand` as ClosedOver because the constructor // always uses it, even a default constructor. We could equivalently // `noteUsedName` when parsing the constructor, except that at that // time, we don't necessarily know if the class has a private brand. if (!noteDeclaredName( TaggedParserAtomIndex::WellKnown::dot_privateBrand_(), DeclarationKind::Synthetic, namePos, ClosedOver::Yes)) { return errorResult(); } } if (classInitializedMembers.instanceFieldKeys > 0) { if (!noteDeclaredName( TaggedParserAtomIndex::WellKnown::dot_fieldKeys_(), DeclarationKind::Synthetic, namePos)) { return errorResult(); } } if (classInitializedMembers.staticFields > 0) { if (!noteDeclaredName( TaggedParserAtomIndex::WellKnown::dot_staticInitializers_(), DeclarationKind::Synthetic, namePos)) { return errorResult(); } } if (classInitializedMembers.staticFieldKeys > 0) { if (!noteDeclaredName( TaggedParserAtomIndex::WellKnown::dot_staticFieldKeys_(), DeclarationKind::Synthetic, namePos)) { return errorResult(); } } classEndOffset = pos().end; if (!finishClassConstructor(classStmt, className, hasHeritage, classStartOffset, classEndOffset, classInitializedMembers, classMembers)) { return errorResult(); } MOZ_TRY_VAR(classBodyBlock, finishClassBodyScope(bodyScope, classMembers)); // Pop the class body scope } if (className) { // The inner name is immutable. if (!noteDeclaredName(className, DeclarationKind::Const, namePos)) { return errorResult(); } MOZ_TRY_VAR(innerName, newName(className, namePos)); } MOZ_TRY_VAR(classBlock, finishLexicalScope(innerScope, classBodyBlock)); // Pop the inner scope. } if (className) { NameNodeType outerName = null(); if (classContext == ClassStatement) { // The outer name is mutable. if (!noteDeclaredName(className, DeclarationKind::Class, namePos)) { return errorResult(); } MOZ_TRY_VAR(outerName, newName(className, namePos)); } MOZ_TRY_VAR(nameNode, handler_.newClassNames(outerName, innerName, namePos)); } MOZ_ALWAYS_TRUE(setLocalStrictMode(savedStrictness)); // We're leaving a class definition that was not itself nested within a class if (!isInClass) { mozilla::Maybe maybeUnboundName; if (!usedNames_.hasUnboundPrivateNames(fc_, maybeUnboundName)) { return errorResult(); } if (maybeUnboundName) { UniqueChars str = this->parserAtoms().toPrintableString(maybeUnboundName->atom); if (!str) { ReportOutOfMemory(this->fc_); return errorResult(); } errorAt(maybeUnboundName->position.begin, JSMSG_MISSING_PRIVATE_DECL, str.get()); return errorResult(); } } return handler_.newClass(nameNode, classHeritage, classBlock, #ifdef ENABLE_DECORATORS decorators, addInitializerFunction, #endif TokenPos(classStartOffset, classEndOffset)); } template typename ParseHandler::FunctionNodeResult GeneralParser::synthesizeConstructor( TaggedParserAtomIndex className, TokenPos synthesizedBodyPos, HasHeritage hasHeritage) { FunctionSyntaxKind functionSyntaxKind = hasHeritage == HasHeritage::Yes ? FunctionSyntaxKind::DerivedClassConstructor : FunctionSyntaxKind::ClassConstructor; bool isSelfHosting = options().selfHostingMode; FunctionFlags flags = InitialFunctionFlags(functionSyntaxKind, GeneratorKind::NotGenerator, FunctionAsyncKind::SyncFunction, isSelfHosting); // Create the top-level field initializer node. FunctionNodeType funNode; MOZ_TRY_VAR(funNode, handler_.newFunction(functionSyntaxKind, synthesizedBodyPos)); // If we see any inner function, note it on our current context. The bytecode // emitter may eliminate the function later, but we use a conservative // definition for consistency between lazy and full parsing. pc_->sc()->setHasInnerFunctions(); // When fully parsing a lazy script, we do not fully reparse its inner // functions, which are also lazy. Instead, their free variables and source // extents are recorded and may be skipped. if (handler_.reuseLazyInnerFunctions()) { if (!skipLazyInnerFunction(funNode, synthesizedBodyPos.begin, /* tryAnnexB = */ false)) { return errorResult(); } return funNode; } // Create the FunctionBox and link it to the function object. Directives directives(true); FunctionBox* funbox = newFunctionBox( funNode, className, flags, synthesizedBodyPos.begin, directives, GeneratorKind::NotGenerator, FunctionAsyncKind::SyncFunction); if (!funbox) { return errorResult(); } funbox->initWithEnclosingParseContext(pc_, functionSyntaxKind); setFunctionEndFromCurrentToken(funbox); // Mark this function as being synthesized by the parser. This means special // handling in delazification will be used since we don't have typical // function syntax. funbox->setSyntheticFunction(); // Push a SourceParseContext on to the stack. ParseContext* outerpc = pc_; SourceParseContext funpc(this, funbox, /* newDirectives = */ nullptr); if (!funpc.init()) { return errorResult(); } if (!synthesizeConstructorBody(synthesizedBodyPos, hasHeritage, funNode, funbox)) { return errorResult(); } if (!leaveInnerFunction(outerpc)) { return errorResult(); } return funNode; } template bool GeneralParser::synthesizeConstructorBody( TokenPos synthesizedBodyPos, HasHeritage hasHeritage, FunctionNodeType funNode, FunctionBox* funbox) { MOZ_ASSERT(funbox->isClassConstructor()); // Create a ParamsBodyNode for the parameters + body (there are no // parameters). ParamsBodyNodeType argsbody; MOZ_TRY_VAR_OR_RETURN(argsbody, handler_.newParamsBody(synthesizedBodyPos), false); handler_.setFunctionFormalParametersAndBody(funNode, argsbody); setFunctionStartAtPosition(funbox, synthesizedBodyPos); if (hasHeritage == HasHeritage::Yes) { // Synthesize the equivalent to `function f(...args)` funbox->setHasRest(); if (!notePositionalFormalParameter( funNode, TaggedParserAtomIndex::WellKnown::dot_args_(), synthesizedBodyPos.begin, /* disallowDuplicateParams = */ false, /* duplicatedParam = */ nullptr)) { return false; } funbox->setArgCount(1); } else { funbox->setArgCount(0); } pc_->functionScope().useAsVarScope(pc_); ListNodeType stmtList; MOZ_TRY_VAR_OR_RETURN(stmtList, handler_.newStatementList(synthesizedBodyPos), false); if (!noteUsedName(TaggedParserAtomIndex::WellKnown::dot_this_())) { return false; } if (!noteUsedName(TaggedParserAtomIndex::WellKnown::dot_initializers_())) { return false; } #ifdef ENABLE_DECORATORS if (!noteUsedName( TaggedParserAtomIndex::WellKnown::dot_instanceExtraInitializers_())) { return false; } #endif if (hasHeritage == HasHeritage::Yes) { // |super()| implicitly reads |new.target|. if (!noteUsedName(TaggedParserAtomIndex::WellKnown::dot_newTarget_())) { return false; } NameNodeType thisName; MOZ_TRY_VAR_OR_RETURN(thisName, newThisName(), false); UnaryNodeType superBase; MOZ_TRY_VAR_OR_RETURN( superBase, handler_.newSuperBase(thisName, synthesizedBodyPos), false); ListNodeType arguments; MOZ_TRY_VAR_OR_RETURN(arguments, handler_.newArguments(synthesizedBodyPos), false); NameNodeType argsNameNode; MOZ_TRY_VAR_OR_RETURN(argsNameNode, newName(TaggedParserAtomIndex::WellKnown::dot_args_(), synthesizedBodyPos), false); if (!noteUsedName(TaggedParserAtomIndex::WellKnown::dot_args_())) { return false; } UnaryNodeType spreadArgs; MOZ_TRY_VAR_OR_RETURN( spreadArgs, handler_.newSpread(synthesizedBodyPos.begin, argsNameNode), false); handler_.addList(arguments, spreadArgs); CallNodeType superCall; MOZ_TRY_VAR_OR_RETURN( superCall, handler_.newSuperCall(superBase, arguments, /* isSpread = */ true), false); BinaryNodeType setThis; MOZ_TRY_VAR_OR_RETURN(setThis, handler_.newSetThis(thisName, superCall), false); UnaryNodeType exprStatement; MOZ_TRY_VAR_OR_RETURN( exprStatement, handler_.newExprStatement(setThis, synthesizedBodyPos.end), false); handler_.addStatementToList(stmtList, exprStatement); } bool canSkipLazyClosedOverBindings = handler_.reuseClosedOverBindings(); if (!pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings)) { return false; } if (!pc_->declareNewTarget(usedNames_, canSkipLazyClosedOverBindings)) { return false; } LexicalScopeNodeType initializerBody; MOZ_TRY_VAR_OR_RETURN( initializerBody, finishLexicalScope(pc_->varScope(), stmtList, ScopeKind::FunctionLexical), false); handler_.setBeginPosition(initializerBody, stmtList); handler_.setEndPosition(initializerBody, stmtList); handler_.setFunctionBody(funNode, initializerBody); return finishFunction(); } template typename ParseHandler::FunctionNodeResult GeneralParser::privateMethodInitializer( TokenPos propNamePos, TaggedParserAtomIndex propAtom, TaggedParserAtomIndex storedMethodAtom) { if (!abortIfSyntaxParser()) { return errorResult(); } // Synthesize an initializer function that the constructor can use to stamp a // private method onto an instance object. FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::FieldInitializer; FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction; GeneratorKind generatorKind = GeneratorKind::NotGenerator; bool isSelfHosting = options().selfHostingMode; FunctionFlags flags = InitialFunctionFlags(syntaxKind, generatorKind, asyncKind, isSelfHosting); FunctionNodeType funNode; MOZ_TRY_VAR(funNode, handler_.newFunction(syntaxKind, propNamePos)); Directives directives(true); FunctionBox* funbox = newFunctionBox(funNode, TaggedParserAtomIndex::null(), flags, propNamePos.begin, directives, generatorKind, asyncKind); if (!funbox) { return errorResult(); } funbox->initWithEnclosingParseContext(pc_, syntaxKind); // Push a SourceParseContext on to the stack. ParseContext* outerpc = pc_; SourceParseContext funpc(this, funbox, /* newDirectives = */ nullptr); if (!funpc.init()) { return errorResult(); } pc_->functionScope().useAsVarScope(pc_); // Add empty parameter list. ParamsBodyNodeType argsbody; MOZ_TRY_VAR(argsbody, handler_.newParamsBody(propNamePos)); handler_.setFunctionFormalParametersAndBody(funNode, argsbody); setFunctionStartAtCurrentToken(funbox); funbox->setArgCount(0); // Note both the stored private method body and it's private name as being // used in the initializer. They will be emitted into the method body in the // BCE. if (!noteUsedName(storedMethodAtom)) { return errorResult(); } MOZ_TRY(privateNameReference(propAtom)); // Unlike field initializers, private method initializers are not created with // a body of synthesized AST nodes. Instead, the body is left empty and the // initializer is synthesized at the bytecode level. // See BytecodeEmitter::emitPrivateMethodInitializer. ListNodeType stmtList; MOZ_TRY_VAR(stmtList, handler_.newStatementList(propNamePos)); bool canSkipLazyClosedOverBindings = handler_.reuseClosedOverBindings(); if (!pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings)) { return errorResult(); } if (!pc_->declareNewTarget(usedNames_, canSkipLazyClosedOverBindings)) { return errorResult(); } LexicalScopeNodeType initializerBody; MOZ_TRY_VAR(initializerBody, finishLexicalScope(pc_->varScope(), stmtList, ScopeKind::FunctionLexical)); handler_.setBeginPosition(initializerBody, stmtList); handler_.setEndPosition(initializerBody, stmtList); handler_.setFunctionBody(funNode, initializerBody); // Set field-initializer lambda boundary to start at property name and end // after method body. setFunctionStartAtPosition(funbox, propNamePos); setFunctionEndFromCurrentToken(funbox); if (!finishFunction()) { return errorResult(); } if (!leaveInnerFunction(outerpc)) { return errorResult(); } return funNode; } template typename ParseHandler::FunctionNodeResult GeneralParser::staticClassBlock( ClassInitializedMembers& classInitializedMembers) { // Both for getting-this-done, and because this will invariably be executed, // syntax parsing should be aborted. if (!abortIfSyntaxParser()) { return errorResult(); } FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::StaticClassBlock; FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction; GeneratorKind generatorKind = GeneratorKind::NotGenerator; bool isSelfHosting = options().selfHostingMode; FunctionFlags flags = InitialFunctionFlags(syntaxKind, generatorKind, asyncKind, isSelfHosting); AutoAwaitIsKeyword awaitIsKeyword(this, AwaitHandling::AwaitIsDisallowed); // Create the function node for the static class body. FunctionNodeType funNode; MOZ_TRY_VAR(funNode, handler_.newFunction(syntaxKind, pos())); // Create the FunctionBox and link it to the function object. Directives directives(true); FunctionBox* funbox = newFunctionBox(funNode, TaggedParserAtomIndex::null(), flags, pos().begin, directives, generatorKind, asyncKind); if (!funbox) { return errorResult(); } funbox->initWithEnclosingParseContext(pc_, syntaxKind); MOZ_ASSERT(funbox->isSyntheticFunction()); MOZ_ASSERT(!funbox->allowSuperCall()); MOZ_ASSERT(!funbox->allowArguments()); MOZ_ASSERT(!funbox->allowReturn()); // Set start at `static` token. MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Static)); setFunctionStartAtCurrentToken(funbox); // Push a SourceParseContext on to the stack. ParseContext* outerpc = pc_; SourceParseContext funpc(this, funbox, /* newDirectives = */ nullptr); if (!funpc.init()) { return errorResult(); } pc_->functionScope().useAsVarScope(pc_); uint32_t start = pos().begin; tokenStream.consumeKnownToken(TokenKind::LeftCurly); // Static class blocks are code-generated as if they were static field // initializers, so we bump the staticFields count here, which ensures // .staticInitializers is noted as used. classInitializedMembers.staticFields++; LexicalScopeNodeType body; MOZ_TRY_VAR(body, functionBody(InHandling::InAllowed, YieldHandling::YieldIsKeyword, syntaxKind, FunctionBodyType::StatementListBody)); if (anyChars.isEOF()) { error(JSMSG_UNTERMINATED_STATIC_CLASS_BLOCK); return errorResult(); } tokenStream.consumeKnownToken(TokenKind::RightCurly, TokenStream::Modifier::SlashIsRegExp); TokenPos wholeBodyPos(start, pos().end); handler_.setEndPosition(funNode, wholeBodyPos.end); setFunctionEndFromCurrentToken(funbox); // Create a ParamsBodyNode for the parameters + body (there are no // parameters). ParamsBodyNodeType argsbody; MOZ_TRY_VAR(argsbody, handler_.newParamsBody(wholeBodyPos)); handler_.setFunctionFormalParametersAndBody(funNode, argsbody); funbox->setArgCount(0); if (pc_->superScopeNeedsHomeObject()) { funbox->setNeedsHomeObject(); } handler_.setEndPosition(body, pos().begin); handler_.setEndPosition(funNode, pos().end); handler_.setFunctionBody(funNode, body); if (!finishFunction()) { return errorResult(); } if (!leaveInnerFunction(outerpc)) { return errorResult(); } return funNode; } template typename ParseHandler::FunctionNodeResult GeneralParser::fieldInitializerOpt( TokenPos propNamePos, Node propName, TaggedParserAtomIndex propAtom, ClassInitializedMembers& classInitializedMembers, bool isStatic, HasHeritage hasHeritage) { if (!abortIfSyntaxParser()) { return errorResult(); } bool hasInitializer = false; if (!tokenStream.matchToken(&hasInitializer, TokenKind::Assign, TokenStream::SlashIsDiv)) { return errorResult(); } FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::FieldInitializer; FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction; GeneratorKind generatorKind = GeneratorKind::NotGenerator; bool isSelfHosting = options().selfHostingMode; FunctionFlags flags = InitialFunctionFlags(syntaxKind, generatorKind, asyncKind, isSelfHosting); // Create the top-level field initializer node. FunctionNodeType funNode; MOZ_TRY_VAR(funNode, handler_.newFunction(syntaxKind, propNamePos)); // Create the FunctionBox and link it to the function object. Directives directives(true); FunctionBox* funbox = newFunctionBox(funNode, TaggedParserAtomIndex::null(), flags, propNamePos.begin, directives, generatorKind, asyncKind); if (!funbox) { return errorResult(); } funbox->initWithEnclosingParseContext(pc_, syntaxKind); MOZ_ASSERT(funbox->isSyntheticFunction()); // We can't use setFunctionStartAtCurrentToken because that uses pos().begin, // which is incorrect for fields without initializers (pos() points to the // field identifier) setFunctionStartAtPosition(funbox, propNamePos); // Push a SourceParseContext on to the stack. ParseContext* outerpc = pc_; SourceParseContext funpc(this, funbox, /* newDirectives = */ nullptr); if (!funpc.init()) { return errorResult(); } pc_->functionScope().useAsVarScope(pc_); Node initializerExpr; if (hasInitializer) { // Parse the expression for the field initializer. { AutoAwaitIsKeyword awaitHandling(this, AwaitIsName); MOZ_TRY_VAR(initializerExpr, assignExpr(InAllowed, YieldIsName, TripledotProhibited)); } handler_.checkAndSetIsDirectRHSAnonFunction(initializerExpr); } else { MOZ_TRY_VAR(initializerExpr, handler_.newRawUndefinedLiteral(propNamePos)); } TokenPos wholeInitializerPos(propNamePos.begin, pos().end); // Update the end position of the parse node. handler_.setEndPosition(funNode, wholeInitializerPos.end); setFunctionEndFromCurrentToken(funbox); // Create a ParamsBodyNode for the parameters + body (there are no // parameters). ParamsBodyNodeType argsbody; MOZ_TRY_VAR(argsbody, handler_.newParamsBody(wholeInitializerPos)); handler_.setFunctionFormalParametersAndBody(funNode, argsbody); funbox->setArgCount(0); NameNodeType thisName; MOZ_TRY_VAR(thisName, newThisName()); // Build `this.field` expression. ThisLiteralType propAssignThis; MOZ_TRY_VAR(propAssignThis, handler_.newThisLiteral(wholeInitializerPos, thisName)); Node propAssignFieldAccess; uint32_t indexValue; if (!propAtom) { // See BytecodeEmitter::emitCreateFieldKeys for an explanation of what // .fieldKeys means and its purpose. NameNodeType fieldKeysName; if (isStatic) { MOZ_TRY_VAR( fieldKeysName, newInternalDotName( TaggedParserAtomIndex::WellKnown::dot_staticFieldKeys_())); } else { MOZ_TRY_VAR(fieldKeysName, newInternalDotName( TaggedParserAtomIndex::WellKnown::dot_fieldKeys_())); } if (!fieldKeysName) { return errorResult(); } double fieldKeyIndex; if (isStatic) { fieldKeyIndex = classInitializedMembers.staticFieldKeys++; } else { fieldKeyIndex = classInitializedMembers.instanceFieldKeys++; } Node fieldKeyIndexNode; MOZ_TRY_VAR(fieldKeyIndexNode, handler_.newNumber(fieldKeyIndex, DecimalPoint::NoDecimal, wholeInitializerPos)); Node fieldKeyValue; MOZ_TRY_VAR(fieldKeyValue, handler_.newPropertyByValue(fieldKeysName, fieldKeyIndexNode, wholeInitializerPos.end)); MOZ_TRY_VAR(propAssignFieldAccess, handler_.newPropertyByValue(propAssignThis, fieldKeyValue, wholeInitializerPos.end)); } else if (handler_.isPrivateName(propName)) { // It would be nice if we could tweak this here such that only if // HasHeritage::Yes we end up emitting CheckPrivateField, but otherwise we // emit InitElem -- this is an optimization to minimize HasOwn checks // in InitElem for classes without heritage. // // Further tweaking would be to ultimately only do CheckPrivateField for the // -first- field in a derived class, which would suffice to match the // semantic check. NameNodeType privateNameNode; MOZ_TRY_VAR(privateNameNode, privateNameReference(propAtom)); MOZ_TRY_VAR(propAssignFieldAccess, handler_.newPrivateMemberAccess(propAssignThis, privateNameNode, wholeInitializerPos.end)); } else if (this->parserAtoms().isIndex(propAtom, &indexValue)) { MOZ_TRY_VAR(propAssignFieldAccess, handler_.newPropertyByValue(propAssignThis, propName, wholeInitializerPos.end)); } else { NameNodeType propAssignName; MOZ_TRY_VAR(propAssignName, handler_.newPropertyName(propAtom, wholeInitializerPos)); MOZ_TRY_VAR(propAssignFieldAccess, handler_.newPropertyAccess(propAssignThis, propAssignName)); } // Synthesize an property init. BinaryNodeType initializerPropInit; MOZ_TRY_VAR(initializerPropInit, handler_.newInitExpr(propAssignFieldAccess, initializerExpr)); UnaryNodeType exprStatement; MOZ_TRY_VAR(exprStatement, handler_.newExprStatement( initializerPropInit, wholeInitializerPos.end)); ListNodeType statementList; MOZ_TRY_VAR(statementList, handler_.newStatementList(wholeInitializerPos)); handler_.addStatementToList(statementList, exprStatement); bool canSkipLazyClosedOverBindings = handler_.reuseClosedOverBindings(); if (!pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings)) { return errorResult(); } if (!pc_->declareNewTarget(usedNames_, canSkipLazyClosedOverBindings)) { return errorResult(); } // Set the function's body to the field assignment. LexicalScopeNodeType initializerBody; MOZ_TRY_VAR(initializerBody, finishLexicalScope(pc_->varScope(), statementList, ScopeKind::FunctionLexical)); handler_.setFunctionBody(funNode, initializerBody); if (pc_->superScopeNeedsHomeObject()) { funbox->setNeedsHomeObject(); } if (!finishFunction()) { return errorResult(); } if (!leaveInnerFunction(outerpc)) { return errorResult(); } return funNode; } template typename ParseHandler::FunctionNodeResult GeneralParser::synthesizePrivateMethodInitializer( TaggedParserAtomIndex propAtom, AccessorType accessorType, TokenPos propNamePos) { if (!abortIfSyntaxParser()) { return errorResult(); } // Synthesize a name for the lexical variable that will store the // accessor body. StringBuffer storedMethodName(fc_); if (!storedMethodName.append(this->parserAtoms(), propAtom)) { return errorResult(); } if (!storedMethodName.append( accessorType == AccessorType::Getter ? ".getter" : ".setter")) { return errorResult(); } auto storedMethodProp = storedMethodName.finishParserAtom(this->parserAtoms(), fc_); if (!storedMethodProp) { return errorResult(); } if (!noteDeclaredName(storedMethodProp, DeclarationKind::Synthetic, pos())) { return errorResult(); } return privateMethodInitializer(propNamePos, propAtom, storedMethodProp); } #ifdef ENABLE_DECORATORS template typename ParseHandler::FunctionNodeResult GeneralParser::synthesizeAddInitializerFunction( TaggedParserAtomIndex initializers, YieldHandling yieldHandling) { if (!abortIfSyntaxParser()) { return errorResult(); } // TODO: Add support for static and class extra initializers, see bug 1868220 // and bug 1868221. MOZ_ASSERT( initializers == TaggedParserAtomIndex::WellKnown::dot_instanceExtraInitializers_()); TokenPos propNamePos = pos(); // Synthesize an addInitializer function that can be used to append to // .initializers FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::Statement; FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction; GeneratorKind generatorKind = GeneratorKind::NotGenerator; bool isSelfHosting = options().selfHostingMode; FunctionFlags flags = InitialFunctionFlags(syntaxKind, generatorKind, asyncKind, isSelfHosting); FunctionNodeType funNode; MOZ_TRY_VAR(funNode, handler_.newFunction(syntaxKind, propNamePos)); Directives directives(true); FunctionBox* funbox = newFunctionBox(funNode, TaggedParserAtomIndex::null(), flags, propNamePos.begin, directives, generatorKind, asyncKind); if (!funbox) { return errorResult(); } funbox->initWithEnclosingParseContext(pc_, syntaxKind); ParseContext* outerpc = pc_; SourceParseContext funpc(this, funbox, /* newDirectives = */ nullptr); if (!funpc.init()) { return errorResult(); } pc_->functionScope().useAsVarScope(pc_); // Takes a single parameter, `initializer`. ParamsBodyNodeType params; MOZ_TRY_VAR(params, handler_.newParamsBody(propNamePos)); handler_.setFunctionFormalParametersAndBody(funNode, params); constexpr bool disallowDuplicateParams = true; bool duplicatedParam = false; if (!notePositionalFormalParameter( funNode, TaggedParserAtomIndex::WellKnown::initializer(), pos().begin, disallowDuplicateParams, &duplicatedParam)) { return null(); } MOZ_ASSERT(!duplicatedParam); MOZ_ASSERT(pc_->positionalFormalParameterNames().length() == 1); funbox->setLength(1); funbox->setArgCount(1); setFunctionStartAtCurrentToken(funbox); // Like private method initializers, the addInitializer method is not created // with a body of synthesized AST nodes. Instead, the body is left empty and // the initializer is synthesized at the bytecode level. See // DecoratorEmitter::emitCreateAddInitializerFunction. ListNodeType stmtList; MOZ_TRY_VAR(stmtList, handler_.newStatementList(propNamePos)); if (!noteUsedName(initializers)) { return null(); } bool canSkipLazyClosedOverBindings = handler_.reuseClosedOverBindings(); if (!pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings)) { return null(); } if (!pc_->declareNewTarget(usedNames_, canSkipLazyClosedOverBindings)) { return null(); } LexicalScopeNodeType addInitializerBody; MOZ_TRY_VAR(addInitializerBody, finishLexicalScope(pc_->varScope(), stmtList, ScopeKind::FunctionLexical)); handler_.setBeginPosition(addInitializerBody, stmtList); handler_.setEndPosition(addInitializerBody, stmtList); handler_.setFunctionBody(funNode, addInitializerBody); // Set field-initializer lambda boundary to start at property name and end // after method body. setFunctionStartAtPosition(funbox, propNamePos); setFunctionEndFromCurrentToken(funbox); if (!finishFunction()) { return errorResult(); } if (!leaveInnerFunction(outerpc)) { return errorResult(); } return funNode; } template typename ParseHandler::ClassMethodResult GeneralParser::synthesizeAccessor( Node propName, TokenPos propNamePos, TaggedParserAtomIndex propAtom, TaggedParserAtomIndex privateStateNameAtom, bool isStatic, FunctionSyntaxKind syntaxKind, ClassInitializedMembers& classInitializedMembers) { // Decorators Proposal // https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-makeautoaccessorgetter // The abstract operation MakeAutoAccessorGetter takes arguments homeObject // (an Object), name (a property key or Private Name), and privateStateName (a // Private Name) and returns a function object. // // https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-makeautoaccessorsetter // The abstract operation MakeAutoAccessorSetter takes arguments homeObject // (an Object), name (a property key or Private Name), and privateStateName (a // Private Name) and returns a function object. if (!abortIfSyntaxParser()) { return errorResult(); } AccessorType accessorType = syntaxKind == FunctionSyntaxKind::Getter ? AccessorType::Getter : AccessorType::Setter; mozilla::Maybe initializerIfPrivate = Nothing(); if (!isStatic && handler_.isPrivateName(propName)) { classInitializedMembers.privateAccessors++; FunctionNodeType initializerNode; MOZ_TRY_VAR(initializerNode, synthesizePrivateMethodInitializer( propAtom, accessorType, propNamePos)); initializerIfPrivate = Some(initializerNode); handler_.setPrivateNameKind(propName, PrivateNameKind::GetterSetter); } // https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-makeautoaccessorgetter // 2. Let getter be CreateBuiltinFunction(getterClosure, 0, "get", « »). // // https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-makeautoaccessorsetter // 2. Let setter be CreateBuiltinFunction(setterClosure, 1, "set", « »). StringBuffer storedMethodName(fc_); if (!storedMethodName.append(accessorType == AccessorType::Getter ? "get" : "set")) { return errorResult(); } TaggedParserAtomIndex funNameAtom = storedMethodName.finishParserAtom(this->parserAtoms(), fc_); FunctionNodeType funNode; MOZ_TRY_VAR(funNode, synthesizeAccessorBody(funNameAtom, propNamePos, privateStateNameAtom, syntaxKind)); // https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-makeautoaccessorgetter // 3. Perform MakeMethod(getter, homeObject). // 4. Return getter. // // https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-makeautoaccessorsetter // 3. Perform MakeMethod(setter, homeObject). // 4. Return setter. return handler_.newClassMethodDefinition( propName, funNode, accessorType, isStatic, initializerIfPrivate, null()); } template typename ParseHandler::FunctionNodeResult GeneralParser::synthesizeAccessorBody( TaggedParserAtomIndex funNameAtom, TokenPos propNamePos, TaggedParserAtomIndex propNameAtom, FunctionSyntaxKind syntaxKind) { if (!abortIfSyntaxParser()) { return errorResult(); } FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction; GeneratorKind generatorKind = GeneratorKind::NotGenerator; bool isSelfHosting = options().selfHostingMode; FunctionFlags flags = InitialFunctionFlags(syntaxKind, generatorKind, asyncKind, isSelfHosting); // Create the top-level function node. FunctionNodeType funNode; MOZ_TRY_VAR(funNode, handler_.newFunction(syntaxKind, propNamePos)); // Create the FunctionBox and link it to the function object. Directives directives(true); FunctionBox* funbox = newFunctionBox(funNode, funNameAtom, flags, propNamePos.begin, directives, generatorKind, asyncKind); if (!funbox) { return errorResult(); } funbox->initWithEnclosingParseContext(pc_, syntaxKind); funbox->setSyntheticFunction(); // Push a SourceParseContext on to the stack. ParseContext* outerpc = pc_; SourceParseContext funpc(this, funbox, /* newDirectives = */ nullptr); if (!funpc.init()) { return errorResult(); } pc_->functionScope().useAsVarScope(pc_); // The function we synthesize is located at the field with the // accessor. setFunctionStartAtCurrentToken(funbox); setFunctionEndFromCurrentToken(funbox); // Create a ListNode for the parameters + body ParamsBodyNodeType paramsbody; MOZ_TRY_VAR(paramsbody, handler_.newParamsBody(propNamePos)); handler_.setFunctionFormalParametersAndBody(funNode, paramsbody); if (syntaxKind == FunctionSyntaxKind::Getter) { funbox->setArgCount(0); } else { funbox->setArgCount(1); } // Build `this` expression to access the privateStateName for use in the // operations to create the getter and setter below. NameNodeType thisName; MOZ_TRY_VAR(thisName, newThisName()); ThisLiteralType propThis; MOZ_TRY_VAR(propThis, handler_.newThisLiteral(propNamePos, thisName)); NameNodeType privateNameNode; MOZ_TRY_VAR(privateNameNode, privateNameReference(propNameAtom)); Node propFieldAccess; MOZ_TRY_VAR(propFieldAccess, handler_.newPrivateMemberAccess( propThis, privateNameNode, propNamePos.end)); Node accessorBody; if (syntaxKind == FunctionSyntaxKind::Getter) { // Decorators Proposal // https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-makeautoaccessorgetter // 1. Let getterClosure be a new Abstract Closure with no parameters that // captures privateStateName and performs the following steps when called: // 1.a. Let o be the this value. // 1.b. Return ? PrivateGet(privateStateName, o). MOZ_TRY_VAR(accessorBody, handler_.newReturnStatement(propFieldAccess, propNamePos)); } else { // Decorators Proposal // https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-makeautoaccessorsetter // The abstract operation MakeAutoAccessorSetter takes arguments homeObject // (an Object), name (a property key or Private Name), and privateStateName // (a Private Name) and returns a function object. // 1. Let setterClosure be a new Abstract Closure with parameters (value) // that captures privateStateName and performs the following steps when // called: // 1.a. Let o be the this value. notePositionalFormalParameter(funNode, TaggedParserAtomIndex::WellKnown::value(), /* pos = */ 0, false, /* duplicatedParam = */ nullptr); Node initializerExpr; MOZ_TRY_VAR(initializerExpr, handler_.newName(TaggedParserAtomIndex::WellKnown::value(), propNamePos)); // 1.b. Perform ? PrivateSet(privateStateName, o, value). Node assignment; MOZ_TRY_VAR(assignment, handler_.newAssignment(ParseNodeKind::AssignExpr, propFieldAccess, initializerExpr)); MOZ_TRY_VAR(accessorBody, handler_.newExprStatement(assignment, propNamePos.end)); // 1.c. Return undefined. } ListNodeType statementList; MOZ_TRY_VAR(statementList, handler_.newStatementList(propNamePos)); handler_.addStatementToList(statementList, accessorBody); bool canSkipLazyClosedOverBindings = handler_.reuseClosedOverBindings(); if (!pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings)) { return errorResult(); } if (!pc_->declareNewTarget(usedNames_, canSkipLazyClosedOverBindings)) { return errorResult(); } LexicalScopeNodeType initializerBody; MOZ_TRY_VAR(initializerBody, finishLexicalScope(pc_->varScope(), statementList, ScopeKind::FunctionLexical)); handler_.setFunctionBody(funNode, initializerBody); if (pc_->superScopeNeedsHomeObject()) { funbox->setNeedsHomeObject(); } if (!finishFunction()) { return errorResult(); } if (!leaveInnerFunction(outerpc)) { return errorResult(); } return funNode; } #endif bool ParserBase::nextTokenContinuesLetDeclaration(TokenKind next) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Let)); MOZ_ASSERT(anyChars.nextToken().type == next); TokenStreamShared::verifyConsistentModifier(TokenStreamShared::SlashIsDiv, anyChars.nextToken()); // Destructuring continues a let declaration. if (next == TokenKind::LeftBracket || next == TokenKind::LeftCurly) { return true; } // A "let" edge case deserves special comment. Consider this: // // let // not an ASI opportunity // let; // // Static semantics in §13.3.1.1 turn a LexicalDeclaration that binds // "let" into an early error. Does this retroactively permit ASI so // that we should parse this as two ExpressionStatements? No. ASI // resolves during parsing. Static semantics only apply to the full // parse tree with ASI applied. No backsies! // Otherwise a let declaration must have a name. return TokenKindIsPossibleIdentifier(next); } template typename ParseHandler::DeclarationListNodeResult GeneralParser::variableStatement( YieldHandling yieldHandling) { DeclarationListNodeType vars; MOZ_TRY_VAR(vars, declarationList(yieldHandling, ParseNodeKind::VarStmt)); if (!matchOrInsertSemicolon()) { return errorResult(); } return vars; } template typename ParseHandler::NodeResult GeneralParser::statement( YieldHandling yieldHandling) { MOZ_ASSERT(checkOptionsCalled_); AutoCheckRecursionLimit recursion(this->fc_); if (!recursion.check(this->fc_)) { return errorResult(); } TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } switch (tt) { // BlockStatement[?Yield, ?Return] case TokenKind::LeftCurly: return blockStatement(yieldHandling); // VariableStatement[?Yield] case TokenKind::Var: return variableStatement(yieldHandling); // EmptyStatement case TokenKind::Semi: return handler_.newEmptyStatement(pos()); // ExpressionStatement[?Yield]. case TokenKind::Yield: { // Don't use a ternary operator here due to obscure linker issues // around using static consts in the arms of a ternary. Modifier modifier; if (yieldExpressionsSupported()) { modifier = TokenStream::SlashIsRegExp; } else { modifier = TokenStream::SlashIsDiv; } TokenKind next; if (!tokenStream.peekToken(&next, modifier)) { return errorResult(); } if (next == TokenKind::Colon) { return labeledStatement(yieldHandling); } return expressionStatement(yieldHandling); } default: { // If we encounter an await in a module, and the module is not marked // as async, mark the module as async. if (tt == TokenKind::Await && !pc_->isAsync()) { if (pc_->atModuleTopLevel()) { if (!options().topLevelAwait) { error(JSMSG_TOP_LEVEL_AWAIT_NOT_SUPPORTED); return errorResult(); } pc_->sc()->asModuleContext()->setIsAsync(); MOZ_ASSERT(pc_->isAsync()); } } // Avoid getting next token with SlashIsDiv. if (tt == TokenKind::Await && pc_->isAsync()) { return expressionStatement(yieldHandling); } if (!TokenKindIsPossibleIdentifier(tt)) { return expressionStatement(yieldHandling); } TokenKind next; if (!tokenStream.peekToken(&next)) { return errorResult(); } // |let| here can only be an Identifier, not a declaration. Give nicer // errors for declaration-looking typos. if (tt == TokenKind::Let) { bool forbiddenLetDeclaration = false; if (next == TokenKind::LeftBracket) { // Enforce ExpressionStatement's 'let [' lookahead restriction. forbiddenLetDeclaration = true; } else if (next == TokenKind::LeftCurly || TokenKindIsPossibleIdentifier(next)) { // 'let {' and 'let foo' aren't completely forbidden, if ASI // causes 'let' to be the entire Statement. But if they're // same-line, we can aggressively give a better error message. // // Note that this ignores 'yield' as TokenKind::Yield: we'll handle it // correctly but with a worse error message. TokenKind nextSameLine; if (!tokenStream.peekTokenSameLine(&nextSameLine)) { return errorResult(); } MOZ_ASSERT(TokenKindIsPossibleIdentifier(nextSameLine) || nextSameLine == TokenKind::LeftCurly || nextSameLine == TokenKind::Eol); forbiddenLetDeclaration = nextSameLine != TokenKind::Eol; } if (forbiddenLetDeclaration) { error(JSMSG_FORBIDDEN_AS_STATEMENT, "lexical declarations"); return errorResult(); } } else if (tt == TokenKind::Async) { // Peek only on the same line: ExpressionStatement's lookahead // restriction is phrased as // // [lookahead ∉ { '{', // function, // async [no LineTerminator here] function, // class, // let '[' }] // // meaning that code like this is valid: // // if (true) // async // ASI opportunity // function clownshoes() {} TokenKind maybeFunction; if (!tokenStream.peekTokenSameLine(&maybeFunction)) { return errorResult(); } if (maybeFunction == TokenKind::Function) { error(JSMSG_FORBIDDEN_AS_STATEMENT, "async function declarations"); return errorResult(); } // Otherwise this |async| begins an ExpressionStatement or is a // label name. } // NOTE: It's unfortunately allowed to have a label named 'let' in // non-strict code. 💯 if (next == TokenKind::Colon) { return labeledStatement(yieldHandling); } return expressionStatement(yieldHandling); } case TokenKind::New: return expressionStatement(yieldHandling, PredictInvoked); // IfStatement[?Yield, ?Return] case TokenKind::If: return ifStatement(yieldHandling); // BreakableStatement[?Yield, ?Return] // // BreakableStatement[Yield, Return]: // IterationStatement[?Yield, ?Return] // SwitchStatement[?Yield, ?Return] case TokenKind::Do: return doWhileStatement(yieldHandling); case TokenKind::While: return whileStatement(yieldHandling); case TokenKind::For: return forStatement(yieldHandling); case TokenKind::Switch: return switchStatement(yieldHandling); // ContinueStatement[?Yield] case TokenKind::Continue: return continueStatement(yieldHandling); // BreakStatement[?Yield] case TokenKind::Break: return breakStatement(yieldHandling); // [+Return] ReturnStatement[?Yield] case TokenKind::Return: // The Return parameter is only used here, and the effect is easily // detected this way, so don't bother passing around an extra parameter // everywhere. if (!pc_->allowReturn()) { error(JSMSG_BAD_RETURN_OR_YIELD, "return"); return errorResult(); } return returnStatement(yieldHandling); // WithStatement[?Yield, ?Return] case TokenKind::With: return withStatement(yieldHandling); // LabelledStatement[?Yield, ?Return] // This is really handled by default and TokenKind::Yield cases above. // ThrowStatement[?Yield] case TokenKind::Throw: return throwStatement(yieldHandling); // TryStatement[?Yield, ?Return] case TokenKind::Try: return tryStatement(yieldHandling); // DebuggerStatement case TokenKind::Debugger: return debuggerStatement(); // |function| is forbidden by lookahead restriction (unless as child // statement of |if| or |else|, but Parser::consequentOrAlternative // handles that). case TokenKind::Function: error(JSMSG_FORBIDDEN_AS_STATEMENT, "function declarations"); return errorResult(); // |class| is also forbidden by lookahead restriction. case TokenKind::Class: error(JSMSG_FORBIDDEN_AS_STATEMENT, "classes"); return errorResult(); // ImportDeclaration (only inside modules) case TokenKind::Import: return importDeclarationOrImportExpr(yieldHandling); // ExportDeclaration (only inside modules) case TokenKind::Export: return exportDeclaration(); // Miscellaneous error cases arguably better caught here than elsewhere. case TokenKind::Catch: error(JSMSG_CATCH_WITHOUT_TRY); return errorResult(); case TokenKind::Finally: error(JSMSG_FINALLY_WITHOUT_TRY); return errorResult(); // NOTE: default case handled in the ExpressionStatement section. } } template typename ParseHandler::NodeResult GeneralParser::statementListItem( YieldHandling yieldHandling, bool canHaveDirectives /* = false */) { MOZ_ASSERT(checkOptionsCalled_); AutoCheckRecursionLimit recursion(this->fc_); if (!recursion.check(this->fc_)) { return errorResult(); } TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } switch (tt) { // BlockStatement[?Yield, ?Return] case TokenKind::LeftCurly: return blockStatement(yieldHandling); // VariableStatement[?Yield] case TokenKind::Var: return variableStatement(yieldHandling); // EmptyStatement case TokenKind::Semi: return handler_.newEmptyStatement(pos()); // ExpressionStatement[?Yield]. // // These should probably be handled by a single ExpressionStatement // function in a default, not split up this way. case TokenKind::String: if (!canHaveDirectives && anyChars.currentToken().atom() == TaggedParserAtomIndex::WellKnown::use_asm_()) { if (!warning(JSMSG_USE_ASM_DIRECTIVE_FAIL)) { return errorResult(); } } return expressionStatement(yieldHandling); case TokenKind::Yield: { // Don't use a ternary operator here due to obscure linker issues // around using static consts in the arms of a ternary. Modifier modifier; if (yieldExpressionsSupported()) { modifier = TokenStream::SlashIsRegExp; } else { modifier = TokenStream::SlashIsDiv; } TokenKind next; if (!tokenStream.peekToken(&next, modifier)) { return errorResult(); } if (next == TokenKind::Colon) { return labeledStatement(yieldHandling); } return expressionStatement(yieldHandling); } default: { // If we encounter an await in a module, and the module is not marked // as async, mark the module as async. if (tt == TokenKind::Await && !pc_->isAsync()) { if (pc_->atModuleTopLevel()) { if (!options().topLevelAwait) { error(JSMSG_TOP_LEVEL_AWAIT_NOT_SUPPORTED); return errorResult(); } pc_->sc()->asModuleContext()->setIsAsync(); MOZ_ASSERT(pc_->isAsync()); } } // Avoid getting next token with SlashIsDiv. if (tt == TokenKind::Await && pc_->isAsync()) { return expressionStatement(yieldHandling); } if (!TokenKindIsPossibleIdentifier(tt)) { return expressionStatement(yieldHandling); } TokenKind next; if (!tokenStream.peekToken(&next)) { return errorResult(); } if (tt == TokenKind::Let && nextTokenContinuesLetDeclaration(next)) { return lexicalDeclaration(yieldHandling, DeclarationKind::Let); } if (tt == TokenKind::Async) { TokenKind nextSameLine = TokenKind::Eof; if (!tokenStream.peekTokenSameLine(&nextSameLine)) { return errorResult(); } if (nextSameLine == TokenKind::Function) { uint32_t toStringStart = pos().begin; tokenStream.consumeKnownToken(TokenKind::Function); return functionStmt(toStringStart, yieldHandling, NameRequired, FunctionAsyncKind::AsyncFunction); } } if (next == TokenKind::Colon) { return labeledStatement(yieldHandling); } return expressionStatement(yieldHandling); } case TokenKind::New: return expressionStatement(yieldHandling, PredictInvoked); // IfStatement[?Yield, ?Return] case TokenKind::If: return ifStatement(yieldHandling); // BreakableStatement[?Yield, ?Return] // // BreakableStatement[Yield, Return]: // IterationStatement[?Yield, ?Return] // SwitchStatement[?Yield, ?Return] case TokenKind::Do: return doWhileStatement(yieldHandling); case TokenKind::While: return whileStatement(yieldHandling); case TokenKind::For: return forStatement(yieldHandling); case TokenKind::Switch: return switchStatement(yieldHandling); // ContinueStatement[?Yield] case TokenKind::Continue: return continueStatement(yieldHandling); // BreakStatement[?Yield] case TokenKind::Break: return breakStatement(yieldHandling); // [+Return] ReturnStatement[?Yield] case TokenKind::Return: // The Return parameter is only used here, and the effect is easily // detected this way, so don't bother passing around an extra parameter // everywhere. if (!pc_->allowReturn()) { error(JSMSG_BAD_RETURN_OR_YIELD, "return"); return errorResult(); } return returnStatement(yieldHandling); // WithStatement[?Yield, ?Return] case TokenKind::With: return withStatement(yieldHandling); // LabelledStatement[?Yield, ?Return] // This is really handled by default and TokenKind::Yield cases above. // ThrowStatement[?Yield] case TokenKind::Throw: return throwStatement(yieldHandling); // TryStatement[?Yield, ?Return] case TokenKind::Try: return tryStatement(yieldHandling); // DebuggerStatement case TokenKind::Debugger: return debuggerStatement(); // Declaration[Yield]: // HoistableDeclaration[?Yield, ~Default] case TokenKind::Function: return functionStmt(pos().begin, yieldHandling, NameRequired); // DecoratorList[?Yield, ?Await] opt ClassDeclaration[?Yield, ~Default] #ifdef ENABLE_DECORATORS case TokenKind::At: return classDefinition(yieldHandling, ClassStatement, NameRequired); #endif case TokenKind::Class: return classDefinition(yieldHandling, ClassStatement, NameRequired); // LexicalDeclaration[In, ?Yield] // LetOrConst BindingList[?In, ?Yield] case TokenKind::Const: // [In] is the default behavior, because for-loops specially parse // their heads to handle |in| in this situation. return lexicalDeclaration(yieldHandling, DeclarationKind::Const); // ImportDeclaration (only inside modules) case TokenKind::Import: return importDeclarationOrImportExpr(yieldHandling); // ExportDeclaration (only inside modules) case TokenKind::Export: return exportDeclaration(); // Miscellaneous error cases arguably better caught here than elsewhere. case TokenKind::Catch: error(JSMSG_CATCH_WITHOUT_TRY); return errorResult(); case TokenKind::Finally: error(JSMSG_FINALLY_WITHOUT_TRY); return errorResult(); // NOTE: default case handled in the ExpressionStatement section. } } template typename ParseHandler::NodeResult GeneralParser::expr( InHandling inHandling, YieldHandling yieldHandling, TripledotHandling tripledotHandling, PossibleError* possibleError /* = nullptr */, InvokedPrediction invoked /* = PredictUninvoked */) { Node pn; MOZ_TRY_VAR(pn, assignExpr(inHandling, yieldHandling, tripledotHandling, possibleError, invoked)); bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::SlashIsRegExp)) { return errorResult(); } if (!matched) { return pn; } ListNodeType seq; MOZ_TRY_VAR(seq, handler_.newCommaExpressionList(pn)); while (true) { // Trailing comma before the closing parenthesis is valid in an arrow // function parameters list: `(a, b, ) => body`. Check if we are // directly under CoverParenthesizedExpressionAndArrowParameterList, // and the next two tokens are closing parenthesis and arrow. If all // are present allow the trailing comma. if (tripledotHandling == TripledotAllowed) { TokenKind tt; if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } if (tt == TokenKind::RightParen) { tokenStream.consumeKnownToken(TokenKind::RightParen, TokenStream::SlashIsRegExp); if (!tokenStream.peekToken(&tt)) { return errorResult(); } if (tt != TokenKind::Arrow) { error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TokenKind::RightParen)); return errorResult(); } anyChars.ungetToken(); // put back right paren break; } } // Additional calls to assignExpr should not reuse the possibleError // which had been passed into the function. Otherwise we would lose // information needed to determine whether or not we're dealing with // a non-recoverable situation. PossibleError possibleErrorInner(*this); MOZ_TRY_VAR(pn, assignExpr(inHandling, yieldHandling, tripledotHandling, &possibleErrorInner)); if (!possibleError) { // Report any pending expression error. if (!possibleErrorInner.checkForExpressionError()) { return errorResult(); } } else { possibleErrorInner.transferErrorsTo(possibleError); } handler_.addList(seq, pn); if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::SlashIsRegExp)) { return errorResult(); } if (!matched) { break; } } return seq; } static ParseNodeKind BinaryOpTokenKindToParseNodeKind(TokenKind tok) { MOZ_ASSERT(TokenKindIsBinaryOp(tok)); return ParseNodeKind(size_t(ParseNodeKind::BinOpFirst) + (size_t(tok) - size_t(TokenKind::BinOpFirst))); } // This list must be kept in the same order in several places: // - The binary operators in ParseNode.h , // - the binary operators in TokenKind.h // - the JSOp code list in BytecodeEmitter.cpp static const int PrecedenceTable[] = { 1, /* ParseNodeKind::Coalesce */ 2, /* ParseNodeKind::Or */ 3, /* ParseNodeKind::And */ 4, /* ParseNodeKind::BitOr */ 5, /* ParseNodeKind::BitXor */ 6, /* ParseNodeKind::BitAnd */ 7, /* ParseNodeKind::StrictEq */ 7, /* ParseNodeKind::Eq */ 7, /* ParseNodeKind::StrictNe */ 7, /* ParseNodeKind::Ne */ 8, /* ParseNodeKind::Lt */ 8, /* ParseNodeKind::Le */ 8, /* ParseNodeKind::Gt */ 8, /* ParseNodeKind::Ge */ 8, /* ParseNodeKind::InstanceOf */ 8, /* ParseNodeKind::In */ 8, /* ParseNodeKind::PrivateIn */ 9, /* ParseNodeKind::Lsh */ 9, /* ParseNodeKind::Rsh */ 9, /* ParseNodeKind::Ursh */ 10, /* ParseNodeKind::Add */ 10, /* ParseNodeKind::Sub */ 11, /* ParseNodeKind::Star */ 11, /* ParseNodeKind::Div */ 11, /* ParseNodeKind::Mod */ 12 /* ParseNodeKind::Pow */ }; static const int PRECEDENCE_CLASSES = 12; static int Precedence(ParseNodeKind pnk) { // Everything binds tighter than ParseNodeKind::Limit, because we want // to reduce all nodes to a single node when we reach a token that is not // another binary operator. if (pnk == ParseNodeKind::Limit) { return 0; } MOZ_ASSERT(pnk >= ParseNodeKind::BinOpFirst); MOZ_ASSERT(pnk <= ParseNodeKind::BinOpLast); return PrecedenceTable[size_t(pnk) - size_t(ParseNodeKind::BinOpFirst)]; } enum class EnforcedParentheses : uint8_t { CoalesceExpr, AndOrExpr, None }; template MOZ_ALWAYS_INLINE typename ParseHandler::NodeResult GeneralParser::orExpr(InHandling inHandling, YieldHandling yieldHandling, TripledotHandling tripledotHandling, PossibleError* possibleError, InvokedPrediction invoked) { // Shift-reduce parser for the binary operator part of the JS expression // syntax. // Conceptually there's just one stack, a stack of pairs (lhs, op). // It's implemented using two separate arrays, though. Node nodeStack[PRECEDENCE_CLASSES]; ParseNodeKind kindStack[PRECEDENCE_CLASSES]; int depth = 0; Node pn; EnforcedParentheses unparenthesizedExpression = EnforcedParentheses::None; for (;;) { MOZ_TRY_VAR( pn, unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked, PrivateNameHandling::PrivateNameAllowed)); // If a binary operator follows, consume it and compute the // corresponding operator. TokenKind tok; if (!tokenStream.getToken(&tok)) { return errorResult(); } // Ensure that if we have a private name lhs we are legally constructing a // `#x in obj` expessions: if (handler_.isPrivateName(pn)) { if (tok != TokenKind::In || inHandling != InAllowed) { error(JSMSG_ILLEGAL_PRIVATE_NAME); return errorResult(); } } ParseNodeKind pnk; if (tok == TokenKind::In ? inHandling == InAllowed : TokenKindIsBinaryOp(tok)) { // We're definitely not in a destructuring context, so report any // pending expression error now. if (possibleError && !possibleError->checkForExpressionError()) { return errorResult(); } bool isErgonomicBrandCheck = false; switch (tok) { // Report an error for unary expressions on the LHS of **. case TokenKind::Pow: if (handler_.isUnparenthesizedUnaryExpression(pn)) { error(JSMSG_BAD_POW_LEFTSIDE); return errorResult(); } break; case TokenKind::Or: case TokenKind::And: // In the case that the `??` is on the left hand side of the // expression: Disallow Mixing of ?? and other logical operators (|| // and &&) unless one expression is parenthesized if (unparenthesizedExpression == EnforcedParentheses::CoalesceExpr) { error(JSMSG_BAD_COALESCE_MIXING); return errorResult(); } // If we have not detected a mixing error at this point, record that // we have an unparenthesized expression, in case we have one later. unparenthesizedExpression = EnforcedParentheses::AndOrExpr; break; case TokenKind::Coalesce: if (unparenthesizedExpression == EnforcedParentheses::AndOrExpr) { error(JSMSG_BAD_COALESCE_MIXING); return errorResult(); } // If we have not detected a mixing error at this point, record that // we have an unparenthesized expression, in case we have one later. unparenthesizedExpression = EnforcedParentheses::CoalesceExpr; break; case TokenKind::In: // if the LHS is a private name, and the operator is In, // ensure we're construcing an ergonomic brand check of // '#x in y', rather than having a higher precedence operator // like + cause a different reduction, such as // 1 + #x in y. if (handler_.isPrivateName(pn)) { if (depth > 0 && Precedence(kindStack[depth - 1]) >= Precedence(ParseNodeKind::InExpr)) { error(JSMSG_INVALID_PRIVATE_NAME_PRECEDENCE); return errorResult(); } isErgonomicBrandCheck = true; } break; default: // do nothing in other cases break; } if (isErgonomicBrandCheck) { pnk = ParseNodeKind::PrivateInExpr; } else { pnk = BinaryOpTokenKindToParseNodeKind(tok); } } else { tok = TokenKind::Eof; pnk = ParseNodeKind::Limit; } // From this point on, destructuring defaults are definitely an error. possibleError = nullptr; // If pnk has precedence less than or equal to another operator on the // stack, reduce. This combines nodes on the stack until we form the // actual lhs of pnk. // // The >= in this condition works because it is appendOrCreateList's // job to decide if the operator in question is left- or // right-associative, and build the corresponding tree. while (depth > 0 && Precedence(kindStack[depth - 1]) >= Precedence(pnk)) { depth--; ParseNodeKind combiningPnk = kindStack[depth]; MOZ_TRY_VAR(pn, handler_.appendOrCreateList(combiningPnk, nodeStack[depth], pn, pc_)); } if (pnk == ParseNodeKind::Limit) { break; } nodeStack[depth] = pn; kindStack[depth] = pnk; depth++; MOZ_ASSERT(depth <= PRECEDENCE_CLASSES); } anyChars.ungetToken(); // Had the next token been a Div, we would have consumed it. So there's no // ambiguity if we later (after ASI) re-get this token with SlashIsRegExp. anyChars.allowGettingNextTokenWithSlashIsRegExp(); MOZ_ASSERT(depth == 0); return pn; } template MOZ_ALWAYS_INLINE typename ParseHandler::NodeResult GeneralParser::condExpr(InHandling inHandling, YieldHandling yieldHandling, TripledotHandling tripledotHandling, PossibleError* possibleError, InvokedPrediction invoked) { Node condition; MOZ_TRY_VAR(condition, orExpr(inHandling, yieldHandling, tripledotHandling, possibleError, invoked)); bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Hook, TokenStream::SlashIsInvalid)) { return errorResult(); } if (!matched) { return condition; } Node thenExpr; MOZ_TRY_VAR(thenExpr, assignExpr(InAllowed, yieldHandling, TripledotProhibited)); if (!mustMatchToken(TokenKind::Colon, JSMSG_COLON_IN_COND)) { return errorResult(); } Node elseExpr; MOZ_TRY_VAR(elseExpr, assignExpr(inHandling, yieldHandling, TripledotProhibited)); return handler_.newConditional(condition, thenExpr, elseExpr); } template typename ParseHandler::NodeResult GeneralParser::assignExpr( InHandling inHandling, YieldHandling yieldHandling, TripledotHandling tripledotHandling, PossibleError* possibleError /* = nullptr */, InvokedPrediction invoked /* = PredictUninvoked */) { AutoCheckRecursionLimit recursion(this->fc_); if (!recursion.check(this->fc_)) { return errorResult(); } // It's very common at this point to have a "detectably simple" expression, // i.e. a name/number/string token followed by one of the following tokens // that obviously isn't part of an expression: , ; : ) ] } // // (In Parsemark this happens 81.4% of the time; in code with large // numeric arrays, such as some Kraken benchmarks, it happens more often.) // // In such cases, we can avoid the full expression parsing route through // assignExpr(), condExpr(), orExpr(), unaryExpr(), memberExpr(), and // primaryExpr(). TokenKind firstToken; if (!tokenStream.getToken(&firstToken, TokenStream::SlashIsRegExp)) { return errorResult(); } TokenPos exprPos = pos(); bool endsExpr; // This only handles identifiers that *never* have special meaning anywhere // in the language. Contextual keywords, reserved words in strict mode, // and other hard cases are handled outside this fast path. if (firstToken == TokenKind::Name) { if (!tokenStream.nextTokenEndsExpr(&endsExpr)) { return errorResult(); } if (endsExpr) { TaggedParserAtomIndex name = identifierReference(yieldHandling); if (!name) { return errorResult(); } return identifierReference(name); } } if (firstToken == TokenKind::Number) { if (!tokenStream.nextTokenEndsExpr(&endsExpr)) { return errorResult(); } if (endsExpr) { return newNumber(anyChars.currentToken()); } } if (firstToken == TokenKind::String) { if (!tokenStream.nextTokenEndsExpr(&endsExpr)) { return errorResult(); } if (endsExpr) { return stringLiteral(); } } if (firstToken == TokenKind::Yield && yieldExpressionsSupported()) { return yieldExpression(inHandling); } bool maybeAsyncArrow = false; if (firstToken == TokenKind::Async) { TokenKind nextSameLine = TokenKind::Eof; if (!tokenStream.peekTokenSameLine(&nextSameLine)) { return errorResult(); } if (TokenKindIsPossibleIdentifier(nextSameLine)) { maybeAsyncArrow = true; } } anyChars.ungetToken(); // Save the tokenizer state in case we find an arrow function and have to // rewind. Position start(tokenStream); auto ghostToken = this->compilationState_.getPosition(); PossibleError possibleErrorInner(*this); Node lhs; TokenKind tokenAfterLHS; bool isArrow; if (maybeAsyncArrow) { tokenStream.consumeKnownToken(TokenKind::Async, TokenStream::SlashIsRegExp); TokenKind tokenAfterAsync; if (!tokenStream.getToken(&tokenAfterAsync)) { return errorResult(); } MOZ_ASSERT(TokenKindIsPossibleIdentifier(tokenAfterAsync)); // Check yield validity here. TaggedParserAtomIndex name = bindingIdentifier(yieldHandling); if (!name) { return errorResult(); } if (!tokenStream.peekToken(&tokenAfterLHS, TokenStream::SlashIsRegExp)) { return errorResult(); } isArrow = tokenAfterLHS == TokenKind::Arrow; // |async [no LineTerminator] of| without being followed by => is only // possible in for-await-of loops, e.g. |for await (async of [])|. Pretend // the |async| token was parsed an identifier reference and then proceed // with the rest of this function. if (!isArrow) { anyChars.ungetToken(); // unget the binding identifier // The next token is guaranteed to never be a Div (, because it's an // identifier), so it's okay to re-get the token with SlashIsRegExp. anyChars.allowGettingNextTokenWithSlashIsRegExp(); TaggedParserAtomIndex asyncName = identifierReference(yieldHandling); if (!asyncName) { return errorResult(); } MOZ_TRY_VAR(lhs, identifierReference(asyncName)); } } else { MOZ_TRY_VAR(lhs, condExpr(inHandling, yieldHandling, tripledotHandling, &possibleErrorInner, invoked)); // Use SlashIsRegExp here because the ConditionalExpression parsed above // could be the entirety of this AssignmentExpression, and then ASI // permits this token to be a regular expression. if (!tokenStream.peekToken(&tokenAfterLHS, TokenStream::SlashIsRegExp)) { return errorResult(); } isArrow = tokenAfterLHS == TokenKind::Arrow; } if (isArrow) { // Rewind to reparse as an arrow function. // // Note: We do not call CompilationState::rewind here because parsing // during delazification will see the same rewind and need the same sequence // of inner functions to skip over. // Instead, we mark inner functions as "ghost". // // See GHOST_FUNCTION in FunctionFlags.h for more details. tokenStream.rewind(start); this->compilationState_.markGhost(ghostToken); TokenKind next; if (!tokenStream.getToken(&next, TokenStream::SlashIsRegExp)) { return errorResult(); } TokenPos startPos = pos(); uint32_t toStringStart = startPos.begin; anyChars.ungetToken(); FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction; if (next == TokenKind::Async) { tokenStream.consumeKnownToken(next, TokenStream::SlashIsRegExp); TokenKind nextSameLine = TokenKind::Eof; if (!tokenStream.peekTokenSameLine(&nextSameLine)) { return errorResult(); } // The AsyncArrowFunction production are // async [no LineTerminator here] AsyncArrowBindingIdentifier ... // async [no LineTerminator here] ArrowFormalParameters ... if (TokenKindIsPossibleIdentifier(nextSameLine) || nextSameLine == TokenKind::LeftParen) { asyncKind = FunctionAsyncKind::AsyncFunction; } else { anyChars.ungetToken(); } } FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::Arrow; FunctionNodeType funNode; MOZ_TRY_VAR(funNode, handler_.newFunction(syntaxKind, startPos)); return functionDefinition(funNode, toStringStart, inHandling, yieldHandling, TaggedParserAtomIndex::null(), syntaxKind, GeneratorKind::NotGenerator, asyncKind); } MOZ_ALWAYS_TRUE( tokenStream.getToken(&tokenAfterLHS, TokenStream::SlashIsRegExp)); ParseNodeKind kind; switch (tokenAfterLHS) { case TokenKind::Assign: kind = ParseNodeKind::AssignExpr; break; case TokenKind::AddAssign: kind = ParseNodeKind::AddAssignExpr; break; case TokenKind::SubAssign: kind = ParseNodeKind::SubAssignExpr; break; case TokenKind::CoalesceAssign: kind = ParseNodeKind::CoalesceAssignExpr; break; case TokenKind::OrAssign: kind = ParseNodeKind::OrAssignExpr; break; case TokenKind::AndAssign: kind = ParseNodeKind::AndAssignExpr; break; case TokenKind::BitOrAssign: kind = ParseNodeKind::BitOrAssignExpr; break; case TokenKind::BitXorAssign: kind = ParseNodeKind::BitXorAssignExpr; break; case TokenKind::BitAndAssign: kind = ParseNodeKind::BitAndAssignExpr; break; case TokenKind::LshAssign: kind = ParseNodeKind::LshAssignExpr; break; case TokenKind::RshAssign: kind = ParseNodeKind::RshAssignExpr; break; case TokenKind::UrshAssign: kind = ParseNodeKind::UrshAssignExpr; break; case TokenKind::MulAssign: kind = ParseNodeKind::MulAssignExpr; break; case TokenKind::DivAssign: kind = ParseNodeKind::DivAssignExpr; break; case TokenKind::ModAssign: kind = ParseNodeKind::ModAssignExpr; break; case TokenKind::PowAssign: kind = ParseNodeKind::PowAssignExpr; break; default: MOZ_ASSERT(!anyChars.isCurrentTokenAssignment()); if (!possibleError) { if (!possibleErrorInner.checkForExpressionError()) { return errorResult(); } } else { possibleErrorInner.transferErrorsTo(possibleError); } anyChars.ungetToken(); return lhs; } // Verify the left-hand side expression doesn't have a forbidden form. if (handler_.isUnparenthesizedDestructuringPattern(lhs)) { if (kind != ParseNodeKind::AssignExpr) { error(JSMSG_BAD_DESTRUCT_ASS); return errorResult(); } if (!possibleErrorInner.checkForDestructuringErrorOrWarning()) { return errorResult(); } } else if (handler_.isName(lhs)) { if (const char* chars = nameIsArgumentsOrEval(lhs)) { // |chars| is "arguments" or "eval" here. if (!strictModeErrorAt(exprPos.begin, JSMSG_BAD_STRICT_ASSIGN, chars)) { return errorResult(); } } } else if (handler_.isArgumentsLength(lhs)) { pc_->sc()->setIneligibleForArgumentsLength(); } else if (handler_.isPropertyOrPrivateMemberAccess(lhs)) { // Permitted: no additional testing/fixup needed. } else if (handler_.isFunctionCall(lhs)) { // We don't have to worry about backward compatibility issues with the new // compound assignment operators, so we always throw here. Also that way we // don't have to worry if |f() &&= expr| should always throw an error or // only if |f()| returns true. if (kind == ParseNodeKind::CoalesceAssignExpr || kind == ParseNodeKind::OrAssignExpr || kind == ParseNodeKind::AndAssignExpr) { errorAt(exprPos.begin, JSMSG_BAD_LEFTSIDE_OF_ASS); return errorResult(); } if (!strictModeErrorAt(exprPos.begin, JSMSG_BAD_LEFTSIDE_OF_ASS)) { return errorResult(); } if (possibleError) { possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET); } } else { errorAt(exprPos.begin, JSMSG_BAD_LEFTSIDE_OF_ASS); return errorResult(); } if (!possibleErrorInner.checkForExpressionError()) { return errorResult(); } Node rhs; MOZ_TRY_VAR(rhs, assignExpr(inHandling, yieldHandling, TripledotProhibited)); return handler_.newAssignment(kind, lhs, rhs); } template const char* PerHandlerParser::nameIsArgumentsOrEval(Node node) { MOZ_ASSERT(handler_.isName(node), "must only call this function on known names"); if (handler_.isEvalName(node)) { return "eval"; } if (handler_.isArgumentsName(node)) { return "arguments"; } return nullptr; } template bool GeneralParser::checkIncDecOperand( Node operand, uint32_t operandOffset) { if (handler_.isName(operand)) { if (const char* chars = nameIsArgumentsOrEval(operand)) { if (!strictModeErrorAt(operandOffset, JSMSG_BAD_STRICT_ASSIGN, chars)) { return false; } } } else if (handler_.isArgumentsLength(operand)) { pc_->sc()->setIneligibleForArgumentsLength(); } else if (handler_.isPropertyOrPrivateMemberAccess(operand)) { // Permitted: no additional testing/fixup needed. } else if (handler_.isFunctionCall(operand)) { // Assignment to function calls is forbidden in ES6. We're still // somewhat concerned about sites using this in dead code, so forbid it // only in strict mode code. if (!strictModeErrorAt(operandOffset, JSMSG_BAD_INCOP_OPERAND)) { return false; } } else { errorAt(operandOffset, JSMSG_BAD_INCOP_OPERAND); return false; } return true; } template typename ParseHandler::UnaryNodeResult GeneralParser::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, uint32_t begin) { Node kid; MOZ_TRY_VAR(kid, unaryExpr(yieldHandling, TripledotProhibited)); return handler_.newUnary(kind, begin, kid); } template typename ParseHandler::NodeResult GeneralParser::optionalExpr( YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt, PossibleError* possibleError /* = nullptr */, InvokedPrediction invoked /* = PredictUninvoked */) { AutoCheckRecursionLimit recursion(this->fc_); if (!recursion.check(this->fc_)) { return errorResult(); } uint32_t begin = pos().begin; Node lhs; MOZ_TRY_VAR(lhs, memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true, possibleError, invoked)); if (!tokenStream.peekToken(&tt, TokenStream::SlashIsDiv)) { return errorResult(); } if (tt != TokenKind::OptionalChain) { return lhs; } while (true) { if (!tokenStream.getToken(&tt)) { return errorResult(); } if (tt == TokenKind::Eof) { anyChars.ungetToken(); break; } Node nextMember; if (tt == TokenKind::OptionalChain) { if (!tokenStream.getToken(&tt)) { return errorResult(); } if (TokenKindIsPossibleIdentifierName(tt)) { MOZ_TRY_VAR(nextMember, memberPropertyAccess(lhs, OptionalKind::Optional)); } else if (tt == TokenKind::PrivateName) { MOZ_TRY_VAR(nextMember, memberPrivateAccess(lhs, OptionalKind::Optional)); } else if (tt == TokenKind::LeftBracket) { MOZ_TRY_VAR(nextMember, memberElemAccess(lhs, yieldHandling, OptionalKind::Optional)); } else if (tt == TokenKind::LeftParen) { MOZ_TRY_VAR(nextMember, memberCall(tt, lhs, yieldHandling, possibleError, OptionalKind::Optional)); } else { error(JSMSG_NAME_AFTER_DOT); return errorResult(); } } else if (tt == TokenKind::Dot) { if (!tokenStream.getToken(&tt)) { return errorResult(); } if (TokenKindIsPossibleIdentifierName(tt)) { MOZ_TRY_VAR(nextMember, memberPropertyAccess(lhs)); } else if (tt == TokenKind::PrivateName) { MOZ_TRY_VAR(nextMember, memberPrivateAccess(lhs)); } else { error(JSMSG_NAME_AFTER_DOT); return errorResult(); } } else if (tt == TokenKind::LeftBracket) { MOZ_TRY_VAR(nextMember, memberElemAccess(lhs, yieldHandling)); } else if (tt == TokenKind::LeftParen) { MOZ_TRY_VAR(nextMember, memberCall(tt, lhs, yieldHandling, possibleError)); } else if (tt == TokenKind::TemplateHead || tt == TokenKind::NoSubsTemplate) { error(JSMSG_BAD_OPTIONAL_TEMPLATE); return errorResult(); } else { anyChars.ungetToken(); break; } MOZ_ASSERT(nextMember); lhs = nextMember; } return handler_.newOptionalChain(begin, lhs); } template typename ParseHandler::NodeResult GeneralParser::unaryExpr( YieldHandling yieldHandling, TripledotHandling tripledotHandling, PossibleError* possibleError /* = nullptr */, InvokedPrediction invoked /* = PredictUninvoked */, PrivateNameHandling privateNameHandling /* = PrivateNameProhibited */) { AutoCheckRecursionLimit recursion(this->fc_); if (!recursion.check(this->fc_)) { return errorResult(); } TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } uint32_t begin = pos().begin; switch (tt) { case TokenKind::Void: return unaryOpExpr(yieldHandling, ParseNodeKind::VoidExpr, begin); case TokenKind::Not: return unaryOpExpr(yieldHandling, ParseNodeKind::NotExpr, begin); case TokenKind::BitNot: return unaryOpExpr(yieldHandling, ParseNodeKind::BitNotExpr, begin); case TokenKind::Add: return unaryOpExpr(yieldHandling, ParseNodeKind::PosExpr, begin); case TokenKind::Sub: return unaryOpExpr(yieldHandling, ParseNodeKind::NegExpr, begin); case TokenKind::TypeOf: { // The |typeof| operator is specially parsed to distinguish its // application to a name, from its application to a non-name // expression: // // // Looks up the name, doesn't find it and so evaluates to // // "undefined". // assertEq(typeof nonExistentName, "undefined"); // // // Evaluates expression, triggering a runtime ReferenceError for // // the undefined name. // typeof (1, nonExistentName); Node kid; MOZ_TRY_VAR(kid, unaryExpr(yieldHandling, TripledotProhibited)); return handler_.newTypeof(begin, kid); } case TokenKind::Inc: case TokenKind::Dec: { TokenKind tt2; if (!tokenStream.getToken(&tt2, TokenStream::SlashIsRegExp)) { return errorResult(); } uint32_t operandOffset = pos().begin; Node operand; MOZ_TRY_VAR(operand, optionalExpr(yieldHandling, TripledotProhibited, tt2)); if (!checkIncDecOperand(operand, operandOffset)) { return errorResult(); } ParseNodeKind pnk = (tt == TokenKind::Inc) ? ParseNodeKind::PreIncrementExpr : ParseNodeKind::PreDecrementExpr; return handler_.newUpdate(pnk, begin, operand); } case TokenKind::PrivateName: { if (privateNameHandling == PrivateNameHandling::PrivateNameAllowed) { TaggedParserAtomIndex field = anyChars.currentName(); return privateNameReference(field); } error(JSMSG_INVALID_PRIVATE_NAME_IN_UNARY_EXPR); return errorResult(); } case TokenKind::Delete: { uint32_t exprOffset; if (!tokenStream.peekOffset(&exprOffset, TokenStream::SlashIsRegExp)) { return errorResult(); } Node expr; MOZ_TRY_VAR(expr, unaryExpr(yieldHandling, TripledotProhibited)); // Per spec, deleting most unary expressions is valid -- it simply // returns true -- except for two cases: // 1. `var x; ...; delete x` is a syntax error in strict mode. // 2. Private fields cannot be deleted. if (handler_.isName(expr)) { if (!strictModeErrorAt(exprOffset, JSMSG_DEPRECATED_DELETE_OPERAND)) { return errorResult(); } pc_->sc()->setBindingsAccessedDynamically(); } if (handler_.isPrivateMemberAccess(expr)) { errorAt(exprOffset, JSMSG_PRIVATE_DELETE); return errorResult(); } return handler_.newDelete(begin, expr); } case TokenKind::Await: { // If we encounter an await in a module, mark it as async. if (!pc_->isAsync() && pc_->sc()->isModule()) { if (!options().topLevelAwait) { error(JSMSG_TOP_LEVEL_AWAIT_NOT_SUPPORTED); return errorResult(); } pc_->sc()->asModuleContext()->setIsAsync(); MOZ_ASSERT(pc_->isAsync()); } if (pc_->isAsync()) { if (inParametersOfAsyncFunction()) { error(JSMSG_AWAIT_IN_PARAMETER); return errorResult(); } Node kid; MOZ_TRY_VAR(kid, unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked)); pc_->lastAwaitOffset = begin; return handler_.newAwaitExpression(begin, kid); } } [[fallthrough]]; default: { Node expr; MOZ_TRY_VAR(expr, optionalExpr(yieldHandling, tripledotHandling, tt, possibleError, invoked)); /* Don't look across a newline boundary for a postfix incop. */ if (!tokenStream.peekTokenSameLine(&tt)) { return errorResult(); } if (tt != TokenKind::Inc && tt != TokenKind::Dec) { return expr; } tokenStream.consumeKnownToken(tt); if (!checkIncDecOperand(expr, begin)) { return errorResult(); } ParseNodeKind pnk = (tt == TokenKind::Inc) ? ParseNodeKind::PostIncrementExpr : ParseNodeKind::PostDecrementExpr; return handler_.newUpdate(pnk, begin, expr); } } } template typename ParseHandler::NodeResult GeneralParser::assignExprWithoutYieldOrAwait( YieldHandling yieldHandling) { uint32_t startYieldOffset = pc_->lastYieldOffset; uint32_t startAwaitOffset = pc_->lastAwaitOffset; Node res; MOZ_TRY_VAR(res, assignExpr(InAllowed, yieldHandling, TripledotProhibited)); if (pc_->lastYieldOffset != startYieldOffset) { errorAt(pc_->lastYieldOffset, JSMSG_YIELD_IN_PARAMETER); return errorResult(); } if (pc_->lastAwaitOffset != startAwaitOffset) { errorAt(pc_->lastAwaitOffset, JSMSG_AWAIT_IN_PARAMETER); return errorResult(); } return res; } template typename ParseHandler::ListNodeResult GeneralParser::argumentList( YieldHandling yieldHandling, bool* isSpread, PossibleError* possibleError /* = nullptr */) { ListNodeType argsList; MOZ_TRY_VAR(argsList, handler_.newArguments(pos())); bool matched; if (!tokenStream.matchToken(&matched, TokenKind::RightParen, TokenStream::SlashIsRegExp)) { return errorResult(); } if (matched) { handler_.setEndPosition(argsList, pos().end); return argsList; } while (true) { bool spread = false; uint32_t begin = 0; if (!tokenStream.matchToken(&matched, TokenKind::TripleDot, TokenStream::SlashIsRegExp)) { return errorResult(); } if (matched) { spread = true; begin = pos().begin; *isSpread = true; } Node argNode; MOZ_TRY_VAR(argNode, assignExpr(InAllowed, yieldHandling, TripledotProhibited, possibleError)); if (spread) { MOZ_TRY_VAR(argNode, handler_.newSpread(begin, argNode)); } handler_.addList(argsList, argNode); bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::SlashIsRegExp)) { return errorResult(); } if (!matched) { break; } TokenKind tt; if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } if (tt == TokenKind::RightParen) { break; } } if (!mustMatchToken(TokenKind::RightParen, JSMSG_PAREN_AFTER_ARGS)) { return errorResult(); } handler_.setEndPosition(argsList, pos().end); return argsList; } bool ParserBase::checkAndMarkSuperScope() { if (!pc_->sc()->allowSuperProperty()) { return false; } pc_->setSuperScopeNeedsHomeObject(); return true; } template bool GeneralParser::computeErrorMetadata( ErrorMetadata* err, const ErrorReportMixin::ErrorOffset& offset) const { if (offset.is()) { return tokenStream.computeErrorMetadata(err, AsVariant(pos().begin)); } return tokenStream.computeErrorMetadata(err, offset); } template typename ParseHandler::NodeResult GeneralParser::memberExpr( YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt, bool allowCallSyntax, PossibleError* possibleError, InvokedPrediction invoked) { MOZ_ASSERT(anyChars.isCurrentTokenType(tt)); Node lhs; AutoCheckRecursionLimit recursion(this->fc_); if (!recursion.check(this->fc_)) { return errorResult(); } /* Check for new expression first. */ if (tt == TokenKind::New) { uint32_t newBegin = pos().begin; // Make sure this wasn't a |new.target| in disguise. NewTargetNodeType newTarget; if (!tryNewTarget(&newTarget)) { return errorResult(); } if (newTarget) { lhs = newTarget; } else { // Gotten by tryNewTarget tt = anyChars.currentToken().type; Node ctorExpr; MOZ_TRY_VAR(ctorExpr, memberExpr(yieldHandling, TripledotProhibited, tt, /* allowCallSyntax = */ false, /* possibleError = */ nullptr, PredictInvoked)); // If we have encountered an optional chain, in the form of `new // ClassName?.()` then we need to throw, as this is disallowed by the // spec. bool optionalToken; if (!tokenStream.matchToken(&optionalToken, TokenKind::OptionalChain)) { return errorResult(); } if (optionalToken) { errorAt(newBegin, JSMSG_BAD_NEW_OPTIONAL); return errorResult(); } bool matched; if (!tokenStream.matchToken(&matched, TokenKind::LeftParen)) { return errorResult(); } bool isSpread = false; ListNodeType args; if (matched) { MOZ_TRY_VAR(args, argumentList(yieldHandling, &isSpread)); } else { MOZ_TRY_VAR(args, handler_.newArguments(pos())); } if (!args) { return errorResult(); } MOZ_TRY_VAR( lhs, handler_.newNewExpression(newBegin, ctorExpr, args, isSpread)); } } else if (tt == TokenKind::Super) { NameNodeType thisName; MOZ_TRY_VAR(thisName, newThisName()); MOZ_TRY_VAR(lhs, handler_.newSuperBase(thisName, pos())); } else if (tt == TokenKind::Import) { MOZ_TRY_VAR(lhs, importExpr(yieldHandling, allowCallSyntax)); } else { MOZ_TRY_VAR(lhs, primaryExpr(yieldHandling, tripledotHandling, tt, possibleError, invoked)); } MOZ_ASSERT_IF(handler_.isSuperBase(lhs), anyChars.isCurrentTokenType(TokenKind::Super)); while (true) { if (!tokenStream.getToken(&tt)) { return errorResult(); } if (tt == TokenKind::Eof) { anyChars.ungetToken(); break; } Node nextMember; if (tt == TokenKind::Dot) { if (!tokenStream.getToken(&tt)) { return errorResult(); } if (TokenKindIsPossibleIdentifierName(tt)) { MOZ_TRY_VAR(nextMember, memberPropertyAccess(lhs)); } else if (tt == TokenKind::PrivateName) { MOZ_TRY_VAR(nextMember, memberPrivateAccess(lhs)); } else { error(JSMSG_NAME_AFTER_DOT); return errorResult(); } } else if (tt == TokenKind::LeftBracket) { MOZ_TRY_VAR(nextMember, memberElemAccess(lhs, yieldHandling)); } else if ((allowCallSyntax && tt == TokenKind::LeftParen) || tt == TokenKind::TemplateHead || tt == TokenKind::NoSubsTemplate) { if (handler_.isSuperBase(lhs)) { if (!pc_->sc()->allowSuperCall()) { error(JSMSG_BAD_SUPERCALL); return errorResult(); } if (tt != TokenKind::LeftParen) { error(JSMSG_BAD_SUPER); return errorResult(); } MOZ_TRY_VAR(nextMember, memberSuperCall(lhs, yieldHandling)); if (!noteUsedName( TaggedParserAtomIndex::WellKnown::dot_initializers_())) { return errorResult(); } #ifdef ENABLE_DECORATORS if (!noteUsedName(TaggedParserAtomIndex::WellKnown:: dot_instanceExtraInitializers_())) { return null(); } #endif } else { MOZ_TRY_VAR(nextMember, memberCall(tt, lhs, yieldHandling, possibleError)); } } else { anyChars.ungetToken(); if (handler_.isSuperBase(lhs)) { break; } return lhs; } lhs = nextMember; } if (handler_.isSuperBase(lhs)) { error(JSMSG_BAD_SUPER); return errorResult(); } return lhs; } template typename ParseHandler::NodeResult GeneralParser::decoratorExpr(YieldHandling yieldHandling, TokenKind tt) { MOZ_ASSERT(anyChars.isCurrentTokenType(tt)); AutoCheckRecursionLimit recursion(this->fc_); if (!recursion.check(this->fc_)) { return errorResult(); } if (tt == TokenKind::LeftParen) { // DecoratorParenthesizedExpression Node expr; MOZ_TRY_VAR(expr, exprInParens(InAllowed, yieldHandling, TripledotAllowed, /* possibleError*/ nullptr)); if (!mustMatchToken(TokenKind::RightParen, JSMSG_PAREN_AFTER_DECORATOR)) { return errorResult(); } return handler_.parenthesize(expr); } if (!TokenKindIsPossibleIdentifier(tt)) { error(JSMSG_DECORATOR_NAME_EXPECTED); return errorResult(); } TaggedParserAtomIndex name = identifierReference(yieldHandling); if (!name) { return errorResult(); } Node lhs; MOZ_TRY_VAR(lhs, identifierReference(name)); while (true) { if (!tokenStream.getToken(&tt)) { return errorResult(); } if (tt == TokenKind::Eof) { anyChars.ungetToken(); break; } Node nextMember; if (tt == TokenKind::Dot) { if (!tokenStream.getToken(&tt)) { return errorResult(); } if (TokenKindIsPossibleIdentifierName(tt)) { MOZ_TRY_VAR(nextMember, memberPropertyAccess(lhs)); } else if (tt == TokenKind::PrivateName) { MOZ_TRY_VAR(nextMember, memberPrivateAccess(lhs)); } else { error(JSMSG_NAME_AFTER_DOT); return errorResult(); } } else if (tt == TokenKind::LeftParen) { MOZ_TRY_VAR(nextMember, memberCall(tt, lhs, yieldHandling, /* possibleError */ nullptr)); lhs = nextMember; // This is a `DecoratorCallExpression` and it's defined at the top level // of `Decorator`, no other `DecoratorMemberExpression` is allowed to // follow after the arguments. break; } else { anyChars.ungetToken(); break; } lhs = nextMember; } return lhs; } template inline typename ParseHandler::NameNodeResult PerHandlerParser::newName(TaggedParserAtomIndex name) { return newName(name, pos()); } template inline typename ParseHandler::NameNodeResult PerHandlerParser::newName(TaggedParserAtomIndex name, TokenPos pos) { if (name == TaggedParserAtomIndex::WellKnown::arguments()) { this->pc_->numberOfArgumentsNames++; } return handler_.newName(name, pos); } template inline typename ParseHandler::NameNodeResult PerHandlerParser::newPrivateName(TaggedParserAtomIndex name) { return handler_.newPrivateName(name, pos()); } template typename ParseHandler::NodeResult GeneralParser::memberPropertyAccess( Node lhs, OptionalKind optionalKind /* = OptionalKind::NonOptional */) { MOZ_ASSERT(TokenKindIsPossibleIdentifierName(anyChars.currentToken().type) || anyChars.currentToken().type == TokenKind::PrivateName); TaggedParserAtomIndex field = anyChars.currentName(); if (handler_.isSuperBase(lhs) && !checkAndMarkSuperScope()) { error(JSMSG_BAD_SUPERPROP, "property"); return errorResult(); } NameNodeType name; MOZ_TRY_VAR(name, handler_.newPropertyName(field, pos())); if (optionalKind == OptionalKind::Optional) { MOZ_ASSERT(!handler_.isSuperBase(lhs)); return handler_.newOptionalPropertyAccess(lhs, name); } if (handler_.isArgumentsName(lhs) && handler_.isLengthName(name)) { MOZ_ASSERT(pc_->numberOfArgumentsNames > 0); pc_->numberOfArgumentsNames--; // Currently when resuming Generators don't get their argument length set // in the interpreter frame (see InterpreterStack::resumeGeneratorCallFrame, // and its call to initCallFrame). if (pc_->isGeneratorOrAsync()) { pc_->sc()->setIneligibleForArgumentsLength(); } return handler_.newArgumentsLength(lhs, name); } return handler_.newPropertyAccess(lhs, name); } template typename ParseHandler::NodeResult GeneralParser::memberPrivateAccess( Node lhs, OptionalKind optionalKind /* = OptionalKind::NonOptional */) { MOZ_ASSERT(anyChars.currentToken().type == TokenKind::PrivateName); TaggedParserAtomIndex field = anyChars.currentName(); // Cannot access private fields on super. if (handler_.isSuperBase(lhs)) { error(JSMSG_BAD_SUPERPRIVATE); return errorResult(); } NameNodeType privateName; MOZ_TRY_VAR(privateName, privateNameReference(field)); if (optionalKind == OptionalKind::Optional) { MOZ_ASSERT(!handler_.isSuperBase(lhs)); return handler_.newOptionalPrivateMemberAccess(lhs, privateName, pos().end); } return handler_.newPrivateMemberAccess(lhs, privateName, pos().end); } template typename ParseHandler::NodeResult GeneralParser::memberElemAccess( Node lhs, YieldHandling yieldHandling, OptionalKind optionalKind /* = OptionalKind::NonOptional */) { MOZ_ASSERT(anyChars.currentToken().type == TokenKind::LeftBracket); Node propExpr; MOZ_TRY_VAR(propExpr, expr(InAllowed, yieldHandling, TripledotProhibited)); if (!mustMatchToken(TokenKind::RightBracket, JSMSG_BRACKET_IN_INDEX)) { return errorResult(); } if (handler_.isSuperBase(lhs) && !checkAndMarkSuperScope()) { error(JSMSG_BAD_SUPERPROP, "member"); return errorResult(); } if (optionalKind == OptionalKind::Optional) { MOZ_ASSERT(!handler_.isSuperBase(lhs)); return handler_.newOptionalPropertyByValue(lhs, propExpr, pos().end); } return handler_.newPropertyByValue(lhs, propExpr, pos().end); } template typename ParseHandler::NodeResult GeneralParser::memberSuperCall( Node lhs, YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.currentToken().type == TokenKind::LeftParen); // Despite the fact that it's impossible to have |super()| in a // generator, we still inherit the yieldHandling of the // memberExpression, per spec. Curious. bool isSpread = false; ListNodeType args; MOZ_TRY_VAR(args, argumentList(yieldHandling, &isSpread)); CallNodeType superCall; MOZ_TRY_VAR(superCall, handler_.newSuperCall(lhs, args, isSpread)); // |super()| implicitly reads |new.target|. if (!noteUsedName(TaggedParserAtomIndex::WellKnown::dot_newTarget_())) { return errorResult(); } NameNodeType thisName; MOZ_TRY_VAR(thisName, newThisName()); return handler_.newSetThis(thisName, superCall); } template typename ParseHandler::NodeResult GeneralParser::memberCall( TokenKind tt, Node lhs, YieldHandling yieldHandling, PossibleError* possibleError /* = nullptr */, OptionalKind optionalKind /* = OptionalKind::NonOptional */) { if (options().selfHostingMode && (handler_.isPropertyOrPrivateMemberAccess(lhs) || handler_.isOptionalPropertyOrPrivateMemberAccess(lhs))) { error(JSMSG_SELFHOSTED_METHOD_CALL); return errorResult(); } MOZ_ASSERT(tt == TokenKind::LeftParen || tt == TokenKind::TemplateHead || tt == TokenKind::NoSubsTemplate, "Unexpected token kind for member call"); JSOp op = JSOp::Call; bool maybeAsyncArrow = false; if (tt == TokenKind::LeftParen && optionalKind == OptionalKind::NonOptional) { if (handler_.isAsyncKeyword(lhs)) { // |async (| can be the start of an async arrow // function, so we need to defer reporting possible // errors from destructuring syntax. To give better // error messages, we only allow the AsyncArrowHead // part of the CoverCallExpressionAndAsyncArrowHead // syntax when the initial name is "async". maybeAsyncArrow = true; } else if (handler_.isEvalName(lhs)) { // Select the right Eval op and flag pc_ as having a // direct eval. op = pc_->sc()->strict() ? JSOp::StrictEval : JSOp::Eval; pc_->sc()->setBindingsAccessedDynamically(); pc_->sc()->setHasDirectEval(); // In non-strict mode code, direct calls to eval can // add variables to the call object. if (pc_->isFunctionBox() && !pc_->sc()->strict()) { pc_->functionBox()->setFunHasExtensibleScope(); } // If we're in a method, mark the method as requiring // support for 'super', since direct eval code can use // it. (If we're not in a method, that's fine, so // ignore the return value.) checkAndMarkSuperScope(); } } if (tt == TokenKind::LeftParen) { bool isSpread = false; PossibleError* asyncPossibleError = maybeAsyncArrow ? possibleError : nullptr; ListNodeType args; MOZ_TRY_VAR(args, argumentList(yieldHandling, &isSpread, asyncPossibleError)); if (isSpread) { if (op == JSOp::Eval) { op = JSOp::SpreadEval; } else if (op == JSOp::StrictEval) { op = JSOp::StrictSpreadEval; } else { op = JSOp::SpreadCall; } } if (optionalKind == OptionalKind::Optional) { return handler_.newOptionalCall(lhs, args, op); } return handler_.newCall(lhs, args, op); } ListNodeType args; MOZ_TRY_VAR(args, handler_.newArguments(pos())); if (!taggedTemplate(yieldHandling, args, tt)) { return errorResult(); } if (optionalKind == OptionalKind::Optional) { error(JSMSG_BAD_OPTIONAL_TEMPLATE); return errorResult(); } return handler_.newTaggedTemplate(lhs, args, op); } template bool GeneralParser::checkLabelOrIdentifierReference( TaggedParserAtomIndex ident, uint32_t offset, YieldHandling yieldHandling, TokenKind hint /* = TokenKind::Limit */) { TokenKind tt; if (hint == TokenKind::Limit) { tt = ReservedWordTokenKind(ident); } else { // All non-reserved word kinds are folded into TokenKind::Limit in // ReservedWordTokenKind and the following code. if (hint == TokenKind::Name || hint == TokenKind::PrivateName) { hint = TokenKind::Limit; } MOZ_ASSERT(hint == ReservedWordTokenKind(ident), "hint doesn't match actual token kind"); tt = hint; } if (!pc_->sc()->allowArguments() && ident == TaggedParserAtomIndex::WellKnown::arguments()) { error(JSMSG_BAD_ARGUMENTS); return false; } if (tt == TokenKind::Limit) { // Either TokenKind::Name or TokenKind::PrivateName return true; } if (TokenKindIsContextualKeyword(tt)) { if (tt == TokenKind::Yield) { if (yieldHandling == YieldIsKeyword) { errorAt(offset, JSMSG_RESERVED_ID, "yield"); return false; } if (pc_->sc()->strict()) { if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "yield")) { return false; } } return true; } if (tt == TokenKind::Await) { if (awaitIsKeyword() || awaitIsDisallowed()) { errorAt(offset, JSMSG_RESERVED_ID, "await"); return false; } return true; } if (pc_->sc()->strict()) { if (tt == TokenKind::Let) { if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "let")) { return false; } return true; } if (tt == TokenKind::Static) { if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, "static")) { return false; } return true; } } return true; } if (TokenKindIsStrictReservedWord(tt)) { if (pc_->sc()->strict()) { if (!strictModeErrorAt(offset, JSMSG_RESERVED_ID, ReservedWordToCharZ(tt))) { return false; } } return true; } if (TokenKindIsKeyword(tt) || TokenKindIsReservedWordLiteral(tt)) { errorAt(offset, JSMSG_INVALID_ID, ReservedWordToCharZ(tt)); return false; } if (TokenKindIsFutureReservedWord(tt)) { errorAt(offset, JSMSG_RESERVED_ID, ReservedWordToCharZ(tt)); return false; } MOZ_ASSERT_UNREACHABLE("Unexpected reserved word kind."); return false; } template bool GeneralParser::checkBindingIdentifier( TaggedParserAtomIndex ident, uint32_t offset, YieldHandling yieldHandling, TokenKind hint /* = TokenKind::Limit */) { if (pc_->sc()->strict()) { if (ident == TaggedParserAtomIndex::WellKnown::arguments()) { if (!strictModeErrorAt(offset, JSMSG_BAD_STRICT_ASSIGN, "arguments")) { return false; } return true; } if (ident == TaggedParserAtomIndex::WellKnown::eval()) { if (!strictModeErrorAt(offset, JSMSG_BAD_STRICT_ASSIGN, "eval")) { return false; } return true; } } return checkLabelOrIdentifierReference(ident, offset, yieldHandling, hint); } template TaggedParserAtomIndex GeneralParser::labelOrIdentifierReference( YieldHandling yieldHandling) { // ES 2017 draft 12.1.1. // StringValue of IdentifierName normalizes any Unicode escape sequences // in IdentifierName hence such escapes cannot be used to write an // Identifier whose code point sequence is the same as a ReservedWord. // // Use const ParserName* instead of TokenKind to reflect the normalization. // Unless the name contains escapes, we can reuse the current TokenKind // to determine if the name is a restricted identifier. TokenKind hint = !anyChars.currentNameHasEscapes(this->parserAtoms()) ? anyChars.currentToken().type : TokenKind::Limit; TaggedParserAtomIndex ident = anyChars.currentName(); if (!checkLabelOrIdentifierReference(ident, pos().begin, yieldHandling, hint)) { return TaggedParserAtomIndex::null(); } return ident; } template TaggedParserAtomIndex GeneralParser::bindingIdentifier( YieldHandling yieldHandling) { TokenKind hint = !anyChars.currentNameHasEscapes(this->parserAtoms()) ? anyChars.currentToken().type : TokenKind::Limit; TaggedParserAtomIndex ident = anyChars.currentName(); if (!checkBindingIdentifier(ident, pos().begin, yieldHandling, hint)) { return TaggedParserAtomIndex::null(); } return ident; } template typename ParseHandler::NameNodeResult PerHandlerParser::identifierReference( TaggedParserAtomIndex name) { NameNodeType id; MOZ_TRY_VAR(id, newName(name)); if (!noteUsedName(name)) { return errorResult(); } return id; } template typename ParseHandler::NameNodeResult PerHandlerParser::privateNameReference( TaggedParserAtomIndex name) { NameNodeType id; MOZ_TRY_VAR(id, newPrivateName(name)); if (!noteUsedName(name, NameVisibility::Private, Some(pos()))) { return errorResult(); } return id; } template typename ParseHandler::NameNodeResult PerHandlerParser::stringLiteral() { return handler_.newStringLiteral(anyChars.currentToken().atom(), pos()); } template typename ParseHandler::NodeResult PerHandlerParser::noSubstitutionTaggedTemplate() { if (anyChars.hasInvalidTemplateEscape()) { anyChars.clearInvalidTemplateEscape(); return handler_.newRawUndefinedLiteral(pos()); } return handler_.newTemplateStringLiteral(anyChars.currentToken().atom(), pos()); } template typename ParseHandler::NameNodeResult GeneralParser::noSubstitutionUntaggedTemplate() { if (!tokenStream.checkForInvalidTemplateEscapeError()) { return errorResult(); } return handler_.newTemplateStringLiteral(anyChars.currentToken().atom(), pos()); } template FullParseHandler::RegExpLiteralResult Parser::newRegExp() { MOZ_ASSERT(!options().selfHostingMode); // Create the regexp and check its syntax. const auto& chars = tokenStream.getCharBuffer(); mozilla::Range range(chars.begin(), chars.length()); RegExpFlags flags = anyChars.currentToken().regExpFlags(); uint32_t offset = anyChars.currentToken().pos.begin; uint32_t line; JS::LimitedColumnNumberOneOrigin column; tokenStream.computeLineAndColumn(offset, &line, &column); if (!handler_.reuseRegexpSyntaxParse()) { // Verify that the Regexp will syntax parse when the time comes to // instantiate it. If we have already done a syntax parse, we can // skip this. if (!irregexp::CheckPatternSyntax( this->alloc_, this->fc_->stackLimit(), anyChars, range, flags, Some(line), Some(JS::ColumnNumberOneOrigin(column)))) { return errorResult(); } } auto atom = this->parserAtoms().internChar16(fc_, chars.begin(), chars.length()); if (!atom) { return errorResult(); } // RegExp patterm must be atomized. this->parserAtoms().markUsedByStencil(atom, ParserAtom::Atomize::Yes); RegExpIndex index(this->compilationState_.regExpData.length()); if (uint32_t(index) >= TaggedScriptThingIndex::IndexLimit) { ReportAllocationOverflow(fc_); return errorResult(); } if (!this->compilationState_.regExpData.emplaceBack(atom, flags)) { js::ReportOutOfMemory(this->fc_); return errorResult(); } return handler_.newRegExp(index, pos()); } template SyntaxParseHandler::RegExpLiteralResult Parser::newRegExp() { MOZ_ASSERT(!options().selfHostingMode); // Only check the regexp's syntax, but don't create a regexp object. const auto& chars = tokenStream.getCharBuffer(); RegExpFlags flags = anyChars.currentToken().regExpFlags(); uint32_t offset = anyChars.currentToken().pos.begin; uint32_t line; JS::LimitedColumnNumberOneOrigin column; tokenStream.computeLineAndColumn(offset, &line, &column); mozilla::Range source(chars.begin(), chars.length()); if (!irregexp::CheckPatternSyntax(this->alloc_, this->fc_->stackLimit(), anyChars, source, flags, Some(line), Some(JS::ColumnNumberOneOrigin(column)))) { return errorResult(); } return handler_.newRegExp(SyntaxParseHandler::Node::NodeGeneric, pos()); } template typename ParseHandler::RegExpLiteralResult GeneralParser::newRegExp() { return asFinalParser()->newRegExp(); } template FullParseHandler::BigIntLiteralResult Parser::newBigInt() { // The token's charBuffer contains the DecimalIntegerLiteral or // NonDecimalIntegerLiteral production, and as such does not include the // BigIntLiteralSuffix (the trailing "n"). Note that NonDecimalIntegerLiteral // productions start with 0[bBoOxX], indicating binary/octal/hex. const auto& chars = tokenStream.getCharBuffer(); if (chars.length() > UINT32_MAX) { ReportAllocationOverflow(fc_); return errorResult(); } BigIntIndex index(this->compilationState_.bigIntData.length()); if (uint32_t(index) >= TaggedScriptThingIndex::IndexLimit) { ReportAllocationOverflow(fc_); return errorResult(); } if (!this->compilationState_.bigIntData.emplaceBack()) { js::ReportOutOfMemory(this->fc_); return errorResult(); } if (!this->compilationState_.bigIntData[index].init( this->fc_, this->stencilAlloc(), chars)) { return errorResult(); } bool isZero = this->compilationState_.bigIntData[index].isZero(); // Should the operations below fail, the buffer held by data will // be cleaned up by the CompilationState destructor. return handler_.newBigInt(index, isZero, pos()); } template SyntaxParseHandler::BigIntLiteralResult Parser::newBigInt() { // The tokenizer has already checked the syntax of the bigint. return handler_.newBigInt(); } template typename ParseHandler::BigIntLiteralResult GeneralParser::newBigInt() { return asFinalParser()->newBigInt(); } // |exprPossibleError| is the PossibleError state within |expr|, // |possibleError| is the surrounding PossibleError state. template bool GeneralParser::checkDestructuringAssignmentTarget( Node expr, TokenPos exprPos, PossibleError* exprPossibleError, PossibleError* possibleError, TargetBehavior behavior) { // Report any pending expression error if we're definitely not in a // destructuring context or the possible destructuring target is a // property accessor. if (!possibleError || handler_.isPropertyOrPrivateMemberAccess(expr)) { return exprPossibleError->checkForExpressionError(); } // |expr| may end up as a destructuring assignment target, so we need to // validate it's either a name or can be parsed as a nested destructuring // pattern. Property accessors are also valid assignment targets, but // those are already handled above. exprPossibleError->transferErrorsTo(possibleError); // Return early if a pending destructuring error is already present. if (possibleError->hasPendingDestructuringError()) { return true; } if (handler_.isName(expr)) { checkDestructuringAssignmentName(handler_.asNameNode(expr), exprPos, possibleError); return true; } if (handler_.isUnparenthesizedDestructuringPattern(expr)) { if (behavior == TargetBehavior::ForbidAssignmentPattern) { possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET); } return true; } // Parentheses are forbidden around destructuring *patterns* (but allowed // around names). Use our nicer error message for parenthesized, nested // patterns if nested destructuring patterns are allowed. if (handler_.isParenthesizedDestructuringPattern(expr) && behavior != TargetBehavior::ForbidAssignmentPattern) { possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_PARENS); } else { possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET); } return true; } template void GeneralParser::checkDestructuringAssignmentName( NameNodeType name, TokenPos namePos, PossibleError* possibleError) { #ifdef DEBUG // GCC 8.0.1 crashes if this is a one-liner. bool isName = handler_.isName(name); MOZ_ASSERT(isName); #endif // Return early if a pending destructuring error is already present. if (possibleError->hasPendingDestructuringError()) { return; } if (handler_.isArgumentsLength(name)) { pc_->sc()->setIneligibleForArgumentsLength(); } if (pc_->sc()->strict()) { if (handler_.isArgumentsName(name)) { if (pc_->sc()->strict()) { possibleError->setPendingDestructuringErrorAt( namePos, JSMSG_BAD_STRICT_ASSIGN_ARGUMENTS); } else { possibleError->setPendingDestructuringWarningAt( namePos, JSMSG_BAD_STRICT_ASSIGN_ARGUMENTS); } return; } if (handler_.isEvalName(name)) { if (pc_->sc()->strict()) { possibleError->setPendingDestructuringErrorAt( namePos, JSMSG_BAD_STRICT_ASSIGN_EVAL); } else { possibleError->setPendingDestructuringWarningAt( namePos, JSMSG_BAD_STRICT_ASSIGN_EVAL); } return; } } } template bool GeneralParser::checkDestructuringAssignmentElement( Node expr, TokenPos exprPos, PossibleError* exprPossibleError, PossibleError* possibleError) { // ES2018 draft rev 0719f44aab93215ed9a626b2f45bd34f36916834 // 12.15.5 Destructuring Assignment // // AssignmentElement[Yield, Await]: // DestructuringAssignmentTarget[?Yield, ?Await] // DestructuringAssignmentTarget[?Yield, ?Await] Initializer[+In, // ?Yield, // ?Await] // If |expr| is an assignment element with an initializer expression, its // destructuring assignment target was already validated in assignExpr(). // Otherwise we need to check that |expr| is a valid destructuring target. if (handler_.isUnparenthesizedAssignment(expr)) { // Report any pending expression error if we're definitely not in a // destructuring context. if (!possibleError) { return exprPossibleError->checkForExpressionError(); } exprPossibleError->transferErrorsTo(possibleError); return true; } return checkDestructuringAssignmentTarget(expr, exprPos, exprPossibleError, possibleError); } template typename ParseHandler::ListNodeResult GeneralParser::arrayInitializer( YieldHandling yieldHandling, PossibleError* possibleError) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftBracket)); uint32_t begin = pos().begin; ListNodeType literal; MOZ_TRY_VAR(literal, handler_.newArrayLiteral(begin)); TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } if (tt == TokenKind::RightBracket) { /* * Mark empty arrays as non-constant, since we cannot easily * determine their type. */ handler_.setListHasNonConstInitializer(literal); } else { anyChars.ungetToken(); for (uint32_t index = 0;; index++) { if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) { error(JSMSG_ARRAY_INIT_TOO_BIG); return errorResult(); } TokenKind tt; if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } if (tt == TokenKind::RightBracket) { break; } if (tt == TokenKind::Comma) { tokenStream.consumeKnownToken(TokenKind::Comma, TokenStream::SlashIsRegExp); if (!handler_.addElision(literal, pos())) { return errorResult(); } continue; } if (tt == TokenKind::TripleDot) { tokenStream.consumeKnownToken(TokenKind::TripleDot, TokenStream::SlashIsRegExp); uint32_t begin = pos().begin; TokenPos innerPos; if (!tokenStream.peekTokenPos(&innerPos, TokenStream::SlashIsRegExp)) { return errorResult(); } PossibleError possibleErrorInner(*this); Node inner; MOZ_TRY_VAR(inner, assignExpr(InAllowed, yieldHandling, TripledotProhibited, &possibleErrorInner)); if (!checkDestructuringAssignmentTarget( inner, innerPos, &possibleErrorInner, possibleError)) { return errorResult(); } if (!handler_.addSpreadElement(literal, begin, inner)) { return errorResult(); } } else { TokenPos elementPos; if (!tokenStream.peekTokenPos(&elementPos, TokenStream::SlashIsRegExp)) { return errorResult(); } PossibleError possibleErrorInner(*this); Node element; MOZ_TRY_VAR(element, assignExpr(InAllowed, yieldHandling, TripledotProhibited, &possibleErrorInner)); if (!checkDestructuringAssignmentElement( element, elementPos, &possibleErrorInner, possibleError)) { return errorResult(); } handler_.addArrayElement(literal, element); } bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::SlashIsRegExp)) { return errorResult(); } if (!matched) { break; } if (tt == TokenKind::TripleDot && possibleError) { possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA); } } if (!mustMatchToken( TokenKind::RightBracket, [this, begin](TokenKind actual) { this->reportMissingClosing(JSMSG_BRACKET_AFTER_LIST, JSMSG_BRACKET_OPENED, begin); })) { return errorResult(); } } handler_.setEndPosition(literal, pos().end); return literal; } template typename ParseHandler::NodeResult GeneralParser::propertyName( YieldHandling yieldHandling, PropertyNameContext propertyNameContext, const Maybe& maybeDecl, ListNodeType propList, TaggedParserAtomIndex* propAtomOut) { // PropertyName[Yield, Await]: // LiteralPropertyName // ComputedPropertyName[?Yield, ?Await] // // LiteralPropertyName: // IdentifierName // StringLiteral // NumericLiteral TokenKind ltok = anyChars.currentToken().type; *propAtomOut = TaggedParserAtomIndex::null(); switch (ltok) { case TokenKind::Number: { auto numAtom = NumberToParserAtom(fc_, this->parserAtoms(), anyChars.currentToken().number()); if (!numAtom) { return errorResult(); } *propAtomOut = numAtom; return newNumber(anyChars.currentToken()); } case TokenKind::BigInt: { Node biNode; MOZ_TRY_VAR(biNode, newBigInt()); return handler_.newSyntheticComputedName(biNode, pos().begin, pos().end); } case TokenKind::String: { auto str = anyChars.currentToken().atom(); *propAtomOut = str; uint32_t index; if (this->parserAtoms().isIndex(str, &index)) { return handler_.newNumber(index, NoDecimal, pos()); } return stringLiteral(); } case TokenKind::LeftBracket: return computedPropertyName(yieldHandling, maybeDecl, propertyNameContext, propList); case TokenKind::PrivateName: { if (propertyNameContext != PropertyNameContext::PropertyNameInClass) { error(JSMSG_ILLEGAL_PRIVATE_FIELD); return errorResult(); } TaggedParserAtomIndex propName = anyChars.currentName(); *propAtomOut = propName; return privateNameReference(propName); } default: { if (!TokenKindIsPossibleIdentifierName(ltok)) { error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(ltok)); return errorResult(); } TaggedParserAtomIndex name = anyChars.currentName(); *propAtomOut = name; return handler_.newObjectLiteralPropertyName(name, pos()); } } } // True if `kind` can be the first token of a PropertyName. static bool TokenKindCanStartPropertyName(TokenKind tt) { return TokenKindIsPossibleIdentifierName(tt) || tt == TokenKind::String || tt == TokenKind::Number || tt == TokenKind::LeftBracket || tt == TokenKind::Mul || tt == TokenKind::BigInt || tt == TokenKind::PrivateName; } template typename ParseHandler::NodeResult GeneralParser::propertyOrMethodName( YieldHandling yieldHandling, PropertyNameContext propertyNameContext, const Maybe& maybeDecl, ListNodeType propList, PropertyType* propType, TaggedParserAtomIndex* propAtomOut) { // We're parsing an object literal, class, or destructuring pattern; // propertyNameContext tells which one. This method parses any of the // following, storing the corresponding PropertyType in `*propType` to tell // the caller what we parsed: // // async [no LineTerminator here] PropertyName // ==> PropertyType::AsyncMethod // async [no LineTerminator here] * PropertyName // ==> PropertyType::AsyncGeneratorMethod // * PropertyName ==> PropertyType::GeneratorMethod // get PropertyName ==> PropertyType::Getter // set PropertyName ==> PropertyType::Setter // accessor PropertyName ==> PropertyType::FieldWithAccessor // PropertyName : ==> PropertyType::Normal // PropertyName ==> see below // // In the last case, where there's not a `:` token to consume, we peek at // (but don't consume) the next token to decide how to set `*propType`. // // `,` or `}` ==> PropertyType::Shorthand // `(` ==> PropertyType::Method // `=`, not in a class ==> PropertyType::CoverInitializedName // '=', in a class ==> PropertyType::Field // any token, in a class ==> PropertyType::Field (ASI) // // The caller must check `*propType` and throw if whatever we parsed isn't // allowed here (for example, a getter in a destructuring pattern). // // This method does *not* match `static` (allowed in classes) or `...` // (allowed in object literals and patterns). The caller must take care of // those before calling this method. TokenKind ltok; if (!tokenStream.getToken(<ok, TokenStream::SlashIsInvalid)) { return errorResult(); } MOZ_ASSERT(ltok != TokenKind::RightCurly, "caller should have handled TokenKind::RightCurly"); // Accept `async` and/or `*`, indicating an async or generator method; // or `get` or `set` or `accessor`, indicating an accessor. bool isGenerator = false; bool isAsync = false; bool isGetter = false; bool isSetter = false; #ifdef ENABLE_DECORATORS bool hasAccessor = false; #endif if (ltok == TokenKind::Async) { // `async` is also a PropertyName by itself (it's a conditional keyword), // so peek at the next token to see if we're really looking at a method. TokenKind tt = TokenKind::Eof; if (!tokenStream.peekTokenSameLine(&tt)) { return errorResult(); } if (TokenKindCanStartPropertyName(tt)) { isAsync = true; tokenStream.consumeKnownToken(tt); ltok = tt; } } if (ltok == TokenKind::Mul) { isGenerator = true; if (!tokenStream.getToken(<ok)) { return errorResult(); } } if (!isAsync && !isGenerator && (ltok == TokenKind::Get || ltok == TokenKind::Set)) { // We have parsed |get| or |set|. Look for an accessor property // name next. TokenKind tt; if (!tokenStream.peekToken(&tt)) { return errorResult(); } if (TokenKindCanStartPropertyName(tt)) { tokenStream.consumeKnownToken(tt); isGetter = (ltok == TokenKind::Get); isSetter = (ltok == TokenKind::Set); } } #ifdef ENABLE_DECORATORS if (!isGenerator && !isAsync && propertyNameContext == PropertyNameInClass && ltok == TokenKind::Accessor) { MOZ_ASSERT(!isGetter && !isSetter); TokenKind tt; if (!tokenStream.peekTokenSameLine(&tt)) { return errorResult(); } // The target rule is `accessor [no LineTerminator here] // ClassElementName[?Yield, ?Await] Initializer[+In, ?Yield, ?Await]opt` if (TokenKindCanStartPropertyName(tt)) { tokenStream.consumeKnownToken(tt); hasAccessor = true; } } #endif Node propName; MOZ_TRY_VAR(propName, propertyName(yieldHandling, propertyNameContext, maybeDecl, propList, propAtomOut)); // Grab the next token following the property/method name. // (If this isn't a colon, we're going to either put it back or throw.) TokenKind tt; if (!tokenStream.getToken(&tt)) { return errorResult(); } if (tt == TokenKind::Colon) { if (isGenerator || isAsync || isGetter || isSetter #ifdef ENABLE_DECORATORS || hasAccessor #endif ) { error(JSMSG_BAD_PROP_ID); return errorResult(); } *propType = PropertyType::Normal; return propName; } if (propertyNameContext != PropertyNameInClass && TokenKindIsPossibleIdentifierName(ltok) && (tt == TokenKind::Comma || tt == TokenKind::RightCurly || tt == TokenKind::Assign)) { #ifdef ENABLE_DECORATORS MOZ_ASSERT(!hasAccessor); #endif if (isGenerator || isAsync || isGetter || isSetter) { error(JSMSG_BAD_PROP_ID); return errorResult(); } anyChars.ungetToken(); *propType = tt == TokenKind::Assign ? PropertyType::CoverInitializedName : PropertyType::Shorthand; return propName; } if (tt == TokenKind::LeftParen) { anyChars.ungetToken(); #ifdef ENABLE_RECORD_TUPLE if (propertyNameContext == PropertyNameInRecord) { // Record & Tuple proposal, section 7.1.1: // RecordPropertyDefinition doesn't cover methods error(JSMSG_BAD_PROP_ID); return errorResult(); } #endif #ifdef ENABLE_DECORATORS if (hasAccessor) { error(JSMSG_BAD_PROP_ID); return errorResult(); } #endif if (isGenerator && isAsync) { *propType = PropertyType::AsyncGeneratorMethod; } else if (isGenerator) { *propType = PropertyType::GeneratorMethod; } else if (isAsync) { *propType = PropertyType::AsyncMethod; } else if (isGetter) { *propType = PropertyType::Getter; } else if (isSetter) { *propType = PropertyType::Setter; } else { *propType = PropertyType::Method; } return propName; } if (propertyNameContext == PropertyNameInClass) { if (isGenerator || isAsync || isGetter || isSetter) { error(JSMSG_BAD_PROP_ID); return errorResult(); } anyChars.ungetToken(); #ifdef ENABLE_DECORATORS if (!hasAccessor) { *propType = PropertyType::Field; } else { *propType = PropertyType::FieldWithAccessor; } #else *propType = PropertyType::Field; #endif return propName; } error(JSMSG_COLON_AFTER_ID); return errorResult(); } template typename ParseHandler::UnaryNodeResult GeneralParser::computedPropertyName( YieldHandling yieldHandling, const Maybe& maybeDecl, PropertyNameContext propertyNameContext, ListNodeType literal) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftBracket)); uint32_t begin = pos().begin; if (maybeDecl) { if (*maybeDecl == DeclarationKind::FormalParameter) { pc_->functionBox()->hasParameterExprs = true; } } else if (propertyNameContext == PropertyNameContext::PropertyNameInLiteral) { handler_.setListHasNonConstInitializer(literal); } Node assignNode; MOZ_TRY_VAR(assignNode, assignExpr(InAllowed, yieldHandling, TripledotProhibited)); if (!mustMatchToken(TokenKind::RightBracket, JSMSG_COMP_PROP_UNTERM_EXPR)) { return errorResult(); } return handler_.newComputedName(assignNode, begin, pos().end); } template typename ParseHandler::ListNodeResult GeneralParser::objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftCurly)); uint32_t openedPos = pos().begin; ListNodeType literal; MOZ_TRY_VAR(literal, handler_.newObjectLiteral(pos().begin)); bool seenPrototypeMutation = false; bool seenCoverInitializedName = false; Maybe declKind = Nothing(); TaggedParserAtomIndex propAtom; for (;;) { TokenKind tt; if (!tokenStream.peekToken(&tt)) { return errorResult(); } if (tt == TokenKind::RightCurly) { break; } if (tt == TokenKind::TripleDot) { tokenStream.consumeKnownToken(TokenKind::TripleDot); uint32_t begin = pos().begin; TokenPos innerPos; if (!tokenStream.peekTokenPos(&innerPos, TokenStream::SlashIsRegExp)) { return errorResult(); } PossibleError possibleErrorInner(*this); Node inner; MOZ_TRY_VAR(inner, assignExpr(InAllowed, yieldHandling, TripledotProhibited, &possibleErrorInner)); if (!checkDestructuringAssignmentTarget( inner, innerPos, &possibleErrorInner, possibleError, TargetBehavior::ForbidAssignmentPattern)) { return errorResult(); } if (!handler_.addSpreadProperty(literal, begin, inner)) { return errorResult(); } } else { TokenPos namePos = anyChars.nextToken().pos; PropertyType propType; Node propName; MOZ_TRY_VAR(propName, propertyOrMethodName( yieldHandling, PropertyNameInLiteral, declKind, literal, &propType, &propAtom)); if (propType == PropertyType::Normal) { TokenPos exprPos; if (!tokenStream.peekTokenPos(&exprPos, TokenStream::SlashIsRegExp)) { return errorResult(); } PossibleError possibleErrorInner(*this); Node propExpr; MOZ_TRY_VAR(propExpr, assignExpr(InAllowed, yieldHandling, TripledotProhibited, &possibleErrorInner)); if (!checkDestructuringAssignmentElement( propExpr, exprPos, &possibleErrorInner, possibleError)) { return errorResult(); } if (propAtom == TaggedParserAtomIndex::WellKnown::proto_()) { if (seenPrototypeMutation) { // Directly report the error when we're definitely not // in a destructuring context. if (!possibleError) { errorAt(namePos.begin, JSMSG_DUPLICATE_PROTO_PROPERTY); return errorResult(); } // Otherwise delay error reporting until we've // determined whether or not we're destructuring. possibleError->setPendingExpressionErrorAt( namePos, JSMSG_DUPLICATE_PROTO_PROPERTY); } seenPrototypeMutation = true; // This occurs *only* if we observe PropertyType::Normal! // Only |__proto__: v| mutates [[Prototype]]. Getters, // setters, method/generator definitions, computed // property name versions of all of these, and shorthands // do not. if (!handler_.addPrototypeMutation(literal, namePos.begin, propExpr)) { return errorResult(); } } else { BinaryNodeType propDef; MOZ_TRY_VAR(propDef, handler_.newPropertyDefinition(propName, propExpr)); handler_.addPropertyDefinition(literal, propDef); } } else if (propType == PropertyType::Shorthand) { /* * Support, e.g., |({x, y} = o)| as destructuring shorthand * for |({x: x, y: y} = o)|, and |var o = {x, y}| as * initializer shorthand for |var o = {x: x, y: y}|. */ TaggedParserAtomIndex name = identifierReference(yieldHandling); if (!name) { return errorResult(); } NameNodeType nameExpr; MOZ_TRY_VAR(nameExpr, identifierReference(name)); if (possibleError) { checkDestructuringAssignmentName(nameExpr, namePos, possibleError); } if (!handler_.addShorthand(literal, handler_.asNameNode(propName), nameExpr)) { return errorResult(); } } else if (propType == PropertyType::CoverInitializedName) { /* * Support, e.g., |({x=1, y=2} = o)| as destructuring * shorthand with default values, as per ES6 12.14.5 */ TaggedParserAtomIndex name = identifierReference(yieldHandling); if (!name) { return errorResult(); } Node lhs; MOZ_TRY_VAR(lhs, identifierReference(name)); tokenStream.consumeKnownToken(TokenKind::Assign); if (!seenCoverInitializedName) { // "shorthand default" or "CoverInitializedName" syntax is // only valid in the case of destructuring. seenCoverInitializedName = true; if (!possibleError) { // Destructuring defaults are definitely not allowed // in this object literal, because of something the // caller knows about the preceding code. For example, // maybe the preceding token is an operator: // |x + {y=z}|. error(JSMSG_COLON_AFTER_ID); return errorResult(); } // Here we set a pending error so that later in the parse, // once we've determined whether or not we're // destructuring, the error can be reported or ignored // appropriately. possibleError->setPendingExpressionErrorAt(pos(), JSMSG_COLON_AFTER_ID); } if (const char* chars = nameIsArgumentsOrEval(lhs)) { // |chars| is "arguments" or "eval" here. if (!strictModeErrorAt(namePos.begin, JSMSG_BAD_STRICT_ASSIGN, chars)) { return errorResult(); } } if (handler_.isArgumentsLength(lhs)) { pc_->sc()->setIneligibleForArgumentsLength(); } Node rhs; MOZ_TRY_VAR(rhs, assignExpr(InAllowed, yieldHandling, TripledotProhibited)); BinaryNodeType propExpr; MOZ_TRY_VAR(propExpr, handler_.newAssignment(ParseNodeKind::AssignExpr, lhs, rhs)); if (!handler_.addPropertyDefinition(literal, propName, propExpr)) { return errorResult(); } } else { TaggedParserAtomIndex funName; bool hasStaticName = !anyChars.isCurrentTokenType(TokenKind::RightBracket) && propAtom; if (hasStaticName) { funName = propAtom; if (propType == PropertyType::Getter || propType == PropertyType::Setter) { funName = prefixAccessorName(propType, propAtom); if (!funName) { return errorResult(); } } } FunctionNodeType funNode; MOZ_TRY_VAR(funNode, methodDefinition(namePos.begin, propType, funName)); AccessorType atype = ToAccessorType(propType); if (!handler_.addObjectMethodDefinition(literal, propName, funNode, atype)) { return errorResult(); } if (possibleError) { possibleError->setPendingDestructuringErrorAt( namePos, JSMSG_BAD_DESTRUCT_TARGET); } } } bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::SlashIsInvalid)) { return errorResult(); } if (!matched) { break; } if (tt == TokenKind::TripleDot && possibleError) { possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA); } } if (!mustMatchToken( TokenKind::RightCurly, [this, openedPos](TokenKind actual) { this->reportMissingClosing(JSMSG_CURLY_AFTER_LIST, JSMSG_CURLY_OPENED, openedPos); })) { return errorResult(); } handler_.setEndPosition(literal, pos().end); return literal; } #ifdef ENABLE_RECORD_TUPLE template typename ParseHandler::ListNodeResult GeneralParser::recordLiteral(YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::HashCurly)); uint32_t openedPos = pos().begin; ListNodeType literal; MOZ_TRY_VAR(literal, handler_.newRecordLiteral(pos().begin)); TaggedParserAtomIndex propAtom; for (;;) { TokenKind tt; if (!tokenStream.peekToken(&tt)) { return errorResult(); } if (tt == TokenKind::RightCurly) { break; } if (tt == TokenKind::TripleDot) { tokenStream.consumeKnownToken(TokenKind::TripleDot); uint32_t begin = pos().begin; TokenPos innerPos; if (!tokenStream.peekTokenPos(&innerPos, TokenStream::SlashIsRegExp)) { return errorResult(); } Node inner; MOZ_TRY_VAR(inner, assignExpr(InAllowed, yieldHandling, TripledotProhibited)); if (!handler_.addSpreadProperty(literal, begin, inner)) { return errorResult(); } } else { TokenPos namePos = anyChars.nextToken().pos; PropertyType propType; Node propName; MOZ_TRY_VAR(propName, propertyOrMethodName(yieldHandling, PropertyNameInRecord, /* maybeDecl */ Nothing(), literal, &propType, &propAtom)); if (propType == PropertyType::Normal) { TokenPos exprPos; if (!tokenStream.peekTokenPos(&exprPos, TokenStream::SlashIsRegExp)) { return errorResult(); } Node propExpr; MOZ_TRY_VAR(propExpr, assignExpr(InAllowed, yieldHandling, TripledotProhibited)); if (propAtom == TaggedParserAtomIndex::WellKnown::proto_()) { errorAt(namePos.begin, JSMSG_RECORD_NO_PROTO); return errorResult(); } BinaryNodeType propDef; MOZ_TRY_VAR(propDef, handler_.newPropertyDefinition(propName, propExpr)); handler_.addPropertyDefinition(literal, propDef); } else if (propType == PropertyType::Shorthand) { /* * Support |var o = #{x, y}| as initializer shorthand for * |var o = #{x: x, y: y}|. */ TaggedParserAtomIndex name = identifierReference(yieldHandling); if (!name) { return errorResult(); } NameNodeType nameExpr; MOZ_TRY_VAR(nameExpr, identifierReference(name)); if (!handler_.addShorthand(literal, handler_.asNameNode(propName), nameExpr)) { return errorResult(); } } else { error(JSMSG_BAD_PROP_ID); return errorResult(); } } bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::SlashIsInvalid)) { return errorResult(); } if (!matched) { break; } } if (!mustMatchToken( TokenKind::RightCurly, [this, openedPos](TokenKind actual) { this->reportMissingClosing(JSMSG_CURLY_AFTER_LIST, JSMSG_CURLY_OPENED, openedPos); })) { return errorResult(); } handler_.setEndPosition(literal, pos().end); return literal; } template typename ParseHandler::ListNodeResult GeneralParser::tupleLiteral(YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::HashBracket)); uint32_t begin = pos().begin; ListNodeType literal; MOZ_TRY_VAR(literal, handler_.newTupleLiteral(begin)); for (uint32_t index = 0;; index++) { if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) { error(JSMSG_ARRAY_INIT_TOO_BIG); return errorResult(); } TokenKind tt; if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) { return errorResult(); } if (tt == TokenKind::RightBracket) { break; } if (tt == TokenKind::TripleDot) { tokenStream.consumeKnownToken(TokenKind::TripleDot, TokenStream::SlashIsRegExp); uint32_t begin = pos().begin; TokenPos innerPos; if (!tokenStream.peekTokenPos(&innerPos, TokenStream::SlashIsRegExp)) { return errorResult(); } Node inner; MOZ_TRY_VAR(inner, assignExpr(InAllowed, yieldHandling, TripledotProhibited)); if (!handler_.addSpreadElement(literal, begin, inner)) { return errorResult(); } } else { TokenPos elementPos; if (!tokenStream.peekTokenPos(&elementPos, TokenStream::SlashIsRegExp)) { return errorResult(); } Node element; MOZ_TRY_VAR(element, assignExpr(InAllowed, yieldHandling, TripledotProhibited)); handler_.addArrayElement(literal, element); } bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::SlashIsRegExp)) { return errorResult(); } if (!matched) { break; } } if (!mustMatchToken(TokenKind::RightBracket, [this, begin](TokenKind actual) { this->reportMissingClosing(JSMSG_BRACKET_AFTER_LIST, JSMSG_BRACKET_OPENED, begin); })) { return errorResult(); } handler_.setEndPosition(literal, pos().end); return literal; } #endif template typename ParseHandler::FunctionNodeResult GeneralParser::methodDefinition( uint32_t toStringStart, PropertyType propType, TaggedParserAtomIndex funName) { FunctionSyntaxKind syntaxKind; switch (propType) { case PropertyType::Getter: syntaxKind = FunctionSyntaxKind::Getter; break; case PropertyType::Setter: syntaxKind = FunctionSyntaxKind::Setter; break; case PropertyType::Method: case PropertyType::GeneratorMethod: case PropertyType::AsyncMethod: case PropertyType::AsyncGeneratorMethod: syntaxKind = FunctionSyntaxKind::Method; break; case PropertyType::Constructor: syntaxKind = FunctionSyntaxKind::ClassConstructor; break; case PropertyType::DerivedConstructor: syntaxKind = FunctionSyntaxKind::DerivedClassConstructor; break; default: MOZ_CRASH("unexpected property type"); } GeneratorKind generatorKind = (propType == PropertyType::GeneratorMethod || propType == PropertyType::AsyncGeneratorMethod) ? GeneratorKind::Generator : GeneratorKind::NotGenerator; FunctionAsyncKind asyncKind = (propType == PropertyType::AsyncMethod || propType == PropertyType::AsyncGeneratorMethod) ? FunctionAsyncKind::AsyncFunction : FunctionAsyncKind::SyncFunction; YieldHandling yieldHandling = GetYieldHandling(generatorKind); FunctionNodeType funNode; MOZ_TRY_VAR(funNode, handler_.newFunction(syntaxKind, pos())); return functionDefinition(funNode, toStringStart, InAllowed, yieldHandling, funName, syntaxKind, generatorKind, asyncKind); } template bool GeneralParser::tryNewTarget( NewTargetNodeType* newTarget) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::New)); *newTarget = null(); NullaryNodeType newHolder; MOZ_TRY_VAR_OR_RETURN(newHolder, handler_.newPosHolder(pos()), false); uint32_t begin = pos().begin; // |new| expects to look for an operand, so we will honor that. TokenKind next; if (!tokenStream.getToken(&next, TokenStream::SlashIsRegExp)) { return false; } // Don't unget the token, since lookahead cannot handle someone calling // getToken() with a different modifier. Callers should inspect // currentToken(). if (next != TokenKind::Dot) { return true; } if (!tokenStream.getToken(&next)) { return false; } if (next != TokenKind::Target) { error(JSMSG_UNEXPECTED_TOKEN, "target", TokenKindToDesc(next)); return false; } if (!pc_->sc()->allowNewTarget()) { errorAt(begin, JSMSG_BAD_NEWTARGET); return false; } NullaryNodeType targetHolder; MOZ_TRY_VAR_OR_RETURN(targetHolder, handler_.newPosHolder(pos()), false); NameNodeType newTargetName; MOZ_TRY_VAR_OR_RETURN(newTargetName, newNewTargetName(), false); MOZ_TRY_VAR_OR_RETURN( *newTarget, handler_.newNewTarget(newHolder, targetHolder, newTargetName), false); return true; } template typename ParseHandler::BinaryNodeResult GeneralParser::importExpr(YieldHandling yieldHandling, bool allowCallSyntax) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Import)); NullaryNodeType importHolder; MOZ_TRY_VAR(importHolder, handler_.newPosHolder(pos())); TokenKind next; if (!tokenStream.getToken(&next)) { return errorResult(); } if (next == TokenKind::Dot) { if (!tokenStream.getToken(&next)) { return errorResult(); } if (next != TokenKind::Meta) { error(JSMSG_UNEXPECTED_TOKEN, "meta", TokenKindToDesc(next)); return errorResult(); } if (parseGoal() != ParseGoal::Module) { errorAt(pos().begin, JSMSG_IMPORT_META_OUTSIDE_MODULE); return errorResult(); } NullaryNodeType metaHolder; MOZ_TRY_VAR(metaHolder, handler_.newPosHolder(pos())); return handler_.newImportMeta(importHolder, metaHolder); } if (next == TokenKind::LeftParen && allowCallSyntax) { Node arg; MOZ_TRY_VAR(arg, assignExpr(InAllowed, yieldHandling, TripledotProhibited)); if (!tokenStream.peekToken(&next, TokenStream::SlashIsRegExp)) { return errorResult(); } Node optionalArg; if (options().importAttributes()) { if (next == TokenKind::Comma) { tokenStream.consumeKnownToken(TokenKind::Comma, TokenStream::SlashIsRegExp); if (!tokenStream.peekToken(&next, TokenStream::SlashIsRegExp)) { return errorResult(); } if (next != TokenKind::RightParen) { MOZ_TRY_VAR(optionalArg, assignExpr(InAllowed, yieldHandling, TripledotProhibited)); if (!tokenStream.peekToken(&next, TokenStream::SlashIsRegExp)) { return errorResult(); } if (next == TokenKind::Comma) { tokenStream.consumeKnownToken(TokenKind::Comma, TokenStream::SlashIsRegExp); } } else { MOZ_TRY_VAR(optionalArg, handler_.newPosHolder(TokenPos(pos().end, pos().end))); } } else { MOZ_TRY_VAR(optionalArg, handler_.newPosHolder(TokenPos(pos().end, pos().end))); } } else { MOZ_TRY_VAR(optionalArg, handler_.newPosHolder(TokenPos(pos().end, pos().end))); } if (!mustMatchToken(TokenKind::RightParen, JSMSG_PAREN_AFTER_ARGS)) { return errorResult(); } Node spec; MOZ_TRY_VAR(spec, handler_.newCallImportSpec(arg, optionalArg)); return handler_.newCallImport(importHolder, spec); } error(JSMSG_UNEXPECTED_TOKEN_NO_EXPECT, TokenKindToDesc(next)); return errorResult(); } template typename ParseHandler::NodeResult GeneralParser::primaryExpr( YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt, PossibleError* possibleError, InvokedPrediction invoked) { MOZ_ASSERT(anyChars.isCurrentTokenType(tt)); AutoCheckRecursionLimit recursion(this->fc_); if (!recursion.check(this->fc_)) { return errorResult(); } switch (tt) { case TokenKind::Function: return functionExpr(pos().begin, invoked, FunctionAsyncKind::SyncFunction); case TokenKind::Class: return classDefinition(yieldHandling, ClassExpression, NameRequired); case TokenKind::LeftBracket: return arrayInitializer(yieldHandling, possibleError); case TokenKind::LeftCurly: return objectLiteral(yieldHandling, possibleError); #ifdef ENABLE_RECORD_TUPLE case TokenKind::HashCurly: return recordLiteral(yieldHandling); case TokenKind::HashBracket: return tupleLiteral(yieldHandling); #endif #ifdef ENABLE_DECORATORS case TokenKind::At: return classDefinition(yieldHandling, ClassExpression, NameRequired); #endif case TokenKind::LeftParen: { TokenKind next; if (!tokenStream.peekToken(&next, TokenStream::SlashIsRegExp)) { return errorResult(); } if (next == TokenKind::RightParen) { // Not valid expression syntax, but this is valid in an arrow function // with no params: `() => body`. tokenStream.consumeKnownToken(TokenKind::RightParen, TokenStream::SlashIsRegExp); if (!tokenStream.peekToken(&next)) { return errorResult(); } if (next != TokenKind::Arrow) { error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TokenKind::RightParen)); return errorResult(); } // Now just return something that will allow parsing to continue. // It doesn't matter what; when we reach the =>, we will rewind and // reparse the whole arrow function. See Parser::assignExpr. return handler_.newNullLiteral(pos()); } // Pass |possibleError| to support destructuring in arrow parameters. Node expr; MOZ_TRY_VAR(expr, exprInParens(InAllowed, yieldHandling, TripledotAllowed, possibleError)); if (!mustMatchToken(TokenKind::RightParen, JSMSG_PAREN_IN_PAREN)) { return errorResult(); } return handler_.parenthesize(expr); } case TokenKind::TemplateHead: return templateLiteral(yieldHandling); case TokenKind::NoSubsTemplate: return noSubstitutionUntaggedTemplate(); case TokenKind::String: return stringLiteral(); default: { if (!TokenKindIsPossibleIdentifier(tt)) { error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt)); return errorResult(); } if (tt == TokenKind::Async) { TokenKind nextSameLine = TokenKind::Eof; if (!tokenStream.peekTokenSameLine(&nextSameLine)) { return errorResult(); } if (nextSameLine == TokenKind::Function) { uint32_t toStringStart = pos().begin; tokenStream.consumeKnownToken(TokenKind::Function); return functionExpr(toStringStart, PredictUninvoked, FunctionAsyncKind::AsyncFunction); } } TaggedParserAtomIndex name = identifierReference(yieldHandling); if (!name) { return errorResult(); } return identifierReference(name); } case TokenKind::RegExp: return newRegExp(); case TokenKind::Number: return newNumber(anyChars.currentToken()); case TokenKind::BigInt: return newBigInt(); case TokenKind::True: return handler_.newBooleanLiteral(true, pos()); case TokenKind::False: return handler_.newBooleanLiteral(false, pos()); case TokenKind::This: { NameNodeType thisName = null(); if (pc_->sc()->hasFunctionThisBinding()) { MOZ_TRY_VAR(thisName, newThisName()); } return handler_.newThisLiteral(pos(), thisName); } case TokenKind::Null: return handler_.newNullLiteral(pos()); case TokenKind::TripleDot: { // This isn't valid expression syntax, but it's valid in an arrow // function as a trailing rest param: `(a, b, ...rest) => body`. Check // if it's directly under // CoverParenthesizedExpressionAndArrowParameterList, and check for a // name, closing parenthesis, and arrow, and allow it only if all are // present. if (tripledotHandling != TripledotAllowed) { error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(tt)); return errorResult(); } TokenKind next; if (!tokenStream.getToken(&next)) { return errorResult(); } if (next == TokenKind::LeftBracket || next == TokenKind::LeftCurly) { // Validate, but don't store the pattern right now. The whole arrow // function is reparsed in functionFormalParametersAndBody(). MOZ_TRY(destructuringDeclaration(DeclarationKind::CoverArrowParameter, yieldHandling, next)); } else { // This doesn't check that the provided name is allowed, e.g. if // the enclosing code is strict mode code, any of "let", "yield", // or "arguments" should be prohibited. Argument-parsing code // handles that. if (!TokenKindIsPossibleIdentifier(next)) { error(JSMSG_UNEXPECTED_TOKEN, "rest argument name", TokenKindToDesc(next)); return errorResult(); } } if (!tokenStream.getToken(&next)) { return errorResult(); } if (next != TokenKind::RightParen) { error(JSMSG_UNEXPECTED_TOKEN, "closing parenthesis", TokenKindToDesc(next)); return errorResult(); } if (!tokenStream.peekToken(&next)) { return errorResult(); } if (next != TokenKind::Arrow) { // Advance the scanner for proper error location reporting. tokenStream.consumeKnownToken(next); error(JSMSG_UNEXPECTED_TOKEN, "'=>' after argument list", TokenKindToDesc(next)); return errorResult(); } anyChars.ungetToken(); // put back right paren // Return an arbitrary expression node. See case TokenKind::RightParen // above. return handler_.newNullLiteral(pos()); } } } template typename ParseHandler::NodeResult GeneralParser::exprInParens( InHandling inHandling, YieldHandling yieldHandling, TripledotHandling tripledotHandling, PossibleError* possibleError /* = nullptr */) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftParen)); return expr(inHandling, yieldHandling, tripledotHandling, possibleError, PredictInvoked); } template class PerHandlerParser; template class PerHandlerParser; template class GeneralParser; template class GeneralParser; template class GeneralParser; template class GeneralParser; template class Parser; template class Parser; template class Parser; template class Parser; } // namespace js::frontend