diff options
Diffstat (limited to 'js/src/frontend/Parser.h')
-rw-r--r-- | js/src/frontend/Parser.h | 1974 |
1 files changed, 1974 insertions, 0 deletions
diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h new file mode 100644 index 0000000000..9cf19cdaf4 --- /dev/null +++ b/js/src/frontend/Parser.h @@ -0,0 +1,1974 @@ +/* -*- 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. */ + +#ifndef frontend_Parser_h +#define frontend_Parser_h + +/* + * [SMDOC] JS Parser + * + * JS parsers capable of generating ASTs from source text. + * + * A parser embeds token stream information, then gets and matches tokens to + * generate a syntax tree that, if desired, BytecodeEmitter will use to compile + * bytecode. + * + * Like token streams (see the comment near the top of TokenStream.h), parser + * classes are heavily templatized -- along the token stream's character-type + * axis, and also along a full-parse/syntax-parse axis. Certain limitations of + * C++ (primarily the inability to partially specialize function templates), + * plus the desire to minimize compiled code size in duplicate function + * template instantiations wherever possible, mean that Parser exhibits much of + * the same unholy template/inheritance complexity as token streams. + * + * == ParserSharedBase == + * + * ParserSharedBase is the base class for both regular JS and BinAST parsing. + * This class contains common fields and methods between both parsers. There is + * currently no BinAST parser here so this can potentially be merged into the + * ParserBase type below. + * + * == ParserBase → ParserSharedBase, ErrorReportMixin == + * + * ParserBase is the base class for regular JS parser, shared by all regular JS + * parsers of all character types and parse-handling behavior. It stores + * everything character- and handler-agnostic. + * + * ParserBase's most important field is the parser's token stream's + * |TokenStreamAnyChars| component, for all tokenizing aspects that are + * character-type-agnostic. The character-type-sensitive components residing + * in |TokenStreamSpecific| (see the comment near the top of TokenStream.h) + * live elsewhere in this hierarchy. These separate locations are the reason + * for the |AnyCharsAccess| template parameter to |TokenStreamChars| and + * |TokenStreamSpecific|. + * + * == PerHandlerParser<ParseHandler> → ParserBase == + * + * Certain parsing behavior varies between full parsing and syntax-only parsing + * but does not vary across source-text character types. For example, the work + * to "create an arguments object for a function" obviously varies between + * syntax and full parsing but (because no source characters are examined) does + * not vary by source text character type. Such functionality is implemented + * through functions in PerHandlerParser. + * + * Functionality only used by syntax parsing or full parsing doesn't live here: + * it should be implemented in the appropriate Parser<ParseHandler> (described + * further below). + * + * == GeneralParser<ParseHandler, Unit> → PerHandlerParser<ParseHandler> == + * + * Most parsing behavior varies across the character-type axis (and possibly + * along the full/syntax axis). For example: + * + * * Parsing ECMAScript's Expression production, implemented by + * GeneralParser::expr, varies in this manner: different types are used to + * represent nodes in full and syntax parsing (ParseNode* versus an enum), + * and reading the tokens comprising the expression requires inspecting + * individual characters (necessarily dependent upon character type). + * * Reporting an error or warning does not depend on the full/syntax parsing + * distinction. But error reports and warnings include a line of context + * (or a slice of one), for pointing out where a mistake was made. + * Computing such line of context requires inspecting the source text to + * make that line/slice of context, which requires knowing the source text + * character type. + * + * Such functionality, implemented using identical function code across these + * axes, should live in GeneralParser. + * + * GeneralParser's most important field is the parser's token stream's + * |TokenStreamSpecific| component, for all aspects of tokenizing that (contra + * |TokenStreamAnyChars| in ParserBase above) are character-type-sensitive. As + * noted above, this field's existence separate from that in ParserBase + * motivates the |AnyCharsAccess| template parameters on various token stream + * classes. + * + * Everything in PerHandlerParser *could* be folded into GeneralParser (below) + * if desired. We don't fold in this manner because all such functions would + * be instantiated once per Unit -- but if exactly equivalent code would be + * generated (because PerHandlerParser functions have no awareness of Unit), + * it's risky to *depend* upon the compiler coalescing the instantiations into + * one in the final binary. PerHandlerParser guarantees no duplication. + * + * == Parser<ParseHandler, Unit> final → GeneralParser<ParseHandler, Unit> == + * + * The final (pun intended) axis of complexity lies in Parser. + * + * Some functionality depends on character type, yet also is defined in + * significantly different form in full and syntax parsing. For example, + * attempting to parse the source text of a module will do so in full parsing + * but immediately fail in syntax parsing -- so the former is a mess'o'code + * while the latter is effectively |return null();|. Such functionality is + * defined in Parser<SyntaxParseHandler or FullParseHandler, Unit> as + * appropriate. + * + * There's a crucial distinction between GeneralParser and Parser, that + * explains why both must exist (despite taking exactly the same template + * parameters, and despite GeneralParser and Parser existing in a one-to-one + * relationship). GeneralParser is one unspecialized template class: + * + * template<class ParseHandler, typename Unit> + * class GeneralParser : ... + * { + * ...parsing functions... + * }; + * + * but Parser is one undefined template class with two separate + * specializations: + * + * // Declare, but do not define. + * template<class ParseHandler, typename Unit> class Parser; + * + * // Define a syntax-parsing specialization. + * template<typename Unit> + * class Parser<SyntaxParseHandler, Unit> final + * : public GeneralParser<SyntaxParseHandler, Unit> + * { + * ...parsing functions... + * }; + * + * // Define a full-parsing specialization. + * template<typename Unit> + * class Parser<SyntaxParseHandler, Unit> final + * : public GeneralParser<SyntaxParseHandler, Unit> + * { + * ...parsing functions... + * }; + * + * This odd distinction is necessary because C++ unfortunately doesn't allow + * partial function specialization: + * + * // BAD: You can only specialize a template function if you specify *every* + * // template parameter, i.e. ParseHandler *and* Unit. + * template<typename Unit> + * void + * GeneralParser<SyntaxParseHandler, Unit>::foo() {} + * + * But if you specialize Parser *as a class*, then this is allowed: + * + * template<typename Unit> + * void + * Parser<SyntaxParseHandler, Unit>::foo() {} + * + * template<typename Unit> + * void + * Parser<FullParseHandler, Unit>::foo() {} + * + * because the only template parameter on the function is Unit -- and so all + * template parameters *are* varying, not a strict subset of them. + * + * So -- any parsing functionality that is differently defined for different + * ParseHandlers, *but* is defined textually identically for different Unit + * (even if different code ends up generated for them by the compiler), should + * reside in Parser. + */ + +#include "mozilla/Maybe.h" + +#include <type_traits> +#include <utility> + +#include "frontend/CompilationStencil.h" // CompilationState +#include "frontend/ErrorReporter.h" +#include "frontend/FullParseHandler.h" +#include "frontend/FunctionSyntaxKind.h" // FunctionSyntaxKind +#include "frontend/IteratorKind.h" +#include "frontend/NameAnalysisTypes.h" +#include "frontend/ParseContext.h" +#include "frontend/ParserAtom.h" // ParserAtomsTable, TaggedParserAtomIndex +#include "frontend/SharedContext.h" +#include "frontend/SyntaxParseHandler.h" +#include "frontend/TokenStream.h" +#include "js/CharacterEncoding.h" // JS::ConstUTF8CharsZ +#include "js/friend/ErrorMessages.h" // JSErrNum, JSMSG_* +#include "vm/GeneratorAndAsyncKind.h" // js::GeneratorKind, js::FunctionAsyncKind + +namespace js { + +class FrontendContext; +struct ErrorMetadata; + +namespace frontend { + +template <class ParseHandler, typename Unit> +class GeneralParser; + +class SourceParseContext : public ParseContext { + public: + template <typename ParseHandler, typename Unit> + SourceParseContext(GeneralParser<ParseHandler, Unit>* prs, SharedContext* sc, + Directives* newDirectives) + : ParseContext(prs->fc_, prs->pc_, sc, prs->tokenStream, + prs->compilationState_, newDirectives, + std::is_same_v<ParseHandler, FullParseHandler>) {} +}; + +enum VarContext { HoistVars, DontHoistVars }; +enum PropListType { ObjectLiteral, ClassBody, DerivedClassBody }; +enum class PropertyType { + Normal, + Shorthand, + CoverInitializedName, + Getter, + Setter, + Method, + GeneratorMethod, + AsyncMethod, + AsyncGeneratorMethod, + Constructor, + DerivedConstructor, + Field, + FieldWithAccessor, +}; + +enum AwaitHandling : uint8_t { + AwaitIsName, + AwaitIsKeyword, + AwaitIsModuleKeyword, + AwaitIsDisallowed +}; + +template <class ParseHandler, typename Unit> +class AutoAwaitIsKeyword; + +template <class ParseHandler, typename Unit> +class AutoInParametersOfAsyncFunction; + +class MOZ_STACK_CLASS ParserSharedBase { + public: + enum class Kind { Parser }; + + ParserSharedBase(FrontendContext* fc, CompilationState& compilationState, + Kind kind); + ~ParserSharedBase(); + + public: + FrontendContext* fc_; + + LifoAlloc& alloc_; + + CompilationState& compilationState_; + + // innermost parse context (stack-allocated) + ParseContext* pc_; + + // For tracking used names in this parsing session. + UsedNameTracker& usedNames_; + + public: + CompilationState& getCompilationState() { return compilationState_; } + + ParserAtomsTable& parserAtoms() { return compilationState_.parserAtoms; } + const ParserAtomsTable& parserAtoms() const { + return compilationState_.parserAtoms; + } + + LifoAlloc& stencilAlloc() { return compilationState_.alloc; } + + const UsedNameTracker& usedNames() { return usedNames_; } + +#if defined(DEBUG) || defined(JS_JITSPEW) + void dumpAtom(TaggedParserAtomIndex index) const; +#endif +}; + +class MOZ_STACK_CLASS ParserBase : public ParserSharedBase, + public ErrorReportMixin { + using Base = ErrorReportMixin; + + public: + TokenStreamAnyChars anyChars; + + ScriptSource* ss; + + // Perform constant-folding; must be true when interfacing with the emitter. + const bool foldConstants_ : 1; + + protected: +#if DEBUG + /* Our fallible 'checkOptions' member function has been called. */ + bool checkOptionsCalled_ : 1; +#endif + + /* Unexpected end of input, i.e. Eof not at top-level. */ + bool isUnexpectedEOF_ : 1; + + /* AwaitHandling */ uint8_t awaitHandling_ : 2; + + bool inParametersOfAsyncFunction_ : 1; + + public: + JSAtom* liftParserAtomToJSAtom(TaggedParserAtomIndex index); + + bool awaitIsKeyword() const { + return awaitHandling_ == AwaitIsKeyword || + awaitHandling_ == AwaitIsModuleKeyword; + } + bool awaitIsDisallowed() const { return awaitHandling_ == AwaitIsDisallowed; } + + bool inParametersOfAsyncFunction() const { + return inParametersOfAsyncFunction_; + } + + ParseGoal parseGoal() const { + return pc_->sc()->hasModuleGoal() ? ParseGoal::Module : ParseGoal::Script; + } + + template <class, typename> + friend class AutoAwaitIsKeyword; + template <class, typename> + friend class AutoInParametersOfAsyncFunction; + + ParserBase(FrontendContext* fc, const JS::ReadOnlyCompileOptions& options, + bool foldConstants, CompilationState& compilationState); + ~ParserBase(); + + bool checkOptions(); + + JS::ConstUTF8CharsZ getFilename() const { return anyChars.getFilename(); } + TokenPos pos() const { return anyChars.currentToken().pos; } + + // Determine whether |yield| is a valid name in the current context. + bool yieldExpressionsSupported() const { return pc_->isGenerator(); } + + bool setLocalStrictMode(bool strict) { + MOZ_ASSERT(anyChars.debugHasNoLookahead()); + return pc_->sc()->setLocalStrictMode(strict); + } + + public: + // Implement ErrorReportMixin. + + FrontendContext* getContext() const override { return fc_; } + + bool strictMode() const override { return pc_->sc()->strict(); } + + const JS::ReadOnlyCompileOptions& options() const override { + return anyChars.options(); + } + + using Base::error; + using Base::errorAt; + using Base::errorNoOffset; + using Base::errorWithNotes; + using Base::errorWithNotesAt; + using Base::errorWithNotesNoOffset; + using Base::strictModeError; + using Base::strictModeErrorAt; + using Base::strictModeErrorNoOffset; + using Base::strictModeErrorWithNotes; + using Base::strictModeErrorWithNotesAt; + using Base::strictModeErrorWithNotesNoOffset; + using Base::warning; + using Base::warningAt; + using Base::warningNoOffset; + + public: + bool isUnexpectedEOF() const { return isUnexpectedEOF_; } + + bool isValidStrictBinding(TaggedParserAtomIndex name); + + bool hasValidSimpleStrictParameterNames(); + + // A Parser::Mark is the extension of the LifoAlloc::Mark to the entire + // Parser's state. Note: clients must still take care that any ParseContext + // that points into released ParseNodes is destroyed. + class Mark { + friend class ParserBase; + LifoAlloc::Mark mark; + CompilationState::CompilationStatePosition pos; + }; + Mark mark() const { + Mark m; + m.mark = alloc_.mark(); + m.pos = compilationState_.getPosition(); + return m; + } + void release(Mark m) { + alloc_.release(m.mark); + compilationState_.rewind(m.pos); + } + + public: + mozilla::Maybe<GlobalScope::ParserData*> newGlobalScopeData( + ParseContext::Scope& scope); + mozilla::Maybe<ModuleScope::ParserData*> newModuleScopeData( + ParseContext::Scope& scope); + mozilla::Maybe<EvalScope::ParserData*> newEvalScopeData( + ParseContext::Scope& scope); + mozilla::Maybe<FunctionScope::ParserData*> newFunctionScopeData( + ParseContext::Scope& scope, bool hasParameterExprs); + mozilla::Maybe<VarScope::ParserData*> newVarScopeData( + ParseContext::Scope& scope); + mozilla::Maybe<LexicalScope::ParserData*> newLexicalScopeData( + ParseContext::Scope& scope); + mozilla::Maybe<ClassBodyScope::ParserData*> newClassBodyScopeData( + ParseContext::Scope& scope); + + protected: + enum InvokedPrediction { PredictUninvoked = false, PredictInvoked = true }; + enum ForInitLocation { InForInit, NotInForInit }; + + // While on a |let| Name token, examine |next| (which must already be + // gotten). Indicate whether |next|, the next token already gotten with + // modifier TokenStream::SlashIsDiv, continues a LexicalDeclaration. + bool nextTokenContinuesLetDeclaration(TokenKind next); + + bool noteUsedNameInternal(TaggedParserAtomIndex name, + NameVisibility visibility, + mozilla::Maybe<TokenPos> tokenPosition); + + bool checkAndMarkSuperScope(); + + bool leaveInnerFunction(ParseContext* outerpc); + + TaggedParserAtomIndex prefixAccessorName(PropertyType propType, + TaggedParserAtomIndex propAtom); + + [[nodiscard]] bool setSourceMapInfo(); + + void setFunctionEndFromCurrentToken(FunctionBox* funbox) const; +}; + +template <class ParseHandler> +class MOZ_STACK_CLASS PerHandlerParser : public ParserBase { + using Base = ParserBase; + + private: + using Node = typename ParseHandler::Node; + using NodeResult = typename ParseHandler::NodeResult; + +#define DECLARE_TYPE(typeName) \ + using typeName##Type = typename ParseHandler::typeName##Type; \ + using typeName##Result = typename ParseHandler::typeName##Result; + FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE) +#undef DECLARE_TYPE + + protected: + /* State specific to the kind of parse being performed. */ + ParseHandler handler_; + + // When ParseHandler is FullParseHandler: + // + // If non-null, this field holds the syntax parser used to attempt lazy + // parsing of inner functions. If null, then lazy parsing is disabled. + // + // When ParseHandler is SyntaxParseHandler: + // + // If non-null, this field must be a sentinel value signaling that the + // syntax parse was aborted. If null, then lazy parsing was aborted due + // to encountering unsupported language constructs. + // + // |internalSyntaxParser_| is really a |Parser<SyntaxParseHandler, Unit>*| + // where |Unit| varies per |Parser<ParseHandler, Unit>|. But this + // template class doesn't know |Unit|, so we store a |void*| here and make + // |GeneralParser<ParseHandler, Unit>::getSyntaxParser| impose the real type. + void* internalSyntaxParser_; + + private: + // NOTE: The argument ordering here is deliberately different from the + // public constructor so that typos calling the public constructor + // are less likely to select this overload. + PerHandlerParser(FrontendContext* fc, + const JS::ReadOnlyCompileOptions& options, + bool foldConstants, CompilationState& compilationState, + void* internalSyntaxParser); + + protected: + template <typename Unit> + PerHandlerParser(FrontendContext* fc, + const JS::ReadOnlyCompileOptions& options, + bool foldConstants, CompilationState& compilationState, + GeneralParser<SyntaxParseHandler, Unit>* syntaxParser) + : PerHandlerParser(fc, options, foldConstants, compilationState, + static_cast<void*>(syntaxParser)) {} + + static typename ParseHandler::NullNode null() { return ParseHandler::null(); } + + // The return value for the error case in the functions that returns + // Result type. + static constexpr typename ParseHandler::NodeErrorResult errorResult() { + return ParseHandler::errorResult(); + } + + NameNodeResult stringLiteral(); + + const char* nameIsArgumentsOrEval(Node node); + + bool noteDestructuredPositionalFormalParameter(FunctionNodeType funNode, + Node destruct); + + bool noteUsedName( + TaggedParserAtomIndex name, + NameVisibility visibility = NameVisibility::Public, + mozilla::Maybe<TokenPos> tokenPosition = mozilla::Nothing()) { + // If the we are delazifying, the BaseScript already has all the closed-over + // info for bindings and there's no need to track used names. + if (handler_.reuseClosedOverBindings()) { + return true; + } + + return ParserBase::noteUsedNameInternal(name, visibility, tokenPosition); + } + + // Required on Scope exit. + bool propagateFreeNamesAndMarkClosedOverBindings(ParseContext::Scope& scope); + + bool checkForUndefinedPrivateFields(EvalSharedContext* evalSc = nullptr); + + bool finishFunctionScopes(bool isStandaloneFunction); + LexicalScopeNodeResult finishLexicalScope( + ParseContext::Scope& scope, Node body, + ScopeKind kind = ScopeKind::Lexical); + ClassBodyScopeNodeResult finishClassBodyScope(ParseContext::Scope& scope, + ListNodeType body); + bool finishFunction(bool isStandaloneFunction = false); + + inline NameNodeResult newName(TaggedParserAtomIndex name); + inline NameNodeResult newName(TaggedParserAtomIndex name, TokenPos pos); + + inline NameNodeResult newPrivateName(TaggedParserAtomIndex name); + + NameNodeResult newInternalDotName(TaggedParserAtomIndex name); + NameNodeResult newThisName(); + NameNodeResult newNewTargetName(); + NameNodeResult newDotGeneratorName(); + + NameNodeResult identifierReference(TaggedParserAtomIndex name); + NameNodeResult privateNameReference(TaggedParserAtomIndex name); + + NodeResult noSubstitutionTaggedTemplate(); + + inline bool processExport(Node node); + inline bool processExportFrom(BinaryNodeType node); + inline bool processImport(BinaryNodeType node); + + // If ParseHandler is SyntaxParseHandler: + // Do nothing. + // If ParseHandler is FullParseHandler: + // Disable syntax parsing of all future inner functions during this + // full-parse. + inline void disableSyntaxParser(); + + // If ParseHandler is SyntaxParseHandler: + // Flag the current syntax parse as aborted due to unsupported language + // constructs and return false. Aborting the current syntax parse does + // not disable attempts to syntax-parse future inner functions. + // If ParseHandler is FullParseHandler: + // Disable syntax parsing of all future inner functions and return true. + inline bool abortIfSyntaxParser(); + + // If ParseHandler is SyntaxParseHandler: + // Return whether the last syntax parse was aborted due to unsupported + // language constructs. + // If ParseHandler is FullParseHandler: + // Return false. + inline bool hadAbortedSyntaxParse(); + + // If ParseHandler is SyntaxParseHandler: + // Clear whether the last syntax parse was aborted. + // If ParseHandler is FullParseHandler: + // Do nothing. + inline void clearAbortedSyntaxParse(); + + public: + FunctionBox* newFunctionBox(FunctionNodeType funNode, + TaggedParserAtomIndex explicitName, + FunctionFlags flags, uint32_t toStringStart, + Directives directives, + GeneratorKind generatorKind, + FunctionAsyncKind asyncKind); + + FunctionBox* newFunctionBox(FunctionNodeType funNode, + const ScriptStencil& cachedScriptData, + const ScriptStencilExtra& cachedScriptExtra); + + public: + // ErrorReportMixin. + + using Base::error; + using Base::errorAt; + using Base::errorNoOffset; + using Base::errorWithNotes; + using Base::errorWithNotesAt; + using Base::errorWithNotesNoOffset; + using Base::strictModeError; + using Base::strictModeErrorAt; + using Base::strictModeErrorNoOffset; + using Base::strictModeErrorWithNotes; + using Base::strictModeErrorWithNotesAt; + using Base::strictModeErrorWithNotesNoOffset; + using Base::warning; + using Base::warningAt; + using Base::warningNoOffset; +}; + +#define ABORTED_SYNTAX_PARSE_SENTINEL reinterpret_cast<void*>(0x1) + +template <> +inline void PerHandlerParser<SyntaxParseHandler>::disableSyntaxParser() {} + +template <> +inline bool PerHandlerParser<SyntaxParseHandler>::abortIfSyntaxParser() { + internalSyntaxParser_ = ABORTED_SYNTAX_PARSE_SENTINEL; + return false; +} + +template <> +inline bool PerHandlerParser<SyntaxParseHandler>::hadAbortedSyntaxParse() { + return internalSyntaxParser_ == ABORTED_SYNTAX_PARSE_SENTINEL; +} + +template <> +inline void PerHandlerParser<SyntaxParseHandler>::clearAbortedSyntaxParse() { + internalSyntaxParser_ = nullptr; +} + +#undef ABORTED_SYNTAX_PARSE_SENTINEL + +// Disable syntax parsing of all future inner functions during this +// full-parse. +template <> +inline void PerHandlerParser<FullParseHandler>::disableSyntaxParser() { + internalSyntaxParser_ = nullptr; +} + +template <> +inline bool PerHandlerParser<FullParseHandler>::abortIfSyntaxParser() { + disableSyntaxParser(); + return true; +} + +template <> +inline bool PerHandlerParser<FullParseHandler>::hadAbortedSyntaxParse() { + return false; +} + +template <> +inline void PerHandlerParser<FullParseHandler>::clearAbortedSyntaxParse() {} + +template <class Parser> +class ParserAnyCharsAccess { + public: + using TokenStreamSpecific = typename Parser::TokenStream; + using GeneralTokenStreamChars = + typename TokenStreamSpecific::GeneralCharsBase; + + static inline TokenStreamAnyChars& anyChars(GeneralTokenStreamChars* ts); + static inline const TokenStreamAnyChars& anyChars( + const GeneralTokenStreamChars* ts); +}; + +// Specify a value for an ES6 grammar parametrization. We have no enum for +// [Return] because its behavior is almost exactly equivalent to checking +// whether we're in a function box -- easier and simpler than passing an extra +// parameter everywhere. +enum YieldHandling { YieldIsName, YieldIsKeyword }; +enum InHandling { InAllowed, InProhibited }; +enum DefaultHandling { NameRequired, AllowDefaultName }; +enum TripledotHandling { TripledotAllowed, TripledotProhibited }; + +// For Ergonomic brand checks. +enum PrivateNameHandling { PrivateNameProhibited, PrivateNameAllowed }; + +template <class ParseHandler, typename Unit> +class Parser; + +template <class ParseHandler, typename Unit> +class MOZ_STACK_CLASS GeneralParser : public PerHandlerParser<ParseHandler> { + public: + using TokenStream = + TokenStreamSpecific<Unit, ParserAnyCharsAccess<GeneralParser>>; + + private: + using Base = PerHandlerParser<ParseHandler>; + using FinalParser = Parser<ParseHandler, Unit>; + using Node = typename ParseHandler::Node; + using NodeResult = typename ParseHandler::NodeResult; + +#define DECLARE_TYPE(typeName) \ + using typeName##Type = typename ParseHandler::typeName##Type; \ + using typeName##Result = typename ParseHandler::typeName##Result; + FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE) +#undef DECLARE_TYPE + + using typename Base::InvokedPrediction; + using SyntaxParser = Parser<SyntaxParseHandler, Unit>; + + protected: + using Modifier = TokenStreamShared::Modifier; + using Position = typename TokenStream::Position; + + using Base::PredictInvoked; + using Base::PredictUninvoked; + + using Base::alloc_; + using Base::awaitIsDisallowed; + using Base::awaitIsKeyword; + using Base::inParametersOfAsyncFunction; + using Base::parseGoal; +#if DEBUG + using Base::checkOptionsCalled_; +#endif + using Base::checkForUndefinedPrivateFields; + using Base::errorResult; + using Base::finishClassBodyScope; + using Base::finishFunctionScopes; + using Base::finishLexicalScope; + using Base::foldConstants_; + using Base::getFilename; + using Base::hasValidSimpleStrictParameterNames; + using Base::isUnexpectedEOF_; + using Base::nameIsArgumentsOrEval; + using Base::newDotGeneratorName; + using Base::newFunctionBox; + using Base::newName; + using Base::null; + using Base::options; + using Base::pos; + using Base::propagateFreeNamesAndMarkClosedOverBindings; + using Base::setLocalStrictMode; + using Base::stringLiteral; + using Base::yieldExpressionsSupported; + + using Base::abortIfSyntaxParser; + using Base::clearAbortedSyntaxParse; + using Base::disableSyntaxParser; + using Base::hadAbortedSyntaxParse; + + public: + // Implement ErrorReportMixin. + + [[nodiscard]] bool computeErrorMetadata( + ErrorMetadata* err, + const ErrorReportMixin::ErrorOffset& offset) const override; + + using Base::error; + using Base::errorAt; + using Base::errorNoOffset; + using Base::errorWithNotes; + using Base::errorWithNotesAt; + using Base::errorWithNotesNoOffset; + using Base::strictModeError; + using Base::strictModeErrorAt; + using Base::strictModeErrorNoOffset; + using Base::strictModeErrorWithNotes; + using Base::strictModeErrorWithNotesAt; + using Base::strictModeErrorWithNotesNoOffset; + using Base::warning; + using Base::warningAt; + using Base::warningNoOffset; + + public: + using Base::anyChars; + using Base::fc_; + using Base::handler_; + using Base::noteUsedName; + using Base::pc_; + using Base::usedNames_; + + private: + using Base::checkAndMarkSuperScope; + using Base::finishFunction; + using Base::identifierReference; + using Base::leaveInnerFunction; + using Base::newInternalDotName; + using Base::newNewTargetName; + using Base::newThisName; + using Base::nextTokenContinuesLetDeclaration; + using Base::noSubstitutionTaggedTemplate; + using Base::noteDestructuredPositionalFormalParameter; + using Base::prefixAccessorName; + using Base::privateNameReference; + using Base::processExport; + using Base::processExportFrom; + using Base::processImport; + using Base::setFunctionEndFromCurrentToken; + + private: + inline FinalParser* asFinalParser(); + inline const FinalParser* asFinalParser() const; + + /* + * A class for temporarily stashing errors while parsing continues. + * + * The ability to stash an error is useful for handling situations where we + * aren't able to verify that an error has occurred until later in the parse. + * For instance | ({x=1}) | is always parsed as an object literal with + * a SyntaxError, however, in the case where it is followed by '=>' we rewind + * and reparse it as a valid arrow function. Here a PossibleError would be + * set to 'pending' when the initial SyntaxError was encountered then + * 'resolved' just before rewinding the parser. + * + * There are currently two kinds of PossibleErrors: Expression and + * Destructuring errors. Expression errors are used to mark a possible + * syntax error when a grammar production is used in an expression context. + * For example in |{x = 1}|, we mark the CoverInitializedName |x = 1| as a + * possible expression error, because CoverInitializedName productions + * are disallowed when an actual ObjectLiteral is expected. + * Destructuring errors are used to record possible syntax errors in + * destructuring contexts. For example in |[...rest, ] = []|, we initially + * mark the trailing comma after the spread expression as a possible + * destructuring error, because the ArrayAssignmentPattern grammar + * production doesn't allow a trailing comma after the rest element. + * + * When using PossibleError one should set a pending error at the location + * where an error occurs. From that point, the error may be resolved + * (invalidated) or left until the PossibleError is checked. + * + * Ex: + * PossibleError possibleError(*this); + * possibleError.setPendingExpressionErrorAt(pos, JSMSG_BAD_PROP_ID); + * // A JSMSG_BAD_PROP_ID ParseError is reported, returns false. + * if (!possibleError.checkForExpressionError()) { + * return false; // we reach this point with a pending exception + * } + * + * PossibleError possibleError(*this); + * possibleError.setPendingExpressionErrorAt(pos, JSMSG_BAD_PROP_ID); + * // Returns true, no error is reported. + * if (!possibleError.checkForDestructuringError()) { + * return false; // not reached, no pending exception + * } + * + * PossibleError possibleError(*this); + * // Returns true, no error is reported. + * if (!possibleError.checkForExpressionError()) { + * return false; // not reached, no pending exception + * } + */ + class MOZ_STACK_CLASS PossibleError { + private: + enum class ErrorKind { Expression, Destructuring, DestructuringWarning }; + + enum class ErrorState { None, Pending }; + + struct Error { + ErrorState state_ = ErrorState::None; + + // Error reporting fields. + uint32_t offset_; + unsigned errorNumber_; + }; + + GeneralParser<ParseHandler, Unit>& parser_; + Error exprError_; + Error destructuringError_; + Error destructuringWarning_; + + // Returns the error report. + Error& error(ErrorKind kind); + + // Return true if an error is pending without reporting. + bool hasError(ErrorKind kind); + + // Resolve any pending error. + void setResolved(ErrorKind kind); + + // Set a pending error. Only a single error may be set per instance and + // error kind. + void setPending(ErrorKind kind, const TokenPos& pos, unsigned errorNumber); + + // If there is a pending error, report it and return false, otherwise + // return true. + [[nodiscard]] bool checkForError(ErrorKind kind); + + // Transfer an existing error to another instance. + void transferErrorTo(ErrorKind kind, PossibleError* other); + + public: + explicit PossibleError(GeneralParser<ParseHandler, Unit>& parser); + + // Return true if a pending destructuring error is present. + bool hasPendingDestructuringError(); + + // Set a pending destructuring error. Only a single error may be set + // per instance, i.e. subsequent calls to this method are ignored and + // won't overwrite the existing pending error. + void setPendingDestructuringErrorAt(const TokenPos& pos, + unsigned errorNumber); + + // Set a pending destructuring warning. Only a single warning may be + // set per instance, i.e. subsequent calls to this method are ignored + // and won't overwrite the existing pending warning. + void setPendingDestructuringWarningAt(const TokenPos& pos, + unsigned errorNumber); + + // Set a pending expression error. Only a single error may be set per + // instance, i.e. subsequent calls to this method are ignored and won't + // overwrite the existing pending error. + void setPendingExpressionErrorAt(const TokenPos& pos, unsigned errorNumber); + + // If there is a pending destructuring error or warning, report it and + // return false, otherwise return true. Clears any pending expression + // error. + [[nodiscard]] bool checkForDestructuringErrorOrWarning(); + + // If there is a pending expression error, report it and return false, + // otherwise return true. Clears any pending destructuring error or + // warning. + [[nodiscard]] bool checkForExpressionError(); + + // Pass pending errors between possible error instances. This is useful + // for extending the lifetime of a pending error beyond the scope of + // the PossibleError where it was initially set (keeping in mind that + // PossibleError is a MOZ_STACK_CLASS). + void transferErrorsTo(PossibleError* other); + }; + + protected: + SyntaxParser* getSyntaxParser() const { + return reinterpret_cast<SyntaxParser*>(Base::internalSyntaxParser_); + } + + public: + TokenStream tokenStream; + + public: + GeneralParser(FrontendContext* fc, const JS::ReadOnlyCompileOptions& options, + const Unit* units, size_t length, bool foldConstants, + CompilationState& compilationState, SyntaxParser* syntaxParser); + + inline void setAwaitHandling(AwaitHandling awaitHandling); + inline void setInParametersOfAsyncFunction(bool inParameters); + + /* + * Parse a top-level JS script. + */ + ListNodeResult parse(); + + private: + /* + * Gets the next token and checks if it matches to the given `condition`. + * If it matches, returns true. + * If it doesn't match, calls `errorReport` to report the error, and + * returns false. + * If other error happens, it returns false but `errorReport` may not be + * called and other error will be thrown in that case. + * + * In any case, the already gotten token is not ungotten. + * + * The signature of `condition` is [...](TokenKind actual) -> bool, and + * the signature of `errorReport` is [...](TokenKind actual). + */ + template <typename ConditionT, typename ErrorReportT> + [[nodiscard]] bool mustMatchTokenInternal(ConditionT condition, + ErrorReportT errorReport); + + public: + /* + * The following mustMatchToken variants follow the behavior and parameter + * types of mustMatchTokenInternal above. + * + * If modifier is omitted, `SlashIsDiv` is used. + * If TokenKind is passed instead of `condition`, it checks if the next + * token is the passed token. + * If error number is passed instead of `errorReport`, it reports an + * error with the passed errorNumber. + */ + [[nodiscard]] bool mustMatchToken(TokenKind expected, JSErrNum errorNumber) { + return mustMatchTokenInternal( + [expected](TokenKind actual) { return actual == expected; }, + [this, errorNumber](TokenKind) { this->error(errorNumber); }); + } + + template <typename ConditionT> + [[nodiscard]] bool mustMatchToken(ConditionT condition, + JSErrNum errorNumber) { + return mustMatchTokenInternal(condition, [this, errorNumber](TokenKind) { + this->error(errorNumber); + }); + } + + template <typename ErrorReportT> + [[nodiscard]] bool mustMatchToken(TokenKind expected, + ErrorReportT errorReport) { + return mustMatchTokenInternal( + [expected](TokenKind actual) { return actual == expected; }, + errorReport); + } + + private: + NameNodeResult noSubstitutionUntaggedTemplate(); + ListNodeResult templateLiteral(YieldHandling yieldHandling); + bool taggedTemplate(YieldHandling yieldHandling, ListNodeType tagArgsList, + TokenKind tt); + bool appendToCallSiteObj(CallSiteNodeType callSiteObj); + bool addExprAndGetNextTemplStrToken(YieldHandling yieldHandling, + ListNodeType nodeList, TokenKind* ttp); + + inline bool 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); + + inline bool skipLazyInnerFunction(FunctionNodeType funNode, + uint32_t toStringStart, bool tryAnnexB); + + void setFunctionStartAtPosition(FunctionBox* funbox, TokenPos pos) const; + void setFunctionStartAtCurrentToken(FunctionBox* funbox) const; + + public: + /* Public entry points for parsing. */ + NodeResult statementListItem(YieldHandling yieldHandling, + bool canHaveDirectives = false); + + // Parse an inner function given an enclosing ParseContext and a + // FunctionBox for the inner function. + [[nodiscard]] FunctionNodeResult innerFunctionForFunctionBox( + FunctionNodeType funNode, ParseContext* outerpc, FunctionBox* funbox, + InHandling inHandling, YieldHandling yieldHandling, + FunctionSyntaxKind kind, Directives* newDirectives); + + // Parse a function's formal parameters and its body assuming its function + // ParseContext is already on the stack. + bool functionFormalParametersAndBody( + InHandling inHandling, YieldHandling yieldHandling, + FunctionNodeType* funNode, FunctionSyntaxKind kind, + const mozilla::Maybe<uint32_t>& parameterListEnd = mozilla::Nothing(), + bool isStandaloneFunction = false); + + private: + /* + * JS parsers, from lowest to highest precedence. + * + * Each parser must be called during the dynamic scope of a ParseContext + * object, pointed to by this->pc_. + * + * Each returns a parse node tree or null on error. + */ + FunctionNodeResult functionStmt( + uint32_t toStringStart, YieldHandling yieldHandling, + DefaultHandling defaultHandling, + FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction); + FunctionNodeResult functionExpr(uint32_t toStringStart, + InvokedPrediction invoked, + FunctionAsyncKind asyncKind); + + NodeResult statement(YieldHandling yieldHandling); + bool maybeParseDirective(ListNodeType list, Node pn, bool* cont); + + LexicalScopeNodeResult blockStatement( + YieldHandling yieldHandling, + unsigned errorNumber = JSMSG_CURLY_IN_COMPOUND); + BinaryNodeResult doWhileStatement(YieldHandling yieldHandling); + BinaryNodeResult whileStatement(YieldHandling yieldHandling); + + NodeResult forStatement(YieldHandling yieldHandling); + bool forHeadStart(YieldHandling yieldHandling, IteratorKind iterKind, + ParseNodeKind* forHeadKind, Node* forInitialPart, + mozilla::Maybe<ParseContext::Scope>& forLetImpliedScope, + Node* forInOrOfExpression); + NodeResult expressionAfterForInOrOf(ParseNodeKind forHeadKind, + YieldHandling yieldHandling); + + SwitchStatementResult switchStatement(YieldHandling yieldHandling); + ContinueStatementResult continueStatement(YieldHandling yieldHandling); + BreakStatementResult breakStatement(YieldHandling yieldHandling); + UnaryNodeResult returnStatement(YieldHandling yieldHandling); + BinaryNodeResult withStatement(YieldHandling yieldHandling); + UnaryNodeResult throwStatement(YieldHandling yieldHandling); + TernaryNodeResult tryStatement(YieldHandling yieldHandling); + LexicalScopeNodeResult catchBlockStatement( + YieldHandling yieldHandling, ParseContext::Scope& catchParamScope); + DebuggerStatementResult debuggerStatement(); + + DeclarationListNodeResult variableStatement(YieldHandling yieldHandling); + + LabeledStatementResult labeledStatement(YieldHandling yieldHandling); + NodeResult labeledItem(YieldHandling yieldHandling); + + TernaryNodeResult ifStatement(YieldHandling yieldHandling); + NodeResult consequentOrAlternative(YieldHandling yieldHandling); + + DeclarationListNodeResult lexicalDeclaration(YieldHandling yieldHandling, + DeclarationKind kind); + + NameNodeResult moduleExportName(); + + bool withClause(ListNodeType attributesSet); + + BinaryNodeResult importDeclaration(); + NodeResult importDeclarationOrImportExpr(YieldHandling yieldHandling); + bool namedImports(ListNodeType importSpecSet); + bool namespaceImport(ListNodeType importSpecSet); + + TaggedParserAtomIndex importedBinding() { + return bindingIdentifier(YieldIsName); + } + + BinaryNodeResult exportFrom(uint32_t begin, Node specList); + BinaryNodeResult exportBatch(uint32_t begin); + inline bool checkLocalExportNames(ListNodeType node); + NodeResult exportClause(uint32_t begin); + UnaryNodeResult exportFunctionDeclaration( + uint32_t begin, uint32_t toStringStart, + FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction); + UnaryNodeResult exportVariableStatement(uint32_t begin); + UnaryNodeResult exportClassDeclaration(uint32_t begin); + UnaryNodeResult exportLexicalDeclaration(uint32_t begin, + DeclarationKind kind); + BinaryNodeResult exportDefaultFunctionDeclaration( + uint32_t begin, uint32_t toStringStart, + FunctionAsyncKind asyncKind = FunctionAsyncKind::SyncFunction); + BinaryNodeResult exportDefaultClassDeclaration(uint32_t begin); + BinaryNodeResult exportDefaultAssignExpr(uint32_t begin); + BinaryNodeResult exportDefault(uint32_t begin); + NodeResult exportDeclaration(); + + UnaryNodeResult expressionStatement( + YieldHandling yieldHandling, + InvokedPrediction invoked = PredictUninvoked); + + // Declaration parsing. The main entrypoint is Parser::declarationList, + // with sub-functionality split out into the remaining methods. + + // |blockScope| may be non-null only when |kind| corresponds to a lexical + // declaration (that is, ParseNodeKind::LetDecl or ParseNodeKind::ConstDecl). + // + // The for* parameters, for normal declarations, should be null/ignored. + // They should be non-null only when Parser::forHeadStart parses a + // declaration at the start of a for-loop head. + // + // In this case, on success |*forHeadKind| is ParseNodeKind::ForHead, + // ParseNodeKind::ForIn, or ParseNodeKind::ForOf, corresponding to the three + // for-loop kinds. The precise value indicates what was parsed. + // + // If parsing recognized a for(;;) loop, the next token is the ';' within + // the loop-head that separates the init/test parts. + // + // Otherwise, for for-in/of loops, the next token is the ')' ending the + // loop-head. Additionally, the expression that the loop iterates over was + // parsed into |*forInOrOfExpression|. + DeclarationListNodeResult declarationList( + YieldHandling yieldHandling, ParseNodeKind kind, + ParseNodeKind* forHeadKind = nullptr, + Node* forInOrOfExpression = nullptr); + + // The items in a declaration list are either patterns or names, with or + // without initializers. These two methods parse a single pattern/name and + // any associated initializer -- and if parsing an |initialDeclaration| + // will, if parsing in a for-loop head (as specified by |forHeadKind| being + // non-null), consume additional tokens up to the closing ')' in a + // for-in/of loop head, returning the iterated expression in + // |*forInOrOfExpression|. (An "initial declaration" is the first + // declaration in a declaration list: |a| but not |b| in |var a, b|, |{c}| + // but not |d| in |let {c} = 3, d|.) + NodeResult declarationPattern(DeclarationKind declKind, TokenKind tt, + bool initialDeclaration, + YieldHandling yieldHandling, + ParseNodeKind* forHeadKind, + Node* forInOrOfExpression); + NodeResult declarationName(DeclarationKind declKind, TokenKind tt, + bool initialDeclaration, + YieldHandling yieldHandling, + ParseNodeKind* forHeadKind, + Node* forInOrOfExpression); + + // Having parsed a name (not found in a destructuring pattern) declared by + // a declaration, with the current token being the '=' separating the name + // from its initializer, parse and bind that initializer -- and possibly + // consume trailing in/of and subsequent expression, if so directed by + // |forHeadKind|. + AssignmentNodeResult initializerInNameDeclaration(NameNodeType binding, + DeclarationKind declKind, + bool initialDeclaration, + YieldHandling yieldHandling, + ParseNodeKind* forHeadKind, + Node* forInOrOfExpression); + + NodeResult expr(InHandling inHandling, YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + PossibleError* possibleError = nullptr, + InvokedPrediction invoked = PredictUninvoked); + NodeResult assignExpr(InHandling inHandling, YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + PossibleError* possibleError = nullptr, + InvokedPrediction invoked = PredictUninvoked); + NodeResult assignExprWithoutYieldOrAwait(YieldHandling yieldHandling); + UnaryNodeResult yieldExpression(InHandling inHandling); + NodeResult condExpr(InHandling inHandling, YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + PossibleError* possibleError, InvokedPrediction invoked); + NodeResult orExpr(InHandling inHandling, YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + PossibleError* possibleError, InvokedPrediction invoked); + NodeResult unaryExpr(YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + PossibleError* possibleError = nullptr, + InvokedPrediction invoked = PredictUninvoked, + PrivateNameHandling privateNameHandling = + PrivateNameHandling::PrivateNameProhibited); + NodeResult optionalExpr(YieldHandling yieldHandling, + TripledotHandling tripledotHandling, TokenKind tt, + PossibleError* possibleError = nullptr, + InvokedPrediction invoked = PredictUninvoked); + NodeResult memberExpr(YieldHandling yieldHandling, + TripledotHandling tripledotHandling, TokenKind tt, + bool allowCallSyntax, PossibleError* possibleError, + InvokedPrediction invoked); + NodeResult decoratorExpr(YieldHandling yieldHandling, TokenKind tt); + NodeResult primaryExpr(YieldHandling yieldHandling, + TripledotHandling tripledotHandling, TokenKind tt, + PossibleError* possibleError, + InvokedPrediction invoked); + NodeResult exprInParens(InHandling inHandling, YieldHandling yieldHandling, + TripledotHandling tripledotHandling, + PossibleError* possibleError = nullptr); + + bool tryNewTarget(NewTargetNodeType* newTarget); + + BinaryNodeResult importExpr(YieldHandling yieldHandling, + bool allowCallSyntax); + + FunctionNodeResult methodDefinition(uint32_t toStringStart, + PropertyType propType, + TaggedParserAtomIndex funName); + + /* + * Additional JS parsers. + */ + bool functionArguments(YieldHandling yieldHandling, FunctionSyntaxKind kind, + FunctionNodeType funNode); + + FunctionNodeResult functionDefinition( + FunctionNodeType funNode, uint32_t toStringStart, InHandling inHandling, + YieldHandling yieldHandling, TaggedParserAtomIndex name, + FunctionSyntaxKind kind, GeneratorKind generatorKind, + FunctionAsyncKind asyncKind, bool tryAnnexB = false); + + // Parse a function body. Pass StatementListBody if the body is a list of + // statements; pass ExpressionBody if the body is a single expression. + // + // Don't include opening LeftCurly token when invoking. + enum FunctionBodyType { StatementListBody, ExpressionBody }; + LexicalScopeNodeResult functionBody(InHandling inHandling, + YieldHandling yieldHandling, + FunctionSyntaxKind kind, + FunctionBodyType type); + + UnaryNodeResult unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, + uint32_t begin); + + NodeResult condition(InHandling inHandling, YieldHandling yieldHandling); + + ListNodeResult argumentList(YieldHandling yieldHandling, bool* isSpread, + PossibleError* possibleError = nullptr); + NodeResult destructuringDeclaration(DeclarationKind kind, + YieldHandling yieldHandling, + TokenKind tt); + NodeResult destructuringDeclarationWithoutYieldOrAwait( + DeclarationKind kind, YieldHandling yieldHandling, TokenKind tt); + + inline bool checkExportedName(TaggedParserAtomIndex exportName); + inline bool checkExportedNamesForArrayBinding(ListNodeType array); + inline bool checkExportedNamesForObjectBinding(ListNodeType obj); + inline bool checkExportedNamesForDeclaration(Node node); + inline bool checkExportedNamesForDeclarationList( + DeclarationListNodeType node); + inline bool checkExportedNameForFunction(FunctionNodeType funNode); + inline bool checkExportedNameForClass(ClassNodeType classNode); + inline bool checkExportedNameForClause(NameNodeType nameNode); + + enum ClassContext { ClassStatement, ClassExpression }; + ClassNodeResult classDefinition(YieldHandling yieldHandling, + ClassContext classContext, + DefaultHandling defaultHandling); + + struct ClassInitializedMembers { +#ifdef ENABLE_DECORATORS + // Whether a non-static field has decorators or not. + bool hasInstanceDecorators = false; +#endif + + // The number of instance class fields. + size_t instanceFields = 0; + + // The number of instance class fields with computed property names. + size_t instanceFieldKeys = 0; + + // The number of static class fields. + size_t staticFields = 0; + + // The number of static blocks + size_t staticBlocks = 0; + + // The number of static class fields with computed property names. + size_t staticFieldKeys = 0; + + // The number of instance class private methods. + size_t privateMethods = 0; + + // The number of instance class private accessors. + size_t privateAccessors = 0; + + bool hasPrivateBrand() const { + return privateMethods > 0 || privateAccessors > 0; + } + }; +#ifdef ENABLE_DECORATORS + ListNodeResult decoratorList(YieldHandling yieldHandling); +#endif + [[nodiscard]] bool classMember( + YieldHandling yieldHandling, + const ParseContext::ClassStatement& classStmt, + TaggedParserAtomIndex className, uint32_t classStartOffset, + HasHeritage hasHeritage, ClassInitializedMembers& classInitializedMembers, + ListNodeType& classMembers, bool* done); + [[nodiscard]] bool finishClassConstructor( + const ParseContext::ClassStatement& classStmt, + TaggedParserAtomIndex className, HasHeritage hasHeritage, + uint32_t classStartOffset, uint32_t classEndOffset, + const ClassInitializedMembers& classInitializedMembers, + ListNodeType& classMembers); + + FunctionNodeResult privateMethodInitializer( + TokenPos propNamePos, TaggedParserAtomIndex propAtom, + TaggedParserAtomIndex storedMethodAtom); + FunctionNodeResult fieldInitializerOpt( + TokenPos propNamePos, Node name, TaggedParserAtomIndex atom, + ClassInitializedMembers& classInitializedMembers, bool isStatic, + HasHeritage hasHeritage); + + FunctionNodeResult synthesizePrivateMethodInitializer( + TaggedParserAtomIndex propAtom, AccessorType accessorType, + TokenPos propNamePos); + +#ifdef ENABLE_DECORATORS + FunctionNodeResult synthesizeAddInitializerFunction( + TaggedParserAtomIndex initializers, YieldHandling yieldHandling); + + ClassMethodResult synthesizeAccessor( + Node propName, TokenPos propNamePos, TaggedParserAtomIndex propAtom, + TaggedParserAtomIndex privateStateNameAtom, bool isStatic, + FunctionSyntaxKind syntaxKind, + ClassInitializedMembers& classInitializedMembers); + + FunctionNodeResult synthesizeAccessorBody(TaggedParserAtomIndex funNameAtom, + TokenPos propNamePos, + TaggedParserAtomIndex propNameAtom, + FunctionSyntaxKind syntaxKind); +#endif + + FunctionNodeResult staticClassBlock( + ClassInitializedMembers& classInitializedMembers); + + FunctionNodeResult synthesizeConstructor(TaggedParserAtomIndex className, + TokenPos synthesizedBodyPos, + HasHeritage hasHeritage); + + protected: + bool synthesizeConstructorBody(TokenPos synthesizedBodyPos, + HasHeritage hasHeritage, + FunctionNodeType funNode, FunctionBox* funbox); + + private: + bool checkBindingIdentifier(TaggedParserAtomIndex ident, uint32_t offset, + YieldHandling yieldHandling, + TokenKind hint = TokenKind::Limit); + + TaggedParserAtomIndex labelOrIdentifierReference(YieldHandling yieldHandling); + + TaggedParserAtomIndex labelIdentifier(YieldHandling yieldHandling) { + return labelOrIdentifierReference(yieldHandling); + } + + TaggedParserAtomIndex identifierReference(YieldHandling yieldHandling) { + return labelOrIdentifierReference(yieldHandling); + } + + bool matchLabel(YieldHandling yieldHandling, TaggedParserAtomIndex* labelOut); + + // Indicate if the next token (tokenized with SlashIsRegExp) is |in| or |of|. + // If so, consume it. + bool matchInOrOf(bool* isForInp, bool* isForOfp); + + private: + bool checkIncDecOperand(Node operand, uint32_t operandOffset); + bool checkStrictAssignment(Node lhs); + + void reportMissingClosing(unsigned errorNumber, unsigned noteNumber, + uint32_t openedPos); + + void reportRedeclarationHelper(TaggedParserAtomIndex& name, + DeclarationKind& prevKind, TokenPos& pos, + uint32_t& prevPos, const unsigned& errorNumber, + const unsigned& noteErrorNumber); + + void reportRedeclaration(TaggedParserAtomIndex name, DeclarationKind prevKind, + TokenPos pos, uint32_t prevPos); + + void reportMismatchedPlacement(TaggedParserAtomIndex name, + DeclarationKind prevKind, TokenPos pos, + uint32_t prevPos); + + bool notePositionalFormalParameter(FunctionNodeType funNode, + TaggedParserAtomIndex name, + uint32_t beginPos, + bool disallowDuplicateParams, + bool* duplicatedParam); + + enum PropertyNameContext { + PropertyNameInLiteral, + PropertyNameInPattern, + PropertyNameInClass, +#ifdef ENABLE_RECORD_TUPLE + PropertyNameInRecord +#endif + }; + NodeResult propertyName(YieldHandling yieldHandling, + PropertyNameContext propertyNameContext, + const mozilla::Maybe<DeclarationKind>& maybeDecl, + ListNodeType propList, + TaggedParserAtomIndex* propAtomOut); + NodeResult propertyOrMethodName( + YieldHandling yieldHandling, PropertyNameContext propertyNameContext, + const mozilla::Maybe<DeclarationKind>& maybeDecl, ListNodeType propList, + PropertyType* propType, TaggedParserAtomIndex* propAtomOut); + UnaryNodeResult computedPropertyName( + YieldHandling yieldHandling, + const mozilla::Maybe<DeclarationKind>& maybeDecl, + PropertyNameContext propertyNameContext, ListNodeType literal); + ListNodeResult arrayInitializer(YieldHandling yieldHandling, + PossibleError* possibleError); + inline RegExpLiteralResult newRegExp(); + + ListNodeResult objectLiteral(YieldHandling yieldHandling, + PossibleError* possibleError); + +#ifdef ENABLE_RECORD_TUPLE + ListNodeResult recordLiteral(YieldHandling yieldHandling); + ListNodeResult tupleLiteral(YieldHandling yieldHandling); +#endif + + BinaryNodeResult bindingInitializer(Node lhs, DeclarationKind kind, + YieldHandling yieldHandling); + NameNodeResult bindingIdentifier(DeclarationKind kind, + YieldHandling yieldHandling); + NodeResult bindingIdentifierOrPattern(DeclarationKind kind, + YieldHandling yieldHandling, + TokenKind tt); + ListNodeResult objectBindingPattern(DeclarationKind kind, + YieldHandling yieldHandling); + ListNodeResult arrayBindingPattern(DeclarationKind kind, + YieldHandling yieldHandling); + + enum class TargetBehavior { + PermitAssignmentPattern, + ForbidAssignmentPattern + }; + bool checkDestructuringAssignmentTarget( + Node expr, TokenPos exprPos, PossibleError* exprPossibleError, + PossibleError* possibleError, + TargetBehavior behavior = TargetBehavior::PermitAssignmentPattern); + void checkDestructuringAssignmentName(NameNodeType name, TokenPos namePos, + PossibleError* possibleError); + bool checkDestructuringAssignmentElement(Node expr, TokenPos exprPos, + PossibleError* exprPossibleError, + PossibleError* possibleError); + + NumericLiteralResult newNumber(const Token& tok) { + return handler_.newNumber(tok.number(), tok.decimalPoint(), tok.pos); + } + + inline BigIntLiteralResult newBigInt(); + + enum class OptionalKind { + NonOptional = 0, + Optional, + }; + NodeResult memberPropertyAccess( + Node lhs, OptionalKind optionalKind = OptionalKind::NonOptional); + NodeResult memberPrivateAccess( + Node lhs, OptionalKind optionalKind = OptionalKind::NonOptional); + NodeResult memberElemAccess( + Node lhs, YieldHandling yieldHandling, + OptionalKind optionalKind = OptionalKind::NonOptional); + NodeResult memberSuperCall(Node lhs, YieldHandling yieldHandling); + NodeResult memberCall(TokenKind tt, Node lhs, YieldHandling yieldHandling, + PossibleError* possibleError, + OptionalKind optionalKind = OptionalKind::NonOptional); + + protected: + // Match the current token against the BindingIdentifier production with + // the given Yield parameter. If there is no match, report a syntax + // error. + TaggedParserAtomIndex bindingIdentifier(YieldHandling yieldHandling); + + bool checkLabelOrIdentifierReference(TaggedParserAtomIndex ident, + uint32_t offset, + YieldHandling yieldHandling, + TokenKind hint = TokenKind::Limit); + + ListNodeResult statementList(YieldHandling yieldHandling); + + [[nodiscard]] FunctionNodeResult 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); + + // Implements Automatic Semicolon Insertion. + // + // Use this to match `;` in contexts where ASI is allowed. Call this after + // ruling out all other possibilities except `;`, by peeking ahead if + // necessary. + // + // Unlike most optional Modifiers, this method's `modifier` argument defaults + // to SlashIsRegExp, since that's by far the most common case: usually an + // optional semicolon is at the end of a statement or declaration, and the + // next token could be a RegExp literal beginning a new ExpressionStatement. + bool matchOrInsertSemicolon(Modifier modifier = TokenStream::SlashIsRegExp); + + bool noteDeclaredName(TaggedParserAtomIndex name, DeclarationKind kind, + TokenPos pos, ClosedOver isClosedOver = ClosedOver::No); + + bool noteDeclaredPrivateName(Node nameNode, TaggedParserAtomIndex name, + PropertyType propType, FieldPlacement placement, + TokenPos pos); + + private: + inline bool asmJS(ListNodeType list); +}; + +template <typename Unit> +class MOZ_STACK_CLASS Parser<SyntaxParseHandler, Unit> final + : public GeneralParser<SyntaxParseHandler, Unit> { + using Base = GeneralParser<SyntaxParseHandler, Unit>; + using Node = SyntaxParseHandler::Node; + using NodeResult = typename SyntaxParseHandler::NodeResult; + +#define DECLARE_TYPE(typeName) \ + using typeName##Type = SyntaxParseHandler::typeName##Type; \ + using typeName##Result = SyntaxParseHandler::typeName##Result; + FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE) +#undef DECLARE_TYPE + + using SyntaxParser = Parser<SyntaxParseHandler, Unit>; + + // Numerous Base::* functions have bodies like + // + // return asFinalParser()->func(...); + // + // and must be able to call functions here. Add a friendship relationship + // so functions here can be hidden when appropriate. + friend class GeneralParser<SyntaxParseHandler, Unit>; + + public: + using Base::Base; + + // Inherited types, listed here to have non-dependent names. + using typename Base::Modifier; + using typename Base::Position; + using typename Base::TokenStream; + + // Inherited functions, listed here to have non-dependent names. + + public: + using Base::anyChars; + using Base::clearAbortedSyntaxParse; + using Base::hadAbortedSyntaxParse; + using Base::innerFunctionForFunctionBox; + using Base::tokenStream; + + public: + // ErrorReportMixin. + + using Base::error; + using Base::errorAt; + using Base::errorNoOffset; + using Base::errorWithNotes; + using Base::errorWithNotesAt; + using Base::errorWithNotesNoOffset; + using Base::strictModeError; + using Base::strictModeErrorAt; + using Base::strictModeErrorNoOffset; + using Base::strictModeErrorWithNotes; + using Base::strictModeErrorWithNotesAt; + using Base::strictModeErrorWithNotesNoOffset; + using Base::warning; + using Base::warningAt; + using Base::warningNoOffset; + + private: + using Base::alloc_; +#if DEBUG + using Base::checkOptionsCalled_; +#endif + using Base::checkForUndefinedPrivateFields; + using Base::errorResult; + using Base::finishFunctionScopes; + using Base::functionFormalParametersAndBody; + using Base::handler_; + using Base::innerFunction; + using Base::matchOrInsertSemicolon; + using Base::mustMatchToken; + using Base::newFunctionBox; + using Base::newLexicalScopeData; + using Base::newModuleScopeData; + using Base::newName; + using Base::noteDeclaredName; + using Base::null; + using Base::options; + using Base::pc_; + using Base::pos; + using Base::propagateFreeNamesAndMarkClosedOverBindings; + using Base::ss; + using Base::statementList; + using Base::stringLiteral; + using Base::usedNames_; + + private: + using Base::abortIfSyntaxParser; + using Base::disableSyntaxParser; + + public: + // Functions with multiple overloads of different visibility. We can't + // |using| the whole thing into existence because of the visibility + // distinction, so we instead must manually delegate the required overload. + + TaggedParserAtomIndex bindingIdentifier(YieldHandling yieldHandling) { + return Base::bindingIdentifier(yieldHandling); + } + + // Functions present in both Parser<ParseHandler, Unit> specializations. + + inline void setAwaitHandling(AwaitHandling awaitHandling); + inline void setInParametersOfAsyncFunction(bool inParameters); + + RegExpLiteralResult newRegExp(); + BigIntLiteralResult newBigInt(); + + // Parse a module. + ModuleNodeResult moduleBody(ModuleSharedContext* modulesc); + + inline bool checkLocalExportNames(ListNodeType node); + inline bool checkExportedName(TaggedParserAtomIndex exportName); + inline bool checkExportedNamesForArrayBinding(ListNodeType array); + inline bool checkExportedNamesForObjectBinding(ListNodeType obj); + inline bool checkExportedNamesForDeclaration(Node node); + inline bool checkExportedNamesForDeclarationList( + DeclarationListNodeType node); + inline bool checkExportedNameForFunction(FunctionNodeType funNode); + inline bool checkExportedNameForClass(ClassNodeType classNode); + inline bool checkExportedNameForClause(NameNodeType nameNode); + + bool 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); + + bool skipLazyInnerFunction(FunctionNodeType funNode, uint32_t toStringStart, + bool tryAnnexB); + + bool asmJS(ListNodeType list); + + // Functions present only in Parser<SyntaxParseHandler, Unit>. +}; + +template <typename Unit> +class MOZ_STACK_CLASS Parser<FullParseHandler, Unit> final + : public GeneralParser<FullParseHandler, Unit> { + using Base = GeneralParser<FullParseHandler, Unit>; + using Node = FullParseHandler::Node; + using NodeResult = typename FullParseHandler::NodeResult; + +#define DECLARE_TYPE(typeName) \ + using typeName##Type = FullParseHandler::typeName##Type; \ + using typeName##Result = FullParseHandler::typeName##Result; + FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE) +#undef DECLARE_TYPE + + using SyntaxParser = Parser<SyntaxParseHandler, Unit>; + + // Numerous Base::* functions have bodies like + // + // return asFinalParser()->func(...); + // + // and must be able to call functions here. Add a friendship relationship + // so functions here can be hidden when appropriate. + friend class GeneralParser<FullParseHandler, Unit>; + + public: + using Base::Base; + + // Inherited types, listed here to have non-dependent names. + using typename Base::Modifier; + using typename Base::Position; + using typename Base::TokenStream; + + // Inherited functions, listed here to have non-dependent names. + + public: + using Base::anyChars; + using Base::clearAbortedSyntaxParse; + using Base::functionFormalParametersAndBody; + using Base::hadAbortedSyntaxParse; + using Base::handler_; + using Base::newFunctionBox; + using Base::options; + using Base::pc_; + using Base::pos; + using Base::ss; + using Base::tokenStream; + + public: + // ErrorReportMixin. + + using Base::error; + using Base::errorAt; + using Base::errorNoOffset; + using Base::errorWithNotes; + using Base::errorWithNotesAt; + using Base::errorWithNotesNoOffset; + using Base::strictModeError; + using Base::strictModeErrorAt; + using Base::strictModeErrorNoOffset; + using Base::strictModeErrorWithNotes; + using Base::strictModeErrorWithNotesAt; + using Base::strictModeErrorWithNotesNoOffset; + using Base::warning; + using Base::warningAt; + using Base::warningNoOffset; + + private: + using Base::alloc_; + using Base::checkLabelOrIdentifierReference; +#if DEBUG + using Base::checkOptionsCalled_; +#endif + using Base::checkForUndefinedPrivateFields; + using Base::errorResult; + using Base::fc_; + using Base::finishClassBodyScope; + using Base::finishFunctionScopes; + using Base::finishLexicalScope; + using Base::innerFunction; + using Base::innerFunctionForFunctionBox; + using Base::matchOrInsertSemicolon; + using Base::mustMatchToken; + using Base::newEvalScopeData; + using Base::newFunctionScopeData; + using Base::newGlobalScopeData; + using Base::newLexicalScopeData; + using Base::newModuleScopeData; + using Base::newName; + using Base::newVarScopeData; + using Base::noteDeclaredName; + using Base::noteUsedName; + using Base::null; + using Base::propagateFreeNamesAndMarkClosedOverBindings; + using Base::statementList; + using Base::stringLiteral; + using Base::usedNames_; + + using Base::abortIfSyntaxParser; + using Base::disableSyntaxParser; + using Base::getSyntaxParser; + + public: + // Functions with multiple overloads of different visibility. We can't + // |using| the whole thing into existence because of the visibility + // distinction, so we instead must manually delegate the required overload. + + TaggedParserAtomIndex bindingIdentifier(YieldHandling yieldHandling) { + return Base::bindingIdentifier(yieldHandling); + } + + // Functions present in both Parser<ParseHandler, Unit> specializations. + + friend class AutoAwaitIsKeyword<SyntaxParseHandler, Unit>; + inline void setAwaitHandling(AwaitHandling awaitHandling); + + friend class AutoInParametersOfAsyncFunction<SyntaxParseHandler, Unit>; + inline void setInParametersOfAsyncFunction(bool inParameters); + + RegExpLiteralResult newRegExp(); + BigIntLiteralResult newBigInt(); + + // Parse a module. + ModuleNodeResult moduleBody(ModuleSharedContext* modulesc); + + bool checkLocalExportNames(ListNodeType node); + bool checkExportedName(TaggedParserAtomIndex exportName); + bool checkExportedNamesForArrayBinding(ListNodeType array); + bool checkExportedNamesForObjectBinding(ListNodeType obj); + bool checkExportedNamesForDeclaration(Node node); + bool checkExportedNamesForDeclarationList(DeclarationListNodeType node); + bool checkExportedNameForFunction(FunctionNodeType funNode); + bool checkExportedNameForClass(ClassNodeType classNode); + inline bool checkExportedNameForClause(NameNodeType nameNode); + + bool 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); + + [[nodiscard]] bool advancePastSyntaxParsedFunction( + SyntaxParser* syntaxParser); + + bool skipLazyInnerFunction(FunctionNodeType funNode, uint32_t toStringStart, + bool tryAnnexB); + + // Functions present only in Parser<FullParseHandler, Unit>. + + // Parse the body of an eval. + // + // Eval scripts are distinguished from global scripts in that in ES6, per + // 18.2.1.1 steps 9 and 10, all eval scripts are executed under a fresh + // lexical scope. + LexicalScopeNodeResult evalBody(EvalSharedContext* evalsc); + + // Parse a function, given only its arguments and body. Used for lazily + // parsed functions. + FunctionNodeResult standaloneLazyFunction(CompilationInput& input, + uint32_t toStringStart, bool strict, + GeneratorKind generatorKind, + FunctionAsyncKind asyncKind); + + // Parse a function, used for the Function, GeneratorFunction, and + // AsyncFunction constructors. + FunctionNodeResult standaloneFunction( + const mozilla::Maybe<uint32_t>& parameterListEnd, + FunctionSyntaxKind syntaxKind, GeneratorKind generatorKind, + FunctionAsyncKind asyncKind, Directives inheritedDirectives, + Directives* newDirectives); + + bool checkStatementsEOF(); + + // Parse the body of a global script. + ListNodeResult globalBody(GlobalSharedContext* globalsc); + + bool checkLocalExportName(TaggedParserAtomIndex ident, uint32_t offset) { + return checkLabelOrIdentifierReference(ident, offset, YieldIsName); + } + + bool asmJS(ListNodeType list); +}; + +template <class Parser> +/* static */ inline const TokenStreamAnyChars& +ParserAnyCharsAccess<Parser>::anyChars(const GeneralTokenStreamChars* ts) { + // The structure we're walking through looks like this: + // + // struct ParserBase + // { + // ...; + // TokenStreamAnyChars anyChars; + // ...; + // }; + // struct Parser : <class that ultimately inherits from ParserBase> + // { + // ...; + // TokenStreamSpecific tokenStream; + // ...; + // }; + // + // We're passed a GeneralTokenStreamChars* (this being a base class of + // Parser::tokenStream). We cast that pointer to a TokenStreamSpecific*, + // then translate that to the enclosing Parser*, then return the |anyChars| + // member within. + + static_assert(std::is_base_of_v<GeneralTokenStreamChars, TokenStreamSpecific>, + "the static_cast<> below assumes a base-class relationship"); + const auto* tss = static_cast<const TokenStreamSpecific*>(ts); + + auto tssAddr = reinterpret_cast<uintptr_t>(tss); + + using ActualTokenStreamType = decltype(std::declval<Parser>().tokenStream); + static_assert(std::is_same_v<ActualTokenStreamType, TokenStreamSpecific>, + "Parser::tokenStream must have type TokenStreamSpecific"); + + uintptr_t parserAddr = tssAddr - offsetof(Parser, tokenStream); + + return reinterpret_cast<const Parser*>(parserAddr)->anyChars; +} + +template <class Parser> +/* static */ inline TokenStreamAnyChars& ParserAnyCharsAccess<Parser>::anyChars( + GeneralTokenStreamChars* ts) { + const TokenStreamAnyChars& anyCharsConst = + anyChars(const_cast<const GeneralTokenStreamChars*>(ts)); + + return const_cast<TokenStreamAnyChars&>(anyCharsConst); +} + +template <class ParseHandler, typename Unit> +class MOZ_STACK_CLASS AutoAwaitIsKeyword { + using GeneralParser = frontend::GeneralParser<ParseHandler, Unit>; + + private: + GeneralParser* parser_; + AwaitHandling oldAwaitHandling_; + + public: + AutoAwaitIsKeyword(GeneralParser* parser, AwaitHandling awaitHandling) { + parser_ = parser; + oldAwaitHandling_ = static_cast<AwaitHandling>(parser_->awaitHandling_); + + // 'await' is always a keyword in module contexts, so we don't modify + // the state when the original handling is AwaitIsModuleKeyword. + if (oldAwaitHandling_ != AwaitIsModuleKeyword) { + parser_->setAwaitHandling(awaitHandling); + } + } + + ~AutoAwaitIsKeyword() { parser_->setAwaitHandling(oldAwaitHandling_); } +}; + +template <class ParseHandler, typename Unit> +class MOZ_STACK_CLASS AutoInParametersOfAsyncFunction { + using GeneralParser = frontend::GeneralParser<ParseHandler, Unit>; + + private: + GeneralParser* parser_; + bool oldInParametersOfAsyncFunction_; + + public: + AutoInParametersOfAsyncFunction(GeneralParser* parser, bool inParameters) { + parser_ = parser; + oldInParametersOfAsyncFunction_ = parser_->inParametersOfAsyncFunction_; + parser_->setInParametersOfAsyncFunction(inParameters); + } + + ~AutoInParametersOfAsyncFunction() { + parser_->setInParametersOfAsyncFunction(oldInParametersOfAsyncFunction_); + } +}; + +GlobalScope::ParserData* NewEmptyGlobalScopeData(FrontendContext* fc, + LifoAlloc& alloc, + uint32_t numBindings); + +VarScope::ParserData* NewEmptyVarScopeData(FrontendContext* fc, + LifoAlloc& alloc, + uint32_t numBindings); + +LexicalScope::ParserData* NewEmptyLexicalScopeData(FrontendContext* fc, + LifoAlloc& alloc, + uint32_t numBindings); + +FunctionScope::ParserData* NewEmptyFunctionScopeData(FrontendContext* fc, + LifoAlloc& alloc, + uint32_t numBindings); + +bool FunctionScopeHasClosedOverBindings(ParseContext* pc); +bool LexicalScopeHasClosedOverBindings(ParseContext* pc, + ParseContext::Scope& scope); + +} /* namespace frontend */ +} /* namespace js */ + +#endif /* frontend_Parser_h */ |