diff options
Diffstat (limited to 'js/src/frontend/ParseNode.h')
-rw-r--r-- | js/src/frontend/ParseNode.h | 2601 |
1 files changed, 2601 insertions, 0 deletions
diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h new file mode 100644 index 0000000000..61c009c6e4 --- /dev/null +++ b/js/src/frontend/ParseNode.h @@ -0,0 +1,2601 @@ +/* -*- 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_ParseNode_h +#define frontend_ParseNode_h + +#include "mozilla/Assertions.h" + +#include <iterator> +#include <stddef.h> +#include <stdint.h> + +#include "jstypes.h" // js::Bit + +#include "frontend/FunctionSyntaxKind.h" // FunctionSyntaxKind +#include "frontend/NameAnalysisTypes.h" // PrivateNameKind +#include "frontend/ParserAtom.h" // TaggedParserAtomIndex +#include "frontend/Stencil.h" // BigIntStencil +#include "frontend/Token.h" +#include "js/TypeDecls.h" +#include "vm/Opcodes.h" +#include "vm/Scope.h" +#include "vm/ScopeKind.h" + +// [SMDOC] ParseNode tree lifetime information +// +// - All the `ParseNode` instances MUST BE explicitly allocated in the context's +// `LifoAlloc`. This is typically implemented by the `FullParseHandler` or it +// can be reimplemented with a custom `new_`. +// +// - The tree is bulk-deallocated when the parser is deallocated. Consequently, +// references to a subtree MUST NOT exist once the parser has been +// deallocated. +// +// - This bulk-deallocation DOES NOT run destructors. +// +// - Instances of `LexicalScope::ParserData` and `ClassBodyScope::ParserData` +// MUST BE allocated as instances of `ParseNode`, in the same `LifoAlloc`. +// They are bulk-deallocated alongside the rest of the tree. + +struct JSContext; + +namespace js { + +class JS_PUBLIC_API GenericPrinter; +class LifoAlloc; +class RegExpObject; + +namespace frontend { + +class ParserAtomsTable; +class ParserBase; +class ParseContext; +struct ExtensibleCompilationStencil; +class ParserSharedBase; +class FullParseHandler; + +class FunctionBox; + +#define FOR_EACH_PARSE_NODE_KIND(F) \ + F(EmptyStmt, NullaryNode) \ + F(ExpressionStmt, UnaryNode) \ + F(CommaExpr, ListNode) \ + F(ConditionalExpr, ConditionalExpression) \ + F(PropertyDefinition, PropertyDefinition) \ + F(Shorthand, BinaryNode) \ + F(PosExpr, UnaryNode) \ + F(NegExpr, UnaryNode) \ + F(PreIncrementExpr, UnaryNode) \ + F(PostIncrementExpr, UnaryNode) \ + F(PreDecrementExpr, UnaryNode) \ + F(PostDecrementExpr, UnaryNode) \ + F(PropertyNameExpr, NameNode) \ + F(DotExpr, PropertyAccess) \ + F(ElemExpr, PropertyByValue) \ + F(PrivateMemberExpr, PrivateMemberAccess) \ + F(OptionalDotExpr, OptionalPropertyAccess) \ + F(OptionalChain, UnaryNode) \ + F(OptionalElemExpr, OptionalPropertyByValue) \ + F(OptionalPrivateMemberExpr, OptionalPrivateMemberAccess) \ + F(OptionalCallExpr, CallNode) \ + F(ArrayExpr, ListNode) \ + F(Elision, NullaryNode) \ + F(StatementList, ListNode) \ + F(LabelStmt, LabeledStatement) \ + F(ObjectExpr, ListNode) \ + F(CallExpr, CallNode) \ + F(Arguments, ListNode) \ + F(Name, NameNode) \ + F(ObjectPropertyName, NameNode) \ + F(PrivateName, NameNode) \ + F(ComputedName, UnaryNode) \ + F(NumberExpr, NumericLiteral) \ + F(BigIntExpr, BigIntLiteral) \ + F(StringExpr, NameNode) \ + F(TemplateStringListExpr, ListNode) \ + F(TemplateStringExpr, NameNode) \ + F(TaggedTemplateExpr, CallNode) \ + F(CallSiteObj, CallSiteNode) \ + F(RegExpExpr, RegExpLiteral) \ + F(TrueExpr, BooleanLiteral) \ + F(FalseExpr, BooleanLiteral) \ + F(NullExpr, NullLiteral) \ + F(RawUndefinedExpr, RawUndefinedLiteral) \ + F(ThisExpr, UnaryNode) \ + IF_RECORD_TUPLE(F(RecordExpr, ListNode)) \ + IF_RECORD_TUPLE(F(TupleExpr, ListNode)) \ + F(Function, FunctionNode) \ + F(Module, ModuleNode) \ + F(IfStmt, TernaryNode) \ + F(SwitchStmt, SwitchStatement) \ + F(Case, CaseClause) \ + F(WhileStmt, BinaryNode) \ + F(DoWhileStmt, BinaryNode) \ + F(ForStmt, ForNode) \ + F(BreakStmt, BreakStatement) \ + F(ContinueStmt, ContinueStatement) \ + F(VarStmt, DeclarationListNode) \ + F(ConstDecl, DeclarationListNode) \ + F(WithStmt, BinaryNode) \ + F(ReturnStmt, UnaryNode) \ + F(NewExpr, CallNode) \ + IF_DECORATORS(F(DecoratorList, ListNode)) \ + /* Delete operations. These must be sequential. */ \ + F(DeleteNameExpr, UnaryNode) \ + F(DeletePropExpr, UnaryNode) \ + F(DeleteElemExpr, UnaryNode) \ + F(DeleteOptionalChainExpr, UnaryNode) \ + F(DeleteExpr, UnaryNode) \ + F(TryStmt, TernaryNode) \ + F(Catch, BinaryNode) \ + F(ThrowStmt, UnaryNode) \ + F(DebuggerStmt, DebuggerStatement) \ + F(Generator, NullaryNode) \ + F(InitialYield, UnaryNode) \ + F(YieldExpr, UnaryNode) \ + F(YieldStarExpr, UnaryNode) \ + F(LexicalScope, LexicalScopeNode) \ + F(LetDecl, DeclarationListNode) \ + F(ImportDecl, BinaryNode) \ + F(ImportSpecList, ListNode) \ + F(ImportSpec, BinaryNode) \ + F(ImportNamespaceSpec, UnaryNode) \ + F(ImportAttributeList, ListNode) \ + F(ImportAttribute, BinaryNode) \ + F(ImportModuleRequest, BinaryNode) \ + F(ExportStmt, UnaryNode) \ + F(ExportFromStmt, BinaryNode) \ + F(ExportDefaultStmt, BinaryNode) \ + F(ExportSpecList, ListNode) \ + F(ExportSpec, BinaryNode) \ + F(ExportNamespaceSpec, UnaryNode) \ + F(ExportBatchSpecStmt, NullaryNode) \ + F(ForIn, TernaryNode) \ + F(ForOf, TernaryNode) \ + F(ForHead, TernaryNode) \ + F(ParamsBody, ParamsBodyNode) \ + F(Spread, UnaryNode) \ + F(MutateProto, UnaryNode) \ + F(ClassDecl, ClassNode) \ + F(DefaultConstructor, ClassMethod) \ + F(ClassBodyScope, ClassBodyScopeNode) \ + F(ClassMethod, ClassMethod) \ + F(StaticClassBlock, StaticClassBlock) \ + F(ClassField, ClassField) \ + F(ClassMemberList, ListNode) \ + F(ClassNames, ClassNames) \ + F(NewTargetExpr, NewTargetNode) \ + F(PosHolder, NullaryNode) \ + F(SuperBase, UnaryNode) \ + F(SuperCallExpr, CallNode) \ + F(SetThis, BinaryNode) \ + F(ImportMetaExpr, BinaryNode) \ + F(CallImportExpr, BinaryNode) \ + F(CallImportSpec, BinaryNode) \ + F(InitExpr, BinaryNode) \ + \ + /* Unary operators. */ \ + F(TypeOfNameExpr, UnaryNode) \ + F(TypeOfExpr, UnaryNode) \ + F(VoidExpr, UnaryNode) \ + F(NotExpr, UnaryNode) \ + F(BitNotExpr, UnaryNode) \ + F(AwaitExpr, UnaryNode) \ + \ + /* \ + * Binary operators. \ + * This list must be kept in the same order in several places: \ + * - The binary operators in ParseNode.h \ + * - the binary operators in TokenKind.h \ + * - the precedence list in Parser.cpp \ + * - the JSOp code list in BytecodeEmitter.cpp \ + */ \ + F(CoalesceExpr, ListNode) \ + F(OrExpr, ListNode) \ + F(AndExpr, ListNode) \ + F(BitOrExpr, ListNode) \ + F(BitXorExpr, ListNode) \ + F(BitAndExpr, ListNode) \ + F(StrictEqExpr, ListNode) \ + F(EqExpr, ListNode) \ + F(StrictNeExpr, ListNode) \ + F(NeExpr, ListNode) \ + F(LtExpr, ListNode) \ + F(LeExpr, ListNode) \ + F(GtExpr, ListNode) \ + F(GeExpr, ListNode) \ + F(InstanceOfExpr, ListNode) \ + F(InExpr, ListNode) \ + F(PrivateInExpr, ListNode) \ + F(LshExpr, ListNode) \ + F(RshExpr, ListNode) \ + F(UrshExpr, ListNode) \ + F(AddExpr, ListNode) \ + F(SubExpr, ListNode) \ + F(MulExpr, ListNode) \ + F(DivExpr, ListNode) \ + F(ModExpr, ListNode) \ + F(PowExpr, ListNode) \ + \ + /* Assignment operators (= += -= etc.). */ \ + /* AssignmentNode::test assumes all these are consecutive. */ \ + F(AssignExpr, AssignmentNode) \ + F(AddAssignExpr, AssignmentNode) \ + F(SubAssignExpr, AssignmentNode) \ + F(CoalesceAssignExpr, AssignmentNode) \ + F(OrAssignExpr, AssignmentNode) \ + F(AndAssignExpr, AssignmentNode) \ + F(BitOrAssignExpr, AssignmentNode) \ + F(BitXorAssignExpr, AssignmentNode) \ + F(BitAndAssignExpr, AssignmentNode) \ + F(LshAssignExpr, AssignmentNode) \ + F(RshAssignExpr, AssignmentNode) \ + F(UrshAssignExpr, AssignmentNode) \ + F(MulAssignExpr, AssignmentNode) \ + F(DivAssignExpr, AssignmentNode) \ + F(ModAssignExpr, AssignmentNode) \ + F(PowAssignExpr, AssignmentNode) + +/* + * Parsing builds a tree of nodes that directs code generation. This tree is + * not a concrete syntax tree in all respects (for example, || and && are left + * associative, but (A && B && C) translates into the right-associated tree + * <A && <B && C>> so that code generation can emit a left-associative branch + * around <B && C> when A is false). Nodes are labeled by kind. + * + * The long comment after this enum block describes the kinds in detail. + */ +enum class ParseNodeKind : uint16_t { + // These constants start at 1001, the better to catch + LastUnused = 1000, +#define EMIT_ENUM(name, _type) name, + FOR_EACH_PARSE_NODE_KIND(EMIT_ENUM) +#undef EMIT_ENUM + Limit, + Start = LastUnused + 1, + BinOpFirst = ParseNodeKind::CoalesceExpr, + BinOpLast = ParseNodeKind::PowExpr, + AssignmentStart = ParseNodeKind::AssignExpr, + AssignmentLast = ParseNodeKind::PowAssignExpr, +}; + +inline bool IsDeleteKind(ParseNodeKind kind) { + return ParseNodeKind::DeleteNameExpr <= kind && + kind <= ParseNodeKind::DeleteExpr; +} + +inline bool IsTypeofKind(ParseNodeKind kind) { + return ParseNodeKind::TypeOfNameExpr <= kind && + kind <= ParseNodeKind::TypeOfExpr; +} + +/* + * <Definitions> + * Function (FunctionNode) + * funbox: ptr to js::FunctionBox + * body: ParamsBody or null for lazily-parsed function + * syntaxKind: the syntax of the function + * ParamsBody (ListNode) + * head: list of formal parameters with + * * Name node with non-empty name for SingleNameBinding without + * Initializer + * * AssignExpr node for SingleNameBinding with Initializer + * * Name node with empty name for destructuring + * expr: Array or Object for BindingPattern without + * Initializer, Assign for BindingPattern with + * Initializer + * followed by: + * * LexicalScopeNode + * count: number of formal parameters + 1 + * Spread (UnaryNode) + * kid: expression being spread + * ClassDecl (ClassNode) + * kid1: ClassNames for class name. can be null for anonymous class. + * kid2: expression after `extends`. null if no expression + * kid3: either of + * * ClassMemberList, if anonymous class + * * LexicalScopeNode which contains ClassMemberList as scopeBody, + * if named class + * ClassNames (ClassNames) + * left: Name node for outer binding, or null if the class is an expression + * that doesn't create an outer binding + * right: Name node for inner binding + * ClassMemberList (ListNode) + * head: list of N ClassMethod, ClassField or StaticClassBlock nodes + * count: N >= 0 + * DefaultConstructor (ClassMethod) + * name: propertyName + * method: methodDefinition + * ClassMethod (ClassMethod) + * name: propertyName + * method: methodDefinition + * initializerIfPrivate: initializer to stamp private method onto instance + * Module (ModuleNode) + * body: statement list of the module + * + * <Statements> + * StatementList (ListNode) + * head: list of N statements + * count: N >= 0 + * IfStmt (TernaryNode) + * kid1: cond + * kid2: then + * kid3: else or null + * SwitchStmt (SwitchStatement) + * left: discriminant + * right: LexicalScope node that contains the list of Case nodes, with at + * most one default node. + * hasDefault: true if there's a default case + * Case (CaseClause) + * left: case-expression if CaseClause, or null if DefaultClause + * right: StatementList node for this case's statements + * WhileStmt (BinaryNode) + * left: cond + * right: body + * DoWhileStmt (BinaryNode) + * left: body + * right: cond + * ForStmt (ForNode) + * left: one of + * * ForIn: for (x in y) ... + * * ForOf: for (x of x) ... + * * ForHead: for (;;) ... + * right: body + * ForIn (TernaryNode) + * kid1: declaration or expression to left of 'in' + * kid2: null + * kid3: object expr to right of 'in' + * ForOf (TernaryNode) + * kid1: declaration or expression to left of 'of' + * kid2: null + * kid3: expr to right of 'of' + * ForHead (TernaryNode) + * kid1: init expr before first ';' or nullptr + * kid2: cond expr before second ';' or nullptr + * kid3: update expr after second ';' or nullptr + * ThrowStmt (UnaryNode) + * kid: thrown exception + * TryStmt (TernaryNode) + * kid1: try block + * kid2: null or LexicalScope for catch-block with scopeBody pointing to a + * Catch node + * kid3: null or finally block + * Catch (BinaryNode) + * left: Name, Array, or Object catch var node + * (Array or Object if destructuring), + * or null if optional catch binding + * right: catch block statements + * BreakStmt (BreakStatement) + * label: label or null + * ContinueStmt (ContinueStatement) + * label: label or null + * WithStmt (BinaryNode) + * left: head expr + * right: body + * VarStmt, LetDecl, ConstDecl (DeclarationListNode) + * head: list of N Name or AssignExpr nodes + * each name node has either + * atom: variable name + * expr: initializer or null + * or + * atom: variable name + * each assignment node has + * left: pattern + * right: initializer + * count: N > 0 + * ReturnStmt (UnaryNode) + * kid: returned expression, or null if none + * ExpressionStmt (UnaryNode) + * kid: expr + * EmptyStmt (NullaryNode) + * (no fields) + * LabelStmt (LabeledStatement) + * atom: label + * expr: labeled statement + * ImportDecl (BinaryNode) + * left: ImportSpecList import specifiers + * right: String module specifier + * ImportSpecList (ListNode) + * head: list of N ImportSpec nodes + * count: N >= 0 (N = 0 for `import {} from ...`) + * ImportSpec (BinaryNode) + * left: import name + * right: local binding name + * ImportNamespaceSpec (UnaryNode) + * kid: local binding name + * ExportStmt (UnaryNode) + * kid: declaration expression + * ExportFromStmt (BinaryNode) + * left: ExportSpecList export specifiers + * right: String module specifier + * ExportSpecList (ListNode) + * head: list of N ExportSpec nodes + * count: N >= 0 (N = 0 for `export {}`) + * ExportSpec (BinaryNode) + * left: local binding name + * right: export name + * ExportNamespaceSpec (UnaryNode) + * kid: export name + * ExportDefaultStmt (BinaryNode) + * left: export default declaration or expression + * right: Name node for assignment + * + * <Expressions> + * The `Expr` suffix is used for nodes that can appear anywhere an expression + * could appear. It is not used on a few weird kinds like Arguments and + * CallSiteObj that are always the child node of an expression node, but which + * can't stand alone. + * + * All left-associated binary trees of the same type are optimized into lists + * to avoid recursion when processing expression chains. + * + * CommaExpr (ListNode) + * head: list of N comma-separated exprs + * count: N >= 2 + * AssignExpr (BinaryNode) + * left: target of assignment + * right: value to assign + * AddAssignExpr, SubAssignExpr, CoalesceAssignExpr, OrAssignExpr, + * AndAssignExpr, BitOrAssignExpr, BitXorAssignExpr, BitAndAssignExpr, + * LshAssignExpr, RshAssignExpr, UrshAssignExpr, MulAssignExpr, DivAssignExpr, + * ModAssignExpr, PowAssignExpr (AssignmentNode) + * left: target of assignment + * right: value to assign + * ConditionalExpr (ConditionalExpression) + * (cond ? thenExpr : elseExpr) + * kid1: cond + * kid2: thenExpr + * kid3: elseExpr + * CoalesceExpr, OrExpr, AndExpr, BitOrExpr, BitXorExpr, + * BitAndExpr, StrictEqExpr, EqExpr, StrictNeExpr, NeExpr, LtExpr, LeExpr, + * GtExpr, GeExpr, InstanceOfExpr, InExpr, LshExpr, RshExpr, UrshExpr, AddExpr, + * SubExpr, MulExpr, DivExpr, ModExpr, PowExpr (ListNode) + * head: list of N subexpressions + * All of these operators are left-associative except Pow which is + * right-associative, but still forms a list (see comments in + * ParseNode::appendOrCreateList). + * count: N >= 2 + * PosExpr, NegExpr, VoidExpr, NotExpr, BitNotExpr, TypeOfNameExpr, + * TypeOfExpr (UnaryNode) + * kid: unary expr + * PreIncrementExpr, PostIncrementExpr, PreDecrementExpr, + * PostDecrementExpr (UnaryNode) + * kid: member expr + * NewExpr (BinaryNode) + * left: ctor expression on the left of the '(' + * right: Arguments + * DecoratorList (ListNode) + * head: list of N nodes, each item is one of: + * * NameNode (DecoratorMemberExpression) + * * CallNode (DecoratorCallExpression) + * * Node (DecoratorParenthesizedExpression) + * count: N > 0 + * DeleteNameExpr, DeletePropExpr, DeleteElemExpr, DeleteExpr (UnaryNode) + * kid: expression that's evaluated, then the overall delete evaluates to + * true; can't be a kind for a more-specific ParseNodeKind::Delete* + * unless constant folding (or a similar parse tree manipulation) has + * occurred + * * DeleteNameExpr: Name expr + * * DeletePropExpr: Dot expr + * * DeleteElemExpr: Elem expr + * * DeleteOptionalChainExpr: Member expr + * * DeleteExpr: Member expr + * DeleteOptionalChainExpr (UnaryNode) + * kid: expression that's evaluated, then the overall delete evaluates to + * true; If constant folding occurs, Elem expr may become Dot expr. + * OptionalElemExpr does not get folded into OptionalDot. + * OptionalChain (UnaryNode) + * kid: expression that is evaluated as a chain. An Optional chain contains + * one or more optional nodes. It's first node (kid) is always an + * optional node, for example: an OptionalElemExpr, OptionalDotExpr, or + * OptionalCall. An OptionalChain will shortcircuit and return + * Undefined without evaluating the rest of the expression if any of the + * optional nodes it contains are nullish. An optionalChain also can + * contain nodes such as DotExpr, ElemExpr, NameExpr CallExpr, etc. + * These are evaluated normally. + * * OptionalDotExpr: Dot expr with jump + * * OptionalElemExpr: Elem expr with jump + * * OptionalCallExpr: Call expr with jump + * * DotExpr: Dot expr without jump + * * ElemExpr: Elem expr without jump + * * CallExpr: Call expr without jump + * PropertyNameExpr (NameNode) + * atom: property name being accessed + * privateNameKind: kind of the name if private + * DotExpr (PropertyAccess) + * left: Member expr to left of '.' + * right: PropertyName to right of '.' + * OptionalDotExpr (OptionalPropertyAccess) + * left: Member expr to left of '.', short circuits back to OptionalChain + * if nullish. + * right: PropertyName to right of '.' + * ElemExpr (PropertyByValue) + * left: Member expr to left of '[' + * right: expr between '[' and ']' + * OptionalElemExpr (OptionalPropertyByValue) + * left: Member expr to left of '[', short circuits back to OptionalChain + * if nullish. + * right: expr between '[' and ']' + * CallExpr (BinaryNode) + * left: callee expression on the left of the '(' + * right: Arguments + * OptionalCallExpr (BinaryNode) + * left: callee expression on the left of the '(', short circuits back to + * OptionalChain if nullish. + * right: Arguments + * Arguments (ListNode) + * head: list of arg1, arg2, ... argN + * count: N >= 0 + * ArrayExpr (ListNode) + * head: list of N array element expressions + * holes ([,,]) are represented by Elision nodes, + * spread elements ([...X]) are represented by Spread nodes + * count: N >= 0 + * ObjectExpr (ListNode) + * head: list of N nodes, each item is one of: + * * MutateProto + * * PropertyDefinition + * * Shorthand + * * Spread + * count: N >= 0 + * PropertyDefinition (PropertyDefinition) + * key-value pair in object initializer or destructuring lhs + * left: property id + * right: value + * Shorthand (BinaryNode) + * Same fields as PropertyDefinition. This is used for object literal + * properties using shorthand ({x}). + * ComputedName (UnaryNode) + * ES6 ComputedPropertyName. + * kid: the AssignmentExpression inside the square brackets + * Name (NameNode) + * atom: name, or object atom + * StringExpr (NameNode) + * atom: string + * TemplateStringListExpr (ListNode) + * head: list of alternating expr and template strings + * TemplateString [, expression, TemplateString]+ + * there's at least one expression. If the template literal contains + * no ${}-delimited expression, it's parsed as a single TemplateString + * TemplateStringExpr (NameNode) + * atom: template string atom + * TaggedTemplateExpr (BinaryNode) + * left: tag expression + * right: Arguments, with the first being the call site object, then + * arg1, arg2, ... argN + * CallSiteObj (CallSiteNode) + * head: an Array of raw TemplateString, then corresponding cooked + * TemplateString nodes + * Array [, cooked TemplateString]+ + * where the Array is + * [raw TemplateString]+ + * RegExpExpr (RegExpLiteral) + * regexp: RegExp model object + * NumberExpr (NumericLiteral) + * value: double value of numeric literal + * BigIntExpr (BigIntLiteral) + * stencil: script compilation struct that has |bigIntData| vector + * index: index into the script compilation's |bigIntData| vector + * TrueExpr, FalseExpr (BooleanLiteral) + * NullExpr (NullLiteral) + * RawUndefinedExpr (RawUndefinedLiteral) + * + * ThisExpr (UnaryNode) + * kid: '.this' Name if function `this`, else nullptr + * SuperBase (UnaryNode) + * kid: '.this' Name + * SuperCallExpr (BinaryNode) + * left: SuperBase + * right: Arguments + * SetThis (BinaryNode) + * left: '.this' Name + * right: SuperCall + * + * LexicalScope (LexicalScopeNode) + * scopeBindings: scope bindings + * scopeBody: scope body + * Generator (NullaryNode) + * InitialYield (UnaryNode) + * kid: generator object + * YieldExpr, YieldStarExpr, AwaitExpr (UnaryNode) + * kid: expr or null + */ + +#define FOR_EACH_PARSENODE_SUBCLASS(MACRO) \ + MACRO(BinaryNode) \ + MACRO(AssignmentNode) \ + MACRO(CaseClause) \ + MACRO(ClassMethod) \ + MACRO(ClassField) \ + MACRO(StaticClassBlock) \ + MACRO(PropertyDefinition) \ + MACRO(ClassNames) \ + MACRO(ForNode) \ + MACRO(PropertyAccess) \ + MACRO(OptionalPropertyAccess) \ + MACRO(PropertyByValue) \ + MACRO(OptionalPropertyByValue) \ + MACRO(PrivateMemberAccess) \ + MACRO(OptionalPrivateMemberAccess) \ + MACRO(NewTargetNode) \ + MACRO(SwitchStatement) \ + MACRO(DeclarationListNode) \ + \ + MACRO(ParamsBodyNode) \ + MACRO(FunctionNode) \ + MACRO(ModuleNode) \ + \ + MACRO(LexicalScopeNode) \ + MACRO(ClassBodyScopeNode) \ + \ + MACRO(ListNode) \ + MACRO(CallSiteNode) \ + MACRO(CallNode) \ + \ + MACRO(LoopControlStatement) \ + MACRO(BreakStatement) \ + MACRO(ContinueStatement) \ + \ + MACRO(NameNode) \ + MACRO(LabeledStatement) \ + \ + MACRO(NullaryNode) \ + MACRO(BooleanLiteral) \ + MACRO(DebuggerStatement) \ + MACRO(NullLiteral) \ + MACRO(RawUndefinedLiteral) \ + \ + MACRO(NumericLiteral) \ + MACRO(BigIntLiteral) \ + \ + MACRO(RegExpLiteral) \ + \ + MACRO(TernaryNode) \ + MACRO(ClassNode) \ + MACRO(ConditionalExpression) \ + MACRO(TryNode) \ + \ + MACRO(UnaryNode) \ + MACRO(ThisLiteral) + +#define DECLARE_CLASS(typeName) class typeName; +FOR_EACH_PARSENODE_SUBCLASS(DECLARE_CLASS) +#undef DECLARE_CLASS + +enum class AccessorType { None, Getter, Setter }; + +static inline bool IsConstructorKind(FunctionSyntaxKind kind) { + return kind == FunctionSyntaxKind::ClassConstructor || + kind == FunctionSyntaxKind::DerivedClassConstructor; +} + +static inline bool IsMethodDefinitionKind(FunctionSyntaxKind kind) { + return IsConstructorKind(kind) || kind == FunctionSyntaxKind::Method || + kind == FunctionSyntaxKind::FieldInitializer || + kind == FunctionSyntaxKind::Getter || + kind == FunctionSyntaxKind::Setter; +} + +// To help diagnose sporadic crashes in the frontend, a few assertions are +// enabled in early beta builds. (Most are not; those still use MOZ_ASSERT.) +// See bug 1547561. +#if defined(EARLY_BETA_OR_EARLIER) +# define JS_PARSE_NODE_ASSERT MOZ_RELEASE_ASSERT +#else +# define JS_PARSE_NODE_ASSERT MOZ_ASSERT +#endif + +class ParseNode; +struct ParseNodeError {}; +using ParseNodeResult = mozilla::Result<ParseNode*, ParseNodeError>; + +class ParseNode { + const ParseNodeKind pn_type; + + bool pn_parens : 1; /* this expr was enclosed in parens */ + bool pn_rhs_anon_fun : 1; /* this expr is anonymous function or class that + * is a direct RHS of ParseNodeKind::Assign or + * ParseNodeKind::PropertyDefinition of property, + * that needs SetFunctionName. */ + + protected: + // Used by ComputedName to indicate if the ComputedName is a + // a synthetic construct. This allows us to avoid needing to + // compute ToString on uncommon property values such as BigInt. + // Instead we parse as though they were computed names. + // + // We need this bit to distinguish a synthetic computed name like + // this however to undo this transformation in Reflect.parse and + // name guessing. + bool pn_synthetic_computed : 1; + + ParseNode(const ParseNode& other) = delete; + void operator=(const ParseNode& other) = delete; + + public: + explicit ParseNode(ParseNodeKind kind) + : pn_type(kind), + pn_parens(false), + pn_rhs_anon_fun(false), + pn_synthetic_computed(false), + pn_pos(0, 0), + pn_next(nullptr) { + JS_PARSE_NODE_ASSERT(ParseNodeKind::Start <= kind); + JS_PARSE_NODE_ASSERT(kind < ParseNodeKind::Limit); + } + + ParseNode(ParseNodeKind kind, const TokenPos& pos) + : pn_type(kind), + pn_parens(false), + pn_rhs_anon_fun(false), + pn_synthetic_computed(false), + pn_pos(pos), + pn_next(nullptr) { + JS_PARSE_NODE_ASSERT(ParseNodeKind::Start <= kind); + JS_PARSE_NODE_ASSERT(kind < ParseNodeKind::Limit); + } + + ParseNodeKind getKind() const { + JS_PARSE_NODE_ASSERT(ParseNodeKind::Start <= pn_type); + JS_PARSE_NODE_ASSERT(pn_type < ParseNodeKind::Limit); + return pn_type; + } + bool isKind(ParseNodeKind kind) const { return getKind() == kind; } + + protected: + size_t getKindAsIndex() const { + return size_t(getKind()) - size_t(ParseNodeKind::Start); + } + + // Used to implement test() on a few ParseNodes efficiently. + // (This enum doesn't fully reflect the ParseNode class hierarchy, + // so don't use it for anything else.) + enum class TypeCode : uint8_t { + Nullary, + Unary, + Binary, + Ternary, + List, + Name, + Other + }; + + // typeCodeTable[getKindAsIndex()] is the type code of a ParseNode of kind + // pnk. + static const TypeCode typeCodeTable[]; + + private: +#ifdef DEBUG + static const size_t sizeTable[]; +#endif + + public: + TypeCode typeCode() const { return typeCodeTable[getKindAsIndex()]; } + + bool isBinaryOperation() const { + ParseNodeKind kind = getKind(); + return ParseNodeKind::BinOpFirst <= kind && + kind <= ParseNodeKind::BinOpLast; + } + inline bool isName(TaggedParserAtomIndex name) const; + + /* Boolean attributes. */ + bool isInParens() const { return pn_parens; } + bool isLikelyIIFE() const { return isInParens(); } + void setInParens(bool enabled) { pn_parens = enabled; } + + bool isDirectRHSAnonFunction() const { return pn_rhs_anon_fun; } + void setDirectRHSAnonFunction(bool enabled) { pn_rhs_anon_fun = enabled; } + + TokenPos pn_pos; /* two 16-bit pairs here, for 64 bits */ + ParseNode* pn_next; /* intrinsic link in parent ListNode */ + + public: + /* + * If |left| is a list of the given kind/left-associative op, append + * |right| to it and return |left|. Otherwise return a [left, right] list. + */ + static ParseNodeResult appendOrCreateList(ParseNodeKind kind, ParseNode* left, + ParseNode* right, + FullParseHandler* handler, + ParseContext* pc); + + /* True if pn is a parsenode representing a literal constant. */ + bool isLiteral() const { + return isKind(ParseNodeKind::NumberExpr) || + isKind(ParseNodeKind::BigIntExpr) || + isKind(ParseNodeKind::StringExpr) || + isKind(ParseNodeKind::TrueExpr) || + isKind(ParseNodeKind::FalseExpr) || + isKind(ParseNodeKind::NullExpr) || + isKind(ParseNodeKind::RawUndefinedExpr); + } + + inline bool isConstant(); + + template <class NodeType> + inline bool is() const { + return NodeType::test(*this); + } + + /* Casting operations. */ + template <class NodeType> + inline NodeType& as() { + MOZ_ASSERT(NodeType::test(*this)); + return *static_cast<NodeType*>(this); + } + + template <class NodeType> + inline const NodeType& as() const { + MOZ_ASSERT(NodeType::test(*this)); + return *static_cast<const NodeType*>(this); + } + +#ifdef DEBUG + // Debugger-friendly stderr printer. + void dump(); + void dump(const ParserAtomsTable* parserAtoms); + void dump(const ParserAtomsTable* parserAtoms, GenericPrinter& out); + void dump(const ParserAtomsTable* parserAtoms, GenericPrinter& out, + int indent); + + // The size of this node, in bytes. + size_t size() const { return sizeTable[getKindAsIndex()]; } +#endif +}; + +// Remove a ParseNode, **pnp, from a parse tree, putting another ParseNode, +// *pn, in its place. +// +// pnp points to a ParseNode pointer. This must be the only pointer that points +// to the parse node being replaced. The replacement, *pn, is unchanged except +// for its pn_next pointer; updating that is necessary if *pn's new parent is a +// list node. +inline void ReplaceNode(ParseNode** pnp, ParseNode* pn) { + pn->pn_next = (*pnp)->pn_next; + *pnp = pn; +} + +class NullaryNode : public ParseNode { + public: + NullaryNode(ParseNodeKind kind, const TokenPos& pos) : ParseNode(kind, pos) { + MOZ_ASSERT(is<NullaryNode>()); + } + + static bool test(const ParseNode& node) { + return node.typeCode() == TypeCode::Nullary; + } + + static constexpr TypeCode classTypeCode() { return TypeCode::Nullary; } + + template <typename Visitor> + bool accept(Visitor& visitor) { + return true; + } + +#ifdef DEBUG + void dumpImpl(const ParserAtomsTable* parserAtoms, GenericPrinter& out, + int indent); +#endif +}; + +class NameNode : public ParseNode { + TaggedParserAtomIndex atom_; /* lexical name or label atom */ + PrivateNameKind privateNameKind_ = PrivateNameKind::None; + + public: + NameNode(ParseNodeKind kind, TaggedParserAtomIndex atom, const TokenPos& pos) + : ParseNode(kind, pos), atom_(atom) { + MOZ_ASSERT(atom); + MOZ_ASSERT(is<NameNode>()); + } + + static bool test(const ParseNode& node) { + return node.typeCode() == TypeCode::Name; + } + + static constexpr TypeCode classTypeCode() { return TypeCode::Name; } + + template <typename Visitor> + bool accept(Visitor& visitor) { + return true; + } + +#ifdef DEBUG + void dumpImpl(const ParserAtomsTable* parserAtoms, GenericPrinter& out, + int indent); +#endif + + TaggedParserAtomIndex atom() const { return atom_; } + + TaggedParserAtomIndex name() const { + MOZ_ASSERT(isKind(ParseNodeKind::Name) || + isKind(ParseNodeKind::PrivateName)); + return atom_; + } + + void setAtom(TaggedParserAtomIndex atom) { atom_ = atom; } + + void setPrivateNameKind(PrivateNameKind privateNameKind) { + privateNameKind_ = privateNameKind; + } + + PrivateNameKind privateNameKind() { return privateNameKind_; } +}; + +inline bool ParseNode::isName(TaggedParserAtomIndex name) const { + return getKind() == ParseNodeKind::Name && as<NameNode>().name() == name; +} + +class UnaryNode : public ParseNode { + ParseNode* kid_; + + public: + UnaryNode(ParseNodeKind kind, const TokenPos& pos, ParseNode* kid) + : ParseNode(kind, pos), kid_(kid) { + MOZ_ASSERT(is<UnaryNode>()); + } + + static bool test(const ParseNode& node) { + return node.typeCode() == TypeCode::Unary; + } + + static constexpr TypeCode classTypeCode() { return TypeCode::Unary; } + + template <typename Visitor> + bool accept(Visitor& visitor) { + if (kid_) { + if (!visitor.visit(kid_)) { + return false; + } + } + return true; + } + +#ifdef DEBUG + void dumpImpl(const ParserAtomsTable* parserAtoms, GenericPrinter& out, + int indent); +#endif + + ParseNode* kid() const { return kid_; } + + /* + * Non-null if this is a statement node which could be a member of a + * Directive Prologue: an expression statement consisting of a single + * string literal. + * + * This considers only the node and its children, not its context. After + * parsing, check the node's prologue flag to see if it is indeed part of + * a directive prologue. + * + * Note that a Directive Prologue can contain statements that cannot + * themselves be directives (string literals that include escape sequences + * or escaped newlines, say). This member function returns true for such + * nodes; we use it to determine the extent of the prologue. + */ + TaggedParserAtomIndex isStringExprStatement() const { + if (isKind(ParseNodeKind::ExpressionStmt)) { + if (kid()->isKind(ParseNodeKind::StringExpr) && !kid()->isInParens()) { + return kid()->as<NameNode>().atom(); + } + } + return TaggedParserAtomIndex::null(); + } + + // Methods used by FoldConstants.cpp. + ParseNode** unsafeKidReference() { return &kid_; } + + void setSyntheticComputedName() { pn_synthetic_computed = true; } + bool isSyntheticComputedName() { + MOZ_ASSERT(isKind(ParseNodeKind::ComputedName)); + return pn_synthetic_computed; + } +}; + +class BinaryNode : public ParseNode { + ParseNode* left_; + ParseNode* right_; + + public: + BinaryNode(ParseNodeKind kind, const TokenPos& pos, ParseNode* left, + ParseNode* right) + : ParseNode(kind, pos), left_(left), right_(right) { + MOZ_ASSERT(is<BinaryNode>()); + } + + BinaryNode(ParseNodeKind kind, ParseNode* left, ParseNode* right) + : ParseNode(kind, TokenPos::box(left->pn_pos, right->pn_pos)), + left_(left), + right_(right) { + MOZ_ASSERT(is<BinaryNode>()); + } + + static bool test(const ParseNode& node) { + return node.typeCode() == TypeCode::Binary; + } + + static constexpr TypeCode classTypeCode() { return TypeCode::Binary; } + + template <typename Visitor> + bool accept(Visitor& visitor) { + if (left_) { + if (!visitor.visit(left_)) { + return false; + } + } + if (right_) { + if (!visitor.visit(right_)) { + return false; + } + } + return true; + } + +#ifdef DEBUG + void dumpImpl(const ParserAtomsTable* parserAtoms, GenericPrinter& out, + int indent); +#endif + + ParseNode* left() const { return left_; } + + ParseNode* right() const { return right_; } + + // Methods used by FoldConstants.cpp. + // callers are responsible for keeping the list consistent. + ParseNode** unsafeLeftReference() { return &left_; } + + ParseNode** unsafeRightReference() { return &right_; } +}; + +class AssignmentNode : public BinaryNode { + public: + AssignmentNode(ParseNodeKind kind, ParseNode* left, ParseNode* right) + : BinaryNode(kind, TokenPos(left->pn_pos.begin, right->pn_pos.end), left, + right) { + MOZ_ASSERT(is<AssignmentNode>()); + } + + static bool test(const ParseNode& node) { + ParseNodeKind kind = node.getKind(); + bool match = ParseNodeKind::AssignmentStart <= kind && + kind <= ParseNodeKind::AssignmentLast; + MOZ_ASSERT_IF(match, node.is<BinaryNode>()); + return match; + } +}; + +class ForNode : public BinaryNode { + unsigned iflags_; /* JSITER_* flags */ + + public: + ForNode(const TokenPos& pos, ParseNode* forHead, ParseNode* body, + unsigned iflags) + : BinaryNode(ParseNodeKind::ForStmt, pos, forHead, body), + iflags_(iflags) { + MOZ_ASSERT(forHead->isKind(ParseNodeKind::ForIn) || + forHead->isKind(ParseNodeKind::ForOf) || + forHead->isKind(ParseNodeKind::ForHead)); + } + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::ForStmt); + MOZ_ASSERT_IF(match, node.is<BinaryNode>()); + return match; + } + + TernaryNode* head() const { return &left()->as<TernaryNode>(); } + + ParseNode* body() const { return right(); } + + unsigned iflags() const { return iflags_; } +}; + +class TernaryNode : public ParseNode { + ParseNode* kid1_; /* condition, discriminant, etc. */ + ParseNode* kid2_; /* then-part, case list, etc. */ + ParseNode* kid3_; /* else-part, default case, etc. */ + + public: + TernaryNode(ParseNodeKind kind, ParseNode* kid1, ParseNode* kid2, + ParseNode* kid3) + : TernaryNode(kind, kid1, kid2, kid3, + TokenPos((kid1 ? kid1 + : kid2 ? kid2 + : kid3) + ->pn_pos.begin, + (kid3 ? kid3 + : kid2 ? kid2 + : kid1) + ->pn_pos.end)) {} + + TernaryNode(ParseNodeKind kind, ParseNode* kid1, ParseNode* kid2, + ParseNode* kid3, const TokenPos& pos) + : ParseNode(kind, pos), kid1_(kid1), kid2_(kid2), kid3_(kid3) { + MOZ_ASSERT(is<TernaryNode>()); + } + + static bool test(const ParseNode& node) { + return node.typeCode() == TypeCode::Ternary; + } + + static constexpr TypeCode classTypeCode() { return TypeCode::Ternary; } + + template <typename Visitor> + bool accept(Visitor& visitor) { + if (kid1_) { + if (!visitor.visit(kid1_)) { + return false; + } + } + if (kid2_) { + if (!visitor.visit(kid2_)) { + return false; + } + } + if (kid3_) { + if (!visitor.visit(kid3_)) { + return false; + } + } + return true; + } + +#ifdef DEBUG + void dumpImpl(const ParserAtomsTable* parserAtoms, GenericPrinter& out, + int indent); +#endif + + ParseNode* kid1() const { return kid1_; } + + ParseNode* kid2() const { return kid2_; } + + ParseNode* kid3() const { return kid3_; } + + // Methods used by FoldConstants.cpp. + ParseNode** unsafeKid1Reference() { return &kid1_; } + + ParseNode** unsafeKid2Reference() { return &kid2_; } + + ParseNode** unsafeKid3Reference() { return &kid3_; } +}; + +class ListNode : public ParseNode { + ParseNode* head_; /* first node in list */ + ParseNode** tail_; /* ptr to last node's pn_next in list */ + uint32_t count_; /* number of nodes in list */ + uint32_t xflags; + + private: + // xflags bits. + + // Statement list has top-level function statements. + static constexpr uint32_t hasTopLevelFunctionDeclarationsBit = Bit(0); + + // Array/Object/Class initializer has non-constants. + // * array has holes + // * array has spread node + // * array has element which is known not to be constant + // * array has no element + // * object/class has __proto__ + // * object/class has property which is known not to be constant + // * object/class shorthand property + // * object/class spread property + // * object/class has method + // * object/class has computed property + static constexpr uint32_t hasNonConstInitializerBit = Bit(1); + + // Flag set by the emitter after emitting top-level function statements. + static constexpr uint32_t emittedTopLevelFunctionDeclarationsBit = Bit(2); + + public: + ListNode(ParseNodeKind kind, const TokenPos& pos) + : ParseNode(kind, pos), + head_(nullptr), + tail_(&head_), + count_(0), + xflags(0) { + MOZ_ASSERT(is<ListNode>()); + } + + ListNode(ParseNodeKind kind, ParseNode* kid) + : ParseNode(kind, kid->pn_pos), + head_(kid), + tail_(&kid->pn_next), + count_(1), + xflags(0) { + if (kid->pn_pos.begin < pn_pos.begin) { + pn_pos.begin = kid->pn_pos.begin; + } + pn_pos.end = kid->pn_pos.end; + + MOZ_ASSERT(is<ListNode>()); + } + + static bool test(const ParseNode& node) { + return node.typeCode() == TypeCode::List; + } + + static constexpr TypeCode classTypeCode() { return TypeCode::List; } + + template <typename Visitor> + bool accept(Visitor& visitor) { + ParseNode** listp = &head_; + for (; *listp; listp = &(*listp)->pn_next) { + // Don't use reference because we want to check if it changed, so we can + // use ReplaceNode + ParseNode* pn = *listp; + if (!visitor.visit(pn)) { + return false; + } + if (pn != *listp) { + ReplaceNode(listp, pn); + } + } + unsafeReplaceTail(listp); + return true; + } + +#ifdef DEBUG + void dumpImpl(const ParserAtomsTable* parserAtoms, GenericPrinter& out, + int indent); +#endif + + ParseNode* head() const { return head_; } + + ParseNode** tail() const { return tail_; } + + uint32_t count() const { return count_; } + + bool empty() const { return count() == 0; } + + void checkConsistency() const +#ifndef DEBUG + {} +#endif + ; + + [[nodiscard]] bool hasTopLevelFunctionDeclarations() const { + MOZ_ASSERT(isKind(ParseNodeKind::StatementList)); + return xflags & hasTopLevelFunctionDeclarationsBit; + } + + [[nodiscard]] bool emittedTopLevelFunctionDeclarations() const { + MOZ_ASSERT(isKind(ParseNodeKind::StatementList)); + MOZ_ASSERT(hasTopLevelFunctionDeclarations()); + return xflags & emittedTopLevelFunctionDeclarationsBit; + } + + [[nodiscard]] bool hasNonConstInitializer() const { + MOZ_ASSERT(isKind(ParseNodeKind::ArrayExpr) || + isKind(ParseNodeKind::ObjectExpr)); + return xflags & hasNonConstInitializerBit; + } + + void setHasTopLevelFunctionDeclarations() { + MOZ_ASSERT(isKind(ParseNodeKind::StatementList)); + xflags |= hasTopLevelFunctionDeclarationsBit; + } + + void setEmittedTopLevelFunctionDeclarations() { + MOZ_ASSERT(isKind(ParseNodeKind::StatementList)); + MOZ_ASSERT(hasTopLevelFunctionDeclarations()); + xflags |= emittedTopLevelFunctionDeclarationsBit; + } + + void setHasNonConstInitializer() { + MOZ_ASSERT(isKind(ParseNodeKind::ArrayExpr) || + isKind(ParseNodeKind::ObjectExpr) || + IF_RECORD_TUPLE(isKind(ParseNodeKind::TupleExpr), false) || + IF_RECORD_TUPLE(isKind(ParseNodeKind::RecordExpr), false)); + xflags |= hasNonConstInitializerBit; + } + + void unsetHasNonConstInitializer() { + MOZ_ASSERT(isKind(ParseNodeKind::ArrayExpr) || + isKind(ParseNodeKind::ObjectExpr) || + IF_RECORD_TUPLE(isKind(ParseNodeKind::TupleExpr), false) || + IF_RECORD_TUPLE(isKind(ParseNodeKind::RecordExpr), false)); + xflags &= ~hasNonConstInitializerBit; + } + + /* + * Compute a pointer to the last element in a singly-linked list. NB: list + * must be non-empty -- this is asserted! + */ + ParseNode* last() const { + MOZ_ASSERT(!empty()); + // + // ParseNode ParseNode + // +-----+---------+-----+ +-----+---------+-----+ + // | ... | pn_next | ... | +-...->| ... | pn_next | ... | + // +-----+---------+-----+ | +-----+---------+-----+ + // ^ | | ^ ^ + // | +---------------+ | | + // | | tail() + // | | + // head() last() + // + return (ParseNode*)(uintptr_t(tail()) - offsetof(ParseNode, pn_next)); + } + + void replaceLast(ParseNode* node) { + MOZ_ASSERT(!empty()); + pn_pos.end = node->pn_pos.end; + + ParseNode* item = head(); + ParseNode* lastNode = last(); + MOZ_ASSERT(item); + if (item == lastNode) { + head_ = node; + } else { + while (item->pn_next != lastNode) { + MOZ_ASSERT(item->pn_next); + item = item->pn_next; + } + item->pn_next = node; + } + tail_ = &node->pn_next; + } + + void append(ParseNode* item) { + MOZ_ASSERT(item->pn_pos.begin >= pn_pos.begin); + pn_pos.end = item->pn_pos.end; + *tail_ = item; + tail_ = &item->pn_next; + count_++; + } + + void prepend(ParseNode* item) { + item->pn_next = head_; + head_ = item; + if (tail_ == &head_) { + tail_ = &item->pn_next; + } + count_++; + } + + // Methods used by FoldConstants.cpp. + // Caller is responsible for keeping the list consistent. + ParseNode** unsafeHeadReference() { return &head_; } + + void unsafeReplaceTail(ParseNode** newTail) { + tail_ = newTail; + checkConsistency(); + } + + void unsafeDecrementCount() { + MOZ_ASSERT(count() > 1); + count_--; + } + + private: + // Classes to iterate over ListNode contents: + // + // Usage: + // ListNode* list; + // for (ParseNode* item : list->contents()) { + // // item is ParseNode* typed. + // } + class iterator { + private: + ParseNode* node_; + + friend class ListNode; + explicit iterator(ParseNode* node) : node_(node) {} + + public: + // Implement std::iterator_traits. + using iterator_category = std::input_iterator_tag; + using value_type = ParseNode*; + using difference_type = ptrdiff_t; + using pointer = ParseNode**; + using reference = ParseNode*&; + + bool operator==(const iterator& other) const { + return node_ == other.node_; + } + + bool operator!=(const iterator& other) const { return !(*this == other); } + + iterator& operator++() { + node_ = node_->pn_next; + return *this; + } + + ParseNode* operator*() { return node_; } + + const ParseNode* operator*() const { return node_; } + }; + + class range { + private: + ParseNode* begin_; + ParseNode* end_; + + friend class ListNode; + range(ParseNode* begin, ParseNode* end) : begin_(begin), end_(end) {} + + public: + iterator begin() { return iterator(begin_); } + + iterator end() { return iterator(end_); } + + const iterator begin() const { return iterator(begin_); } + + const iterator end() const { return iterator(end_); } + + const iterator cbegin() const { return begin(); } + + const iterator cend() const { return end(); } + }; + +#ifdef DEBUG + [[nodiscard]] bool contains(ParseNode* target) const { + MOZ_ASSERT(target); + for (ParseNode* node : contents()) { + if (target == node) { + return true; + } + } + return false; + } +#endif + + public: + range contents() { return range(head(), nullptr); } + + const range contents() const { return range(head(), nullptr); } + + range contentsFrom(ParseNode* begin) { + MOZ_ASSERT_IF(begin, contains(begin)); + return range(begin, nullptr); + } + + const range contentsFrom(ParseNode* begin) const { + MOZ_ASSERT_IF(begin, contains(begin)); + return range(begin, nullptr); + } + + range contentsTo(ParseNode* end) { + MOZ_ASSERT_IF(end, contains(end)); + return range(head(), end); + } + + const range contentsTo(ParseNode* end) const { + MOZ_ASSERT_IF(end, contains(end)); + return range(head(), end); + } +}; + +class DeclarationListNode : public ListNode { + public: + DeclarationListNode(ParseNodeKind kind, const TokenPos& pos) + : ListNode(kind, pos) { + MOZ_ASSERT(is<DeclarationListNode>()); + } + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::VarStmt) || + node.isKind(ParseNodeKind::LetDecl) || + node.isKind(ParseNodeKind::ConstDecl); + MOZ_ASSERT_IF(match, node.is<ListNode>()); + return match; + } + + auto* singleBinding() const { + MOZ_ASSERT(count() == 1); + return head(); + } +}; + +class ParamsBodyNode : public ListNode { + public: + explicit ParamsBodyNode(const TokenPos& pos) + : ListNode(ParseNodeKind::ParamsBody, pos) { + MOZ_ASSERT(is<ParamsBodyNode>()); + } + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::ParamsBody); + MOZ_ASSERT_IF(match, node.is<ListNode>()); + return match; + } + + auto parameters() const { + MOZ_ASSERT(last()->is<LexicalScopeNode>()); + return contentsTo(last()); + } + + auto* body() const { + MOZ_ASSERT(last()->is<LexicalScopeNode>()); + return &last()->as<LexicalScopeNode>(); + } +}; + +class FunctionNode : public ParseNode { + FunctionBox* funbox_; + ParseNode* body_; + FunctionSyntaxKind syntaxKind_; + + public: + FunctionNode(FunctionSyntaxKind syntaxKind, const TokenPos& pos) + : ParseNode(ParseNodeKind::Function, pos), + funbox_(nullptr), + body_(nullptr), + syntaxKind_(syntaxKind) { + MOZ_ASSERT(!body_); + MOZ_ASSERT(!funbox_); + MOZ_ASSERT(is<FunctionNode>()); + } + + static bool test(const ParseNode& node) { + return node.isKind(ParseNodeKind::Function); + } + + static constexpr TypeCode classTypeCode() { return TypeCode::Other; } + + template <typename Visitor> + bool accept(Visitor& visitor) { + // Note: body is null for lazily-parsed functions. + if (body_) { + if (!visitor.visit(body_)) { + return false; + } + MOZ_ASSERT(body_->is<ParamsBodyNode>()); + } + return true; + } + +#ifdef DEBUG + void dumpImpl(const ParserAtomsTable* parserAtoms, GenericPrinter& out, + int indent); +#endif + + FunctionBox* funbox() const { return funbox_; } + + ParamsBodyNode* body() const { + return body_ ? &body_->as<ParamsBodyNode>() : nullptr; + } + + void setFunbox(FunctionBox* funbox) { funbox_ = funbox; } + + void setBody(ParamsBodyNode* body) { body_ = body; } + + FunctionSyntaxKind syntaxKind() const { return syntaxKind_; } + + bool functionIsHoisted() const { + return syntaxKind() == FunctionSyntaxKind::Statement; + } +}; + +class ModuleNode : public ParseNode { + ParseNode* body_; + + public: + explicit ModuleNode(const TokenPos& pos) + : ParseNode(ParseNodeKind::Module, pos), body_(nullptr) { + MOZ_ASSERT(!body_); + MOZ_ASSERT(is<ModuleNode>()); + } + + static bool test(const ParseNode& node) { + return node.isKind(ParseNodeKind::Module); + } + + static constexpr TypeCode classTypeCode() { return TypeCode::Other; } + + template <typename Visitor> + bool accept(Visitor& visitor) { + return visitor.visit(body_); + } + +#ifdef DEBUG + void dumpImpl(const ParserAtomsTable* parserAtoms, GenericPrinter& out, + int indent); +#endif + + ListNode* body() const { return &body_->as<ListNode>(); } + + void setBody(ListNode* body) { body_ = body; } +}; + +class NumericLiteral : public ParseNode { + double value_; /* aligned numeric literal value */ + DecimalPoint decimalPoint_; /* Whether the number has a decimal point */ + + public: + NumericLiteral(double value, DecimalPoint decimalPoint, const TokenPos& pos) + : ParseNode(ParseNodeKind::NumberExpr, pos), + value_(value), + decimalPoint_(decimalPoint) {} + + static bool test(const ParseNode& node) { + return node.isKind(ParseNodeKind::NumberExpr); + } + + static constexpr TypeCode classTypeCode() { return TypeCode::Other; } + + template <typename Visitor> + bool accept(Visitor& visitor) { + return true; + } + +#ifdef DEBUG + void dumpImpl(const ParserAtomsTable* parserAtoms, GenericPrinter& out, + int indent); +#endif + + double value() const { return value_; } + + DecimalPoint decimalPoint() const { return decimalPoint_; } + + // Return the decimal string representation of this numeric literal. + TaggedParserAtomIndex toAtom(FrontendContext* fc, + ParserAtomsTable& parserAtoms) const; +}; + +class BigIntLiteral : public ParseNode { + BigIntIndex index_; + bool isZero_; + + public: + BigIntLiteral(BigIntIndex index, bool isZero, const TokenPos& pos) + : ParseNode(ParseNodeKind::BigIntExpr, pos), + index_(index), + isZero_(isZero) {} + + static bool test(const ParseNode& node) { + return node.isKind(ParseNodeKind::BigIntExpr); + } + + static constexpr TypeCode classTypeCode() { return TypeCode::Other; } + + template <typename Visitor> + bool accept(Visitor& visitor) { + return true; + } + +#ifdef DEBUG + void dumpImpl(const ParserAtomsTable* parserAtoms, GenericPrinter& out, + int indent); +#endif + + BigIntIndex index() { return index_; } + + bool isZero() const { return isZero_; } +}; + +template <ParseNodeKind NodeKind, typename ScopeType> +class BaseScopeNode : public ParseNode { + using ParserData = typename ScopeType::ParserData; + ParserData* bindings; + ParseNode* body; + ScopeKind kind_; + + public: + BaseScopeNode(ParserData* bindings, ParseNode* body, + ScopeKind kind = ScopeKind::Lexical) + : ParseNode(NodeKind, body->pn_pos), + bindings(bindings), + body(body), + kind_(kind) {} + + static bool test(const ParseNode& node) { return node.isKind(NodeKind); } + + static constexpr TypeCode classTypeCode() { return TypeCode::Other; } + + template <typename Visitor> + bool accept(Visitor& visitor) { + return visitor.visit(body); + } + +#ifdef DEBUG + void dumpImpl(const ParserAtomsTable* parserAtoms, GenericPrinter& out, + int indent); +#endif + + ParserData* scopeBindings() const { + MOZ_ASSERT(!isEmptyScope()); + return bindings; + } + + ParseNode* scopeBody() const { return body; } + + void setScopeBody(ParseNode* body) { this->body = body; } + + bool isEmptyScope() const { return !bindings; } + + ScopeKind kind() const { return kind_; } +}; + +class LexicalScopeNode + : public BaseScopeNode<ParseNodeKind::LexicalScope, LexicalScope> { + public: + LexicalScopeNode(LexicalScope::ParserData* bindings, ParseNode* body, + ScopeKind kind = ScopeKind::Lexical) + : BaseScopeNode(bindings, body, kind) {} +}; + +class ClassBodyScopeNode + : public BaseScopeNode<ParseNodeKind::ClassBodyScope, ClassBodyScope> { + public: + ClassBodyScopeNode(ClassBodyScope::ParserData* bindings, ListNode* memberList) + : BaseScopeNode(bindings, memberList, ScopeKind::ClassBody) { + MOZ_ASSERT(memberList->isKind(ParseNodeKind::ClassMemberList)); + } + + ListNode* memberList() const { + ListNode* list = &scopeBody()->as<ListNode>(); + MOZ_ASSERT(list->isKind(ParseNodeKind::ClassMemberList)); + return list; + } +}; + +class LabeledStatement : public NameNode { + ParseNode* statement_; + + public: + LabeledStatement(TaggedParserAtomIndex label, ParseNode* stmt, uint32_t begin) + : NameNode(ParseNodeKind::LabelStmt, label, + TokenPos(begin, stmt->pn_pos.end)), + statement_(stmt) {} + + TaggedParserAtomIndex label() const { return atom(); } + + ParseNode* statement() const { return statement_; } + + static bool test(const ParseNode& node) { + return node.isKind(ParseNodeKind::LabelStmt); + } + + template <typename Visitor> + bool accept(Visitor& visitor) { + if (statement_) { + if (!visitor.visit(statement_)) { + return false; + } + } + return true; + } + +#ifdef DEBUG + void dumpImpl(const ParserAtomsTable* parserAtoms, GenericPrinter& out, + int indent); +#endif +}; + +// Inside a switch statement, a CaseClause is a case-label and the subsequent +// statements. The same node type is used for DefaultClauses. The only +// difference is that their caseExpression() is null. +class CaseClause : public BinaryNode { + public: + CaseClause(ParseNode* expr, ParseNode* stmts, uint32_t begin) + : BinaryNode(ParseNodeKind::Case, TokenPos(begin, stmts->pn_pos.end), + expr, stmts) {} + + ParseNode* caseExpression() const { return left(); } + + bool isDefault() const { return !caseExpression(); } + + ListNode* statementList() const { return &right()->as<ListNode>(); } + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::Case); + MOZ_ASSERT_IF(match, node.is<BinaryNode>()); + return match; + } +}; + +class LoopControlStatement : public ParseNode { + TaggedParserAtomIndex label_; /* target of break/continue statement */ + + protected: + LoopControlStatement(ParseNodeKind kind, TaggedParserAtomIndex label, + const TokenPos& pos) + : ParseNode(kind, pos), label_(label) { + MOZ_ASSERT(kind == ParseNodeKind::BreakStmt || + kind == ParseNodeKind::ContinueStmt); + MOZ_ASSERT(is<LoopControlStatement>()); + } + + public: + /* Label associated with this break/continue statement, if any. */ + TaggedParserAtomIndex label() const { return label_; } + +#ifdef DEBUG + void dumpImpl(const ParserAtomsTable* parserAtoms, GenericPrinter& out, + int indent); +#endif + + static bool test(const ParseNode& node) { + return node.isKind(ParseNodeKind::BreakStmt) || + node.isKind(ParseNodeKind::ContinueStmt); + } + + static constexpr TypeCode classTypeCode() { return TypeCode::Other; } + + template <typename Visitor> + bool accept(Visitor& visitor) { + return true; + } +}; + +class BreakStatement : public LoopControlStatement { + public: + BreakStatement(TaggedParserAtomIndex label, const TokenPos& pos) + : LoopControlStatement(ParseNodeKind::BreakStmt, label, pos) {} + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::BreakStmt); + MOZ_ASSERT_IF(match, node.is<LoopControlStatement>()); + return match; + } +}; + +class ContinueStatement : public LoopControlStatement { + public: + ContinueStatement(TaggedParserAtomIndex label, const TokenPos& pos) + : LoopControlStatement(ParseNodeKind::ContinueStmt, label, pos) {} + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::ContinueStmt); + MOZ_ASSERT_IF(match, node.is<LoopControlStatement>()); + return match; + } +}; + +class DebuggerStatement : public NullaryNode { + public: + explicit DebuggerStatement(const TokenPos& pos) + : NullaryNode(ParseNodeKind::DebuggerStmt, pos) {} + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::DebuggerStmt); + MOZ_ASSERT_IF(match, node.is<NullaryNode>()); + return match; + } +}; + +class ConditionalExpression : public TernaryNode { + public: + ConditionalExpression(ParseNode* condition, ParseNode* thenExpr, + ParseNode* elseExpr) + : TernaryNode(ParseNodeKind::ConditionalExpr, condition, thenExpr, + elseExpr, + TokenPos(condition->pn_pos.begin, elseExpr->pn_pos.end)) { + MOZ_ASSERT(condition); + MOZ_ASSERT(thenExpr); + MOZ_ASSERT(elseExpr); + } + + ParseNode& condition() const { return *kid1(); } + + ParseNode& thenExpression() const { return *kid2(); } + + ParseNode& elseExpression() const { return *kid3(); } + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::ConditionalExpr); + MOZ_ASSERT_IF(match, node.is<TernaryNode>()); + return match; + } +}; + +class TryNode : public TernaryNode { + public: + TryNode(uint32_t begin, ParseNode* body, LexicalScopeNode* catchScope, + ParseNode* finallyBlock) + : TernaryNode( + ParseNodeKind::TryStmt, body, catchScope, finallyBlock, + TokenPos(begin, + (finallyBlock ? finallyBlock : catchScope)->pn_pos.end)) { + MOZ_ASSERT(body); + MOZ_ASSERT(catchScope || finallyBlock); + } + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::TryStmt); + MOZ_ASSERT_IF(match, node.is<TernaryNode>()); + return match; + } + + ParseNode* body() const { return kid1(); } + + LexicalScopeNode* catchScope() const { + return kid2() ? &kid2()->as<LexicalScopeNode>() : nullptr; + } + + ParseNode* finallyBlock() const { return kid3(); } +}; + +class ThisLiteral : public UnaryNode { + public: + ThisLiteral(const TokenPos& pos, ParseNode* thisName) + : UnaryNode(ParseNodeKind::ThisExpr, pos, thisName) {} + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::ThisExpr); + MOZ_ASSERT_IF(match, node.is<UnaryNode>()); + return match; + } +}; + +class NullLiteral : public NullaryNode { + public: + explicit NullLiteral(const TokenPos& pos) + : NullaryNode(ParseNodeKind::NullExpr, pos) {} + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::NullExpr); + MOZ_ASSERT_IF(match, node.is<NullaryNode>()); + return match; + } +}; + +// This is only used internally, currently just for tagged templates and the +// initial value of fields without initializers. It represents the value +// 'undefined' (aka `void 0`), like NullLiteral represents the value 'null'. +class RawUndefinedLiteral : public NullaryNode { + public: + explicit RawUndefinedLiteral(const TokenPos& pos) + : NullaryNode(ParseNodeKind::RawUndefinedExpr, pos) {} + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::RawUndefinedExpr); + MOZ_ASSERT_IF(match, node.is<NullaryNode>()); + return match; + } +}; + +class BooleanLiteral : public NullaryNode { + public: + BooleanLiteral(bool b, const TokenPos& pos) + : NullaryNode(b ? ParseNodeKind::TrueExpr : ParseNodeKind::FalseExpr, + pos) {} + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::TrueExpr) || + node.isKind(ParseNodeKind::FalseExpr); + MOZ_ASSERT_IF(match, node.is<NullaryNode>()); + return match; + } +}; + +class RegExpLiteral : public ParseNode { + RegExpIndex index_; + + public: + RegExpLiteral(RegExpIndex dataIndex, const TokenPos& pos) + : ParseNode(ParseNodeKind::RegExpExpr, pos), index_(dataIndex) {} + + // Create a RegExp object of this RegExp literal. + RegExpObject* create(JSContext* cx, FrontendContext* fc, + ParserAtomsTable& parserAtoms, + CompilationAtomCache& atomCache, + ExtensibleCompilationStencil& stencil) const; + +#ifdef DEBUG + void dumpImpl(const ParserAtomsTable* parserAtoms, GenericPrinter& out, + int indent); +#endif + + static bool test(const ParseNode& node) { + return node.isKind(ParseNodeKind::RegExpExpr); + } + + static constexpr TypeCode classTypeCode() { return TypeCode::Other; } + + template <typename Visitor> + bool accept(Visitor& visitor) { + return true; + } + + RegExpIndex index() { return index_; } +}; + +class PropertyAccessBase : public BinaryNode { + public: + /* + * PropertyAccess nodes can have any expression/'super' as left-hand + * side, but the name must be a ParseNodeKind::PropertyName node. + */ + PropertyAccessBase(ParseNodeKind kind, ParseNode* lhs, NameNode* name, + uint32_t begin, uint32_t end) + : BinaryNode(kind, TokenPos(begin, end), lhs, name) { + MOZ_ASSERT(lhs); + MOZ_ASSERT(name); + } + + ParseNode& expression() const { return *left(); } + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::DotExpr) || + node.isKind(ParseNodeKind::OptionalDotExpr); + MOZ_ASSERT_IF(match, node.is<BinaryNode>()); + MOZ_ASSERT_IF(match, node.as<BinaryNode>().right()->isKind( + ParseNodeKind::PropertyNameExpr)); + return match; + } + + NameNode& key() const { return right()->as<NameNode>(); } + + // Method used by BytecodeEmitter::emitPropLHS for optimization. + // Those methods allow expression to temporarily be nullptr for + // optimization purpose. + ParseNode* maybeExpression() const { return left(); } + + void setExpression(ParseNode* pn) { *unsafeLeftReference() = pn; } + + TaggedParserAtomIndex name() const { return right()->as<NameNode>().atom(); } +}; + +class PropertyAccess : public PropertyAccessBase { + public: + PropertyAccess(ParseNode* lhs, NameNode* name, uint32_t begin, uint32_t end) + : PropertyAccessBase(ParseNodeKind::DotExpr, lhs, name, begin, end) { + MOZ_ASSERT(lhs); + MOZ_ASSERT(name); + } + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::DotExpr); + MOZ_ASSERT_IF(match, node.is<PropertyAccessBase>()); + return match; + } + + bool isSuper() const { + // ParseNodeKind::SuperBase cannot result from any expression syntax. + return expression().isKind(ParseNodeKind::SuperBase); + } +}; + +class OptionalPropertyAccess : public PropertyAccessBase { + public: + OptionalPropertyAccess(ParseNode* lhs, NameNode* name, uint32_t begin, + uint32_t end) + : PropertyAccessBase(ParseNodeKind::OptionalDotExpr, lhs, name, begin, + end) { + MOZ_ASSERT(lhs); + MOZ_ASSERT(name); + } + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::OptionalDotExpr); + MOZ_ASSERT_IF(match, node.is<PropertyAccessBase>()); + return match; + } +}; + +class PropertyByValueBase : public BinaryNode { + public: + PropertyByValueBase(ParseNodeKind kind, ParseNode* lhs, ParseNode* propExpr, + uint32_t begin, uint32_t end) + : BinaryNode(kind, TokenPos(begin, end), lhs, propExpr) {} + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::ElemExpr) || + node.isKind(ParseNodeKind::OptionalElemExpr); + MOZ_ASSERT_IF(match, node.is<BinaryNode>()); + return match; + } + + ParseNode& expression() const { return *left(); } + + ParseNode& key() const { return *right(); } +}; + +class PropertyByValue : public PropertyByValueBase { + public: + PropertyByValue(ParseNode* lhs, ParseNode* propExpr, uint32_t begin, + uint32_t end) + : PropertyByValueBase(ParseNodeKind::ElemExpr, lhs, propExpr, begin, + end) {} + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::ElemExpr); + MOZ_ASSERT_IF(match, node.is<PropertyByValueBase>()); + return match; + } + + bool isSuper() const { return left()->isKind(ParseNodeKind::SuperBase); } +}; + +class OptionalPropertyByValue : public PropertyByValueBase { + public: + OptionalPropertyByValue(ParseNode* lhs, ParseNode* propExpr, uint32_t begin, + uint32_t end) + : PropertyByValueBase(ParseNodeKind::OptionalElemExpr, lhs, propExpr, + begin, end) {} + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::OptionalElemExpr); + MOZ_ASSERT_IF(match, node.is<PropertyByValueBase>()); + return match; + } +}; + +class PrivateMemberAccessBase : public BinaryNode { + public: + PrivateMemberAccessBase(ParseNodeKind kind, ParseNode* lhs, NameNode* name, + uint32_t begin, uint32_t end) + : BinaryNode(kind, TokenPos(begin, end), lhs, name) { + MOZ_ASSERT(name->isKind(ParseNodeKind::PrivateName)); + } + + ParseNode& expression() const { return *left(); } + + NameNode& privateName() const { + NameNode& name = right()->as<NameNode>(); + MOZ_ASSERT(name.isKind(ParseNodeKind::PrivateName)); + return name; + } + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::PrivateMemberExpr) || + node.isKind(ParseNodeKind::OptionalPrivateMemberExpr); + MOZ_ASSERT_IF(match, node.is<BinaryNode>()); + MOZ_ASSERT_IF(match, node.as<BinaryNode>().right()->isKind( + ParseNodeKind::PrivateName)); + return match; + } +}; + +class PrivateMemberAccess : public PrivateMemberAccessBase { + public: + PrivateMemberAccess(ParseNode* lhs, NameNode* name, uint32_t begin, + uint32_t end) + : PrivateMemberAccessBase(ParseNodeKind::PrivateMemberExpr, lhs, name, + begin, end) {} + + static bool test(const ParseNode& node) { + return node.isKind(ParseNodeKind::PrivateMemberExpr); + } +}; + +class OptionalPrivateMemberAccess : public PrivateMemberAccessBase { + public: + OptionalPrivateMemberAccess(ParseNode* lhs, NameNode* name, uint32_t begin, + uint32_t end) + : PrivateMemberAccessBase(ParseNodeKind::OptionalPrivateMemberExpr, lhs, + name, begin, end) {} + + static bool test(const ParseNode& node) { + return node.isKind(ParseNodeKind::OptionalPrivateMemberExpr); + } +}; + +class NewTargetNode : public TernaryNode { + public: + NewTargetNode(NullaryNode* newHolder, NullaryNode* targetHolder, + NameNode* newTargetName) + : TernaryNode(ParseNodeKind::NewTargetExpr, newHolder, targetHolder, + newTargetName) {} + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::NewTargetExpr); + MOZ_ASSERT_IF(match, node.is<TernaryNode>()); + return match; + } + + auto* newHolder() const { return &kid1()->as<NullaryNode>(); } + auto* targetHolder() const { return &kid2()->as<NullaryNode>(); } + auto* newTargetName() const { return &kid3()->as<NameNode>(); } +}; + +/* + * A CallSiteNode represents the implicit call site object argument in a + * TaggedTemplate. + */ +class CallSiteNode : public ListNode { + public: + explicit CallSiteNode(uint32_t begin) + : ListNode(ParseNodeKind::CallSiteObj, TokenPos(begin, begin + 1)) {} + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::CallSiteObj); + MOZ_ASSERT_IF(match, node.is<ListNode>()); + return match; + } + + ListNode* rawNodes() const { + MOZ_ASSERT(head()); + return &head()->as<ListNode>(); + } +}; + +class CallNode : public BinaryNode { + const JSOp callOp_; + + public: + CallNode(ParseNodeKind kind, JSOp callOp, ParseNode* left, ListNode* right) + : CallNode(kind, callOp, TokenPos(left->pn_pos.begin, right->pn_pos.end), + left, right) {} + + CallNode(ParseNodeKind kind, JSOp callOp, TokenPos pos, ParseNode* left, + ListNode* right) + : BinaryNode(kind, pos, left, right), callOp_(callOp) { + MOZ_ASSERT(is<CallNode>()); + } + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::CallExpr) || + node.isKind(ParseNodeKind::SuperCallExpr) || + node.isKind(ParseNodeKind::OptionalCallExpr) || + node.isKind(ParseNodeKind::TaggedTemplateExpr) || + node.isKind(ParseNodeKind::NewExpr); + MOZ_ASSERT_IF(match, node.is<BinaryNode>()); + return match; + } + + JSOp callOp() const { return callOp_; } + auto* callee() const { return left(); } + auto* args() const { return &right()->as<ListNode>(); } +}; + +class ClassMethod : public BinaryNode { + using Base = BinaryNode; + + bool isStatic_; + AccessorType accessorType_; + FunctionNode* initializerIfPrivate_; + +#ifdef ENABLE_DECORATORS + ListNode* decorators_; +#endif + + public: + /* + * Method definitions often keep a name and function body that overlap, + * so explicitly define the beginning and end here. + */ + ClassMethod(ParseNodeKind kind, ParseNode* name, ParseNode* body, + AccessorType accessorType, bool isStatic, + FunctionNode* initializerIfPrivate +#ifdef ENABLE_DECORATORS + , + ListNode* decorators +#endif + ) + : BinaryNode(kind, TokenPos(name->pn_pos.begin, body->pn_pos.end), name, + body), + isStatic_(isStatic), + accessorType_(accessorType), + initializerIfPrivate_(initializerIfPrivate) +#ifdef ENABLE_DECORATORS + , + decorators_(decorators) +#endif + { + MOZ_ASSERT(kind == ParseNodeKind::DefaultConstructor || + kind == ParseNodeKind::ClassMethod); + } + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::DefaultConstructor) || + node.isKind(ParseNodeKind::ClassMethod); + MOZ_ASSERT_IF(match, node.is<BinaryNode>()); + return match; + } + + ParseNode& name() const { return *left(); } + + FunctionNode& method() const { return right()->as<FunctionNode>(); } + + bool isStatic() const { return isStatic_; } + + AccessorType accessorType() const { return accessorType_; } + + FunctionNode* initializerIfPrivate() const { return initializerIfPrivate_; } + +#ifdef ENABLE_DECORATORS + ListNode* decorators() const { return decorators_; } + +# ifdef DEBUG + void dumpImpl(const ParserAtomsTable* parserAtoms, GenericPrinter& out, + int indent); +# endif +#endif +}; + +class ClassField : public BinaryNode { + using Base = BinaryNode; + + bool isStatic_; +#ifdef ENABLE_DECORATORS + // The accessorGetterNode_ and accessorSetterNode_ are used to store the + // getter and setter synthesized by the `accessor` keyword when they are + // decorated. Otherwise, they are null. + // + // In most cases, the accessors are not added to the class members, and the + // code generation occurs immediately prior to the decorator running. For + // non-static private methods, the accessors are added to the class members + // which causes them to be stored in lexical variables. The references here + // are used to store the names of the accessors to look up the values of these + // variables during bytecode generation. + ClassMethod* accessorGetterNode_; + ClassMethod* accessorSetterNode_; + ListNode* decorators_; +#endif + + public: + ClassField(ParseNode* name, ParseNode* initializer, bool isStatic +#ifdef ENABLE_DECORATORS + , + ListNode* decorators, ClassMethod* accessorGetterNode, + ClassMethod* accessorSetterNode +#endif + ) + : BinaryNode(ParseNodeKind::ClassField, initializer->pn_pos, name, + initializer), + isStatic_(isStatic) +#ifdef ENABLE_DECORATORS + , + accessorGetterNode_(accessorGetterNode), + accessorSetterNode_(accessorSetterNode), + decorators_(decorators) +#endif + { +#ifdef ENABLE_DECORATORS + MOZ_ASSERT((accessorGetterNode_ == nullptr) == + (accessorSetterNode_ == nullptr)); +#endif + } + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::ClassField); + MOZ_ASSERT_IF(match, node.is<BinaryNode>()); + return match; + } + + ParseNode& name() const { return *left(); } + + FunctionNode* initializer() const { return &right()->as<FunctionNode>(); } + + bool isStatic() const { return isStatic_; } + +#ifdef ENABLE_DECORATORS + ListNode* decorators() const { return decorators_; } + bool hasAccessor() const { + return accessorGetterNode_ != nullptr && accessorSetterNode_ != nullptr; + } + ClassMethod* accessorGetterNode() { return accessorGetterNode_; } + ClassMethod* accessorSetterNode() { return accessorSetterNode_; } + +# ifdef DEBUG + void dumpImpl(const ParserAtomsTable* parserAtoms, GenericPrinter& out, + int indent); +# endif +#endif +}; + +// Hold onto the function generated for a class static block like +// +// class A { +// static { /* this static block */ } +// } +// +class StaticClassBlock : public UnaryNode { + public: + explicit StaticClassBlock(FunctionNode* function) + : UnaryNode(ParseNodeKind::StaticClassBlock, function->pn_pos, function) { + } + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::StaticClassBlock); + MOZ_ASSERT_IF(match, node.is<UnaryNode>()); + return match; + } + FunctionNode* function() const { return &kid()->as<FunctionNode>(); } +}; + +class PropertyDefinition : public BinaryNode { + AccessorType accessorType_; + + public: + PropertyDefinition(ParseNode* name, ParseNode* value, + AccessorType accessorType) + : BinaryNode(ParseNodeKind::PropertyDefinition, + TokenPos(name->pn_pos.begin, value->pn_pos.end), name, + value), + accessorType_(accessorType) {} + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::PropertyDefinition); + MOZ_ASSERT_IF(match, node.is<BinaryNode>()); + return match; + } + + AccessorType accessorType() { return accessorType_; } +}; + +class SwitchStatement : public BinaryNode { + bool hasDefault_; /* only for ParseNodeKind::Switch */ + + public: + SwitchStatement(uint32_t begin, ParseNode* discriminant, + LexicalScopeNode* lexicalForCaseList, bool hasDefault) + : BinaryNode(ParseNodeKind::SwitchStmt, + TokenPos(begin, lexicalForCaseList->pn_pos.end), + discriminant, lexicalForCaseList), + hasDefault_(hasDefault) { +#ifdef DEBUG + ListNode* cases = &lexicalForCaseList->scopeBody()->as<ListNode>(); + MOZ_ASSERT(cases->isKind(ParseNodeKind::StatementList)); + bool found = false; + for (ParseNode* item : cases->contents()) { + CaseClause* caseNode = &item->as<CaseClause>(); + if (caseNode->isDefault()) { + found = true; + break; + } + } + MOZ_ASSERT(found == hasDefault); +#endif + } + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::SwitchStmt); + MOZ_ASSERT_IF(match, node.is<BinaryNode>()); + return match; + } + + ParseNode& discriminant() const { return *left(); } + + LexicalScopeNode& lexicalForCaseList() const { + return right()->as<LexicalScopeNode>(); + } + + bool hasDefault() const { return hasDefault_; } +}; + +class ClassNames : public BinaryNode { + public: + ClassNames(ParseNode* outerBinding, ParseNode* innerBinding, + const TokenPos& pos) + : BinaryNode(ParseNodeKind::ClassNames, pos, outerBinding, innerBinding) { + MOZ_ASSERT_IF(outerBinding, outerBinding->isKind(ParseNodeKind::Name)); + MOZ_ASSERT(innerBinding->isKind(ParseNodeKind::Name)); + MOZ_ASSERT_IF(outerBinding, innerBinding->as<NameNode>().atom() == + outerBinding->as<NameNode>().atom()); + } + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::ClassNames); + MOZ_ASSERT_IF(match, node.is<BinaryNode>()); + return match; + } + + /* + * Classes require two definitions: The first "outer" binding binds the + * class into the scope in which it was declared. the outer binding is a + * mutable lexial binding. The second "inner" binding binds the class by + * name inside a block in which the methods are evaulated. It is immutable, + * giving the methods access to the static members of the class even if + * the outer binding has been overwritten. + */ + NameNode* outerBinding() const { + if (ParseNode* binding = left()) { + return &binding->as<NameNode>(); + } + return nullptr; + } + + NameNode* innerBinding() const { return &right()->as<NameNode>(); } +}; + +class ClassNode : public TernaryNode { + using Base = TernaryNode; + + private: + LexicalScopeNode* innerScope() const { + return &kid3()->as<LexicalScopeNode>(); + } + + ClassBodyScopeNode* bodyScope() const { + return &innerScope()->scopeBody()->as<ClassBodyScopeNode>(); + } + +#ifdef ENABLE_DECORATORS + ListNode* decorators_; + FunctionNode* addInitializerFunction_; +#endif + + public: + ClassNode(ParseNode* names, ParseNode* heritage, + LexicalScopeNode* memberBlock, +#ifdef ENABLE_DECORATORS + ListNode* decorators, FunctionNode* addInitializerFunction, +#endif + const TokenPos& pos) + : TernaryNode(ParseNodeKind::ClassDecl, names, heritage, memberBlock, pos) +#ifdef ENABLE_DECORATORS + , + decorators_(decorators), + addInitializerFunction_(addInitializerFunction) +#endif + { + MOZ_ASSERT(innerScope()->scopeBody()->is<ClassBodyScopeNode>()); + MOZ_ASSERT_IF(names, names->is<ClassNames>()); + } + + static bool test(const ParseNode& node) { + bool match = node.isKind(ParseNodeKind::ClassDecl); + MOZ_ASSERT_IF(match, node.is<TernaryNode>()); + return match; + } + + ClassNames* names() const { + return kid1() ? &kid1()->as<ClassNames>() : nullptr; + } + + ParseNode* heritage() const { return kid2(); } + + ListNode* memberList() const { return bodyScope()->memberList(); } + + LexicalScopeNode* scopeBindings() const { + LexicalScopeNode* scope = innerScope(); + return scope->isEmptyScope() ? nullptr : scope; + } + + ClassBodyScopeNode* bodyScopeBindings() const { + ClassBodyScopeNode* scope = bodyScope(); + return scope->isEmptyScope() ? nullptr : scope; + } +#ifdef ENABLE_DECORATORS + ListNode* decorators() const { return decorators_; } + + FunctionNode* addInitializerFunction() const { + return addInitializerFunction_; + } +# ifdef DEBUG + void dumpImpl(const ParserAtomsTable* parserAtoms, GenericPrinter& out, + int indent); +# endif +#endif +}; + +#ifdef DEBUG +void DumpParseTree(ParserBase* parser, ParseNode* pn, GenericPrinter& out, + int indent = 0); +#endif + +class ParseNodeAllocator { + public: + explicit ParseNodeAllocator(FrontendContext* fc, LifoAlloc& alloc) + : fc(fc), alloc(alloc) {} + + void* allocNode(size_t size); + + private: + FrontendContext* fc; + LifoAlloc& alloc; +}; + +inline bool ParseNode::isConstant() { + switch (pn_type) { + case ParseNodeKind::NumberExpr: + case ParseNodeKind::StringExpr: + case ParseNodeKind::TemplateStringExpr: + case ParseNodeKind::NullExpr: + case ParseNodeKind::RawUndefinedExpr: + case ParseNodeKind::FalseExpr: + case ParseNodeKind::TrueExpr: + return true; + case ParseNodeKind::ArrayExpr: + case ParseNodeKind::ObjectExpr: + return !as<ListNode>().hasNonConstInitializer(); + default: + return false; + } +} + +bool IsAnonymousFunctionDefinition(ParseNode* pn); + +} /* namespace frontend */ +} /* namespace js */ + +#endif /* frontend_ParseNode_h */ |