/* -*- 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;
  }

  BigIntStencilVector& bigInts() { return compilationState_.bigIntData; }
  const BigIntStencilVector& bigInts() const {
    return compilationState_.bigIntData;
  }

  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;

 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,
             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,
                   CompilationState& compilationState,
                   void* internalSyntaxParser);

 protected:
  template <typename Unit>
  PerHandlerParser(FrontendContext* fc,
                   const JS::ReadOnlyCompileOptions& options,
                   CompilationState& compilationState,
                   GeneralParser<SyntaxParseHandler, Unit>* syntaxParser)
      : PerHandlerParser(fc, options, 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::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,
                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,
  };
  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);

  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 */
