Line data Source code
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/. */
6 :
7 : #include "frontend/ParseNode-inl.h"
8 :
9 : #include "frontend/Parser.h"
10 :
11 : #include "jscntxtinlines.h"
12 :
13 : using namespace js;
14 : using namespace js::frontend;
15 :
16 : using mozilla::ArrayLength;
17 : using mozilla::IsFinite;
18 :
19 : #ifdef DEBUG
20 : void
21 127156 : ParseNode::checkListConsistency()
22 : {
23 127156 : MOZ_ASSERT(isArity(PN_LIST));
24 : ParseNode** tail;
25 127156 : uint32_t count = 0;
26 127156 : if (pn_head) {
27 125227 : ParseNode* last = pn_head;
28 125227 : ParseNode* pn = last;
29 738195 : while (pn) {
30 306484 : last = pn;
31 306484 : pn = pn->pn_next;
32 306484 : count++;
33 : }
34 :
35 125227 : tail = &last->pn_next;
36 : } else {
37 1929 : tail = &pn_head;
38 : }
39 127156 : MOZ_ASSERT(pn_tail == tail);
40 127156 : MOZ_ASSERT(pn_count == count);
41 127156 : }
42 : #endif
43 :
44 : /* Add |node| to |parser|'s free node list. */
45 : void
46 250533 : ParseNodeAllocator::freeNode(ParseNode* pn)
47 : {
48 : /* Catch back-to-back dup recycles. */
49 250533 : MOZ_ASSERT(pn != freelist);
50 :
51 : #ifdef DEBUG
52 : /* Poison the node, to catch attempts to use it without initializing it. */
53 250533 : memset(pn, 0xab, sizeof(*pn));
54 : #endif
55 :
56 250533 : pn->pn_next = freelist;
57 250533 : freelist = pn;
58 250533 : }
59 :
60 : namespace {
61 :
62 : /*
63 : * A work pool of ParseNodes. The work pool is a stack, chained together
64 : * by nodes' pn_next fields. We use this to avoid creating deep C++ stacks
65 : * when recycling deep parse trees.
66 : *
67 : * Since parse nodes are probably allocated in something close to the order
68 : * they appear in a depth-first traversal of the tree, making the work pool
69 : * a stack should give us pretty good locality.
70 : */
71 : class NodeStack {
72 : public:
73 3959 : NodeStack() : top(nullptr) { }
74 508839 : bool empty() { return top == nullptr; }
75 121826 : void push(ParseNode* pn) {
76 121826 : pn->pn_next = top;
77 121826 : top = pn;
78 121826 : }
79 : /* Push the children of the PN_LIST node |pn| on the stack. */
80 53295 : void pushList(ParseNode* pn) {
81 : /* This clobbers pn->pn_head if the list is empty; should be okay. */
82 53295 : *pn->pn_tail = top;
83 53295 : top = pn->pn_head;
84 53295 : }
85 252440 : ParseNode* pop() {
86 252440 : MOZ_ASSERT(!empty());
87 252440 : ParseNode* hold = top; /* my kingdom for a prog1 */
88 252440 : top = top->pn_next;
89 252440 : return hold;
90 : }
91 : private:
92 : ParseNode* top;
93 : };
94 :
95 : } /* anonymous namespace */
96 :
97 : enum class PushResult { Recyclable, CleanUpLater };
98 :
99 : static PushResult
100 5552 : PushCodeNodeChildren(ParseNode* node, NodeStack* stack)
101 : {
102 5552 : MOZ_ASSERT(node->isArity(PN_CODE));
103 :
104 : /*
105 : * Function nodes are linked into the function box tree, and may appear
106 : * on method lists. Both of those lists are singly-linked, so trying to
107 : * update them now could result in quadratic behavior when recycling
108 : * trees containing many functions; and the lists can be very long. So
109 : * we put off cleaning the lists up until just before function
110 : * analysis, when we call CleanFunctionList.
111 : *
112 : * In fact, we can't recycle the parse node yet, either: it may appear
113 : * on a method list, and reusing the node would corrupt that. Instead,
114 : * we clear its pn_funbox pointer to mark it as deleted;
115 : * CleanFunctionList recycles it as well.
116 : *
117 : * We do recycle the nodes around it, though, so we must clear pointers
118 : * to them to avoid leaving dangling references where someone can find
119 : * them.
120 : */
121 5552 : node->pn_funbox = nullptr;
122 5552 : if (node->pn_body)
123 4001 : stack->push(node->pn_body);
124 5552 : node->pn_body = nullptr;
125 :
126 5552 : return PushResult::CleanUpLater;
127 : }
128 :
129 : static PushResult
130 96631 : PushNameNodeChildren(ParseNode* node, NodeStack* stack)
131 : {
132 96631 : MOZ_ASSERT(node->isArity(PN_NAME));
133 :
134 96631 : if (node->pn_expr)
135 28351 : stack->push(node->pn_expr);
136 96631 : node->pn_expr = nullptr;
137 96631 : return PushResult::Recyclable;
138 : }
139 :
140 : static PushResult
141 9660 : PushScopeNodeChildren(ParseNode* node, NodeStack* stack)
142 : {
143 9660 : MOZ_ASSERT(node->isArity(PN_SCOPE));
144 :
145 9660 : if (node->scopeBody())
146 9660 : stack->push(node->scopeBody());
147 9660 : node->setScopeBody(nullptr);
148 9660 : return PushResult::Recyclable;
149 : }
150 :
151 : static PushResult
152 53295 : PushListNodeChildren(ParseNode* node, NodeStack* stack)
153 : {
154 53295 : MOZ_ASSERT(node->isArity(PN_LIST));
155 53295 : node->checkListConsistency();
156 :
157 53295 : stack->pushList(node);
158 :
159 53295 : return PushResult::Recyclable;
160 : }
161 :
162 : static PushResult
163 4637 : PushUnaryNodeChild(ParseNode* node, NodeStack* stack)
164 : {
165 4637 : MOZ_ASSERT(node->isArity(PN_UNARY));
166 :
167 4637 : stack->push(node->pn_kid);
168 :
169 4637 : return PushResult::Recyclable;
170 : }
171 :
172 : /*
173 : * Push the children of |pn| on |stack|. Return true if |pn| itself could be
174 : * safely recycled, or false if it must be cleaned later (pn_used and pn_defn
175 : * nodes, and all function nodes; see comments for CleanFunctionList in
176 : * SemanticAnalysis.cpp). Some callers want to free |pn|; others
177 : * (js::ParseNodeAllocator::prepareNodeForMutation) don't care about |pn|, and
178 : * just need to take care of its children.
179 : */
180 : static PushResult
181 256399 : PushNodeChildren(ParseNode* pn, NodeStack* stack)
182 : {
183 256399 : switch (pn->getKind()) {
184 : // Trivial nodes that refer to no nodes, are referred to by nothing
185 : // but their parents, are never used, and are never a definition.
186 : case PNK_NOP:
187 : case PNK_STRING:
188 : case PNK_TEMPLATE_STRING:
189 : case PNK_REGEXP:
190 : case PNK_TRUE:
191 : case PNK_FALSE:
192 : case PNK_NULL:
193 : case PNK_RAW_UNDEFINED:
194 : case PNK_ELISION:
195 : case PNK_GENERATOR:
196 : case PNK_NUMBER:
197 : case PNK_BREAK:
198 : case PNK_CONTINUE:
199 : case PNK_DEBUGGER:
200 : case PNK_EXPORT_BATCH_SPEC:
201 : case PNK_OBJECT_PROPERTY_NAME:
202 : case PNK_POSHOLDER:
203 38108 : MOZ_ASSERT(pn->isArity(PN_NULLARY));
204 38108 : return PushResult::Recyclable;
205 :
206 : // Nodes with a single non-null child.
207 : case PNK_TYPEOFNAME:
208 : case PNK_TYPEOFEXPR:
209 : case PNK_VOID:
210 : case PNK_NOT:
211 : case PNK_BITNOT:
212 : case PNK_THROW:
213 : case PNK_DELETENAME:
214 : case PNK_DELETEPROP:
215 : case PNK_DELETEELEM:
216 : case PNK_DELETEEXPR:
217 : case PNK_POS:
218 : case PNK_NEG:
219 : case PNK_PREINCREMENT:
220 : case PNK_POSTINCREMENT:
221 : case PNK_PREDECREMENT:
222 : case PNK_POSTDECREMENT:
223 : case PNK_COMPUTED_NAME:
224 : case PNK_ARRAYPUSH:
225 : case PNK_SPREAD:
226 : case PNK_MUTATEPROTO:
227 : case PNK_EXPORT:
228 : case PNK_SUPERBASE:
229 4637 : return PushUnaryNodeChild(pn, stack);
230 :
231 : // Nodes with a single nullable child.
232 : case PNK_THIS:
233 : case PNK_SEMI: {
234 17204 : MOZ_ASSERT(pn->isArity(PN_UNARY));
235 17204 : if (pn->pn_kid)
236 16460 : stack->push(pn->pn_kid);
237 17204 : return PushResult::Recyclable;
238 : }
239 :
240 : // Binary nodes with two non-null children.
241 :
242 : // All assignment and compound assignment nodes qualify.
243 : case PNK_ASSIGN:
244 : case PNK_ADDASSIGN:
245 : case PNK_SUBASSIGN:
246 : case PNK_BITORASSIGN:
247 : case PNK_BITXORASSIGN:
248 : case PNK_BITANDASSIGN:
249 : case PNK_LSHASSIGN:
250 : case PNK_RSHASSIGN:
251 : case PNK_URSHASSIGN:
252 : case PNK_MULASSIGN:
253 : case PNK_DIVASSIGN:
254 : case PNK_MODASSIGN:
255 : case PNK_POWASSIGN:
256 : // ...and a few others.
257 : case PNK_ELEM:
258 : case PNK_IMPORT_SPEC:
259 : case PNK_EXPORT_SPEC:
260 : case PNK_COLON:
261 : case PNK_SHORTHAND:
262 : case PNK_DOWHILE:
263 : case PNK_WHILE:
264 : case PNK_SWITCH:
265 : case PNK_CLASSMETHOD:
266 : case PNK_NEWTARGET:
267 : case PNK_SETTHIS:
268 : case PNK_FOR:
269 : case PNK_COMPREHENSIONFOR:
270 : case PNK_WITH: {
271 17277 : MOZ_ASSERT(pn->isArity(PN_BINARY));
272 17277 : stack->push(pn->pn_left);
273 17277 : stack->push(pn->pn_right);
274 17277 : return PushResult::Recyclable;
275 : }
276 :
277 : // Default clauses are PNK_CASE but do not have case expressions.
278 : // Named class expressions do not have outer binding nodes.
279 : // So both are binary nodes with a possibly-null pn_left.
280 : case PNK_CASE:
281 : case PNK_CLASSNAMES: {
282 947 : MOZ_ASSERT(pn->isArity(PN_BINARY));
283 947 : if (pn->pn_left)
284 901 : stack->push(pn->pn_left);
285 947 : stack->push(pn->pn_right);
286 947 : return PushResult::Recyclable;
287 : }
288 :
289 : // The child is an assignment of a PNK_GENERATOR node to the
290 : // '.generator' local, for a synthesized, prepended initial yield.
291 : case PNK_INITIALYIELD: {
292 96 : MOZ_ASSERT(pn->isArity(PN_UNARY));
293 96 : MOZ_ASSERT(pn->pn_kid->isKind(PNK_ASSIGN) &&
294 : pn->pn_kid->pn_left->isKind(PNK_NAME) &&
295 : pn->pn_kid->pn_right->isKind(PNK_GENERATOR));
296 96 : stack->push(pn->pn_kid);
297 96 : return PushResult::Recyclable;
298 : }
299 :
300 : // The child is the expression being yielded.
301 : case PNK_YIELD_STAR:
302 : case PNK_YIELD:
303 : case PNK_AWAIT: {
304 192 : MOZ_ASSERT(pn->isArity(PN_UNARY));
305 192 : if (pn->pn_kid)
306 192 : stack->push(pn->pn_kid);
307 192 : return PushResult::Recyclable;
308 : }
309 :
310 : // A return node's child is what you'd expect: the return expression,
311 : // if any.
312 : case PNK_RETURN: {
313 4971 : MOZ_ASSERT(pn->isArity(PN_UNARY));
314 4971 : if (pn->pn_kid)
315 4663 : stack->push(pn->pn_kid);
316 4971 : return PushResult::Recyclable;
317 : }
318 :
319 : // Import and export-from nodes have a list of specifiers on the left
320 : // and a module string on the right.
321 : case PNK_IMPORT:
322 : case PNK_EXPORT_FROM: {
323 0 : MOZ_ASSERT(pn->isArity(PN_BINARY));
324 0 : MOZ_ASSERT_IF(pn->isKind(PNK_IMPORT), pn->pn_left->isKind(PNK_IMPORT_SPEC_LIST));
325 0 : MOZ_ASSERT_IF(pn->isKind(PNK_EXPORT_FROM), pn->pn_left->isKind(PNK_EXPORT_SPEC_LIST));
326 0 : MOZ_ASSERT(pn->pn_left->isArity(PN_LIST));
327 0 : MOZ_ASSERT(pn->pn_right->isKind(PNK_STRING));
328 0 : stack->pushList(pn->pn_left);
329 0 : stack->push(pn->pn_right);
330 0 : return PushResult::Recyclable;
331 : }
332 :
333 : case PNK_EXPORT_DEFAULT: {
334 0 : MOZ_ASSERT(pn->isArity(PN_BINARY));
335 0 : MOZ_ASSERT_IF(pn->pn_right, pn->pn_right->isKind(PNK_NAME));
336 0 : stack->push(pn->pn_left);
337 0 : if (pn->pn_right)
338 0 : stack->push(pn->pn_right);
339 0 : return PushResult::Recyclable;
340 : }
341 :
342 : // Ternary nodes with all children non-null.
343 : case PNK_CONDITIONAL: {
344 569 : MOZ_ASSERT(pn->isArity(PN_TERNARY));
345 569 : stack->push(pn->pn_kid1);
346 569 : stack->push(pn->pn_kid2);
347 569 : stack->push(pn->pn_kid3);
348 569 : return PushResult::Recyclable;
349 : }
350 :
351 : // For for-in and for-of, the first child is the left-hand side of the
352 : // 'in' or 'of' (a declaration or an assignment target). The second
353 : // child is always null, and the third child is the expression looped
354 : // over. For example, in |for (var p in obj)|, the first child is |var
355 : // p|, the second child is null, and the third child is |obj|.
356 : case PNK_FORIN:
357 : case PNK_FOROF: {
358 271 : MOZ_ASSERT(pn->isArity(PN_TERNARY));
359 271 : MOZ_ASSERT(!pn->pn_kid2);
360 271 : stack->push(pn->pn_kid1);
361 271 : stack->push(pn->pn_kid3);
362 271 : return PushResult::Recyclable;
363 : }
364 :
365 : // for (;;) nodes have one child per optional component of the loop head.
366 : case PNK_FORHEAD: {
367 412 : MOZ_ASSERT(pn->isArity(PN_TERNARY));
368 412 : if (pn->pn_kid1)
369 365 : stack->push(pn->pn_kid1);
370 412 : if (pn->pn_kid2)
371 403 : stack->push(pn->pn_kid2);
372 412 : if (pn->pn_kid3)
373 403 : stack->push(pn->pn_kid3);
374 412 : return PushResult::Recyclable;
375 : }
376 :
377 : // classes might have an optional node for the heritage, as well as the names
378 : case PNK_CLASS: {
379 32 : MOZ_ASSERT(pn->isArity(PN_TERNARY));
380 32 : if (pn->pn_kid1)
381 26 : stack->push(pn->pn_kid1);
382 32 : if (pn->pn_kid2)
383 16 : stack->push(pn->pn_kid2);
384 32 : stack->push(pn->pn_kid3);
385 32 : return PushResult::Recyclable;
386 : }
387 :
388 : // if-statement nodes have condition and consequent children and a
389 : // possibly-null alternative.
390 : case PNK_IF: {
391 5783 : MOZ_ASSERT(pn->isArity(PN_TERNARY));
392 5783 : stack->push(pn->pn_kid1);
393 5783 : stack->push(pn->pn_kid2);
394 5783 : if (pn->pn_kid3)
395 763 : stack->push(pn->pn_kid3);
396 5783 : return PushResult::Recyclable;
397 : }
398 :
399 : // try-statements have statements to execute, and one or both of a
400 : // catch-list and a finally-block.
401 : case PNK_TRY: {
402 389 : MOZ_ASSERT(pn->isArity(PN_TERNARY));
403 389 : MOZ_ASSERT(pn->pn_kid2 || pn->pn_kid3);
404 389 : stack->push(pn->pn_kid1);
405 389 : if (pn->pn_kid2)
406 373 : stack->push(pn->pn_kid2);
407 389 : if (pn->pn_kid3)
408 33 : stack->push(pn->pn_kid3);
409 389 : return PushResult::Recyclable;
410 : }
411 :
412 : // A catch node has first kid as catch-variable pattern, the second kid
413 : // as catch condition (which, if non-null, records the |<cond>| in
414 : // SpiderMonkey's |catch (e if <cond>)| extension), and third kid as the
415 : // statements in the catch block.
416 : case PNK_CATCH: {
417 373 : MOZ_ASSERT(pn->isArity(PN_TERNARY));
418 373 : stack->push(pn->pn_kid1);
419 373 : if (pn->pn_kid2)
420 0 : stack->push(pn->pn_kid2);
421 373 : stack->push(pn->pn_kid3);
422 373 : return PushResult::Recyclable;
423 : }
424 :
425 : // List nodes with all non-null children.
426 : case PNK_OR:
427 : case PNK_AND:
428 : case PNK_BITOR:
429 : case PNK_BITXOR:
430 : case PNK_BITAND:
431 : case PNK_STRICTEQ:
432 : case PNK_EQ:
433 : case PNK_STRICTNE:
434 : case PNK_NE:
435 : case PNK_LT:
436 : case PNK_LE:
437 : case PNK_GT:
438 : case PNK_GE:
439 : case PNK_INSTANCEOF:
440 : case PNK_IN:
441 : case PNK_LSH:
442 : case PNK_RSH:
443 : case PNK_URSH:
444 : case PNK_ADD:
445 : case PNK_SUB:
446 : case PNK_STAR:
447 : case PNK_DIV:
448 : case PNK_MOD:
449 : case PNK_POW:
450 : case PNK_COMMA:
451 : case PNK_NEW:
452 : case PNK_CALL:
453 : case PNK_SUPERCALL:
454 : case PNK_GENEXP:
455 : case PNK_ARRAY:
456 : case PNK_OBJECT:
457 : case PNK_TEMPLATE_STRING_LIST:
458 : case PNK_TAGGED_TEMPLATE:
459 : case PNK_CALLSITEOBJ:
460 : case PNK_VAR:
461 : case PNK_CONST:
462 : case PNK_LET:
463 : case PNK_CATCHLIST:
464 : case PNK_STATEMENTLIST:
465 : case PNK_IMPORT_SPEC_LIST:
466 : case PNK_EXPORT_SPEC_LIST:
467 : case PNK_PARAMSBODY:
468 : case PNK_CLASSMETHODLIST:
469 53295 : return PushListNodeChildren(pn, stack);
470 :
471 : // Array comprehension nodes are lists with a single child:
472 : // PNK_COMPREHENSIONFOR for comprehensions, PNK_LEXICALSCOPE for legacy
473 : // comprehensions. Probably this should be a non-list eventually.
474 : case PNK_ARRAYCOMP: {
475 : #ifdef DEBUG
476 0 : MOZ_ASSERT(pn->isKind(PNK_ARRAYCOMP));
477 0 : MOZ_ASSERT(pn->isArity(PN_LIST));
478 0 : MOZ_ASSERT(pn->pn_count == 1);
479 0 : MOZ_ASSERT(pn->pn_head->isKind(PNK_LEXICALSCOPE) ||
480 : pn->pn_head->isKind(PNK_COMPREHENSIONFOR));
481 : #endif
482 0 : return PushListNodeChildren(pn, stack);
483 : }
484 :
485 : case PNK_LABEL:
486 : case PNK_DOT:
487 : case PNK_NAME:
488 96631 : return PushNameNodeChildren(pn, stack);
489 :
490 : case PNK_LEXICALSCOPE:
491 9660 : return PushScopeNodeChildren(pn, stack);
492 :
493 : case PNK_FUNCTION:
494 : case PNK_MODULE:
495 5552 : return PushCodeNodeChildren(pn, stack);
496 :
497 : case PNK_LIMIT: // invalid sentinel value
498 0 : MOZ_CRASH("invalid node kind");
499 : }
500 :
501 0 : MOZ_CRASH("bad ParseNodeKind");
502 : return PushResult::CleanUpLater;
503 : }
504 :
505 : /*
506 : * Prepare |pn| to be mutated in place into a new kind of node. Recycle all
507 : * |pn|'s recyclable children (but not |pn| itself!), and disconnect it from
508 : * metadata structures (the function box tree).
509 : */
510 : void
511 455 : ParseNodeAllocator::prepareNodeForMutation(ParseNode* pn)
512 : {
513 : // Nothing to do for nullary nodes.
514 455 : if (pn->isArity(PN_NULLARY))
515 141 : return;
516 :
517 : // Put |pn|'s children (but not |pn| itself) on a work stack.
518 314 : NodeStack stack;
519 314 : PushNodeChildren(pn, &stack);
520 :
521 : // For each node on the work stack, push its children on the work stack,
522 : // and free the node if we can.
523 996 : while (!stack.empty()) {
524 341 : pn = stack.pop();
525 341 : if (PushNodeChildren(pn, &stack) == PushResult::Recyclable)
526 341 : freeNode(pn);
527 : }
528 : }
529 :
530 : /*
531 : * Return the nodes in the subtree |pn| to the parser's free node list, for
532 : * reallocation.
533 : */
534 : ParseNode*
535 3645 : ParseNodeAllocator::freeTree(ParseNode* pn)
536 : {
537 3645 : if (!pn)
538 0 : return nullptr;
539 :
540 3645 : ParseNode* savedNext = pn->pn_next;
541 :
542 3645 : NodeStack stack;
543 : for (;;) {
544 507843 : if (PushNodeChildren(pn, &stack) == PushResult::Recyclable)
545 250192 : freeNode(pn);
546 255744 : if (stack.empty())
547 3645 : break;
548 252099 : pn = stack.pop();
549 : }
550 :
551 3645 : return savedNext;
552 : }
553 :
554 : /*
555 : * Allocate a ParseNode from parser's node freelist or, failing that, from
556 : * cx's temporary arena.
557 : */
558 : void*
559 310658 : ParseNodeAllocator::allocNode()
560 : {
561 310658 : if (ParseNode* pn = freelist) {
562 264 : freelist = pn->pn_next;
563 264 : return pn;
564 : }
565 :
566 620788 : LifoAlloc::AutoFallibleScope fallibleAllocator(&alloc);
567 310394 : void* p = alloc.alloc(sizeof (ParseNode));
568 310394 : if (!p)
569 0 : ReportOutOfMemory(cx);
570 310394 : return p;
571 : }
572 :
573 : ParseNode*
574 13783 : ParseNode::appendOrCreateList(ParseNodeKind kind, JSOp op, ParseNode* left, ParseNode* right,
575 : FullParseHandler* handler, ParseContext* pc)
576 : {
577 : // The asm.js specification is written in ECMAScript grammar terms that
578 : // specify *only* a binary tree. It's a royal pain to implement the asm.js
579 : // spec to act upon n-ary lists as created below. So for asm.js, form a
580 : // binary tree of lists exactly as ECMAScript would by skipping the
581 : // following optimization.
582 13783 : if (!pc->useAsmOrInsideUseAsm()) {
583 : // Left-associative trees of a given operator (e.g. |a + b + c|) are
584 : // binary trees in the spec: (+ (+ a b) c) in Lisp terms. Recursively
585 : // processing such a tree, exactly implemented that way, would blow the
586 : // the stack. We use a list node that uses O(1) stack to represent
587 : // such operations: (+ a b c).
588 : //
589 : // (**) is right-associative; per spec |a ** b ** c| parses as
590 : // (** a (** b c)). But we treat this the same way, creating a list
591 : // node: (** a b c). All consumers must understand that this must be
592 : // processed with a right fold, whereas the list (+ a b c) must be
593 : // processed with a left fold because (+) is left-associative.
594 : //
595 30579 : if (left->isKind(kind) &&
596 19809 : left->isOp(op) &&
597 3013 : (CodeSpec[op].format & JOF_LEFTASSOC ||
598 0 : (kind == PNK_POW && !left->pn_parens)))
599 : {
600 3013 : ListNode* list = &left->as<ListNode>();
601 :
602 3013 : list->append(right);
603 3013 : list->pn_pos.end = right->pn_pos.end;
604 :
605 3013 : return list;
606 : }
607 : }
608 :
609 10770 : ParseNode* list = handler->new_<ListNode>(kind, op, left);
610 10770 : if (!list)
611 0 : return nullptr;
612 :
613 10770 : list->append(right);
614 10770 : return list;
615 : }
616 :
617 : #ifdef DEBUG
618 :
619 : static const char * const parseNodeNames[] = {
620 : #define STRINGIFY(name) #name,
621 : FOR_EACH_PARSE_NODE_KIND(STRINGIFY)
622 : #undef STRINGIFY
623 : };
624 :
625 : void
626 0 : frontend::DumpParseTree(ParseNode* pn, int indent)
627 : {
628 0 : if (pn == nullptr)
629 0 : fprintf(stderr, "#NULL");
630 : else
631 0 : pn->dump(indent);
632 0 : }
633 :
634 : static void
635 0 : IndentNewLine(int indent)
636 : {
637 0 : fputc('\n', stderr);
638 0 : for (int i = 0; i < indent; ++i)
639 0 : fputc(' ', stderr);
640 0 : }
641 :
642 : void
643 0 : ParseNode::dump()
644 : {
645 0 : dump(0);
646 0 : fputc('\n', stderr);
647 0 : }
648 :
649 : void
650 0 : ParseNode::dump(int indent)
651 : {
652 0 : switch (pn_arity) {
653 : case PN_NULLARY:
654 0 : ((NullaryNode*) this)->dump();
655 0 : break;
656 : case PN_UNARY:
657 0 : ((UnaryNode*) this)->dump(indent);
658 0 : break;
659 : case PN_BINARY:
660 0 : ((BinaryNode*) this)->dump(indent);
661 0 : break;
662 : case PN_TERNARY:
663 0 : ((TernaryNode*) this)->dump(indent);
664 0 : break;
665 : case PN_CODE:
666 0 : ((CodeNode*) this)->dump(indent);
667 0 : break;
668 : case PN_LIST:
669 0 : ((ListNode*) this)->dump(indent);
670 0 : break;
671 : case PN_NAME:
672 0 : ((NameNode*) this)->dump(indent);
673 0 : break;
674 : case PN_SCOPE:
675 0 : ((LexicalScopeNode*) this)->dump(indent);
676 0 : break;
677 : default:
678 0 : fprintf(stderr, "#<BAD NODE %p, kind=%u, arity=%u>",
679 0 : (void*) this, unsigned(getKind()), unsigned(pn_arity));
680 0 : break;
681 : }
682 0 : }
683 :
684 : void
685 0 : NullaryNode::dump()
686 : {
687 0 : switch (getKind()) {
688 0 : case PNK_TRUE: fprintf(stderr, "#true"); break;
689 0 : case PNK_FALSE: fprintf(stderr, "#false"); break;
690 0 : case PNK_NULL: fprintf(stderr, "#null"); break;
691 0 : case PNK_RAW_UNDEFINED: fprintf(stderr, "#undefined"); break;
692 :
693 : case PNK_NUMBER: {
694 0 : ToCStringBuf cbuf;
695 0 : const char* cstr = NumberToCString(nullptr, &cbuf, pn_dval);
696 0 : if (!IsFinite(pn_dval))
697 0 : fputc('#', stderr);
698 0 : if (cstr)
699 0 : fprintf(stderr, "%s", cstr);
700 : else
701 0 : fprintf(stderr, "%g", pn_dval);
702 0 : break;
703 : }
704 :
705 : case PNK_STRING:
706 0 : pn_atom->dumpCharsNoNewline();
707 0 : break;
708 :
709 : default:
710 0 : fprintf(stderr, "(%s)", parseNodeNames[getKind()]);
711 : }
712 0 : }
713 :
714 : void
715 0 : UnaryNode::dump(int indent)
716 : {
717 0 : const char* name = parseNodeNames[getKind()];
718 0 : fprintf(stderr, "(%s ", name);
719 0 : indent += strlen(name) + 2;
720 0 : DumpParseTree(pn_kid, indent);
721 0 : fprintf(stderr, ")");
722 0 : }
723 :
724 : void
725 0 : BinaryNode::dump(int indent)
726 : {
727 0 : const char* name = parseNodeNames[getKind()];
728 0 : fprintf(stderr, "(%s ", name);
729 0 : indent += strlen(name) + 2;
730 0 : DumpParseTree(pn_left, indent);
731 0 : IndentNewLine(indent);
732 0 : DumpParseTree(pn_right, indent);
733 0 : fprintf(stderr, ")");
734 0 : }
735 :
736 : void
737 0 : TernaryNode::dump(int indent)
738 : {
739 0 : const char* name = parseNodeNames[getKind()];
740 0 : fprintf(stderr, "(%s ", name);
741 0 : indent += strlen(name) + 2;
742 0 : DumpParseTree(pn_kid1, indent);
743 0 : IndentNewLine(indent);
744 0 : DumpParseTree(pn_kid2, indent);
745 0 : IndentNewLine(indent);
746 0 : DumpParseTree(pn_kid3, indent);
747 0 : fprintf(stderr, ")");
748 0 : }
749 :
750 : void
751 0 : CodeNode::dump(int indent)
752 : {
753 0 : const char* name = parseNodeNames[getKind()];
754 0 : fprintf(stderr, "(%s ", name);
755 0 : indent += strlen(name) + 2;
756 0 : DumpParseTree(pn_body, indent);
757 0 : fprintf(stderr, ")");
758 0 : }
759 :
760 : void
761 0 : ListNode::dump(int indent)
762 : {
763 0 : const char* name = parseNodeNames[getKind()];
764 0 : fprintf(stderr, "(%s [", name);
765 0 : if (pn_head != nullptr) {
766 0 : indent += strlen(name) + 3;
767 0 : DumpParseTree(pn_head, indent);
768 0 : ParseNode* pn = pn_head->pn_next;
769 0 : while (pn != nullptr) {
770 0 : IndentNewLine(indent);
771 0 : DumpParseTree(pn, indent);
772 0 : pn = pn->pn_next;
773 : }
774 : }
775 0 : fprintf(stderr, "])");
776 0 : }
777 :
778 : template <typename CharT>
779 : static void
780 0 : DumpName(const CharT* s, size_t len)
781 : {
782 0 : if (len == 0)
783 0 : fprintf(stderr, "#<zero-length name>");
784 :
785 0 : for (size_t i = 0; i < len; i++) {
786 0 : char16_t c = s[i];
787 0 : if (c > 32 && c < 127)
788 0 : fputc(c, stderr);
789 0 : else if (c <= 255)
790 0 : fprintf(stderr, "\\x%02x", unsigned(c));
791 : else
792 0 : fprintf(stderr, "\\u%04x", unsigned(c));
793 : }
794 0 : }
795 :
796 : void
797 0 : NameNode::dump(int indent)
798 : {
799 0 : if (isKind(PNK_NAME) || isKind(PNK_DOT)) {
800 0 : if (isKind(PNK_DOT))
801 0 : fprintf(stderr, "(.");
802 :
803 0 : if (!pn_atom) {
804 0 : fprintf(stderr, "#<null name>");
805 0 : } else if (getOp() == JSOP_GETARG && pn_atom->length() == 0) {
806 : // Dump destructuring parameter.
807 0 : fprintf(stderr, "(#<zero-length name> ");
808 0 : DumpParseTree(expr(), indent + 21);
809 0 : fputc(')', stderr);
810 : } else {
811 0 : JS::AutoCheckCannotGC nogc;
812 0 : if (pn_atom->hasLatin1Chars())
813 0 : DumpName(pn_atom->latin1Chars(nogc), pn_atom->length());
814 : else
815 0 : DumpName(pn_atom->twoByteChars(nogc), pn_atom->length());
816 : }
817 :
818 0 : if (isKind(PNK_DOT)) {
819 0 : fputc(' ', stderr);
820 0 : if (as<PropertyAccess>().isSuper())
821 0 : fprintf(stderr, "super");
822 : else
823 0 : DumpParseTree(expr(), indent + 2);
824 0 : fputc(')', stderr);
825 : }
826 0 : return;
827 : }
828 :
829 0 : const char* name = parseNodeNames[getKind()];
830 0 : fprintf(stderr, "(%s ", name);
831 0 : indent += strlen(name) + 2;
832 0 : DumpParseTree(expr(), indent);
833 0 : fprintf(stderr, ")");
834 : }
835 :
836 : void
837 0 : LexicalScopeNode::dump(int indent)
838 : {
839 0 : const char* name = parseNodeNames[getKind()];
840 0 : fprintf(stderr, "(%s [", name);
841 0 : int nameIndent = indent + strlen(name) + 3;
842 0 : if (!isEmptyScope()) {
843 0 : LexicalScope::Data* bindings = scopeBindings();
844 0 : for (uint32_t i = 0; i < bindings->length; i++) {
845 0 : JSAtom* name = bindings->names[i].name();
846 0 : JS::AutoCheckCannotGC nogc;
847 0 : if (name->hasLatin1Chars())
848 0 : DumpName(name->latin1Chars(nogc), name->length());
849 : else
850 0 : DumpName(name->twoByteChars(nogc), name->length());
851 0 : if (i < bindings->length - 1)
852 0 : IndentNewLine(nameIndent);
853 : }
854 : }
855 0 : fprintf(stderr, "]");
856 0 : indent += 2;
857 0 : IndentNewLine(indent);
858 0 : DumpParseTree(scopeBody(), indent);
859 0 : fprintf(stderr, ")");
860 0 : }
861 : #endif
862 :
863 2178 : ObjectBox::ObjectBox(JSObject* object, ObjectBox* traceLink)
864 : : object(object),
865 : traceLink(traceLink),
866 2178 : emitLink(nullptr)
867 : {
868 2178 : MOZ_ASSERT(!object->is<JSFunction>());
869 2178 : MOZ_ASSERT(object->isTenured());
870 2178 : }
871 :
872 7633 : ObjectBox::ObjectBox(JSFunction* function, ObjectBox* traceLink)
873 : : object(function),
874 : traceLink(traceLink),
875 7633 : emitLink(nullptr)
876 : {
877 7633 : MOZ_ASSERT(object->is<JSFunction>());
878 7633 : MOZ_ASSERT(asFunctionBox()->function() == function);
879 7633 : MOZ_ASSERT(object->isTenured());
880 7633 : }
881 :
882 : FunctionBox*
883 7633 : ObjectBox::asFunctionBox()
884 : {
885 7633 : MOZ_ASSERT(isFunctionBox());
886 7633 : return static_cast<FunctionBox*>(this);
887 : }
888 :
889 : /* static */ void
890 0 : ObjectBox::TraceList(JSTracer* trc, ObjectBox* listHead)
891 : {
892 0 : for (ObjectBox* box = listHead; box; box = box->traceLink)
893 0 : box->trace(trc);
894 0 : }
895 :
896 : void
897 0 : ObjectBox::trace(JSTracer* trc)
898 : {
899 0 : TraceRoot(trc, &object, "parser.object");
900 0 : }
901 :
902 : void
903 0 : FunctionBox::trace(JSTracer* trc)
904 : {
905 0 : ObjectBox::trace(trc);
906 0 : if (enclosingScope_)
907 0 : TraceRoot(trc, &enclosingScope_, "funbox-enclosingScope");
908 0 : }
909 :
910 : bool
911 22880 : js::frontend::IsAnonymousFunctionDefinition(ParseNode* pn)
912 : {
913 : // ES 2017 draft
914 : // 12.15.2 (ArrowFunction, AsyncArrowFunction).
915 : // 14.1.12 (FunctionExpression).
916 : // 14.4.8 (GeneratorExpression).
917 : // 14.6.8 (AsyncFunctionExpression)
918 22880 : if (pn->isKind(PNK_FUNCTION) && !pn->pn_funbox->function()->explicitName())
919 485 : return true;
920 :
921 : // 14.5.8 (ClassExpression)
922 22395 : if (pn->is<ClassNode>() && !pn->as<ClassNode>().names())
923 6 : return true;
924 :
925 22389 : return false;
926 : }
|