Code

fix 1243587 and misc fixes
[inkscape.git] / src / dom / js / jsopcode.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set sw=4 ts=8 et tw=80:
3  *
4  * ***** BEGIN LICENSE BLOCK *****
5  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is Mozilla Communicator client code, released
18  * March 31, 1998.
19  *
20  * The Initial Developer of the Original Code is
21  * Netscape Communications Corporation.
22  * Portions created by the Initial Developer are Copyright (C) 1998
23  * the Initial Developer. All Rights Reserved.
24  *
25  * Contributor(s):
26  *
27  * Alternatively, the contents of this file may be used under the terms of
28  * either of the GNU General Public License Version 2 or later (the "GPL"),
29  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30  * in which case the provisions of the GPL or the LGPL are applicable instead
31  * of those above. If you wish to allow use of your version of this file only
32  * under the terms of either the GPL or the LGPL, and not to allow others to
33  * use your version of this file under the terms of the MPL, indicate your
34  * decision by deleting the provisions above and replace them with the notice
35  * and other provisions required by the GPL or the LGPL. If you do not delete
36  * the provisions above, a recipient may use your version of this file under
37  * the terms of any one of the MPL, the GPL or the LGPL.
38  *
39  * ***** END LICENSE BLOCK ***** */
41 /*
42  * JS bytecode descriptors, disassemblers, and decompilers.
43  */
44 #include "jsstddef.h"
45 #ifdef HAVE_MEMORY_H
46 #include <memory.h>
47 #endif
48 #include <stdarg.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include "jstypes.h"
53 #include "jsarena.h" /* Added by JSIFY */
54 #include "jsutil.h" /* Added by JSIFY */
55 #include "jsdtoa.h"
56 #include "jsprf.h"
57 #include "jsapi.h"
58 #include "jsarray.h"
59 #include "jsatom.h"
60 #include "jscntxt.h"
61 #include "jsconfig.h"
62 #include "jsdbgapi.h"
63 #include "jsemit.h"
64 #include "jsfun.h"
65 #include "jslock.h"
66 #include "jsobj.h"
67 #include "jsopcode.h"
68 #include "jsregexp.h"
69 #include "jsscope.h"
70 #include "jsscript.h"
71 #include "jsstr.h"
73 const char js_const_str[]       = "const";
74 const char js_var_str[]         = "var";
75 const char js_function_str[]    = "function";
76 const char js_in_str[]          = "in";
77 const char js_instanceof_str[]  = "instanceof";
78 const char js_new_str[]         = "new";
79 const char js_delete_str[]      = "delete";
80 const char js_typeof_str[]      = "typeof";
81 const char js_void_str[]        = "void";
82 const char js_null_str[]        = "null";
83 const char js_this_str[]        = "this";
84 const char js_false_str[]       = "false";
85 const char js_true_str[]        = "true";
86 const char js_default_str[]     = "default";
88 const char *js_incop_str[]      = {"++", "--"};
90 /* Pollute the namespace locally for MSVC Win16, but not for WatCom.  */
91 #ifdef __WINDOWS_386__
92     #ifdef FAR
93         #undef FAR
94     #endif
95 #else  /* !__WINDOWS_386__ */
96 #ifndef FAR
97 #define FAR
98 #endif
99 #endif /* !__WINDOWS_386__ */
101 const JSCodeSpec FAR js_CodeSpec[] = {
102 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
103     {name,token,length,nuses,ndefs,prec,format},
104 #include "jsopcode.tbl"
105 #undef OPDEF
106 };
108 uintN js_NumCodeSpecs = sizeof (js_CodeSpec) / sizeof js_CodeSpec[0];
110 /************************************************************************/
112 static ptrdiff_t
113 GetJumpOffset(jsbytecode *pc, jsbytecode *pc2)
115     uint32 type;
117     type = (js_CodeSpec[*pc].format & JOF_TYPEMASK);
118     if (JOF_TYPE_IS_EXTENDED_JUMP(type))
119         return GET_JUMPX_OFFSET(pc2);
120     return GET_JUMP_OFFSET(pc2);
123 #ifdef DEBUG
125 JS_FRIEND_API(JSBool)
126 js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp)
128     jsbytecode *pc, *end;
129     uintN len;
131     pc = script->code;
132     end = pc + script->length;
133     while (pc < end) {
134         if (pc == script->main)
135             fputs("main:\n", fp);
136         len = js_Disassemble1(cx, script, pc,
137                               PTRDIFF(pc, script->code, jsbytecode),
138                               lines, fp);
139         if (!len)
140             return JS_FALSE;
141         pc += len;
142     }
143     return JS_TRUE;
146 JS_FRIEND_API(uintN)
147 js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
148                 JSBool lines, FILE *fp)
150     JSOp op;
151     const JSCodeSpec *cs;
152     ptrdiff_t len, off, jmplen;
153     uint32 type;
154     JSAtom *atom;
155     JSString *str;
157     op = (JSOp)*pc;
158     if (op >= JSOP_LIMIT) {
159         char numBuf1[12], numBuf2[12];
160         JS_snprintf(numBuf1, sizeof numBuf1, "%d", op);
161         JS_snprintf(numBuf2, sizeof numBuf2, "%d", JSOP_LIMIT);
162         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
163                              JSMSG_BYTECODE_TOO_BIG, numBuf1, numBuf2);
164         return 0;
165     }
166     cs = &js_CodeSpec[op];
167     len = (ptrdiff_t) cs->length;
168     fprintf(fp, "%05u:", loc);
169     if (lines)
170         fprintf(fp, "%4u", JS_PCToLineNumber(cx, script, pc));
171     fprintf(fp, "  %s", cs->name);
172     type = cs->format & JOF_TYPEMASK;
173     switch (type) {
174       case JOF_BYTE:
175         if (op == JSOP_TRAP) {
176             op = JS_GetTrapOpcode(cx, script, pc);
177             if (op == JSOP_LIMIT)
178                 return 0;
179             len = (ptrdiff_t) js_CodeSpec[op].length;
180         }
181         break;
183       case JOF_JUMP:
184       case JOF_JUMPX:
185         off = GetJumpOffset(pc, pc);
186         fprintf(fp, " %u (%d)", loc + off, off);
187         break;
189       case JOF_CONST:
190         atom = GET_ATOM(cx, script, pc);
191         str = js_ValueToSource(cx, ATOM_KEY(atom));
192         if (!str)
193             return 0;
194         fprintf(fp, " %s", JS_GetStringBytes(str));
195         break;
197       case JOF_UINT16:
198         fprintf(fp, " %u", GET_ARGC(pc));
199         break;
201 #if JS_HAS_SWITCH_STATEMENT
202       case JOF_TABLESWITCH:
203       case JOF_TABLESWITCHX:
204       {
205         jsbytecode *pc2;
206         jsint i, low, high;
208         jmplen = (type == JOF_TABLESWITCH) ? JUMP_OFFSET_LEN
209                                            : JUMPX_OFFSET_LEN;
210         pc2 = pc;
211         off = GetJumpOffset(pc, pc2);
212         pc2 += jmplen;
213         low = GET_JUMP_OFFSET(pc2);
214         pc2 += JUMP_OFFSET_LEN;
215         high = GET_JUMP_OFFSET(pc2);
216         pc2 += JUMP_OFFSET_LEN;
217         fprintf(fp, " defaultOffset %d low %d high %d", off, low, high);
218         for (i = low; i <= high; i++) {
219             off = GetJumpOffset(pc, pc2);
220             fprintf(fp, "\n\t%d: %d", i, off);
221             pc2 += jmplen;
222         }
223         len = 1 + pc2 - pc;
224         break;
225       }
227       case JOF_LOOKUPSWITCH:
228       case JOF_LOOKUPSWITCHX:
229       {
230         jsbytecode *pc2;
231         jsatomid npairs;
233         jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN
234                                             : JUMPX_OFFSET_LEN;
235         pc2 = pc;
236         off = GetJumpOffset(pc, pc2);
237         pc2 += jmplen;
238         npairs = GET_ATOM_INDEX(pc2);
239         pc2 += ATOM_INDEX_LEN;
240         fprintf(fp, " offset %d npairs %u", off, (uintN) npairs);
241         while (npairs) {
242             atom = GET_ATOM(cx, script, pc2);
243             pc2 += ATOM_INDEX_LEN;
244             off = GetJumpOffset(pc, pc2);
245             pc2 += jmplen;
247             str = js_ValueToSource(cx, ATOM_KEY(atom));
248             if (!str)
249                 return 0;
250             fprintf(fp, "\n\t%s: %d", JS_GetStringBytes(str), off);
251             npairs--;
252         }
253         len = 1 + pc2 - pc;
254         break;
255       }
256 #endif /* JS_HAS_SWITCH_STATEMENT */
258       case JOF_QARG:
259         fprintf(fp, " %u", GET_ARGNO(pc));
260         break;
262       case JOF_QVAR:
263         fprintf(fp, " %u", GET_VARNO(pc));
264         break;
266 #if JS_HAS_LEXICAL_CLOSURE
267       case JOF_INDEXCONST:
268         fprintf(fp, " %u", GET_VARNO(pc));
269         pc += VARNO_LEN;
270         atom = GET_ATOM(cx, script, pc);
271         str = js_ValueToSource(cx, ATOM_KEY(atom));
272         if (!str)
273             return 0;
274         fprintf(fp, " %s", JS_GetStringBytes(str));
275         break;
276 #endif
278       case JOF_UINT24:
279         if (op == JSOP_FINDNAME) {
280             /* Special case to avoid a JOF_FINDNAME just for this op. */
281             atom = js_GetAtom(cx, &script->atomMap, GET_LITERAL_INDEX(pc));
282             str = js_ValueToSource(cx, ATOM_KEY(atom));
283             if (!str)
284                 return 0;
285             fprintf(fp, " %s", JS_GetStringBytes(str));
286             break;
287         }
289         JS_ASSERT(op == JSOP_UINT24 || op == JSOP_LITERAL);
290         fprintf(fp, " %u", GET_LITERAL_INDEX(pc));
291         break;
293       case JOF_LITOPX:
294         atom = js_GetAtom(cx, &script->atomMap, GET_LITERAL_INDEX(pc));
295         str = js_ValueToSource(cx, ATOM_KEY(atom));
296         if (!str)
297             return 0;
299         /*
300          * Bytecode: JSOP_LITOPX <uint24> op [<varno> if JSOP_DEFLOCALFUN].
301          * Advance pc to point at op.
302          */
303         pc += 1 + LITERAL_INDEX_LEN;
304         op = *pc;
305         cs = &js_CodeSpec[op];
306         fprintf(fp, " %s op %s", JS_GetStringBytes(str), cs->name);
307 #if JS_HAS_LEXICAL_CLOSURE
308         if ((cs->format & JOF_TYPEMASK) == JOF_INDEXCONST)
309             fprintf(fp, " %u", GET_VARNO(pc));
310 #endif
312         /*
313          * Set len to advance pc to skip op and any other immediates (namely,
314          * <varno> if JSOP_DEFLOCALFUN).
315          */
316         JS_ASSERT(cs->length > ATOM_INDEX_LEN);
317         len = cs->length - ATOM_INDEX_LEN;
318         break;
320       default: {
321         char numBuf[12];
322         JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) cs->format);
323         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
324                              JSMSG_UNKNOWN_FORMAT, numBuf);
325         return 0;
326       }
327     }
328     fputs("\n", fp);
329     return len;
332 #endif /* DEBUG */
334 /************************************************************************/
336 /*
337  * Sprintf, but with unlimited and automatically allocated buffering.
338  */
339 typedef struct Sprinter {
340     JSContext       *context;       /* context executing the decompiler */
341     JSArenaPool     *pool;          /* string allocation pool */
342     char            *base;          /* base address of buffer in pool */
343     size_t          size;           /* size of buffer allocated at base */
344     ptrdiff_t       offset;         /* offset of next free char in buffer */
345 } Sprinter;
347 #define INIT_SPRINTER(cx, sp, ap, off) \
348     ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0,  \
349      (sp)->offset = off)
351 #define OFF2STR(sp,off) ((sp)->base + (off))
352 #define STR2OFF(sp,str) ((str) - (sp)->base)
353 #define RETRACT(sp,str) ((sp)->offset = STR2OFF(sp, str))
355 static JSBool
356 SprintAlloc(Sprinter *sp, size_t nb)
358     if (!sp->base) {
359         JS_ARENA_ALLOCATE_CAST(sp->base, char *, sp->pool, nb);
360     } else {
361         JS_ARENA_GROW_CAST(sp->base, char *, sp->pool, sp->size, nb);
362     }
363     if (!sp->base) {
364         JS_ReportOutOfMemory(sp->context);
365         return JS_FALSE;
366     }
367     sp->size += nb;
368     return JS_TRUE;
371 static ptrdiff_t
372 SprintPut(Sprinter *sp, const char *s, size_t len)
374     ptrdiff_t nb, offset;
375     char *bp;
377     /* Allocate space for s, including the '\0' at the end. */
378     nb = (sp->offset + len + 1) - sp->size;
379     if (nb > 0 && !SprintAlloc(sp, nb))
380         return -1;
382     /* Advance offset and copy s into sp's buffer. */
383     offset = sp->offset;
384     sp->offset += len;
385     bp = sp->base + offset;
386     memmove(bp, s, len);
387     bp[len] = 0;
388     return offset;
391 static ptrdiff_t
392 Sprint(Sprinter *sp, const char *format, ...)
394     va_list ap;
395     char *bp;
396     ptrdiff_t offset;
398     va_start(ap, format);
399     bp = JS_vsmprintf(format, ap);      /* XXX vsaprintf */
400     va_end(ap);
401     if (!bp) {
402         JS_ReportOutOfMemory(sp->context);
403         return -1;
404     }
405     offset = SprintPut(sp, bp, strlen(bp));
406     free(bp);
407     return offset;
410 const jschar js_EscapeMap[] = {
411     '\b', 'b',
412     '\f', 'f',
413     '\n', 'n',
414     '\r', 'r',
415     '\t', 't',
416     '\v', 'v',
417     '"',  '"',
418     '\'', '\'',
419     '\\', '\\',
420     0
421 };
423 static char *
424 QuoteString(Sprinter *sp, JSString *str, jschar quote)
426     ptrdiff_t off, len, nb;
427     const jschar *s, *t, *u, *z;
428     char *bp;
429     jschar c;
430     JSBool ok;
432     /* Sample off first for later return value pointer computation. */
433     off = sp->offset;
434     if (quote && Sprint(sp, "%c", (char)quote) < 0)
435         return NULL;
437     /* Loop control variables: z points at end of string sentinel. */
438     s = JSSTRING_CHARS(str);
439     z = s + JSSTRING_LENGTH(str);
440     for (t = s; t < z; s = ++t) {
441         /* Move t forward from s past un-quote-worthy characters. */
442         c = *t;
443         while (JS_ISPRINT(c) && c != quote && c != '\\' && !(c >> 8)) {
444             c = *++t;
445             if (t == z)
446                 break;
447         }
448         len = PTRDIFF(t, s, jschar);
450         /* Allocate space for s, including the '\0' at the end. */
451         nb = (sp->offset + len + 1) - sp->size;
452         if (nb > 0 && !SprintAlloc(sp, nb))
453             return NULL;
455         /* Advance sp->offset and copy s into sp's buffer. */
456         bp = sp->base + sp->offset;
457         sp->offset += len;
458         while (--len >= 0)
459             *bp++ = (char) *s++;
460         *bp = '\0';
462         if (t == z)
463             break;
465         /* Use js_EscapeMap, \u, or \x only if necessary. */
466         if ((u = js_strchr(js_EscapeMap, c)) != NULL) {
467             ok = Sprint(sp, "\\%c", (char)u[1]) >= 0;
468         } else {
469 #ifdef JS_C_STRINGS_ARE_UTF8
470             /* If this is a surrogate pair, make sure to print the pair. */
471             if (c >= 0xD800 && c <= 0xDBFF) {
472                 jschar buffer[3];
473                 buffer[0] = c;
474                 buffer[1] = *++t;
475                 buffer[2] = 0;
476                 if (t == z) {
477                     char numbuf[10];
478                     JS_snprintf(numbuf, sizeof numbuf, "0x%x", c);
479                     JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR,
480                                                  js_GetErrorMessage, NULL,
481                                                  JSMSG_BAD_SURROGATE_CHAR,
482                                                  numbuf);
483                     ok = JS_FALSE;
484                     break;
485                 }
486                 ok = Sprint(sp, "%hs", buffer) >= 0;
487             } else {
488                 /* Print as UTF-8 string. */
489                 ok = Sprint(sp, "%hc", c) >= 0;
490             }
491 #else
492             /* Use \uXXXX or \xXX  if the string can't be displayed as UTF-8. */
493             ok = Sprint(sp, (c >> 8) ? "\\u%04X" : "\\x%02X", c) >= 0;
494 #endif
495         }
496         if (!ok)
497             return NULL;
498     }
500     /* Sprint the closing quote and return the quoted string. */
501     if (quote && Sprint(sp, "%c", (char)quote) < 0)
502         return NULL;
504     /*
505      * If we haven't Sprint'd anything yet, Sprint an empty string so that
506      * the OFF2STR below gives a valid result.
507      */
508     if (off == sp->offset && Sprint(sp, "") < 0)
509         return NULL;
510     return OFF2STR(sp, off);
513 JSString *
514 js_QuoteString(JSContext *cx, JSString *str, jschar quote)
516     void *mark;
517     Sprinter sprinter;
518     char *bytes;
519     JSString *escstr;
521     mark = JS_ARENA_MARK(&cx->tempPool);
522     INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);
523     bytes = QuoteString(&sprinter, str, quote);
524     escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL;
525     JS_ARENA_RELEASE(&cx->tempPool, mark);
526     return escstr;
529 /************************************************************************/
531 struct JSPrinter {
532     Sprinter        sprinter;       /* base class state */
533     JSArenaPool     pool;           /* string allocation pool */
534     uintN           indent;         /* indentation in spaces */
535     JSPackedBool    pretty;         /* pretty-print: indent, use newlines */
536     JSPackedBool    grouped;        /* in parenthesized expression context */
537     JSScript        *script;        /* script being printed */
538     JSScope         *scope;         /* script function scope */
539 };
541 /*
542  * Hack another flag, a la JS_DONT_PRETTY_PRINT, into uintN indent parameters
543  * to functions such as js_DecompileFunction and js_NewPrinter.  This time, as
544  * opposed to JS_DONT_PRETTY_PRINT back in the dark ages, we can assume that a
545  * uintN is at least 32 bits.
546  */
547 #define JS_IN_GROUP_CONTEXT 0x10000
549 JSPrinter *
550 js_NewPrinter(JSContext *cx, const char *name, uintN indent, JSBool pretty)
552     JSPrinter *jp;
554     jp = (JSPrinter *) JS_malloc(cx, sizeof(JSPrinter));
555     if (!jp)
556         return NULL;
557     INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
558     JS_InitArenaPool(&jp->pool, name, 256, 1);
559     jp->indent = indent & ~JS_IN_GROUP_CONTEXT;
560     jp->pretty = pretty;
561     jp->grouped = (indent & JS_IN_GROUP_CONTEXT) != 0;
562     jp->script = NULL;
563     jp->scope = NULL;
564     return jp;
567 void
568 js_DestroyPrinter(JSPrinter *jp)
570     JS_FinishArenaPool(&jp->pool);
571     JS_free(jp->sprinter.context, jp);
574 JSString *
575 js_GetPrinterOutput(JSPrinter *jp)
577     JSContext *cx;
578     JSString *str;
580     cx = jp->sprinter.context;
581     if (!jp->sprinter.base)
582         return cx->runtime->emptyString;
583     str = JS_NewStringCopyZ(cx, jp->sprinter.base);
584     if (!str)
585         return NULL;
586     JS_FreeArenaPool(&jp->pool);
587     INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
588     return str;
591 int
592 js_printf(JSPrinter *jp, const char *format, ...)
594     va_list ap;
595     char *bp, *fp;
596     int cc;
598     if (*format == '\0')
599         return 0;
601     va_start(ap, format);
603     /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */
604     if (*format == '\t') {
605         if (jp->pretty && Sprint(&jp->sprinter, "%*s", jp->indent, "") < 0)
606             return -1;
607         format++;
608     }
610     /* Suppress newlines (must be once per format, at the end) if not pretty. */
611     fp = NULL;
612     if (!jp->pretty && format[cc = strlen(format) - 1] == '\n') {
613         fp = JS_strdup(jp->sprinter.context, format);
614         if (!fp)
615             return -1;
616         fp[cc] = '\0';
617         format = fp;
618     }
620     /* Allocate temp space, convert format, and put. */
621     bp = JS_vsmprintf(format, ap);      /* XXX vsaprintf */
622     if (fp) {
623         JS_free(jp->sprinter.context, fp);
624         format = NULL;
625     }
626     if (!bp) {
627         JS_ReportOutOfMemory(jp->sprinter.context);
628         return -1;
629     }
631     cc = strlen(bp);
632     if (SprintPut(&jp->sprinter, bp, (size_t)cc) < 0)
633         cc = -1;
634     free(bp);
636     va_end(ap);
637     return cc;
640 JSBool
641 js_puts(JSPrinter *jp, const char *s)
643     return SprintPut(&jp->sprinter, s, strlen(s)) >= 0;
646 /************************************************************************/
648 typedef struct SprintStack {
649     Sprinter    sprinter;       /* sprinter for postfix to infix buffering */
650     ptrdiff_t   *offsets;       /* stack of postfix string offsets */
651     jsbytecode  *opcodes;       /* parallel stack of JS opcodes */
652     uintN       top;            /* top of stack index */
653     JSPrinter   *printer;       /* permanent output goes here */
654 } SprintStack;
656 /* Gap between stacked strings to allow for insertion of parens and commas. */
657 #define PAREN_SLOP      (2 + 1)
659 /*
660  * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME,
661  * JSOP_SETPROP, and JSOP_SETELEM, respectively.  See the first assertion in
662  * PushOff.
663  */
664 #define JSOP_GETPROP2   254
665 #define JSOP_GETELEM2   255
667 static JSBool
668 PushOff(SprintStack *ss, ptrdiff_t off, JSOp op)
670     uintN top;
672 #if JSOP_LIMIT > JSOP_GETPROP2
673 #error JSOP_LIMIT must be <= JSOP_GETPROP2
674 #endif
675     if (!SprintAlloc(&ss->sprinter, PAREN_SLOP))
676         return JS_FALSE;
678     /* ss->top points to the next free slot; be paranoid about overflow. */
679     top = ss->top;
680     JS_ASSERT(top < ss->printer->script->depth);
681     if (top >= ss->printer->script->depth) {
682         JS_ReportOutOfMemory(ss->sprinter.context);
683         return JS_FALSE;
684     }
686     /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */
687     ss->offsets[top] = off;
688     ss->opcodes[top] = (op == JSOP_GETPROP2) ? JSOP_GETPROP
689                      : (op == JSOP_GETELEM2) ? JSOP_GETELEM
690                      : (jsbytecode) op;
691     ss->top = ++top;
692     ss->sprinter.offset += PAREN_SLOP;
693     return JS_TRUE;
696 static ptrdiff_t
697 PopOff(SprintStack *ss, JSOp op)
699     uintN top;
700     const JSCodeSpec *cs, *topcs;
701     ptrdiff_t off;
703     /* ss->top points to the next free slot; be paranoid about underflow. */
704     top = ss->top;
705     JS_ASSERT(top != 0);
706     if (top == 0)
707         return 0;
709     ss->top = --top;
710     topcs = &js_CodeSpec[ss->opcodes[top]];
711     cs = &js_CodeSpec[op];
712     if (topcs->prec != 0 && topcs->prec < cs->prec) {
713         ss->offsets[top] -= 2;
714         ss->sprinter.offset = ss->offsets[top];
715         off = Sprint(&ss->sprinter, "(%s)",
716                      OFF2STR(&ss->sprinter, ss->sprinter.offset + 2));
717     } else {
718         off = ss->sprinter.offset = ss->offsets[top];
719     }
720     return off;
723 #if JS_HAS_SWITCH_STATEMENT
724 typedef struct TableEntry {
725     jsval       key;
726     ptrdiff_t   offset;
727     JSAtom      *label;
728     jsint       order;          /* source order for stable tableswitch sort */
729 } TableEntry;
731 static int
732 CompareOffsets(const void *v1, const void *v2, void *arg)
734     const TableEntry *te1 = (const TableEntry *) v1,
735                      *te2 = (const TableEntry *) v2;
737     if (te1->offset == te2->offset)
738         return (int) (te1->order - te2->order);
739     return (int) (te1->offset - te2->offset);
742 static JSBool
743 Decompile(SprintStack *ss, jsbytecode *pc, intN nb);
745 static JSBool
746 DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength,
747                 jsbytecode *pc, ptrdiff_t switchLength,
748                 ptrdiff_t defaultOffset, JSBool isCondSwitch)
750     JSContext *cx;
751     JSPrinter *jp;
752     char *lval, *rval;
753     uintN i;
754     ptrdiff_t diff, off, off2, caseExprOff;
755     jsval key;
756     JSString *str;
758     cx = ss->sprinter.context;
759     jp = ss->printer;
761     lval = OFF2STR(&ss->sprinter, PopOff(ss, JSOP_NOP));
762     js_printf(jp, "\tswitch (%s) {\n", lval);
764     if (tableLength) {
765         diff = table[0].offset - defaultOffset;
766         if (diff > 0) {
767             jp->indent += 2;
768             js_printf(jp, "\t%s:\n", js_default_str);
769             jp->indent += 2;
770             if (!Decompile(ss, pc + defaultOffset, diff))
771                 return JS_FALSE;
772             jp->indent -= 4;
773         }
775         caseExprOff = isCondSwitch
776                       ? (ptrdiff_t) js_CodeSpec[JSOP_CONDSWITCH].length
777                       : 0;
779         for (i = 0; i < tableLength; i++) {
780             off = table[i].offset;
781             off2 = (i + 1 < tableLength) ? table[i + 1].offset : switchLength;
783             key = table[i].key;
784             if (isCondSwitch) {
785                 ptrdiff_t nextCaseExprOff;
787                 /*
788                  * key encodes the JSOP_CASE bytecode's offset from switchtop.
789                  * The next case expression follows immediately, unless we are
790                  * at the last case.
791                  */
792                 nextCaseExprOff = (ptrdiff_t)JSVAL_TO_INT(key);
793                 nextCaseExprOff += js_CodeSpec[pc[nextCaseExprOff]].length;
794                 jp->indent += 2;
795                 if (!Decompile(ss, pc + caseExprOff,
796                                nextCaseExprOff - caseExprOff)) {
797                     return JS_FALSE;
798                 }
799                 caseExprOff = nextCaseExprOff;
800             } else {
801                 /*
802                  * key comes from an atom, not the decompiler, so we need to
803                  * quote it if it's a string literal.  But if table[i].label
804                  * is non-null, key was constant-propagated and label is the
805                  * name of the const we should show as the case label.  We set
806                  * key to undefined so this identifier is escaped, if required
807                  * by non-ASCII characters, but not quoted, by QuoteString.
808                  */
809                 if (table[i].label) {
810                     str = ATOM_TO_STRING(table[i].label);
811                     key = JSVAL_VOID;
812                 } else {
813                     str = js_ValueToString(cx, key);
814                     if (!str)
815                         return JS_FALSE;
816                 }
817                 rval = QuoteString(&ss->sprinter, str,
818                                    (jschar)(JSVAL_IS_STRING(key) ? '"' : 0));
819                 if (!rval)
820                     return JS_FALSE;
821                 RETRACT(&ss->sprinter, rval);
822                 jp->indent += 2;
823                 js_printf(jp, "\tcase %s:\n", rval);
824             }
826             jp->indent += 2;
827             if (off <= defaultOffset && defaultOffset < off2) {
828                 diff = defaultOffset - off;
829                 if (diff != 0) {
830                     if (!Decompile(ss, pc + off, diff))
831                         return JS_FALSE;
832                     off = defaultOffset;
833                 }
834                 jp->indent -= 2;
835                 js_printf(jp, "\t%s:\n", js_default_str);
836                 jp->indent += 2;
837             }
838             if (!Decompile(ss, pc + off, off2 - off))
839                 return JS_FALSE;
840             jp->indent -= 4;
841         }
842     }
844     if (defaultOffset == switchLength) {
845         jp->indent += 2;
846         js_printf(jp, "\t%s:;\n", js_default_str);
847         jp->indent -= 2;
848     }
849     js_printf(jp, "\t}\n");
850     return JS_TRUE;
852 #endif
854 static JSAtom *
855 GetSlotAtom(JSPrinter *jp, JSPropertyOp getter, uintN slot)
857     JSScope *scope;
858     JSScopeProperty *sprop;
859     JSObject *obj, *proto;
861     scope = jp->scope;
862     while (scope) {
863         for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
864             if (sprop->getter != getter)
865                 continue;
866             JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
867             JS_ASSERT(JSID_IS_ATOM(sprop->id));
868             if ((uintN) sprop->shortid == slot)
869                 return JSID_TO_ATOM(sprop->id);
870         }
871         obj = scope->object;
872         if (!obj)
873             break;
874         proto = OBJ_GET_PROTO(jp->sprinter.context, obj);
875         if (!proto)
876             break;
877         scope = OBJ_SCOPE(proto);
878     }
879     return NULL;
882 static const char *
883 VarPrefix(jssrcnote *sn)
885     if (sn) {
886         if (SN_TYPE(sn) == SRC_VAR)
887             return "var ";
888         if (SN_TYPE(sn) == SRC_CONST)
889             return "const ";
890     }
891     return "";
894 static JSBool
895 Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
897     JSContext *cx;
898     JSPrinter *jp, *jp2;
899     jsbytecode *endpc, *done, *forelem_tail, *forelem_done;
900     ptrdiff_t tail, todo, len, oplen, cond, next;
901     JSOp op, lastop, saveop;
902     const JSCodeSpec *cs, *topcs;
903     jssrcnote *sn, *sn2;
904     const char *lval, *rval, *xval, *fmt;
905     jsint i, argc;
906     char **argv;
907     jsatomid atomIndex;
908     JSAtom *atom;
909     JSObject *obj;
910     JSFunction *fun;
911     JSString *str;
912     JSBool ok;
913 #if JS_HAS_XML_SUPPORT
914     JSBool foreach, inXML, quoteAttr;
915 #else
916 #define inXML JS_FALSE
917 #endif
918     jsval val;
919     int stackDummy;
920     static const char catch_cookie[] = "/*CATCH*/";
921     static const char with_cookie[] = "/*WITH*/";
923 /*
924  * Local macros
925  */
926 #define DECOMPILE_CODE(pc,nb)   if (!Decompile(ss, pc, nb)) return JS_FALSE
927 #define POP_STR()               OFF2STR(&ss->sprinter, PopOff(ss, op))
928 #define LOCAL_ASSERT(expr)      JS_ASSERT(expr); if (!(expr)) return JS_FALSE
930 /*
931  * Callers know that ATOM_IS_STRING(atom), and we leave it to the optimizer to
932  * common ATOM_TO_STRING(atom) here and near the call sites.
933  */
934 #define ATOM_IS_IDENTIFIER(atom)                                              \
935     (!ATOM_KEYWORD(atom) && js_IsIdentifier(ATOM_TO_STRING(atom)))
937 /*
938  * Given an atom already fetched from jp->script's atom map, quote/escape its
939  * string appropriately into rval, and select fmt from the quoted and unquoted
940  * alternatives.
941  */
942 #define GET_QUOTE_AND_FMT(qfmt, ufmt, rval)                                   \
943     JS_BEGIN_MACRO                                                            \
944         jschar quote_;                                                        \
945         if (!ATOM_IS_IDENTIFIER(atom)) {                                      \
946             quote_ = '\'';                                                    \
947             fmt = qfmt;                                                       \
948         } else {                                                              \
949             quote_ = 0;                                                       \
950             fmt = ufmt;                                                       \
951         }                                                                     \
952         rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), quote_);      \
953         if (!rval)                                                            \
954             return JS_FALSE;                                                  \
955     JS_END_MACRO
957 /*
958  * Get atom from jp->script's atom map, quote/escape its string appropriately
959  * into rval, and select fmt from the quoted and unquoted alternatives.
960  */
961 #define GET_ATOM_QUOTE_AND_FMT(qfmt, ufmt, rval)                              \
962     JS_BEGIN_MACRO                                                            \
963         atom = GET_ATOM(cx, jp->script, pc);                                  \
964         GET_QUOTE_AND_FMT(qfmt, ufmt, rval);                                  \
965     JS_END_MACRO
967     cx = ss->sprinter.context;
968     if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
969         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
970         return JS_FALSE;
971     }
973     jp = ss->printer;
974     endpc = pc + nb;
975     forelem_tail = forelem_done = NULL;
976     tail = -1;
977     todo = -2;                  /* NB: different from Sprint() error return. */
978     op = JSOP_NOP;
979     sn = NULL;
980     rval = NULL;
981 #if JS_HAS_XML_SUPPORT
982     foreach = inXML = quoteAttr = JS_FALSE;
983 #endif
985     while (pc < endpc) {
986         lastop = op;
987         op = saveop = (JSOp) *pc;
988         if (op >= JSOP_LIMIT) {
989             switch (op) {
990               case JSOP_GETPROP2:
991                 saveop = JSOP_GETPROP;
992                 break;
993               case JSOP_GETELEM2:
994                 saveop = JSOP_GETELEM;
995                 break;
996               default:;
997             }
998         }
999         cs = &js_CodeSpec[saveop];
1000         len = oplen = cs->length;
1002         if (cs->token) {
1003             switch (cs->nuses) {
1004               case 2:
1005                 rval = POP_STR();
1006                 lval = POP_STR();
1007                 sn = js_GetSrcNote(jp->script, pc);
1008                 if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
1009                     /* Print only the right operand of the assignment-op. */
1010                     todo = SprintPut(&ss->sprinter, rval, strlen(rval));
1011                 } else if (!inXML) {
1012                     todo = Sprint(&ss->sprinter, "%s %s %s",
1013                                   lval, cs->token, rval);
1014                 } else {
1015                     /* In XML, just concatenate the two operands. */
1016                     JS_ASSERT(op == JSOP_ADD);
1017                     todo = Sprint(&ss->sprinter, "%s%s", lval, rval);
1018                 }
1019                 break;
1021               case 1:
1022                 rval = POP_STR();
1023                 todo = Sprint(&ss->sprinter, "%s%s", cs->token, rval);
1024                 break;
1026               case 0:
1027                 todo = SprintPut(&ss->sprinter, cs->token, strlen(cs->token));
1028                 break;
1030               default:
1031                 todo = -2;
1032                 break;
1033             }
1034         } else {
1035             switch (op) {
1036 #define BEGIN_LITOPX_CASE(OP)                                                 \
1037               case OP:                                                        \
1038                 atomIndex = GET_ATOM_INDEX(pc);                               \
1039               do_##OP:                                                        \
1040                 atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex);
1042 #define END_LITOPX_CASE                                                       \
1043                 break;
1045               case JSOP_NOP:
1046                 /*
1047                  * Check for a do-while loop, a for-loop with an empty
1048                  * initializer part, a labeled statement, a function
1049                  * definition, or try/finally.
1050                  */
1051                 sn = js_GetSrcNote(jp->script, pc);
1052                 todo = -2;
1053                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
1054 #if JS_HAS_DO_WHILE_LOOP
1055                   case SRC_WHILE:
1056                     js_printf(jp, "\tdo {\n");
1057                     jp->indent += 4;
1058                     break;
1059 #endif /* JS_HAS_DO_WHILE_LOOP */
1061                   case SRC_FOR:
1062                     rval = "";
1064                   do_forloop:
1065                     /* Skip the JSOP_NOP or JSOP_POP bytecode. */
1066                     pc++;
1068                     /* Get the cond, next, and loop-closing tail offsets. */
1069                     cond = js_GetSrcNoteOffset(sn, 0);
1070                     next = js_GetSrcNoteOffset(sn, 1);
1071                     tail = js_GetSrcNoteOffset(sn, 2);
1072                     LOCAL_ASSERT(tail + GetJumpOffset(pc+tail, pc+tail) == 0);
1074                     /* Print the keyword and the possibly empty init-part. */
1075                     js_printf(jp, "\tfor (%s;", rval);
1077                     if (pc[cond] == JSOP_IFEQ || pc[cond] == JSOP_IFEQX) {
1078                         /* Decompile the loop condition. */
1079                         DECOMPILE_CODE(pc, cond);
1080                         js_printf(jp, " %s", POP_STR());
1081                     }
1083                     /* Need a semicolon whether or not there was a cond. */
1084                     js_puts(jp, ";");
1086                     if (pc[next] != JSOP_GOTO && pc[next] != JSOP_GOTOX) {
1087                         /* Decompile the loop updater. */
1088                         DECOMPILE_CODE(pc + next, tail - next - 1);
1089                         js_printf(jp, " %s", POP_STR());
1090                     }
1092                     /* Do the loop body. */
1093                     js_printf(jp, ") {\n");
1094                     jp->indent += 4;
1095                     oplen = (cond) ? js_CodeSpec[pc[cond]].length : 0;
1096                     DECOMPILE_CODE(pc + cond + oplen, next - cond - oplen);
1097                     jp->indent -= 4;
1098                     js_printf(jp, "\t}\n");
1100                     /* Set len so pc skips over the entire loop. */
1101                     len = tail + js_CodeSpec[pc[tail]].length;
1102                     break;
1104                   case SRC_LABEL:
1105                     atom = js_GetAtom(cx, &jp->script->atomMap,
1106                                       (jsatomid) js_GetSrcNoteOffset(sn, 0));
1107                     jp->indent -= 4;
1108                     rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1109                     if (!rval)
1110                         return JS_FALSE;
1111                     RETRACT(&ss->sprinter, rval);
1112                     js_printf(jp, "\t%s:\n", rval);
1113                     jp->indent += 4;
1114                     break;
1116                   case SRC_LABELBRACE:
1117                     atom = js_GetAtom(cx, &jp->script->atomMap,
1118                                       (jsatomid) js_GetSrcNoteOffset(sn, 0));
1119                     rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1120                     if (!rval)
1121                         return JS_FALSE;
1122                     RETRACT(&ss->sprinter, rval);
1123                     js_printf(jp, "\t%s: {\n", rval);
1124                     jp->indent += 4;
1125                     break;
1127                   case SRC_ENDBRACE:
1128                     jp->indent -= 4;
1129                     js_printf(jp, "\t}\n");
1130                     break;
1132                   case SRC_CATCH:
1133                     jp->indent -= 4;
1134                     sn = js_GetSrcNote(jp->script, pc);
1135                     pc += oplen;
1136                     js_printf(jp, "\t} catch (");
1138                     LOCAL_ASSERT(*pc == JSOP_NAME);
1139                     pc += js_CodeSpec[JSOP_NAME].length;
1140                     LOCAL_ASSERT(*pc == JSOP_PUSHOBJ);
1141                     pc += js_CodeSpec[JSOP_PUSHOBJ].length;
1142                     LOCAL_ASSERT(*pc == JSOP_NEWINIT);
1143                     pc += js_CodeSpec[JSOP_NEWINIT].length;
1144                     LOCAL_ASSERT(*pc == JSOP_EXCEPTION);
1145                     pc += js_CodeSpec[JSOP_EXCEPTION].length;
1146                     if (*pc == JSOP_LITOPX) {
1147                         atomIndex = GET_LITERAL_INDEX(pc);
1148                         pc += 1 + LITERAL_INDEX_LEN;
1149                         LOCAL_ASSERT(*pc == JSOP_INITCATCHVAR);
1150                         ++pc;
1151                     } else {
1152                         LOCAL_ASSERT(*pc == JSOP_INITCATCHVAR);
1153                         atomIndex = GET_ATOM_INDEX(pc);
1154                         pc += js_CodeSpec[JSOP_INITCATCHVAR].length;
1155                     }
1156                     atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex);
1157                     rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1158                     if (!rval)
1159                         return JS_FALSE;
1160                     RETRACT(&ss->sprinter, rval);
1161                     js_printf(jp, "%s", rval);
1162                     LOCAL_ASSERT(*pc == JSOP_ENTERWITH);
1163                     pc += js_CodeSpec[JSOP_ENTERWITH].length;
1165                     len = js_GetSrcNoteOffset(sn, 0);
1166                     if (len) {
1167                         js_printf(jp, " if ");
1168                         DECOMPILE_CODE(pc, len);
1169                         js_printf(jp, "%s", POP_STR());
1170                         pc += len;
1171                         LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
1172                         pc += js_CodeSpec[*pc].length;
1173                     }
1175                     js_printf(jp, ") {\n");
1176                     jp->indent += 4;
1177                     todo = Sprint(&ss->sprinter, catch_cookie);
1178                     len = 0;
1179                     break;
1181                   case SRC_FUNCDEF:
1182                     atom = js_GetAtom(cx, &jp->script->atomMap,
1183                                       (jsatomid) js_GetSrcNoteOffset(sn, 0));
1184                     JS_ASSERT(ATOM_IS_OBJECT(atom));
1185                   do_function:
1186                     obj = ATOM_TO_OBJECT(atom);
1187                     fun = (JSFunction *) JS_GetPrivate(cx, obj);
1188                     jp2 = js_NewPrinter(cx, JS_GetFunctionName(fun),
1189                                         jp->indent, jp->pretty);
1190                     if (!jp2)
1191                         return JS_FALSE;
1192                     jp2->scope = jp->scope;
1193                     js_puts(jp2, "\n");
1194                     ok = js_DecompileFunction(jp2, fun);
1195                     if (ok) {
1196                         js_puts(jp2, "\n");
1197                         str = js_GetPrinterOutput(jp2);
1198                         if (str)
1199                             js_printf(jp, "%s\n", JS_GetStringBytes(str));
1200                         else
1201                             ok = JS_FALSE;
1202                     }
1203                     js_DestroyPrinter(jp2);
1204                     if (!ok)
1205                         return JS_FALSE;
1207                     break;
1209                   default:;
1210                 }
1211               case JSOP_RETRVAL:
1212                 break;
1214               case JSOP_GROUP:
1215                 /* Use last real op so PopOff adds parens if needed. */
1216                 todo = PopOff(ss, lastop);
1218                 /* Now add user-supplied parens only if PopOff did not. */
1219                 cs    = &js_CodeSpec[lastop];
1220                 topcs = &js_CodeSpec[ss->opcodes[ss->top]];
1221                 if (topcs->prec >= cs->prec) {
1222                     todo = Sprint(&ss->sprinter, "(%s)",
1223                                   OFF2STR(&ss->sprinter, todo));
1224                 }
1225                 break;
1227               case JSOP_PUSH:
1228               case JSOP_PUSHOBJ:
1229               case JSOP_BINDNAME:
1230               do_JSOP_BINDNAME:
1231                 todo = Sprint(&ss->sprinter, "");
1232                 break;
1234 #if JS_HAS_EXCEPTIONS
1235               case JSOP_TRY:
1236                 js_printf(jp, "\ttry {\n");
1237                 jp->indent += 4;
1238                 todo = -2;
1239                 break;
1241             {
1242               static const char exception_cookie[] = "/*EXCEPTION*/";
1243               static const char retsub_pc_cookie[] = "/*RETSUB_PC*/";
1245               case JSOP_FINALLY:
1246                 jp->indent -= 4;
1247                 js_printf(jp, "\t} finally {\n");
1248                 jp->indent += 4;
1250                 /*
1251                  * We must push an empty string placeholder for gosub's return
1252                  * address, popped by JSOP_RETSUB and counted by script->depth
1253                  * but not by ss->top (see JSOP_SETSP, below).
1254                  */
1255                 todo = Sprint(&ss->sprinter, exception_cookie);
1256                 if (todo < 0 || !PushOff(ss, todo, op))
1257                     return JS_FALSE;
1258                 todo = Sprint(&ss->sprinter, retsub_pc_cookie);
1259                 break;
1261               case JSOP_RETSUB:
1262                 rval = POP_STR();
1263                 LOCAL_ASSERT(strcmp(rval, retsub_pc_cookie) == 0);
1264                 lval = POP_STR();
1265                 LOCAL_ASSERT(strcmp(lval, exception_cookie) == 0);
1266                 todo = -2;
1267                 break;
1268             }
1270               case JSOP_SWAP:
1271                 /*
1272                  * We don't generate this opcode currently, and previously we
1273                  * did not need to decompile it.  If old, serialized bytecode
1274                  * uses it still, we should fall through and set todo = -2.
1275                  */
1276                 /* FALL THROUGH */
1278               case JSOP_GOSUB:
1279               case JSOP_GOSUBX:
1280                 /*
1281                  * JSOP_GOSUB and GOSUBX have no effect on the decompiler's
1282                  * string stack because the next op in bytecode order finds
1283                  * the stack balanced by a JSOP_RETSUB executed elsewhere.
1284                  */
1285                 todo = -2;
1286                 break;
1288               case JSOP_SETSP:
1289                 /*
1290                  * The compiler models operand stack depth and fixes the stack
1291                  * pointer on entry to a catch clause based on its depth model.
1292                  * The decompiler must match the code generator's model, which
1293                  * is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops.
1294                  */
1295                 LOCAL_ASSERT(ss->top >= (uintN) GET_ATOM_INDEX(pc));
1296                 ss->top = (uintN) GET_ATOM_INDEX(pc);
1297                 break;
1299               case JSOP_EXCEPTION:
1300                 /*
1301                  * The only other JSOP_EXCEPTION case occurs as part of a code
1302                  * sequence that follows a SRC_CATCH-annotated JSOP_NOP.
1303                  */
1304                 sn = js_GetSrcNote(jp->script, pc);
1305                 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_HIDDEN);
1306                 todo = -2;
1307                 break;
1308 #endif /* JS_HAS_EXCEPTIONS */
1310               case JSOP_POP:
1311               case JSOP_POPV:
1312                 sn = js_GetSrcNote(jp->script, pc);
1313                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
1314                   case SRC_FOR:
1315                     rval = POP_STR();
1316                     todo = -2;
1317                     goto do_forloop;
1319                   case SRC_PCDELTA:
1320                     /* Pop and save to avoid blowing stack depth budget. */
1321                     lval = JS_strdup(cx, POP_STR());
1322                     if (!lval)
1323                         return JS_FALSE;
1325                     /*
1326                      * The offset tells distance to the end of the right-hand
1327                      * operand of the comma operator.
1328                      */
1329                     done = pc + len;
1330                     pc += js_GetSrcNoteOffset(sn, 0);
1331                     len = 0;
1333                     if (!Decompile(ss, done, pc - done)) {
1334                         JS_free(cx, (char *)lval);
1335                         return JS_FALSE;
1336                     }
1338                     /* Pop Decompile result and print comma expression. */
1339                     rval = POP_STR();
1340                     todo = Sprint(&ss->sprinter, "%s, %s", lval, rval);
1341                     JS_free(cx, (char *)lval);
1342                     break;
1344                   case SRC_HIDDEN:
1345                     /* Hide this pop, it's from a goto in a with or for/in. */
1346                     todo = -2;
1347                     break;
1349                   default:
1350                     rval = POP_STR();
1351                     if (*rval != '\0')
1352                         js_printf(jp, "\t%s;\n", rval);
1353                     todo = -2;
1354                     break;
1355                 }
1356                 break;
1358               case JSOP_POP2:
1359                 (void) PopOff(ss, op);
1360                 (void) PopOff(ss, op);
1361                 todo = -2;
1362                 break;
1364               case JSOP_ENTERWITH:
1365                 JS_ASSERT(!js_GetSrcNote(jp->script, pc));
1366                 rval = POP_STR();
1367                 js_printf(jp, "\twith (%s) {\n", rval);
1368                 jp->indent += 4;
1369                 todo = Sprint(&ss->sprinter, with_cookie);
1370                 break;
1372               case JSOP_LEAVEWITH:
1373                 sn = js_GetSrcNote(jp->script, pc);
1374                 todo = -2;
1375                 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
1376                     break;
1377                 rval = POP_STR();
1378                 if (sn && SN_TYPE(sn) == SRC_CATCH) {
1379                     LOCAL_ASSERT(strcmp(rval, catch_cookie) == 0);
1380                     LOCAL_ASSERT((uintN) js_GetSrcNoteOffset(sn, 0) == ss->top);
1381                     break;
1382                 }
1383                 LOCAL_ASSERT(strcmp(rval, with_cookie) == 0);
1384                 jp->indent -= 4;
1385                 js_printf(jp, "\t}\n");
1386                 break;
1388               case JSOP_SETRVAL:
1389               case JSOP_RETURN:
1390                 lval = js_CodeSpec[JSOP_RETURN].name;
1391                 rval = POP_STR();
1392                 if (*rval != '\0')
1393                     js_printf(jp, "\t%s %s;\n", lval, rval);
1394                 else
1395                     js_printf(jp, "\t%s;\n", lval);
1396                 todo = -2;
1397                 break;
1399 #if JS_HAS_EXCEPTIONS
1400               case JSOP_THROWING:
1401                 todo = -2;
1402                 break;
1404               case JSOP_THROW:
1405                 sn = js_GetSrcNote(jp->script, pc);
1406                 todo = -2;
1407                 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
1408                     break;
1409                 rval = POP_STR();
1410                 js_printf(jp, "\t%s %s;\n", cs->name, rval);
1411                 break;
1412 #endif /* JS_HAS_EXCEPTIONS */
1414               case JSOP_GOTO:
1415               case JSOP_GOTOX:
1416                 sn = js_GetSrcNote(jp->script, pc);
1417                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
1418                   case SRC_CONT2LABEL:
1419                     atom = js_GetAtom(cx, &jp->script->atomMap,
1420                                       (jsatomid) js_GetSrcNoteOffset(sn, 0));
1421                     rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1422                     if (!rval)
1423                         return JS_FALSE;
1424                     RETRACT(&ss->sprinter, rval);
1425                     js_printf(jp, "\tcontinue %s;\n", rval);
1426                     break;
1427                   case SRC_CONTINUE:
1428                     js_printf(jp, "\tcontinue;\n");
1429                     break;
1430                   case SRC_BREAK2LABEL:
1431                     atom = js_GetAtom(cx, &jp->script->atomMap,
1432                                       (jsatomid) js_GetSrcNoteOffset(sn, 0));
1433                     rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1434                     if (!rval)
1435                         return JS_FALSE;
1436                     RETRACT(&ss->sprinter, rval);
1437                     js_printf(jp, "\tbreak %s;\n", rval);
1438                     break;
1439                   case SRC_HIDDEN:
1440                     break;
1441                   default:
1442                     js_printf(jp, "\tbreak;\n");
1443                     break;
1444                 }
1445                 todo = -2;
1446                 break;
1448               case JSOP_IFEQ:
1449               case JSOP_IFEQX:
1450                 len = GetJumpOffset(pc, pc);
1451                 sn = js_GetSrcNote(jp->script, pc);
1453                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
1454                   case SRC_IF:
1455                   case SRC_IF_ELSE:
1456                     rval = POP_STR();
1457                     js_printf(jp, "\tif (%s) {\n", rval);
1458                     jp->indent += 4;
1459                     if (SN_TYPE(sn) == SRC_IF) {
1460                         DECOMPILE_CODE(pc + oplen, len - oplen);
1461                     } else {
1462                         len = js_GetSrcNoteOffset(sn, 0);
1463                         DECOMPILE_CODE(pc + oplen, len - oplen);
1464                         jp->indent -= 4;
1465                         pc += len;
1466                         LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX);
1467                         oplen = js_CodeSpec[*pc].length;
1468                         len = GetJumpOffset(pc, pc);
1469                         js_printf(jp, "\t} else {\n");
1470                         jp->indent += 4;
1471                         DECOMPILE_CODE(pc + oplen, len - oplen);
1472                     }
1473                     jp->indent -= 4;
1474                     js_printf(jp, "\t}\n");
1475                     todo = -2;
1476                     break;
1478                   case SRC_WHILE:
1479                     rval = POP_STR();
1480                     js_printf(jp, "\twhile (%s) {\n", rval);
1481                     jp->indent += 4;
1482                     tail = js_GetSrcNoteOffset(sn, 0);
1483                     DECOMPILE_CODE(pc + oplen, tail - oplen);
1484                     jp->indent -= 4;
1485                     js_printf(jp, "\t}\n");
1486                     todo = -2;
1487                     break;
1489                   case SRC_COND:
1490                     xval = JS_strdup(cx, POP_STR());
1491                     if (!xval)
1492                         return JS_FALSE;
1493                     len = js_GetSrcNoteOffset(sn, 0);
1494                     DECOMPILE_CODE(pc + oplen, len - oplen);
1495                     lval = JS_strdup(cx, POP_STR());
1496                     if (!lval) {
1497                         JS_free(cx, (void *)xval);
1498                         return JS_FALSE;
1499                     }
1500                     pc += len;
1501                     LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX);
1502                     oplen = js_CodeSpec[*pc].length;
1503                     len = GetJumpOffset(pc, pc);
1504                     DECOMPILE_CODE(pc + oplen, len - oplen);
1505                     rval = POP_STR();
1506                     todo = Sprint(&ss->sprinter, "%s ? %s : %s",
1507                                   xval, lval, rval);
1508                     JS_free(cx, (void *)xval);
1509                     JS_free(cx, (void *)lval);
1510                     break;
1512                   default:
1513                     break;
1514                 }
1515                 break;
1517               case JSOP_IFNE:
1518               case JSOP_IFNEX:
1519 #if JS_HAS_DO_WHILE_LOOP
1520                 /* Currently, this must be a do-while loop's upward branch. */
1521                 jp->indent -= 4;
1522                 js_printf(jp, "\t} while (%s);\n", POP_STR());
1523                 todo = -2;
1524 #else
1525                 JS_ASSERT(0);
1526 #endif /* JS_HAS_DO_WHILE_LOOP */
1527                 break;
1529               case JSOP_OR:
1530               case JSOP_ORX:
1531                 xval = "||";
1533               do_logical_connective:
1534                 /* Top of stack is the first clause in a disjunction (||). */
1535                 lval = JS_strdup(cx, POP_STR());
1536                 if (!lval)
1537                     return JS_FALSE;
1538                 done = pc + GetJumpOffset(pc, pc);
1539                 pc += len;
1540                 len = PTRDIFF(done, pc, jsbytecode);
1541                 DECOMPILE_CODE(pc, len);
1542                 rval = POP_STR();
1543                 if (jp->pretty &&
1544                     jp->indent + 4 + strlen(lval) + 4 + strlen(rval) > 75) {
1545                     rval = JS_strdup(cx, rval);
1546                     if (!rval) {
1547                         tail = -1;
1548                     } else {
1549                         todo = Sprint(&ss->sprinter, "%s %s\n", lval, xval);
1550                         tail = Sprint(&ss->sprinter, "%*s%s",
1551                                       jp->indent + 4, "", rval);
1552                         JS_free(cx, (char *)rval);
1553                     }
1554                     if (tail < 0)
1555                         todo = -1;
1556                 } else {
1557                     todo = Sprint(&ss->sprinter, "%s %s %s", lval, xval, rval);
1558                 }
1559                 JS_free(cx, (char *)lval);
1560                 break;
1562               case JSOP_AND:
1563               case JSOP_ANDX:
1564                 xval = "&&";
1565                 goto do_logical_connective;
1567               case JSOP_FORARG:
1568                 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
1569                 LOCAL_ASSERT(atom);
1570                 goto do_fornameinloop;
1572               case JSOP_FORVAR:
1573                 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
1574                 LOCAL_ASSERT(atom);
1575                 goto do_fornameinloop;
1577               case JSOP_FORNAME:
1578                 atom = GET_ATOM(cx, jp->script, pc);
1580               do_fornameinloop:
1581                 sn = js_GetSrcNote(jp->script, pc);
1582                 xval = NULL;
1583                 lval = "";
1584                 goto do_forinloop;
1586               case JSOP_FORPROP:
1587                 xval = NULL;
1588                 atom = GET_ATOM(cx, jp->script, pc);
1589                 if (!ATOM_IS_IDENTIFIER(atom)) {
1590                     xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
1591                                        (jschar)'\'');
1592                     if (!xval)
1593                         return JS_FALSE;
1594                     atom = NULL;
1595                 }
1596                 lval = POP_STR();
1597                 sn = NULL;
1599               do_forinloop:
1600                 pc += oplen;
1601                 LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
1602                 oplen = js_CodeSpec[*pc].length;
1603                 len = GetJumpOffset(pc, pc);
1604                 sn2 = js_GetSrcNote(jp->script, pc);
1605                 tail = js_GetSrcNoteOffset(sn2, 0);
1607               do_forinbody:
1608 #if JS_HAS_XML_SUPPORT
1609                 if (foreach) {
1610                     foreach = JS_FALSE;
1611                     js_printf(jp, "\tfor %s (%s%s",
1612                               js_each_str, VarPrefix(sn), lval);
1613                 } else
1614 #endif
1615                     js_printf(jp, "\tfor (%s%s", VarPrefix(sn), lval);
1616                 if (atom) {
1617                     xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1618                     if (!xval)
1619                         return JS_FALSE;
1620                     RETRACT(&ss->sprinter, xval);
1621                     js_printf(jp, *lval ? ".%s" : "%s", xval);
1622                 } else if (xval && *xval) {
1623                     js_printf(jp,
1624                               (js_CodeSpec[lastop].format & JOF_XMLNAME)
1625                               ? ".%s"
1626                               : "[%s]",
1627                               xval);
1628                 }
1629                 rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]);
1630                 js_printf(jp, " in %s) {\n", rval);
1631                 jp->indent += 4;
1632                 DECOMPILE_CODE(pc + oplen, tail - oplen);
1633                 jp->indent -= 4;
1634                 js_printf(jp, "\t}\n");
1635                 todo = -2;
1636                 break;
1638               case JSOP_FORELEM:
1639                 pc++;
1640                 LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
1641                 len = js_CodeSpec[*pc].length;
1643                 /*
1644                  * Arrange for the JSOP_ENUMELEM case to set tail for use by
1645                  * do_forinbody: code that uses on it to find the loop-closing
1646                  * jump (whatever its format, normal or extended), in order to
1647                  * bound the recursively decompiled loop body.
1648                  */
1649                 sn = js_GetSrcNote(jp->script, pc);
1650                 JS_ASSERT(!forelem_tail);
1651                 forelem_tail = pc + js_GetSrcNoteOffset(sn, 0);
1653                 /*
1654                  * This gets a little wacky.  Only the length of the for loop
1655                  * body PLUS the element-indexing expression is known here, so
1656                  * we pass the after-loop pc to the JSOP_ENUMELEM case, which
1657                  * is immediately below, to decompile that helper bytecode via
1658                  * the 'forelem_done' local.
1659                  *
1660                  * Since a for..in loop can't nest in the head of another for
1661                  * loop, we can use forelem_{tail,done} singletons to remember
1662                  * state from JSOP_FORELEM to JSOP_ENUMELEM, thence (via goto)
1663                  * to label do_forinbody.
1664                  */
1665                 JS_ASSERT(!forelem_done);
1666                 forelem_done = pc + GetJumpOffset(pc, pc);
1667                 break;
1669               case JSOP_ENUMELEM:
1670                 /*
1671                  * The stack has the object under the (top) index expression.
1672                  * The "rval" property id is underneath those two on the stack.
1673                  * The for loop body net and gross lengths can now be adjusted
1674                  * to account for the length of the indexing expression that
1675                  * came after JSOP_FORELEM and before JSOP_ENUMELEM.
1676                  */
1677                 atom = NULL;
1678                 xval = POP_STR();
1679                 lval = POP_STR();
1680                 rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]);
1681                 JS_ASSERT(forelem_tail > pc);
1682                 tail = forelem_tail - pc;
1683                 forelem_tail = NULL;
1684                 JS_ASSERT(forelem_done > pc);
1685                 len = forelem_done - pc;
1686                 forelem_done = NULL;
1687                 goto do_forinbody;
1689 #if JS_HAS_GETTER_SETTER
1690               case JSOP_GETTER:
1691               case JSOP_SETTER:
1692                 todo = -2;
1693                 break;
1694 #endif
1696               case JSOP_DUP2:
1697                 rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-2]);
1698                 todo = SprintPut(&ss->sprinter, rval, strlen(rval));
1699                 if (todo < 0 || !PushOff(ss, todo, ss->opcodes[ss->top-2]))
1700                     return JS_FALSE;
1701                 /* FALL THROUGH */
1703               case JSOP_DUP:
1704                 rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]);
1705                 op = ss->opcodes[ss->top-1];
1706                 todo = SprintPut(&ss->sprinter, rval, strlen(rval));
1707                 break;
1709               case JSOP_SETARG:
1710                 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
1711                 LOCAL_ASSERT(atom);
1712                 goto do_setname;
1714               case JSOP_SETVAR:
1715                 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
1716                 LOCAL_ASSERT(atom);
1717                 goto do_setname;
1719               case JSOP_SETCONST:
1720               case JSOP_SETNAME:
1721               case JSOP_SETGVAR:
1722                 atomIndex = GET_ATOM_INDEX(pc);
1724               do_JSOP_SETCONST:
1725                 atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex);
1727               do_setname:
1728                 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1729                 if (!lval)
1730                     return JS_FALSE;
1731                 rval = POP_STR();
1732                 if (op == JSOP_SETNAME)
1733                     (void) PopOff(ss, op);
1735               do_setlval:
1736                 sn = js_GetSrcNote(jp->script, pc - 1);
1737                 if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
1738                     todo = Sprint(&ss->sprinter, "%s %s= %s",
1739                                   lval, js_CodeSpec[lastop].token, rval);
1740                 } else {
1741                     sn = js_GetSrcNote(jp->script, pc);
1742                     todo = Sprint(&ss->sprinter, "%s%s = %s",
1743                                   VarPrefix(sn), lval, rval);
1744                 }
1745                 break;
1747               case JSOP_NEW:
1748               case JSOP_CALL:
1749               case JSOP_EVAL:
1750 #if JS_HAS_LVALUE_RETURN
1751               case JSOP_SETCALL:
1752 #endif
1753                 saveop = op;
1754                 op = JSOP_NOP;           /* turn off parens */
1755                 argc = GET_ARGC(pc);
1756                 argv = (char **)
1757                     JS_malloc(cx, (size_t)(argc + 1) * sizeof *argv);
1758                 if (!argv)
1759                     return JS_FALSE;
1761                 ok = JS_TRUE;
1762                 for (i = argc; i > 0; i--) {
1763                     argv[i] = JS_strdup(cx, POP_STR());
1764                     if (!argv[i]) {
1765                         ok = JS_FALSE;
1766                         break;
1767                     }
1768                 }
1770                 /* Skip the JSOP_PUSHOBJ-created empty string. */
1771                 LOCAL_ASSERT(ss->top >= 2);
1772                 (void) PopOff(ss, op);
1774                 /* Get the callee's decompiled image in argv[0]. */
1775                 argv[0] = JS_strdup(cx, POP_STR());
1776                 if (!argv[i])
1777                     ok = JS_FALSE;
1779                 lval = "(", rval = ")";
1780                 if (saveop == JSOP_NEW) {
1781                     todo = Sprint(&ss->sprinter, "%s %s%s",
1782                                   js_new_str, argv[0], lval);
1783                 } else {
1784                     todo = Sprint(&ss->sprinter, "%s%s",
1785                                   argv[0], lval);
1786                 }
1787                 if (todo < 0)
1788                     ok = JS_FALSE;
1790                 for (i = 1; i <= argc; i++) {
1791                     if (!argv[i] ||
1792                         Sprint(&ss->sprinter, "%s%s",
1793                                argv[i], (i < argc) ? ", " : "") < 0) {
1794                         ok = JS_FALSE;
1795                         break;
1796                     }
1797                 }
1798                 if (Sprint(&ss->sprinter, rval) < 0)
1799                     ok = JS_FALSE;
1801                 for (i = 0; i <= argc; i++) {
1802                     if (argv[i])
1803                         JS_free(cx, argv[i]);
1804                 }
1805                 JS_free(cx, argv);
1806                 if (!ok)
1807                     return JS_FALSE;
1808                 op = saveop;
1809 #if JS_HAS_LVALUE_RETURN
1810                 if (op == JSOP_SETCALL) {
1811                     if (!PushOff(ss, todo, op))
1812                         return JS_FALSE;
1813                     todo = Sprint(&ss->sprinter, "");
1814                 }
1815 #endif
1816                 break;
1818               case JSOP_DELNAME:
1819                 atom = GET_ATOM(cx, jp->script, pc);
1820                 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1821                 if (!lval)
1822                     return JS_FALSE;
1823                 RETRACT(&ss->sprinter, lval);
1824                 todo = Sprint(&ss->sprinter, "%s %s", js_delete_str, lval);
1825                 break;
1827               case JSOP_DELPROP:
1828                 GET_ATOM_QUOTE_AND_FMT("%s %s[%s]", "%s %s.%s", rval);
1829                 lval = POP_STR();
1830                 todo = Sprint(&ss->sprinter, fmt, js_delete_str, lval, rval);
1831                 break;
1833               case JSOP_DELELEM:
1834                 xval = POP_STR();
1835                 lval = POP_STR();
1836                 todo = Sprint(&ss->sprinter,
1837                               (js_CodeSpec[lastop].format & JOF_XMLNAME)
1838                               ? "%s %s.%s"
1839                               : "%s %s[%s]",
1840                               js_delete_str, lval, xval);
1841                 break;
1843 #if JS_HAS_XML_SUPPORT
1844               case JSOP_DELDESC:
1845                 xval = POP_STR();
1846                 lval = POP_STR();
1847                 todo = Sprint(&ss->sprinter, "%s %s..%s",
1848                               js_delete_str, lval, xval);
1849                 break;
1850 #endif
1852               case JSOP_TYPEOF:
1853               case JSOP_VOID:
1854                 rval = POP_STR();
1855                 todo = Sprint(&ss->sprinter, "%s %s", cs->name, rval);
1856                 break;
1858               case JSOP_INCARG:
1859               case JSOP_DECARG:
1860                 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
1861                 LOCAL_ASSERT(atom);
1862                 goto do_incatom;
1864               case JSOP_INCVAR:
1865               case JSOP_DECVAR:
1866                 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
1867                 LOCAL_ASSERT(atom);
1868                 goto do_incatom;
1870               case JSOP_INCNAME:
1871               case JSOP_DECNAME:
1872               case JSOP_INCGVAR:
1873               case JSOP_DECGVAR:
1874                 atom = GET_ATOM(cx, jp->script, pc);
1875               do_incatom:
1876                 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1877                 if (!lval)
1878                     return JS_FALSE;
1879                 RETRACT(&ss->sprinter, lval);
1880                 todo = Sprint(&ss->sprinter, "%s%s",
1881                               js_incop_str[!(cs->format & JOF_INC)], lval);
1882                 break;
1884               case JSOP_INCPROP:
1885               case JSOP_DECPROP:
1886                 GET_ATOM_QUOTE_AND_FMT("%s%s[%s]", "%s%s.%s", rval);
1887                 lval = POP_STR();
1888                 todo = Sprint(&ss->sprinter, fmt,
1889                               js_incop_str[!(cs->format & JOF_INC)],
1890                               lval, rval);
1891                 break;
1893               case JSOP_INCELEM:
1894               case JSOP_DECELEM:
1895                 xval = POP_STR();
1896                 lval = POP_STR();
1897                 if (*xval != '\0') {
1898                     todo = Sprint(&ss->sprinter,
1899                                   (js_CodeSpec[lastop].format & JOF_XMLNAME)
1900                                   ? "%s%s.%s"
1901                                   : "%s%s[%s]",
1902                                   js_incop_str[!(cs->format & JOF_INC)],
1903                                   lval, xval);
1904                 } else {
1905                     todo = Sprint(&ss->sprinter, "%s%s",
1906                                   js_incop_str[!(cs->format & JOF_INC)], lval);
1907                 }
1908                 break;
1910               case JSOP_ARGINC:
1911               case JSOP_ARGDEC:
1912                 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
1913                 LOCAL_ASSERT(atom);
1914                 goto do_atominc;
1916               case JSOP_VARINC:
1917               case JSOP_VARDEC:
1918                 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
1919                 LOCAL_ASSERT(atom);
1920                 goto do_atominc;
1922               case JSOP_NAMEINC:
1923               case JSOP_NAMEDEC:
1924               case JSOP_GVARINC:
1925               case JSOP_GVARDEC:
1926                 atom = GET_ATOM(cx, jp->script, pc);
1927               do_atominc:
1928                 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1929                 if (!lval)
1930                     return JS_FALSE;
1931                 todo = STR2OFF(&ss->sprinter, lval);
1932                 SprintPut(&ss->sprinter,
1933                           js_incop_str[!(cs->format & JOF_INC)],
1934                           2);
1935                 break;
1937               case JSOP_PROPINC:
1938               case JSOP_PROPDEC:
1939                 GET_ATOM_QUOTE_AND_FMT("%s[%s]%s", "%s.%s%s", rval);
1940                 lval = POP_STR();
1941                 todo = Sprint(&ss->sprinter, fmt, lval, rval,
1942                               js_incop_str[!(cs->format & JOF_INC)]);
1943                 break;
1945               case JSOP_ELEMINC:
1946               case JSOP_ELEMDEC:
1947                 xval = POP_STR();
1948                 lval = POP_STR();
1949                 if (*xval != '\0') {
1950                     todo = Sprint(&ss->sprinter,
1951                                   (js_CodeSpec[lastop].format & JOF_XMLNAME)
1952                                   ? "%s.%s%s"
1953                                   : "%s[%s]%s",
1954                                   lval, xval,
1955                                   js_incop_str[!(cs->format & JOF_INC)]);
1956                 } else {
1957                     todo = Sprint(&ss->sprinter, "%s%s",
1958                                   lval, js_incop_str[!(cs->format & JOF_INC)]);
1959                 }
1960                 break;
1962               case JSOP_GETPROP2:
1963                 op = JSOP_GETPROP;
1964                 (void) PopOff(ss, lastop);
1965                 /* FALL THROUGH */
1967               case JSOP_GETPROP:
1968                 atom = GET_ATOM(cx, jp->script, pc);
1970               do_getprop:
1971                 GET_QUOTE_AND_FMT("%s[%s]", "%s.%s", rval);
1973               do_getprop_lval:
1974                 lval = POP_STR();
1975                 todo = Sprint(&ss->sprinter, fmt, lval, rval);
1976                 break;
1978 #if JS_HAS_XML_SUPPORT
1979               BEGIN_LITOPX_CASE(JSOP_GETMETHOD)
1980                 sn = js_GetSrcNote(jp->script, pc);
1981                 if (sn && SN_TYPE(sn) == SRC_PCBASE)
1982                     goto do_getprop;
1983                 GET_QUOTE_AND_FMT("%s.function::[%s]", "%s.function::%s", rval);
1984                 goto do_getprop_lval;
1986               BEGIN_LITOPX_CASE(JSOP_SETMETHOD)
1987                 sn = js_GetSrcNote(jp->script, pc);
1988                 if (sn && SN_TYPE(sn) == SRC_PCBASE)
1989                     goto do_setprop;
1990                 GET_QUOTE_AND_FMT("%s.function::[%s] %s= %s",
1991                                   "%s.function::%s %s= %s",
1992                                   xval);
1993                 goto do_setprop_rval;
1994 #endif
1996               case JSOP_SETPROP:
1997                 atom = GET_ATOM(cx, jp->script, pc);
1999               do_setprop:
2000                 GET_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval);
2002               do_setprop_rval:
2003                 rval = POP_STR();
2004                 lval = POP_STR();
2005                 sn = js_GetSrcNote(jp->script, pc - 1);
2006                 todo = Sprint(&ss->sprinter, fmt, lval, xval,
2007                               (sn && SN_TYPE(sn) == SRC_ASSIGNOP)
2008                               ? js_CodeSpec[lastop].token
2009                               : "",
2010                               rval);
2011                 break;
2013               case JSOP_GETELEM2:
2014                 op = JSOP_GETELEM;
2015                 (void) PopOff(ss, lastop);
2016                 /* FALL THROUGH */
2018               case JSOP_GETELEM:
2019                 op = JSOP_NOP;           /* turn off parens */
2020                 xval = POP_STR();
2021                 op = JSOP_GETELEM;
2022                 lval = POP_STR();
2023                 if (*xval == '\0') {
2024                     todo = Sprint(&ss->sprinter, "%s", lval);
2025                 } else {
2026                     todo = Sprint(&ss->sprinter,
2027                                   (js_CodeSpec[lastop].format & JOF_XMLNAME)
2028                                   ? "%s.%s"
2029                                   : "%s[%s]",
2030                                   lval, xval);
2031                 }
2032                 break;
2034               case JSOP_SETELEM:
2035                 op = JSOP_NOP;           /* turn off parens */
2036                 rval = POP_STR();
2037                 xval = POP_STR();
2038                 op = JSOP_SETELEM;
2039                 lval = POP_STR();
2040                 if (*xval == '\0')
2041                     goto do_setlval;
2042                 sn = js_GetSrcNote(jp->script, pc - 1);
2043                 todo = Sprint(&ss->sprinter,
2044                               (js_CodeSpec[lastop].format & JOF_XMLNAME)
2045                               ? "%s.%s %s= %s"
2046                               : "%s[%s] %s= %s",
2047                               lval, xval,
2048                               (sn && SN_TYPE(sn) == SRC_ASSIGNOP)
2049                               ? js_CodeSpec[lastop].token
2050                               : "",
2051                               rval);
2052                 break;
2054               case JSOP_ARGSUB:
2055                 i = (jsint) GET_ATOM_INDEX(pc);
2056                 todo = Sprint(&ss->sprinter, "%s[%d]",
2057                               js_arguments_str, (int) i);
2058                 break;
2060               case JSOP_ARGCNT:
2061                 todo = Sprint(&ss->sprinter, "%s.%s",
2062                               js_arguments_str, js_length_str);
2063                 break;
2065               case JSOP_GETARG:
2066                 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
2067                 LOCAL_ASSERT(atom);
2068                 goto do_name;
2070               case JSOP_GETVAR:
2071                 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
2072                 LOCAL_ASSERT(atom);
2073                 goto do_name;
2075               case JSOP_NAME:
2076               case JSOP_GETGVAR:
2077                 atom = GET_ATOM(cx, jp->script, pc);
2078               do_name:
2079                 sn = js_GetSrcNote(jp->script, pc);
2080                 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2081                 if (!rval)
2082                     return JS_FALSE;
2083                 RETRACT(&ss->sprinter, rval);
2084                 todo = Sprint(&ss->sprinter, "%s%s", VarPrefix(sn), rval);
2085                 break;
2087               case JSOP_UINT16:
2088                 i = (jsint) GET_ATOM_INDEX(pc);
2089                 goto do_sprint_int;
2091               case JSOP_UINT24:
2092                 i = (jsint) GET_LITERAL_INDEX(pc);
2093               do_sprint_int:
2094                 todo = Sprint(&ss->sprinter, "%u", (unsigned) i);
2095                 break;
2097               case JSOP_LITERAL:
2098                 atomIndex = GET_LITERAL_INDEX(pc);
2099                 goto do_JSOP_STRING;
2101               case JSOP_FINDNAME:
2102                 atomIndex = GET_LITERAL_INDEX(pc);
2103                 todo = Sprint(&ss->sprinter, "");
2104                 if (todo < 0 || !PushOff(ss, todo, op))
2105                     return JS_FALSE;
2106                 atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex);
2107                 goto do_name;
2109               case JSOP_LITOPX:
2110                 atomIndex = GET_LITERAL_INDEX(pc);
2111                 op = pc[1 + LITERAL_INDEX_LEN];
2112                 switch (op) {
2113                   case JSOP_ANONFUNOBJ:   goto do_JSOP_ANONFUNOBJ;
2114                   case JSOP_BINDNAME:     goto do_JSOP_BINDNAME;
2115                   case JSOP_CLOSURE:      goto do_JSOP_CLOSURE;
2116 #if JS_HAS_EXPORT_IMPORT
2117                   case JSOP_EXPORTNAME:   goto do_JSOP_EXPORTNAME;
2118 #endif
2119 #if JS_HAS_XML_SUPPORT
2120                   case JSOP_GETMETHOD:    goto do_JSOP_GETMETHOD;
2121                   case JSOP_SETMETHOD:    goto do_JSOP_SETMETHOD;
2122 #endif
2123                   case JSOP_NAMEDFUNOBJ:  goto do_JSOP_NAMEDFUNOBJ;
2124                   case JSOP_NUMBER:       goto do_JSOP_NUMBER;
2125                   case JSOP_OBJECT:       goto do_JSOP_OBJECT;
2126 #if JS_HAS_XML_SUPPORT
2127                   case JSOP_QNAMECONST:   goto do_JSOP_QNAMECONST;
2128                   case JSOP_QNAMEPART:    goto do_JSOP_QNAMEPART;
2129 #endif
2130                   case JSOP_REGEXP:       goto do_JSOP_REGEXP;
2131                   case JSOP_SETCONST:     goto do_JSOP_SETCONST;
2132                   case JSOP_STRING:       goto do_JSOP_STRING;
2133 #if JS_HAS_XML_SUPPORT
2134                   case JSOP_XMLCDATA:     goto do_JSOP_XMLCDATA;
2135                   case JSOP_XMLCOMMENT:   goto do_JSOP_XMLCOMMENT;
2136                   case JSOP_XMLOBJECT:    goto do_JSOP_XMLOBJECT;
2137                   case JSOP_XMLPI:        goto do_JSOP_XMLPI;
2138 #endif
2139                   default:                JS_ASSERT(0);
2140                 }
2141                 /* NOTREACHED */
2142                 break;
2144               BEGIN_LITOPX_CASE(JSOP_NUMBER)
2145                 val = ATOM_KEY(atom);
2146                 if (JSVAL_IS_INT(val)) {
2147                     long ival = (long)JSVAL_TO_INT(val);
2148                     todo = Sprint(&ss->sprinter, "%ld", ival);
2149                 } else {
2150                     char buf[DTOSTR_STANDARD_BUFFER_SIZE];
2151                     char *numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD,
2152                                              0, *JSVAL_TO_DOUBLE(val));
2153                     if (!numStr) {
2154                         JS_ReportOutOfMemory(cx);
2155                         return JS_FALSE;
2156                     }
2157                     todo = Sprint(&ss->sprinter, numStr);
2158                 }
2159               END_LITOPX_CASE
2161               BEGIN_LITOPX_CASE(JSOP_STRING)
2162                 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
2163                                    inXML ? 0 : (jschar)'"');
2164                 if (!rval)
2165                     return JS_FALSE;
2166                 todo = STR2OFF(&ss->sprinter, rval);
2167               END_LITOPX_CASE
2169               case JSOP_OBJECT:
2170               case JSOP_REGEXP:
2171               case JSOP_ANONFUNOBJ:
2172               case JSOP_NAMEDFUNOBJ:
2173                 atomIndex = GET_ATOM_INDEX(pc);
2175               do_JSOP_OBJECT:
2176               do_JSOP_REGEXP:
2177               do_JSOP_ANONFUNOBJ:
2178               do_JSOP_NAMEDFUNOBJ:
2179                 atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex);
2180                 if (op == JSOP_OBJECT || op == JSOP_REGEXP) {
2181                     if (!js_regexp_toString(cx, ATOM_TO_OBJECT(atom), 0, NULL,
2182                                             &val)) {
2183                         return JS_FALSE;
2184                     }
2185                 } else {
2186                     if (!js_fun_toString(cx, ATOM_TO_OBJECT(atom),
2187                                          (pc + len < endpc &&
2188                                           pc[len] == JSOP_GROUP)
2189                                          ? JS_IN_GROUP_CONTEXT |
2190                                            JS_DONT_PRETTY_PRINT
2191                                          : JS_DONT_PRETTY_PRINT,
2192                                          0, NULL, &val)) {
2193                         return JS_FALSE;
2194                     }
2195                 }
2196                 str = JSVAL_TO_STRING(val);
2197                 todo = SprintPut(&ss->sprinter, JS_GetStringBytes(str),
2198                                  JSSTRING_LENGTH(str));
2199                 break;
2201 #if JS_HAS_SWITCH_STATEMENT
2202               case JSOP_TABLESWITCH:
2203               case JSOP_TABLESWITCHX:
2204               {
2205                 jsbytecode *pc2;
2206                 ptrdiff_t jmplen, off, off2;
2207                 jsint j, n, low, high;
2208                 TableEntry *table, pivot;
2210                 sn = js_GetSrcNote(jp->script, pc);
2211                 JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
2212                 len = js_GetSrcNoteOffset(sn, 0);
2213                 jmplen = (op == JSOP_TABLESWITCH) ? JUMP_OFFSET_LEN
2214                                                   : JUMPX_OFFSET_LEN;
2215                 pc2 = pc;
2216                 off = GetJumpOffset(pc, pc2);
2217                 pc2 += jmplen;
2218                 low = GET_JUMP_OFFSET(pc2);
2219                 pc2 += JUMP_OFFSET_LEN;
2220                 high = GET_JUMP_OFFSET(pc2);
2221                 pc2 += JUMP_OFFSET_LEN;
2223                 n = high - low + 1;
2224                 if (n == 0) {
2225                     table = NULL;
2226                     j = 0;
2227                 } else {
2228                     table = (TableEntry *)
2229                             JS_malloc(cx, (size_t)n * sizeof *table);
2230                     if (!table)
2231                         return JS_FALSE;
2232                     for (i = j = 0; i < n; i++) {
2233                         table[j].label = NULL;
2234                         off2 = GetJumpOffset(pc, pc2);
2235                         if (off2) {
2236                             sn = js_GetSrcNote(jp->script, pc2);
2237                             if (sn) {
2238                                 JS_ASSERT(SN_TYPE(sn) == SRC_LABEL);
2239                                 table[j].label =
2240                                     js_GetAtom(cx, &jp->script->atomMap,
2241                                                (jsatomid)
2242                                                js_GetSrcNoteOffset(sn, 0));
2243                             }
2244                             table[j].key = INT_TO_JSVAL(low + i);
2245                             table[j].offset = off2;
2246                             table[j].order = j;
2247                             j++;
2248                         }
2249                         pc2 += jmplen;
2250                     }
2251                     js_HeapSort(table, (size_t) j, &pivot, sizeof(TableEntry),
2252                                 CompareOffsets, NULL);
2253                 }
2255                 ok = DecompileSwitch(ss, table, (uintN)j, pc, len, off,
2256                                      JS_FALSE);
2257                 JS_free(cx, table);
2258                 if (!ok)
2259                     return ok;
2260                 todo = -2;
2261                 break;
2262               }
2264               case JSOP_LOOKUPSWITCH:
2265               case JSOP_LOOKUPSWITCHX:
2266               {
2267                 jsbytecode *pc2;
2268                 ptrdiff_t jmplen, off, off2;
2269                 jsatomid npairs, k;
2270                 TableEntry *table;
2272                 sn = js_GetSrcNote(jp->script, pc);
2273                 JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
2274                 len = js_GetSrcNoteOffset(sn, 0);
2275                 jmplen = (op == JSOP_LOOKUPSWITCH) ? JUMP_OFFSET_LEN
2276                                                    : JUMPX_OFFSET_LEN;
2277                 pc2 = pc;
2278                 off = GetJumpOffset(pc, pc2);
2279                 pc2 += jmplen;
2280                 npairs = GET_ATOM_INDEX(pc2);
2281                 pc2 += ATOM_INDEX_LEN;
2283                 table = (TableEntry *)
2284                     JS_malloc(cx, (size_t)npairs * sizeof *table);
2285                 if (!table)
2286                     return JS_FALSE;
2287                 for (k = 0; k < npairs; k++) {
2288                     sn = js_GetSrcNote(jp->script, pc2);
2289                     if (sn) {
2290                         JS_ASSERT(SN_TYPE(sn) == SRC_LABEL);
2291                         table[k].label =
2292                             js_GetAtom(cx, &jp->script->atomMap, (jsatomid)
2293                                        js_GetSrcNoteOffset(sn, 0));
2294                     } else {
2295                         table[k].label = NULL;
2296                     }
2297                     atom = GET_ATOM(cx, jp->script, pc2);
2298                     pc2 += ATOM_INDEX_LEN;
2299                     off2 = GetJumpOffset(pc, pc2);
2300                     pc2 += jmplen;
2301                     table[k].key = ATOM_KEY(atom);
2302                     table[k].offset = off2;
2303                 }
2305                 ok = DecompileSwitch(ss, table, (uintN)npairs, pc, len, off,
2306                                      JS_FALSE);
2307                 JS_free(cx, table);
2308                 if (!ok)
2309                     return ok;
2310                 todo = -2;
2311                 break;
2312               }
2314               case JSOP_CONDSWITCH:
2315               {
2316                 jsbytecode *pc2;
2317                 ptrdiff_t off, off2, caseOff;
2318                 jsint ncases;
2319                 TableEntry *table;
2321                 sn = js_GetSrcNote(jp->script, pc);
2322                 JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
2323                 len = js_GetSrcNoteOffset(sn, 0);
2324                 off = js_GetSrcNoteOffset(sn, 1);
2326                 /*
2327                  * Count the cases using offsets from switch to first case,
2328                  * and case to case, stored in srcnote immediates.
2329                  */
2330                 pc2 = pc;
2331                 off2 = off;
2332                 for (ncases = 0; off2 != 0; ncases++) {
2333                     pc2 += off2;
2334                     JS_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT ||
2335                               *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX);
2336                     if (*pc2 == JSOP_DEFAULT || *pc2 == JSOP_DEFAULTX) {
2337                         /* End of cases, but count default as a case. */
2338                         off2 = 0;
2339                     } else {
2340                         sn = js_GetSrcNote(jp->script, pc2);
2341                         JS_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
2342                         off2 = js_GetSrcNoteOffset(sn, 0);
2343                     }
2344                 }
2346                 /*
2347                  * Allocate table and rescan the cases using their srcnotes,
2348                  * stashing each case's delta from switch top in table[i].key,
2349                  * and the distance to its statements in table[i].offset.
2350                  */
2351                 table = (TableEntry *)
2352                     JS_malloc(cx, (size_t)ncases * sizeof *table);
2353                 if (!table)
2354                     return JS_FALSE;
2355                 pc2 = pc;
2356                 off2 = off;
2357                 for (i = 0; i < ncases; i++) {
2358                     pc2 += off2;
2359                     JS_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT ||
2360                               *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX);
2361                     caseOff = pc2 - pc;
2362                     table[i].key = INT_TO_JSVAL((jsint) caseOff);
2363                     table[i].offset = caseOff + GetJumpOffset(pc2, pc2);
2364                     if (*pc2 == JSOP_CASE || *pc2 == JSOP_CASEX) {
2365                         sn = js_GetSrcNote(jp->script, pc2);
2366                         JS_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
2367                         off2 = js_GetSrcNoteOffset(sn, 0);
2368                     }
2369                 }
2371                 /*
2372                  * Find offset of default code by fetching the default offset
2373                  * from the end of table.  JSOP_CONDSWITCH always has a default
2374                  * case at the end.
2375                  */
2376                 off = JSVAL_TO_INT(table[ncases-1].key);
2377                 pc2 = pc + off;
2378                 off += GetJumpOffset(pc2, pc2);
2380                 ok = DecompileSwitch(ss, table, (uintN)ncases, pc, len, off,
2381                                      JS_TRUE);
2382                 JS_free(cx, table);
2383                 if (!ok)
2384                     return ok;
2385                 todo = -2;
2386                 break;
2387               }
2389               case JSOP_CASE:
2390               case JSOP_CASEX:
2391               {
2392                 lval = POP_STR();
2393                 if (!lval)
2394                     return JS_FALSE;
2395                 js_printf(jp, "\tcase %s:\n", lval);
2396                 todo = -2;
2397                 break;
2398               }
2400 #endif /* JS_HAS_SWITCH_STATEMENT */
2402 #if !JS_BUG_FALLIBLE_EQOPS
2403               case JSOP_NEW_EQ:
2404               case JSOP_NEW_NE:
2405                 rval = POP_STR();
2406                 lval = POP_STR();
2407                 todo = Sprint(&ss->sprinter, "%s %c%s %s",
2408                               lval,
2409                               (op == JSOP_NEW_EQ) ? '=' : '!',
2410 #if JS_HAS_TRIPLE_EQOPS
2411                               JS_VERSION_IS_ECMA(cx) ? "==" :
2412 #endif
2413                               "=",
2414                               rval);
2415                 break;
2416 #endif
2418 #if JS_HAS_LEXICAL_CLOSURE
2419               BEGIN_LITOPX_CASE(JSOP_CLOSURE)
2420                 JS_ASSERT(ATOM_IS_OBJECT(atom));
2421                 todo = -2;
2422                 goto do_function;
2423               END_LITOPX_CASE
2424 #endif
2426 #if JS_HAS_EXPORT_IMPORT
2427               case JSOP_EXPORTALL:
2428                 js_printf(jp, "\texport *\n");
2429                 todo = -2;
2430                 break;
2432               BEGIN_LITOPX_CASE(JSOP_EXPORTNAME)
2433                 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2434                 if (!rval)
2435                     return JS_FALSE;
2436                 RETRACT(&ss->sprinter, rval);
2437                 js_printf(jp, "\texport %s\n", rval);
2438                 todo = -2;
2439               END_LITOPX_CASE
2441               case JSOP_IMPORTALL:
2442                 lval = POP_STR();
2443                 js_printf(jp, "\timport %s.*\n", lval);
2444                 todo = -2;
2445                 break;
2447               case JSOP_IMPORTPROP:
2448               do_importprop:
2449                 GET_ATOM_QUOTE_AND_FMT("\timport %s[%s]\n", "\timport %s.%s\n",
2450                                        rval);
2451                 lval = POP_STR();
2452                 js_printf(jp, fmt, lval, rval);
2453                 todo = -2;
2454                 break;
2456               case JSOP_IMPORTELEM:
2457                 xval = POP_STR();
2458                 op = JSOP_GETELEM;
2459                 if (js_CodeSpec[lastop].format & JOF_XMLNAME)
2460                     goto do_importprop;
2461                 lval = POP_STR();
2462                 js_printf(jp, "\timport %s[%s]\n", lval, xval);
2463                 todo = -2;
2464                 break;
2465 #endif /* JS_HAS_EXPORT_IMPORT */
2467               case JSOP_TRAP:
2468                 op = JS_GetTrapOpcode(cx, jp->script, pc);
2469                 if (op == JSOP_LIMIT)
2470                     return JS_FALSE;
2471                 *pc = op;
2472                 cs = &js_CodeSpec[op];
2473                 len = cs->length;
2474                 DECOMPILE_CODE(pc, len);
2475                 *pc = JSOP_TRAP;
2476                 todo = -2;
2477                 break;
2479 #if JS_HAS_INITIALIZERS
2480               case JSOP_NEWINIT:
2481                 LOCAL_ASSERT(ss->top >= 2);
2482                 (void) PopOff(ss, op);
2483                 lval = POP_STR();
2484 #if JS_HAS_SHARP_VARS
2485                 op = (JSOp)pc[len];
2486                 if (op == JSOP_DEFSHARP) {
2487                     pc += len;
2488                     cs = &js_CodeSpec[op];
2489                     len = cs->length;
2490                     i = (jsint) GET_ATOM_INDEX(pc);
2491                     todo = Sprint(&ss->sprinter, "#%u=%c",
2492                                   (unsigned) i,
2493                                   (*lval == 'O') ? '{' : '[');
2494                 } else
2495 #endif /* JS_HAS_SHARP_VARS */
2496                 {
2497                     todo = Sprint(&ss->sprinter, (*lval == 'O') ? "{" : "[");
2498                 }
2499                 break;
2501               case JSOP_ENDINIT:
2502                 rval = POP_STR();
2503                 sn = js_GetSrcNote(jp->script, pc);
2504                 todo = Sprint(&ss->sprinter, "%s%s%c",
2505                               rval,
2506                               (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "",
2507                               (*rval == '{') ? '}' : ']');
2508                 break;
2510               case JSOP_INITPROP:
2511                 atom = GET_ATOM(cx, jp->script, pc);
2512                 xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
2513                                    (jschar)
2514                                    (ATOM_IS_IDENTIFIER(atom) ? 0 : '\''));
2515                 if (!xval)
2516                     return JS_FALSE;
2517                 rval = POP_STR();
2518                 lval = POP_STR();
2519               do_initprop:
2520 #ifdef OLD_GETTER_SETTER
2521                 todo = Sprint(&ss->sprinter, "%s%s%s%s%s:%s",
2522                               lval,
2523                               (lval[1] != '\0') ? ", " : "",
2524                               xval,
2525                               (lastop == JSOP_GETTER || lastop == JSOP_SETTER)
2526                               ? " " : "",
2527                               (lastop == JSOP_GETTER) ? js_getter_str :
2528                               (lastop == JSOP_SETTER) ? js_setter_str :
2529                               "",
2530                               rval);
2531 #else
2532                 if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) {
2533                     rval += strlen(js_function_str) + 1;
2534                     todo = Sprint(&ss->sprinter, "%s%s%s %s%.*s",
2535                                   lval,
2536                                   (lval[1] != '\0') ? ", " : "",
2537                                   (lastop == JSOP_GETTER)
2538                                   ? js_get_str : js_set_str,
2539                                   xval,
2540                                   strlen(rval) - 1,
2541                                   rval);
2542                 } else {
2543                     todo = Sprint(&ss->sprinter, "%s%s%s:%s",
2544                                   lval,
2545                                   (lval[1] != '\0') ? ", " : "",
2546                                   xval,
2547                                   rval);
2548                 }
2549 #endif
2550                 break;
2552               case JSOP_INITELEM:
2553                 rval = POP_STR();
2554                 xval = POP_STR();
2555                 lval = POP_STR();
2556                 sn = js_GetSrcNote(jp->script, pc);
2557                 if (sn && SN_TYPE(sn) == SRC_LABEL)
2558                     goto do_initprop;
2559                 todo = Sprint(&ss->sprinter, "%s%s%s",
2560                               lval,
2561                               (lval[1] != '\0' || *xval != '0') ? ", " : "",
2562                               rval);
2563                 break;
2565 #if JS_HAS_SHARP_VARS
2566               case JSOP_DEFSHARP:
2567                 i = (jsint) GET_ATOM_INDEX(pc);
2568                 rval = POP_STR();
2569                 todo = Sprint(&ss->sprinter, "#%u=%s", (unsigned) i, rval);
2570                 break;
2572               case JSOP_USESHARP:
2573                 i = (jsint) GET_ATOM_INDEX(pc);
2574                 todo = Sprint(&ss->sprinter, "#%u#", (unsigned) i);
2575                 break;
2576 #endif /* JS_HAS_SHARP_VARS */
2577 #endif /* JS_HAS_INITIALIZERS */
2579 #if JS_HAS_DEBUGGER_KEYWORD
2580               case JSOP_DEBUGGER:
2581                 js_printf(jp, "\tdebugger;\n");
2582                 todo = -2;
2583                 break;
2584 #endif /* JS_HAS_DEBUGGER_KEYWORD */
2586 #if JS_HAS_XML_SUPPORT
2587               case JSOP_STARTXML:
2588               case JSOP_STARTXMLEXPR:
2589                 inXML = op == JSOP_STARTXML;
2590                 todo = -2;
2591                 break;
2593               case JSOP_DEFXMLNS:
2594                 rval = POP_STR();
2595                 js_printf(jp, "\t%s %s %s = %s;\n",
2596                           js_default_str, js_xml_str, js_namespace_str, rval);
2597                 todo = -2;
2598                 break;
2600               case JSOP_ANYNAME:
2601                 todo = SprintPut(&ss->sprinter, "*", 1);
2602                 break;
2604               BEGIN_LITOPX_CASE(JSOP_QNAMEPART)
2605                 goto do_name;
2606               END_LITOPX_CASE
2608               BEGIN_LITOPX_CASE(JSOP_QNAMECONST)
2609                 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2610                 if (!rval)
2611                     return JS_FALSE;
2612                 RETRACT(&ss->sprinter, rval);
2613                 lval = POP_STR();
2614                 todo = Sprint(&ss->sprinter, "%s::%s", lval, rval);
2615               END_LITOPX_CASE
2617               case JSOP_QNAME:
2618                 rval = POP_STR();
2619                 lval = POP_STR();
2620                 todo = Sprint(&ss->sprinter, "%s::[%s]", lval, rval);
2621                 break;
2623               case JSOP_TOATTRNAME:
2624                 rval = POP_STR();
2625                 todo = Sprint(&ss->sprinter, "@%s", rval);
2626                 break;
2628               case JSOP_TOATTRVAL:
2629                 todo = -2;
2630                 break;
2632               case JSOP_ADDATTRNAME:
2633                 rval = POP_STR();
2634                 lval = POP_STR();
2635                 todo = Sprint(&ss->sprinter, "%s %s", lval, rval);
2636                 /* This gets reset by all XML tag expressions. */
2637                 quoteAttr = JS_TRUE;
2638                 break;
2640               case JSOP_ADDATTRVAL:
2641                 rval = POP_STR();
2642                 lval = POP_STR();
2643                 if (quoteAttr)
2644                     todo = Sprint(&ss->sprinter, "%s=\"%s\"", lval, rval);
2645                 else
2646                     todo = Sprint(&ss->sprinter, "%s=%s", lval, rval);
2647                 break;
2649               case JSOP_BINDXMLNAME:
2650                 /* Leave the name stacked and push a dummy string. */
2651                 todo = Sprint(&ss->sprinter, "");
2652                 break;
2654               case JSOP_SETXMLNAME:
2655                 /* Pop the r.h.s., the dummy string, and the name. */
2656                 rval = POP_STR();
2657                 (void) PopOff(ss, op);
2658                 lval = POP_STR();
2659                 goto do_setlval;
2661               case JSOP_XMLELTEXPR:
2662               case JSOP_XMLTAGEXPR:
2663                 todo = Sprint(&ss->sprinter, "{%s}", POP_STR());
2664                 inXML = JS_TRUE;
2665                 /* If we're an attribute value, we shouldn't quote this. */
2666                 quoteAttr = JS_FALSE;
2667                 break;
2669               case JSOP_TOXMLLIST:
2670                 todo = Sprint(&ss->sprinter, "<>%s</>", POP_STR());
2671                 inXML = JS_FALSE;
2672                 break;
2674               case JSOP_FOREACH:
2675                 foreach = JS_TRUE;
2676                 todo = -2;
2677                 break;
2679               case JSOP_TOXML:
2680                 inXML = JS_FALSE;
2681                 /* FALL THROUGH */
2683               case JSOP_XMLNAME:
2684               case JSOP_FILTER:
2685                 /* Conversion and prefix ops do nothing in the decompiler. */
2686                 todo = -2;
2687                 break;
2689               case JSOP_ENDFILTER:
2690                 rval = POP_STR();
2691                 lval = POP_STR();
2692                 todo = Sprint(&ss->sprinter, "%s.(%s)", lval, rval);
2693                 break;
2695               case JSOP_DESCENDANTS:
2696                 rval = POP_STR();
2697                 lval = POP_STR();
2698                 todo = Sprint(&ss->sprinter, "%s..%s", lval, rval);
2699                 break;
2701               BEGIN_LITOPX_CASE(JSOP_XMLOBJECT)
2702                 atom = GET_ATOM(cx, jp->script, pc);
2703                 todo = Sprint(&ss->sprinter, "<xml address='%p'>",
2704                               ATOM_TO_OBJECT(atom));
2705               END_LITOPX_CASE
2707               BEGIN_LITOPX_CASE(JSOP_XMLCDATA)
2708                 todo = SprintPut(&ss->sprinter, "<![CDATA[", 9);
2709                 if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0))
2710                     return JS_FALSE;
2711                 SprintPut(&ss->sprinter, "]]>", 3);
2712               END_LITOPX_CASE
2714               BEGIN_LITOPX_CASE(JSOP_XMLCOMMENT)
2715                 todo = SprintPut(&ss->sprinter, "<!--", 4);
2716                 if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0))
2717                     return JS_FALSE;
2718                 SprintPut(&ss->sprinter, "-->", 3);
2719               END_LITOPX_CASE
2721               BEGIN_LITOPX_CASE(JSOP_XMLPI)
2722                 rval = JS_strdup(cx, POP_STR());
2723                 if (!rval)
2724                     return JS_FALSE;
2725                 todo = SprintPut(&ss->sprinter, "<?", 2);
2726                 ok = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0) &&
2727                      SprintPut(&ss->sprinter, " ", 1) >= 0 &&
2728                      SprintPut(&ss->sprinter, rval, strlen(rval));
2729                 JS_free(cx, (char *)rval);
2730                 if (!ok)
2731                     return JS_FALSE;
2732                 SprintPut(&ss->sprinter, "?>", 2);
2733               END_LITOPX_CASE
2735               case JSOP_GETFUNNS:
2736                 todo = SprintPut(&ss->sprinter, js_function_str, 8);
2737                 break;
2738 #endif /* JS_HAS_XML_SUPPORT */
2740               default:
2741                 todo = -2;
2742                 break;
2744 #undef BEGIN_LITOPX_CASE
2745 #undef END_LITOPX_CASE
2746             }
2747         }
2749         if (todo < 0) {
2750             /* -2 means "don't push", -1 means reported error. */
2751             if (todo == -1)
2752                 return JS_FALSE;
2753         } else {
2754             if (!PushOff(ss, todo, op))
2755                 return JS_FALSE;
2756         }
2757         pc += len;
2758     }
2760 /*
2761  * Undefine local macros.
2762  */
2763 #undef inXML
2764 #undef DECOMPILE_CODE
2765 #undef POP_STR
2766 #undef LOCAL_ASSERT
2767 #undef ATOM_IS_IDENTIFIER
2768 #undef GET_QUOTE_AND_FMT
2769 #undef GET_ATOM_QUOTE_AND_FMT
2771     return JS_TRUE;
2775 JSBool
2776 js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len)
2778     SprintStack ss;
2779     JSContext *cx;
2780     void *mark, *space;
2781     size_t offsetsz, opcodesz;
2782     JSBool ok;
2783     JSScript *oldscript;
2784     char *last;
2786     /* Initialize a sprinter for use with the offset stack. */
2787     ss.printer = jp;
2788     cx = jp->sprinter.context;
2789     mark = JS_ARENA_MARK(&cx->tempPool);
2790     INIT_SPRINTER(cx, &ss.sprinter, &cx->tempPool, PAREN_SLOP);
2792     /* Allocate the parallel (to avoid padding) offset and opcode stacks. */
2793     offsetsz = script->depth * sizeof(ptrdiff_t);
2794     opcodesz = script->depth * sizeof(jsbytecode);
2795     JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz);
2796     if (!space) {
2797         ok = JS_FALSE;
2798         goto out;
2799     }
2800     ss.offsets = (ptrdiff_t *) space;
2801     ss.opcodes = (jsbytecode *) ((char *)space + offsetsz);
2802     ss.top = 0;
2804     /* Call recursive subroutine to do the hard work. */
2805     oldscript = jp->script;
2806     jp->script = script;
2807     ok = Decompile(&ss, pc, len);
2808     jp->script = oldscript;
2810     /* If the given code didn't empty the stack, do it now. */
2811     if (ss.top) {
2812         do {
2813             last = OFF2STR(&ss.sprinter, PopOff(&ss, JSOP_NOP));
2814         } while (ss.top);
2815         js_printf(jp, "%s", last);
2816     }
2818 out:
2819     /* Free all temporary stuff allocated under this call. */
2820     JS_ARENA_RELEASE(&cx->tempPool, mark);
2821     return ok;
2824 JSBool
2825 js_DecompileScript(JSPrinter *jp, JSScript *script)
2827     return js_DecompileCode(jp, script, script->code, (uintN)script->length);
2830 static const char native_code_str[] = "\t[native code]\n";
2832 JSBool
2833 js_DecompileFunctionBody(JSPrinter *jp, JSFunction *fun)
2835     JSScript *script;
2836     JSScope *scope, *save;
2837     JSBool ok;
2839     if (!fun->interpreted) {
2840         js_printf(jp, native_code_str);
2841         return JS_TRUE;
2842     }
2843     script = fun->u.script;
2844     scope = fun->object ? OBJ_SCOPE(fun->object) : NULL;
2845     save = jp->scope;
2846     jp->scope = scope;
2847     ok = js_DecompileCode(jp, script, script->code, (uintN)script->length);
2848     jp->scope = save;
2849     return ok;
2852 JSBool
2853 js_DecompileFunction(JSPrinter *jp, JSFunction *fun)
2855     JSContext *cx;
2856     uintN i, nargs, indent;
2857     void *mark;
2858     JSAtom **params;
2859     JSScope *scope, *oldscope;
2860     JSScopeProperty *sprop;
2861     JSBool ok;
2863     /*
2864      * If pretty, conform to ECMA-262 Edition 3, 15.3.4.2, by decompiling a
2865      * FunctionDeclaration.  Otherwise, check the JSFUN_LAMBDA flag and force
2866      * an expression by parenthesizing.
2867      */
2868     if (jp->pretty) {
2869         js_printf(jp, "\t");
2870     } else {
2871         if (!jp->grouped && (fun->flags & JSFUN_LAMBDA))
2872             js_puts(jp, "(");
2873     }
2874     if (fun->flags & JSFUN_GETTER)
2875         js_printf(jp, "%s ", js_getter_str);
2876     else if (fun->flags & JSFUN_SETTER)
2877         js_printf(jp, "%s ", js_setter_str);
2879     js_printf(jp, "%s ", js_function_str);
2880     if (fun->atom && !QuoteString(&jp->sprinter, ATOM_TO_STRING(fun->atom), 0))
2881         return JS_FALSE;
2882     js_puts(jp, "(");
2884     if (fun->interpreted && fun->object) {
2885         size_t paramsize;
2887         /*
2888          * Print the parameters.
2889          *
2890          * This code is complicated by the need to handle duplicate parameter
2891          * names, as required by ECMA (bah!).  A duplicate parameter is stored
2892          * as another node with the same id (the parameter name) but different
2893          * shortid (the argument index) along the property tree ancestor line
2894          * starting at SCOPE_LAST_PROP(scope).  Only the last duplicate param
2895          * is mapped by the scope's hash table.
2896          */
2897         cx = jp->sprinter.context;
2898         nargs = fun->nargs;
2899         mark = JS_ARENA_MARK(&cx->tempPool);
2900         paramsize = nargs * sizeof(JSAtom *);
2901         JS_ARENA_ALLOCATE_CAST(params, JSAtom **, &cx->tempPool, paramsize);
2902         if (!params) {
2903             JS_ReportOutOfMemory(cx);
2904             return JS_FALSE;
2905         }
2906         memset(params, 0, paramsize);
2907         scope = OBJ_SCOPE(fun->object);
2908         for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
2909             if (sprop->getter != js_GetArgument)
2910                 continue;
2911             JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
2912             JS_ASSERT((uint16) sprop->shortid < nargs);
2913             JS_ASSERT(JSID_IS_ATOM(sprop->id));
2914             params[(uint16) sprop->shortid] = JSID_TO_ATOM(sprop->id);
2915         }
2916         for (i = 0; i < nargs; i++) {
2917             if (i > 0)
2918                 js_puts(jp, ", ");
2919             if (!QuoteString(&jp->sprinter, ATOM_TO_STRING(params[i]), 0))
2920                 return JS_FALSE;
2921         }
2922         JS_ARENA_RELEASE(&cx->tempPool, mark);
2923 #ifdef __GNUC__
2924     } else {
2925         scope = NULL;
2926 #endif
2927     }
2929     js_printf(jp, ") {\n");
2930     indent = jp->indent;
2931     jp->indent += 4;
2932     if (fun->interpreted && fun->object) {
2933         oldscope = jp->scope;
2934         jp->scope = scope;
2935         ok = js_DecompileScript(jp, fun->u.script);
2936         jp->scope = oldscope;
2937         if (!ok) {
2938             jp->indent = indent;
2939             return JS_FALSE;
2940         }
2941     } else {
2942         js_printf(jp, native_code_str);
2943     }
2944     jp->indent -= 4;
2945     js_printf(jp, "\t}");
2947     if (!jp->pretty) {
2948         if (!jp->grouped && (fun->flags & JSFUN_LAMBDA))
2949             js_puts(jp, ")");
2950     }
2951     return JS_TRUE;
2954 JSString *
2955 js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
2956                            JSString *fallback)
2958     JSStackFrame *fp, *down;
2959     jsbytecode *pc, *begin, *end, *tmp;
2960     jsval *sp, *base, *limit;
2961     JSScript *script;
2962     JSOp op;
2963     const JSCodeSpec *cs;
2964     uint32 format, mode, type;
2965     intN depth;
2966     jssrcnote *sn;
2967     uintN len, off;
2968     JSPrinter *jp;
2969     JSString *name;
2971     for (fp = cx->fp; fp && !fp->script; fp = fp->down)
2972         continue;
2973     if (!fp)
2974         goto do_fallback;
2976     /* Try to find sp's generating pc depth slots under it on the stack. */
2977     pc = fp->pc;
2978     if (spindex == JSDVG_SEARCH_STACK) {
2979         if (!pc) {
2980             /*
2981              * Current frame is native: look under it for a scripted call
2982              * in which a decompilable bytecode string that generated the
2983              * value as an actual argument might exist.
2984              */
2985             JS_ASSERT(!fp->script && !(fp->fun && fp->fun->interpreted));
2986             down = fp->down;
2987             if (!down)
2988                 goto do_fallback;
2989             script = down->script;
2990             base = fp->argv;
2991             limit = base + fp->argc;
2992         } else {
2993             /*
2994              * This should be a script activation, either a top-level
2995              * script or a scripted function.  But be paranoid about calls
2996              * to js_DecompileValueGenerator from code that hasn't fully
2997              * initialized a (default-all-zeroes) frame.
2998              */
2999             script = fp->script;
3000             base = fp->spbase;
3001             limit = fp->sp;
3002         }
3004         /*
3005          * Pure paranoia about default-zeroed frames being active while
3006          * js_DecompileValueGenerator is called.  It can't hurt much now;
3007          * error reporting performance is not an issue.
3008          */
3009         if (!script || !base || !limit)
3010             goto do_fallback;
3012         /*
3013          * Try to find operand-generating pc depth slots below sp.
3014          *
3015          * In the native case, we know the arguments have generating pc's
3016          * under them, on account of fp->down->script being non-null: all
3017          * compiled scripts get depth slots for generating pc's allocated
3018          * upon activation, at the top of js_Interpret.
3019          *
3020          * In the script or scripted function case, the same reasoning
3021          * applies to fp rather than to fp->down.
3022          */
3023         for (sp = base; sp < limit; sp++) {
3024             if (*sp == v) {
3025                 depth = (intN)script->depth;
3026                 pc = (jsbytecode *) sp[-depth];
3027                 break;
3028             }
3029         }
3030     } else {
3031         /*
3032          * At this point, pc may or may not be null, i.e., we could be in
3033          * a script activation, or we could be in a native frame that was
3034          * called by another native function.  Check pc and script.
3035          */
3036         if (!pc)
3037             goto do_fallback;
3038         script = fp->script;
3039         if (!script)
3040             goto do_fallback;
3042         if (spindex != JSDVG_IGNORE_STACK) {
3043             JS_ASSERT(spindex < 0);
3044             depth = (intN)script->depth;
3045 #if !JS_HAS_NO_SUCH_METHOD
3046             JS_ASSERT(-depth <= spindex);
3047 #endif
3048             spindex -= depth;
3050             base = (jsval *) cx->stackPool.current->base;
3051             limit = (jsval *) cx->stackPool.current->avail;
3052             sp = fp->sp + spindex;
3053             if (JS_UPTRDIFF(sp, base) < JS_UPTRDIFF(limit, base))
3054                 pc = (jsbytecode *) *sp;
3055         }
3056     }
3058     /*
3059      * Again, be paranoid, this time about possibly loading an invalid pc
3060      * from sp[-(1+depth)].
3061      */
3062     if (JS_UPTRDIFF(pc, script->code) >= (jsuword)script->length) {
3063         pc = fp->pc;
3064         if (!pc)
3065             goto do_fallback;
3066     }
3067     op = (JSOp) *pc;
3068     if (op == JSOP_TRAP)
3069         op = JS_GetTrapOpcode(cx, script, pc);
3071     /* XXX handle null as a special case, to avoid calling null "object" */
3072     if (op == JSOP_NULL)
3073         return ATOM_TO_STRING(cx->runtime->atomState.nullAtom);
3075     cs = &js_CodeSpec[op];
3076     format = cs->format;
3077     mode = (format & JOF_MODEMASK);
3079     /* NAME ops are self-contained, but others require left context. */
3080     if (mode == JOF_NAME) {
3081         begin = pc;
3082     } else {
3083         sn = js_GetSrcNote(script, pc);
3084         if (!sn || (SN_TYPE(sn) != SRC_PCBASE && SN_TYPE(sn) != SRC_PCDELTA)) {
3085             if (cs->token)
3086                 return JS_NewStringCopyZ(cx, cs->token);
3087             goto do_fallback;
3088         }
3089         begin = pc - js_GetSrcNoteOffset(sn, 0);
3090     }
3091     end = pc + cs->length;
3092     len = PTRDIFF(end, begin, jsbytecode);
3094     if (format & (JOF_SET | JOF_DEL | JOF_INCDEC | JOF_IMPORT | JOF_FOR)) {
3095         tmp = (jsbytecode *) JS_malloc(cx, len * sizeof(jsbytecode));
3096         if (!tmp)
3097             return NULL;
3098         memcpy(tmp, begin, len * sizeof(jsbytecode));
3099         if (mode == JOF_NAME) {
3100             /*
3101              * JOF_NAME does not imply JOF_CONST, so we must check for the
3102              * QARG and QVAR format types and translate those to JSOP_GETARG
3103              * or JSOP_GETVAR appropriately, instead of JSOP_NAME.
3104              */
3105             type = format & JOF_TYPEMASK;
3106             tmp[0] = (type == JOF_QARG)
3107                      ? JSOP_GETARG
3108                      : (type == JOF_QVAR)
3109                      ? JSOP_GETVAR
3110                      : JSOP_NAME;
3111         } else {
3112             /*
3113              * We must replace the faulting pc's bytecode with a corresponding
3114              * JSOP_GET* code.  For JSOP_SET{PROP,ELEM}, we must use the "2nd"
3115              * form of JSOP_GET{PROP,ELEM}, to throw away the assignment op's
3116              * right-hand operand and decompile it as if it were a GET of its
3117              * left-hand operand.
3118              */
3119             off = len - cs->length;
3120             JS_ASSERT(off == (uintN) PTRDIFF(pc, begin, jsbytecode));
3121             if (mode == JOF_PROP) {
3122                 tmp[off] = (format & JOF_SET) ? JSOP_GETPROP2 : JSOP_GETPROP;
3123             } else if (mode == JOF_ELEM) {
3124                 tmp[off] = (format & JOF_SET) ? JSOP_GETELEM2 : JSOP_GETELEM;
3125             } else {
3126                 /*
3127                  * A zero mode means precisely that op is uncategorized for our
3128                  * purposes, so we must write per-op special case code here.
3129                  */
3130                 switch (op) {
3131                   case JSOP_ENUMELEM:
3132                     tmp[off] = JSOP_GETELEM;
3133                     break;
3134 #if JS_HAS_LVALUE_RETURN
3135                   case JSOP_SETCALL:
3136                     tmp[off] = JSOP_CALL;
3137                     break;
3138 #endif
3139                   default:
3140                     JS_ASSERT(0);
3141                 }
3142             }
3143         }
3144         begin = tmp;
3145     } else {
3146         /* No need to revise script bytecode. */
3147         tmp = NULL;
3148     }
3150     name = NULL;
3151     jp = js_NewPrinter(cx, "js_DecompileValueGenerator", 0, JS_FALSE);
3152     if (jp) {
3153        if (fp->fun && fp->fun->object) {
3154            JS_ASSERT(OBJ_IS_NATIVE(fp->fun->object));
3155            jp->scope = OBJ_SCOPE(fp->fun->object);
3156         }
3157         if (js_DecompileCode(jp, script, begin, len))
3158             name = js_GetPrinterOutput(jp);
3159         js_DestroyPrinter(jp);
3160     }
3161     if (tmp)
3162         JS_free(cx, tmp);
3163     return name;
3165   do_fallback:
3166     return fallback ? fallback : js_ValueToString(cx, v);