Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 "vm/NumericConversions.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;
31 static bool
32 ContainsVarOrConst(ExclusiveContext *cx, ParseNode *pn, ParseNode **resultp)
33 {
34 JS_CHECK_RECURSION(cx, return false);
36 if (!pn) {
37 *resultp = nullptr;
38 return true;
39 }
40 if (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST)) {
41 *resultp = pn;
42 return true;
43 }
44 switch (pn->getArity()) {
45 case PN_LIST:
46 for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
47 if (!ContainsVarOrConst(cx, pn2, resultp))
48 return false;
49 if (*resultp)
50 return true;
51 }
52 break;
54 case PN_TERNARY:
55 if (!ContainsVarOrConst(cx, pn->pn_kid1, resultp))
56 return false;
57 if (*resultp)
58 return true;
59 if (!ContainsVarOrConst(cx, pn->pn_kid2, resultp))
60 return false;
61 if (*resultp)
62 return true;
63 return ContainsVarOrConst(cx, pn->pn_kid3, resultp);
65 case PN_BINARY:
66 case PN_BINARY_OBJ:
67 // Limit recursion if pn is a binary expression, which can't contain a
68 // var statement.
69 if (!pn->isOp(JSOP_NOP)) {
70 *resultp = nullptr;
71 return true;
72 }
73 if (!ContainsVarOrConst(cx, pn->pn_left, resultp))
74 return false;
75 if (*resultp)
76 return true;
77 return ContainsVarOrConst(cx, pn->pn_right, resultp);
79 case PN_UNARY:
80 if (!pn->isOp(JSOP_NOP)) {
81 *resultp = nullptr;
82 return true;
83 }
84 return ContainsVarOrConst(cx, pn->pn_kid, resultp);
86 case PN_NAME:
87 return ContainsVarOrConst(cx, pn->maybeExpr(), resultp);
89 default:;
90 }
91 *resultp = nullptr;
92 return true;
93 }
95 /*
96 * Fold from one constant type to another.
97 * XXX handles only strings and numbers for now
98 */
99 static bool
100 FoldType(ExclusiveContext *cx, ParseNode *pn, ParseNodeKind kind)
101 {
102 if (!pn->isKind(kind)) {
103 switch (kind) {
104 case PNK_NUMBER:
105 if (pn->isKind(PNK_STRING)) {
106 double d;
107 if (!StringToNumber(cx, pn->pn_atom, &d))
108 return false;
109 pn->pn_dval = d;
110 pn->setKind(PNK_NUMBER);
111 pn->setOp(JSOP_DOUBLE);
112 }
113 break;
115 case PNK_STRING:
116 if (pn->isKind(PNK_NUMBER)) {
117 pn->pn_atom = NumberToAtom(cx, pn->pn_dval);
118 if (!pn->pn_atom)
119 return false;
120 pn->setKind(PNK_STRING);
121 pn->setOp(JSOP_STRING);
122 }
123 break;
125 default:;
126 }
127 }
128 return true;
129 }
131 /*
132 * Fold two numeric constants. Beware that pn1 and pn2 are recycled, unless
133 * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
134 * a successful call to this function.
135 */
136 static bool
137 FoldBinaryNumeric(ExclusiveContext *cx, JSOp op, ParseNode *pn1, ParseNode *pn2,
138 ParseNode *pn)
139 {
140 double d, d2;
141 int32_t i, j;
143 JS_ASSERT(pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER));
144 d = pn1->pn_dval;
145 d2 = pn2->pn_dval;
146 switch (op) {
147 case JSOP_LSH:
148 case JSOP_RSH:
149 i = ToInt32(d);
150 j = ToInt32(d2);
151 j &= 31;
152 d = int32_t((op == JSOP_LSH) ? uint32_t(i) << j : i >> j);
153 break;
155 case JSOP_URSH:
156 j = ToInt32(d2);
157 j &= 31;
158 d = ToUint32(d) >> j;
159 break;
161 case JSOP_ADD:
162 d += d2;
163 break;
165 case JSOP_SUB:
166 d -= d2;
167 break;
169 case JSOP_MUL:
170 d *= d2;
171 break;
173 case JSOP_DIV:
174 if (d2 == 0) {
175 #if defined(XP_WIN)
176 /* XXX MSVC miscompiles such that (NaN == 0) */
177 if (IsNaN(d2))
178 d = GenericNaN();
179 else
180 #endif
181 if (d == 0 || IsNaN(d))
182 d = GenericNaN();
183 else if (IsNegative(d) != IsNegative(d2))
184 d = NegativeInfinity<double>();
185 else
186 d = PositiveInfinity<double>();
187 } else {
188 d /= d2;
189 }
190 break;
192 case JSOP_MOD:
193 if (d2 == 0) {
194 d = GenericNaN();
195 } else {
196 d = js_fmod(d, d2);
197 }
198 break;
200 default:;
201 }
203 /* Take care to allow pn1 or pn2 to alias pn. */
204 pn->setKind(PNK_NUMBER);
205 pn->setOp(JSOP_DOUBLE);
206 pn->setArity(PN_NULLARY);
207 pn->pn_dval = d;
208 return true;
209 }
211 // Remove a ParseNode, **pnp, from a parse tree, putting another ParseNode,
212 // *pn, in its place.
213 //
214 // pnp points to a ParseNode pointer. This must be the only pointer that points
215 // to the parse node being replaced. The replacement, *pn, is unchanged except
216 // for its pn_next pointer; updating that is necessary if *pn's new parent is a
217 // list node.
218 static void
219 ReplaceNode(ParseNode **pnp, ParseNode *pn)
220 {
221 pn->pn_next = (*pnp)->pn_next;
222 *pnp = pn;
223 }
225 enum Truthiness { Truthy, Falsy, Unknown };
227 static Truthiness
228 Boolish(ParseNode *pn)
229 {
230 switch (pn->getKind()) {
231 case PNK_NUMBER:
232 return (pn->pn_dval != 0 && !IsNaN(pn->pn_dval)) ? Truthy : Falsy;
234 case PNK_STRING:
235 return (pn->pn_atom->length() > 0) ? Truthy : Falsy;
237 case PNK_TRUE:
238 case PNK_FUNCTION:
239 case PNK_GENEXP:
240 return Truthy;
242 case PNK_FALSE:
243 case PNK_NULL:
244 return Falsy;
246 default:
247 return Unknown;
248 }
249 }
251 // Expressions that appear in a few specific places are treated specially
252 // during constant folding. This enum tells where a parse node appears.
253 MOZ_BEGIN_ENUM_CLASS(SyntacticContext, int)
254 // pn is an expression, and it appears in a context where only its side
255 // effects and truthiness matter: the condition of an if statement,
256 // conditional expression, while loop, or for(;;) loop; or an operand of &&
257 // or || in such a context.
258 Condition,
260 // pn is the operand of the 'delete' keyword.
261 Delete,
263 // Any other syntactic context.
264 Other
265 MOZ_END_ENUM_CLASS(SyntacticContext)
267 static SyntacticContext
268 condIf(const ParseNode *pn, ParseNodeKind kind)
269 {
270 return pn->isKind(kind) ? SyntacticContext::Condition : SyntacticContext::Other;
271 }
273 static bool
274 Fold(ExclusiveContext *cx, ParseNode **pnp,
275 FullParseHandler &handler, const ReadOnlyCompileOptions &options,
276 bool inGenexpLambda, SyntacticContext sc)
277 {
278 ParseNode *pn = *pnp;
279 ParseNode *pn1 = nullptr, *pn2 = nullptr, *pn3 = nullptr;
281 JS_CHECK_RECURSION(cx, return false);
283 // First, recursively fold constants on the children of this node.
284 switch (pn->getArity()) {
285 case PN_CODE:
286 if (pn->isKind(PNK_FUNCTION) &&
287 pn->pn_funbox->useAsmOrInsideUseAsm() && options.asmJSOption)
288 {
289 return true;
290 } else {
291 // Note: pn_body is nullptr for functions which are being lazily parsed.
292 JS_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;
297 }
298 }
299 break;
301 case PN_LIST:
302 {
303 // Propagate Condition context through logical connectives.
304 SyntacticContext kidsc = SyntacticContext::Other;
305 if (pn->isKind(PNK_OR) || pn->isKind(PNK_AND))
306 kidsc = sc;
308 // Don't fold a parenthesized call expression. See bug 537673.
309 ParseNode **listp = &pn->pn_head;
310 if ((pn->isKind(PNK_CALL) || pn->isKind(PNK_NEW)) && (*listp)->isInParens())
311 listp = &(*listp)->pn_next;
313 for (; *listp; listp = &(*listp)->pn_next) {
314 if (!Fold(cx, listp, handler, options, inGenexpLambda, kidsc))
315 return false;
316 }
318 // If the last node in the list was replaced, pn_tail points into the wrong node.
319 pn->pn_tail = listp;
321 // Save the list head in pn1 for later use.
322 pn1 = pn->pn_head;
323 pn2 = nullptr;
324 break;
325 }
327 case PN_TERNARY:
328 /* Any kid may be null (e.g. for (;;)). */
329 if (pn->pn_kid1) {
330 if (!Fold(cx, &pn->pn_kid1, handler, options, inGenexpLambda, condIf(pn, PNK_IF)))
331 return false;
332 }
333 pn1 = pn->pn_kid1;
335 if (pn->pn_kid2) {
336 if (!Fold(cx, &pn->pn_kid2, handler, options, inGenexpLambda, condIf(pn, PNK_FORHEAD)))
337 return false;
338 if (pn->isKind(PNK_FORHEAD) && pn->pn_kid2->isKind(PNK_TRUE)) {
339 handler.freeTree(pn->pn_kid2);
340 pn->pn_kid2 = nullptr;
341 }
342 }
343 pn2 = pn->pn_kid2;
345 if (pn->pn_kid3) {
346 if (!Fold(cx, &pn->pn_kid3, handler, options, inGenexpLambda, SyntacticContext::Other))
347 return false;
348 }
349 pn3 = pn->pn_kid3;
350 break;
352 case PN_BINARY:
353 case PN_BINARY_OBJ:
354 if (pn->isKind(PNK_OR) || pn->isKind(PNK_AND)) {
355 // Propagate Condition context through logical connectives.
356 SyntacticContext kidsc = SyntacticContext::Other;
357 if (sc == SyntacticContext::Condition)
358 kidsc = sc;
359 if (!Fold(cx, &pn->pn_left, handler, options, inGenexpLambda, kidsc))
360 return false;
361 if (!Fold(cx, &pn->pn_right, handler, options, inGenexpLambda, kidsc))
362 return false;
363 } else {
364 /* First kid may be null (for default case in switch). */
365 if (pn->pn_left) {
366 if (!Fold(cx, &pn->pn_left, handler, options, inGenexpLambda, condIf(pn, PNK_WHILE)))
367 return false;
368 }
369 if (!Fold(cx, &pn->pn_right, handler, options, inGenexpLambda, condIf(pn, PNK_DOWHILE)))
370 return false;
371 }
372 pn1 = pn->pn_left;
373 pn2 = pn->pn_right;
374 break;
376 case PN_UNARY:
377 /*
378 * Kludge to deal with typeof expressions: because constant folding
379 * can turn an expression into a name node, we have to check here,
380 * before folding, to see if we should throw undefined name errors.
381 *
382 * NB: We know that if pn->pn_op is JSOP_TYPEOF, pn1 will not be
383 * null. This assumption does not hold true for other unary
384 * expressions.
385 */
386 if (pn->isKind(PNK_TYPEOF) && !pn->pn_kid->isKind(PNK_NAME))
387 pn->setOp(JSOP_TYPEOFEXPR);
389 if (pn->pn_kid) {
390 SyntacticContext kidsc =
391 pn->isKind(PNK_NOT)
392 ? SyntacticContext::Condition
393 : pn->isKind(PNK_DELETE)
394 ? SyntacticContext::Delete
395 : SyntacticContext::Other;
396 if (!Fold(cx, &pn->pn_kid, handler, options, inGenexpLambda, kidsc))
397 return false;
398 }
399 pn1 = pn->pn_kid;
400 break;
402 case PN_NAME:
403 /*
404 * Skip pn1 down along a chain of dotted member expressions to avoid
405 * excessive recursion. Our only goal here is to fold constants (if
406 * any) in the primary expression operand to the left of the first
407 * dot in the chain.
408 */
409 if (!pn->isUsed()) {
410 ParseNode **lhsp = &pn->pn_expr;
411 while (*lhsp && (*lhsp)->isArity(PN_NAME) && !(*lhsp)->isUsed())
412 lhsp = &(*lhsp)->pn_expr;
413 if (*lhsp && !Fold(cx, lhsp, handler, options, inGenexpLambda, SyntacticContext::Other))
414 return false;
415 pn1 = *lhsp;
416 }
417 break;
419 case PN_NULLARY:
420 break;
421 }
423 // The immediate child of a PNK_DELETE node should not be replaced
424 // with node indicating a different syntactic form; |delete x| is not
425 // the same as |delete (true && x)|. See bug 888002.
426 //
427 // pn is the immediate child in question. Its descendents were already
428 // constant-folded above, so we're done.
429 if (sc == SyntacticContext::Delete)
430 return true;
432 switch (pn->getKind()) {
433 case PNK_IF:
434 {
435 ParseNode *decl;
436 if (!ContainsVarOrConst(cx, pn2, &decl))
437 return false;
438 if (decl)
439 break;
440 if (!ContainsVarOrConst(cx, pn3, &decl))
441 return false;
442 if (decl)
443 break;
444 }
445 /* FALL THROUGH */
447 case PNK_CONDITIONAL:
448 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
449 switch (pn1->getKind()) {
450 case PNK_NUMBER:
451 if (pn1->pn_dval == 0 || IsNaN(pn1->pn_dval))
452 pn2 = pn3;
453 break;
454 case PNK_STRING:
455 if (pn1->pn_atom->length() == 0)
456 pn2 = pn3;
457 break;
458 case PNK_TRUE:
459 break;
460 case PNK_FALSE:
461 case PNK_NULL:
462 pn2 = pn3;
463 break;
464 default:
465 /* Early return to dodge common code that copies pn2 to pn. */
466 return true;
467 }
469 #if JS_HAS_GENERATOR_EXPRS
470 /* Don't fold a trailing |if (0)| in a generator expression. */
471 if (!pn2 && inGenexpLambda)
472 break;
473 #endif
475 if (pn2 && !pn2->isDefn()) {
476 ReplaceNode(pnp, pn2);
477 pn = pn2;
478 }
479 if (!pn2 || (pn->isKind(PNK_SEMI) && !pn->pn_kid)) {
480 /*
481 * False condition and no else, or an empty then-statement was
482 * moved up over pn. Either way, make pn an empty block (not an
483 * empty statement, which does not decompile, even when labeled).
484 * NB: pn must be a PNK_IF as PNK_CONDITIONAL can never have a null
485 * kid or an empty statement for a child.
486 */
487 pn->setKind(PNK_STATEMENTLIST);
488 pn->setArity(PN_LIST);
489 pn->makeEmpty();
490 }
491 if (pn3 && pn3 != pn2)
492 handler.freeTree(pn3);
493 break;
495 case PNK_OR:
496 case PNK_AND:
497 if (sc == SyntacticContext::Condition) {
498 if (pn->isArity(PN_LIST)) {
499 ParseNode **listp = &pn->pn_head;
500 JS_ASSERT(*listp == pn1);
501 uint32_t orig = pn->pn_count;
502 do {
503 Truthiness t = Boolish(pn1);
504 if (t == Unknown) {
505 listp = &pn1->pn_next;
506 continue;
507 }
508 if ((t == Truthy) == pn->isKind(PNK_OR)) {
509 for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
510 pn3 = pn2->pn_next;
511 handler.freeTree(pn2);
512 --pn->pn_count;
513 }
514 pn1->pn_next = nullptr;
515 break;
516 }
517 JS_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
518 if (pn->pn_count == 1)
519 break;
520 *listp = pn1->pn_next;
521 handler.freeTree(pn1);
522 --pn->pn_count;
523 } while ((pn1 = *listp) != nullptr);
525 // We may have to change arity from LIST to BINARY.
526 pn1 = pn->pn_head;
527 if (pn->pn_count == 2) {
528 pn2 = pn1->pn_next;
529 pn1->pn_next = nullptr;
530 JS_ASSERT(!pn2->pn_next);
531 pn->setArity(PN_BINARY);
532 pn->pn_left = pn1;
533 pn->pn_right = pn2;
534 } else if (pn->pn_count == 1) {
535 ReplaceNode(pnp, pn1);
536 pn = pn1;
537 } else if (orig != pn->pn_count) {
538 // Adjust list tail.
539 pn2 = pn1->pn_next;
540 for (; pn1; pn2 = pn1, pn1 = pn1->pn_next)
541 ;
542 pn->pn_tail = &pn2->pn_next;
543 }
544 } else {
545 Truthiness t = Boolish(pn1);
546 if (t != Unknown) {
547 if ((t == Truthy) == pn->isKind(PNK_OR)) {
548 handler.freeTree(pn2);
549 ReplaceNode(pnp, pn1);
550 pn = pn1;
551 } else {
552 JS_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
553 handler.freeTree(pn1);
554 ReplaceNode(pnp, pn2);
555 pn = pn2;
556 }
557 }
558 }
559 }
560 break;
562 case PNK_SUBASSIGN:
563 case PNK_BITORASSIGN:
564 case PNK_BITXORASSIGN:
565 case PNK_BITANDASSIGN:
566 case PNK_LSHASSIGN:
567 case PNK_RSHASSIGN:
568 case PNK_URSHASSIGN:
569 case PNK_MULASSIGN:
570 case PNK_DIVASSIGN:
571 case PNK_MODASSIGN:
572 /*
573 * Compound operators such as *= should be subject to folding, in case
574 * the left-hand side is constant, and so that the decompiler produces
575 * the same string that you get from decompiling a script or function
576 * compiled from that same string. += is special and so must be
577 * handled below.
578 */
579 goto do_binary_op;
581 case PNK_ADDASSIGN:
582 JS_ASSERT(pn->isOp(JSOP_ADD));
583 /* FALL THROUGH */
584 case PNK_ADD:
585 if (pn->isArity(PN_LIST)) {
586 bool folded = false;
588 pn2 = pn1->pn_next;
589 if (pn1->isKind(PNK_NUMBER)) {
590 // Fold addition of numeric literals: (1 + 2 + x === 3 + x).
591 // Note that we can only do this the front of the list:
592 // (x + 1 + 2 !== x + 3) when x is a string.
593 while (pn2 && pn2->isKind(PNK_NUMBER)) {
594 pn1->pn_dval += pn2->pn_dval;
595 pn1->pn_next = pn2->pn_next;
596 handler.freeTree(pn2);
597 pn2 = pn1->pn_next;
598 pn->pn_count--;
599 folded = true;
600 }
601 }
603 // Now search for adjacent pairs of literals to fold for string
604 // concatenation.
605 //
606 // isStringConcat is true if we know the operation we're looking at
607 // will be string concatenation at runtime. As soon as we see a
608 // string, we know that every addition to the right of it will be
609 // string concatenation, even if both operands are numbers:
610 // ("s" + x + 1 + 2 === "s" + x + "12").
611 //
612 bool isStringConcat = false;
613 RootedString foldedStr(cx);
615 // (number + string) is definitely concatenation, but only at the
616 // front of the list: (x + 1 + "2" !== x + "12") when x is a
617 // number.
618 if (pn1->isKind(PNK_NUMBER) && pn2 && pn2->isKind(PNK_STRING))
619 isStringConcat = true;
621 while (pn2) {
622 isStringConcat = isStringConcat || pn1->isKind(PNK_STRING);
624 if (isStringConcat &&
625 (pn1->isKind(PNK_STRING) || pn1->isKind(PNK_NUMBER)) &&
626 (pn2->isKind(PNK_STRING) || pn2->isKind(PNK_NUMBER)))
627 {
628 // Fold string concatenation of literals.
629 if (pn1->isKind(PNK_NUMBER) && !FoldType(cx, pn1, PNK_STRING))
630 return false;
631 if (pn2->isKind(PNK_NUMBER) && !FoldType(cx, pn2, PNK_STRING))
632 return false;
633 if (!foldedStr)
634 foldedStr = pn1->pn_atom;
635 RootedString right(cx, pn2->pn_atom);
636 foldedStr = ConcatStrings<CanGC>(cx, foldedStr, right);
637 if (!foldedStr)
638 return false;
639 pn1->pn_next = pn2->pn_next;
640 handler.freeTree(pn2);
641 pn2 = pn1->pn_next;
642 pn->pn_count--;
643 folded = true;
644 } else {
645 if (foldedStr) {
646 // Convert the rope of folded strings into an Atom.
647 pn1->pn_atom = AtomizeString(cx, foldedStr);
648 if (!pn1->pn_atom)
649 return false;
650 foldedStr = nullptr;
651 }
652 pn1 = pn2;
653 pn2 = pn2->pn_next;
654 }
655 }
657 if (foldedStr) {
658 // Convert the rope of folded strings into an Atom.
659 pn1->pn_atom = AtomizeString(cx, foldedStr);
660 if (!pn1->pn_atom)
661 return false;
662 }
664 if (folded) {
665 if (pn->pn_count == 1) {
666 // We reduced the list to one constant. There is no
667 // addition anymore. Replace the PNK_ADD node with the
668 // single PNK_STRING or PNK_NUMBER node.
669 ReplaceNode(pnp, pn1);
670 pn = pn1;
671 } else if (!pn2) {
672 pn->pn_tail = &pn1->pn_next;
673 }
674 }
675 break;
676 }
678 /* Handle a binary string concatenation. */
679 JS_ASSERT(pn->isArity(PN_BINARY));
680 if (pn1->isKind(PNK_STRING) || pn2->isKind(PNK_STRING)) {
681 if (!FoldType(cx, !pn1->isKind(PNK_STRING) ? pn1 : pn2, PNK_STRING))
682 return false;
683 if (!pn1->isKind(PNK_STRING) || !pn2->isKind(PNK_STRING))
684 return true;
685 RootedString left(cx, pn1->pn_atom);
686 RootedString right(cx, pn2->pn_atom);
687 RootedString str(cx, ConcatStrings<CanGC>(cx, left, right));
688 if (!str)
689 return false;
690 pn->pn_atom = AtomizeString(cx, str);
691 if (!pn->pn_atom)
692 return false;
693 pn->setKind(PNK_STRING);
694 pn->setOp(JSOP_STRING);
695 pn->setArity(PN_NULLARY);
696 handler.freeTree(pn1);
697 handler.freeTree(pn2);
698 break;
699 }
701 /* Can't concatenate string literals, let's try numbers. */
702 goto do_binary_op;
704 case PNK_SUB:
705 case PNK_STAR:
706 case PNK_LSH:
707 case PNK_RSH:
708 case PNK_URSH:
709 case PNK_DIV:
710 case PNK_MOD:
711 do_binary_op:
712 if (pn->isArity(PN_LIST)) {
713 JS_ASSERT(pn->pn_count > 2);
714 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
715 if (!FoldType(cx, pn2, PNK_NUMBER))
716 return false;
717 }
718 for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
719 /* XXX fold only if all operands convert to number */
720 if (!pn2->isKind(PNK_NUMBER))
721 break;
722 }
723 if (!pn2) {
724 JSOp op = pn->getOp();
726 pn2 = pn1->pn_next;
727 pn3 = pn2->pn_next;
728 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn))
729 return false;
730 while ((pn2 = pn3) != nullptr) {
731 pn3 = pn2->pn_next;
732 if (!FoldBinaryNumeric(cx, op, pn, pn2, pn))
733 return false;
734 }
735 }
736 } else {
737 JS_ASSERT(pn->isArity(PN_BINARY));
738 if (!FoldType(cx, pn1, PNK_NUMBER) ||
739 !FoldType(cx, pn2, PNK_NUMBER)) {
740 return false;
741 }
742 if (pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER)) {
743 if (!FoldBinaryNumeric(cx, pn->getOp(), pn1, pn2, pn))
744 return false;
745 }
746 }
747 break;
749 case PNK_TYPEOF:
750 case PNK_VOID:
751 case PNK_NOT:
752 case PNK_BITNOT:
753 case PNK_POS:
754 case PNK_NEG:
755 if (pn1->isKind(PNK_NUMBER)) {
756 double d;
758 /* Operate on one numeric constant. */
759 d = pn1->pn_dval;
760 switch (pn->getKind()) {
761 case PNK_BITNOT:
762 d = ~ToInt32(d);
763 break;
765 case PNK_NEG:
766 d = -d;
767 break;
769 case PNK_POS:
770 break;
772 case PNK_NOT:
773 if (d == 0 || IsNaN(d)) {
774 pn->setKind(PNK_TRUE);
775 pn->setOp(JSOP_TRUE);
776 } else {
777 pn->setKind(PNK_FALSE);
778 pn->setOp(JSOP_FALSE);
779 }
780 pn->setArity(PN_NULLARY);
781 /* FALL THROUGH */
783 default:
784 /* Return early to dodge the common PNK_NUMBER code. */
785 return true;
786 }
787 pn->setKind(PNK_NUMBER);
788 pn->setOp(JSOP_DOUBLE);
789 pn->setArity(PN_NULLARY);
790 pn->pn_dval = d;
791 handler.freeTree(pn1);
792 } else if (pn1->isKind(PNK_TRUE) || pn1->isKind(PNK_FALSE)) {
793 if (pn->isKind(PNK_NOT)) {
794 ReplaceNode(pnp, pn1);
795 pn = pn1;
796 if (pn->isKind(PNK_TRUE)) {
797 pn->setKind(PNK_FALSE);
798 pn->setOp(JSOP_FALSE);
799 } else {
800 pn->setKind(PNK_TRUE);
801 pn->setOp(JSOP_TRUE);
802 }
803 }
804 }
805 break;
807 case PNK_ELEM: {
808 // An indexed expression, pn1[pn2]. A few cases can be improved.
809 PropertyName *name = nullptr;
810 if (pn2->isKind(PNK_STRING)) {
811 JSAtom *atom = pn2->pn_atom;
812 uint32_t index;
814 if (atom->isIndex(&index)) {
815 // Optimization 1: We have something like pn1["100"]. This is
816 // equivalent to pn1[100] which is faster.
817 pn2->setKind(PNK_NUMBER);
818 pn2->setOp(JSOP_DOUBLE);
819 pn2->pn_dval = index;
820 } else {
821 name = atom->asPropertyName();
822 }
823 } else if (pn2->isKind(PNK_NUMBER)) {
824 double number = pn2->pn_dval;
825 if (number != ToUint32(number)) {
826 // Optimization 2: We have something like pn1[3.14]. The number
827 // is not an array index. This is equivalent to pn1["3.14"]
828 // which enables optimization 3 below.
829 JSAtom *atom = ToAtom<NoGC>(cx, DoubleValue(number));
830 if (!atom)
831 return false;
832 name = atom->asPropertyName();
833 }
834 }
836 if (name && NameToId(name) == types::IdToTypeId(NameToId(name))) {
837 // Optimization 3: We have pn1["foo"] where foo is not an index.
838 // Convert to a property access (like pn1.foo) which we optimize
839 // better downstream. Don't bother with this for names which TI
840 // considers to be indexes, to simplify downstream analysis.
841 ParseNode *expr = handler.newPropertyAccess(pn->pn_left, name, pn->pn_pos.end);
842 if (!expr)
843 return false;
844 ReplaceNode(pnp, expr);
846 pn->pn_left = nullptr;
847 pn->pn_right = nullptr;
848 handler.freeTree(pn);
849 pn = expr;
850 }
851 break;
852 }
854 default:;
855 }
857 if (sc == SyntacticContext::Condition) {
858 Truthiness t = Boolish(pn);
859 if (t != Unknown) {
860 /*
861 * We can turn function nodes into constant nodes here, but mutating function
862 * nodes is tricky --- in particular, mutating a function node that appears on
863 * a method list corrupts the method list. However, methods are M's in
864 * statements of the form 'this.foo = M;', which we never fold, so we're okay.
865 */
866 handler.prepareNodeForMutation(pn);
867 if (t == Truthy) {
868 pn->setKind(PNK_TRUE);
869 pn->setOp(JSOP_TRUE);
870 } else {
871 pn->setKind(PNK_FALSE);
872 pn->setOp(JSOP_FALSE);
873 }
874 pn->setArity(PN_NULLARY);
875 }
876 }
878 return true;
879 }
881 bool
882 frontend::FoldConstants(ExclusiveContext *cx, ParseNode **pnp, Parser<FullParseHandler> *parser)
883 {
884 // Don't fold constants if the code has requested "use asm" as
885 // constant-folding will misrepresent the source text for the purpose
886 // of type checking. (Also guard against entering a function containing
887 // "use asm", see PN_FUNC case below.)
888 if (parser->pc->useAsmOrInsideUseAsm() && parser->options().asmJSOption)
889 return true;
891 return Fold(cx, pnp, parser->handler, parser->options(), false, SyntacticContext::Other);
892 }