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