PageRenderTime 80ms CodeModel.GetById 14ms app.highlight 58ms RepoModel.GetById 1ms app.codeStats 0ms

/js/src/frontend/FoldConstants.cpp

http://github.com/zpao/v8monkey
C++ | 935 lines | 710 code | 87 blank | 138 comment | 250 complexity | 2df0141c2e3eb8ce53a0c0d56461e7ae MD5 | raw file
  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
 56using namespace js;
 57
 58static ParseNode *
 59ContainsVarOrConst(ParseNode *pn)
 60{
 61    if (!pn)
 62        return NULL;
 63    if (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST))
 64        return pn;
 65    switch (pn->getArity()) {
 66      case PN_LIST:
 67        for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
 68            if (ParseNode *pnt = ContainsVarOrConst(pn2))
 69                return pnt;
 70        }
 71        break;
 72      case PN_TERNARY:
 73        if (ParseNode *pnt = ContainsVarOrConst(pn->pn_kid1))
 74            return pnt;
 75        if (ParseNode *pnt = ContainsVarOrConst(pn->pn_kid2))
 76            return pnt;
 77        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        if (!pn->isOp(JSOP_NOP))
 84            return NULL;
 85        if (ParseNode *pnt = ContainsVarOrConst(pn->pn_left))
 86            return pnt;
 87        return ContainsVarOrConst(pn->pn_right);
 88      case PN_UNARY:
 89        if (!pn->isOp(JSOP_NOP))
 90            return NULL;
 91        return ContainsVarOrConst(pn->pn_kid);
 92      case PN_NAME:
 93        return ContainsVarOrConst(pn->maybeExpr());
 94      case PN_NAMESET:
 95        return ContainsVarOrConst(pn->pn_tree);
 96      default:;
 97    }
 98    return NULL;
 99}
100
101/*
102 * Fold from one constant type to another.
103 * XXX handles only strings and numbers for now
104 */
105static JSBool
106FoldType(JSContext *cx, ParseNode *pn, ParseNodeKind kind)
107{
108    if (!pn->isKind(kind)) {
109        switch (kind) {
110          case PNK_NUMBER:
111            if (pn->isKind(PNK_STRING)) {
112                jsdouble d;
113                if (!ToNumber(cx, StringValue(pn->pn_atom), &d))
114                    return JS_FALSE;
115                pn->pn_dval = d;
116                pn->setKind(PNK_NUMBER);
117                pn->setOp(JSOP_DOUBLE);
118            }
119            break;
120
121          case PNK_STRING:
122            if (pn->isKind(PNK_NUMBER)) {
123                JSString *str = js_NumberToString(cx, pn->pn_dval);
124                if (!str)
125                    return JS_FALSE;
126                pn->pn_atom = js_AtomizeString(cx, str);
127                if (!pn->pn_atom)
128                    return JS_FALSE;
129                pn->setKind(PNK_STRING);
130                pn->setOp(JSOP_STRING);
131            }
132            break;
133
134          default:;
135        }
136    }
137    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 */
145static JSBool
146FoldBinaryNumeric(JSContext *cx, JSOp op, ParseNode *pn1, ParseNode *pn2,
147                  ParseNode *pn, TreeContext *tc)
148{
149    jsdouble d, d2;
150    int32_t i, j;
151
152    JS_ASSERT(pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER));
153    d = pn1->pn_dval;
154    d2 = pn2->pn_dval;
155    switch (op) {
156      case JSOP_LSH:
157      case JSOP_RSH:
158        i = js_DoubleToECMAInt32(d);
159        j = js_DoubleToECMAInt32(d2);
160        j &= 31;
161        d = (op == JSOP_LSH) ? i << j : i >> j;
162        break;
163
164      case JSOP_URSH:
165        j = js_DoubleToECMAInt32(d2);
166        j &= 31;
167        d = js_DoubleToECMAUint32(d) >> j;
168        break;
169
170      case JSOP_ADD:
171        d += d2;
172        break;
173
174      case JSOP_SUB:
175        d -= d2;
176        break;
177
178      case JSOP_MUL:
179        d *= d2;
180        break;
181
182      case JSOP_DIV:
183        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            if (d == 0 || JSDOUBLE_IS_NaN(d))
191                d = js_NaN;
192            else if (JSDOUBLE_IS_NEG(d) != JSDOUBLE_IS_NEG(d2))
193                d = js_NegativeInfinity;
194            else
195                d = js_PositiveInfinity;
196        } else {
197            d /= d2;
198        }
199        break;
200
201      case JSOP_MOD:
202        if (d2 == 0) {
203            d = js_NaN;
204        } else {
205            d = js_fmod(d, d2);
206        }
207        break;
208
209      default:;
210    }
211
212    /* Take care to allow pn1 or pn2 to alias pn. */
213    if (pn1 != pn)
214        tc->freeTree(pn1);
215    if (pn2 != pn)
216        tc->freeTree(pn2);
217    pn->setKind(PNK_NUMBER);
218    pn->setOp(JSOP_DOUBLE);
219    pn->setArity(PN_NULLARY);
220    pn->pn_dval = d;
221    return JS_TRUE;
222}
223
224#if JS_HAS_XML_SUPPORT
225
226static JSBool
227FoldXMLConstants(JSContext *cx, ParseNode *pn, TreeContext *tc)
228{
229    JS_ASSERT(pn->isArity(PN_LIST));
230    ParseNodeKind kind = pn->getKind();
231    ParseNode **pnp = &pn->pn_head;
232    ParseNode *pn1 = *pnp;
233    JSString *accum = NULL;
234    JSString *str = NULL;
235    if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
236        if (kind == PNK_XMLETAGO)
237            accum = cx->runtime->atomState.etagoAtom;
238        else if (kind == PNK_XMLSTAGO || kind == PNK_XMLPTAGC)
239            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    for (pn2 = pn1, i = j = 0; pn2; pn2 = pn2->pn_next, i++) {
252        /* The parser already rejected end-tags with attributes. */
253        JS_ASSERT(kind != PNK_XMLETAGO || i == 0);
254        switch (pn2->getKind()) {
255          case PNK_XMLATTR:
256            if (!accum)
257                goto cantfold;
258            /* FALL THROUGH */
259          case PNK_XMLNAME:
260          case PNK_XMLSPACE:
261          case PNK_XMLTEXT:
262          case PNK_STRING:
263            if (pn2->isArity(PN_LIST))
264                goto cantfold;
265            str = pn2->pn_atom;
266            break;
267
268          case PNK_XMLCDATA:
269            str = js_MakeXMLCDATAString(cx, pn2->pn_atom);
270            if (!str)
271                return JS_FALSE;
272            break;
273
274          case PNK_XMLCOMMENT:
275            str = js_MakeXMLCommentString(cx, pn2->pn_atom);
276            if (!str)
277                return JS_FALSE;
278            break;
279
280          case PNK_XMLPI: {
281            XMLProcessingInstruction &pi = pn2->asXMLProcessingInstruction();
282            str = js_MakeXMLPIString(cx, pi.target(), pi.data());
283            if (!str)
284                return JS_FALSE;
285            break;
286          }
287
288          cantfold:
289          default:
290            JS_ASSERT(*pnp == pn1);
291            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            } else if (accum && pn1 != pn2) {
302                while (pn1->pn_next != pn2) {
303                    pn1 = tc->freeTree(pn1);
304                    --pn->pn_count;
305                }
306                pn1->setKind(PNK_XMLTEXT);
307                pn1->setOp(JSOP_STRING);
308                pn1->setArity(PN_NULLARY);
309                pn1->pn_atom = js_AtomizeString(cx, accum);
310                if (!pn1->pn_atom)
311                    return JS_FALSE;
312                JS_ASSERT(pnp != &pn1->pn_next);
313                *pnp = pn1;
314            }
315            pnp = &pn2->pn_next;
316            pn1 = *pnp;
317            accum = NULL;
318            continue;
319        }
320
321        if (accum) {
322            {
323                str = ((kind == PNK_XMLSTAGO || kind == PNK_XMLPTAGC) && i != 0)
324                      ? js_AddAttributePart(cx, i & 1, accum, str)
325                      : js_ConcatStrings(cx, accum, str);
326            }
327            if (!str)
328                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            ++j;
335        }
336        accum = str;
337    }
338
339    if (accum) {
340        str = NULL;
341        if ((pn->pn_xflags & PNX_CANTFOLD) == 0) {
342            if (kind == PNK_XMLPTAGC)
343                str = cx->runtime->atomState.ptagcAtom;
344            else if (kind == PNK_XMLSTAGO || kind == PNK_XMLETAGO)
345                str = cx->runtime->atomState.tagcAtom;
346        }
347        if (str) {
348            accum = js_ConcatStrings(cx, accum, str);
349            if (!accum)
350                return JS_FALSE;
351        }
352
353        JS_ASSERT(*pnp == pn1);
354        while (pn1->pn_next) {
355            pn1 = tc->freeTree(pn1);
356            --pn->pn_count;
357        }
358        pn1->setKind(PNK_XMLTEXT);
359        pn1->setOp(JSOP_STRING);
360        pn1->setArity(PN_NULLARY);
361        pn1->pn_atom = js_AtomizeString(cx, accum);
362        if (!pn1->pn_atom)
363            return JS_FALSE;
364        JS_ASSERT(pnp != &pn1->pn_next);
365        *pnp = pn1;
366    }
367
368    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        if (!(pn->pn_xflags & PNX_XMLROOT)) {
377            pn->become(pn1);
378        } else if (kind == PNK_XMLPTAGC) {
379            pn->setKind(PNK_XMLELEM);
380            pn->setOp(JSOP_TOXML);
381        }
382    }
383    return JS_TRUE;
384}
385
386#endif /* JS_HAS_XML_SUPPORT */
387
388enum Truthiness { Truthy, Falsy, Unknown };
389
390static Truthiness
391Boolish(ParseNode *pn)
392{
393    switch (pn->getOp()) {
394      case JSOP_DOUBLE:
395        return (pn->pn_dval != 0 && !JSDOUBLE_IS_NaN(pn->pn_dval)) ? Truthy : Falsy;
396
397      case JSOP_STRING:
398        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        if (pn->pn_count != 1)
409            return Unknown;
410        ParseNode *pn2 = pn->pn_head;
411        if (!pn2->isKind(PNK_FUNCTION))
412            return Unknown;
413        if (!(pn2->pn_funbox->tcflags & TCF_GENEXP_LAMBDA))
414            return Unknown;
415        return Truthy;
416      }
417#endif
418
419      case JSOP_DEFFUN:
420      case JSOP_LAMBDA:
421      case JSOP_TRUE:
422        return Truthy;
423
424      case JSOP_NULL:
425      case JSOP_FALSE:
426        return Falsy;
427
428      default:
429        return Unknown;
430    }
431}
432
433bool
434js::FoldConstants(JSContext *cx, ParseNode *pn, TreeContext *tc, bool inCond)
435{
436    ParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
437
438    JS_CHECK_RECURSION(cx, return false);
439
440    switch (pn->getArity()) {
441      case PN_FUNC:
442      {
443        uint32_t oldflags = tc->flags;
444        FunctionBox *oldlist = tc->functionList;
445
446        tc->flags = pn->pn_funbox->tcflags;
447        tc->functionList = pn->pn_funbox->kids;
448        if (!FoldConstants(cx, pn->pn_body, tc))
449            return false;
450        pn->pn_funbox->kids = tc->functionList;
451        tc->flags = oldflags;
452        tc->functionList = oldlist;
453        break;
454      }
455
456      case PN_LIST:
457      {
458        /* Propagate inCond through logical connectives. */
459        bool cond = inCond && (pn->isKind(PNK_OR) || pn->isKind(PNK_AND));
460
461        /* Don't fold a parenthesized call expression. See bug 537673. */
462        pn1 = pn2 = pn->pn_head;
463        if ((pn->isKind(PNK_LP) || pn->isKind(PNK_NEW)) && pn2->isInParens())
464            pn2 = pn2->pn_next;
465
466        /* Save the list head in pn1 for later use. */
467        for (; pn2; pn2 = pn2->pn_next) {
468            if (!FoldConstants(cx, pn2, tc, cond))
469                return false;
470        }
471        break;
472      }
473
474      case PN_TERNARY:
475        /* Any kid may be null (e.g. for (;;)). */
476        pn1 = pn->pn_kid1;
477        pn2 = pn->pn_kid2;
478        pn3 = pn->pn_kid3;
479        if (pn1 && !FoldConstants(cx, pn1, tc, pn->isKind(PNK_IF)))
480            return false;
481        if (pn2) {
482            if (!FoldConstants(cx, pn2, tc, pn->isKind(PNK_FORHEAD)))
483                return false;
484            if (pn->isKind(PNK_FORHEAD) && pn2->isOp(JSOP_TRUE)) {
485                tc->freeTree(pn2);
486                pn->pn_kid2 = NULL;
487            }
488        }
489        if (pn3 && !FoldConstants(cx, pn3, tc))
490            return false;
491        break;
492
493      case PN_BINARY:
494        pn1 = pn->pn_left;
495        pn2 = pn->pn_right;
496
497        /* Propagate inCond through logical connectives. */
498        if (pn->isKind(PNK_OR) || pn->isKind(PNK_AND)) {
499            if (!FoldConstants(cx, pn1, tc, inCond))
500                return false;
501            if (!FoldConstants(cx, pn2, tc, inCond))
502                return false;
503            break;
504        }
505
506        /* First kid may be null (for default case in switch). */
507        if (pn1 && !FoldConstants(cx, pn1, tc, pn->isKind(PNK_WHILE)))
508            return false;
509        if (!FoldConstants(cx, pn2, tc, pn->isKind(PNK_DOWHILE)))
510            return false;
511        break;
512
513      case PN_UNARY:
514        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        if (pn->isOp(JSOP_TYPEOF) && !pn1->isKind(PNK_NAME))
526            pn->setOp(JSOP_TYPEOFEXPR);
527
528        if (pn1 && !FoldConstants(cx, pn1, tc, pn->isOp(JSOP_NOT)))
529            return false;
530        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        if (!pn->isUsed()) {
540            pn1 = pn->pn_expr;
541            while (pn1 && pn1->isArity(PN_NAME) && !pn1->isUsed())
542                pn1 = pn1->pn_expr;
543            if (pn1 && !FoldConstants(cx, pn1, tc))
544                return false;
545        }
546        break;
547
548      case PN_NAMESET:
549        pn1 = pn->pn_tree;
550        if (!FoldConstants(cx, pn1, tc))
551            return false;
552        break;
553
554      case PN_NULLARY:
555        break;
556    }
557
558    switch (pn->getKind()) {
559      case PNK_IF:
560        if (ContainsVarOrConst(pn2) || ContainsVarOrConst(pn3))
561            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        switch (pn1->getKind()) {
567          case PNK_NUMBER:
568            if (pn1->pn_dval == 0 || JSDOUBLE_IS_NaN(pn1->pn_dval))
569                pn2 = pn3;
570            break;
571          case PNK_STRING:
572            if (pn1->pn_atom->length() == 0)
573                pn2 = pn3;
574            break;
575          case PNK_TRUE:
576            break;
577          case PNK_FALSE:
578          case PNK_NULL:
579            pn2 = pn3;
580            break;
581          default:
582            /* Early return to dodge common code that copies pn2 to pn. */
583            return true;
584        }
585
586#if JS_HAS_GENERATOR_EXPRS
587        /* Don't fold a trailing |if (0)| in a generator expression. */
588        if (!pn2 && (tc->flags & TCF_GENEXP_LAMBDA))
589            break;
590#endif
591
592        if (pn2 && !pn2->isDefn())
593            pn->become(pn2);
594        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            pn->setKind(PNK_STATEMENTLIST);
603            pn->setArity(PN_LIST);
604            pn->makeEmpty();
605        }
606        tc->freeTree(pn2);
607        if (pn3 && pn3 != pn2)
608            tc->freeTree(pn3);
609        break;
610
611      case PNK_OR:
612      case PNK_AND:
613        if (inCond) {
614            if (pn->isArity(PN_LIST)) {
615                ParseNode **pnp = &pn->pn_head;
616                JS_ASSERT(*pnp == pn1);
617                do {
618                    Truthiness t = Boolish(pn1);
619                    if (t == Unknown) {
620                        pnp = &pn1->pn_next;
621                        continue;
622                    }
623                    if ((t == Truthy) == pn->isKind(PNK_OR)) {
624                        for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
625                            pn3 = pn2->pn_next;
626                            tc->freeTree(pn2);
627                            --pn->pn_count;
628                        }
629                        pn1->pn_next = NULL;
630                        break;
631                    }
632                    JS_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
633                    if (pn->pn_count == 1)
634                        break;
635                    *pnp = pn1->pn_next;
636                    tc->freeTree(pn1);
637                    --pn->pn_count;
638                } while ((pn1 = *pnp) != NULL);
639
640                // We may have to change arity from LIST to BINARY.
641                pn1 = pn->pn_head;
642                if (pn->pn_count == 2) {
643                    pn2 = pn1->pn_next;
644                    pn1->pn_next = NULL;
645                    JS_ASSERT(!pn2->pn_next);
646                    pn->setArity(PN_BINARY);
647                    pn->pn_left = pn1;
648                    pn->pn_right = pn2;
649                } else if (pn->pn_count == 1) {
650                    pn->become(pn1);
651                    tc->freeTree(pn1);
652                }
653            } else {
654                Truthiness t = Boolish(pn1);
655                if (t != Unknown) {
656                    if ((t == Truthy) == pn->isKind(PNK_OR)) {
657                        tc->freeTree(pn2);
658                        pn->become(pn1);
659                    } else {
660                        JS_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
661                        tc->freeTree(pn1);
662                        pn->become(pn2);
663                    }
664                }
665            }
666        }
667        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        goto do_binary_op;
687
688      case PNK_ADDASSIGN:
689        JS_ASSERT(pn->isOp(JSOP_ADD));
690        /* FALL THROUGH */
691      case PNK_ADD:
692        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            JS_ASSERT(pn->pn_count > 2);
699            if (pn->pn_xflags & PNX_CANTFOLD)
700                return true;
701            if (pn->pn_xflags != PNX_STRCAT)
702                goto do_binary_op;
703
704            /* Ok, we're concatenating: convert non-string constant operands. */
705            size_t length = 0;
706            for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
707                if (!FoldType(cx, pn2, PNK_STRING))
708                    return false;
709                /* XXX fold only if all operands convert to string */
710                if (!pn2->isKind(PNK_STRING))
711                    return true;
712                length += pn2->pn_atom->length();
713            }
714
715            /* Allocate a new buffer and string descriptor for the result. */
716            jschar *chars = (jschar *) cx->malloc_((length + 1) * sizeof(jschar));
717            if (!chars)
718                return false;
719            chars[length] = 0;
720            JSString *str = js_NewString(cx, chars, length);
721            if (!str) {
722                cx->free_(chars);
723                return false;
724            }
725
726            /* Fill the buffer, advancing chars and recycling kids as we go. */
727            for (pn2 = pn1; pn2; pn2 = tc->freeTree(pn2)) {
728                JSAtom *atom = pn2->pn_atom;
729                size_t length2 = atom->length();
730                js_strncpy(chars, atom->chars(), length2);
731                chars += length2;
732            }
733            JS_ASSERT(*chars == 0);
734
735            /* Atomize the result string and mutate pn to refer to it. */
736            pn->pn_atom = js_AtomizeString(cx, str);
737            if (!pn->pn_atom)
738                return false;
739            pn->setKind(PNK_STRING);
740            pn->setOp(JSOP_STRING);
741            pn->setArity(PN_NULLARY);
742            break;
743        }
744
745        /* Handle a binary string concatenation. */
746        JS_ASSERT(pn->isArity(PN_BINARY));
747        if (pn1->isKind(PNK_STRING) || pn2->isKind(PNK_STRING)) {
748            JSString *left, *right, *str;
749
750            if (!FoldType(cx, !pn1->isKind(PNK_STRING) ? pn1 : pn2, PNK_STRING))
751                return false;
752            if (!pn1->isKind(PNK_STRING) || !pn2->isKind(PNK_STRING))
753                return true;
754            left = pn1->pn_atom;
755            right = pn2->pn_atom;
756            str = js_ConcatStrings(cx, left, right);
757            if (!str)
758                return false;
759            pn->pn_atom = js_AtomizeString(cx, str);
760            if (!pn->pn_atom)
761                return false;
762            pn->setKind(PNK_STRING);
763            pn->setOp(JSOP_STRING);
764            pn->setArity(PN_NULLARY);
765            tc->freeTree(pn1);
766            tc->freeTree(pn2);
767            break;
768        }
769
770        /* Can't concatenate string literals, let's try numbers. */
771        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        if (pn->isArity(PN_LIST)) {
782            JS_ASSERT(pn->pn_count > 2);
783            for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
784                if (!FoldType(cx, pn2, PNK_NUMBER))
785                    return false;
786            }
787            for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
788                /* XXX fold only if all operands convert to number */
789                if (!pn2->isKind(PNK_NUMBER))
790                    break;
791            }
792            if (!pn2) {
793                JSOp op = pn->getOp();
794
795                pn2 = pn1->pn_next;
796                pn3 = pn2->pn_next;
797                if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn, tc))
798                    return false;
799                while ((pn2 = pn3) != NULL) {
800                    pn3 = pn2->pn_next;
801                    if (!FoldBinaryNumeric(cx, op, pn, pn2, pn, tc))
802                        return false;
803                }
804            }
805        } else {
806            JS_ASSERT(pn->isArity(PN_BINARY));
807            if (!FoldType(cx, pn1, PNK_NUMBER) ||
808                !FoldType(cx, pn2, PNK_NUMBER)) {
809                return false;
810            }
811            if (pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER)) {
812                if (!FoldBinaryNumeric(cx, pn->getOp(), pn1, pn2, pn, tc))
813                    return false;
814            }
815        }
816        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        if (pn1->isKind(PNK_NUMBER)) {
825            jsdouble d;
826
827            /* Operate on one numeric constant. */
828            d = pn1->pn_dval;
829            switch (pn->getOp()) {
830              case JSOP_BITNOT:
831                d = ~js_DoubleToECMAInt32(d);
832                break;
833
834              case JSOP_NEG:
835                d = -d;
836                break;
837
838              case JSOP_POS:
839                break;
840
841              case JSOP_NOT:
842                if (d == 0 || JSDOUBLE_IS_NaN(d)) {
843                    pn->setKind(PNK_TRUE);
844                    pn->setOp(JSOP_TRUE);
845                } else {
846                    pn->setKind(PNK_FALSE);
847                    pn->setOp(JSOP_FALSE);
848                }
849                pn->setArity(PN_NULLARY);
850                /* FALL THROUGH */
851
852              default:
853                /* Return early to dodge the common PNK_NUMBER code. */
854                return true;
855            }
856            pn->setKind(PNK_NUMBER);
857            pn->setOp(JSOP_DOUBLE);
858            pn->setArity(PN_NULLARY);
859            pn->pn_dval = d;
860            tc->freeTree(pn1);
861        } else if (pn1->isKind(PNK_TRUE) || pn1->isKind(PNK_FALSE)) {
862            if (pn->isOp(JSOP_NOT)) {
863                pn->become(pn1);
864                if (pn->isKind(PNK_TRUE)) {
865                    pn->setKind(PNK_FALSE);
866                    pn->setOp(JSOP_FALSE);
867                } else {
868                    pn->setKind(PNK_TRUE);
869                    pn->setOp(JSOP_TRUE);
870                }
871                tc->freeTree(pn1);
872            }
873        }
874        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        if (pn->isArity(PN_LIST)) {
884            JS_ASSERT(pn->isKind(PNK_XMLLIST) || pn->pn_count != 0);
885            if (!FoldXMLConstants(cx, pn, tc))
886                return false;
887        }
888        break;
889
890      case PNK_AT:
891        if (pn1->isKind(PNK_XMLNAME)) {
892            Value v = StringValue(pn1->pn_atom);
893            if (!js_ToAttributeName(cx, &v))
894                return false;
895            JS_ASSERT(v.isObject());
896
897            ObjectBox *xmlbox = tc->parser->newObjectBox(&v.toObject());
898            if (!xmlbox)
899                return false;
900
901            pn->setKind(PNK_XMLNAME);
902            pn->setOp(JSOP_OBJECT);
903            pn->setArity(PN_NULLARY);
904            pn->pn_objbox = xmlbox;
905            tc->freeTree(pn1);
906        }
907        break;
908#endif /* JS_HAS_XML_SUPPORT */
909
910      default:;
911    }
912
913    if (inCond) {
914        Truthiness t = Boolish(pn);
915        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            tc->parser->allocator.prepareNodeForMutation(pn);
923            if (t == Truthy) {
924                pn->setKind(PNK_TRUE);
925                pn->setOp(JSOP_TRUE);
926            } else {
927                pn->setKind(PNK_FALSE);
928                pn->setOp(JSOP_FALSE);
929            }
930            pn->setArity(PN_NULLARY);
931        }
932    }
933
934    return true;
935}