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 {