js/src/frontend/FoldConstants.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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 }

mercurial