Back to index

lightning-sunbird  0.9+nobinonly
jsemit.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  * vim: set ts=8 sw=4 et tw=78:
00003  *
00004  * ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is Mozilla Communicator client code, released
00018  * March 31, 1998.
00019  *
00020  * The Initial Developer of the Original Code is
00021  * Netscape Communications Corporation.
00022  * Portions created by the Initial Developer are Copyright (C) 1998
00023  * the Initial Developer. All Rights Reserved.
00024  *
00025  * Contributor(s):
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 /*
00042  * JS bytecode generation.
00043  */
00044 #include "jsstddef.h"
00045 #ifdef HAVE_MEMORY_H
00046 #include <memory.h>
00047 #endif
00048 #include <string.h>
00049 #include "jstypes.h"
00050 #include "jsarena.h" /* Added by JSIFY */
00051 #include "jsutil.h" /* Added by JSIFY */
00052 #include "jsbit.h"
00053 #include "jsprf.h"
00054 #include "jsapi.h"
00055 #include "jsatom.h"
00056 #include "jscntxt.h"
00057 #include "jsconfig.h"
00058 #include "jsemit.h"
00059 #include "jsfun.h"
00060 #include "jsnum.h"
00061 #include "jsopcode.h"
00062 #include "jsparse.h"
00063 #include "jsregexp.h"
00064 #include "jsscan.h"
00065 #include "jsscope.h"
00066 #include "jsscript.h"
00067 
00068 /* Allocation chunk counts, must be powers of two in general. */
00069 #define BYTECODE_CHUNK  256     /* code allocation increment */
00070 #define SRCNOTE_CHUNK   64      /* initial srcnote allocation increment */
00071 #define TRYNOTE_CHUNK   64      /* trynote allocation increment */
00072 
00073 /* Macros to compute byte sizes from typed element counts. */
00074 #define BYTECODE_SIZE(n)        ((n) * sizeof(jsbytecode))
00075 #define SRCNOTE_SIZE(n)         ((n) * sizeof(jssrcnote))
00076 #define TRYNOTE_SIZE(n)         ((n) * sizeof(JSTryNote))
00077 
00078 JS_FRIEND_API(JSBool)
00079 js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg,
00080                      JSArenaPool *codePool, JSArenaPool *notePool,
00081                      const char *filename, uintN lineno,
00082                      JSPrincipals *principals)
00083 {
00084     memset(cg, 0, sizeof *cg);
00085     TREE_CONTEXT_INIT(&cg->treeContext);
00086     cg->treeContext.flags |= TCF_COMPILING;
00087     cg->codePool = codePool;
00088     cg->notePool = notePool;
00089     cg->codeMark = JS_ARENA_MARK(codePool);
00090     cg->noteMark = JS_ARENA_MARK(notePool);
00091     cg->tempMark = JS_ARENA_MARK(&cx->tempPool);
00092     cg->current = &cg->main;
00093     cg->filename = filename;
00094     cg->firstLine = cg->prolog.currentLine = cg->main.currentLine = lineno;
00095     cg->principals = principals;
00096     ATOM_LIST_INIT(&cg->atomList);
00097     cg->prolog.noteMask = cg->main.noteMask = SRCNOTE_CHUNK - 1;
00098     ATOM_LIST_INIT(&cg->constList);
00099     return JS_TRUE;
00100 }
00101 
00102 JS_FRIEND_API(void)
00103 js_FinishCodeGenerator(JSContext *cx, JSCodeGenerator *cg)
00104 {
00105     TREE_CONTEXT_FINISH(&cg->treeContext);
00106     JS_ARENA_RELEASE(cg->codePool, cg->codeMark);
00107     JS_ARENA_RELEASE(cg->notePool, cg->noteMark);
00108     JS_ARENA_RELEASE(&cx->tempPool, cg->tempMark);
00109 }
00110 
00111 static ptrdiff_t
00112 EmitCheck(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t delta)
00113 {
00114     jsbytecode *base, *limit, *next;
00115     ptrdiff_t offset, length;
00116     size_t incr, size;
00117 
00118     base = CG_BASE(cg);
00119     next = CG_NEXT(cg);
00120     limit = CG_LIMIT(cg);
00121     offset = PTRDIFF(next, base, jsbytecode);
00122     if (next + delta > limit) {
00123         length = offset + delta;
00124         length = (length <= BYTECODE_CHUNK)
00125                  ? BYTECODE_CHUNK
00126                  : JS_BIT(JS_CeilingLog2(length));
00127         incr = BYTECODE_SIZE(length);
00128         if (!base) {
00129             JS_ARENA_ALLOCATE_CAST(base, jsbytecode *, cg->codePool, incr);
00130         } else {
00131             size = BYTECODE_SIZE(PTRDIFF(limit, base, jsbytecode));
00132             incr -= size;
00133             JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr);
00134         }
00135         if (!base) {
00136             JS_ReportOutOfMemory(cx);
00137             return -1;
00138         }
00139         CG_BASE(cg) = base;
00140         CG_LIMIT(cg) = base + length;
00141         CG_NEXT(cg) = base + offset;
00142     }
00143     return offset;
00144 }
00145 
00146 static void
00147 UpdateDepth(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t target)
00148 {
00149     jsbytecode *pc;
00150     const JSCodeSpec *cs;
00151     intN nuses;
00152 
00153     pc = CG_CODE(cg, target);
00154     cs = &js_CodeSpec[pc[0]];
00155     nuses = cs->nuses;
00156     if (nuses < 0)
00157         nuses = 2 + GET_ARGC(pc);       /* stack: fun, this, [argc arguments] */
00158     cg->stackDepth -= nuses;
00159     JS_ASSERT(cg->stackDepth >= 0);
00160     if (cg->stackDepth < 0) {
00161         char numBuf[12];
00162         JS_snprintf(numBuf, sizeof numBuf, "%d", target);
00163         JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING,
00164                                      js_GetErrorMessage, NULL,
00165                                      JSMSG_STACK_UNDERFLOW,
00166                                      cg->filename ? cg->filename : "stdin",
00167                                      numBuf);
00168     }
00169     cg->stackDepth += cs->ndefs;
00170     if ((uintN)cg->stackDepth > cg->maxStackDepth)
00171         cg->maxStackDepth = cg->stackDepth;
00172 }
00173 
00174 ptrdiff_t
00175 js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op)
00176 {
00177     ptrdiff_t offset = EmitCheck(cx, cg, op, 1);
00178 
00179     if (offset >= 0) {
00180         *CG_NEXT(cg)++ = (jsbytecode)op;
00181         UpdateDepth(cx, cg, offset);
00182     }
00183     return offset;
00184 }
00185 
00186 ptrdiff_t
00187 js_Emit2(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1)
00188 {
00189     ptrdiff_t offset = EmitCheck(cx, cg, op, 2);
00190 
00191     if (offset >= 0) {
00192         jsbytecode *next = CG_NEXT(cg);
00193         next[0] = (jsbytecode)op;
00194         next[1] = op1;
00195         CG_NEXT(cg) = next + 2;
00196         UpdateDepth(cx, cg, offset);
00197     }
00198     return offset;
00199 }
00200 
00201 ptrdiff_t
00202 js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1,
00203          jsbytecode op2)
00204 {
00205     ptrdiff_t offset = EmitCheck(cx, cg, op, 3);
00206 
00207     if (offset >= 0) {
00208         jsbytecode *next = CG_NEXT(cg);
00209         next[0] = (jsbytecode)op;
00210         next[1] = op1;
00211         next[2] = op2;
00212         CG_NEXT(cg) = next + 3;
00213         UpdateDepth(cx, cg, offset);
00214     }
00215     return offset;
00216 }
00217 
00218 ptrdiff_t
00219 js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra)
00220 {
00221     ptrdiff_t length = 1 + (ptrdiff_t)extra;
00222     ptrdiff_t offset = EmitCheck(cx, cg, op, length);
00223 
00224     if (offset >= 0) {
00225         jsbytecode *next = CG_NEXT(cg);
00226         *next = (jsbytecode)op;
00227         memset(next + 1, 0, BYTECODE_SIZE(extra));
00228         CG_NEXT(cg) = next + length;
00229         UpdateDepth(cx, cg, offset);
00230     }
00231     return offset;
00232 }
00233 
00234 /* XXX too many "... statement" L10N gaffes below -- fix via js.msg! */
00235 const char js_with_statement_str[] = "with statement";
00236 const char js_finally_block_str[]  = "finally block";
00237 const char js_script_str[]         = "script";
00238 
00239 static const char *statementName[] = {
00240     "label statement",       /* LABEL */
00241     "if statement",          /* IF */
00242     "else statement",        /* ELSE */
00243     "switch statement",      /* SWITCH */
00244     "block",                 /* BLOCK */
00245     js_with_statement_str,   /* WITH */
00246     "catch block",           /* CATCH */
00247     "try block",             /* TRY */
00248     js_finally_block_str,    /* FINALLY */
00249     js_finally_block_str,    /* SUBROUTINE */
00250     "do loop",               /* DO_LOOP */
00251     "for loop",              /* FOR_LOOP */
00252     "for/in loop",           /* FOR_IN_LOOP */
00253     "while loop",            /* WHILE_LOOP */
00254 };
00255 
00256 static const char *
00257 StatementName(JSCodeGenerator *cg)
00258 {
00259     if (!cg->treeContext.topStmt)
00260         return js_script_str;
00261     return statementName[cg->treeContext.topStmt->type];
00262 }
00263 
00264 static void
00265 ReportStatementTooLarge(JSContext *cx, JSCodeGenerator *cg)
00266 {
00267     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEED_DIET,
00268                          StatementName(cg));
00269 }
00270 
00337 static int
00338 BalanceJumpTargets(JSJumpTarget **jtp)
00339 {
00340     JSJumpTarget *jt, *jt2, *root;
00341     int dir, otherDir, heightChanged;
00342     JSBool doubleRotate;
00343 
00344     jt = *jtp;
00345     JS_ASSERT(jt->balance != 0);
00346 
00347     if (jt->balance < -1) {
00348         dir = JT_RIGHT;
00349         doubleRotate = (jt->kids[JT_LEFT]->balance > 0);
00350     } else if (jt->balance > 1) {
00351         dir = JT_LEFT;
00352         doubleRotate = (jt->kids[JT_RIGHT]->balance < 0);
00353     } else {
00354         return 0;
00355     }
00356 
00357     otherDir = JT_OTHER_DIR(dir);
00358     if (doubleRotate) {
00359         jt2 = jt->kids[otherDir];
00360         *jtp = root = jt2->kids[dir];
00361 
00362         jt->kids[otherDir] = root->kids[dir];
00363         root->kids[dir] = jt;
00364 
00365         jt2->kids[dir] = root->kids[otherDir];
00366         root->kids[otherDir] = jt2;
00367 
00368         heightChanged = 1;
00369         root->kids[JT_LEFT]->balance = -JS_MAX(root->balance, 0);
00370         root->kids[JT_RIGHT]->balance = -JS_MIN(root->balance, 0);
00371         root->balance = 0;
00372     } else {
00373         *jtp = root = jt->kids[otherDir];
00374         jt->kids[otherDir] = root->kids[dir];
00375         root->kids[dir] = jt;
00376 
00377         heightChanged = (root->balance != 0);
00378         jt->balance = -((dir == JT_LEFT) ? --root->balance : ++root->balance);
00379     }
00380 
00381     return heightChanged;
00382 }
00383 
00384 typedef struct AddJumpTargetArgs {
00385     JSContext           *cx;
00386     JSCodeGenerator     *cg;
00387     ptrdiff_t           offset;
00388     JSJumpTarget        *node;
00389 } AddJumpTargetArgs;
00390 
00391 static int
00392 AddJumpTarget(AddJumpTargetArgs *args, JSJumpTarget **jtp)
00393 {
00394     JSJumpTarget *jt;
00395     int balanceDelta;
00396 
00397     jt = *jtp;
00398     if (!jt) {
00399         JSCodeGenerator *cg = args->cg;
00400 
00401         jt = cg->jtFreeList;
00402         if (jt) {
00403             cg->jtFreeList = jt->kids[JT_LEFT];
00404         } else {
00405             JS_ARENA_ALLOCATE_CAST(jt, JSJumpTarget *, &args->cx->tempPool,
00406                                    sizeof *jt);
00407             if (!jt) {
00408                 JS_ReportOutOfMemory(args->cx);
00409                 return 0;
00410             }
00411         }
00412         jt->offset = args->offset;
00413         jt->balance = 0;
00414         jt->kids[JT_LEFT] = jt->kids[JT_RIGHT] = NULL;
00415         cg->numJumpTargets++;
00416         args->node = jt;
00417         *jtp = jt;
00418         return 1;
00419     }
00420 
00421     if (jt->offset == args->offset) {
00422         args->node = jt;
00423         return 0;
00424     }
00425 
00426     if (args->offset < jt->offset)
00427         balanceDelta = -AddJumpTarget(args, &jt->kids[JT_LEFT]);
00428     else
00429         balanceDelta = AddJumpTarget(args, &jt->kids[JT_RIGHT]);
00430     if (!args->node)
00431         return 0;
00432 
00433     jt->balance += balanceDelta;
00434     return (balanceDelta && jt->balance)
00435            ? 1 - BalanceJumpTargets(jtp)
00436            : 0;
00437 }
00438 
00439 #ifdef DEBUG_brendan
00440 static int AVLCheck(JSJumpTarget *jt)
00441 {
00442     int lh, rh;
00443 
00444     if (!jt) return 0;
00445     JS_ASSERT(-1 <= jt->balance && jt->balance <= 1);
00446     lh = AVLCheck(jt->kids[JT_LEFT]);
00447     rh = AVLCheck(jt->kids[JT_RIGHT]);
00448     JS_ASSERT(jt->balance == rh - lh);
00449     return 1 + JS_MAX(lh, rh);
00450 }
00451 #endif
00452 
00453 static JSBool
00454 SetSpanDepTarget(JSContext *cx, JSCodeGenerator *cg, JSSpanDep *sd,
00455                  ptrdiff_t off)
00456 {
00457     AddJumpTargetArgs args;
00458 
00459     if (off < JUMPX_OFFSET_MIN || JUMPX_OFFSET_MAX < off) {
00460         ReportStatementTooLarge(cx, cg);
00461         return JS_FALSE;
00462     }
00463 
00464     args.cx = cx;
00465     args.cg = cg;
00466     args.offset = sd->top + off;
00467     args.node = NULL;
00468     AddJumpTarget(&args, &cg->jumpTargets);
00469     if (!args.node)
00470         return JS_FALSE;
00471 
00472 #ifdef DEBUG_brendan
00473     AVLCheck(cg->jumpTargets);
00474 #endif
00475 
00476     SD_SET_TARGET(sd, args.node);
00477     return JS_TRUE;
00478 }
00479 
00480 #define SPANDEPS_MIN            256
00481 #define SPANDEPS_SIZE(n)        ((n) * sizeof(JSSpanDep))
00482 #define SPANDEPS_SIZE_MIN       SPANDEPS_SIZE(SPANDEPS_MIN)
00483 
00484 static JSBool
00485 AddSpanDep(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, jsbytecode *pc2,
00486            ptrdiff_t off)
00487 {
00488     uintN index;
00489     JSSpanDep *sdbase, *sd;
00490     size_t size;
00491 
00492     index = cg->numSpanDeps;
00493     if (index + 1 == 0) {
00494         ReportStatementTooLarge(cx, cg);
00495         return JS_FALSE;
00496     }
00497 
00498     if ((index & (index - 1)) == 0 &&
00499         (!(sdbase = cg->spanDeps) || index >= SPANDEPS_MIN)) {
00500         if (!sdbase) {
00501             size = SPANDEPS_SIZE_MIN;
00502             JS_ARENA_ALLOCATE_CAST(sdbase, JSSpanDep *, &cx->tempPool, size);
00503         } else {
00504             size = SPANDEPS_SIZE(index);
00505             JS_ARENA_GROW_CAST(sdbase, JSSpanDep *, &cx->tempPool, size, size);
00506         }
00507         if (!sdbase)
00508             return JS_FALSE;
00509         cg->spanDeps = sdbase;
00510     }
00511 
00512     cg->numSpanDeps = index + 1;
00513     sd = cg->spanDeps + index;
00514     sd->top = PTRDIFF(pc, CG_BASE(cg), jsbytecode);
00515     sd->offset = sd->before = PTRDIFF(pc2, CG_BASE(cg), jsbytecode);
00516 
00517     if (js_CodeSpec[*pc].format & JOF_BACKPATCH) {
00518         /* Jump offset will be backpatched if off is a non-zero "bpdelta". */
00519         if (off != 0) {
00520             JS_ASSERT(off >= 1 + JUMP_OFFSET_LEN);
00521             if (off > BPDELTA_MAX) {
00522                 ReportStatementTooLarge(cx, cg);
00523                 return JS_FALSE;
00524             }
00525         }
00526         SD_SET_BPDELTA(sd, off);
00527     } else if (off == 0) {
00528         /* Jump offset will be patched directly, without backpatch chaining. */
00529         SD_SET_TARGET(sd, NULL);
00530     } else {
00531         /* The jump offset in off is non-zero, therefore it's already known. */
00532         if (!SetSpanDepTarget(cx, cg, sd, off))
00533             return JS_FALSE;
00534     }
00535 
00536     if (index > SPANDEP_INDEX_MAX)
00537         index = SPANDEP_INDEX_HUGE;
00538     SET_SPANDEP_INDEX(pc2, index);
00539     return JS_TRUE;
00540 }
00541 
00542 static jsbytecode *
00543 AddSwitchSpanDeps(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc)
00544 {
00545     JSOp op;
00546     jsbytecode *pc2;
00547     ptrdiff_t off;
00548     jsint low, high;
00549     uintN njumps, indexlen;
00550 
00551     op = (JSOp) *pc;
00552     JS_ASSERT(op == JSOP_TABLESWITCH || op == JSOP_LOOKUPSWITCH);
00553     pc2 = pc;
00554     off = GET_JUMP_OFFSET(pc2);
00555     if (!AddSpanDep(cx, cg, pc, pc2, off))
00556         return NULL;
00557     pc2 += JUMP_OFFSET_LEN;
00558     if (op == JSOP_TABLESWITCH) {
00559         low = GET_JUMP_OFFSET(pc2);
00560         pc2 += JUMP_OFFSET_LEN;
00561         high = GET_JUMP_OFFSET(pc2);
00562         pc2 += JUMP_OFFSET_LEN;
00563         njumps = (uintN) (high - low + 1);
00564         indexlen = 0;
00565     } else {
00566         njumps = GET_UINT16(pc2);
00567         pc2 += JUMP_OFFSET_LEN;
00568         indexlen = ATOM_INDEX_LEN;
00569     }
00570     while (njumps) {
00571         --njumps;
00572         pc2 += indexlen;
00573         off = GET_JUMP_OFFSET(pc2);
00574         if (!AddSpanDep(cx, cg, pc, pc2, off))
00575             return NULL;
00576         pc2 += JUMP_OFFSET_LEN;
00577     }
00578     return 1 + pc2;
00579 }
00580 
00581 static JSBool
00582 BuildSpanDepTable(JSContext *cx, JSCodeGenerator *cg)
00583 {
00584     jsbytecode *pc, *end;
00585     JSOp op;
00586     const JSCodeSpec *cs;
00587     ptrdiff_t off;
00588 
00589     pc = CG_BASE(cg) + cg->spanDepTodo;
00590     end = CG_NEXT(cg);
00591     while (pc != end) {
00592         JS_ASSERT(pc < end);
00593         op = (JSOp)*pc;
00594         cs = &js_CodeSpec[op];
00595 
00596         switch (cs->format & JOF_TYPEMASK) {
00597           case JOF_TABLESWITCH:
00598           case JOF_LOOKUPSWITCH:
00599             pc = AddSwitchSpanDeps(cx, cg, pc);
00600             if (!pc)
00601                 return JS_FALSE;
00602             break;
00603 
00604           case JOF_JUMP:
00605             off = GET_JUMP_OFFSET(pc);
00606             if (!AddSpanDep(cx, cg, pc, pc, off))
00607                 return JS_FALSE;
00608             /* FALL THROUGH */
00609           default:
00610             pc += cs->length;
00611             break;
00612         }
00613     }
00614 
00615     return JS_TRUE;
00616 }
00617 
00618 static JSSpanDep *
00619 GetSpanDep(JSCodeGenerator *cg, jsbytecode *pc)
00620 {
00621     uintN index;
00622     ptrdiff_t offset;
00623     int lo, hi, mid;
00624     JSSpanDep *sd;
00625 
00626     index = GET_SPANDEP_INDEX(pc);
00627     if (index != SPANDEP_INDEX_HUGE)
00628         return cg->spanDeps + index;
00629 
00630     offset = PTRDIFF(pc, CG_BASE(cg), jsbytecode);
00631     lo = 0;
00632     hi = cg->numSpanDeps - 1;
00633     while (lo <= hi) {
00634         mid = (lo + hi) / 2;
00635         sd = cg->spanDeps + mid;
00636         if (sd->before == offset)
00637             return sd;
00638         if (sd->before < offset)
00639             lo = mid + 1;
00640         else
00641             hi = mid - 1;
00642     }
00643 
00644     JS_ASSERT(0);
00645     return NULL;
00646 }
00647 
00648 static JSBool
00649 SetBackPatchDelta(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc,
00650                   ptrdiff_t delta)
00651 {
00652     JSSpanDep *sd;
00653 
00654     JS_ASSERT(delta >= 1 + JUMP_OFFSET_LEN);
00655     if (!cg->spanDeps && delta < JUMP_OFFSET_MAX) {
00656         SET_JUMP_OFFSET(pc, delta);
00657         return JS_TRUE;
00658     }
00659 
00660     if (delta > BPDELTA_MAX) {
00661         ReportStatementTooLarge(cx, cg);
00662         return JS_FALSE;
00663     }
00664 
00665     if (!cg->spanDeps && !BuildSpanDepTable(cx, cg))
00666         return JS_FALSE;
00667 
00668     sd = GetSpanDep(cg, pc);
00669     JS_ASSERT(SD_GET_BPDELTA(sd) == 0);
00670     SD_SET_BPDELTA(sd, delta);
00671     return JS_TRUE;
00672 }
00673 
00674 static void
00675 UpdateJumpTargets(JSJumpTarget *jt, ptrdiff_t pivot, ptrdiff_t delta)
00676 {
00677     if (jt->offset > pivot) {
00678         jt->offset += delta;
00679         if (jt->kids[JT_LEFT])
00680             UpdateJumpTargets(jt->kids[JT_LEFT], pivot, delta);
00681     }
00682     if (jt->kids[JT_RIGHT])
00683         UpdateJumpTargets(jt->kids[JT_RIGHT], pivot, delta);
00684 }
00685 
00686 static JSSpanDep *
00687 FindNearestSpanDep(JSCodeGenerator *cg, ptrdiff_t offset, int lo,
00688                    JSSpanDep *guard)
00689 {
00690     int num, hi, mid;
00691     JSSpanDep *sdbase, *sd;
00692 
00693     num = cg->numSpanDeps;
00694     JS_ASSERT(num > 0);
00695     hi = num - 1;
00696     sdbase = cg->spanDeps;
00697     while (lo <= hi) {
00698         mid = (lo + hi) / 2;
00699         sd = sdbase + mid;
00700         if (sd->before == offset)
00701             return sd;
00702         if (sd->before < offset)
00703             lo = mid + 1;
00704         else
00705             hi = mid - 1;
00706     }
00707     if (lo == num)
00708         return guard;
00709     sd = sdbase + lo;
00710     JS_ASSERT(sd->before >= offset && (lo == 0 || sd[-1].before < offset));
00711     return sd;
00712 }
00713 
00714 static void
00715 FreeJumpTargets(JSCodeGenerator *cg, JSJumpTarget *jt)
00716 {
00717     if (jt->kids[JT_LEFT])
00718         FreeJumpTargets(cg, jt->kids[JT_LEFT]);
00719     if (jt->kids[JT_RIGHT])
00720         FreeJumpTargets(cg, jt->kids[JT_RIGHT]);
00721     jt->kids[JT_LEFT] = cg->jtFreeList;
00722     cg->jtFreeList = jt;
00723 }
00724 
00725 static JSBool
00726 OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg)
00727 {
00728     jsbytecode *pc, *oldpc, *base, *limit, *next;
00729     JSSpanDep *sd, *sd2, *sdbase, *sdlimit, *sdtop, guard;
00730     ptrdiff_t offset, growth, delta, top, pivot, span, length, target;
00731     JSBool done;
00732     JSOp op;
00733     uint32 type;
00734     size_t size, incr;
00735     jssrcnote *sn, *snlimit;
00736     JSSrcNoteSpec *spec;
00737     uintN i, n, noteIndex;
00738     JSTryNote *tn, *tnlimit;
00739 #ifdef DEBUG_brendan
00740     int passes = 0;
00741 #endif
00742 
00743     base = CG_BASE(cg);
00744     sdbase = cg->spanDeps;
00745     sdlimit = sdbase + cg->numSpanDeps;
00746     offset = CG_OFFSET(cg);
00747     growth = 0;
00748 
00749     do {
00750         done = JS_TRUE;
00751         delta = 0;
00752         top = pivot = -1;
00753         sdtop = NULL;
00754         pc = NULL;
00755         op = JSOP_NOP;
00756         type = 0;
00757 #ifdef DEBUG_brendan
00758         passes++;
00759 #endif
00760 
00761         for (sd = sdbase; sd < sdlimit; sd++) {
00762             JS_ASSERT(JT_HAS_TAG(sd->target));
00763             sd->offset += delta;
00764 
00765             if (sd->top != top) {
00766                 sdtop = sd;
00767                 top = sd->top;
00768                 JS_ASSERT(top == sd->before);
00769                 pivot = sd->offset;
00770                 pc = base + top;
00771                 op = (JSOp) *pc;
00772                 type = (js_CodeSpec[op].format & JOF_TYPEMASK);
00773                 if (JOF_TYPE_IS_EXTENDED_JUMP(type)) {
00774                     /*
00775                      * We already extended all the jump offset operands for
00776                      * the opcode at sd->top.  Jumps and branches have only
00777                      * one jump offset operand, but switches have many, all
00778                      * of which are adjacent in cg->spanDeps.
00779                      */
00780                     continue;
00781                 }
00782 
00783                 JS_ASSERT(type == JOF_JUMP ||
00784                           type == JOF_TABLESWITCH ||
00785                           type == JOF_LOOKUPSWITCH);
00786             }
00787 
00788             if (!JOF_TYPE_IS_EXTENDED_JUMP(type)) {
00789                 span = SD_SPAN(sd, pivot);
00790                 if (span < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < span) {
00791                     ptrdiff_t deltaFromTop = 0;
00792 
00793                     done = JS_FALSE;
00794 
00795                     switch (op) {
00796                       case JSOP_GOTO:         op = JSOP_GOTOX; break;
00797                       case JSOP_IFEQ:         op = JSOP_IFEQX; break;
00798                       case JSOP_IFNE:         op = JSOP_IFNEX; break;
00799                       case JSOP_OR:           op = JSOP_ORX; break;
00800                       case JSOP_AND:          op = JSOP_ANDX; break;
00801                       case JSOP_GOSUB:        op = JSOP_GOSUBX; break;
00802                       case JSOP_CASE:         op = JSOP_CASEX; break;
00803                       case JSOP_DEFAULT:      op = JSOP_DEFAULTX; break;
00804                       case JSOP_TABLESWITCH:  op = JSOP_TABLESWITCHX; break;
00805                       case JSOP_LOOKUPSWITCH: op = JSOP_LOOKUPSWITCHX; break;
00806                       default:
00807                         ReportStatementTooLarge(cx, cg);
00808                         return JS_FALSE;
00809                     }
00810                     *pc = (jsbytecode) op;
00811 
00812                     for (sd2 = sdtop; sd2 < sdlimit && sd2->top == top; sd2++) {
00813                         if (sd2 <= sd) {
00814                             /*
00815                              * sd2->offset already includes delta as it stood
00816                              * before we entered this loop, but it must also
00817                              * include the delta relative to top due to all the
00818                              * extended jump offset immediates for the opcode
00819                              * starting at top, which we extend in this loop.
00820                              *
00821                              * If there is only one extended jump offset, then
00822                              * sd2->offset won't change and this for loop will
00823                              * iterate once only.
00824                              */
00825                             sd2->offset += deltaFromTop;
00826                             deltaFromTop += JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN;
00827                         } else {
00828                             /*
00829                              * sd2 comes after sd, and won't be revisited by
00830                              * the outer for loop, so we have to increase its
00831                              * offset by delta, not merely by deltaFromTop.
00832                              */
00833                             sd2->offset += delta;
00834                         }
00835 
00836                         delta += JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN;
00837                         UpdateJumpTargets(cg->jumpTargets, sd2->offset,
00838                                           JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN);
00839                     }
00840                     sd = sd2 - 1;
00841                 }
00842             }
00843         }
00844 
00845         growth += delta;
00846     } while (!done);
00847 
00848     if (growth) {
00849 #ifdef DEBUG_brendan
00850         printf("%s:%u: %u/%u jumps extended in %d passes (%d=%d+%d)\n",
00851                cg->filename ? cg->filename : "stdin", cg->firstLine,
00852                growth / (JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN), cg->numSpanDeps,
00853                passes, offset + growth, offset, growth);
00854 #endif
00855 
00856         /*
00857          * Ensure that we have room for the extended jumps, but don't round up
00858          * to a power of two -- we're done generating code, so we cut to fit.
00859          */
00860         limit = CG_LIMIT(cg);
00861         length = offset + growth;
00862         next = base + length;
00863         if (next > limit) {
00864             JS_ASSERT(length > BYTECODE_CHUNK);
00865             size = BYTECODE_SIZE(PTRDIFF(limit, base, jsbytecode));
00866             incr = BYTECODE_SIZE(length) - size;
00867             JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr);
00868             if (!base) {
00869                 JS_ReportOutOfMemory(cx);
00870                 return JS_FALSE;
00871             }
00872             CG_BASE(cg) = base;
00873             CG_LIMIT(cg) = next = base + length;
00874         }
00875         CG_NEXT(cg) = next;
00876 
00877         /*
00878          * Set up a fake span dependency record to guard the end of the code
00879          * being generated.  This guard record is returned as a fencepost by
00880          * FindNearestSpanDep if there is no real spandep at or above a given
00881          * unextended code offset.
00882          */
00883         guard.top = -1;
00884         guard.offset = offset + growth;
00885         guard.before = offset;
00886         guard.target = NULL;
00887     }
00888 
00889     /*
00890      * Now work backwards through the span dependencies, copying chunks of
00891      * bytecode between each extended jump toward the end of the grown code
00892      * space, and restoring immediate offset operands for all jump bytecodes.
00893      * The first chunk of bytecodes, starting at base and ending at the first
00894      * extended jump offset (NB: this chunk includes the operation bytecode
00895      * just before that immediate jump offset), doesn't need to be copied.
00896      */
00897     JS_ASSERT(sd == sdlimit);
00898     top = -1;
00899     while (--sd >= sdbase) {
00900         if (sd->top != top) {
00901             top = sd->top;
00902             op = (JSOp) base[top];
00903             type = (js_CodeSpec[op].format & JOF_TYPEMASK);
00904 
00905             for (sd2 = sd - 1; sd2 >= sdbase && sd2->top == top; sd2--)
00906                 continue;
00907             sd2++;
00908             pivot = sd2->offset;
00909             JS_ASSERT(top == sd2->before);
00910         }
00911 
00912         oldpc = base + sd->before;
00913         span = SD_SPAN(sd, pivot);
00914 
00915         /*
00916          * If this jump didn't need to be extended, restore its span immediate
00917          * offset operand now, overwriting the index of sd within cg->spanDeps
00918          * that was stored temporarily after *pc when BuildSpanDepTable ran.
00919          *
00920          * Note that span might fit in 16 bits even for an extended jump op,
00921          * if the op has multiple span operands, not all of which overflowed
00922          * (e.g. JSOP_LOOKUPSWITCH or JSOP_TABLESWITCH where some cases are in
00923          * range for a short jump, but others are not).
00924          */
00925         if (!JOF_TYPE_IS_EXTENDED_JUMP(type)) {
00926             JS_ASSERT(JUMP_OFFSET_MIN <= span && span <= JUMP_OFFSET_MAX);
00927             SET_JUMP_OFFSET(oldpc, span);
00928             continue;
00929         }
00930 
00931         /*
00932          * Set up parameters needed to copy the next run of bytecode starting
00933          * at offset (which is a cursor into the unextended, original bytecode
00934          * vector), down to sd->before (a cursor of the same scale as offset,
00935          * it's the index of the original jump pc).  Reuse delta to count the
00936          * nominal number of bytes to copy.
00937          */
00938         pc = base + sd->offset;
00939         delta = offset - sd->before;
00940         JS_ASSERT(delta >= 1 + JUMP_OFFSET_LEN);
00941 
00942         /*
00943          * Don't bother copying the jump offset we're about to reset, but do
00944          * copy the bytecode at oldpc (which comes just before its immediate
00945          * jump offset operand), on the next iteration through the loop, by
00946          * including it in offset's new value.
00947          */
00948         offset = sd->before + 1;
00949         size = BYTECODE_SIZE(delta - (1 + JUMP_OFFSET_LEN));
00950         if (size) {
00951             memmove(pc + 1 + JUMPX_OFFSET_LEN,
00952                     oldpc + 1 + JUMP_OFFSET_LEN,
00953                     size);
00954         }
00955 
00956         SET_JUMPX_OFFSET(pc, span);
00957     }
00958 
00959     if (growth) {
00960         /*
00961          * Fix source note deltas.  Don't hardwire the delta fixup adjustment,
00962          * even though currently it must be JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN
00963          * at each sd that moved.  The future may bring different offset sizes
00964          * for span-dependent instruction operands.  However, we fix only main
00965          * notes here, not prolog notes -- we know that prolog opcodes are not
00966          * span-dependent, and aren't likely ever to be.
00967          */
00968         offset = growth = 0;
00969         sd = sdbase;
00970         for (sn = cg->main.notes, snlimit = sn + cg->main.noteCount;
00971              sn < snlimit;
00972              sn = SN_NEXT(sn)) {
00973             /*
00974              * Recall that the offset of a given note includes its delta, and
00975              * tells the offset of the annotated bytecode from the main entry
00976              * point of the script.
00977              */
00978             offset += SN_DELTA(sn);
00979             while (sd < sdlimit && sd->before < offset) {
00980                 /*
00981                  * To compute the delta to add to sn, we need to look at the
00982                  * spandep after sd, whose offset - (before + growth) tells by
00983                  * how many bytes sd's instruction grew.
00984                  */
00985                 sd2 = sd + 1;
00986                 if (sd2 == sdlimit)
00987                     sd2 = &guard;
00988                 delta = sd2->offset - (sd2->before + growth);
00989                 if (delta > 0) {
00990                     JS_ASSERT(delta == JUMPX_OFFSET_LEN - JUMP_OFFSET_LEN);
00991                     sn = js_AddToSrcNoteDelta(cx, cg, sn, delta);
00992                     if (!sn)
00993                         return JS_FALSE;
00994                     snlimit = cg->main.notes + cg->main.noteCount;
00995                     growth += delta;
00996                 }
00997                 sd++;
00998             }
00999 
01000             /*
01001              * If sn has span-dependent offset operands, check whether each
01002              * covers further span-dependencies, and increase those operands
01003              * accordingly.  Some source notes measure offset not from the
01004              * annotated pc, but from that pc plus some small bias.  NB: we
01005              * assume that spec->offsetBias can't itself span span-dependent
01006              * instructions!
01007              */
01008             spec = &js_SrcNoteSpec[SN_TYPE(sn)];
01009             if (spec->isSpanDep) {
01010                 pivot = offset + spec->offsetBias;
01011                 n = spec->arity;
01012                 for (i = 0; i < n; i++) {
01013                     span = js_GetSrcNoteOffset(sn, i);
01014                     if (span == 0)
01015                         continue;
01016                     target = pivot + span * spec->isSpanDep;
01017                     sd2 = FindNearestSpanDep(cg, target,
01018                                              (target >= pivot)
01019                                              ? sd - sdbase
01020                                              : 0,
01021                                              &guard);
01022 
01023                     /*
01024                      * Increase target by sd2's before-vs-after offset delta,
01025                      * which is absolute (i.e., relative to start of script,
01026                      * as is target).  Recompute the span by subtracting its
01027                      * adjusted pivot from target.
01028                      */
01029                     target += sd2->offset - sd2->before;
01030                     span = target - (pivot + growth);
01031                     span *= spec->isSpanDep;
01032                     noteIndex = sn - cg->main.notes;
01033                     if (!js_SetSrcNoteOffset(cx, cg, noteIndex, i, span))
01034                         return JS_FALSE;
01035                     sn = cg->main.notes + noteIndex;
01036                     snlimit = cg->main.notes + cg->main.noteCount;
01037                 }
01038             }
01039         }
01040         cg->main.lastNoteOffset += growth;
01041 
01042         /*
01043          * Fix try/catch notes (O(numTryNotes * log2(numSpanDeps)), but it's
01044          * not clear how we can beat that).
01045          */
01046         for (tn = cg->tryBase, tnlimit = cg->tryNext; tn < tnlimit; tn++) {
01047             /*
01048              * First, look for the nearest span dependency at/above tn->start.
01049              * There may not be any such spandep, in which case the guard will
01050              * be returned.
01051              */
01052             offset = tn->start;
01053             sd = FindNearestSpanDep(cg, offset, 0, &guard);
01054             delta = sd->offset - sd->before;
01055             tn->start = offset + delta;
01056 
01057             /*
01058              * Next, find the nearest spandep at/above tn->start + tn->length.
01059              * Use its delta minus tn->start's delta to increase tn->length.
01060              */
01061             length = tn->length;
01062             sd2 = FindNearestSpanDep(cg, offset + length, sd - sdbase, &guard);
01063             if (sd2 != sd)
01064                 tn->length = length + sd2->offset - sd2->before - delta;
01065 
01066             /*
01067              * Finally, adjust tn->catchStart upward only if it is non-zero,
01068              * and provided there are spandeps below it that grew.
01069              */
01070             offset = tn->catchStart;
01071             if (offset != 0) {
01072                 sd = FindNearestSpanDep(cg, offset, sd2 - sdbase, &guard);
01073                 tn->catchStart = offset + sd->offset - sd->before;
01074             }
01075         }
01076     }
01077 
01078 #ifdef DEBUG_brendan
01079   {
01080     uintN bigspans = 0;
01081     top = -1;
01082     for (sd = sdbase; sd < sdlimit; sd++) {
01083         offset = sd->offset;
01084 
01085         /* NB: sd->top cursors into the original, unextended bytecode vector. */
01086         if (sd->top != top) {
01087             JS_ASSERT(top == -1 ||
01088                       !JOF_TYPE_IS_EXTENDED_JUMP(type) ||
01089                       bigspans != 0);
01090             bigspans = 0;
01091             top = sd->top;
01092             JS_ASSERT(top == sd->before);
01093             op = (JSOp) base[offset];
01094             type = (js_CodeSpec[op].format & JOF_TYPEMASK);
01095             JS_ASSERT(type == JOF_JUMP ||
01096                       type == JOF_JUMPX ||
01097                       type == JOF_TABLESWITCH ||
01098                       type == JOF_TABLESWITCHX ||
01099                       type == JOF_LOOKUPSWITCH ||
01100                       type == JOF_LOOKUPSWITCHX);
01101             pivot = offset;
01102         }
01103 
01104         pc = base + offset;
01105         if (JOF_TYPE_IS_EXTENDED_JUMP(type)) {
01106             span = GET_JUMPX_OFFSET(pc);
01107             if (span < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < span) {
01108                 bigspans++;
01109             } else {
01110                 JS_ASSERT(type == JOF_TABLESWITCHX ||
01111                           type == JOF_LOOKUPSWITCHX);
01112             }
01113         } else {
01114             span = GET_JUMP_OFFSET(pc);
01115         }
01116         JS_ASSERT(SD_SPAN(sd, pivot) == span);
01117     }
01118     JS_ASSERT(!JOF_TYPE_IS_EXTENDED_JUMP(type) || bigspans != 0);
01119   }
01120 #endif
01121 
01122     /*
01123      * Reset so we optimize at most once -- cg may be used for further code
01124      * generation of successive, independent, top-level statements.  No jump
01125      * can span top-level statements, because JS lacks goto.
01126      */
01127     size = SPANDEPS_SIZE(JS_BIT(JS_CeilingLog2(cg->numSpanDeps)));
01128     JS_ArenaFreeAllocation(&cx->tempPool, cg->spanDeps,
01129                            JS_MAX(size, SPANDEPS_SIZE_MIN));
01130     cg->spanDeps = NULL;
01131     FreeJumpTargets(cg, cg->jumpTargets);
01132     cg->jumpTargets = NULL;
01133     cg->numSpanDeps = cg->numJumpTargets = 0;
01134     cg->spanDepTodo = CG_OFFSET(cg);
01135     return JS_TRUE;
01136 }
01137 
01138 static JSBool
01139 EmitJump(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t off)
01140 {
01141     JSBool extend;
01142     ptrdiff_t jmp;
01143     jsbytecode *pc;
01144 
01145     extend = off < JUMP_OFFSET_MIN || JUMP_OFFSET_MAX < off;
01146     if (extend && !cg->spanDeps && !BuildSpanDepTable(cx, cg))
01147         return JS_FALSE;
01148 
01149     jmp = js_Emit3(cx, cg, op, JUMP_OFFSET_HI(off), JUMP_OFFSET_LO(off));
01150     if (jmp >= 0 && (extend || cg->spanDeps)) {
01151         pc = CG_CODE(cg, jmp);
01152         if (!AddSpanDep(cx, cg, pc, pc, off))
01153             return JS_FALSE;
01154     }
01155     return jmp;
01156 }
01157 
01158 static ptrdiff_t
01159 GetJumpOffset(JSCodeGenerator *cg, jsbytecode *pc)
01160 {
01161     JSSpanDep *sd;
01162     JSJumpTarget *jt;
01163     ptrdiff_t top;
01164 
01165     if (!cg->spanDeps)
01166         return GET_JUMP_OFFSET(pc);
01167 
01168     sd = GetSpanDep(cg, pc);
01169     jt = sd->target;
01170     if (!JT_HAS_TAG(jt))
01171         return JT_TO_BPDELTA(jt);
01172 
01173     top = sd->top;
01174     while (--sd >= cg->spanDeps && sd->top == top)
01175         continue;
01176     sd++;
01177     return JT_CLR_TAG(jt)->offset - sd->offset;
01178 }
01179 
01180 JSBool
01181 js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc,
01182                  ptrdiff_t off)
01183 {
01184     if (!cg->spanDeps) {
01185         if (JUMP_OFFSET_MIN <= off && off <= JUMP_OFFSET_MAX) {
01186             SET_JUMP_OFFSET(pc, off);
01187             return JS_TRUE;
01188         }
01189 
01190         if (!BuildSpanDepTable(cx, cg))
01191             return JS_FALSE;
01192     }
01193 
01194     return SetSpanDepTarget(cx, cg, GetSpanDep(cg, pc), off);
01195 }
01196 
01197 JSBool
01198 js_InStatement(JSTreeContext *tc, JSStmtType type)
01199 {
01200     JSStmtInfo *stmt;
01201 
01202     for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
01203         if (stmt->type == type)
01204             return JS_TRUE;
01205     }
01206     return JS_FALSE;
01207 }
01208 
01209 JSBool
01210 js_IsGlobalReference(JSTreeContext *tc, JSAtom *atom, JSBool *loopyp)
01211 {
01212     JSStmtInfo *stmt;
01213     JSObject *obj;
01214     JSScope *scope;
01215 
01216     *loopyp = JS_FALSE;
01217     for (stmt = tc->topStmt; stmt; stmt = stmt->down) {
01218         if (stmt->type == STMT_WITH)
01219             return JS_FALSE;
01220         if (STMT_IS_LOOP(stmt)) {
01221             *loopyp = JS_TRUE;
01222             continue;
01223         }
01224         if (stmt->flags & SIF_SCOPE) {
01225             obj = ATOM_TO_OBJECT(stmt->atom);
01226             JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass);
01227             scope = OBJ_SCOPE(obj);
01228             if (SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom)))
01229                 return JS_FALSE;
01230         }
01231     }
01232     return JS_TRUE;
01233 }
01234 
01235 void
01236 js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type,
01237                  ptrdiff_t top)
01238 {
01239     stmt->type = type;
01240     stmt->flags = 0;
01241     SET_STATEMENT_TOP(stmt, top);
01242     stmt->atom = NULL;
01243     stmt->down = tc->topStmt;
01244     tc->topStmt = stmt;
01245     if (STMT_LINKS_SCOPE(stmt)) {
01246         stmt->downScope = tc->topScopeStmt;
01247         tc->topScopeStmt = stmt;
01248     } else {
01249         stmt->downScope = NULL;
01250     }
01251 }
01252 
01253 void
01254 js_PushBlockScope(JSTreeContext *tc, JSStmtInfo *stmt, JSAtom *blockAtom,
01255                   ptrdiff_t top)
01256 {
01257     JSObject *blockObj;
01258 
01259     js_PushStatement(tc, stmt, STMT_BLOCK, top);
01260     stmt->flags |= SIF_SCOPE;
01261     blockObj = ATOM_TO_OBJECT(blockAtom);
01262     blockObj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(tc->blockChain);
01263     stmt->downScope = tc->topScopeStmt;
01264     tc->topScopeStmt = stmt;
01265     tc->blockChain = blockObj;
01266     stmt->atom = blockAtom;
01267 }
01268 
01269 /*
01270  * Emit a backpatch op with offset pointing to the previous jump of this type,
01271  * so that we can walk back up the chain fixing up the op and jump offset.
01272  */
01273 static ptrdiff_t
01274 EmitBackPatchOp(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t *lastp)
01275 {
01276     ptrdiff_t offset, delta;
01277 
01278     offset = CG_OFFSET(cg);
01279     delta = offset - *lastp;
01280     *lastp = offset;
01281     JS_ASSERT(delta > 0);
01282     return EmitJump(cx, cg, op, delta);
01283 }
01284 
01285 /*
01286  * Macro to emit a bytecode followed by a uint16 immediate operand stored in
01287  * big-endian order, used for arg and var numbers as well as for atomIndexes.
01288  * NB: We use cx and cg from our caller's lexical environment, and return
01289  * false on error.
01290  */
01291 #define EMIT_UINT16_IMM_OP(op, i)                                             \
01292     JS_BEGIN_MACRO                                                            \
01293         if (js_Emit3(cx, cg, op, UINT16_HI(i), UINT16_LO(i)) < 0)             \
01294             return JS_FALSE;                                                  \
01295     JS_END_MACRO
01296 
01297 /* Emit additional bytecode(s) for non-local jumps. */
01298 static JSBool
01299 EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
01300                       JSOp *returnop)
01301 {
01302     intN depth;
01303     JSStmtInfo *stmt;
01304     ptrdiff_t jmp;
01305 
01306     /*
01307      * Return from within a try block that has a finally clause must be split
01308      * into two ops: JSOP_SETRVAL, to pop the r.v. and store it in fp->rval;
01309      * and JSOP_RETRVAL, which makes control flow go back to the caller, who
01310      * picks up fp->rval as usual.  Otherwise, the stack will be unbalanced
01311      * when executing the finally clause.
01312      *
01313      * We mutate *returnop once only if we find an enclosing try-block (viz,
01314      * STMT_FINALLY) to ensure that we emit just one JSOP_SETRVAL before one
01315      * or more JSOP_GOSUBs and other fixup opcodes emitted by this function.
01316      * Our caller (the TOK_RETURN case of js_EmitTree) then emits *returnop.
01317      * The fixup opcodes and gosubs must interleave in the proper order, from
01318      * inner statement to outer, so that finally clauses run at the correct
01319      * stack depth.
01320      */
01321     if (returnop) {
01322         JS_ASSERT(*returnop == JSOP_RETURN);
01323         for (stmt = cg->treeContext.topStmt; stmt != toStmt;
01324              stmt = stmt->down) {
01325             if (stmt->type == STMT_FINALLY ||
01326                 ((cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT) &&
01327                  STMT_MAYBE_SCOPE(stmt))) {
01328                 if (js_Emit1(cx, cg, JSOP_SETRVAL) < 0)
01329                     return JS_FALSE;
01330                 *returnop = JSOP_RETRVAL;
01331                 break;
01332             }
01333         }
01334 
01335         /*
01336          * If there are no try-with-finally blocks open around this return
01337          * statement, we can generate a return forthwith and skip generating
01338          * any fixup code.
01339          */
01340         if (*returnop == JSOP_RETURN)
01341             return JS_TRUE;
01342     }
01343 
01344     /*
01345      * The non-local jump fixup we emit will unbalance cg->stackDepth, because
01346      * the fixup replicates balanced code such as JSOP_LEAVEWITH emitted at the
01347      * end of a with statement, so we save cg->stackDepth here and restore it
01348      * just before a successful return.
01349      */
01350     depth = cg->stackDepth;
01351     for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) {
01352         switch (stmt->type) {
01353           case STMT_FINALLY:
01354             if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
01355                 return JS_FALSE;
01356             jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(*stmt));
01357             if (jmp < 0)
01358                 return JS_FALSE;
01359             break;
01360 
01361           case STMT_WITH:
01362             /* There's a With object on the stack that we need to pop. */
01363             if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
01364                 return JS_FALSE;
01365             if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0)
01366                 return JS_FALSE;
01367             break;
01368 
01369           case STMT_FOR_IN_LOOP:
01370             /*
01371              * The iterator and the object being iterated need to be popped.
01372              */
01373             if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
01374                 return JS_FALSE;
01375             if (js_Emit1(cx, cg, JSOP_ENDITER) < 0)
01376                 return JS_FALSE;
01377             break;
01378 
01379           case STMT_SUBROUTINE:
01380             /*
01381              * There's a [exception or hole, retsub pc-index] pair on the
01382              * stack that we need to pop.
01383              */
01384             if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
01385                 return JS_FALSE;
01386             if (js_Emit1(cx, cg, JSOP_POP2) < 0)
01387                 return JS_FALSE;
01388             break;
01389 
01390           default:;
01391         }
01392 
01393         if (stmt->flags & SIF_SCOPE) {
01394             uintN i;
01395 
01396             /* There is a Block object with locals on the stack to pop. */
01397             if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
01398                 return JS_FALSE;
01399             i = OBJ_BLOCK_COUNT(cx, ATOM_TO_OBJECT(stmt->atom));
01400             EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, i);
01401         }
01402     }
01403 
01404     cg->stackDepth = depth;
01405     return JS_TRUE;
01406 }
01407 
01408 static ptrdiff_t
01409 EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
01410          ptrdiff_t *lastp, JSAtomListElement *label, JSSrcNoteType noteType)
01411 {
01412     intN index;
01413 
01414     if (!EmitNonLocalJumpFixup(cx, cg, toStmt, NULL))
01415         return -1;
01416 
01417     if (label)
01418         index = js_NewSrcNote2(cx, cg, noteType, (ptrdiff_t) ALE_INDEX(label));
01419     else if (noteType != SRC_NULL)
01420         index = js_NewSrcNote(cx, cg, noteType);
01421     else
01422         index = 0;
01423     if (index < 0)
01424         return -1;
01425 
01426     return EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, lastp);
01427 }
01428 
01429 static JSBool
01430 BackPatch(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t last,
01431           jsbytecode *target, jsbytecode op)
01432 {
01433     jsbytecode *pc, *stop;
01434     ptrdiff_t delta, span;
01435 
01436     pc = CG_CODE(cg, last);
01437     stop = CG_CODE(cg, -1);
01438     while (pc != stop) {
01439         delta = GetJumpOffset(cg, pc);
01440         span = PTRDIFF(target, pc, jsbytecode);
01441         CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, span);
01442 
01443         /*
01444          * Set *pc after jump offset in case bpdelta didn't overflow, but span
01445          * does (if so, CHECK_AND_SET_JUMP_OFFSET might call BuildSpanDepTable
01446          * and need to see the JSOP_BACKPATCH* op at *pc).
01447          */
01448         *pc = op;
01449         pc -= delta;
01450     }
01451     return JS_TRUE;
01452 }
01453 
01454 void
01455 js_PopStatement(JSTreeContext *tc)
01456 {
01457     JSStmtInfo *stmt;
01458     JSObject *blockObj;
01459 
01460     stmt = tc->topStmt;
01461     tc->topStmt = stmt->down;
01462     if (STMT_LINKS_SCOPE(stmt)) {
01463         tc->topScopeStmt = stmt->downScope;
01464         if (stmt->flags & SIF_SCOPE) {
01465             blockObj = ATOM_TO_OBJECT(stmt->atom);
01466             tc->blockChain = JSVAL_TO_OBJECT(blockObj->slots[JSSLOT_PARENT]);
01467         }
01468     }
01469 }
01470 
01471 JSBool
01472 js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg)
01473 {
01474     JSStmtInfo *stmt;
01475 
01476     stmt = cg->treeContext.topStmt;
01477     if (!STMT_IS_TRYING(stmt) &&
01478         (!BackPatch(cx, cg, stmt->breaks, CG_NEXT(cg), JSOP_GOTO) ||
01479          !BackPatch(cx, cg, stmt->continues, CG_CODE(cg, stmt->update),
01480                     JSOP_GOTO))) {
01481         return JS_FALSE;
01482     }
01483     js_PopStatement(&cg->treeContext);
01484     return JS_TRUE;
01485 }
01486 
01487 JSBool
01488 js_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom,
01489                              JSParseNode *pn)
01490 {
01491     jsdouble dval;
01492     jsint ival;
01493     JSAtom *valueAtom;
01494     JSAtomListElement *ale;
01495 
01496     /* XXX just do numbers for now */
01497     if (pn->pn_type == TOK_NUMBER) {
01498         dval = pn->pn_dval;
01499         valueAtom = (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival))
01500                     ? js_AtomizeInt(cx, ival, 0)
01501                     : js_AtomizeDouble(cx, dval, 0);
01502         if (!valueAtom)
01503             return JS_FALSE;
01504         ale = js_IndexAtom(cx, atom, &cg->constList);
01505         if (!ale)
01506             return JS_FALSE;
01507         ALE_SET_VALUE(ale, ATOM_KEY(valueAtom));
01508     }
01509     return JS_TRUE;
01510 }
01511 
01512 JSStmtInfo *
01513 js_LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp, JSBool letdecl)
01514 {
01515     JSStmtInfo *stmt;
01516     JSObject *obj;
01517     JSScope *scope;
01518     JSScopeProperty *sprop;
01519     jsval v;
01520 
01521     for (stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) {
01522         if (stmt->type == STMT_WITH) {
01523             /* Ignore with statements enclosing a single let declaration. */
01524             if (letdecl)
01525                 continue;
01526             break;
01527         }
01528 
01529         /* Skip "maybe scope" statements that don't contain let bindings. */
01530         if (!(stmt->flags & SIF_SCOPE))
01531             continue;
01532 
01533         obj = ATOM_TO_OBJECT(stmt->atom);
01534         JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass);
01535         scope = OBJ_SCOPE(obj);
01536         sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom));
01537         if (sprop) {
01538             JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
01539 
01540             if (slotp) {
01541                 /*
01542                  * Use LOCKED_OBJ_GET_SLOT since we know obj is single-
01543                  * threaded and owned by this compiler activation.
01544                  */
01545                 v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_BLOCK_DEPTH);
01546                 JS_ASSERT(JSVAL_IS_INT(v) && JSVAL_TO_INT(v) >= 0);
01547                 *slotp = JSVAL_TO_INT(v) + sprop->shortid;
01548             }
01549             return stmt;
01550         }
01551     }
01552 
01553     if (slotp)
01554         *slotp = -1;
01555     return stmt;
01556 }
01557 
01558 JSBool
01559 js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom,
01560                              jsval *vp)
01561 {
01562     JSBool ok;
01563     JSStackFrame *fp;
01564     JSStmtInfo *stmt;
01565     jsint slot;
01566     JSAtomListElement *ale;
01567     JSObject *obj, *pobj;
01568     JSProperty *prop;
01569     uintN attrs;
01570 
01571     /*
01572      * fp chases cg down the stack, but only until we reach the outermost cg.
01573      * This enables propagating consts from top-level into switch cases in a
01574      * function compiled along with the top-level script.  All stack frames
01575      * with matching code generators should be flagged with JSFRAME_COMPILING;
01576      * we check sanity here.
01577      */
01578     *vp = JSVAL_VOID;
01579     ok = JS_TRUE;
01580     fp = cx->fp;
01581     do {
01582         JS_ASSERT(fp->flags & JSFRAME_COMPILING);
01583 
01584         obj = fp->varobj;
01585         if (obj == fp->scopeChain) {
01586             /* XXX this will need revising when 'let const' is added. */
01587             stmt = js_LexicalLookup(&cg->treeContext, atom, &slot, JS_FALSE);
01588             if (stmt)
01589                 return JS_TRUE;
01590 
01591             ATOM_LIST_SEARCH(ale, &cg->constList, atom);
01592             if (ale) {
01593                 *vp = ALE_VALUE(ale);
01594                 return JS_TRUE;
01595             }
01596 
01597             /*
01598              * Try looking in the variable object for a direct property that
01599              * is readonly and permanent.  We know such a property can't be
01600              * shadowed by another property on obj's prototype chain, or a
01601              * with object or catch variable; nor can prop's value be changed,
01602              * nor can prop be deleted.
01603              */
01604             prop = NULL;
01605             if (OBJ_GET_CLASS(cx, obj) == &js_FunctionClass) {
01606                 ok = js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom),
01607                                              &pobj, &prop);
01608                 if (!ok)
01609                     break;
01610                 if (prop) {
01611 #ifdef DEBUG
01612                     JSScopeProperty *sprop = (JSScopeProperty *)prop;
01613 
01614                     /*
01615                      * Any hidden property must be a formal arg or local var,
01616                      * which will shadow a global const of the same name.
01617                      */
01618                     JS_ASSERT(sprop->getter == js_GetArgument ||
01619                               sprop->getter == js_GetLocalVariable);
01620 #endif
01621                     OBJ_DROP_PROPERTY(cx, pobj, prop);
01622                     break;
01623                 }
01624             }
01625 
01626             ok = OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop);
01627             if (ok) {
01628                 if (pobj == obj &&
01629                     (fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO))) {
01630                     /*
01631                      * We're compiling code that will be executed immediately,
01632                      * not re-executed against a different scope chain and/or
01633                      * variable object.  Therefore we can get constant values
01634                      * from our variable object here.
01635                      */
01636                     ok = OBJ_GET_ATTRIBUTES(cx, obj, ATOM_TO_JSID(atom), prop,
01637                                             &attrs);
01638                     if (ok && !(~attrs & (JSPROP_READONLY | JSPROP_PERMANENT)))
01639                         ok = OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp);
01640                 }
01641                 if (prop)
01642                     OBJ_DROP_PROPERTY(cx, pobj, prop);
01643             }
01644             if (!ok || prop)
01645                 break;
01646         }
01647         fp = fp->down;
01648     } while ((cg = cg->parent) != NULL);
01649     return ok;
01650 }
01651 
01652 /*
01653  * Allocate an index invariant for all activations of the code being compiled
01654  * in cg, that can be used to store and fetch a reference to a cloned RegExp
01655  * object that shares the same JSRegExp private data created for the object
01656  * literal in pn->pn_atom.  We need clones to hold lastIndex and other direct
01657  * properties that should not be shared among threads sharing a precompiled
01658  * function or script.
01659  *
01660  * If the code being compiled is function code, allocate a reserved slot in
01661  * the cloned function object that shares its precompiled script with other
01662  * cloned function objects and with the compiler-created clone-parent.  There
01663  * are fun->nregexps such reserved slots in each function object cloned from
01664  * fun->object.  NB: during compilation, funobj slots must never be allocated,
01665  * because js_AllocSlot could hand out one of the slots that should be given
01666  * to a regexp clone.
01667  *
01668  * If the code being compiled is global code, reserve the fp->vars slot at
01669  * ALE_INDEX(ale), by ensuring that cg->treeContext.numGlobalVars is at least
01670  * one more than this index.  For global code, fp->vars is parallel to the
01671  * global script->atomMap.vector array, but possibly shorter for the common
01672  * case (where var declarations and regexp literals cluster toward the front
01673  * of the script or function body).
01674  *
01675  * Global variable name literals in script->atomMap have fast-global slot
01676  * numbers (stored as int-tagged jsvals) in the corresponding fp->vars array
01677  * element.  The atomIndex for a regexp object literal thus also addresses an
01678  * fp->vars element that is not used by any optimized global variable, so we
01679  * use that GC-scanned element to keep the regexp object clone alive, as well
01680  * as to lazily create and find it at run-time for the JSOP_REGEXP bytecode.
01681  *
01682  * In no case can cx->fp->varobj be a Call object here, because that implies
01683  * we are compiling eval code, in which case (cx->fp->flags & JSFRAME_EVAL)
01684  * is true, and js_GetToken will have already selected JSOP_OBJECT instead of
01685  * JSOP_REGEXP, to avoid all this RegExp object cloning business.
01686  *
01687  * Why clone regexp objects?  ECMA specifies that when a regular expression
01688  * literal is scanned, a RegExp object is created.  In the spec, compilation
01689  * and execution happen indivisibly, but in this implementation and many of
01690  * its embeddings, code is precompiled early and re-executed in multiple
01691  * threads, or using multiple global objects, or both, for efficiency.
01692  *
01693  * In such cases, naively following ECMA leads to wrongful sharing of RegExp
01694  * objects, which makes for collisions on the lastIndex property (especially
01695  * for global regexps) and on any ad-hoc properties.  Also, __proto__ and
01696  * __parent__ refer to the pre-compilation prototype and global objects, a
01697  * pigeon-hole problem for instanceof tests.
01698  */
01699 static JSBool
01700 IndexRegExpClone(JSContext *cx, JSParseNode *pn, JSAtomListElement *ale,
01701                  JSCodeGenerator *cg)
01702 {
01703     JSObject *varobj, *reobj;
01704     JSClass *clasp;
01705     JSFunction *fun;
01706     JSRegExp *re;
01707     uint16 *countPtr;
01708     uintN cloneIndex;
01709 
01710     JS_ASSERT(!(cx->fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO)));
01711 
01712     varobj = cx->fp->varobj;
01713     clasp = OBJ_GET_CLASS(cx, varobj);
01714     if (clasp == &js_FunctionClass) {
01715         fun = (JSFunction *) JS_GetPrivate(cx, varobj);
01716         countPtr = &fun->u.i.nregexps;
01717         cloneIndex = *countPtr;
01718     } else {
01719         JS_ASSERT(clasp != &js_CallClass);
01720         countPtr = &cg->treeContext.numGlobalVars;
01721         cloneIndex = ALE_INDEX(ale);
01722     }
01723 
01724     if ((cloneIndex + 1) >> 16) {
01725         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
01726                              JSMSG_NEED_DIET, js_script_str);
01727         return JS_FALSE;
01728     }
01729     if (cloneIndex >= *countPtr)
01730         *countPtr = cloneIndex + 1;
01731 
01732     reobj = ATOM_TO_OBJECT(pn->pn_atom);
01733     JS_ASSERT(OBJ_GET_CLASS(cx, reobj) == &js_RegExpClass);
01734     re = (JSRegExp *) JS_GetPrivate(cx, reobj);
01735     re->cloneIndex = cloneIndex;
01736     return JS_TRUE;
01737 }
01738 
01739 /*
01740  * Emit a bytecode and its 2-byte constant (atom) index immediate operand.
01741  * If the atomIndex requires more than 2 bytes, emit a prefix op whose 24-bit
01742  * immediate operand indexes the atom in script->atomMap.
01743  *
01744  * If op has JOF_NAME mode, emit JSOP_FINDNAME to find and push the object in
01745  * the scope chain in which the literal name was found, followed by the name
01746  * as a string.  This enables us to use the JOF_ELEM counterpart to op.
01747  *
01748  * Otherwise, if op has JOF_PROP mode, emit JSOP_LITERAL before op, to push
01749  * the atom's value key.  For JOF_PROP ops, the object being operated on has
01750  * already been pushed, and JSOP_LITERAL will push the id, leaving the stack
01751  * in the proper state for a JOF_ELEM counterpart.
01752  *
01753  * Otherwise, emit JSOP_LITOPX to push the atom index, then perform a special
01754  * dispatch on op, but getting op's atom index from the stack instead of from
01755  * an unsigned 16-bit immediate operand.
01756  */
01757 static JSBool
01758 EmitAtomIndexOp(JSContext *cx, JSOp op, jsatomid atomIndex, JSCodeGenerator *cg)
01759 {
01760     uint32 mode;
01761     JSOp prefixOp;
01762     ptrdiff_t off;
01763     jsbytecode *pc;
01764 
01765     if (atomIndex >= JS_BIT(16)) {
01766         mode = (js_CodeSpec[op].format & JOF_MODEMASK);
01767         if (op != JSOP_SETNAME) {
01768             prefixOp = ((mode != JOF_NAME && mode != JOF_PROP) ||
01769 #if JS_HAS_XML_SUPPORT
01770                         op == JSOP_GETMETHOD ||
01771                         op == JSOP_SETMETHOD ||
01772 #endif
01773                         op == JSOP_SETCONST)
01774                        ? JSOP_LITOPX
01775                        : (mode == JOF_NAME)
01776                        ? JSOP_FINDNAME
01777                        : JSOP_LITERAL;
01778             off = js_EmitN(cx, cg, prefixOp, 3);
01779             if (off < 0)
01780                 return JS_FALSE;
01781             pc = CG_CODE(cg, off);
01782             SET_LITERAL_INDEX(pc, atomIndex);
01783         }
01784 
01785         switch (op) {
01786           case JSOP_DECNAME:    op = JSOP_DECELEM; break;
01787           case JSOP_DECPROP:    op = JSOP_DECELEM; break;
01788           case JSOP_DELNAME:    op = JSOP_DELELEM; break;
01789           case JSOP_DELPROP:    op = JSOP_DELELEM; break;
01790           case JSOP_FORNAME:    op = JSOP_FORELEM; break;
01791           case JSOP_FORPROP:    op = JSOP_FORELEM; break;
01792           case JSOP_GETPROP:    op = JSOP_GETELEM; break;
01793           case JSOP_GETXPROP:   op = JSOP_GETXELEM; break;
01794           case JSOP_IMPORTPROP: op = JSOP_IMPORTELEM; break;
01795           case JSOP_INCNAME:    op = JSOP_INCELEM; break;
01796           case JSOP_INCPROP:    op = JSOP_INCELEM; break;
01797           case JSOP_INITPROP:   op = JSOP_INITELEM; break;
01798           case JSOP_NAME:       op = JSOP_GETELEM; break;
01799           case JSOP_NAMEDEC:    op = JSOP_ELEMDEC; break;
01800           case JSOP_NAMEINC:    op = JSOP_ELEMINC; break;
01801           case JSOP_PROPDEC:    op = JSOP_ELEMDEC; break;
01802           case JSOP_PROPINC:    op = JSOP_ELEMINC; break;
01803           case JSOP_BINDNAME:   return JS_TRUE;
01804           case JSOP_SETNAME:    op = JSOP_SETELEM; break;
01805           case JSOP_SETPROP:    op = JSOP_SETELEM; break;
01806 #if JS_HAS_EXPORT_IMPORT
01807           case JSOP_EXPORTNAME:
01808             ReportStatementTooLarge(cx, cg);
01809             return JS_FALSE;
01810 #endif
01811           default:
01812 #if JS_HAS_XML_SUPPORT
01813             JS_ASSERT(mode == 0 || op == JSOP_SETCONST ||
01814                       op == JSOP_GETMETHOD || op == JSOP_SETMETHOD);
01815 #else
01816             JS_ASSERT(mode == 0 || op == JSOP_SETCONST);
01817 #endif
01818             break;
01819         }
01820 
01821         return js_Emit1(cx, cg, op) >= 0;
01822     }
01823 
01824     EMIT_UINT16_IMM_OP(op, atomIndex);
01825     return JS_TRUE;
01826 }
01827 
01828 /*
01829  * Slight sugar for EmitAtomIndexOp, again accessing cx and cg from the macro
01830  * caller's lexical environment, and embedding a false return on error.
01831  * XXXbe hey, who checks for fun->nvars and fun->nargs overflow?!
01832  */
01833 #define EMIT_ATOM_INDEX_OP(op, atomIndex)                                     \
01834     JS_BEGIN_MACRO                                                            \
01835         if (!EmitAtomIndexOp(cx, op, atomIndex, cg))                          \
01836             return JS_FALSE;                                                  \
01837     JS_END_MACRO
01838 
01839 static JSBool
01840 EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
01841 {
01842     JSAtomListElement *ale;
01843 
01844     ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList);
01845     if (!ale)
01846         return JS_FALSE;
01847     if (op == JSOP_REGEXP && !IndexRegExpClone(cx, pn, ale, cg))
01848         return JS_FALSE;
01849     return EmitAtomIndexOp(cx, op, ALE_INDEX(ale), cg);
01850 }
01851 
01852 /*
01853  * This routine tries to optimize name gets and sets to stack slot loads and
01854  * stores, given the variables object and scope chain in cx's top frame, the
01855  * compile-time context in tc, and a TOK_NAME node pn.  It returns false on
01856  * error, true on success.
01857  *
01858  * The caller can inspect pn->pn_slot for a non-negative slot number to tell
01859  * whether optimization occurred, in which case BindNameToSlot also updated
01860  * pn->pn_op.  If pn->pn_slot is still -1 on return, pn->pn_op nevertheless
01861  * may have been optimized, e.g., from JSOP_NAME to JSOP_ARGUMENTS.  Whether
01862  * or not pn->pn_op was modified, if this function finds an argument or local
01863  * variable name, pn->pn_attrs will contain the property's attributes after a
01864  * successful return.
01865  *
01866  * NB: if you add more opcodes specialized from JSOP_NAME, etc., don't forget
01867  * to update the TOK_FOR (for-in) and TOK_ASSIGN (op=, e.g. +=) special cases
01868  * in js_EmitTree.
01869  */
01870 static JSBool
01871 BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,
01872                JSBool letdecl)
01873 {
01874     JSAtom *atom;
01875     JSStmtInfo *stmt;
01876     jsint slot;
01877     JSOp op;
01878     JSStackFrame *fp;
01879     JSObject *obj, *pobj;
01880     JSClass *clasp;
01881     JSBool optimizeGlobals;
01882     JSPropertyOp getter;
01883     uintN attrs;
01884     JSAtomListElement *ale;
01885     JSProperty *prop;
01886     JSScopeProperty *sprop;
01887 
01888     JS_ASSERT(pn->pn_type == TOK_NAME);
01889     if (pn->pn_slot >= 0 || pn->pn_op == JSOP_ARGUMENTS)
01890         return JS_TRUE;
01891 
01892     /* QNAME references can never be optimized to use arg/var storage. */
01893     if (pn->pn_op == JSOP_QNAMEPART)
01894         return JS_TRUE;
01895 
01896     /*
01897      * We can't optimize if we are compiling a with statement and its body,
01898      * or we're in a catch block whose exception variable has the same name
01899      * as this node.  FIXME: we should be able to optimize catch vars to be
01900      * block-locals.
01901      */
01902     atom = pn->pn_atom;
01903     stmt = js_LexicalLookup(tc, atom, &slot, letdecl);
01904     if (stmt) {
01905         if (stmt->type == STMT_WITH)
01906             return JS_TRUE;
01907 
01908         JS_ASSERT(stmt->flags & SIF_SCOPE);
01909         JS_ASSERT(slot >= 0);
01910         op = pn->pn_op;
01911         switch (op) {
01912           case JSOP_NAME:     op = JSOP_GETLOCAL; break;
01913           case JSOP_SETNAME:  op = JSOP_SETLOCAL; break;
01914           case JSOP_INCNAME:  op = JSOP_INCLOCAL; break;
01915           case JSOP_NAMEINC:  op = JSOP_LOCALINC; break;
01916           case JSOP_DECNAME:  op = JSOP_DECLOCAL; break;
01917           case JSOP_NAMEDEC:  op = JSOP_LOCALDEC; break;
01918           case JSOP_FORNAME:  op = JSOP_FORLOCAL; break;
01919           case JSOP_DELNAME:  op = JSOP_FALSE; break;
01920           default: JS_ASSERT(0);
01921         }
01922         if (op != pn->pn_op) {
01923             pn->pn_op = op;
01924             pn->pn_slot = slot;
01925         }
01926         return JS_TRUE;
01927     }
01928 
01929     /*
01930      * A Script object can be used to split an eval into a compile step done
01931      * at construction time, and an execute step done separately, possibly in
01932      * a different scope altogether.  We therefore cannot do any name-to-slot
01933      * optimizations, but must lookup names at runtime.  Note that script_exec
01934      * ensures that its caller's frame has a Call object, so arg and var name
01935      * lookups will succeed.
01936      */
01937     fp = cx->fp;
01938     if (fp->flags & JSFRAME_SCRIPT_OBJECT)
01939         return JS_TRUE;
01940 
01941     /*
01942      * We can't optimize if var and closure (a local function not in a larger
01943      * expression and not at top-level within another's body) collide.
01944      * XXX suboptimal: keep track of colliding names and deoptimize only those
01945      */
01946     if (tc->flags & TCF_FUN_CLOSURE_VS_VAR)
01947         return JS_TRUE;
01948 
01949     /*
01950      * We can't optimize if we're not compiling a function body, whether via
01951      * eval, or directly when compiling a function statement or expression.
01952      */
01953     obj = fp->varobj;
01954     clasp = OBJ_GET_CLASS(cx, obj);
01955     if (clasp != &js_FunctionClass && clasp != &js_CallClass) {
01956         /* Check for an eval or debugger frame. */
01957         if (fp->flags & JSFRAME_SPECIAL)
01958             return JS_TRUE;
01959 
01960         /*
01961          * Optimize global variable accesses if there are at least 100 uses
01962          * in unambiguous contexts, or failing that, if least half of all the
01963          * uses of global vars/consts/functions are in loops.
01964          */
01965         optimizeGlobals = (tc->globalUses >= 100 ||
01966                            (tc->loopyGlobalUses &&
01967                             tc->loopyGlobalUses >= tc->globalUses / 2));
01968         if (!optimizeGlobals)
01969             return JS_TRUE;
01970     } else {
01971         optimizeGlobals = JS_FALSE;
01972     }
01973 
01974     /*
01975      * We can't optimize if we are in an eval called inside a with statement.
01976      */
01977     if (fp->scopeChain != obj)
01978         return JS_TRUE;
01979 
01980     op = pn->pn_op;
01981     getter = NULL;
01982 #ifdef __GNUC__
01983     attrs = slot = 0;   /* quell GCC overwarning */
01984 #endif
01985     if (optimizeGlobals) {
01986         /*
01987          * We are optimizing global variables, and there is no pre-existing
01988          * global property named atom.  If atom was declared via const or var,
01989          * optimize pn to access fp->vars using the appropriate JOF_QVAR op.
01990          */
01991         ATOM_LIST_SEARCH(ale, &tc->decls, atom);
01992         if (!ale) {
01993             /* Use precedes declaration, or name is never declared. */
01994             return JS_TRUE;
01995         }
01996 
01997         attrs = (ALE_JSOP(ale) == JSOP_DEFCONST)
01998                 ? JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT
01999                 : JSPROP_ENUMERATE | JSPROP_PERMANENT;
02000 
02001         /* Index atom so we can map fast global number to name. */
02002         JS_ASSERT(tc->flags & TCF_COMPILING);
02003         ale = js_IndexAtom(cx, atom, &((JSCodeGenerator *) tc)->atomList);
02004         if (!ale)
02005             return JS_FALSE;
02006 
02007         /* Defend against tc->numGlobalVars 16-bit overflow. */
02008         slot = ALE_INDEX(ale);
02009         if ((slot + 1) >> 16)
02010             return JS_TRUE;
02011 
02012         if ((uint16)(slot + 1) > tc->numGlobalVars)
02013             tc->numGlobalVars = (uint16)(slot + 1);
02014     } else {
02015         /*
02016          * We may be able to optimize name to stack slot. Look for an argument
02017          * or variable property in the function, or its call object, not found
02018          * in any prototype object.  Rewrite pn_op and update pn accordingly.
02019          * NB: We know that JSOP_DELNAME on an argument or variable evaluates
02020          * to false, due to JSPROP_PERMANENT.
02021          */
02022         if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop))
02023             return JS_FALSE;
02024         sprop = (JSScopeProperty *) prop;
02025         if (sprop) {
02026             if (pobj == obj) {
02027                 getter = sprop->getter;
02028                 attrs = sprop->attrs;
02029                 slot = (sprop->flags & SPROP_HAS_SHORTID) ? sprop->shortid : -1;
02030             }
02031             OBJ_DROP_PROPERTY(cx, pobj, prop);
02032         }
02033     }
02034 
02035     if (optimizeGlobals || getter) {
02036         if (optimizeGlobals) {
02037             switch (op) {
02038               case JSOP_NAME:     op = JSOP_GETGVAR; break;
02039               case JSOP_SETNAME:  op = JSOP_SETGVAR; break;
02040               case JSOP_SETCONST: /* NB: no change */ break;
02041               case JSOP_INCNAME:  op = JSOP_INCGVAR; break;
02042               case JSOP_NAMEINC:  op = JSOP_GVARINC; break;
02043               case JSOP_DECNAME:  op = JSOP_DECGVAR; break;
02044               case JSOP_NAMEDEC:  op = JSOP_GVARDEC; break;
02045               case JSOP_FORNAME:  /* NB: no change */ break;
02046               case JSOP_DELNAME:  /* NB: no change */ break;
02047               default: JS_ASSERT(0);
02048             }
02049         } else if (getter == js_GetLocalVariable ||
02050                    getter == js_GetCallVariable) {
02051             switch (op) {
02052               case JSOP_NAME:     op = JSOP_GETVAR; break;
02053               case JSOP_SETNAME:  op = JSOP_SETVAR; break;
02054               case JSOP_SETCONST: op = JSOP_SETVAR; break;
02055               case JSOP_INCNAME:  op = JSOP_INCVAR; break;
02056               case JSOP_NAMEINC:  op = JSOP_VARINC; break;
02057               case JSOP_DECNAME:  op = JSOP_DECVAR; break;
02058               case JSOP_NAMEDEC:  op = JSOP_VARDEC; break;
02059               case JSOP_FORNAME:  op = JSOP_FORVAR; break;
02060               case JSOP_DELNAME:  op = JSOP_FALSE; break;
02061               default: JS_ASSERT(0);
02062             }
02063         } else if (getter == js_GetArgument ||
02064                    (getter == js_CallClass.getProperty &&
02065                     fp->fun && (uintN) slot < fp->fun->nargs)) {
02066             switch (op) {
02067               case JSOP_NAME:     op = JSOP_GETARG; break;
02068               case JSOP_SETNAME:  op = JSOP_SETARG; break;
02069               case JSOP_INCNAME:  op = JSOP_INCARG; break;
02070               case JSOP_NAMEINC:  op = JSOP_ARGINC; break;
02071               case JSOP_DECNAME:  op = JSOP_DECARG; break;
02072               case JSOP_NAMEDEC:  op = JSOP_ARGDEC; break;
02073               case JSOP_FORNAME:  op = JSOP_FORARG; break;
02074               case JSOP_DELNAME:  op = JSOP_FALSE; break;
02075               default: JS_ASSERT(0);
02076             }
02077         }
02078         if (op != pn->pn_op) {
02079             pn->pn_op = op;
02080             pn->pn_slot = slot;
02081         }
02082         pn->pn_attrs = attrs;
02083     }
02084 
02085     if (pn->pn_slot < 0) {
02086         /*
02087          * We couldn't optimize pn, so it's not a global or local slot name.
02088          * Now we must check for the predefined arguments variable.  It may be
02089          * overridden by assignment, in which case the function is heavyweight
02090          * and the interpreter will look up 'arguments' in the function's call
02091          * object.
02092          */
02093         if (pn->pn_op == JSOP_NAME &&
02094             atom == cx->runtime->atomState.argumentsAtom) {
02095             pn->pn_op = JSOP_ARGUMENTS;
02096             return JS_TRUE;
02097         }
02098 
02099         tc->flags |= TCF_FUN_USES_NONLOCALS;
02100     }
02101     return JS_TRUE;
02102 }
02103 
02104 /*
02105  * If pn contains a useful expression, return true with *answer set to true.
02106  * If pn contains a useless expression, return true with *answer set to false.
02107  * Return false on error.
02108  *
02109  * The caller should initialize *answer to false and invoke this function on
02110  * an expression statement or similar subtree to decide whether the tree could
02111  * produce code that has any side effects.  For an expression statement, we
02112  * define useless code as code with no side effects, because the main effect,
02113  * the value left on the stack after the code executes, will be discarded by a
02114  * pop bytecode.
02115  */
02116 static JSBool
02117 CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,
02118                  JSBool *answer)
02119 {
02120     JSBool ok;
02121     JSFunction *fun;
02122     JSParseNode *pn2;
02123 
02124     ok = JS_TRUE;
02125     if (!pn || *answer)
02126         return ok;
02127 
02128     switch (pn->pn_arity) {
02129       case PN_FUNC:
02130         /*
02131          * A named function is presumed useful: we can't yet know that it is
02132          * not called.  The side effects are the creation of a scope object
02133          * to parent this function object, and the binding of the function's
02134          * name in that scope object.  See comments at case JSOP_NAMEDFUNOBJ:
02135          * in jsinterp.c.
02136          */
02137         fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(pn->pn_funAtom));
02138         if (fun->atom)
02139             *answer = JS_TRUE;
02140         break;
02141 
02142       case PN_LIST:
02143         if (pn->pn_type == TOK_NEW ||
02144             pn->pn_type == TOK_LP ||
02145             pn->pn_type == TOK_LB ||
02146             pn->pn_type == TOK_RB ||
02147             pn->pn_type == TOK_RC) {
02148             /*
02149              * All invocation operations (construct: TOK_NEW, call: TOK_LP)
02150              * are presumed to be useful, because they may have side effects
02151              * even if their main effect (their return value) is discarded.
02152              *
02153              * TOK_LB binary trees of 3 or more nodes are flattened into lists
02154              * to avoid too much recursion.  All such lists must be presumed
02155              * to be useful because each index operation could invoke a getter
02156              * (the JSOP_ARGUMENTS special case below, in the PN_BINARY case,
02157              * does not apply here: arguments[i][j] might invoke a getter).
02158              *
02159              * Array and object initializers (TOK_RB and TOK_RC lists) must be
02160              * considered useful, because they are sugar for constructor calls
02161              * (to Array and Object, respectively).
02162              */
02163             *answer = JS_TRUE;
02164         } else {
02165             for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next)
02166                 ok &= CheckSideEffects(cx, tc, pn2, answer);
02167         }
02168         break;
02169 
02170       case PN_TERNARY:
02171         ok = CheckSideEffects(cx, tc, pn->pn_kid1, answer) &&
02172              CheckSideEffects(cx, tc, pn->pn_kid2, answer) &&
02173              CheckSideEffects(cx, tc, pn->pn_kid3, answer);
02174         break;
02175 
02176       case PN_BINARY:
02177         if (pn->pn_type == TOK_ASSIGN) {
02178             /*
02179              * Assignment is presumed to be useful, even if the next operation
02180              * is another assignment overwriting this one's ostensible effect,
02181              * because the left operand may be a property with a setter that
02182              * has side effects.
02183              *
02184              * The only exception is assignment of a useless value to a const
02185              * declared in the function currently being compiled.
02186              */
02187             pn2 = pn->pn_left;
02188             if (pn2->pn_type != TOK_NAME) {
02189                 *answer = JS_TRUE;
02190             } else {
02191                 if (!BindNameToSlot(cx, tc, pn2, JS_FALSE))
02192                     return JS_FALSE;
02193                 if (!CheckSideEffects(cx, tc, pn->pn_right, answer))
02194                     return JS_FALSE;
02195                 if (!*answer &&
02196                     (pn2->pn_slot < 0 || !(pn2->pn_attrs & JSPROP_READONLY))) {
02197                     *answer = JS_TRUE;
02198                 }
02199             }
02200         } else {
02201             if (pn->pn_type == TOK_LB) {
02202                 pn2 = pn->pn_left;
02203                 if (pn2->pn_type == TOK_NAME &&
02204                     !BindNameToSlot(cx, tc, pn2, JS_FALSE)) {
02205                     return JS_FALSE;
02206                 }
02207                 if (pn2->pn_op != JSOP_ARGUMENTS) {
02208                     /*
02209                      * Any indexed property reference could call a getter with
02210                      * side effects, except for arguments[i] where arguments is
02211                      * unambiguous.
02212                      */
02213                     *answer = JS_TRUE;
02214                 }
02215             }
02216             ok = CheckSideEffects(cx, tc, pn->pn_left, answer) &&
02217                  CheckSideEffects(cx, tc, pn->pn_right, answer);
02218         }
02219         break;
02220 
02221       case PN_UNARY:
02222         if (pn->pn_type == TOK_INC || pn->pn_type == TOK_DEC ||
02223             pn->pn_type == TOK_THROW ||
02224 #if JS_HAS_GENERATORS
02225             pn->pn_type == TOK_YIELD ||
02226 #endif
02227             pn->pn_type == TOK_DEFSHARP) {
02228             /* All these operations have effects that we must commit. */
02229             *answer = JS_TRUE;
02230         } else if (pn->pn_type == TOK_DELETE) {
02231             pn2 = pn->pn_kid;
02232             switch (pn2->pn_type) {
02233               case TOK_NAME:
02234               case TOK_DOT:
02235 #if JS_HAS_XML_SUPPORT
02236               case TOK_DBLDOT:
02237 #endif
02238 #if JS_HAS_LVALUE_RETURN
02239               case TOK_LP:
02240 #endif
02241               case TOK_LB:
02242                 /* All these delete addressing modes have effects too. */
02243                 *answer = JS_TRUE;
02244                 break;
02245               default:
02246                 ok = CheckSideEffects(cx, tc, pn2, answer);
02247                 break;
02248             }
02249         } else {
02250             ok = CheckSideEffects(cx, tc, pn->pn_kid, answer);
02251         }
02252         break;
02253 
02254       case PN_NAME:
02255         /*
02256          * Take care to avoid trying to bind a label name (labels, both for
02257          * statements and property values in object initialisers, have pn_op
02258          * defaulted to JSOP_NOP).
02259          */
02260         if (pn->pn_type == TOK_NAME && pn->pn_op != JSOP_NOP) {
02261             if (!BindNameToSlot(cx, tc, pn, JS_FALSE))
02262                 return JS_FALSE;
02263             if (pn->pn_slot < 0 && pn->pn_op != JSOP_ARGUMENTS) {
02264                 /*
02265                  * Not an argument or local variable use, so this expression
02266                  * could invoke a getter that has side effects.
02267                  */
02268                 *answer = JS_TRUE;
02269             }
02270         }
02271         pn2 = pn->pn_expr;
02272         if (pn->pn_type == TOK_DOT) {
02273             if (pn2->pn_type == TOK_NAME &&
02274                 !BindNameToSlot(cx, tc, pn2, JS_FALSE)) {
02275                 return JS_FALSE;
02276             }
02277             if (!(pn2->pn_op == JSOP_ARGUMENTS &&
02278                   pn->pn_atom == cx->runtime->atomState.lengthAtom)) {
02279                 /*
02280                  * Any dotted property reference could call a getter, except
02281                  * for arguments.length where arguments is unambiguous.
02282                  */
02283                 *answer = JS_TRUE;
02284             }
02285         }
02286         ok = CheckSideEffects(cx, tc, pn2, answer);
02287         break;
02288 
02289       case PN_NULLARY:
02290         if (pn->pn_type == TOK_DEBUGGER)
02291             *answer = JS_TRUE;
02292         break;
02293     }
02294     return ok;
02295 }
02296 
02297 /*
02298  * Secret handshake with js_EmitTree's TOK_LP/TOK_NEW case logic, to flag all
02299  * uses of JSOP_GETMETHOD that implicitly qualify the method property's name
02300  * with a function:: prefix.  All other JSOP_GETMETHOD and JSOP_SETMETHOD uses
02301  * must be explicit, so we need a distinct source note (SRC_METHODBASE rather
02302  * than SRC_PCBASE) for round-tripping through the beloved decompiler.
02303  */
02304 #define JSPROP_IMPLICIT_FUNCTION_NAMESPACE      0x100
02305 
02306 static jssrcnote
02307 SrcNoteForPropOp(JSParseNode *pn, JSOp op)
02308 {
02309     return ((op == JSOP_GETMETHOD &&
02310              !(pn->pn_attrs & JSPROP_IMPLICIT_FUNCTION_NAMESPACE)) ||
02311             op == JSOP_SETMETHOD)
02312            ? SRC_METHODBASE
02313            : SRC_PCBASE;
02314 }
02315 
02316 static JSBool
02317 EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
02318 {
02319     JSParseNode *pn2, *pndot, *pnup, *pndown;
02320     ptrdiff_t top;
02321 
02322     pn2 = pn->pn_expr;
02323     if (op == JSOP_GETPROP &&
02324         pn->pn_type == TOK_DOT &&
02325         pn2->pn_type == TOK_NAME) {
02326         /* Try to optimize arguments.length into JSOP_ARGCNT. */
02327         if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE))
02328             return JS_FALSE;
02329         if (pn2->pn_op == JSOP_ARGUMENTS &&
02330             pn->pn_atom == cx->runtime->atomState.lengthAtom) {
02331             return js_Emit1(cx, cg, JSOP_ARGCNT) >= 0;
02332         }
02333     }
02334 
02335     /*
02336      * If the object operand is also a dotted property reference, reverse the
02337      * list linked via pn_expr temporarily so we can iterate over it from the
02338      * bottom up (reversing again as we go), to avoid excessive recursion.
02339      */
02340     if (pn2->pn_type == TOK_DOT) {
02341         pndot = pn2;
02342         pnup = NULL;
02343         top = CG_OFFSET(cg);
02344         for (;;) {
02345             /* Reverse pndot->pn_expr to point up, not down. */
02346             pndot->pn_offset = top;
02347             pndown = pndot->pn_expr;
02348             pndot->pn_expr = pnup;
02349             if (pndown->pn_type != TOK_DOT)
02350                 break;
02351             pnup = pndot;
02352             pndot = pndown;
02353         }
02354 
02355         /* pndown is a primary expression, not a dotted property reference. */
02356         if (!js_EmitTree(cx, cg, pndown))
02357             return JS_FALSE;
02358 
02359         do {
02360             /* Walk back up the list, emitting annotated name ops. */
02361             if (js_NewSrcNote2(cx, cg, SrcNoteForPropOp(pndot, pndot->pn_op),
02362                                CG_OFFSET(cg) - pndown->pn_offset) < 0) {
02363                 return JS_FALSE;
02364             }
02365             if (!EmitAtomOp(cx, pndot, pndot->pn_op, cg))
02366                 return JS_FALSE;
02367 
02368             /* Reverse the pn_expr link again. */
02369             pnup = pndot->pn_expr;
02370             pndot->pn_expr = pndown;
02371             pndown = pndot;
02372         } while ((pndot = pnup) != NULL);
02373     } else {
02374         if (!js_EmitTree(cx, cg, pn2))
02375             return JS_FALSE;
02376     }
02377 
02378     if (js_NewSrcNote2(cx, cg, SrcNoteForPropOp(pn, op),
02379                        CG_OFFSET(cg) - pn2->pn_offset) < 0) {
02380         return JS_FALSE;
02381     }
02382     if (!pn->pn_atom) {
02383         JS_ASSERT(op == JSOP_IMPORTALL);
02384         if (js_Emit1(cx, cg, op) < 0)
02385             return JS_FALSE;
02386     } else {
02387         if (!EmitAtomOp(cx, pn, op, cg))
02388             return JS_FALSE;
02389     }
02390     return JS_TRUE;
02391 }
02392 
02393 static JSBool
02394 EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
02395 {
02396     ptrdiff_t top;
02397     JSParseNode *left, *right, *next, ltmp, rtmp;
02398     jsint slot;
02399 
02400     top = CG_OFFSET(cg);
02401     if (pn->pn_arity == PN_LIST) {
02402         /* Left-associative operator chain to avoid too much recursion. */
02403         JS_ASSERT(pn->pn_op == JSOP_GETELEM || pn->pn_op == JSOP_IMPORTELEM);
02404         JS_ASSERT(pn->pn_count >= 3);
02405         left = pn->pn_head;
02406         right = PN_LAST(pn);
02407         next = left->pn_next;
02408         JS_ASSERT(next != right);
02409 
02410         /*
02411          * Try to optimize arguments[0][j]... into JSOP_ARGSUB<0> followed by
02412          * one or more index expression and JSOP_GETELEM op pairs.
02413          */
02414         if (left->pn_type == TOK_NAME && next->pn_type == TOK_NUMBER) {
02415             if (!BindNameToSlot(cx, &cg->treeContext, left, JS_FALSE))
02416                 return JS_FALSE;
02417             if (left->pn_op == JSOP_ARGUMENTS &&
02418                 JSDOUBLE_IS_INT(next->pn_dval, slot) &&
02419                 (jsuint)slot < JS_BIT(16)) {
02420                 left->pn_offset = next->pn_offset = top;
02421                 EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot);
02422                 left = next;
02423                 next = left->pn_next;
02424             }
02425         }
02426 
02427         /*
02428          * Check whether we generated JSOP_ARGSUB, just above, and have only
02429          * one more index expression to emit.  Given arguments[0][j], we must
02430          * skip the while loop altogether, falling through to emit code for j
02431          * (in the subtree referenced by right), followed by the annotated op,
02432          * at the bottom of this function.
02433          */
02434         JS_ASSERT(next != right || pn->pn_count == 3);
02435         if (left == pn->pn_head) {
02436             if (!js_EmitTree(cx, cg, left))
02437                 return JS_FALSE;
02438         }
02439         while (next != right) {
02440             if (!js_EmitTree(cx, cg, next))
02441                 return JS_FALSE;
02442             if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0)
02443                 return JS_FALSE;
02444             if (js_Emit1(cx, cg, JSOP_GETELEM) < 0)
02445                 return JS_FALSE;
02446             next = next->pn_next;
02447         }
02448     } else {
02449         if (pn->pn_arity == PN_NAME) {
02450             /*
02451              * Set left and right so pn appears to be a TOK_LB node, instead
02452              * of a TOK_DOT node.  See the TOK_FOR/IN case in js_EmitTree, and
02453              * EmitDestructuringOps nearer below.  In the destructuring case,
02454              * the base expression (pn_expr) of the name may be null, which
02455              * means we have to emit a JSOP_BINDNAME.
02456              */
02457             left = pn->pn_expr;
02458             if (!left) {
02459                 left = &ltmp;
02460                 left->pn_type = TOK_OBJECT;
02461                 left->pn_op = JSOP_BINDNAME;
02462                 left->pn_arity = PN_NULLARY;
02463                 left->pn_pos = pn->pn_pos;
02464                 left->pn_atom = pn->pn_atom;
02465             }
02466             right = &rtmp;
02467             right->pn_type = TOK_STRING;
02468             JS_ASSERT(ATOM_IS_STRING(pn->pn_atom));
02469             right->pn_op = js_IsIdentifier(ATOM_TO_STRING(pn->pn_atom))
02470                            ? JSOP_QNAMEPART
02471                            : JSOP_STRING;
02472             right->pn_arity = PN_NULLARY;
02473             right->pn_pos = pn->pn_pos;
02474             right->pn_atom = pn->pn_atom;
02475         } else {
02476             JS_ASSERT(pn->pn_arity == PN_BINARY);
02477             left = pn->pn_left;
02478             right = pn->pn_right;
02479         }
02480 
02481         /* Try to optimize arguments[0] (e.g.) into JSOP_ARGSUB<0>. */
02482         if (op == JSOP_GETELEM &&
02483             left->pn_type == TOK_NAME &&
02484             right->pn_type == TOK_NUMBER) {
02485             if (!BindNameToSlot(cx, &cg->treeContext, left, JS_FALSE))
02486                 return JS_FALSE;
02487             if (left->pn_op == JSOP_ARGUMENTS &&
02488                 JSDOUBLE_IS_INT(right->pn_dval, slot) &&
02489                 (jsuint)slot < JS_BIT(16)) {
02490                 left->pn_offset = right->pn_offset = top;
02491                 EMIT_UINT16_IMM_OP(JSOP_ARGSUB, (jsatomid)slot);
02492                 return JS_TRUE;
02493             }
02494         }
02495 
02496         if (!js_EmitTree(cx, cg, left))
02497             return JS_FALSE;
02498     }
02499 
02500     /* The right side of the descendant operator is implicitly quoted. */
02501     JS_ASSERT(op != JSOP_DESCENDANTS || right->pn_type != TOK_STRING ||
02502               right->pn_op == JSOP_QNAMEPART);
02503     if (!js_EmitTree(cx, cg, right))
02504         return JS_FALSE;
02505     if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0)
02506         return JS_FALSE;
02507     return js_Emit1(cx, cg, op) >= 0;
02508 }
02509 
02510 static JSBool
02511 EmitNumberOp(JSContext *cx, jsdouble dval, JSCodeGenerator *cg)
02512 {
02513     jsint ival;
02514     jsatomid atomIndex;
02515     ptrdiff_t off;
02516     jsbytecode *pc;
02517     JSAtom *atom;
02518     JSAtomListElement *ale;
02519 
02520     if (JSDOUBLE_IS_INT(dval, ival) && INT_FITS_IN_JSVAL(ival)) {
02521         if (ival == 0)
02522             return js_Emit1(cx, cg, JSOP_ZERO) >= 0;
02523         if (ival == 1)
02524             return js_Emit1(cx, cg, JSOP_ONE) >= 0;
02525 
02526         atomIndex = (jsatomid)ival;
02527         if (atomIndex < JS_BIT(16)) {
02528             EMIT_UINT16_IMM_OP(JSOP_UINT16, atomIndex);
02529             return JS_TRUE;
02530         }
02531 
02532         if (atomIndex < JS_BIT(24)) {
02533             off = js_EmitN(cx, cg, JSOP_UINT24, 3);
02534             if (off < 0)
02535                 return JS_FALSE;
02536             pc = CG_CODE(cg, off);
02537             SET_LITERAL_INDEX(pc, atomIndex);
02538             return JS_TRUE;
02539         }
02540 
02541         atom = js_AtomizeInt(cx, ival, 0);
02542     } else {
02543         atom = js_AtomizeDouble(cx, dval, 0);
02544     }
02545     if (!atom)
02546         return JS_FALSE;
02547 
02548     ale = js_IndexAtom(cx, atom, &cg->atomList);
02549     if (!ale)
02550         return JS_FALSE;
02551     return EmitAtomIndexOp(cx, JSOP_NUMBER, ALE_INDEX(ale), cg);
02552 }
02553 
02554 static JSBool
02555 EmitSwitch(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
02556            JSStmtInfo *stmtInfo)
02557 {
02558     JSOp switchOp;
02559     JSBool ok, hasDefault, constPropagated;
02560     ptrdiff_t top, off, defaultOffset;
02561     JSParseNode *pn2, *pn3, *pn4;
02562     uint32 caseCount, tableLength;
02563     JSParseNode **table;
02564     jsdouble d;
02565     jsint i, low, high;
02566     jsval v;
02567     JSAtom *atom;
02568     JSAtomListElement *ale;
02569     intN noteIndex;
02570     size_t switchSize, tableSize;
02571     jsbytecode *pc, *savepc;
02572 #if JS_HAS_BLOCK_SCOPE
02573     JSObject *obj;
02574     jsint count;
02575 #endif
02576 
02577     /* Try for most optimal, fall back if not dense ints, and per ECMAv2. */
02578     switchOp = JSOP_TABLESWITCH;
02579     ok = JS_TRUE;
02580     hasDefault = constPropagated = JS_FALSE;
02581     defaultOffset = -1;
02582 
02583     /*
02584      * If the switch contains let variables scoped by its body, model the
02585      * resulting block on the stack first, before emitting the discriminant's
02586      * bytecode (in case the discriminant contains a stack-model dependency
02587      * such as a let expression).
02588      */
02589     pn2 = pn->pn_right;
02590 #if JS_HAS_BLOCK_SCOPE
02591     if (pn2->pn_type == TOK_LEXICALSCOPE) {
02592         atom = pn2->pn_atom;
02593         obj = ATOM_TO_OBJECT(atom);
02594         OBJ_SET_BLOCK_DEPTH(cx, obj, cg->stackDepth);
02595 
02596         /*
02597          * Push the body's block scope before discriminant code-gen for proper
02598          * static block scope linkage in case the discriminant contains a let
02599          * expression.  The block's locals must lie under the discriminant on
02600          * the stack so that case-dispatch bytecodes can find the discriminant
02601          * on top of stack.
02602          */
02603         js_PushBlockScope(&cg->treeContext, stmtInfo, atom, -1);
02604         stmtInfo->type = STMT_SWITCH;
02605 
02606         count = OBJ_BLOCK_COUNT(cx, obj);
02607         cg->stackDepth += count;
02608         if ((uintN)cg->stackDepth > cg->maxStackDepth)
02609             cg->maxStackDepth = cg->stackDepth;
02610 
02611         /* Emit JSOP_ENTERBLOCK before code to evaluate the discriminant. */
02612         ale = js_IndexAtom(cx, atom, &cg->atomList);
02613         if (!ale)
02614             return JS_FALSE;
02615         EMIT_ATOM_INDEX_OP(JSOP_ENTERBLOCK, ALE_INDEX(ale));
02616 
02617         /*
02618          * Pop the switch's statement info around discriminant code-gen.  Note
02619          * how this leaves cg->treeContext.blockChain referencing the switch's
02620          * block scope object, which is necessary for correct block parenting
02621          * in the case where the discriminant contains a let expression.
02622          */
02623         cg->treeContext.topStmt = stmtInfo->down;
02624         cg->treeContext.topScopeStmt = stmtInfo->downScope;
02625     }
02626 #ifdef __GNUC__
02627     else {
02628         atom = NULL;
02629         count = -1;
02630     }
02631 #endif
02632 #endif
02633 
02634     /*
02635      * Emit code for the discriminant first (or nearly first, in the case of a
02636      * switch whose body is a block scope).
02637      */
02638     if (!js_EmitTree(cx, cg, pn->pn_left))
02639         return JS_FALSE;
02640 
02641     /* Switch bytecodes run from here till end of final case. */
02642     top = CG_OFFSET(cg);
02643 #if !JS_HAS_BLOCK_SCOPE
02644     js_PushStatement(&cg->treeContext, stmtInfo, STMT_SWITCH, top);
02645 #else
02646     if (pn2->pn_type == TOK_LC) {
02647         js_PushStatement(&cg->treeContext, stmtInfo, STMT_SWITCH, top);
02648     } else {
02649         /* Re-push the switch's statement info record. */
02650         cg->treeContext.topStmt = cg->treeContext.topScopeStmt = stmtInfo;
02651 
02652         /* Set the statement info record's idea of top. */
02653         stmtInfo->update = top;
02654 
02655         /* Advance pn2 to refer to the switch case list. */
02656         pn2 = pn2->pn_expr;
02657     }
02658 #endif
02659 
02660     caseCount = pn2->pn_count;
02661     tableLength = 0;
02662     table = NULL;
02663 
02664     if (caseCount == 0 ||
02665         (caseCount == 1 &&
02666          (hasDefault = (pn2->pn_head->pn_type == TOK_DEFAULT)))) {
02667         caseCount = 0;
02668         low = 0;
02669         high = -1;
02670     } else {
02671 #define INTMAP_LENGTH   256
02672         jsbitmap intmap_space[INTMAP_LENGTH];
02673         jsbitmap *intmap = NULL;
02674         int32 intmap_bitlen = 0;
02675 
02676         low  = JSVAL_INT_MAX;
02677         high = JSVAL_INT_MIN;
02678 
02679         for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
02680             if (pn3->pn_type == TOK_DEFAULT) {
02681                 hasDefault = JS_TRUE;
02682                 caseCount--;    /* one of the "cases" was the default */
02683                 continue;
02684             }
02685 
02686             JS_ASSERT(pn3->pn_type == TOK_CASE);
02687             if (switchOp == JSOP_CONDSWITCH)
02688                 continue;
02689 
02690             pn4 = pn3->pn_left;
02691             switch (pn4->pn_type) {
02692               case TOK_NUMBER:
02693                 d = pn4->pn_dval;
02694                 if (JSDOUBLE_IS_INT(d, i) && INT_FITS_IN_JSVAL(i)) {
02695                     pn3->pn_val = INT_TO_JSVAL(i);
02696                 } else {
02697                     atom = js_AtomizeDouble(cx, d, 0);
02698                     if (!atom) {
02699                         ok = JS_FALSE;
02700                         goto release;
02701                     }
02702                     pn3->pn_val = ATOM_KEY(atom);
02703                 }
02704                 break;
02705               case TOK_STRING:
02706                 pn3->pn_val = ATOM_KEY(pn4->pn_atom);
02707                 break;
02708               case TOK_NAME:
02709                 if (!pn4->pn_expr) {
02710                     ok = js_LookupCompileTimeConstant(cx, cg, pn4->pn_atom, &v);
02711                     if (!ok)
02712                         goto release;
02713                     if (!JSVAL_IS_VOID(v)) {
02714                         pn3->pn_val = v;
02715                         constPropagated = JS_TRUE;
02716                         break;
02717                     }
02718                 }
02719                 /* FALL THROUGH */
02720               case TOK_PRIMARY:
02721                 if (pn4->pn_op == JSOP_TRUE) {
02722                     pn3->pn_val = JSVAL_TRUE;
02723                     break;
02724                 }
02725                 if (pn4->pn_op == JSOP_FALSE) {
02726                     pn3->pn_val = JSVAL_FALSE;
02727                     break;
02728                 }
02729                 /* FALL THROUGH */
02730               default:
02731                 switchOp = JSOP_CONDSWITCH;
02732                 continue;
02733             }
02734 
02735             JS_ASSERT(JSVAL_IS_NUMBER(pn3->pn_val) ||
02736                       JSVAL_IS_STRING(pn3->pn_val) ||
02737                       JSVAL_IS_BOOLEAN(pn3->pn_val));
02738 
02739             if (switchOp != JSOP_TABLESWITCH)
02740                 continue;
02741             if (!JSVAL_IS_INT(pn3->pn_val)) {
02742                 switchOp = JSOP_LOOKUPSWITCH;
02743                 continue;
02744             }
02745             i = JSVAL_TO_INT(pn3->pn_val);
02746             if ((jsuint)(i + (jsint)JS_BIT(15)) >= (jsuint)JS_BIT(16)) {
02747                 switchOp = JSOP_LOOKUPSWITCH;
02748                 continue;
02749             }
02750             if (i < low)
02751                 low = i;
02752             if (high < i)
02753                 high = i;
02754 
02755             /*
02756              * Check for duplicates, which require a JSOP_LOOKUPSWITCH.
02757              * We bias i by 65536 if it's negative, and hope that's a rare
02758              * case (because it requires a malloc'd bitmap).
02759              */
02760             if (i < 0)
02761                 i += JS_BIT(16);
02762             if (i >= intmap_bitlen) {
02763                 if (!intmap &&
02764                     i < (INTMAP_LENGTH << JS_BITS_PER_WORD_LOG2)) {
02765                     intmap = intmap_space;
02766                     intmap_bitlen = INTMAP_LENGTH << JS_BITS_PER_WORD_LOG2;
02767                 } else {
02768                     /* Just grab 8K for the worst-case bitmap. */
02769                     intmap_bitlen = JS_BIT(16);
02770                     intmap = (jsbitmap *)
02771                         JS_malloc(cx,
02772                                   (JS_BIT(16) >> JS_BITS_PER_WORD_LOG2)
02773                                   * sizeof(jsbitmap));
02774                     if (!intmap) {
02775                         JS_ReportOutOfMemory(cx);
02776                         return JS_FALSE;
02777                     }
02778                 }
02779                 memset(intmap, 0, intmap_bitlen >> JS_BITS_PER_BYTE_LOG2);
02780             }
02781             if (JS_TEST_BIT(intmap, i)) {
02782                 switchOp = JSOP_LOOKUPSWITCH;
02783                 continue;
02784             }
02785             JS_SET_BIT(intmap, i);
02786         }
02787 
02788       release:
02789         if (intmap && intmap != intmap_space)
02790             JS_free(cx, intmap);
02791         if (!ok)
02792             return JS_FALSE;
02793 
02794         /*
02795          * Compute table length and select lookup instead if overlarge or
02796          * more than half-sparse.
02797          */
02798         if (switchOp == JSOP_TABLESWITCH) {
02799             tableLength = (uint32)(high - low + 1);
02800             if (tableLength >= JS_BIT(16) || tableLength > 2 * caseCount)
02801                 switchOp = JSOP_LOOKUPSWITCH;
02802         } else if (switchOp == JSOP_LOOKUPSWITCH) {
02803             /*
02804              * Lookup switch supports only atom indexes below 64K limit.
02805              * Conservatively estimate the maximum possible index during
02806              * switch generation and use conditional switch if it exceeds
02807              * the limit.
02808              */
02809             if (caseCount + cg->atomList.count > JS_BIT(16))
02810                 switchOp = JSOP_CONDSWITCH;
02811         }
02812     }
02813 
02814     /*
02815      * Emit a note with two offsets: first tells total switch code length,
02816      * second tells offset to first JSOP_CASE if condswitch.
02817      */
02818     noteIndex = js_NewSrcNote3(cx, cg, SRC_SWITCH, 0, 0);
02819     if (noteIndex < 0)
02820         return JS_FALSE;
02821 
02822     if (switchOp == JSOP_CONDSWITCH) {
02823         /*
02824          * 0 bytes of immediate for unoptimized ECMAv2 switch.
02825          */
02826         switchSize = 0;
02827     } else if (switchOp == JSOP_TABLESWITCH) {
02828         /*
02829          * 3 offsets (len, low, high) before the table, 1 per entry.
02830          */
02831         switchSize = (size_t)(JUMP_OFFSET_LEN * (3 + tableLength));
02832     } else {
02833         /*
02834          * JSOP_LOOKUPSWITCH:
02835          * 1 offset (len) and 1 atom index (npairs) before the table,
02836          * 1 atom index and 1 jump offset per entry.
02837          */
02838         switchSize = (size_t)(JUMP_OFFSET_LEN + ATOM_INDEX_LEN +
02839                               (ATOM_INDEX_LEN + JUMP_OFFSET_LEN) * caseCount);
02840     }
02841 
02842     /*
02843      * Emit switchOp followed by switchSize bytes of jump or lookup table.
02844      *
02845      * If switchOp is JSOP_LOOKUPSWITCH or JSOP_TABLESWITCH, it is crucial
02846      * to emit the immediate operand(s) by which bytecode readers such as
02847      * BuildSpanDepTable discover the length of the switch opcode *before*
02848      * calling js_SetJumpOffset (which may call BuildSpanDepTable).  It's
02849      * also important to zero all unknown jump offset immediate operands,
02850      * so they can be converted to span dependencies with null targets to
02851      * be computed later (js_EmitN zeros switchSize bytes after switchOp).
02852      */
02853     if (js_EmitN(cx, cg, switchOp, switchSize) < 0)
02854         return JS_FALSE;
02855 
02856     off = -1;
02857     if (switchOp == JSOP_CONDSWITCH) {
02858         intN caseNoteIndex = -1;
02859         JSBool beforeCases = JS_TRUE;
02860 
02861         /* Emit code for evaluating cases and jumping to case statements. */
02862         for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
02863             pn4 = pn3->pn_left;
02864             if (pn4 && !js_EmitTree(cx, cg, pn4))
02865                 return JS_FALSE;
02866             if (caseNoteIndex >= 0) {
02867                 /* off is the previous JSOP_CASE's bytecode offset. */
02868                 if (!js_SetSrcNoteOffset(cx, cg, (uintN)caseNoteIndex, 0,
02869                                          CG_OFFSET(cg) - off)) {
02870                     return JS_FALSE;
02871                 }
02872             }
02873             if (!pn4) {
02874                 JS_ASSERT(pn3->pn_type == TOK_DEFAULT);
02875                 continue;
02876             }
02877             caseNoteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0);
02878             if (caseNoteIndex < 0)
02879                 return JS_FALSE;
02880             off = EmitJump(cx, cg, JSOP_CASE, 0);
02881             if (off < 0)
02882                 return JS_FALSE;
02883             pn3->pn_offset = off;
02884             if (beforeCases) {
02885                 uintN noteCount, noteCountDelta;
02886 
02887                 /* Switch note's second offset is to first JSOP_CASE. */
02888                 noteCount = CG_NOTE_COUNT(cg);
02889                 if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1,
02890                                          off - top)) {
02891                     return JS_FALSE;
02892                 }
02893                 noteCountDelta = CG_NOTE_COUNT(cg) - noteCount;
02894                 if (noteCountDelta != 0)
02895                     caseNoteIndex += noteCountDelta;
02896                 beforeCases = JS_FALSE;
02897             }
02898         }
02899 
02900         /*
02901          * If we didn't have an explicit default (which could fall in between
02902          * cases, preventing us from fusing this js_SetSrcNoteOffset with the
02903          * call in the loop above), link the last case to the implicit default
02904          * for the decompiler.
02905          */
02906         if (!hasDefault &&
02907             caseNoteIndex >= 0 &&
02908             !js_SetSrcNoteOffset(cx, cg, (uintN)caseNoteIndex, 0,
02909                                  CG_OFFSET(cg) - off)) {
02910             return JS_FALSE;
02911         }
02912 
02913         /* Emit default even if no explicit default statement. */
02914         defaultOffset = EmitJump(cx, cg, JSOP_DEFAULT, 0);
02915         if (defaultOffset < 0)
02916             return JS_FALSE;
02917     } else {
02918         pc = CG_CODE(cg, top + JUMP_OFFSET_LEN);
02919 
02920         if (switchOp == JSOP_TABLESWITCH) {
02921             /* Fill in switch bounds, which we know fit in 16-bit offsets. */
02922             SET_JUMP_OFFSET(pc, low);
02923             pc += JUMP_OFFSET_LEN;
02924             SET_JUMP_OFFSET(pc, high);
02925             pc += JUMP_OFFSET_LEN;
02926 
02927             /*
02928              * Use malloc to avoid arena bloat for programs with many switches.
02929              * We free table if non-null at label out, so all control flow must
02930              * exit this function through goto out or goto bad.
02931              */
02932             if (tableLength != 0) {
02933                 tableSize = (size_t)tableLength * sizeof *table;
02934                 table = (JSParseNode **) JS_malloc(cx, tableSize);
02935                 if (!table)
02936                     return JS_FALSE;
02937                 memset(table, 0, tableSize);
02938                 for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
02939                     if (pn3->pn_type == TOK_DEFAULT)
02940                         continue;
02941                     i = JSVAL_TO_INT(pn3->pn_val);
02942                     i -= low;
02943                     JS_ASSERT((uint32)i < tableLength);
02944                     table[i] = pn3;
02945                 }
02946             }
02947         } else {
02948             JS_ASSERT(switchOp == JSOP_LOOKUPSWITCH);
02949 
02950             /* Fill in the number of cases. */
02951             SET_ATOM_INDEX(pc, caseCount);
02952             pc += ATOM_INDEX_LEN;
02953         }
02954 
02955         /*
02956          * After this point, all control flow involving JSOP_TABLESWITCH
02957          * must set ok and goto out to exit this function.  To keep things
02958          * simple, all switchOp cases exit that way.
02959          */
02960         if (cg->spanDeps) {
02961             /*
02962              * We have already generated at least one big jump so we must
02963              * explicitly add span dependencies for the switch jumps. When
02964              * called below, js_SetJumpOffset can only do it when patching
02965              * the first big jump or when cg->spanDeps is null.
02966              */
02967             if (!AddSwitchSpanDeps(cx, cg, CG_CODE(cg, top)))
02968                 goto bad;
02969         }
02970 
02971         if (constPropagated) {
02972             /*
02973              * Skip switchOp, as we are not setting jump offsets in the two
02974              * for loops below.  We'll restore CG_NEXT(cg) from savepc after,
02975              * unless there was an error.
02976              */
02977             savepc = CG_NEXT(cg);
02978             CG_NEXT(cg) = pc + 1;
02979             if (switchOp == JSOP_TABLESWITCH) {
02980                 for (i = 0; i < (jsint)tableLength; i++) {
02981                     pn3 = table[i];
02982                     if (pn3 &&
02983                         (pn4 = pn3->pn_left) != NULL &&
02984                         pn4->pn_type == TOK_NAME) {
02985                         /* Note a propagated constant with the const's name. */
02986                         JS_ASSERT(!pn4->pn_expr);
02987                         ale = js_IndexAtom(cx, pn4->pn_atom, &cg->atomList);
02988                         if (!ale)
02989                             goto bad;
02990                         CG_NEXT(cg) = pc;
02991                         if (js_NewSrcNote2(cx, cg, SRC_LABEL, (ptrdiff_t)
02992                                            ALE_INDEX(ale)) < 0) {
02993                             goto bad;
02994                         }
02995                     }
02996                     pc += JUMP_OFFSET_LEN;
02997                 }
02998             } else {
02999                 for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
03000                     pn4 = pn3->pn_left;
03001                     if (pn4 && pn4->pn_type == TOK_NAME) {
03002                         /* Note a propagated constant with the const's name. */
03003                         JS_ASSERT(!pn4->pn_expr);
03004                         ale = js_IndexAtom(cx, pn4->pn_atom, &cg->atomList);
03005                         if (!ale)
03006                             goto bad;
03007                         CG_NEXT(cg) = pc;
03008                         if (js_NewSrcNote2(cx, cg, SRC_LABEL, (ptrdiff_t)
03009                                            ALE_INDEX(ale)) < 0) {
03010                             goto bad;
03011                         }
03012                     }
03013                     pc += ATOM_INDEX_LEN + JUMP_OFFSET_LEN;
03014                 }
03015             }
03016             CG_NEXT(cg) = savepc;
03017         }
03018     }
03019 
03020     /* Emit code for each case's statements, copying pn_offset up to pn3. */
03021     for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
03022         if (switchOp == JSOP_CONDSWITCH && pn3->pn_type != TOK_DEFAULT)
03023             CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, pn3->pn_offset);
03024         pn4 = pn3->pn_right;
03025         ok = js_EmitTree(cx, cg, pn4);
03026         if (!ok)
03027             goto out;
03028         pn3->pn_offset = pn4->pn_offset;
03029         if (pn3->pn_type == TOK_DEFAULT)
03030             off = pn3->pn_offset - top;
03031     }
03032 
03033     if (!hasDefault) {
03034         /* If no default case, offset for default is to end of switch. */
03035         off = CG_OFFSET(cg) - top;
03036     }
03037 
03038     /* We better have set "off" by now. */
03039     JS_ASSERT(off != -1);
03040 
03041     /* Set the default offset (to end of switch if no default). */
03042     if (switchOp == JSOP_CONDSWITCH) {
03043         pc = NULL;
03044         JS_ASSERT(defaultOffset != -1);
03045         ok = js_SetJumpOffset(cx, cg, CG_CODE(cg, defaultOffset),
03046                               off - (defaultOffset - top));
03047         if (!ok)
03048             goto out;
03049     } else {
03050         pc = CG_CODE(cg, top);
03051         ok = js_SetJumpOffset(cx, cg, pc, off);
03052         if (!ok)
03053             goto out;
03054         pc += JUMP_OFFSET_LEN;
03055     }
03056 
03057     /* Set the SRC_SWITCH note's offset operand to tell end of switch. */
03058     off = CG_OFFSET(cg) - top;
03059     ok = js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, off);
03060     if (!ok)
03061         goto out;
03062 
03063     if (switchOp == JSOP_TABLESWITCH) {
03064         /* Skip over the already-initialized switch bounds. */
03065         pc += 2 * JUMP_OFFSET_LEN;
03066 
03067         /* Fill in the jump table, if there is one. */
03068         for (i = 0; i < (jsint)tableLength; i++) {
03069             pn3 = table[i];
03070             off = pn3 ? pn3->pn_offset - top : 0;
03071             ok = js_SetJumpOffset(cx, cg, pc, off);
03072             if (!ok)
03073                 goto out;
03074             pc += JUMP_OFFSET_LEN;
03075         }
03076     } else if (switchOp == JSOP_LOOKUPSWITCH) {
03077         /* Skip over the already-initialized number of cases. */
03078         pc += ATOM_INDEX_LEN;
03079 
03080         for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
03081             if (pn3->pn_type == TOK_DEFAULT)
03082                 continue;
03083             atom = js_AtomizeValue(cx, pn3->pn_val, 0);
03084             if (!atom)
03085                 goto bad;
03086             ale = js_IndexAtom(cx, atom, &cg->atomList);
03087             if (!ale)
03088                 goto bad;
03089             SET_ATOM_INDEX(pc, ALE_INDEX(ale));
03090             pc += ATOM_INDEX_LEN;
03091 
03092             off = pn3->pn_offset - top;
03093             ok = js_SetJumpOffset(cx, cg, pc, off);
03094             if (!ok)
03095                 goto out;
03096             pc += JUMP_OFFSET_LEN;
03097         }
03098     }
03099 
03100 out:
03101     if (table)
03102         JS_free(cx, table);
03103     if (ok) {
03104         ok = js_PopStatementCG(cx, cg);
03105 
03106 #if JS_HAS_BLOCK_SCOPE
03107         if (ok && pn->pn_right->pn_type == TOK_LEXICALSCOPE) {
03108             EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count);
03109             cg->stackDepth -= count;
03110         }
03111 #endif
03112     }
03113     return ok;
03114 
03115 bad:
03116     ok = JS_FALSE;
03117     goto out;
03118 }
03119 
03120 JSBool
03121 js_EmitFunctionBytecode(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body)
03122 {
03123     if (!js_AllocTryNotes(cx, cg))
03124         return JS_FALSE;
03125 
03126     if (cg->treeContext.flags & TCF_FUN_IS_GENERATOR) {
03127         /* JSOP_GENERATOR must be the first instruction. */
03128         CG_SWITCH_TO_PROLOG(cg);
03129         JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg));
03130         if (js_Emit1(cx, cg, JSOP_GENERATOR) < 0)
03131             return JS_FALSE;
03132         CG_SWITCH_TO_MAIN(cg);
03133     }
03134 
03135     return js_EmitTree(cx, cg, body) &&
03136            js_Emit1(cx, cg, JSOP_STOP) >= 0;
03137 }
03138 
03139 JSBool
03140 js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body,
03141                     JSFunction *fun)
03142 {
03143     JSStackFrame *fp, frame;
03144     JSObject *funobj;
03145     JSBool ok;
03146 
03147     fp = cx->fp;
03148     funobj = fun->object;
03149     JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj &&
03150                       fp->scopeChain != funobj));
03151     memset(&frame, 0, sizeof frame);
03152     frame.fun = fun;
03153     frame.varobj = frame.scopeChain = funobj;
03154     frame.down = fp;
03155     frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx)
03156                   ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
03157                   : JSFRAME_COMPILING;
03158     cx->fp = &frame;
03159     ok = js_EmitFunctionBytecode(cx, cg, body);
03160     cx->fp = fp;
03161     if (!ok)
03162         return JS_FALSE;
03163 
03164     if (!js_NewScriptFromCG(cx, cg, fun))
03165         return JS_FALSE;
03166 
03167     JS_ASSERT(FUN_INTERPRETED(fun));
03168     return JS_TRUE;
03169 }
03170 
03171 /* A macro for inlining at the top of js_EmitTree (whence it came). */
03172 #define UPDATE_LINE_NUMBER_NOTES(cx, cg, pn)                                  \
03173     JS_BEGIN_MACRO                                                            \
03174         uintN line_ = (pn)->pn_pos.begin.lineno;                              \
03175         uintN delta_ = line_ - CG_CURRENT_LINE(cg);                           \
03176         if (delta_ != 0) {                                                    \
03177             /*                                                                \
03178              * Encode any change in the current source line number by using   \
03179              * either several SRC_NEWLINE notes or just one SRC_SETLINE note, \
03180              * whichever consumes less space.                                 \
03181              *                                                                \
03182              * NB: We handle backward line number deltas (possible with for   \
03183              * loops where the update part is emitted after the body, but its \
03184              * line number is <= any line number in the body) here by letting \
03185              * unsigned delta_ wrap to a very large number, which triggers a  \
03186              * SRC_SETLINE.                                                   \
03187              */                                                               \
03188             CG_CURRENT_LINE(cg) = line_;                                      \
03189             if (delta_ >= (uintN)(2 + ((line_ > SN_3BYTE_OFFSET_MASK)<<1))) { \
03190                 if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)line_) < 0)\
03191                     return JS_FALSE;                                          \
03192             } else {                                                          \
03193                 do {                                                          \
03194                     if (js_NewSrcNote(cx, cg, SRC_NEWLINE) < 0)               \
03195                         return JS_FALSE;                                      \
03196                 } while (--delta_ != 0);                                      \
03197             }                                                                 \
03198         }                                                                     \
03199     JS_END_MACRO
03200 
03201 /* A function, so that we avoid macro-bloating all the other callsites. */
03202 static JSBool
03203 UpdateLineNumberNotes(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
03204 {
03205     UPDATE_LINE_NUMBER_NOTES(cx, cg, pn);
03206     return JS_TRUE;
03207 }
03208 
03209 static JSBool
03210 MaybeEmitVarDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp,
03211                  JSParseNode *pn, jsatomid *result)
03212 {
03213     jsatomid atomIndex;
03214     JSAtomListElement *ale;
03215 
03216     if (pn->pn_slot >= 0) {
03217         atomIndex = (jsatomid) pn->pn_slot;
03218     } else {
03219         ale = js_IndexAtom(cx, pn->pn_atom, &cg->atomList);
03220         if (!ale)
03221             return JS_FALSE;
03222         atomIndex = ALE_INDEX(ale);
03223     }
03224 
03225     if ((js_CodeSpec[pn->pn_op].format & JOF_TYPEMASK) == JOF_CONST &&
03226         (!(cg->treeContext.flags & TCF_IN_FUNCTION) ||
03227          (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT))) {
03228         /* Emit a prolog bytecode to predefine the variable. */
03229         CG_SWITCH_TO_PROLOG(cg);
03230         if (!UpdateLineNumberNotes(cx, cg, pn))
03231             return JS_FALSE;
03232         EMIT_ATOM_INDEX_OP(prologOp, atomIndex);
03233         CG_SWITCH_TO_MAIN(cg);
03234     }
03235 
03236     if (result)
03237         *result = atomIndex;
03238     return JS_TRUE;
03239 }
03240 
03241 #if JS_HAS_DESTRUCTURING
03242 
03243 typedef JSBool
03244 (*DestructuringDeclEmitter)(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp,
03245                             JSParseNode *pn);
03246 
03247 static JSBool
03248 EmitDestructuringDecl(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp,
03249                       JSParseNode *pn)
03250 {
03251     JS_ASSERT(pn->pn_type == TOK_NAME);
03252     if (!BindNameToSlot(cx, &cg->treeContext, pn, prologOp == JSOP_NOP))
03253         return JS_FALSE;
03254 
03255     JS_ASSERT(pn->pn_op != JSOP_ARGUMENTS);
03256     return MaybeEmitVarDecl(cx, cg, prologOp, pn, NULL);
03257 }
03258 
03259 static JSBool
03260 EmitDestructuringDecls(JSContext *cx, JSCodeGenerator *cg, JSOp prologOp,
03261                        JSParseNode *pn)
03262 {
03263     JSParseNode *pn2, *pn3;
03264     DestructuringDeclEmitter emitter;
03265 
03266     if (pn->pn_type == TOK_RB) {
03267         for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
03268             if (pn2->pn_type == TOK_COMMA)
03269                 continue;
03270             emitter = (pn2->pn_type == TOK_NAME)
03271                       ? EmitDestructuringDecl
03272                       : EmitDestructuringDecls;
03273             if (!emitter(cx, cg, prologOp, pn2))
03274                 return JS_FALSE;
03275         }
03276     } else {
03277         JS_ASSERT(pn->pn_type == TOK_RC);
03278         for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
03279             pn3 = pn2->pn_right;
03280             emitter = (pn3->pn_type == TOK_NAME)
03281                       ? EmitDestructuringDecl
03282                       : EmitDestructuringDecls;
03283             if (!emitter(cx, cg, prologOp, pn3))
03284                 return JS_FALSE;
03285         }
03286     }
03287     return JS_TRUE;
03288 }
03289 
03290 static JSBool
03291 EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn);
03292 
03293 static JSBool
03294 EmitDestructuringLHS(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
03295                      JSBool wantpop)
03296 {
03297     jsuint slot;
03298 
03299     /* Skip any parenthesization. */
03300     while (pn->pn_type == TOK_RP)
03301         pn = pn->pn_kid;
03302 
03303     /*
03304      * Now emit the lvalue opcode sequence.  If the lvalue is a nested
03305      * destructuring initialiser-form, call ourselves to handle it, then
03306      * pop the matched value.  Otherwise emit an lvalue bytecode sequence
03307      * ending with a JSOP_ENUMELEM or equivalent op.
03308      */
03309     if (pn->pn_type == TOK_RB || pn->pn_type == TOK_RC) {
03310         if (!EmitDestructuringOpsHelper(cx, cg, pn))
03311             return JS_FALSE;
03312         if (wantpop && js_Emit1(cx, cg, JSOP_POP) < 0)
03313             return JS_FALSE;
03314     } else {
03315         if (pn->pn_type == TOK_NAME &&
03316             !BindNameToSlot(cx, &cg->treeContext, pn, JS_FALSE)) {
03317             return JS_FALSE;
03318         }
03319 
03320         switch (pn->pn_op) {
03321           case JSOP_SETNAME:
03322             /*
03323              * NB: pn is a PN_NAME node, not a PN_BINARY.  Nevertheless,
03324              * we want to emit JSOP_ENUMELEM, which has format JOF_ELEM.
03325              * So here and for JSOP_ENUMCONSTELEM, we use EmitElemOp.
03326              */
03327             if (!EmitElemOp(cx, pn, JSOP_ENUMELEM, cg))
03328                 return JS_FALSE;
03329             break;
03330 
03331           case JSOP_SETCONST:
03332             if (!EmitElemOp(cx, pn, JSOP_ENUMCONSTELEM, cg))
03333                 return JS_FALSE;
03334             break;
03335 
03336           case JSOP_SETLOCAL:
03337             if (wantpop) {
03338                 slot = (jsuint) pn->pn_slot;
03339                 EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, slot);
03340                 break;
03341             }
03342             /* FALL THROUGH */
03343 
03344           case JSOP_SETARG:
03345           case JSOP_SETVAR:
03346           case JSOP_SETGVAR:
03347             slot = (jsuint) pn->pn_slot;
03348             EMIT_UINT16_IMM_OP(pn->pn_op, slot);
03349             if (wantpop && js_Emit1(cx, cg, JSOP_POP) < 0)
03350                 return JS_FALSE;
03351             break;
03352 
03353           default:
03354 #if JS_HAS_LVALUE_RETURN || JS_HAS_XML_SUPPORT
03355           {
03356             ptrdiff_t top;
03357 
03358             top = CG_OFFSET(cg);
03359             if (!js_EmitTree(cx, cg, pn))
03360                 return JS_FALSE;
03361             if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0)
03362                 return JS_FALSE;
03363             if (js_Emit1(cx, cg, JSOP_ENUMELEM) < 0)
03364                 return JS_FALSE;
03365             break;
03366           }
03367 #endif
03368           case JSOP_ENUMELEM:
03369             JS_ASSERT(0);
03370         }
03371     }
03372 
03373     return JS_TRUE;
03374 }
03375 
03376 /*
03377  * Recursive helper for EmitDestructuringOps.
03378  *
03379  * Given a value to destructure on the stack, walk over an object or array
03380  * initialiser at pn, emitting bytecodes to match property values and store
03381  * them in the lvalues identified by the matched property names.
03382  */
03383 static JSBool
03384 EmitDestructuringOpsHelper(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
03385 {
03386     jsuint index;
03387     JSParseNode *pn2, *pn3;
03388     JSBool doElemOp;
03389 
03390 #ifdef DEBUG
03391     intN stackDepth = cg->stackDepth;
03392     JS_ASSERT(stackDepth != 0);
03393     JS_ASSERT(pn->pn_arity == PN_LIST);
03394     JS_ASSERT(pn->pn_type == TOK_RB || pn->pn_type == TOK_RC);
03395 #endif
03396 
03397     if (pn->pn_count == 0) {
03398         /* Emit a DUP;POP sequence for the decompiler. */
03399         return js_Emit1(cx, cg, JSOP_DUP) >= 0 &&
03400                js_Emit1(cx, cg, JSOP_POP) >= 0;
03401     }
03402 
03403     index = 0;
03404     for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
03405         /*
03406          * Duplicate the value being destructured to use as a reference base.
03407          */
03408         if (js_Emit1(cx, cg, JSOP_DUP) < 0)
03409             return JS_FALSE;
03410 
03411         /*
03412          * Now push the property name currently being matched, which is either
03413          * the array initialiser's current index, or the current property name
03414          * "label" on the left of a colon in the object initialiser.  Set pn3
03415          * to the lvalue node, which is in the value-initializing position.
03416          */
03417         doElemOp = JS_TRUE;
03418         if (pn->pn_type == TOK_RB) {
03419             if (!EmitNumberOp(cx, index, cg))
03420                 return JS_FALSE;
03421             pn3 = pn2;
03422         } else {
03423             JS_ASSERT(pn->pn_type == TOK_RC);
03424             JS_ASSERT(pn2->pn_type == TOK_COLON);
03425             pn3 = pn2->pn_left;
03426             if (pn3->pn_type == TOK_NUMBER) {
03427                 /*
03428                  * If we are emitting an object destructuring initialiser,
03429                  * annotate the index op with SRC_INITPROP so we know we are
03430                  * not decompiling an array initialiser.
03431                  */
03432                 if (js_NewSrcNote(cx, cg, SRC_INITPROP) < 0)
03433                     return JS_FALSE;
03434                 if (!EmitNumberOp(cx, pn3->pn_dval, cg))
03435                     return JS_FALSE;
03436             } else {
03437                 JS_ASSERT(pn3->pn_type == TOK_STRING ||
03438                           pn3->pn_type == TOK_NAME);
03439                 if (!EmitAtomOp(cx, pn3, JSOP_GETPROP, cg))
03440                     return JS_FALSE;
03441                 doElemOp = JS_FALSE;
03442             }
03443             pn3 = pn2->pn_right;
03444         }
03445 
03446         if (doElemOp) {
03447             /*
03448              * Ok, get the value of the matching property name.  This leaves
03449              * that value on top of the value being destructured, so the stack
03450              * is one deeper than when we started.
03451              */
03452             if (js_Emit1(cx, cg, JSOP_GETELEM) < 0)
03453                 return JS_FALSE;
03454             JS_ASSERT(cg->stackDepth == stackDepth + 1);
03455         }
03456 
03457         /* Nullary comma node makes a hole in the array destructurer. */
03458         if (pn3->pn_type == TOK_COMMA && pn3->pn_arity == PN_NULLARY) {
03459             JS_ASSERT(pn->pn_type == TOK_RB);
03460             JS_ASSERT(pn2 == pn3);
03461             if (js_Emit1(cx, cg, JSOP_POP) < 0)
03462                 return JS_FALSE;
03463         } else {
03464             if (!EmitDestructuringLHS(cx, cg, pn3, JS_TRUE))
03465                 return JS_FALSE;
03466         }
03467 
03468         JS_ASSERT(cg->stackDepth == stackDepth);
03469         ++index;
03470     }
03471 
03472     return JS_TRUE;
03473 }
03474 
03475 static ptrdiff_t
03476 OpToDeclType(JSOp op)
03477 {
03478     switch (op) {
03479       case JSOP_NOP:
03480         return SRC_DECL_LET;
03481       case JSOP_DEFCONST:
03482         return SRC_DECL_CONST;
03483       case JSOP_DEFVAR:
03484         return SRC_DECL_VAR;
03485       default:
03486         return SRC_DECL_NONE;
03487     }
03488 }
03489 
03490 static JSBool
03491 EmitDestructuringOps(JSContext *cx, JSCodeGenerator *cg, JSOp declOp,
03492                      JSParseNode *pn)
03493 {
03494     /*
03495      * If we're called from a variable declaration, help the decompiler by
03496      * annotating the first JSOP_DUP that EmitDestructuringOpsHelper emits.
03497      * If the destructuring initialiser is empty, our helper will emit a
03498      * JSOP_DUP followed by a JSOP_POP for the decompiler.
03499      */
03500     if (js_NewSrcNote2(cx, cg, SRC_DESTRUCT, OpToDeclType(declOp)) < 0)
03501         return JS_FALSE;
03502 
03503     /*
03504      * Call our recursive helper to emit the destructuring assignments and
03505      * related stack manipulations.
03506      */
03507     return EmitDestructuringOpsHelper(cx, cg, pn);
03508 }
03509 
03510 static JSBool
03511 EmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp declOp,
03512                     JSParseNode *lhs, JSParseNode *rhs)
03513 {
03514     jsuint depth, limit, slot;
03515     JSParseNode *pn;
03516 
03517     depth = limit = (uintN) cg->stackDepth;
03518     for (pn = rhs->pn_head; pn; pn = pn->pn_next) {
03519         if (limit == JS_BIT(16)) {
03520             js_ReportCompileErrorNumber(cx, rhs,
03521                                         JSREPORT_PN | JSREPORT_ERROR,
03522                                         JSMSG_ARRAY_INIT_TOO_BIG);
03523             return JS_FALSE;
03524         }
03525 
03526         if (pn->pn_type == TOK_COMMA) {
03527             if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
03528                 return JS_FALSE;
03529         } else {
03530             JS_ASSERT(pn->pn_type != TOK_DEFSHARP);
03531             if (!js_EmitTree(cx, cg, pn))
03532                 return JS_FALSE;
03533         }
03534         ++limit;
03535     }
03536 
03537     if (js_NewSrcNote2(cx, cg, SRC_GROUPASSIGN, OpToDeclType(declOp)) < 0)
03538         return JS_FALSE;
03539 
03540     slot = depth;
03541     for (pn = lhs->pn_head; pn; pn = pn->pn_next) {
03542         if (slot < limit) {
03543             EMIT_UINT16_IMM_OP(JSOP_GETLOCAL, slot);
03544         } else {
03545             if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
03546                 return JS_FALSE;
03547         }
03548         if (pn->pn_type == TOK_COMMA && pn->pn_arity == PN_NULLARY) {
03549             if (js_Emit1(cx, cg, JSOP_POP) < 0)
03550                 return JS_FALSE;
03551         } else {
03552             if (!EmitDestructuringLHS(cx, cg, pn, pn->pn_next != NULL))
03553                 return JS_FALSE;
03554         }
03555         ++slot;
03556     }
03557 
03558     EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth);
03559     cg->stackDepth = (uintN) depth;
03560     return JS_TRUE;
03561 }
03562 
03563 /*
03564  * Helper called with pop out param initialized to a JSOP_POP* opcode.  If we
03565  * can emit a group assignment sequence, which results in 0 stack depth delta,
03566  * we set *pop to JSOP_NOP so callers can veto emitting pn followed by a pop.
03567  */
03568 static JSBool
03569 MaybeEmitGroupAssignment(JSContext *cx, JSCodeGenerator *cg, JSOp declOp,
03570                          JSParseNode *pn, JSOp *pop)
03571 {
03572     JSParseNode *lhs, *rhs;
03573 
03574     JS_ASSERT(pn->pn_type == TOK_ASSIGN);
03575     JS_ASSERT(*pop == JSOP_POP || *pop == JSOP_POPV);
03576     lhs = pn->pn_left;
03577     rhs = pn->pn_right;
03578     if (lhs->pn_type == TOK_RB && rhs->pn_type == TOK_RB &&
03579         lhs->pn_count <= rhs->pn_count &&
03580         (rhs->pn_count == 0 ||
03581          rhs->pn_head->pn_type != TOK_DEFSHARP)) {
03582         if (!EmitGroupAssignment(cx, cg, declOp, lhs, rhs))
03583             return JS_FALSE;
03584         *pop = JSOP_NOP;
03585     }
03586     return JS_TRUE;
03587 }
03588 
03589 #endif /* JS_HAS_DESTRUCTURING */
03590 
03591 static JSBool
03592 EmitVariables(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn,
03593               JSBool inLetHead, ptrdiff_t *headNoteIndex)
03594 {
03595     JSTreeContext *tc;
03596     JSBool let, forInVar;
03597 #if JS_HAS_BLOCK_SCOPE
03598     JSBool forInLet, popScope;
03599     JSStmtInfo *stmt, *scopeStmt;
03600 #endif
03601     ptrdiff_t off, noteIndex, tmp;
03602     JSParseNode *pn2, *pn3;
03603     JSOp op;
03604     jsatomid atomIndex;
03605     uintN oldflags;
03606 
03607     /* Default in case of JS_HAS_BLOCK_SCOPE early return, below. */
03608     *headNoteIndex = -1;
03609 
03610     /*
03611      * Let blocks and expressions have a parenthesized head in which the new
03612      * scope is not yet open. Initializer evaluation uses the parent node's
03613      * lexical scope. If popScope is true below, then we hide the top lexical
03614      * block from any calls to BindNameToSlot hiding in pn2->pn_expr so that
03615      * it won't find any names in the new let block.
03616      *
03617      * The same goes for let declarations in the head of any kind of for loop.
03618      * Unlike a let declaration 'let x = i' within a block, where x is hoisted
03619      * to the start of the block, a 'for (let x = i...) ...' loop evaluates i
03620      * in the containing scope, and puts x in the loop body's scope.
03621      */
03622     tc = &cg->treeContext;
03623     let = (pn->pn_op == JSOP_NOP);
03624     forInVar = (pn->pn_extra & PNX_FORINVAR) != 0;
03625 #if JS_HAS_BLOCK_SCOPE
03626     forInLet = let && forInVar;
03627     popScope = (inLetHead || (let && (tc->flags & TCF_IN_FOR_INIT)));
03628     JS_ASSERT(!popScope || let);
03629 #endif
03630 
03631     off = noteIndex = -1;
03632     for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) {
03633 #if JS_HAS_DESTRUCTURING
03634         if (pn2->pn_type != TOK_NAME) {
03635             if (pn2->pn_type == TOK_RB || pn2->pn_type == TOK_RC) {
03636                 /*
03637                  * Emit variable binding ops, but not destructuring ops.
03638                  * The parser (see Variables, jsparse.c) has ensured that
03639                  * our caller will be the TOK_FOR/TOK_IN case in js_EmitTree,
03640                  * and that case will emit the destructuring code only after
03641                  * emitting an enumerating opcode and a branch that tests
03642                  * whether the enumeration ended.
03643                  */
03644                 JS_ASSERT(forInVar);
03645                 JS_ASSERT(pn->pn_count == 1);
03646                 if (!EmitDestructuringDecls(cx, cg, pn->pn_op, pn2))
03647                     return JS_FALSE;
03648                 break;
03649             }
03650 
03651             /*
03652              * A destructuring initialiser assignment preceded by var is
03653              * always evaluated promptly, even if it is to the left of 'in'
03654              * in a for-in loop.  As with 'for (var x = i in o)...', this
03655              * will cause the entire 'var [a, b] = i' to be hoisted out of
03656              * the head of the loop.
03657              */
03658             JS_ASSERT(pn2->pn_type == TOK_ASSIGN);
03659             if (pn->pn_count == 1 && !forInLet) {
03660                 /*
03661                  * If this is the only destructuring assignment in the list,
03662                  * try to optimize to a group assignment.  If we're in a let
03663                  * head, pass JSOP_POP rather than the pseudo-prolog JSOP_NOP
03664                  * in pn->pn_op, to suppress a second (and misplaced) 'let'.
03665                  */
03666                 JS_ASSERT(noteIndex < 0 && !pn2->pn_next);
03667                 op = JSOP_POP;
03668                 if (!MaybeEmitGroupAssignment(cx, cg,
03669                                               inLetHead ? JSOP_POP : pn->pn_op,
03670                                               pn2, &op)) {
03671                     return JS_FALSE;
03672                 }
03673                 if (op == JSOP_NOP) {
03674                     pn->pn_extra = (pn->pn_extra & ~PNX_POPVAR) | PNX_GROUPINIT;
03675                     break;
03676                 }
03677             }
03678 
03679             pn3 = pn2->pn_left;
03680             if (!EmitDestructuringDecls(cx, cg, pn->pn_op, pn3))
03681                 return JS_FALSE;
03682 
03683 #if JS_HAS_BLOCK_SCOPE
03684             /*
03685              * If this is a 'for (let [x, y] = i in o) ...' let declaration,
03686              * throw away i if it is a useless expression.
03687              */
03688             if (forInLet) {
03689                 JSBool useful = JS_FALSE;
03690 
03691                 JS_ASSERT(pn->pn_count == 1);
03692                 if (!CheckSideEffects(cx, tc, pn2->pn_right, &useful))
03693                     return JS_FALSE;
03694                 if (!useful)
03695                     return JS_TRUE;
03696             }
03697 #endif
03698 
03699             if (!js_EmitTree(cx, cg, pn2->pn_right))
03700                 return JS_FALSE;
03701 
03702 #if JS_HAS_BLOCK_SCOPE
03703             /*
03704              * The expression i in 'for (let [x, y] = i in o) ...', which is
03705              * pn2->pn_right above, appears to have side effects.  We've just
03706              * emitted code to evaluate i, but we must not destructure i yet.
03707              * Let the TOK_FOR: code in js_EmitTree do the destructuring to
03708              * emit the right combination of source notes and bytecode for the
03709              * decompiler.
03710              *
03711              * This has the effect of hoisting the evaluation of i out of the
03712              * for-in loop, without hoisting the let variables, which must of
03713              * course be scoped by the loop.  Set PNX_POPVAR to cause JSOP_POP
03714              * to be emitted, just before returning from this function.
03715              */
03716             if (forInVar) {
03717                 pn->pn_extra |= PNX_POPVAR;
03718                 if (forInLet)
03719                     break;
03720             }
03721 #endif
03722 
03723             /*
03724              * Veto pn->pn_op if inLetHead to avoid emitting a SRC_DESTRUCT
03725              * that's redundant with respect to the SRC_DECL/SRC_DECL_LET that
03726              * we will emit at the bottom of this function.
03727              */
03728             if (!EmitDestructuringOps(cx, cg,
03729                                       inLetHead ? JSOP_POP : pn->pn_op,
03730                                       pn3)) {
03731                 return JS_FALSE;
03732             }
03733             goto emit_note_pop;
03734         }
03735 #else
03736         JS_ASSERT(pn2->pn_type == TOK_NAME);
03737 #endif
03738 
03739         if (!BindNameToSlot(cx, &cg->treeContext, pn2, let))
03740             return JS_FALSE;
03741         JS_ASSERT(pn2->pn_slot >= 0 || !let);
03742 
03743         op = pn2->pn_op;
03744         if (op == JSOP_ARGUMENTS) {
03745             /* JSOP_ARGUMENTS => no initializer */
03746             JS_ASSERT(!pn2->pn_expr && !let);
03747             pn3 = NULL;
03748 #ifdef __GNUC__
03749             atomIndex = 0;            /* quell GCC overwarning */
03750 #endif
03751         } else {
03752             if (!MaybeEmitVarDecl(cx, cg, pn->pn_op, pn2, &atomIndex))
03753                 return JS_FALSE;
03754 
03755             pn3 = pn2->pn_expr;
03756             if (pn3) {
03757 #if JS_HAS_BLOCK_SCOPE
03758                 /*
03759                  * If this is a 'for (let x = i in o) ...' let declaration,
03760                  * throw away i if it is a useless expression.
03761                  */
03762                 if (forInLet) {
03763                     JSBool useful = JS_FALSE;
03764 
03765                     JS_ASSERT(pn->pn_count == 1);
03766                     if (!CheckSideEffects(cx, tc, pn3, &useful))
03767                         return JS_FALSE;
03768                     if (!useful)
03769                         return JS_TRUE;
03770                 }
03771 #endif
03772 
03773                 if (op == JSOP_SETNAME) {
03774                     JS_ASSERT(!let);
03775                     EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex);
03776                 }
03777                 if (pn->pn_op == JSOP_DEFCONST &&
03778                     !js_DefineCompileTimeConstant(cx, cg, pn2->pn_atom,
03779                                                   pn3)) {
03780                     return JS_FALSE;
03781                 }
03782 
03783 #if JS_HAS_BLOCK_SCOPE
03784                 /* Evaluate expr in the outer lexical scope if requested. */
03785                 if (popScope) {
03786                     stmt = tc->topStmt;
03787                     scopeStmt = tc->topScopeStmt;
03788 
03789                     tc->topStmt = stmt->down;
03790                     tc->topScopeStmt = scopeStmt->downScope;
03791                 }
03792 #ifdef __GNUC__
03793                 else {
03794                     stmt = scopeStmt = NULL;    /* quell GCC overwarning */
03795                 }
03796 #endif
03797 #endif
03798 
03799                 oldflags = cg->treeContext.flags;
03800                 cg->treeContext.flags &= ~TCF_IN_FOR_INIT;
03801                 if (!js_EmitTree(cx, cg, pn3))
03802                     return JS_FALSE;
03803                 cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT;
03804 
03805 #if JS_HAS_BLOCK_SCOPE
03806                 if (popScope) {
03807                     tc->topStmt = stmt;
03808                     tc->topScopeStmt = scopeStmt;
03809                 }
03810 #endif
03811             }
03812         }
03813 
03814         /*
03815          * 'for (var x in o) ...' and 'for (var x = i in o) ...' call the
03816          * TOK_VAR case, but only the initialized case (a strange one that
03817          * falls out of ECMA-262's grammar) wants to run past this point.
03818          * Both cases must conditionally emit a JSOP_DEFVAR, above.  Note
03819          * that the parser error-checks to ensure that pn->pn_count is 1.
03820          *
03821          * 'for (let x = i in o) ...' must evaluate i before the loop, and
03822          * subject it to useless expression elimination.  The variable list
03823          * in pn is a single let declaration if pn_op == JSOP_NOP.  We test
03824          * the let local in order to break early in this case, as well as in
03825          * the 'for (var x in o)' case.
03826          *
03827          * XXX Narcissus keeps track of variable declarations in the node
03828          * for the script being compiled, so there's no need to share any
03829          * conditional prolog code generation there.  We could do likewise,
03830          * but it's a big change, requiring extra allocation, so probably
03831          * not worth the trouble for SpiderMonkey.
03832          */
03833         JS_ASSERT(pn3 == pn2->pn_expr);
03834         if (forInVar && (!pn3 || let)) {
03835             JS_ASSERT(pn->pn_count == 1);
03836             break;
03837         }
03838 
03839         if (pn2 == pn->pn_head &&
03840             !inLetHead &&
03841             js_NewSrcNote2(cx, cg, SRC_DECL,
03842                            (pn->pn_op == JSOP_DEFCONST)
03843                            ? SRC_DECL_CONST
03844                            : (pn->pn_op == JSOP_DEFVAR)
03845                            ? SRC_DECL_VAR
03846                            : SRC_DECL_LET) < 0) {
03847             return JS_FALSE;
03848         }
03849         if (op == JSOP_ARGUMENTS) {
03850             if (js_Emit1(cx, cg, op) < 0)
03851                 return JS_FALSE;
03852         } else if (pn2->pn_slot >= 0) {
03853             EMIT_UINT16_IMM_OP(op, atomIndex);
03854         } else {
03855             EMIT_ATOM_INDEX_OP(op, atomIndex);
03856         }
03857 
03858 #if JS_HAS_DESTRUCTURING
03859     emit_note_pop:
03860 #endif
03861         tmp = CG_OFFSET(cg);
03862         if (noteIndex >= 0) {
03863             if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off))
03864                 return JS_FALSE;
03865         }
03866         if (!pn2->pn_next)
03867             break;
03868         off = tmp;
03869         noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0);
03870         if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0)
03871             return JS_FALSE;
03872     }
03873 
03874     /* If this is a let head, emit and return a srcnote on the pop. */
03875     if (inLetHead) {
03876         *headNoteIndex = js_NewSrcNote(cx, cg, SRC_DECL);
03877         if (*headNoteIndex < 0)
03878             return JS_FALSE;
03879         if (!(pn->pn_extra & PNX_POPVAR))
03880             return js_Emit1(cx, cg, JSOP_NOP) >= 0;
03881     }
03882 
03883     return !(pn->pn_extra & PNX_POPVAR) || js_Emit1(cx, cg, JSOP_POP) >= 0;
03884 }
03885 
03886 #if defined DEBUG_brendan || defined DEBUG_mrbkap
03887 static JSBool
03888 GettableNoteForNextOp(JSCodeGenerator *cg)
03889 {
03890     ptrdiff_t offset, target;
03891     jssrcnote *sn, *end;
03892 
03893     offset = 0;
03894     target = CG_OFFSET(cg);
03895     for (sn = CG_NOTES(cg), end = sn + CG_NOTE_COUNT(cg); sn < end;
03896          sn = SN_NEXT(sn)) {
03897         if (offset == target && SN_IS_GETTABLE(sn))
03898             return JS_TRUE;
03899         offset += SN_DELTA(sn);
03900     }
03901     return JS_FALSE;
03902 }
03903 #endif
03904 
03905 JSBool
03906 js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
03907 {
03908     JSBool ok, useful, wantval;
03909     JSStmtInfo *stmt, stmtInfo;
03910     ptrdiff_t top, off, tmp, beq, jmp;
03911     JSParseNode *pn2, *pn3;
03912     JSAtom *atom;
03913     JSAtomListElement *ale;
03914     jsatomid atomIndex;
03915     ptrdiff_t noteIndex;
03916     JSSrcNoteType noteType;
03917     jsbytecode *pc;
03918     JSOp op;
03919     JSTokenType type;
03920     uint32 argc;
03921     int stackDummy;
03922 
03923     if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
03924         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
03925         return JS_FALSE;
03926     }
03927 
03928     ok = JS_TRUE;
03929     cg->emitLevel++;
03930     pn->pn_offset = top = CG_OFFSET(cg);
03931 
03932     /* Emit notes to tell the current bytecode's source line number. */
03933     UPDATE_LINE_NUMBER_NOTES(cx, cg, pn);
03934 
03935     switch (pn->pn_type) {
03936       case TOK_FUNCTION:
03937       {
03938         void *cg2mark;
03939         JSCodeGenerator *cg2;
03940         JSFunction *fun;
03941 
03942 #if JS_HAS_XML_SUPPORT
03943         if (pn->pn_arity == PN_NULLARY) {
03944             if (js_Emit1(cx, cg, JSOP_GETFUNNS) < 0)
03945                 return JS_FALSE;
03946             break;
03947         }
03948 #endif
03949 
03950         /* Generate code for the function's body. */
03951         cg2mark = JS_ARENA_MARK(cg->codePool);
03952         JS_ARENA_ALLOCATE_TYPE(cg2, JSCodeGenerator, cg->codePool);
03953         if (!cg2) {
03954             JS_ReportOutOfMemory(cx);
03955             return JS_FALSE;
03956         }
03957         if (!js_InitCodeGenerator(cx, cg2, cg->codePool, cg->notePool,
03958                                   cg->filename, pn->pn_pos.begin.lineno,
03959                                   cg->principals)) {
03960             return JS_FALSE;
03961         }
03962         cg2->treeContext.flags = (uint16) (pn->pn_flags | TCF_IN_FUNCTION);
03963         cg2->treeContext.tryCount = pn->pn_tryCount;
03964         cg2->parent = cg;
03965         fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(pn->pn_funAtom));
03966         if (!js_EmitFunctionBody(cx, cg2, pn->pn_body, fun))
03967             return JS_FALSE;
03968 
03969         /*
03970          * We need an activation object if an inner peeks out, or if such
03971          * inner-peeking caused one of our inners to become heavyweight.
03972          */
03973         if (cg2->treeContext.flags &
03974             (TCF_FUN_USES_NONLOCALS | TCF_FUN_HEAVYWEIGHT)) {
03975             cg->treeContext.flags |= TCF_FUN_HEAVYWEIGHT;
03976         }
03977         js_FinishCodeGenerator(cx, cg2);
03978         JS_ARENA_RELEASE(cg->codePool, cg2mark);
03979 
03980         /* Make the function object a literal in the outer script's pool. */
03981         ale = js_IndexAtom(cx, pn->pn_funAtom, &cg->atomList);
03982         if (!ale)
03983             return JS_FALSE;
03984         atomIndex = ALE_INDEX(ale);
03985 
03986         /* Emit a bytecode pointing to the closure object in its immediate. */
03987         if (pn->pn_op != JSOP_NOP) {
03988             EMIT_ATOM_INDEX_OP(pn->pn_op, atomIndex);
03989             break;
03990         }
03991 
03992         /* Top-level named functions need a nop for decompilation. */
03993         noteIndex = js_NewSrcNote2(cx, cg, SRC_FUNCDEF, (ptrdiff_t)atomIndex);
03994         if (noteIndex < 0 ||
03995             js_Emit1(cx, cg, JSOP_NOP) < 0) {
03996             return JS_FALSE;
03997         }
03998 
03999         /*
04000          * Top-levels also need a prolog op to predefine their names in the
04001          * variable object, or if local, to fill their stack slots.
04002          */
04003         CG_SWITCH_TO_PROLOG(cg);
04004 
04005         if (cg->treeContext.flags & TCF_IN_FUNCTION) {
04006             JSObject *obj, *pobj;
04007             JSProperty *prop;
04008             JSScopeProperty *sprop;
04009             uintN slot;
04010 
04011             obj = OBJ_GET_PARENT(cx, fun->object);
04012             if (!js_LookupHiddenProperty(cx, obj, ATOM_TO_JSID(fun->atom),
04013                                          &pobj, &prop)) {
04014                 return JS_FALSE;
04015             }
04016 
04017             JS_ASSERT(prop && pobj == obj);
04018             sprop = (JSScopeProperty *) prop;
04019             JS_ASSERT(sprop->getter == js_GetLocalVariable);
04020             slot = sprop->shortid;
04021             OBJ_DROP_PROPERTY(cx, pobj, prop);
04022 
04023             /*
04024              * If this local function is declared in a body block induced by
04025              * let declarations, reparent fun->object to the compiler-created
04026              * body block object so that JSOP_DEFLOCALFUN can clone that block
04027              * into the runtime scope chain.
04028              */
04029             stmt = cg->treeContext.topStmt;
04030             if (stmt && stmt->type == STMT_BLOCK &&
04031                 stmt->down && stmt->down->type == STMT_BLOCK &&
04032                 (stmt->down->flags & SIF_SCOPE)) {
04033                 obj = ATOM_TO_OBJECT(stmt->down->atom);
04034                 JS_ASSERT(LOCKED_OBJ_GET_CLASS(obj) == &js_BlockClass);
04035                 OBJ_SET_PARENT(cx, fun->object, obj);
04036             }
04037 
04038             if (atomIndex >= JS_BIT(16)) {
04039                 /*
04040                  * Lots of literals in the outer function, so we have to emit
04041                  * [JSOP_LITOPX, atomIndex, JSOP_DEFLOCALFUN, var slot].
04042                  */
04043                 off = js_EmitN(cx, cg, JSOP_LITOPX, 3);
04044                 if (off < 0)
04045                     return JS_FALSE;
04046                 pc = CG_CODE(cg, off);
04047                 SET_LITERAL_INDEX(pc, atomIndex);
04048                 EMIT_UINT16_IMM_OP(JSOP_DEFLOCALFUN, slot);
04049             } else {
04050                 /* Emit [JSOP_DEFLOCALFUN, var slot, atomIndex]. */
04051                 off = js_EmitN(cx, cg, JSOP_DEFLOCALFUN,
04052                                VARNO_LEN + ATOM_INDEX_LEN);
04053                 if (off < 0)
04054                     return JS_FALSE;
04055                 pc = CG_CODE(cg, off);
04056                 SET_VARNO(pc, slot);
04057                 pc += VARNO_LEN;
04058                 SET_ATOM_INDEX(pc, atomIndex);
04059             }
04060         } else {
04061             JS_ASSERT(!cg->treeContext.topStmt);
04062             EMIT_ATOM_INDEX_OP(JSOP_DEFFUN, atomIndex);
04063         }
04064 
04065         CG_SWITCH_TO_MAIN(cg);
04066         break;
04067       }
04068 
04069 #if JS_HAS_EXPORT_IMPORT
04070       case TOK_EXPORT:
04071         pn2 = pn->pn_head;
04072         if (pn2->pn_type == TOK_STAR) {
04073             /*
04074              * 'export *' must have no other elements in the list (what would
04075              * be the point?).
04076              */
04077             if (js_Emit1(cx, cg, JSOP_EXPORTALL) < 0)
04078                 return JS_FALSE;
04079         } else {
04080             /*
04081              * If not 'export *', the list consists of NAME nodes identifying
04082              * properties of the variables object to flag as exported.
04083              */
04084             do {
04085                 ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
04086                 if (!ale)
04087                     return JS_FALSE;
04088                 EMIT_ATOM_INDEX_OP(JSOP_EXPORTNAME, ALE_INDEX(ale));
04089             } while ((pn2 = pn2->pn_next) != NULL);
04090         }
04091         break;
04092 
04093       case TOK_IMPORT:
04094         for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
04095             /*
04096              * Each subtree on an import list is rooted by a DOT or LB node.
04097              * A DOT may have a null pn_atom member, in which case pn_op must
04098              * be JSOP_IMPORTALL -- see EmitPropOp above.
04099              */
04100             if (!js_EmitTree(cx, cg, pn2))
04101                 return JS_FALSE;
04102         }
04103         break;
04104 #endif /* JS_HAS_EXPORT_IMPORT */
04105 
04106       case TOK_IF:
04107         /* Initialize so we can detect else-if chains and avoid recursion. */
04108         stmtInfo.type = STMT_IF;
04109         beq = jmp = -1;
04110         noteIndex = -1;
04111 
04112       if_again:
04113         /* Emit code for the condition before pushing stmtInfo. */
04114         if (!js_EmitTree(cx, cg, pn->pn_kid1))
04115             return JS_FALSE;
04116         top = CG_OFFSET(cg);
04117         if (stmtInfo.type == STMT_IF) {
04118             js_PushStatement(&cg->treeContext, &stmtInfo, STMT_IF, top);
04119         } else {
04120             /*
04121              * We came here from the goto further below that detects else-if
04122              * chains, so we must mutate stmtInfo back into a STMT_IF record.
04123              * Also (see below for why) we need a note offset for SRC_IF_ELSE
04124              * to help the decompiler.  Actually, we need two offsets, one for
04125              * decompiling any else clause and the second for decompiling an
04126              * else-if chain without bracing, overindenting, or incorrectly
04127              * scoping let declarations.
04128              */
04129             JS_ASSERT(stmtInfo.type == STMT_ELSE);
04130             stmtInfo.type = STMT_IF;
04131             stmtInfo.update = top;
04132             if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
04133                 return JS_FALSE;
04134             if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 1, top - jmp))
04135                 return JS_FALSE;
04136         }
04137 
04138         /* Emit an annotated branch-if-false around the then part. */
04139         pn3 = pn->pn_kid3;
04140         noteIndex = js_NewSrcNote(cx, cg, pn3 ? SRC_IF_ELSE : SRC_IF);
04141         if (noteIndex < 0)
04142             return JS_FALSE;
04143         beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
04144         if (beq < 0)
04145             return JS_FALSE;
04146 
04147         /* Emit code for the then and optional else parts. */
04148         if (!js_EmitTree(cx, cg, pn->pn_kid2))
04149             return JS_FALSE;
04150         if (pn3) {
04151             /* Modify stmtInfo so we know we're in the else part. */
04152             stmtInfo.type = STMT_ELSE;
04153 
04154             /*
04155              * Emit a JSOP_BACKPATCH op to jump from the end of our then part
04156              * around the else part.  The js_PopStatementCG call at the bottom
04157              * of this switch case will fix up the backpatch chain linked from
04158              * stmtInfo.breaks.
04159              */
04160             jmp = EmitGoto(cx, cg, &stmtInfo, &stmtInfo.breaks, NULL, SRC_NULL);
04161             if (jmp < 0)
04162                 return JS_FALSE;
04163 
04164             /* Ensure the branch-if-false comes here, then emit the else. */
04165             CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
04166             if (pn3->pn_type == TOK_IF) {
04167                 pn = pn3;
04168                 goto if_again;
04169             }
04170 
04171             if (!js_EmitTree(cx, cg, pn3))
04172                 return JS_FALSE;
04173 
04174             /*
04175              * Annotate SRC_IF_ELSE with the offset from branch to jump, for
04176              * the decompiler's benefit.  We can't just "back up" from the pc
04177              * of the else clause, because we don't know whether an extended
04178              * jump was required to leap from the end of the then clause over
04179              * the else clause.
04180              */
04181             if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
04182                 return JS_FALSE;
04183         } else {
04184             /* No else part, fixup the branch-if-false to come here. */
04185             CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
04186         }
04187         ok = js_PopStatementCG(cx, cg);
04188         break;
04189 
04190       case TOK_SWITCH:
04191         /* Out of line to avoid bloating js_EmitTree's stack frame size. */
04192         ok = EmitSwitch(cx, cg, pn, &stmtInfo);
04193         break;
04194 
04195       case TOK_WHILE:
04196         js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WHILE_LOOP, top);
04197         if (!js_EmitTree(cx, cg, pn->pn_left))
04198             return JS_FALSE;
04199         noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE);
04200         if (noteIndex < 0)
04201             return JS_FALSE;
04202         beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
04203         if (beq < 0)
04204             return JS_FALSE;
04205         if (!js_EmitTree(cx, cg, pn->pn_right))
04206             return JS_FALSE;
04207         jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg));
04208         if (jmp < 0)
04209             return JS_FALSE;
04210         CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
04211         if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
04212             return JS_FALSE;
04213         ok = js_PopStatementCG(cx, cg);
04214         break;
04215 
04216       case TOK_DO:
04217         /* Emit an annotated nop so we know to decompile a 'do' keyword. */
04218         if (js_NewSrcNote(cx, cg, SRC_WHILE) < 0 ||
04219             js_Emit1(cx, cg, JSOP_NOP) < 0) {
04220             return JS_FALSE;
04221         }
04222 
04223         /* Compile the loop body. */
04224         top = CG_OFFSET(cg);
04225         js_PushStatement(&cg->treeContext, &stmtInfo, STMT_DO_LOOP, top);
04226         if (!js_EmitTree(cx, cg, pn->pn_left))
04227             return JS_FALSE;
04228 
04229         /* Set loop and enclosing label update offsets, for continue. */
04230         stmt = &stmtInfo;
04231         do {
04232             stmt->update = CG_OFFSET(cg);
04233         } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);
04234 
04235         /* Compile the loop condition, now that continues know where to go. */
04236         if (!js_EmitTree(cx, cg, pn->pn_right))
04237             return JS_FALSE;
04238 
04239         /*
04240          * No source note needed, because JSOP_IFNE is used only for do-while.
04241          * If we ever use JSOP_IFNE for other purposes, we can still avoid yet
04242          * another note here, by storing (jmp - top) in the SRC_WHILE note's
04243          * offset, and fetching that delta in order to decompile recursively.
04244          */
04245         if (EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg)) < 0)
04246             return JS_FALSE;
04247         ok = js_PopStatementCG(cx, cg);
04248         break;
04249 
04250       case TOK_FOR:
04251         beq = 0;                /* suppress gcc warnings */
04252         pn2 = pn->pn_left;
04253         js_PushStatement(&cg->treeContext, &stmtInfo, STMT_FOR_LOOP, top);
04254 
04255         if (pn2->pn_type == TOK_IN) {
04256             JSBool emitIFEQ;
04257 
04258             /* Set stmtInfo type for later testing. */
04259             stmtInfo.type = STMT_FOR_IN_LOOP;
04260             noteIndex = -1;
04261 
04262             /*
04263              * If the left part is 'var x', emit code to define x if necessary
04264              * using a prolog opcode, but do not emit a pop.  If the left part
04265              * is 'var x = i', emit prolog code to define x if necessary; then
04266              * emit code to evaluate i, assign the result to x, and pop the
04267              * result off the stack.
04268              *
04269              * All the logic to do this is implemented in the outer switch's
04270              * TOK_VAR case, conditioned on pn_extra flags set by the parser.
04271              *
04272              * In the 'for (var x = i in o) ...' case, the js_EmitTree(...pn3)
04273              * called here will generate the proper note for the assignment
04274              * op that sets x = i, hoisting the initialized var declaration
04275              * out of the loop: 'var x = i; for (x in o) ...'.
04276              *
04277              * In the 'for (var x in o) ...' case, nothing but the prolog op
04278              * (if needed) should be generated here, we must emit the note
04279              * just before the JSOP_FOR* opcode in the switch on pn3->pn_type
04280              * a bit below, so nothing is hoisted: 'for (var x in o) ...'.
04281              *
04282              * A 'for (let x = i in o)' loop must not be hoisted, since in
04283              * this form the let variable is scoped by the loop body (but not
04284              * the head).  The initializer expression i must be evaluated for
04285              * any side effects.  So we hoist only i in the let case.
04286              */
04287             pn3 = pn2->pn_left;
04288             type = pn3->pn_type;
04289             cg->treeContext.flags |= TCF_IN_FOR_INIT;
04290             if (TOKEN_TYPE_IS_DECL(type) && !js_EmitTree(cx, cg, pn3))
04291                 return JS_FALSE;
04292             cg->treeContext.flags &= ~TCF_IN_FOR_INIT;
04293 
04294             /* Compile the object expression to the right of 'in'. */
04295             if (!js_EmitTree(cx, cg, pn2->pn_right))
04296                 return JS_FALSE;
04297 
04298             /*
04299              * Emit a bytecode to convert top of stack value to the iterator
04300              * object depending on the loop variant (for-in, for-each-in, or
04301              * destructuring for-in).
04302              */
04303 #if JS_HAS_DESTRUCTURING
04304             JS_ASSERT(pn->pn_op == JSOP_FORIN ||
04305                       pn->pn_op == JSOP_FOREACHKEYVAL ||
04306                       pn->pn_op == JSOP_FOREACH);
04307 #else
04308             JS_ASSERT(pn->pn_op == JSOP_FORIN || pn->pn_op == JSOP_FOREACH);
04309 #endif
04310             if (js_Emit1(cx, cg, pn->pn_op) < 0)
04311                 return JS_FALSE;
04312 
04313             top = CG_OFFSET(cg);
04314             SET_STATEMENT_TOP(&stmtInfo, top);
04315 
04316             /*
04317              * Compile a JSOP_FOR* bytecode based on the left hand side.
04318              *
04319              * Initialize op to JSOP_SETNAME in case of |for ([a, b] in o)...|
04320              * or similar, to signify assignment, rather than declaration, to
04321              * the decompiler.  EmitDestructuringOps takes a prolog bytecode
04322              * parameter and emits the appropriate source note, defaulting to
04323              * assignment, so JSOP_SETNAME is not critical here; many similar
04324              * ops could be used -- just not JSOP_NOP (which means 'let').
04325              */
04326             emitIFEQ = JS_TRUE;
04327             op = JSOP_SETNAME;
04328             switch (type) {
04329 #if JS_HAS_BLOCK_SCOPE
04330               case TOK_LET:
04331 #endif
04332               case TOK_VAR:
04333                 JS_ASSERT(pn3->pn_arity == PN_LIST && pn3->pn_count == 1);
04334                 pn3 = pn3->pn_head;
04335 #if JS_HAS_DESTRUCTURING
04336                 if (pn3->pn_type == TOK_ASSIGN) {
04337                     pn3 = pn3->pn_left;
04338                     JS_ASSERT(pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC);
04339                 }
04340                 if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) {
04341                     op = pn2->pn_left->pn_op;
04342                     goto destructuring_for;
04343                 }
04344 #else
04345                 JS_ASSERT(pn3->pn_type == TOK_NAME);
04346 #endif
04347                 /*
04348                  * Always annotate JSOP_FORLOCAL if given input of the form
04349                  * 'for (let x in * o)' -- the decompiler must not hoist the
04350                  * 'let x' out of the loop head, or x will be bound in the
04351                  * wrong scope.  Likewise, but in this case only for the sake
04352                  * of higher decompilation fidelity only, do not hoist 'var x'
04353                  * when given 'for (var x in o)'.  But 'for (var x = i in o)'
04354                  * requires hoisting in order to preserve the initializer i.
04355                  * The decompiler can only handle so much!
04356                  */
04357                 if ((
04358 #if JS_HAS_BLOCK_SCOPE
04359                      type == TOK_LET ||
04360 #endif
04361                      !pn3->pn_expr) &&
04362                     js_NewSrcNote2(cx, cg, SRC_DECL,
04363                                    type == TOK_VAR
04364                                    ? SRC_DECL_VAR
04365                                    : SRC_DECL_LET) < 0) {
04366                     return JS_FALSE;
04367                 }
04368                 /* FALL THROUGH */
04369               case TOK_NAME:
04370                 if (pn3->pn_slot >= 0) {
04371                     op = pn3->pn_op;
04372                     switch (op) {
04373                       case JSOP_GETARG:   /* FALL THROUGH */
04374                       case JSOP_SETARG:   op = JSOP_FORARG; break;
04375                       case JSOP_GETVAR:   /* FALL THROUGH */
04376                       case JSOP_SETVAR:   op = JSOP_FORVAR; break;
04377                       case JSOP_GETGVAR:  /* FALL THROUGH */
04378                       case JSOP_SETGVAR:  op = JSOP_FORNAME; break;
04379                       case JSOP_GETLOCAL: /* FALL THROUGH */
04380                       case JSOP_SETLOCAL: op = JSOP_FORLOCAL; break;
04381                       default:            JS_ASSERT(0);
04382                     }
04383                 } else {
04384                     pn3->pn_op = JSOP_FORNAME;
04385                     if (!BindNameToSlot(cx, &cg->treeContext, pn3, JS_FALSE))
04386                         return JS_FALSE;
04387                     op = pn3->pn_op;
04388                 }
04389                 if (pn3->pn_slot >= 0) {
04390                     if (pn3->pn_attrs & JSPROP_READONLY) {
04391                         JS_ASSERT(op == JSOP_FORVAR);
04392                         op = JSOP_FORCONST;
04393                     }
04394                     atomIndex = (jsatomid) pn3->pn_slot;
04395                     EMIT_UINT16_IMM_OP(op, atomIndex);
04396                 } else {
04397                     if (!EmitAtomOp(cx, pn3, op, cg))
04398                         return JS_FALSE;
04399                 }
04400                 break;
04401 
04402               case TOK_DOT:
04403                 useful = JS_FALSE;
04404                 if (!CheckSideEffects(cx, &cg->treeContext, pn3->pn_expr,
04405                                       &useful)) {
04406                     return JS_FALSE;
04407                 }
04408                 if (!useful) {
04409                     if (!EmitPropOp(cx, pn3, JSOP_FORPROP, cg))
04410                         return JS_FALSE;
04411                     break;
04412                 }
04413                 /* FALL THROUGH */
04414 
04415 #if JS_HAS_DESTRUCTURING
04416               case TOK_RB:
04417               case TOK_RC:
04418               destructuring_for:
04419 #endif
04420 #if JS_HAS_XML_SUPPORT
04421               case TOK_UNARYOP:
04422 #endif
04423 #if JS_HAS_LVALUE_RETURN
04424               case TOK_LP:
04425 #endif
04426               case TOK_LB:
04427                 /*
04428                  * We separate the first/next bytecode from the enumerator
04429                  * variable binding to avoid any side-effects in the index
04430                  * expression (e.g., for (x[i++] in {}) should not bind x[i]
04431                  * or increment i at all).
04432                  */
04433                 emitIFEQ = JS_FALSE;
04434                 if (!js_Emit1(cx, cg, JSOP_FORELEM))
04435                     return JS_FALSE;
04436 
04437                 /*
04438                  * Emit a SRC_WHILE note with offset telling the distance to
04439                  * the loop-closing jump (we can't reckon from the branch at
04440                  * the top of the loop, because the loop-closing jump might
04441                  * need to be an extended jump, independent of whether the
04442                  * branch is short or long).
04443                  */
04444                 noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE);
04445                 if (noteIndex < 0)
04446                     return JS_FALSE;
04447                 beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
04448                 if (beq < 0)
04449                     return JS_FALSE;
04450 
04451 #if JS_HAS_DESTRUCTURING
04452                 if (pn3->pn_type == TOK_RB || pn3->pn_type == TOK_RC) {
04453                     if (!EmitDestructuringOps(cx, cg, op, pn3))
04454                         return JS_FALSE;
04455                     if (js_Emit1(cx, cg, JSOP_POP) < 0)
04456                         return JS_FALSE;
04457                     break;
04458                 }
04459 #endif
04460 #if JS_HAS_LVALUE_RETURN
04461                 if (pn3->pn_type == TOK_LP) {
04462                     JS_ASSERT(pn3->pn_op == JSOP_SETCALL);
04463                     if (!js_EmitTree(cx, cg, pn3))
04464                         return JS_FALSE;
04465                     if (!js_Emit1(cx, cg, JSOP_ENUMELEM))
04466                         return JS_FALSE;
04467                     break;
04468                 }
04469 #endif
04470 #if JS_HAS_XML_SUPPORT
04471                 if (pn3->pn_type == TOK_UNARYOP) {
04472                     JS_ASSERT(pn3->pn_op == JSOP_BINDXMLNAME);
04473                     if (!js_EmitTree(cx, cg, pn3))
04474                         return JS_FALSE;
04475                     if (!js_Emit1(cx, cg, JSOP_ENUMELEM))
04476                         return JS_FALSE;
04477                     break;
04478                 }
04479 #endif
04480 
04481                 /* Now that we're safely past the IFEQ, commit side effects. */
04482                 if (!EmitElemOp(cx, pn3, JSOP_ENUMELEM, cg))
04483                     return JS_FALSE;
04484                 break;
04485 
04486               default:
04487                 JS_ASSERT(0);
04488             }
04489 
04490             if (emitIFEQ) {
04491                 /* Annotate so the decompiler can find the loop-closing jump. */
04492                 noteIndex = js_NewSrcNote(cx, cg, SRC_WHILE);
04493                 if (noteIndex < 0)
04494                     return JS_FALSE;
04495 
04496                 /* Pop and test the loop condition generated by JSOP_FOR*. */
04497                 beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
04498                 if (beq < 0)
04499                     return JS_FALSE;
04500             }
04501         } else {
04502             op = JSOP_POP;
04503             if (!pn2->pn_kid1) {
04504                 /* No initializer: emit an annotated nop for the decompiler. */
04505                 op = JSOP_NOP;
04506             } else {
04507                 cg->treeContext.flags |= TCF_IN_FOR_INIT;
04508 #if JS_HAS_DESTRUCTURING
04509                 pn3 = pn2->pn_kid1;
04510                 if (pn3->pn_type == TOK_ASSIGN &&
04511                     !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) {
04512                     return JS_FALSE;
04513                 }
04514 #endif
04515                 if (op == JSOP_POP) {
04516                     if (!js_EmitTree(cx, cg, pn3))
04517                         return JS_FALSE;
04518                     if (TOKEN_TYPE_IS_DECL(pn3->pn_type)) {
04519                         /*
04520                          * Check whether a destructuring-initialized var decl
04521                          * was optimized to a group assignment.  If so, we do
04522                          * not need to emit a pop below, so switch to a nop,
04523                          * just for the decompiler.
04524                          */
04525                         JS_ASSERT(pn3->pn_arity == PN_LIST);
04526                         if (pn3->pn_extra & PNX_GROUPINIT)
04527                             op = JSOP_NOP;
04528                     }
04529                 }
04530                 cg->treeContext.flags &= ~TCF_IN_FOR_INIT;
04531             }
04532             noteIndex = js_NewSrcNote(cx, cg, SRC_FOR);
04533             if (noteIndex < 0 ||
04534                 js_Emit1(cx, cg, op) < 0) {
04535                 return JS_FALSE;
04536             }
04537 
04538             top = CG_OFFSET(cg);
04539             SET_STATEMENT_TOP(&stmtInfo, top);
04540             if (!pn2->pn_kid2) {
04541                 /* No loop condition: flag this fact in the source notes. */
04542                 if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, 0))
04543                     return JS_FALSE;
04544             } else {
04545                 if (!js_EmitTree(cx, cg, pn2->pn_kid2))
04546                     return JS_FALSE;
04547                 if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0,
04548                                          CG_OFFSET(cg) - top)) {
04549                     return JS_FALSE;
04550                 }
04551                 beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
04552                 if (beq < 0)
04553                     return JS_FALSE;
04554             }
04555 
04556             /* Set pn3 (used below) here to avoid spurious gcc warnings. */
04557             pn3 = pn2->pn_kid3;
04558         }
04559 
04560         /* Emit code for the loop body. */
04561         if (!js_EmitTree(cx, cg, pn->pn_right))
04562             return JS_FALSE;
04563 
04564         if (pn2->pn_type != TOK_IN) {
04565             /* Set the second note offset so we can find the update part. */
04566             JS_ASSERT(noteIndex != -1);
04567             if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1,
04568                                      CG_OFFSET(cg) - top)) {
04569                 return JS_FALSE;
04570             }
04571 
04572             if (pn3) {
04573                 /* Set loop and enclosing "update" offsets, for continue. */
04574                 stmt = &stmtInfo;
04575                 do {
04576                     stmt->update = CG_OFFSET(cg);
04577                 } while ((stmt = stmt->down) != NULL &&
04578                          stmt->type == STMT_LABEL);
04579 
04580                 op = JSOP_POP;
04581 #if JS_HAS_DESTRUCTURING
04582                 if (pn3->pn_type == TOK_ASSIGN &&
04583                     !MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) {
04584                     return JS_FALSE;
04585                 }
04586 #endif
04587                 if (op == JSOP_POP) {
04588                     if (!js_EmitTree(cx, cg, pn3))
04589                         return JS_FALSE;
04590                     if (js_Emit1(cx, cg, op) < 0)
04591                         return JS_FALSE;
04592                 }
04593 
04594                 /* Restore the absolute line number for source note readers. */
04595                 off = (ptrdiff_t) pn->pn_pos.end.lineno;
04596                 if (CG_CURRENT_LINE(cg) != (uintN) off) {
04597                     if (js_NewSrcNote2(cx, cg, SRC_SETLINE, off) < 0)
04598                         return JS_FALSE;
04599                     CG_CURRENT_LINE(cg) = (uintN) off;
04600                 }
04601             }
04602 
04603             /* The third note offset helps us find the loop-closing jump. */
04604             if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 2,
04605                                      CG_OFFSET(cg) - top)) {
04606                 return JS_FALSE;
04607             }
04608         }
04609 
04610         /* Emit the loop-closing jump and fixup all jump offsets. */
04611         jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg));
04612         if (jmp < 0)
04613             return JS_FALSE;
04614         if (beq > 0)
04615             CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
04616         if (pn2->pn_type == TOK_IN) {
04617             /* Set the SRC_WHILE note offset so we can find the closing jump. */
04618             JS_ASSERT(noteIndex != -1);
04619             if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, jmp - beq))
04620                 return JS_FALSE;
04621         }
04622 
04623         /* Now fixup all breaks and continues (before for/in's JSOP_ENDITER). */
04624         if (!js_PopStatementCG(cx, cg))
04625             return JS_FALSE;
04626 
04627         if (pn2->pn_type == TOK_IN) {
04628             if (js_Emit1(cx, cg, JSOP_ENDITER) < 0)
04629                 return JS_FALSE;
04630         }
04631         break;
04632 
04633       case TOK_BREAK:
04634         stmt = cg->treeContext.topStmt;
04635         atom = pn->pn_atom;
04636         if (atom) {
04637             ale = js_IndexAtom(cx, atom, &cg->atomList);
04638             if (!ale)
04639                 return JS_FALSE;
04640             while (stmt->type != STMT_LABEL || stmt->atom != atom)
04641                 stmt = stmt->down;
04642             noteType = SRC_BREAK2LABEL;
04643         } else {
04644             ale = NULL;
04645             while (!STMT_IS_LOOP(stmt) && stmt->type != STMT_SWITCH)
04646                 stmt = stmt->down;
04647             noteType = SRC_NULL;
04648         }
04649 
04650         if (EmitGoto(cx, cg, stmt, &stmt->breaks, ale, noteType) < 0)
04651             return JS_FALSE;
04652         break;
04653 
04654       case TOK_CONTINUE:
04655         stmt = cg->treeContext.topStmt;
04656         atom = pn->pn_atom;
04657         if (atom) {
04658             /* Find the loop statement enclosed by the matching label. */
04659             JSStmtInfo *loop = NULL;
04660             ale = js_IndexAtom(cx, atom, &cg->atomList);
04661             if (!ale)
04662                 return JS_FALSE;
04663             while (stmt->type != STMT_LABEL || stmt->atom != atom) {
04664                 if (STMT_IS_LOOP(stmt))
04665                     loop = stmt;
04666                 stmt = stmt->down;
04667             }
04668             stmt = loop;
04669             noteType = SRC_CONT2LABEL;
04670         } else {
04671             ale = NULL;
04672             while (!STMT_IS_LOOP(stmt))
04673                 stmt = stmt->down;
04674             noteType = SRC_CONTINUE;
04675         }
04676 
04677         if (EmitGoto(cx, cg, stmt, &stmt->continues, ale, noteType) < 0)
04678             return JS_FALSE;
04679         break;
04680 
04681       case TOK_WITH:
04682         if (!js_EmitTree(cx, cg, pn->pn_left))
04683             return JS_FALSE;
04684         js_PushStatement(&cg->treeContext, &stmtInfo, STMT_WITH, CG_OFFSET(cg));
04685         if (js_Emit1(cx, cg, JSOP_ENTERWITH) < 0)
04686             return JS_FALSE;
04687         if (!js_EmitTree(cx, cg, pn->pn_right))
04688             return JS_FALSE;
04689         if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0)
04690             return JS_FALSE;
04691         ok = js_PopStatementCG(cx, cg);
04692         break;
04693 
04694       case TOK_TRY:
04695       {
04696         ptrdiff_t start, end, catchJump, catchStart, finallyCatch;
04697         intN depth;
04698         JSParseNode *lastCatch;
04699 
04700         catchJump = catchStart = finallyCatch = -1;
04701 
04702         /*
04703          * Push stmtInfo to track jumps-over-catches and gosubs-to-finally
04704          * for later fixup.
04705          *
04706          * When a finally block is 'active' (STMT_FINALLY on the treeContext),
04707          * non-local jumps (including jumps-over-catches) result in a GOSUB
04708          * being written into the bytecode stream and fixed-up later (c.f.
04709          * EmitBackPatchOp and BackPatch).
04710          */
04711         js_PushStatement(&cg->treeContext, &stmtInfo,
04712                          pn->pn_kid3 ? STMT_FINALLY : STMT_TRY,
04713                          CG_OFFSET(cg));
04714 
04715         /*
04716          * About JSOP_SETSP: an exception can be thrown while the stack is in
04717          * an unbalanced state, and this imbalance causes problems with things
04718          * like function invocation later on.
04719          *
04720          * To fix this, we compute the 'balanced' stack depth upon try entry,
04721          * and then restore the stack to this depth when we hit the first catch
04722          * or finally block.  We can't just zero the stack, because things like
04723          * for/in and with that are active upon entry to the block keep state
04724          * variables on the stack.
04725          */
04726         depth = cg->stackDepth;
04727 
04728         /* Mark try location for decompilation, then emit try block. */
04729         if (js_Emit1(cx, cg, JSOP_TRY) < 0)
04730             return JS_FALSE;
04731         start = CG_OFFSET(cg);
04732         if (!js_EmitTree(cx, cg, pn->pn_kid1))
04733             return JS_FALSE;
04734 
04735         /* GOSUB to finally, if present. */
04736         if (pn->pn_kid3) {
04737             if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
04738                 return JS_FALSE;
04739             jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(stmtInfo));
04740             if (jmp < 0)
04741                 return JS_FALSE;
04742 
04743             /* JSOP_RETSUB pops the return pc-index, balancing the stack. */
04744             cg->stackDepth = depth;
04745         }
04746 
04747         /* Emit (hidden) jump over catch and/or finally. */
04748         if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
04749             return JS_FALSE;
04750         jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump);
04751         if (jmp < 0)
04752             return JS_FALSE;
04753 
04754         end = CG_OFFSET(cg);
04755 
04756         /* If this try has a catch block, emit it. */
04757         pn2 = pn->pn_kid2;
04758         lastCatch = NULL;
04759         if (pn2) {
04760             jsint count = 0;    /* previous catch block's population */
04761 
04762             catchStart = end;
04763 
04764             /*
04765              * The emitted code for a catch block looks like:
04766              *
04767              * [throwing]                          only if 2nd+ catch block
04768              * [leaveblock]                        only if 2nd+ catch block
04769              * enterblock                          with SRC_CATCH
04770              * exception
04771              * [dup]                               only if catchguard
04772              * setlocalpop <slot>                  or destructuring code
04773              * [< catchguard code >]               if there's a catchguard
04774              * [ifeq <offset to next catch block>]         " "
04775              * [pop]                               only if catchguard
04776              * < catch block contents >
04777              * leaveblock
04778              * goto <end of catch blocks>          non-local; finally applies
04779              *
04780              * If there's no catch block without a catchguard, the last
04781              * <offset to next catch block> points to rethrow code.  This
04782              * code will [gosub] to the finally code if appropriate, and is
04783              * also used for the catch-all trynote for capturing exceptions
04784              * thrown from catch{} blocks.
04785              */
04786             for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
04787                 ptrdiff_t guardJump, catchNote;
04788 
04789                 guardJump = GUARDJUMP(stmtInfo);
04790                 if (guardJump == -1) {
04791                     /* Set stack to original depth (see SETSP comment above). */
04792                     EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth);
04793                     cg->stackDepth = depth;
04794                 } else {
04795                     /* Fix up and clean up previous catch block. */
04796                     CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, guardJump);
04797 
04798                     /*
04799                      * Account for the pushed exception object that we still
04800                      * have after the jumping from the previous guard.
04801                      */
04802                     JS_ASSERT(cg->stackDepth == depth);
04803                     cg->stackDepth = depth + 1;
04804 
04805                     /*
04806                      * Move exception back to cx->exception to prepare for
04807                      * the next catch. We hide [throwing] from the decompiler
04808                      * since it compensates for the hidden JSOP_DUP at the
04809                      * start of the previous guarded catch.
04810                      */
04811                     if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
04812                         js_Emit1(cx, cg, JSOP_THROWING) < 0) {
04813                         return JS_FALSE;
04814                     }
04815 
04816                     /*
04817                      * Emit an unbalanced [leaveblock] for the previous catch,
04818                      * whose block object count is saved below.
04819                      */
04820                     if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
04821                         return JS_FALSE;
04822                     JS_ASSERT(count >= 0);
04823                     EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count);
04824                 }
04825 
04826                 /*
04827                  * Annotate the JSOP_ENTERBLOCK that's about to be generated
04828                  * by the call to js_EmitTree immediately below.  Save this
04829                  * source note's index in stmtInfo for use by the TOK_CATCH:
04830                  * case, where the length of the catch guard is set as the
04831                  * note's offset.
04832                  */
04833                 catchNote = js_NewSrcNote2(cx, cg, SRC_CATCH, 0);
04834                 if (catchNote < 0)
04835                     return JS_FALSE;
04836                 CATCHNOTE(stmtInfo) = catchNote;
04837 
04838                 /*
04839                  * Emit the lexical scope and catch body.  Save the catch's
04840                  * block object population via count, for use when targeting
04841                  * guardJump at the next catch (the guard mismatch case).
04842                  */
04843                 JS_ASSERT(pn3->pn_type == TOK_LEXICALSCOPE);
04844                 count = OBJ_BLOCK_COUNT(cx, ATOM_TO_OBJECT(pn3->pn_atom));
04845                 if (!js_EmitTree(cx, cg, pn3))
04846                     return JS_FALSE;
04847 
04848                 /* gosub <finally>, if required */
04849                 if (pn->pn_kid3) {
04850                     jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH,
04851                                           &GOSUBS(stmtInfo));
04852                     if (jmp < 0)
04853                         return JS_FALSE;
04854                     JS_ASSERT(cg->stackDepth == depth);
04855                 }
04856 
04857                 /*
04858                  * Jump over the remaining catch blocks.  This will get fixed
04859                  * up to jump to after catch/finally.
04860                  */
04861                 if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
04862                     return JS_FALSE;
04863                 jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &catchJump);
04864                 if (jmp < 0)
04865                     return JS_FALSE;
04866 
04867                 /*
04868                  * Save a pointer to the last catch node to handle try-finally
04869                  * and try-catch(guard)-finally special cases.
04870                  */
04871                 lastCatch = pn3->pn_expr;
04872             }
04873         }
04874 
04875         /*
04876          * Last catch guard jumps to the rethrow code sequence if none of the
04877          * guards match. Target guardJump at the beginning of the rethrow
04878          * sequence, just in case a guard expression throws and leaves the
04879          * stack unbalanced.
04880          */
04881         if (lastCatch && lastCatch->pn_kid2) {
04882             CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, GUARDJUMP(stmtInfo));
04883 
04884             /* Sync the stack to take into account pushed exception. */
04885             JS_ASSERT(cg->stackDepth == depth);
04886             cg->stackDepth = depth + 1;
04887 
04888             /*
04889              * Rethrow the exception, delegating executing of finally if any
04890              * to the exception handler.
04891              */
04892             if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
04893                 js_Emit1(cx, cg, JSOP_THROW) < 0) {
04894                 return JS_FALSE;
04895             }
04896         }
04897 
04898         JS_ASSERT(cg->stackDepth == depth);
04899 
04900         /* Emit finally handler if any. */
04901         if (pn->pn_kid3) {
04902             /*
04903              * We emit [setsp][gosub] to call try-finally when an exception is
04904              * thrown from try or try-catch blocks. The [gosub] and [retsub]
04905              * opcodes will take care of stacking and rethrowing any exception
04906              * pending across the finally.
04907              */
04908             finallyCatch = CG_OFFSET(cg);
04909             EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth);
04910 
04911             jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH,
04912                                   &GOSUBS(stmtInfo));
04913             if (jmp < 0)
04914                 return JS_FALSE;
04915 
04916             JS_ASSERT(cg->stackDepth == depth);
04917             JS_ASSERT((uintN)depth <= cg->maxStackDepth);
04918 
04919             /*
04920              * Fix up the gosubs that might have been emitted before non-local
04921              * jumps to the finally code.
04922              */
04923             if (!BackPatch(cx, cg, GOSUBS(stmtInfo), CG_NEXT(cg), JSOP_GOSUB))
04924                 return JS_FALSE;
04925 
04926             /*
04927              * The stack budget must be balanced at this point.  All [gosub]
04928              * calls emitted before this point will push two stack slots, one
04929              * for the pending exception (or JSVAL_HOLE if there is no pending
04930              * exception) and one for the [retsub] pc-index.
04931              */
04932             JS_ASSERT(cg->stackDepth == depth);
04933             cg->stackDepth += 2;
04934             if ((uintN)cg->stackDepth > cg->maxStackDepth)
04935                 cg->maxStackDepth = cg->stackDepth;
04936 
04937             /* Now indicate that we're emitting a subroutine body. */
04938             stmtInfo.type = STMT_SUBROUTINE;
04939             if (!UpdateLineNumberNotes(cx, cg, pn->pn_kid3))
04940                 return JS_FALSE;
04941             if (js_Emit1(cx, cg, JSOP_FINALLY) < 0 ||
04942                 !js_EmitTree(cx, cg, pn->pn_kid3) ||
04943                 js_Emit1(cx, cg, JSOP_RETSUB) < 0) {
04944                 return JS_FALSE;
04945             }
04946 
04947             /* Restore stack depth budget to its balanced state. */
04948             JS_ASSERT(cg->stackDepth == depth + 2);
04949             cg->stackDepth = depth;
04950         }
04951         if (!js_PopStatementCG(cx, cg))
04952             return JS_FALSE;
04953 
04954         if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 ||
04955             js_Emit1(cx, cg, JSOP_NOP) < 0) {
04956             return JS_FALSE;
04957         }
04958 
04959         /* Fix up the end-of-try/catch jumps to come here. */
04960         if (!BackPatch(cx, cg, catchJump, CG_NEXT(cg), JSOP_GOTO))
04961             return JS_FALSE;
04962 
04963         /*
04964          * Add the try note last, to let post-order give us the right ordering
04965          * (first to last for a given nesting level, inner to outer by level).
04966          */
04967         if (pn->pn_kid2) {
04968             JS_ASSERT(end != -1 && catchStart != -1);
04969             if (!js_NewTryNote(cx, cg, start, end, catchStart))
04970                 return JS_FALSE;
04971         }
04972 
04973         /*
04974          * If we've got a finally, mark try+catch region with additional
04975          * trynote to catch exceptions (re)thrown from a catch block or
04976          * for the try{}finally{} case.
04977          */
04978         if (pn->pn_kid3) {
04979             JS_ASSERT(finallyCatch != -1);
04980             if (!js_NewTryNote(cx, cg, start, finallyCatch, finallyCatch))
04981                 return JS_FALSE;
04982         }
04983         break;
04984       }
04985 
04986       case TOK_CATCH:
04987       {
04988         ptrdiff_t catchStart, guardJump;
04989 
04990         /*
04991          * Morph STMT_BLOCK to STMT_CATCH, note the block entry code offset,
04992          * and save the block object atom.
04993          */
04994         stmt = cg->treeContext.topStmt;
04995         JS_ASSERT(stmt->type == STMT_BLOCK && (stmt->flags & SIF_SCOPE));
04996         stmt->type = STMT_CATCH;
04997         catchStart = stmt->update;
04998         atom = stmt->atom;
04999 
05000         /* Go up one statement info record to the TRY or FINALLY record. */
05001         stmt = stmt->down;
05002         JS_ASSERT(stmt->type == STMT_TRY || stmt->type == STMT_FINALLY);
05003 
05004         /* Pick up the pending exception and bind it to the catch variable. */
05005         if (js_Emit1(cx, cg, JSOP_EXCEPTION) < 0)
05006             return JS_FALSE;
05007 
05008         /*
05009          * Dup the exception object if there is a guard for rethrowing to use
05010          * it later when rethrowing or in other catches.
05011          */
05012         if (pn->pn_kid2) {
05013             if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
05014                 js_Emit1(cx, cg, JSOP_DUP) < 0) {
05015                 return JS_FALSE;
05016             }
05017         }
05018 
05019         pn2 = pn->pn_kid1;
05020         switch (pn2->pn_type) {
05021 #if JS_HAS_DESTRUCTURING
05022           case TOK_RB:
05023           case TOK_RC:
05024             if (!EmitDestructuringOps(cx, cg, JSOP_NOP, pn2))
05025                 return JS_FALSE;
05026             if (js_Emit1(cx, cg, JSOP_POP) < 0)
05027                 return JS_FALSE;
05028             break;
05029 #endif
05030 
05031           case TOK_NAME:
05032             /* Inline BindNameToSlot, adding block depth to pn2->pn_slot. */
05033             pn2->pn_slot += OBJ_BLOCK_DEPTH(cx, ATOM_TO_OBJECT(atom));
05034             EMIT_UINT16_IMM_OP(JSOP_SETLOCALPOP, pn2->pn_slot);
05035             break;
05036 
05037           default:
05038             JS_ASSERT(0);
05039         }
05040 
05041         /* Emit the guard expression, if there is one. */
05042         if (pn->pn_kid2) {
05043             if (!js_EmitTree(cx, cg, pn->pn_kid2))
05044                 return JS_FALSE;
05045             if (!js_SetSrcNoteOffset(cx, cg, CATCHNOTE(*stmt), 0,
05046                                      CG_OFFSET(cg) - catchStart)) {
05047                 return JS_FALSE;
05048             }
05049             /* ifeq <next block> */
05050             guardJump = EmitJump(cx, cg, JSOP_IFEQ, 0);
05051             if (guardJump < 0)
05052                 return JS_FALSE;
05053             GUARDJUMP(*stmt) = guardJump;
05054 
05055             /* Pop duplicated exception object as we no longer need it. */
05056             if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
05057                 js_Emit1(cx, cg, JSOP_POP) < 0) {
05058                 return JS_FALSE;
05059             }
05060         }
05061 
05062         /* Emit the catch body. */
05063         if (!js_EmitTree(cx, cg, pn->pn_kid3))
05064             return JS_FALSE;
05065 
05066         /*
05067          * Annotate the JSOP_LEAVEBLOCK that will be emitted as we unwind via
05068          * our TOK_LEXICALSCOPE parent, so the decompiler knows to pop.
05069          */
05070         off = cg->stackDepth;
05071         if (js_NewSrcNote2(cx, cg, SRC_CATCH, off) < 0)
05072             return JS_FALSE;
05073         break;
05074       }
05075 
05076       case TOK_VAR:
05077         if (!EmitVariables(cx, cg, pn, JS_FALSE, &noteIndex))
05078             return JS_FALSE;
05079         break;
05080 
05081       case TOK_RETURN:
05082         /* Push a return value */
05083         pn2 = pn->pn_kid;
05084         if (pn2) {
05085             if (!js_EmitTree(cx, cg, pn2))
05086                 return JS_FALSE;
05087         } else {
05088             if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
05089                 return JS_FALSE;
05090         }
05091 
05092         /*
05093          * EmitNonLocalJumpFixup mutates op to JSOP_RETRVAL after emitting a
05094          * JSOP_SETRVAL if there are open try blocks having finally clauses.
05095          * We can't simply transfer control flow to our caller in that case,
05096          * because we must gosub to those clauses from inner to outer, with
05097          * the correct stack pointer (i.e., after popping any with, for/in,
05098          * etc., slots nested inside the finally's try).
05099          */
05100         op = JSOP_RETURN;
05101         if (!EmitNonLocalJumpFixup(cx, cg, NULL, &op))
05102             return JS_FALSE;
05103         if (js_Emit1(cx, cg, op) < 0)
05104             return JS_FALSE;
05105         break;
05106 
05107 #if JS_HAS_GENERATORS
05108       case TOK_YIELD:
05109         if (pn->pn_kid) {
05110             if (!js_EmitTree(cx, cg, pn->pn_kid))
05111                 return JS_FALSE;
05112         } else {
05113             if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
05114                 return JS_FALSE;
05115         }
05116         if (js_Emit1(cx, cg, JSOP_YIELD) < 0)
05117             return JS_FALSE;
05118         break;
05119 #endif
05120 
05121       case TOK_LC:
05122 #if JS_HAS_XML_SUPPORT
05123         if (pn->pn_arity == PN_UNARY) {
05124             if (!js_EmitTree(cx, cg, pn->pn_kid))
05125                 return JS_FALSE;
05126             if (js_Emit1(cx, cg, pn->pn_op) < 0)
05127                 return JS_FALSE;
05128             break;
05129         }
05130 #endif
05131 
05132         JS_ASSERT(pn->pn_arity == PN_LIST);
05133 
05134         noteIndex = -1;
05135         tmp = CG_OFFSET(cg);
05136         if (pn->pn_extra & PNX_NEEDBRACES) {
05137             noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0);
05138             if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_NOP) < 0)
05139                 return JS_FALSE;
05140         }
05141 
05142         js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BLOCK, top);
05143         for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
05144             if (!js_EmitTree(cx, cg, pn2))
05145                 return JS_FALSE;
05146         }
05147 
05148         if (noteIndex >= 0 &&
05149             !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0,
05150                                  CG_OFFSET(cg) - tmp)) {
05151             return JS_FALSE;
05152         }
05153 
05154         ok = js_PopStatementCG(cx, cg);
05155         break;
05156 
05157       case TOK_BODY:
05158         JS_ASSERT(pn->pn_arity == PN_LIST);
05159         js_PushStatement(&cg->treeContext, &stmtInfo, STMT_BODY, top);
05160         for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
05161             if (!js_EmitTree(cx, cg, pn2))
05162                 return JS_FALSE;
05163         }
05164         ok = js_PopStatementCG(cx, cg);
05165         break;
05166 
05167       case TOK_SEMI:
05168         pn2 = pn->pn_kid;
05169         if (pn2) {
05170             /*
05171              * Top-level or called-from-a-native JS_Execute/EvaluateScript,
05172              * debugger, and eval frames may need the value of the ultimate
05173              * expression statement as the script's result, despite the fact
05174              * that it appears useless to the compiler.
05175              */
05176             useful = wantval = !cx->fp->fun ||
05177                                !FUN_INTERPRETED(cx->fp->fun) ||
05178                                (cx->fp->flags & JSFRAME_SPECIAL);
05179             if (!useful) {
05180                 if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful))
05181                     return JS_FALSE;
05182             }
05183 
05184             /*
05185              * Don't eliminate apparently useless expressions if they are
05186              * labeled expression statements.  The tc->topStmt->update test
05187              * catches the case where we are nesting in js_EmitTree for a
05188              * labeled compound statement.
05189              */
05190             if (!useful &&
05191                 (!cg->treeContext.topStmt ||
05192                  cg->treeContext.topStmt->type != STMT_LABEL ||
05193                  cg->treeContext.topStmt->update < CG_OFFSET(cg))) {
05194                 CG_CURRENT_LINE(cg) = pn2->pn_pos.begin.lineno;
05195                 if (!js_ReportCompileErrorNumber(cx, cg,
05196                                                  JSREPORT_CG |
05197                                                  JSREPORT_WARNING |
05198                                                  JSREPORT_STRICT,
05199                                                  JSMSG_USELESS_EXPR)) {
05200                     return JS_FALSE;
05201                 }
05202             } else {
05203                 op = wantval ? JSOP_POPV : JSOP_POP;
05204 #if JS_HAS_DESTRUCTURING
05205                 if (!wantval &&
05206                     pn2->pn_type == TOK_ASSIGN &&
05207                     !MaybeEmitGroupAssignment(cx, cg, op, pn2, &op)) {
05208                     return JS_FALSE;
05209                 }
05210 #endif
05211                 if (op != JSOP_NOP) {
05212                     if (!js_EmitTree(cx, cg, pn2))
05213                         return JS_FALSE;
05214                     if (js_Emit1(cx, cg, op) < 0)
05215                         return JS_FALSE;
05216                 }
05217             }
05218         }
05219         break;
05220 
05221       case TOK_COLON:
05222         /* Emit an annotated nop so we know to decompile a label. */
05223         atom = pn->pn_atom;
05224         ale = js_IndexAtom(cx, atom, &cg->atomList);
05225         if (!ale)
05226             return JS_FALSE;
05227         pn2 = pn->pn_expr;
05228         noteType = (pn2->pn_type == TOK_LC ||
05229                     (pn2->pn_type == TOK_LEXICALSCOPE &&
05230                      pn2->pn_expr->pn_type == TOK_LC))
05231                    ? SRC_LABELBRACE
05232                    : SRC_LABEL;
05233         noteIndex = js_NewSrcNote2(cx, cg, noteType,
05234                                    (ptrdiff_t) ALE_INDEX(ale));
05235         if (noteIndex < 0 ||
05236             js_Emit1(cx, cg, JSOP_NOP) < 0) {
05237             return JS_FALSE;
05238         }
05239 
05240         /* Emit code for the labeled statement. */
05241         js_PushStatement(&cg->treeContext, &stmtInfo, STMT_LABEL,
05242                          CG_OFFSET(cg));
05243         stmtInfo.atom = atom;
05244         if (!js_EmitTree(cx, cg, pn2))
05245             return JS_FALSE;
05246         if (!js_PopStatementCG(cx, cg))
05247             return JS_FALSE;
05248 
05249         /* If the statement was compound, emit a note for the end brace. */
05250         if (noteType == SRC_LABELBRACE) {
05251             if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 ||
05252                 js_Emit1(cx, cg, JSOP_NOP) < 0) {
05253                 return JS_FALSE;
05254             }
05255         }
05256         break;
05257 
05258       case TOK_COMMA:
05259         /*
05260          * Emit SRC_PCDELTA notes on each JSOP_POP between comma operands.
05261          * These notes help the decompiler bracket the bytecodes generated
05262          * from each sub-expression that follows a comma.
05263          */
05264         off = noteIndex = -1;
05265         for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) {
05266             if (!js_EmitTree(cx, cg, pn2))
05267                 return JS_FALSE;
05268             tmp = CG_OFFSET(cg);
05269             if (noteIndex >= 0) {
05270                 if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off))
05271                     return JS_FALSE;
05272             }
05273             if (!pn2->pn_next)
05274                 break;
05275             off = tmp;
05276             noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0);
05277             if (noteIndex < 0 ||
05278                 js_Emit1(cx, cg, JSOP_POP) < 0) {
05279                 return JS_FALSE;
05280             }
05281         }
05282         break;
05283 
05284       case TOK_ASSIGN:
05285         /*
05286          * Check left operand type and generate specialized code for it.
05287          * Specialize to avoid ECMA "reference type" values on the operand
05288          * stack, which impose pervasive runtime "GetValue" costs.
05289          */
05290         pn2 = pn->pn_left;
05291         JS_ASSERT(pn2->pn_type != TOK_RP);
05292         atomIndex = (jsatomid) -1;
05293         switch (pn2->pn_type) {
05294           case TOK_NAME:
05295             if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE))
05296                 return JS_FALSE;
05297             if (pn2->pn_slot >= 0) {
05298                 atomIndex = (jsatomid) pn2->pn_slot;
05299             } else {
05300                 ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
05301                 if (!ale)
05302                     return JS_FALSE;
05303                 atomIndex = ALE_INDEX(ale);
05304                 EMIT_ATOM_INDEX_OP(JSOP_BINDNAME, atomIndex);
05305             }
05306             break;
05307           case TOK_DOT:
05308             if (!js_EmitTree(cx, cg, pn2->pn_expr))
05309                 return JS_FALSE;
05310             ale = js_IndexAtom(cx, pn2->pn_atom, &cg->atomList);
05311             if (!ale)
05312                 return JS_FALSE;
05313             atomIndex = ALE_INDEX(ale);
05314             break;
05315           case TOK_LB:
05316             JS_ASSERT(pn2->pn_arity == PN_BINARY);
05317             if (!js_EmitTree(cx, cg, pn2->pn_left))
05318                 return JS_FALSE;
05319             if (!js_EmitTree(cx, cg, pn2->pn_right))
05320                 return JS_FALSE;
05321             break;
05322 #if JS_HAS_DESTRUCTURING
05323           case TOK_RB:
05324           case TOK_RC:
05325             break;
05326 #endif
05327 #if JS_HAS_LVALUE_RETURN
05328           case TOK_LP:
05329             if (!js_EmitTree(cx, cg, pn2))
05330                 return JS_FALSE;
05331             break;
05332 #endif
05333 #if JS_HAS_XML_SUPPORT
05334           case TOK_UNARYOP:
05335             JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME);
05336             if (!js_EmitTree(cx, cg, pn2->pn_kid))
05337                 return JS_FALSE;
05338             if (js_Emit1(cx, cg, JSOP_BINDXMLNAME) < 0)
05339                 return JS_FALSE;
05340             break;
05341 #endif
05342           default:
05343             JS_ASSERT(0);
05344         }
05345 
05346         op = pn->pn_op;
05347 #if JS_HAS_GETTER_SETTER
05348         if (op == JSOP_GETTER || op == JSOP_SETTER) {
05349             /* We'll emit these prefix bytecodes after emitting the r.h.s. */
05350             if (atomIndex != (jsatomid) -1 && atomIndex >= JS_BIT(16)) {
05351                 ReportStatementTooLarge(cx, cg);
05352                 return JS_FALSE;
05353             }
05354             if (pn2->pn_type == TOK_NAME && pn2->pn_op != JSOP_SETNAME) {
05355                 /*
05356                  * x getter = y where x is a local or let variable is not
05357                  * supported.
05358                  */
05359                 js_ReportCompileErrorNumber(cx,
05360                                             pn2, JSREPORT_PN | JSREPORT_ERROR,
05361                                             JSMSG_BAD_GETTER_OR_SETTER,
05362                                             (op == JSOP_GETTER)
05363                                             ? js_getter_str
05364                                             : js_setter_str);
05365                 return JS_FALSE;
05366             }
05367         } else
05368 #endif
05369         /* If += or similar, dup the left operand and get its value. */
05370         if (op != JSOP_NOP) {
05371             switch (pn2->pn_type) {
05372               case TOK_NAME:
05373                 if (pn2->pn_op != JSOP_SETNAME) {
05374                     EMIT_UINT16_IMM_OP((pn2->pn_op == JSOP_SETGVAR)
05375                                        ? JSOP_GETGVAR
05376                                        : (pn2->pn_op == JSOP_SETARG)
05377                                        ? JSOP_GETARG
05378                                        : (pn2->pn_op == JSOP_SETLOCAL)
05379                                        ? JSOP_GETLOCAL
05380                                        : JSOP_GETVAR,
05381                                        atomIndex);
05382                     break;
05383                 }
05384                 /* FALL THROUGH */
05385               case TOK_DOT:
05386                 if (js_Emit1(cx, cg, JSOP_DUP) < 0)
05387                     return JS_FALSE;
05388                 EMIT_ATOM_INDEX_OP((pn2->pn_type == TOK_NAME)
05389                                    ? JSOP_GETXPROP
05390                                    : JSOP_GETPROP,
05391                                    atomIndex);
05392                 break;
05393               case TOK_LB:
05394 #if JS_HAS_LVALUE_RETURN
05395               case TOK_LP:
05396 #endif
05397 #if JS_HAS_XML_SUPPORT
05398               case TOK_UNARYOP:
05399 #endif
05400                 if (js_Emit1(cx, cg, JSOP_DUP2) < 0)
05401                     return JS_FALSE;
05402                 if (js_Emit1(cx, cg, JSOP_GETELEM) < 0)
05403                     return JS_FALSE;
05404                 break;
05405               default:;
05406             }
05407         }
05408 
05409         /* Now emit the right operand (it may affect the namespace). */
05410         if (!js_EmitTree(cx, cg, pn->pn_right))
05411             return JS_FALSE;
05412 
05413         /* If += etc., emit the binary operator with a decompiler note. */
05414         if (op != JSOP_NOP) {
05415             /*
05416              * Take care to avoid SRC_ASSIGNOP if the left-hand side is a
05417              * const declared in a function (i.e., with non-negative pn_slot
05418              * and JSPROP_READONLY in pn_attrs), as in this case (just a bit
05419              * further below) we will avoid emitting the assignment op.
05420              */
05421             if (pn2->pn_type != TOK_NAME ||
05422                 pn2->pn_slot < 0 ||
05423                 !(pn2->pn_attrs & JSPROP_READONLY)) {
05424                 if (js_NewSrcNote(cx, cg, SRC_ASSIGNOP) < 0)
05425                     return JS_FALSE;
05426             }
05427             if (js_Emit1(cx, cg, op) < 0)
05428                 return JS_FALSE;
05429         }
05430 
05431         /* Left parts such as a.b.c and a[b].c need a decompiler note. */
05432         if (pn2->pn_type != TOK_NAME &&
05433 #if JS_HAS_DESTRUCTURING
05434             pn2->pn_type != TOK_RB &&
05435             pn2->pn_type != TOK_RC &&
05436 #endif
05437             js_NewSrcNote2(cx, cg, SrcNoteForPropOp(pn2, pn2->pn_op),
05438                            CG_OFFSET(cg) - top) < 0) {
05439             return JS_FALSE;
05440         }
05441 
05442         /* Finally, emit the specialized assignment bytecode. */
05443         switch (pn2->pn_type) {
05444           case TOK_NAME:
05445             if (pn2->pn_slot >= 0) {
05446                 if (!(pn2->pn_attrs & JSPROP_READONLY))
05447                     EMIT_UINT16_IMM_OP(pn2->pn_op, atomIndex);
05448                 break;
05449             }
05450             /* FALL THROUGH */
05451           case TOK_DOT:
05452             EMIT_ATOM_INDEX_OP(pn2->pn_op, atomIndex);
05453             break;
05454           case TOK_LB:
05455 #if JS_HAS_LVALUE_RETURN
05456           case TOK_LP:
05457 #endif
05458             if (js_Emit1(cx, cg, JSOP_SETELEM) < 0)
05459                 return JS_FALSE;
05460             break;
05461 #if JS_HAS_DESTRUCTURING
05462           case TOK_RB:
05463           case TOK_RC:
05464             if (!EmitDestructuringOps(cx, cg, JSOP_SETNAME, pn2))
05465                 return JS_FALSE;
05466             break;
05467 #endif
05468 #if JS_HAS_XML_SUPPORT
05469           case TOK_UNARYOP:
05470             if (js_Emit1(cx, cg, JSOP_SETXMLNAME) < 0)
05471                 return JS_FALSE;
05472             break;
05473 #endif
05474           default:
05475             JS_ASSERT(0);
05476         }
05477         break;
05478 
05479       case TOK_HOOK:
05480         /* Emit the condition, then branch if false to the else part. */
05481         if (!js_EmitTree(cx, cg, pn->pn_kid1))
05482             return JS_FALSE;
05483         noteIndex = js_NewSrcNote(cx, cg, SRC_COND);
05484         if (noteIndex < 0)
05485             return JS_FALSE;
05486         beq = EmitJump(cx, cg, JSOP_IFEQ, 0);
05487         if (beq < 0 || !js_EmitTree(cx, cg, pn->pn_kid2))
05488             return JS_FALSE;
05489 
05490         /* Jump around else, fixup the branch, emit else, fixup jump. */
05491         jmp = EmitJump(cx, cg, JSOP_GOTO, 0);
05492         if (jmp < 0)
05493             return JS_FALSE;
05494         CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, beq);
05495 
05496         /*
05497          * Because each branch pushes a single value, but our stack budgeting
05498          * analysis ignores branches, we now have to adjust cg->stackDepth to
05499          * ignore the value pushed by the first branch.  Execution will follow
05500          * only one path, so we must decrement cg->stackDepth.
05501          *
05502          * Failing to do this will foil code, such as the try/catch/finally
05503          * exception handling code generator, that samples cg->stackDepth for
05504          * use at runtime (JSOP_SETSP), or in let expression and block code
05505          * generation, which must use the stack depth to compute local stack
05506          * indexes correctly.
05507          */
05508         JS_ASSERT(cg->stackDepth > 0);
05509         cg->stackDepth--;
05510         if (!js_EmitTree(cx, cg, pn->pn_kid3))
05511             return JS_FALSE;
05512         CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
05513         if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
05514             return JS_FALSE;
05515         break;
05516 
05517       case TOK_OR:
05518       case TOK_AND:
05519         /*
05520          * JSOP_OR converts the operand on the stack to boolean, and if true,
05521          * leaves the original operand value on the stack and jumps; otherwise
05522          * it pops and falls into the next bytecode, which evaluates the right
05523          * operand.  The jump goes around the right operand evaluation.
05524          *
05525          * JSOP_AND converts the operand on the stack to boolean, and if false,
05526          * leaves the original operand value on the stack and jumps; otherwise
05527          * it pops and falls into the right operand's bytecode.
05528          *
05529          * Avoid tail recursion for long ||...|| expressions and long &&...&&
05530          * expressions or long mixtures of ||'s and &&'s that can easily blow
05531          * the stack, by forward-linking and then backpatching all the JSOP_OR
05532          * and JSOP_AND bytecodes' immediate jump-offset operands.
05533          */
05534         pn3 = pn;
05535         if (!js_EmitTree(cx, cg, pn->pn_left))
05536             return JS_FALSE;
05537         top = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0);
05538         if (top < 0)
05539             return JS_FALSE;
05540         jmp = top;
05541         pn2 = pn->pn_right;
05542         while (pn2->pn_type == TOK_OR || pn2->pn_type == TOK_AND) {
05543             pn = pn2;
05544             if (!js_EmitTree(cx, cg, pn->pn_left))
05545                 return JS_FALSE;
05546             off = EmitJump(cx, cg, JSOP_BACKPATCH_POP, 0);
05547             if (off < 0)
05548                 return JS_FALSE;
05549             if (!SetBackPatchDelta(cx, cg, CG_CODE(cg, jmp), off - jmp))
05550                 return JS_FALSE;
05551             jmp = off;
05552             pn2 = pn->pn_right;
05553         }
05554         if (!js_EmitTree(cx, cg, pn2))
05555             return JS_FALSE;
05556         off = CG_OFFSET(cg);
05557         do {
05558             pc = CG_CODE(cg, top);
05559             tmp = GetJumpOffset(cg, pc);
05560             CHECK_AND_SET_JUMP_OFFSET(cx, cg, pc, off - top);
05561             *pc = pn3->pn_op;
05562             top += tmp;
05563         } while ((pn3 = pn3->pn_right) != pn2);
05564         break;
05565 
05566       case TOK_BITOR:
05567       case TOK_BITXOR:
05568       case TOK_BITAND:
05569       case TOK_EQOP:
05570       case TOK_RELOP:
05571       case TOK_IN:
05572       case TOK_INSTANCEOF:
05573       case TOK_SHOP:
05574       case TOK_PLUS:
05575       case TOK_MINUS:
05576       case TOK_STAR:
05577       case TOK_DIVOP:
05578         if (pn->pn_arity == PN_LIST) {
05579             /* Left-associative operator chain: avoid too much recursion. */
05580             pn2 = pn->pn_head;
05581             if (!js_EmitTree(cx, cg, pn2))
05582                 return JS_FALSE;
05583             op = pn->pn_op;
05584             while ((pn2 = pn2->pn_next) != NULL) {
05585                 if (!js_EmitTree(cx, cg, pn2))
05586                     return JS_FALSE;
05587                 if (js_Emit1(cx, cg, op) < 0)
05588                     return JS_FALSE;
05589             }
05590         } else {
05591 #if JS_HAS_XML_SUPPORT
05592             uintN oldflags;
05593 
05594       case TOK_DBLCOLON:
05595             if (pn->pn_arity == PN_NAME) {
05596                 if (!js_EmitTree(cx, cg, pn->pn_expr))
05597                     return JS_FALSE;
05598                 if (!EmitAtomOp(cx, pn, pn->pn_op, cg))
05599                     return JS_FALSE;
05600                 break;
05601             }
05602 
05603             /*
05604              * Binary :: has a right operand that brackets arbitrary code,
05605              * possibly including a let (a = b) ... expression.  We must clear
05606              * TCF_IN_FOR_INIT to avoid mis-compiling such beasts.
05607              */
05608             oldflags = cg->treeContext.flags;
05609             cg->treeContext.flags &= ~TCF_IN_FOR_INIT;
05610 #endif
05611 
05612             /* Binary operators that evaluate both operands unconditionally. */
05613             if (!js_EmitTree(cx, cg, pn->pn_left))
05614                 return JS_FALSE;
05615             if (!js_EmitTree(cx, cg, pn->pn_right))
05616                 return JS_FALSE;
05617 #if JS_HAS_XML_SUPPORT
05618             cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT;
05619 #endif
05620             if (js_Emit1(cx, cg, pn->pn_op) < 0)
05621                 return JS_FALSE;
05622         }
05623         break;
05624 
05625       case TOK_THROW:
05626 #if JS_HAS_XML_SUPPORT
05627       case TOK_AT:
05628       case TOK_DEFAULT:
05629         JS_ASSERT(pn->pn_arity == PN_UNARY);
05630         /* FALL THROUGH */
05631 #endif
05632       case TOK_UNARYOP:
05633       {
05634         uintN oldflags;
05635 
05636         /* Unary op, including unary +/-. */
05637         pn2 = pn->pn_kid;
05638         op = pn->pn_op;
05639         if (op == JSOP_TYPEOF) {
05640             for (pn3 = pn2; pn3->pn_type == TOK_RP; pn3 = pn3->pn_kid)
05641                 continue;
05642             if (pn3->pn_type != TOK_NAME)
05643                 op = JSOP_TYPEOFEXPR;
05644         }
05645         oldflags = cg->treeContext.flags;
05646         cg->treeContext.flags &= ~TCF_IN_FOR_INIT;
05647         if (!js_EmitTree(cx, cg, pn2))
05648             return JS_FALSE;
05649         cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT;
05650 #if JS_HAS_XML_SUPPORT
05651         if (op == JSOP_XMLNAME &&
05652             js_NewSrcNote2(cx, cg, SRC_PCBASE,
05653                            CG_OFFSET(cg) - pn2->pn_offset) < 0) {
05654             return JS_FALSE;
05655         }
05656 #endif
05657         if (js_Emit1(cx, cg, op) < 0)
05658             return JS_FALSE;
05659         break;
05660       }
05661 
05662       case TOK_INC:
05663       case TOK_DEC:
05664       {
05665         intN depth;
05666 
05667         /* Emit lvalue-specialized code for ++/-- operators. */
05668         pn2 = pn->pn_kid;
05669         JS_ASSERT(pn2->pn_type != TOK_RP);
05670         op = pn->pn_op;
05671         depth = cg->stackDepth;
05672         switch (pn2->pn_type) {
05673           case TOK_NAME:
05674             pn2->pn_op = op;
05675             if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE))
05676                 return JS_FALSE;
05677             op = pn2->pn_op;
05678             if (pn2->pn_slot >= 0) {
05679                 if (pn2->pn_attrs & JSPROP_READONLY) {
05680                     /* Incrementing a declared const: just get its value. */
05681                     op = ((js_CodeSpec[op].format & JOF_TYPEMASK) == JOF_CONST)
05682                          ? JSOP_GETGVAR
05683                          : JSOP_GETVAR;
05684                 }
05685                 atomIndex = (jsatomid) pn2->pn_slot;
05686                 EMIT_UINT16_IMM_OP(op, atomIndex);
05687             } else {
05688                 if (!EmitAtomOp(cx, pn2, op, cg))
05689                     return JS_FALSE;
05690             }
05691             break;
05692           case TOK_DOT:
05693             if (!EmitPropOp(cx, pn2, op, cg))
05694                 return JS_FALSE;
05695             ++depth;
05696             break;
05697           case TOK_LB:
05698             if (!EmitElemOp(cx, pn2, op, cg))
05699                 return JS_FALSE;
05700             depth += 2;
05701             break;
05702 #if JS_HAS_LVALUE_RETURN
05703           case TOK_LP:
05704             if (!js_EmitTree(cx, cg, pn2))
05705                 return JS_FALSE;
05706             depth = cg->stackDepth;
05707             if (js_NewSrcNote2(cx, cg, SRC_PCBASE,
05708                                CG_OFFSET(cg) - pn2->pn_offset) < 0) {
05709                 return JS_FALSE;
05710             }
05711             if (js_Emit1(cx, cg, op) < 0)
05712                 return JS_FALSE;
05713             break;
05714 #endif
05715 #if JS_HAS_XML_SUPPORT
05716           case TOK_UNARYOP:
05717             JS_ASSERT(pn2->pn_op == JSOP_SETXMLNAME);
05718             if (!js_EmitTree(cx, cg, pn2->pn_kid))
05719                 return JS_FALSE;
05720             if (js_Emit1(cx, cg, JSOP_BINDXMLNAME) < 0)
05721                 return JS_FALSE;
05722             depth = cg->stackDepth;
05723             if (js_Emit1(cx, cg, op) < 0)
05724                 return JS_FALSE;
05725             break;
05726 #endif
05727           default:
05728             JS_ASSERT(0);
05729         }
05730 
05731         /*
05732          * Allocate another stack slot for GC protection in case the initial
05733          * value being incremented or decremented is not a number, but
05734          * converts to a jsdouble. In the TOK_NAME cases, op has 0 operand
05735          * uses and 1 definition, so we don't need an extra stack slot -- we
05736          * can use the one allocated for the def.
05737          */
05738         if (pn2->pn_type != TOK_NAME &&
05739             (js_CodeSpec[op].format & (JOF_INC | JOF_DEC)) &&
05740             (uintN)depth == cg->maxStackDepth) {
05741             ++cg->maxStackDepth;
05742         }
05743         break;
05744       }
05745 
05746       case TOK_DELETE:
05747         /*
05748          * Under ECMA 3, deleting a non-reference returns true -- but alas we
05749          * must evaluate the operand if it appears it might have side effects.
05750          */
05751         pn2 = pn->pn_kid;
05752         switch (pn2->pn_type) {
05753           case TOK_NAME:
05754             pn2->pn_op = JSOP_DELNAME;
05755             if (!BindNameToSlot(cx, &cg->treeContext, pn2, JS_FALSE))
05756                 return JS_FALSE;
05757             op = pn2->pn_op;
05758             if (op == JSOP_FALSE) {
05759                 if (js_Emit1(cx, cg, op) < 0)
05760                     return JS_FALSE;
05761             } else {
05762                 if (!EmitAtomOp(cx, pn2, op, cg))
05763                     return JS_FALSE;
05764             }
05765             break;
05766           case TOK_DOT:
05767             if (!EmitPropOp(cx, pn2, JSOP_DELPROP, cg))
05768                 return JS_FALSE;
05769             break;
05770 #if JS_HAS_XML_SUPPORT
05771           case TOK_DBLDOT:
05772             if (!EmitElemOp(cx, pn2, JSOP_DELDESC, cg))
05773                 return JS_FALSE;
05774             break;
05775 #endif
05776 #if JS_HAS_LVALUE_RETURN
05777           case TOK_LP:
05778             if (pn2->pn_op != JSOP_SETCALL) {
05779                 JS_ASSERT(pn2->pn_op == JSOP_CALL || pn2->pn_op == JSOP_EVAL);
05780                 pn2->pn_op = JSOP_SETCALL;
05781             }
05782             top = CG_OFFSET(cg);
05783             if (!js_EmitTree(cx, cg, pn2))
05784                 return JS_FALSE;
05785             if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0)
05786                 return JS_FALSE;
05787             if (js_Emit1(cx, cg, JSOP_DELELEM) < 0)
05788                 return JS_FALSE;
05789             break;
05790 #endif
05791           case TOK_LB:
05792             if (!EmitElemOp(cx, pn2, JSOP_DELELEM, cg))
05793                 return JS_FALSE;
05794             break;
05795           default:
05796             /*
05797              * If useless, just emit JSOP_TRUE; otherwise convert delete foo()
05798              * to foo(), true (a comma expression, requiring SRC_PCDELTA).
05799              */
05800             useful = JS_FALSE;
05801             if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful))
05802                 return JS_FALSE;
05803             if (!useful) {
05804                 off = noteIndex = -1;
05805             } else {
05806                 if (!js_EmitTree(cx, cg, pn2))
05807                     return JS_FALSE;
05808                 off = CG_OFFSET(cg);
05809                 noteIndex = js_NewSrcNote2(cx, cg, SRC_PCDELTA, 0);
05810                 if (noteIndex < 0 || js_Emit1(cx, cg, JSOP_POP) < 0)
05811                     return JS_FALSE;
05812             }
05813             if (js_Emit1(cx, cg, JSOP_TRUE) < 0)
05814                 return JS_FALSE;
05815             if (noteIndex >= 0) {
05816                 tmp = CG_OFFSET(cg);
05817                 if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off))
05818                     return JS_FALSE;
05819             }
05820         }
05821         break;
05822 
05823 #if JS_HAS_XML_SUPPORT
05824       case TOK_FILTER:
05825         if (!js_EmitTree(cx, cg, pn->pn_left))
05826             return JS_FALSE;
05827         jmp = js_Emit3(cx, cg, JSOP_FILTER, 0, 0);
05828         if (jmp < 0)
05829             return JS_FALSE;
05830         if (!js_EmitTree(cx, cg, pn->pn_right))
05831             return JS_FALSE;
05832         if (js_Emit1(cx, cg, JSOP_ENDFILTER) < 0)
05833             return JS_FALSE;
05834         CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
05835         break;
05836 #endif
05837 
05838       case TOK_DOT:
05839         /*
05840          * Pop a stack operand, convert it to object, get a property named by
05841          * this bytecode's immediate-indexed atom operand, and push its value
05842          * (not a reference to it).  This bytecode sets the virtual machine's
05843          * "obj" register to the left operand's ToObject conversion result,
05844          * for use by JSOP_PUSHOBJ.
05845          */
05846         ok = EmitPropOp(cx, pn, pn->pn_op, cg);
05847         break;
05848 
05849       case TOK_LB:
05850 #if JS_HAS_XML_SUPPORT
05851       case TOK_DBLDOT:
05852 #endif
05853         /*
05854          * Pop two operands, convert the left one to object and the right one
05855          * to property name (atom or tagged int), get the named property, and
05856          * push its value.  Set the "obj" register to the result of ToObject
05857          * on the left operand.
05858          */
05859         ok = EmitElemOp(cx, pn, pn->pn_op, cg);
05860         break;
05861 
05862       case TOK_NEW:
05863       case TOK_LP:
05864       {
05865         uintN oldflags;
05866 
05867         /*
05868          * Emit function call or operator new (constructor call) code.
05869          * First, emit code for the left operand to evaluate the callable or
05870          * constructable object expression.
05871          *
05872          * For E4X, if this expression is a dotted member reference, select
05873          * JSOP_GETMETHOD instead of JSOP_GETPROP.  ECMA-357 separates XML
05874          * method lookup from the normal property id lookup done for native
05875          * objects.
05876          */
05877         pn2 = pn->pn_head;
05878 #if JS_HAS_XML_SUPPORT
05879         if (pn2->pn_type == TOK_DOT && pn2->pn_op != JSOP_GETMETHOD) {
05880             JS_ASSERT(pn2->pn_op == JSOP_GETPROP);
05881             pn2->pn_op = JSOP_GETMETHOD;
05882             pn2->pn_attrs |= JSPROP_IMPLICIT_FUNCTION_NAMESPACE;
05883         }
05884 #endif
05885         if (!js_EmitTree(cx, cg, pn2))
05886             return JS_FALSE;
05887 
05888         /*
05889          * Push the virtual machine's "obj" register, which was set by a
05890          * name, property, or element get (or set) bytecode.
05891          */
05892         if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0)
05893             return JS_FALSE;
05894 
05895         /* Remember start of callable-object bytecode for decompilation hint. */
05896         off = top;
05897 
05898         /*
05899          * Emit code for each argument in order, then emit the JSOP_*CALL or
05900          * JSOP_NEW bytecode with a two-byte immediate telling how many args
05901          * were pushed on the operand stack.
05902          */
05903         oldflags = cg->treeContext.flags;
05904         cg->treeContext.flags &= ~TCF_IN_FOR_INIT;
05905         for (pn2 = pn2->pn_next; pn2; pn2 = pn2->pn_next) {
05906             if (!js_EmitTree(cx, cg, pn2))
05907                 return JS_FALSE;
05908         }
05909         cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT;
05910         if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - off) < 0)
05911             return JS_FALSE;
05912 
05913         argc = pn->pn_count - 1;
05914         if (js_Emit3(cx, cg, pn->pn_op, ARGC_HI(argc), ARGC_LO(argc)) < 0)
05915             return JS_FALSE;
05916         break;
05917       }
05918 
05919       case TOK_LEXICALSCOPE:
05920       {
05921         JSObject *obj;
05922         jsint count;
05923 
05924         atom = pn->pn_atom;
05925         obj = ATOM_TO_OBJECT(atom);
05926         js_PushBlockScope(&cg->treeContext, &stmtInfo, atom, CG_OFFSET(cg));
05927 
05928         OBJ_SET_BLOCK_DEPTH(cx, obj, cg->stackDepth);
05929         count = OBJ_BLOCK_COUNT(cx, obj);
05930         cg->stackDepth += count;
05931         if ((uintN)cg->stackDepth > cg->maxStackDepth)
05932             cg->maxStackDepth = cg->stackDepth;
05933 
05934         /*
05935          * If this lexical scope is not for a catch block, let block or let
05936          * expression, or any kind of for loop (where the scope starts in the
05937          * head after the first part if for (;;), else in the body if for-in);
05938          * and if our container is top-level but not a function body, or else
05939          * a block statement; then emit a SRC_BRACE note.  All other container
05940          * statements get braces by default from the decompiler.
05941          */
05942         noteIndex = -1;
05943         type = pn->pn_expr->pn_type;
05944         if (type != TOK_CATCH && type != TOK_LET && type != TOK_FOR &&
05945             (!(stmt = stmtInfo.down)
05946              ? !(cg->treeContext.flags & TCF_IN_FUNCTION)
05947              : stmt->type == STMT_BLOCK)) {
05948 #if defined DEBUG_brendan || defined DEBUG_mrbkap
05949             /* There must be no source note already output for the next op. */
05950             JS_ASSERT(CG_NOTE_COUNT(cg) == 0 ||
05951                       CG_LAST_NOTE_OFFSET(cg) != CG_OFFSET(cg) ||
05952                       !GettableNoteForNextOp(cg));
05953 #endif
05954             noteIndex = js_NewSrcNote2(cx, cg, SRC_BRACE, 0);
05955             if (noteIndex < 0)
05956                 return JS_FALSE;
05957         }
05958 
05959         ale = js_IndexAtom(cx, atom, &cg->atomList);
05960         if (!ale)
05961             return JS_FALSE;
05962         JS_ASSERT(CG_OFFSET(cg) == top);
05963         EMIT_ATOM_INDEX_OP(JSOP_ENTERBLOCK, ALE_INDEX(ale));
05964 
05965         if (!js_EmitTree(cx, cg, pn->pn_expr))
05966             return JS_FALSE;
05967 
05968         op = pn->pn_op;
05969         if (op == JSOP_LEAVEBLOCKEXPR) {
05970             if (js_NewSrcNote2(cx, cg, SRC_PCBASE, CG_OFFSET(cg) - top) < 0)
05971                 return JS_FALSE;
05972         } else {
05973             if (noteIndex >= 0 &&
05974                 !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0,
05975                                      CG_OFFSET(cg) - top)) {
05976                 return JS_FALSE;
05977             }
05978         }
05979 
05980         /* Emit the JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR opcode. */
05981         EMIT_UINT16_IMM_OP(op, count);
05982         cg->stackDepth -= count;
05983 
05984         ok = js_PopStatementCG(cx, cg);
05985         break;
05986       }
05987 
05988 #if JS_HAS_BLOCK_SCOPE
05989       case TOK_LET:
05990         /* Let statements have their variable declarations on the left. */
05991         if (pn->pn_arity == PN_BINARY) {
05992             pn2 = pn->pn_right;
05993             pn = pn->pn_left;
05994         } else {
05995             pn2 = NULL;
05996         }
05997 
05998         /* Non-null pn2 means that pn is the variable list from a let head. */
05999         JS_ASSERT(pn->pn_arity == PN_LIST);
06000         if (!EmitVariables(cx, cg, pn, pn2 != NULL, &noteIndex))
06001             return JS_FALSE;
06002 
06003         /* Thus non-null pn2 is the body of the let block or expression. */
06004         tmp = CG_OFFSET(cg);
06005         if (pn2 && !js_EmitTree(cx, cg, pn2))
06006             return JS_FALSE;
06007 
06008         if (noteIndex >= 0 &&
06009             !js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0,
06010                                  CG_OFFSET(cg) - tmp)) {
06011             return JS_FALSE;
06012         }
06013         break;
06014 #endif /* JS_HAS_BLOCK_SCOPE */
06015 
06016 #if JS_HAS_GENERATORS
06017        case TOK_ARRAYPUSH:
06018         /*
06019          * The array object's stack index is in cg->arrayCompSlot.  See below
06020          * under the array initialiser code generator for array comprehension
06021          * special casing.
06022          */
06023         if (!js_EmitTree(cx, cg, pn->pn_kid))
06024             return JS_FALSE;
06025         EMIT_UINT16_IMM_OP(pn->pn_op, cg->arrayCompSlot);
06026         break;
06027 #endif
06028 
06029       case TOK_RB:
06030 #if JS_HAS_GENERATORS
06031       case TOK_ARRAYCOMP:
06032 #endif
06033         /*
06034          * Emit code for [a, b, c] of the form:
06035          *   t = new Array; t[0] = a; t[1] = b; t[2] = c; t;
06036          * but use a stack slot for t and avoid dup'ing and popping it via
06037          * the JSOP_NEWINIT and JSOP_INITELEM bytecodes.
06038          */
06039         ale = js_IndexAtom(cx, CLASS_ATOM(cx, Array), &cg->atomList);
06040         if (!ale)
06041             return JS_FALSE;
06042         EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale));
06043         if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0)
06044             return JS_FALSE;
06045         if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0)
06046             return JS_FALSE;
06047 
06048         pn2 = pn->pn_head;
06049 #if JS_HAS_SHARP_VARS
06050         if (pn2 && pn2->pn_type == TOK_DEFSHARP) {
06051             EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num);
06052             pn2 = pn2->pn_next;
06053         }
06054 #endif
06055 
06056 #if JS_HAS_GENERATORS
06057         if (pn->pn_type == TOK_ARRAYCOMP) {
06058             uintN saveSlot;
06059 
06060             /*
06061              * Pass the new array's stack index to the TOK_ARRAYPUSH case by
06062              * storing it in pn->pn_extra, then simply traverse the TOK_FOR
06063              * node and its kids under pn2 to generate this comprehension.
06064              */
06065             JS_ASSERT(cg->stackDepth > 0);
06066             saveSlot = cg->arrayCompSlot;
06067             cg->arrayCompSlot = (uint32) (cg->stackDepth - 1);
06068             if (!js_EmitTree(cx, cg, pn2))
06069                 return JS_FALSE;
06070             cg->arrayCompSlot = saveSlot;
06071 
06072             /* Emit the usual op needed for decompilation. */
06073             if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0)
06074                 return JS_FALSE;
06075             break;
06076         }
06077 #endif /* JS_HAS_GENERATORS */
06078 
06079         for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) {
06080             if (!EmitNumberOp(cx, atomIndex, cg))
06081                 return JS_FALSE;
06082 
06083             /* FIXME 260106: holes in a sparse initializer are void-filled. */
06084             if (pn2->pn_type == TOK_COMMA) {
06085                 if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
06086                     return JS_FALSE;
06087             } else {
06088                 if (!js_EmitTree(cx, cg, pn2))
06089                     return JS_FALSE;
06090             }
06091 
06092             if (js_Emit1(cx, cg, JSOP_INITELEM) < 0)
06093                 return JS_FALSE;
06094         }
06095 
06096         if (pn->pn_extra & PNX_ENDCOMMA) {
06097             /* Emit a source note so we know to decompile an extra comma. */
06098             if (js_NewSrcNote(cx, cg, SRC_CONTINUE) < 0)
06099                 return JS_FALSE;
06100         }
06101 
06102         /* Emit an op for sharp array cleanup and decompilation. */
06103         if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0)
06104             return JS_FALSE;
06105         break;
06106 
06107       case TOK_RC:
06108         /*
06109          * Emit code for {p:a, '%q':b, 2:c} of the form:
06110          *   t = new Object; t.p = a; t['%q'] = b; t[2] = c; t;
06111          * but use a stack slot for t and avoid dup'ing and popping it via
06112          * the JSOP_NEWINIT and JSOP_INITELEM bytecodes.
06113          */
06114         ale = js_IndexAtom(cx, CLASS_ATOM(cx, Object), &cg->atomList);
06115         if (!ale)
06116             return JS_FALSE;
06117         EMIT_ATOM_INDEX_OP(JSOP_NAME, ALE_INDEX(ale));
06118 
06119         if (js_Emit1(cx, cg, JSOP_PUSHOBJ) < 0)
06120             return JS_FALSE;
06121         if (js_Emit1(cx, cg, JSOP_NEWINIT) < 0)
06122             return JS_FALSE;
06123 
06124         pn2 = pn->pn_head;
06125 #if JS_HAS_SHARP_VARS
06126         if (pn2 && pn2->pn_type == TOK_DEFSHARP) {
06127             EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid)pn2->pn_num);
06128             pn2 = pn2->pn_next;
06129         }
06130 #endif
06131 
06132         for (; pn2; pn2 = pn2->pn_next) {
06133             /* Emit an index for t[2], else map an atom for t.p or t['%q']. */
06134             pn3 = pn2->pn_left;
06135             switch (pn3->pn_type) {
06136               case TOK_NUMBER:
06137                 if (!EmitNumberOp(cx, pn3->pn_dval, cg))
06138                     return JS_FALSE;
06139                 break;
06140               case TOK_NAME:
06141               case TOK_STRING:
06142                 ale = js_IndexAtom(cx, pn3->pn_atom, &cg->atomList);
06143                 if (!ale)
06144                     return JS_FALSE;
06145                 break;
06146               default:
06147                 JS_ASSERT(0);
06148             }
06149 
06150             /* Emit code for the property initializer. */
06151             if (!js_EmitTree(cx, cg, pn2->pn_right))
06152                 return JS_FALSE;
06153 
06154 #if JS_HAS_GETTER_SETTER
06155             op = pn2->pn_op;
06156             if (op == JSOP_GETTER || op == JSOP_SETTER) {
06157                 if (pn3->pn_type != TOK_NUMBER &&
06158                     ALE_INDEX(ale) >= JS_BIT(16)) {
06159                     ReportStatementTooLarge(cx, cg);
06160                     return JS_FALSE;
06161                 }
06162                 if (js_Emit1(cx, cg, op) < 0)
06163                     return JS_FALSE;
06164             }
06165 #endif
06166             /* Annotate JSOP_INITELEM so we decompile 2:c and not just c. */
06167             if (pn3->pn_type == TOK_NUMBER) {
06168                 if (js_NewSrcNote(cx, cg, SRC_INITPROP) < 0)
06169                     return JS_FALSE;
06170                 if (js_Emit1(cx, cg, JSOP_INITELEM) < 0)
06171                     return JS_FALSE;
06172             } else {
06173                 EMIT_ATOM_INDEX_OP(JSOP_INITPROP, ALE_INDEX(ale));
06174             }
06175         }
06176 
06177         /* Emit an op for sharpArray cleanup and decompilation. */
06178         if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0)
06179             return JS_FALSE;
06180         break;
06181 
06182 #if JS_HAS_SHARP_VARS
06183       case TOK_DEFSHARP:
06184         if (!js_EmitTree(cx, cg, pn->pn_kid))
06185             return JS_FALSE;
06186         EMIT_UINT16_IMM_OP(JSOP_DEFSHARP, (jsatomid) pn->pn_num);
06187         break;
06188 
06189       case TOK_USESHARP:
06190         EMIT_UINT16_IMM_OP(JSOP_USESHARP, (jsatomid) pn->pn_num);
06191         break;
06192 #endif /* JS_HAS_SHARP_VARS */
06193 
06194       case TOK_RP:
06195       {
06196         uintN oldflags;
06197 
06198         /*
06199          * The node for (e) has e as its kid, enabling users who want to nest
06200          * assignment expressions in conditions to avoid the error correction
06201          * done by Condition (from x = y to x == y) by double-parenthesizing.
06202          */
06203         oldflags = cg->treeContext.flags;
06204         cg->treeContext.flags &= ~TCF_IN_FOR_INIT;
06205         if (!js_EmitTree(cx, cg, pn->pn_kid))
06206             return JS_FALSE;
06207         cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT;
06208         if (js_Emit1(cx, cg, JSOP_GROUP) < 0)
06209             return JS_FALSE;
06210         break;
06211       }
06212 
06213       case TOK_NAME:
06214         if (!BindNameToSlot(cx, &cg->treeContext, pn, JS_FALSE))
06215             return JS_FALSE;
06216         op = pn->pn_op;
06217         if (op == JSOP_ARGUMENTS) {
06218             if (js_Emit1(cx, cg, op) < 0)
06219                 return JS_FALSE;
06220             break;
06221         }
06222         if (pn->pn_slot >= 0) {
06223             atomIndex = (jsatomid) pn->pn_slot;
06224             EMIT_UINT16_IMM_OP(op, atomIndex);
06225             break;
06226         }
06227         /* FALL THROUGH */
06228 
06229 #if JS_HAS_XML_SUPPORT
06230       case TOK_XMLATTR:
06231       case TOK_XMLSPACE:
06232       case TOK_XMLTEXT:
06233       case TOK_XMLCDATA:
06234       case TOK_XMLCOMMENT:
06235 #endif
06236       case TOK_STRING:
06237       case TOK_OBJECT:
06238         /*
06239          * The scanner and parser associate JSOP_NAME with TOK_NAME, although
06240          * other bytecodes may result instead (JSOP_BINDNAME/JSOP_SETNAME,
06241          * JSOP_FORNAME, etc.).  Among JSOP_*NAME* variants, only JSOP_NAME
06242          * may generate the first operand of a call or new expression, so only
06243          * it sets the "obj" virtual machine register to the object along the
06244          * scope chain in which the name was found.
06245          *
06246          * Token types for STRING and OBJECT have corresponding bytecode ops
06247          * in pn_op and emit the same format as NAME, so they share this code.
06248          */
06249         ok = EmitAtomOp(cx, pn, pn->pn_op, cg);
06250         break;
06251 
06252       case TOK_NUMBER:
06253         ok = EmitNumberOp(cx, pn->pn_dval, cg);
06254         break;
06255 
06256 #if JS_HAS_XML_SUPPORT
06257       case TOK_ANYNAME:
06258 #endif
06259       case TOK_PRIMARY:
06260         if (js_Emit1(cx, cg, pn->pn_op) < 0)
06261             return JS_FALSE;
06262         break;
06263 
06264 #if JS_HAS_DEBUGGER_KEYWORD
06265       case TOK_DEBUGGER:
06266         if (js_Emit1(cx, cg, JSOP_DEBUGGER) < 0)
06267             return JS_FALSE;
06268         break;
06269 #endif /* JS_HAS_DEBUGGER_KEYWORD */
06270 
06271 #if JS_HAS_XML_SUPPORT
06272       case TOK_XMLELEM:
06273       case TOK_XMLLIST:
06274         if (pn->pn_op == JSOP_XMLOBJECT) {
06275             ok = EmitAtomOp(cx, pn, pn->pn_op, cg);
06276             break;
06277         }
06278 
06279         JS_ASSERT(pn->pn_type == TOK_XMLLIST || pn->pn_count != 0);
06280         switch (pn->pn_head ? pn->pn_head->pn_type : TOK_XMLLIST) {
06281           case TOK_XMLETAGO:
06282             JS_ASSERT(0);
06283             /* FALL THROUGH */
06284           case TOK_XMLPTAGC:
06285           case TOK_XMLSTAGO:
06286             break;
06287           default:
06288             if (js_Emit1(cx, cg, JSOP_STARTXML) < 0)
06289                 return JS_FALSE;
06290         }
06291 
06292         for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
06293             if (pn2->pn_type == TOK_LC &&
06294                 js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) {
06295                 return JS_FALSE;
06296             }
06297             if (!js_EmitTree(cx, cg, pn2))
06298                 return JS_FALSE;
06299             if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0)
06300                 return JS_FALSE;
06301         }
06302 
06303         if (pn->pn_extra & PNX_XMLROOT) {
06304             if (pn->pn_count == 0) {
06305                 JS_ASSERT(pn->pn_type == TOK_XMLLIST);
06306                 atom = cx->runtime->atomState.emptyAtom;
06307                 ale = js_IndexAtom(cx, atom, &cg->atomList);
06308                 if (!ale)
06309                     return JS_FALSE;
06310                 EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale));
06311             }
06312             if (js_Emit1(cx, cg, pn->pn_op) < 0)
06313                 return JS_FALSE;
06314         }
06315 #ifdef DEBUG
06316         else
06317             JS_ASSERT(pn->pn_count != 0);
06318 #endif
06319         break;
06320 
06321       case TOK_XMLPTAGC:
06322         if (pn->pn_op == JSOP_XMLOBJECT) {
06323             ok = EmitAtomOp(cx, pn, pn->pn_op, cg);
06324             break;
06325         }
06326         /* FALL THROUGH */
06327 
06328       case TOK_XMLSTAGO:
06329       case TOK_XMLETAGO:
06330       {
06331         uint32 i;
06332 
06333         if (js_Emit1(cx, cg, JSOP_STARTXML) < 0)
06334             return JS_FALSE;
06335 
06336         ale = js_IndexAtom(cx,
06337                            (pn->pn_type == TOK_XMLETAGO)
06338                            ? cx->runtime->atomState.etagoAtom
06339                            : cx->runtime->atomState.stagoAtom,
06340                            &cg->atomList);
06341         if (!ale)
06342             return JS_FALSE;
06343         EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale));
06344 
06345         JS_ASSERT(pn->pn_count != 0);
06346         pn2 = pn->pn_head;
06347         if (pn2->pn_type == TOK_LC && js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0)
06348             return JS_FALSE;
06349         if (!js_EmitTree(cx, cg, pn2))
06350             return JS_FALSE;
06351         if (js_Emit1(cx, cg, JSOP_ADD) < 0)
06352             return JS_FALSE;
06353 
06354         for (pn2 = pn2->pn_next, i = 0; pn2; pn2 = pn2->pn_next, i++) {
06355             if (pn2->pn_type == TOK_LC &&
06356                 js_Emit1(cx, cg, JSOP_STARTXMLEXPR) < 0) {
06357                 return JS_FALSE;
06358             }
06359             if (!js_EmitTree(cx, cg, pn2))
06360                 return JS_FALSE;
06361             if ((i & 1) && pn2->pn_type == TOK_LC) {
06362                 if (js_Emit1(cx, cg, JSOP_TOATTRVAL) < 0)
06363                     return JS_FALSE;
06364             }
06365             if (js_Emit1(cx, cg,
06366                          (i & 1) ? JSOP_ADDATTRVAL : JSOP_ADDATTRNAME) < 0) {
06367                 return JS_FALSE;
06368             }
06369         }
06370 
06371         ale = js_IndexAtom(cx,
06372                            (pn->pn_type == TOK_XMLPTAGC)
06373                            ? cx->runtime->atomState.ptagcAtom
06374                            : cx->runtime->atomState.tagcAtom,
06375                            &cg->atomList);
06376         if (!ale)
06377             return JS_FALSE;
06378         EMIT_ATOM_INDEX_OP(JSOP_STRING, ALE_INDEX(ale));
06379         if (js_Emit1(cx, cg, JSOP_ADD) < 0)
06380             return JS_FALSE;
06381 
06382         if ((pn->pn_extra & PNX_XMLROOT) && js_Emit1(cx, cg, pn->pn_op) < 0)
06383             return JS_FALSE;
06384         break;
06385       }
06386 
06387       case TOK_XMLNAME:
06388         if (pn->pn_arity == PN_LIST) {
06389             JS_ASSERT(pn->pn_count != 0);
06390             for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
06391                 if (!js_EmitTree(cx, cg, pn2))
06392                     return JS_FALSE;
06393                 if (pn2 != pn->pn_head && js_Emit1(cx, cg, JSOP_ADD) < 0)
06394                     return JS_FALSE;
06395             }
06396         } else {
06397             JS_ASSERT(pn->pn_arity == PN_NULLARY);
06398             ok = EmitAtomOp(cx, pn, pn->pn_op, cg);
06399         }
06400         break;
06401 
06402       case TOK_XMLPI:
06403         ale = js_IndexAtom(cx, pn->pn_atom2, &cg->atomList);
06404         if (!ale)
06405             return JS_FALSE;
06406         if (!EmitAtomIndexOp(cx, JSOP_QNAMEPART, ALE_INDEX(ale), cg))
06407             return JS_FALSE;
06408         if (!EmitAtomOp(cx, pn, JSOP_XMLPI, cg))
06409             return JS_FALSE;
06410         break;
06411 #endif /* JS_HAS_XML_SUPPORT */
06412 
06413       default:
06414         JS_ASSERT(0);
06415     }
06416 
06417     if (ok && --cg->emitLevel == 0 && cg->spanDeps)
06418         ok = OptimizeSpanDeps(cx, cg);
06419 
06420     return ok;
06421 }
06422 
06423 /* XXX get rid of offsetBias, it's used only by SRC_FOR and SRC_DECL */
06424 JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = {
06425     {"null",            0,      0,      0},
06426     {"if",              0,      0,      0},
06427     {"if-else",         2,      0,      1},
06428     {"while",           1,      0,      1},
06429     {"for",             3,      1,      1},
06430     {"continue",        0,      0,      0},
06431     {"decl",            1,      1,      1},
06432     {"pcdelta",         1,      0,      1},
06433     {"assignop",        0,      0,      0},
06434     {"cond",            1,      0,      1},
06435     {"brace",           1,      0,      1},
06436     {"hidden",          0,      0,      0},
06437     {"pcbase",          1,      0,     -1},
06438     {"label",           1,      0,      0},
06439     {"labelbrace",      1,      0,      0},
06440     {"endbrace",        0,      0,      0},
06441     {"break2label",     1,      0,      0},
06442     {"cont2label",      1,      0,      0},
06443     {"switch",          2,      0,      1},
06444     {"funcdef",         1,      0,      0},
06445     {"catch",           1,      0,      1},
06446     {"extended",       -1,      0,      0},
06447     {"newline",         0,      0,      0},
06448     {"setline",         1,      0,      0},
06449     {"xdelta",          0,      0,      0},
06450 };
06451 
06452 static intN
06453 AllocSrcNote(JSContext *cx, JSCodeGenerator *cg)
06454 {
06455     intN index;
06456     JSArenaPool *pool;
06457     size_t size;
06458 
06459     index = CG_NOTE_COUNT(cg);
06460     if (((uintN)index & CG_NOTE_MASK(cg)) == 0) {
06461         pool = cg->notePool;
06462         size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1);
06463         if (!CG_NOTES(cg)) {
06464             /* Allocate the first note array lazily; leave noteMask alone. */
06465             JS_ARENA_ALLOCATE_CAST(CG_NOTES(cg), jssrcnote *, pool, size);
06466         } else {
06467             /* Grow by doubling note array size; update noteMask on success. */
06468             JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size);
06469             if (CG_NOTES(cg))
06470                 CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1;
06471         }
06472         if (!CG_NOTES(cg)) {
06473             JS_ReportOutOfMemory(cx);
06474             return -1;
06475         }
06476     }
06477 
06478     CG_NOTE_COUNT(cg) = index + 1;
06479     return index;
06480 }
06481 
06482 intN
06483 js_NewSrcNote(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type)
06484 {
06485     intN index, n;
06486     jssrcnote *sn;
06487     ptrdiff_t offset, delta, xdelta;
06488 
06489     /*
06490      * Claim a note slot in CG_NOTES(cg) by growing it if necessary and then
06491      * incrementing CG_NOTE_COUNT(cg).
06492      */
06493     index = AllocSrcNote(cx, cg);
06494     if (index < 0)
06495         return -1;
06496     sn = &CG_NOTES(cg)[index];
06497 
06498     /*
06499      * Compute delta from the last annotated bytecode's offset.  If it's too
06500      * big to fit in sn, allocate one or more xdelta notes and reset sn.
06501      */
06502     offset = CG_OFFSET(cg);
06503     delta = offset - CG_LAST_NOTE_OFFSET(cg);
06504     CG_LAST_NOTE_OFFSET(cg) = offset;
06505     if (delta >= SN_DELTA_LIMIT) {
06506         do {
06507             xdelta = JS_MIN(delta, SN_XDELTA_MASK);
06508             SN_MAKE_XDELTA(sn, xdelta);
06509             delta -= xdelta;
06510             index = AllocSrcNote(cx, cg);
06511             if (index < 0)
06512                 return -1;
06513             sn = &CG_NOTES(cg)[index];
06514         } while (delta >= SN_DELTA_LIMIT);
06515     }
06516 
06517     /*
06518      * Initialize type and delta, then allocate the minimum number of notes
06519      * needed for type's arity.  Usually, we won't need more, but if an offset
06520      * does take two bytes, js_SetSrcNoteOffset will grow CG_NOTES(cg).
06521      */
06522     SN_MAKE_NOTE(sn, type, delta);
06523     for (n = (intN)js_SrcNoteSpec[type].arity; n > 0; n--) {
06524         if (js_NewSrcNote(cx, cg, SRC_NULL) < 0)
06525             return -1;
06526     }
06527     return index;
06528 }
06529 
06530 intN
06531 js_NewSrcNote2(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type,
06532                ptrdiff_t offset)
06533 {
06534     intN index;
06535 
06536     index = js_NewSrcNote(cx, cg, type);
06537     if (index >= 0) {
06538         if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset))
06539             return -1;
06540     }
06541     return index;
06542 }
06543 
06544 intN
06545 js_NewSrcNote3(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type,
06546                ptrdiff_t offset1, ptrdiff_t offset2)
06547 {
06548     intN index;
06549 
06550     index = js_NewSrcNote(cx, cg, type);
06551     if (index >= 0) {
06552         if (!js_SetSrcNoteOffset(cx, cg, index, 0, offset1))
06553             return -1;
06554         if (!js_SetSrcNoteOffset(cx, cg, index, 1, offset2))
06555             return -1;
06556     }
06557     return index;
06558 }
06559 
06560 static JSBool
06561 GrowSrcNotes(JSContext *cx, JSCodeGenerator *cg)
06562 {
06563     JSArenaPool *pool;
06564     size_t size;
06565 
06566     /* Grow by doubling note array size; update noteMask on success. */
06567     pool = cg->notePool;
06568     size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1);
06569     JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size);
06570     if (!CG_NOTES(cg)) {
06571         JS_ReportOutOfMemory(cx);
06572         return JS_FALSE;
06573     }
06574     CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1;
06575     return JS_TRUE;
06576 }
06577 
06578 jssrcnote *
06579 js_AddToSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, jssrcnote *sn,
06580                      ptrdiff_t delta)
06581 {
06582     ptrdiff_t base, limit, newdelta, diff;
06583     intN index;
06584 
06585     /*
06586      * Called only from OptimizeSpanDeps and js_FinishTakingSrcNotes to add to
06587      * main script note deltas, and only by a small positive amount.
06588      */
06589     JS_ASSERT(cg->current == &cg->main);
06590     JS_ASSERT((unsigned) delta < (unsigned) SN_XDELTA_LIMIT);
06591 
06592     base = SN_DELTA(sn);
06593     limit = SN_IS_XDELTA(sn) ? SN_XDELTA_LIMIT : SN_DELTA_LIMIT;
06594     newdelta = base + delta;
06595     if (newdelta < limit) {
06596         SN_SET_DELTA(sn, newdelta);
06597     } else {
06598         index = sn - cg->main.notes;
06599         if ((cg->main.noteCount & cg->main.noteMask) == 0) {
06600             if (!GrowSrcNotes(cx, cg))
06601                 return NULL;
06602             sn = cg->main.notes + index;
06603         }
06604         diff = cg->main.noteCount - index;
06605         cg->main.noteCount++;
06606         memmove(sn + 1, sn, SRCNOTE_SIZE(diff));
06607         SN_MAKE_XDELTA(sn, delta);
06608         sn++;
06609     }
06610     return sn;
06611 }
06612 
06613 JS_FRIEND_API(uintN)
06614 js_SrcNoteLength(jssrcnote *sn)
06615 {
06616     uintN arity;
06617     jssrcnote *base;
06618 
06619     arity = (intN)js_SrcNoteSpec[SN_TYPE(sn)].arity;
06620     for (base = sn++; arity; sn++, arity--) {
06621         if (*sn & SN_3BYTE_OFFSET_FLAG)
06622             sn += 2;
06623     }
06624     return sn - base;
06625 }
06626 
06627 JS_FRIEND_API(ptrdiff_t)
06628 js_GetSrcNoteOffset(jssrcnote *sn, uintN which)
06629 {
06630     /* Find the offset numbered which (i.e., skip exactly which offsets). */
06631     JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
06632     JS_ASSERT(which < js_SrcNoteSpec[SN_TYPE(sn)].arity);
06633     for (sn++; which; sn++, which--) {
06634         if (*sn & SN_3BYTE_OFFSET_FLAG)
06635             sn += 2;
06636     }
06637     if (*sn & SN_3BYTE_OFFSET_FLAG) {
06638         return (ptrdiff_t)(((uint32)(sn[0] & SN_3BYTE_OFFSET_MASK) << 16)
06639                            | (sn[1] << 8)
06640                            | sn[2]);
06641     }
06642     return (ptrdiff_t)*sn;
06643 }
06644 
06645 JSBool
06646 js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index,
06647                     uintN which, ptrdiff_t offset)
06648 {
06649     jssrcnote *sn;
06650     ptrdiff_t diff;
06651 
06652     if ((jsuword)offset >= (jsuword)((ptrdiff_t)SN_3BYTE_OFFSET_FLAG << 16)) {
06653         ReportStatementTooLarge(cx, cg);
06654         return JS_FALSE;
06655     }
06656 
06657     /* Find the offset numbered which (i.e., skip exactly which offsets). */
06658     sn = &CG_NOTES(cg)[index];
06659     JS_ASSERT(SN_TYPE(sn) != SRC_XDELTA);
06660     JS_ASSERT(which < js_SrcNoteSpec[SN_TYPE(sn)].arity);
06661     for (sn++; which; sn++, which--) {
06662         if (*sn & SN_3BYTE_OFFSET_FLAG)
06663             sn += 2;
06664     }
06665 
06666     /* See if the new offset requires three bytes. */
06667     if (offset > (ptrdiff_t)SN_3BYTE_OFFSET_MASK) {
06668         /* Maybe this offset was already set to a three-byte value. */
06669         if (!(*sn & SN_3BYTE_OFFSET_FLAG)) {
06670             /* Losing, need to insert another two bytes for this offset. */
06671             index = PTRDIFF(sn, CG_NOTES(cg), jssrcnote);
06672 
06673             /*
06674              * Simultaneously test to see if the source note array must grow to
06675              * accomodate either the first or second byte of additional storage
06676              * required by this 3-byte offset.
06677              */
06678             if (((CG_NOTE_COUNT(cg) + 1) & CG_NOTE_MASK(cg)) <= 1) {
06679                 if (!GrowSrcNotes(cx, cg))
06680                     return JS_FALSE;
06681                 sn = CG_NOTES(cg) + index;
06682             }
06683             CG_NOTE_COUNT(cg) += 2;
06684 
06685             diff = CG_NOTE_COUNT(cg) - (index + 3);
06686             JS_ASSERT(diff >= 0);
06687             if (diff > 0)
06688                 memmove(sn + 3, sn + 1, SRCNOTE_SIZE(diff));
06689         }
06690         *sn++ = (jssrcnote)(SN_3BYTE_OFFSET_FLAG | (offset >> 16));
06691         *sn++ = (jssrcnote)(offset >> 8);
06692     }
06693     *sn = (jssrcnote)offset;
06694     return JS_TRUE;
06695 }
06696 
06697 #ifdef DEBUG_notme
06698 #define DEBUG_srcnotesize
06699 #endif
06700 
06701 #ifdef DEBUG_srcnotesize
06702 #define NBINS 10
06703 static uint32 hist[NBINS];
06704 
06705 void DumpSrcNoteSizeHist()
06706 {
06707     static FILE *fp;
06708     int i, n;
06709 
06710     if (!fp) {
06711         fp = fopen("/tmp/srcnotes.hist", "w");
06712         if (!fp)
06713             return;
06714         setvbuf(fp, NULL, _IONBF, 0);
06715     }
06716     fprintf(fp, "SrcNote size histogram:\n");
06717     for (i = 0; i < NBINS; i++) {
06718         fprintf(fp, "%4u %4u ", JS_BIT(i), hist[i]);
06719         for (n = (int) JS_HOWMANY(hist[i], 10); n > 0; --n)
06720             fputc('*', fp);
06721         fputc('\n', fp);
06722     }
06723     fputc('\n', fp);
06724 }
06725 #endif
06726 
06727 /*
06728  * Fill in the storage at notes with prolog and main srcnotes; the space at
06729  * notes was allocated using the CG_COUNT_FINAL_SRCNOTES macro from jsemit.h.
06730  * SO DON'T CHANGE THIS FUNCTION WITHOUT AT LEAST CHECKING WHETHER jsemit.h's
06731  * CG_COUNT_FINAL_SRCNOTES MACRO NEEDS CORRESPONDING CHANGES!
06732  */
06733 JSBool
06734 js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg, jssrcnote *notes)
06735 {
06736     uintN prologCount, mainCount, totalCount;
06737     ptrdiff_t offset, delta;
06738     jssrcnote *sn;
06739 
06740     JS_ASSERT(cg->current == &cg->main);
06741 
06742     prologCount = cg->prolog.noteCount;
06743     if (prologCount && cg->prolog.currentLine != cg->firstLine) {
06744         CG_SWITCH_TO_PROLOG(cg);
06745         if (js_NewSrcNote2(cx, cg, SRC_SETLINE, (ptrdiff_t)cg->firstLine) < 0)
06746             return JS_FALSE;
06747         prologCount = cg->prolog.noteCount;
06748         CG_SWITCH_TO_MAIN(cg);
06749     } else {
06750         /*
06751          * Either no prolog srcnotes, or no line number change over prolog.
06752          * We don't need a SRC_SETLINE, but we may need to adjust the offset
06753          * of the first main note, by adding to its delta and possibly even
06754          * prepending SRC_XDELTA notes to it to account for prolog bytecodes
06755          * that came at and after the last annotated bytecode.
06756          */
06757         offset = CG_PROLOG_OFFSET(cg) - cg->prolog.lastNoteOffset;
06758         JS_ASSERT(offset >= 0);
06759         if (offset > 0 && cg->main.noteCount != 0) {
06760             /* NB: Use as much of the first main note's delta as we can. */
06761             sn = cg->main.notes;
06762             delta = SN_IS_XDELTA(sn)
06763                     ? SN_XDELTA_MASK - (*sn & SN_XDELTA_MASK)
06764                     : SN_DELTA_MASK - (*sn & SN_DELTA_MASK);
06765             if (offset < delta)
06766                 delta = offset;
06767             for (;;) {
06768                 if (!js_AddToSrcNoteDelta(cx, cg, sn, delta))
06769                     return JS_FALSE;
06770                 offset -= delta;
06771                 if (offset == 0)
06772                     break;
06773                 delta = JS_MIN(offset, SN_XDELTA_MASK);
06774                 sn = cg->main.notes;
06775             }
06776         }
06777     }
06778 
06779     mainCount = cg->main.noteCount;
06780     totalCount = prologCount + mainCount;
06781     if (prologCount)
06782         memcpy(notes, cg->prolog.notes, SRCNOTE_SIZE(prologCount));
06783     memcpy(notes + prologCount, cg->main.notes, SRCNOTE_SIZE(mainCount));
06784     SN_MAKE_TERMINATOR(&notes[totalCount]);
06785 
06786 #ifdef DEBUG_notme
06787   { int bin = JS_CeilingLog2(totalCount);
06788     if (bin >= NBINS)
06789         bin = NBINS - 1;
06790     ++hist[bin];
06791   }
06792 #endif
06793     return JS_TRUE;
06794 }
06795 
06796 JSBool
06797 js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg)
06798 {
06799     size_t size, incr;
06800     ptrdiff_t delta;
06801 
06802     size = TRYNOTE_SIZE(cg->treeContext.tryCount);
06803     if (size <= cg->tryNoteSpace)
06804         return JS_TRUE;
06805 
06806     /*
06807      * Allocate trynotes from cx->tempPool.
06808      * XXX Too much growing and we bloat, as other tempPool allocators block
06809      * in-place growth, and we never recycle old free space in an arena.
06810      * YYY But once we consume an entire arena, we'll realloc it, letting the
06811      * malloc heap recycle old space, while still freeing _en masse_ via the
06812      * arena pool.
06813      */
06814     if (!cg->tryBase) {
06815         size = JS_ROUNDUP(size, TRYNOTE_SIZE(TRYNOTE_CHUNK));
06816         JS_ARENA_ALLOCATE_CAST(cg->tryBase, JSTryNote *, &cx->tempPool, size);
06817         if (!cg->tryBase)
06818             return JS_FALSE;
06819         cg->tryNoteSpace = size;
06820         cg->tryNext = cg->tryBase;
06821     } else {
06822         delta = PTRDIFF((char *)cg->tryNext, (char *)cg->tryBase, char);
06823         incr = size - cg->tryNoteSpace;
06824         incr = JS_ROUNDUP(incr, TRYNOTE_SIZE(TRYNOTE_CHUNK));
06825         size = cg->tryNoteSpace;
06826         JS_ARENA_GROW_CAST(cg->tryBase, JSTryNote *, &cx->tempPool, size, incr);
06827         if (!cg->tryBase)
06828             return JS_FALSE;
06829         cg->tryNoteSpace = size + incr;
06830         cg->tryNext = (JSTryNote *)((char *)cg->tryBase + delta);
06831     }
06832     return JS_TRUE;
06833 }
06834 
06835 JSTryNote *
06836 js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start,
06837               ptrdiff_t end, ptrdiff_t catchStart)
06838 {
06839     JSTryNote *tn;
06840 
06841     JS_ASSERT(cg->tryBase <= cg->tryNext);
06842     JS_ASSERT(catchStart >= 0);
06843     tn = cg->tryNext++;
06844     tn->start = start;
06845     tn->length = end - start;
06846     tn->catchStart = catchStart;
06847     return tn;
06848 }
06849 
06850 void
06851 js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote *notes)
06852 {
06853     uintN count;
06854 
06855     count = PTRDIFF(cg->tryNext, cg->tryBase, JSTryNote);
06856     if (!count)
06857         return;
06858 
06859     memcpy(notes, cg->tryBase, TRYNOTE_SIZE(count));
06860     notes[count].start = 0;
06861     notes[count].length = CG_OFFSET(cg);
06862     notes[count].catchStart = 0;
06863 }