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