Code

moving trunk for module inkscape
[inkscape.git] / src / dom / js / jsopcode.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is Mozilla Communicator client code, released
17  * March 31, 1998.
18  *
19  * The Initial Developer of the Original Code is
20  * Netscape Communications Corporation.
21  * Portions created by the Initial Developer are Copyright (C) 1998
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either of the GNU General Public License Version 2 or later (the "GPL"),
28  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the MPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the MPL, the GPL or the LGPL.
37  *
38  * ***** END LICENSE BLOCK ***** */
40 /*
41  * JS bytecode descriptors, disassemblers, and decompilers.
42  */
43 #include "jsstddef.h"
44 #ifdef HAVE_MEMORY_H
45 #include <memory.h>
46 #endif
47 #include <stdarg.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include "jstypes.h"
52 #include "jsarena.h" /* Added by JSIFY */
53 #include "jsutil.h" /* Added by JSIFY */
54 #include "jsdtoa.h"
55 #include "jsprf.h"
56 #include "jsapi.h"
57 #include "jsarray.h"
58 #include "jsatom.h"
59 #include "jscntxt.h"
60 #include "jsconfig.h"
61 #include "jsdbgapi.h"
62 #include "jsemit.h"
63 #include "jsfun.h"
64 #include "jslock.h"
65 #include "jsobj.h"
66 #include "jsopcode.h"
67 #include "jsscope.h"
68 #include "jsscript.h"
69 #include "jsstr.h"
71 const char js_const_str[]       = "const";
72 const char js_var_str[]         = "var";
73 const char js_function_str[]    = "function";
74 const char js_in_str[]          = "in";
75 const char js_instanceof_str[]  = "instanceof";
76 const char js_new_str[]         = "new";
77 const char js_delete_str[]      = "delete";
78 const char js_typeof_str[]      = "typeof";
79 const char js_void_str[]        = "void";
80 const char js_null_str[]        = "null";
81 const char js_this_str[]        = "this";
82 const char js_false_str[]       = "false";
83 const char js_true_str[]        = "true";
85 const char *js_incop_str[]      = {"++", "--"};
87 /* Pollute the namespace locally for MSVC Win16, but not for WatCom.  */
88 #ifdef __WINDOWS_386__
89     #ifdef FAR
90         #undef FAR
91     #endif
92 #else  /* !__WINDOWS_386__ */
93 #ifndef FAR
94 #define FAR
95 #endif
96 #endif /* !__WINDOWS_386__ */
98 const JSCodeSpec FAR js_CodeSpec[] = {
99 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
100     {name,token,length,nuses,ndefs,prec,format},
101 #include "jsopcode.tbl"
102 #undef OPDEF
103 };
105 uintN js_NumCodeSpecs = sizeof (js_CodeSpec) / sizeof js_CodeSpec[0];
107 /************************************************************************/
109 static ptrdiff_t
110 GetJumpOffset(jsbytecode *pc, jsbytecode *pc2)
112     uint32 type;
113     
114     type = (js_CodeSpec[*pc].format & JOF_TYPEMASK);
115     if (JOF_TYPE_IS_EXTENDED_JUMP(type))
116         return GET_JUMPX_OFFSET(pc2);
117     return GET_JUMP_OFFSET(pc2);
120 #ifdef DEBUG
122 JS_FRIEND_API(void)
123 js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp)
125     jsbytecode *pc, *end;
126     uintN len;
128     pc = script->code;
129     end = pc + script->length;
130     while (pc < end) {
131         if (pc == script->main)
132             fputs("main:\n", fp);
133         len = js_Disassemble1(cx, script, pc,
134                               PTRDIFF(pc, script->code, jsbytecode),
135                               lines, fp);
136         if (!len)
137             return;
138         pc += len;
139     }
142 JS_FRIEND_API(uintN)
143 js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
144                 JSBool lines, FILE *fp)
146     JSOp op;
147     const JSCodeSpec *cs;
148     intN len, off;
149     JSAtom *atom;
150     JSString *str;
151     char *cstr;
153     op = (JSOp)*pc;
154     if (op >= JSOP_LIMIT) {
155         char numBuf1[12], numBuf2[12];
156         JS_snprintf(numBuf1, sizeof numBuf1, "%d", op);
157         JS_snprintf(numBuf2, sizeof numBuf2, "%d", JSOP_LIMIT);
158         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
159                              JSMSG_BYTECODE_TOO_BIG, numBuf1, numBuf2);
160         return 0;
161     }
162     cs = &js_CodeSpec[op];
163     len = (intN)cs->length;
164     fprintf(fp, "%05u:", loc);
165     if (lines)
166         fprintf(fp, "%4u", JS_PCToLineNumber(cx, script, pc));
167     fprintf(fp, "  %s", cs->name);
168     switch (cs->format & JOF_TYPEMASK) {
169       case JOF_BYTE:
170         if (op == JSOP_TRAP) {
171             op = JS_GetTrapOpcode(cx, script, pc);
172             if (op == JSOP_LIMIT)
173                 return 0;
174             len = (intN)js_CodeSpec[op].length;
175         }
176         break;
178       case JOF_JUMP:
179       case JOF_JUMPX:
180         off = GetJumpOffset(pc, pc);
181         fprintf(fp, " %u (%d)", loc + off, off);
182         break;
184       case JOF_CONST:
185         atom = GET_ATOM(cx, script, pc);
186         str = js_ValueToSource(cx, ATOM_KEY(atom));
187         if (!str)
188             return 0;
189         cstr = js_DeflateString(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str));
190         if (!cstr)
191             return 0;
192         fprintf(fp, " %s", cstr);
193         JS_free(cx, cstr);
194         break;
196       case JOF_UINT16:
197         fprintf(fp, " %u", GET_ARGC(pc));
198         break;
200 #if JS_HAS_SWITCH_STATEMENT
201       case JOF_TABLESWITCH:
202       {
203         jsbytecode *pc2;
204         jsint i, low, high;
206         pc2 = pc;
207         off = GetJumpOffset(pc, pc2);
208         pc2 += JUMP_OFFSET_LEN;
209         low = GetJumpOffset(pc, pc2);
210         pc2 += JUMP_OFFSET_LEN;
211         high = GetJumpOffset(pc, pc2);
212         pc2 += JUMP_OFFSET_LEN;
213         fprintf(fp, " defaultOffset %d low %d high %d", off, low, high);
214         for (i = low; i <= high; i++) {
215             off = GetJumpOffset(pc, pc2);
216             fprintf(fp, "\n\t%d: %d", i, off);
217             pc2 += JUMP_OFFSET_LEN;
218         }
219         len = 1 + pc2 - pc;
220         break;
221       }
223       case JOF_LOOKUPSWITCH:
224       {
225         jsbytecode *pc2;
226         jsint npairs;
228         pc2 = pc;
229         off = GetJumpOffset(pc, pc2);
230         pc2 += JUMP_OFFSET_LEN;
231         npairs = (jsint) GET_ATOM_INDEX(pc2);
232         pc2 += ATOM_INDEX_LEN;
233         fprintf(fp, " offset %d npairs %u", off, (uintN) npairs);
234         while (npairs) {
235             atom = GET_ATOM(cx, script, pc2);
236             pc2 += ATOM_INDEX_LEN;
237             off = GetJumpOffset(pc, pc2);
238             pc2 += JUMP_OFFSET_LEN;
240             str = js_ValueToSource(cx, ATOM_KEY(atom));
241             if (!str)
242                 return 0;
243             cstr = js_DeflateString(cx, JSSTRING_CHARS(str),
244                                     JSSTRING_LENGTH(str));
245             if (!cstr)
246                 return 0;
247             fprintf(fp, "\n\t%s: %d", cstr, off);
248             JS_free(cx, cstr);
249             npairs--;
250         }
251         len = 1 + pc2 - pc;
252         break;
253       }
254 #endif /* JS_HAS_SWITCH_STATEMENT */
256       case JOF_QARG:
257         fprintf(fp, " %u", GET_ARGNO(pc));
258         break;
260       case JOF_QVAR:
261         fprintf(fp, " %u", GET_VARNO(pc));
262         break;
264 #if JS_HAS_LEXICAL_CLOSURE
265       case JOF_DEFLOCALVAR:
266         fprintf(fp, " %u", GET_VARNO(pc));
267         pc += VARNO_LEN;
268         atom = GET_ATOM(cx, script, pc);
269         str = js_ValueToSource(cx, ATOM_KEY(atom));
270         if (!str)
271             return 0;
272         cstr = js_DeflateString(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str));
273         if (!cstr)
274             return 0;
275         fprintf(fp, " %s", cstr);
276         JS_free(cx, cstr);
277         break;
278 #endif
280       default: {
281         char numBuf[12];
282         JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) cs->format);
283         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
284                              JSMSG_UNKNOWN_FORMAT, numBuf);
285         return 0;
286       }
287     }
288     fputs("\n", fp);
289     return len;
292 #endif /* DEBUG */
294 /************************************************************************/
296 /*
297  * Sprintf, but with unlimited and automatically allocated buffering.
298  */
299 typedef struct Sprinter {
300     JSContext       *context;       /* context executing the decompiler */
301     JSArenaPool     *pool;          /* string allocation pool */
302     char            *base;          /* base address of buffer in pool */
303     size_t          size;           /* size of buffer allocated at base */
304     ptrdiff_t       offset;         /* offset of next free char in buffer */
305 } Sprinter;
307 #define INIT_SPRINTER(cx, sp, ap, off) \
308     ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0,  \
309      (sp)->offset = off)
311 #define OFF2STR(sp,off) ((sp)->base + (off))
312 #define STR2OFF(sp,str) ((str) - (sp)->base)
313 #define RETRACT(sp,str) ((sp)->offset = STR2OFF(sp, str))
315 static JSBool
316 SprintAlloc(Sprinter *sp, size_t nb)
318     if (!sp->base) {
319         JS_ARENA_ALLOCATE_CAST(sp->base, char *, sp->pool, nb);
320     } else {
321         JS_ARENA_GROW_CAST(sp->base, char *, sp->pool, sp->size, nb);
322     }
323     if (!sp->base) {
324         JS_ReportOutOfMemory(sp->context);
325         return JS_FALSE;
326     }
327     sp->size += nb;
328     return JS_TRUE;
331 static ptrdiff_t
332 SprintPut(Sprinter *sp, const char *s, size_t len)
334     ptrdiff_t nb, offset;
335     char *bp;
337     /* Allocate space for s, including the '\0' at the end. */
338     nb = (sp->offset + len + 1) - sp->size;
339     if (nb > 0 && !SprintAlloc(sp, nb))
340         return -1;
342     /* Advance offset and copy s into sp's buffer. */
343     offset = sp->offset;
344     sp->offset += len;
345     bp = sp->base + offset;
346     memmove(bp, s, len);
347     bp[len] = 0;
348     return offset;
351 static ptrdiff_t
352 Sprint(Sprinter *sp, const char *format, ...)
354     va_list ap;
355     char *bp;
356     ptrdiff_t offset;
358     va_start(ap, format);
359     bp = JS_vsmprintf(format, ap);      /* XXX vsaprintf */
360     va_end(ap);
361     if (!bp) {
362         JS_ReportOutOfMemory(sp->context);
363         return -1;
364     }
365     offset = SprintPut(sp, bp, strlen(bp));
366     free(bp);
367     return offset;
370 const jschar js_EscapeMap[] = {
371     '\b', 'b',
372     '\f', 'f',
373     '\n', 'n',
374     '\r', 'r',
375     '\t', 't',
376     '\v', 'v',
377     '"',  '"',
378     '\'', '\'',
379     '\\', '\\',
380     0
381 };
383 static char *
384 QuoteString(Sprinter *sp, JSString *str, jschar quote)
386     ptrdiff_t off, len, nb;
387     const jschar *s, *t, *u, *z;
388     char *bp;
389     jschar c;
390     JSBool ok;
392     /* Sample off first for later return value pointer computation. */
393     off = sp->offset;
394     if (quote && Sprint(sp, "%c", (char)quote) < 0)
395         return NULL;
397     /* Loop control variables: z points at end of string sentinel. */
398     s = JSSTRING_CHARS(str);
399     z = s + JSSTRING_LENGTH(str);
400     for (t = s; t < z; s = ++t) {
401         /* Move t forward from s past un-quote-worthy characters. */
402         c = *t;
403         while (JS_ISPRINT(c) && c != quote && c != '\\' && !(c >> 8)) {
404             c = *++t;
405             if (t == z)
406                 break;
407         }
408         len = PTRDIFF(t, s, jschar);
410         /* Allocate space for s, including the '\0' at the end. */
411         nb = (sp->offset + len + 1) - sp->size;
412         if (nb > 0 && !SprintAlloc(sp, nb))
413             return NULL;
415         /* Advance sp->offset and copy s into sp's buffer. */
416         bp = sp->base + sp->offset;
417         sp->offset += len;
418         while (--len >= 0)
419             *bp++ = (char) *s++;
420         *bp = '\0';
422         if (t == z)
423             break;
425         /* Use js_EscapeMap, \u, or \x only if necessary. */
426         if ((u = js_strchr(js_EscapeMap, c)) != NULL)
427             ok = Sprint(sp, "\\%c", (char)u[1]) >= 0;
428         else
429             ok = Sprint(sp, (c >> 8) ? "\\u%04X" : "\\x%02X", c) >= 0;
430         if (!ok)
431             return NULL;
432     }
434     /* Sprint the closing quote and return the quoted string. */
435     if (quote && Sprint(sp, "%c", (char)quote) < 0)
436         return NULL;
437     return OFF2STR(sp, off);
440 JSString *
441 js_QuoteString(JSContext *cx, JSString *str, jschar quote)
443     void *mark;
444     Sprinter sprinter;
445     char *bytes;
446     JSString *escstr;
448     mark = JS_ARENA_MARK(&cx->tempPool);
449     INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);
450     bytes = QuoteString(&sprinter, str, quote);
451     escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL;
452     JS_ARENA_RELEASE(&cx->tempPool, mark);
453     return escstr;
456 /************************************************************************/
458 struct JSPrinter {
459     Sprinter        sprinter;       /* base class state */
460     JSArenaPool     pool;           /* string allocation pool */
461     uintN           indent;         /* indentation in spaces */
462     JSBool          pretty;         /* pretty-print: indent, use newlines */
463     JSScript        *script;        /* script being printed */
464     JSScope         *scope;         /* script function scope */
465 };
467 JSPrinter *
468 js_NewPrinter(JSContext *cx, const char *name, uintN indent, JSBool pretty)
470     JSPrinter *jp;
471     JSStackFrame *fp;
472     JSObjectMap *map;
474     jp = (JSPrinter *) JS_malloc(cx, sizeof(JSPrinter));
475     if (!jp)
476         return NULL;
477     INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
478     JS_InitArenaPool(&jp->pool, name, 256, 1);
479     jp->indent = indent;
480     jp->pretty = pretty;
481     jp->script = NULL;
482     jp->scope = NULL;
483     fp = cx->fp;
484     if (fp && fp->fun && fp->fun->object) {
485         map = fp->fun->object->map;
486         if (MAP_IS_NATIVE(map))
487             jp->scope = (JSScope *)map;
488     }
489     return jp;
492 void
493 js_DestroyPrinter(JSPrinter *jp)
495     JS_FinishArenaPool(&jp->pool);
496     JS_free(jp->sprinter.context, jp);
499 JSString *
500 js_GetPrinterOutput(JSPrinter *jp)
502     JSContext *cx;
503     JSString *str;
505     cx = jp->sprinter.context;
506     if (!jp->sprinter.base)
507         return cx->runtime->emptyString;
508     str = JS_NewStringCopyZ(cx, jp->sprinter.base);
509     if (!str)
510         return NULL;
511     JS_FreeArenaPool(&jp->pool);
512     INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
513     return str;
516 int
517 js_printf(JSPrinter *jp, const char *format, ...)
519     va_list ap;
520     char *bp, *fp;
521     int cc;
523     if (*format == '\0')
524         return 0;
526     va_start(ap, format);
528     /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */
529     if (*format == '\t') {
530         if (jp->pretty && Sprint(&jp->sprinter, "%*s", jp->indent, "") < 0)
531             return -1;
532         format++;
533     }
535     /* Suppress newlines (must be once per format, at the end) if not pretty. */
536     fp = NULL;
537     if (!jp->pretty && format[cc = strlen(format)-1] == '\n') {
538         fp = JS_strdup(jp->sprinter.context, format);
539         if (!fp)
540             return -1;
541         fp[cc] = '\0';
542         format = fp;
543     }
545     /* Allocate temp space, convert format, and put. */
546     bp = JS_vsmprintf(format, ap);      /* XXX vsaprintf */
547     if (fp) {
548         JS_free(jp->sprinter.context, fp);
549         format = NULL;
550     }
551     if (!bp) {
552         JS_ReportOutOfMemory(jp->sprinter.context);
553         return -1;
554     }
556     cc = strlen(bp);
557     if (SprintPut(&jp->sprinter, bp, (size_t)cc) < 0)
558         cc = -1;
559     free(bp);
561     va_end(ap);
562     return cc;
565 JSBool
566 js_puts(JSPrinter *jp, const char *s)
568     return SprintPut(&jp->sprinter, s, strlen(s)) >= 0;
571 /************************************************************************/
573 typedef struct SprintStack {
574     Sprinter    sprinter;       /* sprinter for postfix to infix buffering */
575     ptrdiff_t   *offsets;       /* stack of postfix string offsets */
576     jsbytecode  *opcodes;       /* parallel stack of JS opcodes */
577     uintN       top;            /* top of stack index */
578     JSPrinter   *printer;       /* permanent output goes here */
579 } SprintStack;
581 /* Gap between stacked strings to allow for insertion of parens and commas. */
582 #define PAREN_SLOP      (2 + 1)
584 /*
585  * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME,
586  * JSOP_SETPROP, and JSOP_SETELEM, respectively.  See the first assertion in
587  * PushOff.
588  */
589 #define JSOP_GETPROP2   254
590 #define JSOP_GETELEM2   255
592 static JSBool
593 PushOff(SprintStack *ss, ptrdiff_t off, JSOp op)
595     uintN top;
597 #if JSOP_LIMIT > JSOP_GETPROP2
598 #error JSOP_LIMIT must be <= JSOP_GETPROP2
599 #endif
600     if (!SprintAlloc(&ss->sprinter, PAREN_SLOP))
601         return JS_FALSE;
603     /* ss->top points to the next free slot; be paranoid about overflow. */
604     top = ss->top;
605     JS_ASSERT(top < ss->printer->script->depth);
606     if (top >= ss->printer->script->depth) {
607         JS_ReportOutOfMemory(ss->sprinter.context);
608         return JS_FALSE;
609     }
611     /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */
612     ss->offsets[top] = off;
613     ss->opcodes[top] = (op == JSOP_GETPROP2) ? JSOP_GETPROP
614                      : (op == JSOP_GETELEM2) ? JSOP_GETELEM
615                      : (jsbytecode) op;
616     ss->top = ++top;
617     ss->sprinter.offset += PAREN_SLOP;
618     return JS_TRUE;
621 static ptrdiff_t
622 PopOff(SprintStack *ss, JSOp op)
624     uintN top;
625     const JSCodeSpec *cs, *topcs;
626     ptrdiff_t off;
628     /* ss->top points to the next free slot; be paranoid about underflow. */
629     top = ss->top;
630     JS_ASSERT(top != 0);
631     if (top == 0)
632         return 0;
634     ss->top = --top;
635     topcs = &js_CodeSpec[ss->opcodes[top]];
636     cs = &js_CodeSpec[op];
637     if (topcs->prec != 0 && topcs->prec < cs->prec) {
638         ss->offsets[top] -= 2;
639         ss->sprinter.offset = ss->offsets[top];
640         off = Sprint(&ss->sprinter, "(%s)",
641                      OFF2STR(&ss->sprinter, ss->sprinter.offset + 2));
642     } else {
643         off = ss->sprinter.offset = ss->offsets[top];
644     }
645     return off;
648 #if JS_HAS_SWITCH_STATEMENT
649 typedef struct TableEntry {
650     jsval       key;
651     ptrdiff_t   offset;
652 } TableEntry;
654 static int
655 CompareOffsets(const void *v1, const void *v2, void *arg)
657     const TableEntry *te1 = (const TableEntry *) v1,
658                      *te2 = (const TableEntry *) v2;
660     return te1->offset - te2->offset;
663 static JSBool
664 Decompile(SprintStack *ss, jsbytecode *pc, intN nb);
666 static JSBool
667 DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength,
668                 jsbytecode *pc, ptrdiff_t switchLength,
669                 ptrdiff_t defaultOffset, JSBool isCondSwitch)
671     JSContext *cx;
672     JSPrinter *jp;
673     char *lval, *rval;
674     uintN i;
675     ptrdiff_t diff, off, off2, caseExprOff;
676     jsval key;
677     JSString *str;
679     cx = ss->sprinter.context;
680     jp = ss->printer;
682     lval = OFF2STR(&ss->sprinter, PopOff(ss, JSOP_NOP));
683     js_printf(jp, "\tswitch (%s) {\n", lval);
685     if (tableLength) {
686         diff = table[0].offset - defaultOffset;
687         if (diff > 0) {
688             jp->indent += 2;
689             js_printf(jp, "\tdefault:\n");
690             jp->indent += 2;
691             if (!Decompile(ss, pc + defaultOffset, diff))
692                 return JS_FALSE;
693             jp->indent -= 4;
694         }
696         caseExprOff = isCondSwitch
697                       ? (ptrdiff_t) js_CodeSpec[JSOP_CONDSWITCH].length
698                       : 0;
700         for (i = 0; i < tableLength; i++) {
701             off = table[i].offset;
702             if (i + 1 < tableLength)
703                 off2 = table[i + 1].offset;
704             else
705                 off2 = switchLength;
707             key = table[i].key;
708             if (isCondSwitch) {
709                 ptrdiff_t nextCaseExprOff;
711                 /*
712                  * key encodes the JSOP_CASE bytecode's offset from switchtop.
713                  * The next case expression follows immediately, unless we are
714                  * at the last case.
715                  */
716                 nextCaseExprOff = (ptrdiff_t)JSVAL_TO_INT(key);
717                 nextCaseExprOff += js_CodeSpec[pc[nextCaseExprOff]].length;
718                 jp->indent += 2;
719                 if (!Decompile(ss, pc + caseExprOff,
720                                nextCaseExprOff - caseExprOff)) {
721                     return JS_FALSE;
722                 }
723                 caseExprOff = nextCaseExprOff;
724            } else {
725                 /*
726                  * key comes from an atom, not the decompiler, so we need to
727                  * quote it if it's a string literal.
728                  */
729                 str = js_ValueToString(cx, key);
730                 if (!str)
731                     return JS_FALSE;
732                 jp->indent += 2;
733                 if (JSVAL_IS_STRING(key)) {
734                     rval = QuoteString(&ss->sprinter, str, (jschar)'"');
735                     if (!rval)
736                         return JS_FALSE;
737                     RETRACT(&ss->sprinter, rval);
738                 } else {
739                     rval = JS_GetStringBytes(str);
740                 }
741                 js_printf(jp, "\tcase %s:\n", rval);
742             }
744             jp->indent += 2;
745             if (off <= defaultOffset && defaultOffset < off2) {
746                 diff = defaultOffset - off;
747                 if (diff != 0) {
748                     if (!Decompile(ss, pc + off, diff))
749                         return JS_FALSE;
750                     off = defaultOffset;
751                 }
752                 jp->indent -= 2;
753                 js_printf(jp, "\tdefault:\n");
754                 jp->indent += 2;
755             }
756             if (!Decompile(ss, pc + off, off2 - off))
757                 return JS_FALSE;
758             jp->indent -= 4;
759         }
760     }
762     if (defaultOffset == switchLength) {
763         jp->indent += 2;
764         js_printf(jp, "\tdefault:\n");
765         jp->indent -= 2;
766     }
767     js_printf(jp, "\t}\n");
768     return JS_TRUE;
770 #endif
772 static JSAtom *
773 GetSlotAtom(JSPrinter *jp, JSPropertyOp getter, uintN slot)
775     JSScope *scope;
776     JSScopeProperty *sprop;
777     JSObject *obj, *proto;
779     scope = jp->scope;
780     while (scope) {
781         for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
782             if (sprop->getter != getter)
783                 continue;
784             JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
785             JS_ASSERT(!JSVAL_IS_INT(sprop->id));
786             if ((uintN) sprop->shortid == slot)
787                 return (JSAtom *) sprop->id;
788         }
789         obj = scope->object;
790         if (!obj)
791             break;
792         proto = OBJ_GET_PROTO(jp->sprinter.context, obj);
793         if (!proto)
794             break;
795         scope = OBJ_SCOPE(proto);
796     }
797     return NULL;
800 static const char *
801 VarPrefix(jssrcnote *sn)
803     const char *kw;
804     static char buf[8];
806     kw = NULL;
807     if (sn) {
808         if (SN_TYPE(sn) == SRC_VAR)
809             kw = js_var_str;
810         else if (SN_TYPE(sn) == SRC_CONST)
811             kw = js_const_str;
812     }
813     if (!kw)
814         return "";
815     JS_snprintf(buf, sizeof buf, "%s ", kw);
816     return buf;
819 static JSBool
820 Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
822     JSContext *cx;
823     JSPrinter *jp, *jp2;
824     jsbytecode *endpc, *done, *forelem_tail, *forelem_done;
825     ptrdiff_t len, todo, oplen, cond, next, tail;
826     JSOp op, lastop, saveop;
827     const JSCodeSpec *cs, *topcs;
828     jssrcnote *sn, *sn2;
829     const char *lval, *rval, *xval, *fmt;
830     jsint i, argc;
831     char **argv;
832     JSAtom *atom;
833     JSObject *obj;
834     JSFunction *fun;
835     JSString *str;
836     JSBool ok;
837     jsval val;
839 /*
840  * Local macros
841  */
842 #define DECOMPILE_CODE(pc,nb)   if (!Decompile(ss, pc, nb)) return JS_FALSE
843 #define POP_STR()               OFF2STR(&ss->sprinter, PopOff(ss, op))
844 #define LOCAL_ASSERT(expr)      JS_ASSERT(expr); if (!(expr)) return JS_FALSE
846 /*
847  * Get atom from script's atom map, quote/escape its string appropriately into
848  * rval, and select fmt from the quoted and unquoted alternatives.
849  */
850 #define GET_ATOM_QUOTE_AND_FMT(qfmt, ufmt, rval)                              \
851     JS_BEGIN_MACRO                                                            \
852         jschar quote_;                                                        \
853         atom = GET_ATOM(cx, jp->script, pc);                                  \
854         if (ATOM_KEYWORD(atom)) {                                             \
855             quote_ = '\'';                                                    \
856             fmt = qfmt;                                                       \
857         } else {                                                              \
858             quote_ = 0;                                                       \
859             fmt = ufmt;                                                       \
860         }                                                                     \
861         rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), quote_);      \
862         if (!rval)                                                            \
863             return JS_FALSE;                                                  \
864     JS_END_MACRO
866     cx = ss->sprinter.context;
867     jp = ss->printer;
868     endpc = pc + nb;
869     forelem_tail = forelem_done = NULL;
870     todo = -2;                  /* NB: different from Sprint() error return. */
871     tail = -1;
872     op = JSOP_NOP;
873     sn = NULL;
874     rval = NULL;
876     while (pc < endpc) {
877         lastop = op;
878         op = saveop = (JSOp) *pc;
879         if (op >= JSOP_LIMIT) {
880             switch (op) {
881               case JSOP_GETPROP2:
882                 saveop = JSOP_GETPROP;
883                 break;
884               case JSOP_GETELEM2:
885                 saveop = JSOP_GETELEM;
886                 break;
887               default:;
888             }
889         }
890         cs = &js_CodeSpec[saveop];
891         len = oplen = cs->length;
893         if (cs->token) {
894             switch (cs->nuses) {
895               case 2:
896                 rval = POP_STR();
897                 lval = POP_STR();
898                 sn = js_GetSrcNote(jp->script, pc);
899                 if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
900                     /* Print only the right operand of the assignment-op. */
901                     todo = SprintPut(&ss->sprinter, rval, strlen(rval));
902                 } else {
903                     todo = Sprint(&ss->sprinter, "%s %s %s",
904                                   lval, cs->token, rval);
905                 }
906                 break;
908               case 1:
909                 rval = POP_STR();
910                 todo = Sprint(&ss->sprinter, "%s%s", cs->token, rval);
911                 break;
913               case 0:
914 #if JS_HAS_GETTER_SETTER
915                 if (op == JSOP_GETTER || op == JSOP_SETTER) {
916                     todo = -2;
917                     break;
918                 }
919 #endif
920                 todo = SprintPut(&ss->sprinter, cs->token, strlen(cs->token));
921                 break;
923               default:
924                 todo = -2;
925                 break;
926             }
927         } else {
928             switch (op) {
929               case JSOP_NOP:
930                 /*
931                  * Check for a do-while loop, a for-loop with an empty
932                  * initializer part, a labeled statement, a function
933                  * definition, or try/finally.
934                  */
935                 sn = js_GetSrcNote(jp->script, pc);
936                 todo = -2;
937                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
938 #if JS_HAS_DO_WHILE_LOOP
939                   case SRC_WHILE:
940                     js_printf(jp, "\tdo {\n");
941                     jp->indent += 4;
942                     break;
943 #endif /* JS_HAS_DO_WHILE_LOOP */
945                   case SRC_FOR:
946                     rval = "";
948                   do_forloop:
949                     /* Skip the JSOP_NOP or JSOP_POP bytecode. */
950                     pc++;
952                     /* Get the cond, next, and loop-closing tail offsets. */
953                     cond = js_GetSrcNoteOffset(sn, 0);
954                     next = js_GetSrcNoteOffset(sn, 1);
955                     tail = js_GetSrcNoteOffset(sn, 2);
956                     LOCAL_ASSERT(tail + GetJumpOffset(pc+tail, pc+tail) == 0);
958                     /* Print the keyword and the possibly empty init-part. */
959                     js_printf(jp, "\tfor (%s;", rval);
961                     if (pc[cond] == JSOP_IFEQ || pc[cond] == JSOP_IFEQX) {
962                         /* Decompile the loop condition. */
963                         DECOMPILE_CODE(pc, cond);
964                         js_printf(jp, " %s", POP_STR());
965                     }
967                     /* Need a semicolon whether or not there was a cond. */
968                     js_puts(jp, ";");
970                     if (pc[next] != JSOP_GOTO && pc[next] != JSOP_GOTOX) {
971                         /* Decompile the loop updater. */
972                         DECOMPILE_CODE(pc + next, tail - next - 1);
973                         js_printf(jp, " %s", POP_STR());
974                     }
976                     /* Do the loop body. */
977                     js_puts(jp, ") {\n");
978                     jp->indent += 4;
979                     oplen = (cond) ? js_CodeSpec[pc[cond]].length : 0;
980                     DECOMPILE_CODE(pc + cond + oplen, next - cond - oplen);
981                     jp->indent -= 4;
982                     js_printf(jp, "\t}\n");
984                     /* Set len so pc skips over the entire loop. */
985                     len = tail + js_CodeSpec[pc[tail]].length;
986                     break;
988                   case SRC_LABEL:
989                     atom = js_GetAtom(cx, &jp->script->atomMap,
990                                       (jsatomid) js_GetSrcNoteOffset(sn, 0));
991                     jp->indent -= 4;
992                     rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
993                     if (!rval)
994                         return JS_FALSE;
995                     RETRACT(&ss->sprinter, rval);
996                     js_printf(jp, "\t%s:\n", rval);
997                     jp->indent += 4;
998                     break;
1000                   case SRC_LABELBRACE:
1001                     atom = js_GetAtom(cx, &jp->script->atomMap,
1002                                       (jsatomid) js_GetSrcNoteOffset(sn, 0));
1003                     rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1004                     if (!rval)
1005                         return JS_FALSE;
1006                     RETRACT(&ss->sprinter, rval);
1007                     js_printf(jp, "\t%s: {\n", rval);
1008                     jp->indent += 4;
1009                     break;
1011                   case SRC_ENDBRACE:
1012                     jp->indent -= 4;
1013                     js_printf(jp, "\t}\n");
1014                     break;
1016                   case SRC_CATCH:
1017                     jp->indent -= 4;
1018                     sn = js_GetSrcNote(jp->script, pc);
1019                     pc += oplen;
1020                     js_printf(jp, "\t} catch (");
1022                     LOCAL_ASSERT(*pc == JSOP_NAME);
1023                     pc += js_CodeSpec[JSOP_NAME].length;
1024                     LOCAL_ASSERT(*pc == JSOP_PUSHOBJ);
1025                     pc += js_CodeSpec[JSOP_PUSHOBJ].length;
1026                     LOCAL_ASSERT(*pc == JSOP_NEWINIT);
1027                     pc += js_CodeSpec[JSOP_NEWINIT].length;
1028                     LOCAL_ASSERT(*pc == JSOP_EXCEPTION);
1029                     pc += js_CodeSpec[JSOP_EXCEPTION].length;
1030                     LOCAL_ASSERT(*pc == JSOP_INITCATCHVAR);
1031                     atom = GET_ATOM(cx, jp->script, pc);
1032                     rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1033                     if (!rval)
1034                         return JS_FALSE;
1035                     RETRACT(&ss->sprinter, rval);
1036                     js_printf(jp, "%s", rval);
1037                     pc += js_CodeSpec[JSOP_INITCATCHVAR].length;
1038                     LOCAL_ASSERT(*pc == JSOP_ENTERWITH);
1039                     pc += js_CodeSpec[JSOP_ENTERWITH].length;
1041                     len = js_GetSrcNoteOffset(sn, 0);
1042                     if (len) {
1043                         js_printf(jp, " if ");
1044                         DECOMPILE_CODE(pc, len);
1045                         js_printf(jp, "%s", POP_STR());
1046                         pc += len;
1047                         LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
1048                         pc += js_CodeSpec[*pc].length;
1049                     }
1051                     js_printf(jp, ") {\n");
1052                     jp->indent += 4;
1053                     len = 0;
1054                     break;
1056                   case SRC_FUNCDEF:
1057                     atom = js_GetAtom(cx, &jp->script->atomMap,
1058                                       (jsatomid) js_GetSrcNoteOffset(sn, 0));
1059                     JS_ASSERT(ATOM_IS_OBJECT(atom));
1060                   do_function:
1061                     obj = ATOM_TO_OBJECT(atom);
1062                     fun = (JSFunction *) JS_GetPrivate(cx, obj);
1063                     jp2 = js_NewPrinter(cx, JS_GetFunctionName(fun),
1064                                         jp->indent, jp->pretty);
1065                     if (!jp2)
1066                         return JS_FALSE;
1067                     jp2->scope = jp->scope;
1068                     if (js_DecompileFunction(jp2, fun)) {
1069                         str = js_GetPrinterOutput(jp2);
1070                         if (str)
1071                             js_printf(jp, "%s\n", JS_GetStringBytes(str));
1072                     }
1073                     js_DestroyPrinter(jp2);
1074                     break;
1076                   default:;
1077                 }
1078               case JSOP_RETRVAL:
1079                 break;
1081               case JSOP_GROUP:
1082                 /* Use last real op so PopOff adds parens if needed. */
1083                 todo = PopOff(ss, lastop);
1085                 /* Now add user-supplied parens only if PopOff did not. */
1086                 cs    = &js_CodeSpec[lastop];
1087                 topcs = &js_CodeSpec[ss->opcodes[ss->top]];
1088                 if (topcs->prec >= cs->prec) {
1089                     todo = Sprint(&ss->sprinter, "(%s)",
1090                                   OFF2STR(&ss->sprinter, todo));
1091                 }
1092                 break;
1094               case JSOP_PUSH:
1095               case JSOP_PUSHOBJ:
1096               case JSOP_BINDNAME:
1097                 todo = Sprint(&ss->sprinter, "");
1098                 break;
1100 #if JS_HAS_EXCEPTIONS
1101               case JSOP_TRY:
1102                 js_printf(jp, "\ttry {\n");
1103                 jp->indent += 4;
1104                 todo = -2;
1105                 break;
1107             {
1108               static const char finally_cookie[] = "finally-cookie";
1110               case JSOP_FINALLY:
1111                 jp->indent -= 4;
1112                 js_printf(jp, "\t} finally {\n");
1113                 jp->indent += 4;
1115                 /*
1116                  * We must push an empty string placeholder for gosub's return
1117                  * address, popped by JSOP_RETSUB and counted by script->depth
1118                  * but not by ss->top (see JSOP_SETSP, below).
1119                  */
1120                 todo = Sprint(&ss->sprinter, finally_cookie);
1121                 break;
1123               case JSOP_RETSUB:
1124                 rval = POP_STR();
1125                 LOCAL_ASSERT(strcmp(rval, finally_cookie) == 0);
1126                 todo = -2;
1127                 break;
1128             }
1130               case JSOP_SWAP:
1131                 /*
1132                  * We don't generate this opcode currently, and previously we
1133                  * did not need to decompile it.  If old, serialized bytecode
1134                  * uses it still, we should fall through and set todo = -2.
1135                  */
1136                 /* FALL THROUGH */
1138               case JSOP_GOSUB:
1139               case JSOP_GOSUBX:
1140                 /*
1141                  * JSOP_GOSUB and GOSUBX have no effect on the decompiler's
1142                  * string stack because the next op in bytecode order finds
1143                  * the stack balanced by a JSOP_RETSUB executed elsewhere.
1144                  */
1145                 todo = -2;
1146                 break;
1148               case JSOP_SETSP:
1149                 /*
1150                  * The compiler models operand stack depth and fixes the stack
1151                  * pointer on entry to a catch clause based on its depth model.
1152                  * The decompiler must match the code generator's model, which
1153                  * is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops.
1154                  */
1155                 ss->top = (uintN) GET_ATOM_INDEX(pc);
1156                 break;
1158               case JSOP_EXCEPTION:
1159                 /*
1160                  * The only other JSOP_EXCEPTION case occurs as part of a code
1161                  * sequence that follows a SRC_CATCH-annotated JSOP_NOP.
1162                  */
1163                 sn = js_GetSrcNote(jp->script, pc);
1164                 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_HIDDEN);
1165                 todo = -2;
1166                 break;
1167 #endif /* JS_HAS_EXCEPTIONS */
1169               case JSOP_POP:
1170               case JSOP_POPV:
1171                 sn = js_GetSrcNote(jp->script, pc);
1172                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
1173                   case SRC_FOR:
1174                     rval = POP_STR();
1175                     todo = -2;
1176                     goto do_forloop;
1178                   case SRC_PCDELTA:
1179                     /* Pop and save to avoid blowing stack depth budget. */
1180                     lval = JS_strdup(cx, POP_STR());
1181                     if (!lval)
1182                         return JS_FALSE;
1184                     /*
1185                      * The offset tells distance to the end of the right-hand
1186                      * operand of the comma operator.
1187                      */
1188                     done = pc + len;
1189                     pc += js_GetSrcNoteOffset(sn, 0);
1190                     len = 0;
1192                     if (!Decompile(ss, done, pc - done)) {
1193                         JS_free(cx, (char *)lval);
1194                         return JS_FALSE;
1195                     }
1197                     /* Pop Decompile result and print comma expression. */
1198                     rval = POP_STR();
1199                     todo = Sprint(&ss->sprinter, "%s, %s", lval, rval);
1200                     JS_free(cx, (char *)lval);
1201                     break;
1203                   case SRC_HIDDEN:
1204                     /* Hide this pop, it's from a goto in a with or for/in. */
1205                     todo = -2;
1206                     break;
1208                   default:
1209                     rval = POP_STR();
1210                     if (*rval != '\0')
1211                         js_printf(jp, "\t%s;\n", rval);
1212                     todo = -2;
1213                     break;
1214                 }
1215                 break;
1217               case JSOP_POP2:
1218                 (void) PopOff(ss, op);
1219                 (void) PopOff(ss, op);
1220                 todo = -2;
1221                 break;
1223             {
1224               static const char with_cookie[] = "with-cookie";
1226               case JSOP_ENTERWITH:
1227                 sn = js_GetSrcNote(jp->script, pc);
1228                 if (sn && SN_TYPE(sn) == SRC_HIDDEN) {
1229                     todo = -2;
1230                     break;
1231                 }
1232                 rval = POP_STR();
1233                 js_printf(jp, "\twith (%s) {\n", rval);
1234                 jp->indent += 4;
1235                 todo = Sprint(&ss->sprinter, with_cookie);
1236                 break;
1238               case JSOP_LEAVEWITH:
1239                 sn = js_GetSrcNote(jp->script, pc);
1240                 todo = -2;
1241                 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
1242                     break;
1243                 rval = POP_STR();
1244                 LOCAL_ASSERT(strcmp(rval, with_cookie) == 0);
1245                 jp->indent -= 4;
1246                 js_printf(jp, "\t}\n");
1247                 break;
1248             }
1250               case JSOP_SETRVAL:
1251               case JSOP_RETURN:
1252                 rval = POP_STR();
1253                 if (*rval != '\0')
1254                     js_printf(jp, "\t%s %s;\n", cs->name, rval);
1255                 else
1256                     js_printf(jp, "\t%s;\n", cs->name);
1257                 todo = -2;
1258                 break;
1260 #if JS_HAS_EXCEPTIONS
1261               case JSOP_THROW:
1262                 sn = js_GetSrcNote(jp->script, pc);
1263                 todo = -2;
1264                 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
1265                     break;
1266                 rval = POP_STR();
1267                 js_printf(jp, "\t%s %s;\n", cs->name, rval);
1268                 break;
1269 #endif /* JS_HAS_EXCEPTIONS */
1271               case JSOP_GOTO:
1272               case JSOP_GOTOX:
1273                 sn = js_GetSrcNote(jp->script, pc);
1274                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
1275                   case SRC_CONT2LABEL:
1276                     atom = js_GetAtom(cx, &jp->script->atomMap,
1277                                       (jsatomid) js_GetSrcNoteOffset(sn, 0));
1278                     rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1279                     if (!rval)
1280                         return JS_FALSE;
1281                     RETRACT(&ss->sprinter, rval);
1282                     js_printf(jp, "\tcontinue %s;\n", rval);
1283                     break;
1284                   case SRC_CONTINUE:
1285                     js_printf(jp, "\tcontinue;\n");
1286                     break;
1287                   case SRC_BREAK2LABEL:
1288                     atom = js_GetAtom(cx, &jp->script->atomMap,
1289                                       (jsatomid) js_GetSrcNoteOffset(sn, 0));
1290                     rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1291                     if (!rval)
1292                         return JS_FALSE;
1293                     RETRACT(&ss->sprinter, rval);
1294                     js_printf(jp, "\tbreak %s;\n", rval);
1295                     break;
1296                   case SRC_HIDDEN:
1297                     break;
1298                   default:
1299                     js_printf(jp, "\tbreak;\n");
1300                     break;
1301                 }
1302                 todo = -2;
1303                 break;
1305               case JSOP_IFEQ:
1306               case JSOP_IFEQX:
1307                 len = GetJumpOffset(pc, pc);
1308                 sn = js_GetSrcNote(jp->script, pc);
1310                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
1311                   case SRC_IF:
1312                   case SRC_IF_ELSE:
1313                     rval = POP_STR();
1314                     js_printf(jp, "\tif (%s) {\n", rval);
1315                     jp->indent += 4;
1316                     if (SN_TYPE(sn) == SRC_IF) {
1317                         DECOMPILE_CODE(pc + oplen, len - oplen);
1318                     } else {
1319                         len = js_GetSrcNoteOffset(sn, 0);
1320                         DECOMPILE_CODE(pc + oplen, len - oplen);
1321                         jp->indent -= 4;
1322                         pc += len;
1323                         LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX);
1324                         oplen = js_CodeSpec[*pc].length;
1325                         len = GetJumpOffset(pc, pc);
1326                         js_printf(jp, "\t} else {\n");
1327                         jp->indent += 4;
1328                         DECOMPILE_CODE(pc + oplen, len - oplen);
1329                     }
1330                     jp->indent -= 4;
1331                     js_printf(jp, "\t}\n");
1332                     todo = -2;
1333                     break;
1335                   case SRC_WHILE:
1336                     rval = POP_STR();
1337                     js_printf(jp, "\twhile (%s) {\n", rval);
1338                     jp->indent += 4;
1339                     tail = js_GetSrcNoteOffset(sn, 0);
1340                     DECOMPILE_CODE(pc + oplen, tail - oplen);
1341                     jp->indent -= 4;
1342                     js_printf(jp, "\t}\n");
1343                     todo = -2;
1344                     break;
1346                   case SRC_COND:
1347                     xval = JS_strdup(cx, POP_STR());
1348                     if (!xval)
1349                         return JS_FALSE;
1350                     len = js_GetSrcNoteOffset(sn, 0);
1351                     DECOMPILE_CODE(pc + oplen, len - oplen);
1352                     lval = JS_strdup(cx, POP_STR());
1353                     if (!lval) {
1354                         JS_free(cx, (void *)xval);
1355                         return JS_FALSE;
1356                     }
1357                     pc += len;
1358                     LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX);
1359                     oplen = js_CodeSpec[*pc].length;
1360                     len = GetJumpOffset(pc, pc);
1361                     DECOMPILE_CODE(pc + oplen, len - oplen);
1362                     rval = POP_STR();
1363                     todo = Sprint(&ss->sprinter, "%s ? %s : %s",
1364                                   xval, lval, rval);
1365                     JS_free(cx, (void *)xval);
1366                     JS_free(cx, (void *)lval);
1367                     break;
1369                   default:
1370                     break;
1371                 }
1372                 break;
1374               case JSOP_IFNE:
1375               case JSOP_IFNEX:
1376 #if JS_HAS_DO_WHILE_LOOP
1377                 /* Currently, this must be a do-while loop's upward branch. */
1378                 jp->indent -= 4;
1379                 js_printf(jp, "\t} while (%s);\n", POP_STR());
1380                 todo = -2;
1381 #else
1382                 JS_ASSERT(0);
1383 #endif /* JS_HAS_DO_WHILE_LOOP */
1384                 break;
1386               case JSOP_OR:
1387               case JSOP_ORX:
1388                 xval = "||";
1390               do_logical_connective:
1391                 /* Top of stack is the first clause in a disjunction (||). */
1392                 lval = JS_strdup(cx, POP_STR());
1393                 if (!lval)
1394                     return JS_FALSE;
1395                 done = pc + GetJumpOffset(pc, pc);
1396                 pc += len;
1397                 len = PTRDIFF(done, pc, jsbytecode);
1398                 DECOMPILE_CODE(pc, len);
1399                 rval = POP_STR();
1400                 if (jp->pretty &&
1401                     jp->indent + 4 + strlen(lval) + 4 + strlen(rval) > 75) {
1402                     rval = JS_strdup(cx, rval);
1403                     if (!rval) {
1404                         tail = -1;
1405                     } else {
1406                         todo = Sprint(&ss->sprinter, "%s %s\n", lval, xval);
1407                         tail = Sprint(&ss->sprinter, "%*s%s",
1408                                       jp->indent + 4, "", rval);
1409                         JS_free(cx, (char *)rval);
1410                     }
1411                     if (tail < 0)
1412                         todo = -1;
1413                 } else {
1414                     todo = Sprint(&ss->sprinter, "%s %s %s", lval, xval, rval);
1415                 }
1416                 JS_free(cx, (char *)lval);
1417                 break;
1419               case JSOP_AND:
1420               case JSOP_ANDX:
1421                 xval = "&&";
1422                 goto do_logical_connective;
1424               case JSOP_FORARG:
1425                 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
1426                 LOCAL_ASSERT(atom);
1427                 goto do_fornameinloop;
1429               case JSOP_FORVAR:
1430                 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
1431                 LOCAL_ASSERT(atom);
1432                 goto do_fornameinloop;
1434               case JSOP_FORNAME:
1435                 atom = GET_ATOM(cx, jp->script, pc);
1437               do_fornameinloop:
1438                 sn = js_GetSrcNote(jp->script, pc);
1439                 xval = NULL;
1440                 lval = "";
1441                 goto do_forinloop;
1443               case JSOP_FORPROP:
1444                 xval = NULL;
1445                 atom = GET_ATOM(cx, jp->script, pc);
1446                 if (ATOM_KEYWORD(atom)) {
1447                     xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
1448                                        (jschar)'\'');
1449                     if (!xval)
1450                         return JS_FALSE;
1451                     atom = NULL;
1452                 }
1453                 lval = POP_STR();
1454                 sn = NULL;
1456               do_forinloop:
1457                 pc += oplen;
1458                 LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
1459                 oplen = js_CodeSpec[*pc].length;
1460                 len = GetJumpOffset(pc, pc);
1461                 sn2 = js_GetSrcNote(jp->script, pc);
1462                 tail = js_GetSrcNoteOffset(sn2, 0);
1464               do_forinbody:
1465                 js_printf(jp, "\tfor (%s%s", VarPrefix(sn), lval);
1466                 if (atom) {
1467                     xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1468                     if (!xval)
1469                         return JS_FALSE;
1470                     RETRACT(&ss->sprinter, xval);
1471                     js_printf(jp, *lval ? ".%s" : "%s", xval);
1472                 } else if (xval) {
1473                     js_printf(jp, "[%s]", xval);
1474                 }
1475                 rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]);
1476                 js_printf(jp, " in %s) {\n", rval);
1477                 jp->indent += 4;
1478                 DECOMPILE_CODE(pc + oplen, tail - oplen);
1479                 jp->indent -= 4;
1480                 js_printf(jp, "\t}\n");
1481                 todo = -2;
1482                 break;
1484               case JSOP_FORELEM:
1485                 pc++;
1486                 LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
1487                 len = js_CodeSpec[*pc].length;
1489                 /*
1490                  * Arrange for the JSOP_ENUMELEM case to set tail for use by
1491                  * do_forinbody: code that uses on it to find the loop-closing
1492                  * jump (whatever its format, normal or extended), in order to
1493                  * bound the recursively decompiled loop body.
1494                  */
1495                 sn = js_GetSrcNote(jp->script, pc);
1496                 JS_ASSERT(!forelem_tail);
1497                 forelem_tail = pc + js_GetSrcNoteOffset(sn, 0);
1499                 /*
1500                  * This gets a little wacky.  Only the length of the for loop
1501                  * body PLUS the element-indexing expression is known here, so
1502                  * we pass the after-loop pc to the JSOP_ENUMELEM case, which
1503                  * is immediately below, to decompile that helper bytecode via
1504                  * the 'forelem_done' local.
1505                  *
1506                  * Since a for..in loop can't nest in the head of another for
1507                  * loop, we can use forelem_{tail,done} singletons to remember
1508                  * state from JSOP_FORELEM to JSOP_ENUMELEM, thence (via goto)
1509                  * to label do_forinbody.
1510                  */
1511                 JS_ASSERT(!forelem_done);
1512                 forelem_done = pc + GetJumpOffset(pc, pc);
1513                 break;
1515               case JSOP_ENUMELEM:
1516                 /*
1517                  * The stack has the object under the (top) index expression.
1518                  * The "rval" property id is underneath those two on the stack.
1519                  * The for loop body net and gross lengths can now be adjusted
1520                  * to account for the length of the indexing expression that
1521                  * came after JSOP_FORELEM and before JSOP_ENUMELEM.
1522                  */
1523                 atom = NULL;
1524                 xval = POP_STR();
1525                 lval = POP_STR();
1526                 rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]);
1527                 JS_ASSERT(forelem_tail > pc);
1528                 tail = forelem_tail - pc;
1529                 forelem_tail = NULL;
1530                 JS_ASSERT(forelem_done > pc);
1531                 len = forelem_done - pc;
1532                 forelem_done = NULL;
1533                 goto do_forinbody;
1535               case JSOP_DUP2:
1536                 rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-2]);
1537                 todo = SprintPut(&ss->sprinter, rval, strlen(rval));
1538                 if (todo < 0 || !PushOff(ss, todo, ss->opcodes[ss->top-2]))
1539                     return JS_FALSE;
1540                 /* FALL THROUGH */
1542               case JSOP_DUP:
1543                 rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]);
1544                 op = ss->opcodes[ss->top-1];
1545                 todo = SprintPut(&ss->sprinter, rval, strlen(rval));
1546                 break;
1548               case JSOP_SETARG:
1549                 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
1550                 LOCAL_ASSERT(atom);
1551                 goto do_setname;
1553               case JSOP_SETVAR:
1554                 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
1555                 LOCAL_ASSERT(atom);
1556                 goto do_setname;
1558               case JSOP_SETCONST:
1559               case JSOP_SETNAME:
1560                 atom = GET_ATOM(cx, jp->script, pc);
1561               do_setname:
1562                 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1563                 if (!lval)
1564                     return JS_FALSE;
1565                 rval = POP_STR();
1566                 if (op == JSOP_SETNAME)
1567                     (void) PopOff(ss, op);
1568               do_setlval:
1569                 sn = js_GetSrcNote(jp->script, pc - 1);
1570                 if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
1571                     todo = Sprint(&ss->sprinter, "%s %s= %s",
1572                                   lval, js_CodeSpec[lastop].token, rval);
1573                 } else {
1574                     sn = js_GetSrcNote(jp->script, pc);
1575                     todo = Sprint(&ss->sprinter, "%s%s = %s",
1576                                   VarPrefix(sn), lval, rval);
1577                 }
1578                 break;
1580               case JSOP_NEW:
1581               case JSOP_CALL:
1582               case JSOP_EVAL:
1583 #if JS_HAS_LVALUE_RETURN
1584               case JSOP_SETCALL:
1585 #endif
1586                 saveop = op;
1587                 op = JSOP_NOP;           /* turn off parens */
1588                 argc = GET_ARGC(pc);
1589                 argv = (char **)
1590                     JS_malloc(cx, (size_t)(argc + 1) * sizeof *argv);
1591                 if (!argv)
1592                     return JS_FALSE;
1594                 ok = JS_TRUE;
1595                 for (i = argc; i > 0; i--) {
1596                     argv[i] = JS_strdup(cx, POP_STR());
1597                     if (!argv[i]) {
1598                         ok = JS_FALSE;
1599                         break;
1600                     }
1601                 }
1603                 /* Skip the JSOP_PUSHOBJ-created empty string. */
1604                 LOCAL_ASSERT(ss->top >= 2);
1605                 (void) PopOff(ss, op);
1607                 /* Get the callee's decompiled image in argv[0]. */
1608                 argv[0] = JS_strdup(cx, POP_STR());
1609                 if (!argv[i])
1610                     ok = JS_FALSE;
1612                 lval = "(", rval = ")";
1613                 if (saveop == JSOP_NEW) {
1614                     todo = Sprint(&ss->sprinter, "%s %s%s",
1615                                   js_new_str, argv[0], lval);
1616                 } else {
1617                     todo = Sprint(&ss->sprinter, "%s%s",
1618                                   argv[0], lval);
1619                 }
1620                 if (todo < 0)
1621                     ok = JS_FALSE;
1623                 for (i = 1; i <= argc; i++) {
1624                     if (!argv[i] ||
1625                         Sprint(&ss->sprinter, "%s%s",
1626                                argv[i], (i < argc) ? ", " : "") < 0) {
1627                         ok = JS_FALSE;
1628                         break;
1629                     }
1630                 }
1631                 if (Sprint(&ss->sprinter, rval) < 0)
1632                     ok = JS_FALSE;
1634                 for (i = 0; i <= argc; i++) {
1635                     if (argv[i])
1636                         JS_free(cx, argv[i]);
1637                 }
1638                 JS_free(cx, argv);
1639                 if (!ok)
1640                     return JS_FALSE;
1641                 op = saveop;
1642 #if JS_HAS_LVALUE_RETURN
1643                 if (op == JSOP_SETCALL) {
1644                     if (!PushOff(ss, todo, op))
1645                         return JS_FALSE;
1646                     todo = Sprint(&ss->sprinter, "");
1647                 }
1648 #endif
1649                 break;
1651               case JSOP_DELNAME:
1652                 atom = GET_ATOM(cx, jp->script, pc);
1653                 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1654                 if (!lval)
1655                     return JS_FALSE;
1656                 RETRACT(&ss->sprinter, lval);
1657                 todo = Sprint(&ss->sprinter, "%s %s", js_delete_str, lval);
1658                 break;
1660               case JSOP_DELPROP:
1661                 GET_ATOM_QUOTE_AND_FMT("%s %s[%s]", "%s %s.%s", rval);
1662                 lval = POP_STR();
1663                 todo = Sprint(&ss->sprinter, fmt, js_delete_str, lval, rval);
1664                 break;
1666               case JSOP_DELELEM:
1667                 xval = POP_STR();
1668                 lval = POP_STR();
1669                 todo = Sprint(&ss->sprinter, "%s %s[%s]",
1670                               js_delete_str, lval, xval);
1671                 break;
1673               case JSOP_TYPEOF:
1674               case JSOP_VOID:
1675                 rval = POP_STR();
1676                 todo = Sprint(&ss->sprinter, "%s %s", cs->name, rval);
1677                 break;
1679               case JSOP_INCARG:
1680               case JSOP_DECARG:
1681                 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
1682                 LOCAL_ASSERT(atom);
1683                 goto do_incatom;
1685               case JSOP_INCVAR:
1686               case JSOP_DECVAR:
1687                 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
1688                 LOCAL_ASSERT(atom);
1689                 goto do_incatom;
1691               case JSOP_INCNAME:
1692               case JSOP_DECNAME:
1693                 atom = GET_ATOM(cx, jp->script, pc);
1694               do_incatom:
1695                 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1696                 if (!lval)
1697                     return JS_FALSE;
1698                 RETRACT(&ss->sprinter, lval);
1699                 todo = Sprint(&ss->sprinter, "%s%s",
1700                               js_incop_str[!(cs->format & JOF_INC)], lval);
1701                 break;
1703               case JSOP_INCPROP:
1704               case JSOP_DECPROP:
1705                 GET_ATOM_QUOTE_AND_FMT("%s%s[%s]", "%s%s.%s", rval);
1706                 lval = POP_STR();
1707                 todo = Sprint(&ss->sprinter, fmt,
1708                               js_incop_str[!(cs->format & JOF_INC)],
1709                               lval, rval);
1710                 break;
1712               case JSOP_INCELEM:
1713               case JSOP_DECELEM:
1714                 xval = POP_STR();
1715                 lval = POP_STR();
1716                 todo = Sprint(&ss->sprinter, "%s%s[%s]",
1717                               js_incop_str[!(cs->format & JOF_INC)],
1718                               lval, xval);
1719                 break;
1721               case JSOP_ARGINC:
1722               case JSOP_ARGDEC:
1723                 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
1724                 LOCAL_ASSERT(atom);
1725                 goto do_atominc;
1727               case JSOP_VARINC:
1728               case JSOP_VARDEC:
1729                 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
1730                 LOCAL_ASSERT(atom);
1731                 goto do_atominc;
1733               case JSOP_NAMEINC:
1734               case JSOP_NAMEDEC:
1735                 atom = GET_ATOM(cx, jp->script, pc);
1736               do_atominc:
1737                 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1738                 if (!lval)
1739                     return JS_FALSE;
1740                 todo = STR2OFF(&ss->sprinter, lval);
1741                 SprintPut(&ss->sprinter,
1742                           js_incop_str[!(cs->format & JOF_INC)],
1743                           2);
1744                 break;
1746               case JSOP_PROPINC:
1747               case JSOP_PROPDEC:
1748                 GET_ATOM_QUOTE_AND_FMT("%s[%s]%s", "%s.%s%s", rval);
1749                 lval = POP_STR();
1750                 todo = Sprint(&ss->sprinter, fmt, lval, rval,
1751                               js_incop_str[!(cs->format & JOF_INC)]);
1752                 break;
1754               case JSOP_ELEMINC:
1755               case JSOP_ELEMDEC:
1756                 xval = POP_STR();
1757                 lval = POP_STR();
1758                 todo = Sprint(&ss->sprinter, "%s[%s]%s",
1759                               lval, xval,
1760                               js_incop_str[!(cs->format & JOF_INC)]);
1761                 break;
1763               case JSOP_GETPROP2:
1764                 op = JSOP_GETPROP;
1765                 (void) PopOff(ss, lastop);
1766                 /* FALL THROUGH */
1768               case JSOP_GETPROP:
1769                 GET_ATOM_QUOTE_AND_FMT("%s[%s]", "%s.%s", rval);
1770                 lval = POP_STR();
1771                 todo = Sprint(&ss->sprinter, fmt, lval, rval);
1772                 break;
1774               case JSOP_SETPROP:
1775                 GET_ATOM_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval);
1776                 rval = POP_STR();
1777                 lval = POP_STR();
1778                 sn = js_GetSrcNote(jp->script, pc - 1);
1779                 todo = Sprint(&ss->sprinter, fmt, lval, xval,
1780                               (sn && SN_TYPE(sn) == SRC_ASSIGNOP)
1781                               ? js_CodeSpec[lastop].token
1782                               : "",
1783                               rval);
1784                 break;
1786               case JSOP_GETELEM2:
1787                 op = JSOP_GETELEM;
1788                 (void) PopOff(ss, lastop);
1789                 /* FALL THROUGH */
1791               case JSOP_GETELEM:
1792                 op = JSOP_NOP;           /* turn off parens */
1793                 xval = POP_STR();
1794                 op = JSOP_GETELEM;
1795                 lval = POP_STR();
1796                 if (*xval == '\0')
1797                     todo = Sprint(&ss->sprinter, "%s", lval);
1798                 else
1799                     todo = Sprint(&ss->sprinter, "%s[%s]", lval, xval);
1800                 break;
1802               case JSOP_SETELEM:
1803                 op = JSOP_NOP;           /* turn off parens */
1804                 rval = POP_STR();
1805                 xval = POP_STR();
1806                 op = JSOP_SETELEM;
1807                 lval = POP_STR();
1808                 if (*xval == '\0')
1809                     goto do_setlval;
1810                 sn = js_GetSrcNote(jp->script, pc - 1);
1811                 todo = Sprint(&ss->sprinter, "%s[%s] %s= %s",
1812                               lval, xval,
1813                               (sn && SN_TYPE(sn) == SRC_ASSIGNOP)
1814                               ? js_CodeSpec[lastop].token
1815                               : "",
1816                               rval);
1817                 break;
1819               case JSOP_ARGSUB:
1820                 i = (jsint) GET_ATOM_INDEX(pc);
1821                 todo = Sprint(&ss->sprinter, "%s[%d]",
1822                               js_arguments_str, (int) i);
1823                 break;
1825               case JSOP_ARGCNT:
1826                 todo = Sprint(&ss->sprinter, "%s.%s",
1827                               js_arguments_str, js_length_str);
1828                 break;
1830               case JSOP_GETARG:
1831                 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
1832                 LOCAL_ASSERT(atom);
1833                 goto do_name;
1835               case JSOP_GETVAR:
1836                 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
1837                 LOCAL_ASSERT(atom);
1838                 goto do_name;
1840               case JSOP_NAME:
1841                 atom = GET_ATOM(cx, jp->script, pc);
1842               do_name:
1843                 sn = js_GetSrcNote(jp->script, pc);
1844                 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1845                 if (!rval)
1846                     return JS_FALSE;
1847                 RETRACT(&ss->sprinter, rval);
1848                 todo = Sprint(&ss->sprinter, "%s%s", VarPrefix(sn), rval);
1849                 break;
1851               case JSOP_UINT16:
1852                 i = (jsint) GET_ATOM_INDEX(pc);
1853                 todo = Sprint(&ss->sprinter, "%u", (unsigned) i);
1854                 break;
1856               case JSOP_NUMBER:
1857                 atom = GET_ATOM(cx, jp->script, pc);
1858                 val = ATOM_KEY(atom);
1859                 if (JSVAL_IS_INT(val)) {
1860                     long ival = (long)JSVAL_TO_INT(val);
1861                     todo = Sprint(&ss->sprinter, "%ld", ival);
1862                 } else {
1863                     char buf[DTOSTR_STANDARD_BUFFER_SIZE];
1864                     char *numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD,
1865                                              0, *JSVAL_TO_DOUBLE(val));
1866                     if (!numStr) {
1867                         JS_ReportOutOfMemory(cx);
1868                         return JS_FALSE;
1869                     }
1870                     todo = Sprint(&ss->sprinter, numStr);
1871                 }
1872                 break;
1874               case JSOP_STRING:
1875                 atom = GET_ATOM(cx, jp->script, pc);
1876                 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
1877                                    (jschar)'"');
1878                 if (!rval)
1879                     return JS_FALSE;
1880                 todo = STR2OFF(&ss->sprinter, rval);
1881                 break;
1883               case JSOP_OBJECT:
1884               case JSOP_ANONFUNOBJ:
1885               case JSOP_NAMEDFUNOBJ:
1886                 atom = GET_ATOM(cx, jp->script, pc);
1887                 if (op == JSOP_OBJECT) {
1888                     str = js_ValueToSource(cx, ATOM_KEY(atom));
1889                     if (!str)
1890                         return JS_FALSE;
1891                 } else {
1892                     if (!js_fun_toString(cx, ATOM_TO_OBJECT(atom),
1893                                          JS_DONT_PRETTY_PRINT, 0, NULL,
1894                                          &val)) {
1895                         return JS_FALSE;
1896                     }
1897                     str = JSVAL_TO_STRING(val);
1898                 }
1899                 todo = SprintPut(&ss->sprinter, JS_GetStringBytes(str),
1900                                  JSSTRING_LENGTH(str));
1901                 break;
1903 #if JS_HAS_SWITCH_STATEMENT
1904               case JSOP_TABLESWITCH:
1905               case JSOP_TABLESWITCHX:
1906               {
1907                 jsbytecode *pc2;
1908                 ptrdiff_t off, off2;
1909                 jsint j, n, low, high;
1910                 TableEntry *table;
1912                 sn = js_GetSrcNote(jp->script, pc);
1913                 JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
1914                 len = js_GetSrcNoteOffset(sn, 0);
1915                 pc2 = pc;
1916                 off = GetJumpOffset(pc, pc2);
1917                 pc2 += JUMP_OFFSET_LEN;
1918                 low = GetJumpOffset(pc, pc2);
1919                 pc2 += JUMP_OFFSET_LEN;
1920                 high = GetJumpOffset(pc, pc2);
1922                 n = high - low + 1;
1923                 if (n == 0) {
1924                     table = NULL;
1925                     j = 0;
1926                 } else {
1927                     table = (TableEntry *)
1928                             JS_malloc(cx, (size_t)n * sizeof *table);
1929                     if (!table)
1930                         return JS_FALSE;
1931                     for (i = j = 0; i < n; i++) {
1932                         pc2 += JUMP_OFFSET_LEN;
1933                         off2 = GetJumpOffset(pc, pc2);
1934                         if (off2) {
1935                             table[j].key = INT_TO_JSVAL(low + i);
1936                             table[j++].offset = off2;
1937                         }
1938                     }
1939                     js_HeapSort(table, (size_t) j, sizeof *table,
1940                                 CompareOffsets, NULL);
1941                 }
1943                 ok = DecompileSwitch(ss, table, (uintN)j, pc, len, off,
1944                                      JS_FALSE);
1945                 JS_free(cx, table);
1946                 if (!ok)
1947                     return ok;
1948                 todo = -2;
1949                 break;
1950               }
1952               case JSOP_LOOKUPSWITCH:
1953               case JSOP_LOOKUPSWITCHX:
1954               {
1955                 jsbytecode *pc2;
1956                 ptrdiff_t off, off2;
1957                 jsint npairs;
1958                 TableEntry *table;
1960                 sn = js_GetSrcNote(jp->script, pc);
1961                 JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
1962                 len = js_GetSrcNoteOffset(sn, 0);
1963                 pc2 = pc;
1964                 off = GetJumpOffset(pc, pc2);
1965                 pc2 += JUMP_OFFSET_LEN;
1966                 npairs = (jsint) GET_ATOM_INDEX(pc2);
1967                 pc2 += ATOM_INDEX_LEN;
1969                 table = (TableEntry *)
1970                     JS_malloc(cx, (size_t)npairs * sizeof *table);
1971                 if (!table)
1972                     return JS_FALSE;
1973                 for (i = 0; i < npairs; i++) {
1974                     atom = GET_ATOM(cx, jp->script, pc2);
1975                     pc2 += ATOM_INDEX_LEN;
1976                     off2 = GetJumpOffset(pc, pc2);
1977                     pc2 += JUMP_OFFSET_LEN;
1978                     table[i].key = ATOM_KEY(atom);
1979                     table[i].offset = off2;
1980                 }
1982                 ok = DecompileSwitch(ss, table, (uintN)npairs, pc, len, off,
1983                                      JS_FALSE);
1984                 JS_free(cx, table);
1985                 if (!ok)
1986                     return ok;
1987                 todo = -2;
1988                 break;
1989               }
1991               case JSOP_CONDSWITCH:
1992               {
1993                 jsbytecode *pc2;
1994                 ptrdiff_t off, off2, caseOff;
1995                 jsint ncases;
1996                 TableEntry *table;
1998                 sn = js_GetSrcNote(jp->script, pc);
1999                 JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
2000                 len = js_GetSrcNoteOffset(sn, 0);
2001                 off = js_GetSrcNoteOffset(sn, 1);
2003                 /*
2004                  * Count the cases using offsets from switch to first case,
2005                  * and case to case, stored in srcnote immediates.
2006                  */
2007                 pc2 = pc;
2008                 off2 = off;
2009                 for (ncases = 0; off2 != 0; ncases++) {
2010                     pc2 += off2;
2011                     JS_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT ||
2012                               *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX);
2013                     if (*pc2 == JSOP_DEFAULT || *pc2 == JSOP_DEFAULTX) {
2014                         /* End of cases, but count default as a case. */
2015                         off2 = 0;
2016                     } else {
2017                         sn = js_GetSrcNote(jp->script, pc2);
2018                         JS_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
2019                         off2 = js_GetSrcNoteOffset(sn, 0);
2020                     }
2021                 }
2023                 /*
2024                  * Allocate table and rescan the cases using their srcnotes,
2025                  * stashing each case's delta from switch top in table[i].key,
2026                  * and the distance to its statements in table[i].offset.
2027                  */
2028                 table = (TableEntry *)
2029                     JS_malloc(cx, (size_t)ncases * sizeof *table);
2030                 if (!table)
2031                     return JS_FALSE;
2032                 pc2 = pc;
2033                 off2 = off;
2034                 for (i = 0; i < ncases; i++) {
2035                     pc2 += off2;
2036                     JS_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT ||
2037                               *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX);
2038                     caseOff = pc2 - pc;
2039                     table[i].key = INT_TO_JSVAL((jsint) caseOff);
2040                     table[i].offset = caseOff + GetJumpOffset(pc2, pc2);
2041                     if (*pc2 == JSOP_CASE || *pc2 == JSOP_CASEX) {
2042                         sn = js_GetSrcNote(jp->script, pc2);
2043                         JS_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
2044                         off2 = js_GetSrcNoteOffset(sn, 0);
2045                     }
2046                 }
2048                 /*
2049                  * Find offset of default code by fetching the default offset
2050                  * from the end of table.  JSOP_CONDSWITCH always has a default
2051                  * case at the end.
2052                  */
2053                 off = JSVAL_TO_INT(table[ncases-1].key);
2054                 pc2 = pc + off;
2055                 off += GetJumpOffset(pc2, pc2);
2057                 ok = DecompileSwitch(ss, table, (uintN)ncases, pc, len, off,
2058                                      JS_TRUE);
2059                 JS_free(cx, table);
2060                 if (!ok)
2061                     return ok;
2062                 todo = -2;
2063                 break;
2064               }
2066               case JSOP_CASE:
2067               case JSOP_CASEX:
2068               {
2069                 lval = POP_STR();
2070                 if (!lval)
2071                     return JS_FALSE;
2072                 js_printf(jp, "\tcase %s:\n", lval);
2073                 todo = -2;
2074                 break;
2075               }
2077 #endif /* JS_HAS_SWITCH_STATEMENT */
2079 #if !JS_BUG_FALLIBLE_EQOPS
2080               case JSOP_NEW_EQ:
2081               case JSOP_NEW_NE:
2082                 rval = POP_STR();
2083                 lval = POP_STR();
2084                 todo = Sprint(&ss->sprinter, "%s %c%s %s",
2085                               lval,
2086                               (op == JSOP_NEW_EQ) ? '=' : '!',
2087 #if JS_HAS_TRIPLE_EQOPS
2088                               JSVERSION_IS_ECMA(cx->version) ? "==" :
2089 #endif
2090                               "=",
2091                               rval);
2092                 break;
2093 #endif /* !JS_BUG_FALLIBLE_EQOPS */
2095 #if JS_HAS_LEXICAL_CLOSURE
2096               case JSOP_CLOSURE:
2097                 atom = GET_ATOM(cx, jp->script, pc);
2098                 JS_ASSERT(ATOM_IS_OBJECT(atom));
2099                 goto do_function;
2100 #endif /* JS_HAS_LEXICAL_CLOSURE */
2102 #if JS_HAS_EXPORT_IMPORT
2103               case JSOP_EXPORTALL:
2104                 js_printf(jp, "\texport *\n");
2105                 todo = -2;
2106                 break;
2108               case JSOP_EXPORTNAME:
2109                 atom = GET_ATOM(cx, jp->script, pc);
2110                 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2111                 if (!rval)
2112                     return JS_FALSE;
2113                 RETRACT(&ss->sprinter, rval);
2114                 js_printf(jp, "\texport %s\n", rval);
2115                 todo = -2;
2116                 break;
2118               case JSOP_IMPORTALL:
2119                 lval = POP_STR();
2120                 js_printf(jp, "\timport %s.*\n", lval);
2121                 todo = -2;
2122                 break;
2124               case JSOP_IMPORTPROP:
2125                 GET_ATOM_QUOTE_AND_FMT("\timport %s[%s]\n", "\timport %s.%s\n",
2126                                        rval);
2127                 lval = POP_STR();
2128                 js_printf(jp, fmt, lval, rval);
2129                 todo = -2;
2130                 break;
2132               case JSOP_IMPORTELEM:
2133                 xval = POP_STR();
2134                 op = JSOP_GETELEM;
2135                 lval = POP_STR();
2136                 js_printf(jp, "\timport %s[%s]\n", lval, xval);
2137                 todo = -2;
2138                 break;
2139 #endif /* JS_HAS_EXPORT_IMPORT */
2141               case JSOP_TRAP:
2142                 op = JS_GetTrapOpcode(cx, jp->script, pc);
2143                 if (op == JSOP_LIMIT)
2144                     return JS_FALSE;
2145                 *pc = op;
2146                 cs = &js_CodeSpec[op];
2147                 len = cs->length;
2148                 DECOMPILE_CODE(pc, len);
2149                 *pc = JSOP_TRAP;
2150                 todo = -2;
2151                 break;
2153 #if JS_HAS_INITIALIZERS
2154               case JSOP_NEWINIT:
2155                 LOCAL_ASSERT(ss->top >= 2);
2156                 (void) PopOff(ss, op);
2157                 lval = POP_STR();
2158 #if JS_HAS_SHARP_VARS
2159                 op = (JSOp)pc[len];
2160                 if (op == JSOP_DEFSHARP) {
2161                     pc += len;
2162                     cs = &js_CodeSpec[op];
2163                     len = cs->length;
2164                     i = (jsint) GET_ATOM_INDEX(pc);
2165                     todo = Sprint(&ss->sprinter, "#%u=%c",
2166                                   (unsigned) i,
2167                                   (*lval == 'O') ? '{' : '[');
2168                 } else
2169 #endif /* JS_HAS_SHARP_VARS */
2170                 {
2171                     todo = Sprint(&ss->sprinter, (*lval == 'O') ? "{" : "[");
2172                 }
2173                 break;
2175               case JSOP_ENDINIT:
2176                 rval = POP_STR();
2177                 sn = js_GetSrcNote(jp->script, pc);
2178                 todo = Sprint(&ss->sprinter, "%s%s%c",
2179                               rval,
2180                               (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "",
2181                               (*rval == '{') ? '}' : ']');
2182                 break;
2184               case JSOP_INITPROP:
2185               case JSOP_INITCATCHVAR:
2186                 atom = GET_ATOM(cx, jp->script, pc);
2187                 xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
2188                                    ATOM_KEYWORD(atom) ? '\'' : 0);
2189                 if (!xval)
2190                     return JS_FALSE;
2191                 rval = POP_STR();
2192                 lval = POP_STR();
2193               do_initprop:
2194 #ifdef OLD_GETTER_SETTER
2195                 todo = Sprint(&ss->sprinter, "%s%s%s%s%s:%s",
2196                               lval,
2197                               (lval[1] != '\0') ? ", " : "",
2198                               xval,
2199                               (lastop == JSOP_GETTER || lastop == JSOP_SETTER)
2200                               ? " " : "",
2201                               (lastop == JSOP_GETTER) ? js_getter_str :
2202                               (lastop == JSOP_SETTER) ? js_setter_str :
2203                               "",
2204                               rval);
2205 #else
2206                 if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) {
2207                     todo = Sprint(&ss->sprinter, "%s%s%s %s%s",
2208                                   lval,
2209                                   (lval[1] != '\0') ? ", " : "",
2210                                   (lastop == JSOP_GETTER)
2211                                   ? js_get_str : js_set_str,
2212                                   xval,
2213                                   rval + strlen(js_function_str) + 1);
2214                 } else {
2215                     todo = Sprint(&ss->sprinter, "%s%s%s:%s",
2216                                   lval,
2217                                   (lval[1] != '\0') ? ", " : "",
2218                                   xval,
2219                                   rval);
2220                 }
2221 #endif
2222                 break;
2224               case JSOP_INITELEM:
2225                 rval = POP_STR();
2226                 xval = POP_STR();
2227                 lval = POP_STR();
2228                 sn = js_GetSrcNote(jp->script, pc);
2229                 if (sn && SN_TYPE(sn) == SRC_LABEL)
2230                     goto do_initprop;
2231                 todo = Sprint(&ss->sprinter, "%s%s%s",
2232                               lval,
2233                               (lval[1] != '\0' || *xval != '0') ? ", " : "",
2234                               rval);
2235                 break;
2237 #if JS_HAS_SHARP_VARS
2238               case JSOP_DEFSHARP:
2239                 i = (jsint) GET_ATOM_INDEX(pc);
2240                 rval = POP_STR();
2241                 todo = Sprint(&ss->sprinter, "#%u=%s", (unsigned) i, rval);
2242                 break;
2244               case JSOP_USESHARP:
2245                 i = (jsint) GET_ATOM_INDEX(pc);
2246                 todo = Sprint(&ss->sprinter, "#%u#", (unsigned) i);
2247                 break;
2248 #endif /* JS_HAS_SHARP_VARS */
2249 #endif /* JS_HAS_INITIALIZERS */
2251 #if JS_HAS_DEBUGGER_KEYWORD
2252               case JSOP_DEBUGGER:
2253                 js_printf(jp, "\tdebugger;\n");
2254                 todo = -2;
2255                 break;
2256 #endif /* JS_HAS_DEBUGGER_KEYWORD */
2258               default:
2259                 todo = -2;
2260                 break;
2261             }
2262         }
2264         if (todo < 0) {
2265             /* -2 means "don't push", -1 means reported error. */
2266             if (todo == -1)
2267                 return JS_FALSE;
2268         } else {
2269             if (!PushOff(ss, todo, op))
2270                 return JS_FALSE;
2271         }
2272         pc += len;
2273     }
2275 /*
2276  * Undefine local macros.
2277  */
2278 #undef DECOMPILE_CODE
2279 #undef POP_STR
2280 #undef LOCAL_ASSERT
2281 #undef GET_ATOM_QUOTE_AND_FMT
2283     return JS_TRUE;
2287 JSBool
2288 js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len)
2290     SprintStack ss;
2291     JSContext *cx;
2292     void *mark, *space;
2293     size_t offsetsz, opcodesz;
2294     JSBool ok;
2295     JSScript *oldscript;
2296     char *last;
2298     /* Initialize a sprinter for use with the offset stack. */
2299     ss.printer = jp;
2300     cx = jp->sprinter.context;
2301     mark = JS_ARENA_MARK(&cx->tempPool);
2302     INIT_SPRINTER(cx, &ss.sprinter, &cx->tempPool, PAREN_SLOP);
2304     /* Allocate the parallel (to avoid padding) offset and opcode stacks. */
2305     offsetsz = script->depth * sizeof(ptrdiff_t);
2306     opcodesz = script->depth * sizeof(jsbytecode);
2307     JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz);
2308     if (!space) {
2309         ok = JS_FALSE;
2310         goto out;
2311     }
2312     ss.offsets = (ptrdiff_t *) space;
2313     ss.opcodes = (jsbytecode *) ((char *)space + offsetsz);
2314     ss.top = 0;
2316     /* Call recursive subroutine to do the hard work. */
2317     oldscript = jp->script;
2318     jp->script = script;
2319     ok = Decompile(&ss, pc, len);
2320     jp->script = oldscript;
2322     /* If the given code didn't empty the stack, do it now. */
2323     if (ss.top) {
2324         do {
2325             last = OFF2STR(&ss.sprinter, PopOff(&ss, JSOP_NOP));
2326         } while (ss.top);
2327         js_printf(jp, "%s", last);
2328     }
2330 out:
2331     /* Free all temporary stuff allocated under this call. */
2332     JS_ARENA_RELEASE(&cx->tempPool, mark);
2333     return ok;
2336 JSBool
2337 js_DecompileScript(JSPrinter *jp, JSScript *script)
2339     return js_DecompileCode(jp, script, script->code, (uintN)script->length);
2342 static const char native_code_str[] = "\t[native code]\n";
2344 JSBool
2345 js_DecompileFunctionBody(JSPrinter *jp, JSFunction *fun)
2347     JSScript *script;
2348     JSScope *scope, *save;
2349     JSBool ok;
2351     script = fun->script;
2352     if (!script) {
2353         js_printf(jp, native_code_str);
2354         return JS_TRUE;
2355     }
2356     scope = fun->object ? OBJ_SCOPE(fun->object) : NULL;
2357     save = jp->scope;
2358     jp->scope = scope;
2359     ok = js_DecompileCode(jp, script, script->code, (uintN)script->length);
2360     jp->scope = save;
2361     return ok;
2364 JSBool
2365 js_DecompileFunction(JSPrinter *jp, JSFunction *fun)
2367     JSContext *cx;
2368     uintN i, nargs, indent;
2369     void *mark;
2370     JSAtom **params;
2371     JSScope *scope, *oldscope;
2372     JSScopeProperty *sprop;
2373     JSBool ok;
2375     /*
2376      * If pretty, conform to ECMA-262 Edition 3, 15.3.4.2, by decompiling a
2377      * FunctionDeclaration.  Otherwise, check the JSFUN_LAMBDA flag and force
2378      * an expression by parenthesizing.
2379      */
2380     if (jp->pretty) {
2381         js_puts(jp, "\n");
2382         js_printf(jp, "\t");
2383     } else {
2384         if (fun->flags & JSFUN_LAMBDA)
2385             js_puts(jp, "(");
2386     }
2387     if (fun->flags & JSFUN_GETTER)
2388         js_printf(jp, "%s ", js_getter_str);
2389     else if (fun->flags & JSFUN_SETTER)
2390         js_printf(jp, "%s ", js_setter_str);
2392     js_printf(jp, "%s ", js_function_str);
2393     if (fun->atom && !QuoteString(&jp->sprinter, ATOM_TO_STRING(fun->atom), 0))
2394         return JS_FALSE;
2395     js_puts(jp, "(");
2397     if (fun->script && fun->object) {
2398         /*
2399          * Print the parameters.
2400          *
2401          * This code is complicated by the need to handle duplicate parameter
2402          * names, as required by ECMA (bah!).  A duplicate parameter is stored
2403          * as another node with the same id (the parameter name) but different
2404          * shortid (the argument index) along the property tree ancestor line
2405          * starting at SCOPE_LAST_PROP(scope).  Only the last duplicate param
2406          * is mapped by the scope's hash table.
2407          */
2408         cx = jp->sprinter.context;
2409         nargs = fun->nargs;
2410         mark = JS_ARENA_MARK(&cx->tempPool);
2411         JS_ARENA_ALLOCATE_CAST(params, JSAtom **, &cx->tempPool,
2412                                nargs * sizeof(JSAtom *));
2413         if (!params) {
2414             JS_ReportOutOfMemory(cx);
2415             return JS_FALSE;
2416         }
2417         scope = OBJ_SCOPE(fun->object);
2418         for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
2419             if (sprop->getter != js_GetArgument)
2420                 continue;
2421             JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
2422             JS_ASSERT((uintN) sprop->shortid < nargs);
2423             JS_ASSERT(!JSVAL_IS_INT(sprop->id));
2424             params[(uintN) sprop->shortid] = (JSAtom *) sprop->id;
2425         }
2426         for (i = 0; i < nargs; i++) {
2427             if (i > 0)
2428                 js_puts(jp, ", ");
2429             if (!QuoteString(&jp->sprinter, ATOM_TO_STRING(params[i]), 0))
2430                 return JS_FALSE;
2431         }
2432         JS_ARENA_RELEASE(&cx->tempPool, mark);
2433 #ifdef __GNUC__
2434     } else {
2435         scope = NULL;
2436 #endif
2437     }
2439     js_printf(jp, ") {\n");
2440     indent = jp->indent;
2441     jp->indent += 4;
2442     if (fun->script && fun->object) {
2443         oldscope = jp->scope;
2444         jp->scope = scope;
2445         ok = js_DecompileScript(jp, fun->script);
2446         jp->scope = oldscope;
2447         if (!ok) {
2448             jp->indent = indent;
2449             return JS_FALSE;
2450         }
2451     } else {
2452         js_printf(jp, native_code_str);
2453     }
2454     jp->indent -= 4;
2455     js_printf(jp, "\t}");
2457     if (jp->pretty) {
2458         js_puts(jp, "\n");
2459     } else {
2460         if (fun->flags & JSFUN_LAMBDA)
2461             js_puts(jp, ")");
2462     }
2463     return JS_TRUE;
2466 JSString *
2467 js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
2468                            JSString *fallback)
2470     JSStackFrame *fp, *down;
2471     jsbytecode *pc, *begin, *end, *tmp;
2472     jsval *sp, *base, *limit;
2473     JSScript *script;
2474     JSOp op;
2475     const JSCodeSpec *cs;
2476     uint32 format, mode;
2477     intN depth;
2478     jssrcnote *sn;
2479     uintN len, off;
2480     JSPrinter *jp;
2481     JSString *name;
2483     fp = cx->fp;
2484     if (!fp)
2485         goto do_fallback;
2487     /* Try to find sp's generating pc depth slots under it on the stack. */
2488     pc = fp->pc;
2489     if (spindex == JSDVG_SEARCH_STACK) {
2490         if (!pc) {
2491             /*
2492              * Current frame is native: look under it for a scripted call
2493              * in which a decompilable bytecode string that generated the
2494              * value as an actual argument might exist.
2495              */
2496             JS_ASSERT(!fp->script && fp->fun && fp->fun->native);
2497             down = fp->down;
2498             if (!down)
2499                 goto do_fallback;
2500             script = down->script;
2501             base = fp->argv;
2502             limit = base + fp->argc;
2503         } else {
2504             /*
2505              * This should be a script activation, either a top-level
2506              * script or a scripted function.  But be paranoid about calls
2507              * to js_DecompileValueGenerator from code that hasn't fully
2508              * initialized a (default-all-zeroes) frame.
2509              */
2510             script = fp->script;
2511             base = fp->spbase;
2512             limit = fp->sp;
2513         }
2515         /*
2516          * Pure paranoia about default-zeroed frames being active while
2517          * js_DecompileValueGenerator is called.  It can't hurt much now;
2518          * error reporting performance is not an issue.
2519          */
2520         if (!script || !base || !limit)
2521             goto do_fallback;
2523         /*
2524          * Try to find operand-generating pc depth slots below sp.
2525          *
2526          * In the native case, we know the arguments have generating pc's
2527          * under them, on account of fp->down->script being non-null: all
2528          * compiled scripts get depth slots for generating pc's allocated
2529          * upon activation, at the top of js_Interpret.
2530          *
2531          * In the script or scripted function case, the same reasoning
2532          * applies to fp rather than to fp->down.
2533          */
2534         for (sp = base; sp < limit; sp++) {
2535             if (*sp == v) {
2536                 depth = (intN)script->depth;
2537                 pc = (jsbytecode *) sp[-depth];
2538                 break;
2539             }
2540         }
2541     } else {
2542         /*
2543          * At this point, pc may or may not be null, i.e., we could be in
2544          * a script activation, or we could be in a native frame that was
2545          * called by another native function.  Check pc and script.
2546          */
2547         if (!pc)
2548             goto do_fallback;
2549         script = fp->script;
2550         if (!script)
2551             goto do_fallback;
2553         if (spindex != JSDVG_IGNORE_STACK) {
2554             JS_ASSERT(spindex < 0);
2555             depth = (intN)script->depth;
2556 #if !JS_HAS_NO_SUCH_METHOD
2557             JS_ASSERT(-depth <= spindex);
2558 #endif
2559             spindex -= depth;
2561             base = (jsval *) cx->stackPool.current->base;
2562             limit = (jsval *) cx->stackPool.current->avail;
2563             sp = fp->sp + spindex;
2564             if (JS_UPTRDIFF(sp, base) < JS_UPTRDIFF(limit, base))
2565                 pc = (jsbytecode *) *sp;
2566         }
2567     }
2569     /*
2570      * Again, be paranoid, this time about possibly loading an invalid pc
2571      * from sp[-(1+depth)].
2572      */
2573     if (JS_UPTRDIFF(pc, script->code) >= (jsuword)script->length) {
2574         pc = fp->pc;
2575         if (!pc)
2576             goto do_fallback;
2577     }
2578     op = (JSOp) *pc;
2579     if (op == JSOP_TRAP)
2580         op = JS_GetTrapOpcode(cx, script, pc);
2582     /* XXX handle null as a special case, to avoid calling null "object" */
2583     if (op == JSOP_NULL)
2584         return ATOM_TO_STRING(cx->runtime->atomState.nullAtom);
2586     cs = &js_CodeSpec[op];
2587     format = cs->format;
2588     mode = (format & JOF_MODEMASK);
2590     /* NAME ops are self-contained, but others require left context. */
2591     if (mode == JOF_NAME) {
2592         begin = pc;
2593     } else {
2594         sn = js_GetSrcNote(script, pc);
2595         if (!sn || SN_TYPE(sn) != SRC_PCBASE)
2596             goto do_fallback;
2597         begin = pc - js_GetSrcNoteOffset(sn, 0);
2598     }
2599     end = pc + cs->length;
2600     len = PTRDIFF(end, begin, jsbytecode);
2602     if (format & (JOF_SET | JOF_DEL | JOF_INCDEC | JOF_IMPORT | JOF_FOR)) {
2603         tmp = (jsbytecode *) JS_malloc(cx, len * sizeof(jsbytecode));
2604         if (!tmp)
2605             return NULL;
2606         memcpy(tmp, begin, len * sizeof(jsbytecode));
2607         if (mode == JOF_NAME) {
2608             tmp[0] = JSOP_NAME;
2609         } else {
2610             /*
2611              * We must replace the faulting pc's bytecode with a corresponding
2612              * JSOP_GET* code.  For JSOP_SET{PROP,ELEM}, we must use the "2nd"
2613              * form of JSOP_GET{PROP,ELEM}, to throw away the assignment op's
2614              * right-hand operand and decompile it as if it were a GET of its
2615              * left-hand operand.
2616              */
2617             off = len - cs->length;
2618             JS_ASSERT(off == (uintN) PTRDIFF(pc, begin, jsbytecode));
2619             if (mode == JOF_PROP) {
2620                 tmp[off] = (format & JOF_SET) ? JSOP_GETPROP2 : JSOP_GETPROP;
2621             } else if (mode == JOF_ELEM) {
2622                 tmp[off] = (format & JOF_SET) ? JSOP_GETELEM2 : JSOP_GETELEM;
2623             } else {
2624                 /*
2625                  * A zero mode means precisely that op is uncategorized for our
2626                  * purposes, so we must write per-op special case code here.
2627                  */
2628                 switch (op) {
2629                   case JSOP_ENUMELEM:
2630                     tmp[off] = JSOP_GETELEM;
2631                     break;
2632 #if JS_HAS_LVALUE_RETURN
2633                   case JSOP_SETCALL:
2634                     tmp[off] = JSOP_CALL;
2635                     break;
2636 #endif
2637                   default:
2638                     JS_ASSERT(0);
2639                 }
2640             }
2641         }
2642         begin = tmp;
2643     } else {
2644         /* No need to revise script bytecode. */
2645         tmp = NULL;
2646     }
2648     jp = js_NewPrinter(cx, "js_DecompileValueGenerator", 0, JS_FALSE);
2649     if (jp && js_DecompileCode(jp, script, begin, len))
2650         name = js_GetPrinterOutput(jp);
2651     else
2652         name = NULL;
2653     js_DestroyPrinter(jp);
2654     if (tmp)
2655         JS_free(cx, tmp);
2656     return name;
2658   do_fallback:
2659     return fallback ? fallback : js_ValueToString(cx, v);