Back to index

lightning-sunbird  0.9+nobinonly
jsopcode.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 sw=4 ts=8 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 descriptors, disassemblers, and decompilers.
00043  */
00044 #include "jsstddef.h"
00045 #ifdef HAVE_MEMORY_H
00046 #include <memory.h>
00047 #endif
00048 #include <stdarg.h>
00049 #include <stdio.h>
00050 #include <stdlib.h>
00051 #include <string.h>
00052 #include "jstypes.h"
00053 #include "jsarena.h" /* Added by JSIFY */
00054 #include "jsutil.h" /* Added by JSIFY */
00055 #include "jsdtoa.h"
00056 #include "jsprf.h"
00057 #include "jsapi.h"
00058 #include "jsarray.h"
00059 #include "jsatom.h"
00060 #include "jscntxt.h"
00061 #include "jsconfig.h"
00062 #include "jsdbgapi.h"
00063 #include "jsemit.h"
00064 #include "jsfun.h"
00065 #include "jslock.h"
00066 #include "jsobj.h"
00067 #include "jsopcode.h"
00068 #include "jsregexp.h"
00069 #include "jsscan.h"
00070 #include "jsscope.h"
00071 #include "jsscript.h"
00072 #include "jsstr.h"
00073 
00074 #if JS_HAS_DESTRUCTURING
00075 # include "jsnum.h"
00076 #endif
00077 
00078 static const char js_incop_strs[][3] = {"++", "--"};
00079 
00080 /* Pollute the namespace locally for MSVC Win16, but not for WatCom.  */
00081 #ifdef __WINDOWS_386__
00082     #ifdef FAR
00083         #undef FAR
00084     #endif
00085 #else  /* !__WINDOWS_386__ */
00086 #ifndef FAR
00087 #define FAR
00088 #endif
00089 #endif /* !__WINDOWS_386__ */
00090 
00091 const JSCodeSpec FAR js_CodeSpec[] = {
00092 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
00093     {name,token,length,nuses,ndefs,prec,format},
00094 #include "jsopcode.tbl"
00095 #undef OPDEF
00096 };
00097 
00098 uintN js_NumCodeSpecs = sizeof (js_CodeSpec) / sizeof js_CodeSpec[0];
00099 
00100 /************************************************************************/
00101 
00102 static ptrdiff_t
00103 GetJumpOffset(jsbytecode *pc, jsbytecode *pc2)
00104 {
00105     uint32 type;
00106 
00107     type = (js_CodeSpec[*pc].format & JOF_TYPEMASK);
00108     if (JOF_TYPE_IS_EXTENDED_JUMP(type))
00109         return GET_JUMPX_OFFSET(pc2);
00110     return GET_JUMP_OFFSET(pc2);
00111 }
00112 
00113 #ifdef DEBUG
00114 
00115 JS_FRIEND_API(JSBool)
00116 js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp)
00117 {
00118     jsbytecode *pc, *end;
00119     uintN len;
00120 
00121     pc = script->code;
00122     end = pc + script->length;
00123     while (pc < end) {
00124         if (pc == script->main)
00125             fputs("main:\n", fp);
00126         len = js_Disassemble1(cx, script, pc,
00127                               PTRDIFF(pc, script->code, jsbytecode),
00128                               lines, fp);
00129         if (!len)
00130             return JS_FALSE;
00131         pc += len;
00132     }
00133     return JS_TRUE;
00134 }
00135 
00136 const char *
00137 ToDisassemblySource(JSContext *cx, jsval v)
00138 {
00139     JSObject *obj;
00140     JSScopeProperty *sprop;
00141     char *source;
00142     const char *bytes;
00143     JSString *str;
00144 
00145     if (!JSVAL_IS_PRIMITIVE(v)) {
00146         obj = JSVAL_TO_OBJECT(v);
00147         if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) {
00148             source = JS_sprintf_append(NULL, "depth %d {",
00149                                        OBJ_BLOCK_DEPTH(cx, obj));
00150             for (sprop = OBJ_SCOPE(obj)->lastProp; sprop;
00151                  sprop = sprop->parent) {
00152                 bytes = js_AtomToPrintableString(cx, JSID_TO_ATOM(sprop->id));
00153                 if (!bytes)
00154                     return NULL;
00155                 source = JS_sprintf_append(source, "%s: %d%s",
00156                                            bytes, sprop->shortid,
00157                                            sprop->parent ? ", " : "");
00158             }
00159             source = JS_sprintf_append(source, "}");
00160             if (!source)
00161                 return NULL;
00162             str = JS_NewString(cx, source, strlen(source));
00163             if (!str)
00164                 return NULL;
00165             return JS_GetStringBytes(str);
00166         }
00167     }
00168     return js_ValueToPrintableSource(cx, v);
00169 }
00170 
00171 JS_FRIEND_API(uintN)
00172 js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
00173                 JSBool lines, FILE *fp)
00174 {
00175     JSOp op;
00176     const JSCodeSpec *cs;
00177     ptrdiff_t len, off, jmplen;
00178     uint32 type;
00179     JSAtom *atom;
00180     const char *bytes;
00181 
00182     op = (JSOp)*pc;
00183     if (op >= JSOP_LIMIT) {
00184         char numBuf1[12], numBuf2[12];
00185         JS_snprintf(numBuf1, sizeof numBuf1, "%d", op);
00186         JS_snprintf(numBuf2, sizeof numBuf2, "%d", JSOP_LIMIT);
00187         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
00188                              JSMSG_BYTECODE_TOO_BIG, numBuf1, numBuf2);
00189         return 0;
00190     }
00191     cs = &js_CodeSpec[op];
00192     len = (ptrdiff_t) cs->length;
00193     fprintf(fp, "%05u:", loc);
00194     if (lines)
00195         fprintf(fp, "%4u", JS_PCToLineNumber(cx, script, pc));
00196     fprintf(fp, "  %s", cs->name);
00197     type = cs->format & JOF_TYPEMASK;
00198     switch (type) {
00199       case JOF_BYTE:
00200         if (op == JSOP_TRAP) {
00201             op = JS_GetTrapOpcode(cx, script, pc);
00202             if (op == JSOP_LIMIT)
00203                 return 0;
00204             len = (ptrdiff_t) js_CodeSpec[op].length;
00205         }
00206         break;
00207 
00208       case JOF_JUMP:
00209       case JOF_JUMPX:
00210         off = GetJumpOffset(pc, pc);
00211         fprintf(fp, " %u (%d)", loc + off, off);
00212         break;
00213 
00214       case JOF_CONST:
00215         atom = GET_ATOM(cx, script, pc);
00216         bytes = ToDisassemblySource(cx, ATOM_KEY(atom));
00217         if (!bytes)
00218             return 0;
00219         fprintf(fp, " %s", bytes);
00220         break;
00221 
00222       case JOF_UINT16:
00223       case JOF_LOCAL:
00224         fprintf(fp, " %u", GET_UINT16(pc));
00225         break;
00226 
00227       case JOF_TABLESWITCH:
00228       case JOF_TABLESWITCHX:
00229       {
00230         jsbytecode *pc2;
00231         jsint i, low, high;
00232 
00233         jmplen = (type == JOF_TABLESWITCH) ? JUMP_OFFSET_LEN
00234                                            : JUMPX_OFFSET_LEN;
00235         pc2 = pc;
00236         off = GetJumpOffset(pc, pc2);
00237         pc2 += jmplen;
00238         low = GET_JUMP_OFFSET(pc2);
00239         pc2 += JUMP_OFFSET_LEN;
00240         high = GET_JUMP_OFFSET(pc2);
00241         pc2 += JUMP_OFFSET_LEN;
00242         fprintf(fp, " defaultOffset %d low %d high %d", off, low, high);
00243         for (i = low; i <= high; i++) {
00244             off = GetJumpOffset(pc, pc2);
00245             fprintf(fp, "\n\t%d: %d", i, off);
00246             pc2 += jmplen;
00247         }
00248         len = 1 + pc2 - pc;
00249         break;
00250       }
00251 
00252       case JOF_LOOKUPSWITCH:
00253       case JOF_LOOKUPSWITCHX:
00254       {
00255         jsbytecode *pc2;
00256         jsatomid npairs;
00257 
00258         jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN
00259                                             : JUMPX_OFFSET_LEN;
00260         pc2 = pc;
00261         off = GetJumpOffset(pc, pc2);
00262         pc2 += jmplen;
00263         npairs = GET_ATOM_INDEX(pc2);
00264         pc2 += ATOM_INDEX_LEN;
00265         fprintf(fp, " offset %d npairs %u", off, (uintN) npairs);
00266         while (npairs) {
00267             atom = GET_ATOM(cx, script, pc2);
00268             pc2 += ATOM_INDEX_LEN;
00269             off = GetJumpOffset(pc, pc2);
00270             pc2 += jmplen;
00271 
00272             bytes = ToDisassemblySource(cx, ATOM_KEY(atom));
00273             if (!bytes)
00274                 return 0;
00275             fprintf(fp, "\n\t%s: %d", bytes, off);
00276             npairs--;
00277         }
00278         len = 1 + pc2 - pc;
00279         break;
00280       }
00281 
00282       case JOF_QARG:
00283         fprintf(fp, " %u", GET_ARGNO(pc));
00284         break;
00285 
00286       case JOF_QVAR:
00287         fprintf(fp, " %u", GET_VARNO(pc));
00288         break;
00289 
00290       case JOF_INDEXCONST:
00291         fprintf(fp, " %u", GET_VARNO(pc));
00292         pc += VARNO_LEN;
00293         atom = GET_ATOM(cx, script, pc);
00294         bytes = ToDisassemblySource(cx, ATOM_KEY(atom));
00295         if (!bytes)
00296             return 0;
00297         fprintf(fp, " %s", bytes);
00298         break;
00299 
00300       case JOF_UINT24:
00301         if (op == JSOP_FINDNAME) {
00302             /* Special case to avoid a JOF_FINDNAME just for this op. */
00303             atom = js_GetAtom(cx, &script->atomMap, GET_UINT24(pc));
00304             bytes = ToDisassemblySource(cx, ATOM_KEY(atom));
00305             if (!bytes)
00306                 return 0;
00307             fprintf(fp, " %s", bytes);
00308             break;
00309         }
00310 
00311         JS_ASSERT(op == JSOP_UINT24 || op == JSOP_LITERAL);
00312         fprintf(fp, " %u", GET_UINT24(pc));
00313         break;
00314 
00315       case JOF_LITOPX:
00316         atom = js_GetAtom(cx, &script->atomMap, GET_LITERAL_INDEX(pc));
00317         bytes = ToDisassemblySource(cx, ATOM_KEY(atom));
00318         if (!bytes)
00319             return 0;
00320 
00321         /*
00322          * Bytecode: JSOP_LITOPX <uint24> op [<varno> if JSOP_DEFLOCALFUN].
00323          * Advance pc to point at op.
00324          */
00325         pc += 1 + LITERAL_INDEX_LEN;
00326         op = *pc;
00327         cs = &js_CodeSpec[op];
00328         fprintf(fp, " %s op %s", bytes, cs->name);
00329         if ((cs->format & JOF_TYPEMASK) == JOF_INDEXCONST)
00330             fprintf(fp, " %u", GET_VARNO(pc));
00331 
00332         /*
00333          * Set len to advance pc to skip op and any other immediates (namely,
00334          * <varno> if JSOP_DEFLOCALFUN).
00335          */
00336         JS_ASSERT(cs->length > ATOM_INDEX_LEN);
00337         len = cs->length - ATOM_INDEX_LEN;
00338         break;
00339 
00340       default: {
00341         char numBuf[12];
00342         JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) cs->format);
00343         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
00344                              JSMSG_UNKNOWN_FORMAT, numBuf);
00345         return 0;
00346       }
00347     }
00348     fputs("\n", fp);
00349     return len;
00350 }
00351 
00352 #endif /* DEBUG */
00353 
00354 /************************************************************************/
00355 
00356 /*
00357  * Sprintf, but with unlimited and automatically allocated buffering.
00358  */
00359 typedef struct Sprinter {
00360     JSContext       *context;       /* context executing the decompiler */
00361     JSArenaPool     *pool;          /* string allocation pool */
00362     char            *base;          /* base address of buffer in pool */
00363     size_t          size;           /* size of buffer allocated at base */
00364     ptrdiff_t       offset;         /* offset of next free char in buffer */
00365 } Sprinter;
00366 
00367 #define INIT_SPRINTER(cx, sp, ap, off) \
00368     ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0,  \
00369      (sp)->offset = off)
00370 
00371 #define OFF2STR(sp,off) ((sp)->base + (off))
00372 #define STR2OFF(sp,str) ((str) - (sp)->base)
00373 #define RETRACT(sp,str) ((sp)->offset = STR2OFF(sp, str))
00374 
00375 static JSBool
00376 SprintAlloc(Sprinter *sp, size_t nb)
00377 {
00378     char *base;
00379 
00380     base = sp->base;
00381     if (!base) {
00382         JS_ARENA_ALLOCATE_CAST(base, char *, sp->pool, nb);
00383     } else {
00384         JS_ARENA_GROW_CAST(base, char *, sp->pool, sp->size, nb);
00385     }
00386     if (!base) {
00387         JS_ReportOutOfMemory(sp->context);
00388         return JS_FALSE;
00389     }
00390     sp->base = base;
00391     sp->size += nb;
00392     return JS_TRUE;
00393 }
00394 
00395 static ptrdiff_t
00396 SprintPut(Sprinter *sp, const char *s, size_t len)
00397 {
00398     ptrdiff_t nb, offset;
00399     char *bp;
00400 
00401     /* Allocate space for s, including the '\0' at the end. */
00402     nb = (sp->offset + len + 1) - sp->size;
00403     if (nb > 0 && !SprintAlloc(sp, nb))
00404         return -1;
00405 
00406     /* Advance offset and copy s into sp's buffer. */
00407     offset = sp->offset;
00408     sp->offset += len;
00409     bp = sp->base + offset;
00410     memmove(bp, s, len);
00411     bp[len] = 0;
00412     return offset;
00413 }
00414 
00415 static ptrdiff_t
00416 SprintCString(Sprinter *sp, const char *s)
00417 {
00418     return SprintPut(sp, s, strlen(s));
00419 }
00420 
00421 static ptrdiff_t
00422 Sprint(Sprinter *sp, const char *format, ...)
00423 {
00424     va_list ap;
00425     char *bp;
00426     ptrdiff_t offset;
00427 
00428     va_start(ap, format);
00429     bp = JS_vsmprintf(format, ap);      /* XXX vsaprintf */
00430     va_end(ap);
00431     if (!bp) {
00432         JS_ReportOutOfMemory(sp->context);
00433         return -1;
00434     }
00435     offset = SprintCString(sp, bp);
00436     free(bp);
00437     return offset;
00438 }
00439 
00440 const jschar js_EscapeMap[] = {
00441     '\b', 'b',
00442     '\f', 'f',
00443     '\n', 'n',
00444     '\r', 'r',
00445     '\t', 't',
00446     '\v', 'v',
00447     '"',  '"',
00448     '\'', '\'',
00449     '\\', '\\',
00450     0
00451 };
00452 
00453 #define DONT_ESCAPE     0x10000
00454 
00455 static char *
00456 QuoteString(Sprinter *sp, JSString *str, uint32 quote)
00457 {
00458     JSBool dontEscape, ok;
00459     jschar qc, c;
00460     ptrdiff_t off, len, nb;
00461     const jschar *s, *t, *u, *z;
00462     char *bp;
00463 
00464     /* Sample off first for later return value pointer computation. */
00465     dontEscape = (quote & DONT_ESCAPE) != 0;
00466     qc = (jschar) quote;
00467     off = sp->offset;
00468     if (qc && Sprint(sp, "%c", (char)qc) < 0)
00469         return NULL;
00470 
00471     /* Loop control variables: z points at end of string sentinel. */
00472     s = JSSTRING_CHARS(str);
00473     z = s + JSSTRING_LENGTH(str);
00474     for (t = s; t < z; s = ++t) {
00475         /* Move t forward from s past un-quote-worthy characters. */
00476         c = *t;
00477         while (JS_ISPRINT(c) && c != qc && c != '\\' && !(c >> 8)) {
00478             c = *++t;
00479             if (t == z)
00480                 break;
00481         }
00482         len = PTRDIFF(t, s, jschar);
00483 
00484         /* Allocate space for s, including the '\0' at the end. */
00485         nb = (sp->offset + len + 1) - sp->size;
00486         if (nb > 0 && !SprintAlloc(sp, nb))
00487             return NULL;
00488 
00489         /* Advance sp->offset and copy s into sp's buffer. */
00490         bp = sp->base + sp->offset;
00491         sp->offset += len;
00492         while (--len >= 0)
00493             *bp++ = (char) *s++;
00494         *bp = '\0';
00495 
00496         if (t == z)
00497             break;
00498 
00499         /* Use js_EscapeMap, \u, or \x only if necessary. */
00500         if ((u = js_strchr(js_EscapeMap, c)) != NULL) {
00501             ok = dontEscape
00502                  ? Sprint(sp, "%c", (char)c) >= 0
00503                  : Sprint(sp, "\\%c", (char)u[1]) >= 0;
00504         } else {
00505             ok = Sprint(sp, (c >> 8) ? "\\u%04X" : "\\x%02X", c) >= 0;
00506         }
00507         if (!ok)
00508             return NULL;
00509     }
00510 
00511     /* Sprint the closing quote and return the quoted string. */
00512     if (qc && Sprint(sp, "%c", (char)qc) < 0)
00513         return NULL;
00514 
00515     /*
00516      * If we haven't Sprint'd anything yet, Sprint an empty string so that
00517      * the OFF2STR below gives a valid result.
00518      */
00519     if (off == sp->offset && Sprint(sp, "") < 0)
00520         return NULL;
00521     return OFF2STR(sp, off);
00522 }
00523 
00524 JSString *
00525 js_QuoteString(JSContext *cx, JSString *str, jschar quote)
00526 {
00527     void *mark;
00528     Sprinter sprinter;
00529     char *bytes;
00530     JSString *escstr;
00531 
00532     mark = JS_ARENA_MARK(&cx->tempPool);
00533     INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);
00534     bytes = QuoteString(&sprinter, str, quote);
00535     escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL;
00536     JS_ARENA_RELEASE(&cx->tempPool, mark);
00537     return escstr;
00538 }
00539 
00540 /************************************************************************/
00541 
00542 #if JS_HAS_BLOCK_SCOPE
00543 typedef enum JSBraceState {
00544     ALWAYS_BRACE,
00545     MAYBE_BRACE,
00546     DONT_BRACE
00547 } JSBraceState;
00548 #endif
00549 
00550 struct JSPrinter {
00551     Sprinter        sprinter;       /* base class state */
00552     JSArenaPool     pool;           /* string allocation pool */
00553     uintN           indent;         /* indentation in spaces */
00554     JSPackedBool    pretty;         /* pretty-print: indent, use newlines */
00555     JSPackedBool    grouped;        /* in parenthesized expression context */
00556     JSScript        *script;        /* script being printed */
00557     jsbytecode      *dvgfence;      /* js_DecompileValueGenerator fencepost */
00558     JSScope         *scope;         /* script function scope */
00559 #if JS_HAS_BLOCK_SCOPE
00560     JSBraceState    braceState;     /* remove braces around let declaration */
00561     ptrdiff_t       spaceOffset;    /* -1 or offset of space before maybe-{ */
00562 #endif
00563 };
00564 
00565 /*
00566  * Hack another flag, a la JS_DONT_PRETTY_PRINT, into uintN indent parameters
00567  * to functions such as js_DecompileFunction and js_NewPrinter.  This time, as
00568  * opposed to JS_DONT_PRETTY_PRINT back in the dark ages, we can assume that a
00569  * uintN is at least 32 bits.
00570  */
00571 #define JS_IN_GROUP_CONTEXT 0x10000
00572 
00573 JSPrinter *
00574 js_NewPrinter(JSContext *cx, const char *name, uintN indent, JSBool pretty)
00575 {
00576     JSPrinter *jp;
00577 
00578     jp = (JSPrinter *) JS_malloc(cx, sizeof(JSPrinter));
00579     if (!jp)
00580         return NULL;
00581     INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
00582     JS_InitArenaPool(&jp->pool, name, 256, 1);
00583     jp->indent = indent & ~JS_IN_GROUP_CONTEXT;
00584     jp->pretty = pretty;
00585     jp->grouped = (indent & JS_IN_GROUP_CONTEXT) != 0;
00586     jp->script = NULL;
00587     jp->dvgfence = NULL;
00588     jp->scope = NULL;
00589 #if JS_HAS_BLOCK_SCOPE
00590     jp->braceState = ALWAYS_BRACE;
00591     jp->spaceOffset = -1;
00592 #endif
00593     return jp;
00594 }
00595 
00596 void
00597 js_DestroyPrinter(JSPrinter *jp)
00598 {
00599     JS_FinishArenaPool(&jp->pool);
00600     JS_free(jp->sprinter.context, jp);
00601 }
00602 
00603 JSString *
00604 js_GetPrinterOutput(JSPrinter *jp)
00605 {
00606     JSContext *cx;
00607     JSString *str;
00608 
00609     cx = jp->sprinter.context;
00610     if (!jp->sprinter.base)
00611         return cx->runtime->emptyString;
00612     str = JS_NewStringCopyZ(cx, jp->sprinter.base);
00613     if (!str)
00614         return NULL;
00615     JS_FreeArenaPool(&jp->pool);
00616     INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
00617     return str;
00618 }
00619 
00620 #if !JS_HAS_BLOCK_SCOPE
00621 # define SET_MAYBE_BRACE(jp)    jp
00622 # define CLEAR_MAYBE_BRACE(jp)  jp
00623 #else
00624 # define SET_MAYBE_BRACE(jp)    ((jp)->braceState = MAYBE_BRACE, (jp))
00625 # define CLEAR_MAYBE_BRACE(jp)  ((jp)->braceState = ALWAYS_BRACE, (jp))
00626 
00627 static void
00628 SetDontBrace(JSPrinter *jp)
00629 {
00630     ptrdiff_t offset;
00631     const char *bp;
00632 
00633     /* When not pretty-printing, newline after brace is chopped. */
00634     JS_ASSERT(jp->spaceOffset < 0);
00635     offset = jp->sprinter.offset - (jp->pretty ? 3 : 2);
00636 
00637     /* The shortest case is "if (x) {". */
00638     JS_ASSERT(offset >= 6);
00639     bp = jp->sprinter.base;
00640     if (bp[offset+0] == ' ' && bp[offset+1] == '{') {
00641         JS_ASSERT(!jp->pretty || bp[offset+2] == '\n');
00642         jp->spaceOffset = offset;
00643         jp->braceState = DONT_BRACE;
00644     }
00645 }
00646 #endif
00647 
00648 int
00649 js_printf(JSPrinter *jp, const char *format, ...)
00650 {
00651     va_list ap;
00652     char *bp, *fp;
00653     int cc;
00654 
00655     if (*format == '\0')
00656         return 0;
00657 
00658     va_start(ap, format);
00659 
00660     /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */
00661     if (*format == '\t') {
00662         format++;
00663 
00664 #if JS_HAS_BLOCK_SCOPE
00665         if (*format == '}' && jp->braceState != ALWAYS_BRACE) {
00666             JSBraceState braceState;
00667 
00668             braceState = jp->braceState;
00669             jp->braceState = ALWAYS_BRACE;
00670             if (braceState == DONT_BRACE) {
00671                 ptrdiff_t offset, delta, from;
00672 
00673                 JS_ASSERT(format[1] == '\n' || format[1] == ' ');
00674                 offset = jp->spaceOffset;
00675                 JS_ASSERT(offset >= 6);
00676 
00677                 /* Replace " {\n" at the end of jp->sprinter with "\n". */
00678                 bp = jp->sprinter.base;
00679                 if (bp[offset+0] == ' ' && bp[offset+1] == '{') {
00680                     delta = 2;
00681                     if (jp->pretty) {
00682                         /* If pretty, we don't have to worry about 'else'. */
00683                         JS_ASSERT(bp[offset+2] == '\n');
00684                     } else if (bp[offset-1] != ')') {
00685                         /* Must keep ' ' to avoid 'dolet' or 'elselet'. */
00686                         ++offset;
00687                         delta = 1;
00688                     }
00689 
00690                     from = offset + delta;
00691                     memmove(bp + offset, bp + from, jp->sprinter.offset - from);
00692                     jp->sprinter.offset -= delta;
00693                     jp->spaceOffset = -1;
00694 
00695                     format += 2;
00696                     if (*format == '\0')
00697                         return 0;
00698                 }
00699             }
00700         }
00701 #endif
00702 
00703         if (jp->pretty && Sprint(&jp->sprinter, "%*s", jp->indent, "") < 0)
00704             return -1;
00705     }
00706 
00707     /* Suppress newlines (must be once per format, at the end) if not pretty. */
00708     fp = NULL;
00709     if (!jp->pretty && format[cc = strlen(format) - 1] == '\n') {
00710         fp = JS_strdup(jp->sprinter.context, format);
00711         if (!fp)
00712             return -1;
00713         fp[cc] = '\0';
00714         format = fp;
00715     }
00716 
00717     /* Allocate temp space, convert format, and put. */
00718     bp = JS_vsmprintf(format, ap);      /* XXX vsaprintf */
00719     if (fp) {
00720         JS_free(jp->sprinter.context, fp);
00721         format = NULL;
00722     }
00723     if (!bp) {
00724         JS_ReportOutOfMemory(jp->sprinter.context);
00725         return -1;
00726     }
00727 
00728     cc = strlen(bp);
00729     if (SprintPut(&jp->sprinter, bp, (size_t)cc) < 0)
00730         cc = -1;
00731     free(bp);
00732 
00733     va_end(ap);
00734     return cc;
00735 }
00736 
00737 JSBool
00738 js_puts(JSPrinter *jp, const char *s)
00739 {
00740     return SprintCString(&jp->sprinter, s) >= 0;
00741 }
00742 
00743 /************************************************************************/
00744 
00745 typedef struct SprintStack {
00746     Sprinter    sprinter;       /* sprinter for postfix to infix buffering */
00747     ptrdiff_t   *offsets;       /* stack of postfix string offsets */
00748     jsbytecode  *opcodes;       /* parallel stack of JS opcodes */
00749     uintN       top;            /* top of stack index */
00750     uintN       inArrayInit;    /* array initialiser/comprehension level */
00751     JSPrinter   *printer;       /* permanent output goes here */
00752 } SprintStack;
00753 
00754 /*
00755  * Get a stacked offset from ss->sprinter.base, or if the stacked value |off|
00756  * is negative, lazily fetch the generating pc at |spindex = 1 + off| and try
00757  * to decompile the code that generated the missing value.  This is used when
00758  * reporting errors, where the model stack will lack |pcdepth| non-negative
00759  * offsets (see js_DecompileValueGenerator and js_DecompileCode).
00760  *
00761  * If the stacked offset is -1, return 0 to index the NUL padding at the start
00762  * of ss->sprinter.base.  If this happens, it means there is a decompiler bug
00763  * to fix, but it won't violate memory safety.
00764  */
00765 static ptrdiff_t
00766 GetOff(SprintStack *ss, uintN i)
00767 {
00768     ptrdiff_t off;
00769     JSString *str;
00770 
00771     off = ss->offsets[i];
00772     if (off < 0) {
00773 #if defined DEBUG_brendan || defined DEBUG_mrbkap || defined DEBUG_crowder
00774         JS_ASSERT(off < -1);
00775 #endif
00776         if (++off == 0) {
00777             if (!ss->sprinter.base && SprintPut(&ss->sprinter, "", 0) >= 0)
00778                 memset(ss->sprinter.base, 0, ss->sprinter.offset);
00779             return 0;
00780         }
00781 
00782         str = js_DecompileValueGenerator(ss->sprinter.context, off,
00783                                          JSVAL_NULL, NULL);
00784         if (!str)
00785             return 0;
00786         off = SprintCString(&ss->sprinter, JS_GetStringBytes(str));
00787         if (off < 0)
00788             off = 0;
00789         ss->offsets[i] = off;
00790     }
00791     return off;
00792 }
00793 
00794 static const char *
00795 GetStr(SprintStack *ss, uintN i)
00796 {
00797     ptrdiff_t off;
00798 
00799     /*
00800      * Must call GetOff before using ss->sprinter.base, since it may be null
00801      * until bootstrapped by GetOff.
00802      */
00803     off = GetOff(ss, i);
00804     return OFF2STR(&ss->sprinter, off);
00805 }
00806 
00807 /* Gap between stacked strings to allow for insertion of parens and commas. */
00808 #define PAREN_SLOP      (2 + 1)
00809 
00810 /*
00811  * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME,
00812  * JSOP_SETPROP, and JSOP_SETELEM, respectively.  They are never stored in
00813  * bytecode, so they don't preempt valid opcodes.
00814  */
00815 #define JSOP_GETPROP2   256
00816 #define JSOP_GETELEM2   257
00817 
00818 static JSBool
00819 PushOff(SprintStack *ss, ptrdiff_t off, JSOp op)
00820 {
00821     uintN top;
00822 
00823     if (!SprintAlloc(&ss->sprinter, PAREN_SLOP))
00824         return JS_FALSE;
00825 
00826     /* ss->top points to the next free slot; be paranoid about overflow. */
00827     top = ss->top;
00828     JS_ASSERT(top < ss->printer->script->depth);
00829     if (top >= ss->printer->script->depth) {
00830         JS_ReportOutOfMemory(ss->sprinter.context);
00831         return JS_FALSE;
00832     }
00833 
00834     /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */
00835     ss->offsets[top] = off;
00836     ss->opcodes[top] = (op == JSOP_GETPROP2) ? JSOP_GETPROP
00837                      : (op == JSOP_GETELEM2) ? JSOP_GETELEM
00838                      : (jsbytecode) op;
00839     ss->top = ++top;
00840     memset(OFF2STR(&ss->sprinter, ss->sprinter.offset), 0, PAREN_SLOP);
00841     ss->sprinter.offset += PAREN_SLOP;
00842     return JS_TRUE;
00843 }
00844 
00845 static ptrdiff_t
00846 PopOff(SprintStack *ss, JSOp op)
00847 {
00848     uintN top;
00849     const JSCodeSpec *cs, *topcs;
00850     ptrdiff_t off;
00851 
00852     /* ss->top points to the next free slot; be paranoid about underflow. */
00853     top = ss->top;
00854     JS_ASSERT(top != 0);
00855     if (top == 0)
00856         return 0;
00857 
00858     ss->top = --top;
00859     off = GetOff(ss, top);
00860     topcs = &js_CodeSpec[ss->opcodes[top]];
00861     cs = &js_CodeSpec[op];
00862     if (topcs->prec != 0 && topcs->prec < cs->prec) {
00863         ss->sprinter.offset = ss->offsets[top] = off - 2;
00864         off = Sprint(&ss->sprinter, "(%s)", OFF2STR(&ss->sprinter, off));
00865     } else {
00866         ss->sprinter.offset = off;
00867     }
00868     return off;
00869 }
00870 
00871 static const char *
00872 PopStr(SprintStack *ss, JSOp op)
00873 {
00874     ptrdiff_t off;
00875 
00876     off = PopOff(ss, op);
00877     return OFF2STR(&ss->sprinter, off);
00878 }
00879 
00880 typedef struct TableEntry {
00881     jsval       key;
00882     ptrdiff_t   offset;
00883     JSAtom      *label;
00884     jsint       order;          /* source order for stable tableswitch sort */
00885 } TableEntry;
00886 
00887 static JSBool
00888 CompareOffsets(void *arg, const void *v1, const void *v2, int *result)
00889 {
00890     ptrdiff_t offset_diff;
00891     const TableEntry *te1 = (const TableEntry *) v1,
00892                      *te2 = (const TableEntry *) v2;
00893 
00894     offset_diff = te1->offset - te2->offset;
00895     *result = (offset_diff == 0 ? te1->order - te2->order
00896                : offset_diff < 0 ? -1
00897                : 1);
00898     return JS_TRUE;
00899 }
00900 
00901 static jsbytecode *
00902 Decompile(SprintStack *ss, jsbytecode *pc, intN nb);
00903 
00904 static JSBool
00905 DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength,
00906                 jsbytecode *pc, ptrdiff_t switchLength,
00907                 ptrdiff_t defaultOffset, JSBool isCondSwitch)
00908 {
00909     JSContext *cx;
00910     JSPrinter *jp;
00911     ptrdiff_t off, off2, diff, caseExprOff;
00912     char *lval, *rval;
00913     uintN i;
00914     jsval key;
00915     JSString *str;
00916 
00917     cx = ss->sprinter.context;
00918     jp = ss->printer;
00919 
00920     /* JSOP_CONDSWITCH doesn't pop, unlike JSOP_{LOOKUP,TABLE}SWITCH. */
00921     off = isCondSwitch ? GetOff(ss, ss->top-1) : PopOff(ss, JSOP_NOP);
00922     lval = OFF2STR(&ss->sprinter, off);
00923 
00924     js_printf(CLEAR_MAYBE_BRACE(jp), "\tswitch (%s) {\n", lval);
00925 
00926     if (tableLength) {
00927         diff = table[0].offset - defaultOffset;
00928         if (diff > 0) {
00929             jp->indent += 2;
00930             js_printf(jp, "\t%s:\n", js_default_str);
00931             jp->indent += 2;
00932             if (!Decompile(ss, pc + defaultOffset, diff))
00933                 return JS_FALSE;
00934             jp->indent -= 4;
00935         }
00936 
00937         caseExprOff = isCondSwitch ? JSOP_CONDSWITCH_LENGTH : 0;
00938 
00939         for (i = 0; i < tableLength; i++) {
00940             off = table[i].offset;
00941             off2 = (i + 1 < tableLength) ? table[i + 1].offset : switchLength;
00942 
00943             key = table[i].key;
00944             if (isCondSwitch) {
00945                 ptrdiff_t nextCaseExprOff;
00946 
00947                 /*
00948                  * key encodes the JSOP_CASE bytecode's offset from switchtop.
00949                  * The next case expression follows immediately, unless we are
00950                  * at the last case.
00951                  */
00952                 nextCaseExprOff = (ptrdiff_t)JSVAL_TO_INT(key);
00953                 nextCaseExprOff += js_CodeSpec[pc[nextCaseExprOff]].length;
00954                 jp->indent += 2;
00955                 if (!Decompile(ss, pc + caseExprOff,
00956                                nextCaseExprOff - caseExprOff)) {
00957                     return JS_FALSE;
00958                 }
00959                 caseExprOff = nextCaseExprOff;
00960 
00961                 /* Balance the stack as if this JSOP_CASE matched. */
00962                 --ss->top;
00963             } else {
00964                 /*
00965                  * key comes from an atom, not the decompiler, so we need to
00966                  * quote it if it's a string literal.  But if table[i].label
00967                  * is non-null, key was constant-propagated and label is the
00968                  * name of the const we should show as the case label.  We set
00969                  * key to undefined so this identifier is escaped, if required
00970                  * by non-ASCII characters, but not quoted, by QuoteString.
00971                  */
00972                 if (table[i].label) {
00973                     str = ATOM_TO_STRING(table[i].label);
00974                     key = JSVAL_VOID;
00975                 } else {
00976                     str = js_ValueToString(cx, key);
00977                     if (!str)
00978                         return JS_FALSE;
00979                 }
00980                 rval = QuoteString(&ss->sprinter, str,
00981                                    (jschar)(JSVAL_IS_STRING(key) ? '"' : 0));
00982                 if (!rval)
00983                     return JS_FALSE;
00984                 RETRACT(&ss->sprinter, rval);
00985                 jp->indent += 2;
00986                 js_printf(jp, "\tcase %s:\n", rval);
00987             }
00988 
00989             jp->indent += 2;
00990             if (off <= defaultOffset && defaultOffset < off2) {
00991                 diff = defaultOffset - off;
00992                 if (diff != 0) {
00993                     if (!Decompile(ss, pc + off, diff))
00994                         return JS_FALSE;
00995                     off = defaultOffset;
00996                 }
00997                 jp->indent -= 2;
00998                 js_printf(jp, "\t%s:\n", js_default_str);
00999                 jp->indent += 2;
01000             }
01001             if (!Decompile(ss, pc + off, off2 - off))
01002                 return JS_FALSE;
01003             jp->indent -= 4;
01004 
01005             /* Re-balance as if last JSOP_CASE or JSOP_DEFAULT mismatched. */
01006             if (isCondSwitch)
01007                 ++ss->top;
01008         }
01009     }
01010 
01011     if (defaultOffset == switchLength) {
01012         jp->indent += 2;
01013         js_printf(jp, "\t%s:;\n", js_default_str);
01014         jp->indent -= 2;
01015     }
01016     js_printf(jp, "\t}\n");
01017 
01018     /* By the end of a JSOP_CONDSWITCH, the discriminant has been popped. */
01019     if (isCondSwitch)
01020         --ss->top;
01021     return JS_TRUE;
01022 }
01023 
01024 static JSAtom *
01025 GetSlotAtom(JSPrinter *jp, JSPropertyOp getter, uintN slot)
01026 {
01027     JSScope *scope;
01028     JSScopeProperty *sprop;
01029     JSObject *obj, *proto;
01030 
01031     scope = jp->scope;
01032     while (scope) {
01033         for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
01034             if (sprop->getter != getter)
01035                 continue;
01036             JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
01037             JS_ASSERT(JSID_IS_ATOM(sprop->id));
01038             if ((uintN) sprop->shortid == slot)
01039                 return JSID_TO_ATOM(sprop->id);
01040         }
01041         obj = scope->object;
01042         if (!obj)
01043             break;
01044         proto = OBJ_GET_PROTO(jp->sprinter.context, obj);
01045         if (!proto)
01046             break;
01047         scope = OBJ_SCOPE(proto);
01048     }
01049     return NULL;
01050 }
01051 
01052 /*
01053  * NB: Indexed by SRC_DECL_* defines from jsemit.h.
01054  */
01055 static const char * const var_prefix[] = {"var ", "const ", "let "};
01056 
01057 static const char *
01058 VarPrefix(jssrcnote *sn)
01059 {
01060     if (sn && (SN_TYPE(sn) == SRC_DECL || SN_TYPE(sn) == SRC_GROUPASSIGN)) {
01061         ptrdiff_t type = js_GetSrcNoteOffset(sn, 0);
01062         if ((uintN)type <= SRC_DECL_LET)
01063             return var_prefix[type];
01064     }
01065     return "";
01066 }
01067 #define LOCAL_ASSERT_RV(expr, rv)                                             \
01068     JS_BEGIN_MACRO                                                            \
01069         JS_ASSERT(expr);                                                      \
01070         if (!(expr)) return (rv);                                             \
01071     JS_END_MACRO
01072 
01073 const char *
01074 GetLocal(SprintStack *ss, jsint i)
01075 {
01076     ptrdiff_t off;
01077     JSContext *cx;
01078     JSScript *script;
01079     jsatomid j, n;
01080     JSAtom *atom;
01081     JSObject *obj;
01082     jsint depth, count;
01083     JSScopeProperty *sprop;
01084     const char *rval;
01085 
01086 #define LOCAL_ASSERT(expr)      LOCAL_ASSERT_RV(expr, "")
01087 
01088     off = ss->offsets[i];
01089     if (off >= 0)
01090         return OFF2STR(&ss->sprinter, off);
01091 
01092     /*
01093      * We must be called from js_DecompileValueGenerator (via Decompile) when
01094      * dereferencing a local that's undefined or null.  Search script->atomMap
01095      * for the block containing this local by its stack index, i.
01096      */
01097     cx = ss->sprinter.context;
01098     script = ss->printer->script;
01099     for (j = 0, n = script->atomMap.length; j < n; j++) {
01100         atom = script->atomMap.vector[j];
01101         if (ATOM_IS_OBJECT(atom)) {
01102             obj = ATOM_TO_OBJECT(atom);
01103             if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) {
01104                 depth = OBJ_BLOCK_DEPTH(cx, obj);
01105                 count = OBJ_BLOCK_COUNT(cx, obj);
01106                 if ((jsuint)(i - depth) < (jsuint)count)
01107                     break;
01108             }
01109         }
01110     }
01111 
01112     LOCAL_ASSERT(j < n);
01113     i -= depth;
01114     for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) {
01115         if (sprop->shortid == i)
01116             break;
01117     }
01118 
01119     LOCAL_ASSERT(sprop && JSID_IS_ATOM(sprop->id));
01120     atom = JSID_TO_ATOM(sprop->id);
01121     rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
01122     if (!rval)
01123         return NULL;
01124     RETRACT(&ss->sprinter, rval);
01125     return rval;
01126 
01127 #undef LOCAL_ASSERT
01128 }
01129 
01130 #if JS_HAS_DESTRUCTURING
01131 
01132 #define LOCAL_ASSERT(expr)  LOCAL_ASSERT_RV(expr, NULL)
01133 #define LOAD_OP_DATA(pc)    (oplen = (cs = &js_CodeSpec[op = *pc])->length)
01134 
01135 static jsbytecode *
01136 DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc);
01137 
01138 static jsbytecode *
01139 DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
01140                           JSBool *hole)
01141 {
01142     JSContext *cx;
01143     JSPrinter *jp;
01144     JSOp op;
01145     const JSCodeSpec *cs;
01146     uintN oplen, i;
01147     const char *lval, *xval;
01148     ptrdiff_t todo;
01149     JSAtom *atom;
01150 
01151     *hole = JS_FALSE;
01152     cx = ss->sprinter.context;
01153     jp = ss->printer;
01154     LOAD_OP_DATA(pc);
01155 
01156     switch (op) {
01157       case JSOP_POP:
01158         *hole = JS_TRUE;
01159         todo = SprintPut(&ss->sprinter, ", ", 2);
01160         break;
01161 
01162       case JSOP_DUP:
01163         pc = DecompileDestructuring(ss, pc, endpc);
01164         if (!pc)
01165             return NULL;
01166         if (pc == endpc)
01167             return pc;
01168         LOAD_OP_DATA(pc);
01169         lval = PopStr(ss, JSOP_NOP);
01170         todo = SprintCString(&ss->sprinter, lval);
01171         if (op == JSOP_SETSP)
01172             return pc;
01173         LOCAL_ASSERT(*pc == JSOP_POP);
01174         break;
01175 
01176       case JSOP_SETARG:
01177       case JSOP_SETVAR:
01178       case JSOP_SETGVAR:
01179       case JSOP_SETLOCAL:
01180         LOCAL_ASSERT(pc[oplen] == JSOP_POP || pc[oplen] == JSOP_SETSP);
01181         /* FALL THROUGH */
01182 
01183       case JSOP_SETLOCALPOP:
01184         i = GET_UINT16(pc);
01185         atom = NULL;
01186         lval = NULL;
01187         if (op == JSOP_SETARG)
01188             atom = GetSlotAtom(jp, js_GetArgument, i);
01189         else if (op == JSOP_SETVAR)
01190             atom = GetSlotAtom(jp, js_GetLocalVariable, i);
01191         else if (op == JSOP_SETGVAR)
01192             atom = GET_ATOM(cx, jp->script, pc);
01193         else
01194             lval = GetLocal(ss, i);
01195         if (atom)
01196             lval = js_AtomToPrintableString(cx, atom);
01197         LOCAL_ASSERT(lval);
01198         todo = SprintCString(&ss->sprinter, lval);
01199         if (op != JSOP_SETLOCALPOP) {
01200             pc += oplen;
01201             if (pc == endpc)
01202                 return pc;
01203             LOAD_OP_DATA(pc);
01204             if (op == JSOP_SETSP)
01205                 return pc;
01206             LOCAL_ASSERT(op == JSOP_POP);
01207         }
01208         break;
01209 
01210       default:
01211         /*
01212          * We may need to auto-parenthesize the left-most value decompiled
01213          * here, so add back PAREN_SLOP temporarily.  Then decompile until the
01214          * opcode that would reduce the stack depth to (ss->top-1), which we
01215          * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for
01216          * the nb parameter.
01217          */
01218         todo = ss->sprinter.offset;
01219         ss->sprinter.offset = todo + PAREN_SLOP;
01220         pc = Decompile(ss, pc, -ss->top);
01221         if (!pc)
01222             return NULL;
01223         if (pc == endpc)
01224             return pc;
01225         LOAD_OP_DATA(pc);
01226         LOCAL_ASSERT(op == JSOP_ENUMELEM || op == JSOP_ENUMCONSTELEM);
01227         xval = PopStr(ss, JSOP_NOP);
01228         lval = PopStr(ss, JSOP_GETPROP);
01229         ss->sprinter.offset = todo;
01230         if (*lval == '\0') {
01231             /* lval is from JSOP_BINDNAME, so just print xval. */
01232             todo = SprintCString(&ss->sprinter, xval);
01233         } else if (*xval == '\0') {
01234             /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */
01235             todo = SprintCString(&ss->sprinter, lval);
01236         } else {
01237             todo = Sprint(&ss->sprinter,
01238                           (js_CodeSpec[ss->opcodes[ss->top+1]].format
01239                            & JOF_XMLNAME)
01240                           ? "%s.%s"
01241                           : "%s[%s]",
01242                           lval, xval);
01243         }
01244         break;
01245     }
01246 
01247     if (todo < 0)
01248         return NULL;
01249 
01250     LOCAL_ASSERT(pc < endpc);
01251     pc += oplen;
01252     return pc;
01253 }
01254 
01255 /*
01256  * Starting with a SRC_DESTRUCT-annotated JSOP_DUP, decompile a destructuring
01257  * left-hand side object or array initialiser, including nested destructuring
01258  * initialisers.  On successful return, the decompilation will be pushed on ss
01259  * and the return value will point to the POP or GROUP bytecode following the
01260  * destructuring expression.
01261  *
01262  * At any point, if pc is equal to endpc and would otherwise advance, we stop
01263  * immediately and return endpc.
01264  */
01265 static jsbytecode *
01266 DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc)
01267 {
01268     ptrdiff_t head, todo;
01269     JSContext *cx;
01270     JSPrinter *jp;
01271     JSOp op, saveop;
01272     const JSCodeSpec *cs;
01273     uintN oplen;
01274     jsint i, lasti;
01275     jsdouble d;
01276     const char *lval;
01277     jsbytecode *pc2;
01278     jsatomid atomIndex;
01279     JSAtom *atom;
01280     jssrcnote *sn;
01281     JSString *str;
01282     JSBool hole;
01283 
01284     LOCAL_ASSERT(*pc == JSOP_DUP);
01285     pc += JSOP_DUP_LENGTH;
01286 
01287     /*
01288      * Set head so we can rewrite '[' to '{' as needed.  Back up PAREN_SLOP
01289      * chars so the destructuring decompilation accumulates contiguously in
01290      * ss->sprinter starting with "[".
01291      */
01292     head = SprintPut(&ss->sprinter, "[", 1);
01293     if (head < 0 || !PushOff(ss, head, JSOP_NOP))
01294         return NULL;
01295     ss->sprinter.offset -= PAREN_SLOP;
01296     LOCAL_ASSERT(head == ss->sprinter.offset - 1);
01297     LOCAL_ASSERT(*OFF2STR(&ss->sprinter, head) == '[');
01298 
01299     cx = ss->sprinter.context;
01300     jp = ss->printer;
01301     lasti = -1;
01302 
01303     while (pc < endpc) {
01304         LOAD_OP_DATA(pc);
01305         saveop = op;
01306 
01307         switch (op) {
01308           case JSOP_POP:
01309             pc += oplen;
01310             goto out;
01311 
01312           /* Handle the optimized number-pushing opcodes. */
01313           case JSOP_ZERO:   d = i = 0; goto do_getelem;
01314           case JSOP_ONE:    d = i = 1; goto do_getelem;
01315           case JSOP_UINT16: d = i = GET_UINT16(pc); goto do_getelem;
01316           case JSOP_UINT24: d = i = GET_UINT24(pc); goto do_getelem;
01317 
01318           /* Handle the extended literal form of JSOP_NUMBER. */
01319           case JSOP_LITOPX:
01320             atomIndex = GET_LITERAL_INDEX(pc);
01321             pc2 = pc + 1 + LITERAL_INDEX_LEN;
01322             op = *pc2;
01323             LOCAL_ASSERT(op == JSOP_NUMBER);
01324             goto do_getatom;
01325 
01326           case JSOP_NUMBER:
01327             atomIndex = GET_ATOM_INDEX(pc);
01328 
01329           do_getatom:
01330             atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex);
01331             d = *ATOM_TO_DOUBLE(atom);
01332             LOCAL_ASSERT(JSDOUBLE_IS_FINITE(d) && !JSDOUBLE_IS_NEGZERO(d));
01333             i = (jsint)d;
01334 
01335           do_getelem:
01336             sn = js_GetSrcNote(jp->script, pc);
01337             pc += oplen;
01338             if (pc == endpc)
01339                 return pc;
01340             LOAD_OP_DATA(pc);
01341             LOCAL_ASSERT(op == JSOP_GETELEM);
01342 
01343             /* Distinguish object from array by opcode or source note. */
01344             if (saveop == JSOP_LITERAL ||
01345                 (sn && SN_TYPE(sn) == SRC_INITPROP)) {
01346                 *OFF2STR(&ss->sprinter, head) = '{';
01347                 if (Sprint(&ss->sprinter, "%g: ", d) < 0)
01348                     return NULL;
01349             } else {
01350                 /* Sanity check for the gnarly control flow above. */
01351                 LOCAL_ASSERT(i == d);
01352 
01353                 /* Fill in any holes (holes at the end don't matter). */
01354                 while (++lasti < i) {
01355                     if (SprintPut(&ss->sprinter, ", ", 2) < 0)
01356                         return NULL;
01357                 }
01358             }
01359             break;
01360 
01361           case JSOP_LITERAL:
01362             atomIndex = GET_LITERAL_INDEX(pc);
01363             goto do_getatom;
01364 
01365           case JSOP_GETPROP:
01366             *OFF2STR(&ss->sprinter, head) = '{';
01367             atom = GET_ATOM(cx, jp->script, pc);
01368             str = ATOM_TO_STRING(atom);
01369             if (!QuoteString(&ss->sprinter, str,
01370                              js_IsIdentifier(str) ? 0 : (jschar)'\'')) {
01371                 return NULL;
01372             }
01373             if (SprintPut(&ss->sprinter, ": ", 2) < 0)
01374                 return NULL;
01375             break;
01376 
01377           default:
01378             LOCAL_ASSERT(0);
01379         }
01380 
01381         pc += oplen;
01382         if (pc == endpc)
01383             return pc;
01384 
01385         /*
01386          * Decompile the left-hand side expression whose bytecode starts at pc
01387          * and continues for a bounded number of bytecodes or stack operations
01388          * (and which in any event stops before endpc).
01389          */
01390         pc = DecompileDestructuringLHS(ss, pc, endpc, &hole);
01391         if (!pc)
01392             return NULL;
01393         if (pc == endpc || *pc != JSOP_DUP)
01394             break;
01395 
01396         /*
01397          * Check for SRC_DESTRUCT on this JSOP_DUP, which would mean another
01398          * destructuring initialiser abuts this one, and we should stop.  This
01399          * happens with source of the form '[a] = [b] = c'.
01400          */
01401         sn = js_GetSrcNote(jp->script, pc);
01402         if (sn && SN_TYPE(sn) == SRC_DESTRUCT)
01403             break;
01404 
01405         if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
01406             return NULL;
01407 
01408         pc += JSOP_DUP_LENGTH;
01409     }
01410 
01411 out:
01412     lval = OFF2STR(&ss->sprinter, head);
01413     todo = SprintPut(&ss->sprinter, (*lval == '[') ? "]" : "}", 1);
01414     if (todo < 0)
01415         return NULL;
01416     return pc;
01417 }
01418 
01419 static jsbytecode *
01420 DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
01421                          jssrcnote *sn, ptrdiff_t *todop)
01422 {
01423     JSOp op;
01424     const JSCodeSpec *cs;
01425     uintN oplen, start, end, i;
01426     ptrdiff_t todo;
01427     JSBool hole;
01428     const char *rval;
01429 
01430     LOAD_OP_DATA(pc);
01431     LOCAL_ASSERT(op == JSOP_PUSH || op == JSOP_GETLOCAL);
01432 
01433     todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn));
01434     if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
01435         return NULL;
01436     ss->sprinter.offset -= PAREN_SLOP;
01437 
01438     for (;;) {
01439         pc += oplen;
01440         if (pc == endpc)
01441             return pc;
01442         pc = DecompileDestructuringLHS(ss, pc, endpc, &hole);
01443         if (!pc)
01444             return NULL;
01445         if (pc == endpc)
01446             return pc;
01447         LOAD_OP_DATA(pc);
01448         if (op != JSOP_PUSH && op != JSOP_GETLOCAL)
01449             break;
01450         if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
01451             return NULL;
01452     }
01453 
01454     LOCAL_ASSERT(op == JSOP_SETSP);
01455     if (SprintPut(&ss->sprinter, "] = [", 5) < 0)
01456         return NULL;
01457 
01458     start = GET_UINT16(pc);
01459     end = ss->top - 1;
01460     for (i = start; i < end; i++) {
01461         rval = GetStr(ss, i);
01462         if (Sprint(&ss->sprinter, "%s%s",
01463                    (i == start) ? "" : ", ",
01464                    (i == end - 1 && *rval == '\0') ? ", " : rval) < 0) {
01465             return NULL;
01466         }
01467     }
01468 
01469     if (SprintPut(&ss->sprinter, "]", 1) < 0)
01470         return NULL;
01471     ss->sprinter.offset = ss->offsets[i];
01472     ss->top = start;
01473     *todop = todo;
01474     return pc;
01475 }
01476 
01477 #undef LOCAL_ASSERT
01478 #undef LOAD_OP_DATA
01479 
01480 #endif /* JS_HAS_DESTRUCTURING */
01481 
01482 /*
01483  * If nb is non-negative, decompile nb bytecodes starting at pc.  Otherwise
01484  * the decompiler starts at pc and continues until it reaches an opcode for
01485  * which decompiling would result in the stack depth equaling -(nb + 1).
01486  */
01487 static jsbytecode *
01488 Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
01489 {
01490     JSContext *cx;
01491     JSPrinter *jp, *jp2;
01492     jsbytecode *startpc, *endpc, *pc2, *done, *forelem_tail, *forelem_done;
01493     ptrdiff_t tail, todo, len, oplen, cond, next;
01494     JSOp op, lastop, saveop;
01495     const JSCodeSpec *cs;
01496     jssrcnote *sn, *sn2;
01497     const char *lval, *rval, *xval, *fmt;
01498     jsint i, argc;
01499     char **argv;
01500     jsatomid atomIndex;
01501     JSAtom *atom;
01502     JSObject *obj;
01503     JSFunction *fun;
01504     JSString *str;
01505     JSBool ok;
01506 #if JS_HAS_XML_SUPPORT
01507     JSBool foreach, inXML, quoteAttr;
01508 #else
01509 #define inXML JS_FALSE
01510 #endif
01511     jsval val;
01512     int stackDummy;
01513 
01514     static const char exception_cookie[] = "/*EXCEPTION*/";
01515     static const char retsub_pc_cookie[] = "/*RETSUB_PC*/";
01516     static const char forelem_cookie[]   = "/*FORELEM*/";
01517     static const char with_cookie[]      = "/*WITH*/";
01518     static const char dot_format[]       = "%s.%s";
01519     static const char index_format[]     = "%s[%s]";
01520     static const char predot_format[]    = "%s%s.%s";
01521     static const char postdot_format[]   = "%s.%s%s";
01522     static const char preindex_format[]  = "%s%s[%s]";
01523     static const char postindex_format[] = "%s[%s]%s";
01524     static const char ss_format[]        = "%s%s";
01525 
01526 /*
01527  * Local macros
01528  */
01529 #define DECOMPILE_CODE(pc,nb)   if (!Decompile(ss, pc, nb)) return NULL
01530 #define POP_STR()               PopStr(ss, op)
01531 #define LOCAL_ASSERT(expr)      LOCAL_ASSERT_RV(expr, JS_FALSE)
01532 
01533 /*
01534  * Callers know that ATOM_IS_STRING(atom), and we leave it to the optimizer to
01535  * common ATOM_TO_STRING(atom) here and near the call sites.
01536  */
01537 #define ATOM_IS_IDENTIFIER(atom) js_IsIdentifier(ATOM_TO_STRING(atom))
01538 #define ATOM_IS_KEYWORD(atom)                                                 \
01539             (js_CheckKeyword(JSSTRING_CHARS(ATOM_TO_STRING(atom)),            \
01540                              JSSTRING_LENGTH(ATOM_TO_STRING(atom))) != TOK_EOF)
01541 
01542 /*
01543  * Given an atom already fetched from jp->script's atom map, quote/escape its
01544  * string appropriately into rval, and select fmt from the quoted and unquoted
01545  * alternatives.
01546  */
01547 #define GET_QUOTE_AND_FMT(qfmt, ufmt, rval)                                   \
01548     JS_BEGIN_MACRO                                                            \
01549         jschar quote_;                                                        \
01550         if (!ATOM_IS_IDENTIFIER(atom)) {                                      \
01551             quote_ = '\'';                                                    \
01552             fmt = qfmt;                                                       \
01553         } else {                                                              \
01554             quote_ = 0;                                                       \
01555             fmt = ufmt;                                                       \
01556         }                                                                     \
01557         rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), quote_);      \
01558         if (!rval)                                                            \
01559             return NULL;                                                      \
01560     JS_END_MACRO
01561 
01562 /*
01563  * Get atom from jp->script's atom map, quote/escape its string appropriately
01564  * into rval, and select fmt from the quoted and unquoted alternatives.
01565  */
01566 #define GET_ATOM_QUOTE_AND_FMT(qfmt, ufmt, rval)                              \
01567     JS_BEGIN_MACRO                                                            \
01568         atom = GET_ATOM(cx, jp->script, pc);                                  \
01569         GET_QUOTE_AND_FMT(qfmt, ufmt, rval);                                  \
01570     JS_END_MACRO
01571 
01572     cx = ss->sprinter.context;
01573     if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
01574         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
01575         return NULL;
01576     }
01577 
01578     jp = ss->printer;
01579     startpc = pc;
01580     endpc = (nb < 0) ? jp->script->code + jp->script->length : pc + nb;
01581     forelem_tail = forelem_done = NULL;
01582     tail = -1;
01583     todo = -2;                  /* NB: different from Sprint() error return. */
01584     saveop = JSOP_NOP;
01585     sn = NULL;
01586     rval = NULL;
01587 #if JS_HAS_XML_SUPPORT
01588     foreach = inXML = quoteAttr = JS_FALSE;
01589 #endif
01590 
01591     while (nb < 0 || pc < endpc) {
01592         /*
01593          * Move saveop to lastop so prefixed bytecodes can take special action
01594          * while sharing maximal code.  Set op and saveop to the new bytecode,
01595          * use op in POP_STR to trigger automatic parenthesization, but push
01596          * saveop at the bottom of the loop if this op pushes.  Thus op may be
01597          * set to nop or otherwise mutated to suppress auto-parens.
01598          */
01599         lastop = saveop;
01600         op = saveop = (JSOp) *pc;
01601         cs = &js_CodeSpec[saveop];
01602         len = oplen = cs->length;
01603 
01604         if (nb < 0 && -(nb + 1) == (intN)ss->top - cs->nuses + cs->ndefs)
01605             return pc;
01606 
01607         if (pc + oplen == jp->dvgfence) {
01608             JSStackFrame *fp;
01609             uint32 format, mode, type;
01610 
01611             /*
01612              * Rewrite non-get ops to their "get" format if the error is in
01613              * the bytecode at pc, so we don't decompile more than the error
01614              * expression.
01615              */
01616             for (fp = cx->fp; fp && !fp->script; fp = fp->down)
01617                 continue;
01618             format = cs->format;
01619             if (((fp && pc == fp->pc) ||
01620                  (pc == startpc && cs->nuses != 0)) &&
01621                 format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_IMPORT|JOF_FOR)) {
01622                 mode = (format & JOF_MODEMASK);
01623                 if (mode == JOF_NAME) {
01624                     /*
01625                      * JOF_NAME does not imply JOF_CONST, so we must check for
01626                      * the QARG and QVAR format types, and translate those to
01627                      * JSOP_GETARG or JSOP_GETVAR appropriately, instead of to
01628                      * JSOP_NAME.
01629                      */
01630                     type = format & JOF_TYPEMASK;
01631                     op = (type == JOF_QARG)
01632                          ? JSOP_GETARG
01633                          : (type == JOF_QVAR)
01634                          ? JSOP_GETVAR
01635                          : (type == JOF_LOCAL)
01636                          ? JSOP_GETLOCAL
01637                          : JSOP_NAME;
01638 
01639                     i = cs->nuses - js_CodeSpec[op].nuses;
01640                     while (--i >= 0)
01641                         PopOff(ss, JSOP_NOP);
01642                 } else {
01643                     /*
01644                      * We must replace the faulting pc's bytecode with a
01645                      * corresponding JSOP_GET* code.  For JSOP_SET{PROP,ELEM},
01646                      * we must use the "2nd" form of JSOP_GET{PROP,ELEM}, to
01647                      * throw away the assignment op's right-hand operand and
01648                      * decompile it as if it were a GET of its left-hand
01649                      * operand.
01650                      */
01651                     if (mode == JOF_PROP) {
01652                         op = (format & JOF_SET) ? JSOP_GETPROP2 : JSOP_GETPROP;
01653                     } else if (mode == JOF_ELEM) {
01654                         op = (format & JOF_SET) ? JSOP_GETELEM2 : JSOP_GETELEM;
01655                     } else {
01656                         /*
01657                          * Zero mode means precisely that op is uncategorized
01658                          * for our purposes, so we must write per-op special
01659                          * case code here.
01660                          */
01661                         switch (op) {
01662                           case JSOP_ENUMELEM:
01663                           case JSOP_ENUMCONSTELEM:
01664                             op = JSOP_GETELEM;
01665                             break;
01666 #if JS_HAS_LVALUE_RETURN
01667                           case JSOP_SETCALL:
01668                             op = JSOP_CALL;
01669                             break;
01670 #endif
01671                           default:
01672                             LOCAL_ASSERT(0);
01673                         }
01674                     }
01675                 }
01676             }
01677 
01678             saveop = op;
01679             if (op >= JSOP_LIMIT) {
01680                 switch (op) {
01681                   case JSOP_GETPROP2:
01682                     saveop = JSOP_GETPROP;
01683                     break;
01684                   case JSOP_GETELEM2:
01685                     saveop = JSOP_GETELEM;
01686                     break;
01687                   default:;
01688                 }
01689             }
01690             LOCAL_ASSERT(js_CodeSpec[saveop].length == oplen);
01691 
01692             jp->dvgfence = NULL;
01693         }
01694 
01695         if (cs->token) {
01696             switch (cs->nuses) {
01697               case 2:
01698                 sn = js_GetSrcNote(jp->script, pc);
01699                 if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
01700                     /*
01701                      * Avoid over-parenthesizing y in x op= y based on its
01702                      * expansion: x = x op y (replace y by z = w to see the
01703                      * problem).
01704                      */
01705                     op = pc[oplen];
01706                     LOCAL_ASSERT(op != saveop);
01707                 }
01708                 rval = POP_STR();
01709                 lval = POP_STR();
01710                 if (op != saveop) {
01711                     /* Print only the right operand of the assignment-op. */
01712                     todo = SprintCString(&ss->sprinter, rval);
01713                     op = saveop;
01714                 } else if (!inXML) {
01715                     todo = Sprint(&ss->sprinter, "%s %s %s",
01716                                   lval, cs->token, rval);
01717                 } else {
01718                     /* In XML, just concatenate the two operands. */
01719                     LOCAL_ASSERT(op == JSOP_ADD);
01720                     todo = Sprint(&ss->sprinter, ss_format, lval, rval);
01721                 }
01722                 break;
01723 
01724               case 1:
01725                 rval = POP_STR();
01726                 todo = Sprint(&ss->sprinter, ss_format, cs->token, rval);
01727                 break;
01728 
01729               case 0:
01730                 todo = SprintCString(&ss->sprinter, cs->token);
01731                 break;
01732 
01733               default:
01734                 todo = -2;
01735                 break;
01736             }
01737         } else {
01738             switch (op) {
01739 #define BEGIN_LITOPX_CASE(OP)                                                 \
01740               case OP:                                                        \
01741                 atomIndex = GET_ATOM_INDEX(pc);                               \
01742               do_##OP:                                                        \
01743                 atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex);
01744 
01745 #define END_LITOPX_CASE                                                       \
01746                 break;
01747 
01748               case JSOP_NOP:
01749                 /*
01750                  * Check for a do-while loop, a for-loop with an empty
01751                  * initializer part, a labeled statement, a function
01752                  * definition, or try/finally.
01753                  */
01754                 sn = js_GetSrcNote(jp->script, pc);
01755                 todo = -2;
01756                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
01757                   case SRC_WHILE:
01758                     js_printf(SET_MAYBE_BRACE(jp), "\tdo {\n");
01759                     jp->indent += 4;
01760                     break;
01761 
01762                   case SRC_FOR:
01763                     rval = "";
01764 
01765                   do_forloop:
01766                     /* Skip the JSOP_NOP or JSOP_POP bytecode. */
01767                     pc++;
01768 
01769                     /* Get the cond, next, and loop-closing tail offsets. */
01770                     cond = js_GetSrcNoteOffset(sn, 0);
01771                     next = js_GetSrcNoteOffset(sn, 1);
01772                     tail = js_GetSrcNoteOffset(sn, 2);
01773                     LOCAL_ASSERT(tail + GetJumpOffset(pc+tail, pc+tail) == 0);
01774 
01775                     /* Print the keyword and the possibly empty init-part. */
01776                     js_printf(jp, "\tfor (%s;", rval);
01777 
01778                     if (pc[cond] == JSOP_IFEQ || pc[cond] == JSOP_IFEQX) {
01779                         /* Decompile the loop condition. */
01780                         DECOMPILE_CODE(pc, cond);
01781                         js_printf(jp, " %s", POP_STR());
01782                     }
01783 
01784                     /* Need a semicolon whether or not there was a cond. */
01785                     js_puts(jp, ";");
01786 
01787                     if (pc[next] != JSOP_GOTO && pc[next] != JSOP_GOTOX) {
01788                         /* Decompile the loop updater. */
01789                         DECOMPILE_CODE(pc + next, tail - next - 1);
01790                         js_printf(jp, " %s", POP_STR());
01791                     }
01792 
01793                     /* Do the loop body. */
01794                     js_printf(SET_MAYBE_BRACE(jp), ") {\n");
01795                     jp->indent += 4;
01796                     oplen = (cond) ? js_CodeSpec[pc[cond]].length : 0;
01797                     DECOMPILE_CODE(pc + cond + oplen, next - cond - oplen);
01798                     jp->indent -= 4;
01799                     js_printf(jp, "\t}\n");
01800 
01801                     /* Set len so pc skips over the entire loop. */
01802                     len = tail + js_CodeSpec[pc[tail]].length;
01803                     break;
01804 
01805                   case SRC_LABEL:
01806                     atom = js_GetAtom(cx, &jp->script->atomMap,
01807                                       (jsatomid) js_GetSrcNoteOffset(sn, 0));
01808                     jp->indent -= 4;
01809                     rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
01810                     if (!rval)
01811                         return NULL;
01812                     RETRACT(&ss->sprinter, rval);
01813                     js_printf(CLEAR_MAYBE_BRACE(jp), "\t%s:\n", rval);
01814                     jp->indent += 4;
01815                     break;
01816 
01817                   case SRC_LABELBRACE:
01818                     atom = js_GetAtom(cx, &jp->script->atomMap,
01819                                       (jsatomid) js_GetSrcNoteOffset(sn, 0));
01820                     rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
01821                     if (!rval)
01822                         return NULL;
01823                     RETRACT(&ss->sprinter, rval);
01824                     js_printf(CLEAR_MAYBE_BRACE(jp), "\t%s: {\n", rval);
01825                     jp->indent += 4;
01826                     break;
01827 
01828                   case SRC_ENDBRACE:
01829                     jp->indent -= 4;
01830                     js_printf(jp, "\t}\n");
01831                     break;
01832 
01833                   case SRC_FUNCDEF:
01834                     atom = js_GetAtom(cx, &jp->script->atomMap,
01835                                       (jsatomid) js_GetSrcNoteOffset(sn, 0));
01836                     LOCAL_ASSERT(ATOM_IS_OBJECT(atom));
01837                   do_function:
01838                     obj = ATOM_TO_OBJECT(atom);
01839                     fun = (JSFunction *) JS_GetPrivate(cx, obj);
01840                     jp2 = js_NewPrinter(cx, JS_GetFunctionName(fun),
01841                                         jp->indent, jp->pretty);
01842                     if (!jp2)
01843                         return NULL;
01844                     jp2->scope = jp->scope;
01845                     js_puts(jp2, "\n");
01846                     ok = js_DecompileFunction(jp2, fun);
01847                     if (ok) {
01848                         js_puts(jp2, "\n");
01849                         str = js_GetPrinterOutput(jp2);
01850                         if (str)
01851                             js_printf(jp, "%s\n", JS_GetStringBytes(str));
01852                         else
01853                             ok = JS_FALSE;
01854                     }
01855                     js_DestroyPrinter(jp2);
01856                     if (!ok)
01857                         return NULL;
01858 
01859                     break;
01860 
01861                   case SRC_BRACE:
01862                     js_printf(CLEAR_MAYBE_BRACE(jp), "\t{\n");
01863                     jp->indent += 4;
01864                     len = js_GetSrcNoteOffset(sn, 0);
01865                     DECOMPILE_CODE(pc + oplen, len - oplen);
01866                     jp->indent -= 4;
01867                     js_printf(jp, "\t}\n");
01868                     break;
01869 
01870                   default:;
01871                 }
01872                 break;
01873 
01874               case JSOP_GROUP:
01875                 cs = &js_CodeSpec[lastop];
01876                 if ((cs->prec != 0 &&
01877                      cs->prec == js_CodeSpec[pc[JSOP_GROUP_LENGTH]].prec) ||
01878                     pc[JSOP_GROUP_LENGTH] == JSOP_PUSHOBJ ||
01879                     pc[JSOP_GROUP_LENGTH] == JSOP_DUP) {
01880                     /*
01881                      * Force parens if this JSOP_GROUP forced re-association
01882                      * against precedence, or if this is a call or constructor
01883                      * expression, or if it is destructured (JSOP_DUP).
01884                      *
01885                      * This is necessary to handle the operator new grammar,
01886                      * by which new x(y).z means (new x(y))).z.  For example
01887                      * new (x(y).z) must decompile with the constructor
01888                      * parenthesized, but normal precedence has JSOP_GETPROP
01889                      * (for the final .z) higher than JSOP_NEW.  In general,
01890                      * if the call or constructor expression is parenthesized,
01891                      * we preserve parens.
01892                      */
01893                     op = JSOP_NAME;
01894                     rval = POP_STR();
01895                     todo = SprintCString(&ss->sprinter, rval);
01896                 } else {
01897                     /*
01898                      * Don't explicitly parenthesize -- just fix the top
01899                      * opcode so that the auto-parens magic in PopOff can do
01900                      * its thing.
01901                      */
01902                     LOCAL_ASSERT(ss->top != 0);
01903                     ss->opcodes[ss->top-1] = saveop = lastop;
01904                     todo = -2;
01905                 }
01906                 break;
01907 
01908               case JSOP_PUSH:
01909 #if JS_HAS_DESTRUCTURING
01910                 sn = js_GetSrcNote(jp->script, pc);
01911                 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
01912                     pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo);
01913                     if (!pc)
01914                         return NULL;
01915                     LOCAL_ASSERT(*pc == JSOP_SETSP);
01916                     len = oplen = JSOP_SETSP_LENGTH;
01917                     goto end_groupassignment;
01918                 }
01919 #endif
01920                 /* FALL THROUGH */
01921 
01922               case JSOP_PUSHOBJ:
01923               case JSOP_BINDNAME:
01924               do_JSOP_BINDNAME:
01925                 todo = Sprint(&ss->sprinter, "");
01926                 break;
01927 
01928               case JSOP_TRY:
01929                 js_printf(CLEAR_MAYBE_BRACE(jp), "\ttry {\n");
01930                 jp->indent += 4;
01931                 todo = -2;
01932                 break;
01933 
01934               case JSOP_FINALLY:
01935                 jp->indent -= 4;
01936                 js_printf(CLEAR_MAYBE_BRACE(jp), "\t} finally {\n");
01937                 jp->indent += 4;
01938 
01939                 /*
01940                  * We must push an empty string placeholder for gosub's return
01941                  * address, popped by JSOP_RETSUB and counted by script->depth
01942                  * but not by ss->top (see JSOP_SETSP, below).
01943                  */
01944                 todo = Sprint(&ss->sprinter, exception_cookie);
01945                 if (todo < 0 || !PushOff(ss, todo, op))
01946                     return NULL;
01947                 todo = Sprint(&ss->sprinter, retsub_pc_cookie);
01948                 break;
01949 
01950               case JSOP_RETSUB:
01951                 rval = POP_STR();
01952                 LOCAL_ASSERT(strcmp(rval, retsub_pc_cookie) == 0);
01953                 lval = POP_STR();
01954                 LOCAL_ASSERT(strcmp(lval, exception_cookie) == 0);
01955                 todo = -2;
01956                 break;
01957 
01958               case JSOP_SWAP:
01959                 /*
01960                  * We don't generate this opcode currently, and previously we
01961                  * did not need to decompile it.  If old, serialized bytecode
01962                  * uses it still, we should fall through and set todo = -2.
01963                  */
01964                 /* FALL THROUGH */
01965 
01966               case JSOP_GOSUB:
01967               case JSOP_GOSUBX:
01968                 /*
01969                  * JSOP_GOSUB and GOSUBX have no effect on the decompiler's
01970                  * string stack because the next op in bytecode order finds
01971                  * the stack balanced by a JSOP_RETSUB executed elsewhere.
01972                  */
01973                 todo = -2;
01974                 break;
01975 
01976               case JSOP_SETSP:
01977               {
01978                 uintN newtop, oldtop, i;
01979 
01980                 /*
01981                  * The compiler models operand stack depth and fixes the stack
01982                  * pointer on entry to a catch clause based on its depth model.
01983                  * The decompiler must match the code generator's model, which
01984                  * is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops.
01985                  */
01986                 newtop = (uintN) GET_UINT16(pc);
01987                 oldtop = ss->top;
01988                 LOCAL_ASSERT(newtop <= oldtop);
01989                 todo = -2;
01990 
01991 #if JS_HAS_DESTRUCTURING
01992                 sn = js_GetSrcNote(jp->script, pc);
01993                 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
01994                     todo = Sprint(&ss->sprinter, "%s[] = [",
01995                                   VarPrefix(sn));
01996                     if (todo < 0)
01997                         return NULL;
01998                     for (i = newtop; i < oldtop; i++) {
01999                         rval = OFF2STR(&ss->sprinter, ss->offsets[i]);
02000                         if (Sprint(&ss->sprinter, ss_format,
02001                                    (i == newtop) ? "" : ", ",
02002                                    (i == oldtop - 1 && *rval == '\0')
02003                                    ? ", " : rval) < 0) {
02004                             return NULL;
02005                         }
02006                     }
02007                     if (SprintPut(&ss->sprinter, "]", 1) < 0)
02008                         return NULL;
02009 
02010                     /*
02011                      * Kill newtop before the end_groupassignment: label by
02012                      * retracting/popping early.  Control will either jump to
02013                      * do_forloop: or do_letheadbody: or else break from our
02014                      * case JSOP_SETSP: after the switch (*pc2) below.
02015                      */
02016                     if (newtop < oldtop) {
02017                         ss->sprinter.offset = GetOff(ss, newtop);
02018                         ss->top = newtop;
02019                     }
02020 
02021                   end_groupassignment:
02022                     /*
02023                      * Thread directly to the next opcode if we can, to handle
02024                      * the special cases of a group assignment in the first or
02025                      * last part of a for(;;) loop head, or in a let block or
02026                      * expression head.
02027                      *
02028                      * NB: todo at this point indexes space in ss->sprinter
02029                      * that is liable to be overwritten.  The code below knows
02030                      * exactly how long rval lives, or else copies it down via
02031                      * SprintCString.
02032                      */
02033                     rval = OFF2STR(&ss->sprinter, todo);
02034                     todo = -2;
02035                     pc2 = pc + oplen;
02036                     switch (*pc2) {
02037                       case JSOP_NOP:
02038                         /* First part of for(;;) or let block/expr head. */
02039                         sn = js_GetSrcNote(jp->script, pc2);
02040                         if (sn) {
02041                             if (SN_TYPE(sn) == SRC_FOR) {
02042                                 pc = pc2;
02043                                 goto do_forloop;
02044                             }
02045                             if (SN_TYPE(sn) == SRC_DECL) {
02046                                 if (ss->top == jp->script->depth) {
02047                                     /*
02048                                      * This must be an empty destructuring
02049                                      * in the head of a let whose body block
02050                                      * is also empty.
02051                                      */
02052                                     pc = pc2 + 1;
02053                                     len = js_GetSrcNoteOffset(sn, 0);
02054                                     LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCK);
02055                                     js_printf(jp, "\tlet (%s) {\n", rval);
02056                                     js_printf(jp, "\t}\n");
02057                                     goto end_setsp;
02058                                 }
02059                                 todo = SprintCString(&ss->sprinter, rval);
02060                                 if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
02061                                     return NULL;
02062                                 op = JSOP_POP;
02063                                 pc = pc2 + 1;
02064                                 goto do_letheadbody;
02065                             }
02066                         }
02067                         break;
02068 
02069                       case JSOP_GOTO:
02070                       case JSOP_GOTOX:
02071                         /* Third part of for(;;) loop head. */
02072                         cond = GetJumpOffset(pc2, pc2);
02073                         sn = js_GetSrcNote(jp->script, pc2 + cond - 1);
02074                         if (sn && SN_TYPE(sn) == SRC_FOR) {
02075                             todo = SprintCString(&ss->sprinter, rval);
02076                             saveop = JSOP_NOP;
02077                         }
02078                         break;
02079                     }
02080 
02081                     /*
02082                      * If control flow reaches this point with todo still -2,
02083                      * just print rval as an expression statement.
02084                      */
02085                     if (todo == -2)
02086                         js_printf(jp, "\t%s;\n", rval);
02087                   end_setsp:
02088                     break;
02089                 }
02090 #endif
02091                 if (newtop < oldtop) {
02092                     ss->sprinter.offset = GetOff(ss, newtop);
02093                     ss->top = newtop;
02094                 }
02095                 break;
02096               }
02097 
02098               case JSOP_EXCEPTION:
02099                 /* The catch decompiler handles this op itself. */
02100                 LOCAL_ASSERT(JS_FALSE);
02101                 break;
02102 
02103               case JSOP_POP:
02104                 /*
02105                  * By default, do not automatically parenthesize when popping
02106                  * a stacked expression decompilation.  We auto-parenthesize
02107                  * only when JSOP_POP is annotated with SRC_PCDELTA, meaning
02108                  * comma operator.
02109                  */
02110                 op = JSOP_POPV;
02111                 /* FALL THROUGH */
02112 
02113               case JSOP_POPV:
02114                 sn = js_GetSrcNote(jp->script, pc);
02115                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
02116                   case SRC_FOR:
02117                     /* Force parens around 'in' expression at 'for' front. */
02118                     if (ss->opcodes[ss->top-1] == JSOP_IN)
02119                         op = JSOP_LSH;
02120                     rval = POP_STR();
02121                     todo = -2;
02122                     goto do_forloop;
02123 
02124                   case SRC_PCDELTA:
02125                     /* Comma operator: use JSOP_POP for correct precedence. */
02126                     op = JSOP_POP;
02127 
02128                     /* Pop and save to avoid blowing stack depth budget. */
02129                     lval = JS_strdup(cx, POP_STR());
02130                     if (!lval)
02131                         return NULL;
02132 
02133                     /*
02134                      * The offset tells distance to the end of the right-hand
02135                      * operand of the comma operator.
02136                      */
02137                     done = pc + len;
02138                     pc += js_GetSrcNoteOffset(sn, 0);
02139                     len = 0;
02140 
02141                     if (!Decompile(ss, done, pc - done)) {
02142                         JS_free(cx, (char *)lval);
02143                         return NULL;
02144                     }
02145 
02146                     /* Pop Decompile result and print comma expression. */
02147                     rval = POP_STR();
02148                     todo = Sprint(&ss->sprinter, "%s, %s", lval, rval);
02149                     JS_free(cx, (char *)lval);
02150                     break;
02151 
02152                   case SRC_HIDDEN:
02153                     /* Hide this pop, it's from a goto in a with or for/in. */
02154                     todo = -2;
02155                     break;
02156 
02157                   case SRC_DECL:
02158                     /* This pop is at the end of the let block/expr head. */
02159                     pc += JSOP_POP_LENGTH;
02160 #if JS_HAS_DESTRUCTURING
02161                   do_letheadbody:
02162 #endif
02163                     len = js_GetSrcNoteOffset(sn, 0);
02164                     if (pc[len] == JSOP_LEAVEBLOCK) {
02165                         js_printf(CLEAR_MAYBE_BRACE(jp), "\tlet (%s) {\n",
02166                                   POP_STR());
02167                         jp->indent += 4;
02168                         DECOMPILE_CODE(pc, len);
02169                         jp->indent -= 4;
02170                         js_printf(jp, "\t}\n");
02171                         todo = -2;
02172                     } else {
02173                         LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCKEXPR);
02174 
02175                         lval = JS_strdup(cx, POP_STR());
02176                         if (!lval)
02177                             return NULL;
02178 
02179                         if (!Decompile(ss, pc, len)) {
02180                             JS_free(cx, (char *)lval);
02181                             return NULL;
02182                         }
02183                         rval = POP_STR();
02184                         todo = Sprint(&ss->sprinter,
02185                                       (*rval == '{')
02186                                       ? "let (%s) (%s)"
02187                                       : "let (%s) %s",
02188                                       lval, rval);
02189                         JS_free(cx, (char *)lval);
02190                     }
02191                     break;
02192 
02193                   default:
02194                     /* Turn off parens around a yield statement. */
02195                     if (ss->opcodes[ss->top-1] == JSOP_YIELD)
02196                         op = JSOP_NOP;
02197 
02198                     rval = POP_STR();
02199                     if (*rval != '\0') {
02200 #if JS_HAS_BLOCK_SCOPE
02201                         /*
02202                          * If a let declaration is the only child of a control
02203                          * structure that does not require braces, it must not
02204                          * be braced.  If it were braced explicitly, it would
02205                          * be bracketed by JSOP_ENTERBLOCK/JSOP_LEAVEBLOCK.
02206                          */
02207                         if (jp->braceState == MAYBE_BRACE &&
02208                             pc + JSOP_POP_LENGTH == endpc &&
02209                             !strncmp(rval, var_prefix[SRC_DECL_LET], 4) &&
02210                             rval[4] != '(') {
02211                             SetDontBrace(jp);
02212                         }
02213 #endif
02214                         js_printf(jp,
02215                                   (*rval == '{' ||
02216                                    (strncmp(rval, js_function_str, 8) == 0 &&
02217                                     rval[8] == ' '))
02218                                   ? "\t(%s);\n"
02219                                   : "\t%s;\n",
02220                                   rval);
02221                     }
02222                     todo = -2;
02223                     break;
02224                 }
02225                 break;
02226 
02227               case JSOP_POP2:
02228               case JSOP_ENDITER:
02229                 sn = js_GetSrcNote(jp->script, pc);
02230                 todo = -2;
02231                 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
02232                     break;
02233                 (void) PopOff(ss, op);
02234                 if (op == JSOP_POP2)
02235                     (void) PopOff(ss, op);
02236                 break;
02237 
02238               case JSOP_ENTERWITH:
02239                 LOCAL_ASSERT(!js_GetSrcNote(jp->script, pc));
02240                 rval = POP_STR();
02241                 js_printf(SET_MAYBE_BRACE(jp), "\twith (%s) {\n", rval);
02242                 jp->indent += 4;
02243                 todo = Sprint(&ss->sprinter, with_cookie);
02244                 break;
02245 
02246               case JSOP_LEAVEWITH:
02247                 sn = js_GetSrcNote(jp->script, pc);
02248                 todo = -2;
02249                 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
02250                     break;
02251                 rval = POP_STR();
02252                 LOCAL_ASSERT(strcmp(rval, with_cookie) == 0);
02253                 jp->indent -= 4;
02254                 js_printf(jp, "\t}\n");
02255                 break;
02256 
02257               BEGIN_LITOPX_CASE(JSOP_ENTERBLOCK)
02258               {
02259                 JSAtom **atomv, *smallv[5];
02260                 JSScopeProperty *sprop;
02261 
02262                 obj = ATOM_TO_OBJECT(atom);
02263                 argc = OBJ_BLOCK_COUNT(cx, obj);
02264                 if ((size_t)argc <= sizeof smallv / sizeof smallv[0]) {
02265                     atomv = smallv;
02266                 } else {
02267                     atomv = (JSAtom **) JS_malloc(cx, argc * sizeof(JSAtom *));
02268                     if (!atomv)
02269                         return NULL;
02270                 }
02271 
02272                 /* From here on, control must flow through enterblock_out. */
02273                 for (sprop = OBJ_SCOPE(obj)->lastProp; sprop;
02274                      sprop = sprop->parent) {
02275                     if (!(sprop->flags & SPROP_HAS_SHORTID))
02276                         continue;
02277                     LOCAL_ASSERT(sprop->shortid < argc);
02278                     atomv[sprop->shortid] = JSID_TO_ATOM(sprop->id);
02279                 }
02280                 ok = JS_TRUE;
02281                 for (i = 0; i < argc; i++) {
02282                     atom = atomv[i];
02283                     rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
02284                     if (!rval ||
02285                         !PushOff(ss, STR2OFF(&ss->sprinter, rval), op)) {
02286                         ok = JS_FALSE;
02287                         goto enterblock_out;
02288                     }
02289                 }
02290 
02291                 sn = js_GetSrcNote(jp->script, pc);
02292                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
02293 #if JS_HAS_BLOCK_SCOPE
02294                   case SRC_BRACE:
02295                     js_printf(CLEAR_MAYBE_BRACE(jp), "\t{\n");
02296                     jp->indent += 4;
02297                     len = js_GetSrcNoteOffset(sn, 0);
02298                     ok = Decompile(ss, pc + oplen, len - oplen) != NULL;
02299                     if (!ok)
02300                         goto enterblock_out;
02301                     jp->indent -= 4;
02302                     js_printf(jp, "\t}\n");
02303                     break;
02304 #endif
02305 
02306                   case SRC_CATCH:
02307                     jp->indent -= 4;
02308                     js_printf(CLEAR_MAYBE_BRACE(jp), "\t} catch (");
02309 
02310                     pc2 = pc;
02311                     pc += oplen;
02312                     LOCAL_ASSERT(*pc == JSOP_EXCEPTION);
02313                     pc += JSOP_EXCEPTION_LENGTH;
02314                     if (*pc == JSOP_DUP) {
02315                         sn2 = js_GetSrcNote(jp->script, pc);
02316                         if (sn2 && SN_TYPE(sn2) == SRC_HIDDEN) {
02317                             /*
02318                              * This is a hidden dup to save the exception for
02319                              * later. It must exist only when the catch has
02320                              * an exception guard.
02321                              */
02322                             LOCAL_ASSERT(js_GetSrcNoteOffset(sn, 0) != 0);
02323                             pc += JSOP_DUP_LENGTH;
02324                         }
02325                     }
02326 #if JS_HAS_DESTRUCTURING
02327                     if (*pc == JSOP_DUP) {
02328                         pc = DecompileDestructuring(ss, pc, endpc);
02329                         if (!pc) {
02330                             ok = JS_FALSE;
02331                             goto enterblock_out;
02332                         }
02333                         LOCAL_ASSERT(*pc == JSOP_POP);
02334                         pc += JSOP_POP_LENGTH;
02335                         lval = PopStr(ss, JSOP_NOP);
02336                         js_puts(jp, lval);
02337                     } else {
02338 #endif
02339                         LOCAL_ASSERT(*pc == JSOP_SETLOCALPOP);
02340                         i = GET_UINT16(pc);
02341                         pc += JSOP_SETLOCALPOP_LENGTH;
02342                         atom = atomv[i - OBJ_BLOCK_DEPTH(cx, obj)];
02343                         str = ATOM_TO_STRING(atom);
02344                         if (!QuoteString(&jp->sprinter, str, 0)) {
02345                             ok = JS_FALSE;
02346                             goto enterblock_out;
02347                         }
02348 #if JS_HAS_DESTRUCTURING
02349                     }
02350 #endif
02351 
02352                     len = js_GetSrcNoteOffset(sn, 0);
02353                     if (len) {
02354                         len -= PTRDIFF(pc, pc2, jsbytecode);
02355                         LOCAL_ASSERT(len > 0);
02356                         js_printf(jp, " if ");
02357                         ok = Decompile(ss, pc, len) != NULL;
02358                         if (!ok)
02359                             goto enterblock_out;
02360                         js_printf(jp, "%s", POP_STR());
02361                         pc += len;
02362                         LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
02363                         pc += js_CodeSpec[*pc].length;
02364                     }
02365 
02366                     js_printf(jp, ") {\n");
02367                     jp->indent += 4;
02368                     len = 0;
02369                     break;
02370                 }
02371 
02372                 todo = -2;
02373 
02374               enterblock_out:
02375                 if (atomv != smallv)
02376                     JS_free(cx, atomv);
02377                 if (!ok)
02378                     return NULL;
02379               }
02380               END_LITOPX_CASE
02381 
02382               case JSOP_LEAVEBLOCK:
02383               case JSOP_LEAVEBLOCKEXPR:
02384               {
02385                 uintN top, depth;
02386 
02387                 sn = js_GetSrcNote(jp->script, pc);
02388                 todo = -2;
02389                 if (op == JSOP_LEAVEBLOCKEXPR) {
02390                     LOCAL_ASSERT(SN_TYPE(sn) == SRC_PCBASE);
02391                     rval = POP_STR();
02392                 } else if (sn) {
02393                     LOCAL_ASSERT(op == JSOP_LEAVEBLOCK);
02394                     if (SN_TYPE(sn) == SRC_HIDDEN)
02395                         break;
02396                     LOCAL_ASSERT(SN_TYPE(sn) == SRC_CATCH);
02397                     LOCAL_ASSERT((uintN)js_GetSrcNoteOffset(sn, 0) == ss->top);
02398                 }
02399                 top = ss->top;
02400                 depth = GET_UINT16(pc);
02401                 LOCAL_ASSERT(top >= depth);
02402                 top -= depth;
02403                 ss->top = top;
02404                 ss->sprinter.offset = GetOff(ss, top);
02405                 if (op == JSOP_LEAVEBLOCKEXPR)
02406                     todo = SprintCString(&ss->sprinter, rval);
02407                 break;
02408               }
02409 
02410               case JSOP_GETLOCAL:
02411                 i = GET_UINT16(pc);
02412                 sn = js_GetSrcNote(jp->script, pc);
02413                 LOCAL_ASSERT((uintN)i < ss->top);
02414                 rval = GetLocal(ss, i);
02415 
02416 #if JS_HAS_DESTRUCTURING
02417                 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
02418                     pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo);
02419                     if (!pc)
02420                         return NULL;
02421                     LOCAL_ASSERT(*pc == JSOP_SETSP);
02422                     len = oplen = JSOP_SETSP_LENGTH;
02423                     goto end_groupassignment;
02424                 }
02425 #endif
02426 
02427                 todo = Sprint(&ss->sprinter, ss_format, VarPrefix(sn), rval);
02428                 break;
02429 
02430               case JSOP_SETLOCAL:
02431               case JSOP_SETLOCALPOP:
02432                 i = GET_UINT16(pc);
02433                 lval = GetStr(ss, i);
02434                 rval = POP_STR();
02435                 goto do_setlval;
02436 
02437               case JSOP_INCLOCAL:
02438               case JSOP_DECLOCAL:
02439                 i = GET_UINT16(pc);
02440                 lval = GetLocal(ss, i);
02441                 goto do_inclval;
02442 
02443               case JSOP_LOCALINC:
02444               case JSOP_LOCALDEC:
02445                 i = GET_UINT16(pc);
02446                 lval = GetLocal(ss, i);
02447                 goto do_lvalinc;
02448 
02449               case JSOP_FORLOCAL:
02450                 i = GET_UINT16(pc);
02451                 lval = GetStr(ss, i);
02452                 atom = NULL;
02453                 goto do_forlvalinloop;
02454 
02455               case JSOP_RETRVAL:
02456                 todo = -2;
02457                 break;
02458 
02459               case JSOP_SETRVAL:
02460               case JSOP_RETURN:
02461                 rval = POP_STR();
02462                 if (*rval != '\0')
02463                     js_printf(jp, "\t%s %s;\n", js_return_str, rval);
02464                 else
02465                     js_printf(jp, "\t%s;\n", js_return_str);
02466                 todo = -2;
02467                 break;
02468 
02469 #if JS_HAS_GENERATORS
02470               case JSOP_YIELD:
02471                 op = JSOP_SETNAME;      /* turn off most parens */
02472                 rval = POP_STR();
02473                 todo = (*rval != '\0')
02474                        ? Sprint(&ss->sprinter,
02475                                 (strncmp(rval, js_yield_str, 5) == 0 &&
02476                                  (rval[5] == ' ' || rval[5] == '\0'))
02477                                 ? "%s (%s)"
02478                                 : "%s %s",
02479                                 js_yield_str, rval)
02480                        : SprintCString(&ss->sprinter, js_yield_str);
02481                 break;
02482 
02483               case JSOP_ARRAYPUSH:
02484               {
02485                 uintN pos, blockpos, startpos;
02486                 ptrdiff_t start;
02487 
02488                 rval = POP_STR();
02489                 pos = ss->top;
02490                 while ((op = ss->opcodes[--pos]) != JSOP_ENTERBLOCK &&
02491                        op != JSOP_NEWINIT) {
02492                     LOCAL_ASSERT(pos != 0);
02493                 }
02494                 blockpos = pos;
02495                 while (ss->opcodes[pos] == JSOP_ENTERBLOCK) {
02496                     if (pos == 0)
02497                         break;
02498                     --pos;
02499                 }
02500                 LOCAL_ASSERT(ss->opcodes[pos] == JSOP_NEWINIT);
02501                 startpos = pos;
02502                 start = ss->offsets[pos];
02503                 LOCAL_ASSERT(ss->sprinter.base[start] == '[' ||
02504                              ss->sprinter.base[start] == '#');
02505                 pos = blockpos + 1;
02506                 LOCAL_ASSERT(pos < ss->top);
02507                 xval = OFF2STR(&ss->sprinter, ss->offsets[pos]);
02508                 lval = OFF2STR(&ss->sprinter, start);
02509                 RETRACT(&ss->sprinter, lval);
02510                 todo = Sprint(&ss->sprinter, "%s%s%.*s",
02511                               lval, rval, rval - xval, xval);
02512                 if (todo < 0)
02513                     return NULL;
02514                 ss->offsets[startpos] = todo;
02515                 todo = -2;
02516                 break;
02517               }
02518 #endif
02519 
02520               case JSOP_THROWING:
02521                 todo = -2;
02522                 break;
02523 
02524               case JSOP_THROW:
02525                 sn = js_GetSrcNote(jp->script, pc);
02526                 todo = -2;
02527                 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
02528                     break;
02529                 rval = POP_STR();
02530                 js_printf(jp, "\t%s %s;\n", cs->name, rval);
02531                 break;
02532 
02533               case JSOP_GOTO:
02534               case JSOP_GOTOX:
02535                 sn = js_GetSrcNote(jp->script, pc);
02536                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
02537                   case SRC_CONT2LABEL:
02538                     atom = js_GetAtom(cx, &jp->script->atomMap,
02539                                       (jsatomid) js_GetSrcNoteOffset(sn, 0));
02540                     rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
02541                     if (!rval)
02542                         return NULL;
02543                     RETRACT(&ss->sprinter, rval);
02544                     js_printf(jp, "\tcontinue %s;\n", rval);
02545                     break;
02546                   case SRC_CONTINUE:
02547                     js_printf(jp, "\tcontinue;\n");
02548                     break;
02549                   case SRC_BREAK2LABEL:
02550                     atom = js_GetAtom(cx, &jp->script->atomMap,
02551                                       (jsatomid) js_GetSrcNoteOffset(sn, 0));
02552                     rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
02553                     if (!rval)
02554                         return NULL;
02555                     RETRACT(&ss->sprinter, rval);
02556                     js_printf(jp, "\tbreak %s;\n", rval);
02557                     break;
02558                   case SRC_HIDDEN:
02559                     break;
02560                   default:
02561                     js_printf(jp, "\tbreak;\n");
02562                     break;
02563                 }
02564                 todo = -2;
02565                 break;
02566 
02567               case JSOP_IFEQ:
02568               case JSOP_IFEQX:
02569               {
02570                 JSBool elseif = JS_FALSE;
02571 
02572               if_again:
02573                 len = GetJumpOffset(pc, pc);
02574                 sn = js_GetSrcNote(jp->script, pc);
02575 
02576                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
02577                   case SRC_IF:
02578                   case SRC_IF_ELSE:
02579                     op = JSOP_NOP;              /* turn off parens */
02580                     rval = POP_STR();
02581                     if (ss->inArrayInit) {
02582                         LOCAL_ASSERT(SN_TYPE(sn) == SRC_IF);
02583                         if (Sprint(&ss->sprinter, " if (%s)", rval) < 0)
02584                             return NULL;
02585                     } else {
02586                         js_printf(SET_MAYBE_BRACE(jp),
02587                                   elseif ? " if (%s) {\n" : "\tif (%s) {\n",
02588                                   rval);
02589                         jp->indent += 4;
02590                     }
02591 
02592                     if (SN_TYPE(sn) == SRC_IF) {
02593                         DECOMPILE_CODE(pc + oplen, len - oplen);
02594                     } else {
02595                         LOCAL_ASSERT(!ss->inArrayInit);
02596                         tail = js_GetSrcNoteOffset(sn, 0);
02597                         DECOMPILE_CODE(pc + oplen, tail - oplen);
02598                         jp->indent -= 4;
02599                         pc += tail;
02600                         LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX);
02601                         oplen = js_CodeSpec[*pc].length;
02602                         len = GetJumpOffset(pc, pc);
02603                         js_printf(jp, "\t} else");
02604 
02605                         /*
02606                          * If the second offset for sn is non-zero, it tells
02607                          * the distance from the goto around the else, to the
02608                          * ifeq for the if inside the else that forms an "if
02609                          * else if" chain.  Thus cond spans the condition of
02610                          * the second if, so we simply decompile it and start
02611                          * over at label if_again.
02612                          */
02613                         cond = js_GetSrcNoteOffset(sn, 1);
02614                         if (cond != 0) {
02615                             DECOMPILE_CODE(pc + oplen, cond - oplen);
02616                             pc += cond;
02617                             elseif = JS_TRUE;
02618                             goto if_again;
02619                         }
02620 
02621                         js_printf(SET_MAYBE_BRACE(jp), " {\n");
02622                         jp->indent += 4;
02623                         DECOMPILE_CODE(pc + oplen, len - oplen);
02624                     }
02625 
02626                     if (!ss->inArrayInit) {
02627                         jp->indent -= 4;
02628                         js_printf(jp, "\t}\n");
02629                     }
02630                     todo = -2;
02631                     break;
02632 
02633                   case SRC_WHILE:
02634                     rval = POP_STR();
02635                     js_printf(SET_MAYBE_BRACE(jp), "\twhile (%s) {\n", rval);
02636                     jp->indent += 4;
02637                     tail = js_GetSrcNoteOffset(sn, 0);
02638                     DECOMPILE_CODE(pc + oplen, tail - oplen);
02639                     jp->indent -= 4;
02640                     js_printf(jp, "\t}\n");
02641                     todo = -2;
02642                     break;
02643 
02644                   case SRC_COND:
02645                     xval = JS_strdup(cx, POP_STR());
02646                     if (!xval)
02647                         return NULL;
02648                     len = js_GetSrcNoteOffset(sn, 0);
02649                     DECOMPILE_CODE(pc + oplen, len - oplen);
02650                     lval = JS_strdup(cx, POP_STR());
02651                     if (!lval) {
02652                         JS_free(cx, (void *)xval);
02653                         return NULL;
02654                     }
02655                     pc += len;
02656                     LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX);
02657                     oplen = js_CodeSpec[*pc].length;
02658                     len = GetJumpOffset(pc, pc);
02659                     DECOMPILE_CODE(pc + oplen, len - oplen);
02660                     rval = POP_STR();
02661                     todo = Sprint(&ss->sprinter, "%s ? %s : %s",
02662                                   xval, lval, rval);
02663                     JS_free(cx, (void *)xval);
02664                     JS_free(cx, (void *)lval);
02665                     break;
02666 
02667                   default:
02668                     break;
02669                 }
02670                 break;
02671               }
02672 
02673               case JSOP_IFNE:
02674               case JSOP_IFNEX:
02675                 /* Currently, this must be a do-while loop's upward branch. */
02676                 jp->indent -= 4;
02677                 js_printf(jp, "\t} while (%s);\n", POP_STR());
02678                 todo = -2;
02679                 break;
02680 
02681               case JSOP_OR:
02682               case JSOP_ORX:
02683                 xval = "||";
02684 
02685               do_logical_connective:
02686                 /* Top of stack is the first clause in a disjunction (||). */
02687                 lval = JS_strdup(cx, POP_STR());
02688                 if (!lval)
02689                     return NULL;
02690                 done = pc + GetJumpOffset(pc, pc);
02691                 pc += len;
02692                 len = PTRDIFF(done, pc, jsbytecode);
02693                 DECOMPILE_CODE(pc, len);
02694                 rval = POP_STR();
02695                 if (jp->pretty &&
02696                     jp->indent + 4 + strlen(lval) + 4 + strlen(rval) > 75) {
02697                     rval = JS_strdup(cx, rval);
02698                     if (!rval) {
02699                         tail = -1;
02700                     } else {
02701                         todo = Sprint(&ss->sprinter, "%s %s\n", lval, xval);
02702                         tail = Sprint(&ss->sprinter, "%*s%s",
02703                                       jp->indent + 4, "", rval);
02704                         JS_free(cx, (char *)rval);
02705                     }
02706                     if (tail < 0)
02707                         todo = -1;
02708                 } else {
02709                     todo = Sprint(&ss->sprinter, "%s %s %s", lval, xval, rval);
02710                 }
02711                 JS_free(cx, (char *)lval);
02712                 break;
02713 
02714               case JSOP_AND:
02715               case JSOP_ANDX:
02716                 xval = "&&";
02717                 goto do_logical_connective;
02718 
02719               case JSOP_FORARG:
02720                 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
02721                 LOCAL_ASSERT(atom);
02722                 goto do_fornameinloop;
02723 
02724               case JSOP_FORCONST:
02725                 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
02726                 LOCAL_ASSERT(atom);
02727                 goto do_fornameinloop;
02728 
02729               case JSOP_FORNAME:
02730                 atom = GET_ATOM(cx, jp->script, pc);
02731 
02732               do_fornameinloop:
02733                 lval = "";
02734               do_forlvalinloop:
02735                 sn = js_GetSrcNote(jp->script, pc);
02736                 xval = NULL;
02737                 goto do_forinloop;
02738 
02739               case JSOP_FORPROP:
02740                 xval = NULL;
02741                 atom = GET_ATOM(cx, jp->script, pc);
02742                 if (!ATOM_IS_IDENTIFIER(atom)) {
02743                     xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
02744                                        (jschar)'\'');
02745                     if (!xval)
02746                         return NULL;
02747                     atom = NULL;
02748                 }
02749                 lval = POP_STR();
02750                 sn = NULL;
02751 
02752               do_forinloop:
02753                 pc += oplen;
02754                 LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
02755                 oplen = js_CodeSpec[*pc].length;
02756                 len = GetJumpOffset(pc, pc);
02757                 sn2 = js_GetSrcNote(jp->script, pc);
02758                 tail = js_GetSrcNoteOffset(sn2, 0);
02759 
02760               do_forinhead:
02761                 if (!atom && xval) {
02762                     /*
02763                      * If xval is not a dummy empty string, we have to strdup
02764                      * it to save it from being clobbered by the first Sprint
02765                      * below.  Standard dumb decompiler operating procedure!
02766                      */
02767                     if (*xval == '\0') {
02768                         xval = NULL;
02769                     } else {
02770                         xval = JS_strdup(cx, xval);
02771                         if (!xval)
02772                             return NULL;
02773                     }
02774                 }
02775 
02776 #if JS_HAS_XML_SUPPORT
02777                 if (foreach) {
02778                     foreach = JS_FALSE;
02779                     todo = Sprint(&ss->sprinter, "for %s (%s%s",
02780                                   js_each_str, VarPrefix(sn), lval);
02781                 } else
02782 #endif
02783                 {
02784                     todo = Sprint(&ss->sprinter, "for (%s%s",
02785                                   VarPrefix(sn), lval);
02786                 }
02787                 if (atom) {
02788                     if (*lval && SprintPut(&ss->sprinter, ".", 1) < 0)
02789                         return NULL;
02790                     xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
02791                     if (!xval)
02792                         return NULL;
02793                 } else if (xval) {
02794                     LOCAL_ASSERT(*xval != '\0');
02795                     ok = (Sprint(&ss->sprinter,
02796                                  (js_CodeSpec[lastop].format & JOF_XMLNAME)
02797                                  ? ".%s"
02798                                  : "[%s]",
02799                                  xval)
02800                           >= 0);
02801                     JS_free(cx, (char *)xval);
02802                     if (!ok)
02803                         return NULL;
02804                 }
02805                 if (todo < 0)
02806                     return NULL;
02807 
02808                 lval = OFF2STR(&ss->sprinter, todo);
02809                 rval = GetStr(ss, ss->top-1);
02810                 RETRACT(&ss->sprinter, rval);
02811                 if (ss->inArrayInit) {
02812                     todo = Sprint(&ss->sprinter, " %s in %s)", lval, rval);
02813                     if (todo < 0)
02814                         return NULL;
02815                     ss->offsets[ss->top-1] = todo;
02816                     ss->sprinter.offset += PAREN_SLOP;
02817                     DECOMPILE_CODE(pc + oplen, tail - oplen);
02818                 } else {
02819                     js_printf(SET_MAYBE_BRACE(jp), "\t%s in %s) {\n",
02820                               lval, rval);
02821                     jp->indent += 4;
02822                     DECOMPILE_CODE(pc + oplen, tail - oplen);
02823                     jp->indent -= 4;
02824                     js_printf(jp, "\t}\n");
02825                 }
02826                 todo = -2;
02827                 break;
02828 
02829               case JSOP_FORELEM:
02830                 pc++;
02831                 LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
02832                 len = js_CodeSpec[*pc].length;
02833 
02834                 /*
02835                  * Arrange for the JSOP_ENUMELEM case to set tail for use by
02836                  * do_forinhead: code that uses on it to find the loop-closing
02837                  * jump (whatever its format, normal or extended), in order to
02838                  * bound the recursively decompiled loop body.
02839                  */
02840                 sn = js_GetSrcNote(jp->script, pc);
02841                 LOCAL_ASSERT(!forelem_tail);
02842                 forelem_tail = pc + js_GetSrcNoteOffset(sn, 0);
02843 
02844                 /*
02845                  * This gets a little wacky.  Only the length of the for loop
02846                  * body PLUS the element-indexing expression is known here, so
02847                  * we pass the after-loop pc to the JSOP_ENUMELEM case, which
02848                  * is immediately below, to decompile that helper bytecode via
02849                  * the 'forelem_done' local.
02850                  *
02851                  * Since a for..in loop can't nest in the head of another for
02852                  * loop, we can use forelem_{tail,done} singletons to remember
02853                  * state from JSOP_FORELEM to JSOP_ENUMELEM, thence (via goto)
02854                  * to label do_forinhead.
02855                  */
02856                 LOCAL_ASSERT(!forelem_done);
02857                 forelem_done = pc + GetJumpOffset(pc, pc);
02858 
02859                 /* Our net stack balance after forelem;ifeq is +1. */
02860                 todo = SprintCString(&ss->sprinter, forelem_cookie);
02861                 break;
02862 
02863               case JSOP_ENUMELEM:
02864               case JSOP_ENUMCONSTELEM:
02865                 /*
02866                  * The stack has the object under the (top) index expression.
02867                  * The "rval" property id is underneath those two on the stack.
02868                  * The for loop body net and gross lengths can now be adjusted
02869                  * to account for the length of the indexing expression that
02870                  * came after JSOP_FORELEM and before JSOP_ENUMELEM.
02871                  */
02872                 atom = NULL;
02873                 xval = POP_STR();
02874                 op = JSOP_GETELEM;      /* lval must have high precedence */
02875                 lval = POP_STR();
02876                 op = saveop;
02877                 rval = POP_STR();
02878                 LOCAL_ASSERT(strcmp(rval, forelem_cookie) == 0);
02879                 LOCAL_ASSERT(forelem_tail > pc);
02880                 tail = forelem_tail - pc;
02881                 forelem_tail = NULL;
02882                 LOCAL_ASSERT(forelem_done > pc);
02883                 len = forelem_done - pc;
02884                 forelem_done = NULL;
02885                 goto do_forinhead;
02886 
02887 #if JS_HAS_GETTER_SETTER
02888               case JSOP_GETTER:
02889               case JSOP_SETTER:
02890                 todo = -2;
02891                 break;
02892 #endif
02893 
02894               case JSOP_DUP2:
02895                 rval = GetStr(ss, ss->top-2);
02896                 todo = SprintCString(&ss->sprinter, rval);
02897                 if (todo < 0 || !PushOff(ss, todo, ss->opcodes[ss->top-2]))
02898                     return NULL;
02899                 /* FALL THROUGH */
02900 
02901               case JSOP_DUP:
02902 #if JS_HAS_DESTRUCTURING
02903                 sn = js_GetSrcNote(jp->script, pc);
02904                 if (sn) {
02905                     LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT);
02906                     pc = DecompileDestructuring(ss, pc, endpc);
02907                     if (!pc)
02908                         return NULL;
02909                     len = 0;
02910                     lval = POP_STR();
02911                     op = saveop = JSOP_ENUMELEM;
02912                     rval = POP_STR();
02913 
02914                     if (strcmp(rval, forelem_cookie) == 0) {
02915                         LOCAL_ASSERT(forelem_tail > pc);
02916                         tail = forelem_tail - pc;
02917                         forelem_tail = NULL;
02918                         LOCAL_ASSERT(forelem_done > pc);
02919                         len = forelem_done - pc;
02920                         forelem_done = NULL;
02921                         xval = NULL;
02922                         atom = NULL;
02923 
02924                         /*
02925                          * Null sn if this is a 'for (var [k, v] = i in o)'
02926                          * loop, because 'var [k, v = i;' has already been
02927                          * hoisted.
02928                          */
02929                         if (js_GetSrcNoteOffset(sn, 0) == SRC_DECL_VAR)
02930                             sn = NULL;
02931                         goto do_forinhead;
02932                     }
02933 
02934                     todo = Sprint(&ss->sprinter, "%s%s = %s",
02935                                   VarPrefix(sn), lval, rval);
02936                     break;
02937                 }
02938 #endif
02939 
02940                 rval = GetStr(ss, ss->top-1);
02941                 saveop = ss->opcodes[ss->top-1];
02942                 todo = SprintCString(&ss->sprinter, rval);
02943                 break;
02944 
02945               case JSOP_SETARG:
02946                 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
02947                 LOCAL_ASSERT(atom);
02948                 goto do_setname;
02949 
02950               case JSOP_SETVAR:
02951                 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
02952                 LOCAL_ASSERT(atom);
02953                 goto do_setname;
02954 
02955               case JSOP_SETCONST:
02956               case JSOP_SETNAME:
02957               case JSOP_SETGVAR:
02958                 atomIndex = GET_ATOM_INDEX(pc);
02959 
02960               do_JSOP_SETCONST:
02961                 atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex);
02962 
02963               do_setname:
02964                 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
02965                 if (!lval)
02966                     return NULL;
02967                 rval = POP_STR();
02968                 if (op == JSOP_SETNAME)
02969                     (void) PopOff(ss, op);
02970 
02971               do_setlval:
02972                 sn = js_GetSrcNote(jp->script, pc - 1);
02973                 if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
02974                     todo = Sprint(&ss->sprinter, "%s %s= %s",
02975                                   lval,
02976                                   (lastop == JSOP_GETTER)
02977                                   ? js_getter_str
02978                                   : (lastop == JSOP_SETTER)
02979                                   ? js_setter_str
02980                                   : js_CodeSpec[lastop].token,
02981                                   rval);
02982                 } else {
02983                     sn = js_GetSrcNote(jp->script, pc);
02984                     todo = Sprint(&ss->sprinter, "%s%s = %s",
02985                                   VarPrefix(sn), lval, rval);
02986                 }
02987                 if (op == JSOP_SETLOCALPOP) {
02988                     if (!PushOff(ss, todo, saveop))
02989                         return NULL;
02990                     rval = POP_STR();
02991                     LOCAL_ASSERT(*rval != '\0');
02992                     js_printf(jp, "\t%s;\n", rval);
02993                     todo = -2;
02994                 }
02995                 break;
02996 
02997               case JSOP_NEW:
02998               case JSOP_CALL:
02999               case JSOP_EVAL:
03000 #if JS_HAS_LVALUE_RETURN
03001               case JSOP_SETCALL:
03002 #endif
03003                 op = JSOP_SETNAME;      /* turn off most parens */
03004                 argc = GET_ARGC(pc);
03005                 argv = (char **)
03006                     JS_malloc(cx, (size_t)(argc + 1) * sizeof *argv);
03007                 if (!argv)
03008                     return NULL;
03009 
03010                 ok = JS_TRUE;
03011                 for (i = argc; i > 0; i--) {
03012                     argv[i] = JS_strdup(cx, POP_STR());
03013                     if (!argv[i]) {
03014                         ok = JS_FALSE;
03015                         break;
03016                     }
03017                 }
03018 
03019                 /* Skip the JSOP_PUSHOBJ-created empty string. */
03020                 LOCAL_ASSERT(ss->top >= 2);
03021                 (void) PopOff(ss, op);
03022 
03023                 op = saveop;
03024                 argv[0] = JS_strdup(cx, POP_STR());
03025                 if (!argv[i])
03026                     ok = JS_FALSE;
03027 
03028                 lval = "(", rval = ")";
03029                 if (op == JSOP_NEW) {
03030                     if (argc == 0)
03031                         lval = rval = "";
03032                     todo = Sprint(&ss->sprinter, "%s %s%s",
03033                                   js_new_str, argv[0], lval);
03034                 } else {
03035                     todo = Sprint(&ss->sprinter, ss_format,
03036                                   argv[0], lval);
03037                 }
03038                 if (todo < 0)
03039                     ok = JS_FALSE;
03040 
03041                 for (i = 1; i <= argc; i++) {
03042                     if (!argv[i] ||
03043                         Sprint(&ss->sprinter, ss_format,
03044                                argv[i], (i < argc) ? ", " : "") < 0) {
03045                         ok = JS_FALSE;
03046                         break;
03047                     }
03048                 }
03049                 if (Sprint(&ss->sprinter, rval) < 0)
03050                     ok = JS_FALSE;
03051 
03052                 for (i = 0; i <= argc; i++) {
03053                     if (argv[i])
03054                         JS_free(cx, argv[i]);
03055                 }
03056                 JS_free(cx, argv);
03057                 if (!ok)
03058                     return NULL;
03059 #if JS_HAS_LVALUE_RETURN
03060                 if (op == JSOP_SETCALL) {
03061                     if (!PushOff(ss, todo, op))
03062                         return NULL;
03063                     todo = Sprint(&ss->sprinter, "");
03064                 }
03065 #endif
03066                 break;
03067 
03068               case JSOP_DELNAME:
03069                 atom = GET_ATOM(cx, jp->script, pc);
03070                 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
03071                 if (!lval)
03072                     return NULL;
03073                 RETRACT(&ss->sprinter, lval);
03074               do_delete_lval:
03075                 todo = Sprint(&ss->sprinter, "%s %s", js_delete_str, lval);
03076                 break;
03077 
03078               case JSOP_DELPROP:
03079                 GET_ATOM_QUOTE_AND_FMT("%s %s[%s]", "%s %s.%s", rval);
03080                 lval = POP_STR();
03081                 todo = Sprint(&ss->sprinter, fmt, js_delete_str, lval, rval);
03082                 break;
03083 
03084               case JSOP_DELELEM:
03085                 op = JSOP_NOP;          /* turn off parens */
03086                 xval = POP_STR();
03087                 op = saveop;
03088                 lval = POP_STR();
03089                 if (*xval == '\0')
03090                     goto do_delete_lval;
03091                 todo = Sprint(&ss->sprinter,
03092                               (js_CodeSpec[lastop].format & JOF_XMLNAME)
03093                               ? "%s %s.%s"
03094                               : "%s %s[%s]",
03095                               js_delete_str, lval, xval);
03096                 break;
03097 
03098 #if JS_HAS_XML_SUPPORT
03099               case JSOP_DELDESC:
03100                 xval = POP_STR();
03101                 lval = POP_STR();
03102                 todo = Sprint(&ss->sprinter, "%s %s..%s",
03103                               js_delete_str, lval, xval);
03104                 break;
03105 #endif
03106 
03107               case JSOP_TYPEOFEXPR:
03108               case JSOP_TYPEOF:
03109               case JSOP_VOID:
03110                 rval = POP_STR();
03111                 todo = Sprint(&ss->sprinter, "%s %s", cs->name, rval);
03112                 break;
03113 
03114               case JSOP_INCARG:
03115               case JSOP_DECARG:
03116                 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
03117                 LOCAL_ASSERT(atom);
03118                 goto do_incatom;
03119 
03120               case JSOP_INCVAR:
03121               case JSOP_DECVAR:
03122                 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
03123                 LOCAL_ASSERT(atom);
03124                 goto do_incatom;
03125 
03126               case JSOP_INCNAME:
03127               case JSOP_DECNAME:
03128               case JSOP_INCGVAR:
03129               case JSOP_DECGVAR:
03130                 atom = GET_ATOM(cx, jp->script, pc);
03131               do_incatom:
03132                 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
03133                 if (!lval)
03134                     return NULL;
03135                 RETRACT(&ss->sprinter, lval);
03136               do_inclval:
03137                 todo = Sprint(&ss->sprinter, ss_format,
03138                               js_incop_strs[!(cs->format & JOF_INC)], lval);
03139                 break;
03140 
03141               case JSOP_INCPROP:
03142               case JSOP_DECPROP:
03143                 GET_ATOM_QUOTE_AND_FMT(preindex_format, predot_format, rval);
03144 
03145                 /*
03146                  * Force precedence below the numeric literal opcodes, so that
03147                  * 42..foo or 10000..toString(16), e.g., decompile with parens
03148                  * around the left-hand side of dot.
03149                  */
03150                 op = JSOP_GETPROP;
03151                 lval = POP_STR();
03152                 todo = Sprint(&ss->sprinter, fmt,
03153                               js_incop_strs[!(cs->format & JOF_INC)],
03154                               lval, rval);
03155                 break;
03156 
03157               case JSOP_INCELEM:
03158               case JSOP_DECELEM:
03159                 op = JSOP_NOP;          /* turn off parens */
03160                 xval = POP_STR();
03161                 op = JSOP_GETELEM;
03162                 lval = POP_STR();
03163                 if (*xval != '\0') {
03164                     todo = Sprint(&ss->sprinter,
03165                                   (js_CodeSpec[lastop].format & JOF_XMLNAME)
03166                                   ? predot_format
03167                                   : preindex_format,
03168                                   js_incop_strs[!(cs->format & JOF_INC)],
03169                                   lval, xval);
03170                 } else {
03171                     todo = Sprint(&ss->sprinter, ss_format,
03172                                   js_incop_strs[!(cs->format & JOF_INC)], lval);
03173                 }
03174                 break;
03175 
03176               case JSOP_ARGINC:
03177               case JSOP_ARGDEC:
03178                 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
03179                 LOCAL_ASSERT(atom);
03180                 goto do_atominc;
03181 
03182               case JSOP_VARINC:
03183               case JSOP_VARDEC:
03184                 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
03185                 LOCAL_ASSERT(atom);
03186                 goto do_atominc;
03187 
03188               case JSOP_NAMEINC:
03189               case JSOP_NAMEDEC:
03190               case JSOP_GVARINC:
03191               case JSOP_GVARDEC:
03192                 atom = GET_ATOM(cx, jp->script, pc);
03193               do_atominc:
03194                 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
03195                 if (!lval)
03196                     return NULL;
03197                 RETRACT(&ss->sprinter, lval);
03198               do_lvalinc:
03199                 todo = Sprint(&ss->sprinter, ss_format,
03200                               lval, js_incop_strs[!(cs->format & JOF_INC)]);
03201                 break;
03202 
03203               case JSOP_PROPINC:
03204               case JSOP_PROPDEC:
03205                 GET_ATOM_QUOTE_AND_FMT(postindex_format, postdot_format, rval);
03206 
03207                 /*
03208                  * Force precedence below the numeric literal opcodes, so that
03209                  * 42..foo or 10000..toString(16), e.g., decompile with parens
03210                  * around the left-hand side of dot.
03211                  */
03212                 op = JSOP_GETPROP;
03213                 lval = POP_STR();
03214                 todo = Sprint(&ss->sprinter, fmt, lval, rval,
03215                               js_incop_strs[!(cs->format & JOF_INC)]);
03216                 break;
03217 
03218               case JSOP_ELEMINC:
03219               case JSOP_ELEMDEC:
03220                 op = JSOP_NOP;          /* turn off parens */
03221                 xval = POP_STR();
03222                 op = JSOP_GETELEM;
03223                 lval = POP_STR();
03224                 if (*xval != '\0') {
03225                     todo = Sprint(&ss->sprinter,
03226                                   (js_CodeSpec[lastop].format & JOF_XMLNAME)
03227                                   ? postdot_format
03228                                   : postindex_format,
03229                                   lval, xval,
03230                                   js_incop_strs[!(cs->format & JOF_INC)]);
03231                 } else {
03232                     todo = Sprint(&ss->sprinter, ss_format,
03233                                   lval, js_incop_strs[!(cs->format & JOF_INC)]);
03234                 }
03235                 break;
03236 
03237               case JSOP_GETPROP2:
03238                 op = JSOP_GETPROP;
03239                 (void) PopOff(ss, lastop);
03240                 /* FALL THROUGH */
03241 
03242               case JSOP_GETPROP:
03243               case JSOP_GETXPROP:
03244                 atom = GET_ATOM(cx, jp->script, pc);
03245 
03246               do_getprop:
03247                 GET_QUOTE_AND_FMT(index_format, dot_format, rval);
03248 
03249               do_getprop_lval:
03250                 lval = POP_STR();
03251                 todo = Sprint(&ss->sprinter, fmt, lval, rval);
03252                 break;
03253 
03254 #if JS_HAS_XML_SUPPORT
03255               BEGIN_LITOPX_CASE(JSOP_GETMETHOD)
03256                 sn = js_GetSrcNote(jp->script, pc);
03257                 if (sn && SN_TYPE(sn) == SRC_PCBASE)
03258                     goto do_getprop;
03259                 GET_QUOTE_AND_FMT("%s.function::[%s]", "%s.function::%s", rval);
03260                 goto do_getprop_lval;
03261 
03262               BEGIN_LITOPX_CASE(JSOP_SETMETHOD)
03263                 sn = js_GetSrcNote(jp->script, pc);
03264                 if (sn && SN_TYPE(sn) == SRC_PCBASE)
03265                     goto do_setprop;
03266                 GET_QUOTE_AND_FMT("%s.function::[%s] %s= %s",
03267                                   "%s.function::%s %s= %s",
03268                                   xval);
03269                 goto do_setprop_rval;
03270 #endif
03271 
03272               case JSOP_SETPROP:
03273                 atom = GET_ATOM(cx, jp->script, pc);
03274 
03275               do_setprop:
03276                 GET_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval);
03277 
03278               do_setprop_rval:
03279                 rval = POP_STR();
03280 
03281                 /*
03282                  * Force precedence below the numeric literal opcodes, so that
03283                  * 42..foo or 10000..toString(16), e.g., decompile with parens
03284                  * around the left-hand side of dot.
03285                  */
03286                 op = JSOP_GETPROP;
03287                 lval = POP_STR();
03288                 sn = js_GetSrcNote(jp->script, pc - 1);
03289                 todo = Sprint(&ss->sprinter, fmt, lval, xval,
03290                               (sn && SN_TYPE(sn) == SRC_ASSIGNOP)
03291                               ? (lastop == JSOP_GETTER)
03292                                 ? js_getter_str
03293                                 : (lastop == JSOP_SETTER)
03294                                 ? js_setter_str
03295                                 : js_CodeSpec[lastop].token
03296                               : "",
03297                               rval);
03298                 break;
03299 
03300               case JSOP_GETELEM2:
03301                 op = JSOP_GETELEM;
03302                 (void) PopOff(ss, lastop);
03303                 /* FALL THROUGH */
03304 
03305               case JSOP_GETELEM:
03306               case JSOP_GETXELEM:
03307                 op = JSOP_NOP;          /* turn off parens */
03308                 xval = POP_STR();
03309                 op = saveop;
03310                 lval = POP_STR();
03311                 if (*xval == '\0') {
03312                     todo = Sprint(&ss->sprinter, "%s", lval);
03313                 } else {
03314                     todo = Sprint(&ss->sprinter,
03315                                   (js_CodeSpec[lastop].format & JOF_XMLNAME)
03316                                   ? dot_format
03317                                   : index_format,
03318                                   lval, xval);
03319                 }
03320                 break;
03321 
03322               case JSOP_SETELEM:
03323                 rval = POP_STR();
03324                 op = JSOP_NOP;          /* turn off parens */
03325                 xval = POP_STR();
03326                 cs = &js_CodeSpec[ss->opcodes[ss->top]];
03327                 op = JSOP_GETELEM;      /* lval must have high precedence */
03328                 lval = POP_STR();
03329                 op = saveop;
03330                 if (*xval == '\0')
03331                     goto do_setlval;
03332                 sn = js_GetSrcNote(jp->script, pc - 1);
03333                 todo = Sprint(&ss->sprinter,
03334                               (cs->format & JOF_XMLNAME)
03335                               ? "%s.%s %s= %s"
03336                               : "%s[%s] %s= %s",
03337                               lval, xval,
03338                               (sn && SN_TYPE(sn) == SRC_ASSIGNOP)
03339                               ? (lastop == JSOP_GETTER)
03340                                 ? js_getter_str
03341                                 : (lastop == JSOP_SETTER)
03342                                 ? js_setter_str
03343                                 : js_CodeSpec[lastop].token
03344                               : "",
03345                               rval);
03346                 break;
03347 
03348               case JSOP_ARGSUB:
03349                 i = (jsint) GET_ATOM_INDEX(pc);
03350                 todo = Sprint(&ss->sprinter, "%s[%d]",
03351                               js_arguments_str, (int) i);
03352                 break;
03353 
03354               case JSOP_ARGCNT:
03355                 todo = Sprint(&ss->sprinter, dot_format,
03356                               js_arguments_str, js_length_str);
03357                 break;
03358 
03359               case JSOP_GETARG:
03360                 i = GET_ARGNO(pc);
03361                 atom = GetSlotAtom(jp, js_GetArgument, i);
03362 #if JS_HAS_DESTRUCTURING
03363                 if (!atom) {
03364                     todo = Sprint(&ss->sprinter, "%s[%d]", js_arguments_str, i);
03365                     break;
03366                 }
03367 #else
03368                 LOCAL_ASSERT(atom);
03369 #endif
03370                 goto do_name;
03371 
03372               case JSOP_GETVAR:
03373                 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
03374                 LOCAL_ASSERT(atom);
03375                 goto do_name;
03376 
03377               case JSOP_NAME:
03378               case JSOP_GETGVAR:
03379                 atom = GET_ATOM(cx, jp->script, pc);
03380               do_name:
03381                 lval = "";
03382               do_qname:
03383                 sn = js_GetSrcNote(jp->script, pc);
03384                 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
03385                 if (!rval)
03386                     return NULL;
03387                 RETRACT(&ss->sprinter, rval);
03388                 todo = Sprint(&ss->sprinter, "%s%s%s",
03389                               VarPrefix(sn), lval, rval);
03390                 break;
03391 
03392               case JSOP_UINT16:
03393                 i = (jsint) GET_ATOM_INDEX(pc);
03394                 goto do_sprint_int;
03395 
03396               case JSOP_UINT24:
03397                 i = (jsint) GET_UINT24(pc);
03398               do_sprint_int:
03399                 todo = Sprint(&ss->sprinter, "%u", (unsigned) i);
03400                 break;
03401 
03402               case JSOP_LITERAL:
03403                 atomIndex = GET_LITERAL_INDEX(pc);
03404                 goto do_JSOP_STRING;
03405 
03406               case JSOP_FINDNAME:
03407                 atomIndex = GET_LITERAL_INDEX(pc);
03408                 todo = Sprint(&ss->sprinter, "");
03409                 if (todo < 0 || !PushOff(ss, todo, op))
03410                     return NULL;
03411                 atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex);
03412                 goto do_name;
03413 
03414               case JSOP_LITOPX:
03415                 atomIndex = GET_LITERAL_INDEX(pc);
03416                 pc2 = pc + 1 + LITERAL_INDEX_LEN;
03417                 op = saveop = *pc2;
03418                 pc += len - (1 + ATOM_INDEX_LEN);
03419                 cs = &js_CodeSpec[op];
03420                 len = cs->length;
03421                 switch (op) {
03422                   case JSOP_ANONFUNOBJ:   goto do_JSOP_ANONFUNOBJ;
03423                   case JSOP_BINDNAME:     goto do_JSOP_BINDNAME;
03424                   case JSOP_CLOSURE:      goto do_JSOP_CLOSURE;
03425 #if JS_HAS_EXPORT_IMPORT
03426                   case JSOP_EXPORTNAME:   goto do_JSOP_EXPORTNAME;
03427 #endif
03428 #if JS_HAS_XML_SUPPORT
03429                   case JSOP_GETMETHOD:    goto do_JSOP_GETMETHOD;
03430                   case JSOP_SETMETHOD:    goto do_JSOP_SETMETHOD;
03431 #endif
03432                   case JSOP_NAMEDFUNOBJ:  goto do_JSOP_NAMEDFUNOBJ;
03433                   case JSOP_NUMBER:       goto do_JSOP_NUMBER;
03434                   case JSOP_OBJECT:       goto do_JSOP_OBJECT;
03435 #if JS_HAS_XML_SUPPORT
03436                   case JSOP_QNAMECONST:   goto do_JSOP_QNAMECONST;
03437                   case JSOP_QNAMEPART:    goto do_JSOP_QNAMEPART;
03438 #endif
03439                   case JSOP_REGEXP:       goto do_JSOP_REGEXP;
03440                   case JSOP_SETCONST:     goto do_JSOP_SETCONST;
03441                   case JSOP_STRING:       goto do_JSOP_STRING;
03442 #if JS_HAS_XML_SUPPORT
03443                   case JSOP_XMLCDATA:     goto do_JSOP_XMLCDATA;
03444                   case JSOP_XMLCOMMENT:   goto do_JSOP_XMLCOMMENT;
03445                   case JSOP_XMLOBJECT:    goto do_JSOP_XMLOBJECT;
03446                   case JSOP_XMLPI:        goto do_JSOP_XMLPI;
03447 #endif
03448                   case JSOP_ENTERBLOCK:   goto do_JSOP_ENTERBLOCK;
03449                   default:                LOCAL_ASSERT(0);
03450                 }
03451                 /* NOTREACHED */
03452                 break;
03453 
03454               BEGIN_LITOPX_CASE(JSOP_NUMBER)
03455                 val = ATOM_KEY(atom);
03456                 if (JSVAL_IS_INT(val)) {
03457                     long ival = (long)JSVAL_TO_INT(val);
03458                     todo = Sprint(&ss->sprinter, "%ld", ival);
03459                 } else {
03460                     char buf[DTOSTR_STANDARD_BUFFER_SIZE];
03461                     char *numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD,
03462                                              0, *JSVAL_TO_DOUBLE(val));
03463                     if (!numStr) {
03464                         JS_ReportOutOfMemory(cx);
03465                         return NULL;
03466                     }
03467                     todo = Sprint(&ss->sprinter, numStr);
03468                 }
03469               END_LITOPX_CASE
03470 
03471               BEGIN_LITOPX_CASE(JSOP_STRING)
03472                 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
03473                                    inXML ? DONT_ESCAPE : '"');
03474                 if (!rval)
03475                     return NULL;
03476                 todo = STR2OFF(&ss->sprinter, rval);
03477               END_LITOPX_CASE
03478 
03479               case JSOP_OBJECT:
03480               case JSOP_REGEXP:
03481               case JSOP_ANONFUNOBJ:
03482               case JSOP_NAMEDFUNOBJ:
03483                 atomIndex = GET_ATOM_INDEX(pc);
03484 
03485               do_JSOP_OBJECT:
03486               do_JSOP_REGEXP:
03487               do_JSOP_ANONFUNOBJ:
03488               do_JSOP_NAMEDFUNOBJ:
03489                 atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex);
03490                 if (op == JSOP_OBJECT || op == JSOP_REGEXP) {
03491                     if (!js_regexp_toString(cx, ATOM_TO_OBJECT(atom), 0, NULL,
03492                                             &val)) {
03493                         return NULL;
03494                     }
03495                 } else {
03496                     if (!js_fun_toString(cx, ATOM_TO_OBJECT(atom),
03497                                          JS_IN_GROUP_CONTEXT |
03498                                          JS_DONT_PRETTY_PRINT,
03499                                          0, NULL, &val)) {
03500                         return NULL;
03501                     }
03502                 }
03503                 str = JSVAL_TO_STRING(val);
03504                 todo = SprintPut(&ss->sprinter, JS_GetStringBytes(str),
03505                                  JSSTRING_LENGTH(str));
03506                 break;
03507 
03508               case JSOP_TABLESWITCH:
03509               case JSOP_TABLESWITCHX:
03510               {
03511                 ptrdiff_t jmplen, off, off2;
03512                 jsint j, n, low, high;
03513                 TableEntry *table, pivot;
03514 
03515                 sn = js_GetSrcNote(jp->script, pc);
03516                 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
03517                 len = js_GetSrcNoteOffset(sn, 0);
03518                 jmplen = (op == JSOP_TABLESWITCH) ? JUMP_OFFSET_LEN
03519                                                   : JUMPX_OFFSET_LEN;
03520                 pc2 = pc;
03521                 off = GetJumpOffset(pc, pc2);
03522                 pc2 += jmplen;
03523                 low = GET_JUMP_OFFSET(pc2);
03524                 pc2 += JUMP_OFFSET_LEN;
03525                 high = GET_JUMP_OFFSET(pc2);
03526                 pc2 += JUMP_OFFSET_LEN;
03527 
03528                 n = high - low + 1;
03529                 if (n == 0) {
03530                     table = NULL;
03531                     j = 0;
03532                 } else {
03533                     table = (TableEntry *)
03534                             JS_malloc(cx, (size_t)n * sizeof *table);
03535                     if (!table)
03536                         return NULL;
03537                     for (i = j = 0; i < n; i++) {
03538                         table[j].label = NULL;
03539                         off2 = GetJumpOffset(pc, pc2);
03540                         if (off2) {
03541                             sn = js_GetSrcNote(jp->script, pc2);
03542                             if (sn) {
03543                                 LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL);
03544                                 table[j].label =
03545                                     js_GetAtom(cx, &jp->script->atomMap,
03546                                                (jsatomid)
03547                                                js_GetSrcNoteOffset(sn, 0));
03548                             }
03549                             table[j].key = INT_TO_JSVAL(low + i);
03550                             table[j].offset = off2;
03551                             table[j].order = j;
03552                             j++;
03553                         }
03554                         pc2 += jmplen;
03555                     }
03556                     js_HeapSort(table, (size_t) j, &pivot, sizeof(TableEntry),
03557                                 CompareOffsets, NULL);
03558                 }
03559 
03560                 ok = DecompileSwitch(ss, table, (uintN)j, pc, len, off,
03561                                      JS_FALSE);
03562                 JS_free(cx, table);
03563                 if (!ok)
03564                     return NULL;
03565                 todo = -2;
03566                 break;
03567               }
03568 
03569               case JSOP_LOOKUPSWITCH:
03570               case JSOP_LOOKUPSWITCHX:
03571               {
03572                 ptrdiff_t jmplen, off, off2;
03573                 jsatomid npairs, k;
03574                 TableEntry *table;
03575 
03576                 sn = js_GetSrcNote(jp->script, pc);
03577                 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
03578                 len = js_GetSrcNoteOffset(sn, 0);
03579                 jmplen = (op == JSOP_LOOKUPSWITCH) ? JUMP_OFFSET_LEN
03580                                                    : JUMPX_OFFSET_LEN;
03581                 pc2 = pc;
03582                 off = GetJumpOffset(pc, pc2);
03583                 pc2 += jmplen;
03584                 npairs = GET_ATOM_INDEX(pc2);
03585                 pc2 += ATOM_INDEX_LEN;
03586 
03587                 table = (TableEntry *)
03588                     JS_malloc(cx, (size_t)npairs * sizeof *table);
03589                 if (!table)
03590                     return NULL;
03591                 for (k = 0; k < npairs; k++) {
03592                     sn = js_GetSrcNote(jp->script, pc2);
03593                     if (sn) {
03594                         LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL);
03595                         table[k].label =
03596                             js_GetAtom(cx, &jp->script->atomMap, (jsatomid)
03597                                        js_GetSrcNoteOffset(sn, 0));
03598                     } else {
03599                         table[k].label = NULL;
03600                     }
03601                     atom = GET_ATOM(cx, jp->script, pc2);
03602                     pc2 += ATOM_INDEX_LEN;
03603                     off2 = GetJumpOffset(pc, pc2);
03604                     pc2 += jmplen;
03605                     table[k].key = ATOM_KEY(atom);
03606                     table[k].offset = off2;
03607                 }
03608 
03609                 ok = DecompileSwitch(ss, table, (uintN)npairs, pc, len, off,
03610                                      JS_FALSE);
03611                 JS_free(cx, table);
03612                 if (!ok)
03613                     return NULL;
03614                 todo = -2;
03615                 break;
03616               }
03617 
03618               case JSOP_CONDSWITCH:
03619               {
03620                 ptrdiff_t off, off2, caseOff;
03621                 jsint ncases;
03622                 TableEntry *table;
03623 
03624                 sn = js_GetSrcNote(jp->script, pc);
03625                 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
03626                 len = js_GetSrcNoteOffset(sn, 0);
03627                 off = js_GetSrcNoteOffset(sn, 1);
03628 
03629                 /*
03630                  * Count the cases using offsets from switch to first case,
03631                  * and case to case, stored in srcnote immediates.
03632                  */
03633                 pc2 = pc;
03634                 off2 = off;
03635                 for (ncases = 0; off2 != 0; ncases++) {
03636                     pc2 += off2;
03637                     LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT ||
03638                                  *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX);
03639                     if (*pc2 == JSOP_DEFAULT || *pc2 == JSOP_DEFAULTX) {
03640                         /* End of cases, but count default as a case. */
03641                         off2 = 0;
03642                     } else {
03643                         sn = js_GetSrcNote(jp->script, pc2);
03644                         LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
03645                         off2 = js_GetSrcNoteOffset(sn, 0);
03646                     }
03647                 }
03648 
03649                 /*
03650                  * Allocate table and rescan the cases using their srcnotes,
03651                  * stashing each case's delta from switch top in table[i].key,
03652                  * and the distance to its statements in table[i].offset.
03653                  */
03654                 table = (TableEntry *)
03655                     JS_malloc(cx, (size_t)ncases * sizeof *table);
03656                 if (!table)
03657                     return NULL;
03658                 pc2 = pc;
03659                 off2 = off;
03660                 for (i = 0; i < ncases; i++) {
03661                     pc2 += off2;
03662                     LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT ||
03663                                  *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX);
03664                     caseOff = pc2 - pc;
03665                     table[i].key = INT_TO_JSVAL((jsint) caseOff);
03666                     table[i].offset = caseOff + GetJumpOffset(pc2, pc2);
03667                     if (*pc2 == JSOP_CASE || *pc2 == JSOP_CASEX) {
03668                         sn = js_GetSrcNote(jp->script, pc2);
03669                         LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
03670                         off2 = js_GetSrcNoteOffset(sn, 0);
03671                     }
03672                 }
03673 
03674                 /*
03675                  * Find offset of default code by fetching the default offset
03676                  * from the end of table.  JSOP_CONDSWITCH always has a default
03677                  * case at the end.
03678                  */
03679                 off = JSVAL_TO_INT(table[ncases-1].key);
03680                 pc2 = pc + off;
03681                 off += GetJumpOffset(pc2, pc2);
03682 
03683                 ok = DecompileSwitch(ss, table, (uintN)ncases, pc, len, off,
03684                                      JS_TRUE);
03685                 JS_free(cx, table);
03686                 if (!ok)
03687                     return NULL;
03688                 todo = -2;
03689                 break;
03690               }
03691 
03692               case JSOP_CASE:
03693               case JSOP_CASEX:
03694               {
03695                 lval = POP_STR();
03696                 if (!lval)
03697                     return NULL;
03698                 js_printf(jp, "\tcase %s:\n", lval);
03699                 todo = -2;
03700                 break;
03701               }
03702 
03703               case JSOP_NEW_EQ:
03704               case JSOP_NEW_NE:
03705                 rval = POP_STR();
03706                 lval = POP_STR();
03707                 todo = Sprint(&ss->sprinter, "%s %c== %s",
03708                               lval, (op == JSOP_NEW_EQ) ? '=' : '!', rval);
03709                 break;
03710 
03711               BEGIN_LITOPX_CASE(JSOP_CLOSURE)
03712                 LOCAL_ASSERT(ATOM_IS_OBJECT(atom));
03713                 todo = -2;
03714                 goto do_function;
03715               END_LITOPX_CASE
03716 
03717 #if JS_HAS_EXPORT_IMPORT
03718               case JSOP_EXPORTALL:
03719                 js_printf(jp, "\texport *;\n");
03720                 todo = -2;
03721                 break;
03722 
03723               BEGIN_LITOPX_CASE(JSOP_EXPORTNAME)
03724                 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
03725                 if (!rval)
03726                     return NULL;
03727                 RETRACT(&ss->sprinter, rval);
03728                 js_printf(jp, "\texport %s;\n", rval);
03729                 todo = -2;
03730               END_LITOPX_CASE
03731 
03732               case JSOP_IMPORTALL:
03733                 lval = POP_STR();
03734                 js_printf(jp, "\timport %s.*;\n", lval);
03735                 todo = -2;
03736                 break;
03737 
03738               case JSOP_IMPORTPROP:
03739               do_importprop:
03740                 GET_ATOM_QUOTE_AND_FMT("\timport %s[%s];\n",
03741                                        "\timport %s.%s;\n",
03742                                        rval);
03743                 lval = POP_STR();
03744                 js_printf(jp, fmt, lval, rval);
03745                 todo = -2;
03746                 break;
03747 
03748               case JSOP_IMPORTELEM:
03749                 xval = POP_STR();
03750                 op = JSOP_GETELEM;
03751                 if (js_CodeSpec[lastop].format & JOF_XMLNAME)
03752                     goto do_importprop;
03753                 lval = POP_STR();
03754                 js_printf(jp, "\timport %s[%s];\n", lval, xval);
03755                 todo = -2;
03756                 break;
03757 #endif /* JS_HAS_EXPORT_IMPORT */
03758 
03759               case JSOP_TRAP:
03760                 op = JS_GetTrapOpcode(cx, jp->script, pc);
03761                 if (op == JSOP_LIMIT)
03762                     return NULL;
03763                 saveop = op;
03764                 *pc = op;
03765                 cs = &js_CodeSpec[op];
03766                 len = cs->length;
03767                 DECOMPILE_CODE(pc, len);
03768                 *pc = JSOP_TRAP;
03769                 todo = -2;
03770                 break;
03771 
03772               case JSOP_NEWINIT:
03773               {
03774                 JSBool isArray;
03775 
03776                 LOCAL_ASSERT(ss->top >= 2);
03777                 (void) PopOff(ss, op);
03778                 lval = POP_STR();
03779                 isArray = (*lval == 'A');
03780                 todo = ss->sprinter.offset;
03781 #if JS_HAS_SHARP_VARS
03782                 op = (JSOp)pc[len];
03783                 if (op == JSOP_DEFSHARP) {
03784                     pc += len;
03785                     cs = &js_CodeSpec[op];
03786                     len = cs->length;
03787                     i = (jsint) GET_ATOM_INDEX(pc);
03788                     if (Sprint(&ss->sprinter, "#%u=", (unsigned) i) < 0)
03789                         return NULL;
03790                 }
03791 #endif /* JS_HAS_SHARP_VARS */
03792                 if (isArray) {
03793                     ++ss->inArrayInit;
03794                     if (SprintCString(&ss->sprinter, "[") < 0)
03795                         return NULL;
03796                 } else {
03797                     if (SprintCString(&ss->sprinter, "{") < 0)
03798                         return NULL;
03799                 }
03800                 break;
03801               }
03802 
03803               case JSOP_ENDINIT:
03804                 op = JSOP_NOP;           /* turn off parens */
03805                 rval = POP_STR();
03806                 sn = js_GetSrcNote(jp->script, pc);
03807 
03808                 /* Skip any #n= prefix to find the opening bracket. */
03809                 for (xval = rval; *xval != '[' && *xval != '{'; xval++)
03810                     continue;
03811                 if (*xval == '[')
03812                     --ss->inArrayInit;
03813                 todo = Sprint(&ss->sprinter, "%s%s%c",
03814                               rval,
03815                               (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "",
03816                               (*xval == '[') ? ']' : '}');
03817                 break;
03818 
03819               case JSOP_INITPROP:
03820                 atom = GET_ATOM(cx, jp->script, pc);
03821                 xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
03822                                    (jschar)
03823                                    (ATOM_IS_IDENTIFIER(atom) ? 0 : '\''));
03824                 if (!xval)
03825                     return NULL;
03826                 rval = POP_STR();
03827                 lval = POP_STR();
03828               do_initprop:
03829 #ifdef OLD_GETTER_SETTER
03830                 todo = Sprint(&ss->sprinter, "%s%s%s%s%s:%s",
03831                               lval,
03832                               (lval[1] != '\0') ? ", " : "",
03833                               xval,
03834                               (lastop == JSOP_GETTER || lastop == JSOP_SETTER)
03835                               ? " " : "",
03836                               (lastop == JSOP_GETTER) ? js_getter_str :
03837                               (lastop == JSOP_SETTER) ? js_setter_str :
03838                               "",
03839                               rval);
03840 #else
03841                 if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) {
03842                     if (!atom || !ATOM_IS_STRING(atom) ||
03843                         !ATOM_IS_IDENTIFIER(atom) ||
03844                         ATOM_IS_KEYWORD(atom) ||
03845                         ((ss->opcodes[ss->top+1] != JSOP_ANONFUNOBJ ||
03846                           strncmp(rval, js_function_str, 8) != 0) &&
03847                          ss->opcodes[ss->top+1] != JSOP_NAMEDFUNOBJ)) {
03848                         todo = Sprint(&ss->sprinter, "%s%s%s%s%s:%s", lval,
03849                                       (lval[1] != '\0') ? ", " : "", xval,
03850                                       (lastop == JSOP_GETTER ||
03851                                        lastop == JSOP_SETTER)
03852                                         ? " " : "",
03853                                       (lastop == JSOP_GETTER) ? js_getter_str :
03854                                       (lastop == JSOP_SETTER) ? js_setter_str :
03855                                       "",
03856                                       rval);
03857                     } else {
03858                         rval += 8 + 1;
03859                         LOCAL_ASSERT(rval[strlen(rval)-1] == '}');
03860                         todo = Sprint(&ss->sprinter, "%s%s%s %s%s",
03861                                       lval,
03862                                       (lval[1] != '\0') ? ", " : "",
03863                                       (lastop == JSOP_GETTER)
03864                                       ? js_get_str : js_set_str,
03865                                       xval,
03866                                       rval);
03867                     }
03868                 } else {
03869                     todo = Sprint(&ss->sprinter, "%s%s%s:%s",
03870                                   lval,
03871                                   (lval[1] != '\0') ? ", " : "",
03872                                   xval,
03873                                   rval);
03874                 }
03875 #endif
03876                 break;
03877 
03878               case JSOP_INITELEM:
03879                 rval = POP_STR();
03880                 xval = POP_STR();
03881                 lval = POP_STR();
03882                 sn = js_GetSrcNote(jp->script, pc);
03883                 if (sn && SN_TYPE(sn) == SRC_INITPROP) {
03884                     atom = NULL;
03885                     goto do_initprop;
03886                 }
03887                 todo = Sprint(&ss->sprinter, "%s%s%s",
03888                               lval,
03889                               (lval[1] != '\0' || *xval != '0') ? ", " : "",
03890                               rval);
03891                 break;
03892 
03893 #if JS_HAS_SHARP_VARS
03894               case JSOP_DEFSHARP:
03895                 i = (jsint) GET_ATOM_INDEX(pc);
03896                 rval = POP_STR();
03897                 todo = Sprint(&ss->sprinter, "#%u=%s", (unsigned) i, rval);
03898                 break;
03899 
03900               case JSOP_USESHARP:
03901                 i = (jsint) GET_ATOM_INDEX(pc);
03902                 todo = Sprint(&ss->sprinter, "#%u#", (unsigned) i);
03903                 break;
03904 #endif /* JS_HAS_SHARP_VARS */
03905 
03906 #if JS_HAS_DEBUGGER_KEYWORD
03907               case JSOP_DEBUGGER:
03908                 js_printf(jp, "\tdebugger;\n");
03909                 todo = -2;
03910                 break;
03911 #endif /* JS_HAS_DEBUGGER_KEYWORD */
03912 
03913 #if JS_HAS_XML_SUPPORT
03914               case JSOP_STARTXML:
03915               case JSOP_STARTXMLEXPR:
03916                 inXML = op == JSOP_STARTXML;
03917                 todo = -2;
03918                 break;
03919 
03920               case JSOP_DEFXMLNS:
03921                 rval = POP_STR();
03922                 js_printf(jp, "\t%s %s %s = %s;\n",
03923                           js_default_str, js_xml_str, js_namespace_str, rval);
03924                 todo = -2;
03925                 break;
03926 
03927               case JSOP_ANYNAME:
03928                 if (pc[JSOP_ANYNAME_LENGTH] == JSOP_TOATTRNAME) {
03929                     len += JSOP_TOATTRNAME_LENGTH;
03930                     todo = SprintPut(&ss->sprinter, "@*", 2);
03931                 } else {
03932                     todo = SprintPut(&ss->sprinter, "*", 1);
03933                 }
03934                 break;
03935 
03936               BEGIN_LITOPX_CASE(JSOP_QNAMEPART)
03937                 if (pc[JSOP_QNAMEPART_LENGTH] == JSOP_TOATTRNAME) {
03938                     saveop = JSOP_TOATTRNAME;
03939                     len += JSOP_TOATTRNAME_LENGTH;
03940                     lval = "@";
03941                     goto do_qname;
03942                 }
03943                 goto do_name;
03944               END_LITOPX_CASE
03945 
03946               BEGIN_LITOPX_CASE(JSOP_QNAMECONST)
03947                 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
03948                 if (!rval)
03949                     return NULL;
03950                 RETRACT(&ss->sprinter, rval);
03951                 lval = POP_STR();
03952                 todo = Sprint(&ss->sprinter, "%s::%s", lval, rval);
03953               END_LITOPX_CASE
03954 
03955               case JSOP_QNAME:
03956                 rval = POP_STR();
03957                 lval = POP_STR();
03958                 todo = Sprint(&ss->sprinter, "%s::[%s]", lval, rval);
03959                 break;
03960 
03961               case JSOP_TOATTRNAME:
03962                 op = JSOP_NOP;           /* turn off parens */
03963                 rval = POP_STR();
03964                 todo = Sprint(&ss->sprinter, "@[%s]", rval);
03965                 break;
03966 
03967               case JSOP_TOATTRVAL:
03968                 todo = -2;
03969                 break;
03970 
03971               case JSOP_ADDATTRNAME:
03972                 rval = POP_STR();
03973                 lval = POP_STR();
03974                 todo = Sprint(&ss->sprinter, "%s %s", lval, rval);
03975                 /* This gets reset by all XML tag expressions. */
03976                 quoteAttr = JS_TRUE;
03977                 break;
03978 
03979               case JSOP_ADDATTRVAL:
03980                 rval = POP_STR();
03981                 lval = POP_STR();
03982                 if (quoteAttr)
03983                     todo = Sprint(&ss->sprinter, "%s=\"%s\"", lval, rval);
03984                 else
03985                     todo = Sprint(&ss->sprinter, "%s=%s", lval, rval);
03986                 break;
03987 
03988               case JSOP_BINDXMLNAME:
03989                 /* Leave the name stacked and push a dummy string. */
03990                 todo = Sprint(&ss->sprinter, "");
03991                 break;
03992 
03993               case JSOP_SETXMLNAME:
03994                 /* Pop the r.h.s., the dummy string, and the name. */
03995                 rval = POP_STR();
03996                 (void) PopOff(ss, op);
03997                 lval = POP_STR();
03998                 goto do_setlval;
03999 
04000               case JSOP_XMLELTEXPR:
04001               case JSOP_XMLTAGEXPR:
04002                 todo = Sprint(&ss->sprinter, "{%s}", POP_STR());
04003                 inXML = JS_TRUE;
04004                 /* If we're an attribute value, we shouldn't quote this. */
04005                 quoteAttr = JS_FALSE;
04006                 break;
04007 
04008               case JSOP_TOXMLLIST:
04009                 op = JSOP_NOP;           /* turn off parens */
04010                 todo = Sprint(&ss->sprinter, "<>%s</>", POP_STR());
04011                 inXML = JS_FALSE;
04012                 break;
04013 
04014               case JSOP_FOREACH:
04015                 foreach = JS_TRUE;
04016                 todo = -2;
04017                 break;
04018 
04019               case JSOP_TOXML:
04020                 inXML = JS_FALSE;
04021                 /* FALL THROUGH */
04022 
04023               case JSOP_XMLNAME:
04024               case JSOP_FILTER:
04025                 /* Conversion and prefix ops do nothing in the decompiler. */
04026                 todo = -2;
04027                 break;
04028 
04029               case JSOP_ENDFILTER:
04030                 rval = POP_STR();
04031                 lval = POP_STR();
04032                 todo = Sprint(&ss->sprinter, "%s.(%s)", lval, rval);
04033                 break;
04034 
04035               case JSOP_DESCENDANTS:
04036                 rval = POP_STR();
04037                 lval = POP_STR();
04038                 todo = Sprint(&ss->sprinter, "%s..%s", lval, rval);
04039                 break;
04040 
04041               BEGIN_LITOPX_CASE(JSOP_XMLOBJECT)
04042                 todo = Sprint(&ss->sprinter, "<xml address='%p'>",
04043                               ATOM_TO_OBJECT(atom));
04044               END_LITOPX_CASE
04045 
04046               BEGIN_LITOPX_CASE(JSOP_XMLCDATA)
04047                 todo = SprintPut(&ss->sprinter, "<![CDATA[", 9);
04048                 if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0))
04049                     return NULL;
04050                 SprintPut(&ss->sprinter, "]]>", 3);
04051               END_LITOPX_CASE
04052 
04053               BEGIN_LITOPX_CASE(JSOP_XMLCOMMENT)
04054                 todo = SprintPut(&ss->sprinter, "<!--", 4);
04055                 if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0))
04056                     return NULL;
04057                 SprintPut(&ss->sprinter, "-->", 3);
04058               END_LITOPX_CASE
04059 
04060               BEGIN_LITOPX_CASE(JSOP_XMLPI)
04061                 rval = JS_strdup(cx, POP_STR());
04062                 if (!rval)
04063                     return NULL;
04064                 todo = SprintPut(&ss->sprinter, "<?", 2);
04065                 ok = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0) &&
04066                      (*rval == '\0' ||
04067                       (SprintPut(&ss->sprinter, " ", 1) >= 0 &&
04068                        SprintCString(&ss->sprinter, rval)));
04069                 JS_free(cx, (char *)rval);
04070                 if (!ok)
04071                     return NULL;
04072                 SprintPut(&ss->sprinter, "?>", 2);
04073               END_LITOPX_CASE
04074 
04075               case JSOP_GETFUNNS:
04076                 todo = SprintPut(&ss->sprinter, js_function_str, 8);
04077                 break;
04078 #endif /* JS_HAS_XML_SUPPORT */
04079 
04080               default:
04081                 todo = -2;
04082                 break;
04083 
04084 #undef BEGIN_LITOPX_CASE
04085 #undef END_LITOPX_CASE
04086             }
04087         }
04088 
04089         if (todo < 0) {
04090             /* -2 means "don't push", -1 means reported error. */
04091             if (todo == -1)
04092                 return NULL;
04093         } else {
04094             if (!PushOff(ss, todo, saveop))
04095                 return NULL;
04096         }
04097         pc += len;
04098     }
04099 
04100 /*
04101  * Undefine local macros.
04102  */
04103 #undef inXML
04104 #undef DECOMPILE_CODE
04105 #undef POP_STR
04106 #undef LOCAL_ASSERT
04107 #undef ATOM_IS_IDENTIFIER
04108 #undef GET_QUOTE_AND_FMT
04109 #undef GET_ATOM_QUOTE_AND_FMT
04110 
04111     return pc;
04112 }
04113 
04114 static JSBool
04115 InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth)
04116 {
04117     size_t offsetsz, opcodesz;
04118     void *space;
04119 
04120     INIT_SPRINTER(cx, &ss->sprinter, &cx->tempPool, PAREN_SLOP);
04121 
04122     /* Allocate the parallel (to avoid padding) offset and opcode stacks. */
04123     offsetsz = depth * sizeof(ptrdiff_t);
04124     opcodesz = depth * sizeof(jsbytecode);
04125     JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz);
04126     if (!space)
04127         return JS_FALSE;
04128     ss->offsets = (ptrdiff_t *) space;
04129     ss->opcodes = (jsbytecode *) ((char *)space + offsetsz);
04130 
04131     ss->top = ss->inArrayInit = 0;
04132     ss->printer = jp;
04133     return JS_TRUE;
04134 }
04135 
04136 JSBool
04137 js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len,
04138                  uintN pcdepth)
04139 {
04140     uintN depth, i;
04141     SprintStack ss;
04142     JSContext *cx;
04143     void *mark;
04144     JSBool ok;
04145     JSScript *oldscript;
04146     char *last;
04147 
04148     depth = script->depth;
04149     JS_ASSERT(pcdepth <= depth);
04150 
04151     /* Initialize a sprinter for use with the offset stack. */
04152     cx = jp->sprinter.context;
04153     mark = JS_ARENA_MARK(&cx->tempPool);
04154     ok = InitSprintStack(cx, &ss, jp, depth);
04155     if (!ok)
04156         goto out;
04157 
04158     /*
04159      * If we are called from js_DecompileValueGenerator with a portion of
04160      * script's bytecode that starts with a non-zero model stack depth given
04161      * by pcdepth, attempt to initialize the missing string offsets in ss to
04162      * |spindex| negative indexes from fp->sp for the activation fp in which
04163      * the error arose.
04164      *
04165      * See js_DecompileValueGenerator for how its |spindex| parameter is used,
04166      * and see also GetOff, which makes use of the ss.offsets[i] < -1 that are
04167      * potentially stored below.
04168      */
04169     ss.top = pcdepth;
04170     if (pcdepth != 0) {
04171         JSStackFrame *fp;
04172         ptrdiff_t top;
04173 
04174         for (fp = cx->fp; fp && !fp->script; fp = fp->down)
04175             continue;
04176         top = fp ? fp->sp - fp->spbase : 0;
04177         for (i = 0; i < pcdepth; i++) {
04178             ss.offsets[i] = -1;
04179             ss.opcodes[i] = JSOP_NOP;
04180         }
04181         if (fp && fp->pc == pc && (uintN)top == pcdepth) {
04182             for (i = 0; i < pcdepth; i++) {
04183                 ptrdiff_t off;
04184                 jsbytecode *genpc;
04185 
04186                 off = (intN)i - (intN)depth;
04187                 genpc = (jsbytecode *) fp->spbase[off];
04188                 if (JS_UPTRDIFF(genpc, script->code) < script->length) {
04189                     ss.offsets[i] += (ptrdiff_t)i - top;
04190                     ss.opcodes[i] = *genpc;
04191                 }
04192             }
04193         }
04194     }
04195 
04196     /* Call recursive subroutine to do the hard work. */
04197     oldscript = jp->script;
04198     jp->script = script;
04199     ok = Decompile(&ss, pc, len) != NULL;
04200     jp->script = oldscript;
04201 
04202     /* If the given code didn't empty the stack, do it now. */
04203     if (ok && ss.top) {
04204         do {
04205             last = OFF2STR(&ss.sprinter, PopOff(&ss, JSOP_POP));
04206         } while (ss.top > pcdepth);
04207         js_printf(jp, "%s", last);
04208     }
04209 
04210 out:
04211     /* Free all temporary stuff allocated under this call. */
04212     JS_ARENA_RELEASE(&cx->tempPool, mark);
04213     return ok;
04214 }
04215 
04216 JSBool
04217 js_DecompileScript(JSPrinter *jp, JSScript *script)
04218 {
04219     return js_DecompileCode(jp, script, script->code, (uintN)script->length, 0);
04220 }
04221 
04222 static const char native_code_str[] = "\t[native code]\n";
04223 
04224 JSBool
04225 js_DecompileFunctionBody(JSPrinter *jp, JSFunction *fun)
04226 {
04227     JSScript *script;
04228     JSScope *scope, *save;
04229     JSBool ok;
04230 
04231     if (!FUN_INTERPRETED(fun)) {
04232         js_printf(jp, native_code_str);
04233         return JS_TRUE;
04234     }
04235     script = fun->u.i.script;
04236     scope = fun->object ? OBJ_SCOPE(fun->object) : NULL;
04237     save = jp->scope;
04238     jp->scope = scope;
04239     ok = js_DecompileCode(jp, script, script->code, (uintN)script->length, 0);
04240     jp->scope = save;
04241     return ok;
04242 }
04243 
04244 JSBool
04245 js_DecompileFunction(JSPrinter *jp, JSFunction *fun)
04246 {
04247     JSContext *cx;
04248     uintN i, nargs, indent;
04249     void *mark;
04250     JSAtom **params;
04251     JSScope *scope, *oldscope;
04252     JSScopeProperty *sprop;
04253     jsbytecode *pc, *endpc;
04254     ptrdiff_t len;
04255     JSBool ok;
04256 
04257     /*
04258      * If pretty, conform to ECMA-262 Edition 3, 15.3.4.2, by decompiling a
04259      * FunctionDeclaration.  Otherwise, check the JSFUN_LAMBDA flag and force
04260      * an expression by parenthesizing.
04261      */
04262     if (jp->pretty) {
04263         js_printf(jp, "\t");
04264     } else {
04265         if (!jp->grouped && (fun->flags & JSFUN_LAMBDA))
04266             js_puts(jp, "(");
04267     }
04268     if (JSFUN_GETTER_TEST(fun->flags))
04269         js_printf(jp, "%s ", js_getter_str);
04270     else if (JSFUN_SETTER_TEST(fun->flags))
04271         js_printf(jp, "%s ", js_setter_str);
04272 
04273     js_printf(jp, "%s ", js_function_str);
04274     if (fun->atom && !QuoteString(&jp->sprinter, ATOM_TO_STRING(fun->atom), 0))
04275         return JS_FALSE;
04276     js_puts(jp, "(");
04277 
04278     if (FUN_INTERPRETED(fun) && fun->object) {
04279         size_t paramsize;
04280 #ifdef JS_HAS_DESTRUCTURING
04281         SprintStack ss;
04282         JSScript *oldscript;
04283 #endif
04284 
04285         /*
04286          * Print the parameters.
04287          *
04288          * This code is complicated by the need to handle duplicate parameter
04289          * names, as required by ECMA (bah!).  A duplicate parameter is stored
04290          * as another node with the same id (the parameter name) but different
04291          * shortid (the argument index) along the property tree ancestor line
04292          * starting at SCOPE_LAST_PROP(scope).  Only the last duplicate param
04293          * is mapped by the scope's hash table.
04294          */
04295         cx = jp->sprinter.context;
04296         nargs = fun->nargs;
04297         mark = JS_ARENA_MARK(&cx->tempPool);
04298         paramsize = nargs * sizeof(JSAtom *);
04299         JS_ARENA_ALLOCATE_CAST(params, JSAtom **, &cx->tempPool, paramsize);
04300         if (!params) {
04301             JS_ReportOutOfMemory(cx);
04302             return JS_FALSE;
04303         }
04304 
04305         memset(params, 0, paramsize);
04306         scope = OBJ_SCOPE(fun->object);
04307         for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
04308             if (sprop->getter != js_GetArgument)
04309                 continue;
04310             JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
04311             JS_ASSERT((uint16) sprop->shortid < nargs);
04312             JS_ASSERT(JSID_IS_ATOM(sprop->id));
04313             params[(uint16) sprop->shortid] = JSID_TO_ATOM(sprop->id);
04314         }
04315 
04316         pc = fun->u.i.script->main;
04317         endpc = pc + fun->u.i.script->length;
04318         ok = JS_TRUE;
04319 
04320 #ifdef JS_HAS_DESTRUCTURING
04321         /* Skip JSOP_GENERATOR in case of destructuring parameters. */
04322         if (*pc == JSOP_GENERATOR)
04323             pc += JSOP_GENERATOR_LENGTH;
04324 
04325         ss.printer = NULL;
04326         oldscript = jp->script;
04327         jp->script = fun->u.i.script;
04328         oldscope = jp->scope;
04329         jp->scope = scope;
04330 #endif
04331 
04332         for (i = 0; i < nargs; i++) {
04333             if (i > 0)
04334                 js_puts(jp, ", ");
04335 
04336 #if JS_HAS_DESTRUCTURING
04337 #define LOCAL_ASSERT(expr)      LOCAL_ASSERT_RV(expr, JS_FALSE)
04338 
04339             if (!params[i]) {
04340                 ptrdiff_t todo;
04341                 const char *lval;
04342 
04343                 LOCAL_ASSERT(*pc == JSOP_GETARG);
04344                 pc += JSOP_GETARG_LENGTH;
04345                 LOCAL_ASSERT(*pc == JSOP_DUP);
04346                 if (!ss.printer) {
04347                     ok = InitSprintStack(cx, &ss, jp, fun->u.i.script->depth);
04348                     if (!ok)
04349                         break;
04350                 }
04351                 pc = DecompileDestructuring(&ss, pc, endpc);
04352                 if (!pc) {
04353                     ok = JS_FALSE;
04354                     break;
04355                 }
04356                 LOCAL_ASSERT(*pc == JSOP_POP);
04357                 pc += JSOP_POP_LENGTH;
04358                 lval = PopStr(&ss, JSOP_NOP);
04359                 todo = SprintCString(&jp->sprinter, lval);
04360                 if (todo < 0) {
04361                     ok = JS_FALSE;
04362                     break;
04363                 }
04364                 continue;
04365             }
04366 
04367 #undef LOCAL_ASSERT
04368 #endif
04369 
04370             if (!QuoteString(&jp->sprinter, ATOM_TO_STRING(params[i]), 0)) {
04371                 ok = JS_FALSE;
04372                 break;
04373             }
04374         }
04375 
04376 #ifdef JS_HAS_DESTRUCTURING
04377         jp->script = oldscript;
04378         jp->scope = oldscope;
04379 #endif
04380         JS_ARENA_RELEASE(&cx->tempPool, mark);
04381         if (!ok)
04382             return JS_FALSE;
04383 #ifdef __GNUC__
04384     } else {
04385         scope = NULL;
04386         pc = NULL;
04387 #endif
04388     }
04389 
04390     js_printf(jp, ") {\n");
04391     indent = jp->indent;
04392     jp->indent += 4;
04393     if (FUN_INTERPRETED(fun) && fun->object) {
04394         oldscope = jp->scope;
04395         jp->scope = scope;
04396         len = fun->u.i.script->code + fun->u.i.script->length - pc;
04397         ok = js_DecompileCode(jp, fun->u.i.script, pc, (uintN)len, 0);
04398         jp->scope = oldscope;
04399         if (!ok) {
04400             jp->indent = indent;
04401             return JS_FALSE;
04402         }
04403     } else {
04404         js_printf(jp, native_code_str);
04405     }
04406     jp->indent -= 4;
04407     js_printf(jp, "\t}");
04408 
04409     if (!jp->pretty) {
04410         if (!jp->grouped && (fun->flags & JSFUN_LAMBDA))
04411             js_puts(jp, ")");
04412     }
04413     return JS_TRUE;
04414 }
04415 
04416 #undef LOCAL_ASSERT_RV
04417 
04418 JSString *
04419 js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
04420                            JSString *fallback)
04421 {
04422     JSStackFrame *fp, *down;
04423     jsbytecode *pc, *begin, *end;
04424     jsval *sp, *spbase, *base, *limit;
04425     intN depth, pcdepth;
04426     JSScript *script;
04427     JSOp op;
04428     const JSCodeSpec *cs;
04429     jssrcnote *sn;
04430     ptrdiff_t len, oplen;
04431     JSPrinter *jp;
04432     JSString *name;
04433 
04434     JS_ASSERT(spindex < 0 ||
04435               spindex == JSDVG_IGNORE_STACK ||
04436               spindex == JSDVG_SEARCH_STACK);
04437 
04438     for (fp = cx->fp; fp && !fp->script; fp = fp->down)
04439         continue;
04440     if (!fp)
04441         goto do_fallback;
04442 
04443     /* Try to find sp's generating pc depth slots under it on the stack. */
04444     pc = fp->pc;
04445     sp = fp->sp;
04446     spbase = fp->spbase;
04447     if ((uintN)(sp - spbase) > fp->script->depth) {
04448         /*
04449          * Preparing to make an internal invocation, using an argv stack
04450          * segment pushed just above fp's operand stack space.  Such an argv
04451          * stack has no generating pc "basement", so we must fall back.
04452          */
04453         goto do_fallback;
04454     }
04455 
04456     if (spindex == JSDVG_SEARCH_STACK) {
04457         if (!pc) {
04458             /*
04459              * Current frame is native: look under it for a scripted call
04460              * in which a decompilable bytecode string that generated the
04461              * value as an actual argument might exist.
04462              */
04463             JS_ASSERT(!fp->script && !(fp->fun && FUN_INTERPRETED(fp->fun)));
04464             down = fp->down;
04465             if (!down)
04466                 goto do_fallback;
04467             script = down->script;
04468             spbase = down->spbase;
04469             base = fp->argv;
04470             limit = base + fp->argc;
04471         } else {
04472             /*
04473              * This should be a script activation, either a top-level
04474              * script or a scripted function.  But be paranoid about calls
04475              * to js_DecompileValueGenerator from code that hasn't fully
04476              * initialized a (default-all-zeroes) frame.
04477              */
04478             script = fp->script;
04479             spbase = base = fp->spbase;
04480             limit = fp->sp;
04481         }
04482 
04483         /*
04484          * Pure paranoia about default-zeroed frames being active while
04485          * js_DecompileValueGenerator is called.  It can't hurt much now;
04486          * error reporting performance is not an issue.
04487          */
04488         if (!script || !base || !limit)
04489             goto do_fallback;
04490 
04491         /*
04492          * Try to find operand-generating pc depth slots below sp.
04493          *
04494          * In the native case, we know the arguments have generating pc's
04495          * under them, on account of fp->down->script being non-null: all
04496          * compiled scripts get depth slots for generating pc's allocated
04497          * upon activation, at the top of js_Interpret.
04498          *
04499          * In the script or scripted function case, the same reasoning
04500          * applies to fp rather than to fp->down.
04501          *
04502          * We search from limit to base to find the most recently calculated
04503          * value matching v under assumption that it is it that caused
04504          * exception, see bug 328664.
04505          */
04506         for (sp = limit;;) {
04507             if (sp <= base)
04508                 goto do_fallback;
04509             --sp;
04510             if (*sp == v) {
04511                 depth = (intN)script->depth;
04512                 sp -= depth;
04513                 pc = (jsbytecode *) *sp;
04514                 break;
04515             }
04516         }
04517     } else {
04518         /*
04519          * At this point, pc may or may not be null, i.e., we could be in
04520          * a script activation, or we could be in a native frame that was
04521          * called by another native function.  Check pc and script.
04522          */
04523         if (!pc)
04524             goto do_fallback;
04525         script = fp->script;
04526         if (!script)
04527             goto do_fallback;
04528 
04529         if (spindex != JSDVG_IGNORE_STACK) {
04530             JS_ASSERT(spindex < 0);
04531             depth = (intN)script->depth;
04532 #if !JS_HAS_NO_SUCH_METHOD
04533             JS_ASSERT(-depth <= spindex);
04534 #endif
04535             sp = fp->sp + spindex;
04536             if ((jsuword) (sp - fp->spbase) < (jsuword) depth)
04537                 pc = (jsbytecode *) *(sp - depth);
04538 
04539         }
04540     }
04541 
04542     /*
04543      * Again, be paranoid, this time about possibly loading an invalid pc
04544      * from fp->sp[spindex - script->depth)].
04545      */
04546     if (JS_UPTRDIFF(pc, script->code) >= (jsuword)script->length) {
04547         pc = fp->pc;
04548         if (!pc)
04549             goto do_fallback;
04550     }
04551     op = (JSOp) *pc;
04552     if (op == JSOP_TRAP)
04553         op = JS_GetTrapOpcode(cx, script, pc);
04554 
04555     /* None of these stack-writing ops generates novel values. */
04556     JS_ASSERT(op != JSOP_CASE && op != JSOP_CASEX &&
04557               op != JSOP_DUP && op != JSOP_DUP2 &&
04558               op != JSOP_SWAP);
04559 
04560     /*
04561      * |this| could convert to a very long object initialiser, so cite it by
04562      * its keyword name instead.
04563      */
04564     if (op == JSOP_THIS)
04565         return JS_NewStringCopyZ(cx, js_this_str);
04566 
04567     /*
04568      * JSOP_BINDNAME is special: it generates a value, the base object of a
04569      * reference.  But if it is the generating op for a diagnostic produced by
04570      * js_DecompileValueGenerator, the name being bound is irrelevant.  Just
04571      * fall back to the base object.
04572      */
04573     if (op == JSOP_BINDNAME)
04574         goto do_fallback;
04575 
04576     /* NAME ops are self-contained, others require left or right context. */
04577     cs = &js_CodeSpec[op];
04578     begin = pc;
04579     end = pc + cs->length;
04580     if ((cs->format & JOF_MODEMASK) != JOF_NAME) {
04581         JSSrcNoteType noteType;
04582 
04583         sn = js_GetSrcNote(script, pc);
04584         if (!sn)
04585             goto do_fallback;
04586         noteType = SN_TYPE(sn);
04587         if (noteType == SRC_PCBASE) {
04588             begin -= js_GetSrcNoteOffset(sn, 0);
04589         } else if (noteType == SRC_PCDELTA) {
04590             end = begin + js_GetSrcNoteOffset(sn, 0);
04591             begin += cs->length;
04592         } else {
04593             goto do_fallback;
04594         }
04595     }
04596     len = PTRDIFF(end, begin, jsbytecode);
04597     if (len <= 0)
04598         goto do_fallback;
04599 
04600     /*
04601      * Walk forward from script->main and compute starting stack depth.
04602      * FIXME: Code to compute oplen copied from js_Disassemble1 and reduced.
04603      * FIXME: Optimize to use last empty-stack sequence point.
04604      */
04605     pcdepth = 0;
04606     for (pc = script->main; pc < begin; pc += oplen) {
04607         jsbytecode *pc2;
04608         uint32 type;
04609         intN nuses, ndefs;
04610 
04611         /* Let pc2 be non-null only for JSOP_LITOPX. */
04612         pc2 = NULL;
04613         op = (JSOp) *pc;
04614         if (op == JSOP_TRAP)
04615             op = JS_GetTrapOpcode(cx, script, pc);
04616         cs = &js_CodeSpec[op];
04617         oplen = cs->length;
04618 
04619         if (op == JSOP_SETSP) {
04620             pcdepth = GET_UINT16(pc);
04621             continue;
04622         }
04623 
04624         /*
04625          * A (C ? T : E) expression requires skipping either T (if begin is in
04626          * E) or both T and E (if begin is after the whole expression) before
04627          * adjusting pcdepth based on the JSOP_IFEQ or JSOP_IFEQX at pc that
04628          * tests condition C.  We know that the stack depth can't change from
04629          * what it was with C on top of stack.
04630          */
04631         sn = js_GetSrcNote(script, pc);
04632         if (sn && SN_TYPE(sn) == SRC_COND) {
04633             ptrdiff_t jmpoff, jmplen;
04634 
04635             jmpoff = js_GetSrcNoteOffset(sn, 0);
04636             if (pc + jmpoff < begin) {
04637                 pc += jmpoff;
04638                 op = *pc;
04639                 JS_ASSERT(op == JSOP_GOTO || op == JSOP_GOTOX);
04640                 cs = &js_CodeSpec[op];
04641                 oplen = cs->length;
04642                 jmplen = GetJumpOffset(pc, pc);
04643                 if (pc + jmplen < begin) {
04644                     oplen = (uintN) jmplen;
04645                     continue;
04646                 }
04647 
04648                 /*
04649                  * Ok, begin lies in E.  Manually pop C off the model stack,
04650                  * since we have moved beyond the IFEQ now.
04651                  */
04652                 --pcdepth;
04653             }
04654         }
04655 
04656         type = cs->format & JOF_TYPEMASK;
04657         switch (type) {
04658           case JOF_TABLESWITCH:
04659           case JOF_TABLESWITCHX:
04660           {
04661             jsint jmplen, i, low, high;
04662 
04663             jmplen = (type == JOF_TABLESWITCH) ? JUMP_OFFSET_LEN
04664                                                : JUMPX_OFFSET_LEN;
04665             pc2 = pc;
04666             pc2 += jmplen;
04667             low = GET_JUMP_OFFSET(pc2);
04668             pc2 += JUMP_OFFSET_LEN;
04669             high = GET_JUMP_OFFSET(pc2);
04670             pc2 += JUMP_OFFSET_LEN;
04671             for (i = low; i <= high; i++)
04672                 pc2 += jmplen;
04673             oplen = 1 + pc2 - pc;
04674             break;
04675           }
04676 
04677           case JOF_LOOKUPSWITCH:
04678           case JOF_LOOKUPSWITCHX:
04679           {
04680             jsint jmplen;
04681             jsbytecode *pc2;
04682             jsatomid npairs;
04683 
04684             jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN
04685                                                 : JUMPX_OFFSET_LEN;
04686             pc2 = pc;
04687             pc2 += jmplen;
04688             npairs = GET_ATOM_INDEX(pc2);
04689             pc2 += ATOM_INDEX_LEN;
04690             while (npairs) {
04691                 pc2 += ATOM_INDEX_LEN;
04692                 pc2 += jmplen;
04693                 npairs--;
04694             }
04695             oplen = 1 + pc2 - pc;
04696             break;
04697           }
04698 
04699           case JOF_LITOPX:
04700             pc2 = pc + 1 + LITERAL_INDEX_LEN;
04701             op = *pc2;
04702             cs = &js_CodeSpec[op];
04703             JS_ASSERT(cs->length > ATOM_INDEX_LEN);
04704             oplen += cs->length - (1 + ATOM_INDEX_LEN);
04705             break;
04706 
04707           default:;
04708         }
04709 
04710         if (sn && SN_TYPE(sn) == SRC_HIDDEN)
04711             continue;
04712 
04713         nuses = cs->nuses;
04714         if (nuses < 0) {
04715             /* Call opcode pushes [callee, this, argv...]. */
04716             nuses = 2 + GET_ARGC(pc);
04717         } else if (op == JSOP_RETSUB) {
04718             /* Pop [exception or hole, retsub pc-index]. */
04719             JS_ASSERT(nuses == 0);
04720             nuses = 2;
04721         } else if (op == JSOP_LEAVEBLOCK || op == JSOP_LEAVEBLOCKEXPR) {
04722             JS_ASSERT(nuses == 0);
04723             nuses = GET_UINT16(pc);
04724         }
04725         pcdepth -= nuses;
04726         JS_ASSERT(pcdepth >= 0);
04727 
04728         ndefs = cs->ndefs;
04729         if (op == JSOP_FINALLY) {
04730             /* Push [exception or hole, retsub pc-index]. */
04731             JS_ASSERT(ndefs == 0);
04732             ndefs = 2;
04733         } else if (op == JSOP_ENTERBLOCK) {
04734             jsatomid atomIndex;
04735             JSAtom *atom;
04736             JSObject *obj;
04737 
04738             JS_ASSERT(ndefs == 0);
04739             atomIndex = pc2 ? GET_LITERAL_INDEX(pc) : GET_ATOM_INDEX(pc);
04740             atom = js_GetAtom(cx, &script->atomMap, atomIndex);
04741             obj = ATOM_TO_OBJECT(atom);
04742             JS_ASSERT(OBJ_BLOCK_DEPTH(cx, obj) == pcdepth);
04743             ndefs = OBJ_BLOCK_COUNT(cx, obj);
04744         }
04745         pcdepth += ndefs;
04746     }
04747 
04748     name = NULL;
04749     jp = js_NewPrinter(cx, "js_DecompileValueGenerator", 0, JS_FALSE);
04750     if (jp) {
04751         if (fp->fun && fp->fun->object) {
04752             JS_ASSERT(OBJ_IS_NATIVE(fp->fun->object));
04753             jp->scope = OBJ_SCOPE(fp->fun->object);
04754         }
04755         jp->dvgfence = end;
04756         if (js_DecompileCode(jp, script, begin, (uintN)len, (uintN)pcdepth))
04757             name = js_GetPrinterOutput(jp);
04758         js_DestroyPrinter(jp);
04759     }
04760     return name;
04761 
04762   do_fallback:
04763     return fallback ? fallback : js_ValueToSource(cx, v);
04764 }