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