Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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
#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(ArgumentsLength, ArgumentsLength) \
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) \
IF_EXPLICIT_RESOURCE_MANAGEMENT(F(UsingDecl, DeclarationListNode)) \
IF_EXPLICIT_RESOURCE_MANAGEMENT(F(AwaitUsingDecl, 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(ArgumentsLength) \
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.)
#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) ||
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
node.isKind(ParseNodeKind::UsingDecl) ||
node.isKind(ParseNodeKind::AwaitUsingDecl) ||
#endif
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_;
public:
BigIntLiteral(BigIntIndex index, const TokenPos& pos)
: ParseNode(ParseNodeKind::BigIntExpr, pos), index_(index) {}
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_; }
};
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) ||
node.isKind(ParseNodeKind::ArgumentsLength);
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) ||
node.isKind(ParseNodeKind::ArgumentsLength);
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);
}
protected:
using PropertyAccessBase::PropertyAccessBase;
};
class ArgumentsLength : public PropertyAccess {
public:
ArgumentsLength(ParseNode* lhs, NameNode* name, uint32_t begin, uint32_t end)
: PropertyAccess(ParseNodeKind::ArgumentsLength, lhs, name, begin, end) {
MOZ_ASSERT(lhs);
MOZ_ASSERT(name);
}
static bool test(const ParseNode& node) {
bool match = node.isKind(ParseNodeKind::ArgumentsLength);
MOZ_ASSERT_IF(match, node.is<PropertyAccessBase>());
return match;
}
bool isSuper() const { return false; }
};
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 */