From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- js/src/frontend/Parser.cpp | 13078 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 13078 insertions(+) create mode 100644 js/src/frontend/Parser.cpp (limited to 'js/src/frontend/Parser.cpp') diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp new file mode 100644 index 0000000000..b4d0e76cb7 --- /dev/null +++ b/js/src/frontend/Parser.cpp @@ -0,0 +1,13078 @@ +/* -*- 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 -- cgit v1.2.3