LCOV - app.info - js/src/frontend/FoldConstants.cpp

LCOV - code coverage report
| Current view: | directory - js/src/frontend - FoldConstants.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 470 | 335 | 71.3 %
| Date: | 2012-04-07 | Functions: | 6 | 6 | 100.0 %

           1                 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
           2                 :  * vim: set ts=8 sw=4 et tw=99:
           3                 :  *
           4                 :  * ***** BEGIN LICENSE BLOCK *****
           5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
           6                 :  *
           7                 :  * The contents of this file are subject to the Mozilla Public License Version
           8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
           9                 :  * the License. You may obtain a copy of the License at
          10                 :  * http://www.mozilla.org/MPL/
          11                 :  *
          12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
          13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
          14                 :  * for the specific language governing rights and limitations under the
          15                 :  * License.
          16                 :  *
          17                 :  * The Original Code is Mozilla Communicator client code, released
          18                 :  * March 31, 1998.
          19                 :  *
          20                 :  * The Initial Developer of the Original Code is
          21                 :  * Netscape Communications Corporation.
          22                 :  * Portions created by the Initial Developer are Copyright (C) 1998-2011
          23                 :  * the Initial Developer. All Rights Reserved.
          24                 :  *
          25                 :  * Contributor(s):
          26                 :  *
          27                 :  * Alternatively, the contents of this file may be used under the terms of
          28                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
          29                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
          30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
          31                 :  * of those above. If you wish to allow use of your version of this file only
          32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
          33                 :  * use your version of this file under the terms of the MPL, indicate your
          34                 :  * decision by deleting the provisions above and replace them with the notice
          35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
          36                 :  * the provisions above, a recipient may use your version of this file under
          37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
          38                 :  *
          39                 :  * ***** END LICENSE BLOCK ***** */
          40                 :
          41                 : #include "frontend/FoldConstants.h"
          42                 :
          43                 : #include "jslibmath.h"
          44                 :
          45                 : #include "frontend/BytecodeEmitter.h"
          46                 : #include "frontend/ParseNode.h"
          47                 :
          48                 : #if JS_HAS_XML_SUPPORT
          49                 : #include "jsxml.h"
          50                 : #endif
          51                 :
          52                 : #include "jsatominlines.h"
          53                 :
          54                 : #include "vm/String-inl.h"
          55                 :
          56                 : using namespace js;
          57                 :
          58                 : static ParseNode *
          59         1332423 : ContainsVarOrConst(ParseNode *pn)
          60                 : {
          61         1332423 :     if (!pn)
          62          320469 :         return NULL;
          63         1011954 :     if (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST))
          64            1971 :         return pn;
          65         1009983 :     switch (pn->getArity()) {
          66                 :       case PN_LIST:
          67          343917 :         for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
          68          248022 :             if (ParseNode *pnt = ContainsVarOrConst(pn2))
          69            1854 :                 return pnt;
          70                 :         }
          71           95895 :         break;
          72                 :       case PN_TERNARY:
          73            3285 :         if (ParseNode *pnt = ContainsVarOrConst(pn->pn_kid1))
          74             144 :             return pnt;
          75            3141 :         if (ParseNode *pnt = ContainsVarOrConst(pn->pn_kid2))
          76              27 :             return pnt;
          77            3114 :         return ContainsVarOrConst(pn->pn_kid3);
          78                 :       case PN_BINARY:
          79                 :         /*
          80                 :          * Limit recursion if pn is a binary expression, which can't contain a
          81                 :          * var statement.
          82                 :          */
          83          223283 :         if (!pn->isOp(JSOP_NOP))
          84           11826 :             return NULL;
          85          211457 :         if (ParseNode *pnt = ContainsVarOrConst(pn->pn_left))
          86             144 :             return pnt;
          87          211313 :         return ContainsVarOrConst(pn->pn_right);
          88                 :       case PN_UNARY:
          89          228975 :         if (!pn->isOp(JSOP_NOP))
          90            1840 :             return NULL;
          91          227135 :         return ContainsVarOrConst(pn->pn_kid);
          92                 :       case PN_NAME:
          93          241616 :         return ContainsVarOrConst(pn->maybeExpr());
          94                 :       case PN_NAMESET:
          95               0 :         return ContainsVarOrConst(pn->pn_tree);
          96                 :       default:;
          97                 :     }
          98          310970 :     return NULL;
          99                 : }
         100                 :
         101                 : /*
         102                 :  * Fold from one constant type to another.
         103                 :  * XXX handles only strings and numbers for now
         104                 :  */
         105                 : static JSBool
         106          142086 : FoldType(JSContext *cx, ParseNode *pn, ParseNodeKind kind)
         107                 : {
         108          142086 :     if (!pn->isKind(kind)) {
         109          115015 :         switch (kind) {
         110                 :           case PNK_NUMBER:
         111           87618 :             if (pn->isKind(PNK_STRING)) {
         112                 :                 double d;
         113             279 :                 if (!ToNumber(cx, StringValue(pn->pn_atom), &d))
         114               0 :                     return JS_FALSE;
         115             279 :                 pn->pn_dval = d;
         116             279 :                 pn->setKind(PNK_NUMBER);
         117             279 :                 pn->setOp(JSOP_DOUBLE);
         118                 :             }
         119           87618 :             break;
         120                 :
         121                 :           case PNK_STRING:
         122           27397 :             if (pn->isKind(PNK_NUMBER)) {
         123               9 :                 JSString *str = js_NumberToString(cx, pn->pn_dval);
         124               9 :                 if (!str)
         125               0 :                     return JS_FALSE;
         126               9 :                 pn->pn_atom = js_AtomizeString(cx, str);
         127               9 :                 if (!pn->pn_atom)
         128               0 :                     return JS_FALSE;
         129               9 :                 pn->setKind(PNK_STRING);
         130               9 :                 pn->setOp(JSOP_STRING);
         131                 :             }
         132           27397 :             break;
         133                 :
         134                 :           default:;
         135                 :         }
         136                 :     }
         137          142086 :     return JS_TRUE;
         138                 : }
         139                 :
         140                 : /*
         141                 :  * Fold two numeric constants.  Beware that pn1 and pn2 are recycled, unless
         142                 :  * one of them aliases pn, so you can't safely fetch pn2->pn_next, e.g., after
         143                 :  * a successful call to this function.
         144                 :  */
         145                 : static JSBool
         146             910 : FoldBinaryNumeric(JSContext *cx, JSOp op, ParseNode *pn1, ParseNode *pn2,
         147                 :                   ParseNode *pn, TreeContext *tc)
         148                 : {
         149                 :     double d, d2;
         150                 :     int32_t i, j;
         151                 :
         152             910 :     JS_ASSERT(pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER));
         153             910 :     d = pn1->pn_dval;
         154             910 :     d2 = pn2->pn_dval;
         155             910 :     switch (op) {
         156                 :       case JSOP_LSH:
         157                 :       case JSOP_RSH:
         158              90 :         i = js_DoubleToECMAInt32(d);
         159              90 :         j = js_DoubleToECMAInt32(d2);
         160              90 :         j &= 31;
         161              90 :         d = (op == JSOP_LSH) ? i << j : i >> j;
         162              90 :         break;
         163                 :
         164                 :       case JSOP_URSH:
         165               0 :         j = js_DoubleToECMAInt32(d2);
         166               0 :         j &= 31;
         167               0 :         d = js_DoubleToECMAUint32(d) >> j;
         168               0 :         break;
         169                 :
         170                 :       case JSOP_ADD:
         171              81 :         d += d2;
         172              81 :         break;
         173                 :
         174                 :       case JSOP_SUB:
         175             162 :         d -= d2;
         176             162 :         break;
         177                 :
         178                 :       case JSOP_MUL:
         179             306 :         d *= d2;
         180             306 :         break;
         181                 :
         182                 :       case JSOP_DIV:
         183             271 :         if (d2 == 0) {
         184                 : #if defined(XP_WIN)
         185                 :             /* XXX MSVC miscompiles such that (NaN == 0) */
         186                 :             if (JSDOUBLE_IS_NaN(d2))
         187                 :                 d = js_NaN;
         188                 :             else
         189                 : #endif
         190             244 :             if (d == 0 || JSDOUBLE_IS_NaN(d))
         191              55 :                 d = js_NaN;
         192             189 :             else if (JSDOUBLE_IS_NEG(d) != JSDOUBLE_IS_NEG(d2))
         193              45 :                 d = js_NegativeInfinity;
         194                 :             else
         195             144 :                 d = js_PositiveInfinity;
         196                 :         } else {
         197              27 :             d /= d2;
         198                 :         }
         199             271 :         break;
         200                 :
         201                 :       case JSOP_MOD:
         202               0 :         if (d2 == 0) {
         203               0 :             d = js_NaN;
         204                 :         } else {
         205               0 :             d = js_fmod(d, d2);
         206                 :         }
         207               0 :         break;
         208                 :
         209                 :       default:;
         210                 :     }
         211                 :
         212                 :     /* Take care to allow pn1 or pn2 to alias pn. */
         213             910 :     if (pn1 != pn)
         214             829 :         tc->freeTree(pn1);
         215             910 :     if (pn2 != pn)
         216             910 :         tc->freeTree(pn2);
         217             910 :     pn->setKind(PNK_NUMBER);
         218             910 :     pn->setOp(JSOP_DOUBLE);
         219             910 :     pn->setArity(PN_NULLARY);
         220             910 :     pn->pn_dval = d;
         221             910 :     return JS_TRUE;
         222                 : }
         223                 :
         224                 : #if JS_HAS_XML_SUPPORT
         225                 :
         226                 : static JSBool
         227             570 : FoldXMLConstants(JSContext *cx, ParseNode *pn, TreeContext *tc)
         228                 : {
         229             570 :     JS_ASSERT(pn->isArity(PN_LIST));
         230             570 :     ParseNodeKind kind = pn->getKind();
         231             570 :     ParseNode **pnp = &pn->pn_head;
         232             570 :     ParseNode *pn1 = *pnp;
         233             570 :     JSString *accum = NULL;
         234             570 :     JSString *str = NULL;
         235             570 :     if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
         236             570 :         if (kind == PNK_XMLETAGO)
         237             109 :             accum = cx->runtime->atomState.etagoAtom;
         238             461 :         else if (kind == PNK_XMLSTAGO || kind == PNK_XMLPTAGC)
         239             334 :             accum = cx->runtime->atomState.stagoAtom;
         240                 :     }
         241                 :
         242                 :     /*
         243                 :      * GC Rooting here is tricky: for most of the loop, |accum| is safe via
         244                 :      * the newborn string root. However, when |pn2->getKind()| is PNK_XMLCDATA,
         245                 :      * PNK_XMLCOMMENT, or PNK_XMLPI it is knocked out of the newborn root.
         246                 :      * Therefore, we have to add additonal protection from GC nesting under
         247                 :      * js_ConcatStrings.
         248                 :      */
         249                 :     ParseNode *pn2;
         250                 :     uint32_t i, j;
         251            1330 :     for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) {
         252                 :         /* The parser already rejected end-tags with attributes. */
         253             760 :         JS_ASSERT(kind != PNK_XMLETAGO || i == 0);
         254             760 :         switch (pn2->getKind()) {
         255                 :           case PNK_XMLATTR:
         256               9 :             if (!accum)
         257               0 :                 goto cantfold;
         258                 :             /* FALL THROUGH */
         259                 :           case PNK_XMLNAME:
         260                 :           case PNK_XMLSPACE:
         261                 :           case PNK_XMLTEXT:
         262                 :           case PNK_STRING:
         263             760 :             if (pn2->isArity(PN_LIST))
         264               0 :                 goto cantfold;
         265             760 :             str = pn2->pn_atom;
         266             760 :             break;
         267                 :
         268                 :           case PNK_XMLCDATA:
         269               0 :             str = js_MakeXMLCDATAString(cx, pn2->pn_atom);
         270               0 :             if (!str)
         271               0 :                 return JS_FALSE;
         272               0 :             break;
         273                 :
         274                 :           case PNK_XMLCOMMENT:
         275               0 :             str = js_MakeXMLCommentString(cx, pn2->pn_atom);
         276               0 :             if (!str)
         277               0 :                 return JS_FALSE;
         278               0 :             break;
         279                 :
         280                 :           case PNK_XMLPI: {
         281               0 :             XMLProcessingInstruction &pi = pn2->asXMLProcessingInstruction();
         282               0 :             str = js_MakeXMLPIString(cx, pi.target(), pi.data());
         283               0 :             if (!str)
         284               0 :                 return JS_FALSE;
         285               0 :             break;
         286                 :           }
         287                 :
         288                 :           cantfold:
         289                 :           default:
         290               0 :             JS_ASSERT(*pnp == pn1);
         291               0 :             if ((kind == PNK_XMLSTAGO || kind == PNK_XMLPTAGC) &&
         292                 :                 (i & 1) ^ (j & 1)) {
         293                 : #ifdef DEBUG_brendanXXX
         294                 :                 printf("1: %d, %d => ", i, j);
         295                 :                 if (accum)
         296                 :                     FileEscapedString(stdout, accum, 0);
         297                 :                 else
         298                 :                     fputs("NULL", stdout);
         299                 :                 fputc('\n', stdout);
         300                 : #endif
         301               0 :             } else if (accum && pn1 != pn2) {
         302               0 :                 while (pn1->pn_next != pn2) {
         303               0 :                     pn1 = tc->freeTree(pn1);
         304               0 :                     --pn->pn_count;
         305                 :                 }
         306               0 :                 pn1->setKind(PNK_XMLTEXT);
         307               0 :                 pn1->setOp(JSOP_STRING);
         308               0 :                 pn1->setArity(PN_NULLARY);
         309               0 :                 pn1->pn_atom = js_AtomizeString(cx, accum);
         310               0 :                 if (!pn1->pn_atom)
         311               0 :                     return JS_FALSE;
         312               0 :                 JS_ASSERT(pnp != &pn1->pn_next);
         313               0 :                 *pnp = pn1;
         314                 :             }
         315               0 :             pnp = &pn2->pn_next;
         316               0 :             pn1 = *pnp;
         317               0 :             accum = NULL;
         318               0 :             continue;
         319                 :         }
         320                 :
         321             760 :         if (accum) {
         322                 :             {
         323                 :                 str = ((kind == PNK_XMLSTAGO || kind == PNK_XMLPTAGC) && i != 0)
         324              18 :                       ? js_AddAttributePart(cx, i & 1, accum, str)
         325             651 :                       : js_ConcatStrings(cx, accum, str);
         326                 :             }
         327             633 :             if (!str)
         328               0 :                 return JS_FALSE;
         329                 : #ifdef DEBUG_brendanXXX
         330                 :             printf("2: %d, %d => ", i, j);
         331                 :             FileEscapedString(stdout, str, 0);
         332                 :             printf(" (%u)\n", str->length());
         333                 : #endif
         334             633 :             ++j;
         335                 :         }
         336             760 :         accum = str;
         337                 :     }
         338                 :
         339             570 :     if (accum) {
         340             570 :         str = NULL;
         341             570 :         if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
         342             570 :             if (kind == PNK_XMLPTAGC)
         343             225 :                 str = cx->runtime->atomState.ptagcAtom;
         344             345 :             else if (kind == PNK_XMLSTAGO || kind == PNK_XMLETAGO)
         345             218 :                 str = cx->runtime->atomState.tagcAtom;
         346                 :         }
         347             570 :         if (str) {
         348             443 :             accum = js_ConcatStrings(cx, accum, str);
         349             443 :             if (!accum)
         350               0 :                 return JS_FALSE;
         351                 :         }
         352                 :
         353             570 :         JS_ASSERT(*pnp == pn1);
         354            1330 :         while (pn1->pn_next) {
         355             190 :             pn1 = tc->freeTree(pn1);
         356             190 :             --pn->pn_count;
         357                 :         }
         358             570 :         pn1->setKind(PNK_XMLTEXT);
         359             570 :         pn1->setOp(JSOP_STRING);
         360             570 :         pn1->setArity(PN_NULLARY);
         361             570 :         pn1->pn_atom = js_AtomizeString(cx, accum);
         362             570 :         if (!pn1->pn_atom)
         363               0 :             return JS_FALSE;
         364             570 :         JS_ASSERT(pnp != &pn1->pn_next);
         365             570 :         *pnp = pn1;
         366                 :     }
         367                 :
         368             570 :     if (pn1 && pn->pn_count == 1) {
         369                 :         /*
         370                 :          * Only one node under pn, and it has been folded: move pn1 onto pn
         371                 :          * unless pn is an XML root (in which case we need it to tell the code
         372                 :          * generator to emit a JSOP_TOXML or JSOP_TOXMLLIST op).  If pn is an
         373                 :          * XML root *and* it's a point-tag, rewrite it to PNK_XMLELEM to avoid
         374                 :          * extra "<" and "/>" bracketing at runtime.
         375                 :          */
         376             570 :         if (!(pn->pn_xflags & PNX_XMLROOT)) {
         377             272 :             pn->become(pn1);
         378             298 :         } else if (kind == PNK_XMLPTAGC) {
         379             180 :             pn->setKind(PNK_XMLELEM);
         380             180 :             pn->setOp(JSOP_TOXML);
         381                 :         }
         382                 :     }
         383             570 :     return JS_TRUE;
         384                 : }
         385                 :
         386                 : #endif /* JS_HAS_XML_SUPPORT */
         387                 :
         388                 : enum Truthiness { Truthy, Falsy, Unknown };
         389                 :
         390                 : static Truthiness
         391          203814 : Boolish(ParseNode *pn)
         392                 : {
         393          203814 :     switch (pn->getOp()) {
         394                 :       case JSOP_DOUBLE:
         395              91 :         return (pn->pn_dval != 0 && !JSDOUBLE_IS_NaN(pn->pn_dval)) ? Truthy : Falsy;
         396                 :
         397                 :       case JSOP_STRING:
         398               0 :         return (pn->pn_atom->length() > 0) ? Truthy : Falsy;
         399                 :
         400                 : #if JS_HAS_GENERATOR_EXPRS
         401                 :       case JSOP_CALL:
         402                 :       {
         403                 :         /*
         404                 :          * A generator expression as an if or loop condition has no effects, it
         405                 :          * simply results in a truthy object reference. This condition folding
         406                 :          * is needed for the decompiler. See bug 442342 and bug 443074.
         407                 :          */
         408            1134 :         if (pn->pn_count != 1)
         409             837 :             return Unknown;
         410             297 :         ParseNode *pn2 = pn->pn_head;
         411             297 :         if (!pn2->isKind(PNK_FUNCTION))
         412             297 :             return Unknown;
         413               0 :         if (!(pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA))
         414               0 :             return Unknown;
         415               0 :         return Truthy;
         416                 :       }
         417                 : #endif
         418                 :
         419                 :       case JSOP_DEFFUN:
         420                 :       case JSOP_LAMBDA:
         421                 :       case JSOP_TRUE:
         422             369 :         return Truthy;
         423                 :
         424                 :       case JSOP_NULL:
         425                 :       case JSOP_FALSE:
         426              63 :         return Falsy;
         427                 :
         428                 :       default:
         429          202157 :         return Unknown;
         430                 :     }
         431                 : }
         432                 :
         433                 : bool
         434         7279199 : js::FoldConstants(JSContext *cx, ParseNode *pn, TreeContext *tc, bool inCond)
         435                 : {
         436         7279199 :     ParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
         437                 :
         438         7279199 :     JS_CHECK_RECURSION(cx, return false);
         439                 :
         440         7279199 :     switch (pn->getArity()) {
         441                 :       case PN_FUNC:
         442                 :       {
         443          150421 :         uint32_t oldflags = tc->flags;
         444          150421 :         FunctionBox *oldlist = tc->functionList;
         445                 :
         446          150421 :         tc->flags = pn->pn_funbox->tcflags;
         447          150421 :         tc->functionList = pn->pn_funbox->kids;
         448          150421 :         if (!FoldConstants(cx, pn->pn_body, tc))
         449               0 :             return false;
         450          150421 :         pn->pn_funbox->kids = tc->functionList;
         451          150421 :         tc->flags = oldflags;
         452          150421 :         tc->functionList = oldlist;
         453          150421 :         break;
         454                 :       }
         455                 :
         456                 :       case PN_LIST:
         457                 :       {
         458                 :         /* Propagate inCond through logical connectives. */
         459         1002433 :         bool cond = inCond && (pn->isKind(PNK_OR) || pn->isKind(PNK_AND));
         460                 :
         461                 :         /* Don't fold a parenthesized call expression. See bug 537673. */
         462         1002433 :         pn1 = pn2 = pn->pn_head;
         463         1002433 :         if ((pn->isKind(PNK_LP) || pn->isKind(PNK_NEW)) && pn2->isInParens())
         464            2813 :             pn2 = pn2->pn_next;
         465                 :
         466                 :         /* Save the list head in pn1 for later use. */
         467         4572552 :         for (; pn2; pn2 = pn2->pn_next) {
         468         3570119 :             if (!FoldConstants(cx, pn2, tc, cond))
         469               0 :                 return false;
         470                 :         }
         471         1002433 :         break;
         472                 :       }
         473                 :
         474                 :       case PN_TERNARY:
         475                 :         /* Any kid may be null (e.g. for (;;)). */
         476          301696 :         pn1 = pn->pn_kid1;
         477          301696 :         pn2 = pn->pn_kid2;
         478          301696 :         pn3 = pn->pn_kid3;
         479          301696 :         if (pn1 && !FoldConstants(cx, pn1, tc, pn->isKind(PNK_IF)))
         480               0 :             return false;
         481          301696 :         if (pn2) {
         482          214351 :             if (!FoldConstants(cx, pn2, tc, pn->isKind(PNK_FORHEAD)))
         483               0 :                 return false;
         484          214351 :             if (pn->isKind(PNK_FORHEAD) && pn2->isOp(JSOP_TRUE)) {
         485               9 :                 tc->freeTree(pn2);
         486               9 :                 pn->pn_kid2 = NULL;
         487                 :             }
         488                 :         }
         489          301696 :         if (pn3 && !FoldConstants(cx, pn3, tc))
         490               0 :             return false;
         491          301696 :         break;
         492                 :
         493                 :       case PN_BINARY:
         494          723775 :         pn1 = pn->pn_left;
         495          723775 :         pn2 = pn->pn_right;
         496                 :
         497                 :         /* Propagate inCond through logical connectives. */
         498          723775 :         if (pn->isKind(PNK_OR) || pn->isKind(PNK_AND)) {
         499            6120 :             if (!FoldConstants(cx, pn1, tc, inCond))
         500               0 :                 return false;
         501            6120 :             if (!FoldConstants(cx, pn2, tc, inCond))
         502               0 :                 return false;
         503            6120 :             break;
         504                 :         }
         505                 :
         506                 :         /* First kid may be null (for default case in switch). */
         507          717655 :         if (pn1 && !FoldConstants(cx, pn1, tc, pn->isKind(PNK_WHILE)))
         508               0 :             return false;
         509          717655 :         if (!FoldConstants(cx, pn2, tc, pn->isKind(PNK_DOWHILE)))
         510               0 :             return false;
         511          717655 :         break;
         512                 :
         513                 :       case PN_UNARY:
         514          733176 :         pn1 = pn->pn_kid;
         515                 :
         516                 :         /*
         517                 :          * Kludge to deal with typeof expressions: because constant folding
         518                 :          * can turn an expression into a name node, we have to check here,
         519                 :          * before folding, to see if we should throw undefined name errors.
         520                 :          *
         521                 :          * NB: We know that if pn->pn_op is JSOP_TYPEOF, pn1 will not be
         522                 :          * null. This assumption does not hold true for other unary
         523                 :          * expressions.
         524                 :          */
         525          733176 :         if (pn->isOp(JSOP_TYPEOF) && !pn1->isKind(PNK_NAME))
         526             594 :             pn->setOp(JSOP_TYPEOFEXPR);
         527                 :
         528          733176 :         if (pn1 && !FoldConstants(cx, pn1, tc, pn->isOp(JSOP_NOT)))
         529               0 :             return false;
         530          733176 :         break;
         531                 :
         532                 :       case PN_NAME:
         533                 :         /*
         534                 :          * Skip pn1 down along a chain of dotted member expressions to avoid
         535                 :          * excessive recursion.  Our only goal here is to fold constants (if
         536                 :          * any) in the primary expression operand to the left of the first
         537                 :          * dot in the chain.
         538                 :          */
         539         3465323 :         if (!pn->isUsed()) {
         540          989761 :             pn1 = pn->pn_expr;
         541         1996722 :             while (pn1 && pn1->isArity(PN_NAME) && !pn1->isUsed())
         542           17200 :                 pn1 = pn1->pn_expr;
         543          989761 :             if (pn1 && !FoldConstants(cx, pn1, tc))
         544               0 :                 return false;
         545                 :         }
         546         3465323 :         break;
         547                 :
         548                 :       case PN_NAMESET:
         549           57683 :         pn1 = pn->pn_tree;
         550           57683 :         if (!FoldConstants(cx, pn1, tc))
         551               0 :             return false;
         552           57683 :         break;
         553                 :
         554                 :       case PN_NULLARY:
         555          844692 :         break;
         556                 :     }
         557                 :
         558         7279199 :     switch (pn->getKind()) {
         559                 :       case PNK_IF:
         560           92588 :         if (ContainsVarOrConst(pn2) || ContainsVarOrConst(pn3))
         561            1971 :             break;
         562                 :         /* FALL THROUGH */
         563                 :
         564                 :       case PNK_CONDITIONAL:
         565                 :         /* Reduce 'if (C) T; else E' into T for true C, E for false. */
         566           97117 :         switch (pn1->getKind()) {
         567                 :           case PNK_NUMBER:
         568               0 :             if (pn1->pn_dval == 0 || JSDOUBLE_IS_NaN(pn1->pn_dval))
         569               0 :                 pn2 = pn3;
         570               0 :             break;
         571                 :           case PNK_STRING:
         572               0 :             if (pn1->pn_atom->length() == 0)
         573               0 :                 pn2 = pn3;
         574               0 :             break;
         575                 :           case PNK_TRUE:
         576              36 :             break;
         577                 :           case PNK_FALSE:
         578                 :           case PNK_NULL:
         579              45 :             pn2 = pn3;
         580              45 :             break;
         581                 :           default:
         582                 :             /* Early return to dodge common code that copies pn2 to pn. */
         583           97036 :             return true;
         584                 :         }
         585                 :
         586                 : #if JS_HAS_GENERATOR_EXPRS
         587                 :         /* Don't fold a trailing |if (0)| in a generator expression. */
         588              81 :         if (!pn2 && (tc->flags & TCF_GENEXP_LAMBDA))
         589               0 :             break;
         590                 : #endif
         591                 :
         592              81 :         if (pn2 && !pn2->isDefn())
         593              36 :             pn->become(pn2);
         594              81 :         if (!pn2 || (pn->isKind(PNK_SEMI) && !pn->pn_kid)) {
         595                 :             /*
         596                 :              * False condition and no else, or an empty then-statement was
         597                 :              * moved up over pn.  Either way, make pn an empty block (not an
         598                 :              * empty statement, which does not decompile, even when labeled).
         599                 :              * NB: pn must be a PNK_IF as PNK_CONDITIONAL can never have a null
         600                 :              * kid or an empty statement for a child.
         601                 :              */
         602              45 :             pn->setKind(PNK_STATEMENTLIST);
         603              45 :             pn->setArity(PN_LIST);
         604              45 :             pn->makeEmpty();
         605                 :         }
         606              81 :         tc->freeTree(pn2);
         607              81 :         if (pn3 && pn3 != pn2)
         608               0 :             tc->freeTree(pn3);
         609              81 :         break;
         610                 :
         611                 :       case PNK_OR:
         612                 :       case PNK_AND:
         613            6363 :         if (inCond) {
         614            2601 :             if (pn->isArity(PN_LIST)) {
         615             144 :                 ParseNode **pnp = &pn->pn_head;
         616             144 :                 JS_ASSERT(*pnp == pn1);
         617             468 :                 do {
         618             468 :                     Truthiness t = Boolish(pn1);
         619             468 :                     if (t == Unknown) {
         620             468 :                         pnp = &pn1->pn_next;
         621             468 :                         continue;
         622                 :                     }
         623               0 :                     if ((t == Truthy) == pn->isKind(PNK_OR)) {
         624               0 :                         for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
         625               0 :                             pn3 = pn2->pn_next;
         626               0 :                             tc->freeTree(pn2);
         627               0 :                             --pn->pn_count;
         628                 :                         }
         629               0 :                         pn1->pn_next = NULL;
         630               0 :                         break;
         631                 :                     }
         632               0 :                     JS_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
         633               0 :                     if (pn->pn_count == 1)
         634               0 :                         break;
         635               0 :                     *pnp = pn1->pn_next;
         636               0 :                     tc->freeTree(pn1);
         637               0 :                     --pn->pn_count;
         638                 :                 } while ((pn1 = *pnp) != NULL);
         639                 :
         640                 :                 // We may have to change arity from LIST to BINARY.
         641             144 :                 pn1 = pn->pn_head;
         642             144 :                 if (pn->pn_count == 2) {
         643               0 :                     pn2 = pn1->pn_next;
         644               0 :                     pn1->pn_next = NULL;
         645               0 :                     JS_ASSERT(!pn2->pn_next);
         646               0 :                     pn->setArity(PN_BINARY);
         647               0 :                     pn->pn_left = pn1;
         648               0 :                     pn->pn_right = pn2;
         649             144 :                 } else if (pn->pn_count == 1) {
         650               0 :                     pn->become(pn1);
         651               0 :                     tc->freeTree(pn1);
         652                 :                 }
         653                 :             } else {
         654            2457 :                 Truthiness t = Boolish(pn1);
         655            2457 :                 if (t != Unknown) {
         656               9 :                     if ((t == Truthy) == pn->isKind(PNK_OR)) {
         657               9 :                         tc->freeTree(pn2);
         658               9 :                         pn->become(pn1);
         659                 :                     } else {
         660               0 :                         JS_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
         661               0 :                         tc->freeTree(pn1);
         662               0 :                         pn->become(pn2);
         663                 :                     }
         664                 :                 }
         665                 :             }
         666                 :         }
         667            6363 :         break;
         668                 :
         669                 :       case PNK_SUBASSIGN:
         670                 :       case PNK_BITORASSIGN:
         671                 :       case PNK_BITXORASSIGN:
         672                 :       case PNK_BITANDASSIGN:
         673                 :       case PNK_LSHASSIGN:
         674                 :       case PNK_RSHASSIGN:
         675                 :       case PNK_URSHASSIGN:
         676                 :       case PNK_MULASSIGN:
         677                 :       case PNK_DIVASSIGN:
         678                 :       case PNK_MODASSIGN:
         679                 :         /*
         680                 :          * Compound operators such as *= should be subject to folding, in case
         681                 :          * the left-hand side is constant, and so that the decompiler produces
         682                 :          * the same string that you get from decompiling a script or function
         683                 :          * compiled from that same string.  += is special and so must be
         684                 :          * handled below.
         685                 :          */
         686            1008 :         goto do_binary_op;
         687                 :
         688                 :       case PNK_ADDASSIGN:
         689           27122 :         JS_ASSERT(pn->isOp(JSOP_ADD));
         690                 :         /* FALL THROUGH */
         691                 :       case PNK_ADD:
         692           67642 :         if (pn->isArity(PN_LIST)) {
         693                 :             /*
         694                 :              * Any string literal term with all others number or string means
         695                 :              * this is a concatenation.  If any term is not a string or number
         696                 :              * literal, we can't fold.
         697                 :              */
         698            5992 :             JS_ASSERT(pn->pn_count > 2);
         699            5992 :             if (pn->pn_xflags & PNX_CANTFOLD)
         700            5191 :                 return true;
         701             801 :             if (pn->pn_xflags != PNX_STRCAT)
         702               0 :                 goto do_binary_op;
         703                 :
         704                 :             /* Ok, we're concatenating: convert non-string constant operands. */
         705             801 :             size_t length = 0;
         706            4905 :             for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
         707            4104 :                 if (!FoldType(cx, pn2, PNK_STRING))
         708               0 :                     return false;
         709                 :                 /* XXX fold only if all operands convert to string */
         710            4104 :                 if (!pn2->isKind(PNK_STRING))
         711               0 :                     return true;
         712            4104 :                 length += pn2->pn_atom->length();
         713                 :             }
         714                 :
         715                 :             /* Allocate a new buffer and string descriptor for the result. */
         716             801 :             jschar *chars = (jschar *) cx->malloc_((length + 1) * sizeof(jschar));
         717             801 :             if (!chars)
         718               0 :                 return false;
         719             801 :             chars[length] = 0;
         720             801 :             JSString *str = js_NewString(cx, chars, length);
         721             801 :             if (!str) {
         722               0 :                 cx->free_(chars);
         723               0 :                 return false;
         724                 :             }
         725                 :
         726                 :             /* Fill the buffer, advancing chars and recycling kids as we go. */
         727            4905 :             for (pn2 = pn1; pn2; pn2 = tc->freeTree(pn2)) {
         728            4104 :                 JSAtom *atom = pn2->pn_atom;
         729            4104 :                 size_t length2 = atom->length();
         730            4104 :                 js_strncpy(chars, atom->chars(), length2);
         731            4104 :                 chars += length2;
         732                 :             }
         733             801 :             JS_ASSERT(*chars == 0);
         734                 :
         735                 :             /* Atomize the result string and mutate pn to refer to it. */
         736             801 :             pn->pn_atom = js_AtomizeString(cx, str);
         737             801 :             if (!pn->pn_atom)
         738               0 :                 return false;
         739             801 :             pn->setKind(PNK_STRING);
         740             801 :             pn->setOp(JSOP_STRING);
         741             801 :             pn->setArity(PN_NULLARY);
         742             801 :             break;
         743                 :         }
         744                 :
         745                 :         /* Handle a binary string concatenation. */
         746           61650 :         JS_ASSERT(pn->isArity(PN_BINARY));
         747           61650 :         if (pn1->isKind(PNK_STRING) || pn2->isKind(PNK_STRING)) {
         748                 :             JSString *left, *right, *str;
         749                 :
         750           27730 :             if (!FoldType(cx, !pn1->isKind(PNK_STRING) ? pn1 : pn2, PNK_STRING))
         751               0 :                 return false;
         752           27730 :             if (!pn1->isKind(PNK_STRING) || !pn2->isKind(PNK_STRING))
         753           27388 :                 return true;
         754             342 :             left = pn1->pn_atom;
         755             342 :             right = pn2->pn_atom;
         756             342 :             str = js_ConcatStrings(cx, left, right);
         757             342 :             if (!str)
         758               0 :                 return false;
         759             342 :             pn->pn_atom = js_AtomizeString(cx, str);
         760             342 :             if (!pn->pn_atom)
         761               0 :                 return false;
         762             342 :             pn->setKind(PNK_STRING);
         763             342 :             pn->setOp(JSOP_STRING);
         764             342 :             pn->setArity(PN_NULLARY);
         765             342 :             tc->freeTree(pn1);
         766             342 :             tc->freeTree(pn2);
         767             342 :             break;
         768                 :         }
         769                 :
         770                 :         /* Can't concatenate string literals, let's try numbers. */
         771           33920 :         goto do_binary_op;
         772                 :
         773                 :       case PNK_SUB:
         774                 :       case PNK_STAR:
         775                 :       case PNK_LSH:
         776                 :       case PNK_RSH:
         777                 :       case PNK_URSH:
         778                 :       case PNK_DIV:
         779                 :       case PNK_MOD:
         780                 :       do_binary_op:
         781           54874 :         if (pn->isArity(PN_LIST)) {
         782             324 :             JS_ASSERT(pn->pn_count > 2);
         783            1476 :             for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
         784            1152 :                 if (!FoldType(cx, pn2, PNK_NUMBER))
         785               0 :                     return false;
         786                 :             }
         787             576 :             for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
         788                 :                 /* XXX fold only if all operands convert to number */
         789             540 :                 if (!pn2->isKind(PNK_NUMBER))
         790             288 :                     break;
         791                 :             }
         792             324 :             if (!pn2) {
         793              36 :                 JSOp op = pn->getOp();
         794                 :
         795              36 :                 pn2 = pn1->pn_next;
         796              36 :                 pn3 = pn2->pn_next;
         797              36 :                 if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
         798               0 :                     return false;
         799             153 :                 while ((pn2 = pn3) != NULL) {
         800              81 :                     pn3 = pn2->pn_next;
         801              81 :                     if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
         802               0 :                         return false;
         803                 :                 }
         804                 :             }
         805                 :         } else {
         806           54550 :             JS_ASSERT(pn->isArity(PN_BINARY));
         807          109100 :             if (!FoldType(cx, pn1, PNK_NUMBER) ||
         808           54550 :                 !FoldType(cx, pn2, PNK_NUMBER)) {
         809               0 :                 return false;
         810                 :             }
         811           54550 :             if (pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER)) {
         812             793 :                 if (!FoldBinaryNumeric(cx, pn->getOp(), pn1, pn2, pn, tc))
         813               0 :                     return false;
         814                 :             }
         815                 :         }
         816           54874 :         break;
         817                 :
         818                 :       case PNK_TYPEOF:
         819                 :       case PNK_VOID:
         820                 :       case PNK_NOT:
         821                 :       case PNK_BITNOT:
         822                 :       case PNK_POS:
         823                 :       case PNK_NEG:
         824          109633 :         if (pn1->isKind(PNK_NUMBER)) {
         825                 :             double d;
         826                 :
         827                 :             /* Operate on one numeric constant. */
         828           24734 :             d = pn1->pn_dval;
         829           24734 :             switch (pn->getOp()) {
         830                 :               case JSOP_BITNOT:
         831               0 :                 d = ~js_DoubleToECMAInt32(d);
         832               0 :                 break;
         833                 :
         834                 :               case JSOP_NEG:
         835           23564 :                 d = -d;
         836           23564 :                 break;
         837                 :
         838                 :               case JSOP_POS:
         839               0 :                 break;
         840                 :
         841                 :               case JSOP_NOT:
         842               0 :                 if (d == 0 || JSDOUBLE_IS_NaN(d)) {
         843               0 :                     pn->setKind(PNK_TRUE);
         844               0 :                     pn->setOp(JSOP_TRUE);
         845                 :                 } else {
         846               0 :                     pn->setKind(PNK_FALSE);
         847               0 :                     pn->setOp(JSOP_FALSE);
         848                 :                 }
         849               0 :                 pn->setArity(PN_NULLARY);
         850                 :                 /* FALL THROUGH */
         851                 :
         852                 :               default:
         853                 :                 /* Return early to dodge the common PNK_NUMBER code. */
         854            1170 :                 return true;
         855                 :             }
         856           23564 :             pn->setKind(PNK_NUMBER);
         857           23564 :             pn->setOp(JSOP_DOUBLE);
         858           23564 :             pn->setArity(PN_NULLARY);
         859           23564 :             pn->pn_dval = d;
         860           23564 :             tc->freeTree(pn1);
         861           84899 :         } else if (pn1->isKind(PNK_TRUE) || pn1->isKind(PNK_FALSE)) {
         862              27 :             if (pn->isOp(JSOP_NOT)) {
         863               9 :                 pn->become(pn1);
         864               9 :                 if (pn->isKind(PNK_TRUE)) {
         865               0 :                     pn->setKind(PNK_FALSE);
         866               0 :                     pn->setOp(JSOP_FALSE);
         867                 :                 } else {
         868               9 :                     pn->setKind(PNK_TRUE);
         869               9 :                     pn->setOp(JSOP_TRUE);
         870                 :                 }
         871               9 :                 tc->freeTree(pn1);
         872                 :             }
         873                 :         }
         874          108463 :         break;
         875                 :
         876                 : #if JS_HAS_XML_SUPPORT
         877                 :       case PNK_XMLELEM:
         878                 :       case PNK_XMLLIST:
         879                 :       case PNK_XMLPTAGC:
         880                 :       case PNK_XMLSTAGO:
         881                 :       case PNK_XMLETAGO:
         882                 :       case PNK_XMLNAME:
         883            1022 :         if (pn->isArity(PN_LIST)) {
         884             570 :             JS_ASSERT(pn->isKind(PNK_XMLLIST) || pn->pn_count != 0);
         885             570 :             if (!FoldXMLConstants(cx, pn, tc))
         886               0 :                 return false;
         887                 :         }
         888            1022 :         break;
         889                 :
         890                 :       case PNK_AT:
         891               9 :         if (pn1->isKind(PNK_XMLNAME)) {
         892               0 :             Value v = StringValue(pn1->pn_atom);
         893               0 :             if (!js_ToAttributeName(cx, &v))
         894               0 :                 return false;
         895               0 :             JS_ASSERT(v.isObject());
         896                 :
         897               0 :             ObjectBox *xmlbox = tc->parser->newObjectBox(&v.toObject());
         898               0 :             if (!xmlbox)
         899               0 :                 return false;
         900                 :
         901               0 :             pn->setKind(PNK_XMLNAME);
         902               0 :             pn->setOp(JSOP_OBJECT);
         903               0 :             pn->setArity(PN_NULLARY);
         904               0 :             pn->pn_objbox = xmlbox;
         905               0 :             tc->freeTree(pn1);
         906                 :         }
         907               9 :         break;
         908                 : #endif /* JS_HAS_XML_SUPPORT */
         909                 :
         910                 :       default:;
         911                 :     }
         912                 :
         913         7148414 :     if (inCond) {
         914          200889 :         Truthiness t = Boolish(pn);
         915          200889 :         if (t != Unknown) {
         916                 :             /*
         917                 :              * We can turn function nodes into constant nodes here, but mutating function
         918                 :              * nodes is tricky --- in particular, mutating a function node that appears on
         919                 :              * a method list corrupts the method list. However, methods are M's in
         920                 :              * statements of the form 'this.foo = M;', which we never fold, so we're okay.
         921                 :              */
         922             514 :             tc->parser->allocator.prepareNodeForMutation(pn);
         923             514 :             if (t == Truthy) {
         924             397 :                 pn->setKind(PNK_TRUE);
         925             397 :                 pn->setOp(JSOP_TRUE);
         926                 :             } else {
         927             117 :                 pn->setKind(PNK_FALSE);
         928             117 :                 pn->setOp(JSOP_FALSE);
         929                 :             }
         930             514 :             pn->setArity(PN_NULLARY);
         931                 :         }
         932                 :     }
         933                 :
         934         7148414 :     return true;
         935                 : }

Generated by: LCOV version 1.7