/* -*- 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/Utf8.h" #include "mozilla/Variant.h" #include #include #include #include "jsnum.h" #include "jstypes.h" #include "frontend/BytecodeCompiler.h" #include "frontend/FoldConstants.h" #include "frontend/FunctionSyntaxKind.h" // FunctionSyntaxKind #include "frontend/ModuleSharedContext.h" #include "frontend/ParseNode.h" #include "frontend/ParseNodeVerify.h" #include "frontend/ParserAtom.h" // TaggedParserAtomIndex, ParserAtomsTable, ParserAtom #include "frontend/ScriptIndex.h" // ScriptIndex #include "frontend/TokenStream.h" #include "irregexp/RegExpAPI.h" #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 "vm/WellKnownAtom.h" // js_*_str #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; /* * We use JSContext.tempLifoAlloc to allocate parsed objects and place them * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc * arenas containing the entries must be alive until we are done with * scanning, parsing and code generation for the whole script or top-level * function. */ 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; } /* * We use JSContext.tempLifoAlloc to allocate parsed objects and place them * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc * arenas containing the entries must be alive until we are done with * scanning, parsing and code generation for the whole script or top-level * function. */ 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::ListNodeType GeneralParser::parse() { MOZ_ASSERT(checkOptionsCalled_); SourceExtent extent = SourceExtent::makeGlobalExtent( /* len = */ 0, options().lineno, 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 null(); } ParseContext::VarScope varScope(this); if (!varScope.init(pc_)) { return null(); } ListNodeType stmtList = statementList(YieldIsName); if (!stmtList) { return null(); } TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return null(); } if (tt != TokenKind::Eof) { error(JSMSG_GARBAGE_AFTER_INPUT, "script", TokenKindToDesc(tt)); return null(); } if (!CheckParseTree(this->fc_, alloc_, stmtList)) { return null(); } 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 null(); } } stmtList = handler_.asList(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, column; tokenStream.computeLineAndColumn(openedPos, &line, &column); const size_t MaxWidth = sizeof("4294967295"); char columnNumber[MaxWidth]; SprintfLiteral(columnNumber, "%" PRIu32, column); char lineNumber[MaxWidth]; SprintfLiteral(lineNumber, "%" PRIu32, line); if (!notes->addNoteASCII(this->fc_, getFilename(), 0, line, column, GetErrorMessage, nullptr, noteNumber, lineNumber, columnNumber)) { return; } errorWithNotes(std::move(notes), errorNumber); } template void GeneralParser::reportRedeclaration( TaggedParserAtomIndex name, DeclarationKind prevKind, TokenPos pos, uint32_t prevPos) { UniqueChars bytes = this->parserAtoms().toPrintableString(name); if (!bytes) { ReportOutOfMemory(this->fc_); return; } if (prevPos == DeclaredNameInfo::npos) { errorAt(pos.begin, JSMSG_REDECLARED_VAR, DeclarationKindString(prevKind), bytes.get()); return; } auto notes = MakeUnique(); if (!notes) { ReportOutOfMemory(this->fc_); return; } uint32_t line, column; tokenStream.computeLineAndColumn(prevPos, &line, &column); const size_t MaxWidth = sizeof("4294967295"); char columnNumber[MaxWidth]; SprintfLiteral(columnNumber, "%" PRIu32, column); char lineNumber[MaxWidth]; SprintfLiteral(lineNumber, "%" PRIu32, line); if (!notes->addNoteASCII(this->fc_, getFilename(), 0, line, column, GetErrorMessage, nullptr, JSMSG_REDECLARED_PREV, lineNumber, columnNumber)) { return; } errorWithNotesAt(std::move(notes), pos.begin, JSMSG_REDECLARED_VAR, DeclarationKindString(prevKind), bytes.get()); } // 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 = newName(name); if (!paramNode) { return 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 more complex than calling checkLexicalDeclarationDirectlyWithin- // Block and are done in checkFunctionDefinition. 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 " "tryDeclareVarForAnnexBLexicalFunction"); 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: case PropertyType::FieldWithAccessor: kind = PrivateNameKind::Field; 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; } } reportRedeclaration(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. // // As an exception however, we continue to track private name references, // as the used names tracker is used to provide early errors for undeclared // private name references ParseContext::Scope* scope = pc_->innermostScope(); if (pc_->sc()->isGlobalContext() && scope == &pc_->varScope() && visibility == NameVisibility::Public) { 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; } 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_); } 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_); } 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_); } 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); } 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_); } 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_); } 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::dotPrivateBrand()) { 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::dotPrivateBrand()); return Some(bindings); } Maybe ParserBase::newClassBodyScopeData( ParseContext::Scope& scope) { return NewClassBodyScopeData(fc_, scope, stencilAlloc(), pc_); } template <> SyntaxParseHandler::LexicalScopeNodeType PerHandlerParser::finishLexicalScope( ParseContext::Scope& scope, Node body, ScopeKind kind) { if (!propagateFreeNamesAndMarkClosedOverBindings(scope)) { return null(); } return handler_.newLexicalScope(body); } template <> LexicalScopeNode* PerHandlerParser::finishLexicalScope( ParseContext::Scope& scope, ParseNode* body, ScopeKind kind) { if (!propagateFreeNamesAndMarkClosedOverBindings(scope)) { return nullptr; } Maybe bindings = newLexicalScopeData(scope); if (!bindings) { return nullptr; } return handler_.newLexicalScope(*bindings, body, kind); } template <> SyntaxParseHandler::ClassBodyScopeNodeType PerHandlerParser::finishClassBodyScope( ParseContext::Scope& scope, ListNodeType body) { if (!propagateFreeNamesAndMarkClosedOverBindings(scope)) { return null(); } return handler_.newClassBodyScope(body); } template <> ClassBodyScopeNode* PerHandlerParser::finishClassBodyScope( ParseContext::Scope& scope, ListNode* body) { if (!propagateFreeNamesAndMarkClosedOverBindings(scope)) { return nullptr; } Maybe bindings = newClassBodyScopeData(scope); if (!bindings) { return nullptr; } 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 LexicalScopeNode* Parser::evalBody( EvalSharedContext* evalsc) { SourceParseContext evalpc(this, evalsc, /* newDirectives = */ nullptr); if (!evalpc.init()) { return nullptr; } ParseContext::VarScope varScope(this); if (!varScope.init(pc_)) { return nullptr; } LexicalScopeNode* body; { // All evals have an implicit non-extensible lexical scope. ParseContext::Scope lexicalScope(this); if (!lexicalScope.init(pc_)) { return nullptr; } ListNode* list = statementList(YieldIsName); if (!list) { return nullptr; } if (!checkStatementsEOF()) { return nullptr; } // Private names not lexically defined must trigger a syntax error. if (!checkForUndefinedPrivateFields(evalsc)) { return nullptr; } body = finishLexicalScope(lexicalScope, list); if (!body) { return nullptr; } } #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 null(); } 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 null(); } } body = handler_.asLexicalScope(node); if (!this->setSourceMapInfo()) { return nullptr; } if (pc_->sc()->strict()) { if (!propagateFreeNamesAndMarkClosedOverBindings(varScope)) { return nullptr; } } 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 nullptr; } } Maybe bindings = newEvalScopeData(pc_->varScope()); if (!bindings) { return nullptr; } evalsc->bindings = *bindings; return body; } template ListNode* Parser::globalBody( GlobalSharedContext* globalsc) { SourceParseContext globalpc(this, globalsc, /* newDirectives = */ nullptr); if (!globalpc.init()) { return nullptr; } ParseContext::VarScope varScope(this); if (!varScope.init(pc_)) { return nullptr; } ListNode* body = statementList(YieldIsName); if (!body) { return nullptr; } if (!checkStatementsEOF()) { return nullptr; } if (!CheckParseTree(this->fc_, alloc_, body)) { return null(); } if (!checkForUndefinedPrivateFields()) { return null(); } 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 null(); } } body = &node->as(); if (!this->setSourceMapInfo()) { return nullptr; } // 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 nullptr; } Maybe bindings = newGlobalScopeData(pc_->varScope()); if (!bindings) { return nullptr; } globalsc->bindings = *bindings; return body; } template ModuleNode* Parser::moduleBody( ModuleSharedContext* modulesc) { MOZ_ASSERT(checkOptionsCalled_); this->compilationState_.moduleMetadata = fc_->getAllocator()->template new_(); if (!this->compilationState_.moduleMetadata) { return null(); } SourceParseContext modulepc(this, modulesc, nullptr); if (!modulepc.init()) { return null(); } ParseContext::VarScope varScope(this); if (!varScope.init(pc_)) { return null(); } ModuleNodeType moduleNode = handler_.newModule(pos()); if (!moduleNode) { return null(); } AutoAwaitIsKeyword awaitIsKeyword( this, AwaitIsModuleKeyword); ListNode* stmtList = statementList(YieldIsName); if (!stmtList) { return null(); } MOZ_ASSERT(stmtList->isKind(ParseNodeKind::StatementList)); moduleNode->setBody(&stmtList->as()); if (pc_->isAsync()) { if (!noteUsedName(TaggedParserAtomIndex::WellKnown::dotGenerator())) { return null(); } if (!pc_->declareTopLevelDotGeneratorName()) { return null(); } } TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return null(); } if (tt != TokenKind::Eof) { error(JSMSG_GARBAGE_AFTER_INPUT, "module", TokenKindToDesc(tt)); return null(); } // 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 null(); } // 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 null(); } errorNoOffset(JSMSG_MISSING_EXPORT, str.get()); return null(); } 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::starNamespaceStar(), DeclarationKind::Const, pos())) { return nullptr; } modulepc.varScope() .lookupDeclaredName(TaggedParserAtomIndex::WellKnown::starNamespaceStar()) ->value() ->setClosedOver(); if (options().deoptimizeModuleGlobalVars) { for (BindingIter bi = modulepc.varScope().bindings(pc_); bi; bi++) { bi.setClosedOver(); } } if (!CheckParseTree(this->fc_, alloc_, stmtList)) { return null(); } 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 null(); } } stmtList = &node->as(); if (!this->setSourceMapInfo()) { return null(); } // Private names not lexically defined must trigger a syntax error. if (!checkForUndefinedPrivateFields()) { return null(); } if (!propagateFreeNamesAndMarkClosedOverBindings(modulepc.varScope())) { return null(); } Maybe bindings = newModuleScopeData(modulepc.varScope()); if (!bindings) { return nullptr; } modulesc->bindings = *bindings; return moduleNode; } template SyntaxParseHandler::ModuleNodeType Parser::moduleBody( ModuleSharedContext* modulesc) { MOZ_ALWAYS_FALSE(abortIfSyntaxParser()); return SyntaxParseHandler::NodeFailure; } template typename ParseHandler::NameNodeType PerHandlerParser::newInternalDotName(TaggedParserAtomIndex name) { NameNodeType nameNode = newName(name); if (!nameNode) { return null(); } if (!noteUsedName(name)) { return null(); } return nameNode; } template typename ParseHandler::NameNodeType PerHandlerParser::newThisName() { return newInternalDotName(TaggedParserAtomIndex::WellKnown::dotThis()); } template typename ParseHandler::NameNodeType PerHandlerParser::newNewTargetName() { return newInternalDotName(TaggedParserAtomIndex::WellKnown::dotNewTarget()); } template typename ParseHandler::NameNodeType PerHandlerParser::newDotGeneratorName() { return newInternalDotName(TaggedParserAtomIndex::WellKnown::dotGenerator()); } 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 FunctionNode* 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 null(); } if (asyncKind == FunctionAsyncKind::AsyncFunction) { MOZ_ASSERT(tt == TokenKind::Async); if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return null(); } } MOZ_ASSERT(tt == TokenKind::Function); if (!tokenStream.getToken(&tt)) { return null(); } if (generatorKind == GeneratorKind::Generator) { MOZ_ASSERT(tt == TokenKind::Mul); if (!tokenStream.getToken(&tt)) { return null(); } } // Skip function name, if present. TaggedParserAtomIndex explicitName; if (TokenKindIsPossibleIdentifierName(tt)) { explicitName = anyChars.currentName(); } else { anyChars.ungetToken(); } FunctionNodeType funNode = handler_.newFunction(syntaxKind, pos()); if (!funNode) { return null(); } ParamsBodyNodeType argsbody = handler_.newParamsBody(pos()); if (!argsbody) { return null(); } 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 null(); } // 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 null(); } YieldHandling yieldHandling = GetYieldHandling(generatorKind); AwaitHandling awaitHandling = GetAwaitHandling(asyncKind); AutoAwaitIsKeyword awaitIsKeyword(this, awaitHandling); if (!functionFormalParametersAndBody(InAllowed, yieldHandling, &funNode, syntaxKind, parameterListEnd, /* isStandaloneFunction = */ true)) { return null(); } if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return null(); } if (tt != TokenKind::Eof) { error(JSMSG_GARBAGE_AFTER_INPUT, "function body", TokenKindToDesc(tt)); return null(); } if (!CheckParseTree(this->fc_, alloc_, funNode)) { return null(); } 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 null(); } } funNode = &node->as(); if (!checkForUndefinedPrivateFields(nullptr)) { return null(); } if (!this->setSourceMapInfo()) { return null(); } return funNode; } template typename ParseHandler::LexicalScopeNodeType 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(); body = statementList(yieldHandling); if (!body) { return null(); } // 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 null(); } } } 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()) { stmtList = handler_.newStatementList(pos()); if (!stmtList) { return null(); } } Node kid = assignExpr(inHandling, yieldHandling, TripledotProhibited); if (!kid) { return null(); } body = handler_.newExpressionBody(kid); if (!body) { return null(); } 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 null(); } if (pc_->isGenerator()) { NameNodeType generator = newDotGeneratorName(); if (!generator) { return null(); } if (!handler_.prependInitialYield(handler_.asList(body), generator)) { return null(); } } } // 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 null(); } if (!pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings)) { return null(); } if (!pc_->declareNewTarget(usedNames_, canSkipLazyClosedOverBindings)) { return null(); } } 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, 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 = handler_.newParamsBody(pos()); if (!argsbody) { return 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 = handler_.newParamsBody(pos()); if (!argsbody) { return 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 = destructuringDeclarationWithoutYieldOrAwait( DeclarationKind::FormalParameter, yieldHandling, tt); if (!destruct) { return 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 = assignExprWithoutYieldOrAwait(yieldHandling); if (!def_expr) { return 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 = expr(InAllowed, yieldHandling, TripledotProhibited); if (!pn) { return 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 = handler_.newCallSiteObject(pos().begin); if (!callSiteObjNode) { return 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::ListNodeType GeneralParser::templateLiteral( YieldHandling yieldHandling) { NameNodeType literal = noSubstitutionUntaggedTemplate(); if (!literal) { return null(); } ListNodeType nodeList = handler_.newList(ParseNodeKind::TemplateStringListExpr, literal); if (!nodeList) { return null(); } TokenKind tt; do { if (!addExprAndGetNextTemplStrToken(yieldHandling, nodeList, &tt)) { return null(); } literal = noSubstitutionUntaggedTemplate(); if (!literal) { return null(); } handler_.addList(nodeList, literal); } while (tt == TokenKind::TemplateHead); return nodeList; } template typename ParseHandler::FunctionNodeType 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 null(); } 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 null(); } // 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); SyntaxParseHandler::Node syntaxNode = syntaxParser->innerFunctionForFunctionBox( SyntaxParseHandler::NodeGeneric, pc_, funbox, inHandling, yieldHandling, kind, newDirectives); if (!syntaxNode) { 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 = innerFunction(*funNode, pc_, explicitName, flags, toStringStart, inHandling, yieldHandling, kind, generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives); if (!innerFunc) { return 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 = innerFunction(*funNode, pc_, explicitName, flags, toStringStart, inHandling, yieldHandling, kind, generatorKind, asyncKind, tryAnnexB, inheritedDirectives, newDirectives); if (!innerFunc) { return 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::FunctionNodeType 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 null(); } if (!functionFormalParametersAndBody(inHandling, yieldHandling, &funNode, kind)) { return null(); } if (!leaveInnerFunction(outerpc)) { return null(); } return funNode; } template typename ParseHandler::FunctionNodeType 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 null(); } funbox->initWithEnclosingParseContext(outerpc, kind); FunctionNodeType innerFunc = innerFunctionForFunctionBox( funNode, outerpc, funbox, inHandling, yieldHandling, kind, newDirectives); if (!innerFunc) { return null(); } // Append possible Annex B function box only upon successfully parsing. if (tryAnnexB) { if (!pc_->innermostScope()->addPossibleAnnexBFunctionBox(pc_, funbox)) { return null(); } } return innerFunc; } template bool GeneralParser::appendToCallSiteObj( CallSiteNodeType callSiteObj) { Node cookedNode = noSubstitutionTaggedTemplate(); if (!cookedNode) { return false; } auto atom = tokenStream.getRawTemplateStringAtom(); if (!atom) { return false; } NameNodeType rawNode = handler_.newTemplateStringLiteral(atom, pos()); if (!rawNode) { return false; } handler_.addToCallSiteObject(callSiteObj, rawNode, cookedNode); return true; } template FunctionNode* Parser::standaloneLazyFunction( CompilationInput& input, uint32_t toStringStart, bool strict, GeneratorKind generatorKind, FunctionAsyncKind asyncKind) { MOZ_ASSERT(checkOptionsCalled_); FunctionSyntaxKind syntaxKind = input.functionSyntaxKind(); FunctionNodeType funNode = handler_.newFunction(syntaxKind, pos()); if (!funNode) { return null(); } TaggedParserAtomIndex displayAtom = this->getCompilationState().previousParseCache.displayAtom(); Directives directives(strict); FunctionBox* funbox = newFunctionBox(funNode, displayAtom, input.functionFlags(), toStringStart, directives, generatorKind, asyncKind); if (!funbox) { return null(); } 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 null(); } // 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 null(); } 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 null(); } } else { if (!functionFormalParametersAndBody(InAllowed, yieldHandling, &funNode, syntaxKind)) { MOZ_ASSERT(directives == newDirectives); return null(); } } if (!CheckParseTree(this->fc_, alloc_, funNode)) { return null(); } 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 null(); } } 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::dotInitializers())) { return false; } } // 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); body = functionBody(inHandling, bodyYieldHandling, kind, bodyType); if (!body) { return 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::FunctionNodeType 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 null(); } } TokenKind tt; if (!tokenStream.getToken(&tt)) { return null(); } GeneratorKind generatorKind = GeneratorKind::NotGenerator; if (tt == TokenKind::Mul) { generatorKind = GeneratorKind::Generator; if (!tokenStream.getToken(&tt)) { return null(); } } TaggedParserAtomIndex name; if (TokenKindIsPossibleIdentifier(tt)) { name = bindingIdentifier(yieldHandling); if (!name) { return null(); } } 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 null(); } // 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 null(); } FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::Statement; FunctionNodeType funNode = handler_.newFunction(syntaxKind, pos()); if (!funNode) { return null(); } // 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::FunctionNodeType 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 null(); } if (tt == TokenKind::Mul) { generatorKind = GeneratorKind::Generator; if (!tokenStream.getToken(&tt)) { return null(); } } YieldHandling yieldHandling = GetYieldHandling(generatorKind); TaggedParserAtomIndex name; if (TokenKindIsPossibleIdentifier(tt)) { name = bindingIdentifier(yieldHandling); if (!name) { return null(); } } else { anyChars.ungetToken(); } FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::Expression; FunctionNodeType funNode = handler_.newFunction(syntaxKind, pos()); if (!funNode) { return null(); } 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::useStrict() && 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::useAsm() && 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::ListNodeType GeneralParser::statementList(YieldHandling yieldHandling) { AutoCheckRecursionLimit recursion(this->fc_); if (!recursion.check(this->fc_)) { return null(); } ListNodeType stmtList = handler_.newStatementList(pos()); if (!stmtList) { return null(); } 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 null(); } if (tt == TokenKind::Eof || tt == TokenKind::RightCurly) { TokenPos pos; if (!tokenStream.peekTokenPos(&pos, TokenStream::SlashIsRegExp)) { return null(); } handler_.setListEndPosition(stmtList, pos); break; } if (afterReturn) { if (!tokenStream.peekOffset(&statementBegin, TokenStream::SlashIsRegExp)) { return null(); } } Node next = statementListItem(yieldHandling, canHaveDirectives); if (!next) { if (anyChars.isEOF()) { isUnexpectedEOF_ = true; } return null(); } if (!warnedAboutStatementsAfterReturn) { if (afterReturn) { if (!handler_.isStatementPermittedAfterReturnStatement(next)) { if (!warningAt(statementBegin, JSMSG_STMT_AFTER_RETURN)) { return null(); } warnedAboutStatementsAfterReturn = true; } } else if (handler_.isReturnStatement(next)) { afterReturn = true; } } if (canHaveDirectives) { if (!maybeParseDirective(stmtList, next, &canHaveDirectives)) { return null(); } } handler_.addStatementToList(stmtList, next); } return stmtList; } template typename ParseHandler::Node GeneralParser::condition( InHandling inHandling, YieldHandling yieldHandling) { if (!mustMatchToken(TokenKind::LeftParen, JSMSG_PAREN_BEFORE_COND)) { return null(); } Node pn = exprInParens(inHandling, yieldHandling, TripledotProhibited); if (!pn) { return null(); } if (!mustMatchToken(TokenKind::RightParen, JSMSG_PAREN_AFTER_COND)) { return null(); } 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::BinaryNodeType GeneralParser::bindingInitializer( Node lhs, DeclarationKind kind, YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Assign)); if (kind == DeclarationKind::FormalParameter) { pc_->functionBox()->hasParameterExprs = true; } Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited); if (!rhs) { return null(); } BinaryNodeType assign = handler_.newAssignment(ParseNodeKind::AssignExpr, lhs, rhs); if (!assign) { return null(); } return assign; } template typename ParseHandler::NameNodeType GeneralParser::bindingIdentifier( DeclarationKind kind, YieldHandling yieldHandling) { TaggedParserAtomIndex name = bindingIdentifier(yieldHandling); if (!name) { return null(); } NameNodeType binding = newName(name); if (!binding || !noteDeclaredName(name, kind, pos())) { return null(); } return binding; } template typename ParseHandler::Node 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 null(); } return bindingIdentifier(kind, yieldHandling); } template typename ParseHandler::ListNodeType GeneralParser::objectBindingPattern( DeclarationKind kind, YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftCurly)); AutoCheckRecursionLimit recursion(this->fc_); if (!recursion.check(this->fc_)) { return null(); } uint32_t begin = pos().begin; ListNodeType literal = handler_.newObjectLiteral(begin); if (!literal) { return null(); } Maybe declKind = Some(kind); TaggedParserAtomIndex propAtom; for (;;) { TokenKind tt; if (!tokenStream.peekToken(&tt)) { return null(); } 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 null(); } if (!TokenKindIsPossibleIdentifierName(tt)) { error(JSMSG_NO_VARIABLE_NAME); return null(); } NameNodeType inner = bindingIdentifier(kind, yieldHandling); if (!inner) { return null(); } if (!handler_.addSpreadProperty(literal, begin, inner)) { return null(); } } else { TokenPos namePos = anyChars.nextToken().pos; PropertyType propType; Node propName = propertyOrMethodName(yieldHandling, PropertyNameInPattern, declKind, literal, &propType, &propAtom); if (!propName) { return null(); } if (propType == PropertyType::Normal) { // Handle e.g., |var {p: x} = o| and |var {p: x=0} = o|. if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return null(); } Node binding = bindingIdentifierOrPattern(kind, yieldHandling, tt); if (!binding) { return null(); } bool hasInitializer; if (!tokenStream.matchToken(&hasInitializer, TokenKind::Assign, TokenStream::SlashIsRegExp)) { return null(); } Node bindingExpr = hasInitializer ? bindingInitializer(binding, kind, yieldHandling) : binding; if (!bindingExpr) { return null(); } if (!handler_.addPropertyDefinition(literal, propName, bindingExpr)) { return null(); } } 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 = bindingIdentifier(kind, yieldHandling); if (!binding) { return null(); } if (!handler_.addShorthand(literal, handler_.asName(propName), binding)) { return null(); } } 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 = bindingIdentifier(kind, yieldHandling); if (!binding) { return null(); } tokenStream.consumeKnownToken(TokenKind::Assign); BinaryNodeType bindingExpr = bindingInitializer(binding, kind, yieldHandling); if (!bindingExpr) { return null(); } if (!handler_.addPropertyDefinition(literal, propName, bindingExpr)) { return null(); } } else { errorAt(namePos.begin, JSMSG_NO_VARIABLE_NAME); return null(); } } bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::SlashIsInvalid)) { return null(); } if (!matched) { break; } if (tt == TokenKind::TripleDot) { error(JSMSG_REST_WITH_COMMA); return null(); } } if (!mustMatchToken(TokenKind::RightCurly, [this, begin](TokenKind actual) { this->reportMissingClosing(JSMSG_CURLY_AFTER_LIST, JSMSG_CURLY_OPENED, begin); })) { return null(); } handler_.setEndPosition(literal, pos().end); return literal; } template typename ParseHandler::ListNodeType GeneralParser::arrayBindingPattern( DeclarationKind kind, YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftBracket)); AutoCheckRecursionLimit recursion(this->fc_); if (!recursion.check(this->fc_)) { return null(); } uint32_t begin = pos().begin; ListNodeType literal = handler_.newArrayLiteral(begin); if (!literal) { return null(); } uint32_t index = 0; for (;; index++) { if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) { error(JSMSG_ARRAY_INIT_TOO_BIG); return null(); } TokenKind tt; if (!tokenStream.getToken(&tt)) { return null(); } if (tt == TokenKind::RightBracket) { anyChars.ungetToken(); break; } if (tt == TokenKind::Comma) { if (!handler_.addElision(literal, pos())) { return null(); } } else if (tt == TokenKind::TripleDot) { uint32_t begin = pos().begin; TokenKind tt; if (!tokenStream.getToken(&tt)) { return null(); } Node inner = bindingIdentifierOrPattern(kind, yieldHandling, tt); if (!inner) { return null(); } if (!handler_.addSpreadElement(literal, begin, inner)) { return null(); } } else { Node binding = bindingIdentifierOrPattern(kind, yieldHandling, tt); if (!binding) { return null(); } bool hasInitializer; if (!tokenStream.matchToken(&hasInitializer, TokenKind::Assign, TokenStream::SlashIsRegExp)) { return null(); } Node element = hasInitializer ? bindingInitializer(binding, kind, yieldHandling) : binding; if (!element) { return null(); } 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 null(); } if (!matched) { break; } if (tt == TokenKind::TripleDot) { error(JSMSG_REST_WITH_COMMA); return null(); } } } if (!mustMatchToken(TokenKind::RightBracket, [this, begin](TokenKind actual) { this->reportMissingClosing(JSMSG_BRACKET_AFTER_LIST, JSMSG_BRACKET_OPENED, begin); })) { return null(); } handler_.setEndPosition(literal, pos().end); return literal; } template typename ParseHandler::Node GeneralParser::destructuringDeclaration( DeclarationKind kind, YieldHandling yieldHandling, TokenKind tt) { MOZ_ASSERT(anyChars.isCurrentTokenType(tt)); MOZ_ASSERT(tt == TokenKind::LeftBracket || tt == TokenKind::LeftCurly); return tt == TokenKind::LeftBracket ? arrayBindingPattern(kind, yieldHandling) : objectBindingPattern(kind, yieldHandling); } template typename ParseHandler::Node GeneralParser::destructuringDeclarationWithoutYieldOrAwait( DeclarationKind kind, YieldHandling yieldHandling, TokenKind tt) { uint32_t startYieldOffset = pc_->lastYieldOffset; uint32_t startAwaitOffset = pc_->lastAwaitOffset; Node res = destructuringDeclaration(kind, yieldHandling, tt); if (res) { if (pc_->lastYieldOffset != startYieldOffset) { errorAt(pc_->lastYieldOffset, JSMSG_YIELD_IN_PARAMETER); return null(); } if (pc_->lastAwaitOffset != startAwaitOffset) { errorAt(pc_->lastAwaitOffset, JSMSG_AWAIT_IN_PARAMETER); return null(); } } return res; } template typename ParseHandler::LexicalScopeNodeType 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 null(); } ListNodeType list = statementList(yieldHandling); if (!list) { return null(); } if (!mustMatchToken(TokenKind::RightCurly, [this, errorNumber, openedPos](TokenKind actual) { this->reportMissingClosing(errorNumber, JSMSG_CURLY_OPENED, openedPos); })) { return null(); } return finishLexicalScope(scope, list); } template typename ParseHandler::Node GeneralParser::expressionAfterForInOrOf( ParseNodeKind forHeadKind, YieldHandling yieldHandling) { MOZ_ASSERT(forHeadKind == ParseNodeKind::ForIn || forHeadKind == ParseNodeKind::ForOf); Node pn = forHeadKind == ParseNodeKind::ForOf ? assignExpr(InAllowed, yieldHandling, TripledotProhibited) : expr(InAllowed, yieldHandling, TripledotProhibited); return pn; } template typename ParseHandler::Node 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 = destructuringDeclaration(declKind, yieldHandling, tt); if (!pattern) { return null(); } if (initialDeclaration && forHeadKind) { bool isForIn, isForOf; if (!matchInOrOf(&isForIn, &isForOf)) { return null(); } if (isForIn) { *forHeadKind = ParseNodeKind::ForIn; } else if (isForOf) { *forHeadKind = ParseNodeKind::ForOf; } else { *forHeadKind = ParseNodeKind::ForHead; } if (*forHeadKind != ParseNodeKind::ForHead) { *forInOrOfExpression = expressionAfterForInOrOf(*forHeadKind, yieldHandling); if (!*forInOrOfExpression) { return null(); } return pattern; } } if (!mustMatchToken(TokenKind::Assign, JSMSG_BAD_DESTRUCT_DECL)) { return null(); } Node init = assignExpr(forHeadKind ? InProhibited : InAllowed, yieldHandling, TripledotProhibited); if (!init) { return null(); } return handler_.newAssignment(ParseNodeKind::AssignExpr, pattern, init); } template typename ParseHandler::AssignmentNodeType 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 null(); } Node initializer = assignExpr(forHeadKind ? InProhibited : InAllowed, yieldHandling, TripledotProhibited); if (!initializer) { return null(); } if (forHeadKind && initialDeclaration) { bool isForIn, isForOf; if (!matchInOrOf(&isForIn, &isForOf)) { return null(); } // 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 null(); } 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 null(); } // 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 null(); } *forInOrOfExpression = expressionAfterForInOrOf(ParseNodeKind::ForIn, yieldHandling); if (!*forInOrOfExpression) { return null(); } } else { *forHeadKind = ParseNodeKind::ForHead; } } return handler_.finishInitializerAssignment(binding, initializer); } template typename ParseHandler::Node 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 null(); } TaggedParserAtomIndex name = bindingIdentifier(yieldHandling); if (!name) { return null(); } NameNodeType binding = newName(name); if (!binding) { return null(); } 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 null(); } Node declaration; if (matched) { declaration = initializerInNameDeclaration( binding, declKind, initialDeclaration, yieldHandling, forHeadKind, forInOrOfExpression); if (!declaration) { return null(); } } else { declaration = binding; if (initialDeclaration && forHeadKind) { bool isForIn, isForOf; if (!matchInOrOf(&isForIn, &isForOf)) { return null(); } if (isForIn) { *forHeadKind = ParseNodeKind::ForIn; } else if (isForOf) { *forHeadKind = ParseNodeKind::ForOf; } else { *forHeadKind = ParseNodeKind::ForHead; } } if (forHeadKind && *forHeadKind != ParseNodeKind::ForHead) { *forInOrOfExpression = expressionAfterForInOrOf(*forHeadKind, yieldHandling); if (!*forInOrOfExpression) { return null(); } } 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 null(); } } } // 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 null(); } return declaration; } template typename ParseHandler::DeclarationListNodeType 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 = handler_.newDeclarationList(kind, pos()); if (!decl) { return null(); } bool moreDeclarations; bool initialDeclaration = true; do { MOZ_ASSERT_IF(!initialDeclaration && forHeadKind, *forHeadKind == ParseNodeKind::ForHead); TokenKind tt; if (!tokenStream.getToken(&tt)) { return null(); } Node binding = (tt == TokenKind::LeftBracket || tt == TokenKind::LeftCurly) ? declarationPattern(declKind, tt, initialDeclaration, yieldHandling, forHeadKind, forInOrOfExpression) : declarationName(declKind, tt, initialDeclaration, yieldHandling, forHeadKind, forInOrOfExpression); if (!binding) { return null(); } 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 null(); } } while (moreDeclarations); return decl; } template typename ParseHandler::DeclarationListNodeType GeneralParser::lexicalDeclaration( YieldHandling yieldHandling, DeclarationKind kind) { MOZ_ASSERT(kind == DeclarationKind::Const || kind == DeclarationKind::Let); /* * 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 = declarationList( yieldHandling, kind == DeclarationKind::Const ? ParseNodeKind::ConstDecl : ParseNodeKind::LetDecl); if (!decl || !matchOrInsertSemicolon()) { return null(); } return decl; } template typename ParseHandler::NameNodeType 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 null(); } return handler_.newStringLiteral(name, pos()); } template bool GeneralParser::assertClause( ListNodeType assertionsSet) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Assert)); if (!options().importAssertions) { 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 = newName(keyName); if (!keyNode) { return false; } if (!mustMatchToken(TokenKind::Colon, JSMSG_COLON_AFTER_ASSERT_KEY)) { return false; } if (!mustMatchToken(TokenKind::String, JSMSG_ASSERT_STRING_LITERAL)) { return false; } NameNodeType valueNode = stringLiteral(); if (!valueNode) { return false; } BinaryNodeType importAssertionNode = handler_.newImportAssertion(keyNode, valueNode); if (!importAssertionNode) { return false; } handler_.addList(assertionsSet, importAssertionNode); 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(); importNameNode = newName(importName); } else if (tt == TokenKind::String) { importNameNode = moduleExportName(); } else { error(JSMSG_NO_IMPORT_NAME); } if (!importNameNode) { 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 = newName(bindingAtom); if (!bindingName) { return false; } if (!noteDeclaredName(bindingAtom, DeclarationKind::Import, pos())) { return false; } BinaryNodeType importSpec = handler_.newImportSpec(importNameNode, bindingName); if (!importSpec) { return 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 = newName(bindingName); if (!bindingNameNode) { return 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 = handler_.newImportNamespaceSpec(begin, bindingNameNode); if (!importSpec) { return false; } handler_.addList(importSpecSet, importSpec); return true; } template typename ParseHandler::BinaryNodeType GeneralParser::importDeclaration() { if (!abortIfSyntaxParser()) { return null(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Import)); if (!pc_->atModuleLevel()) { error(JSMSG_IMPORT_DECL_AT_TOP_LEVEL); return null(); } uint32_t begin = pos().begin; TokenKind tt; if (!tokenStream.getToken(&tt)) { return null(); } ListNodeType importSpecSet = handler_.newList(ParseNodeKind::ImportSpecList, pos()); if (!importSpecSet) { return null(); } 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 null(); } } else if (tt == TokenKind::Mul) { if (!namespaceImport(importSpecSet)) { return null(); } } 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 = newName(TaggedParserAtomIndex::WellKnown::default_()); if (!importName) { return null(); } TaggedParserAtomIndex bindingAtom = importedBinding(); if (!bindingAtom) { return null(); } NameNodeType bindingName = newName(bindingAtom); if (!bindingName) { return null(); } if (!noteDeclaredName(bindingAtom, DeclarationKind::Import, pos())) { return null(); } BinaryNodeType importSpec = handler_.newImportSpec(importName, bindingName); if (!importSpec) { return null(); } handler_.addList(importSpecSet, importSpec); if (!tokenStream.peekToken(&tt)) { return null(); } if (tt == TokenKind::Comma) { tokenStream.consumeKnownToken(tt); if (!tokenStream.getToken(&tt)) { return null(); } if (tt == TokenKind::LeftCurly) { if (!namedImports(importSpecSet)) { return null(); } } else if (tt == TokenKind::Mul) { if (!namespaceImport(importSpecSet)) { return null(); } } else { error(JSMSG_NAMED_IMPORTS_OR_NAMESPACE_IMPORT); return null(); } } } else { error(JSMSG_DECLARATION_AFTER_IMPORT); return null(); } if (!mustMatchToken(TokenKind::From, JSMSG_FROM_AFTER_IMPORT_CLAUSE)) { return null(); } if (!mustMatchToken(TokenKind::String, JSMSG_MODULE_SPEC_AFTER_FROM)) { return null(); } } NameNodeType moduleSpec = stringLiteral(); if (!moduleSpec) { return null(); } if (!tokenStream.peekTokenSameLine(&tt, TokenStream::SlashIsRegExp)) { return null(); } ListNodeType importAssertionList = handler_.newList(ParseNodeKind::ImportAssertionList, pos()); if (!importAssertionList) { return null(); } if (tt == TokenKind::Assert) { tokenStream.consumeKnownToken(TokenKind::Assert, TokenStream::SlashIsRegExp); if (!assertClause(importAssertionList)) { return null(); } } if (!matchOrInsertSemicolon(TokenStream::SlashIsRegExp)) { return null(); } BinaryNodeType moduleRequest = handler_.newModuleRequest( moduleSpec, importAssertionList, TokenPos(begin, pos().end)); if (!moduleRequest) { return null(); } BinaryNodeType node = handler_.newImportDeclaration( importSpecSet, moduleRequest, TokenPos(begin, pos().end)); if (!node || !processImport(node)) { return null(); } return node; } template inline typename ParseHandler::Node GeneralParser::importDeclarationOrImportExpr( YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Import)); TokenKind tt; if (!tokenStream.peekToken(&tt)) { return null(); } 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::BinaryNodeType GeneralParser::exportFrom(uint32_t begin, Node specList) { if (!abortIfSyntaxParser()) { return null(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::From)); if (!mustMatchToken(TokenKind::String, JSMSG_MODULE_SPEC_AFTER_FROM)) { return null(); } NameNodeType moduleSpec = stringLiteral(); if (!moduleSpec) { return null(); } TokenKind tt; if (!tokenStream.peekTokenSameLine(&tt, TokenStream::SlashIsRegExp)) { return null(); } uint32_t moduleSpecPos = pos().begin; ListNodeType importAssertionList = handler_.newList(ParseNodeKind::ImportAssertionList, pos()); if (!importAssertionList) { return null(); } if (tt == TokenKind::Assert) { tokenStream.consumeKnownToken(TokenKind::Assert, TokenStream::SlashIsRegExp); if (!assertClause(importAssertionList)) { return null(); } } if (!matchOrInsertSemicolon(TokenStream::SlashIsRegExp)) { return null(); } BinaryNodeType moduleRequest = handler_.newModuleRequest( moduleSpec, importAssertionList, TokenPos(moduleSpecPos, pos().end)); if (!moduleRequest) { return null(); } BinaryNodeType node = handler_.newExportFromDeclaration(begin, specList, moduleRequest); if (!node) { return null(); } if (!processExportFrom(node)) { return null(); } return node; } template typename ParseHandler::BinaryNodeType GeneralParser::exportBatch(uint32_t begin) { if (!abortIfSyntaxParser()) { return null(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Mul)); uint32_t beginExportSpec = pos().begin; ListNodeType kid = handler_.newList(ParseNodeKind::ExportSpecList, pos()); if (!kid) { return null(); } bool foundAs; if (!tokenStream.matchToken(&foundAs, TokenKind::As)) { return null(); } if (foundAs) { TokenKind tt; if (!tokenStream.getToken(&tt)) { return null(); } NameNodeType exportName = null(); if (TokenKindIsPossibleIdentifierName(tt)) { exportName = newName(anyChars.currentName()); } else if (tt == TokenKind::String) { exportName = moduleExportName(); } else { error(JSMSG_NO_EXPORT_NAME); } if (!exportName) { return null(); } if (!checkExportedNameForClause(exportName)) { return null(); } UnaryNodeType exportSpec = handler_.newExportNamespaceSpec(beginExportSpec, exportName); if (!exportSpec) { return null(); } handler_.addList(kid, exportSpec); } else { // Handle the form |export *| by adding a special export batch // specifier to the list. NullaryNodeType exportSpec = handler_.newExportBatchSpec(pos()); if (!exportSpec) { return null(); } handler_.addList(kid, exportSpec); } if (!mustMatchToken(TokenKind::From, JSMSG_FROM_AFTER_EXPORT_STAR)) { return null(); } 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::Node GeneralParser::exportClause( uint32_t begin) { if (!abortIfSyntaxParser()) { return null(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftCurly)); ListNodeType kid = handler_.newList(ParseNodeKind::ExportSpecList, pos()); if (!kid) { return null(); } 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 null(); } if (tt == TokenKind::RightCurly) { break; } NameNodeType bindingName = null(); if (TokenKindIsPossibleIdentifierName(tt)) { bindingName = newName(anyChars.currentName()); } else if (tt == TokenKind::String) { bindingName = moduleExportName(); } else { error(JSMSG_NO_BINDING_NAME); } if (!bindingName) { return null(); } bool foundAs; if (!tokenStream.matchToken(&foundAs, TokenKind::As)) { return null(); } NameNodeType exportName = null(); if (foundAs) { TokenKind tt; if (!tokenStream.getToken(&tt)) { return null(); } if (TokenKindIsPossibleIdentifierName(tt)) { exportName = newName(anyChars.currentName()); } else if (tt == TokenKind::String) { exportName = moduleExportName(); } else { error(JSMSG_NO_EXPORT_NAME); } } else { if (tt != TokenKind::String) { exportName = newName(anyChars.currentName()); } else { exportName = moduleExportName(); } } if (!exportName) { return null(); } if (!checkExportedNameForClause(exportName)) { return null(); } BinaryNodeType exportSpec = handler_.newExportSpec(bindingName, exportName); if (!exportSpec) { return null(); } handler_.addList(kid, exportSpec); TokenKind next; if (!tokenStream.getToken(&next)) { return null(); } if (next == TokenKind::RightCurly) { break; } if (next != TokenKind::Comma) { error(JSMSG_RC_AFTER_EXPORT_SPEC_LIST); return null(); } } // 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 null(); } if (matched) { return exportFrom(begin, kid); } if (!matchOrInsertSemicolon()) { return null(); } if (!checkLocalExportNames(kid)) { return null(); } UnaryNodeType node = handler_.newExportDeclaration(kid, TokenPos(begin, pos().end)); if (!node) { return null(); } if (!processExport(node)) { return null(); } return node; } template typename ParseHandler::UnaryNodeType GeneralParser::exportVariableStatement(uint32_t begin) { if (!abortIfSyntaxParser()) { return null(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Var)); DeclarationListNodeType kid = declarationList(YieldIsName, ParseNodeKind::VarStmt); if (!kid) { return null(); } if (!matchOrInsertSemicolon()) { return null(); } if (!checkExportedNamesForDeclarationList(kid)) { return null(); } UnaryNodeType node = handler_.newExportDeclaration(kid, TokenPos(begin, pos().end)); if (!node) { return null(); } if (!processExport(node)) { return null(); } return node; } template typename ParseHandler::UnaryNodeType GeneralParser::exportFunctionDeclaration( uint32_t begin, uint32_t toStringStart, FunctionAsyncKind asyncKind /* = SyncFunction */) { if (!abortIfSyntaxParser()) { return null(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Function)); Node kid = functionStmt(toStringStart, YieldIsName, NameRequired, asyncKind); if (!kid) { return null(); } if (!checkExportedNameForFunction(handler_.asFunction(kid))) { return null(); } UnaryNodeType node = handler_.newExportDeclaration(kid, TokenPos(begin, pos().end)); if (!node) { return null(); } if (!processExport(node)) { return null(); } return node; } template typename ParseHandler::UnaryNodeType GeneralParser::exportClassDeclaration(uint32_t begin) { if (!abortIfSyntaxParser()) { return null(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Class)); ClassNodeType kid = classDefinition(YieldIsName, ClassStatement, NameRequired); if (!kid) { return null(); } if (!checkExportedNameForClass(kid)) { return null(); } UnaryNodeType node = handler_.newExportDeclaration(kid, TokenPos(begin, pos().end)); if (!node) { return null(); } if (!processExport(node)) { return null(); } return node; } template typename ParseHandler::UnaryNodeType GeneralParser::exportLexicalDeclaration( uint32_t begin, DeclarationKind kind) { if (!abortIfSyntaxParser()) { return null(); } 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 = lexicalDeclaration(YieldIsName, kind); if (!kid) { return null(); } if (!checkExportedNamesForDeclarationList(kid)) { return null(); } UnaryNodeType node = handler_.newExportDeclaration(kid, TokenPos(begin, pos().end)); if (!node) { return null(); } if (!processExport(node)) { return null(); } return node; } template typename ParseHandler::BinaryNodeType GeneralParser::exportDefaultFunctionDeclaration( uint32_t begin, uint32_t toStringStart, FunctionAsyncKind asyncKind /* = SyncFunction */) { if (!abortIfSyntaxParser()) { return null(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Function)); Node kid = functionStmt(toStringStart, YieldIsName, AllowDefaultName, asyncKind); if (!kid) { return null(); } BinaryNodeType node = handler_.newExportDefaultDeclaration( kid, null(), TokenPos(begin, pos().end)); if (!node) { return null(); } if (!processExport(node)) { return null(); } return node; } template typename ParseHandler::BinaryNodeType GeneralParser::exportDefaultClassDeclaration( uint32_t begin) { if (!abortIfSyntaxParser()) { return null(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Class)); ClassNodeType kid = classDefinition(YieldIsName, ClassStatement, AllowDefaultName); if (!kid) { return null(); } BinaryNodeType node = handler_.newExportDefaultDeclaration( kid, null(), TokenPos(begin, pos().end)); if (!node) { return null(); } if (!processExport(node)) { return null(); } return node; } template typename ParseHandler::BinaryNodeType GeneralParser::exportDefaultAssignExpr(uint32_t begin) { if (!abortIfSyntaxParser()) { return null(); } TaggedParserAtomIndex name = TaggedParserAtomIndex::WellKnown::default_(); NameNodeType nameNode = newName(name); if (!nameNode) { return null(); } if (!noteDeclaredName(name, DeclarationKind::Const, pos())) { return null(); } Node kid = assignExpr(InAllowed, YieldIsName, TripledotProhibited); if (!kid) { return null(); } if (!matchOrInsertSemicolon()) { return null(); } BinaryNodeType node = handler_.newExportDefaultDeclaration( kid, nameNode, TokenPos(begin, pos().end)); if (!node) { return null(); } if (!processExport(node)) { return null(); } return node; } template typename ParseHandler::BinaryNodeType GeneralParser::exportDefault(uint32_t begin) { if (!abortIfSyntaxParser()) { return null(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Default)); TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return null(); } if (!checkExportedName(TaggedParserAtomIndex::WellKnown::default_())) { return null(); } switch (tt) { case TokenKind::Function: return exportDefaultFunctionDeclaration(begin, pos().begin); case TokenKind::Async: { TokenKind nextSameLine = TokenKind::Eof; if (!tokenStream.peekTokenSameLine(&nextSameLine)) { return null(); } 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::Node GeneralParser::exportDeclaration() { if (!abortIfSyntaxParser()) { return null(); } MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Export)); if (!pc_->atModuleLevel()) { error(JSMSG_EXPORT_DECL_AT_TOP_LEVEL); return null(); } uint32_t begin = pos().begin; TokenKind tt; if (!tokenStream.getToken(&tt)) { return null(); } 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 null(); } 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 null(); } 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 null(); } } template typename ParseHandler::UnaryNodeType GeneralParser::expressionStatement( YieldHandling yieldHandling, InvokedPrediction invoked) { anyChars.ungetToken(); Node pnexpr = expr(InAllowed, yieldHandling, TripledotProhibited, /* possibleError = */ nullptr, invoked); if (!pnexpr) { return null(); } if (!matchOrInsertSemicolon()) { return null(); } return handler_.newExprStatement(pnexpr, pos().end); } template typename ParseHandler::Node GeneralParser::consequentOrAlternative( YieldHandling yieldHandling) { TokenKind next; if (!tokenStream.peekToken(&next, TokenStream::SlashIsRegExp)) { return null(); } // 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 null(); } TokenKind maybeStar; if (!tokenStream.peekToken(&maybeStar)) { return null(); } if (maybeStar == TokenKind::Mul) { error(JSMSG_FORBIDDEN_AS_STATEMENT, "generator declarations"); return null(); } ParseContext::Statement stmt(pc_, StatementKind::Block); ParseContext::Scope scope(this); if (!scope.init(pc_)) { return null(); } TokenPos funcPos = pos(); Node fun = functionStmt(pos().begin, yieldHandling, NameRequired); if (!fun) { return null(); } ListNodeType block = handler_.newStatementList(funcPos); if (!block) { return null(); } handler_.addStatementToList(block, fun); return finishLexicalScope(scope, block); } return statement(yieldHandling); } template typename ParseHandler::TernaryNodeType 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 = condition(InAllowed, yieldHandling); if (!cond) { return null(); } TokenKind tt; if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) { return null(); } Node thenBranch = consequentOrAlternative(yieldHandling); if (!thenBranch) { return null(); } if (!condList.append(cond) || !thenList.append(thenBranch) || !posList.append(begin)) { return null(); } bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Else, TokenStream::SlashIsRegExp)) { return null(); } if (matched) { if (!tokenStream.matchToken(&matched, TokenKind::If, TokenStream::SlashIsRegExp)) { return null(); } if (matched) { continue; } elseBranch = consequentOrAlternative(yieldHandling); if (!elseBranch) { return null(); } } else { elseBranch = null(); } break; } TernaryNodeType ifNode; for (int i = condList.length() - 1; i >= 0; i--) { ifNode = handler_.newIfStatement(posList[i], condList[i], thenList[i], elseBranch); if (!ifNode) { return null(); } elseBranch = ifNode; } return ifNode; } template typename ParseHandler::BinaryNodeType GeneralParser::doWhileStatement( YieldHandling yieldHandling) { uint32_t begin = pos().begin; ParseContext::Statement stmt(pc_, StatementKind::DoLoop); Node body = statement(yieldHandling); if (!body) { return null(); } if (!mustMatchToken(TokenKind::While, JSMSG_WHILE_AFTER_DO)) { return null(); } Node cond = condition(InAllowed, yieldHandling); if (!cond) { return null(); } // 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 null(); } return handler_.newDoWhileStatement(body, cond, TokenPos(begin, pos().end)); } template typename ParseHandler::BinaryNodeType GeneralParser::whileStatement(YieldHandling yieldHandling) { uint32_t begin = pos().begin; ParseContext::Statement stmt(pc_, StatementKind::WhileLoop); Node cond = condition(InAllowed, yieldHandling); if (!cond) { return null(); } Node body = statement(yieldHandling); if (!body) { return null(); } 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. *forInitialPart = declarationList(yieldHandling, ParseNodeKind::VarStmt, forHeadKind, forInOrOfExpression); return *forInitialPart != null(); } // 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) { 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) { 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); *forInitialPart = declarationList(yieldHandling, tt == TokenKind::Const ? ParseNodeKind::ConstDecl : ParseNodeKind::LetDecl, forHeadKind, forInOrOfExpression); return *forInitialPart != null(); } 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); *forInitialPart = expr(InProhibited, yieldHandling, TripledotProhibited, &possibleError); if (!*forInitialPart) { return 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_.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. *forInOrOfExpression = expressionAfterForInOrOf(*forHeadKind, yieldHandling); return *forInOrOfExpression != null(); } template typename ParseHandler::Node 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 null(); } // 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 null(); } 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 null(); } // 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 null(); } 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 null(); } 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 null(); } TokenKind tt; if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) { return null(); } Node test; if (tt == TokenKind::Semi) { test = null(); } else { test = expr(InAllowed, yieldHandling, TripledotProhibited); if (!test) { return null(); } } if (!mustMatchToken(TokenKind::Semi, JSMSG_SEMI_AFTER_FOR_COND)) { return null(); } if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) { return null(); } Node update; if (tt == TokenKind::RightParen) { update = null(); } else { update = expr(InAllowed, yieldHandling, TripledotProhibited); if (!update) { return null(); } } if (!mustMatchToken(TokenKind::RightParen, JSMSG_PAREN_AFTER_FOR_CTRL)) { return null(); } TokenPos headPos(begin, pos().end); forHead = handler_.newForHead(init, test, update, headPos); if (!forHead) { return null(); } } 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 null(); } TokenPos headPos(begin, pos().end); forHead = handler_.newForInOrOfHead(headKind, target, iteratedExpr, headPos); if (!forHead) { return null(); } } Node body = statement(yieldHandling); if (!body) { return null(); } ForNodeType forLoop = handler_.newForStatement(begin, forHead, body, iflags); if (!forLoop) { return null(); } if (forLoopLexicalScope) { return finishLexicalScope(*forLoopLexicalScope, forLoop); } return forLoop; } template typename ParseHandler::SwitchStatementType GeneralParser::switchStatement( YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Switch)); uint32_t begin = pos().begin; if (!mustMatchToken(TokenKind::LeftParen, JSMSG_PAREN_BEFORE_SWITCH)) { return null(); } Node discriminant = exprInParens(InAllowed, yieldHandling, TripledotProhibited); if (!discriminant) { return null(); } if (!mustMatchToken(TokenKind::RightParen, JSMSG_PAREN_AFTER_SWITCH)) { return null(); } if (!mustMatchToken(TokenKind::LeftCurly, JSMSG_CURLY_BEFORE_SWITCH)) { return null(); } ParseContext::Statement stmt(pc_, StatementKind::Switch); ParseContext::Scope scope(this); if (!scope.init(pc_)) { return null(); } ListNodeType caseList = handler_.newStatementList(pos()); if (!caseList) { return null(); } bool seenDefault = false; TokenKind tt; while (true) { if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return null(); } 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 null(); } seenDefault = true; caseExpr = null(); // The default case has pn_left == nullptr. break; case TokenKind::Case: caseExpr = expr(InAllowed, yieldHandling, TripledotProhibited); if (!caseExpr) { return null(); } break; default: error(JSMSG_BAD_SWITCH); return null(); } if (!mustMatchToken(TokenKind::Colon, JSMSG_COLON_AFTER_CASE)) { return null(); } ListNodeType body = handler_.newStatementList(pos()); if (!body) { return null(); } bool afterReturn = false; bool warnedAboutStatementsAfterReturn = false; uint32_t statementBegin = 0; while (true) { if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) { return null(); } if (tt == TokenKind::RightCurly || tt == TokenKind::Case || tt == TokenKind::Default) { break; } if (afterReturn) { if (!tokenStream.peekOffset(&statementBegin, TokenStream::SlashIsRegExp)) { return null(); } } Node stmt = statementListItem(yieldHandling); if (!stmt) { return null(); } if (!warnedAboutStatementsAfterReturn) { if (afterReturn) { if (!handler_.isStatementPermittedAfterReturnStatement(stmt)) { if (!warningAt(statementBegin, JSMSG_STMT_AFTER_RETURN)) { return null(); } warnedAboutStatementsAfterReturn = true; } } else if (handler_.isReturnStatement(stmt)) { afterReturn = true; } } handler_.addStatementToList(body, stmt); } CaseClauseType caseClause = handler_.newCaseOrDefault(caseBegin, caseExpr, body); if (!caseClause) { return null(); } handler_.addCaseStatementToList(caseList, caseClause); } LexicalScopeNodeType lexicalForCaseList = finishLexicalScope(scope, caseList); if (!lexicalForCaseList) { return null(); } handler_.setEndPosition(lexicalForCaseList, pos().end); return handler_.newSwitchStatement(begin, discriminant, lexicalForCaseList, seenDefault); } template typename ParseHandler::ContinueStatementType GeneralParser::continueStatement( YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Continue)); uint32_t begin = pos().begin; TaggedParserAtomIndex label; if (!matchLabel(yieldHandling, &label)) { return null(); } 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 null(); } if (!matchOrInsertSemicolon()) { return null(); } return handler_.newContinueStatement(label, TokenPos(begin, pos().end)); } template typename ParseHandler::BreakStatementType GeneralParser::breakStatement(YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Break)); uint32_t begin = pos().begin; TaggedParserAtomIndex label; if (!matchLabel(yieldHandling, &label)) { return null(); } auto validity = pc_->checkBreakStatement(label); if (validity.isErr()) { switch (validity.unwrapErr()) { case ParseContext::BreakStatementError::ToughBreak: errorAt(begin, JSMSG_TOUGH_BREAK); return null(); case ParseContext::BreakStatementError::LabelNotFound: error(JSMSG_LABEL_NOT_FOUND); return null(); } } if (!matchOrInsertSemicolon()) { return null(); } return handler_.newBreakStatement(label, TokenPos(begin, pos().end)); } template typename ParseHandler::UnaryNodeType 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 null(); } switch (tt) { case TokenKind::Eol: case TokenKind::Eof: case TokenKind::Semi: case TokenKind::RightCurly: exprNode = null(); break; default: { exprNode = expr(InAllowed, yieldHandling, TripledotProhibited); if (!exprNode) { return null(); } } } if (!matchOrInsertSemicolon()) { return null(); } return handler_.newReturnStatement(exprNode, TokenPos(begin, pos().end)); } template typename ParseHandler::UnaryNodeType 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 null(); } 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: exprNode = assignExpr(inHandling, YieldIsKeyword, TripledotProhibited); if (!exprNode) { return null(); } } if (kind == ParseNodeKind::YieldStarExpr) { return handler_.newYieldStarExpression(begin, exprNode); } return handler_.newYieldExpression(begin, exprNode); } template typename ParseHandler::BinaryNodeType 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 null(); } } if (!mustMatchToken(TokenKind::LeftParen, JSMSG_PAREN_BEFORE_WITH)) { return null(); } Node objectExpr = exprInParens(InAllowed, yieldHandling, TripledotProhibited); if (!objectExpr) { return null(); } if (!mustMatchToken(TokenKind::RightParen, JSMSG_PAREN_AFTER_WITH)) { return null(); } Node innerBlock; { ParseContext::Statement stmt(pc_, StatementKind::With); innerBlock = statement(yieldHandling); if (!innerBlock) { return null(); } } pc_->sc()->setBindingsAccessedDynamically(); return handler_.newWithStatement(begin, objectExpr, innerBlock); } template typename ParseHandler::Node GeneralParser::labeledItem( YieldHandling yieldHandling) { TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return null(); } if (tt == TokenKind::Function) { TokenKind next; if (!tokenStream.peekToken(&next)) { return null(); } // GeneratorDeclaration is only matched by HoistableDeclaration in // StatementListItem, so generators can't be inside labels. if (next == TokenKind::Mul) { error(JSMSG_GENERATOR_LABEL); return null(); } // 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 null(); } return functionStmt(pos().begin, yieldHandling, NameRequired); } anyChars.ungetToken(); return statement(yieldHandling); } template typename ParseHandler::LabeledStatementType GeneralParser::labeledStatement( YieldHandling yieldHandling) { TaggedParserAtomIndex label = labelIdentifier(yieldHandling); if (!label) { return null(); } 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 null(); } tokenStream.consumeKnownToken(TokenKind::Colon); /* Push a label struct and parse the statement. */ ParseContext::LabelStatement stmt(pc_, label); Node pn = labeledItem(yieldHandling); if (!pn) { return null(); } return handler_.newLabeledStatement(label, pn, begin); } template typename ParseHandler::UnaryNodeType 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 null(); } if (tt == TokenKind::Eof || tt == TokenKind::Semi || tt == TokenKind::RightCurly) { error(JSMSG_MISSING_EXPR_AFTER_THROW); return null(); } if (tt == TokenKind::Eol) { error(JSMSG_LINE_BREAK_AFTER_THROW); return null(); } Node throwExpr = expr(InAllowed, yieldHandling, TripledotProhibited); if (!throwExpr) { return null(); } if (!matchOrInsertSemicolon()) { return null(); } return handler_.newThrowStatement(throwExpr, TokenPos(begin, pos().end)); } template typename ParseHandler::TernaryNodeType 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 null(); } uint32_t openedPos = pos().begin; ParseContext::Statement stmt(pc_, StatementKind::Try); ParseContext::Scope scope(this); if (!scope.init(pc_)) { return null(); } innerBlock = statementList(yieldHandling); if (!innerBlock) { return null(); } innerBlock = finishLexicalScope(scope, innerBlock); if (!innerBlock) { return null(); } if (!mustMatchToken( TokenKind::RightCurly, [this, openedPos](TokenKind actual) { this->reportMissingClosing(JSMSG_CURLY_AFTER_TRY, JSMSG_CURLY_OPENED, openedPos); })) { return null(); } } LexicalScopeNodeType catchScope = null(); TokenKind tt; if (!tokenStream.getToken(&tt)) { return null(); } 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 null(); } /* * 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 null(); } Node catchName; if (omittedBinding) { catchName = null(); } else { if (!mustMatchToken(TokenKind::LeftParen, JSMSG_PAREN_BEFORE_CATCH)) { return null(); } if (!tokenStream.getToken(&tt)) { return null(); } switch (tt) { case TokenKind::LeftBracket: case TokenKind::LeftCurly: catchName = destructuringDeclaration(DeclarationKind::CatchParameter, yieldHandling, tt); if (!catchName) { return null(); } break; default: { if (!TokenKindIsPossibleIdentifierName(tt)) { error(JSMSG_CATCH_IDENTIFIER); return null(); } catchName = bindingIdentifier(DeclarationKind::SimpleCatchParameter, yieldHandling); if (!catchName) { return null(); } break; } } if (!mustMatchToken(TokenKind::RightParen, JSMSG_PAREN_AFTER_CATCH)) { return null(); } if (!mustMatchToken(TokenKind::LeftCurly, JSMSG_CURLY_BEFORE_CATCH)) { return null(); } } LexicalScopeNodeType catchBody = catchBlockStatement(yieldHandling, scope); if (!catchBody) { return null(); } catchScope = finishLexicalScope(scope, catchBody); if (!catchScope) { return null(); } if (!handler_.setupCatchScope(catchScope, catchName, catchBody)) { return null(); } handler_.setEndPosition(catchScope, pos().end); if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return null(); } } Node finallyBlock = null(); if (tt == TokenKind::Finally) { if (!mustMatchToken(TokenKind::LeftCurly, JSMSG_CURLY_BEFORE_FINALLY)) { return null(); } uint32_t openedPos = pos().begin; ParseContext::Statement stmt(pc_, StatementKind::Finally); ParseContext::Scope scope(this); if (!scope.init(pc_)) { return null(); } finallyBlock = statementList(yieldHandling); if (!finallyBlock) { return null(); } finallyBlock = finishLexicalScope(scope, finallyBlock); if (!finallyBlock) { return null(); } if (!mustMatchToken( TokenKind::RightCurly, [this, openedPos](TokenKind actual) { this->reportMissingClosing(JSMSG_CURLY_AFTER_FINALLY, JSMSG_CURLY_OPENED, openedPos); })) { return null(); } } else { anyChars.ungetToken(); } if (!catchScope && !finallyBlock) { error(JSMSG_CATCH_OR_FINALLY); return null(); } return handler_.newTryStatement(begin, innerBlock, catchScope, finallyBlock); } template typename ParseHandler::LexicalScopeNodeType 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 null(); } // 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 null(); } ListNodeType list = statementList(yieldHandling); if (!list) { return null(); } if (!mustMatchToken( TokenKind::RightCurly, [this, openedPos](TokenKind actual) { this->reportMissingClosing(JSMSG_CURLY_AFTER_CATCH, JSMSG_CURLY_OPENED, openedPos); })) { return null(); } // 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::DebuggerStatementType GeneralParser::debuggerStatement() { TokenPos p; p.begin = pos().begin; if (!matchOrInsertSemicolon()) { return null(); } 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::ListNodeType GeneralParser::decoratorList(YieldHandling yieldHandling) { ListNodeType 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 null(); } Node decorator = null(); if (tt == TokenKind::LeftParen) { // Handle DecoratorParenthesizedExpression decorator = exprInParens(InAllowed, yieldHandling, TripledotProhibited); if (!decorator) { return null(); } if (!mustMatchToken(TokenKind::RightParen, JSMSG_PAREN_AFTER_DECORATOR)) { return null(); } if (!tokenStream.getToken(&tt)) { return null(); } } else { // Get decorator identifier if (!(tt == TokenKind::Name || TokenKindIsContextualKeyword(tt))) { error(JSMSG_DECORATOR_NAME_EXPECTED); return null(); } TaggedParserAtomIndex name = anyChars.currentName(); if (!tokenStream.getToken(&tt)) { return null(); } // Handle DecoratorMemberExpression decorator = handler_.newName(name, pos()); // Ok, this DecoratorMemberExpression is actually a list of // Identifiers separated by `.` if (tt == TokenKind::Dot) { ListNodeType ids = handler_.newList(ParseNodeKind::DecoratorList, pos()); handler_.addList(ids, decorator); for (;;) { if (!tokenStream.getToken(&tt)) { return null(); } // Reject invalid or missing identifiers, like dec1.( if (!(tt == TokenKind::Name || TokenKindIsContextualKeyword(tt))) { error(JSMSG_DECORATOR_NAME_EXPECTED); return null(); } TaggedParserAtomIndex name = anyChars.currentName(); Node id = handler_.newName(name, pos()); handler_.addList(ids, id); if (!tokenStream.getToken(&tt)) { return null(); } if (tt != TokenKind::Dot) { break; } } decorator = ids; } // We've hit a `(`, so it's actually a DecoratorCallExpression if (tt == TokenKind::LeftParen) { bool isSpread = false; Node args = argumentList(yieldHandling, &isSpread); if (!args) { return null(); } decorator = handler_.newCall(decorator, args, isSpread ? JSOp::SpreadCall : JSOp::Call); if (!tokenStream.getToken(&tt)) { return null(); } } } MOZ_ASSERT(decorator, "Decorator should exist"); handler_.addList(decorators, decorator); 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) { decorators = decoratorList(yieldHandling); if (!decorators) { return 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 = staticClassBlock(classInitializedMembers); if (!staticBlockBody) { return false; } StaticClassBlockType classBlock = handler_.newStaticClassBlock(staticBlockBody); if (!classBlock) { return 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 = propertyOrMethodName(yieldHandling, PropertyNameInClass, /* maybeDecl = */ Nothing(), classMembers, &propType, &propAtom); if (!propName) { return 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::hashConstructor()) { 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 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). Node method = synthesizeAccessor( propName, propNamePos, propAtom, privateStateName, isStatic, FunctionSyntaxKind::Getter, decorators, classInitializedMembers); if (!method) { return false; } if (!handler_.addClassMemberDefinition(classMembers, method)) { return false; } // Step 6. Let setter be MakeAutoAccessorSetter(homeObject, name, // privateStateName). method = synthesizeAccessor( propName, propNamePos, propAtom, privateStateName, isStatic, FunctionSyntaxKind::Setter, decorators, classInitializedMembers); if (!method) { return false; } if (!handler_.addClassMemberDefinition(classMembers, method)) { return false; } // Step 10. Return ClassElementDefinition Record { [[Key]]: name, // [[Kind]]: accessor, [[Get]]: getter, [[Set]]: setter, // [[BackingStorageKey]]: privateStateName, [[Initializers]]: // initializers, [[Decorators]]: empty }. propName = handler_.newPrivateName(privateStateName, pos()); propAtom = privateStateName; decorators = handler_.newList(ParseNodeKind::DecoratorList, pos()); } #endif if (isStatic) { classInitializedMembers.staticFields++; } else { classInitializedMembers.instanceFields++; } TokenPos propNamePos(propNameOffset, pos().end); FunctionNodeType initializer = fieldInitializerOpt(propNamePos, propName, propAtom, classInitializedMembers, isStatic, hasHeritage); if (!initializer) { return false; } if (!matchOrInsertSemicolon(TokenStream::SlashIsInvalid)) { return false; } ClassFieldType field = handler_.newClassFieldDefinition( propName, initializer, isStatic #ifdef ENABLE_DECORATORS , decorators, propType == PropertyType::FieldWithAccessor #endif ); if (!field) { return 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::dotInitializers(), DeclarationKind::Let, pos())) { return false; } } // 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 = methodDefinition( isConstructor ? classStartOffset : propNameOffset, propType, funName); if (!funNode) { return false; } AccessorType atype = ToAccessorType(propType); Maybe initializerIfPrivate = Nothing(); if (handler_.isPrivateName(propName)) { if (propAtom == TaggedParserAtomIndex::WellKnown::hashConstructor()) { // #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); auto initializerNode = synthesizePrivateMethodInitializer(propAtom, atype, propNamePos); if (!initializerNode) { return false; } initializerIfPrivate = Some(initializerNode); } else { MOZ_ASSERT(atype == AccessorType::None); classInitializedMembers.privateMethods++; } } } Node method = handler_.newClassMethodDefinition(propName, funNode, atype, isStatic, initializerIfPrivate #ifdef ENABLE_DECORATORS , decorators #endif ); if (!method) { return false; } if (dotInitializersScope.isSome()) { method = finishLexicalScope(*dotInitializersScope, method); if (!method) { return 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::dotInitializers(), DeclarationKind::Let, pos())) { return false; } // synthesizeConstructor assigns to classStmt.constructorBox TokenPos synthesizedBodyPos(classStartOffset, classEndOffset); FunctionNodeType synthesizedCtor = synthesizeConstructor(className, synthesizedBodyPos, hasHeritage); if (!synthesizedCtor) { return false; } // Note: the *function* has the name of the class, but the *property* // containing the function has the name "constructor" Node constructorNameNode = handler_.newObjectLiteralPropertyName( TaggedParserAtomIndex::WellKnown::constructor(), pos()); if (!constructorNameNode) { return false; } ClassMethodType method = handler_.newDefaultClassConstructor( constructorNameNode, synthesizedCtor); if (!method) { return false; } LexicalScopeNodeType scope = finishLexicalScope(dotInitializersScope, method); if (!scope) { return 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, numMemberInitializers); ctorbox->setMemberInitializers(initializers); // Field initialization need access to `this`. ctorbox->setCtorFunctionHasThisBinding(); } return true; } template typename ParseHandler::ClassNodeType GeneralParser::classDefinition( YieldHandling yieldHandling, ClassContext classContext, DefaultHandling defaultHandling) { #ifdef ENABLE_DECORATORS MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::At) || anyChars.isCurrentTokenType(TokenKind::Class)); ListNodeType decorators = null(); if (anyChars.isCurrentTokenType(TokenKind::At)) { decorators = decoratorList(yieldHandling); if (!decorators) { return null(); } TokenKind next; if (!tokenStream.getToken(&next)) { return null(); } if (next != TokenKind::Class) { error(JSMSG_CLASS_EXPECTED); return null(); } } #else MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Class)); #endif uint32_t classStartOffset = pos().begin; bool savedStrictness = setLocalStrictMode(true); TokenKind tt; if (!tokenStream.getToken(&tt)) { return null(); } TaggedParserAtomIndex className; if (TokenKindIsPossibleIdentifier(tt)) { className = bindingIdentifier(yieldHandling); if (!className) { return null(); } } 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 null(); } } 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 null(); } bool hasHeritageBool; if (!tokenStream.matchToken(&hasHeritageBool, TokenKind::Extends)) { return null(); } HasHeritage hasHeritage = hasHeritageBool ? HasHeritage::Yes : HasHeritage::No; if (hasHeritage == HasHeritage::Yes) { if (!tokenStream.getToken(&tt)) { return null(); } classHeritage = optionalExpr(yieldHandling, TripledotProhibited, tt); if (!classHeritage) { return null(); } } if (!mustMatchToken(TokenKind::LeftCurly, JSMSG_CURLY_BEFORE_CLASS)) { return null(); } { ParseContext::Statement bodyScopeStmt(pc_, StatementKind::Block); ParseContext::Scope bodyScope(this); if (!bodyScope.init(pc_)) { return null(); } ListNodeType classMembers = handler_.newClassMemberList(pos().begin); if (!classMembers) { return null(); } ClassInitializedMembers classInitializedMembers{}; for (;;) { bool done; if (!classMember(yieldHandling, classStmt, className, classStartOffset, hasHeritage, classInitializedMembers, classMembers, &done)) { return null(); } if (done) { break; } } if (classInitializedMembers.privateMethods + classInitializedMembers.privateAccessors > 0) { // We declare `.privateBrand` as ClosedOver because the constructor // always uses it, even a default constructor. We could equivalently // `noteNameUsed` 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::dotPrivateBrand(), DeclarationKind::Synthetic, namePos, ClosedOver::Yes)) { return null(); } } if (classInitializedMembers.instanceFieldKeys > 0) { if (!noteDeclaredName(TaggedParserAtomIndex::WellKnown::dotFieldKeys(), DeclarationKind::Synthetic, namePos)) { return null(); } } if (classInitializedMembers.staticFields > 0) { if (!noteDeclaredName( TaggedParserAtomIndex::WellKnown::dotStaticInitializers(), DeclarationKind::Synthetic, namePos)) { return null(); } } if (classInitializedMembers.staticFieldKeys > 0) { if (!noteDeclaredName( TaggedParserAtomIndex::WellKnown::dotStaticFieldKeys(), DeclarationKind::Synthetic, namePos)) { return null(); } } classEndOffset = pos().end; if (!finishClassConstructor(classStmt, className, hasHeritage, classStartOffset, classEndOffset, classInitializedMembers, classMembers)) { return null(); } classBodyBlock = finishClassBodyScope(bodyScope, classMembers); if (!classBodyBlock) { return null(); } // Pop the class body scope } if (className) { // The inner name is immutable. if (!noteDeclaredName(className, DeclarationKind::Const, namePos)) { return null(); } innerName = newName(className, namePos); if (!innerName) { return null(); } } classBlock = finishLexicalScope(innerScope, classBodyBlock); if (!classBlock) { return null(); } // Pop the inner scope. } if (className) { NameNodeType outerName = null(); if (classContext == ClassStatement) { // The outer name is mutable. if (!noteDeclaredName(className, DeclarationKind::Class, namePos)) { return null(); } outerName = newName(className, namePos); if (!outerName) { return null(); } } nameNode = handler_.newClassNames(outerName, innerName, namePos); if (!nameNode) { return null(); } } 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 null(); } if (maybeUnboundName) { UniqueChars str = this->parserAtoms().toPrintableString(maybeUnboundName->atom); if (!str) { ReportOutOfMemory(this->fc_); return null(); } errorAt(maybeUnboundName->position.begin, JSMSG_MISSING_PRIVATE_DECL, str.get()); return null(); } } return handler_.newClass(nameNode, classHeritage, classBlock, #ifdef ENABLE_DECORATORS decorators, #endif TokenPos(classStartOffset, classEndOffset)); } template typename ParseHandler::FunctionNodeType 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 = handler_.newFunction(functionSyntaxKind, synthesizedBodyPos); if (!funNode) { return null(); } // 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 null(); } 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 null(); } 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 null(); } if (!synthesizeConstructorBody(synthesizedBodyPos, hasHeritage, funNode, funbox)) { return null(); } if (!leaveInnerFunction(outerpc)) { return null(); } return funNode; } template typename ParseHandler::FunctionNodeType 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 = handler_.newParamsBody(synthesizedBodyPos); if (!argsbody) { return null(); } 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::dotArgs(), synthesizedBodyPos.begin, /* disallowDuplicateParams = */ false, /* duplicatedParam = */ nullptr)) { return null(); } funbox->setArgCount(1); } else { funbox->setArgCount(0); } pc_->functionScope().useAsVarScope(pc_); auto stmtList = handler_.newStatementList(synthesizedBodyPos); if (!stmtList) { return null(); } if (!noteUsedName(TaggedParserAtomIndex::WellKnown::dotThis())) { return null(); } if (!noteUsedName(TaggedParserAtomIndex::WellKnown::dotInitializers())) { return null(); } if (hasHeritage == HasHeritage::Yes) { // |super()| implicitly reads |new.target|. if (!noteUsedName(TaggedParserAtomIndex::WellKnown::dotNewTarget())) { return null(); } NameNodeType thisName = newThisName(); if (!thisName) { return null(); } UnaryNodeType superBase = handler_.newSuperBase(thisName, synthesizedBodyPos); if (!superBase) { return null(); } ListNodeType arguments = handler_.newArguments(synthesizedBodyPos); if (!arguments) { return null(); } NameNodeType argsNameNode = newName( TaggedParserAtomIndex::WellKnown::dotArgs(), synthesizedBodyPos); if (!argsNameNode) { return null(); } if (!noteUsedName(TaggedParserAtomIndex::WellKnown::dotArgs())) { return null(); } UnaryNodeType spreadArgs = handler_.newSpread(synthesizedBodyPos.begin, argsNameNode); if (!spreadArgs) { return null(); } handler_.addList(arguments, spreadArgs); CallNodeType superCall = handler_.newSuperCall(superBase, arguments, /* isSpread = */ true); if (!superCall) { return null(); } BinaryNodeType setThis = handler_.newSetThis(thisName, superCall); if (!setThis) { return null(); } UnaryNodeType exprStatement = handler_.newExprStatement(setThis, synthesizedBodyPos.end); if (!exprStatement) { return null(); } handler_.addStatementToList(stmtList, exprStatement); } bool canSkipLazyClosedOverBindings = handler_.reuseClosedOverBindings(); if (!pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings)) { return null(); } if (!pc_->declareNewTarget(usedNames_, canSkipLazyClosedOverBindings)) { return null(); } auto initializerBody = finishLexicalScope(pc_->varScope(), stmtList, ScopeKind::FunctionLexical); if (!initializerBody) { return null(); } handler_.setBeginPosition(initializerBody, stmtList); handler_.setEndPosition(initializerBody, stmtList); handler_.setFunctionBody(funNode, initializerBody); if (!finishFunction()) { return null(); } return funNode; } template typename ParseHandler::FunctionNodeType GeneralParser::privateMethodInitializer( TokenPos propNamePos, TaggedParserAtomIndex propAtom, TaggedParserAtomIndex storedMethodAtom) { if (!abortIfSyntaxParser()) { return null(); } // 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 = handler_.newFunction(syntaxKind, propNamePos); if (!funNode) { return null(); } Directives directives(true); FunctionBox* funbox = newFunctionBox(funNode, TaggedParserAtomIndex::null(), flags, propNamePos.begin, directives, generatorKind, asyncKind); if (!funbox) { return null(); } funbox->initWithEnclosingParseContext(pc_, syntaxKind); // Push a SourceParseContext on to the stack. ParseContext* outerpc = pc_; SourceParseContext funpc(this, funbox, /* newDirectives = */ nullptr); if (!funpc.init()) { return null(); } pc_->functionScope().useAsVarScope(pc_); // Add empty parameter list. ParamsBodyNodeType argsbody = handler_.newParamsBody(propNamePos); if (!argsbody) { return null(); } 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 null(); } NameNodeType privateNameNode = privateNameReference(propAtom); if (!privateNameNode) { return null(); } // 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 = handler_.newStatementList(propNamePos); if (!stmtList) { return null(); } bool canSkipLazyClosedOverBindings = handler_.reuseClosedOverBindings(); if (!pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings)) { return null(); } if (!pc_->declareNewTarget(usedNames_, canSkipLazyClosedOverBindings)) { return null(); } LexicalScopeNodeType initializerBody = finishLexicalScope(pc_->varScope(), stmtList, ScopeKind::FunctionLexical); if (!initializerBody) { return null(); } 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 null(); } if (!leaveInnerFunction(outerpc)) { return null(); } return funNode; } template typename ParseHandler::FunctionNodeType GeneralParser::staticClassBlock( ClassInitializedMembers& classInitializedMembers) { // Both for getting-this-done, and because this will invariably be executed, // syntax parsing should be aborted. if (!abortIfSyntaxParser()) { return null(); } 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 = handler_.newFunction(syntaxKind, pos()); if (!funNode) { return null(); } // 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 null(); } 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 null(); } 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 = functionBody(InHandling::InAllowed, YieldHandling::YieldIsKeyword, syntaxKind, FunctionBodyType::StatementListBody); if (!body) { return null(); } if (anyChars.isEOF()) { error(JSMSG_UNTERMINATED_STATIC_CLASS_BLOCK); return null(); } 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 = handler_.newParamsBody(wholeBodyPos); if (!argsbody) { return null(); } 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 null(); } if (!leaveInnerFunction(outerpc)) { return null(); } return funNode; } template typename ParseHandler::FunctionNodeType GeneralParser::fieldInitializerOpt( TokenPos propNamePos, Node propName, TaggedParserAtomIndex propAtom, ClassInitializedMembers& classInitializedMembers, bool isStatic, HasHeritage hasHeritage) { if (!abortIfSyntaxParser()) { return null(); } bool hasInitializer = false; if (!tokenStream.matchToken(&hasInitializer, TokenKind::Assign, TokenStream::SlashIsDiv)) { return null(); } 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 = handler_.newFunction(syntaxKind, propNamePos); if (!funNode) { return null(); } // 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 null(); } 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 null(); } pc_->functionScope().useAsVarScope(pc_); Node initializerExpr; if (hasInitializer) { // Parse the expression for the field initializer. { AutoAwaitIsKeyword awaitHandling(this, AwaitIsName); initializerExpr = assignExpr(InAllowed, YieldIsName, TripledotProhibited); if (!initializerExpr) { return null(); } } handler_.checkAndSetIsDirectRHSAnonFunction(initializerExpr); } else { initializerExpr = handler_.newRawUndefinedLiteral(propNamePos); if (!initializerExpr) { return null(); } } 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 = handler_.newParamsBody(wholeInitializerPos); if (!argsbody) { return null(); } handler_.setFunctionFormalParametersAndBody(funNode, argsbody); funbox->setArgCount(0); NameNodeType thisName = newThisName(); if (!thisName) { return null(); } // Build `this.field` expression. ThisLiteralType propAssignThis = handler_.newThisLiteral(wholeInitializerPos, thisName); if (!propAssignThis) { return null(); } Node propAssignFieldAccess; uint32_t indexValue; if (!propAtom) { // See BytecodeEmitter::emitCreateFieldKeys for an explanation of what // .fieldKeys means and its purpose. NameNodeType fieldKeysName; if (isStatic) { fieldKeysName = newInternalDotName( TaggedParserAtomIndex::WellKnown::dotStaticFieldKeys()); } else { fieldKeysName = newInternalDotName(TaggedParserAtomIndex::WellKnown::dotFieldKeys()); } if (!fieldKeysName) { return null(); } double fieldKeyIndex; if (isStatic) { fieldKeyIndex = classInitializedMembers.staticFieldKeys++; } else { fieldKeyIndex = classInitializedMembers.instanceFieldKeys++; } Node fieldKeyIndexNode = handler_.newNumber( fieldKeyIndex, DecimalPoint::NoDecimal, wholeInitializerPos); if (!fieldKeyIndexNode) { return null(); } Node fieldKeyValue = handler_.newPropertyByValue( fieldKeysName, fieldKeyIndexNode, wholeInitializerPos.end); if (!fieldKeyValue) { return null(); } propAssignFieldAccess = handler_.newPropertyByValue( propAssignThis, fieldKeyValue, wholeInitializerPos.end); if (!propAssignFieldAccess) { return null(); } } 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 = privateNameReference(propAtom); if (!privateNameNode) { return null(); } propAssignFieldAccess = handler_.newPrivateMemberAccess( propAssignThis, privateNameNode, wholeInitializerPos.end); if (!propAssignFieldAccess) { return null(); } } else if (this->parserAtoms().isIndex(propAtom, &indexValue)) { propAssignFieldAccess = handler_.newPropertyByValue( propAssignThis, propName, wholeInitializerPos.end); if (!propAssignFieldAccess) { return null(); } } else { NameNodeType propAssignName = handler_.newPropertyName(propAtom, wholeInitializerPos); if (!propAssignName) { return null(); } propAssignFieldAccess = handler_.newPropertyAccess(propAssignThis, propAssignName); if (!propAssignFieldAccess) { return null(); } } // Synthesize an property init. BinaryNodeType initializerPropInit = handler_.newInitExpr(propAssignFieldAccess, initializerExpr); if (!initializerPropInit) { return null(); } UnaryNodeType exprStatement = handler_.newExprStatement(initializerPropInit, wholeInitializerPos.end); if (!exprStatement) { return null(); } ListNodeType statementList = handler_.newStatementList(wholeInitializerPos); if (!statementList) { return null(); } handler_.addStatementToList(statementList, exprStatement); bool canSkipLazyClosedOverBindings = handler_.reuseClosedOverBindings(); if (!pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings)) { return null(); } if (!pc_->declareNewTarget(usedNames_, canSkipLazyClosedOverBindings)) { return null(); } // Set the function's body to the field assignment. LexicalScopeNodeType initializerBody = finishLexicalScope( pc_->varScope(), statementList, ScopeKind::FunctionLexical); if (!initializerBody) { return null(); } handler_.setFunctionBody(funNode, initializerBody); if (pc_->superScopeNeedsHomeObject()) { funbox->setNeedsHomeObject(); } if (!finishFunction()) { return null(); } if (!leaveInnerFunction(outerpc)) { return null(); } return funNode; } template typename ParseHandler::FunctionNodeType GeneralParser::synthesizePrivateMethodInitializer( TaggedParserAtomIndex propAtom, AccessorType accessorType, TokenPos propNamePos) { if (!abortIfSyntaxParser()) { return null(); } // Synthesize a name for the lexical variable that will store the // accessor body. StringBuffer storedMethodName(fc_); if (!storedMethodName.append(this->parserAtoms(), propAtom)) { return null(); } if (!storedMethodName.append( accessorType == AccessorType::Getter ? ".getter" : ".setter")) { return null(); } auto storedMethodProp = storedMethodName.finishParserAtom(this->parserAtoms(), fc_); if (!storedMethodProp) { return null(); } if (!noteDeclaredName(storedMethodProp, DeclarationKind::Synthetic, pos())) { return null(); } return privateMethodInitializer(propNamePos, propAtom, storedMethodProp); } #ifdef ENABLE_DECORATORS template typename ParseHandler::Node GeneralParser::synthesizeAccessor( Node propName, TokenPos propNamePos, TaggedParserAtomIndex propAtom, TaggedParserAtomIndex privateStateNameAtom, bool isStatic, FunctionSyntaxKind syntaxKind, ListNodeType decorators, 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 null(); } AccessorType accessorType = syntaxKind == FunctionSyntaxKind::Getter ? AccessorType::Getter : AccessorType::Setter; mozilla::Maybe initializerIfPrivate = Nothing(); if (handler_.isPrivateName(propName)) { classInitializedMembers.privateAccessors++; auto initializerNode = synthesizePrivateMethodInitializer(propAtom, accessorType, propNamePos); if (!initializerNode) { return null(); } 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", « »). FunctionNodeType funNode = synthesizeAccessorBody(propNamePos, privateStateNameAtom, syntaxKind); if (!funNode) { return null(); } // 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, decorators); } template typename ParseHandler::FunctionNodeType GeneralParser::synthesizeAccessorBody( TokenPos propNamePos, TaggedParserAtomIndex propAtom, FunctionSyntaxKind syntaxKind) { if (!abortIfSyntaxParser()) { return null(); } 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 = handler_.newFunction(syntaxKind, propNamePos); if (!funNode) { return null(); } // 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 null(); } 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 null(); } 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 = handler_.newParamsBody(propNamePos); if (!paramsbody) { return null(); } 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 = newThisName(); if (!thisName) { return null(); } ThisLiteralType propThis = handler_.newThisLiteral(propNamePos, thisName); if (!propThis) { return null(); } NameNodeType privateNameNode = privateNameReference(propAtom); if (!privateNameNode) { return null(); } Node propFieldAccess = handler_.newPrivateMemberAccess( propThis, privateNameNode, propNamePos.end); if (!propFieldAccess) { return null(); } 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). 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 = handler_.newName( TaggedParserAtomIndex::WellKnown::value(), propNamePos); if (!initializerExpr) { return null(); } // 1.b. Perform ? PrivateSet(privateStateName, o, value). Node assignment = handler_.newAssignment(ParseNodeKind::AssignExpr, propFieldAccess, initializerExpr); if (!assignment) { return null(); } accessorBody = handler_.newExprStatement(assignment, propNamePos.end); if (!accessorBody) { return null(); } // 1.c. Return undefined. } ListNodeType statementList = handler_.newStatementList(propNamePos); if (!statementList) { return null(); } handler_.addStatementToList(statementList, accessorBody); bool canSkipLazyClosedOverBindings = handler_.reuseClosedOverBindings(); if (!pc_->declareFunctionThis(usedNames_, canSkipLazyClosedOverBindings)) { return null(); } if (!pc_->declareNewTarget(usedNames_, canSkipLazyClosedOverBindings)) { return null(); } LexicalScopeNodeType initializerBody = finishLexicalScope( pc_->varScope(), statementList, ScopeKind::FunctionLexical); if (!initializerBody) { return null(); } handler_.setFunctionBody(funNode, initializerBody); if (pc_->superScopeNeedsHomeObject()) { funbox->setNeedsHomeObject(); } if (!finishFunction()) { return null(); } if (!leaveInnerFunction(outerpc)) { return null(); } 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::DeclarationListNodeType GeneralParser::variableStatement( YieldHandling yieldHandling) { DeclarationListNodeType vars = declarationList(yieldHandling, ParseNodeKind::VarStmt); if (!vars) { return null(); } if (!matchOrInsertSemicolon()) { return null(); } return vars; } template typename ParseHandler::Node GeneralParser::statement( YieldHandling yieldHandling) { MOZ_ASSERT(checkOptionsCalled_); AutoCheckRecursionLimit recursion(this->fc_); if (!recursion.check(this->fc_)) { return null(); } TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return null(); } 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 null(); } 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 null(); } 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 null(); } // |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 null(); } MOZ_ASSERT(TokenKindIsPossibleIdentifier(nextSameLine) || nextSameLine == TokenKind::LeftCurly || nextSameLine == TokenKind::Eol); forbiddenLetDeclaration = nextSameLine != TokenKind::Eol; } if (forbiddenLetDeclaration) { error(JSMSG_FORBIDDEN_AS_STATEMENT, "lexical declarations"); return null(); } } 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 null(); } if (maybeFunction == TokenKind::Function) { error(JSMSG_FORBIDDEN_AS_STATEMENT, "async function declarations"); return null(); } // 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, js_return_str); return null(); } 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 null(); // |class| is also forbidden by lookahead restriction. case TokenKind::Class: error(JSMSG_FORBIDDEN_AS_STATEMENT, "classes"); return null(); // 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 null(); case TokenKind::Finally: error(JSMSG_FINALLY_WITHOUT_TRY); return null(); // NOTE: default case handled in the ExpressionStatement section. } } template typename ParseHandler::Node GeneralParser::statementListItem( YieldHandling yieldHandling, bool canHaveDirectives /* = false */) { MOZ_ASSERT(checkOptionsCalled_); AutoCheckRecursionLimit recursion(this->fc_); if (!recursion.check(this->fc_)) { return null(); } TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return null(); } 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::useAsm()) { if (!warning(JSMSG_USE_ASM_DIRECTIVE_FAIL)) { return null(); } } 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 null(); } 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 null(); } 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 null(); } if (tt == TokenKind::Let && nextTokenContinuesLetDeclaration(next)) { return lexicalDeclaration(yieldHandling, DeclarationKind::Let); } if (tt == TokenKind::Async) { TokenKind nextSameLine = TokenKind::Eof; if (!tokenStream.peekTokenSameLine(&nextSameLine)) { return null(); } 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, js_return_str); return null(); } 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, ClassExpression, 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 null(); case TokenKind::Finally: error(JSMSG_FINALLY_WITHOUT_TRY); return null(); // NOTE: default case handled in the ExpressionStatement section. } } template typename ParseHandler::Node GeneralParser::expr( InHandling inHandling, YieldHandling yieldHandling, TripledotHandling tripledotHandling, PossibleError* possibleError /* = nullptr */, InvokedPrediction invoked /* = PredictUninvoked */) { Node pn = assignExpr(inHandling, yieldHandling, tripledotHandling, possibleError, invoked); if (!pn) { return null(); } bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::SlashIsRegExp)) { return null(); } if (!matched) { return pn; } ListNodeType seq = handler_.newCommaExpressionList(pn); if (!seq) { return null(); } 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 null(); } if (tt == TokenKind::RightParen) { tokenStream.consumeKnownToken(TokenKind::RightParen, TokenStream::SlashIsRegExp); if (!tokenStream.peekToken(&tt)) { return null(); } if (tt != TokenKind::Arrow) { error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TokenKind::RightParen)); return null(); } 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); pn = assignExpr(inHandling, yieldHandling, tripledotHandling, &possibleErrorInner); if (!pn) { return null(); } if (!possibleError) { // Report any pending expression error. if (!possibleErrorInner.checkForExpressionError()) { return null(); } } else { possibleErrorInner.transferErrorsTo(possibleError); } handler_.addList(seq, pn); if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::SlashIsRegExp)) { return null(); } 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::Node 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 (;;) { pn = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked, PrivateNameHandling::PrivateNameAllowed); if (!pn) { return null(); } // If a binary operator follows, consume it and compute the // corresponding operator. TokenKind tok; if (!tokenStream.getToken(&tok)) { return null(); } // 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 null(); } } 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 null(); } 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 null(); } 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 null(); } // 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 null(); } // 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 null(); } 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]; pn = handler_.appendOrCreateList(combiningPnk, nodeStack[depth], pn, pc_); if (!pn) { return null(); } } 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::Node GeneralParser::condExpr(InHandling inHandling, YieldHandling yieldHandling, TripledotHandling tripledotHandling, PossibleError* possibleError, InvokedPrediction invoked) { Node condition = orExpr(inHandling, yieldHandling, tripledotHandling, possibleError, invoked); if (!condition) { return null(); } bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Hook, TokenStream::SlashIsInvalid)) { return null(); } if (!matched) { return condition; } Node thenExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited); if (!thenExpr) { return null(); } if (!mustMatchToken(TokenKind::Colon, JSMSG_COLON_IN_COND)) { return null(); } Node elseExpr = assignExpr(inHandling, yieldHandling, TripledotProhibited); if (!elseExpr) { return null(); } return handler_.newConditional(condition, thenExpr, elseExpr); } template typename ParseHandler::Node GeneralParser::assignExpr( InHandling inHandling, YieldHandling yieldHandling, TripledotHandling tripledotHandling, PossibleError* possibleError /* = nullptr */, InvokedPrediction invoked /* = PredictUninvoked */) { AutoCheckRecursionLimit recursion(this->fc_); if (!recursion.check(this->fc_)) { return null(); } // 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 null(); } 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 null(); } if (endsExpr) { TaggedParserAtomIndex name = identifierReference(yieldHandling); if (!name) { return null(); } return identifierReference(name); } } if (firstToken == TokenKind::Number) { if (!tokenStream.nextTokenEndsExpr(&endsExpr)) { return null(); } if (endsExpr) { return newNumber(anyChars.currentToken()); } } if (firstToken == TokenKind::String) { if (!tokenStream.nextTokenEndsExpr(&endsExpr)) { return null(); } 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 null(); } 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 null(); } MOZ_ASSERT(TokenKindIsPossibleIdentifier(tokenAfterAsync)); // Check yield validity here. TaggedParserAtomIndex name = bindingIdentifier(yieldHandling); if (!name) { return null(); } if (!tokenStream.peekToken(&tokenAfterLHS, TokenStream::SlashIsRegExp)) { return null(); } 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 null(); } lhs = identifierReference(asyncName); if (!lhs) { return null(); } } } else { lhs = condExpr(inHandling, yieldHandling, tripledotHandling, &possibleErrorInner, invoked); if (!lhs) { return null(); } // 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 null(); } 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 null(); } 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 null(); } // 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 = handler_.newFunction(syntaxKind, startPos); if (!funNode) { return null(); } 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 null(); } } 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 null(); } if (!possibleErrorInner.checkForDestructuringErrorOrWarning()) { return null(); } } 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 null(); } } } 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 null(); } if (!strictModeErrorAt(exprPos.begin, JSMSG_BAD_LEFTSIDE_OF_ASS)) { return null(); } if (possibleError) { possibleError->setPendingDestructuringErrorAt(exprPos, JSMSG_BAD_DESTRUCT_TARGET); } } else { errorAt(exprPos.begin, JSMSG_BAD_LEFTSIDE_OF_ASS); return null(); } if (!possibleErrorInner.checkForExpressionError()) { return null(); } Node rhs = assignExpr(inHandling, yieldHandling, TripledotProhibited); if (!rhs) { return null(); } 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 js_eval_str; } if (handler_.isArgumentsName(node)) { return js_arguments_str; } 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_.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::UnaryNodeType GeneralParser::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, uint32_t begin) { Node kid = unaryExpr(yieldHandling, TripledotProhibited); if (!kid) { return null(); } return handler_.newUnary(kind, begin, kid); } template typename ParseHandler::Node GeneralParser::optionalExpr( YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt, PossibleError* possibleError /* = nullptr */, InvokedPrediction invoked /* = PredictUninvoked */) { AutoCheckRecursionLimit recursion(this->fc_); if (!recursion.check(this->fc_)) { return null(); } uint32_t begin = pos().begin; Node lhs = memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true, possibleError, invoked); if (!lhs) { return null(); } if (!tokenStream.peekToken(&tt, TokenStream::SlashIsDiv)) { return null(); } if (tt != TokenKind::OptionalChain) { return lhs; } while (true) { if (!tokenStream.getToken(&tt)) { return null(); } if (tt == TokenKind::Eof) { anyChars.ungetToken(); break; } Node nextMember; if (tt == TokenKind::OptionalChain) { if (!tokenStream.getToken(&tt)) { return null(); } if (TokenKindIsPossibleIdentifierName(tt)) { nextMember = memberPropertyAccess(lhs, OptionalKind::Optional); if (!nextMember) { return null(); } } else if (tt == TokenKind::PrivateName) { nextMember = memberPrivateAccess(lhs, OptionalKind::Optional); if (!nextMember) { return null(); } } else if (tt == TokenKind::LeftBracket) { nextMember = memberElemAccess(lhs, yieldHandling, OptionalKind::Optional); if (!nextMember) { return null(); } } else if (tt == TokenKind::LeftParen) { nextMember = memberCall(tt, lhs, yieldHandling, possibleError, OptionalKind::Optional); if (!nextMember) { return null(); } } else { error(JSMSG_NAME_AFTER_DOT); return null(); } } else if (tt == TokenKind::Dot) { if (!tokenStream.getToken(&tt)) { return null(); } if (TokenKindIsPossibleIdentifierName(tt)) { nextMember = memberPropertyAccess(lhs); if (!nextMember) { return null(); } } else if (tt == TokenKind::PrivateName) { nextMember = memberPrivateAccess(lhs); if (!nextMember) { return null(); } } else { error(JSMSG_NAME_AFTER_DOT); return null(); } } else if (tt == TokenKind::LeftBracket) { nextMember = memberElemAccess(lhs, yieldHandling); if (!nextMember) { return null(); } } else if (tt == TokenKind::LeftParen) { nextMember = memberCall(tt, lhs, yieldHandling, possibleError); if (!nextMember) { return null(); } } else if (tt == TokenKind::TemplateHead || tt == TokenKind::NoSubsTemplate) { error(JSMSG_BAD_OPTIONAL_TEMPLATE); return null(); } else { anyChars.ungetToken(); break; } MOZ_ASSERT(nextMember); lhs = nextMember; } return handler_.newOptionalChain(begin, lhs); } template typename ParseHandler::Node 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 null(); } TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return null(); } 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 = unaryExpr(yieldHandling, TripledotProhibited); if (!kid) { return null(); } return handler_.newTypeof(begin, kid); } case TokenKind::Inc: case TokenKind::Dec: { TokenKind tt2; if (!tokenStream.getToken(&tt2, TokenStream::SlashIsRegExp)) { return null(); } uint32_t operandOffset = pos().begin; Node operand = optionalExpr(yieldHandling, TripledotProhibited, tt2); if (!operand || !checkIncDecOperand(operand, operandOffset)) { return null(); } 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 null(); } case TokenKind::Delete: { uint32_t exprOffset; if (!tokenStream.peekOffset(&exprOffset, TokenStream::SlashIsRegExp)) { return null(); } Node expr = unaryExpr(yieldHandling, TripledotProhibited); if (!expr) { return null(); } // 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 null(); } pc_->sc()->setBindingsAccessedDynamically(); } if (handler_.isPrivateMemberAccess(expr)) { errorAt(exprOffset, JSMSG_PRIVATE_DELETE); return null(); } 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 null(); } pc_->sc()->asModuleContext()->setIsAsync(); MOZ_ASSERT(pc_->isAsync()); } if (pc_->isAsync()) { if (inParametersOfAsyncFunction()) { error(JSMSG_AWAIT_IN_PARAMETER); return null(); } Node kid = unaryExpr(yieldHandling, tripledotHandling, possibleError, invoked); if (!kid) { return null(); } pc_->lastAwaitOffset = begin; return handler_.newAwaitExpression(begin, kid); } } [[fallthrough]]; default: { Node expr = optionalExpr(yieldHandling, tripledotHandling, tt, possibleError, invoked); if (!expr) { return null(); } /* Don't look across a newline boundary for a postfix incop. */ if (!tokenStream.peekTokenSameLine(&tt)) { return null(); } if (tt != TokenKind::Inc && tt != TokenKind::Dec) { return expr; } tokenStream.consumeKnownToken(tt); if (!checkIncDecOperand(expr, begin)) { return null(); } ParseNodeKind pnk = (tt == TokenKind::Inc) ? ParseNodeKind::PostIncrementExpr : ParseNodeKind::PostDecrementExpr; return handler_.newUpdate(pnk, begin, expr); } } } template typename ParseHandler::Node GeneralParser::assignExprWithoutYieldOrAwait( YieldHandling yieldHandling) { uint32_t startYieldOffset = pc_->lastYieldOffset; uint32_t startAwaitOffset = pc_->lastAwaitOffset; Node res = assignExpr(InAllowed, yieldHandling, TripledotProhibited); if (res) { if (pc_->lastYieldOffset != startYieldOffset) { errorAt(pc_->lastYieldOffset, JSMSG_YIELD_IN_PARAMETER); return null(); } if (pc_->lastAwaitOffset != startAwaitOffset) { errorAt(pc_->lastAwaitOffset, JSMSG_AWAIT_IN_PARAMETER); return null(); } } return res; } template typename ParseHandler::ListNodeType GeneralParser::argumentList( YieldHandling yieldHandling, bool* isSpread, PossibleError* possibleError /* = nullptr */) { ListNodeType argsList = handler_.newArguments(pos()); if (!argsList) { return null(); } bool matched; if (!tokenStream.matchToken(&matched, TokenKind::RightParen, TokenStream::SlashIsRegExp)) { return null(); } 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 null(); } if (matched) { spread = true; begin = pos().begin; *isSpread = true; } Node argNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited, possibleError); if (!argNode) { return null(); } if (spread) { argNode = handler_.newSpread(begin, argNode); if (!argNode) { return null(); } } handler_.addList(argsList, argNode); bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::SlashIsRegExp)) { return null(); } if (!matched) { break; } TokenKind tt; if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) { return null(); } if (tt == TokenKind::RightParen) { break; } } if (!mustMatchToken(TokenKind::RightParen, JSMSG_PAREN_AFTER_ARGS)) { return null(); } 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::Node 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 null(); } /* 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 null(); } if (newTarget) { lhs = newTarget; } else { // Gotten by tryNewTarget tt = anyChars.currentToken().type; Node ctorExpr = memberExpr(yieldHandling, TripledotProhibited, tt, /* allowCallSyntax = */ false, /* possibleError = */ nullptr, PredictInvoked); if (!ctorExpr) { return null(); } // 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 null(); } if (optionalToken) { errorAt(newBegin, JSMSG_BAD_NEW_OPTIONAL); return null(); } bool matched; if (!tokenStream.matchToken(&matched, TokenKind::LeftParen)) { return null(); } bool isSpread = false; Node args; if (matched) { args = argumentList(yieldHandling, &isSpread); } else { args = handler_.newArguments(pos()); } if (!args) { return null(); } lhs = handler_.newNewExpression(newBegin, ctorExpr, args, isSpread); if (!lhs) { return null(); } } } else if (tt == TokenKind::Super) { NameNodeType thisName = newThisName(); if (!thisName) { return null(); } lhs = handler_.newSuperBase(thisName, pos()); if (!lhs) { return null(); } } else if (tt == TokenKind::Import) { lhs = importExpr(yieldHandling, allowCallSyntax); if (!lhs) { return null(); } } else { lhs = primaryExpr(yieldHandling, tripledotHandling, tt, possibleError, invoked); if (!lhs) { return null(); } } MOZ_ASSERT_IF(handler_.isSuperBase(lhs), anyChars.isCurrentTokenType(TokenKind::Super)); while (true) { if (!tokenStream.getToken(&tt)) { return null(); } if (tt == TokenKind::Eof) { anyChars.ungetToken(); break; } Node nextMember; if (tt == TokenKind::Dot) { if (!tokenStream.getToken(&tt)) { return null(); } if (TokenKindIsPossibleIdentifierName(tt)) { nextMember = memberPropertyAccess(lhs); if (!nextMember) { return null(); } } else if (tt == TokenKind::PrivateName) { nextMember = memberPrivateAccess(lhs); if (!nextMember) { return null(); } } else { error(JSMSG_NAME_AFTER_DOT); return null(); } } else if (tt == TokenKind::LeftBracket) { nextMember = memberElemAccess(lhs, yieldHandling); if (!nextMember) { return null(); } } 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 null(); } if (tt != TokenKind::LeftParen) { error(JSMSG_BAD_SUPER); return null(); } nextMember = memberSuperCall(lhs, yieldHandling); if (!nextMember) { return null(); } if (!noteUsedName( TaggedParserAtomIndex::WellKnown::dotInitializers())) { return null(); } } else { nextMember = memberCall(tt, lhs, yieldHandling, possibleError); if (!nextMember) { return null(); } } } else { anyChars.ungetToken(); if (handler_.isSuperBase(lhs)) { break; } return lhs; } lhs = nextMember; } if (handler_.isSuperBase(lhs)) { error(JSMSG_BAD_SUPER); return null(); } return lhs; } template inline typename ParseHandler::NameNodeType PerHandlerParser::newName(TaggedParserAtomIndex name) { return newName(name, pos()); } template inline typename ParseHandler::NameNodeType PerHandlerParser::newName(TaggedParserAtomIndex name, TokenPos pos) { return handler_.newName(name, pos); } template inline typename ParseHandler::NameNodeType PerHandlerParser::newPrivateName(TaggedParserAtomIndex name) { return handler_.newPrivateName(name, pos()); } template typename ParseHandler::Node 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 null(); } NameNodeType name = handler_.newPropertyName(field, pos()); if (!name) { return null(); } if (optionalKind == OptionalKind::Optional) { MOZ_ASSERT(!handler_.isSuperBase(lhs)); return handler_.newOptionalPropertyAccess(lhs, name); } return handler_.newPropertyAccess(lhs, name); } template typename ParseHandler::Node 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 null(); } NameNodeType privateName = privateNameReference(field); if (!privateName) { return null(); } 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::Node GeneralParser::memberElemAccess( Node lhs, YieldHandling yieldHandling, OptionalKind optionalKind /* = OptionalKind::NonOptional */) { MOZ_ASSERT(anyChars.currentToken().type == TokenKind::LeftBracket); Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited); if (!propExpr) { return null(); } if (!mustMatchToken(TokenKind::RightBracket, JSMSG_BRACKET_IN_INDEX)) { return null(); } if (handler_.isSuperBase(lhs) && !checkAndMarkSuperScope()) { error(JSMSG_BAD_SUPERPROP, "member"); return null(); } 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::Node 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; Node args = argumentList(yieldHandling, &isSpread); if (!args) { return null(); } CallNodeType superCall = handler_.newSuperCall(lhs, args, isSpread); if (!superCall) { return null(); } // |super()| implicitly reads |new.target|. if (!noteUsedName(TaggedParserAtomIndex::WellKnown::dotNewTarget())) { return null(); } NameNodeType thisName = newThisName(); if (!thisName) { return null(); } return handler_.newSetThis(thisName, superCall); } template typename ParseHandler::Node 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 null(); } 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; Node args = argumentList(yieldHandling, &isSpread, asyncPossibleError); if (!args) { return null(); } 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 = handler_.newArguments(pos()); if (!args) { return null(); } if (!taggedTemplate(yieldHandling, args, tt)) { return null(); } if (optionalKind == OptionalKind::Optional) { error(JSMSG_BAD_OPTIONAL_TEMPLATE); return null(); } 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::NameNodeType PerHandlerParser::identifierReference( TaggedParserAtomIndex name) { NameNodeType id = newName(name); if (!id) { return null(); } if (!noteUsedName(name)) { return null(); } return id; } template typename ParseHandler::NameNodeType PerHandlerParser::privateNameReference( TaggedParserAtomIndex name) { NameNodeType id = newPrivateName(name); if (!id) { return null(); } if (!noteUsedName(name, NameVisibility::Private, Some(pos()))) { return null(); } return id; } template typename ParseHandler::NameNodeType PerHandlerParser::stringLiteral() { return handler_.newStringLiteral(anyChars.currentToken().atom(), pos()); } template typename ParseHandler::Node PerHandlerParser::noSubstitutionTaggedTemplate() { if (anyChars.hasInvalidTemplateEscape()) { anyChars.clearInvalidTemplateEscape(); return handler_.newRawUndefinedLiteral(pos()); } return handler_.newTemplateStringLiteral(anyChars.currentToken().atom(), pos()); } template typename ParseHandler::NameNodeType GeneralParser::noSubstitutionUntaggedTemplate() { if (!tokenStream.checkForInvalidTemplateEscapeError()) { return null(); } return handler_.newTemplateStringLiteral(anyChars.currentToken().atom(), pos()); } template RegExpLiteral* 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, 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(column))) { return nullptr; } } auto atom = this->parserAtoms().internChar16(fc_, chars.begin(), chars.length()); if (!atom) { return nullptr; } // 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 nullptr; } if (!this->compilationState_.regExpData.emplaceBack(atom, flags)) { js::ReportOutOfMemory(this->fc_); return nullptr; } return handler_.newRegExp(index, pos()); } template SyntaxParseHandler::RegExpLiteralType 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, 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(column))) { return null(); } return handler_.newRegExp(SyntaxParseHandler::NodeGeneric, pos()); } template typename ParseHandler::RegExpLiteralType GeneralParser::newRegExp() { return asFinalParser()->newRegExp(); } template BigIntLiteral* 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 null(); } BigIntIndex index(this->compilationState_.bigIntData.length()); if (uint32_t(index) >= TaggedScriptThingIndex::IndexLimit) { ReportAllocationOverflow(fc_); return null(); } if (!this->compilationState_.bigIntData.emplaceBack()) { js::ReportOutOfMemory(this->fc_); return null(); } if (!this->compilationState_.bigIntData[index].init( this->fc_, this->stencilAlloc(), chars)) { return null(); } 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::BigIntLiteralType Parser::newBigInt() { // The tokenizer has already checked the syntax of the bigint. return handler_.newBigInt(); } template typename ParseHandler::BigIntLiteralType 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_.asName(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 (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::ListNodeType GeneralParser::arrayInitializer( YieldHandling yieldHandling, PossibleError* possibleError) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftBracket)); uint32_t begin = pos().begin; ListNodeType literal = handler_.newArrayLiteral(begin); if (!literal) { return null(); } TokenKind tt; if (!tokenStream.getToken(&tt, TokenStream::SlashIsRegExp)) { return null(); } 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 null(); } TokenKind tt; if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) { return null(); } if (tt == TokenKind::RightBracket) { break; } if (tt == TokenKind::Comma) { tokenStream.consumeKnownToken(TokenKind::Comma, TokenStream::SlashIsRegExp); if (!handler_.addElision(literal, pos())) { return null(); } 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 null(); } PossibleError possibleErrorInner(*this); Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited, &possibleErrorInner); if (!inner) { return null(); } if (!checkDestructuringAssignmentTarget( inner, innerPos, &possibleErrorInner, possibleError)) { return null(); } if (!handler_.addSpreadElement(literal, begin, inner)) { return null(); } } else { TokenPos elementPos; if (!tokenStream.peekTokenPos(&elementPos, TokenStream::SlashIsRegExp)) { return null(); } PossibleError possibleErrorInner(*this); Node element = assignExpr(InAllowed, yieldHandling, TripledotProhibited, &possibleErrorInner); if (!element) { return null(); } if (!checkDestructuringAssignmentElement( element, elementPos, &possibleErrorInner, possibleError)) { return null(); } handler_.addArrayElement(literal, element); } bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::SlashIsRegExp)) { return null(); } 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 null(); } } handler_.setEndPosition(literal, pos().end); return literal; } template typename ParseHandler::Node 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 null(); } *propAtomOut = numAtom; return newNumber(anyChars.currentToken()); } case TokenKind::BigInt: { Node biNode = newBigInt(); if (!biNode) { return null(); } 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 null(); } TaggedParserAtomIndex propName = anyChars.currentName(); *propAtomOut = propName; return privateNameReference(propName); } default: { if (!TokenKindIsPossibleIdentifierName(ltok)) { error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(ltok)); return null(); } 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::Node 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 null(); } 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 null(); } if (TokenKindCanStartPropertyName(tt)) { isAsync = true; tokenStream.consumeKnownToken(tt); ltok = tt; } } if (ltok == TokenKind::Mul) { isGenerator = true; if (!tokenStream.getToken(<ok)) { return null(); } } 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 null(); } 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 null(); } // 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 = propertyName(yieldHandling, propertyNameContext, maybeDecl, propList, propAtomOut); if (!propName) { return null(); } // 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 null(); } if (tt == TokenKind::Colon) { if (isGenerator || isAsync || isGetter || isSetter #ifdef ENABLE_DECORATORS || hasAccessor #endif ) { error(JSMSG_BAD_PROP_ID); return null(); } *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 null(); } 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 null(); } #endif #ifdef ENABLE_DECORATORS if (hasAccessor) { error(JSMSG_BAD_PROP_ID); return null(); } #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 null(); } 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 null(); } template typename ParseHandler::UnaryNodeType 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 = assignExpr(InAllowed, yieldHandling, TripledotProhibited); if (!assignNode) { return null(); } if (!mustMatchToken(TokenKind::RightBracket, JSMSG_COMP_PROP_UNTERM_EXPR)) { return null(); } return handler_.newComputedName(assignNode, begin, pos().end); } template typename ParseHandler::ListNodeType GeneralParser::objectLiteral(YieldHandling yieldHandling, PossibleError* possibleError) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::LeftCurly)); uint32_t openedPos = pos().begin; ListNodeType literal = handler_.newObjectLiteral(pos().begin); if (!literal) { return null(); } bool seenPrototypeMutation = false; bool seenCoverInitializedName = false; Maybe declKind = Nothing(); TaggedParserAtomIndex propAtom; for (;;) { TokenKind tt; if (!tokenStream.peekToken(&tt)) { return null(); } 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 null(); } PossibleError possibleErrorInner(*this); Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited, &possibleErrorInner); if (!inner) { return null(); } if (!checkDestructuringAssignmentTarget( inner, innerPos, &possibleErrorInner, possibleError, TargetBehavior::ForbidAssignmentPattern)) { return null(); } if (!handler_.addSpreadProperty(literal, begin, inner)) { return null(); } } else { TokenPos namePos = anyChars.nextToken().pos; PropertyType propType; Node propName = propertyOrMethodName(yieldHandling, PropertyNameInLiteral, declKind, literal, &propType, &propAtom); if (!propName) { return null(); } if (propType == PropertyType::Normal) { TokenPos exprPos; if (!tokenStream.peekTokenPos(&exprPos, TokenStream::SlashIsRegExp)) { return null(); } PossibleError possibleErrorInner(*this); Node propExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited, &possibleErrorInner); if (!propExpr) { return null(); } if (!checkDestructuringAssignmentElement( propExpr, exprPos, &possibleErrorInner, possibleError)) { return null(); } 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 null(); } // 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 null(); } } else { BinaryNodeType propDef = handler_.newPropertyDefinition(propName, propExpr); if (!propDef) { return null(); } 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 null(); } NameNodeType nameExpr = identifierReference(name); if (!nameExpr) { return null(); } if (possibleError) { checkDestructuringAssignmentName(nameExpr, namePos, possibleError); } if (!handler_.addShorthand(literal, handler_.asName(propName), nameExpr)) { return null(); } } 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 null(); } Node lhs = identifierReference(name); if (!lhs) { return null(); } 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 null(); } // 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 null(); } } Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited); if (!rhs) { return null(); } BinaryNodeType propExpr = handler_.newAssignment(ParseNodeKind::AssignExpr, lhs, rhs); if (!propExpr) { return null(); } if (!handler_.addPropertyDefinition(literal, propName, propExpr)) { return null(); } } 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 null(); } } } FunctionNodeType funNode = methodDefinition(namePos.begin, propType, funName); if (!funNode) { return null(); } AccessorType atype = ToAccessorType(propType); if (!handler_.addObjectMethodDefinition(literal, propName, funNode, atype)) { return null(); } if (possibleError) { possibleError->setPendingDestructuringErrorAt( namePos, JSMSG_BAD_DESTRUCT_TARGET); } } } bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::SlashIsInvalid)) { return null(); } 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 null(); } handler_.setEndPosition(literal, pos().end); return literal; } #ifdef ENABLE_RECORD_TUPLE template typename ParseHandler::ListNodeType GeneralParser::recordLiteral(YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::HashCurly)); uint32_t openedPos = pos().begin; ListNodeType literal = handler_.newRecordLiteral(pos().begin); if (!literal) { return null(); } TaggedParserAtomIndex propAtom; for (;;) { TokenKind tt; if (!tokenStream.peekToken(&tt)) { return null(); } 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 null(); } Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited); if (!inner) { return null(); } if (!handler_.addSpreadProperty(literal, begin, inner)) { return null(); } } else { TokenPos namePos = anyChars.nextToken().pos; PropertyType propType; Node propName = propertyOrMethodName(yieldHandling, PropertyNameInRecord, /* maybeDecl */ Nothing(), literal, &propType, &propAtom); if (!propName) { return null(); } if (propType == PropertyType::Normal) { TokenPos exprPos; if (!tokenStream.peekTokenPos(&exprPos, TokenStream::SlashIsRegExp)) { return null(); } Node propExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited); if (!propExpr) { return null(); } if (propAtom == TaggedParserAtomIndex::WellKnown::proto()) { errorAt(namePos.begin, JSMSG_RECORD_NO_PROTO); return null(); } BinaryNodeType propDef = handler_.newPropertyDefinition(propName, propExpr); if (!propDef) { return null(); } 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 null(); } NameNodeType nameExpr = identifierReference(name); if (!nameExpr) { return null(); } if (!handler_.addShorthand(literal, handler_.asName(propName), nameExpr)) { return null(); } } else { error(JSMSG_BAD_PROP_ID); return null(); } } bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::SlashIsInvalid)) { return null(); } if (!matched) { break; } } if (!mustMatchToken( TokenKind::RightCurly, [this, openedPos](TokenKind actual) { this->reportMissingClosing(JSMSG_CURLY_AFTER_LIST, JSMSG_CURLY_OPENED, openedPos); })) { return null(); } handler_.setEndPosition(literal, pos().end); return literal; } template typename ParseHandler::ListNodeType GeneralParser::tupleLiteral(YieldHandling yieldHandling) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::HashBracket)); uint32_t begin = pos().begin; ListNodeType literal = handler_.newTupleLiteral(begin); if (!literal) { return null(); } for (uint32_t index = 0;; index++) { if (index >= NativeObject::MAX_DENSE_ELEMENTS_COUNT) { error(JSMSG_ARRAY_INIT_TOO_BIG); return null(); } TokenKind tt; if (!tokenStream.peekToken(&tt, TokenStream::SlashIsRegExp)) { return null(); } 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 null(); } Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited); if (!inner) { return null(); } if (!handler_.addSpreadElement(literal, begin, inner)) { return null(); } } else { TokenPos elementPos; if (!tokenStream.peekTokenPos(&elementPos, TokenStream::SlashIsRegExp)) { return null(); } Node element = assignExpr(InAllowed, yieldHandling, TripledotProhibited); if (!element) { return null(); } handler_.addArrayElement(literal, element); } bool matched; if (!tokenStream.matchToken(&matched, TokenKind::Comma, TokenStream::SlashIsRegExp)) { return null(); } if (!matched) { break; } } if (!mustMatchToken(TokenKind::RightBracket, [this, begin](TokenKind actual) { this->reportMissingClosing(JSMSG_BRACKET_AFTER_LIST, JSMSG_BRACKET_OPENED, begin); })) { return null(); } handler_.setEndPosition(literal, pos().end); return literal; } #endif template typename ParseHandler::FunctionNodeType 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 = handler_.newFunction(syntaxKind, pos()); if (!funNode) { return null(); } 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 = handler_.newPosHolder(pos()); if (!newHolder) { return 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 = handler_.newPosHolder(pos()); if (!targetHolder) { return false; } NameNodeType newTargetName = newNewTargetName(); if (!newTargetName) { return false; } *newTarget = handler_.newNewTarget(newHolder, targetHolder, newTargetName); return !!*newTarget; } template typename ParseHandler::BinaryNodeType GeneralParser::importExpr(YieldHandling yieldHandling, bool allowCallSyntax) { MOZ_ASSERT(anyChars.isCurrentTokenType(TokenKind::Import)); NullaryNodeType importHolder = handler_.newPosHolder(pos()); if (!importHolder) { return null(); } TokenKind next; if (!tokenStream.getToken(&next)) { return null(); } if (next == TokenKind::Dot) { if (!tokenStream.getToken(&next)) { return null(); } if (next != TokenKind::Meta) { error(JSMSG_UNEXPECTED_TOKEN, "meta", TokenKindToDesc(next)); return null(); } if (parseGoal() != ParseGoal::Module) { errorAt(pos().begin, JSMSG_IMPORT_META_OUTSIDE_MODULE); return null(); } NullaryNodeType metaHolder = handler_.newPosHolder(pos()); if (!metaHolder) { return null(); } return handler_.newImportMeta(importHolder, metaHolder); } else if (next == TokenKind::LeftParen && allowCallSyntax) { Node arg = assignExpr(InAllowed, yieldHandling, TripledotProhibited); if (!arg) { return null(); } if (!tokenStream.peekToken(&next, TokenStream::SlashIsRegExp)) { return null(); } Node optionalArg; if (options().importAssertions) { if (next == TokenKind::Comma) { tokenStream.consumeKnownToken(TokenKind::Comma, TokenStream::SlashIsRegExp); if (!tokenStream.peekToken(&next, TokenStream::SlashIsRegExp)) { return null(); } if (next != TokenKind::RightParen) { optionalArg = assignExpr(InAllowed, yieldHandling, TripledotProhibited); if (!optionalArg) { return null(); } if (!tokenStream.peekToken(&next, TokenStream::SlashIsRegExp)) { return null(); } if (next == TokenKind::Comma) { tokenStream.consumeKnownToken(TokenKind::Comma, TokenStream::SlashIsRegExp); } } else { optionalArg = handler_.newPosHolder(TokenPos(pos().end, pos().end)); if (!optionalArg) { return null(); } } } else { optionalArg = handler_.newPosHolder(TokenPos(pos().end, pos().end)); if (!optionalArg) { return null(); } } } else { optionalArg = handler_.newPosHolder(TokenPos(pos().end, pos().end)); if (!optionalArg) { return null(); } } if (!mustMatchToken(TokenKind::RightParen, JSMSG_PAREN_AFTER_ARGS)) { return null(); } Node spec = handler_.newCallImportSpec(arg, optionalArg); if (!spec) { return null(); } return handler_.newCallImport(importHolder, spec); } else { error(JSMSG_UNEXPECTED_TOKEN_NO_EXPECT, TokenKindToDesc(next)); return null(); } } template typename ParseHandler::Node 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 null(); } 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 null(); } 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 null(); } if (next != TokenKind::Arrow) { error(JSMSG_UNEXPECTED_TOKEN, "expression", TokenKindToDesc(TokenKind::RightParen)); return null(); } // 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 = exprInParens(InAllowed, yieldHandling, TripledotAllowed, possibleError); if (!expr) { return null(); } if (!mustMatchToken(TokenKind::RightParen, JSMSG_PAREN_IN_PAREN)) { return null(); } 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 null(); } if (tt == TokenKind::Async) { TokenKind nextSameLine = TokenKind::Eof; if (!tokenStream.peekTokenSameLine(&nextSameLine)) { return null(); } 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 null(); } 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()) { thisName = newThisName(); if (!thisName) { return null(); } } 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 null(); } TokenKind next; if (!tokenStream.getToken(&next)) { return null(); } if (next == TokenKind::LeftBracket || next == TokenKind::LeftCurly) { // Validate, but don't store the pattern right now. The whole arrow // function is reparsed in functionFormalParametersAndBody(). if (!destructuringDeclaration(DeclarationKind::CoverArrowParameter, yieldHandling, next)) { return null(); } } 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 null(); } } if (!tokenStream.getToken(&next)) { return null(); } if (next != TokenKind::RightParen) { error(JSMSG_UNEXPECTED_TOKEN, "closing parenthesis", TokenKindToDesc(next)); return null(); } if (!tokenStream.peekToken(&next)) { return null(); } 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 null(); } anyChars.ungetToken(); // put back right paren // Return an arbitrary expression node. See case TokenKind::RightParen // above. return handler_.newNullLiteral(pos()); } } } template typename ParseHandler::Node 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