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