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