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"
23 using namespace js::frontend
;
26 using mozilla::IsNegative
;
27 using mozilla::NegativeInfinity
;
28 using mozilla::PositiveInfinity
;
34 ContainsVarOrConst(ExclusiveContext
* cx
, ParseNode
* pn
, ParseNode
** resultp
)
36 JS_CHECK_RECURSION(cx
, return false);
42 if (pn
->isKind(PNK_VAR
) || pn
->isKind(PNK_CONST
)) {
46 switch (pn
->getArity()) {
48 for (ParseNode
* pn2
= pn
->pn_head
; pn2
; pn2
= pn2
->pn_next
) {
49 if (!ContainsVarOrConst(cx
, pn2
, resultp
))
57 if (!ContainsVarOrConst(cx
, pn
->pn_kid1
, resultp
))
61 if (!ContainsVarOrConst(cx
, pn
->pn_kid2
, resultp
))
65 return ContainsVarOrConst(cx
, pn
->pn_kid3
, resultp
);
69 // Limit recursion if pn is a binary expression, which can't contain a
71 if (!pn
->isOp(JSOP_NOP
)) {
75 if (!ContainsVarOrConst(cx
, pn
->pn_left
, resultp
))
79 return ContainsVarOrConst(cx
, pn
->pn_right
, resultp
);
82 if (!pn
->isOp(JSOP_NOP
)) {
86 return ContainsVarOrConst(cx
, pn
->pn_kid
, resultp
);
89 return ContainsVarOrConst(cx
, pn
->maybeExpr(), resultp
);
98 * Fold from one constant type to another.
99 * XXX handles only strings and numbers for now
102 FoldType(ExclusiveContext
* cx
, ParseNode
* pn
, ParseNodeKind kind
)
104 if (!pn
->isKind(kind
)) {
107 if (pn
->isKind(PNK_STRING
)) {
109 if (!StringToNumber(cx
, pn
->pn_atom
, &d
))
112 pn
->setKind(PNK_NUMBER
);
113 pn
->setOp(JSOP_DOUBLE
);
118 if (pn
->isKind(PNK_NUMBER
)) {
119 pn
->pn_atom
= NumberToAtom(cx
, pn
->pn_dval
);
122 pn
->setKind(PNK_STRING
);
123 pn
->setOp(JSOP_STRING
);
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.
139 FoldBinaryNumeric(ExclusiveContext
* cx
, JSOp op
, ParseNode
* pn1
, ParseNode
* pn2
,
145 MOZ_ASSERT(pn1
->isKind(PNK_NUMBER
) && pn2
->isKind(PNK_NUMBER
));
154 d
= int32_t((op
== JSOP_LSH
) ? uint32_t(i
) << j
: i
>> j
);
160 d
= ToUint32(d
) >> j
;
178 /* XXX MSVC miscompiles such that (NaN == 0) */
183 if (d
== 0 || IsNaN(d
))
185 else if (IsNegative(d
) != IsNegative(d2
))
186 d
= NegativeInfinity
<double>();
188 d
= PositiveInfinity
<double>();
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
);
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
221 ReplaceNode(ParseNode
** pnp
, ParseNode
* pn
)
223 pn
->pn_next
= (*pnp
)->pn_next
;
227 enum Truthiness
{ Truthy
, Falsy
, Unknown
};
230 Boolish(ParseNode
* pn
)
232 switch (pn
->getKind()) {
234 return (pn
->pn_dval
!= 0 && !IsNaN(pn
->pn_dval
)) ? Truthy
: Falsy
;
237 return (pn
->pn_atom
->length() > 0) ? Truthy
: Falsy
;
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.
262 // pn is the operand of the 'delete' keyword.
265 // Any other syntactic context.
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
;
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()) {
288 if (pn
->isKind(PNK_FUNCTION
) && pn
->pn_funbox
->useAsmOrInsideUseAsm())
291 // Note: pn_body is nullptr for functions which are being lazily parsed.
292 MOZ_ASSERT(pn
->getKind() == PNK_FUNCTION
);
294 if (!Fold(cx
, &pn
->pn_body
, handler
, options
, pn
->pn_funbox
->inGenexpLambda
,
295 SyntacticContext::Other
))
302 // Propagate Condition context through logical connectives.
303 SyntacticContext kidsc
= SyntacticContext::Other
;
304 if (pn
->isKind(PNK_OR
) || pn
->isKind(PNK_AND
))
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
))
317 // If the last node in the list was replaced, pn_tail points into the wrong node.
320 // Save the list head in pn1 for later use.
327 /* Any kid may be null (e.g. for (;;)). */
329 if (!Fold(cx
, &pn
->pn_kid1
, handler
, options
, inGenexpLambda
, condIf(pn
, PNK_IF
)))
335 if (!Fold(cx
, &pn
->pn_kid2
, handler
, options
, inGenexpLambda
, condIf(pn
, PNK_FORHEAD
)))
337 if (pn
->isKind(PNK_FORHEAD
) && pn
->pn_kid2
->isKind(PNK_TRUE
)) {
338 handler
.freeTree(pn
->pn_kid2
);
339 pn
->pn_kid2
= nullptr;
345 if (!Fold(cx
, &pn
->pn_kid3
, handler
, options
, inGenexpLambda
, SyntacticContext::Other
))
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
)
358 if (!Fold(cx
, &pn
->pn_left
, handler
, options
, inGenexpLambda
, kidsc
))
360 if (!Fold(cx
, &pn
->pn_right
, handler
, options
, inGenexpLambda
, kidsc
))
363 /* First kid may be null (for default case in switch). */
365 if (!Fold(cx
, &pn
->pn_left
, handler
, options
, inGenexpLambda
, condIf(pn
, PNK_WHILE
)))
368 /* Second kid may be null (for return in non-generator). */
370 if (!Fold(cx
, &pn
->pn_right
, handler
, options
, inGenexpLambda
, condIf(pn
, PNK_DOWHILE
)))
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
388 if (pn
->isKind(PNK_TYPEOF
) && !pn
->pn_kid
->isKind(PNK_NAME
))
389 pn
->setOp(JSOP_TYPEOFEXPR
);
392 SyntacticContext kidsc
=
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
))
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
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
))
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
)
434 switch (pn
->getKind()) {
438 if (!ContainsVarOrConst(cx
, pn2
, &decl
))
442 if (!ContainsVarOrConst(cx
, pn3
, &decl
))
449 case PNK_CONDITIONAL
:
450 /* Reduce 'if (C) T; else E' into T for true C, E for false. */
451 switch (pn1
->getKind()) {
453 if (pn1
->pn_dval
== 0 || IsNaN(pn1
->pn_dval
))
457 if (pn1
->pn_atom
->length() == 0)
467 /* Early return to dodge common code that copies pn2 to pn. */
471 #if JS_HAS_GENERATOR_EXPRS
472 /* Don't fold a trailing |if (0)| in a generator expression. */
473 if (!pn2
&& inGenexpLambda
)
477 if (pn2
&& !pn2
->isDefn()) {
478 ReplaceNode(pnp
, 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
);
493 if (pn3
&& pn3
!= pn2
)
494 handler
.freeTree(pn3
);
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
;
505 Truthiness t
= Boolish(pn1
);
507 listp
= &pn1
->pn_next
;
510 if ((t
== Truthy
) == pn
->isKind(PNK_OR
)) {
511 for (pn2
= pn1
->pn_next
; pn2
; pn2
= pn3
) {
513 handler
.freeTree(pn2
);
516 pn1
->pn_next
= nullptr;
519 MOZ_ASSERT((t
== Truthy
) == pn
->isKind(PNK_AND
));
520 if (pn
->pn_count
== 1)
522 *listp
= pn1
->pn_next
;
523 handler
.freeTree(pn1
);
525 } while ((pn1
= *listp
) != nullptr);
527 // We may have to change arity from LIST to BINARY.
529 if (pn
->pn_count
== 2) {
531 pn1
->pn_next
= nullptr;
532 MOZ_ASSERT(!pn2
->pn_next
);
533 pn
->setArity(PN_BINARY
);
536 } else if (pn
->pn_count
== 1) {
537 ReplaceNode(pnp
, pn1
);
539 } else if (orig
!= pn
->pn_count
) {
542 for (; pn1
; pn2
= pn1
, pn1
= pn1
->pn_next
)
544 pn
->pn_tail
= &pn2
->pn_next
;
547 Truthiness t
= Boolish(pn1
);
549 if ((t
== Truthy
) == pn
->isKind(PNK_OR
)) {
550 handler
.freeTree(pn2
);
551 ReplaceNode(pnp
, pn1
);
554 MOZ_ASSERT((t
== Truthy
) == pn
->isKind(PNK_AND
));
555 handler
.freeTree(pn1
);
556 ReplaceNode(pnp
, pn2
);
565 case PNK_BITORASSIGN
:
566 case PNK_BITXORASSIGN
:
567 case PNK_BITANDASSIGN
:
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
584 MOZ_ASSERT(pn
->isOp(JSOP_ADD
));
587 if (pn
->isArity(PN_LIST
)) {
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
);
605 // Now search for adjacent pairs of literals to fold for string
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
620 if (pn1
->isKind(PNK_NUMBER
) && pn2
&& pn2
->isKind(PNK_STRING
))
621 isStringConcat
= true;
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
))
633 if (pn2
->isKind(PNK_NUMBER
) && !FoldType(cx
, pn2
, PNK_STRING
))
636 foldedStr
= pn1
->pn_atom
;
637 RootedString
right(cx
, pn2
->pn_atom
);
638 foldedStr
= ConcatStrings
<CanGC
>(cx
, foldedStr
, right
);
641 pn1
->pn_next
= pn2
->pn_next
;
642 handler
.freeTree(pn2
);
648 // Convert the rope of folded strings into an Atom.
649 pn1
->pn_atom
= AtomizeString(cx
, foldedStr
);
660 // Convert the rope of folded strings into an Atom.
661 pn1
->pn_atom
= AtomizeString(cx
, foldedStr
);
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
);
674 pn
->pn_tail
= &pn1
->pn_next
;
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
))
685 if (!pn1
->isKind(PNK_STRING
) || !pn2
->isKind(PNK_STRING
))
687 RootedString
left(cx
, pn1
->pn_atom
);
688 RootedString
right(cx
, pn2
->pn_atom
);
689 RootedString
str(cx
, ConcatStrings
<CanGC
>(cx
, left
, right
));
692 pn
->pn_atom
= AtomizeString(cx
, str
);
695 pn
->setKind(PNK_STRING
);
696 pn
->setOp(JSOP_STRING
);
697 pn
->setArity(PN_NULLARY
);
698 handler
.freeTree(pn1
);
699 handler
.freeTree(pn2
);
703 /* Can't concatenate string literals, let's try numbers. */
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
))
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
))
726 JSOp op
= pn
->getOp();
730 if (!FoldBinaryNumeric(cx
, op
, pn1
, pn2
, pn
))
732 while ((pn2
= pn3
) != nullptr) {
734 if (!FoldBinaryNumeric(cx
, op
, pn
, pn2
, pn
))
739 MOZ_ASSERT(pn
->isArity(PN_BINARY
));
740 if (!FoldType(cx
, pn1
, PNK_NUMBER
) ||
741 !FoldType(cx
, pn2
, PNK_NUMBER
)) {
744 if (pn1
->isKind(PNK_NUMBER
) && pn2
->isKind(PNK_NUMBER
)) {
745 if (!FoldBinaryNumeric(cx
, pn
->getOp(), pn1
, pn2
, pn
))
757 if (pn1
->isKind(PNK_NUMBER
)) {
760 /* Operate on one numeric constant. */
762 switch (pn
->getKind()) {
775 if (d
== 0 || IsNaN(d
)) {
776 pn
->setKind(PNK_TRUE
);
777 pn
->setOp(JSOP_TRUE
);
779 pn
->setKind(PNK_FALSE
);
780 pn
->setOp(JSOP_FALSE
);
782 pn
->setArity(PN_NULLARY
);
786 /* Return early to dodge the common PNK_NUMBER code. */
789 pn
->setKind(PNK_NUMBER
);
790 pn
->setOp(JSOP_DOUBLE
);
791 pn
->setArity(PN_NULLARY
);
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
);
798 if (pn
->isKind(PNK_TRUE
)) {
799 pn
->setKind(PNK_FALSE
);
800 pn
->setOp(JSOP_FALSE
);
802 pn
->setKind(PNK_TRUE
);
803 pn
->setOp(JSOP_TRUE
);
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
;
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
;
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
));
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
);
846 ReplaceNode(pnp
, expr
);
848 pn
->pn_left
= nullptr;
849 pn
->pn_right
= nullptr;
850 handler
.freeTree(pn
);
859 if (sc
== SyntacticContext::Condition
) {
860 Truthiness t
= Boolish(pn
);
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
);
870 pn
->setKind(PNK_TRUE
);
871 pn
->setOp(JSOP_TRUE
);
873 pn
->setKind(PNK_FALSE
);
874 pn
->setOp(JSOP_FALSE
);
876 pn
->setArity(PN_NULLARY
);
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())
893 return Fold(cx
, pnp
, parser
->handler
, parser
->options(), false, SyntacticContext::Other
);