js/src/frontend/ParseNode.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial