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.h1450
1 files changed, 1450 insertions, 0 deletions
diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h
new file mode 100644
index 000000000..d37aaaae0
--- /dev/null
+++ b/js/src/frontend/ParseNode.h
@@ -0,0 +1,1450 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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/Attributes.h"
+
+#include "builtin/ModuleObject.h"
+#include "frontend/TokenStream.h"
+
+namespace js {
+namespace frontend {
+
+class ParseContext;
+class FullParseHandler;
+class FunctionBox;
+class ObjectBox;
+
+#define FOR_EACH_PARSE_NODE_KIND(F) \
+ F(NOP) \
+ F(SEMI) \
+ F(COMMA) \
+ F(CONDITIONAL) \
+ F(COLON) \
+ F(SHORTHAND) \
+ F(POS) \
+ F(NEG) \
+ F(PREINCREMENT) \
+ F(POSTINCREMENT) \
+ F(PREDECREMENT) \
+ F(POSTDECREMENT) \
+ F(DOT) \
+ F(ELEM) \
+ F(ARRAY) \
+ F(ELISION) \
+ F(STATEMENTLIST) \
+ F(LABEL) \
+ F(OBJECT) \
+ F(CALL) \
+ F(NAME) \
+ F(OBJECT_PROPERTY_NAME) \
+ F(COMPUTED_NAME) \
+ F(NUMBER) \
+ F(STRING) \
+ F(TEMPLATE_STRING_LIST) \
+ F(TEMPLATE_STRING) \
+ F(TAGGED_TEMPLATE) \
+ F(CALLSITEOBJ) \
+ F(REGEXP) \
+ F(TRUE) \
+ F(FALSE) \
+ F(NULL) \
+ F(THIS) \
+ F(FUNCTION) \
+ F(MODULE) \
+ F(IF) \
+ F(SWITCH) \
+ F(CASE) \
+ F(WHILE) \
+ F(DOWHILE) \
+ F(FOR) \
+ F(COMPREHENSIONFOR) \
+ F(BREAK) \
+ F(CONTINUE) \
+ F(VAR) \
+ F(CONST) \
+ F(WITH) \
+ F(RETURN) \
+ F(NEW) \
+ /* Delete operations. These must be sequential. */ \
+ F(DELETENAME) \
+ F(DELETEPROP) \
+ F(DELETEELEM) \
+ F(DELETEEXPR) \
+ F(TRY) \
+ F(CATCH) \
+ F(CATCHLIST) \
+ F(THROW) \
+ F(DEBUGGER) \
+ F(GENERATOR) \
+ F(YIELD) \
+ F(YIELD_STAR) \
+ F(GENEXP) \
+ F(ARRAYCOMP) \
+ F(ARRAYPUSH) \
+ F(LEXICALSCOPE) \
+ F(LET) \
+ F(IMPORT) \
+ F(IMPORT_SPEC_LIST) \
+ F(IMPORT_SPEC) \
+ F(EXPORT) \
+ F(EXPORT_FROM) \
+ F(EXPORT_DEFAULT) \
+ F(EXPORT_SPEC_LIST) \
+ F(EXPORT_SPEC) \
+ F(EXPORT_BATCH_SPEC) \
+ F(FORIN) \
+ F(FOROF) \
+ F(FORHEAD) \
+ F(PARAMSBODY) \
+ F(SPREAD) \
+ F(MUTATEPROTO) \
+ F(CLASS) \
+ F(CLASSMETHOD) \
+ F(CLASSMETHODLIST) \
+ F(CLASSNAMES) \
+ F(NEWTARGET) \
+ F(POSHOLDER) \
+ F(SUPERBASE) \
+ F(SUPERCALL) \
+ F(SETTHIS) \
+ \
+ /* Unary operators. */ \
+ F(TYPEOFNAME) \
+ F(TYPEOFEXPR) \
+ F(VOID) \
+ F(NOT) \
+ F(BITNOT) \
+ F(AWAIT) \
+ \
+ /* \
+ * Binary operators. \
+ * These must be in the same order as TOK_OR and friends in TokenStream.h. \
+ */ \
+ F(OR) \
+ F(AND) \
+ F(BITOR) \
+ F(BITXOR) \
+ F(BITAND) \
+ F(STRICTEQ) \
+ F(EQ) \
+ F(STRICTNE) \
+ F(NE) \
+ F(LT) \
+ F(LE) \
+ F(GT) \
+ F(GE) \
+ F(INSTANCEOF) \
+ F(IN) \
+ F(LSH) \
+ F(RSH) \
+ F(URSH) \
+ F(ADD) \
+ F(SUB) \
+ F(STAR) \
+ F(DIV) \
+ F(MOD) \
+ F(POW) \
+ \
+ /* Assignment operators (= += -= etc.). */ \
+ /* ParseNode::isAssignment assumes all these are consecutive. */ \
+ F(ASSIGN) \
+ F(ADDASSIGN) \
+ F(SUBASSIGN) \
+ F(BITORASSIGN) \
+ F(BITXORASSIGN) \
+ F(BITANDASSIGN) \
+ F(LSHASSIGN) \
+ F(RSHASSIGN) \
+ F(URSHASSIGN) \
+ F(MULASSIGN) \
+ F(DIVASSIGN) \
+ F(MODASSIGN) \
+ F(POWASSIGN)
+
+/*
+ * 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, with a
+ * secondary JSOp label when needed.
+ *
+ * The long comment after this enum block describes the kinds in detail.
+ */
+enum ParseNodeKind
+{
+#define EMIT_ENUM(name) PNK_##name,
+ FOR_EACH_PARSE_NODE_KIND(EMIT_ENUM)
+#undef EMIT_ENUM
+ PNK_LIMIT, /* domain size */
+ PNK_BINOP_FIRST = PNK_OR,
+ PNK_BINOP_LAST = PNK_POW,
+ PNK_ASSIGNMENT_START = PNK_ASSIGN,
+ PNK_ASSIGNMENT_LAST = PNK_POWASSIGN
+};
+
+inline bool
+IsDeleteKind(ParseNodeKind kind)
+{
+ return PNK_DELETENAME <= kind && kind <= PNK_DELETEEXPR;
+}
+
+inline bool
+IsTypeofKind(ParseNodeKind kind)
+{
+ return PNK_TYPEOFNAME <= kind && kind <= PNK_TYPEOFEXPR;
+}
+
+/*
+ * Label Variant Members
+ * ----- ------- -------
+ * <Definitions>
+ * PNK_FUNCTION name pn_funbox: ptr to js::FunctionBox holding function
+ * object containing arg and var properties. We
+ * create the function object at parse (not emit)
+ * time to specialize arg and var bytecodes early.
+ * pn_body: PNK_PARAMSBODY, ordinarily;
+ * PNK_LEXICALSCOPE for implicit function in genexpr
+ * PNK_PARAMSBODY list list of formal parameters with
+ * PNK_NAME node with non-empty name for
+ * SingleNameBinding without Initializer
+ * PNK_ASSIGN node for SingleNameBinding with
+ * Initializer
+ * PNK_NAME node with empty name for destructuring
+ * pn_expr: PNK_ARRAY, PNK_OBJECT, or PNK_ASSIGN
+ * PNK_ARRAY or PNK_OBJECT for BindingPattern
+ * without Initializer
+ * PNK_ASSIGN for BindingPattern with
+ * Initializer
+ * followed by:
+ * PNK_STATEMENTLIST node for function body
+ * statements,
+ * PNK_RETURN for expression closure
+ * pn_count: 1 + number of formal parameters
+ * pn_tree: PNK_PARAMSBODY or PNK_STATEMENTLIST node
+ * PNK_SPREAD unary pn_kid: expression being spread
+ *
+ * <Statements>
+ * PNK_STATEMENTLIST list pn_head: list of pn_count statements
+ * PNK_IF ternary pn_kid1: cond, pn_kid2: then, pn_kid3: else or null.
+ * In body of a comprehension or desugared generator
+ * expression, pn_kid2 is PNK_YIELD, PNK_ARRAYPUSH,
+ * or (if the push was optimized away) empty
+ * PNK_STATEMENTLIST.
+ * PNK_SWITCH binary pn_left: discriminant
+ * pn_right: list of PNK_CASE nodes, with at most one
+ * default node, or if there are let bindings
+ * in the top level of the switch body's cases, a
+ * PNK_LEXICALSCOPE node that contains the list of
+ * PNK_CASE nodes.
+ * PNK_CASE binary pn_left: case-expression if CaseClause, or
+ * null if DefaultClause
+ * pn_right: PNK_STATEMENTLIST node for this case's
+ * statements
+ * pn_u.binary.offset: scratch space for the emitter
+ * PNK_WHILE binary pn_left: cond, pn_right: body
+ * PNK_DOWHILE binary pn_left: body, pn_right: cond
+ * PNK_FOR binary pn_left: either PNK_FORIN (for-in statement),
+ * PNK_FOROF (for-of) or PNK_FORHEAD (for(;;))
+ * pn_right: body
+ * PNK_COMPREHENSIONFOR pn_left: either PNK_FORIN or PNK_FOROF
+ * binary pn_right: body
+ * PNK_FORIN ternary pn_kid1: declaration or expression to left of 'in'
+ * pn_kid2: null
+ * pn_kid3: object expr to right of 'in'
+ * PNK_FOROF ternary pn_kid1: declaration or expression to left of 'of'
+ * pn_kid2: null
+ * pn_kid3: expr to right of 'of'
+ * PNK_FORHEAD ternary pn_kid1: init expr before first ';' or nullptr
+ * pn_kid2: cond expr before second ';' or nullptr
+ * pn_kid3: update expr after second ';' or nullptr
+ * PNK_THROW unary pn_op: JSOP_THROW, pn_kid: exception
+ * PNK_TRY ternary pn_kid1: try block
+ * pn_kid2: null or PNK_CATCHLIST list
+ * pn_kid3: null or finally block
+ * PNK_CATCHLIST list pn_head: list of PNK_LEXICALSCOPE nodes, one per
+ * catch-block, each with pn_expr pointing
+ * to a PNK_CATCH node
+ * PNK_CATCH ternary pn_kid1: PNK_NAME, PNK_ARRAY, or PNK_OBJECT catch var node
+ * (PNK_ARRAY or PNK_OBJECT if destructuring)
+ * pn_kid2: null or the catch guard expression
+ * pn_kid3: catch block statements
+ * PNK_BREAK name pn_atom: label or null
+ * PNK_CONTINUE name pn_atom: label or null
+ * PNK_WITH binary pn_left: head expr; pn_right: body;
+ * PNK_VAR, list pn_head: list of PNK_NAME or PNK_ASSIGN nodes
+ * PNK_LET, each name node has either
+ * PNK_CONST pn_used: false
+ * pn_atom: variable name
+ * pn_expr: initializer or null
+ * or
+ * pn_used: true
+ * pn_atom: variable name
+ * pn_lexdef: def node
+ * each assignment node has
+ * pn_left: PNK_NAME with pn_used true and
+ * pn_lexdef (NOT pn_expr) set
+ * pn_right: initializer
+ * PNK_RETURN unary pn_kid: return expr or null
+ * PNK_SEMI unary pn_kid: expr or null statement
+ * pn_prologue: true if Directive Prologue member
+ * in original source, not introduced via
+ * constant folding or other tree rewriting
+ * PNK_LABEL name pn_atom: label, pn_expr: labeled statement
+ * PNK_IMPORT binary pn_left: PNK_IMPORT_SPEC_LIST import specifiers
+ * pn_right: PNK_STRING module specifier
+ * PNK_EXPORT unary pn_kid: declaration expression
+ * PNK_EXPORT_FROM binary pn_left: PNK_EXPORT_SPEC_LIST export specifiers
+ * pn_right: PNK_STRING module specifier
+ * PNK_EXPORT_DEFAULT unary pn_kid: export default declaration or expression
+ *
+ * <Expressions>
+ * All left-associated binary trees of the same type are optimized into lists
+ * to avoid recursion when processing expression chains.
+ * PNK_COMMA list pn_head: list of pn_count comma-separated exprs
+ * PNK_ASSIGN binary pn_left: lvalue, pn_right: rvalue
+ * PNK_ADDASSIGN, binary pn_left: lvalue, pn_right: rvalue
+ * PNK_SUBASSIGN, pn_op: JSOP_ADD for +=, etc.
+ * PNK_BITORASSIGN,
+ * PNK_BITXORASSIGN,
+ * PNK_BITANDASSIGN,
+ * PNK_LSHASSIGN,
+ * PNK_RSHASSIGN,
+ * PNK_URSHASSIGN,
+ * PNK_MULASSIGN,
+ * PNK_DIVASSIGN,
+ * PNK_MODASSIGN,
+ * PNK_POWASSIGN
+ * PNK_CONDITIONAL ternary (cond ? trueExpr : falseExpr)
+ * pn_kid1: cond, pn_kid2: then, pn_kid3: else
+ * PNK_OR, list pn_head; list of pn_count subexpressions
+ * PNK_AND, All of these operators are left-associative except (**).
+ * PNK_BITOR,
+ * PNK_BITXOR,
+ * PNK_BITAND,
+ * PNK_EQ,
+ * PNK_NE,
+ * PNK_STRICTEQ,
+ * PNK_STRICTNE,
+ * PNK_LT,
+ * PNK_LE,
+ * PNK_GT,
+ * PNK_GE,
+ * PNK_LSH,
+ * PNK_RSH,
+ * PNK_URSH,
+ * PNK_ADD,
+ * PNK_SUB,
+ * PNK_STAR,
+ * PNK_DIV,
+ * PNK_MOD,
+ * PNK_POW (**) is right-associative, but forms a list
+ * nonetheless. Special hacks everywhere.
+ *
+ * PNK_POS, unary pn_kid: UNARY expr
+ * PNK_NEG
+ * PNK_VOID, unary pn_kid: UNARY expr
+ * PNK_NOT,
+ * PNK_BITNOT,
+ * PNK_AWAIT
+ * PNK_TYPEOFNAME, unary pn_kid: UNARY expr
+ * PNK_TYPEOFEXPR
+ * PNK_PREINCREMENT, unary pn_kid: MEMBER expr
+ * PNK_POSTINCREMENT,
+ * PNK_PREDECREMENT,
+ * PNK_POSTDECREMENT
+ * PNK_NEW list pn_head: list of ctor, arg1, arg2, ... argN
+ * pn_count: 1 + N (where N is number of args)
+ * ctor is a MEMBER expr
+ * PNK_DELETENAME unary pn_kid: PNK_NAME expr
+ * PNK_DELETEPROP unary pn_kid: PNK_DOT expr
+ * PNK_DELETEELEM unary pn_kid: PNK_ELEM expr
+ * PNK_DELETEEXPR unary pn_kid: MEMBER expr that's evaluated, then the
+ * overall delete evaluates to true; can't be a kind
+ * for a more-specific PNK_DELETE* unless constant
+ * folding (or a similar parse tree manipulation) has
+ * occurred
+ * PNK_DOT name pn_expr: MEMBER expr to left of .
+ * pn_atom: name to right of .
+ * PNK_ELEM binary pn_left: MEMBER expr to left of [
+ * pn_right: expr between [ and ]
+ * PNK_CALL list pn_head: list of call, arg1, arg2, ... argN
+ * pn_count: 1 + N (where N is number of args)
+ * call is a MEMBER expr naming a callable object
+ * PNK_GENEXP list Exactly like PNK_CALL, used for the implicit call
+ * in the desugaring of a generator-expression.
+ * PNK_ARRAY list pn_head: list of pn_count array element exprs
+ * [,,] holes are represented by PNK_ELISION nodes
+ * pn_xflags: PN_ENDCOMMA if extra comma at end
+ * PNK_OBJECT list pn_head: list of pn_count binary PNK_COLON nodes
+ * PNK_COLON binary key-value pair in object initializer or
+ * destructuring lhs
+ * pn_left: property id, pn_right: value
+ * PNK_SHORTHAND binary Same fields as PNK_COLON. This is used for object
+ * literal properties using shorthand ({x}).
+ * PNK_COMPUTED_NAME unary ES6 ComputedPropertyName.
+ * pn_kid: the AssignmentExpression inside the square brackets
+ * PNK_NAME, name pn_atom: name, string, or object atom
+ * PNK_STRING pn_op: JSOP_GETNAME, JSOP_STRING, or JSOP_OBJECT
+ * If JSOP_GETNAME, pn_op may be JSOP_*ARG or JSOP_*VAR
+ * telling const-ness and static analysis results
+ * PNK_TEMPLATE_STRING_LIST pn_head: list of alternating expr and template strings
+ * list
+ * PNK_TEMPLATE_STRING pn_atom: template string atom
+ nullary pn_op: JSOP_NOP
+ * PNK_TAGGED_TEMPLATE pn_head: list of call, call site object, arg1, arg2, ... argN
+ * list pn_count: 2 + N (N is the number of substitutions)
+ * PNK_CALLSITEOBJ list pn_head: a PNK_ARRAY node followed by
+ * list of pn_count - 1 PNK_TEMPLATE_STRING nodes
+ * PNK_REGEXP nullary pn_objbox: RegExp model object
+ * PNK_NUMBER dval pn_dval: double value of numeric literal
+ * PNK_TRUE, nullary pn_op: JSOp bytecode
+ * PNK_FALSE,
+ * PNK_NULL
+ *
+ * PNK_THIS, unary pn_kid: '.this' Name if function `this`, else nullptr
+ * PNK_SUPERBASE unary pn_kid: '.this' Name
+ *
+ * PNK_SETTHIS binary pn_left: '.this' Name, pn_right: SuperCall
+ *
+ * PNK_LEXICALSCOPE scope pn_u.scope.bindings: scope bindings
+ * pn_u.scope.body: scope body
+ * PNK_GENERATOR nullary
+ * PNK_YIELD, binary pn_left: expr or null; pn_right: generator object
+ * PNK_YIELD_STAR
+ * PNK_ARRAYCOMP list pn_count: 1
+ * pn_head: list of 1 element, which is block
+ * enclosing for loop(s) and optionally
+ * if-guarded PNK_ARRAYPUSH
+ * PNK_ARRAYPUSH unary pn_op: JSOP_ARRAYCOMP
+ * pn_kid: array comprehension expression
+ * PNK_NOP nullary
+ */
+enum ParseNodeArity
+{
+ PN_NULLARY, /* 0 kids, only pn_atom/pn_dval/etc. */
+ PN_UNARY, /* one kid, plus a couple of scalars */
+ PN_BINARY, /* two kids, plus a couple of scalars */
+ PN_TERNARY, /* three kids */
+ PN_CODE, /* module or function definition node */
+ PN_LIST, /* generic singly linked list */
+ PN_NAME, /* name, label, or regexp */
+ PN_SCOPE /* lexical scope */
+};
+
+class LoopControlStatement;
+class BreakStatement;
+class ContinueStatement;
+class ConditionalExpression;
+class PropertyAccess;
+
+class ParseNode
+{
+ uint16_t pn_type; /* PNK_* type */
+ uint8_t pn_op; /* see JSOp enum and jsopcode.tbl */
+ uint8_t pn_arity:4; /* see ParseNodeArity enum */
+ bool pn_parens:1; /* this expr was enclosed in parens */
+
+ ParseNode(const ParseNode& other) = delete;
+ void operator=(const ParseNode& other) = delete;
+
+ public:
+ ParseNode(ParseNodeKind kind, JSOp op, ParseNodeArity arity)
+ : pn_type(kind),
+ pn_op(op),
+ pn_arity(arity),
+ pn_parens(false),
+ pn_pos(0, 0),
+ pn_next(nullptr)
+ {
+ MOZ_ASSERT(kind < PNK_LIMIT);
+ memset(&pn_u, 0, sizeof pn_u);
+ }
+
+ ParseNode(ParseNodeKind kind, JSOp op, ParseNodeArity arity, const TokenPos& pos)
+ : pn_type(kind),
+ pn_op(op),
+ pn_arity(arity),
+ pn_parens(false),
+ pn_pos(pos),
+ pn_next(nullptr)
+ {
+ MOZ_ASSERT(kind < PNK_LIMIT);
+ memset(&pn_u, 0, sizeof pn_u);
+ }
+
+ JSOp getOp() const { return JSOp(pn_op); }
+ void setOp(JSOp op) { pn_op = op; }
+ bool isOp(JSOp op) const { return getOp() == op; }
+
+ ParseNodeKind getKind() const {
+ MOZ_ASSERT(pn_type < PNK_LIMIT);
+ return ParseNodeKind(pn_type);
+ }
+ void setKind(ParseNodeKind kind) {
+ MOZ_ASSERT(kind < PNK_LIMIT);
+ pn_type = kind;
+ }
+ bool isKind(ParseNodeKind kind) const { return getKind() == kind; }
+
+ ParseNodeArity getArity() const { return ParseNodeArity(pn_arity); }
+ bool isArity(ParseNodeArity a) const { return getArity() == a; }
+ void setArity(ParseNodeArity a) { pn_arity = a; }
+
+ bool isAssignment() const {
+ ParseNodeKind kind = getKind();
+ return PNK_ASSIGNMENT_START <= kind && kind <= PNK_ASSIGNMENT_LAST;
+ }
+
+ bool isBinaryOperation() const {
+ ParseNodeKind kind = getKind();
+ return PNK_BINOP_FIRST <= kind && kind <= PNK_BINOP_LAST;
+ }
+
+ /* Boolean attributes. */
+ bool isInParens() const { return pn_parens; }
+ bool isLikelyIIFE() const { return isInParens(); }
+ void setInParens(bool enabled) { pn_parens = enabled; }
+
+ TokenPos pn_pos; /* two 16-bit pairs here, for 64 bits */
+ ParseNode* pn_next; /* intrinsic link in parent PN_LIST */
+
+ union {
+ struct { /* list of next-linked nodes */
+ ParseNode* head; /* first node in list */
+ ParseNode** tail; /* ptr to ptr to last node in list */
+ uint32_t count; /* number of nodes in list */
+ uint32_t xflags; /* see PNX_* below */
+ } list;
+ struct { /* ternary: if, for(;;), ?: */
+ ParseNode* kid1; /* condition, discriminant, etc. */
+ ParseNode* kid2; /* then-part, case list, etc. */
+ ParseNode* kid3; /* else-part, default case, etc. */
+ } ternary;
+ struct { /* two kids if binary */
+ ParseNode* left;
+ ParseNode* right;
+ union {
+ unsigned iflags; /* JSITER_* flags for PNK_{COMPREHENSION,}FOR node */
+ bool isStatic; /* only for PNK_CLASSMETHOD */
+ uint32_t offset; /* for the emitter's use on PNK_CASE nodes */
+ };
+ } binary;
+ struct { /* one kid if unary */
+ ParseNode* kid;
+ bool prologue; /* directive prologue member (as
+ pn_prologue) */
+ } unary;
+ struct { /* name, labeled statement, etc. */
+ union {
+ JSAtom* atom; /* lexical name or label atom */
+ ObjectBox* objbox; /* regexp object */
+ FunctionBox* funbox; /* function object */
+ };
+ ParseNode* expr; /* module or function body, var
+ initializer, argument default, or
+ base object of PNK_DOT */
+ } name;
+ struct {
+ LexicalScope::Data* bindings;
+ ParseNode* body;
+ } scope;
+ struct {
+ double value; /* aligned numeric literal value */
+ DecimalPoint decimalPoint; /* Whether the number has a decimal point */
+ } number;
+ class {
+ friend class LoopControlStatement;
+ PropertyName* label; /* target of break/continue statement */
+ } loopControl;
+ } pn_u;
+
+#define pn_objbox pn_u.name.objbox
+#define pn_funbox pn_u.name.funbox
+#define pn_body pn_u.name.expr
+#define pn_head pn_u.list.head
+#define pn_tail pn_u.list.tail
+#define pn_count pn_u.list.count
+#define pn_xflags pn_u.list.xflags
+#define pn_kid1 pn_u.ternary.kid1
+#define pn_kid2 pn_u.ternary.kid2
+#define pn_kid3 pn_u.ternary.kid3
+#define pn_left pn_u.binary.left
+#define pn_right pn_u.binary.right
+#define pn_pval pn_u.binary.pval
+#define pn_iflags pn_u.binary.iflags
+#define pn_kid pn_u.unary.kid
+#define pn_prologue pn_u.unary.prologue
+#define pn_atom pn_u.name.atom
+#define pn_objbox pn_u.name.objbox
+#define pn_expr pn_u.name.expr
+#define pn_dval pn_u.number.value
+
+
+ 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 ParseNode*
+ appendOrCreateList(ParseNodeKind kind, JSOp op, ParseNode* left, ParseNode* right,
+ FullParseHandler* handler, ParseContext* pc);
+
+ inline PropertyName* name() const;
+ inline JSAtom* atom() const;
+
+ ParseNode* expr() const {
+ MOZ_ASSERT(pn_arity == PN_NAME || pn_arity == PN_CODE);
+ return pn_expr;
+ }
+
+ bool isEmptyScope() const {
+ MOZ_ASSERT(pn_arity == PN_SCOPE);
+ return !pn_u.scope.bindings;
+ }
+
+ Handle<LexicalScope::Data*> scopeBindings() const {
+ MOZ_ASSERT(!isEmptyScope());
+ // Bindings' GC safety depend on the presence of an AutoKeepAtoms that
+ // the rest of the frontend also depends on.
+ return Handle<LexicalScope::Data*>::fromMarkedLocation(&pn_u.scope.bindings);
+ }
+
+ ParseNode* scopeBody() const {
+ MOZ_ASSERT(pn_arity == PN_SCOPE);
+ return pn_u.scope.body;
+ }
+
+ void setScopeBody(ParseNode* body) {
+ MOZ_ASSERT(pn_arity == PN_SCOPE);
+ pn_u.scope.body = body;
+ }
+
+/* PN_LIST pn_xflags bits. */
+#define PNX_FUNCDEFS 0x01 /* contains top-level function statements */
+#define PNX_ARRAYHOLESPREAD 0x02 /* one or more of
+ 1. array initialiser has holes
+ 2. array initializer has spread node */
+#define PNX_NONCONST 0x04 /* initialiser has non-constants */
+
+ bool functionIsHoisted() const {
+ MOZ_ASSERT(pn_arity == PN_CODE && getKind() == PNK_FUNCTION);
+ MOZ_ASSERT(isOp(JSOP_LAMBDA) || // lambda, genexpr
+ isOp(JSOP_LAMBDA_ARROW) || // arrow function
+ isOp(JSOP_FUNWITHPROTO) || // already emitted lambda with needsProto
+ isOp(JSOP_DEFFUN) || // non-body-level function statement
+ isOp(JSOP_NOP) || // body-level function stmt in global code
+ isOp(JSOP_GETLOCAL) || // body-level function stmt in function code
+ isOp(JSOP_GETARG) || // body-level function redeclaring formal
+ isOp(JSOP_INITLEXICAL)); // block-level function stmt
+ return !isOp(JSOP_LAMBDA) && !isOp(JSOP_LAMBDA_ARROW) &&
+ !isOp(JSOP_FUNWITHPROTO) && !isOp(JSOP_DEFFUN);
+ }
+
+ /*
+ * True if this statement node 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 pn_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.
+ */
+ JSAtom* isStringExprStatement() const {
+ if (getKind() == PNK_SEMI) {
+ MOZ_ASSERT(pn_arity == PN_UNARY);
+ ParseNode* kid = pn_kid;
+ if (kid && kid->getKind() == PNK_STRING && !kid->pn_parens)
+ return kid->pn_atom;
+ }
+ return nullptr;
+ }
+
+ /* True if pn is a parsenode representing a literal constant. */
+ bool isLiteral() const {
+ return isKind(PNK_NUMBER) ||
+ isKind(PNK_STRING) ||
+ isKind(PNK_TRUE) ||
+ isKind(PNK_FALSE) ||
+ isKind(PNK_NULL);
+ }
+
+ /* Return true if this node appears in a Directive Prologue. */
+ bool isDirectivePrologueMember() const { return pn_prologue; }
+
+ // True iff this is a for-in/of loop variable declaration (var/let/const).
+ bool isForLoopDeclaration() const {
+ if (isKind(PNK_VAR) || isKind(PNK_LET) || isKind(PNK_CONST)) {
+ MOZ_ASSERT(isArity(PN_LIST));
+ MOZ_ASSERT(pn_count > 0);
+ return true;
+ }
+
+ return false;
+ }
+
+ ParseNode* generatorExpr() const {
+ MOZ_ASSERT(isKind(PNK_GENEXP));
+
+ ParseNode* callee = this->pn_head;
+ MOZ_ASSERT(callee->isKind(PNK_FUNCTION));
+
+ ParseNode* paramsBody = callee->pn_body;
+ MOZ_ASSERT(paramsBody->isKind(PNK_PARAMSBODY));
+
+ ParseNode* body = paramsBody->last();
+ MOZ_ASSERT(body->isKind(PNK_STATEMENTLIST));
+ MOZ_ASSERT(body->last()->isKind(PNK_LEXICALSCOPE) ||
+ body->last()->isKind(PNK_COMPREHENSIONFOR));
+ return body->last();
+ }
+
+ /*
+ * Compute a pointer to the last element in a singly-linked list. NB: list
+ * must be non-empty for correct PN_LAST usage -- this is asserted!
+ */
+ ParseNode* last() const {
+ MOZ_ASSERT(pn_arity == PN_LIST);
+ MOZ_ASSERT(pn_count != 0);
+ return (ParseNode*)(uintptr_t(pn_tail) - offsetof(ParseNode, pn_next));
+ }
+
+ void initNumber(double value, DecimalPoint decimalPoint) {
+ MOZ_ASSERT(pn_arity == PN_NULLARY);
+ MOZ_ASSERT(getKind() == PNK_NUMBER);
+ pn_u.number.value = value;
+ pn_u.number.decimalPoint = decimalPoint;
+ }
+
+ void makeEmpty() {
+ MOZ_ASSERT(pn_arity == PN_LIST);
+ pn_head = nullptr;
+ pn_tail = &pn_head;
+ pn_count = 0;
+ pn_xflags = 0;
+ }
+
+ void initList(ParseNode* pn) {
+ MOZ_ASSERT(pn_arity == PN_LIST);
+ if (pn->pn_pos.begin < pn_pos.begin)
+ pn_pos.begin = pn->pn_pos.begin;
+ pn_pos.end = pn->pn_pos.end;
+ pn_head = pn;
+ pn_tail = &pn->pn_next;
+ pn_count = 1;
+ pn_xflags = 0;
+ }
+
+ void append(ParseNode* pn) {
+ MOZ_ASSERT(pn_arity == PN_LIST);
+ MOZ_ASSERT(pn->pn_pos.begin >= pn_pos.begin);
+ pn_pos.end = pn->pn_pos.end;
+ *pn_tail = pn;
+ pn_tail = &pn->pn_next;
+ pn_count++;
+ }
+
+ void prepend(ParseNode* pn) {
+ MOZ_ASSERT(pn_arity == PN_LIST);
+ pn->pn_next = pn_head;
+ pn_head = pn;
+ if (pn_tail == &pn_head)
+ pn_tail = &pn->pn_next;
+ pn_count++;
+ }
+
+ void checkListConsistency()
+#ifndef DEBUG
+ {}
+#endif
+ ;
+
+ enum AllowConstantObjects {
+ DontAllowObjects = 0,
+ AllowObjects,
+ ForCopyOnWriteArray
+ };
+
+ MOZ_MUST_USE bool getConstantValue(ExclusiveContext* cx, AllowConstantObjects allowObjects,
+ MutableHandleValue vp, Value* compare = nullptr,
+ size_t ncompare = 0, NewObjectKind newKind = TenuredObject);
+ 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
+ void dump();
+ void dump(int indent);
+#endif
+};
+
+struct NullaryNode : public ParseNode
+{
+ NullaryNode(ParseNodeKind kind, const TokenPos& pos)
+ : ParseNode(kind, JSOP_NOP, PN_NULLARY, pos) {}
+ NullaryNode(ParseNodeKind kind, JSOp op, const TokenPos& pos)
+ : ParseNode(kind, op, PN_NULLARY, pos) {}
+
+ // This constructor is for a few mad uses in the emitter. It populates
+ // the pn_atom field even though that field belongs to a branch in pn_u
+ // that nullary nodes shouldn't use -- bogus.
+ NullaryNode(ParseNodeKind kind, JSOp op, const TokenPos& pos, JSAtom* atom)
+ : ParseNode(kind, op, PN_NULLARY, pos)
+ {
+ pn_atom = atom;
+ }
+
+#ifdef DEBUG
+ void dump();
+#endif
+};
+
+struct UnaryNode : public ParseNode
+{
+ UnaryNode(ParseNodeKind kind, JSOp op, const TokenPos& pos, ParseNode* kid)
+ : ParseNode(kind, op, PN_UNARY, pos)
+ {
+ pn_kid = kid;
+ }
+
+#ifdef DEBUG
+ void dump(int indent);
+#endif
+};
+
+struct BinaryNode : public ParseNode
+{
+ BinaryNode(ParseNodeKind kind, JSOp op, const TokenPos& pos, ParseNode* left, ParseNode* right)
+ : ParseNode(kind, op, PN_BINARY, pos)
+ {
+ pn_left = left;
+ pn_right = right;
+ }
+
+ BinaryNode(ParseNodeKind kind, JSOp op, ParseNode* left, ParseNode* right)
+ : ParseNode(kind, op, PN_BINARY, TokenPos::box(left->pn_pos, right->pn_pos))
+ {
+ pn_left = left;
+ pn_right = right;
+ }
+
+#ifdef DEBUG
+ void dump(int indent);
+#endif
+};
+
+struct TernaryNode : public ParseNode
+{
+ TernaryNode(ParseNodeKind kind, JSOp op, ParseNode* kid1, ParseNode* kid2, ParseNode* kid3)
+ : ParseNode(kind, op, PN_TERNARY,
+ TokenPos((kid1 ? kid1 : kid2 ? kid2 : kid3)->pn_pos.begin,
+ (kid3 ? kid3 : kid2 ? kid2 : kid1)->pn_pos.end))
+ {
+ pn_kid1 = kid1;
+ pn_kid2 = kid2;
+ pn_kid3 = kid3;
+ }
+
+ TernaryNode(ParseNodeKind kind, JSOp op, ParseNode* kid1, ParseNode* kid2, ParseNode* kid3,
+ const TokenPos& pos)
+ : ParseNode(kind, op, PN_TERNARY, pos)
+ {
+ pn_kid1 = kid1;
+ pn_kid2 = kid2;
+ pn_kid3 = kid3;
+ }
+
+#ifdef DEBUG
+ void dump(int indent);
+#endif
+};
+
+struct ListNode : public ParseNode
+{
+ ListNode(ParseNodeKind kind, const TokenPos& pos)
+ : ParseNode(kind, JSOP_NOP, PN_LIST, pos)
+ {
+ makeEmpty();
+ }
+
+ ListNode(ParseNodeKind kind, JSOp op, const TokenPos& pos)
+ : ParseNode(kind, op, PN_LIST, pos)
+ {
+ makeEmpty();
+ }
+
+ ListNode(ParseNodeKind kind, JSOp op, ParseNode* kid)
+ : ParseNode(kind, op, PN_LIST, kid->pn_pos)
+ {
+ initList(kid);
+ }
+
+ static bool test(const ParseNode& node) {
+ return node.isArity(PN_LIST);
+ }
+
+#ifdef DEBUG
+ void dump(int indent);
+#endif
+};
+
+struct CodeNode : public ParseNode
+{
+ CodeNode(ParseNodeKind kind, const TokenPos& pos)
+ : ParseNode(kind, JSOP_NOP, PN_CODE, pos)
+ {
+ MOZ_ASSERT(kind == PNK_FUNCTION || kind == PNK_MODULE);
+ MOZ_ASSERT(!pn_body);
+ MOZ_ASSERT(!pn_objbox);
+ }
+
+ public:
+#ifdef DEBUG
+ void dump(int indent);
+#endif
+};
+
+struct NameNode : public ParseNode
+{
+ NameNode(ParseNodeKind kind, JSOp op, JSAtom* atom, const TokenPos& pos)
+ : ParseNode(kind, op, PN_NAME, pos)
+ {
+ pn_atom = atom;
+ pn_expr = nullptr;
+ }
+
+#ifdef DEBUG
+ void dump(int indent);
+#endif
+};
+
+struct LexicalScopeNode : public ParseNode
+{
+ LexicalScopeNode(LexicalScope::Data* bindings, ParseNode* body)
+ : ParseNode(PNK_LEXICALSCOPE, JSOP_NOP, PN_SCOPE, body->pn_pos)
+ {
+ pn_u.scope.bindings = bindings;
+ pn_u.scope.body = body;
+ }
+
+ static bool test(const ParseNode& node) {
+ return node.isKind(PNK_LEXICALSCOPE);
+ }
+
+#ifdef DEBUG
+ void dump(int indent);
+#endif
+};
+
+class LabeledStatement : public ParseNode
+{
+ public:
+ LabeledStatement(PropertyName* label, ParseNode* stmt, uint32_t begin)
+ : ParseNode(PNK_LABEL, JSOP_NOP, PN_NAME, TokenPos(begin, stmt->pn_pos.end))
+ {
+ pn_atom = label;
+ pn_expr = stmt;
+ }
+
+ PropertyName* label() const {
+ return pn_atom->asPropertyName();
+ }
+
+ ParseNode* statement() const {
+ return pn_expr;
+ }
+
+ static bool test(const ParseNode& node) {
+ bool match = node.isKind(PNK_LABEL);
+ MOZ_ASSERT_IF(match, node.isArity(PN_NAME));
+ MOZ_ASSERT_IF(match, node.isOp(JSOP_NOP));
+ return match;
+ }
+};
+
+// 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(PNK_CASE, JSOP_NOP, TokenPos(begin, stmts->pn_pos.end), expr, stmts) {}
+
+ ParseNode* caseExpression() const { return pn_left; }
+ bool isDefault() const { return !caseExpression(); }
+ ParseNode* statementList() const { return pn_right; }
+
+ // The next CaseClause in the same switch statement.
+ CaseClause* next() const { return pn_next ? &pn_next->as<CaseClause>() : nullptr; }
+
+ // Scratch space used by the emitter.
+ uint32_t offset() const { return pn_u.binary.offset; }
+ void setOffset(uint32_t u) { pn_u.binary.offset = u; }
+
+ static bool test(const ParseNode& node) {
+ bool match = node.isKind(PNK_CASE);
+ MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
+ MOZ_ASSERT_IF(match, node.isOp(JSOP_NOP));
+ return match;
+ }
+};
+
+class LoopControlStatement : public ParseNode
+{
+ protected:
+ LoopControlStatement(ParseNodeKind kind, PropertyName* label, const TokenPos& pos)
+ : ParseNode(kind, JSOP_NOP, PN_NULLARY, pos)
+ {
+ MOZ_ASSERT(kind == PNK_BREAK || kind == PNK_CONTINUE);
+ pn_u.loopControl.label = label;
+ }
+
+ public:
+ /* Label associated with this break/continue statement, if any. */
+ PropertyName* label() const {
+ return pn_u.loopControl.label;
+ }
+
+ static bool test(const ParseNode& node) {
+ bool match = node.isKind(PNK_BREAK) || node.isKind(PNK_CONTINUE);
+ MOZ_ASSERT_IF(match, node.isArity(PN_NULLARY));
+ MOZ_ASSERT_IF(match, node.isOp(JSOP_NOP));
+ return match;
+ }
+};
+
+class BreakStatement : public LoopControlStatement
+{
+ public:
+ BreakStatement(PropertyName* label, const TokenPos& pos)
+ : LoopControlStatement(PNK_BREAK, label, pos)
+ { }
+
+ static bool test(const ParseNode& node) {
+ bool match = node.isKind(PNK_BREAK);
+ MOZ_ASSERT_IF(match, node.isArity(PN_NULLARY));
+ MOZ_ASSERT_IF(match, node.isOp(JSOP_NOP));
+ return match;
+ }
+};
+
+class ContinueStatement : public LoopControlStatement
+{
+ public:
+ ContinueStatement(PropertyName* label, const TokenPos& pos)
+ : LoopControlStatement(PNK_CONTINUE, label, pos)
+ { }
+
+ static bool test(const ParseNode& node) {
+ bool match = node.isKind(PNK_CONTINUE);
+ MOZ_ASSERT_IF(match, node.isArity(PN_NULLARY));
+ MOZ_ASSERT_IF(match, node.isOp(JSOP_NOP));
+ return match;
+ }
+};
+
+class DebuggerStatement : public ParseNode
+{
+ public:
+ explicit DebuggerStatement(const TokenPos& pos)
+ : ParseNode(PNK_DEBUGGER, JSOP_NOP, PN_NULLARY, pos)
+ { }
+};
+
+class ConditionalExpression : public ParseNode
+{
+ public:
+ ConditionalExpression(ParseNode* condition, ParseNode* thenExpr, ParseNode* elseExpr)
+ : ParseNode(PNK_CONDITIONAL, JSOP_NOP, PN_TERNARY,
+ TokenPos(condition->pn_pos.begin, elseExpr->pn_pos.end))
+ {
+ MOZ_ASSERT(condition);
+ MOZ_ASSERT(thenExpr);
+ MOZ_ASSERT(elseExpr);
+ pn_u.ternary.kid1 = condition;
+ pn_u.ternary.kid2 = thenExpr;
+ pn_u.ternary.kid3 = elseExpr;
+ }
+
+ ParseNode& condition() const {
+ return *pn_u.ternary.kid1;
+ }
+
+ ParseNode& thenExpression() const {
+ return *pn_u.ternary.kid2;
+ }
+
+ ParseNode& elseExpression() const {
+ return *pn_u.ternary.kid3;
+ }
+
+ static bool test(const ParseNode& node) {
+ bool match = node.isKind(PNK_CONDITIONAL);
+ MOZ_ASSERT_IF(match, node.isArity(PN_TERNARY));
+ MOZ_ASSERT_IF(match, node.isOp(JSOP_NOP));
+ return match;
+ }
+};
+
+class ThisLiteral : public UnaryNode
+{
+ public:
+ ThisLiteral(const TokenPos& pos, ParseNode* thisName)
+ : UnaryNode(PNK_THIS, JSOP_NOP, pos, thisName)
+ { }
+};
+
+class NullLiteral : public ParseNode
+{
+ public:
+ explicit NullLiteral(const TokenPos& pos) : ParseNode(PNK_NULL, JSOP_NULL, PN_NULLARY, pos) { }
+};
+
+class BooleanLiteral : public ParseNode
+{
+ public:
+ BooleanLiteral(bool b, const TokenPos& pos)
+ : ParseNode(b ? PNK_TRUE : PNK_FALSE, b ? JSOP_TRUE : JSOP_FALSE, PN_NULLARY, pos)
+ { }
+};
+
+class RegExpLiteral : public NullaryNode
+{
+ public:
+ RegExpLiteral(ObjectBox* reobj, const TokenPos& pos)
+ : NullaryNode(PNK_REGEXP, JSOP_REGEXP, pos)
+ {
+ pn_objbox = reobj;
+ }
+
+ ObjectBox* objbox() const { return pn_objbox; }
+
+ static bool test(const ParseNode& node) {
+ bool match = node.isKind(PNK_REGEXP);
+ MOZ_ASSERT_IF(match, node.isArity(PN_NULLARY));
+ MOZ_ASSERT_IF(match, node.isOp(JSOP_REGEXP));
+ return match;
+ }
+};
+
+class PropertyAccess : public ParseNode
+{
+ public:
+ PropertyAccess(ParseNode* lhs, PropertyName* name, uint32_t begin, uint32_t end)
+ : ParseNode(PNK_DOT, JSOP_NOP, PN_NAME, TokenPos(begin, end))
+ {
+ MOZ_ASSERT(lhs != nullptr);
+ MOZ_ASSERT(name != nullptr);
+ pn_u.name.expr = lhs;
+ pn_u.name.atom = name;
+ }
+
+ static bool test(const ParseNode& node) {
+ bool match = node.isKind(PNK_DOT);
+ MOZ_ASSERT_IF(match, node.isArity(PN_NAME));
+ return match;
+ }
+
+ ParseNode& expression() const {
+ return *pn_u.name.expr;
+ }
+
+ PropertyName& name() const {
+ return *pn_u.name.atom->asPropertyName();
+ }
+
+ bool isSuper() const {
+ // PNK_SUPERBASE cannot result from any expression syntax.
+ return expression().isKind(PNK_SUPERBASE);
+ }
+};
+
+class PropertyByValue : public ParseNode
+{
+ public:
+ PropertyByValue(ParseNode* lhs, ParseNode* propExpr, uint32_t begin, uint32_t end)
+ : ParseNode(PNK_ELEM, JSOP_NOP, PN_BINARY, TokenPos(begin, end))
+ {
+ pn_u.binary.left = lhs;
+ pn_u.binary.right = propExpr;
+ }
+
+ static bool test(const ParseNode& node) {
+ bool match = node.isKind(PNK_ELEM);
+ MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
+ return match;
+ }
+
+ bool isSuper() const {
+ return pn_left->isKind(PNK_SUPERBASE);
+ }
+};
+
+/*
+ * A CallSiteNode represents the implicit call site object argument in a TaggedTemplate.
+ */
+struct CallSiteNode : public ListNode {
+ explicit CallSiteNode(uint32_t begin): ListNode(PNK_CALLSITEOBJ, TokenPos(begin, begin + 1)) {}
+
+ static bool test(const ParseNode& node) {
+ return node.isKind(PNK_CALLSITEOBJ);
+ }
+
+ MOZ_MUST_USE bool getRawArrayValue(ExclusiveContext* cx, MutableHandleValue vp) {
+ return pn_head->getConstantValue(cx, AllowObjects, vp);
+ }
+};
+
+struct ClassMethod : public BinaryNode {
+ /*
+ * Method defintions often keep a name and function body that overlap,
+ * so explicitly define the beginning and end here.
+ */
+ ClassMethod(ParseNode* name, ParseNode* body, JSOp op, bool isStatic)
+ : BinaryNode(PNK_CLASSMETHOD, op, TokenPos(name->pn_pos.begin, body->pn_pos.end), name, body)
+ {
+ pn_u.binary.isStatic = isStatic;
+ }
+
+ static bool test(const ParseNode& node) {
+ bool match = node.isKind(PNK_CLASSMETHOD);
+ MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
+ return match;
+ }
+
+ ParseNode& name() const {
+ return *pn_u.binary.left;
+ }
+ ParseNode& method() const {
+ return *pn_u.binary.right;
+ }
+ bool isStatic() const {
+ return pn_u.binary.isStatic;
+ }
+};
+
+struct ClassNames : public BinaryNode {
+ ClassNames(ParseNode* outerBinding, ParseNode* innerBinding, const TokenPos& pos)
+ : BinaryNode(PNK_CLASSNAMES, JSOP_NOP, pos, outerBinding, innerBinding)
+ {
+ MOZ_ASSERT_IF(outerBinding, outerBinding->isKind(PNK_NAME));
+ MOZ_ASSERT(innerBinding->isKind(PNK_NAME));
+ MOZ_ASSERT_IF(outerBinding, innerBinding->pn_atom == outerBinding->pn_atom);
+ }
+
+ static bool test(const ParseNode& node) {
+ bool match = node.isKind(PNK_CLASSNAMES);
+ MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
+ 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.
+ */
+ ParseNode* outerBinding() const {
+ return pn_u.binary.left;
+ }
+ ParseNode* innerBinding() const {
+ return pn_u.binary.right;
+ }
+};
+
+struct ClassNode : public TernaryNode {
+ ClassNode(ParseNode* names, ParseNode* heritage, ParseNode* methodsOrBlock)
+ : TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, methodsOrBlock)
+ {
+ MOZ_ASSERT_IF(names, names->is<ClassNames>());
+ MOZ_ASSERT(methodsOrBlock->is<LexicalScopeNode>() ||
+ methodsOrBlock->isKind(PNK_CLASSMETHODLIST));
+ }
+
+ static bool test(const ParseNode& node) {
+ bool match = node.isKind(PNK_CLASS);
+ MOZ_ASSERT_IF(match, node.isArity(PN_TERNARY));
+ return match;
+ }
+
+ ClassNames* names() const {
+ return pn_kid1 ? &pn_kid1->as<ClassNames>() : nullptr;
+ }
+ ParseNode* heritage() const {
+ return pn_kid2;
+ }
+ ParseNode* methodList() const {
+ if (pn_kid3->isKind(PNK_CLASSMETHODLIST))
+ return pn_kid3;
+
+ MOZ_ASSERT(pn_kid3->is<LexicalScopeNode>());
+ ParseNode* list = pn_kid3->scopeBody();
+ MOZ_ASSERT(list->isKind(PNK_CLASSMETHODLIST));
+ return list;
+ }
+ Handle<LexicalScope::Data*> scopeBindings() const {
+ MOZ_ASSERT(pn_kid3->is<LexicalScopeNode>());
+ return pn_kid3->scopeBindings();
+ }
+};
+
+#ifdef DEBUG
+void DumpParseTree(ParseNode* pn, int indent = 0);
+#endif
+
+class ParseNodeAllocator
+{
+ public:
+ explicit ParseNodeAllocator(ExclusiveContext* cx, LifoAlloc& alloc)
+ : cx(cx), alloc(alloc), freelist(nullptr)
+ {}
+
+ void* allocNode();
+ void freeNode(ParseNode* pn);
+ ParseNode* freeTree(ParseNode* pn);
+ void prepareNodeForMutation(ParseNode* pn);
+
+ private:
+ ExclusiveContext* cx;
+ LifoAlloc& alloc;
+ ParseNode* freelist;
+};
+
+inline bool
+ParseNode::isConstant()
+{
+ switch (pn_type) {
+ case PNK_NUMBER:
+ case PNK_STRING:
+ case PNK_TEMPLATE_STRING:
+ case PNK_NULL:
+ case PNK_FALSE:
+ case PNK_TRUE:
+ return true;
+ case PNK_ARRAY:
+ case PNK_OBJECT:
+ MOZ_ASSERT(isOp(JSOP_NEWINIT));
+ return !(pn_xflags & PNX_NONCONST);
+ default:
+ return false;
+ }
+}
+
+class ObjectBox
+{
+ public:
+ JSObject* object;
+
+ ObjectBox(JSObject* object, ObjectBox* traceLink);
+ bool isFunctionBox() { return object->is<JSFunction>(); }
+ FunctionBox* asFunctionBox();
+ virtual void trace(JSTracer* trc);
+
+ static void TraceList(JSTracer* trc, ObjectBox* listHead);
+
+ protected:
+ friend struct CGObjectList;
+
+ ObjectBox* traceLink;
+ ObjectBox* emitLink;
+
+ ObjectBox(JSFunction* function, ObjectBox* traceLink);
+};
+
+enum ParseReportKind
+{
+ ParseError,
+ ParseWarning,
+ ParseExtraWarning,
+ ParseStrictError
+};
+
+enum FunctionSyntaxKind
+{
+ Expression,
+ Statement,
+ Arrow,
+ Method,
+ ClassConstructor,
+ DerivedClassConstructor,
+ Getter,
+ GetterNoExpressionClosure,
+ Setter,
+ SetterNoExpressionClosure
+};
+
+static inline bool
+IsConstructorKind(FunctionSyntaxKind kind)
+{
+ return kind == ClassConstructor || kind == DerivedClassConstructor;
+}
+
+static inline bool
+IsGetterKind(FunctionSyntaxKind kind)
+{
+ return kind == Getter || kind == GetterNoExpressionClosure;
+}
+
+static inline bool
+IsSetterKind(FunctionSyntaxKind kind)
+{
+ return kind == Setter || kind == SetterNoExpressionClosure;
+}
+
+static inline bool
+IsMethodDefinitionKind(FunctionSyntaxKind kind)
+{
+ return kind == Method || IsConstructorKind(kind) ||
+ IsGetterKind(kind) || IsSetterKind(kind);
+}
+
+static inline ParseNode*
+FunctionFormalParametersList(ParseNode* fn, unsigned* numFormals)
+{
+ MOZ_ASSERT(fn->isKind(PNK_FUNCTION));
+ ParseNode* argsBody = fn->pn_body;
+ MOZ_ASSERT(argsBody->isKind(PNK_PARAMSBODY));
+ *numFormals = argsBody->pn_count;
+ if (*numFormals > 0 &&
+ argsBody->last()->isKind(PNK_LEXICALSCOPE) &&
+ argsBody->last()->scopeBody()->isKind(PNK_STATEMENTLIST))
+ {
+ (*numFormals)--;
+ }
+ MOZ_ASSERT(argsBody->isArity(PN_LIST));
+ return argsBody->pn_head;
+}
+
+} /* namespace frontend */
+} /* namespace js */
+
+#endif /* frontend_ParseNode_h */