mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-06-16 14:29:46 +00:00
1800 lines
61 KiB
C++
1800 lines
61 KiB
C++
/* -*- 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 "frontend/TokenStream.h"
|
|
|
|
namespace js {
|
|
namespace frontend {
|
|
|
|
template <typename ParseHandler>
|
|
struct ParseContext;
|
|
|
|
class FullParseHandler;
|
|
class FunctionBox;
|
|
class ModuleBox;
|
|
class ObjectBox;
|
|
|
|
// A packed ScopeCoordinate for use in the frontend during bytecode
|
|
// compilation.
|
|
//
|
|
// Definitions start out !isFree() && isHopsUnknown().
|
|
// Uses start out isFree().
|
|
//
|
|
// The BCE computes the correct number of hops based on the static scope
|
|
// chain. This is ncessary because due to hoisting, the Parser does not know
|
|
// the final static scope chain.
|
|
//
|
|
// The BCE also computes the correct slot number depending on whether the
|
|
// binding is aliased. If it is aliased, the slot number is the slot on the
|
|
// dynamic scope object. Otherwise, the slot number is the frame slot.
|
|
class PackedScopeCoordinate
|
|
{
|
|
uint32_t hops_ : SCOPECOORD_HOPS_BITS;
|
|
uint32_t slot_ : SCOPECOORD_SLOT_BITS;
|
|
|
|
void checkInvariants() {
|
|
static_assert(sizeof(PackedScopeCoordinate) == sizeof(uint32_t),
|
|
"Not necessary for correctness, but good for ParseNode memory use");
|
|
}
|
|
|
|
public:
|
|
// Steal one value to represent the sentinel value signaling that the
|
|
// binding is free, and one value to represent the sentinel value
|
|
// signaling that the number of hop count need to be computed by the
|
|
// BytecodeEmitter.
|
|
static const uint32_t UNKNOWN_HOPS = SCOPECOORD_HOPS_LIMIT - 1;
|
|
static const uint32_t UNKNOWN_SLOT = SCOPECOORD_SLOT_LIMIT - 1;
|
|
bool isHopsUnknown() const { return hops_ == UNKNOWN_HOPS; }
|
|
bool isFree() const { return slot_ == UNKNOWN_SLOT; }
|
|
|
|
uint32_t hops() const { MOZ_ASSERT(!isFree()); return hops_; }
|
|
uint32_t slot() const { MOZ_ASSERT(!isFree()); return slot_; }
|
|
|
|
bool setSlot(TokenStream& ts, uint32_t newSlot) {
|
|
if (newSlot >= UNKNOWN_SLOT)
|
|
return ts.reportError(JSMSG_TOO_MANY_LOCALS);
|
|
slot_ = newSlot;
|
|
return true;
|
|
}
|
|
|
|
bool setHops(TokenStream& ts, uint32_t newHops) {
|
|
if (newHops >= UNKNOWN_HOPS)
|
|
return ts.reportError(JSMSG_TOO_DEEP, js_function_str);
|
|
hops_ = newHops;
|
|
return true;
|
|
}
|
|
|
|
bool set(TokenStream& ts, uint32_t newHops, uint32_t newSlot) {
|
|
return setHops(ts, newHops) && setSlot(ts, newSlot);
|
|
}
|
|
|
|
void makeFree() {
|
|
hops_ = UNKNOWN_HOPS;
|
|
slot_ = UNKNOWN_SLOT;
|
|
MOZ_ASSERT(isFree());
|
|
}
|
|
};
|
|
|
|
#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(LETBLOCK) \
|
|
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(ANNEXB_FUNCTION) \
|
|
F(ARGSBODY) \
|
|
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_ARGSBODY, ordinarily;
|
|
* PNK_LEXICALSCOPE for implicit function in genexpr
|
|
* pn_scopecoord: hops and var index for function
|
|
* pn_dflags: PND_* definition/use flags (see below)
|
|
* pn_blockid: block id number
|
|
* PNK_ANNEXB_FUNCTION binary pn_left: PNK_FUNCTION
|
|
* pn_right: assignment for annex B semantics for
|
|
* block-scoped function
|
|
* PNK_ARGSBODY 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_ARGSBODY 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: PNK_VAR to left of 'in', or nullptr
|
|
* pn_kid2: PNK_NAME or destructuring expr
|
|
* to left of 'in'; if pn_kid1, then this
|
|
* is a clone of pn_kid1->pn_head
|
|
* pn_kid3: object expr to right of 'in'
|
|
* PNK_FOROF ternary pn_kid1: PNK_VAR to left of 'of', or nullptr
|
|
* pn_kid2: PNK_NAME or destructuring expr
|
|
* to left of 'of'; if pn_kid1, then this
|
|
* is a clone of pn_kid1->pn_head
|
|
* 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-obj pn_left: head expr; pn_right: body; pn_binary_obj: StaticWithObject
|
|
* PNK_VAR, list pn_head: list of PNK_NAME or PNK_ASSIGN nodes
|
|
* PNK_CONST each name node has either
|
|
* 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
|
|
* with pn_scoppecord telling (hops, slot) and pn_dflags
|
|
* 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_NAME name If pn_used, PNK_NAME uses the lexdef member instead
|
|
* of the expr member it overlays
|
|
* 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 name pn_objbox: block object in ObjectBox holder
|
|
* pn_expr: block 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_BINARY_OBJ, /* two kids, plus an objbox */
|
|
PN_TERNARY, /* three kids */
|
|
PN_CODE, /* module or function definition node */
|
|
PN_LIST, /* generic singly linked list */
|
|
PN_NAME /* name use or definition node */
|
|
};
|
|
|
|
struct Definition;
|
|
|
|
class LoopControlStatement;
|
|
class BreakStatement;
|
|
class ContinueStatement;
|
|
class ConditionalExpression;
|
|
class PropertyAccess;
|
|
|
|
class ParseNode
|
|
{
|
|
uint32_t pn_type : 16, /* PNK_* type */
|
|
pn_op : 8, /* see JSOp enum and jsopcode.tbl */
|
|
pn_arity : 4, /* see ParseNodeArity enum */
|
|
pn_parens : 1, /* this expr was enclosed in parens */
|
|
pn_used : 1, /* name node is on a use-chain */
|
|
pn_defn : 1; /* this node is a Definition */
|
|
|
|
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(0), pn_used(0), pn_defn(0),
|
|
pn_pos(0, 0), pn_next(nullptr), pn_link(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(0), pn_used(0), pn_defn(0),
|
|
pn_pos(pos), pn_next(nullptr), pn_link(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; }
|
|
bool isUsed() const { return pn_used; }
|
|
void setUsed(bool enabled) { pn_used = enabled; }
|
|
bool isDefn() const { return pn_defn; }
|
|
void setDefn(bool enabled) { pn_defn = enabled; }
|
|
|
|
static const unsigned NumDefinitionFlagBits = 10;
|
|
static const unsigned NumListFlagBits = 10;
|
|
static const unsigned NumBlockIdBits = 22;
|
|
static_assert(NumDefinitionFlagBits == NumListFlagBits,
|
|
"Assumed below to achieve consistent blockid offset");
|
|
static_assert(NumDefinitionFlagBits + NumBlockIdBits <= 32,
|
|
"This is supposed to fit in a single uint32_t");
|
|
|
|
TokenPos pn_pos; /* two 16-bit pairs here, for 64 bits */
|
|
ParseNode* pn_next; /* intrinsic link in parent PN_LIST */
|
|
|
|
/*
|
|
* Nodes that represent lexical bindings may, in addition to being
|
|
* ParseNodes, also be Definition nodes. (Definition is defined far below,
|
|
* with a lengthy comment that you should read.) Each binding has one
|
|
* canonical Definition; all uses of that definition are reached starting
|
|
* from dn_uses, then following subsequent pn_link pointers.
|
|
*
|
|
* The dn_uses chain elements are unordered. Any apparent ordering in some
|
|
* cases, will not be present in all others.
|
|
*/
|
|
union {
|
|
ParseNode* dn_uses;
|
|
ParseNode* pn_link;
|
|
};
|
|
|
|
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:NumListFlagBits, /* see PNX_* below */
|
|
blockid:NumBlockIdBits; /* see name variant 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 */
|
|
ObjectBox* objbox; /* only for PN_BINARY_OBJ */
|
|
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; /* block or regexp object */
|
|
FunctionBox* funbox; /* function object */
|
|
ModuleBox* modulebox; /* module object */
|
|
};
|
|
union {
|
|
ParseNode* expr; /* module or function body, var
|
|
initializer, argument default, or
|
|
base object of PNK_DOT */
|
|
Definition* lexdef; /* lexical definition for this use */
|
|
};
|
|
PackedScopeCoordinate scopeCoord;
|
|
uint32_t dflags:NumDefinitionFlagBits, /* see PND_* below */
|
|
blockid:NumBlockIdBits; /* block number, for subset dominance
|
|
computation */
|
|
} name;
|
|
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_modulebox pn_u.name.modulebox
|
|
#define pn_objbox pn_u.name.objbox
|
|
#define pn_funbox pn_u.name.funbox
|
|
#define pn_body pn_u.name.expr
|
|
#define pn_scopecoord pn_u.name.scopeCoord
|
|
#define pn_dflags pn_u.name.dflags
|
|
#define pn_blockid pn_u.name.blockid
|
|
#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_binary_obj pn_u.binary.objbox
|
|
#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_lexdef pn_u.name.lexdef
|
|
#define pn_dval pn_u.number.value
|
|
|
|
protected:
|
|
void init(TokenKind type, JSOp op, ParseNodeArity arity) {
|
|
pn_type = type;
|
|
pn_op = op;
|
|
pn_arity = arity;
|
|
pn_parens = false;
|
|
MOZ_ASSERT(!pn_used);
|
|
MOZ_ASSERT(!pn_defn);
|
|
pn_next = pn_link = nullptr;
|
|
}
|
|
|
|
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<FullParseHandler>* pc);
|
|
|
|
inline PropertyName* name() const;
|
|
inline JSAtom* atom() const;
|
|
|
|
/*
|
|
* The pn_expr and lexdef members are arms of an unsafe union. Unless you
|
|
* know exactly what you're doing, use only the following methods to access
|
|
* them. For less overhead and assertions for protection, use pn->expr()
|
|
* and pn->lexdef(). Otherwise, use pn->maybeExpr() and pn->maybeLexDef().
|
|
*/
|
|
ParseNode* expr() const {
|
|
MOZ_ASSERT(!pn_used);
|
|
MOZ_ASSERT(pn_arity == PN_NAME || pn_arity == PN_CODE);
|
|
return pn_expr;
|
|
}
|
|
|
|
Definition* lexdef() const {
|
|
MOZ_ASSERT(pn_used || isDeoptimized());
|
|
MOZ_ASSERT(pn_arity == PN_NAME);
|
|
return pn_lexdef;
|
|
}
|
|
|
|
ParseNode* maybeExpr() { return pn_used ? nullptr : expr(); }
|
|
Definition* maybeLexDef() { return pn_used ? lexdef() : nullptr; }
|
|
|
|
Definition* resolve();
|
|
|
|
/* PN_CODE and PN_NAME pn_dflags bits. */
|
|
#define PND_LEXICAL 0x01 /* lexical (block-scoped) binding or use of a hoisted
|
|
let or const */
|
|
#define PND_CONST 0x02 /* const binding (orthogonal to let) */
|
|
#define PND_ASSIGNED 0x04 /* set if ever LHS of assignment */
|
|
#define PND_PLACEHOLDER 0x08 /* placeholder definition for lexdep */
|
|
#define PND_BOUND 0x10 /* bound to a stack or global slot */
|
|
#define PND_DEOPTIMIZED 0x20 /* former pn_used name node, pn_lexdef
|
|
still valid, but this use no longer
|
|
optimizable via an upvar opcode */
|
|
#define PND_CLOSED 0x40 /* variable is closed over */
|
|
#define PND_KNOWNALIASED 0x80 /* definition known to be aliased and
|
|
already has a translated pnk_scopecoord */
|
|
#define PND_IMPLICITARGUMENTS 0x100 /* the definition is a placeholder for
|
|
'arguments' that has been converted
|
|
into a definition after the function
|
|
body has been parsed. */
|
|
#define PND_IMPORT 0x200 /* the definition is a module import. */
|
|
|
|
static_assert(PND_IMPORT < (1 << NumDefinitionFlagBits), "Not enough bits");
|
|
|
|
/* Flags to propagate from uses to definition. */
|
|
#define PND_USE2DEF_FLAGS (PND_ASSIGNED | PND_CLOSED)
|
|
|
|
/* PN_LIST pn_xflags bits. */
|
|
#define PNX_FUNCDEFS 0x01 /* contains top-level function statements */
|
|
#define PNX_SETCALL 0x02 /* call expression in lvalue context */
|
|
#define PNX_ARRAYHOLESPREAD 0x04 /* one or more of
|
|
1. array initialiser has holes
|
|
2. array initializer has spread node */
|
|
#define PNX_NONCONST 0x08 /* initialiser has non-constants */
|
|
|
|
static_assert(PNX_NONCONST < (1 << NumListFlagBits), "Not enough bits");
|
|
|
|
uint32_t frameSlot() const {
|
|
MOZ_ASSERT(pn_arity == PN_CODE || pn_arity == PN_NAME);
|
|
return pn_scopecoord.slot();
|
|
}
|
|
|
|
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_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_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;
|
|
}
|
|
|
|
inline bool test(unsigned flag) const;
|
|
|
|
bool isLexical() const { return test(PND_LEXICAL) && !isUsed(); }
|
|
bool isConst() const { return test(PND_CONST); }
|
|
bool isPlaceholder() const { return test(PND_PLACEHOLDER); }
|
|
bool isDeoptimized() const { return test(PND_DEOPTIMIZED); }
|
|
bool isAssigned() const { return test(PND_ASSIGNED); }
|
|
bool isClosed() const { return test(PND_CLOSED); }
|
|
bool isBound() const { return test(PND_BOUND); }
|
|
bool isImplicitArguments() const { return test(PND_IMPLICITARGUMENTS); }
|
|
bool isHoistedLexicalUse() const { return test(PND_LEXICAL) && isUsed(); }
|
|
bool isKnownAliased() const { return test(PND_KNOWNALIASED); }
|
|
bool isImport() const { return test(PND_IMPORT); }
|
|
|
|
/* 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; }
|
|
|
|
#ifdef JS_HAS_GENERATOR_EXPRS
|
|
ParseNode* generatorExpr() const {
|
|
MOZ_ASSERT(isKind(PNK_GENEXP));
|
|
ParseNode* callee = this->pn_head;
|
|
ParseNode* body = callee->pn_body;
|
|
MOZ_ASSERT(body->isKind(PNK_STATEMENTLIST));
|
|
MOZ_ASSERT(body->last()->isKind(PNK_LEXICALSCOPE) ||
|
|
body->last()->isKind(PNK_COMPREHENSIONFOR));
|
|
return body->last();
|
|
}
|
|
#endif
|
|
|
|
inline void markAsAssigned();
|
|
|
|
/*
|
|
* 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;
|
|
pn_blockid = 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;
|
|
pn_blockid = 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
|
|
};
|
|
|
|
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 BinaryObjNode : public ParseNode
|
|
{
|
|
BinaryObjNode(ParseNodeKind kind, JSOp op, const TokenPos& pos, ParseNode* left, ParseNode* right,
|
|
ObjectBox* objbox)
|
|
: ParseNode(kind, op, PN_BINARY_OBJ, pos)
|
|
{
|
|
pn_left = left;
|
|
pn_right = right;
|
|
pn_binary_obj = objbox;
|
|
}
|
|
|
|
#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);
|
|
MOZ_ASSERT(pn_dflags == 0);
|
|
pn_scopecoord.makeFree();
|
|
}
|
|
|
|
public:
|
|
#ifdef DEBUG
|
|
void dump(int indent);
|
|
#endif
|
|
};
|
|
|
|
struct NameNode : public ParseNode
|
|
{
|
|
NameNode(ParseNodeKind kind, JSOp op, JSAtom* atom, uint32_t blockid,
|
|
const TokenPos& pos)
|
|
: ParseNode(kind, op, PN_NAME, pos)
|
|
{
|
|
pn_atom = atom;
|
|
pn_expr = nullptr;
|
|
pn_scopecoord.makeFree();
|
|
pn_dflags = 0;
|
|
pn_blockid = blockid;
|
|
MOZ_ASSERT(pn_blockid == blockid); // check for bitfield overflow
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void dump(int indent);
|
|
#endif
|
|
};
|
|
|
|
struct LexicalScopeNode : public ParseNode
|
|
{
|
|
LexicalScopeNode(ObjectBox* blockBox, const TokenPos& pos)
|
|
: ParseNode(PNK_LEXICALSCOPE, JSOP_NOP, PN_NAME, pos)
|
|
{
|
|
MOZ_ASSERT(!pn_expr);
|
|
MOZ_ASSERT(pn_dflags == 0);
|
|
MOZ_ASSERT(pn_blockid == 0);
|
|
pn_objbox = blockBox;
|
|
pn_scopecoord.makeFree();
|
|
}
|
|
|
|
LexicalScopeNode(ObjectBox* blockBox, ParseNode* blockNode)
|
|
: ParseNode(PNK_LEXICALSCOPE, JSOP_NOP, PN_NAME, blockNode->pn_pos)
|
|
{
|
|
pn_objbox = blockBox;
|
|
pn_expr = blockNode;
|
|
pn_blockid = blockNode->pn_blockid;
|
|
}
|
|
|
|
static bool test(const ParseNode& node) {
|
|
return node.isKind(PNK_LEXICALSCOPE);
|
|
}
|
|
};
|
|
|
|
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);
|
|
}
|
|
|
|
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->pn_expr;
|
|
MOZ_ASSERT(list->isKind(PNK_CLASSMETHODLIST));
|
|
return list;
|
|
}
|
|
ObjectBox* scopeObject() const {
|
|
MOZ_ASSERT(pn_kid3->is<LexicalScopeNode>());
|
|
return pn_kid3->pn_objbox;
|
|
}
|
|
};
|
|
|
|
#ifdef DEBUG
|
|
void DumpParseTree(ParseNode* pn, int indent = 0);
|
|
#endif
|
|
|
|
/*
|
|
* js::Definition is a degenerate subtype of the PN_FUNC and PN_NAME variants
|
|
* of js::ParseNode, allocated only for function, var, const, and let
|
|
* declarations that define truly lexical bindings. This means that a child of
|
|
* a PNK_VAR list may be a Definition as well as a ParseNode. The pn_defn bit
|
|
* is set for all Definitions, clear otherwise.
|
|
*
|
|
* In an upvars list, defn->resolve() is the outermost definition the
|
|
* name may reference. If a with block or a function that calls eval encloses
|
|
* the use, the name may end up referring to something else at runtime.
|
|
*
|
|
* Note that not all var declarations are definitions: JS allows multiple var
|
|
* declarations in a function or script, but only the first creates the hoisted
|
|
* binding. JS programmers do redeclare variables for good refactoring reasons,
|
|
* for example:
|
|
*
|
|
* function foo() {
|
|
* ...
|
|
* for (var i ...) ...;
|
|
* ...
|
|
* for (var i ...) ...;
|
|
* ...
|
|
* }
|
|
*
|
|
* Not all definitions bind lexical variables, alas. In global and eval code
|
|
* var may re-declare a pre-existing property having any attributes, with or
|
|
* without JSPROP_PERMANENT. In eval code, indeed, ECMA-262 Editions 1 through
|
|
* 3 require function and var to bind deletable bindings. Global vars thus are
|
|
* properties of the global object, so they can be aliased even if they can't
|
|
* be deleted.
|
|
*
|
|
* Only bindings within function code may be treated as lexical, of course with
|
|
* the caveat that hoisting means use before initialization is allowed. We deal
|
|
* with use before declaration in one pass as follows (error checking elided):
|
|
*
|
|
* for (each use of unqualified name x in parse order) {
|
|
* if (this use of x is a declaration) {
|
|
* if (x in pc->decls) { // redeclaring
|
|
* pn = allocate a PN_NAME ParseNode;
|
|
* } else { // defining
|
|
* dn = lookup x in pc->lexdeps;
|
|
* if (dn) // use before def
|
|
* remove x from pc->lexdeps;
|
|
* else // def before use
|
|
* dn = allocate a PN_NAME Definition;
|
|
* map x to dn via pc->decls;
|
|
* pn = dn;
|
|
* }
|
|
* insert pn into its parent PNK_VAR/PNK_CONST list;
|
|
* } else {
|
|
* pn = allocate a ParseNode for this reference to x;
|
|
* dn = lookup x in pc's lexical scope chain;
|
|
* if (!dn) {
|
|
* dn = lookup x in pc->lexdeps;
|
|
* if (!dn) {
|
|
* dn = pre-allocate a Definition for x;
|
|
* map x to dn in pc->lexdeps;
|
|
* }
|
|
* }
|
|
* append pn to dn's use chain;
|
|
* }
|
|
* }
|
|
*
|
|
* See frontend/BytecodeEmitter.h for js::ParseContext and its top*Stmt,
|
|
* decls, and lexdeps members.
|
|
*
|
|
* Notes:
|
|
*
|
|
* 0. To avoid bloating ParseNode, we steal a bit from pn_arity for pn_defn
|
|
* and set it on a ParseNode instead of allocating a Definition.
|
|
*
|
|
* 1. Due to hoisting, a definition cannot be eliminated even if its "Variable
|
|
* statement" (ECMA-262 12.2) can be proven to be dead code. RecycleTree in
|
|
* ParseNode.cpp will not recycle a node whose pn_defn bit is set.
|
|
*
|
|
* 2. "lookup x in pc's lexical scope chain" gives up on def/use chaining if a
|
|
* with statement is found along the the scope chain, which includes pc,
|
|
* pc->parent, etc. Thus we eagerly connect an inner function's use of an
|
|
* outer's var x if the var x was parsed before the inner function.
|
|
*
|
|
* 3. A use may be eliminated as dead by the constant folder, which therefore
|
|
* must remove the dead name node from its singly-linked use chain, which
|
|
* would mean hashing to find the definition node and searching to update
|
|
* the pn_link pointing at the use to be removed. This is costly, so as for
|
|
* dead definitions, we do not recycle dead pn_used nodes.
|
|
*
|
|
* At the end of parsing a function body or global or eval program, pc->lexdeps
|
|
* holds the lexical dependencies of the parsed unit. The name to def/use chain
|
|
* mappings are then merged into the parent pc->lexdeps.
|
|
*
|
|
* Thus if a later var x is parsed in the outer function satisfying an earlier
|
|
* inner function's use of x, we will remove dn from pc->lexdeps and re-use it
|
|
* as the new definition node in the outer function's parse tree.
|
|
*
|
|
* When the compiler unwinds from the outermost pc, pc->lexdeps contains the
|
|
* definition nodes with use chains for all free variables. These are either
|
|
* global variables or reference errors.
|
|
*/
|
|
struct Definition : public ParseNode
|
|
{
|
|
bool isFreeVar() const {
|
|
MOZ_ASSERT(isDefn());
|
|
return pn_scopecoord.isFree();
|
|
}
|
|
|
|
enum Kind {
|
|
MISSING = 0,
|
|
VAR,
|
|
CONSTANT,
|
|
LET,
|
|
ARG,
|
|
NAMED_LAMBDA,
|
|
PLACEHOLDER,
|
|
IMPORT
|
|
};
|
|
|
|
static bool test(const ParseNode& pn) { return pn.isDefn(); }
|
|
|
|
bool canHaveInitializer() { return int(kind()) <= int(ARG); }
|
|
|
|
static const char* kindString(Kind kind);
|
|
|
|
Kind kind() {
|
|
if (getKind() == PNK_FUNCTION) {
|
|
if (isOp(JSOP_GETARG))
|
|
return ARG;
|
|
if (isOp(JSOP_INITLEXICAL))
|
|
return LET;
|
|
return VAR;
|
|
}
|
|
MOZ_ASSERT(getKind() == PNK_NAME);
|
|
if (isOp(JSOP_CALLEE))
|
|
return NAMED_LAMBDA;
|
|
if (isPlaceholder())
|
|
return PLACEHOLDER;
|
|
if (isOp(JSOP_GETARG))
|
|
return ARG;
|
|
if (isImport())
|
|
return IMPORT;
|
|
if (isLexical())
|
|
return isConst() ? CONSTANT : LET;
|
|
return VAR;
|
|
}
|
|
};
|
|
|
|
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::test(unsigned flag) const
|
|
{
|
|
MOZ_ASSERT(pn_defn || pn_arity == PN_CODE || pn_arity == PN_NAME);
|
|
#ifdef DEBUG
|
|
if ((flag & PND_ASSIGNED) && pn_defn && !(pn_dflags & flag)) {
|
|
for (ParseNode* pn = ((Definition*) this)->dn_uses; pn; pn = pn->pn_link) {
|
|
MOZ_ASSERT(!pn->pn_defn);
|
|
MOZ_ASSERT(!(pn->pn_dflags & flag));
|
|
}
|
|
}
|
|
#endif
|
|
return !!(pn_dflags & flag);
|
|
}
|
|
|
|
inline void
|
|
ParseNode::markAsAssigned()
|
|
{
|
|
MOZ_ASSERT(CodeSpec[pn_op].format & JOF_NAME);
|
|
if (isUsed())
|
|
pn_lexdef->pn_dflags |= PND_ASSIGNED;
|
|
pn_dflags |= PND_ASSIGNED;
|
|
}
|
|
|
|
inline Definition*
|
|
ParseNode::resolve()
|
|
{
|
|
if (isDefn())
|
|
return (Definition*)this;
|
|
MOZ_ASSERT(lexdef()->isDefn());
|
|
return (Definition*)lexdef();
|
|
}
|
|
|
|
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();
|
|
bool isModuleBox() { return object->is<ModuleObject>(); }
|
|
ModuleBox* asModuleBox();
|
|
void trace(JSTracer* trc);
|
|
|
|
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 ParseNode*
|
|
FunctionArgsList(ParseNode* fn, unsigned* numFormals)
|
|
{
|
|
MOZ_ASSERT(fn->isKind(PNK_FUNCTION));
|
|
ParseNode* argsBody = fn->pn_body;
|
|
MOZ_ASSERT(argsBody->isKind(PNK_ARGSBODY));
|
|
*numFormals = argsBody->pn_count;
|
|
if (*numFormals > 0 && argsBody->last()->isKind(PNK_STATEMENTLIST))
|
|
(*numFormals)--;
|
|
MOZ_ASSERT(argsBody->isArity(PN_LIST));
|
|
return argsBody->pn_head;
|
|
}
|
|
|
|
} /* namespace frontend */
|
|
} /* namespace js */
|
|
|
|
#endif /* frontend_ParseNode_h */
|