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