Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "frontend/ParseNode-inl.h"
9 #include "frontend/Parser.h"
11 #include "jscntxtinlines.h"
13 using namespace js;
14 using namespace js::frontend;
16 using mozilla::IsFinite;
18 /*
19 * Asserts to verify assumptions behind pn_ macros.
20 */
21 #define pn_offsetof(m) offsetof(ParseNode, m)
23 JS_STATIC_ASSERT(pn_offsetof(pn_link) == pn_offsetof(dn_uses));
25 #undef pn_offsetof
27 #ifdef DEBUG
28 void
29 ParseNode::checkListConsistency()
30 {
31 JS_ASSERT(isArity(PN_LIST));
32 ParseNode **tail;
33 uint32_t count = 0;
34 if (pn_head) {
35 ParseNode *pn, *last;
36 for (pn = last = pn_head; pn; last = pn, pn = pn->pn_next, count++)
37 ;
38 tail = &last->pn_next;
39 } else {
40 tail = &pn_head;
41 }
42 JS_ASSERT(pn_tail == tail);
43 JS_ASSERT(pn_count == count);
44 }
45 #endif
47 /* Add |node| to |parser|'s free node list. */
48 void
49 ParseNodeAllocator::freeNode(ParseNode *pn)
50 {
51 /* Catch back-to-back dup recycles. */
52 JS_ASSERT(pn != freelist);
54 /*
55 * It's too hard to clear these nodes from the AtomDefnMaps, etc. that
56 * hold references to them, so we never free them. It's our caller's job to
57 * recognize and process these, since their children do need to be dealt
58 * with.
59 */
60 JS_ASSERT(!pn->isUsed());
61 JS_ASSERT(!pn->isDefn());
63 #ifdef DEBUG
64 /* Poison the node, to catch attempts to use it without initializing it. */
65 memset(pn, 0xab, sizeof(*pn));
66 #endif
68 pn->pn_next = freelist;
69 freelist = pn;
70 }
72 namespace {
74 /*
75 * A work pool of ParseNodes. The work pool is a stack, chained together
76 * by nodes' pn_next fields. We use this to avoid creating deep C++ stacks
77 * when recycling deep parse trees.
78 *
79 * Since parse nodes are probably allocated in something close to the order
80 * they appear in a depth-first traversal of the tree, making the work pool
81 * a stack should give us pretty good locality.
82 */
83 class NodeStack {
84 public:
85 NodeStack() : top(nullptr) { }
86 bool empty() { return top == nullptr; }
87 void push(ParseNode *pn) {
88 pn->pn_next = top;
89 top = pn;
90 }
91 void pushUnlessNull(ParseNode *pn) { if (pn) push(pn); }
92 /* Push the children of the PN_LIST node |pn| on the stack. */
93 void pushList(ParseNode *pn) {
94 /* This clobbers pn->pn_head if the list is empty; should be okay. */
95 *pn->pn_tail = top;
96 top = pn->pn_head;
97 }
98 ParseNode *pop() {
99 JS_ASSERT(!empty());
100 ParseNode *hold = top; /* my kingdom for a prog1 */
101 top = top->pn_next;
102 return hold;
103 }
104 private:
105 ParseNode *top;
106 };
108 } /* anonymous namespace */
110 /*
111 * Push the children of |pn| on |stack|. Return true if |pn| itself could be
112 * safely recycled, or false if it must be cleaned later (pn_used and pn_defn
113 * nodes, and all function nodes; see comments for CleanFunctionList in
114 * SemanticAnalysis.cpp). Some callers want to free |pn|; others
115 * (js::ParseNodeAllocator::prepareNodeForMutation) don't care about |pn|, and
116 * just need to take care of its children.
117 */
118 static bool
119 PushNodeChildren(ParseNode *pn, NodeStack *stack)
120 {
121 switch (pn->getArity()) {
122 case PN_CODE:
123 /*
124 * Function nodes are linked into the function box tree, and may appear
125 * on method lists. Both of those lists are singly-linked, so trying to
126 * update them now could result in quadratic behavior when recycling
127 * trees containing many functions; and the lists can be very long. So
128 * we put off cleaning the lists up until just before function
129 * analysis, when we call CleanFunctionList.
130 *
131 * In fact, we can't recycle the parse node yet, either: it may appear
132 * on a method list, and reusing the node would corrupt that. Instead,
133 * we clear its pn_funbox pointer to mark it as deleted;
134 * CleanFunctionList recycles it as well.
135 *
136 * We do recycle the nodes around it, though, so we must clear pointers
137 * to them to avoid leaving dangling references where someone can find
138 * them.
139 */
140 pn->pn_funbox = nullptr;
141 stack->pushUnlessNull(pn->pn_body);
142 pn->pn_body = nullptr;
143 return false;
145 case PN_NAME:
146 /*
147 * Because used/defn nodes appear in AtomDefnMaps and elsewhere, we
148 * don't recycle them. (We'll recover their storage when we free the
149 * temporary arena.) However, we do recycle the nodes around them, so
150 * clean up the pointers to avoid dangling references. The top-level
151 * decls table carries references to them that later iterations through
152 * the compileScript loop may find, so they need to be neat.
153 *
154 * pn_expr and pn_lexdef share storage; the latter isn't an owning
155 * reference.
156 */
157 if (!pn->isUsed()) {
158 stack->pushUnlessNull(pn->pn_expr);
159 pn->pn_expr = nullptr;
160 }
161 return !pn->isUsed() && !pn->isDefn();
163 case PN_LIST:
164 pn->checkListConsistency();
165 stack->pushList(pn);
166 break;
167 case PN_TERNARY:
168 stack->pushUnlessNull(pn->pn_kid1);
169 stack->pushUnlessNull(pn->pn_kid2);
170 stack->pushUnlessNull(pn->pn_kid3);
171 break;
172 case PN_BINARY:
173 case PN_BINARY_OBJ:
174 if (pn->pn_left != pn->pn_right)
175 stack->pushUnlessNull(pn->pn_left);
176 stack->pushUnlessNull(pn->pn_right);
177 break;
178 case PN_UNARY:
179 stack->pushUnlessNull(pn->pn_kid);
180 break;
181 case PN_NULLARY:
182 return !pn->isUsed() && !pn->isDefn();
183 default:
184 ;
185 }
187 return true;
188 }
190 /*
191 * Prepare |pn| to be mutated in place into a new kind of node. Recycle all
192 * |pn|'s recyclable children (but not |pn| itself!), and disconnect it from
193 * metadata structures (the function box tree).
194 */
195 void
196 ParseNodeAllocator::prepareNodeForMutation(ParseNode *pn)
197 {
198 if (!pn->isArity(PN_NULLARY)) {
199 /* Put |pn|'s children (but not |pn| itself) on a work stack. */
200 NodeStack stack;
201 PushNodeChildren(pn, &stack);
202 /*
203 * For each node on the work stack, push its children on the work stack,
204 * and free the node if we can.
205 */
206 while (!stack.empty()) {
207 pn = stack.pop();
208 if (PushNodeChildren(pn, &stack))
209 freeNode(pn);
210 }
211 }
212 }
214 /*
215 * Return the nodes in the subtree |pn| to the parser's free node list, for
216 * reallocation.
217 */
218 ParseNode *
219 ParseNodeAllocator::freeTree(ParseNode *pn)
220 {
221 if (!pn)
222 return nullptr;
224 ParseNode *savedNext = pn->pn_next;
226 NodeStack stack;
227 for (;;) {
228 if (PushNodeChildren(pn, &stack))
229 freeNode(pn);
230 if (stack.empty())
231 break;
232 pn = stack.pop();
233 }
235 return savedNext;
236 }
238 /*
239 * Allocate a ParseNode from parser's node freelist or, failing that, from
240 * cx's temporary arena.
241 */
242 void *
243 ParseNodeAllocator::allocNode()
244 {
245 if (ParseNode *pn = freelist) {
246 freelist = pn->pn_next;
247 return pn;
248 }
250 void *p = alloc.alloc(sizeof (ParseNode));
251 if (!p)
252 js_ReportOutOfMemory(cx);
253 return p;
254 }
256 /* used only by static create methods of subclasses */
258 ParseNode *
259 ParseNode::create(ParseNodeKind kind, ParseNodeArity arity, FullParseHandler *handler)
260 {
261 const Token &tok = handler->currentToken();
262 return handler->new_<ParseNode>(kind, JSOP_NOP, arity, tok.pos);
263 }
265 ParseNode *
266 ParseNode::append(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
267 FullParseHandler *handler)
268 {
269 if (!left || !right)
270 return nullptr;
272 JS_ASSERT(left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC));
274 ListNode *list;
275 if (left->pn_arity == PN_LIST) {
276 list = &left->as<ListNode>();
277 } else {
278 ParseNode *pn1 = left->pn_left, *pn2 = left->pn_right;
279 list = handler->new_<ListNode>(kind, op, pn1);
280 if (!list)
281 return nullptr;
282 list->append(pn2);
283 }
285 list->append(right);
286 list->pn_pos.end = right->pn_pos.end;
288 return list;
289 }
291 ParseNode *
292 ParseNode::newBinaryOrAppend(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
293 FullParseHandler *handler, ParseContext<FullParseHandler> *pc,
294 bool foldConstants)
295 {
296 if (!left || !right)
297 return nullptr;
299 /*
300 * Ensure that the parse tree is faithful to the source when "use asm" (for
301 * the purpose of type checking).
302 */
303 if (pc->useAsmOrInsideUseAsm())
304 return handler->new_<BinaryNode>(kind, op, left, right);
306 /*
307 * Flatten a left-associative (left-heavy) tree of a given operator into
308 * a list to reduce js::FoldConstants and js::frontend::EmitTree recursion.
309 */
310 if (left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC))
311 return append(kind, op, left, right, handler);
313 return handler->new_<BinaryNode>(kind, op, left, right);
314 }
316 const char *
317 Definition::kindString(Kind kind)
318 {
319 static const char * const table[] = {
320 "", js_var_str, js_const_str, js_let_str, js_function_str, "argument", "unknown"
321 };
323 JS_ASSERT(unsigned(kind) <= unsigned(ARG));
324 return table[kind];
325 }
327 namespace js {
328 namespace frontend {
330 /*
331 * This function assumes the cloned tree is for use in the same statement and
332 * binding context as the original tree.
333 */
334 template <>
335 ParseNode *
336 Parser<FullParseHandler>::cloneParseTree(ParseNode *opn)
337 {
338 JS_CHECK_RECURSION(context, return nullptr);
340 ParseNode *pn = handler.new_<ParseNode>(opn->getKind(), opn->getOp(), opn->getArity(),
341 opn->pn_pos);
342 if (!pn)
343 return nullptr;
344 pn->setInParens(opn->isInParens());
345 pn->setDefn(opn->isDefn());
346 pn->setUsed(opn->isUsed());
348 switch (pn->getArity()) {
349 #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return nullptr; JS_END_MACRO
351 case PN_CODE:
352 NULLCHECK(pn->pn_funbox = newFunctionBox(pn, opn->pn_funbox->function(), pc,
353 Directives(/* strict = */ opn->pn_funbox->strict),
354 opn->pn_funbox->generatorKind()));
355 NULLCHECK(pn->pn_body = cloneParseTree(opn->pn_body));
356 pn->pn_cookie = opn->pn_cookie;
357 pn->pn_dflags = opn->pn_dflags;
358 pn->pn_blockid = opn->pn_blockid;
359 break;
361 case PN_LIST:
362 pn->makeEmpty();
363 for (ParseNode *opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) {
364 ParseNode *pn2;
365 NULLCHECK(pn2 = cloneParseTree(opn2));
366 pn->append(pn2);
367 }
368 pn->pn_xflags = opn->pn_xflags;
369 break;
371 case PN_TERNARY:
372 NULLCHECK(pn->pn_kid1 = cloneParseTree(opn->pn_kid1));
373 NULLCHECK(pn->pn_kid2 = cloneParseTree(opn->pn_kid2));
374 NULLCHECK(pn->pn_kid3 = cloneParseTree(opn->pn_kid3));
375 break;
377 case PN_BINARY:
378 NULLCHECK(pn->pn_left = cloneParseTree(opn->pn_left));
379 if (opn->pn_right != opn->pn_left)
380 NULLCHECK(pn->pn_right = cloneParseTree(opn->pn_right));
381 else
382 pn->pn_right = pn->pn_left;
383 pn->pn_iflags = opn->pn_iflags;
384 break;
386 case PN_BINARY_OBJ:
387 NULLCHECK(pn->pn_left = cloneParseTree(opn->pn_left));
388 if (opn->pn_right != opn->pn_left)
389 NULLCHECK(pn->pn_right = cloneParseTree(opn->pn_right));
390 else
391 pn->pn_right = pn->pn_left;
392 pn->pn_binary_obj = opn->pn_binary_obj;
393 break;
395 case PN_UNARY:
396 NULLCHECK(pn->pn_kid = cloneParseTree(opn->pn_kid));
397 break;
399 case PN_NAME:
400 // PN_NAME could mean several arms in pn_u, so copy the whole thing.
401 pn->pn_u = opn->pn_u;
402 if (opn->isUsed()) {
403 /*
404 * The old name is a use of its pn_lexdef. Make the clone also be a
405 * use of that definition.
406 */
407 Definition *dn = pn->pn_lexdef;
409 pn->pn_link = dn->dn_uses;
410 dn->dn_uses = pn;
411 } else if (opn->pn_expr) {
412 NULLCHECK(pn->pn_expr = cloneParseTree(opn->pn_expr));
414 /*
415 * If the old name is a definition, the new one has pn_defn set.
416 * Make the old name a use of the new node.
417 */
418 if (opn->isDefn()) {
419 opn->setDefn(false);
420 handler.linkUseToDef(opn, (Definition *) pn);
421 }
422 }
423 break;
425 case PN_NULLARY:
426 pn->pn_u = opn->pn_u;
427 break;
429 #undef NULLCHECK
430 }
431 return pn;
432 }
434 /*
435 * Used by Parser::forStatement and comprehensionTail to clone the TARGET in
436 * for (var/const/let TARGET in EXPR)
437 *
438 * opn must be the pn_head of a node produced by Parser::variables, so its form
439 * is known to be LHS = NAME | [LHS] | {id:LHS}.
440 *
441 * The cloned tree is for use only in the same statement and binding context as
442 * the original tree.
443 */
444 template <>
445 ParseNode *
446 Parser<FullParseHandler>::cloneLeftHandSide(ParseNode *opn)
447 {
448 ParseNode *pn = handler.new_<ParseNode>(opn->getKind(), opn->getOp(), opn->getArity(),
449 opn->pn_pos);
450 if (!pn)
451 return nullptr;
452 pn->setInParens(opn->isInParens());
453 pn->setDefn(opn->isDefn());
454 pn->setUsed(opn->isUsed());
456 if (opn->isArity(PN_LIST)) {
457 JS_ASSERT(opn->isKind(PNK_ARRAY) || opn->isKind(PNK_OBJECT));
458 pn->makeEmpty();
459 for (ParseNode *opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) {
460 ParseNode *pn2;
461 if (opn->isKind(PNK_OBJECT)) {
462 JS_ASSERT(opn2->isArity(PN_BINARY));
463 JS_ASSERT(opn2->isKind(PNK_COLON));
465 ParseNode *tag = cloneParseTree(opn2->pn_left);
466 if (!tag)
467 return nullptr;
468 ParseNode *target = cloneLeftHandSide(opn2->pn_right);
469 if (!target)
470 return nullptr;
472 pn2 = handler.new_<BinaryNode>(PNK_COLON, JSOP_INITPROP, opn2->pn_pos, tag, target);
473 } else if (opn2->isArity(PN_NULLARY)) {
474 JS_ASSERT(opn2->isKind(PNK_ELISION));
475 pn2 = cloneParseTree(opn2);
476 } else {
477 pn2 = cloneLeftHandSide(opn2);
478 }
480 if (!pn2)
481 return nullptr;
482 pn->append(pn2);
483 }
484 pn->pn_xflags = opn->pn_xflags;
485 return pn;
486 }
488 JS_ASSERT(opn->isArity(PN_NAME));
489 JS_ASSERT(opn->isKind(PNK_NAME));
491 /* If opn is a definition or use, make pn a use. */
492 pn->pn_u.name = opn->pn_u.name;
493 pn->setOp(JSOP_SETNAME);
494 if (opn->isUsed()) {
495 Definition *dn = pn->pn_lexdef;
497 pn->pn_link = dn->dn_uses;
498 dn->dn_uses = pn;
499 } else {
500 pn->pn_expr = nullptr;
501 if (opn->isDefn()) {
502 /* We copied some definition-specific state into pn. Clear it out. */
503 pn->pn_cookie.makeFree();
504 pn->pn_dflags &= ~PND_BOUND;
505 pn->setDefn(false);
507 handler.linkUseToDef(pn, (Definition *) opn);
508 }
509 }
510 return pn;
511 }
513 } /* namespace frontend */
514 } /* namespace js */
516 #ifdef DEBUG
518 static const char * const parseNodeNames[] = {
519 #define STRINGIFY(name) #name,
520 FOR_EACH_PARSE_NODE_KIND(STRINGIFY)
521 #undef STRINGIFY
522 };
524 void
525 frontend::DumpParseTree(ParseNode *pn, int indent)
526 {
527 if (pn == nullptr)
528 fprintf(stderr, "#NULL");
529 else
530 pn->dump(indent);
531 }
533 static void
534 IndentNewLine(int indent)
535 {
536 fputc('\n', stderr);
537 for (int i = 0; i < indent; ++i)
538 fputc(' ', stderr);
539 }
541 void
542 ParseNode::dump()
543 {
544 dump(0);
545 fputc('\n', stderr);
546 }
548 void
549 ParseNode::dump(int indent)
550 {
551 switch (pn_arity) {
552 case PN_NULLARY:
553 ((NullaryNode *) this)->dump();
554 break;
555 case PN_UNARY:
556 ((UnaryNode *) this)->dump(indent);
557 break;
558 case PN_BINARY:
559 ((BinaryNode *) this)->dump(indent);
560 break;
561 case PN_BINARY_OBJ:
562 ((BinaryObjNode *) this)->dump(indent);
563 break;
564 case PN_TERNARY:
565 ((TernaryNode *) this)->dump(indent);
566 break;
567 case PN_CODE:
568 ((CodeNode *) this)->dump(indent);
569 break;
570 case PN_LIST:
571 ((ListNode *) this)->dump(indent);
572 break;
573 case PN_NAME:
574 ((NameNode *) this)->dump(indent);
575 break;
576 default:
577 fprintf(stderr, "#<BAD NODE %p, kind=%u, arity=%u>",
578 (void *) this, unsigned(getKind()), unsigned(pn_arity));
579 break;
580 }
581 }
583 void
584 NullaryNode::dump()
585 {
586 switch (getKind()) {
587 case PNK_TRUE: fprintf(stderr, "#true"); break;
588 case PNK_FALSE: fprintf(stderr, "#false"); break;
589 case PNK_NULL: fprintf(stderr, "#null"); break;
591 case PNK_NUMBER: {
592 ToCStringBuf cbuf;
593 const char *cstr = NumberToCString(nullptr, &cbuf, pn_dval);
594 if (!IsFinite(pn_dval))
595 fputc('#', stderr);
596 if (cstr)
597 fprintf(stderr, "%s", cstr);
598 else
599 fprintf(stderr, "%g", pn_dval);
600 break;
601 }
603 case PNK_STRING:
604 JSString::dumpChars(pn_atom->chars(), pn_atom->length());
605 break;
607 default:
608 fprintf(stderr, "(%s)", parseNodeNames[getKind()]);
609 }
610 }
612 void
613 UnaryNode::dump(int indent)
614 {
615 const char *name = parseNodeNames[getKind()];
616 fprintf(stderr, "(%s ", name);
617 indent += strlen(name) + 2;
618 DumpParseTree(pn_kid, indent);
619 fprintf(stderr, ")");
620 }
622 void
623 BinaryNode::dump(int indent)
624 {
625 const char *name = parseNodeNames[getKind()];
626 fprintf(stderr, "(%s ", name);
627 indent += strlen(name) + 2;
628 DumpParseTree(pn_left, indent);
629 IndentNewLine(indent);
630 DumpParseTree(pn_right, indent);
631 fprintf(stderr, ")");
632 }
634 void
635 BinaryObjNode::dump(int indent)
636 {
637 const char *name = parseNodeNames[getKind()];
638 fprintf(stderr, "(%s ", name);
639 indent += strlen(name) + 2;
640 DumpParseTree(pn_left, indent);
641 IndentNewLine(indent);
642 DumpParseTree(pn_right, indent);
643 fprintf(stderr, ")");
644 }
646 void
647 TernaryNode::dump(int indent)
648 {
649 const char *name = parseNodeNames[getKind()];
650 fprintf(stderr, "(%s ", name);
651 indent += strlen(name) + 2;
652 DumpParseTree(pn_kid1, indent);
653 IndentNewLine(indent);
654 DumpParseTree(pn_kid2, indent);
655 IndentNewLine(indent);
656 DumpParseTree(pn_kid3, indent);
657 fprintf(stderr, ")");
658 }
660 void
661 CodeNode::dump(int indent)
662 {
663 const char *name = parseNodeNames[getKind()];
664 fprintf(stderr, "(%s ", name);
665 indent += strlen(name) + 2;
666 DumpParseTree(pn_body, indent);
667 fprintf(stderr, ")");
668 }
670 void
671 ListNode::dump(int indent)
672 {
673 const char *name = parseNodeNames[getKind()];
674 fprintf(stderr, "(%s [", name);
675 if (pn_head != nullptr) {
676 indent += strlen(name) + 3;
677 DumpParseTree(pn_head, indent);
678 ParseNode *pn = pn_head->pn_next;
679 while (pn != nullptr) {
680 IndentNewLine(indent);
681 DumpParseTree(pn, indent);
682 pn = pn->pn_next;
683 }
684 }
685 fprintf(stderr, "])");
686 }
688 void
689 NameNode::dump(int indent)
690 {
691 if (isKind(PNK_NAME) || isKind(PNK_DOT)) {
692 if (isKind(PNK_DOT))
693 fprintf(stderr, "(.");
695 if (!pn_atom) {
696 fprintf(stderr, "#<null name>");
697 } else {
698 const jschar *s = pn_atom->chars();
699 size_t len = pn_atom->length();
700 if (len == 0)
701 fprintf(stderr, "#<zero-length name>");
702 for (size_t i = 0; i < len; i++) {
703 if (s[i] > 32 && s[i] < 127)
704 fputc(s[i], stderr);
705 else if (s[i] <= 255)
706 fprintf(stderr, "\\x%02x", (unsigned int) s[i]);
707 else
708 fprintf(stderr, "\\u%04x", (unsigned int) s[i]);
709 }
710 }
712 if (isKind(PNK_DOT)) {
713 fputc(' ', stderr);
714 DumpParseTree(expr(), indent + 2);
715 fputc(')', stderr);
716 }
717 return;
718 }
720 JS_ASSERT(!isUsed());
721 const char *name = parseNodeNames[getKind()];
722 if (isUsed())
723 fprintf(stderr, "(%s)", name);
724 else {
725 fprintf(stderr, "(%s ", name);
726 indent += strlen(name) + 2;
727 DumpParseTree(expr(), indent);
728 fprintf(stderr, ")");
729 }
730 }
731 #endif
733 ObjectBox::ObjectBox(JSObject *object, ObjectBox* traceLink)
734 : object(object),
735 traceLink(traceLink),
736 emitLink(nullptr)
737 {
738 JS_ASSERT(!object->is<JSFunction>());
739 }
741 ObjectBox::ObjectBox(JSFunction *function, ObjectBox* traceLink)
742 : object(function),
743 traceLink(traceLink),
744 emitLink(nullptr)
745 {
746 JS_ASSERT(object->is<JSFunction>());
747 JS_ASSERT(asFunctionBox()->function() == function);
748 }
750 FunctionBox *
751 ObjectBox::asFunctionBox()
752 {
753 JS_ASSERT(isFunctionBox());
754 return static_cast<FunctionBox *>(this);
755 }
757 void
758 ObjectBox::trace(JSTracer *trc)
759 {
760 ObjectBox *box = this;
761 while (box) {
762 MarkObjectRoot(trc, &box->object, "parser.object");
763 if (box->isFunctionBox())
764 box->asFunctionBox()->bindings.trace(trc);
765 box = box->traceLink;
766 }
767 }