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 ***** */
40
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"
72
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";
87
88 const char *js_incop_str[] = {"++", "--"};
89
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__ */
100
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 };
107
108 uintN js_NumCodeSpecs = sizeof (js_CodeSpec) / sizeof js_CodeSpec[0];
109
110 /************************************************************************/
111
112 static ptrdiff_t
113 GetJumpOffset(jsbytecode *pc, jsbytecode *pc2)
114 0 {
115 uint32 type;
116
117 0 type = (js_CodeSpec[*pc].format & JOF_TYPEMASK);
118 0 if (JOF_TYPE_IS_EXTENDED_JUMP(type))
119 0 return GET_JUMPX_OFFSET(pc2);
120 0 return GET_JUMP_OFFSET(pc2);
121 }
122
123 #ifdef DEBUG
124
125 JS_FRIEND_API(JSBool)
126 js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp)
127 {
128 jsbytecode *pc, *end;
129 uintN len;
130
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 }
145
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;
156
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;
182
183 case JOF_JUMP:
184 case JOF_JUMPX:
185 off = GetJumpOffset(pc, pc);
186 fprintf(fp, " %u (%d)", loc + off, off);
187 break;
188
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;
196
197 case JOF_UINT16:
198 fprintf(fp, " %u", GET_ARGC(pc));
199 break;
200
201 #if JS_HAS_SWITCH_STATEMENT
202 case JOF_TABLESWITCH:
203 case JOF_TABLESWITCHX:
204 {
205 jsbytecode *pc2;
206 jsint i, low, high;
207
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 }
226
227 case JOF_LOOKUPSWITCH:
228 case JOF_LOOKUPSWITCHX:
229 {
230 jsbytecode *pc2;
231 jsatomid npairs;
232
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;
246
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 */
257
258 case JOF_QARG:
259 fprintf(fp, " %u", GET_ARGNO(pc));
260 break;
261
262 case JOF_QVAR:
263 fprintf(fp, " %u", GET_VARNO(pc));
264 break;
265
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
277
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 }
288
289 JS_ASSERT(op == JSOP_UINT24 || op == JSOP_LITERAL);
290 fprintf(fp, " %u", GET_LITERAL_INDEX(pc));
291 break;
292
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;
298
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
311
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;
319
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 }
331
332 #endif /* DEBUG */
333
334 /************************************************************************/
335
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;
346
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)
350
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))
354
355 static JSBool
356 SprintAlloc(Sprinter *sp, size_t nb)
357 66 {
358 66 if (!sp->base) {
359 66 JS_ARENA_ALLOCATE_CAST(sp->base, char *, sp->pool, nb);
360 } else {
361 0 JS_ARENA_GROW_CAST(sp->base, char *, sp->pool, sp->size, nb);
362 }
363 66 if (!sp->base) {
364 0 JS_ReportOutOfMemory(sp->context);
365 0 return JS_FALSE;
366 }
367 66 sp->size += nb;
368 66 return JS_TRUE;
369 }
370
371 static ptrdiff_t
372 SprintPut(Sprinter *sp, const char *s, size_t len)
373 0 {
374 ptrdiff_t nb, offset;
375 char *bp;
376
377 /* Allocate space for s, including the '\0' at the end. */
378 0 nb = (sp->offset + len + 1) - sp->size;
379 0 if (nb > 0 && !SprintAlloc(sp, nb))
380 0 return -1;
381
382 /* Advance offset and copy s into sp's buffer. */
383 0 offset = sp->offset;
384 0 sp->offset += len;
385 0 bp = sp->base + offset;
386 0 memmove(bp, s, len);
387 0 bp[len] = 0;
388 0 return offset;
389 }
390
391 static ptrdiff_t
392 Sprint(Sprinter *sp, const char *format, ...)
393 0 {
394 va_list ap;
395 char *bp;
396 ptrdiff_t offset;
397
398 0 va_start(ap, format);
399 0 bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */
400 0 va_end(ap);
401 0 if (!bp) {
402 0 JS_ReportOutOfMemory(sp->context);
403 0 return -1;
404 }
405 0 offset = SprintPut(sp, bp, strlen(bp));
406 0 free(bp);
407 0 return offset;
408 }
409
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 };
422
423 static char *
424 QuoteString(Sprinter *sp, JSString *str, jschar quote)
425 66 {
426 ptrdiff_t off, len, nb;
427 const jschar *s, *t, *u, *z;
428 char *bp;
429 jschar c;
430 JSBool ok;
431
432 /* Sample off first for later return value pointer computation. */
433 66 off = sp->offset;
434 66 if (quote && Sprint(sp, "%c", (char)quote) < 0)
435 0 return NULL;
436
437 /* Loop control variables: z points at end of string sentinel. */
438 66 s = JSSTRING_CHARS(str);
439 66 z = s + JSSTRING_LENGTH(str);
440 66 for (t = s; t < z; s = ++t) {
441 /* Move t forward from s past un-quote-worthy characters. */
442 66 c = *t;
443 396 while (JS_ISPRINT(c) && c != quote && c != '\\' && !(c >> 8)) {
444 330 c = *++t;
445 330 if (t == z)
446 66 break;
447 }
448 66 len = PTRDIFF(t, s, jschar);
449
450 /* Allocate space for s, including the '\0' at the end. */
451 66 nb = (sp->offset + len + 1) - sp->size;
452 66 if (nb > 0 && !SprintAlloc(sp, nb))
453 0 return NULL;
454
455 /* Advance sp->offset and copy s into sp's buffer. */
456 66 bp = sp->base + sp->offset;
457 66 sp->offset += len;
458 462 while (--len >= 0)
459 330 *bp++ = (char) *s++;
460 66 *bp = '\0';
461
462 66 if (t == z)
463 66 break;
464
465 /* Use js_EscapeMap, \u, or \x only if necessary. */
466 0 if ((u = js_strchr(js_EscapeMap, c)) != NULL) {
467 0 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 0 ok = Sprint(sp, (c >> 8) ? "\\u%04X" : "\\x%02X", c) >= 0;
494 #endif
495 }
496 0 if (!ok)
497 0 return NULL;
498 }
499
500 /* Sprint the closing quote and return the quoted string. */
501 66 if (quote && Sprint(sp, "%c", (char)quote) < 0)
502 0 return NULL;
503
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 66 if (off == sp->offset && Sprint(sp, "") < 0)
509 0 return NULL;
510 66 return OFF2STR(sp, off);
511 }
512
513 JSString *
514 js_QuoteString(JSContext *cx, JSString *str, jschar quote)
515 66 {
516 void *mark;
517 Sprinter sprinter;
518 char *bytes;
519 JSString *escstr;
520
521 66 mark = JS_ARENA_MARK(&cx->tempPool);
522 66 INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);
523 66 bytes = QuoteString(&sprinter, str, quote);
524 66 escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL;
525 66 JS_ARENA_RELEASE(&cx->tempPool, mark);
526 66 return escstr;
527 }
528
529 /************************************************************************/
530
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 };
540
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
548
549 JSPrinter *
550 js_NewPrinter(JSContext *cx, const char *name, uintN indent, JSBool pretty)
551 0 {
552 JSPrinter *jp;
553
554 0 jp = (JSPrinter *) JS_malloc(cx, sizeof(JSPrinter));
555 0 if (!jp)
556 0 return NULL;
557 0 INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
558 0 JS_InitArenaPool(&jp->pool, name, 256, 1);
559 0 jp->indent = indent & ~JS_IN_GROUP_CONTEXT;
560 0 jp->pretty = pretty;
561 0 jp->grouped = (indent & JS_IN_GROUP_CONTEXT) != 0;
562 0 jp->script = NULL;
563 0 jp->scope = NULL;
564 0 return jp;
565 }
566
567 void
568 js_DestroyPrinter(JSPrinter *jp)
569 0 {
570 0 JS_FinishArenaPool(&jp->pool);
571 0 JS_free(jp->sprinter.context, jp);
572 0 }
573
574 JSString *
575 js_GetPrinterOutput(JSPrinter *jp)
576 0 {
577 JSContext *cx;
578 JSString *str;
579
580 0 cx = jp->sprinter.context;
581 0 if (!jp->sprinter.base)
582 0 return cx->runtime->emptyString;
583 0 str = JS_NewStringCopyZ(cx, jp->sprinter.base);
584 0 if (!str)
585 0 return NULL;
586 0 JS_FreeArenaPool(&jp->pool);
587 0 INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
588 0 return str;
589 }
590
591 int
592 js_printf(JSPrinter *jp, const char *format, ...)
593 0 {
594 va_list ap;
595 char *bp, *fp;
596 int cc;
597
598 0 if (*format == '\0')
599 0 return 0;
600
601 0 va_start(ap, format);
602
603 /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */
604 0 if (*format == '\t') {
605 0 if (jp->pretty && Sprint(&jp->sprinter, "%*s", jp->indent, "") < 0)
606 0 return -1;
607 0 format++;
608 }
609
610 /* Suppress newlines (must be once per format, at the end) if not pretty. */
611 0 fp = NULL;
612 0 if (!jp->pretty && format[cc = strlen(format) - 1] == '\n') {
613 0 fp = JS_strdup(jp->sprinter.context, format);
614 0 if (!fp)
615 0 return -1;
616 0 fp[cc] = '\0';
617 0 format = fp;
618 }
619
620 /* Allocate temp space, convert format, and put. */
621 0 bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */
622 0 if (fp) {
623 0 JS_free(jp->sprinter.context, fp);
624 0 format = NULL;
625 }
626 0 if (!bp) {
627 0 JS_ReportOutOfMemory(jp->sprinter.context);
628 0 return -1;
629 }
630
631 0 cc = strlen(bp);
632 0 if (SprintPut(&jp->sprinter, bp, (size_t)cc) < 0)
633 0 cc = -1;
634 0 free(bp);
635
636 0 va_end(ap);
637 0 return cc;
638 }
639
640 JSBool
641 js_puts(JSPrinter *jp, const char *s)
642 0 {
643 0 return SprintPut(&jp->sprinter, s, strlen(s)) >= 0;
644 }
645
646 /************************************************************************/
647
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;
655
656 /* Gap between stacked strings to allow for insertion of parens and commas. */
657 #define PAREN_SLOP (2 + 1)
658
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
666
667 static JSBool
668 PushOff(SprintStack *ss, ptrdiff_t off, JSOp op)
669 0 {
670 uintN top;
671
672 #if JSOP_LIMIT > JSOP_GETPROP2
673 #error JSOP_LIMIT must be <= JSOP_GETPROP2
674 #endif
675 0 if (!SprintAlloc(&ss->sprinter, PAREN_SLOP))
676 0 return JS_FALSE;
677
678 /* ss->top points to the next free slot; be paranoid about overflow. */
679 0 top = ss->top;
680 JS_ASSERT(top < ss->printer->script->depth);
681 0 if (top >= ss->printer->script->depth) {
682 0 JS_ReportOutOfMemory(ss->sprinter.context);
683 0 return JS_FALSE;
684 }
685
686 /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */
687 0 ss->offsets[top] = off;
688 0 ss->opcodes[top] = (op == JSOP_GETPROP2) ? JSOP_GETPROP
689 : (op == JSOP_GETELEM2) ? JSOP_GETELEM
690 : (jsbytecode) op;
691 0 ss->top = ++top;
692 0 ss->sprinter.offset += PAREN_SLOP;
693 0 return JS_TRUE;
694 }
695
696 static ptrdiff_t
697 PopOff(SprintStack *ss, JSOp op)
698 0 {
699 uintN top;
700 const JSCodeSpec *cs, *topcs;
701 ptrdiff_t off;
702
703 /* ss->top points to the next free slot; be paranoid about underflow. */
704 0 top = ss->top;
705 JS_ASSERT(top != 0);
706 0 if (top == 0)
707 0 return 0;
708
709 0 ss->top = --top;
710 0 topcs = &js_CodeSpec[ss->opcodes[top]];
711 0 cs = &js_CodeSpec[op];
712 0 if (topcs->prec != 0 && topcs->prec < cs->prec) {
713 0 ss->offsets[top] -= 2;
714 0 ss->sprinter.offset = ss->offsets[top];
715 0 off = Sprint(&ss->sprinter, "(%s)",
716 OFF2STR(&ss->sprinter, ss->sprinter.offset + 2));
717 } else {
718 0 off = ss->sprinter.offset = ss->offsets[top];
719 }
720 0 return off;
721 }
722
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;
730
731 static int
732 CompareOffsets(const void *v1, const void *v2, void *arg)
733 0 {
734 0 const TableEntry *te1 = (const TableEntry *) v1,
735 0 *te2 = (const TableEntry *) v2;
736
737 0 if (te1->offset == te2->offset)
738 0 return (int) (te1->order - te2->order);
739 0 return (int) (te1->offset - te2->offset);
740 }
741
742 static JSBool
743 Decompile(SprintStack *ss, jsbytecode *pc, intN nb);
744
745 static JSBool
746 DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength,
747 jsbytecode *pc, ptrdiff_t switchLength,
748 ptrdiff_t defaultOffset, JSBool isCondSwitch)
749 0 {
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;
757
758 0 cx = ss->sprinter.context;
759 0 jp = ss->printer;
760
761 0 lval = OFF2STR(&ss->sprinter, PopOff(ss, JSOP_NOP));
762 0 js_printf(jp, "\tswitch (%s) {\n", lval);
763
764 0 if (tableLength) {
765 0 diff = table[0].offset - defaultOffset;
766 0 if (diff > 0) {
767 0 jp->indent += 2;
768 0 js_printf(jp, "\t%s:\n", js_default_str);
769 0 jp->indent += 2;
770 0 if (!Decompile(ss, pc + defaultOffset, diff))
771 0 return JS_FALSE;
772 0 jp->indent -= 4;
773 }
774
775 0 caseExprOff = isCondSwitch
776 ? (ptrdiff_t) js_CodeSpec[JSOP_CONDSWITCH].length
777 : 0;
778
779 0 for (i = 0; i < tableLength; i++) {
780 0 off = table[i].offset;
781 0 off2 = (i + 1 < tableLength) ? table[i + 1].offset : switchLength;
782
783 0 key = table[i].key;
784 0 if (isCondSwitch) {
785 ptrdiff_t nextCaseExprOff;
786
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 0 nextCaseExprOff = (ptrdiff_t)JSVAL_TO_INT(key);
793 0 nextCaseExprOff += js_CodeSpec[pc[nextCaseExprOff]].length;
794 0 jp->indent += 2;
795 0 if (!Decompile(ss, pc + caseExprOff,
796 nextCaseExprOff - caseExprOff)) {
797 0 return JS_FALSE;
798 }
799 0 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 0 if (table[i].label) {
810 0 str = ATOM_TO_STRING(table[i].label);
811 0 key = JSVAL_VOID;
812 } else {
813 0 str = js_ValueToString(cx, key);
814 0 if (!str)
815 0 return JS_FALSE;
816 }
817 0 rval = QuoteString(&ss->sprinter, str,
818 (jschar)(JSVAL_IS_STRING(key) ? '"' : 0));
819 0 if (!rval)
820 0 return JS_FALSE;
821 0 RETRACT(&ss->sprinter, rval);
822 0 jp->indent += 2;
823 0 js_printf(jp, "\tcase %s:\n", rval);
824 }
825
826 0 jp->indent += 2;
827 0 if (off <= defaultOffset && defaultOffset < off2) {
828 0 diff = defaultOffset - off;
829 0 if (diff != 0) {
830 0 if (!Decompile(ss, pc + off, diff))
831 0 return JS_FALSE;
832 0 off = defaultOffset;
833 }
834 0 jp->indent -= 2;
835 0 js_printf(jp, "\t%s:\n", js_default_str);
836 0 jp->indent += 2;
837 }
838 0 if (!Decompile(ss, pc + off, off2 - off))
839 0 return JS_FALSE;
840 0 jp->indent -= 4;
841 }
842 }
843
844 0 if (defaultOffset == switchLength) {
845 0 jp->indent += 2;
846 0 js_printf(jp, "\t%s:;\n", js_default_str);
847 0 jp->indent -= 2;
848 }
849 0 js_printf(jp, "\t}\n");
850 0 return JS_TRUE;
851 }
852 #endif
853
854 static JSAtom *
855 GetSlotAtom(JSPrinter *jp, JSPropertyOp getter, uintN slot)
856 0 {
857 JSScope *scope;
858 JSScopeProperty *sprop;
859 JSObject *obj, *proto;
860
861 0 scope = jp->scope;
862 0 while (scope) {
863 0 for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
864 0 if (sprop->getter != getter)
865 0 continue;
866 JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
867 JS_ASSERT(JSID_IS_ATOM(sprop->id));
868 0 if ((uintN) sprop->shortid == slot)
869 0 return JSID_TO_ATOM(sprop->id);
870 }
871 0 obj = scope->object;
872 0 if (!obj)
873 0 break;
874 0 proto = OBJ_GET_PROTO(jp->sprinter.context, obj);
875 0 if (!proto)
876 0 break;
877 0 scope = OBJ_SCOPE(proto);
878 }
879 0 return NULL;
880 }
881
882 static const char *
883 VarPrefix(jssrcnote *sn)
884 0 {
885 0 if (sn) {
886 0 if (SN_TYPE(sn) == SRC_VAR)
887 0 return "var ";
888 0 if (SN_TYPE(sn) == SRC_CONST)
889 0 return "const ";
890 }
891 0 return "";
892 }
893
894 static JSBool
895 Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
896 0 {
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*/";
922
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
929
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)))
936
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
956
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
966
967 0 cx = ss->sprinter.context;
968 0 if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
969 0 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
970 0 return JS_FALSE;
971 }
972
973 0 jp = ss->printer;
974 0 endpc = pc + nb;
975 0 forelem_tail = forelem_done = NULL;
976 0 tail = -1;
977 0 todo = -2; /* NB: different from Sprint() error return. */
978 0 op = JSOP_NOP;
979 0 sn = NULL;
980 0 rval = NULL;
981 #if JS_HAS_XML_SUPPORT
982 0 foreach = inXML = quoteAttr = JS_FALSE;
983 #endif
984
985 0 while (pc < endpc) {
986 0 lastop = op;
987 0 op = saveop = (JSOp) *pc;
988 0 if (op >= JSOP_LIMIT) {
989 0 switch (op) {
990 case JSOP_GETPROP2:
991 0 saveop = JSOP_GETPROP;
992 0 break;
993 case JSOP_GETELEM2:
994 0 saveop = JSOP_GETELEM;
995 break;
996 default:;
997 }
998 }
999 0 cs = &js_CodeSpec[saveop];
1000 0 len = oplen = cs->length;
1001
1002 0 if (cs->token) {
1003 0 switch (cs->nuses) {
1004 case 2:
1005 0 rval = POP_STR();
1006 0 lval = POP_STR();
1007 0 sn = js_GetSrcNote(jp->script, pc);
1008 0 if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
1009 /* Print only the right operand of the assignment-op. */
1010 0 todo = SprintPut(&ss->sprinter, rval, strlen(rval));
1011 0 } else if (!inXML) {
1012 0 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 0 todo = Sprint(&ss->sprinter, "%s%s", lval, rval);
1018 }
1019 0 break;
1020
1021 case 1:
1022 0 rval = POP_STR();
1023 0 todo = Sprint(&ss->sprinter, "%s%s", cs->token, rval);
1024 0 break;
1025
1026 case 0:
1027 0 todo = SprintPut(&ss->sprinter, cs->token, strlen(cs->token));
1028 0 break;
1029
1030 default:
1031 0 todo = -2;
1032 break;
1033 }
1034 } else {
1035 0 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);
1041
1042 #define END_LITOPX_CASE \
1043 break;
1044
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 0 sn = js_GetSrcNote(jp->script, pc);
1052 0 todo = -2;
1053 0 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
1054 #if JS_HAS_DO_WHILE_LOOP
1055 case SRC_WHILE:
1056 0 js_printf(jp, "\tdo {\n");
1057 0 jp->indent += 4;
1058 0 break;
1059 #endif /* JS_HAS_DO_WHILE_LOOP */
1060
1061 case SRC_FOR:
1062 0 rval = "";
1063
1064 0 do_forloop:
1065 /* Skip the JSOP_NOP or JSOP_POP bytecode. */
1066 0 pc++;
1067
1068 /* Get the cond, next, and loop-closing tail offsets. */
1069 0 cond = js_GetSrcNoteOffset(sn, 0);
1070 0 next = js_GetSrcNoteOffset(sn, 1);
1071 0 tail = js_GetSrcNoteOffset(sn, 2);
1072 0 LOCAL_ASSERT(tail + GetJumpOffset(pc+tail, pc+tail) == 0);
1073
1074 /* Print the keyword and the possibly empty init-part. */
1075 0 js_printf(jp, "\tfor (%s;", rval);
1076
1077 0 if (pc[cond] == JSOP_IFEQ || pc[cond] == JSOP_IFEQX) {
1078 /* Decompile the loop condition. */
1079 0 DECOMPILE_CODE(pc, cond);
1080 0 js_printf(jp, " %s", POP_STR());
1081 }
1082
1083 /* Need a semicolon whether or not there was a cond. */
1084 0 js_puts(jp, ";");
1085
1086 0 if (pc[next] != JSOP_GOTO && pc[next] != JSOP_GOTOX) {
1087 /* Decompile the loop updater. */
1088 0 DECOMPILE_CODE(pc + next, tail - next - 1);
1089 0 js_printf(jp, " %s", POP_STR());
1090 }
1091
1092 /* Do the loop body. */
1093 0 js_printf(jp, ") {\n");
1094 0 jp->indent += 4;
1095 0 oplen = (cond) ? js_CodeSpec[pc[cond]].length : 0;
1096 0 DECOMPILE_CODE(pc + cond + oplen, next - cond - oplen);
1097 0 jp->indent -= 4;
1098 0 js_printf(jp, "\t}\n");
1099
1100 /* Set len so pc skips over the entire loop. */
1101 0 len = tail + js_CodeSpec[pc[tail]].length;
1102 0 break;
1103
1104 case SRC_LABEL:
1105 0 atom = js_GetAtom(cx, &jp->script->atomMap,
1106 (jsatomid) js_GetSrcNoteOffset(sn, 0));
1107 0 jp->indent -= 4;
1108 0 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1109 0 if (!rval)
1110 0 return JS_FALSE;
1111 0 RETRACT(&ss->sprinter, rval);
1112 0 js_printf(jp, "\t%s:\n", rval);
1113 0 jp->indent += 4;
1114 0 break;
1115
1116 case SRC_LABELBRACE:
1117 0 atom = js_GetAtom(cx, &jp->script->atomMap,
1118 (jsatomid) js_GetSrcNoteOffset(sn, 0));
1119 0 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1120 0 if (!rval)
1121 0 return JS_FALSE;
1122 0 RETRACT(&ss->sprinter, rval);
1123 0 js_printf(jp, "\t%s: {\n", rval);
1124 0 jp->indent += 4;
1125 0 break;
1126
1127 case SRC_ENDBRACE:
1128 0 jp->indent -= 4;
1129 0 js_printf(jp, "\t}\n");
1130 0 break;
1131
1132 case SRC_CATCH:
1133 0 jp->indent -= 4;
1134 0 sn = js_GetSrcNote(jp->script, pc);
1135 0 pc += oplen;
1136 0 js_printf(jp, "\t} catch (");
1137
1138 0 LOCAL_ASSERT(*pc == JSOP_NAME);
1139 0 pc += js_CodeSpec[JSOP_NAME].length;
1140 0 LOCAL_ASSERT(*pc == JSOP_PUSHOBJ);
1141 0 pc += js_CodeSpec[JSOP_PUSHOBJ].length;
1142 0 LOCAL_ASSERT(*pc == JSOP_NEWINIT);
1143 0 pc += js_CodeSpec[JSOP_NEWINIT].length;
1144 0 LOCAL_ASSERT(*pc == JSOP_EXCEPTION);
1145 0 pc += js_CodeSpec[JSOP_EXCEPTION].length;
1146 0 if (*pc == JSOP_LITOPX) {
1147 0 atomIndex = GET_LITERAL_INDEX(pc);
1148 0 pc += 1 + LITERAL_INDEX_LEN;
1149 0 LOCAL_ASSERT(*pc == JSOP_INITCATCHVAR);
1150 0 ++pc;
1151 } else {
1152 0 LOCAL_ASSERT(*pc == JSOP_INITCATCHVAR);
1153 0 atomIndex = GET_ATOM_INDEX(pc);
1154 0 pc += js_CodeSpec[JSOP_INITCATCHVAR].length;
1155 }
1156 0 atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex);
1157 0 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1158 0 if (!rval)
1159 0 return JS_FALSE;
1160 0 RETRACT(&ss->sprinter, rval);
1161 0 js_printf(jp, "%s", rval);
1162 0 LOCAL_ASSERT(*pc == JSOP_ENTERWITH);
1163 0 pc += js_CodeSpec[JSOP_ENTERWITH].length;
1164
1165 0 len = js_GetSrcNoteOffset(sn, 0);
1166 0 if (len) {
1167 0 js_printf(jp, " if ");
1168 0 DECOMPILE_CODE(pc, len);
1169 0 js_printf(jp, "%s", POP_STR());
1170 0 pc += len;
1171 0 LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
1172 0 pc += js_CodeSpec[*pc].length;
1173 }
1174
1175 0 js_printf(jp, ") {\n");
1176 0 jp->indent += 4;
1177 0 todo = Sprint(&ss->sprinter, catch_cookie);
1178 0 len = 0;
1179 0 break;
1180
1181 case SRC_FUNCDEF:
1182 0 atom = js_GetAtom(cx, &jp->script->atomMap,
1183 (jsatomid) js_GetSrcNoteOffset(sn, 0));
1184 JS_ASSERT(ATOM_IS_OBJECT(atom));
1185 0 do_function:
1186 0 obj = ATOM_TO_OBJECT(atom);
1187 0 fun = (JSFunction *) JS_GetPrivate(cx, obj);
1188 0 jp2 = js_NewPrinter(cx, JS_GetFunctionName(fun),
1189 jp->indent, jp->pretty);
1190 0 if (!jp2)
1191 0 return JS_FALSE;
1192 0 jp2->scope = jp->scope;
1193 0 js_puts(jp2, "\n");
1194 0 ok = js_DecompileFunction(jp2, fun);
1195 0 if (ok) {
1196 0 js_puts(jp2, "\n");
1197 0 str = js_GetPrinterOutput(jp2);
1198 0 if (str)
1199 0 js_printf(jp, "%s\n", JS_GetStringBytes(str));
1200 else
1201 0 ok = JS_FALSE;
1202 }
1203 0 js_DestroyPrinter(jp2);
1204 0 if (!ok)
1205 0 return JS_FALSE;
1206
1207 break;
1208
1209 default:;
1210 }
1211 case JSOP_RETRVAL:
1212 0 break;
1213
1214 case JSOP_GROUP:
1215 /* Use last real op so PopOff adds parens if needed. */
1216 0 todo = PopOff(ss, lastop);
1217
1218 /* Now add user-supplied parens only if PopOff did not. */
1219 0 cs = &js_CodeSpec[lastop];
1220 0 topcs = &js_CodeSpec[ss->opcodes[ss->top]];
1221 0 if (topcs->prec >= cs->prec) {
1222 0 todo = Sprint(&ss->sprinter, "(%s)",
1223 OFF2STR(&ss->sprinter, todo));
1224 }
1225 0 break;
1226
1227 case JSOP_PUSH:
1228 case JSOP_PUSHOBJ:
1229 case JSOP_BINDNAME:
1230 0 do_JSOP_BINDNAME:
1231 0 todo = Sprint(&ss->sprinter, "");
1232 0 break;
1233
1234 #if JS_HAS_EXCEPTIONS
1235 case JSOP_TRY:
1236 0 js_printf(jp, "\ttry {\n");
1237 0 jp->indent += 4;
1238 0 todo = -2;
1239 0 break;
1240
1241 {
1242 static const char exception_cookie[] = "/*EXCEPTION*/";
1243 static const char retsub_pc_cookie[] = "/*RETSUB_PC*/";
1244
1245 case JSOP_FINALLY:
1246 0 jp->indent -= 4;
1247 0 js_printf(jp, "\t} finally {\n");
1248 0 jp->indent += 4;
1249
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 0 todo = Sprint(&ss->sprinter, exception_cookie);
1256 0 if (todo < 0 || !PushOff(ss, todo, op))
1257 0 return JS_FALSE;
1258 0 todo = Sprint(&ss->sprinter, retsub_pc_cookie);
1259 0 break;
1260
1261 case JSOP_RETSUB:
1262 0 rval = POP_STR();
1263 0 LOCAL_ASSERT(strcmp(rval, retsub_pc_cookie) == 0);
1264 0 lval = POP_STR();
1265 0 LOCAL_ASSERT(strcmp(lval, exception_cookie) == 0);
1266 0 todo = -2;
1267 0 break;
1268 }
1269
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 */
1277
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 0 todo = -2;
1286 0 break;
1287
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 0 LOCAL_ASSERT(ss->top >= (uintN) GET_ATOM_INDEX(pc));
1296 0 ss->top = (uintN) GET_ATOM_INDEX(pc);
1297 0 break;
1298
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 0 sn = js_GetSrcNote(jp->script, pc);
1305 0 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_HIDDEN);
1306 0 todo = -2;
1307 0 break;
1308 #endif /* JS_HAS_EXCEPTIONS */
1309
1310 case JSOP_POP:
1311 case JSOP_POPV:
1312 0 sn = js_GetSrcNote(jp->script, pc);
1313 0 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
1314 case SRC_FOR:
1315 0 rval = POP_STR();
1316 0 todo = -2;
1317 0 goto do_forloop;
1318
1319 case SRC_PCDELTA:
1320 /* Pop and save to avoid blowing stack depth budget. */
1321 0 lval = JS_strdup(cx, POP_STR());
1322 0 if (!lval)
1323 0 return JS_FALSE;
1324
1325 /*
1326 * The offset tells distance to the end of the right-hand
1327 * operand of the comma operator.
1328 */
1329 0 done = pc + len;
1330 0 pc += js_GetSrcNoteOffset(sn, 0);
1331 0 len = 0;
1332
1333 0 if (!Decompile(ss, done, pc - done)) {
1334 0 JS_free(cx, (char *)lval);
1335 0 return JS_FALSE;
1336 }
1337
1338 /* Pop Decompile result and print comma expression. */
1339 0 rval = POP_STR();
1340 0 todo = Sprint(&ss->sprinter, "%s, %s", lval, rval);
1341 0 JS_free(cx, (char *)lval);
1342 0 break;
1343
1344 case SRC_HIDDEN:
1345 /* Hide this pop, it's from a goto in a with or for/in. */
1346 0 todo = -2;
1347 0 break;
1348
1349 default:
1350 0 rval = POP_STR();
1351 0 if (*rval != '\0')
1352 0 js_printf(jp, "\t%s;\n", rval);
1353 0 todo = -2;
1354 break;
1355 }
1356 0 break;
1357
1358 case JSOP_POP2:
1359 0 (void) PopOff(ss, op);
1360 0 (void) PopOff(ss, op);
1361 0 todo = -2;
1362 0 break;
1363
1364 case JSOP_ENTERWITH:
1365 JS_ASSERT(!js_GetSrcNote(jp->script, pc));
1366 0 rval = POP_STR();
1367 0 js_printf(jp, "\twith (%s) {\n", rval);
1368 0 jp->indent += 4;
1369 0 todo = Sprint(&ss->sprinter, with_cookie);
1370 0 break;
1371
1372 case JSOP_LEAVEWITH:
1373 0 sn = js_GetSrcNote(jp->script, pc);
1374 0 todo = -2;
1375 0 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
1376 0 break;
1377 0 rval = POP_STR();
1378 0 if (sn && SN_TYPE(sn) == SRC_CATCH) {
1379 0 LOCAL_ASSERT(strcmp(rval, catch_cookie) == 0);
1380 0 LOCAL_ASSERT((uintN) js_GetSrcNoteOffset(sn, 0) == ss->top);
1381 0 break;
1382 }
1383 0 LOCAL_ASSERT(strcmp(rval, with_cookie) == 0);
1384 0 jp->indent -= 4;
1385 0 js_printf(jp, "\t}\n");
1386 0 break;
1387
1388 case JSOP_SETRVAL:
1389 case JSOP_RETURN:
1390 0 lval = js_CodeSpec[JSOP_RETURN].name;
1391 0 rval = POP_STR();
1392 0 if (*rval != '\0')
1393 0 js_printf(jp, "\t%s %s;\n", lval, rval);
1394 else
1395 0 js_printf(jp, "\t%s;\n", lval);
1396 0 todo = -2;
1397 0 break;
1398
1399 #if JS_HAS_EXCEPTIONS
1400 case JSOP_THROWING:
1401 0 todo = -2;
1402 0 break;
1403
1404 case JSOP_THROW:
1405 0 sn = js_GetSrcNote(jp->script, pc);
1406 0 todo = -2;
1407 0 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
1408 0 break;
1409 0 rval = POP_STR();
1410 0 js_printf(jp, "\t%s %s;\n", cs->name, rval);
1411 0 break;
1412 #endif /* JS_HAS_EXCEPTIONS */
1413
1414 case JSOP_GOTO:
1415 case JSOP_GOTOX:
1416 0 sn = js_GetSrcNote(jp->script, pc);
1417 0 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
1418 case SRC_CONT2LABEL:
1419 0 atom = js_GetAtom(cx, &jp->script->atomMap,
1420 (jsatomid) js_GetSrcNoteOffset(sn, 0));
1421 0 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1422 0 if (!rval)
1423 0 return JS_FALSE;
1424 0 RETRACT(&ss->sprinter, rval);
1425 0 js_printf(jp, "\tcontinue %s;\n", rval);
1426 0 break;
1427 case SRC_CONTINUE:
1428 0 js_printf(jp, "\tcontinue;\n");
1429 0 break;
1430 case SRC_BREAK2LABEL:
1431 0 atom = js_GetAtom(cx, &jp->script->atomMap,
1432 (jsatomid) js_GetSrcNoteOffset(sn, 0));
1433 0 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1434 0 if (!rval)
1435 0 return JS_FALSE;
1436 0 RETRACT(&ss->sprinter, rval);
1437 0 js_printf(jp, "\tbreak %s;\n", rval);
1438 0 break;
1439 case SRC_HIDDEN:
1440 0 break;
1441 default:
1442 0 js_printf(jp, "\tbreak;\n");
1443 break;
1444 }
1445 0 todo = -2;
1446 0 break;
1447
1448 case JSOP_IFEQ:
1449 case JSOP_IFEQX:
1450 0 len = GetJumpOffset(pc, pc);
1451 0 sn = js_GetSrcNote(jp->script, pc);
1452
1453 0 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
1454 case SRC_IF:
1455 case SRC_IF_ELSE:
1456 0 rval = POP_STR();
1457 0 js_printf(jp, "\tif (%s) {\n", rval);
1458 0 jp->indent += 4;
1459 0 if (SN_TYPE(sn) == SRC_IF) {
1460 0 DECOMPILE_CODE(pc + oplen, len - oplen);
1461 } else {
1462 0 len = js_GetSrcNoteOffset(sn, 0);
1463 0 DECOMPILE_CODE(pc + oplen, len - oplen);
1464 0 jp->indent -= 4;
1465 0 pc += len;
1466 0 LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX);
1467 0 oplen = js_CodeSpec[*pc].length;
1468 0 len = GetJumpOffset(pc, pc);
1469 0 js_printf(jp, "\t} else {\n");
1470 0 jp->indent += 4;
1471 0 DECOMPILE_CODE(pc + oplen, len - oplen);
1472 }
1473 0 jp->indent -= 4;
1474 0 js_printf(jp, "\t}\n");
1475 0 todo = -2;
1476 0 break;
1477
1478 case SRC_WHILE:
1479 0 rval = POP_STR();
1480 0 js_printf(jp, "\twhile (%s) {\n", rval);
1481 0 jp->indent += 4;
1482 0 tail = js_GetSrcNoteOffset(sn, 0);
1483 0 DECOMPILE_CODE(pc + oplen, tail - oplen);
1484 0 jp->indent -= 4;
1485 0 js_printf(jp, "\t}\n");
1486 0 todo = -2;
1487 0 break;
1488
1489 case SRC_COND:
1490 0 xval = JS_strdup(cx, POP_STR());
1491 0 if (!xval)
1492 0 return JS_FALSE;
1493 0 len = js_GetSrcNoteOffset(sn, 0);
1494 0 DECOMPILE_CODE(pc + oplen, len - oplen);
1495 0 lval = JS_strdup(cx, POP_STR());
1496 0 if (!lval) {
1497 0 JS_free(cx, (void *)xval);
1498 0 return JS_FALSE;
1499 }
1500 0 pc += len;
1501 0 LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX);
1502 0 oplen = js_CodeSpec[*pc].length;
1503 0 len = GetJumpOffset(pc, pc);
1504 0 DECOMPILE_CODE(pc + oplen, len - oplen);
1505 0 rval = POP_STR();
1506 0 todo = Sprint(&ss->sprinter, "%s ? %s : %s",
1507 xval, lval, rval);
1508 0 JS_free(cx, (void *)xval);
1509 0 JS_free(cx, (void *)lval);
1510 break;
1511
1512 default:
1513 break;
1514 }
1515 0 break;
1516
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 0 jp->indent -= 4;
1522 0 js_printf(jp, "\t} while (%s);\n", POP_STR());
1523 0 todo = -2;
1524 #else
1525 JS_ASSERT(0);
1526 #endif /* JS_HAS_DO_WHILE_LOOP */
1527 0 break;
1528
1529 case JSOP_OR:
1530 case JSOP_ORX:
1531 0 xval = "||";
1532
1533 0 do_logical_connective:
1534 /* Top of stack is the first clause in a disjunction (||). */
1535 0 lval = JS_strdup(cx, POP_STR());
1536 0 if (!lval)
1537 0 return JS_FALSE;
1538 0 done = pc + GetJumpOffset(pc, pc);
1539 0 pc += len;
1540 0 len = PTRDIFF(done, pc, jsbytecode);
1541 0 DECOMPILE_CODE(pc, len);
1542 0 rval = POP_STR();
1543 0 if (jp->pretty &&
1544 jp->indent + 4 + strlen(lval) + 4 + strlen(rval) > 75) {
1545 0 rval = JS_strdup(cx, rval);
1546 0 if (!rval) {
1547 0 tail = -1;
1548 } else {
1549 0 todo = Sprint(&ss->sprinter, "%s %s\n", lval, xval);
1550 0 tail = Sprint(&ss->sprinter, "%*s%s",
1551 jp->indent + 4, "", rval);
1552 0 JS_free(cx, (char *)rval);
1553 }
1554 0 if (tail < 0)
1555 0 todo = -1;
1556 } else {
1557 0 todo = Sprint(&ss->sprinter, "%s %s %s", lval, xval, rval);
1558 }
1559 0 JS_free(cx, (char *)lval);
1560 0 break;
1561
1562 case JSOP_AND:
1563 case JSOP_ANDX:
1564 0 xval = "&&";
1565 0 goto do_logical_connective;
1566
1567 case JSOP_FORARG:
1568 0 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
1569 0 LOCAL_ASSERT(atom);
1570 0 goto do_fornameinloop;
1571
1572 case JSOP_FORVAR:
1573 0 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
1574 0 LOCAL_ASSERT(atom);
1575 0 goto do_fornameinloop;
1576
1577 case JSOP_FORNAME:
1578 0 atom = GET_ATOM(cx, jp->script, pc);
1579
1580 0 do_fornameinloop:
1581 0 sn = js_GetSrcNote(jp->script, pc);
1582 0 xval = NULL;
1583 0 lval = "";
1584 0 goto do_forinloop;
1585
1586 case JSOP_FORPROP:
1587 0 xval = NULL;
1588 0 atom = GET_ATOM(cx, jp->script, pc);
1589 0 if (!ATOM_IS_IDENTIFIER(atom)) {
1590 0 xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
1591 (jschar)'\'');
1592 0 if (!xval)
1593 0 return JS_FALSE;
1594 0 atom = NULL;
1595 }
1596 0 lval = POP_STR();
1597 0 sn = NULL;
1598
1599 0 do_forinloop:
1600 0 pc += oplen;
1601 0 LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
1602 0 oplen = js_CodeSpec[*pc].length;
1603 0 len = GetJumpOffset(pc, pc);
1604 0 sn2 = js_GetSrcNote(jp->script, pc);
1605 0 tail = js_GetSrcNoteOffset(sn2, 0);
1606
1607 0 do_forinbody:
1608 #if JS_HAS_XML_SUPPORT
1609 0 if (foreach) {
1610 0 foreach = JS_FALSE;
1611 0 js_printf(jp, "\tfor %s (%s%s",
1612 js_each_str, VarPrefix(sn), lval);
1613 } else
1614 #endif
1615 0 js_printf(jp, "\tfor (%s%s", VarPrefix(sn), lval);
1616 0 if (atom) {
1617 0 xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1618 0 if (!xval)
1619 0 return JS_FALSE;
1620 0 RETRACT(&ss->sprinter, xval);
1621 0 js_printf(jp, *lval ? ".%s" : "%s", xval);
1622 0 } else if (xval && *xval) {
1623 0 js_printf(jp,
1624 (js_CodeSpec[lastop].format & JOF_XMLNAME)
1625 ? ".%s"
1626 : "[%s]",
1627 xval);
1628 }
1629 0 rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]);
1630 0 js_printf(jp, " in %s) {\n", rval);
1631 0 jp->indent += 4;
1632 0 DECOMPILE_CODE(pc + oplen, tail - oplen);
1633 0 jp->indent -= 4;
1634 0 js_printf(jp, "\t}\n");
1635 0 todo = -2;
1636 0 break;
1637
1638 case JSOP_FORELEM:
1639 0 pc++;
1640 0 LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
1641 0 len = js_CodeSpec[*pc].length;
1642
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 0 sn = js_GetSrcNote(jp->script, pc);
1650 JS_ASSERT(!forelem_tail);
1651 0 forelem_tail = pc + js_GetSrcNoteOffset(sn, 0);
1652
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 0 forelem_done = pc + GetJumpOffset(pc, pc);
1667 0 break;
1668
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 0 atom = NULL;
1678 0 xval = POP_STR();
1679 0 lval = POP_STR();
1680 0 rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]);
1681 JS_ASSERT(forelem_tail > pc);
1682 0 tail = forelem_tail - pc;
1683 0 forelem_tail = NULL;
1684 JS_ASSERT(forelem_done > pc);
1685 0 len = forelem_done - pc;
1686 0 forelem_done = NULL;
1687 0 goto do_forinbody;
1688
1689 #if JS_HAS_GETTER_SETTER
1690 case JSOP_GETTER:
1691 case JSOP_SETTER:
1692 0 todo = -2;
1693 0 break;
1694 #endif
1695
1696 case JSOP_DUP2:
1697 0 rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-2]);
1698 0 todo = SprintPut(&ss->sprinter, rval, strlen(rval));
1699 0 if (todo < 0 || !PushOff(ss, todo, ss->opcodes[ss->top-2]))
1700 0 return JS_FALSE;
1701 /* FALL THROUGH */
1702
1703 case JSOP_DUP:
1704 0 rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]);
1705 0 op = ss->opcodes[ss->top-1];
1706 0 todo = SprintPut(&ss->sprinter, rval, strlen(rval));
1707 0 break;
1708
1709 case JSOP_SETARG:
1710 0 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
1711 0 LOCAL_ASSERT(atom);
1712 0 goto do_setname;
1713
1714 case JSOP_SETVAR:
1715 0 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
1716 0 LOCAL_ASSERT(atom);
1717 0 goto do_setname;
1718
1719 case JSOP_SETCONST:
1720 case JSOP_SETNAME:
1721 case JSOP_SETGVAR:
1722 0 atomIndex = GET_ATOM_INDEX(pc);
1723
1724 0 do_JSOP_SETCONST:
1725 0 atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex);
1726
1727 0 do_setname:
1728 0 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1729 0 if (!lval)
1730 0 return JS_FALSE;
1731 0 rval = POP_STR();
1732 0 if (op == JSOP_SETNAME)
1733 0 (void) PopOff(ss, op);
1734
1735 0 do_setlval:
1736 0 sn = js_GetSrcNote(jp->script, pc - 1);
1737 0 if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
1738 0 todo = Sprint(&ss->sprinter, "%s %s= %s",
1739 lval, js_CodeSpec[lastop].token, rval);
1740 } else {
1741 0 sn = js_GetSrcNote(jp->script, pc);
1742 0 todo = Sprint(&ss->sprinter, "%s%s = %s",
1743 VarPrefix(sn), lval, rval);
1744 }
1745 0 break;
1746
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 0 saveop = op;
1754 0 op = JSOP_NOP; /* turn off parens */
1755 0 argc = GET_ARGC(pc);
1756 0 argv = (char **)
1757 JS_malloc(cx, (size_t)(argc + 1) * sizeof *argv);
1758 0 if (!argv)
1759 0 return JS_FALSE;
1760
1761 0 ok = JS_TRUE;
1762 0 for (i = argc; i > 0; i--) {
1763 0 argv[i] = JS_strdup(cx, POP_STR());
1764 0 if (!argv[i]) {
1765 0 ok = JS_FALSE;
1766 0 break;
1767 }
1768 }
1769
1770 /* Skip the JSOP_PUSHOBJ-created empty string. */
1771 0 LOCAL_ASSERT(ss->top >= 2);
1772 0 (void) PopOff(ss, op);
1773
1774 /* Get the callee's decompiled image in argv[0]. */
1775 0 argv[0] = JS_strdup(cx, POP_STR());
1776 0 if (!argv[i])
1777 0 ok = JS_FALSE;
1778
1779 0 lval = "(", rval = ")";
1780 0 if (saveop == JSOP_NEW) {
1781 0 todo = Sprint(&ss->sprinter, "%s %s%s",
1782 js_new_str, argv[0], lval);
1783 } else {
1784 0 todo = Sprint(&ss->sprinter, "%s%s",
1785 argv[0], lval);
1786 }
1787 0 if (todo < 0)
1788 0 ok = JS_FALSE;
1789
1790 0 for (i = 1; i <= argc; i++) {
1791 0 if (!argv[i] ||
1792 Sprint(&ss->sprinter, "%s%s",
1793 argv[i], (i < argc) ? ", " : "") < 0) {
1794 0 ok = JS_FALSE;
1795 0 break;
1796 }
1797 }
1798 0 if (Sprint(&ss->sprinter, rval) < 0)
1799 0 ok = JS_FALSE;
1800
1801 0 for (i = 0; i <= argc; i++) {
1802 0 if (argv[i])
1803 0 JS_free(cx, argv[i]);
1804 }
1805 0 JS_free(cx, argv);
1806 0 if (!ok)
1807 0 return JS_FALSE;
1808 0 op = saveop;
1809 #if JS_HAS_LVALUE_RETURN
1810 0 if (op == JSOP_SETCALL) {
1811 0 if (!PushOff(ss, todo, op))
1812 0 return JS_FALSE;
1813 0 todo = Sprint(&ss->sprinter, "");
1814 }
1815 #endif
1816 0 break;
1817
1818 case JSOP_DELNAME:
1819 0 atom = GET_ATOM(cx, jp->script, pc);
1820 0 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1821 0 if (!lval)
1822 0 return JS_FALSE;
1823 0 RETRACT(&ss->sprinter, lval);
1824 0 todo = Sprint(&ss->sprinter, "%s %s", js_delete_str, lval);
1825 0 break;
1826
1827 case JSOP_DELPROP:
1828 0 GET_ATOM_QUOTE_AND_FMT("%s %s[%s]", "%s %s.%s", rval);
1829 0 lval = POP_STR();
1830 0 todo = Sprint(&ss->sprinter, fmt, js_delete_str, lval, rval);
1831 0 break;
1832
1833 case JSOP_DELELEM:
1834 0 xval = POP_STR();
1835 0 lval = POP_STR();
1836 0 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 0 break;
1842
1843 #if JS_HAS_XML_SUPPORT
1844 case JSOP_DELDESC:
1845 0 xval = POP_STR();
1846 0 lval = POP_STR();
1847 0 todo = Sprint(&ss->sprinter, "%s %s..%s",
1848 js_delete_str, lval, xval);
1849 0 break;
1850 #endif
1851
1852 case JSOP_TYPEOF:
1853 case JSOP_VOID:
1854 0 rval = POP_STR();
1855 0 todo = Sprint(&ss->sprinter, "%s %s", cs->name, rval);
1856 0 break;
1857
1858 case JSOP_INCARG:
1859 case JSOP_DECARG:
1860 0 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
1861 0 LOCAL_ASSERT(atom);
1862 0 goto do_incatom;
1863
1864 case JSOP_INCVAR:
1865 case JSOP_DECVAR:
1866 0 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
1867 0 LOCAL_ASSERT(atom);
1868 0 goto do_incatom;
1869
1870 case JSOP_INCNAME:
1871 case JSOP_DECNAME:
1872 case JSOP_INCGVAR:
1873 case JSOP_DECGVAR:
1874 0 atom = GET_ATOM(cx, jp->script, pc);
1875 0 do_incatom:
1876 0 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1877 0 if (!lval)
1878 0 return JS_FALSE;
1879 0 RETRACT(&ss->sprinter, lval);
1880 0 todo = Sprint(&ss->sprinter, "%s%s",
1881 js_incop_str[!(cs->format & JOF_INC)], lval);
1882 0 break;
1883
1884 case JSOP_INCPROP:
1885 case JSOP_DECPROP:
1886 0 GET_ATOM_QUOTE_AND_FMT("%s%s[%s]", "%s%s.%s", rval);
1887 0 lval = POP_STR();
1888 0 todo = Sprint(&ss->sprinter, fmt,
1889 js_incop_str[!(cs->format & JOF_INC)],
1890 lval, rval);
1891 0 break;
1892
1893 case JSOP_INCELEM:
1894 case JSOP_DECELEM:
1895 0 xval = POP_STR();
1896 0 lval = POP_STR();
1897 0 if (*xval != '\0') {
1898 0 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 0 todo = Sprint(&ss->sprinter, "%s%s",
1906 js_incop_str[!(cs->format & JOF_INC)], lval);
1907 }
1908 0 break;
1909
1910 case JSOP_ARGINC:
1911 case JSOP_ARGDEC:
1912 0 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
1913 0 LOCAL_ASSERT(atom);
1914 0 goto do_atominc;
1915
1916 case JSOP_VARINC:
1917 case JSOP_VARDEC:
1918 0 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
1919 0 LOCAL_ASSERT(atom);
1920 0 goto do_atominc;
1921
1922 case JSOP_NAMEINC:
1923 case JSOP_NAMEDEC:
1924 case JSOP_GVARINC:
1925 case JSOP_GVARDEC:
1926 0 atom = GET_ATOM(cx, jp->script, pc);
1927 0 do_atominc:
1928 0 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1929 0 if (!lval)
1930 0 return JS_FALSE;
1931 0 todo = STR2OFF(&ss->sprinter, lval);
1932 0 SprintPut(&ss->sprinter,
1933 js_incop_str[!(cs->format & JOF_INC)],
1934 2);
1935 0 break;
1936
1937 case JSOP_PROPINC:
1938 case JSOP_PROPDEC:
1939 0 GET_ATOM_QUOTE_AND_FMT("%s[%s]%s", "%s.%s%s", rval);
1940 0 lval = POP_STR();
1941 0 todo = Sprint(&ss->sprinter, fmt, lval, rval,
1942 js_incop_str[!(cs->format & JOF_INC)]);
1943 0 break;
1944
1945 case JSOP_ELEMINC:
1946 case JSOP_ELEMDEC:
1947 0 xval = POP_STR();
1948 0 lval = POP_STR();
1949 0 if (*xval != '\0') {
1950 0 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 0 todo = Sprint(&ss->sprinter, "%s%s",
1958 lval, js_incop_str[!(cs->format & JOF_INC)]);
1959 }
1960 0 break;
1961
1962 case JSOP_GETPROP2:
1963 0 op = JSOP_GETPROP;
1964 0 (void) PopOff(ss, lastop);
1965 /* FALL THROUGH */
1966
1967 case JSOP_GETPROP:
1968 0 atom = GET_ATOM(cx, jp->script, pc);
1969
1970 0 do_getprop:
1971 0 GET_QUOTE_AND_FMT("%s[%s]", "%s.%s", rval);
1972
1973 0 do_getprop_lval:
1974 0 lval = POP_STR();
1975 0 todo = Sprint(&ss->sprinter, fmt, lval, rval);
1976 0 break;
1977
1978 #if JS_HAS_XML_SUPPORT
1979 0 BEGIN_LITOPX_CASE(JSOP_GETMETHOD)
1980 0 sn = js_GetSrcNote(jp->script, pc);
1981 0 if (sn && SN_TYPE(sn) == SRC_PCBASE)
1982 0 goto do_getprop;
1983 0 GET_QUOTE_AND_FMT("%s.function::[%s]", "%s.function::%s", rval);
1984 0 goto do_getprop_lval;
1985
1986 0 BEGIN_LITOPX_CASE(JSOP_SETMETHOD)
1987 0 sn = js_GetSrcNote(jp->script, pc);
1988 0 if (sn && SN_TYPE(sn) == SRC_PCBASE)
1989 0 goto do_setprop;
1990 0 GET_QUOTE_AND_FMT("%s.function::[%s] %s= %s",
1991 "%s.function::%s %s= %s",
1992 xval);
1993 0 goto do_setprop_rval;
1994 #endif
1995
1996 case JSOP_SETPROP:
1997 0 atom = GET_ATOM(cx, jp->script, pc);
1998
1999 0 do_setprop:
2000 0 GET_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval);
2001
2002 0 do_setprop_rval:
2003 0 rval = POP_STR();
2004 0 lval = POP_STR();
2005 0 sn = js_GetSrcNote(jp->script, pc - 1);
2006 0 todo = Sprint(&ss->sprinter, fmt, lval, xval,
2007 (sn && SN_TYPE(sn) == SRC_ASSIGNOP)
2008 ? js_CodeSpec[lastop].token
2009 : "",
2010 rval);
2011 0 break;
2012
2013 case JSOP_GETELEM2:
2014 0 op = JSOP_GETELEM;
2015 0 (void) PopOff(ss, lastop);
2016 /* FALL THROUGH */
2017
2018 case JSOP_GETELEM:
2019 0 op = JSOP_NOP; /* turn off parens */
2020 0 xval = POP_STR();
2021 0 op = JSOP_GETELEM;
2022 0 lval = POP_STR();
2023 0 if (*xval == '\0') {
2024 0 todo = Sprint(&ss->sprinter, "%s", lval);
2025 } else {
2026 0 todo = Sprint(&ss->sprinter,
2027 (js_CodeSpec[lastop].format & JOF_XMLNAME)
2028 ? "%s.%s"
2029 : "%s[%s]",
2030 lval, xval);
2031 }
2032 0 break;
2033
2034 case JSOP_SETELEM:
2035 0 op = JSOP_NOP; /* turn off parens */
2036 0 rval = POP_STR();
2037 0 xval = POP_STR();
2038 0 op = JSOP_SETELEM;
2039 0 lval = POP_STR();
2040 0 if (*xval == '\0')
2041 0 goto do_setlval;
2042 0 sn = js_GetSrcNote(jp->script, pc - 1);
2043 0 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 0 break;
2053
2054 case JSOP_ARGSUB:
2055 0 i = (jsint) GET_ATOM_INDEX(pc);
2056 0 todo = Sprint(&ss->sprinter, "%s[%d]",
2057 js_arguments_str, (int) i);
2058 0 break;
2059
2060 case JSOP_ARGCNT:
2061 0 todo = Sprint(&ss->sprinter, "%s.%s",
2062 js_arguments_str, js_length_str);
2063 0 break;
2064
2065 case JSOP_GETARG:
2066 0 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
2067 0 LOCAL_ASSERT(atom);
2068 0 goto do_name;
2069
2070 case JSOP_GETVAR:
2071 0 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
2072 0 LOCAL_ASSERT(atom);
2073 0 goto do_name;
2074
2075 case JSOP_NAME:
2076 case JSOP_GETGVAR:
2077 0 atom = GET_ATOM(cx, jp->script, pc);
2078 0 do_name:
2079 0 sn = js_GetSrcNote(jp->script, pc);
2080 0 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2081 0 if (!rval)
2082 0 return JS_FALSE;
2083 0 RETRACT(&ss->sprinter, rval);
2084 0 todo = Sprint(&ss->sprinter, "%s%s", VarPrefix(sn), rval);
2085 0 break;
2086
2087 case JSOP_UINT16:
2088 0 i = (jsint) GET_ATOM_INDEX(pc);
2089 0 goto do_sprint_int;
2090
2091 case JSOP_UINT24:
2092 0 i = (jsint) GET_LITERAL_INDEX(pc);
2093 0 do_sprint_int:
2094 0 todo = Sprint(&ss->sprinter, "%u", (unsigned) i);
2095 0 break;
2096
2097 case JSOP_LITERAL:
2098 0 atomIndex = GET_LITERAL_INDEX(pc);
2099 0 goto do_JSOP_STRING;
2100
2101 case JSOP_FINDNAME:
2102 0 atomIndex = GET_LITERAL_INDEX(pc);
2103 0 todo = Sprint(&ss->sprinter, "");
2104 0 if (todo < 0 || !PushOff(ss, todo, op))
2105 0 return JS_FALSE;
2106 0 atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex);
2107 0 goto do_name;
2108
2109 case JSOP_LITOPX:
2110 0 atomIndex = GET_LITERAL_INDEX(pc);
2111 0 op = pc[1 + LITERAL_INDEX_LEN];
2112 0 switch (op) {
2113 0 case JSOP_ANONFUNOBJ: goto do_JSOP_ANONFUNOBJ;
2114 0 case JSOP_BINDNAME: goto do_JSOP_BINDNAME;
2115 0 case JSOP_CLOSURE: goto do_JSOP_CLOSURE;
2116 #if JS_HAS_EXPORT_IMPORT
2117 0 case JSOP_EXPORTNAME: goto do_JSOP_EXPORTNAME;
2118 #endif
2119 #if JS_HAS_XML_SUPPORT
2120 0 case JSOP_GETMETHOD: goto do_JSOP_GETMETHOD;
2121 0 case JSOP_SETMETHOD: goto do_JSOP_SETMETHOD;
2122 #endif
2123 0 case JSOP_NAMEDFUNOBJ: goto do_JSOP_NAMEDFUNOBJ;
2124 0 case JSOP_NUMBER: goto do_JSOP_NUMBER;
2125 0 case JSOP_OBJECT: goto do_JSOP_OBJECT;
2126 #if JS_HAS_XML_SUPPORT
2127 0 case JSOP_QNAMECONST: goto do_JSOP_QNAMECONST;
2128 0 case JSOP_QNAMEPART: goto do_JSOP_QNAMEPART;
2129 #endif
2130 0 case JSOP_REGEXP: goto do_JSOP_REGEXP;
2131 0 case JSOP_SETCONST: goto do_JSOP_SETCONST;
2132 0 case JSOP_STRING: goto do_JSOP_STRING;
2133 #if JS_HAS_XML_SUPPORT
2134 0 case JSOP_XMLCDATA: goto do_JSOP_XMLCDATA;
2135 0 case JSOP_XMLCOMMENT: goto do_JSOP_XMLCOMMENT;
2136 0 case JSOP_XMLOBJECT: goto do_JSOP_XMLOBJECT;
2137 0 case JSOP_XMLPI: goto do_JSOP_XMLPI;
2138 #endif
2139 default: JS_ASSERT(0);
2140 }
2141 /* NOTREACHED */
2142 0 break;
2143
2144 0 BEGIN_LITOPX_CASE(JSOP_NUMBER)
2145 0 val = ATOM_KEY(atom);
2146 0 if (JSVAL_IS_INT(val)) {
2147 0 long ival = (long)JSVAL_TO_INT(val);
2148 0 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 0, *JSVAL_TO_DOUBLE(val));
2153 0 if (!numStr) {
2154 0 JS_ReportOutOfMemory(cx);
2155 0 return JS_FALSE;
2156 }
2157 0 todo = Sprint(&ss->sprinter, numStr);
2158 }
2159 0 END_LITOPX_CASE
2160
2161 0 BEGIN_LITOPX_CASE(JSOP_STRING)
2162 0 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
2163 inXML ? 0 : (jschar)'"');
2164 0 if (!rval)
2165 0 return JS_FALSE;
2166 0 todo = STR2OFF(&ss->sprinter, rval);
2167 0 END_LITOPX_CASE
2168
2169 case JSOP_OBJECT:
2170 case JSOP_REGEXP:
2171 case JSOP_ANONFUNOBJ:
2172 case JSOP_NAMEDFUNOBJ:
2173 0 atomIndex = GET_ATOM_INDEX(pc);
2174
2175 0 do_JSOP_OBJECT:
2176 0 do_JSOP_REGEXP:
2177 0 do_JSOP_ANONFUNOBJ:
2178 0 do_JSOP_NAMEDFUNOBJ:
2179 0 atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex);
2180 0 if (op == JSOP_OBJECT || op == JSOP_REGEXP) {
2181 0 if (!js_regexp_toString(cx, ATOM_TO_OBJECT(atom), 0, NULL,
2182 &val)) {
2183 0 return JS_FALSE;
2184 }
2185 } else {
2186 0 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 0 return JS_FALSE;
2194 }
2195 }
2196 0 str = JSVAL_TO_STRING(val);
2197 0 todo = SprintPut(&ss->sprinter, JS_GetStringBytes(str),
2198 JSSTRING_LENGTH(str));
2199 0 break;
2200
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;
2209
2210 0 sn = js_GetSrcNote(jp->script, pc);
2211 JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
2212 0 len = js_GetSrcNoteOffset(sn, 0);
2213 0 jmplen = (op == JSOP_TABLESWITCH) ? JUMP_OFFSET_LEN
2214 : JUMPX_OFFSET_LEN;
2215 0 pc2 = pc;
2216 0 off = GetJumpOffset(pc, pc2);
2217 0 pc2 += jmplen;
2218 0 low = GET_JUMP_OFFSET(pc2);
2219 0 pc2 += JUMP_OFFSET_LEN;
2220 0 high = GET_JUMP_OFFSET(pc2);
2221 0 pc2 += JUMP_OFFSET_LEN;
2222
2223 0 n = high - low + 1;
2224 0 if (n == 0) {
2225 0 table = NULL;
2226 0 j = 0;
2227 } else {
2228 0 table = (TableEntry *)
2229 JS_malloc(cx, (size_t)n * sizeof *table);
2230 0 if (!table)
2231 0 return JS_FALSE;
2232 0 for (i = j = 0; i < n; i++) {
2233 0 table[j].label = NULL;
2234 0 off2 = GetJumpOffset(pc, pc2);
2235 0 if (off2) {
2236 0 sn = js_GetSrcNote(jp->script, pc2);
2237 0 if (sn) {
2238 JS_ASSERT(SN_TYPE(sn) == SRC_LABEL);
2239 0 table[j].label =
2240 js_GetAtom(cx, &jp->script->atomMap,
2241 (jsatomid)
2242 js_GetSrcNoteOffset(sn, 0));
2243 }
2244 0 table[j].key = INT_TO_JSVAL(low + i);
2245 0 table[j].offset = off2;
2246 0 table[j].order = j;
2247 0 j++;
2248 }
2249 0 pc2 += jmplen;
2250 }
2251 0 js_HeapSort(table, (size_t) j, &pivot, sizeof(TableEntry),
2252 CompareOffsets, NULL);
2253 }
2254
2255 0 ok = DecompileSwitch(ss, table, (uintN)j, pc, len, off,
2256 JS_FALSE);
2257 0 JS_free(cx, table);
2258 0 if (!ok)
2259 0 return ok;
2260 0 todo = -2;
2261 0 break;
2262 }
2263
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;
2271
2272 0 sn = js_GetSrcNote(jp->script, pc);
2273 JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
2274 0 len = js_GetSrcNoteOffset(sn, 0);
2275 0 jmplen = (op == JSOP_LOOKUPSWITCH) ? JUMP_OFFSET_LEN
2276 : JUMPX_OFFSET_LEN;
2277 0 pc2 = pc;
2278 0 off = GetJumpOffset(pc, pc2);
2279 0 pc2 += jmplen;
2280 0 npairs = GET_ATOM_INDEX(pc2);
2281 0 pc2 += ATOM_INDEX_LEN;
2282
2283 0 table = (TableEntry *)
2284 JS_malloc(cx, (size_t)npairs * sizeof *table);
2285 0 if (!table)
2286 0 return JS_FALSE;
2287 0 for (k = 0; k < npairs; k++) {
2288 0 sn = js_GetSrcNote(jp->script, pc2);
2289 0 if (sn) {
2290 JS_ASSERT(SN_TYPE(sn) == SRC_LABEL);
2291 0 table[k].label =
2292 js_GetAtom(cx, &jp->script->atomMap, (jsatomid)
2293 js_GetSrcNoteOffset(sn, 0));
2294 } else {
2295 0 table[k].label = NULL;
2296 }
2297 0 atom = GET_ATOM(cx, jp->script, pc2);
2298 0 pc2 += ATOM_INDEX_LEN;
2299 0 off2 = GetJumpOffset(pc, pc2);
2300 0 pc2 += jmplen;
2301 0 table[k].key = ATOM_KEY(atom);
2302 0 table[k].offset = off2;
2303 }
2304
2305 0 ok = DecompileSwitch(ss, table, (uintN)npairs, pc, len, off,
2306 JS_FALSE);
2307 0 JS_free(cx, table);
2308 0 if (!ok)
2309 0 return ok;
2310 0 todo = -2;
2311 0 break;
2312 }
2313
2314 case JSOP_CONDSWITCH:
2315 {
2316 jsbytecode *pc2;
2317 ptrdiff_t off, off2, caseOff;
2318 jsint ncases;
2319 TableEntry *table;
2320
2321 0 sn = js_GetSrcNote(jp->script, pc);
2322 JS_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
2323 0 len = js_GetSrcNoteOffset(sn, 0);
2324 0 off = js_GetSrcNoteOffset(sn, 1);
2325
2326 /*
2327 * Count the cases using offsets from switch to first case,
2328 * and case to case, stored in srcnote immediates.
2329 */
2330 0 pc2 = pc;
2331 0 off2 = off;
2332 0 for (ncases = 0; off2 != 0; ncases++) {
2333 0 pc2 += off2;
2334 JS_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT ||
2335 *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX);
2336 0 if (*pc2 == JSOP_DEFAULT || *pc2 == JSOP_DEFAULTX) {
2337 /* End of cases, but count default as a case. */
2338 0 off2 = 0;
2339 } else {
2340 0 sn = js_GetSrcNote(jp->script, pc2);
2341 JS_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
2342 0 off2 = js_GetSrcNoteOffset(sn, 0);
2343 }
2344 }
2345
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 0 table = (TableEntry *)
2352 JS_malloc(cx, (size_t)ncases * sizeof *table);
2353 0 if (!table)
2354 0 return JS_FALSE;
2355 0 pc2 = pc;
2356 0 off2 = off;
2357 0 for (i = 0; i < ncases; i++) {
2358 0 pc2 += off2;
2359 JS_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT ||
2360 *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX);
2361 0 caseOff = pc2 - pc;
2362 0 table[i].key = INT_TO_JSVAL((jsint) caseOff);
2363 0 table[i].offset = caseOff + GetJumpOffset(pc2, pc2);
2364 0 if (*pc2 == JSOP_CASE || *pc2 == JSOP_CASEX) {
2365 0 sn = js_GetSrcNote(jp->script, pc2);
2366 JS_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
2367 0 off2 = js_GetSrcNoteOffset(sn, 0);
2368 }
2369 }
2370
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 0 off = JSVAL_TO_INT(table[ncases-1].key);
2377 0 pc2 = pc + off;
2378 0 off += GetJumpOffset(pc2, pc2);
2379
2380 0 ok = DecompileSwitch(ss, table, (uintN)ncases, pc, len, off,
2381 JS_TRUE);
2382 0 JS_free(cx, table);
2383 0 if (!ok)
2384 0 return ok;
2385 0 todo = -2;
2386 0 break;
2387 }
2388
2389 case JSOP_CASE:
2390 case JSOP_CASEX:
2391 {
2392 0 lval = POP_STR();
2393 0 if (!lval)
2394 0 return JS_FALSE;
2395 0 js_printf(jp, "\tcase %s:\n", lval);
2396 0 todo = -2;
2397 0 break;
2398 }
2399
2400 #endif /* JS_HAS_SWITCH_STATEMENT */
2401
2402 #if !JS_BUG_FALLIBLE_EQOPS
2403 case JSOP_NEW_EQ:
2404 case JSOP_NEW_NE:
2405 0 rval = POP_STR();
2406 0 lval = POP_STR();
2407 0 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 0 break;
2416 #endif
2417
2418 #if JS_HAS_LEXICAL_CLOSURE
2419 0 BEGIN_LITOPX_CASE(JSOP_CLOSURE)
2420 JS_ASSERT(ATOM_IS_OBJECT(atom));
2421 0 todo = -2;
2422 0 goto do_function;
2423 END_LITOPX_CASE
2424 #endif
2425
2426 #if JS_HAS_EXPORT_IMPORT
2427 case JSOP_EXPORTALL:
2428 0 js_printf(jp, "\texport *\n");
2429 0 todo = -2;
2430 0 break;
2431
2432 0 BEGIN_LITOPX_CASE(JSOP_EXPORTNAME)
2433 0 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2434 0 if (!rval)
2435 0 return JS_FALSE;
2436 0 RETRACT(&ss->sprinter, rval);
2437 0 js_printf(jp, "\texport %s\n", rval);
2438 0 todo = -2;
2439 0 END_LITOPX_CASE
2440
2441 case JSOP_IMPORTALL:
2442 0 lval = POP_STR();
2443 0 js_printf(jp, "\timport %s.*\n", lval);
2444 0 todo = -2;
2445 0 break;
2446
2447 case JSOP_IMPORTPROP:
2448 0 do_importprop:
2449 0 GET_ATOM_QUOTE_AND_FMT("\timport %s[%s]\n", "\timport %s.%s\n",
2450 rval);
2451 0 lval = POP_STR();
2452 0 js_printf(jp, fmt, lval, rval);
2453 0 todo = -2;
2454 0 break;
2455
2456 case JSOP_IMPORTELEM:
2457 0 xval = POP_STR();
2458 0 op = JSOP_GETELEM;
2459 0 if (js_CodeSpec[lastop].format & JOF_XMLNAME)
2460 0 goto do_importprop;
2461 0 lval = POP_STR();
2462 0 js_printf(jp, "\timport %s[%s]\n", lval, xval);
2463 0 todo = -2;
2464 0 break;
2465 #endif /* JS_HAS_EXPORT_IMPORT */
2466
2467 case JSOP_TRAP:
2468 0 op = JS_GetTrapOpcode(cx, jp->script, pc);
2469 0 if (op == JSOP_LIMIT)
2470 0 return JS_FALSE;
2471 0 *pc = op;
2472 0 cs = &js_CodeSpec[op];
2473 0 len = cs->length;
2474 0 DECOMPILE_CODE(pc, len);
2475 0 *pc = JSOP_TRAP;
2476 0 todo = -2;
2477 0 break;
2478
2479 #if JS_HAS_INITIALIZERS
2480 case JSOP_NEWINIT:
2481 0 LOCAL_ASSERT(ss->top >= 2);
2482 0 (void) PopOff(ss, op);
2483 0 lval = POP_STR();
2484 #if JS_HAS_SHARP_VARS
2485 0 op = (JSOp)pc[len];
2486 0 if (op == JSOP_DEFSHARP) {
2487 0 pc += len;
2488 0 cs = &js_CodeSpec[op];
2489 0 len = cs->length;
2490 0 i = (jsint) GET_ATOM_INDEX(pc);
2491 0 todo = Sprint(&ss->sprinter, "#%u=%c",
2492 (unsigned) i,
2493 (*lval == 'O') ? '{' : '[');
2494 } else
2495 #endif /* JS_HAS_SHARP_VARS */
2496 {
2497 0 todo = Sprint(&ss->sprinter, (*lval == 'O') ? "{" : "[");
2498 }
2499 0 break;
2500
2501 case JSOP_ENDINIT:
2502 0 rval = POP_STR();
2503 0 sn = js_GetSrcNote(jp->script, pc);
2504 0 todo = Sprint(&ss->sprinter, "%s%s%c",
2505 rval,
2506 (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "",
2507 (*rval == '{') ? '}' : ']');
2508 0 break;
2509
2510 case JSOP_INITPROP:
2511 0 atom = GET_ATOM(cx, jp->script, pc);
2512 0 xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
2513 (jschar)
2514 (ATOM_IS_IDENTIFIER(atom) ? 0 : '\''));
2515 0 if (!xval)
2516 0 return JS_FALSE;
2517 0 rval = POP_STR();
2518 0 lval = POP_STR();
2519 0 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 0 if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) {
2533 0 rval += strlen(js_function_str) + 1;
2534 0 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 0 todo = Sprint(&ss->sprinter, "%s%s%s:%s",
2544 lval,
2545 (lval[1] != '\0') ? ", " : "",
2546 xval,
2547 rval);
2548 }
2549 #endif
2550 0 break;
2551
2552 case JSOP_INITELEM:
2553 0 rval = POP_STR();
2554 0 xval = POP_STR();
2555 0 lval = POP_STR();
2556 0 sn = js_GetSrcNote(jp->script, pc);
2557 0 if (sn && SN_TYPE(sn) == SRC_LABEL)
2558 0 goto do_initprop;
2559 0 todo = Sprint(&ss->sprinter, "%s%s%s",
2560 lval,
2561 (lval[1] != '\0' || *xval != '0') ? ", " : "",
2562 rval);
2563 0 break;
2564
2565 #if JS_HAS_SHARP_VARS
2566 case JSOP_DEFSHARP:
2567 0 i = (jsint) GET_ATOM_INDEX(pc);
2568 0 rval = POP_STR();
2569 0 todo = Sprint(&ss->sprinter, "#%u=%s", (unsigned) i, rval);
2570 0 break;
2571
2572 case JSOP_USESHARP:
2573 0 i = (jsint) GET_ATOM_INDEX(pc);
2574 0 todo = Sprint(&ss->sprinter, "#%u#", (unsigned) i);
2575 0 break;
2576 #endif /* JS_HAS_SHARP_VARS */
2577 #endif /* JS_HAS_INITIALIZERS */
2578
2579 #if JS_HAS_DEBUGGER_KEYWORD
2580 case JSOP_DEBUGGER:
2581 0 js_printf(jp, "\tdebugger;\n");
2582 0 todo = -2;
2583 0 break;
2584 #endif /* JS_HAS_DEBUGGER_KEYWORD */
2585
2586 #if JS_HAS_XML_SUPPORT
2587 case JSOP_STARTXML:
2588 case JSOP_STARTXMLEXPR:
2589 0 inXML = op == JSOP_STARTXML;
2590 0 todo = -2;
2591 0 break;
2592
2593 case JSOP_DEFXMLNS:
2594 0 rval = POP_STR();
2595 0 js_printf(jp, "\t%s %s %s = %s;\n",
2596 js_default_str, js_xml_str, js_namespace_str, rval);
2597 0 todo = -2;
2598 0 break;
2599
2600 case JSOP_ANYNAME:
2601 0 todo = SprintPut(&ss->sprinter, "*", 1);
2602 0 break;
2603
2604 0 BEGIN_LITOPX_CASE(JSOP_QNAMEPART)
2605 0 goto do_name;
2606 END_LITOPX_CASE
2607
2608 0 BEGIN_LITOPX_CASE(JSOP_QNAMECONST)
2609 0 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2610 0 if (!rval)
2611 0 return JS_FALSE;
2612 0 RETRACT(&ss->sprinter, rval);
2613 0 lval = POP_STR();
2614 0 todo = Sprint(&ss->sprinter, "%s::%s", lval, rval);
2615 0 END_LITOPX_CASE
2616
2617 case JSOP_QNAME:
2618 0 rval = POP_STR();
2619 0 lval = POP_STR();
2620 0 todo = Sprint(&ss->sprinter, "%s::[%s]", lval, rval);
2621 0 break;
2622
2623 case JSOP_TOATTRNAME:
2624 0 rval = POP_STR();
2625 0 todo = Sprint(&ss->sprinter, "@%s", rval);
2626 0 break;
2627
2628 case JSOP_TOATTRVAL:
2629 0 todo = -2;
2630 0 break;
2631
2632 case JSOP_ADDATTRNAME:
2633 0 rval = POP_STR();
2634 0 lval = POP_STR();
2635 0 todo = Sprint(&ss->sprinter, "%s %s", lval, rval);
2636 /* This gets reset by all XML tag expressions. */
2637 0 quoteAttr = JS_TRUE;
2638 0 break;
2639
2640 case JSOP_ADDATTRVAL:
2641 0 rval = POP_STR();
2642 0 lval = POP_STR();
2643 0 if (quoteAttr)
2644 0 todo = Sprint(&ss->sprinter, "%s=\"%s\"", lval, rval);
2645 else
2646 0 todo = Sprint(&ss->sprinter, "%s=%s", lval, rval);
2647 0 break;
2648
2649 case JSOP_BINDXMLNAME:
2650 /* Leave the name stacked and push a dummy string. */
2651 0 todo = Sprint(&ss->sprinter, "");
2652 0 break;
2653
2654 case JSOP_SETXMLNAME:
2655 /* Pop the r.h.s., the dummy string, and the name. */
2656 0 rval = POP_STR();
2657 0 (void) PopOff(ss, op);
2658 0 lval = POP_STR();
2659 0 goto do_setlval;
2660
2661 case JSOP_XMLELTEXPR:
2662 case JSOP_XMLTAGEXPR:
2663 0 todo = Sprint(&ss->sprinter, "{%s}", POP_STR());
2664 0 inXML = JS_TRUE;
2665 /* If we're an attribute value, we shouldn't quote this. */
2666 0 quoteAttr = JS_FALSE;
2667 0 break;
2668
2669 case JSOP_TOXMLLIST:
2670 0 todo = Sprint(&ss->sprinter, "<>%s</>", POP_STR());
2671 0 inXML = JS_FALSE;
2672 0 break;
2673
2674 case JSOP_FOREACH:
2675 0 foreach = JS_TRUE;
2676 0 todo = -2;
2677 0 break;
2678
2679 case JSOP_TOXML:
2680 0 inXML = JS_FALSE;
2681 /* FALL THROUGH */
2682
2683 case JSOP_XMLNAME:
2684 case JSOP_FILTER:
2685 /* Conversion and prefix ops do nothing in the decompiler. */
2686 0 todo = -2;
2687 0 break;
2688
2689 case JSOP_ENDFILTER:
2690 0 rval = POP_STR();
2691 0 lval = POP_STR();
2692 0 todo = Sprint(&ss->sprinter, "%s.(%s)", lval, rval);
2693 0 break;
2694
2695 case JSOP_DESCENDANTS:
2696 0 rval = POP_STR();
2697 0 lval = POP_STR();
2698 0 todo = Sprint(&ss->sprinter, "%s..%s", lval, rval);
2699 0 break;
2700
2701 0 BEGIN_LITOPX_CASE(JSOP_XMLOBJECT)
2702 0 atom = GET_ATOM(cx, jp->script, pc);
2703 0 todo = Sprint(&ss->sprinter, "<xml address='%p'>",
2704 ATOM_TO_OBJECT(atom));
2705 0 END_LITOPX_CASE
2706
2707 0 BEGIN_LITOPX_CASE(JSOP_XMLCDATA)
2708 0 todo = SprintPut(&ss->sprinter, "<![CDATA[", 9);
2709 0 if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0))
2710 0 return JS_FALSE;
2711 0 SprintPut(&ss->sprinter, "]]>", 3);
2712 0 END_LITOPX_CASE
2713
2714 0 BEGIN_LITOPX_CASE(JSOP_XMLCOMMENT)
2715 0 todo = SprintPut(&ss->sprinter, "<!--", 4);
2716 0 if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0))
2717 0 return JS_FALSE;
2718 0 SprintPut(&ss->sprinter, "-->", 3);
2719 0 END_LITOPX_CASE
2720
2721 0 BEGIN_LITOPX_CASE(JSOP_XMLPI)
2722 0 rval = JS_strdup(cx, POP_STR());
2723 0 if (!rval)
2724 0 return JS_FALSE;
2725 0 todo = SprintPut(&ss->sprinter, "<?", 2);
2726 0 ok = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0) &&
2727 SprintPut(&ss->sprinter, " ", 1) >= 0 &&
2728 SprintPut(&ss->sprinter, rval, strlen(rval));
2729 0 JS_free(cx, (char *)rval);
2730 0 if (!ok)
2731 0 return JS_FALSE;
2732 0 SprintPut(&ss->sprinter, "?>", 2);
2733 0 END_LITOPX_CASE
2734
2735 case JSOP_GETFUNNS:
2736 0 todo = SprintPut(&ss->sprinter, js_function_str, 8);
2737 0 break;
2738 #endif /* JS_HAS_XML_SUPPORT */
2739
2740 default:
2741 0 todo = -2;
2742 break;
2743
2744 #undef BEGIN_LITOPX_CASE
2745 #undef END_LITOPX_CASE
2746 }
2747 }
2748
2749 0 if (todo < 0) {
2750 /* -2 means "don't push", -1 means reported error. */
2751 0 if (todo == -1)
2752 0 return JS_FALSE;
2753 } else {
2754 0 if (!PushOff(ss, todo, op))
2755 0 return JS_FALSE;
2756 }
2757 0 pc += len;
2758 }
2759
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
2770
2771 0 return JS_TRUE;
2772 }
2773
2774
2775 JSBool
2776 js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len)
2777 0 {
2778 SprintStack ss;
2779 JSContext *cx;
2780 void *mark, *space;
2781 size_t offsetsz, opcodesz;
2782 JSBool ok;
2783 JSScript *oldscript;
2784 char *last;
2785
2786 /* Initialize a sprinter for use with the offset stack. */
2787 0 ss.printer = jp;
2788 0 cx = jp->sprinter.context;
2789 0 mark = JS_ARENA_MARK(&cx->tempPool);
2790 0 INIT_SPRINTER(cx, &ss.sprinter, &cx->tempPool, PAREN_SLOP);
2791
2792 /* Allocate the parallel (to avoid padding) offset and opcode stacks. */
2793 0 offsetsz = script->depth * sizeof(ptrdiff_t);
2794 0 opcodesz = script->depth * sizeof(jsbytecode);
2795 0 JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz);
2796 0 if (!space) {
2797 0 ok = JS_FALSE;
2798 0 goto out;
2799 }
2800 0 ss.offsets = (ptrdiff_t *) space;
2801 0 ss.opcodes = (jsbytecode *) ((char *)space + offsetsz);
2802 0 ss.top = 0;
2803
2804 /* Call recursive subroutine to do the hard work. */
2805 0 oldscript = jp->script;
2806 0 jp->script = script;
2807 0 ok = Decompile(&ss, pc, len);
2808 0 jp->script = oldscript;
2809
2810 /* If the given code didn't empty the stack, do it now. */
2811 0 if (ss.top) {
2812 do {
2813 0 last = OFF2STR(&ss.sprinter, PopOff(&ss, JSOP_NOP));
2814 0 } while (ss.top);
2815 0 js_printf(jp, "%s", last);
2816 }
2817
2818 0 out:
2819 /* Free all temporary stuff allocated under this call. */
2820 0 JS_ARENA_RELEASE(&cx->tempPool, mark);
2821 0 return ok;
2822 }
2823
2824 JSBool
2825 js_DecompileScript(JSPrinter *jp, JSScript *script)
2826 0 {
2827 0 return js_DecompileCode(jp, script, script->code, (uintN)script->length);
2828 }
2829
2830 static const char native_code_str[] = "\t[native code]\n";
2831
2832 JSBool
2833 js_DecompileFunctionBody(JSPrinter *jp, JSFunction *fun)
2834 0 {
2835 JSScript *script;
2836 JSScope *scope, *save;
2837 JSBool ok;
2838
2839 0 if (!fun->interpreted) {
2840 0 js_printf(jp, native_code_str);
2841 0 return JS_TRUE;
2842 }
2843 0 script = fun->u.script;
2844 0 scope = fun->object ? OBJ_SCOPE(fun->object) : NULL;
2845 0 save = jp->scope;
2846 0 jp->scope = scope;
2847 0 ok = js_DecompileCode(jp, script, script->code, (uintN)script->length);
2848 0 jp->scope = save;
2849 0 return ok;
2850 }
2851
2852 JSBool
2853 js_DecompileFunction(JSPrinter *jp, JSFunction *fun)
2854 0 {
2855 JSContext *cx;
2856 uintN i, nargs, indent;
2857 void *mark;
2858 JSAtom **params;
2859 JSScope *scope, *oldscope;
2860 JSScopeProperty *sprop;
2861 JSBool ok;
2862
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 0 if (jp->pretty) {
2869 0 js_printf(jp, "\t");
2870 } else {
2871 0 if (!jp->grouped && (fun->flags & JSFUN_LAMBDA))
2872 0 js_puts(jp, "(");
2873 }
2874 0 if (fun->flags & JSFUN_GETTER)
2875 0 js_printf(jp, "%s ", js_getter_str);
2876 0 else if (fun->flags & JSFUN_SETTER)
2877 0 js_printf(jp, "%s ", js_setter_str);
2878
2879 0 js_printf(jp, "%s ", js_function_str);
2880 0 if (fun->atom && !QuoteString(&jp->sprinter, ATOM_TO_STRING(fun->atom), 0))
2881 0 return JS_FALSE;
2882 0 js_puts(jp, "(");
2883
2884 0 if (fun->interpreted && fun->object) {
2885 size_t paramsize;
2886
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 0 cx = jp->sprinter.context;
2898 0 nargs = fun->nargs;
2899 0 mark = JS_ARENA_MARK(&cx->tempPool);
2900 0 paramsize = nargs * sizeof(JSAtom *);
2901 0 JS_ARENA_ALLOCATE_CAST(params, JSAtom **, &cx->tempPool, paramsize);
2902 0 if (!params) {
2903 0 JS_ReportOutOfMemory(cx);
2904 0 return JS_FALSE;
2905 }
2906 0 memset(params, 0, paramsize);
2907 0 scope = OBJ_SCOPE(fun->object);
2908 0 for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
2909 0 if (sprop->getter != js_GetArgument)
2910 0 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 0 params[(uint16) sprop->shortid] = JSID_TO_ATOM(sprop->id);
2915 }
2916 0 for (i = 0; i < nargs; i++) {
2917 0 if (i > 0)
2918 0 js_puts(jp, ", ");
2919 0 if (!QuoteString(&jp->sprinter, ATOM_TO_STRING(params[i]), 0))
2920 0 return JS_FALSE;
2921 }
2922 0 JS_ARENA_RELEASE(&cx->tempPool, mark);
2923 #ifdef __GNUC__
2924 } else {
2925 0 scope = NULL;
2926 #endif
2927 }
2928
2929 0 js_printf(jp, ") {\n");
2930 0 indent = jp->indent;
2931 0 jp->indent += 4;
2932 0 if (fun->interpreted && fun->object) {
2933 0 oldscope = jp->scope;
2934 0 jp->scope = scope;
2935 0 ok = js_DecompileScript(jp, fun->u.script);
2936 0 jp->scope = oldscope;
2937 0 if (!ok) {
2938 0 jp->indent = indent;
2939 0 return JS_FALSE;
2940 }
2941 } else {
2942 0 js_printf(jp, native_code_str);
2943 }
2944 0 jp->indent -= 4;
2945 0 js_printf(jp, "\t}");
2946
2947 0 if (!jp->pretty) {
2948 0 if (!jp->grouped && (fun->flags & JSFUN_LAMBDA))
2949 0 js_puts(jp, ")");
2950 }
2951 0 return JS_TRUE;
2952 }
2953
2954 JSString *
2955 js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
2956 JSString *fallback)
2957 0 {
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;
2970
2971 0 for (fp = cx->fp; fp && !fp->script; fp = fp->down)
2972 continue;
2973 0 if (!fp)
2974 0 goto do_fallback;
2975
2976 /* Try to find sp's generating pc depth slots under it on the stack. */
2977 0 pc = fp->pc;
2978 0 if (spindex == JSDVG_SEARCH_STACK) {
2979 0 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 0 down = fp->down;
2987 0 if (!down)
2988 0 goto do_fallback;
2989 0 script = down->script;
2990 0 base = fp->argv;
2991 0 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 0 script = fp->script;
3000 0 base = fp->spbase;
3001 0 limit = fp->sp;
3002 }
3003
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 0 if (!script || !base || !limit)
3010 goto do_fallback;
3011
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 0 for (sp = base; sp < limit; sp++) {
3024 0 if (*sp == v) {
3025 0 depth = (intN)script->depth;
3026 0 pc = (jsbytecode *) sp[-depth];
3027 0 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 0 if (!pc)
3037 0 goto do_fallback;
3038 0 script = fp->script;
3039 0 if (!script)
3040 0 goto do_fallback;
3041
3042 0 if (spindex != JSDVG_IGNORE_STACK) {
3043 JS_ASSERT(spindex < 0);
3044 0 depth = (intN)script->depth;
3045 #if !JS_HAS_NO_SUCH_METHOD
3046 JS_ASSERT(-depth <= spindex);
3047 #endif
3048 0 spindex -= depth;
3049
3050 0 base = (jsval *) cx->stackPool.current->base;
3051 0 limit = (jsval *) cx->stackPool.current->avail;
3052 0 sp = fp->sp + spindex;
3053 0 if (JS_UPTRDIFF(sp, base) < JS_UPTRDIFF(limit, base))
3054 0 pc = (jsbytecode *) *sp;
3055 }
3056 }
3057
3058 /*
3059 * Again, be paranoid, this time about possibly loading an invalid pc
3060 * from sp[-(1+depth)].
3061 */
3062 0 if (JS_UPTRDIFF(pc, script->code) >= (jsuword)script->length) {
3063 0 pc = fp->pc;
3064 0 if (!pc)
3065 0 goto do_fallback;
3066 }
3067 0 op = (JSOp) *pc;
3068 0 if (op == JSOP_TRAP)
3069 0 op = JS_GetTrapOpcode(cx, script, pc);
3070
3071 /* XXX handle null as a special case, to avoid calling null "object" */
3072 0 if (op == JSOP_NULL)
3073 0 return ATOM_TO_STRING(cx->runtime->atomState.nullAtom);
3074
3075 0 cs = &js_CodeSpec[op];
3076 0 format = cs->format;
3077 0 mode = (format & JOF_MODEMASK);
3078
3079 /* NAME ops are self-contained, but others require left context. */
3080 0 if (mode == JOF_NAME) {
3081 0 begin = pc;
3082 } else {
3083 0 sn = js_GetSrcNote(script, pc);
3084 0 if (!sn || (SN_TYPE(sn) != SRC_PCBASE && SN_TYPE(sn) != SRC_PCDELTA)) {
3085 0 if (cs->token)
3086 0 return JS_NewStringCopyZ(cx, cs->token);
3087 0 goto do_fallback;
3088 }
3089 0 begin = pc - js_GetSrcNoteOffset(sn, 0);
3090 }
3091 0 end = pc + cs->length;
3092 0 len = PTRDIFF(end, begin, jsbytecode);
3093
3094 0 if (format & (JOF_SET | JOF_DEL | JOF_INCDEC | JOF_IMPORT | JOF_FOR)) {
3095 0 tmp = (jsbytecode *) JS_malloc(cx, len * sizeof(jsbytecode));
3096 0 if (!tmp)
3097 0 return NULL;
3098 0 memcpy(tmp, begin, len * sizeof(jsbytecode));
3099 0 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 0 type = format & JOF_TYPEMASK;
3106 0 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 0 off = len - cs->length;
3120 JS_ASSERT(off == (uintN) PTRDIFF(pc, begin, jsbytecode));
3121 0 if (mode == JOF_PROP) {
3122 0 tmp[off] = (format & JOF_SET) ? JSOP_GETPROP2 : JSOP_GETPROP;
3123 0 } else if (mode == JOF_ELEM) {
3124 0 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 0 switch (op) {
3131 case JSOP_ENUMELEM:
3132 0 tmp[off] = JSOP_GETELEM;
3133 0 break;
3134 #if JS_HAS_LVALUE_RETURN
3135 case JSOP_SETCALL:
3136 0 tmp[off] = JSOP_CALL;
3137 break;
3138 #endif
3139 default:
3140 JS_ASSERT(0);
3141 }
3142 }
3143 }
3144 0 begin = tmp;
3145 } else {
3146 /* No need to revise script bytecode. */
3147 0 tmp = NULL;
3148 }
3149
3150 0 name = NULL;
3151 0 jp = js_NewPrinter(cx, "js_DecompileValueGenerator", 0, JS_FALSE);
3152 0 if (jp) {
3153 0 if (fp->fun && fp->fun->object) {
3154 JS_ASSERT(OBJ_IS_NATIVE(fp->fun->object));
3155 0 jp->scope = OBJ_SCOPE(fp->fun->object);
3156 }
3157 0 if (js_DecompileCode(jp, script, begin, len))
3158 0 name = js_GetPrinterOutput(jp);
3159 0 js_DestroyPrinter(jp);
3160 }
3161 0 if (tmp)
3162 0 JS_free(cx, tmp);
3163 0 return name;
3164
3165 0 do_fallback:
3166 0 return fallback ? fallback : js_ValueToString(cx, v);