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

#ifndef frontend_SyntaxParseHandler_h
#define frontend_SyntaxParseHandler_h

#include "mozilla/Assertions.h"
#include "mozilla/Maybe.h"  // mozilla::Maybe

#include <string.h>

#include "jstypes.h"

#include "frontend/CompilationStencil.h"  // CompilationState
#include "frontend/FunctionSyntaxKind.h"  // FunctionSyntaxKind
#include "frontend/NameAnalysisTypes.h"   // PrivateNameKind
#include "frontend/ParseNode.h"
#include "frontend/ParserAtom.h"  // TaggedParserAtomIndex
#include "frontend/TokenStream.h"

namespace js {

namespace frontend {

// Parse handler used when processing the syntax in a block of code, to generate
// the minimal information which is required to detect syntax errors and allow
// bytecode to be emitted for outer functions.
//
// When parsing, we start at the top level with a full parse, and when possible
// only check the syntax for inner functions, so that they can be lazily parsed
// into bytecode when/if they first run. Checking the syntax of a function is
// several times faster than doing a full parse/emit, and lazy parsing improves
// both performance and memory usage significantly when pages contain large
// amounts of code that never executes (which happens often).
class SyntaxParseHandler {
  // Remember the last encountered name or string literal during syntax parses.
  TaggedParserAtomIndex lastAtom;
  TokenPos lastStringPos;

 public:
  enum Node {
    NodeFailure = 0,
    NodeGeneric,
    NodeGetProp,
    NodeStringExprStatement,
    NodeReturn,
    NodeBreak,
    NodeThrow,
    NodeEmptyStatement,

    NodeVarDeclaration,
    NodeLexicalDeclaration,

    // A non-arrow function expression with block body, from bog-standard
    // ECMAScript.
    NodeFunctionExpression,

    NodeFunctionArrow,
    NodeFunctionStatement,

    // This is needed for proper assignment-target handling.  ES6 formally
    // requires function calls *not* pass IsValidSimpleAssignmentTarget,
    // but at last check there were still sites with |f() = 5| and similar
    // in code not actually executed (or at least not executed enough to be
    // noticed).
    NodeFunctionCall,

    NodeOptionalFunctionCall,

    // Node representing normal names which don't require any special
    // casing.
    NodeName,

    // Nodes representing the names "arguments" and "eval".
    NodeArgumentsName,
    NodeEvalName,

    // Node representing the "async" name, which may actually be a
    // contextual keyword.
    NodePotentialAsyncKeyword,

    // Node representing private names.
    NodePrivateName,

    NodeDottedProperty,
    NodeOptionalDottedProperty,
    NodeElement,
    NodeOptionalElement,
    // A distinct node for [PrivateName], to make detecting delete this.#x
    // detectable in syntax parse
    NodePrivateMemberAccess,
    NodeOptionalPrivateMemberAccess,

    // Destructuring target patterns can't be parenthesized: |([a]) = [3];|
    // must be a syntax error.  (We can't use NodeGeneric instead of these
    // because that would trigger invalid-left-hand-side ReferenceError
    // semantics when SyntaxError semantics are desired.)
    NodeParenthesizedArray,
    NodeParenthesizedObject,

    // In rare cases a parenthesized |node| doesn't have the same semantics
    // as |node|.  Each such node has a special Node value, and we use a
    // different Node value to represent the parenthesized form.  See also
    // is{Unp,P}arenthesized*(Node), parenthesize(Node), and the various
    // functions that deal in NodeUnparenthesized* below.

    // Valuable for recognizing potential destructuring patterns.
    NodeUnparenthesizedArray,
    NodeUnparenthesizedObject,

    // The directive prologue at the start of a FunctionBody or ScriptBody
    // is the longest sequence (possibly empty) of string literal
    // expression statements at the start of a function.  Thus we need this
    // to treat |"use strict";| as a possible Use Strict Directive and
    // |("use strict");| as a useless statement.
    NodeUnparenthesizedString,

    // For destructuring patterns an assignment element with
    // an initializer expression is not allowed be parenthesized.
    // i.e. |{x = 1} = obj|
    NodeUnparenthesizedAssignment,

    // This node is necessary to determine if the base operand in an
    // exponentiation operation is an unparenthesized unary expression.
    // We want to reject |-2 ** 3|, but still need to allow |(-2) ** 3|.
    NodeUnparenthesizedUnary,

    // This node is necessary to determine if the LHS of a property access is
    // super related.
    NodeSuperBase
  };

#define DECLARE_TYPE(typeName, longTypeName, asMethodName) \
  using longTypeName = Node;
  FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
#undef DECLARE_TYPE

  using NullNode = Node;

  bool isNonArrowFunctionExpression(Node node) const {
    return node == NodeFunctionExpression;
  }

  bool isPropertyOrPrivateMemberAccess(Node node) {
    return node == NodeDottedProperty || node == NodeElement ||
           node == NodePrivateMemberAccess;
  }

  bool isOptionalPropertyOrPrivateMemberAccess(Node node) {
    return node == NodeOptionalDottedProperty || node == NodeOptionalElement ||
           node == NodeOptionalPrivateMemberAccess;
  }

  bool isFunctionCall(Node node) {
    // Note: super() is a special form, *not* a function call.
    return node == NodeFunctionCall;
  }

  static bool isUnparenthesizedDestructuringPattern(Node node) {
    return node == NodeUnparenthesizedArray ||
           node == NodeUnparenthesizedObject;
  }

  static bool isParenthesizedDestructuringPattern(Node node) {
    // Technically this isn't a destructuring target at all -- the grammar
    // doesn't treat it as such.  But we need to know when this happens to
    // consider it a SyntaxError rather than an invalid-left-hand-side
    // ReferenceError.
    return node == NodeParenthesizedArray || node == NodeParenthesizedObject;
  }

 public:
  SyntaxParseHandler(FrontendContext* fc, CompilationState& compilationState) {
    MOZ_ASSERT(!compilationState.input.isDelazifying());
  }

  static NullNode null() { return NodeFailure; }

#define DECLARE_AS(typeName, longTypeName, asMethodName) \
  static longTypeName asMethodName(Node node) { return node; }
  FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
#undef DECLARE_AS

  NameNodeType newName(TaggedParserAtomIndex name, const TokenPos& pos) {
    lastAtom = name;
    if (name == TaggedParserAtomIndex::WellKnown::arguments()) {
      return NodeArgumentsName;
    }
    if (pos.begin + strlen("async") == pos.end &&
        name == TaggedParserAtomIndex::WellKnown::async()) {
      return NodePotentialAsyncKeyword;
    }
    if (name == TaggedParserAtomIndex::WellKnown::eval()) {
      return NodeEvalName;
    }
    return NodeName;
  }

  UnaryNodeType newComputedName(Node expr, uint32_t start, uint32_t end) {
    return NodeGeneric;
  }

  UnaryNodeType newSyntheticComputedName(Node expr, uint32_t start,
                                         uint32_t end) {
    return NodeGeneric;
  }

  NameNodeType newObjectLiteralPropertyName(TaggedParserAtomIndex atom,
                                            const TokenPos& pos) {
    return NodeName;
  }

  NameNodeType newPrivateName(TaggedParserAtomIndex atom, const TokenPos& pos) {
    return NodePrivateName;
  }

  NumericLiteralType newNumber(double value, DecimalPoint decimalPoint,
                               const TokenPos& pos) {
    return NodeGeneric;
  }

  BigIntLiteralType newBigInt() { return NodeGeneric; }

  BooleanLiteralType newBooleanLiteral(bool cond, const TokenPos& pos) {
    return NodeGeneric;
  }

  NameNodeType newStringLiteral(TaggedParserAtomIndex atom,
                                const TokenPos& pos) {
    lastAtom = atom;
    lastStringPos = pos;
    return NodeUnparenthesizedString;
  }

  NameNodeType newTemplateStringLiteral(TaggedParserAtomIndex atom,
                                        const TokenPos& pos) {
    return NodeGeneric;
  }

  CallSiteNodeType newCallSiteObject(uint32_t begin) { return NodeGeneric; }

  void addToCallSiteObject(CallSiteNodeType callSiteObj, Node rawNode,
                           Node cookedNode) {}

  ThisLiteralType newThisLiteral(const TokenPos& pos, Node thisName) {
    return NodeGeneric;
  }
  NullLiteralType newNullLiteral(const TokenPos& pos) { return NodeGeneric; }
  RawUndefinedLiteralType newRawUndefinedLiteral(const TokenPos& pos) {
    return NodeGeneric;
  }

  RegExpLiteralType newRegExp(Node reobj, const TokenPos& pos) {
    return NodeGeneric;
  }

  ConditionalExpressionType newConditional(Node cond, Node thenExpr,
                                           Node elseExpr) {
    return NodeGeneric;
  }

  UnaryNodeType newDelete(uint32_t begin, Node expr) {
    return NodeUnparenthesizedUnary;
  }

  UnaryNodeType newTypeof(uint32_t begin, Node kid) {
    return NodeUnparenthesizedUnary;
  }

  UnaryNodeType newUnary(ParseNodeKind kind, uint32_t begin, Node kid) {
    return NodeUnparenthesizedUnary;
  }

  UnaryNodeType newUpdate(ParseNodeKind kind, uint32_t begin, Node kid) {
    return NodeGeneric;
  }

  UnaryNodeType newSpread(uint32_t begin, Node kid) { return NodeGeneric; }

  Node appendOrCreateList(ParseNodeKind kind, Node left, Node right,
                          ParseContext* pc) {
    return NodeGeneric;
  }

  // Expressions

  ListNodeType newArrayLiteral(uint32_t begin) {
    return NodeUnparenthesizedArray;
  }
  [[nodiscard]] bool addElision(ListNodeType literal, const TokenPos& pos) {
    return true;
  }
  [[nodiscard]] bool addSpreadElement(ListNodeType literal, uint32_t begin,
                                      Node inner) {
    return true;
  }
  void addArrayElement(ListNodeType literal, Node element) {}

  ListNodeType newArguments(const TokenPos& pos) { return NodeGeneric; }
  CallNodeType newCall(Node callee, ListNodeType args, JSOp callOp) {
    return NodeFunctionCall;
  }

  CallNodeType newOptionalCall(Node callee, ListNodeType args, JSOp callOp) {
    return NodeOptionalFunctionCall;
  }

  CallNodeType newSuperCall(Node callee, ListNodeType args, bool isSpread) {
    return NodeGeneric;
  }
  CallNodeType newTaggedTemplate(Node tag, ListNodeType args, JSOp callOp) {
    return NodeGeneric;
  }

  ListNodeType newObjectLiteral(uint32_t begin) {
    return NodeUnparenthesizedObject;
  }

#ifdef ENABLE_RECORD_TUPLE
  ListNodeType newRecordLiteral(uint32_t begin) { return NodeGeneric; }

  ListNodeType newTupleLiteral(uint32_t begin) { return NodeGeneric; }
#endif

  ListNodeType newClassMemberList(uint32_t begin) { return NodeGeneric; }
  ClassNamesType newClassNames(Node outer, Node inner, const TokenPos& pos) {
    return NodeGeneric;
  }
  ClassNodeType newClass(Node name, Node heritage, Node methodBlock,
#ifdef ENABLE_DECORATORS
                         ListNodeType decorators,
#endif
                         const TokenPos& pos) {
    return NodeGeneric;
  }

  LexicalScopeNodeType newLexicalScope(Node body) {
    return NodeLexicalDeclaration;
  }

  ClassBodyScopeNodeType newClassBodyScope(Node body) {
    return NodeLexicalDeclaration;
  }

  NewTargetNodeType newNewTarget(NullaryNodeType newHolder,
                                 NullaryNodeType targetHolder,
                                 NameNodeType newTargetName) {
    return NodeGeneric;
  }
  NullaryNodeType newPosHolder(const TokenPos& pos) { return NodeGeneric; }
  UnaryNodeType newSuperBase(Node thisName, const TokenPos& pos) {
    return NodeSuperBase;
  }

  [[nodiscard]] bool addPrototypeMutation(ListNodeType literal, uint32_t begin,
                                          Node expr) {
    return true;
  }
  BinaryNodeType newPropertyDefinition(Node key, Node val) {
    return NodeGeneric;
  }
  void addPropertyDefinition(ListNodeType literal, BinaryNodeType propdef) {}
  [[nodiscard]] bool addPropertyDefinition(ListNodeType literal, Node key,
                                           Node expr) {
    return true;
  }
  [[nodiscard]] bool addShorthand(ListNodeType literal, NameNodeType name,
                                  NameNodeType expr) {
    return true;
  }
  [[nodiscard]] bool addSpreadProperty(ListNodeType literal, uint32_t begin,
                                       Node inner) {
    return true;
  }
  [[nodiscard]] bool addObjectMethodDefinition(ListNodeType literal, Node key,
                                               FunctionNodeType funNode,
                                               AccessorType atype) {
    return true;
  }
  [[nodiscard]] Node newDefaultClassConstructor(Node key,
                                                FunctionNodeType funNode) {
    return NodeGeneric;
  }
  [[nodiscard]] Node newClassMethodDefinition(
      Node key, FunctionNodeType funNode, AccessorType atype, bool isStatic,
      mozilla::Maybe<FunctionNodeType> initializerIfPrivate
#ifdef ENABLE_DECORATORS
      ,
      ListNodeType decorators
#endif
  ) {
    return NodeGeneric;
  }
  [[nodiscard]] Node newClassFieldDefinition(Node name,
                                             FunctionNodeType initializer,
                                             bool isStatic
#ifdef ENABLE_DECORATORS
                                             ,
                                             ListNodeType decorators,
                                             ClassMethodType accessorGetterNode,
                                             ClassMethodType accessorSetterNode
#endif
  ) {
    return NodeGeneric;
  }

  [[nodiscard]] Node newStaticClassBlock(FunctionNodeType block) {
    return NodeGeneric;
  }

  [[nodiscard]] bool addClassMemberDefinition(ListNodeType memberList,
                                              Node member) {
    return true;
  }
  UnaryNodeType newYieldExpression(uint32_t begin, Node value) {
    return NodeGeneric;
  }
  UnaryNodeType newYieldStarExpression(uint32_t begin, Node value) {
    return NodeGeneric;
  }
  UnaryNodeType newAwaitExpression(uint32_t begin, Node value) {
    return NodeUnparenthesizedUnary;
  }
  UnaryNodeType newOptionalChain(uint32_t begin, Node value) {
    return NodeGeneric;
  }

  // Statements

  ListNodeType newStatementList(const TokenPos& pos) { return NodeGeneric; }
  void addStatementToList(ListNodeType list, Node stmt) {}
  void setListEndPosition(ListNodeType list, const TokenPos& pos) {}
  void addCaseStatementToList(ListNodeType list, CaseClauseType caseClause) {}
  [[nodiscard]] bool prependInitialYield(ListNodeType stmtList, Node genName) {
    return true;
  }
  NullaryNodeType newEmptyStatement(const TokenPos& pos) {
    return NodeEmptyStatement;
  }

  BinaryNodeType newImportAssertion(Node keyNode, Node valueNode) {
    return NodeGeneric;
  }
  BinaryNodeType newModuleRequest(Node moduleSpec, Node importAssertionList,
                                  const TokenPos& pos) {
    return NodeGeneric;
  }
  BinaryNodeType newImportDeclaration(Node importSpecSet, Node moduleRequest,
                                      const TokenPos& pos) {
    return NodeGeneric;
  }
  BinaryNodeType newImportSpec(Node importNameNode, Node bindingName) {
    return NodeGeneric;
  }
  UnaryNodeType newImportNamespaceSpec(uint32_t begin, Node bindingName) {
    return NodeGeneric;
  }
  UnaryNodeType newExportDeclaration(Node kid, const TokenPos& pos) {
    return NodeGeneric;
  }
  BinaryNodeType newExportFromDeclaration(uint32_t begin, Node exportSpecSet,
                                          Node moduleRequest) {
    return NodeGeneric;
  }
  BinaryNodeType newExportDefaultDeclaration(Node kid, Node maybeBinding,
                                             const TokenPos& pos) {
    return NodeGeneric;
  }
  BinaryNodeType newExportSpec(Node bindingName, Node exportName) {
    return NodeGeneric;
  }
  UnaryNodeType newExportNamespaceSpec(uint32_t begin, Node exportName) {
    return NodeGeneric;
  }
  NullaryNodeType newExportBatchSpec(const TokenPos& pos) {
    return NodeGeneric;
  }
  BinaryNodeType newImportMeta(NullaryNodeType importHolder,
                               NullaryNodeType metaHolder) {
    return NodeGeneric;
  }
  BinaryNodeType newCallImport(NullaryNodeType importHolder, Node singleArg) {
    return NodeGeneric;
  }
  BinaryNodeType newCallImportSpec(Node specifierArg, Node optionalArg) {
    return NodeGeneric;
  }

  BinaryNodeType newSetThis(Node thisName, Node value) { return value; }

  UnaryNodeType newExprStatement(Node expr, uint32_t end) {
    return expr == NodeUnparenthesizedString ? NodeStringExprStatement
                                             : NodeGeneric;
  }

  TernaryNodeType newIfStatement(uint32_t begin, Node cond, Node thenBranch,
                                 Node elseBranch) {
    return NodeGeneric;
  }
  BinaryNodeType newDoWhileStatement(Node body, Node cond,
                                     const TokenPos& pos) {
    return NodeGeneric;
  }
  BinaryNodeType newWhileStatement(uint32_t begin, Node cond, Node body) {
    return NodeGeneric;
  }
  SwitchStatementType newSwitchStatement(
      uint32_t begin, Node discriminant,
      LexicalScopeNodeType lexicalForCaseList, bool hasDefault) {
    return NodeGeneric;
  }
  CaseClauseType newCaseOrDefault(uint32_t begin, Node expr, Node body) {
    return NodeGeneric;
  }
  ContinueStatementType newContinueStatement(TaggedParserAtomIndex label,
                                             const TokenPos& pos) {
    return NodeGeneric;
  }
  BreakStatementType newBreakStatement(TaggedParserAtomIndex label,
                                       const TokenPos& pos) {
    return NodeBreak;
  }
  UnaryNodeType newReturnStatement(Node expr, const TokenPos& pos) {
    return NodeReturn;
  }
  UnaryNodeType newExpressionBody(Node expr) { return NodeReturn; }
  BinaryNodeType newWithStatement(uint32_t begin, Node expr, Node body) {
    return NodeGeneric;
  }

  LabeledStatementType newLabeledStatement(TaggedParserAtomIndex label,
                                           Node stmt, uint32_t begin) {
    return NodeGeneric;
  }

  UnaryNodeType newThrowStatement(Node expr, const TokenPos& pos) {
    return NodeThrow;
  }
  TernaryNodeType newTryStatement(uint32_t begin, Node body,
                                  LexicalScopeNodeType catchScope,
                                  Node finallyBlock) {
    return NodeGeneric;
  }
  DebuggerStatementType newDebuggerStatement(const TokenPos& pos) {
    return NodeGeneric;
  }

  NameNodeType newPropertyName(TaggedParserAtomIndex name,
                               const TokenPos& pos) {
    lastAtom = name;
    return NodeGeneric;
  }

  PropertyAccessType newPropertyAccess(Node expr, NameNodeType key) {
    return NodeDottedProperty;
  }

  PropertyAccessType newOptionalPropertyAccess(Node expr, NameNodeType key) {
    return NodeOptionalDottedProperty;
  }

  PropertyByValueType newPropertyByValue(Node lhs, Node index, uint32_t end) {
    MOZ_ASSERT(!isPrivateName(index));
    return NodeElement;
  }

  PropertyByValueType newOptionalPropertyByValue(Node lhs, Node index,
                                                 uint32_t end) {
    return NodeOptionalElement;
  }

  PrivateMemberAccessType newPrivateMemberAccess(Node lhs, Node privateName,
                                                 uint32_t end) {
    return NodePrivateMemberAccess;
  }

  PrivateMemberAccessType newOptionalPrivateMemberAccess(Node lhs,
                                                         Node privateName,
                                                         uint32_t end) {
    return NodeOptionalPrivateMemberAccess;
  }

  [[nodiscard]] bool setupCatchScope(LexicalScopeNodeType lexicalScope,
                                     Node catchName, Node catchBody) {
    return true;
  }

  [[nodiscard]] bool setLastFunctionFormalParameterDefault(
      FunctionNodeType funNode, Node defaultValue) {
    return true;
  }

  void checkAndSetIsDirectRHSAnonFunction(Node pn) {}

  ParamsBodyNodeType newParamsBody(const TokenPos& pos) { return NodeGeneric; }

  FunctionNodeType newFunction(FunctionSyntaxKind syntaxKind,
                               const TokenPos& pos) {
    switch (syntaxKind) {
      case FunctionSyntaxKind::Statement:
        return NodeFunctionStatement;
      case FunctionSyntaxKind::Arrow:
        return NodeFunctionArrow;
      default:
        // All non-arrow function expressions are initially presumed to have
        // block body.  This will be overridden later *if* the function
        // expression permissibly has an AssignmentExpression body.
        return NodeFunctionExpression;
    }
  }

  void setFunctionFormalParametersAndBody(FunctionNodeType funNode,
                                          ParamsBodyNodeType paramsBody) {}
  void setFunctionBody(FunctionNodeType funNode, LexicalScopeNodeType body) {}
  void setFunctionBox(FunctionNodeType funNode, FunctionBox* funbox) {}
  void addFunctionFormalParameter(FunctionNodeType funNode, Node argpn) {}

  ForNodeType newForStatement(uint32_t begin, TernaryNodeType forHead,
                              Node body, unsigned iflags) {
    return NodeGeneric;
  }

  TernaryNodeType newForHead(Node init, Node test, Node update,
                             const TokenPos& pos) {
    return NodeGeneric;
  }

  TernaryNodeType newForInOrOfHead(ParseNodeKind kind, Node target,
                                   Node iteratedExpr, const TokenPos& pos) {
    return NodeGeneric;
  }

  AssignmentNodeType finishInitializerAssignment(NameNodeType nameNode,
                                                 Node init) {
    return NodeUnparenthesizedAssignment;
  }

  void setBeginPosition(Node pn, Node oth) {}
  void setBeginPosition(Node pn, uint32_t begin) {}

  void setEndPosition(Node pn, Node oth) {}
  void setEndPosition(Node pn, uint32_t end) {}

  uint32_t getFunctionNameOffset(Node func, TokenStreamAnyChars& ts) {
    // XXX This offset isn't relevant to the offending function name.  But
    //     we may not *have* that function name around, because of how lazy
    //     parsing works -- the actual name could be outside
    //     |tokenStream.userbuf|'s observed region.  So the current offset
    //     is the best we can do.
    return ts.currentToken().pos.begin;
  }

  ListNodeType newList(ParseNodeKind kind, const TokenPos& pos) {
    MOZ_ASSERT(kind != ParseNodeKind::VarStmt);
    MOZ_ASSERT(kind != ParseNodeKind::LetDecl);
    MOZ_ASSERT(kind != ParseNodeKind::ConstDecl);
    MOZ_ASSERT(kind != ParseNodeKind::ParamsBody);
    return NodeGeneric;
  }

  ListNodeType newList(ParseNodeKind kind, Node kid) {
    return newList(kind, TokenPos());
  }

  DeclarationListNodeType newDeclarationList(ParseNodeKind kind,
                                             const TokenPos& pos) {
    if (kind == ParseNodeKind::VarStmt) {
      return NodeVarDeclaration;
    }
    MOZ_ASSERT(kind == ParseNodeKind::LetDecl ||
               kind == ParseNodeKind::ConstDecl);
    return NodeLexicalDeclaration;
  }

  ListNodeType newCommaExpressionList(Node kid) { return NodeGeneric; }

  void addList(ListNodeType list, Node kid) {
    MOZ_ASSERT(list == NodeGeneric || list == NodeUnparenthesizedArray ||
               list == NodeUnparenthesizedObject ||
               list == NodeVarDeclaration || list == NodeLexicalDeclaration ||
               list == NodeFunctionCall);
  }

  CallNodeType newNewExpression(uint32_t begin, Node ctor, ListNodeType args,
                                bool isSpread) {
    return NodeGeneric;
  }

  AssignmentNodeType newAssignment(ParseNodeKind kind, Node lhs, Node rhs) {
    return kind == ParseNodeKind::AssignExpr ? NodeUnparenthesizedAssignment
                                             : NodeGeneric;
  }

  AssignmentNodeType newInitExpr(Node lhs, Node rhs) { return NodeGeneric; }

  bool isUnparenthesizedAssignment(Node node) {
    return node == NodeUnparenthesizedAssignment;
  }

  bool isUnparenthesizedUnaryExpression(Node node) {
    return node == NodeUnparenthesizedUnary;
  }

  bool isReturnStatement(Node node) { return node == NodeReturn; }

  bool isStatementPermittedAfterReturnStatement(Node pn) {
    return pn == NodeFunctionStatement || isNonArrowFunctionExpression(pn) ||
           pn == NodeVarDeclaration || pn == NodeBreak || pn == NodeThrow ||
           pn == NodeEmptyStatement;
  }

  bool isSuperBase(Node pn) { return pn == NodeSuperBase; }

  void setListHasNonConstInitializer(ListNodeType literal) {}
  [[nodiscard]] Node parenthesize(Node node) {
    // A number of nodes have different behavior upon parenthesization, but
    // only in some circumstances.  Convert these nodes to special
    // parenthesized forms.
    if (node == NodeUnparenthesizedArray) {
      return NodeParenthesizedArray;
    }
    if (node == NodeUnparenthesizedObject) {
      return NodeParenthesizedObject;
    }

    // Other nodes need not be recognizable after parenthesization; convert
    // them to a generic node.
    if (node == NodeUnparenthesizedString ||
        node == NodeUnparenthesizedAssignment ||
        node == NodeUnparenthesizedUnary) {
      return NodeGeneric;
    }

    // Convert parenthesized |async| to a normal name node.
    if (node == NodePotentialAsyncKeyword) {
      return NodeName;
    }

    // In all other cases, the parenthesized form of |node| is equivalent
    // to the unparenthesized form: return |node| unchanged.
    return node;
  }
  template <typename NodeType>
  [[nodiscard]] NodeType setLikelyIIFE(NodeType node) {
    return node;  // Remain in syntax-parse mode.
  }

  bool isName(Node node) {
    return node == NodeName || node == NodeArgumentsName ||
           node == NodeEvalName || node == NodePotentialAsyncKeyword;
  }

  bool isArgumentsName(Node node) { return node == NodeArgumentsName; }
  bool isEvalName(Node node) { return node == NodeEvalName; }
  bool isAsyncKeyword(Node node) { return node == NodePotentialAsyncKeyword; }

  bool isPrivateName(Node node) { return node == NodePrivateName; }
  bool isPrivateMemberAccess(Node node) {
    return node == NodePrivateMemberAccess;
  }

  TaggedParserAtomIndex maybeDottedProperty(Node node) {
    // Note: |super.apply(...)| is a special form that calls an "apply"
    // method retrieved from one value, but using a *different* value as
    // |this|.  It's not really eligible for the funapply/funcall
    // optimizations as they're currently implemented (assuming a single
    // value is used for both retrieval and |this|).
    if (node != NodeDottedProperty && node != NodeOptionalDottedProperty) {
      return TaggedParserAtomIndex::null();
    }
    return lastAtom;
  }

  TaggedParserAtomIndex isStringExprStatement(Node pn, TokenPos* pos) {
    if (pn == NodeStringExprStatement) {
      *pos = lastStringPos;
      return lastAtom;
    }
    return TaggedParserAtomIndex::null();
  }

  bool reuseLazyInnerFunctions() { return false; }
  bool reuseClosedOverBindings() { return false; }
  TaggedParserAtomIndex nextLazyClosedOverBinding() {
    MOZ_CRASH(
        "SyntaxParseHandler::canSkipLazyClosedOverBindings must return false");
  }

  void setPrivateNameKind(Node node, PrivateNameKind kind) {}
};

}  // namespace frontend
}  // namespace js

#endif /* frontend_SyntaxParseHandler_h */
