Bumping manifests a=b2g-bump
[gecko.git] / js / src / frontend / FoldConstants.cpp
blobe0cf208598b3314005fc26273869393e8e21d830
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "frontend/FoldConstants.h"
9 #include "mozilla/FloatingPoint.h"
10 #include "mozilla/TypedEnum.h"
12 #include "jslibmath.h"
14 #include "frontend/ParseNode.h"
15 #include "frontend/Parser.h"
16 #include "js/Conversions.h"
18 #include "jscntxtinlines.h"
19 #include "jsinferinlines.h"
20 #include "jsobjinlines.h"
22 using namespace js;
23 using namespace js::frontend;
25 using mozilla::IsNaN;
26 using mozilla::IsNegative;
27 using mozilla::NegativeInfinity;
28 using mozilla::PositiveInfinity;
29 using JS::GenericNaN;
30 using JS::ToInt32;
31 using JS::ToUint32;
33 static bool
34 ContainsVarOrConst(ExclusiveContext* cx, ParseNode* pn, ParseNode** resultp)
36 JS_CHECK_RECURSION(cx, return false);
38 if (!pn) {
39 *resultp = nullptr;
40 return true;
42 if (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST)) {
43 *resultp = pn;
44 return true;
46 switch (pn->getArity()) {
47 case PN_LIST:
48 for (ParseNode* pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
49 if (!ContainsVarOrConst(cx, pn2, resultp))
50 return false;
51 if (*resultp)
52 return true;
54 break;
56 case PN_TERNARY:
57 if (!ContainsVarOrConst(cx, pn->pn_kid1, resultp))
58 return false;
59 if (*resultp)
60 return true;
61 if (!ContainsVarOrConst(cx, pn->pn_kid2, resultp))
62 return false;
63 if (*resultp)
64 return true;
65 return ContainsVarOrConst(cx, pn->pn_kid3, resultp);
67 case PN_BINARY:
68 case PN_BINARY_OBJ:
69 // Limit recursion if pn is a binary expression, which can't contain a
70 // var statement.
71 if (!pn->isOp(JSOP_NOP)) {
72 *resultp = nullptr;
73 return true;
75 if (!ContainsVarOrConst(cx, pn->pn_left, resultp))
76 return false;
77 if (*resultp)
78 return true;
79 return ContainsVarOrConst(cx, pn->pn_right, resultp);
81 case PN_UNARY:
82 if (!pn->isOp(JSOP_NOP)) {
83 *resultp = nullptr;
84 return true;
86 return ContainsVarOrConst(cx, pn->pn_kid, resultp);
88 case PN_NAME:
89 return ContainsVarOrConst(cx, pn->maybeExpr(), resultp);
91 default:;
93 *resultp = nullptr;
94 return true;
98 * Fold from one constant type to another.
99 * XXX handles only strings and numbers for now
101 static bool
102 FoldType(ExclusiveContext* cx, ParseNode* pn, ParseNodeKind kind)
104 if (!pn->isKind(kind)) {
105 switch (kind) {
106 case PNK_NUMBER:
107 if (pn->isKind(PNK_STRING)) {
108 double d;
109 if (!StringToNumber(cx, pn->pn_atom, &d))
110 return false;
111 pn->pn_dval = d;
112 pn->setKind(PNK_NUMBER);
113 pn->setOp(JSOP_DOUBLE);
115 break;
117 case PNK_STRING:
118 if (pn->isKind(PNK_NUMBER)) {
119 pn->pn_atom = NumberToAtom(cx, pn->pn_dval);
120 if (!pn->pn_atom)
121 return false;
122 pn->setKind(PNK_STRING);
123 pn->setOp(JSOP_STRING);
125 break;
127 default:;
130 return true;
134 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
135 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
136 * a successful call to this function.
138 static bool
139 FoldBinaryNumeric(ExclusiveContext* cx, JSOp op, ParseNode* pn1, ParseNode* pn2,
140 ParseNode* pn)
142 double d, d2;
143 int32_t i, j;
145 MOZ_ASSERT(pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER));
146 d = pn1->pn_dval;
147 d2 = pn2->pn_dval;
148 switch (op) {
149 case JSOP_LSH:
150 case JSOP_RSH:
151 i = ToInt32(d);
152 j = ToInt32(d2);
153 j &= 31;
154 d = int32_t((op == JSOP_LSH) ? uint32_t(i) << j : i >> j);
155 break;
157 case JSOP_URSH:
158 j = ToInt32(d2);
159 j &= 31;
160 d = ToUint32(d) >> j;
161 break;
163 case JSOP_ADD:
164 d += d2;
165 break;
167 case JSOP_SUB:
168 d -= d2;
169 break;
171 case JSOP_MUL:
172 d *= d2;
173 break;
175 case JSOP_DIV:
176 if (d2 == 0) {
177 #if defined(XP_WIN)
178 /* XXX MSVC miscompiles such that (NaN == 0) */
179 if (IsNaN(d2))
180 d = GenericNaN();
181 else
182 #endif
183 if (d == 0 || IsNaN(d))
184 d = GenericNaN();
185 else if (IsNegative(d) != IsNegative(d2))
186 d = NegativeInfinity<double>();
187 else
188 d = PositiveInfinity<double>();
189 } else {
190 d /= d2;
192 break;
194 case JSOP_MOD:
195 if (d2 == 0) {
196 d = GenericNaN();
197 } else {
198 d = js_fmod(d, d2);
200 break;
202 default:;
205 /* Take care to allow pn1 or pn2 to alias pn. */
206 pn->setKind(PNK_NUMBER);
207 pn->setOp(JSOP_DOUBLE);
208 pn->setArity(PN_NULLARY);
209 pn->pn_dval = d;
210 return true;
213 // Remove a ParseNode, **pnp, from a parse tree, putting another ParseNode,
214 // *pn, in its place.
216 // pnp points to a ParseNode pointer. This must be the only pointer that points
217 // to the parse node being replaced. The replacement, *pn, is unchanged except
218 // for its pn_next pointer; updating that is necessary if *pn's new parent is a
219 // list node.
220 static void
221 ReplaceNode(ParseNode** pnp, ParseNode* pn)
223 pn->pn_next = (*pnp)->pn_next;
224 *pnp = pn;
227 enum Truthiness { Truthy, Falsy, Unknown };
229 static Truthiness
230 Boolish(ParseNode* pn)
232 switch (pn->getKind()) {
233 case PNK_NUMBER:
234 return (pn->pn_dval != 0 && !IsNaN(pn->pn_dval)) ? Truthy : Falsy;
236 case PNK_STRING:
237 return (pn->pn_atom->length() > 0) ? Truthy : Falsy;
239 case PNK_TRUE:
240 case PNK_FUNCTION:
241 case PNK_GENEXP:
242 return Truthy;
244 case PNK_FALSE:
245 case PNK_NULL:
246 return Falsy;
248 default:
249 return Unknown;
253 // Expressions that appear in a few specific places are treated specially
254 // during constant folding. This enum tells where a parse node appears.
255 MOZ_BEGIN_ENUM_CLASS(SyntacticContext, int)
256 // pn is an expression, and it appears in a context where only its side
257 // effects and truthiness matter: the condition of an if statement,
258 // conditional expression, while loop, or for(;;) loop; or an operand of &&
259 // or || in such a context.
260 Condition,
262 // pn is the operand of the 'delete' keyword.
263 Delete,
265 // Any other syntactic context.
266 Other
267 MOZ_END_ENUM_CLASS(SyntacticContext)
269 static SyntacticContext
270 condIf(const ParseNode* pn, ParseNodeKind kind)
272 return pn->isKind(kind) ? SyntacticContext::Condition : SyntacticContext::Other;
275 static bool
276 Fold(ExclusiveContext* cx, ParseNode** pnp,
277 FullParseHandler& handler, const ReadOnlyCompileOptions& options,
278 bool inGenexpLambda, SyntacticContext sc)
280 ParseNode* pn = *pnp;
281 ParseNode* pn1 = nullptr, *pn2 = nullptr, *pn3 = nullptr;
283 JS_CHECK_RECURSION(cx, return false);
285 // First, recursively fold constants on the children of this node.
286 switch (pn->getArity()) {
287 case PN_CODE:
288 if (pn->isKind(PNK_FUNCTION) && pn->pn_funbox->useAsmOrInsideUseAsm())
289 return true;
291 // Note: pn_body is nullptr for functions which are being lazily parsed.
292 MOZ_ASSERT(pn->getKind() == PNK_FUNCTION);
293 if (pn->pn_body) {
294 if (!Fold(cx, &pn->pn_body, handler, options, pn->pn_funbox->inGenexpLambda,
295 SyntacticContext::Other))
296 return false;
298 break;
300 case PN_LIST:
302 // Propagate Condition context through logical connectives.
303 SyntacticContext kidsc = SyntacticContext::Other;
304 if (pn->isKind(PNK_OR) || pn->isKind(PNK_AND))
305 kidsc = sc;
307 // Don't fold a parenthesized call expression. See bug 537673.
308 ParseNode** listp = &pn->pn_head;
309 if ((pn->isKind(PNK_CALL) || pn->isKind(PNK_NEW)) && (*listp)->isInParens())
310 listp = &(*listp)->pn_next;
312 for (; *listp; listp = &(*listp)->pn_next) {
313 if (!Fold(cx, listp, handler, options, inGenexpLambda, kidsc))
314 return false;
317 // If the last node in the list was replaced, pn_tail points into the wrong node.
318 pn->pn_tail = listp;
320 // Save the list head in pn1 for later use.
321 pn1 = pn->pn_head;
322 pn2 = nullptr;
323 break;
326 case PN_TERNARY:
327 /* Any kid may be null (e.g. for (;;)). */
328 if (pn->pn_kid1) {
329 if (!Fold(cx, &pn->pn_kid1, handler, options, inGenexpLambda, condIf(pn, PNK_IF)))
330 return false;
332 pn1 = pn->pn_kid1;
334 if (pn->pn_kid2) {
335 if (!Fold(cx, &pn->pn_kid2, handler, options, inGenexpLambda, condIf(pn, PNK_FORHEAD)))
336 return false;
337 if (pn->isKind(PNK_FORHEAD) && pn->pn_kid2->isKind(PNK_TRUE)) {
338 handler.freeTree(pn->pn_kid2);
339 pn->pn_kid2 = nullptr;
342 pn2 = pn->pn_kid2;
344 if (pn->pn_kid3) {
345 if (!Fold(cx, &pn->pn_kid3, handler, options, inGenexpLambda, SyntacticContext::Other))
346 return false;
348 pn3 = pn->pn_kid3;
349 break;
351 case PN_BINARY:
352 case PN_BINARY_OBJ:
353 if (pn->isKind(PNK_OR) || pn->isKind(PNK_AND)) {
354 // Propagate Condition context through logical connectives.
355 SyntacticContext kidsc = SyntacticContext::Other;
356 if (sc == SyntacticContext::Condition)
357 kidsc = sc;
358 if (!Fold(cx, &pn->pn_left, handler, options, inGenexpLambda, kidsc))
359 return false;
360 if (!Fold(cx, &pn->pn_right, handler, options, inGenexpLambda, kidsc))
361 return false;
362 } else {
363 /* First kid may be null (for default case in switch). */
364 if (pn->pn_left) {
365 if (!Fold(cx, &pn->pn_left, handler, options, inGenexpLambda, condIf(pn, PNK_WHILE)))
366 return false;
368 /* Second kid may be null (for return in non-generator). */
369 if (pn->pn_right) {
370 if (!Fold(cx, &pn->pn_right, handler, options, inGenexpLambda, condIf(pn, PNK_DOWHILE)))
371 return false;
374 pn1 = pn->pn_left;
375 pn2 = pn->pn_right;
376 break;
378 case PN_UNARY:
380 * Kludge to deal with typeof expressions: because constant folding
381 * can turn an expression into a name node, we have to check here,
382 * before folding, to see if we should throw undefined name errors.
384 * NB: We know that if pn->pn_op is JSOP_TYPEOF, pn1 will not be
385 * null. This assumption does not hold true for other unary
386 * expressions.
388 if (pn->isKind(PNK_TYPEOF) && !pn->pn_kid->isKind(PNK_NAME))
389 pn->setOp(JSOP_TYPEOFEXPR);
391 if (pn->pn_kid) {
392 SyntacticContext kidsc =
393 pn->isKind(PNK_NOT)
394 ? SyntacticContext::Condition
395 : pn->isKind(PNK_DELETE)
396 ? SyntacticContext::Delete
397 : SyntacticContext::Other;
398 if (!Fold(cx, &pn->pn_kid, handler, options, inGenexpLambda, kidsc))
399 return false;
401 pn1 = pn->pn_kid;
402 break;
404 case PN_NAME:
406 * Skip pn1 down along a chain of dotted member expressions to avoid
407 * excessive recursion. Our only goal here is to fold constants (if
408 * any) in the primary expression operand to the left of the first
409 * dot in the chain.
411 if (!pn->isUsed()) {
412 ParseNode** lhsp = &pn->pn_expr;
413 while (*lhsp && (*lhsp)->isArity(PN_NAME) && !(*lhsp)->isUsed())
414 lhsp = &(*lhsp)->pn_expr;
415 if (*lhsp && !Fold(cx, lhsp, handler, options, inGenexpLambda, SyntacticContext::Other))
416 return false;
417 pn1 = *lhsp;
419 break;
421 case PN_NULLARY:
422 break;
425 // The immediate child of a PNK_DELETE node should not be replaced
426 // with node indicating a different syntactic form; |delete x| is not
427 // the same as |delete (true && x)|. See bug 888002.
429 // pn is the immediate child in question. Its descendents were already
430 // constant-folded above, so we're done.
431 if (sc == SyntacticContext::Delete)
432 return true;
434 switch (pn->getKind()) {
435 case PNK_IF:
437 ParseNode* decl;
438 if (!ContainsVarOrConst(cx, pn2, &decl))
439 return false;
440 if (decl)
441 break;
442 if (!ContainsVarOrConst(cx, pn3, &decl))
443 return false;
444 if (decl)
445 break;
447 /* FALL THROUGH */
449 case PNK_CONDITIONAL:
450 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
451 switch (pn1->getKind()) {
452 case PNK_NUMBER:
453 if (pn1->pn_dval == 0 || IsNaN(pn1->pn_dval))
454 pn2 = pn3;
455 break;
456 case PNK_STRING:
457 if (pn1->pn_atom->length() == 0)
458 pn2 = pn3;
459 break;
460 case PNK_TRUE:
461 break;
462 case PNK_FALSE:
463 case PNK_NULL:
464 pn2 = pn3;
465 break;
466 default:
467 /* Early return to dodge common code that copies pn2 to pn. */
468 return true;
471 #if JS_HAS_GENERATOR_EXPRS
472 /* Don't fold a trailing |if (0)| in a generator expression. */
473 if (!pn2 && inGenexpLambda)
474 break;
475 #endif
477 if (pn2 && !pn2->isDefn()) {
478 ReplaceNode(pnp, pn2);
479 pn = pn2;
481 if (!pn2 || (pn->isKind(PNK_SEMI) && !pn->pn_kid)) {
483 * False condition and no else, or an empty then-statement was
484 * moved up over pn. Either way, make pn an empty block (not an
485 * empty statement, which does not decompile, even when labeled).
486 * NB: pn must be a PNK_IF as PNK_CONDITIONAL can never have a null
487 * kid or an empty statement for a child.
489 pn->setKind(PNK_STATEMENTLIST);
490 pn->setArity(PN_LIST);
491 pn->makeEmpty();
493 if (pn3 && pn3 != pn2)
494 handler.freeTree(pn3);
495 break;
497 case PNK_OR:
498 case PNK_AND:
499 if (sc == SyntacticContext::Condition) {
500 if (pn->isArity(PN_LIST)) {
501 ParseNode** listp = &pn->pn_head;
502 MOZ_ASSERT(*listp == pn1);
503 uint32_t orig = pn->pn_count;
504 do {
505 Truthiness t = Boolish(pn1);
506 if (t == Unknown) {
507 listp = &pn1->pn_next;
508 continue;
510 if ((t == Truthy) == pn->isKind(PNK_OR)) {
511 for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
512 pn3 = pn2->pn_next;
513 handler.freeTree(pn2);
514 --pn->pn_count;
516 pn1->pn_next = nullptr;
517 break;
519 MOZ_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
520 if (pn->pn_count == 1)
521 break;
522 *listp = pn1->pn_next;
523 handler.freeTree(pn1);
524 --pn->pn_count;
525 } while ((pn1 = *listp) != nullptr);
527 // We may have to change arity from LIST to BINARY.
528 pn1 = pn->pn_head;
529 if (pn->pn_count == 2) {
530 pn2 = pn1->pn_next;
531 pn1->pn_next = nullptr;
532 MOZ_ASSERT(!pn2->pn_next);
533 pn->setArity(PN_BINARY);
534 pn->pn_left = pn1;
535 pn->pn_right = pn2;
536 } else if (pn->pn_count == 1) {
537 ReplaceNode(pnp, pn1);
538 pn = pn1;
539 } else if (orig != pn->pn_count) {
540 // Adjust list tail.
541 pn2 = pn1->pn_next;
542 for (; pn1; pn2 = pn1, pn1 = pn1->pn_next)
544 pn->pn_tail = &pn2->pn_next;
546 } else {
547 Truthiness t = Boolish(pn1);
548 if (t != Unknown) {
549 if ((t == Truthy) == pn->isKind(PNK_OR)) {
550 handler.freeTree(pn2);
551 ReplaceNode(pnp, pn1);
552 pn = pn1;
553 } else {
554 MOZ_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
555 handler.freeTree(pn1);
556 ReplaceNode(pnp, pn2);
557 pn = pn2;
562 break;
564 case PNK_SUBASSIGN:
565 case PNK_BITORASSIGN:
566 case PNK_BITXORASSIGN:
567 case PNK_BITANDASSIGN:
568 case PNK_LSHASSIGN:
569 case PNK_RSHASSIGN:
570 case PNK_URSHASSIGN:
571 case PNK_MULASSIGN:
572 case PNK_DIVASSIGN:
573 case PNK_MODASSIGN:
575 * Compound operators such as *= should be subject to folding, in case
576 * the left-hand side is constant, and so that the decompiler produces
577 * the same string that you get from decompiling a script or function
578 * compiled from that same string. += is special and so must be
579 * handled below.
581 goto do_binary_op;
583 case PNK_ADDASSIGN:
584 MOZ_ASSERT(pn->isOp(JSOP_ADD));
585 /* FALL THROUGH */
586 case PNK_ADD:
587 if (pn->isArity(PN_LIST)) {
588 bool folded = false;
590 pn2 = pn1->pn_next;
591 if (pn1->isKind(PNK_NUMBER)) {
592 // Fold addition of numeric literals: (1 + 2 + x === 3 + x).
593 // Note that we can only do this the front of the list:
594 // (x + 1 + 2 !== x + 3) when x is a string.
595 while (pn2 && pn2->isKind(PNK_NUMBER)) {
596 pn1->pn_dval += pn2->pn_dval;
597 pn1->pn_next = pn2->pn_next;
598 handler.freeTree(pn2);
599 pn2 = pn1->pn_next;
600 pn->pn_count--;
601 folded = true;
605 // Now search for adjacent pairs of literals to fold for string
606 // concatenation.
608 // isStringConcat is true if we know the operation we're looking at
609 // will be string concatenation at runtime. As soon as we see a
610 // string, we know that every addition to the right of it will be
611 // string concatenation, even if both operands are numbers:
612 // ("s" + x + 1 + 2 === "s" + x + "12").
614 bool isStringConcat = false;
615 RootedString foldedStr(cx);
617 // (number + string) is definitely concatenation, but only at the
618 // front of the list: (x + 1 + "2" !== x + "12") when x is a
619 // number.
620 if (pn1->isKind(PNK_NUMBER) && pn2 && pn2->isKind(PNK_STRING))
621 isStringConcat = true;
623 while (pn2) {
624 isStringConcat = isStringConcat || pn1->isKind(PNK_STRING);
626 if (isStringConcat &&
627 (pn1->isKind(PNK_STRING) || pn1->isKind(PNK_NUMBER)) &&
628 (pn2->isKind(PNK_STRING) || pn2->isKind(PNK_NUMBER)))
630 // Fold string concatenation of literals.
631 if (pn1->isKind(PNK_NUMBER) && !FoldType(cx, pn1, PNK_STRING))
632 return false;
633 if (pn2->isKind(PNK_NUMBER) && !FoldType(cx, pn2, PNK_STRING))
634 return false;
635 if (!foldedStr)
636 foldedStr = pn1->pn_atom;
637 RootedString right(cx, pn2->pn_atom);
638 foldedStr = ConcatStrings<CanGC>(cx, foldedStr, right);
639 if (!foldedStr)
640 return false;
641 pn1->pn_next = pn2->pn_next;
642 handler.freeTree(pn2);
643 pn2 = pn1->pn_next;
644 pn->pn_count--;
645 folded = true;
646 } else {
647 if (foldedStr) {
648 // Convert the rope of folded strings into an Atom.
649 pn1->pn_atom = AtomizeString(cx, foldedStr);
650 if (!pn1->pn_atom)
651 return false;
652 foldedStr = nullptr;
654 pn1 = pn2;
655 pn2 = pn2->pn_next;
659 if (foldedStr) {
660 // Convert the rope of folded strings into an Atom.
661 pn1->pn_atom = AtomizeString(cx, foldedStr);
662 if (!pn1->pn_atom)
663 return false;
666 if (folded) {
667 if (pn->pn_count == 1) {
668 // We reduced the list to one constant. There is no
669 // addition anymore. Replace the PNK_ADD node with the
670 // single PNK_STRING or PNK_NUMBER node.
671 ReplaceNode(pnp, pn1);
672 pn = pn1;
673 } else if (!pn2) {
674 pn->pn_tail = &pn1->pn_next;
677 break;
680 /* Handle a binary string concatenation. */
681 MOZ_ASSERT(pn->isArity(PN_BINARY));
682 if (pn1->isKind(PNK_STRING) || pn2->isKind(PNK_STRING)) {
683 if (!FoldType(cx, !pn1->isKind(PNK_STRING) ? pn1 : pn2, PNK_STRING))
684 return false;
685 if (!pn1->isKind(PNK_STRING) || !pn2->isKind(PNK_STRING))
686 return true;
687 RootedString left(cx, pn1->pn_atom);
688 RootedString right(cx, pn2->pn_atom);
689 RootedString str(cx, ConcatStrings<CanGC>(cx, left, right));
690 if (!str)
691 return false;
692 pn->pn_atom = AtomizeString(cx, str);
693 if (!pn->pn_atom)
694 return false;
695 pn->setKind(PNK_STRING);
696 pn->setOp(JSOP_STRING);
697 pn->setArity(PN_NULLARY);
698 handler.freeTree(pn1);
699 handler.freeTree(pn2);
700 break;
703 /* Can't concatenate string literals, let's try numbers. */
704 goto do_binary_op;
706 case PNK_SUB:
707 case PNK_STAR:
708 case PNK_LSH:
709 case PNK_RSH:
710 case PNK_URSH:
711 case PNK_DIV:
712 case PNK_MOD:
713 do_binary_op:
714 if (pn->isArity(PN_LIST)) {
715 MOZ_ASSERT(pn->pn_count > 2);
716 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
717 if (!FoldType(cx, pn2, PNK_NUMBER))
718 return false;
720 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
721 /* XXX fold only if all operands convert to number */
722 if (!pn2->isKind(PNK_NUMBER))
723 break;
725 if (!pn2) {
726 JSOp op = pn->getOp();
728 pn2 = pn1->pn_next;
729 pn3 = pn2->pn_next;
730 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn))
731 return false;
732 while ((pn2 = pn3) != nullptr) {
733 pn3 = pn2->pn_next;
734 if (!FoldBinaryNumeric(cx, op, pn, pn2, pn))
735 return false;
738 } else {
739 MOZ_ASSERT(pn->isArity(PN_BINARY));
740 if (!FoldType(cx, pn1, PNK_NUMBER) ||
741 !FoldType(cx, pn2, PNK_NUMBER)) {
742 return false;
744 if (pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER)) {
745 if (!FoldBinaryNumeric(cx, pn->getOp(), pn1, pn2, pn))
746 return false;
749 break;
751 case PNK_TYPEOF:
752 case PNK_VOID:
753 case PNK_NOT:
754 case PNK_BITNOT:
755 case PNK_POS:
756 case PNK_NEG:
757 if (pn1->isKind(PNK_NUMBER)) {
758 double d;
760 /* Operate on one numeric constant. */
761 d = pn1->pn_dval;
762 switch (pn->getKind()) {
763 case PNK_BITNOT:
764 d = ~ToInt32(d);
765 break;
767 case PNK_NEG:
768 d = -d;
769 break;
771 case PNK_POS:
772 break;
774 case PNK_NOT:
775 if (d == 0 || IsNaN(d)) {
776 pn->setKind(PNK_TRUE);
777 pn->setOp(JSOP_TRUE);
778 } else {
779 pn->setKind(PNK_FALSE);
780 pn->setOp(JSOP_FALSE);
782 pn->setArity(PN_NULLARY);
783 /* FALL THROUGH */
785 default:
786 /* Return early to dodge the common PNK_NUMBER code. */
787 return true;
789 pn->setKind(PNK_NUMBER);
790 pn->setOp(JSOP_DOUBLE);
791 pn->setArity(PN_NULLARY);
792 pn->pn_dval = d;
793 handler.freeTree(pn1);
794 } else if (pn1->isKind(PNK_TRUE) || pn1->isKind(PNK_FALSE)) {
795 if (pn->isKind(PNK_NOT)) {
796 ReplaceNode(pnp, pn1);
797 pn = pn1;
798 if (pn->isKind(PNK_TRUE)) {
799 pn->setKind(PNK_FALSE);
800 pn->setOp(JSOP_FALSE);
801 } else {
802 pn->setKind(PNK_TRUE);
803 pn->setOp(JSOP_TRUE);
807 break;
809 case PNK_ELEM: {
810 // An indexed expression, pn1[pn2]. A few cases can be improved.
811 PropertyName* name = nullptr;
812 if (pn2->isKind(PNK_STRING)) {
813 JSAtom* atom = pn2->pn_atom;
814 uint32_t index;
816 if (atom->isIndex(&index)) {
817 // Optimization 1: We have something like pn1["100"]. This is
818 // equivalent to pn1[100] which is faster.
819 pn2->setKind(PNK_NUMBER);
820 pn2->setOp(JSOP_DOUBLE);
821 pn2->pn_dval = index;
822 } else {
823 name = atom->asPropertyName();
825 } else if (pn2->isKind(PNK_NUMBER)) {
826 double number = pn2->pn_dval;
827 if (number != ToUint32(number)) {
828 // Optimization 2: We have something like pn1[3.14]. The number
829 // is not an array index. This is equivalent to pn1["3.14"]
830 // which enables optimization 3 below.
831 JSAtom* atom = ToAtom<NoGC>(cx, DoubleValue(number));
832 if (!atom)
833 return false;
834 name = atom->asPropertyName();
838 if (name && NameToId(name) == types::IdToTypeId(NameToId(name))) {
839 // Optimization 3: We have pn1["foo"] where foo is not an index.
840 // Convert to a property access (like pn1.foo) which we optimize
841 // better downstream. Don't bother with this for names which TI
842 // considers to be indexes, to simplify downstream analysis.
843 ParseNode* expr = handler.newPropertyAccess(pn->pn_left, name, pn->pn_pos.end);
844 if (!expr)
845 return false;
846 ReplaceNode(pnp, expr);
848 pn->pn_left = nullptr;
849 pn->pn_right = nullptr;
850 handler.freeTree(pn);
851 pn = expr;
853 break;
856 default:;
859 if (sc == SyntacticContext::Condition) {
860 Truthiness t = Boolish(pn);
861 if (t != Unknown) {
863 * We can turn function nodes into constant nodes here, but mutating function
864 * nodes is tricky --- in particular, mutating a function node that appears on
865 * a method list corrupts the method list. However, methods are M's in
866 * statements of the form 'this.foo = M;', which we never fold, so we're okay.
868 handler.prepareNodeForMutation(pn);
869 if (t == Truthy) {
870 pn->setKind(PNK_TRUE);
871 pn->setOp(JSOP_TRUE);
872 } else {
873 pn->setKind(PNK_FALSE);
874 pn->setOp(JSOP_FALSE);
876 pn->setArity(PN_NULLARY);
880 return true;
883 bool
884 frontend::FoldConstants(ExclusiveContext* cx, ParseNode** pnp, Parser<FullParseHandler>* parser)
886 // Don't fold constants if the code has requested "use asm" as
887 // constant-folding will misrepresent the source text for the purpose
888 // of type checking. (Also guard against entering a function containing
889 // "use asm", see PN_FUNC case below.)
890 if (parser->pc->useAsmOrInsideUseAsm())
891 return true;
893 return Fold(cx, pnp, parser->handler, parser->options(), false, SyntacticContext::Other);