diff options
Diffstat (limited to 'js/src/frontend/ParseNode.h')
-rw-r--r-- | js/src/frontend/ParseNode.h | 1450 |
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 */ |