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/FoldConstants.h"
8
9 #include "mozilla/FloatingPoint.h"
10
11 #include "jslibmath.h"
12
13 #include "frontend/ParseNode.h"
14 #include "frontend/Parser.h"
15 #include "js/Conversions.h"
16
17 #include "jscntxtinlines.h"
18 #include "jsobjinlines.h"
19
20 using namespace js;
21 using namespace js::frontend;
22
23 using mozilla::IsNaN;
24 using mozilla::IsNegative;
25 using mozilla::NegativeInfinity;
26 using mozilla::PositiveInfinity;
27 using JS::GenericNaN;
28 using JS::ToInt32;
29 using JS::ToUint32;
30
31 static bool
32 ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result);
33
34 static bool
ListContainsHoistedDeclaration(ExclusiveContext * cx,ListNode * list,bool * result)35 ListContainsHoistedDeclaration(ExclusiveContext* cx, ListNode* list, bool* result)
36 {
37 for (ParseNode* node = list->pn_head; node; node = node->pn_next) {
38 if (!ContainsHoistedDeclaration(cx, node, result))
39 return false;
40 if (*result)
41 return true;
42 }
43
44 *result = false;
45 return true;
46 }
47
48 // Determines whether the given ParseNode contains any declarations whose
49 // visibility will extend outside the node itself -- that is, whether the
50 // ParseNode contains any var statements.
51 //
52 // THIS IS NOT A GENERAL-PURPOSE FUNCTION. It is only written to work in the
53 // specific context of deciding that |node|, as one arm of a PNK_IF controlled
54 // by a constant condition, contains a declaration that forbids |node| being
55 // completely eliminated as dead.
56 static bool
ContainsHoistedDeclaration(ExclusiveContext * cx,ParseNode * node,bool * result)57 ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result)
58 {
59 JS_CHECK_RECURSION(cx, return false);
60
61 restart:
62
63 // With a better-typed AST, we would have distinct parse node classes for
64 // expressions and for statements and would characterize expressions with
65 // ExpressionKind and statements with StatementKind. Perhaps someday. In
66 // the meantime we must characterize every ParseNodeKind, even the
67 // expression/sub-expression ones that, if we handle all statement kinds
68 // correctly, we'll never see.
69 switch (node->getKind()) {
70 // Base case.
71 case PNK_VAR:
72 *result = true;
73 return true;
74
75 // Non-global lexical declarations are block-scoped (ergo not hoistable).
76 // (Global lexical declarations, in addition to being irrelevant here as
77 // ContainsHoistedDeclaration is only used on the arms of an |if|
78 // statement, are handled by PNK_VAR.)
79 case PNK_LET:
80 case PNK_CONST:
81 MOZ_ASSERT(node->isArity(PN_LIST));
82 *result = false;
83 return true;
84
85 // Similarly to the lexical declarations above, classes cannot add hoisted
86 // declarations
87 case PNK_CLASS:
88 MOZ_ASSERT(node->isArity(PN_TERNARY));
89 *result = false;
90 return true;
91
92 // ContainsHoistedDeclaration is only called on nested nodes, so any
93 // instance of this can't be function statements at body level. In
94 // SpiderMonkey, a binding induced by a function statement is added when
95 // the function statement is evaluated. Thus any declaration introduced
96 // by a function statement, as observed by this function, isn't a hoisted
97 // declaration.
98 case PNK_FUNCTION:
99 MOZ_ASSERT(node->isArity(PN_CODE));
100 *result = false;
101 return true;
102
103 case PNK_MODULE:
104 *result = false;
105 return true;
106
107 // Statements with no sub-components at all.
108 case PNK_NOP: // induced by function f() {} function f() {}
109 case PNK_DEBUGGER:
110 MOZ_ASSERT(node->isArity(PN_NULLARY));
111 *result = false;
112 return true;
113
114 // Statements containing only an expression have no declarations.
115 case PNK_SEMI:
116 case PNK_THROW:
117 case PNK_RETURN:
118 MOZ_ASSERT(node->isArity(PN_UNARY));
119 *result = false;
120 return true;
121
122 // These two aren't statements in the spec, but we sometimes insert them
123 // in statement lists anyway.
124 case PNK_YIELD_STAR:
125 case PNK_YIELD:
126 MOZ_ASSERT(node->isArity(PN_BINARY));
127 *result = false;
128 return true;
129
130 // Other statements with no sub-statement components.
131 case PNK_BREAK:
132 case PNK_CONTINUE:
133 case PNK_IMPORT:
134 case PNK_IMPORT_SPEC_LIST:
135 case PNK_IMPORT_SPEC:
136 case PNK_EXPORT_FROM:
137 case PNK_EXPORT_DEFAULT:
138 case PNK_EXPORT_SPEC_LIST:
139 case PNK_EXPORT_SPEC:
140 case PNK_EXPORT:
141 case PNK_EXPORT_BATCH_SPEC:
142 *result = false;
143 return true;
144
145 // Statements possibly containing hoistable declarations only in the left
146 // half, in ParseNode terms -- the loop body in AST terms.
147 case PNK_DOWHILE:
148 return ContainsHoistedDeclaration(cx, node->pn_left, result);
149
150 // Statements possibly containing hoistable declarations only in the
151 // right half, in ParseNode terms -- the loop body or nested statement
152 // (usually a block statement), in AST terms.
153 case PNK_WHILE:
154 case PNK_WITH:
155 return ContainsHoistedDeclaration(cx, node->pn_right, result);
156
157 case PNK_LABEL:
158 return ContainsHoistedDeclaration(cx, node->pn_expr, result);
159
160 // Statements with more complicated structures.
161
162 // if-statement nodes may have hoisted declarations in their consequent
163 // and alternative components.
164 case PNK_IF: {
165 MOZ_ASSERT(node->isArity(PN_TERNARY));
166
167 ParseNode* consequent = node->pn_kid2;
168 if (!ContainsHoistedDeclaration(cx, consequent, result))
169 return false;
170 if (*result)
171 return true;
172
173 if ((node = node->pn_kid3))
174 goto restart;
175
176 *result = false;
177 return true;
178 }
179
180 // Legacy array and generator comprehensions use PNK_IF to represent
181 // conditions specified in the comprehension tail: for example,
182 // [x for (x in obj) if (x)]. The consequent of such PNK_IF nodes is
183 // either PNK_YIELD in a PNK_SEMI statement (generator comprehensions) or
184 // PNK_ARRAYPUSH (array comprehensions) . The first case is consistent
185 // with normal if-statement structure with consequent/alternative as
186 // statements. The second case is abnormal and requires that we not
187 // banish PNK_ARRAYPUSH to the unreachable list, handling it explicitly.
188 //
189 // We could require that this one weird PNK_ARRAYPUSH case be packaged in
190 // a PNK_SEMI, for consistency. That requires careful bytecode emitter
191 // adjustment that seems unwarranted for a deprecated feature.
192 case PNK_ARRAYPUSH:
193 *result = false;
194 return true;
195
196 // try-statements have statements to execute, and one or both of a
197 // catch-list and a finally-block.
198 case PNK_TRY: {
199 MOZ_ASSERT(node->isArity(PN_TERNARY));
200 MOZ_ASSERT(node->pn_kid2 || node->pn_kid3,
201 "must have either catch(es) or finally");
202
203 ParseNode* tryBlock = node->pn_kid1;
204 if (!ContainsHoistedDeclaration(cx, tryBlock, result))
205 return false;
206 if (*result)
207 return true;
208
209 if (ParseNode* catchList = node->pn_kid2) {
210 for (ParseNode* lexicalScope = catchList->pn_head;
211 lexicalScope;
212 lexicalScope = lexicalScope->pn_next)
213 {
214 MOZ_ASSERT(lexicalScope->isKind(PNK_LEXICALSCOPE));
215
216 ParseNode* catchNode = lexicalScope->pn_expr;
217 MOZ_ASSERT(catchNode->isKind(PNK_CATCH));
218
219 ParseNode* catchStatements = catchNode->pn_kid3;
220 if (!ContainsHoistedDeclaration(cx, catchStatements, result))
221 return false;
222 if (*result)
223 return true;
224 }
225 }
226
227 if (ParseNode* finallyBlock = node->pn_kid3)
228 return ContainsHoistedDeclaration(cx, finallyBlock, result);
229
230 *result = false;
231 return true;
232 }
233
234 // A switch node's left half is an expression; only its right half (a
235 // list of cases/defaults, or a block node) could contain hoisted
236 // declarations.
237 case PNK_SWITCH:
238 MOZ_ASSERT(node->isArity(PN_BINARY));
239 return ContainsHoistedDeclaration(cx, node->pn_right, result);
240
241 case PNK_CASE:
242 return ContainsHoistedDeclaration(cx, node->as<CaseClause>().statementList(), result);
243
244 case PNK_FOR:
245 case PNK_COMPREHENSIONFOR: {
246 MOZ_ASSERT(node->isArity(PN_BINARY));
247
248 ParseNode* loopHead = node->pn_left;
249 MOZ_ASSERT(loopHead->isKind(PNK_FORHEAD) ||
250 loopHead->isKind(PNK_FORIN) ||
251 loopHead->isKind(PNK_FOROF));
252
253 if (loopHead->isKind(PNK_FORHEAD)) {
254 // for (init?; cond?; update?), with only init possibly containing
255 // a hoisted declaration. (Note: a lexical-declaration |init| is
256 // (at present) hoisted in SpiderMonkey parlance -- but such
257 // hoisting doesn't extend outside of this statement, so it is not
258 // hoisting in the sense meant by ContainsHoistedDeclaration.)
259 MOZ_ASSERT(loopHead->isArity(PN_TERNARY));
260
261 ParseNode* init = loopHead->pn_kid1;
262 if (init && init->isKind(PNK_VAR)) {
263 *result = true;
264 return true;
265 }
266 } else {
267 MOZ_ASSERT(loopHead->isKind(PNK_FORIN) || loopHead->isKind(PNK_FOROF));
268
269 // for each? (target in ...), where only target may introduce
270 // hoisted declarations.
271 //
272 // -- or --
273 //
274 // for (target of ...), where only target may introduce hoisted
275 // declarations.
276 //
277 // Either way, if |target| contains a declaration, it's |loopHead|'s
278 // first kid.
279 MOZ_ASSERT(loopHead->isArity(PN_TERNARY));
280
281 ParseNode* decl = loopHead->pn_kid1;
282 if (decl && decl->isKind(PNK_VAR)) {
283 *result = true;
284 return true;
285 }
286 }
287
288 ParseNode* loopBody = node->pn_right;
289 return ContainsHoistedDeclaration(cx, loopBody, result);
290 }
291
292 case PNK_LETBLOCK: {
293 MOZ_ASSERT(node->isArity(PN_BINARY));
294 MOZ_ASSERT(node->pn_right->isKind(PNK_LEXICALSCOPE));
295 MOZ_ASSERT(node->pn_left->isKind(PNK_LET) ||
296 (node->pn_left->isKind(PNK_CONST) && node->pn_right->pn_expr->isKind(PNK_FOR)),
297 "a let-block's left half is its declarations: ordinarily a PNK_LET node but "
298 "PNK_CONST in the weird case of |for (const x ...)|");
299 return ContainsHoistedDeclaration(cx, node->pn_right, result);
300 }
301
302 case PNK_LEXICALSCOPE: {
303 MOZ_ASSERT(node->isArity(PN_NAME));
304 ParseNode* expr = node->pn_expr;
305
306 if (expr->isKind(PNK_FOR))
307 return ContainsHoistedDeclaration(cx, expr, result);
308
309 MOZ_ASSERT(expr->isKind(PNK_STATEMENTLIST));
310 return ListContainsHoistedDeclaration(cx, &node->pn_expr->as<ListNode>(), result);
311 }
312
313 // List nodes with all non-null children.
314 case PNK_STATEMENTLIST:
315 return ListContainsHoistedDeclaration(cx, &node->as<ListNode>(), result);
316
317 // Grammar sub-components that should never be reached directly by this
318 // method, because some parent component should have asserted itself.
319 case PNK_OBJECT_PROPERTY_NAME:
320 case PNK_COMPUTED_NAME:
321 case PNK_SPREAD:
322 case PNK_MUTATEPROTO:
323 case PNK_COLON:
324 case PNK_SHORTHAND:
325 case PNK_CONDITIONAL:
326 case PNK_TYPEOFNAME:
327 case PNK_TYPEOFEXPR:
328 case PNK_VOID:
329 case PNK_NOT:
330 case PNK_BITNOT:
331 case PNK_DELETENAME:
332 case PNK_DELETEPROP:
333 case PNK_DELETEELEM:
334 case PNK_DELETEEXPR:
335 case PNK_POS:
336 case PNK_NEG:
337 case PNK_PREINCREMENT:
338 case PNK_POSTINCREMENT:
339 case PNK_PREDECREMENT:
340 case PNK_POSTDECREMENT:
341 case PNK_OR:
342 case PNK_AND:
343 case PNK_BITOR:
344 case PNK_BITXOR:
345 case PNK_BITAND:
346 case PNK_STRICTEQ:
347 case PNK_EQ:
348 case PNK_STRICTNE:
349 case PNK_NE:
350 case PNK_LT:
351 case PNK_LE:
352 case PNK_GT:
353 case PNK_GE:
354 case PNK_INSTANCEOF:
355 case PNK_IN:
356 case PNK_LSH:
357 case PNK_RSH:
358 case PNK_URSH:
359 case PNK_ADD:
360 case PNK_SUB:
361 case PNK_STAR:
362 case PNK_DIV:
363 case PNK_MOD:
364 case PNK_POW:
365 case PNK_ASSIGN:
366 case PNK_ADDASSIGN:
367 case PNK_SUBASSIGN:
368 case PNK_BITORASSIGN:
369 case PNK_BITXORASSIGN:
370 case PNK_BITANDASSIGN:
371 case PNK_LSHASSIGN:
372 case PNK_RSHASSIGN:
373 case PNK_URSHASSIGN:
374 case PNK_MULASSIGN:
375 case PNK_DIVASSIGN:
376 case PNK_MODASSIGN:
377 case PNK_POWASSIGN:
378 case PNK_COMMA:
379 case PNK_ARRAY:
380 case PNK_OBJECT:
381 case PNK_DOT:
382 case PNK_ELEM:
383 case PNK_CALL:
384 case PNK_NAME:
385 case PNK_TEMPLATE_STRING:
386 case PNK_TEMPLATE_STRING_LIST:
387 case PNK_TAGGED_TEMPLATE:
388 case PNK_CALLSITEOBJ:
389 case PNK_STRING:
390 case PNK_REGEXP:
391 case PNK_TRUE:
392 case PNK_FALSE:
393 case PNK_NULL:
394 case PNK_THIS:
395 case PNK_ELISION:
396 case PNK_NUMBER:
397 case PNK_NEW:
398 case PNK_GENERATOR:
399 case PNK_GENEXP:
400 case PNK_ARRAYCOMP:
401 case PNK_ARGSBODY:
402 case PNK_CATCHLIST:
403 case PNK_CATCH:
404 case PNK_FORIN:
405 case PNK_FOROF:
406 case PNK_FORHEAD:
407 case PNK_CLASSMETHOD:
408 case PNK_CLASSMETHODLIST:
409 case PNK_CLASSNAMES:
410 case PNK_NEWTARGET:
411 case PNK_POSHOLDER:
412 case PNK_SUPERCALL:
413 case PNK_SUPERBASE:
414 case PNK_SETTHIS:
415 MOZ_CRASH("ContainsHoistedDeclaration should have indicated false on "
416 "some parent node without recurring to test this node");
417
418 case PNK_LIMIT: // invalid sentinel value
419 MOZ_CRASH("unexpected PNK_LIMIT in node");
420 }
421
422 MOZ_CRASH("invalid node kind");
423 }
424
425 /*
426 * Fold from one constant type to another.
427 * XXX handles only strings and numbers for now
428 */
429 static bool
FoldType(ExclusiveContext * cx,ParseNode * pn,ParseNodeKind kind)430 FoldType(ExclusiveContext* cx, ParseNode* pn, ParseNodeKind kind)
431 {
432 if (!pn->isKind(kind)) {
433 switch (kind) {
434 case PNK_NUMBER:
435 if (pn->isKind(PNK_STRING)) {
436 double d;
437 if (!StringToNumber(cx, pn->pn_atom, &d))
438 return false;
439 pn->pn_dval = d;
440 pn->setKind(PNK_NUMBER);
441 pn->setOp(JSOP_DOUBLE);
442 }
443 break;
444
445 case PNK_STRING:
446 if (pn->isKind(PNK_NUMBER)) {
447 pn->pn_atom = NumberToAtom(cx, pn->pn_dval);
448 if (!pn->pn_atom)
449 return false;
450 pn->setKind(PNK_STRING);
451 pn->setOp(JSOP_STRING);
452 }
453 break;
454
455 default:;
456 }
457 }
458 return true;
459 }
460
461 // Remove a ParseNode, **pnp, from a parse tree, putting another ParseNode,
462 // *pn, in its place.
463 //
464 // pnp points to a ParseNode pointer. This must be the only pointer that points
465 // to the parse node being replaced. The replacement, *pn, is unchanged except
466 // for its pn_next pointer; updating that is necessary if *pn's new parent is a
467 // list node.
468 static void
ReplaceNode(ParseNode ** pnp,ParseNode * pn)469 ReplaceNode(ParseNode** pnp, ParseNode* pn)
470 {
471 pn->pn_next = (*pnp)->pn_next;
472 *pnp = pn;
473 }
474
475 static bool
IsEffectless(ParseNode * node)476 IsEffectless(ParseNode* node)
477 {
478 return node->isKind(PNK_TRUE) ||
479 node->isKind(PNK_FALSE) ||
480 node->isKind(PNK_STRING) ||
481 node->isKind(PNK_TEMPLATE_STRING) ||
482 node->isKind(PNK_NUMBER) ||
483 node->isKind(PNK_NULL) ||
484 node->isKind(PNK_FUNCTION) ||
485 node->isKind(PNK_GENEXP);
486 }
487
488 enum Truthiness { Truthy, Falsy, Unknown };
489
490 static Truthiness
Boolish(ParseNode * pn)491 Boolish(ParseNode* pn)
492 {
493 switch (pn->getKind()) {
494 case PNK_NUMBER:
495 return (pn->pn_dval != 0 && !IsNaN(pn->pn_dval)) ? Truthy : Falsy;
496
497 case PNK_STRING:
498 case PNK_TEMPLATE_STRING:
499 return (pn->pn_atom->length() > 0) ? Truthy : Falsy;
500
501 case PNK_TRUE:
502 case PNK_FUNCTION:
503 case PNK_GENEXP:
504 return Truthy;
505
506 case PNK_FALSE:
507 case PNK_NULL:
508 return Falsy;
509
510 case PNK_VOID: {
511 // |void <foo>| evaluates to |undefined| which isn't truthy. But the
512 // sense of this method requires that the expression be literally
513 // replaceable with true/false: not the case if the nested expression
514 // is effectful, might throw, &c. Walk past the |void| (and nested
515 // |void| expressions, for good measure) and check that the nested
516 // expression doesn't break this requirement before indicating falsity.
517 do {
518 pn = pn->pn_kid;
519 } while (pn->isKind(PNK_VOID));
520
521 return IsEffectless(pn) ? Falsy : Unknown;
522 }
523
524 default:
525 return Unknown;
526 }
527 }
528
529 static bool
530 Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bool inGenexpLambda);
531
532 static bool
FoldCondition(ExclusiveContext * cx,ParseNode ** nodePtr,Parser<FullParseHandler> & parser,bool inGenexpLambda)533 FoldCondition(ExclusiveContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
534 bool inGenexpLambda)
535 {
536 // Conditions fold like any other expression...
537 if (!Fold(cx, nodePtr, parser, inGenexpLambda))
538 return false;
539
540 // ...but then they sometimes can be further folded to constants.
541 ParseNode* node = *nodePtr;
542 Truthiness t = Boolish(node);
543 if (t != Unknown) {
544 // We can turn function nodes into constant nodes here, but mutating
545 // function nodes is tricky --- in particular, mutating a function node
546 // that appears on a method list corrupts the method list. However,
547 // methods are M's in statements of the form 'this.foo = M;', which we
548 // never fold, so we're okay.
549 parser.prepareNodeForMutation(node);
550 if (t == Truthy) {
551 node->setKind(PNK_TRUE);
552 node->setOp(JSOP_TRUE);
553 } else {
554 node->setKind(PNK_FALSE);
555 node->setOp(JSOP_FALSE);
556 }
557 node->setArity(PN_NULLARY);
558 }
559
560 return true;
561 }
562
563 static bool
FoldTypeOfExpr(ExclusiveContext * cx,ParseNode * node,Parser<FullParseHandler> & parser,bool inGenexpLambda)564 FoldTypeOfExpr(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
565 bool inGenexpLambda)
566 {
567 MOZ_ASSERT(node->isKind(PNK_TYPEOFEXPR));
568 MOZ_ASSERT(node->isArity(PN_UNARY));
569
570 ParseNode*& expr = node->pn_kid;
571 if (!Fold(cx, &expr, parser, inGenexpLambda))
572 return false;
573
574 // Constant-fold the entire |typeof| if given a constant with known type.
575 RootedPropertyName result(cx);
576 if (expr->isKind(PNK_STRING) || expr->isKind(PNK_TEMPLATE_STRING))
577 result = cx->names().string;
578 else if (expr->isKind(PNK_NUMBER))
579 result = cx->names().number;
580 else if (expr->isKind(PNK_NULL))
581 result = cx->names().object;
582 else if (expr->isKind(PNK_TRUE) || expr->isKind(PNK_FALSE))
583 result = cx->names().boolean;
584 else if (expr->isKind(PNK_FUNCTION))
585 result = cx->names().function;
586
587 if (result) {
588 parser.prepareNodeForMutation(node);
589
590 node->setKind(PNK_STRING);
591 node->setArity(PN_NULLARY);
592 node->setOp(JSOP_NOP);
593 node->pn_atom = result;
594 }
595
596 return true;
597 }
598
599 static bool
FoldDeleteExpr(ExclusiveContext * cx,ParseNode * node,Parser<FullParseHandler> & parser,bool inGenexpLambda)600 FoldDeleteExpr(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
601 bool inGenexpLambda)
602 {
603 MOZ_ASSERT(node->isKind(PNK_DELETEEXPR));
604 MOZ_ASSERT(node->isArity(PN_UNARY));
605
606 ParseNode*& expr = node->pn_kid;
607 if (!Fold(cx, &expr, parser, inGenexpLambda))
608 return false;
609
610 // Expression deletion evaluates the expression, then evaluates to true.
611 // For effectless expressions, eliminate the expression evaluation.
612 if (IsEffectless(expr)) {
613 parser.prepareNodeForMutation(node);
614 node->setKind(PNK_TRUE);
615 node->setArity(PN_NULLARY);
616 node->setOp(JSOP_TRUE);
617 }
618
619 return true;
620 }
621
622 static bool
FoldDeleteElement(ExclusiveContext * cx,ParseNode * node,Parser<FullParseHandler> & parser,bool inGenexpLambda)623 FoldDeleteElement(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
624 bool inGenexpLambda)
625 {
626 MOZ_ASSERT(node->isKind(PNK_DELETEELEM));
627 MOZ_ASSERT(node->isArity(PN_UNARY));
628 MOZ_ASSERT(node->pn_kid->isKind(PNK_ELEM));
629
630 ParseNode*& expr = node->pn_kid;
631 if (!Fold(cx, &expr, parser, inGenexpLambda))
632 return false;
633
634 // If we're deleting an element, but constant-folding converted our
635 // element reference into a dotted property access, we must *also*
636 // morph the node's kind.
637 //
638 // In principle this also applies to |super["foo"] -> super.foo|,
639 // but we don't constant-fold |super["foo"]| yet.
640 MOZ_ASSERT(expr->isKind(PNK_ELEM) || expr->isKind(PNK_DOT));
641 if (expr->isKind(PNK_DOT))
642 node->setKind(PNK_DELETEPROP);
643
644 return true;
645 }
646
647 static bool
FoldDeleteProperty(ExclusiveContext * cx,ParseNode * node,Parser<FullParseHandler> & parser,bool inGenexpLambda)648 FoldDeleteProperty(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
649 bool inGenexpLambda)
650 {
651 MOZ_ASSERT(node->isKind(PNK_DELETEPROP));
652 MOZ_ASSERT(node->isArity(PN_UNARY));
653 MOZ_ASSERT(node->pn_kid->isKind(PNK_DOT));
654
655 ParseNode*& expr = node->pn_kid;
656 #ifdef DEBUG
657 ParseNodeKind oldKind = expr->getKind();
658 #endif
659
660 if (!Fold(cx, &expr, parser, inGenexpLambda))
661 return false;
662
663 MOZ_ASSERT(expr->isKind(oldKind),
664 "kind should have remained invariant under folding");
665
666 return true;
667 }
668
669 static bool
FoldNot(ExclusiveContext * cx,ParseNode * node,Parser<FullParseHandler> & parser,bool inGenexpLambda)670 FoldNot(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
671 bool inGenexpLambda)
672 {
673 MOZ_ASSERT(node->isKind(PNK_NOT));
674 MOZ_ASSERT(node->isArity(PN_UNARY));
675
676 ParseNode*& expr = node->pn_kid;
677 if (!FoldCondition(cx, &expr, parser, inGenexpLambda))
678 return false;
679
680 if (expr->isKind(PNK_NUMBER)) {
681 double d = expr->pn_dval;
682
683 parser.prepareNodeForMutation(node);
684 if (d == 0 || IsNaN(d)) {
685 node->setKind(PNK_TRUE);
686 node->setOp(JSOP_TRUE);
687 } else {
688 node->setKind(PNK_FALSE);
689 node->setOp(JSOP_FALSE);
690 }
691 node->setArity(PN_NULLARY);
692 } else if (expr->isKind(PNK_TRUE) || expr->isKind(PNK_FALSE)) {
693 bool newval = !expr->isKind(PNK_TRUE);
694
695 parser.prepareNodeForMutation(node);
696 node->setKind(newval ? PNK_TRUE : PNK_FALSE);
697 node->setArity(PN_NULLARY);
698 node->setOp(newval ? JSOP_TRUE : JSOP_FALSE);
699 }
700
701 return true;
702 }
703
704 static bool
FoldUnaryArithmetic(ExclusiveContext * cx,ParseNode * node,Parser<FullParseHandler> & parser,bool inGenexpLambda)705 FoldUnaryArithmetic(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
706 bool inGenexpLambda)
707 {
708 MOZ_ASSERT(node->isKind(PNK_BITNOT) || node->isKind(PNK_POS) || node->isKind(PNK_NEG),
709 "need a different method for this node kind");
710 MOZ_ASSERT(node->isArity(PN_UNARY));
711
712 ParseNode*& expr = node->pn_kid;
713 if (!Fold(cx, &expr, parser, inGenexpLambda))
714 return false;
715
716 if (expr->isKind(PNK_NUMBER) || expr->isKind(PNK_TRUE) || expr->isKind(PNK_FALSE)) {
717 double d = expr->isKind(PNK_NUMBER)
718 ? expr->pn_dval
719 : double(expr->isKind(PNK_TRUE));
720
721 if (node->isKind(PNK_BITNOT))
722 d = ~ToInt32(d);
723 else if (node->isKind(PNK_NEG))
724 d = -d;
725 else
726 MOZ_ASSERT(node->isKind(PNK_POS)); // nothing to do
727
728 parser.prepareNodeForMutation(node);
729 node->setKind(PNK_NUMBER);
730 node->setOp(JSOP_DOUBLE);
731 node->setArity(PN_NULLARY);
732 node->pn_dval = d;
733 }
734
735 return true;
736 }
737
738 static bool
FoldIncrementDecrement(ExclusiveContext * cx,ParseNode * node,Parser<FullParseHandler> & parser,bool inGenexpLambda)739 FoldIncrementDecrement(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
740 bool inGenexpLambda)
741 {
742 MOZ_ASSERT(node->isKind(PNK_PREINCREMENT) ||
743 node->isKind(PNK_POSTINCREMENT) ||
744 node->isKind(PNK_PREDECREMENT) ||
745 node->isKind(PNK_POSTDECREMENT));
746 MOZ_ASSERT(node->isArity(PN_UNARY));
747
748 ParseNode*& target = node->pn_kid;
749 MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser<FullParseHandler>::PermitAssignmentToFunctionCalls));
750
751 if (!Fold(cx, &target, parser, inGenexpLambda))
752 return false;
753
754 MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser<FullParseHandler>::PermitAssignmentToFunctionCalls));
755
756 return true;
757 }
758
759 static bool
FoldAndOr(ExclusiveContext * cx,ParseNode ** nodePtr,Parser<FullParseHandler> & parser,bool inGenexpLambda)760 FoldAndOr(ExclusiveContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
761 bool inGenexpLambda)
762 {
763 ParseNode* node = *nodePtr;
764
765 MOZ_ASSERT(node->isKind(PNK_AND) || node->isKind(PNK_OR));
766 MOZ_ASSERT(node->isArity(PN_LIST));
767
768 bool isOrNode = node->isKind(PNK_OR);
769 ParseNode** elem = &node->pn_head;
770 do {
771 if (!Fold(cx, elem, parser, inGenexpLambda))
772 return false;
773
774 Truthiness t = Boolish(*elem);
775
776 // If we don't know the constant-folded node's truthiness, we can't
777 // reduce this node with its surroundings. Continue folding any
778 // remaining nodes.
779 if (t == Unknown) {
780 elem = &(*elem)->pn_next;
781 continue;
782 }
783
784 // If the constant-folded node's truthiness will terminate the
785 // condition -- `a || true || expr` or |b && false && expr| -- then
786 // trailing nodes will never be evaluated. Truncate the list after
787 // the known-truthiness node, as it's the overall result.
788 if ((t == Truthy) == isOrNode) {
789 ParseNode* afterNext;
790 for (ParseNode* next = (*elem)->pn_next; next; next = afterNext) {
791 afterNext = next->pn_next;
792 parser.handler.freeTree(next);
793 --node->pn_count;
794 }
795
796 // Terminate the original and/or list at the known-truthiness
797 // node.
798 (*elem)->pn_next = nullptr;
799 elem = &(*elem)->pn_next;
800 break;
801 }
802
803 MOZ_ASSERT((t == Truthy) == !isOrNode);
804
805 // We've encountered a vacuous node that'll never short- circuit
806 // evaluation.
807 if ((*elem)->pn_next) {
808 // This node is never the overall result when there are
809 // subsequent nodes. Remove it.
810 ParseNode* elt = *elem;
811 *elem = elt->pn_next;
812 parser.handler.freeTree(elt);
813 --node->pn_count;
814 } else {
815 // Otherwise this node is the result of the overall expression,
816 // so leave it alone. And we're done.
817 elem = &(*elem)->pn_next;
818 break;
819 }
820 } while (*elem);
821
822 // If the last node in the list was replaced, we need to update the
823 // tail pointer in the original and/or node.
824 node->pn_tail = elem;
825
826 node->checkListConsistency();
827
828 // If we removed nodes, we may have to replace a one-element list with
829 // its element.
830 if (node->pn_count == 1) {
831 ParseNode* first = node->pn_head;
832 ReplaceNode(nodePtr, first);
833
834 node->setKind(PNK_NULL);
835 node->setArity(PN_NULLARY);
836 parser.freeTree(node);
837 }
838
839 return true;
840 }
841
842 static bool
FoldConditional(ExclusiveContext * cx,ParseNode ** nodePtr,Parser<FullParseHandler> & parser,bool inGenexpLambda)843 FoldConditional(ExclusiveContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
844 bool inGenexpLambda)
845 {
846 ParseNode** nextNode = nodePtr;
847
848 do {
849 // |nextNode| on entry points to the C?T:F expression to be folded.
850 // Reset it to exit the loop in the common case where F isn't another
851 // ?: expression.
852 nodePtr = nextNode;
853 nextNode = nullptr;
854
855 ParseNode* node = *nodePtr;
856 MOZ_ASSERT(node->isKind(PNK_CONDITIONAL));
857 MOZ_ASSERT(node->isArity(PN_TERNARY));
858
859 ParseNode*& expr = node->pn_kid1;
860 if (!FoldCondition(cx, &expr, parser, inGenexpLambda))
861 return false;
862
863 ParseNode*& ifTruthy = node->pn_kid2;
864 if (!Fold(cx, &ifTruthy, parser, inGenexpLambda))
865 return false;
866
867 ParseNode*& ifFalsy = node->pn_kid3;
868
869 // If our C?T:F node has F as another ?: node, *iteratively* constant-
870 // fold F *after* folding C and T (and possibly eliminating C and one
871 // of T/F entirely); otherwise fold F normally. Making |nextNode| non-
872 // null causes this loop to run again to fold F.
873 //
874 // Conceivably we could instead/also iteratively constant-fold T, if T
875 // were more complex than F. Such an optimization is unimplemented.
876 if (ifFalsy->isKind(PNK_CONDITIONAL)) {
877 nextNode = &ifFalsy;
878 } else {
879 if (!Fold(cx, &ifFalsy, parser, inGenexpLambda))
880 return false;
881 }
882
883 // Try to constant-fold based on the condition expression.
884 Truthiness t = Boolish(expr);
885 if (t == Unknown)
886 continue;
887
888 // Otherwise reduce 'C ? T : F' to T or F as directed by C.
889 ParseNode* replacement;
890 ParseNode* discarded;
891 if (t == Truthy) {
892 replacement = ifTruthy;
893 discarded = ifFalsy;
894 } else {
895 replacement = ifFalsy;
896 discarded = ifTruthy;
897 }
898
899 // Don't decay the overall expression if the replacement node is a
900 // a definition.
901 //
902 // The rationale for this pre-existing restriction is unclear; if you
903 // discover it, please document it! Speculation is that it has
904 // something to do with constant-folding something like:
905 //
906 // true ? function f() {} : false;
907 //
908 // into
909 //
910 // function f() {}
911 //
912 // and worrying this might convert a function *expression* into a
913 // function *statement* that defined its name early. But function
914 // expressions aren't isDefn(), so this can't be it.
915 //
916 // This lack of explanation is tolerated only because failing to
917 // optimize *should* always be okay.
918 if (replacement->isDefn())
919 continue;
920
921 // Otherwise perform a replacement. This invalidates |nextNode|, so
922 // reset it (if the replacement requires folding) or clear it (if
923 // |ifFalsy| is dead code) as needed.
924 if (nextNode)
925 nextNode = (*nextNode == replacement) ? nodePtr : nullptr;
926 ReplaceNode(nodePtr, replacement);
927
928 parser.freeTree(discarded);
929 } while (nextNode);
930
931 return true;
932 }
933
934 static bool
FoldIf(ExclusiveContext * cx,ParseNode ** nodePtr,Parser<FullParseHandler> & parser,bool inGenexpLambda)935 FoldIf(ExclusiveContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
936 bool inGenexpLambda)
937 {
938 ParseNode** nextNode = nodePtr;
939
940 do {
941 // |nextNode| on entry points to the initial |if| to be folded. Reset
942 // it to exit the loop when the |else| arm isn't another |if|.
943 nodePtr = nextNode;
944 nextNode = nullptr;
945
946 ParseNode* node = *nodePtr;
947 MOZ_ASSERT(node->isKind(PNK_IF));
948 MOZ_ASSERT(node->isArity(PN_TERNARY));
949
950 ParseNode*& expr = node->pn_kid1;
951 if (!FoldCondition(cx, &expr, parser, inGenexpLambda))
952 return false;
953
954 ParseNode*& consequent = node->pn_kid2;
955 if (!Fold(cx, &consequent, parser, inGenexpLambda))
956 return false;
957
958 ParseNode*& alternative = node->pn_kid3;
959 if (alternative) {
960 // If in |if (C) T; else F;| we have |F| as another |if|,
961 // *iteratively* constant-fold |F| *after* folding |C| and |T| (and
962 // possibly completely replacing the whole thing with |T| or |F|);
963 // otherwise fold F normally. Making |nextNode| non-null causes
964 // this loop to run again to fold F.
965 if (alternative->isKind(PNK_IF)) {
966 nextNode = &alternative;
967 } else {
968 if (!Fold(cx, &alternative, parser, inGenexpLambda))
969 return false;
970 }
971 }
972
973 // Eliminate the consequent or alternative if the condition has
974 // constant truthiness. Don't eliminate if we have an |if (0)| in
975 // trailing position in a generator expression, as this is a special
976 // form we can't fold away.
977 Truthiness t = Boolish(expr);
978 if (t == Unknown || inGenexpLambda)
979 continue;
980
981 // Careful! Either of these can be null: |replacement| in |if (0) T;|,
982 // and |discarded| in |if (true) T;|.
983 ParseNode* replacement;
984 ParseNode* discarded;
985 if (t == Truthy) {
986 replacement = consequent;
987 discarded = alternative;
988 } else {
989 replacement = alternative;
990 discarded = consequent;
991 }
992
993 bool performReplacement = true;
994 if (discarded) {
995 // A declaration that hoists outside the discarded arm prevents the
996 // |if| from being folded away.
997 bool containsHoistedDecls;
998 if (!ContainsHoistedDeclaration(cx, discarded, &containsHoistedDecls))
999 return false;
1000
1001 performReplacement = !containsHoistedDecls;
1002 }
1003
1004 if (!performReplacement)
1005 continue;
1006
1007 if (!replacement) {
1008 // If there's no replacement node, we have a constantly-false |if|
1009 // with no |else|. Replace the entire thing with an empty
1010 // statement list.
1011 parser.prepareNodeForMutation(node);
1012 node->setKind(PNK_STATEMENTLIST);
1013 node->setArity(PN_LIST);
1014 node->makeEmpty();
1015 } else {
1016 // As with PNK_CONDITIONAL, replace only if the replacement isn't a
1017 // definition. As there, the rationale for this restriction is
1018 // unclear and undocumented: tolerated only because a failure to
1019 // optimize *should* be safe. The best guess is that this test was
1020 // an incomplete, buggy version of the |ContainsHoistedDeclaration|
1021 // test.
1022 if (replacement->isDefn())
1023 continue;
1024
1025 // Replacement invalidates |nextNode|, so reset it (if the
1026 // replacement requires folding) or clear it (if |alternative|
1027 // is dead code) as needed.
1028 if (nextNode)
1029 nextNode = (*nextNode == replacement) ? nodePtr : nullptr;
1030 ReplaceNode(nodePtr, replacement);
1031
1032 // Morph the original node into a discardable node, then
1033 // aggressively free it and the discarded arm (if any) to suss out
1034 // any bugs in the preceding logic.
1035 node->setKind(PNK_STATEMENTLIST);
1036 node->setArity(PN_LIST);
1037 node->makeEmpty();
1038 if (discarded)
1039 node->append(discarded);
1040 parser.freeTree(node);
1041 }
1042 } while (nextNode);
1043
1044 return true;
1045 }
1046
1047 static bool
FoldFunction(ExclusiveContext * cx,ParseNode * node,Parser<FullParseHandler> & parser,bool inGenexpLambda)1048 FoldFunction(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
1049 bool inGenexpLambda)
1050 {
1051 MOZ_ASSERT(node->isKind(PNK_FUNCTION));
1052 MOZ_ASSERT(node->isArity(PN_CODE));
1053
1054 // Don't constant-fold inside "use asm" code, as this could create a parse
1055 // tree that doesn't type-check as asm.js.
1056 if (node->pn_funbox->useAsmOrInsideUseAsm())
1057 return true;
1058
1059 // Note: pn_body is null for lazily-parsed functions.
1060 if (ParseNode*& functionBody = node->pn_body) {
1061 if (!Fold(cx, &functionBody, parser, node->pn_funbox->inGenexpLambda))
1062 return false;
1063 }
1064
1065 return true;
1066 }
1067
1068 static double
ComputeBinary(ParseNodeKind kind,double left,double right)1069 ComputeBinary(ParseNodeKind kind, double left, double right)
1070 {
1071 if (kind == PNK_ADD)
1072 return left + right;
1073
1074 if (kind == PNK_SUB)
1075 return left - right;
1076
1077 if (kind == PNK_STAR)
1078 return left * right;
1079
1080 if (kind == PNK_MOD)
1081 return right == 0 ? GenericNaN() : js_fmod(left, right);
1082
1083 if (kind == PNK_URSH)
1084 return ToUint32(left) >> (ToUint32(right) & 31);
1085
1086 if (kind == PNK_DIV) {
1087 if (right == 0) {
1088 #if defined(XP_WIN)
1089 /* XXX MSVC miscompiles such that (NaN == 0) */
1090 if (IsNaN(right))
1091 return GenericNaN();
1092 #endif
1093 if (left == 0 || IsNaN(left))
1094 return GenericNaN();
1095 if (IsNegative(left) != IsNegative(right))
1096 return NegativeInfinity<double>();
1097 return PositiveInfinity<double>();
1098 }
1099
1100 return left / right;
1101 }
1102
1103 MOZ_ASSERT(kind == PNK_LSH || kind == PNK_RSH);
1104
1105 int32_t i = ToInt32(left);
1106 uint32_t j = ToUint32(right) & 31;
1107 return int32_t((kind == PNK_LSH) ? uint32_t(i) << j : i >> j);
1108 }
1109
1110 static bool
FoldModule(ExclusiveContext * cx,ParseNode * node,Parser<FullParseHandler> & parser)1111 FoldModule(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser)
1112 {
1113 MOZ_ASSERT(node->isKind(PNK_MODULE));
1114 MOZ_ASSERT(node->isArity(PN_CODE));
1115
1116 ParseNode*& moduleBody = node->pn_body;
1117 MOZ_ASSERT(moduleBody);
1118 return Fold(cx, &moduleBody, parser, false);
1119 }
1120
1121 static bool
FoldBinaryArithmetic(ExclusiveContext * cx,ParseNode * node,Parser<FullParseHandler> & parser,bool inGenexpLambda)1122 FoldBinaryArithmetic(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
1123 bool inGenexpLambda)
1124 {
1125 MOZ_ASSERT(node->isKind(PNK_SUB) ||
1126 node->isKind(PNK_STAR) ||
1127 node->isKind(PNK_LSH) ||
1128 node->isKind(PNK_RSH) ||
1129 node->isKind(PNK_URSH) ||
1130 node->isKind(PNK_DIV) ||
1131 node->isKind(PNK_MOD));
1132 MOZ_ASSERT(node->isArity(PN_LIST));
1133 MOZ_ASSERT(node->pn_count >= 2);
1134
1135 // Fold each operand, ideally into a number.
1136 ParseNode** listp = &node->pn_head;
1137 for (; *listp; listp = &(*listp)->pn_next) {
1138 if (!Fold(cx, listp, parser, inGenexpLambda))
1139 return false;
1140
1141 if (!FoldType(cx, *listp, PNK_NUMBER))
1142 return false;
1143 }
1144
1145 // Repoint the list's tail pointer.
1146 node->pn_tail = listp;
1147
1148 // Now fold all leading numeric terms together into a single number.
1149 // (Trailing terms for the non-shift operations can't be folded together
1150 // due to floating point imprecision. For example, if |x === -2**53|,
1151 // |x - 1 - 1 === -2**53| but |x - 2 === -2**53 - 2|. Shifts could be
1152 // folded, but it doesn't seem worth the effort.)
1153 ParseNode* elem = node->pn_head;
1154 ParseNode* next = elem->pn_next;
1155 if (elem->isKind(PNK_NUMBER)) {
1156 ParseNodeKind kind = node->getKind();
1157 while (true) {
1158 if (!next || !next->isKind(PNK_NUMBER))
1159 break;
1160
1161 double d = ComputeBinary(kind, elem->pn_dval, next->pn_dval);
1162
1163 ParseNode* afterNext = next->pn_next;
1164 parser.freeTree(next);
1165 next = afterNext;
1166 elem->pn_next = next;
1167
1168 elem->setKind(PNK_NUMBER);
1169 elem->setOp(JSOP_DOUBLE);
1170 elem->setArity(PN_NULLARY);
1171 elem->pn_dval = d;
1172
1173 node->pn_count--;
1174 }
1175
1176 if (node->pn_count == 1) {
1177 MOZ_ASSERT(node->pn_head == elem);
1178 MOZ_ASSERT(elem->isKind(PNK_NUMBER));
1179
1180 double d = elem->pn_dval;
1181 node->setKind(PNK_NUMBER);
1182 node->setArity(PN_NULLARY);
1183 node->setOp(JSOP_DOUBLE);
1184 node->pn_dval = d;
1185
1186 parser.freeTree(elem);
1187 }
1188 }
1189
1190 return true;
1191 }
1192
1193 static bool
FoldExponentiation(ExclusiveContext * cx,ParseNode * node,Parser<FullParseHandler> & parser,bool inGenexpLambda)1194 FoldExponentiation(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
1195 bool inGenexpLambda)
1196 {
1197 MOZ_ASSERT(node->isKind(PNK_POW));
1198 MOZ_ASSERT(node->isArity(PN_LIST));
1199 MOZ_ASSERT(node->pn_count >= 2);
1200
1201 // Fold each operand, ideally into a number.
1202 ParseNode** listp = &node->pn_head;
1203 for (; *listp; listp = &(*listp)->pn_next) {
1204 if (!Fold(cx, listp, parser, inGenexpLambda))
1205 return false;
1206
1207 if (!FoldType(cx, *listp, PNK_NUMBER))
1208 return false;
1209 }
1210
1211 // Repoint the list's tail pointer.
1212 node->pn_tail = listp;
1213
1214 // Unlike all other binary arithmetic operators, ** is right-associative:
1215 // 2**3**5 is 2**(3**5), not (2**3)**5. As list nodes singly-link their
1216 // children, full constant-folding requires either linear space or dodgy
1217 // in-place linked list reversal. So we only fold one exponentiation: it's
1218 // easy and addresses common cases like |2**32|.
1219 if (node->pn_count > 2)
1220 return true;
1221
1222 ParseNode* base = node->pn_head;
1223 ParseNode* exponent = base->pn_next;
1224 if (!base->isKind(PNK_NUMBER) || !exponent->isKind(PNK_NUMBER))
1225 return true;
1226
1227 double d1 = base->pn_dval, d2 = exponent->pn_dval;
1228
1229 parser.prepareNodeForMutation(node);
1230 node->setKind(PNK_NUMBER);
1231 node->setArity(PN_NULLARY);
1232 node->setOp(JSOP_DOUBLE);
1233 node->pn_dval = ecmaPow(d1, d2);
1234 return true;
1235 }
1236
1237 static bool
FoldList(ExclusiveContext * cx,ParseNode * list,Parser<FullParseHandler> & parser,bool inGenexpLambda)1238 FoldList(ExclusiveContext* cx, ParseNode* list, Parser<FullParseHandler>& parser,
1239 bool inGenexpLambda)
1240 {
1241 MOZ_ASSERT(list->isArity(PN_LIST));
1242
1243 ParseNode** elem = &list->pn_head;
1244 for (; *elem; elem = &(*elem)->pn_next) {
1245 if (!Fold(cx, elem, parser, inGenexpLambda))
1246 return false;
1247 }
1248
1249 // Repoint the list's tail pointer if the final element was replaced.
1250 list->pn_tail = elem;
1251
1252 list->checkListConsistency();
1253
1254 return true;
1255 }
1256
1257 static bool
FoldReturn(ExclusiveContext * cx,ParseNode * node,Parser<FullParseHandler> & parser,bool inGenexpLambda)1258 FoldReturn(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
1259 bool inGenexpLambda)
1260 {
1261 MOZ_ASSERT(node->isKind(PNK_RETURN));
1262 MOZ_ASSERT(node->isArity(PN_UNARY));
1263
1264 if (ParseNode*& expr = node->pn_kid) {
1265 if (!Fold(cx, &expr, parser, inGenexpLambda))
1266 return false;
1267 }
1268
1269 return true;
1270 }
1271
1272 static bool
FoldTry(ExclusiveContext * cx,ParseNode * node,Parser<FullParseHandler> & parser,bool inGenexpLambda)1273 FoldTry(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
1274 bool inGenexpLambda)
1275 {
1276 MOZ_ASSERT(node->isKind(PNK_TRY));
1277 MOZ_ASSERT(node->isArity(PN_TERNARY));
1278
1279 ParseNode*& statements = node->pn_kid1;
1280 if (!Fold(cx, &statements, parser, inGenexpLambda))
1281 return false;
1282
1283 if (ParseNode*& catchList = node->pn_kid2) {
1284 if (!Fold(cx, &catchList, parser, inGenexpLambda))
1285 return false;
1286 }
1287
1288 if (ParseNode*& finally = node->pn_kid3) {
1289 if (!Fold(cx, &finally, parser, inGenexpLambda))
1290 return false;
1291 }
1292
1293 return true;
1294 }
1295
1296 static bool
FoldCatch(ExclusiveContext * cx,ParseNode * node,Parser<FullParseHandler> & parser,bool inGenexpLambda)1297 FoldCatch(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
1298 bool inGenexpLambda)
1299 {
1300 MOZ_ASSERT(node->isKind(PNK_CATCH));
1301 MOZ_ASSERT(node->isArity(PN_TERNARY));
1302
1303 ParseNode*& declPattern = node->pn_kid1;
1304 if (!Fold(cx, &declPattern, parser, inGenexpLambda))
1305 return false;
1306
1307 if (ParseNode*& cond = node->pn_kid2) {
1308 if (!FoldCondition(cx, &cond, parser, inGenexpLambda))
1309 return false;
1310 }
1311
1312 if (ParseNode*& statements = node->pn_kid3) {
1313 if (!Fold(cx, &statements, parser, inGenexpLambda))
1314 return false;
1315 }
1316
1317 return true;
1318 }
1319
1320 static bool
FoldClass(ExclusiveContext * cx,ParseNode * node,Parser<FullParseHandler> & parser,bool inGenexpLambda)1321 FoldClass(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
1322 bool inGenexpLambda)
1323 {
1324 MOZ_ASSERT(node->isKind(PNK_CLASS));
1325 MOZ_ASSERT(node->isArity(PN_TERNARY));
1326
1327 if (ParseNode*& classNames = node->pn_kid1) {
1328 if (!Fold(cx, &classNames, parser, inGenexpLambda))
1329 return false;
1330 }
1331
1332 if (ParseNode*& heritage = node->pn_kid2) {
1333 if (!Fold(cx, &heritage, parser, inGenexpLambda))
1334 return false;
1335 }
1336
1337 ParseNode*& body = node->pn_kid3;
1338 return Fold(cx, &body, parser, inGenexpLambda);
1339 }
1340
1341 static bool
FoldElement(ExclusiveContext * cx,ParseNode ** nodePtr,Parser<FullParseHandler> & parser,bool inGenexpLambda)1342 FoldElement(ExclusiveContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
1343 bool inGenexpLambda)
1344 {
1345 ParseNode* node = *nodePtr;
1346
1347 MOZ_ASSERT(node->isKind(PNK_ELEM));
1348 MOZ_ASSERT(node->isArity(PN_BINARY));
1349
1350 ParseNode*& expr = node->pn_left;
1351 if (!Fold(cx, &expr, parser, inGenexpLambda))
1352 return false;
1353
1354 ParseNode*& key = node->pn_right;
1355 if (!Fold(cx, &key, parser, inGenexpLambda))
1356 return false;
1357
1358 PropertyName* name = nullptr;
1359 if (key->isKind(PNK_STRING)) {
1360 JSAtom* atom = key->pn_atom;
1361 uint32_t index;
1362
1363 if (atom->isIndex(&index)) {
1364 // Optimization 1: We have something like expr["100"]. This is
1365 // equivalent to expr[100] which is faster.
1366 key->setKind(PNK_NUMBER);
1367 key->setOp(JSOP_DOUBLE);
1368 key->pn_dval = index;
1369 } else {
1370 name = atom->asPropertyName();
1371 }
1372 } else if (key->isKind(PNK_NUMBER)) {
1373 double number = key->pn_dval;
1374 if (number != ToUint32(number)) {
1375 // Optimization 2: We have something like expr[3.14]. The number
1376 // isn't an array index, so it converts to a string ("3.14"),
1377 // enabling optimization 3 below.
1378 JSAtom* atom = ToAtom<NoGC>(cx, DoubleValue(number));
1379 if (!atom)
1380 return false;
1381 name = atom->asPropertyName();
1382 }
1383 }
1384
1385 // If we don't have a name, we can't optimize to getprop.
1386 if (!name)
1387 return true;
1388
1389 // Also don't optimize if the name doesn't map directly to its id for TI's
1390 // purposes.
1391 if (NameToId(name) != IdToTypeId(NameToId(name)))
1392 return true;
1393
1394 // Optimization 3: We have expr["foo"] where foo is not an index. Convert
1395 // to a property access (like expr.foo) that optimizes better downstream.
1396 // Don't bother with this for names that TI considers to be indexes, to
1397 // simplify downstream analysis.
1398 ParseNode* dottedAccess = parser.handler.newPropertyAccess(expr, name, node->pn_pos.end);
1399 if (!dottedAccess)
1400 return false;
1401 dottedAccess->setInParens(node->isInParens());
1402 ReplaceNode(nodePtr, dottedAccess);
1403
1404 // If we've replaced |expr["prop"]| with |expr.prop|, we can now free the
1405 // |"prop"| and |expr["prop"]| nodes -- but not the |expr| node that we're
1406 // now using as a sub-node of |dottedAccess|. Munge |expr["prop"]| into a
1407 // node with |"prop"| as its only child, that'll pass AST sanity-checking
1408 // assertions during freeing, then free it.
1409 node->setKind(PNK_TYPEOFEXPR);
1410 node->setArity(PN_UNARY);
1411 node->pn_kid = key;
1412 parser.freeTree(node);
1413
1414 return true;
1415 }
1416
1417 static bool
FoldAdd(ExclusiveContext * cx,ParseNode ** nodePtr,Parser<FullParseHandler> & parser,bool inGenexpLambda)1418 FoldAdd(ExclusiveContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
1419 bool inGenexpLambda)
1420 {
1421 ParseNode* node = *nodePtr;
1422
1423 MOZ_ASSERT(node->isKind(PNK_ADD));
1424 MOZ_ASSERT(node->isArity(PN_LIST));
1425 MOZ_ASSERT(node->pn_count >= 2);
1426
1427 // Generically fold all operands first.
1428 if (!FoldList(cx, node, parser, inGenexpLambda))
1429 return false;
1430
1431 // Fold leading numeric operands together:
1432 //
1433 // (1 + 2 + x) becomes (3 + x)
1434 //
1435 // Don't go past the leading operands: additions after a string are
1436 // string concatenations, not additions: ("1" + 2 + 3 === "123").
1437 ParseNode* current = node->pn_head;
1438 ParseNode* next = current->pn_next;
1439 if (current->isKind(PNK_NUMBER)) {
1440 do {
1441 if (!next->isKind(PNK_NUMBER))
1442 break;
1443
1444 current->pn_dval += next->pn_dval;
1445 current->pn_next = next->pn_next;
1446 parser.freeTree(next);
1447 next = current->pn_next;
1448
1449 MOZ_ASSERT(node->pn_count > 1);
1450 node->pn_count--;
1451 } while (next);
1452 }
1453
1454 // If any operands remain, attempt string concatenation folding.
1455 do {
1456 // If no operands remain, we're done.
1457 if (!next)
1458 break;
1459
1460 // (number + string) is string concatenation *only* at the start of
1461 // the list: (x + 1 + "2" !== x + "12") when x is a number.
1462 if (current->isKind(PNK_NUMBER) && next->isKind(PNK_STRING)) {
1463 if (!FoldType(cx, current, PNK_STRING))
1464 return false;
1465 next = current->pn_next;
1466 }
1467
1468 // The first string forces all subsequent additions to be
1469 // string concatenations.
1470 do {
1471 if (current->isKind(PNK_STRING))
1472 break;
1473
1474 current = next;
1475 next = next->pn_next;
1476 } while (next);
1477
1478 // If there's nothing left to fold, we're done.
1479 if (!next)
1480 break;
1481
1482 RootedString combination(cx);
1483 RootedString tmp(cx);
1484 do {
1485 // Create a rope of the current string and all succeeding
1486 // constants that we can convert to strings, then atomize it
1487 // and replace them all with that fresh string.
1488 MOZ_ASSERT(current->isKind(PNK_STRING));
1489
1490 combination = current->pn_atom;
1491
1492 do {
1493 // Try folding the next operand to a string.
1494 if (!FoldType(cx, next, PNK_STRING))
1495 return false;
1496
1497 // Stop glomming once folding doesn't produce a string.
1498 if (!next->isKind(PNK_STRING))
1499 break;
1500
1501 // Add this string to the combination and remove the node.
1502 tmp = next->pn_atom;
1503 combination = ConcatStrings<CanGC>(cx, combination, tmp);
1504 if (!combination)
1505 return false;
1506
1507 current->pn_next = next->pn_next;
1508 parser.freeTree(next);
1509 next = current->pn_next;
1510
1511 MOZ_ASSERT(node->pn_count > 1);
1512 node->pn_count--;
1513 } while (next);
1514
1515 // Replace |current|'s string with the entire combination.
1516 MOZ_ASSERT(current->isKind(PNK_STRING));
1517 combination = AtomizeString(cx, combination);
1518 if (!combination)
1519 return false;
1520 current->pn_atom = &combination->asAtom();
1521
1522
1523 // If we're out of nodes, we're done.
1524 if (!next)
1525 break;
1526
1527 current = next;
1528 next = current->pn_next;
1529
1530 // If we're out of nodes *after* the non-foldable-to-string
1531 // node, we're done.
1532 if (!next)
1533 break;
1534
1535 // Otherwise find the next node foldable to a string, and loop.
1536 do {
1537 current = next;
1538 next = current->pn_next;
1539
1540 if (!FoldType(cx, current, PNK_STRING))
1541 return false;
1542 next = current->pn_next;
1543 } while (!current->isKind(PNK_STRING) && next);
1544 } while (next);
1545 } while (false);
1546
1547 MOZ_ASSERT(!next, "must have considered all nodes here");
1548 MOZ_ASSERT(!current->pn_next, "current node must be the last node");
1549
1550 node->pn_tail = ¤t->pn_next;
1551 node->checkListConsistency();
1552
1553 if (node->pn_count == 1) {
1554 // We reduced the list to a constant. Replace the PNK_ADD node
1555 // with that constant.
1556 ReplaceNode(nodePtr, current);
1557
1558 // Free the old node to aggressively verify nothing uses it.
1559 node->setKind(PNK_TRUE);
1560 node->setArity(PN_NULLARY);
1561 node->setOp(JSOP_TRUE);
1562 parser.freeTree(node);
1563 }
1564
1565 return true;
1566 }
1567
1568 static bool
FoldCall(ExclusiveContext * cx,ParseNode * node,Parser<FullParseHandler> & parser,bool inGenexpLambda)1569 FoldCall(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
1570 bool inGenexpLambda)
1571 {
1572 MOZ_ASSERT(node->isKind(PNK_CALL) || node->isKind(PNK_SUPERCALL) ||
1573 node->isKind(PNK_TAGGED_TEMPLATE));
1574 MOZ_ASSERT(node->isArity(PN_LIST));
1575
1576 // Don't fold a parenthesized callable component in an invocation, as this
1577 // might cause a different |this| value to be used, changing semantics:
1578 //
1579 // var prop = "global";
1580 // var obj = { prop: "obj", f: function() { return this.prop; } };
1581 // assertEq((true ? obj.f : null)(), "global");
1582 // assertEq(obj.f(), "obj");
1583 // assertEq((true ? obj.f : null)``, "global");
1584 // assertEq(obj.f``, "obj");
1585 //
1586 // See bug 537673 and bug 1182373.
1587 ParseNode** listp = &node->pn_head;
1588 if ((*listp)->isInParens())
1589 listp = &(*listp)->pn_next;
1590
1591 for (; *listp; listp = &(*listp)->pn_next) {
1592 if (!Fold(cx, listp, parser, inGenexpLambda))
1593 return false;
1594 }
1595
1596 // If the last node in the list was replaced, pn_tail points into the wrong node.
1597 node->pn_tail = listp;
1598
1599 node->checkListConsistency();
1600 return true;
1601 }
1602
1603 static bool
FoldForInOrOf(ExclusiveContext * cx,ParseNode * node,Parser<FullParseHandler> & parser,bool inGenexpLambda)1604 FoldForInOrOf(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
1605 bool inGenexpLambda)
1606 {
1607 MOZ_ASSERT(node->isKind(PNK_FORIN) || node->isKind(PNK_FOROF));
1608 MOZ_ASSERT(node->isArity(PN_TERNARY));
1609
1610 if (ParseNode*& decl = node->pn_kid1) {
1611 if (!Fold(cx, &decl, parser, inGenexpLambda))
1612 return false;
1613 }
1614
1615 return Fold(cx, &node->pn_kid2, parser, inGenexpLambda) &&
1616 Fold(cx, &node->pn_kid3, parser, inGenexpLambda);
1617 }
1618
1619 static bool
FoldForHead(ExclusiveContext * cx,ParseNode * node,Parser<FullParseHandler> & parser,bool inGenexpLambda)1620 FoldForHead(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
1621 bool inGenexpLambda)
1622 {
1623 MOZ_ASSERT(node->isKind(PNK_FORHEAD));
1624 MOZ_ASSERT(node->isArity(PN_TERNARY));
1625
1626 if (ParseNode*& init = node->pn_kid1) {
1627 if (!Fold(cx, &init, parser, inGenexpLambda))
1628 return false;
1629 }
1630
1631 if (ParseNode*& test = node->pn_kid2) {
1632 if (!FoldCondition(cx, &test, parser, inGenexpLambda))
1633 return false;
1634
1635 if (test->isKind(PNK_TRUE)) {
1636 parser.freeTree(test);
1637 test = nullptr;
1638 }
1639 }
1640
1641 if (ParseNode*& update = node->pn_kid3) {
1642 if (!Fold(cx, &update, parser, inGenexpLambda))
1643 return false;
1644 }
1645
1646 return true;
1647 }
1648
1649 static bool
FoldDottedProperty(ExclusiveContext * cx,ParseNode * node,Parser<FullParseHandler> & parser,bool inGenexpLambda)1650 FoldDottedProperty(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
1651 bool inGenexpLambda)
1652 {
1653 MOZ_ASSERT(node->isKind(PNK_DOT));
1654 MOZ_ASSERT(node->isArity(PN_NAME));
1655
1656 // Iterate through a long chain of dotted property accesses to find the
1657 // most-nested non-dotted property node, then fold that.
1658 ParseNode** nested = &node->pn_expr;
1659 while ((*nested)->isKind(PNK_DOT)) {
1660 MOZ_ASSERT((*nested)->isArity(PN_NAME));
1661 nested = &(*nested)->pn_expr;
1662 }
1663
1664 return Fold(cx, nested, parser, inGenexpLambda);
1665 }
1666
1667 static bool
FoldName(ExclusiveContext * cx,ParseNode * node,Parser<FullParseHandler> & parser,bool inGenexpLambda)1668 FoldName(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
1669 bool inGenexpLambda)
1670 {
1671 MOZ_ASSERT(node->isKind(PNK_NAME));
1672 MOZ_ASSERT(node->isArity(PN_NAME));
1673
1674 // Name nodes that are used, are in use-definition lists. Such nodes store
1675 // name analysis information and contain nothing foldable.
1676 if (node->isUsed())
1677 return true;
1678
1679 // Other names might have a foldable expression in pn_expr.
1680 if (!node->pn_expr)
1681 return true;
1682
1683 return Fold(cx, &node->pn_expr, parser, inGenexpLambda);
1684 }
1685
1686 bool
Fold(ExclusiveContext * cx,ParseNode ** pnp,Parser<FullParseHandler> & parser,bool inGenexpLambda)1687 Fold(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bool inGenexpLambda)
1688 {
1689 JS_CHECK_RECURSION(cx, return false);
1690
1691 ParseNode* pn = *pnp;
1692
1693 switch (pn->getKind()) {
1694 case PNK_NOP:
1695 case PNK_REGEXP:
1696 case PNK_STRING:
1697 case PNK_TRUE:
1698 case PNK_FALSE:
1699 case PNK_NULL:
1700 case PNK_ELISION:
1701 case PNK_NUMBER:
1702 case PNK_DEBUGGER:
1703 case PNK_BREAK:
1704 case PNK_CONTINUE:
1705 case PNK_TEMPLATE_STRING:
1706 case PNK_GENERATOR:
1707 case PNK_EXPORT_BATCH_SPEC:
1708 case PNK_OBJECT_PROPERTY_NAME:
1709 case PNK_POSHOLDER:
1710 MOZ_ASSERT(pn->isArity(PN_NULLARY));
1711 return true;
1712
1713 case PNK_SUPERBASE:
1714 case PNK_TYPEOFNAME:
1715 MOZ_ASSERT(pn->isArity(PN_UNARY));
1716 MOZ_ASSERT(pn->pn_kid->isKind(PNK_NAME));
1717 MOZ_ASSERT(!pn->pn_kid->maybeExpr());
1718 return true;
1719
1720 case PNK_TYPEOFEXPR:
1721 return FoldTypeOfExpr(cx, pn, parser, inGenexpLambda);
1722
1723 case PNK_DELETENAME: {
1724 MOZ_ASSERT(pn->isArity(PN_UNARY));
1725 MOZ_ASSERT(pn->pn_kid->isKind(PNK_NAME));
1726 return true;
1727 }
1728
1729 case PNK_DELETEEXPR:
1730 return FoldDeleteExpr(cx, pn, parser, inGenexpLambda);
1731
1732 case PNK_DELETEELEM:
1733 return FoldDeleteElement(cx, pn, parser, inGenexpLambda);
1734
1735 case PNK_DELETEPROP:
1736 return FoldDeleteProperty(cx, pn, parser, inGenexpLambda);
1737
1738 case PNK_CONDITIONAL:
1739 return FoldConditional(cx, pnp, parser, inGenexpLambda);
1740
1741 case PNK_IF:
1742 return FoldIf(cx, pnp, parser, inGenexpLambda);
1743
1744 case PNK_NOT:
1745 return FoldNot(cx, pn, parser, inGenexpLambda);
1746
1747 case PNK_BITNOT:
1748 case PNK_POS:
1749 case PNK_NEG:
1750 return FoldUnaryArithmetic(cx, pn, parser, inGenexpLambda);
1751
1752 case PNK_PREINCREMENT:
1753 case PNK_POSTINCREMENT:
1754 case PNK_PREDECREMENT:
1755 case PNK_POSTDECREMENT:
1756 return FoldIncrementDecrement(cx, pn, parser, inGenexpLambda);
1757
1758 case PNK_THROW:
1759 case PNK_ARRAYPUSH:
1760 case PNK_MUTATEPROTO:
1761 case PNK_COMPUTED_NAME:
1762 case PNK_SPREAD:
1763 case PNK_EXPORT:
1764 case PNK_VOID:
1765 MOZ_ASSERT(pn->isArity(PN_UNARY));
1766 return Fold(cx, &pn->pn_kid, parser, inGenexpLambda);
1767
1768 case PNK_EXPORT_DEFAULT:
1769 MOZ_ASSERT(pn->isArity(PN_BINARY));
1770 return Fold(cx, &pn->pn_left, parser, inGenexpLambda);
1771
1772 case PNK_SEMI:
1773 case PNK_THIS:
1774 MOZ_ASSERT(pn->isArity(PN_UNARY));
1775 if (ParseNode*& expr = pn->pn_kid)
1776 return Fold(cx, &expr, parser, inGenexpLambda);
1777 return true;
1778
1779 case PNK_AND:
1780 case PNK_OR:
1781 return FoldAndOr(cx, pnp, parser, inGenexpLambda);
1782
1783 case PNK_FUNCTION:
1784 return FoldFunction(cx, pn, parser, inGenexpLambda);
1785
1786 case PNK_MODULE:
1787 return FoldModule(cx, pn, parser);
1788
1789 case PNK_SUB:
1790 case PNK_STAR:
1791 case PNK_LSH:
1792 case PNK_RSH:
1793 case PNK_URSH:
1794 case PNK_DIV:
1795 case PNK_MOD:
1796 return FoldBinaryArithmetic(cx, pn, parser, inGenexpLambda);
1797
1798 case PNK_POW:
1799 return FoldExponentiation(cx, pn, parser, inGenexpLambda);
1800
1801 // Various list nodes not requiring care to minimally fold. Some of
1802 // these could be further folded/optimized, but we don't make the effort.
1803 case PNK_BITOR:
1804 case PNK_BITXOR:
1805 case PNK_BITAND:
1806 case PNK_STRICTEQ:
1807 case PNK_EQ:
1808 case PNK_STRICTNE:
1809 case PNK_NE:
1810 case PNK_LT:
1811 case PNK_LE:
1812 case PNK_GT:
1813 case PNK_GE:
1814 case PNK_INSTANCEOF:
1815 case PNK_IN:
1816 case PNK_COMMA:
1817 case PNK_NEW:
1818 case PNK_ARRAY:
1819 case PNK_OBJECT:
1820 case PNK_ARRAYCOMP:
1821 case PNK_STATEMENTLIST:
1822 case PNK_CLASSMETHODLIST:
1823 case PNK_CATCHLIST:
1824 case PNK_TEMPLATE_STRING_LIST:
1825 case PNK_VAR:
1826 case PNK_CONST:
1827 case PNK_LET:
1828 case PNK_ARGSBODY:
1829 case PNK_CALLSITEOBJ:
1830 case PNK_EXPORT_SPEC_LIST:
1831 case PNK_IMPORT_SPEC_LIST:
1832 case PNK_GENEXP:
1833 return FoldList(cx, pn, parser, inGenexpLambda);
1834
1835 case PNK_YIELD_STAR:
1836 MOZ_ASSERT(pn->isArity(PN_BINARY));
1837 MOZ_ASSERT(pn->pn_right->isKind(PNK_NAME));
1838 MOZ_ASSERT(!pn->pn_right->isAssigned());
1839 return Fold(cx, &pn->pn_left, parser, inGenexpLambda);
1840
1841 case PNK_YIELD:
1842 MOZ_ASSERT(pn->isArity(PN_BINARY));
1843 MOZ_ASSERT((pn->pn_right->isKind(PNK_NAME) && !pn->pn_right->isAssigned()) ||
1844 (pn->pn_right->isKind(PNK_ASSIGN) &&
1845 pn->pn_right->pn_left->isKind(PNK_NAME) &&
1846 pn->pn_right->pn_right->isKind(PNK_GENERATOR)));
1847 if (!pn->pn_left)
1848 return true;
1849 return Fold(cx, &pn->pn_left, parser, inGenexpLambda);
1850
1851 case PNK_RETURN:
1852 return FoldReturn(cx, pn, parser, inGenexpLambda);
1853
1854 case PNK_TRY:
1855 return FoldTry(cx, pn, parser, inGenexpLambda);
1856
1857 case PNK_CATCH:
1858 return FoldCatch(cx, pn, parser, inGenexpLambda);
1859
1860 case PNK_CLASS:
1861 return FoldClass(cx, pn, parser, inGenexpLambda);
1862
1863 case PNK_ELEM:
1864 return FoldElement(cx, pnp, parser, inGenexpLambda);
1865
1866 case PNK_ADD:
1867 return FoldAdd(cx, pnp, parser, inGenexpLambda);
1868
1869 case PNK_CALL:
1870 case PNK_SUPERCALL:
1871 case PNK_TAGGED_TEMPLATE:
1872 return FoldCall(cx, pn, parser, inGenexpLambda);
1873
1874 case PNK_SWITCH:
1875 case PNK_COLON:
1876 case PNK_ASSIGN:
1877 case PNK_ADDASSIGN:
1878 case PNK_SUBASSIGN:
1879 case PNK_BITORASSIGN:
1880 case PNK_BITANDASSIGN:
1881 case PNK_BITXORASSIGN:
1882 case PNK_LSHASSIGN:
1883 case PNK_RSHASSIGN:
1884 case PNK_URSHASSIGN:
1885 case PNK_DIVASSIGN:
1886 case PNK_MODASSIGN:
1887 case PNK_MULASSIGN:
1888 case PNK_POWASSIGN:
1889 case PNK_IMPORT:
1890 case PNK_EXPORT_FROM:
1891 case PNK_SHORTHAND:
1892 case PNK_LETBLOCK:
1893 case PNK_FOR:
1894 case PNK_COMPREHENSIONFOR:
1895 case PNK_CLASSMETHOD:
1896 case PNK_IMPORT_SPEC:
1897 case PNK_EXPORT_SPEC:
1898 case PNK_SETTHIS:
1899 MOZ_ASSERT(pn->isArity(PN_BINARY));
1900 return Fold(cx, &pn->pn_left, parser, inGenexpLambda) &&
1901 Fold(cx, &pn->pn_right, parser, inGenexpLambda);
1902
1903 case PNK_NEWTARGET:
1904 MOZ_ASSERT(pn->isArity(PN_BINARY));
1905 MOZ_ASSERT(pn->pn_left->isKind(PNK_POSHOLDER));
1906 MOZ_ASSERT(pn->pn_right->isKind(PNK_POSHOLDER));
1907 return true;
1908
1909 case PNK_CLASSNAMES:
1910 MOZ_ASSERT(pn->isArity(PN_BINARY));
1911 if (ParseNode*& outerBinding = pn->pn_left) {
1912 if (!Fold(cx, &outerBinding, parser, inGenexpLambda))
1913 return false;
1914 }
1915 return Fold(cx, &pn->pn_right, parser, inGenexpLambda);
1916
1917 case PNK_DOWHILE:
1918 MOZ_ASSERT(pn->isArity(PN_BINARY));
1919 return Fold(cx, &pn->pn_left, parser, inGenexpLambda) &&
1920 FoldCondition(cx, &pn->pn_right, parser, inGenexpLambda);
1921
1922 case PNK_WHILE:
1923 MOZ_ASSERT(pn->isArity(PN_BINARY));
1924 return FoldCondition(cx, &pn->pn_left, parser, inGenexpLambda) &&
1925 Fold(cx, &pn->pn_right, parser, inGenexpLambda);
1926
1927 case PNK_CASE: {
1928 MOZ_ASSERT(pn->isArity(PN_BINARY));
1929
1930 // pn_left is null for DefaultClauses.
1931 if (pn->pn_left) {
1932 if (!Fold(cx, &pn->pn_left, parser, inGenexpLambda))
1933 return false;
1934 }
1935 return Fold(cx, &pn->pn_right, parser, inGenexpLambda);
1936 }
1937
1938 case PNK_WITH:
1939 MOZ_ASSERT(pn->isArity(PN_BINARY_OBJ));
1940 return Fold(cx, &pn->pn_left, parser, inGenexpLambda) &&
1941 Fold(cx, &pn->pn_right, parser, inGenexpLambda);
1942
1943 case PNK_FORIN:
1944 case PNK_FOROF:
1945 return FoldForInOrOf(cx, pn, parser, inGenexpLambda);
1946
1947 case PNK_FORHEAD:
1948 return FoldForHead(cx, pn, parser, inGenexpLambda);
1949
1950 case PNK_LABEL:
1951 MOZ_ASSERT(pn->isArity(PN_NAME));
1952 return Fold(cx, &pn->pn_expr, parser, inGenexpLambda);
1953
1954 case PNK_DOT:
1955 return FoldDottedProperty(cx, pn, parser, inGenexpLambda);
1956
1957 case PNK_LEXICALSCOPE:
1958 MOZ_ASSERT(pn->isArity(PN_NAME));
1959 if (!pn->pn_expr)
1960 return true;
1961 return Fold(cx, &pn->pn_expr, parser, inGenexpLambda);
1962
1963 case PNK_NAME:
1964 return FoldName(cx, pn, parser, inGenexpLambda);
1965
1966 case PNK_LIMIT: // invalid sentinel value
1967 MOZ_CRASH("invalid node kind");
1968 }
1969
1970 MOZ_CRASH("shouldn't reach here");
1971 return false;
1972 }
1973
1974 bool
FoldConstants(ExclusiveContext * cx,ParseNode ** pnp,Parser<FullParseHandler> * parser)1975 frontend::FoldConstants(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>* parser)
1976 {
1977 // Don't constant-fold inside "use asm" code, as this could create a parse
1978 // tree that doesn't type-check as asm.js.
1979 if (parser->pc->useAsmOrInsideUseAsm())
1980 return true;
1981
1982 return Fold(cx, pnp, *parser, false);
1983 }
1984