michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "frontend/ParseNode-inl.h" michael@0: michael@0: #include "frontend/Parser.h" michael@0: michael@0: #include "jscntxtinlines.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::frontend; michael@0: michael@0: using mozilla::IsFinite; michael@0: michael@0: /* michael@0: * Asserts to verify assumptions behind pn_ macros. michael@0: */ michael@0: #define pn_offsetof(m) offsetof(ParseNode, m) michael@0: michael@0: JS_STATIC_ASSERT(pn_offsetof(pn_link) == pn_offsetof(dn_uses)); michael@0: michael@0: #undef pn_offsetof michael@0: michael@0: #ifdef DEBUG michael@0: void michael@0: ParseNode::checkListConsistency() michael@0: { michael@0: JS_ASSERT(isArity(PN_LIST)); michael@0: ParseNode **tail; michael@0: uint32_t count = 0; michael@0: if (pn_head) { michael@0: ParseNode *pn, *last; michael@0: for (pn = last = pn_head; pn; last = pn, pn = pn->pn_next, count++) michael@0: ; michael@0: tail = &last->pn_next; michael@0: } else { michael@0: tail = &pn_head; michael@0: } michael@0: JS_ASSERT(pn_tail == tail); michael@0: JS_ASSERT(pn_count == count); michael@0: } michael@0: #endif michael@0: michael@0: /* Add |node| to |parser|'s free node list. */ michael@0: void michael@0: ParseNodeAllocator::freeNode(ParseNode *pn) michael@0: { michael@0: /* Catch back-to-back dup recycles. */ michael@0: JS_ASSERT(pn != freelist); michael@0: michael@0: /* michael@0: * It's too hard to clear these nodes from the AtomDefnMaps, etc. that michael@0: * hold references to them, so we never free them. It's our caller's job to michael@0: * recognize and process these, since their children do need to be dealt michael@0: * with. michael@0: */ michael@0: JS_ASSERT(!pn->isUsed()); michael@0: JS_ASSERT(!pn->isDefn()); michael@0: michael@0: #ifdef DEBUG michael@0: /* Poison the node, to catch attempts to use it without initializing it. */ michael@0: memset(pn, 0xab, sizeof(*pn)); michael@0: #endif michael@0: michael@0: pn->pn_next = freelist; michael@0: freelist = pn; michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: /* michael@0: * A work pool of ParseNodes. The work pool is a stack, chained together michael@0: * by nodes' pn_next fields. We use this to avoid creating deep C++ stacks michael@0: * when recycling deep parse trees. michael@0: * michael@0: * Since parse nodes are probably allocated in something close to the order michael@0: * they appear in a depth-first traversal of the tree, making the work pool michael@0: * a stack should give us pretty good locality. michael@0: */ michael@0: class NodeStack { michael@0: public: michael@0: NodeStack() : top(nullptr) { } michael@0: bool empty() { return top == nullptr; } michael@0: void push(ParseNode *pn) { michael@0: pn->pn_next = top; michael@0: top = pn; michael@0: } michael@0: void pushUnlessNull(ParseNode *pn) { if (pn) push(pn); } michael@0: /* Push the children of the PN_LIST node |pn| on the stack. */ michael@0: void pushList(ParseNode *pn) { michael@0: /* This clobbers pn->pn_head if the list is empty; should be okay. */ michael@0: *pn->pn_tail = top; michael@0: top = pn->pn_head; michael@0: } michael@0: ParseNode *pop() { michael@0: JS_ASSERT(!empty()); michael@0: ParseNode *hold = top; /* my kingdom for a prog1 */ michael@0: top = top->pn_next; michael@0: return hold; michael@0: } michael@0: private: michael@0: ParseNode *top; michael@0: }; michael@0: michael@0: } /* anonymous namespace */ michael@0: michael@0: /* michael@0: * Push the children of |pn| on |stack|. Return true if |pn| itself could be michael@0: * safely recycled, or false if it must be cleaned later (pn_used and pn_defn michael@0: * nodes, and all function nodes; see comments for CleanFunctionList in michael@0: * SemanticAnalysis.cpp). Some callers want to free |pn|; others michael@0: * (js::ParseNodeAllocator::prepareNodeForMutation) don't care about |pn|, and michael@0: * just need to take care of its children. michael@0: */ michael@0: static bool michael@0: PushNodeChildren(ParseNode *pn, NodeStack *stack) michael@0: { michael@0: switch (pn->getArity()) { michael@0: case PN_CODE: michael@0: /* michael@0: * Function nodes are linked into the function box tree, and may appear michael@0: * on method lists. Both of those lists are singly-linked, so trying to michael@0: * update them now could result in quadratic behavior when recycling michael@0: * trees containing many functions; and the lists can be very long. So michael@0: * we put off cleaning the lists up until just before function michael@0: * analysis, when we call CleanFunctionList. michael@0: * michael@0: * In fact, we can't recycle the parse node yet, either: it may appear michael@0: * on a method list, and reusing the node would corrupt that. Instead, michael@0: * we clear its pn_funbox pointer to mark it as deleted; michael@0: * CleanFunctionList recycles it as well. michael@0: * michael@0: * We do recycle the nodes around it, though, so we must clear pointers michael@0: * to them to avoid leaving dangling references where someone can find michael@0: * them. michael@0: */ michael@0: pn->pn_funbox = nullptr; michael@0: stack->pushUnlessNull(pn->pn_body); michael@0: pn->pn_body = nullptr; michael@0: return false; michael@0: michael@0: case PN_NAME: michael@0: /* michael@0: * Because used/defn nodes appear in AtomDefnMaps and elsewhere, we michael@0: * don't recycle them. (We'll recover their storage when we free the michael@0: * temporary arena.) However, we do recycle the nodes around them, so michael@0: * clean up the pointers to avoid dangling references. The top-level michael@0: * decls table carries references to them that later iterations through michael@0: * the compileScript loop may find, so they need to be neat. michael@0: * michael@0: * pn_expr and pn_lexdef share storage; the latter isn't an owning michael@0: * reference. michael@0: */ michael@0: if (!pn->isUsed()) { michael@0: stack->pushUnlessNull(pn->pn_expr); michael@0: pn->pn_expr = nullptr; michael@0: } michael@0: return !pn->isUsed() && !pn->isDefn(); michael@0: michael@0: case PN_LIST: michael@0: pn->checkListConsistency(); michael@0: stack->pushList(pn); michael@0: break; michael@0: case PN_TERNARY: michael@0: stack->pushUnlessNull(pn->pn_kid1); michael@0: stack->pushUnlessNull(pn->pn_kid2); michael@0: stack->pushUnlessNull(pn->pn_kid3); michael@0: break; michael@0: case PN_BINARY: michael@0: case PN_BINARY_OBJ: michael@0: if (pn->pn_left != pn->pn_right) michael@0: stack->pushUnlessNull(pn->pn_left); michael@0: stack->pushUnlessNull(pn->pn_right); michael@0: break; michael@0: case PN_UNARY: michael@0: stack->pushUnlessNull(pn->pn_kid); michael@0: break; michael@0: case PN_NULLARY: michael@0: return !pn->isUsed() && !pn->isDefn(); michael@0: default: michael@0: ; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * Prepare |pn| to be mutated in place into a new kind of node. Recycle all michael@0: * |pn|'s recyclable children (but not |pn| itself!), and disconnect it from michael@0: * metadata structures (the function box tree). michael@0: */ michael@0: void michael@0: ParseNodeAllocator::prepareNodeForMutation(ParseNode *pn) michael@0: { michael@0: if (!pn->isArity(PN_NULLARY)) { michael@0: /* Put |pn|'s children (but not |pn| itself) on a work stack. */ michael@0: NodeStack stack; michael@0: PushNodeChildren(pn, &stack); michael@0: /* michael@0: * For each node on the work stack, push its children on the work stack, michael@0: * and free the node if we can. michael@0: */ michael@0: while (!stack.empty()) { michael@0: pn = stack.pop(); michael@0: if (PushNodeChildren(pn, &stack)) michael@0: freeNode(pn); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * Return the nodes in the subtree |pn| to the parser's free node list, for michael@0: * reallocation. michael@0: */ michael@0: ParseNode * michael@0: ParseNodeAllocator::freeTree(ParseNode *pn) michael@0: { michael@0: if (!pn) michael@0: return nullptr; michael@0: michael@0: ParseNode *savedNext = pn->pn_next; michael@0: michael@0: NodeStack stack; michael@0: for (;;) { michael@0: if (PushNodeChildren(pn, &stack)) michael@0: freeNode(pn); michael@0: if (stack.empty()) michael@0: break; michael@0: pn = stack.pop(); michael@0: } michael@0: michael@0: return savedNext; michael@0: } michael@0: michael@0: /* michael@0: * Allocate a ParseNode from parser's node freelist or, failing that, from michael@0: * cx's temporary arena. michael@0: */ michael@0: void * michael@0: ParseNodeAllocator::allocNode() michael@0: { michael@0: if (ParseNode *pn = freelist) { michael@0: freelist = pn->pn_next; michael@0: return pn; michael@0: } michael@0: michael@0: void *p = alloc.alloc(sizeof (ParseNode)); michael@0: if (!p) michael@0: js_ReportOutOfMemory(cx); michael@0: return p; michael@0: } michael@0: michael@0: /* used only by static create methods of subclasses */ michael@0: michael@0: ParseNode * michael@0: ParseNode::create(ParseNodeKind kind, ParseNodeArity arity, FullParseHandler *handler) michael@0: { michael@0: const Token &tok = handler->currentToken(); michael@0: return handler->new_(kind, JSOP_NOP, arity, tok.pos); michael@0: } michael@0: michael@0: ParseNode * michael@0: ParseNode::append(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right, michael@0: FullParseHandler *handler) michael@0: { michael@0: if (!left || !right) michael@0: return nullptr; michael@0: michael@0: JS_ASSERT(left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC)); michael@0: michael@0: ListNode *list; michael@0: if (left->pn_arity == PN_LIST) { michael@0: list = &left->as(); michael@0: } else { michael@0: ParseNode *pn1 = left->pn_left, *pn2 = left->pn_right; michael@0: list = handler->new_(kind, op, pn1); michael@0: if (!list) michael@0: return nullptr; michael@0: list->append(pn2); michael@0: } michael@0: michael@0: list->append(right); michael@0: list->pn_pos.end = right->pn_pos.end; michael@0: michael@0: return list; michael@0: } michael@0: michael@0: ParseNode * michael@0: ParseNode::newBinaryOrAppend(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right, michael@0: FullParseHandler *handler, ParseContext *pc, michael@0: bool foldConstants) michael@0: { michael@0: if (!left || !right) michael@0: return nullptr; michael@0: michael@0: /* michael@0: * Ensure that the parse tree is faithful to the source when "use asm" (for michael@0: * the purpose of type checking). michael@0: */ michael@0: if (pc->useAsmOrInsideUseAsm()) michael@0: return handler->new_(kind, op, left, right); michael@0: michael@0: /* michael@0: * Flatten a left-associative (left-heavy) tree of a given operator into michael@0: * a list to reduce js::FoldConstants and js::frontend::EmitTree recursion. michael@0: */ michael@0: if (left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC)) michael@0: return append(kind, op, left, right, handler); michael@0: michael@0: return handler->new_(kind, op, left, right); michael@0: } michael@0: michael@0: const char * michael@0: Definition::kindString(Kind kind) michael@0: { michael@0: static const char * const table[] = { michael@0: "", js_var_str, js_const_str, js_let_str, js_function_str, "argument", "unknown" michael@0: }; michael@0: michael@0: JS_ASSERT(unsigned(kind) <= unsigned(ARG)); michael@0: return table[kind]; michael@0: } michael@0: michael@0: namespace js { michael@0: namespace frontend { michael@0: michael@0: /* michael@0: * This function assumes the cloned tree is for use in the same statement and michael@0: * binding context as the original tree. michael@0: */ michael@0: template <> michael@0: ParseNode * michael@0: Parser::cloneParseTree(ParseNode *opn) michael@0: { michael@0: JS_CHECK_RECURSION(context, return nullptr); michael@0: michael@0: ParseNode *pn = handler.new_(opn->getKind(), opn->getOp(), opn->getArity(), michael@0: opn->pn_pos); michael@0: if (!pn) michael@0: return nullptr; michael@0: pn->setInParens(opn->isInParens()); michael@0: pn->setDefn(opn->isDefn()); michael@0: pn->setUsed(opn->isUsed()); michael@0: michael@0: switch (pn->getArity()) { michael@0: #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return nullptr; JS_END_MACRO michael@0: michael@0: case PN_CODE: michael@0: NULLCHECK(pn->pn_funbox = newFunctionBox(pn, opn->pn_funbox->function(), pc, michael@0: Directives(/* strict = */ opn->pn_funbox->strict), michael@0: opn->pn_funbox->generatorKind())); michael@0: NULLCHECK(pn->pn_body = cloneParseTree(opn->pn_body)); michael@0: pn->pn_cookie = opn->pn_cookie; michael@0: pn->pn_dflags = opn->pn_dflags; michael@0: pn->pn_blockid = opn->pn_blockid; michael@0: break; michael@0: michael@0: case PN_LIST: michael@0: pn->makeEmpty(); michael@0: for (ParseNode *opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) { michael@0: ParseNode *pn2; michael@0: NULLCHECK(pn2 = cloneParseTree(opn2)); michael@0: pn->append(pn2); michael@0: } michael@0: pn->pn_xflags = opn->pn_xflags; michael@0: break; michael@0: michael@0: case PN_TERNARY: michael@0: NULLCHECK(pn->pn_kid1 = cloneParseTree(opn->pn_kid1)); michael@0: NULLCHECK(pn->pn_kid2 = cloneParseTree(opn->pn_kid2)); michael@0: NULLCHECK(pn->pn_kid3 = cloneParseTree(opn->pn_kid3)); michael@0: break; michael@0: michael@0: case PN_BINARY: michael@0: NULLCHECK(pn->pn_left = cloneParseTree(opn->pn_left)); michael@0: if (opn->pn_right != opn->pn_left) michael@0: NULLCHECK(pn->pn_right = cloneParseTree(opn->pn_right)); michael@0: else michael@0: pn->pn_right = pn->pn_left; michael@0: pn->pn_iflags = opn->pn_iflags; michael@0: break; michael@0: michael@0: case PN_BINARY_OBJ: michael@0: NULLCHECK(pn->pn_left = cloneParseTree(opn->pn_left)); michael@0: if (opn->pn_right != opn->pn_left) michael@0: NULLCHECK(pn->pn_right = cloneParseTree(opn->pn_right)); michael@0: else michael@0: pn->pn_right = pn->pn_left; michael@0: pn->pn_binary_obj = opn->pn_binary_obj; michael@0: break; michael@0: michael@0: case PN_UNARY: michael@0: NULLCHECK(pn->pn_kid = cloneParseTree(opn->pn_kid)); michael@0: break; michael@0: michael@0: case PN_NAME: michael@0: // PN_NAME could mean several arms in pn_u, so copy the whole thing. michael@0: pn->pn_u = opn->pn_u; michael@0: if (opn->isUsed()) { michael@0: /* michael@0: * The old name is a use of its pn_lexdef. Make the clone also be a michael@0: * use of that definition. michael@0: */ michael@0: Definition *dn = pn->pn_lexdef; michael@0: michael@0: pn->pn_link = dn->dn_uses; michael@0: dn->dn_uses = pn; michael@0: } else if (opn->pn_expr) { michael@0: NULLCHECK(pn->pn_expr = cloneParseTree(opn->pn_expr)); michael@0: michael@0: /* michael@0: * If the old name is a definition, the new one has pn_defn set. michael@0: * Make the old name a use of the new node. michael@0: */ michael@0: if (opn->isDefn()) { michael@0: opn->setDefn(false); michael@0: handler.linkUseToDef(opn, (Definition *) pn); michael@0: } michael@0: } michael@0: break; michael@0: michael@0: case PN_NULLARY: michael@0: pn->pn_u = opn->pn_u; michael@0: break; michael@0: michael@0: #undef NULLCHECK michael@0: } michael@0: return pn; michael@0: } michael@0: michael@0: /* michael@0: * Used by Parser::forStatement and comprehensionTail to clone the TARGET in michael@0: * for (var/const/let TARGET in EXPR) michael@0: * michael@0: * opn must be the pn_head of a node produced by Parser::variables, so its form michael@0: * is known to be LHS = NAME | [LHS] | {id:LHS}. michael@0: * michael@0: * The cloned tree is for use only in the same statement and binding context as michael@0: * the original tree. michael@0: */ michael@0: template <> michael@0: ParseNode * michael@0: Parser::cloneLeftHandSide(ParseNode *opn) michael@0: { michael@0: ParseNode *pn = handler.new_(opn->getKind(), opn->getOp(), opn->getArity(), michael@0: opn->pn_pos); michael@0: if (!pn) michael@0: return nullptr; michael@0: pn->setInParens(opn->isInParens()); michael@0: pn->setDefn(opn->isDefn()); michael@0: pn->setUsed(opn->isUsed()); michael@0: michael@0: if (opn->isArity(PN_LIST)) { michael@0: JS_ASSERT(opn->isKind(PNK_ARRAY) || opn->isKind(PNK_OBJECT)); michael@0: pn->makeEmpty(); michael@0: for (ParseNode *opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) { michael@0: ParseNode *pn2; michael@0: if (opn->isKind(PNK_OBJECT)) { michael@0: JS_ASSERT(opn2->isArity(PN_BINARY)); michael@0: JS_ASSERT(opn2->isKind(PNK_COLON)); michael@0: michael@0: ParseNode *tag = cloneParseTree(opn2->pn_left); michael@0: if (!tag) michael@0: return nullptr; michael@0: ParseNode *target = cloneLeftHandSide(opn2->pn_right); michael@0: if (!target) michael@0: return nullptr; michael@0: michael@0: pn2 = handler.new_(PNK_COLON, JSOP_INITPROP, opn2->pn_pos, tag, target); michael@0: } else if (opn2->isArity(PN_NULLARY)) { michael@0: JS_ASSERT(opn2->isKind(PNK_ELISION)); michael@0: pn2 = cloneParseTree(opn2); michael@0: } else { michael@0: pn2 = cloneLeftHandSide(opn2); michael@0: } michael@0: michael@0: if (!pn2) michael@0: return nullptr; michael@0: pn->append(pn2); michael@0: } michael@0: pn->pn_xflags = opn->pn_xflags; michael@0: return pn; michael@0: } michael@0: michael@0: JS_ASSERT(opn->isArity(PN_NAME)); michael@0: JS_ASSERT(opn->isKind(PNK_NAME)); michael@0: michael@0: /* If opn is a definition or use, make pn a use. */ michael@0: pn->pn_u.name = opn->pn_u.name; michael@0: pn->setOp(JSOP_SETNAME); michael@0: if (opn->isUsed()) { michael@0: Definition *dn = pn->pn_lexdef; michael@0: michael@0: pn->pn_link = dn->dn_uses; michael@0: dn->dn_uses = pn; michael@0: } else { michael@0: pn->pn_expr = nullptr; michael@0: if (opn->isDefn()) { michael@0: /* We copied some definition-specific state into pn. Clear it out. */ michael@0: pn->pn_cookie.makeFree(); michael@0: pn->pn_dflags &= ~PND_BOUND; michael@0: pn->setDefn(false); michael@0: michael@0: handler.linkUseToDef(pn, (Definition *) opn); michael@0: } michael@0: } michael@0: return pn; michael@0: } michael@0: michael@0: } /* namespace frontend */ michael@0: } /* namespace js */ michael@0: michael@0: #ifdef DEBUG michael@0: michael@0: static const char * const parseNodeNames[] = { michael@0: #define STRINGIFY(name) #name, michael@0: FOR_EACH_PARSE_NODE_KIND(STRINGIFY) michael@0: #undef STRINGIFY michael@0: }; michael@0: michael@0: void michael@0: frontend::DumpParseTree(ParseNode *pn, int indent) michael@0: { michael@0: if (pn == nullptr) michael@0: fprintf(stderr, "#NULL"); michael@0: else michael@0: pn->dump(indent); michael@0: } michael@0: michael@0: static void michael@0: IndentNewLine(int indent) michael@0: { michael@0: fputc('\n', stderr); michael@0: for (int i = 0; i < indent; ++i) michael@0: fputc(' ', stderr); michael@0: } michael@0: michael@0: void michael@0: ParseNode::dump() michael@0: { michael@0: dump(0); michael@0: fputc('\n', stderr); michael@0: } michael@0: michael@0: void michael@0: ParseNode::dump(int indent) michael@0: { michael@0: switch (pn_arity) { michael@0: case PN_NULLARY: michael@0: ((NullaryNode *) this)->dump(); michael@0: break; michael@0: case PN_UNARY: michael@0: ((UnaryNode *) this)->dump(indent); michael@0: break; michael@0: case PN_BINARY: michael@0: ((BinaryNode *) this)->dump(indent); michael@0: break; michael@0: case PN_BINARY_OBJ: michael@0: ((BinaryObjNode *) this)->dump(indent); michael@0: break; michael@0: case PN_TERNARY: michael@0: ((TernaryNode *) this)->dump(indent); michael@0: break; michael@0: case PN_CODE: michael@0: ((CodeNode *) this)->dump(indent); michael@0: break; michael@0: case PN_LIST: michael@0: ((ListNode *) this)->dump(indent); michael@0: break; michael@0: case PN_NAME: michael@0: ((NameNode *) this)->dump(indent); michael@0: break; michael@0: default: michael@0: fprintf(stderr, "#", michael@0: (void *) this, unsigned(getKind()), unsigned(pn_arity)); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: NullaryNode::dump() michael@0: { michael@0: switch (getKind()) { michael@0: case PNK_TRUE: fprintf(stderr, "#true"); break; michael@0: case PNK_FALSE: fprintf(stderr, "#false"); break; michael@0: case PNK_NULL: fprintf(stderr, "#null"); break; michael@0: michael@0: case PNK_NUMBER: { michael@0: ToCStringBuf cbuf; michael@0: const char *cstr = NumberToCString(nullptr, &cbuf, pn_dval); michael@0: if (!IsFinite(pn_dval)) michael@0: fputc('#', stderr); michael@0: if (cstr) michael@0: fprintf(stderr, "%s", cstr); michael@0: else michael@0: fprintf(stderr, "%g", pn_dval); michael@0: break; michael@0: } michael@0: michael@0: case PNK_STRING: michael@0: JSString::dumpChars(pn_atom->chars(), pn_atom->length()); michael@0: break; michael@0: michael@0: default: michael@0: fprintf(stderr, "(%s)", parseNodeNames[getKind()]); michael@0: } michael@0: } michael@0: michael@0: void michael@0: UnaryNode::dump(int indent) michael@0: { michael@0: const char *name = parseNodeNames[getKind()]; michael@0: fprintf(stderr, "(%s ", name); michael@0: indent += strlen(name) + 2; michael@0: DumpParseTree(pn_kid, indent); michael@0: fprintf(stderr, ")"); michael@0: } michael@0: michael@0: void michael@0: BinaryNode::dump(int indent) michael@0: { michael@0: const char *name = parseNodeNames[getKind()]; michael@0: fprintf(stderr, "(%s ", name); michael@0: indent += strlen(name) + 2; michael@0: DumpParseTree(pn_left, indent); michael@0: IndentNewLine(indent); michael@0: DumpParseTree(pn_right, indent); michael@0: fprintf(stderr, ")"); michael@0: } michael@0: michael@0: void michael@0: BinaryObjNode::dump(int indent) michael@0: { michael@0: const char *name = parseNodeNames[getKind()]; michael@0: fprintf(stderr, "(%s ", name); michael@0: indent += strlen(name) + 2; michael@0: DumpParseTree(pn_left, indent); michael@0: IndentNewLine(indent); michael@0: DumpParseTree(pn_right, indent); michael@0: fprintf(stderr, ")"); michael@0: } michael@0: michael@0: void michael@0: TernaryNode::dump(int indent) michael@0: { michael@0: const char *name = parseNodeNames[getKind()]; michael@0: fprintf(stderr, "(%s ", name); michael@0: indent += strlen(name) + 2; michael@0: DumpParseTree(pn_kid1, indent); michael@0: IndentNewLine(indent); michael@0: DumpParseTree(pn_kid2, indent); michael@0: IndentNewLine(indent); michael@0: DumpParseTree(pn_kid3, indent); michael@0: fprintf(stderr, ")"); michael@0: } michael@0: michael@0: void michael@0: CodeNode::dump(int indent) michael@0: { michael@0: const char *name = parseNodeNames[getKind()]; michael@0: fprintf(stderr, "(%s ", name); michael@0: indent += strlen(name) + 2; michael@0: DumpParseTree(pn_body, indent); michael@0: fprintf(stderr, ")"); michael@0: } michael@0: michael@0: void michael@0: ListNode::dump(int indent) michael@0: { michael@0: const char *name = parseNodeNames[getKind()]; michael@0: fprintf(stderr, "(%s [", name); michael@0: if (pn_head != nullptr) { michael@0: indent += strlen(name) + 3; michael@0: DumpParseTree(pn_head, indent); michael@0: ParseNode *pn = pn_head->pn_next; michael@0: while (pn != nullptr) { michael@0: IndentNewLine(indent); michael@0: DumpParseTree(pn, indent); michael@0: pn = pn->pn_next; michael@0: } michael@0: } michael@0: fprintf(stderr, "])"); michael@0: } michael@0: michael@0: void michael@0: NameNode::dump(int indent) michael@0: { michael@0: if (isKind(PNK_NAME) || isKind(PNK_DOT)) { michael@0: if (isKind(PNK_DOT)) michael@0: fprintf(stderr, "(."); michael@0: michael@0: if (!pn_atom) { michael@0: fprintf(stderr, "#"); michael@0: } else { michael@0: const jschar *s = pn_atom->chars(); michael@0: size_t len = pn_atom->length(); michael@0: if (len == 0) michael@0: fprintf(stderr, "#"); michael@0: for (size_t i = 0; i < len; i++) { michael@0: if (s[i] > 32 && s[i] < 127) michael@0: fputc(s[i], stderr); michael@0: else if (s[i] <= 255) michael@0: fprintf(stderr, "\\x%02x", (unsigned int) s[i]); michael@0: else michael@0: fprintf(stderr, "\\u%04x", (unsigned int) s[i]); michael@0: } michael@0: } michael@0: michael@0: if (isKind(PNK_DOT)) { michael@0: fputc(' ', stderr); michael@0: DumpParseTree(expr(), indent + 2); michael@0: fputc(')', stderr); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: JS_ASSERT(!isUsed()); michael@0: const char *name = parseNodeNames[getKind()]; michael@0: if (isUsed()) michael@0: fprintf(stderr, "(%s)", name); michael@0: else { michael@0: fprintf(stderr, "(%s ", name); michael@0: indent += strlen(name) + 2; michael@0: DumpParseTree(expr(), indent); michael@0: fprintf(stderr, ")"); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: ObjectBox::ObjectBox(JSObject *object, ObjectBox* traceLink) michael@0: : object(object), michael@0: traceLink(traceLink), michael@0: emitLink(nullptr) michael@0: { michael@0: JS_ASSERT(!object->is()); michael@0: } michael@0: michael@0: ObjectBox::ObjectBox(JSFunction *function, ObjectBox* traceLink) michael@0: : object(function), michael@0: traceLink(traceLink), michael@0: emitLink(nullptr) michael@0: { michael@0: JS_ASSERT(object->is()); michael@0: JS_ASSERT(asFunctionBox()->function() == function); michael@0: } michael@0: michael@0: FunctionBox * michael@0: ObjectBox::asFunctionBox() michael@0: { michael@0: JS_ASSERT(isFunctionBox()); michael@0: return static_cast(this); michael@0: } michael@0: michael@0: void michael@0: ObjectBox::trace(JSTracer *trc) michael@0: { michael@0: ObjectBox *box = this; michael@0: while (box) { michael@0: MarkObjectRoot(trc, &box->object, "parser.object"); michael@0: if (box->isFunctionBox()) michael@0: box->asFunctionBox()->bindings.trace(trc); michael@0: box = box->traceLink; michael@0: } michael@0: }