Bug 559408: Arena pool macros to methods. (r=gal)
[mozilla-central.git] / js / src / jsopcode.cpp
blob2956e95345edda0e5c89eb6136a4fe13e6fc18aa
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set sw=4 ts=8 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
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.
25 * Contributor(s):
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.
39 * ***** END LICENSE BLOCK ***** */
42 * JS bytecode descriptors, disassemblers, and decompilers.
44 #ifdef HAVE_MEMORY_H
45 #include <memory.h>
46 #endif
47 #include <stdarg.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include "jstypes.h"
52 #include "jsstdint.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 "jsversion.h"
62 #include "jsdbgapi.h"
63 #include "jsemit.h"
64 #include "jsfun.h"
65 #include "jsiter.h"
66 #include "jsnum.h"
67 #include "jsobj.h"
68 #include "jsopcode.h"
69 #include "jsregexp.h"
70 #include "jsscan.h"
71 #include "jsscope.h"
72 #include "jsscript.h"
73 #include "jsstr.h"
74 #include "jsstaticcheck.h"
75 #include "jstracer.h"
76 #include "jsvector.h"
78 #include "jsscriptinlines.h"
80 #include "jsautooplen.h"
82 using namespace js;
85 * Index limit must stay within 32 bits.
87 JS_STATIC_ASSERT(sizeof(uint32) * JS_BITS_PER_BYTE >= INDEX_LIMIT_LOG2 + 1);
89 /* Verify JSOP_XXX_LENGTH constant definitions. */
90 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
91 JS_STATIC_ASSERT(op##_LENGTH == length);
92 #include "jsopcode.tbl"
93 #undef OPDEF
95 static const char js_incop_strs[][3] = {"++", "--"};
96 static const char js_for_each_str[] = "for each";
98 const JSCodeSpec js_CodeSpec[] = {
99 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
100 {length,nuses,ndefs,prec,format},
101 #include "jsopcode.tbl"
102 #undef OPDEF
105 uintN js_NumCodeSpecs = JS_ARRAY_LENGTH(js_CodeSpec);
108 * Each element of the array is either a source literal associated with JS
109 * bytecode or null.
111 static const char *CodeToken[] = {
112 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
113 token,
114 #include "jsopcode.tbl"
115 #undef OPDEF
118 #if defined(DEBUG) || defined(JS_JIT_SPEW)
120 * Array of JS bytecode names used by DEBUG-only js_Disassemble and by
121 * JIT debug spew.
123 const char *js_CodeName[] = {
124 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
125 name,
126 #include "jsopcode.tbl"
127 #undef OPDEF
129 #endif
131 /************************************************************************/
133 static ptrdiff_t
134 GetJumpOffset(jsbytecode *pc, jsbytecode *pc2)
136 uint32 type;
138 type = JOF_OPTYPE(*pc);
139 if (JOF_TYPE_IS_EXTENDED_JUMP(type))
140 return GET_JUMPX_OFFSET(pc2);
141 return GET_JUMP_OFFSET(pc2);
144 uintN
145 js_GetIndexFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc,
146 ptrdiff_t pcoff)
148 JSOp op;
149 uintN span, base;
151 op = js_GetOpcode(cx, script, pc);
152 JS_ASSERT(js_CodeSpec[op].length >= 1 + pcoff + UINT16_LEN);
155 * We need to detect index base prefix. It presents when resetbase
156 * follows the bytecode.
158 span = js_CodeSpec[op].length;
159 base = 0;
160 if (pc - script->code + span < script->length) {
161 if (pc[span] == JSOP_RESETBASE) {
162 base = GET_INDEXBASE(pc - JSOP_INDEXBASE_LENGTH);
163 } else if (pc[span] == JSOP_RESETBASE0) {
164 JS_ASSERT(JSOP_INDEXBASE1 <= pc[-1] || pc[-1] <= JSOP_INDEXBASE3);
165 base = (pc[-1] - JSOP_INDEXBASE1 + 1) << 16;
168 return base + GET_UINT16(pc + pcoff);
171 uintN
172 js_GetVariableBytecodeLength(jsbytecode *pc)
174 JSOp op;
175 uintN jmplen, ncases;
176 jsint low, high;
178 op = (JSOp) *pc;
179 JS_ASSERT(js_CodeSpec[op].length == -1);
180 switch (op) {
181 case JSOP_TABLESWITCHX:
182 jmplen = JUMPX_OFFSET_LEN;
183 goto do_table;
184 case JSOP_TABLESWITCH:
185 jmplen = JUMP_OFFSET_LEN;
186 do_table:
187 /* Structure: default-jump case-low case-high case1-jump ... */
188 pc += jmplen;
189 low = GET_JUMP_OFFSET(pc);
190 pc += JUMP_OFFSET_LEN;
191 high = GET_JUMP_OFFSET(pc);
192 ncases = (uintN)(high - low + 1);
193 return 1 + jmplen + INDEX_LEN + INDEX_LEN + ncases * jmplen;
195 case JSOP_LOOKUPSWITCHX:
196 jmplen = JUMPX_OFFSET_LEN;
197 goto do_lookup;
198 default:
199 JS_ASSERT(op == JSOP_LOOKUPSWITCH);
200 jmplen = JUMP_OFFSET_LEN;
201 do_lookup:
202 /* Structure: default-jump case-count (case1-value case1-jump) ... */
203 pc += jmplen;
204 ncases = GET_UINT16(pc);
205 return 1 + jmplen + INDEX_LEN + ncases * (INDEX_LEN + jmplen);
209 uintN
210 js_GetVariableStackUses(JSOp op, jsbytecode *pc)
212 JS_ASSERT(*pc == op || *pc == JSOP_TRAP);
213 JS_ASSERT(js_CodeSpec[op].nuses == -1);
214 switch (op) {
215 case JSOP_POPN:
216 return GET_UINT16(pc);
217 case JSOP_CONCATN:
218 return GET_UINT16(pc);
219 case JSOP_LEAVEBLOCK:
220 return GET_UINT16(pc);
221 case JSOP_LEAVEBLOCKEXPR:
222 return GET_UINT16(pc) + 1;
223 case JSOP_NEWARRAY:
224 return GET_UINT16(pc);
225 default:
226 /* stack: fun, this, [argc arguments] */
227 JS_ASSERT(op == JSOP_NEW || op == JSOP_CALL ||
228 op == JSOP_EVAL || op == JSOP_SETCALL ||
229 op == JSOP_APPLY);
230 return 2 + GET_ARGC(pc);
234 uintN
235 js_GetEnterBlockStackDefs(JSContext *cx, JSScript *script, jsbytecode *pc)
237 JSObject *obj;
239 JS_ASSERT(*pc == JSOP_ENTERBLOCK || *pc == JSOP_TRAP);
240 GET_OBJECT_FROM_BYTECODE(script, pc, 0, obj);
241 return OBJ_BLOCK_COUNT(cx, obj);
244 #ifdef DEBUG
246 JS_FRIEND_API(JSBool)
247 js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp)
249 jsbytecode *pc, *end;
250 uintN len;
252 pc = script->code;
253 end = pc + script->length;
254 while (pc < end) {
255 if (pc == script->main)
256 fputs("main:\n", fp);
257 len = js_Disassemble1(cx, script, pc,
258 pc - script->code,
259 lines, fp);
260 if (!len)
261 return JS_FALSE;
262 pc += len;
264 return JS_TRUE;
267 JSBool
268 js_DumpScript(JSContext *cx, JSScript *script)
270 return js_Disassemble(cx, script, true, stdout);
273 const char *
274 ToDisassemblySource(JSContext *cx, jsval v)
276 if (!JSVAL_IS_PRIMITIVE(v)) {
277 JSObject *obj = JSVAL_TO_OBJECT(v);
278 JSClass *clasp = obj->getClass();
280 if (clasp == &js_BlockClass) {
281 char *source = JS_sprintf_append(NULL, "depth %d {", OBJ_BLOCK_DEPTH(cx, obj));
282 for (JSScopeProperty *sprop = obj->scope()->lastProperty();
283 sprop;
284 sprop = sprop->parent) {
285 const char *bytes = js_AtomToPrintableString(cx, JSID_TO_ATOM(sprop->id));
286 if (!bytes)
287 return NULL;
288 source = JS_sprintf_append(source, "%s: %d%s",
289 bytes, sprop->shortid,
290 sprop->parent ? ", " : "");
293 source = JS_sprintf_append(source, "}");
294 if (!source)
295 return NULL;
297 JSString *str = JS_NewString(cx, source, strlen(source));
298 if (!str)
299 return NULL;
300 return js_GetStringBytes(cx, str);
303 if (clasp == &js_FunctionClass) {
304 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj);
305 JSString *str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT);
306 if (!str)
307 return NULL;
308 return js_GetStringBytes(cx, str);
311 if (clasp == &js_RegExpClass) {
312 AutoValueRooter tvr(cx);
313 if (!js_regexp_toString(cx, obj, tvr.addr()))
314 return NULL;
315 return js_GetStringBytes(cx, JSVAL_TO_STRING(tvr.value()));
319 return js_ValueToPrintableSource(cx, v);
322 JS_FRIEND_API(uintN)
323 js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc,
324 uintN loc, JSBool lines, FILE *fp)
326 JSOp op;
327 const JSCodeSpec *cs;
328 ptrdiff_t len, off, jmplen;
329 uint32 type;
330 JSAtom *atom;
331 uintN index;
332 JSObject *obj;
333 jsval v;
334 const char *bytes;
335 jsint i;
337 op = (JSOp)*pc;
338 if (op >= JSOP_LIMIT) {
339 char numBuf1[12], numBuf2[12];
340 JS_snprintf(numBuf1, sizeof numBuf1, "%d", op);
341 JS_snprintf(numBuf2, sizeof numBuf2, "%d", JSOP_LIMIT);
342 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
343 JSMSG_BYTECODE_TOO_BIG, numBuf1, numBuf2);
344 return 0;
346 cs = &js_CodeSpec[op];
347 len = (ptrdiff_t) cs->length;
348 fprintf(fp, "%05u:", loc);
349 if (lines)
350 fprintf(fp, "%4u", JS_PCToLineNumber(cx, script, pc));
351 fprintf(fp, " %s", js_CodeName[op]);
352 type = JOF_TYPE(cs->format);
353 switch (type) {
354 case JOF_BYTE:
355 if (op == JSOP_TRAP) {
356 op = JS_GetTrapOpcode(cx, script, pc);
357 len = (ptrdiff_t) js_CodeSpec[op].length;
359 break;
361 case JOF_JUMP:
362 case JOF_JUMPX:
363 off = GetJumpOffset(pc, pc);
364 fprintf(fp, " %u (%d)", loc + (intN) off, (intN) off);
365 break;
367 case JOF_ATOM:
368 case JOF_OBJECT:
369 case JOF_REGEXP:
370 index = js_GetIndexFromBytecode(cx, script, pc, 0);
371 if (type == JOF_ATOM) {
372 JS_GET_SCRIPT_ATOM(script, pc, index, atom);
373 v = ATOM_KEY(atom);
374 } else {
375 if (type == JOF_OBJECT)
376 obj = script->getObject(index);
377 else
378 obj = script->getRegExp(index);
379 v = OBJECT_TO_JSVAL(obj);
381 bytes = ToDisassemblySource(cx, v);
382 if (!bytes)
383 return 0;
384 fprintf(fp, " %s", bytes);
385 break;
387 case JOF_UINT16PAIR:
388 i = (jsint)GET_UINT16(pc);
389 fprintf(fp, " %d", i);
390 /* FALL THROUGH */
392 case JOF_UINT16:
393 i = (jsint)GET_UINT16(pc);
394 goto print_int;
396 case JOF_TABLESWITCH:
397 case JOF_TABLESWITCHX:
399 jsbytecode *pc2;
400 jsint i, low, high;
402 jmplen = (type == JOF_TABLESWITCH) ? JUMP_OFFSET_LEN
403 : JUMPX_OFFSET_LEN;
404 pc2 = pc;
405 off = GetJumpOffset(pc, pc2);
406 pc2 += jmplen;
407 low = GET_JUMP_OFFSET(pc2);
408 pc2 += JUMP_OFFSET_LEN;
409 high = GET_JUMP_OFFSET(pc2);
410 pc2 += JUMP_OFFSET_LEN;
411 fprintf(fp, " defaultOffset %d low %d high %d", (intN) off, low, high);
412 for (i = low; i <= high; i++) {
413 off = GetJumpOffset(pc, pc2);
414 fprintf(fp, "\n\t%d: %d", i, (intN) off);
415 pc2 += jmplen;
417 len = 1 + pc2 - pc;
418 break;
421 case JOF_LOOKUPSWITCH:
422 case JOF_LOOKUPSWITCHX:
424 jsbytecode *pc2;
425 jsatomid npairs;
427 jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN
428 : JUMPX_OFFSET_LEN;
429 pc2 = pc;
430 off = GetJumpOffset(pc, pc2);
431 pc2 += jmplen;
432 npairs = GET_UINT16(pc2);
433 pc2 += UINT16_LEN;
434 fprintf(fp, " offset %d npairs %u", (intN) off, (uintN) npairs);
435 while (npairs) {
436 JS_GET_SCRIPT_ATOM(script, pc, GET_INDEX(pc2), atom);
437 pc2 += INDEX_LEN;
438 off = GetJumpOffset(pc, pc2);
439 pc2 += jmplen;
441 bytes = ToDisassemblySource(cx, ATOM_KEY(atom));
442 if (!bytes)
443 return 0;
444 fprintf(fp, "\n\t%s: %d", bytes, (intN) off);
445 npairs--;
447 len = 1 + pc2 - pc;
448 break;
451 case JOF_QARG:
452 fprintf(fp, " %u", GET_ARGNO(pc));
453 break;
455 case JOF_LOCAL:
456 fprintf(fp, " %u", GET_SLOTNO(pc));
457 break;
459 case JOF_SLOTATOM:
460 case JOF_SLOTOBJECT:
461 fprintf(fp, " %u", GET_SLOTNO(pc));
462 index = js_GetIndexFromBytecode(cx, script, pc, SLOTNO_LEN);
463 if (type == JOF_SLOTATOM) {
464 JS_GET_SCRIPT_ATOM(script, pc, index, atom);
465 v = ATOM_KEY(atom);
466 } else {
467 obj = script->getObject(index);
468 v = OBJECT_TO_JSVAL(obj);
470 bytes = ToDisassemblySource(cx, v);
471 if (!bytes)
472 return 0;
473 fprintf(fp, " %s", bytes);
474 break;
476 case JOF_UINT24:
477 JS_ASSERT(op == JSOP_UINT24 || op == JSOP_NEWARRAY);
478 i = (jsint)GET_UINT24(pc);
479 goto print_int;
481 case JOF_UINT8:
482 i = pc[1];
483 goto print_int;
485 case JOF_INT8:
486 i = GET_INT8(pc);
487 goto print_int;
489 case JOF_INT32:
490 JS_ASSERT(op == JSOP_INT32);
491 i = GET_INT32(pc);
492 print_int:
493 fprintf(fp, " %d", i);
494 break;
496 default: {
497 char numBuf[12];
498 JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) cs->format);
499 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
500 JSMSG_UNKNOWN_FORMAT, numBuf);
501 return 0;
504 fputs("\n", fp);
505 return len;
508 #endif /* DEBUG */
510 /************************************************************************/
513 * Sprintf, but with unlimited and automatically allocated buffering.
515 typedef struct Sprinter {
516 JSContext *context; /* context executing the decompiler */
517 JSArenaPool *pool; /* string allocation pool */
518 char *base; /* base address of buffer in pool */
519 size_t size; /* size of buffer allocated at base */
520 ptrdiff_t offset; /* offset of next free char in buffer */
521 } Sprinter;
523 #define INIT_SPRINTER(cx, sp, ap, off) \
524 ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0, \
525 (sp)->offset = off)
527 #define OFF2STR(sp,off) ((sp)->base + (off))
528 #define STR2OFF(sp,str) ((str) - (sp)->base)
529 #define RETRACT(sp,str) ((sp)->offset = STR2OFF(sp, str))
531 static JSBool
532 SprintEnsureBuffer(Sprinter *sp, size_t len)
534 ptrdiff_t nb;
535 char *base;
537 nb = (sp->offset + len + 1) - sp->size;
538 if (nb < 0)
539 return JS_TRUE;
540 base = sp->base;
541 if (!base) {
542 sp->pool->allocateCast<char *>(base, nb);
543 } else {
544 sp->pool->growCast<char *>(base, sp->size, nb);
546 if (!base) {
547 js_ReportOutOfScriptQuota(sp->context);
548 return JS_FALSE;
550 sp->base = base;
551 sp->size += nb;
552 return JS_TRUE;
555 static ptrdiff_t
556 SprintPut(Sprinter *sp, const char *s, size_t len)
558 ptrdiff_t offset = sp->size; /* save old size */
559 char *bp = sp->base; /* save old base */
561 /* Allocate space for s, including the '\0' at the end. */
562 if (!SprintEnsureBuffer(sp, len))
563 return -1;
565 if (sp->base != bp && /* buffer was realloc'ed */
566 s >= bp && s < bp + offset) { /* s was within the buffer */
567 s = sp->base + (s - bp); /* this is where it lives now */
570 /* Advance offset and copy s into sp's buffer. */
571 offset = sp->offset;
572 sp->offset += len;
573 bp = sp->base + offset;
574 memmove(bp, s, len);
575 bp[len] = 0;
576 return offset;
579 static ptrdiff_t
580 SprintCString(Sprinter *sp, const char *s)
582 return SprintPut(sp, s, strlen(s));
585 static ptrdiff_t
586 SprintString(Sprinter *sp, JSString *str)
588 const jschar *chars;
589 size_t length, size;
590 ptrdiff_t offset;
592 str->getCharsAndLength(chars, length);
593 if (length == 0)
594 return sp->offset;
596 size = js_GetDeflatedStringLength(sp->context, chars, length);
597 if (size == (size_t)-1 || !SprintEnsureBuffer(sp, size))
598 return -1;
600 offset = sp->offset;
601 sp->offset += size;
602 js_DeflateStringToBuffer(sp->context, chars, length, sp->base + offset,
603 &size);
604 sp->base[sp->offset] = 0;
605 return offset;
609 static ptrdiff_t
610 Sprint(Sprinter *sp, const char *format, ...)
612 va_list ap;
613 char *bp;
614 ptrdiff_t offset;
616 va_start(ap, format);
617 bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */
618 va_end(ap);
619 if (!bp) {
620 JS_ReportOutOfMemory(sp->context);
621 return -1;
623 offset = SprintCString(sp, bp);
624 js_free(bp);
625 return offset;
628 const char js_EscapeMap[] = {
629 '\b', 'b',
630 '\f', 'f',
631 '\n', 'n',
632 '\r', 'r',
633 '\t', 't',
634 '\v', 'v',
635 '"', '"',
636 '\'', '\'',
637 '\\', '\\',
638 '\0', '0'
641 #define DONT_ESCAPE 0x10000
643 static char *
644 QuoteString(Sprinter *sp, JSString *str, uint32 quote)
646 JSBool dontEscape, ok;
647 jschar qc, c;
648 ptrdiff_t off, len;
649 const jschar *s, *t, *z;
650 const char *e;
651 char *bp;
653 /* Sample off first for later return value pointer computation. */
654 dontEscape = (quote & DONT_ESCAPE) != 0;
655 qc = (jschar) quote;
656 off = sp->offset;
657 if (qc && Sprint(sp, "%c", (char)qc) < 0)
658 return NULL;
660 /* Loop control variables: z points at end of string sentinel. */
661 str->getCharsAndEnd(s, z);
662 for (t = s; t < z; s = ++t) {
663 /* Move t forward from s past un-quote-worthy characters. */
664 c = *t;
665 while (JS_ISPRINT(c) && c != qc && c != '\\' && c != '\t' &&
666 !(c >> 8)) {
667 c = *++t;
668 if (t == z)
669 break;
671 len = t - s;
673 /* Allocate space for s, including the '\0' at the end. */
674 if (!SprintEnsureBuffer(sp, len))
675 return NULL;
677 /* Advance sp->offset and copy s into sp's buffer. */
678 bp = sp->base + sp->offset;
679 sp->offset += len;
680 while (--len >= 0)
681 *bp++ = (char) *s++;
682 *bp = '\0';
684 if (t == z)
685 break;
687 /* Use js_EscapeMap, \u, or \x only if necessary. */
688 if (!(c >> 8) && (e = strchr(js_EscapeMap, (int)c)) != NULL) {
689 ok = dontEscape
690 ? Sprint(sp, "%c", (char)c) >= 0
691 : Sprint(sp, "\\%c", e[1]) >= 0;
692 } else {
693 ok = Sprint(sp, (c >> 8) ? "\\u%04X" : "\\x%02X", c) >= 0;
695 if (!ok)
696 return NULL;
699 /* Sprint the closing quote and return the quoted string. */
700 if (qc && Sprint(sp, "%c", (char)qc) < 0)
701 return NULL;
704 * If we haven't Sprint'd anything yet, Sprint an empty string so that
705 * the OFF2STR below gives a valid result.
707 if (off == sp->offset && Sprint(sp, "") < 0)
708 return NULL;
709 return OFF2STR(sp, off);
712 JSString *
713 js_QuoteString(JSContext *cx, JSString *str, jschar quote)
715 void *mark;
716 Sprinter sprinter;
717 char *bytes;
718 JSString *escstr;
720 mark = cx->tempPool.getMark();
721 INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);
722 bytes = QuoteString(&sprinter, str, quote);
723 escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL;
724 cx->tempPool.release(mark);
725 return escstr;
728 /************************************************************************/
730 struct JSPrinter {
731 Sprinter sprinter; /* base class state */
732 JSArenaPool pool; /* string allocation pool */
733 uintN indent; /* indentation in spaces */
734 bool pretty; /* pretty-print: indent, use newlines */
735 bool grouped; /* in parenthesized expression context */
736 bool strict; /* in code marked strict */
737 JSScript *script; /* script being printed */
738 jsbytecode *dvgfence; /* DecompileExpression fencepost */
739 jsbytecode **pcstack; /* DecompileExpression modeled stack */
740 JSFunction *fun; /* interpreted function */
741 jsuword *localNames; /* argument and variable names */
744 JSPrinter *
745 js_NewPrinter(JSContext *cx, const char *name, JSFunction *fun,
746 uintN indent, JSBool pretty, JSBool grouped, JSBool strict)
748 JSPrinter *jp;
750 jp = (JSPrinter *) cx->malloc(sizeof(JSPrinter));
751 if (!jp)
752 return NULL;
753 INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
754 jp->pool.init(name, 256, 1, &cx->scriptStackQuota);
755 jp->indent = indent;
756 jp->pretty = !!pretty;
757 jp->grouped = !!grouped;
758 jp->strict = !!strict;
759 jp->script = NULL;
760 jp->dvgfence = NULL;
761 jp->pcstack = NULL;
762 jp->fun = fun;
763 jp->localNames = NULL;
764 if (fun && FUN_INTERPRETED(fun) && fun->hasLocalNames()) {
765 jp->localNames = js_GetLocalNameArray(cx, fun, &jp->pool);
766 if (!jp->localNames) {
767 js_DestroyPrinter(jp);
768 return NULL;
771 return jp;
774 void
775 js_DestroyPrinter(JSPrinter *jp)
777 jp->pool.finish();
778 jp->sprinter.context->free(jp);
781 JSString *
782 js_GetPrinterOutput(JSPrinter *jp)
784 JSContext *cx;
785 JSString *str;
787 cx = jp->sprinter.context;
788 if (!jp->sprinter.base)
789 return cx->runtime->emptyString;
790 str = JS_NewStringCopyZ(cx, jp->sprinter.base);
791 if (!str)
792 return NULL;
793 jp->pool.free();
794 INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
795 return str;
799 * NB: Indexed by SRC_DECL_* defines from jsemit.h.
801 static const char * const var_prefix[] = {"var ", "const ", "let "};
803 static const char *
804 VarPrefix(jssrcnote *sn)
806 if (sn && (SN_TYPE(sn) == SRC_DECL || SN_TYPE(sn) == SRC_GROUPASSIGN)) {
807 ptrdiff_t type = js_GetSrcNoteOffset(sn, 0);
808 if ((uintN)type <= SRC_DECL_LET)
809 return var_prefix[type];
811 return "";
815 js_printf(JSPrinter *jp, const char *format, ...)
817 va_list ap;
818 char *bp, *fp;
819 int cc;
821 if (*format == '\0')
822 return 0;
824 va_start(ap, format);
826 /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */
827 if (*format == '\t') {
828 format++;
829 if (jp->pretty && Sprint(&jp->sprinter, "%*s", jp->indent, "") < 0) {
830 va_end(ap);
831 return -1;
835 /* Suppress newlines (must be once per format, at the end) if not pretty. */
836 fp = NULL;
837 if (!jp->pretty && format[cc = strlen(format) - 1] == '\n') {
838 fp = JS_strdup(jp->sprinter.context, format);
839 if (!fp) {
840 va_end(ap);
841 return -1;
843 fp[cc] = '\0';
844 format = fp;
847 /* Allocate temp space, convert format, and put. */
848 bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */
849 if (fp) {
850 jp->sprinter.context->free(fp);
851 format = NULL;
853 if (!bp) {
854 JS_ReportOutOfMemory(jp->sprinter.context);
855 va_end(ap);
856 return -1;
859 cc = strlen(bp);
860 if (SprintPut(&jp->sprinter, bp, (size_t)cc) < 0)
861 cc = -1;
862 js_free(bp);
864 va_end(ap);
865 return cc;
868 JSBool
869 js_puts(JSPrinter *jp, const char *s)
871 return SprintCString(&jp->sprinter, s) >= 0;
874 /************************************************************************/
876 typedef struct SprintStack {
877 Sprinter sprinter; /* sprinter for postfix to infix buffering */
878 ptrdiff_t *offsets; /* stack of postfix string offsets */
879 jsbytecode *opcodes; /* parallel stack of JS opcodes */
880 uintN top; /* top of stack index */
881 uintN inArrayInit; /* array initialiser/comprehension level */
882 JSBool inGenExp; /* in generator expression */
883 JSPrinter *printer; /* permanent output goes here */
884 } SprintStack;
887 * Find the depth of the operand stack when the interpreter reaches the given
888 * pc in script. pcstack must have space for least script->depth elements. On
889 * return it will contain pointers to opcodes that populated the interpreter's
890 * current operand stack.
892 * This function cannot raise an exception or error. However, due to a risk of
893 * potential bugs when modeling the stack, the function returns -1 if it
894 * detects an inconsistency in the model. Such an inconsistency triggers an
895 * assert in a debug build.
897 static intN
898 ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *pc,
899 jsbytecode **pcstack);
901 #define FAILED_EXPRESSION_DECOMPILER ((char *) 1)
904 * Decompile a part of expression up to the given pc. The function returns
905 * NULL on out-of-memory, or the FAILED_EXPRESSION_DECOMPILER sentinel when
906 * the decompiler fails due to a bug and/or unimplemented feature, or the
907 * decompiled string on success.
909 static char *
910 DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun,
911 jsbytecode *pc);
914 * Get a stacked offset from ss->sprinter.base, or if the stacked value |off|
915 * is negative, fetch the generating pc from printer->pcstack[-2 - off] and
916 * decompile the code that generated the missing value. This is used when
917 * reporting errors, where the model stack will lack |pcdepth| non-negative
918 * offsets (see DecompileExpression and DecompileCode).
920 * If the stacked offset is -1, return 0 to index the NUL padding at the start
921 * of ss->sprinter.base. If this happens, it means there is a decompiler bug
922 * to fix, but it won't violate memory safety.
924 static ptrdiff_t
925 GetOff(SprintStack *ss, uintN i)
927 ptrdiff_t off;
928 jsbytecode *pc;
929 char *bytes;
931 off = ss->offsets[i];
932 if (off >= 0)
933 return off;
935 JS_ASSERT(off <= -2);
936 JS_ASSERT(ss->printer->pcstack);
937 if (off <= -2 && ss->printer->pcstack) {
938 pc = ss->printer->pcstack[-2 - off];
939 bytes = DecompileExpression(ss->sprinter.context, ss->printer->script,
940 ss->printer->fun, pc);
941 if (!bytes)
942 return 0;
943 if (bytes != FAILED_EXPRESSION_DECOMPILER) {
944 off = SprintCString(&ss->sprinter, bytes);
945 if (off < 0)
946 off = 0;
947 ss->offsets[i] = off;
948 ss->sprinter.context->free(bytes);
949 return off;
951 if (!ss->sprinter.base && SprintPut(&ss->sprinter, "", 0) >= 0) {
952 memset(ss->sprinter.base, 0, ss->sprinter.offset);
953 ss->offsets[i] = -1;
956 return 0;
959 static const char *
960 GetStr(SprintStack *ss, uintN i)
962 ptrdiff_t off;
965 * Must call GetOff before using ss->sprinter.base, since it may be null
966 * until bootstrapped by GetOff.
968 off = GetOff(ss, i);
969 return OFF2STR(&ss->sprinter, off);
973 * Gap between stacked strings to allow for insertion of parens and commas
974 * when auto-parenthesizing expressions and decompiling array initialisers
975 * (see the JSOP_NEWARRAY case in Decompile).
977 #define PAREN_SLOP (2 + 1)
980 * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME,
981 * JSOP_SETPROP, and JSOP_SETELEM, respectively. They are never stored in
982 * bytecode, so they don't preempt valid opcodes.
984 #define JSOP_GETPROP2 JSOP_LIMIT
985 #define JSOP_GETELEM2 JSOP_LIMIT + 1
986 JS_STATIC_ASSERT(JSOP_GETELEM2 <= 255);
988 static void
989 AddParenSlop(SprintStack *ss)
991 memset(OFF2STR(&ss->sprinter, ss->sprinter.offset), 0, PAREN_SLOP);
992 ss->sprinter.offset += PAREN_SLOP;
995 static JSBool
996 PushOff(SprintStack *ss, ptrdiff_t off, JSOp op)
998 uintN top;
1000 if (!SprintEnsureBuffer(&ss->sprinter, PAREN_SLOP))
1001 return JS_FALSE;
1003 /* ss->top points to the next free slot; be paranoid about overflow. */
1004 top = ss->top;
1005 JS_ASSERT(top < StackDepth(ss->printer->script));
1006 if (top >= StackDepth(ss->printer->script)) {
1007 JS_ReportOutOfMemory(ss->sprinter.context);
1008 return JS_FALSE;
1011 /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */
1012 ss->offsets[top] = off;
1013 ss->opcodes[top] = jsbytecode((op == JSOP_GETPROP2) ? JSOP_GETPROP
1014 : (op == JSOP_GETELEM2) ? JSOP_GETELEM
1015 : op);
1016 ss->top = ++top;
1017 AddParenSlop(ss);
1018 return JS_TRUE;
1021 static ptrdiff_t
1022 PopOffPrec(SprintStack *ss, uint8 prec)
1024 uintN top;
1025 const JSCodeSpec *topcs;
1026 ptrdiff_t off;
1028 /* ss->top points to the next free slot; be paranoid about underflow. */
1029 top = ss->top;
1030 JS_ASSERT(top != 0);
1031 if (top == 0)
1032 return 0;
1034 ss->top = --top;
1035 off = GetOff(ss, top);
1036 topcs = &js_CodeSpec[ss->opcodes[top]];
1037 if (topcs->prec != 0 && topcs->prec < prec) {
1038 ss->sprinter.offset = ss->offsets[top] = off - 2;
1039 off = Sprint(&ss->sprinter, "(%s)", OFF2STR(&ss->sprinter, off));
1040 } else {
1041 ss->sprinter.offset = off;
1043 return off;
1046 static const char *
1047 PopStrPrec(SprintStack *ss, uint8 prec)
1049 ptrdiff_t off;
1051 off = PopOffPrec(ss, prec);
1052 return OFF2STR(&ss->sprinter, off);
1055 static ptrdiff_t
1056 PopOff(SprintStack *ss, JSOp op)
1058 return PopOffPrec(ss, js_CodeSpec[op].prec);
1061 static const char *
1062 PopStr(SprintStack *ss, JSOp op)
1064 return PopStrPrec(ss, js_CodeSpec[op].prec);
1067 typedef struct TableEntry {
1068 jsval key;
1069 ptrdiff_t offset;
1070 JSAtom *label;
1071 jsint order; /* source order for stable tableswitch sort */
1072 } TableEntry;
1074 static JSBool
1075 CompareOffsets(void *arg, const void *v1, const void *v2, int *result)
1077 ptrdiff_t offset_diff;
1078 const TableEntry *te1 = (const TableEntry *) v1,
1079 *te2 = (const TableEntry *) v2;
1081 offset_diff = te1->offset - te2->offset;
1082 *result = (offset_diff == 0 ? te1->order - te2->order
1083 : offset_diff < 0 ? -1
1084 : 1);
1085 return JS_TRUE;
1088 static ptrdiff_t
1089 SprintDoubleValue(Sprinter *sp, jsval v, JSOp *opp)
1091 jsdouble d;
1092 ptrdiff_t todo;
1093 char *s, buf[DTOSTR_STANDARD_BUFFER_SIZE];
1095 JS_ASSERT(JSVAL_IS_DOUBLE(v));
1096 d = *JSVAL_TO_DOUBLE(v);
1097 if (JSDOUBLE_IS_NEGZERO(d)) {
1098 todo = SprintCString(sp, "-0");
1099 *opp = JSOP_NEG;
1100 } else if (!JSDOUBLE_IS_FINITE(d)) {
1101 /* Don't use Infinity and NaN, they're mutable. */
1102 todo = SprintCString(sp,
1103 JSDOUBLE_IS_NaN(d)
1104 ? "0 / 0"
1105 : (d < 0)
1106 ? "1 / -0"
1107 : "1 / 0");
1108 *opp = JSOP_DIV;
1109 } else {
1110 s = js_dtostr(JS_THREAD_DATA(sp->context)->dtoaState, buf, sizeof buf,
1111 DTOSTR_STANDARD, 0, d);
1112 if (!s) {
1113 JS_ReportOutOfMemory(sp->context);
1114 return -1;
1116 JS_ASSERT(strcmp(s, js_Infinity_str) &&
1117 (*s != '-' ||
1118 strcmp(s + 1, js_Infinity_str)) &&
1119 strcmp(s, js_NaN_str));
1120 todo = Sprint(sp, s);
1122 return todo;
1125 static jsbytecode *
1126 Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop);
1128 static JSBool
1129 DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength,
1130 jsbytecode *pc, ptrdiff_t switchLength,
1131 ptrdiff_t defaultOffset, JSBool isCondSwitch)
1133 JSContext *cx;
1134 JSPrinter *jp;
1135 ptrdiff_t off, off2, diff, caseExprOff, todo;
1136 char *lval, *rval;
1137 uintN i;
1138 jsval key;
1139 JSString *str;
1141 cx = ss->sprinter.context;
1142 jp = ss->printer;
1144 /* JSOP_CONDSWITCH doesn't pop, unlike JSOP_{LOOKUP,TABLE}SWITCH. */
1145 off = isCondSwitch ? GetOff(ss, ss->top-1) : PopOff(ss, JSOP_NOP);
1146 lval = OFF2STR(&ss->sprinter, off);
1148 js_printf(jp, "\tswitch (%s) {\n", lval);
1150 if (tableLength) {
1151 diff = table[0].offset - defaultOffset;
1152 if (diff > 0) {
1153 jp->indent += 2;
1154 js_printf(jp, "\t%s:\n", js_default_str);
1155 jp->indent += 2;
1156 if (!Decompile(ss, pc + defaultOffset, diff, JSOP_NOP))
1157 return JS_FALSE;
1158 jp->indent -= 4;
1161 caseExprOff = isCondSwitch ? JSOP_CONDSWITCH_LENGTH : 0;
1163 for (i = 0; i < tableLength; i++) {
1164 off = table[i].offset;
1165 off2 = (i + 1 < tableLength) ? table[i + 1].offset : switchLength;
1167 key = table[i].key;
1168 if (isCondSwitch) {
1169 ptrdiff_t nextCaseExprOff;
1172 * key encodes the JSOP_CASE bytecode's offset from switchtop.
1173 * The next case expression follows immediately, unless we are
1174 * at the last case.
1176 nextCaseExprOff = (ptrdiff_t)JSVAL_TO_INT(key);
1177 nextCaseExprOff += js_CodeSpec[pc[nextCaseExprOff]].length;
1178 jp->indent += 2;
1179 if (!Decompile(ss, pc + caseExprOff,
1180 nextCaseExprOff - caseExprOff, JSOP_NOP)) {
1181 return JS_FALSE;
1183 caseExprOff = nextCaseExprOff;
1185 /* Balance the stack as if this JSOP_CASE matched. */
1186 --ss->top;
1187 } else {
1189 * key comes from an atom, not the decompiler, so we need to
1190 * quote it if it's a string literal. But if table[i].label
1191 * is non-null, key was constant-propagated and label is the
1192 * name of the const we should show as the case label. We set
1193 * key to undefined so this identifier is escaped, if required
1194 * by non-ASCII characters, but not quoted, by QuoteString.
1196 todo = -1;
1197 if (table[i].label) {
1198 str = ATOM_TO_STRING(table[i].label);
1199 key = JSVAL_VOID;
1200 } else if (JSVAL_IS_DOUBLE(key)) {
1201 JSOp junk;
1203 todo = SprintDoubleValue(&ss->sprinter, key, &junk);
1204 str = NULL;
1205 } else {
1206 str = js_ValueToString(cx, key);
1207 if (!str)
1208 return JS_FALSE;
1210 if (todo >= 0) {
1211 rval = OFF2STR(&ss->sprinter, todo);
1212 } else {
1213 rval = QuoteString(&ss->sprinter, str, (jschar)
1214 (JSVAL_IS_STRING(key) ? '"' : 0));
1215 if (!rval)
1216 return JS_FALSE;
1218 RETRACT(&ss->sprinter, rval);
1219 jp->indent += 2;
1220 js_printf(jp, "\tcase %s:\n", rval);
1223 jp->indent += 2;
1224 if (off <= defaultOffset && defaultOffset < off2) {
1225 diff = defaultOffset - off;
1226 if (diff != 0) {
1227 if (!Decompile(ss, pc + off, diff, JSOP_NOP))
1228 return JS_FALSE;
1229 off = defaultOffset;
1231 jp->indent -= 2;
1232 js_printf(jp, "\t%s:\n", js_default_str);
1233 jp->indent += 2;
1235 if (!Decompile(ss, pc + off, off2 - off, JSOP_NOP))
1236 return JS_FALSE;
1237 jp->indent -= 4;
1239 /* Re-balance as if last JSOP_CASE or JSOP_DEFAULT mismatched. */
1240 if (isCondSwitch)
1241 ++ss->top;
1245 if (defaultOffset == switchLength) {
1246 jp->indent += 2;
1247 js_printf(jp, "\t%s:;\n", js_default_str);
1248 jp->indent -= 2;
1250 js_printf(jp, "\t}\n");
1252 /* By the end of a JSOP_CONDSWITCH, the discriminant has been popped. */
1253 if (isCondSwitch)
1254 --ss->top;
1255 return JS_TRUE;
1258 #define LOCAL_ASSERT_CUSTOM(expr, BAD_EXIT) \
1259 JS_BEGIN_MACRO \
1260 JS_ASSERT(expr); \
1261 if (!(expr)) { BAD_EXIT; } \
1262 JS_END_MACRO
1264 #define LOCAL_ASSERT_RV(expr, rv) \
1265 LOCAL_ASSERT_CUSTOM(expr, return (rv))
1267 static JSAtom *
1268 GetArgOrVarAtom(JSPrinter *jp, uintN slot)
1270 JSAtom *name;
1272 LOCAL_ASSERT_RV(jp->fun, NULL);
1273 LOCAL_ASSERT_RV(slot < jp->fun->countLocalNames(), NULL);
1274 name = JS_LOCAL_NAME_TO_ATOM(jp->localNames[slot]);
1275 #if !JS_HAS_DESTRUCTURING
1276 LOCAL_ASSERT_RV(name, NULL);
1277 #endif
1278 return name;
1281 const char *
1282 GetLocal(SprintStack *ss, jsint i)
1284 ptrdiff_t off;
1285 JSContext *cx;
1286 JSScript *script;
1287 jsatomid j, n;
1288 JSAtom *atom;
1289 JSObject *obj;
1290 jsint depth, count;
1291 JSScopeProperty *sprop;
1292 const char *rval;
1294 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, "")
1296 off = ss->offsets[i];
1297 if (off >= 0)
1298 return OFF2STR(&ss->sprinter, off);
1301 * We must be called from js_DecompileValueGenerator (via Decompile) when
1302 * dereferencing a local that's undefined or null. Search script->objects
1303 * for the block containing this local by its stack index, i.
1305 * In case of destructuring's use of JSOP_GETLOCAL, however, there may be
1306 * no such local. This could mean no blocks (no script objects at all, or
1307 * none of the script's object literals are blocks), or the stack slot i is
1308 * not in a block. In either case, return GetStr(ss, i).
1310 cx = ss->sprinter.context;
1311 script = ss->printer->script;
1312 if (script->objectsOffset == 0)
1313 return GetStr(ss, i);
1314 for (j = 0, n = script->objects()->length; ; j++) {
1315 if (j == n)
1316 return GetStr(ss, i);
1317 obj = script->getObject(j);
1318 if (obj->getClass() == &js_BlockClass) {
1319 depth = OBJ_BLOCK_DEPTH(cx, obj);
1320 count = OBJ_BLOCK_COUNT(cx, obj);
1321 if ((jsuint)(i - depth) < (jsuint)count)
1322 break;
1326 i -= depth;
1327 for (sprop = obj->scope()->lastProperty(); sprop; sprop = sprop->parent) {
1328 if (sprop->shortid == i)
1329 break;
1332 LOCAL_ASSERT(sprop && JSID_IS_ATOM(sprop->id));
1333 atom = JSID_TO_ATOM(sprop->id);
1334 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1335 if (!rval)
1336 return NULL;
1337 RETRACT(&ss->sprinter, rval);
1338 return rval;
1340 #undef LOCAL_ASSERT
1343 static JSBool
1344 IsVarSlot(JSPrinter *jp, jsbytecode *pc, jsint *indexp)
1346 uintN slot;
1348 slot = GET_SLOTNO(pc);
1349 if (slot < jp->script->nfixed) {
1350 /* The slot refers to a variable with name stored in jp->localNames. */
1351 *indexp = jp->fun->nargs + slot;
1352 return JS_TRUE;
1355 /* We have a local which index is relative to the stack base. */
1356 slot -= jp->script->nfixed;
1357 JS_ASSERT(slot < StackDepth(jp->script));
1358 *indexp = slot;
1359 return JS_FALSE;
1362 #define LOAD_ATOM(PCOFF) \
1363 GET_ATOM_FROM_BYTECODE(jp->script, pc, PCOFF, atom)
1365 #if JS_HAS_DESTRUCTURING
1367 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
1368 #define LOAD_OP_DATA(pc) (oplen = (cs = &js_CodeSpec[op=(JSOp)*pc])->length)
1370 static jsbytecode *
1371 DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc);
1373 static jsbytecode *
1374 DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
1375 JSBool *hole)
1377 JSContext *cx;
1378 JSPrinter *jp;
1379 JSOp op;
1380 const JSCodeSpec *cs;
1381 uintN oplen;
1382 jsint i;
1383 const char *lval, *xval;
1384 ptrdiff_t todo;
1385 JSAtom *atom;
1387 *hole = JS_FALSE;
1388 cx = ss->sprinter.context;
1389 jp = ss->printer;
1390 LOAD_OP_DATA(pc);
1392 switch (op) {
1393 case JSOP_POP:
1394 *hole = JS_TRUE;
1395 todo = SprintPut(&ss->sprinter, ", ", 2);
1396 break;
1398 case JSOP_DUP:
1399 pc = DecompileDestructuring(ss, pc, endpc);
1400 if (!pc)
1401 return NULL;
1402 if (pc == endpc)
1403 return pc;
1404 LOAD_OP_DATA(pc);
1405 lval = PopStr(ss, JSOP_NOP);
1406 todo = SprintCString(&ss->sprinter, lval);
1407 if (op == JSOP_POPN)
1408 return pc;
1409 LOCAL_ASSERT(*pc == JSOP_POP);
1410 break;
1412 case JSOP_SETARG:
1413 case JSOP_SETGVAR:
1414 case JSOP_SETLOCAL:
1415 LOCAL_ASSERT(pc[oplen] == JSOP_POP || pc[oplen] == JSOP_POPN);
1416 /* FALL THROUGH */
1418 case JSOP_SETLOCALPOP:
1419 atom = NULL;
1420 lval = NULL;
1421 if (op == JSOP_SETARG) {
1422 atom = GetArgOrVarAtom(jp, GET_SLOTNO(pc));
1423 LOCAL_ASSERT(atom);
1424 } else if (op == JSOP_SETGVAR) {
1425 LOAD_ATOM(0);
1426 } else if (IsVarSlot(jp, pc, &i)) {
1427 atom = GetArgOrVarAtom(jp, i);
1428 LOCAL_ASSERT(atom);
1429 } else {
1430 lval = GetLocal(ss, i);
1432 if (atom)
1433 lval = js_AtomToPrintableString(cx, atom);
1434 LOCAL_ASSERT(lval);
1435 todo = SprintCString(&ss->sprinter, lval);
1436 if (op != JSOP_SETLOCALPOP) {
1437 pc += oplen;
1438 if (pc == endpc)
1439 return pc;
1440 LOAD_OP_DATA(pc);
1441 if (op == JSOP_POPN)
1442 return pc;
1443 LOCAL_ASSERT(op == JSOP_POP);
1445 break;
1447 default:
1449 * We may need to auto-parenthesize the left-most value decompiled
1450 * here, so add back PAREN_SLOP temporarily. Then decompile until the
1451 * opcode that would reduce the stack depth to (ss->top-1), which we
1452 * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for
1453 * the nb parameter.
1455 todo = ss->sprinter.offset;
1456 ss->sprinter.offset = todo + PAREN_SLOP;
1457 pc = Decompile(ss, pc, -((intN)ss->top), JSOP_NOP);
1458 if (!pc)
1459 return NULL;
1460 if (pc == endpc)
1461 return pc;
1462 LOAD_OP_DATA(pc);
1463 LOCAL_ASSERT(op == JSOP_ENUMELEM || op == JSOP_ENUMCONSTELEM);
1464 xval = PopStr(ss, JSOP_NOP);
1465 lval = PopStr(ss, JSOP_GETPROP);
1466 ss->sprinter.offset = todo;
1467 if (*lval == '\0') {
1468 /* lval is from JSOP_BINDNAME, so just print xval. */
1469 todo = SprintCString(&ss->sprinter, xval);
1470 } else if (*xval == '\0') {
1471 /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */
1472 todo = SprintCString(&ss->sprinter, lval);
1473 } else {
1474 todo = Sprint(&ss->sprinter,
1475 (JOF_OPMODE(ss->opcodes[ss->top+1]) == JOF_XMLNAME)
1476 ? "%s.%s"
1477 : "%s[%s]",
1478 lval, xval);
1480 break;
1483 if (todo < 0)
1484 return NULL;
1486 LOCAL_ASSERT(pc < endpc);
1487 pc += oplen;
1488 return pc;
1492 * Starting with a SRC_DESTRUCT-annotated JSOP_DUP, decompile a destructuring
1493 * left-hand side object or array initialiser, including nested destructuring
1494 * initialisers. On successful return, the decompilation will be pushed on ss
1495 * and the return value will point to the POP or GROUP bytecode following the
1496 * destructuring expression.
1498 * At any point, if pc is equal to endpc and would otherwise advance, we stop
1499 * immediately and return endpc.
1501 static jsbytecode *
1502 DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc)
1504 ptrdiff_t head;
1505 JSContext *cx;
1506 JSPrinter *jp;
1507 JSOp op, saveop;
1508 const JSCodeSpec *cs;
1509 uintN oplen;
1510 jsint i, lasti;
1511 jsdouble d;
1512 const char *lval;
1513 JSAtom *atom;
1514 jssrcnote *sn;
1515 JSString *str;
1516 JSBool hole;
1518 LOCAL_ASSERT(*pc == JSOP_DUP);
1519 pc += JSOP_DUP_LENGTH;
1522 * Set head so we can rewrite '[' to '{' as needed. Back up PAREN_SLOP
1523 * chars so the destructuring decompilation accumulates contiguously in
1524 * ss->sprinter starting with "[".
1526 head = SprintPut(&ss->sprinter, "[", 1);
1527 if (head < 0 || !PushOff(ss, head, JSOP_NOP))
1528 return NULL;
1529 ss->sprinter.offset -= PAREN_SLOP;
1530 LOCAL_ASSERT(head == ss->sprinter.offset - 1);
1531 LOCAL_ASSERT(*OFF2STR(&ss->sprinter, head) == '[');
1533 cx = ss->sprinter.context;
1534 jp = ss->printer;
1535 lasti = -1;
1537 while (pc < endpc) {
1538 #if JS_HAS_DESTRUCTURING_SHORTHAND
1539 ptrdiff_t nameoff = -1;
1540 #endif
1542 LOAD_OP_DATA(pc);
1543 saveop = op;
1545 switch (op) {
1546 case JSOP_POP:
1547 pc += oplen;
1548 goto out;
1550 /* Handle the optimized number-pushing opcodes. */
1551 case JSOP_ZERO: d = i = 0; goto do_getelem;
1552 case JSOP_ONE: d = i = 1; goto do_getelem;
1553 case JSOP_UINT16: d = i = GET_UINT16(pc); goto do_getelem;
1554 case JSOP_UINT24: d = i = GET_UINT24(pc); goto do_getelem;
1555 case JSOP_INT8: d = i = GET_INT8(pc); goto do_getelem;
1556 case JSOP_INT32: d = i = GET_INT32(pc); goto do_getelem;
1558 case JSOP_DOUBLE:
1559 GET_DOUBLE_FROM_BYTECODE(jp->script, pc, 0, atom);
1560 d = *ATOM_TO_DOUBLE(atom);
1561 LOCAL_ASSERT(JSDOUBLE_IS_FINITE(d) && !JSDOUBLE_IS_NEGZERO(d));
1562 i = (jsint)d;
1564 do_getelem:
1565 sn = js_GetSrcNote(jp->script, pc);
1566 pc += oplen;
1567 if (pc == endpc)
1568 return pc;
1569 LOAD_OP_DATA(pc);
1570 LOCAL_ASSERT(op == JSOP_GETELEM);
1572 /* Distinguish object from array by opcode or source note. */
1573 if (sn && SN_TYPE(sn) == SRC_INITPROP) {
1574 *OFF2STR(&ss->sprinter, head) = '{';
1575 if (Sprint(&ss->sprinter, "%g: ", d) < 0)
1576 return NULL;
1577 } else {
1578 /* Sanity check for the gnarly control flow above. */
1579 LOCAL_ASSERT(i == d);
1581 /* Fill in any holes (holes at the end don't matter). */
1582 while (++lasti < i) {
1583 if (SprintPut(&ss->sprinter, ", ", 2) < 0)
1584 return NULL;
1587 break;
1589 case JSOP_LENGTH:
1590 atom = cx->runtime->atomState.lengthAtom;
1591 goto do_destructure_atom;
1593 case JSOP_CALLPROP:
1594 case JSOP_GETPROP:
1595 LOAD_ATOM(0);
1596 do_destructure_atom:
1597 *OFF2STR(&ss->sprinter, head) = '{';
1598 str = ATOM_TO_STRING(atom);
1599 #if JS_HAS_DESTRUCTURING_SHORTHAND
1600 nameoff = ss->sprinter.offset;
1601 #endif
1602 if (!QuoteString(&ss->sprinter, str,
1603 js_IsIdentifier(str) ? 0 : (jschar)'\'')) {
1604 return NULL;
1606 if (SprintPut(&ss->sprinter, ": ", 2) < 0)
1607 return NULL;
1608 break;
1610 default:
1611 LOCAL_ASSERT(0);
1614 pc += oplen;
1615 if (pc == endpc)
1616 return pc;
1619 * Decompile the left-hand side expression whose bytecode starts at pc
1620 * and continues for a bounded number of bytecodes or stack operations
1621 * (and which in any event stops before endpc).
1623 pc = DecompileDestructuringLHS(ss, pc, endpc, &hole);
1624 if (!pc)
1625 return NULL;
1627 #if JS_HAS_DESTRUCTURING_SHORTHAND
1628 if (nameoff >= 0) {
1629 ptrdiff_t offset, initlen;
1631 offset = ss->sprinter.offset;
1632 LOCAL_ASSERT(*OFF2STR(&ss->sprinter, offset) == '\0');
1633 initlen = offset - nameoff;
1634 LOCAL_ASSERT(initlen >= 4);
1636 /* Early check to rule out odd "name: lval" length. */
1637 if (((size_t)initlen & 1) == 0) {
1638 size_t namelen;
1639 const char *name;
1642 * Even "name: lval" string length: check for "x: x" and the
1643 * like, and apply the shorthand if we can.
1645 namelen = (size_t)(initlen - 2) >> 1;
1646 name = OFF2STR(&ss->sprinter, nameoff);
1647 if (!strncmp(name + namelen, ": ", 2) &&
1648 !strncmp(name, name + namelen + 2, namelen)) {
1649 offset -= namelen + 2;
1650 *OFF2STR(&ss->sprinter, offset) = '\0';
1651 ss->sprinter.offset = offset;
1655 #endif
1657 if (pc == endpc || *pc != JSOP_DUP)
1658 break;
1661 * We should stop if JSOP_DUP is either without notes or its note is
1662 * not SRC_CONTINUE. The former happens when JSOP_DUP duplicates the
1663 * last destructuring reference implementing an op= assignment like in
1664 * '([t] = z).y += x'. In the latter case the note is SRC_DESTRUCT and
1665 * means another destructuring initialiser abuts this one like in
1666 * '[a] = [b] = c'.
1668 sn = js_GetSrcNote(jp->script, pc);
1669 if (!sn)
1670 break;
1671 if (SN_TYPE(sn) != SRC_CONTINUE) {
1672 LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT);
1673 break;
1676 if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
1677 return NULL;
1679 pc += JSOP_DUP_LENGTH;
1682 out:
1683 lval = OFF2STR(&ss->sprinter, head);
1684 if (SprintPut(&ss->sprinter, (*lval == '[') ? "]" : "}", 1) < 0)
1685 return NULL;
1686 return pc;
1689 static jsbytecode *
1690 DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
1691 jssrcnote *sn, ptrdiff_t *todop)
1693 JSOp op;
1694 const JSCodeSpec *cs;
1695 uintN oplen, start, end, i;
1696 ptrdiff_t todo;
1697 JSBool hole;
1698 const char *rval;
1700 LOAD_OP_DATA(pc);
1701 LOCAL_ASSERT(op == JSOP_PUSH || op == JSOP_GETLOCAL);
1703 todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn));
1704 if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
1705 return NULL;
1706 ss->sprinter.offset -= PAREN_SLOP;
1708 for (;;) {
1709 pc += oplen;
1710 if (pc == endpc)
1711 return pc;
1712 pc = DecompileDestructuringLHS(ss, pc, endpc, &hole);
1713 if (!pc)
1714 return NULL;
1715 if (pc == endpc)
1716 return pc;
1717 LOAD_OP_DATA(pc);
1718 if (op != JSOP_PUSH && op != JSOP_GETLOCAL)
1719 break;
1720 if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
1721 return NULL;
1724 LOCAL_ASSERT(op == JSOP_POPN);
1725 if (SprintPut(&ss->sprinter, "] = [", 5) < 0)
1726 return NULL;
1728 end = ss->top - 1;
1729 start = end - GET_UINT16(pc);
1730 for (i = start; i < end; i++) {
1731 rval = GetStr(ss, i);
1732 if (Sprint(&ss->sprinter,
1733 (i == start) ? "%s" : ", %s",
1734 (i == end - 1 && *rval == '\0') ? ", " : rval) < 0) {
1735 return NULL;
1739 if (SprintPut(&ss->sprinter, "]", 1) < 0)
1740 return NULL;
1741 ss->sprinter.offset = ss->offsets[i];
1742 ss->top = start;
1743 *todop = todo;
1744 return pc;
1747 #undef LOCAL_ASSERT
1748 #undef LOAD_OP_DATA
1750 #endif /* JS_HAS_DESTRUCTURING */
1752 static JSBool
1753 InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth)
1755 size_t offsetsz, opcodesz;
1756 void *space;
1758 INIT_SPRINTER(cx, &ss->sprinter, &cx->tempPool, PAREN_SLOP);
1760 /* Allocate the parallel (to avoid padding) offset and opcode stacks. */
1761 offsetsz = depth * sizeof(ptrdiff_t);
1762 opcodesz = depth * sizeof(jsbytecode);
1763 cx->tempPool.allocate(space, offsetsz + opcodesz);
1764 if (!space) {
1765 js_ReportOutOfScriptQuota(cx);
1766 return JS_FALSE;
1768 ss->offsets = (ptrdiff_t *) space;
1769 ss->opcodes = (jsbytecode *) ((char *)space + offsetsz);
1771 ss->top = ss->inArrayInit = 0;
1772 ss->inGenExp = JS_FALSE;
1773 ss->printer = jp;
1774 return JS_TRUE;
1778 * If nb is non-negative, decompile nb bytecodes starting at pc. Otherwise
1779 * the decompiler starts at pc and continues until it reaches an opcode for
1780 * which decompiling would result in the stack depth equaling -(nb + 1).
1782 * The nextop parameter is either JSOP_NOP or the "next" opcode in order of
1783 * abstract interpretation (not necessarily physically next in a bytecode
1784 * vector). So nextop is JSOP_POP for the last operand in a comma expression,
1785 * or JSOP_AND for the right operand of &&.
1787 static jsbytecode *
1788 Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
1790 JSContext *cx;
1791 JSPrinter *jp, *jp2;
1792 jsbytecode *startpc, *endpc, *pc2, *done;
1793 ptrdiff_t tail, todo, len, oplen, cond, next;
1794 JSOp op, lastop, saveop;
1795 const JSCodeSpec *cs;
1796 jssrcnote *sn, *sn2;
1797 const char *lval, *rval, *xval, *fmt, *token;
1798 uintN nuses;
1799 jsint i, argc;
1800 char **argv;
1801 JSAtom *atom;
1802 JSObject *obj;
1803 JSFunction *fun;
1804 JSString *str;
1805 JSBool ok;
1806 #if JS_HAS_XML_SUPPORT
1807 JSBool foreach, inXML, quoteAttr;
1808 #else
1809 #define inXML JS_FALSE
1810 #endif
1811 jsval val;
1813 static const char exception_cookie[] = "/*EXCEPTION*/";
1814 static const char retsub_pc_cookie[] = "/*RETSUB_PC*/";
1815 static const char forelem_cookie[] = "/*FORELEM*/";
1816 static const char with_cookie[] = "/*WITH*/";
1817 static const char dot_format[] = "%s.%s";
1818 static const char index_format[] = "%s[%s]";
1819 static const char predot_format[] = "%s%s.%s";
1820 static const char postdot_format[] = "%s.%s%s";
1821 static const char preindex_format[] = "%s%s[%s]";
1822 static const char postindex_format[] = "%s[%s]%s";
1823 static const char ss_format[] = "%s%s";
1824 static const char sss_format[] = "%s%s%s";
1826 /* Argument and variables decompilation uses the following to share code. */
1827 JS_STATIC_ASSERT(ARGNO_LEN == SLOTNO_LEN);
1830 * Local macros
1832 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
1833 #define DECOMPILE_CODE(pc,nb) if (!Decompile(ss, pc, nb, JSOP_NOP)) return NULL
1834 #define NEXT_OP(pc) (((pc) + (len) == endpc) ? nextop : pc[len])
1835 #define TOP_STR() GetStr(ss, ss->top - 1)
1836 #define POP_STR() PopStr(ss, op)
1837 #define POP_STR_PREC(prec) PopStrPrec(ss, prec)
1840 * Pop a condition expression for if/while. JSOP_IFEQ's precedence forces
1841 * extra parens around assignment, which avoids a strict-mode warning.
1843 #define POP_COND_STR() \
1844 PopStr(ss, (js_CodeSpec[ss->opcodes[ss->top - 1]].format & JOF_SET) \
1845 ? JSOP_IFEQ \
1846 : JSOP_NOP)
1849 * Callers know that ATOM_IS_STRING(atom), and we leave it to the optimizer to
1850 * common ATOM_TO_STRING(atom) here and near the call sites.
1852 #define ATOM_IS_IDENTIFIER(atom) js_IsIdentifier(ATOM_TO_STRING(atom))
1853 #define ATOM_IS_KEYWORD(atom) \
1854 (js_CheckKeyword(ATOM_TO_STRING(atom)->chars(), \
1855 ATOM_TO_STRING(atom)->length()) != TOK_EOF)
1858 * Given an atom already fetched from jp->script's atom map, quote/escape its
1859 * string appropriately into rval, and select fmt from the quoted and unquoted
1860 * alternatives.
1862 #define GET_QUOTE_AND_FMT(qfmt, ufmt, rval) \
1863 JS_BEGIN_MACRO \
1864 jschar quote_; \
1865 if (!ATOM_IS_IDENTIFIER(atom)) { \
1866 quote_ = '\''; \
1867 fmt = qfmt; \
1868 } else { \
1869 quote_ = 0; \
1870 fmt = ufmt; \
1872 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), quote_); \
1873 if (!rval) \
1874 return NULL; \
1875 JS_END_MACRO
1877 #define LOAD_OBJECT(PCOFF) \
1878 GET_OBJECT_FROM_BYTECODE(jp->script, pc, PCOFF, obj)
1880 #define LOAD_FUNCTION(PCOFF) \
1881 GET_FUNCTION_FROM_BYTECODE(jp->script, pc, PCOFF, fun)
1883 #define LOAD_REGEXP(PCOFF) \
1884 GET_REGEXP_FROM_BYTECODE(jp->script, pc, PCOFF, obj)
1886 #define GET_SOURCE_NOTE_ATOM(sn, atom) \
1887 JS_BEGIN_MACRO \
1888 jsatomid atomIndex_ = (jsatomid) js_GetSrcNoteOffset((sn), 0); \
1890 LOCAL_ASSERT(atomIndex_ < jp->script->atomMap.length); \
1891 (atom) = jp->script->atomMap.vector[atomIndex_]; \
1892 JS_END_MACRO
1895 * Get atom from jp->script's atom map, quote/escape its string appropriately
1896 * into rval, and select fmt from the quoted and unquoted alternatives.
1898 #define GET_ATOM_QUOTE_AND_FMT(qfmt, ufmt, rval) \
1899 JS_BEGIN_MACRO \
1900 LOAD_ATOM(0); \
1901 GET_QUOTE_AND_FMT(qfmt, ufmt, rval); \
1902 JS_END_MACRO
1905 * Per spec, new x(y).z means (new x(y))).z. For example new (x(y).z) must
1906 * decompile with the constructor parenthesized, but new x.z should not. The
1907 * normal rules give x(y).z and x.z identical precedence: both are produced by
1908 * JSOP_GETPROP.
1910 * Therefore, we need to know in case JSOP_NEW whether the constructor
1911 * expression contains any unparenthesized function calls. So when building a
1912 * MemberExpression or CallExpression, we set ss->opcodes[n] to JSOP_CALL if
1913 * this is true. x(y).z gets JSOP_CALL, not JSOP_GETPROP.
1915 #define PROPAGATE_CALLNESS() \
1916 JS_BEGIN_MACRO \
1917 if (ss->opcodes[ss->top - 1] == JSOP_CALL || \
1918 ss->opcodes[ss->top - 1] == JSOP_EVAL || \
1919 ss->opcodes[ss->top - 1] == JSOP_APPLY) { \
1920 saveop = JSOP_CALL; \
1922 JS_END_MACRO
1924 cx = ss->sprinter.context;
1925 JS_CHECK_RECURSION(cx, return NULL);
1927 jp = ss->printer;
1928 startpc = pc;
1929 endpc = (nb < 0) ? jp->script->code + jp->script->length : pc + nb;
1930 tail = -1;
1931 todo = -2; /* NB: different from Sprint() error return. */
1932 saveop = JSOP_NOP;
1933 sn = NULL;
1934 rval = NULL;
1935 #if JS_HAS_XML_SUPPORT
1936 foreach = inXML = quoteAttr = JS_FALSE;
1937 #endif
1939 while (nb < 0 || pc < endpc) {
1941 * Move saveop to lastop so prefixed bytecodes can take special action
1942 * while sharing maximal code. Set op and saveop to the new bytecode,
1943 * use op in POP_STR to trigger automatic parenthesization, but push
1944 * saveop at the bottom of the loop if this op pushes. Thus op may be
1945 * set to nop or otherwise mutated to suppress auto-parens.
1947 lastop = saveop;
1948 op = (JSOp) *pc;
1949 cs = &js_CodeSpec[op];
1950 if (cs->format & JOF_INDEXBASE) {
1952 * The decompiler uses js_GetIndexFromBytecode to get atoms and
1953 * objects and ignores these suffix/prefix bytecodes, thus
1954 * simplifying code that must process JSOP_GETTER/JSOP_SETTER
1955 * prefixes.
1957 pc += cs->length;
1958 if (pc >= endpc)
1959 break;
1960 op = (JSOp) *pc;
1961 cs = &js_CodeSpec[op];
1963 saveop = op;
1964 len = oplen = cs->length;
1965 nuses = js_GetStackUses(cs, op, pc);
1968 * Here it is possible that nuses > ss->top when the op has a hidden
1969 * source note. But when nb < 0 we assume that the caller knows that
1970 * Decompile would never meet such opcodes.
1972 if (nb < 0) {
1973 LOCAL_ASSERT(ss->top >= nuses);
1974 uintN ndefs = js_GetStackDefs(cx, cs, op, jp->script, pc);
1975 if ((uintN) -(nb + 1) == ss->top - nuses + ndefs)
1976 return pc;
1980 * Save source literal associated with JS now before the following
1981 * rewrite changes op. See bug 380197.
1983 token = CodeToken[op];
1985 if (pc + oplen == jp->dvgfence) {
1986 JSStackFrame *fp;
1987 uint32 format, mode, type;
1990 * Rewrite non-get ops to their "get" format if the error is in
1991 * the bytecode at pc, so we don't decompile more than the error
1992 * expression.
1994 fp = js_GetScriptedCaller(cx, NULL);
1995 format = cs->format;
1996 if (((fp && fp->regs && pc == fp->regs->pc) ||
1997 (pc == startpc && nuses != 0)) &&
1998 format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_FOR|JOF_VARPROP)) {
1999 mode = JOF_MODE(format);
2000 if (mode == JOF_NAME) {
2002 * JOF_NAME does not imply JOF_ATOM, so we must check for
2003 * the QARG and QVAR format types, and translate those to
2004 * JSOP_GETARG or JSOP_GETLOCAL appropriately, instead of
2005 * to JSOP_NAME.
2007 type = JOF_TYPE(format);
2008 op = (type == JOF_QARG)
2009 ? JSOP_GETARG
2010 : (type == JOF_LOCAL)
2011 ? JSOP_GETLOCAL
2012 : JSOP_NAME;
2014 JS_ASSERT(js_CodeSpec[op].nuses >= 0);
2015 i = nuses - js_CodeSpec[op].nuses;
2016 while (--i >= 0)
2017 PopOff(ss, JSOP_NOP);
2018 } else {
2020 * We must replace the faulting pc's bytecode with a
2021 * corresponding JSOP_GET* code. For JSOP_SET{PROP,ELEM},
2022 * we must use the "2nd" form of JSOP_GET{PROP,ELEM}, to
2023 * throw away the assignment op's right-hand operand and
2024 * decompile it as if it were a GET of its left-hand
2025 * operand.
2027 if (mode == JOF_PROP) {
2028 op = (JSOp) ((format & JOF_SET)
2029 ? JSOP_GETPROP2
2030 : JSOP_GETPROP);
2031 } else if (mode == JOF_ELEM) {
2032 op = (JSOp) ((format & JOF_SET)
2033 ? JSOP_GETELEM2
2034 : JSOP_GETELEM);
2035 } else {
2037 * Unknown mode (including mode 0) means that op is
2038 * uncategorized for our purposes, so we must write
2039 * per-op special case code here.
2041 switch (op) {
2042 case JSOP_ENUMELEM:
2043 case JSOP_ENUMCONSTELEM:
2044 op = JSOP_GETELEM;
2045 break;
2046 case JSOP_SETCALL:
2047 op = JSOP_CALL;
2048 break;
2049 case JSOP_GETTHISPROP:
2051 * NB: JSOP_GETTHISPROP can't fail due to |this|
2052 * being null or undefined at runtime (beware that
2053 * this may change for ES4). Therefore any error
2054 * resulting from this op must be due to the value
2055 * of the property accessed via |this|, so do not
2056 * rewrite op to JSOP_THIS.
2058 * The next two cases should not change op if
2059 * js_DecompileValueGenerator was called from the
2060 * the property getter. They should rewrite only
2061 * if the base object in the arg/var/local is null
2062 * or undefined. FIXME: bug 431569.
2064 break;
2065 case JSOP_GETARGPROP:
2066 op = JSOP_GETARG;
2067 break;
2068 case JSOP_GETLOCALPROP:
2069 op = JSOP_GETLOCAL;
2070 break;
2071 default:
2072 LOCAL_ASSERT(0);
2078 saveop = op;
2079 if (op >= JSOP_LIMIT) {
2080 switch (op) {
2081 case JSOP_GETPROP2:
2082 saveop = JSOP_GETPROP;
2083 break;
2084 case JSOP_GETELEM2:
2085 saveop = JSOP_GETELEM;
2086 break;
2087 default:;
2090 LOCAL_ASSERT(js_CodeSpec[saveop].length == oplen ||
2091 JOF_TYPE(format) == JOF_SLOTATOM);
2093 jp->dvgfence = NULL;
2096 if (token) {
2097 switch (nuses) {
2098 case 2:
2099 sn = js_GetSrcNote(jp->script, pc);
2100 if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
2102 * Avoid over-parenthesizing y in x op= y based on its
2103 * expansion: x = x op y (replace y by z = w to see the
2104 * problem).
2106 op = (JSOp) pc[oplen];
2107 rval = POP_STR();
2108 lval = POP_STR();
2109 /* Print only the right operand of the assignment-op. */
2110 todo = SprintCString(&ss->sprinter, rval);
2111 op = saveop;
2112 } else if (!inXML) {
2113 rval = POP_STR_PREC(cs->prec + !!(cs->format & JOF_LEFTASSOC));
2114 lval = POP_STR_PREC(cs->prec + !(cs->format & JOF_LEFTASSOC));
2115 todo = Sprint(&ss->sprinter, "%s %s %s",
2116 lval, token, rval);
2117 } else {
2118 /* In XML, just concatenate the two operands. */
2119 LOCAL_ASSERT(op == JSOP_ADD);
2120 rval = POP_STR();
2121 lval = POP_STR();
2122 todo = Sprint(&ss->sprinter, ss_format, lval, rval);
2124 break;
2126 case 1:
2127 rval = POP_STR();
2128 todo = Sprint(&ss->sprinter, ss_format, token, rval);
2129 break;
2131 case 0:
2132 todo = SprintCString(&ss->sprinter, token);
2133 break;
2135 default:
2136 todo = -2;
2137 break;
2139 } else {
2140 switch (op) {
2141 case JSOP_NOP:
2143 * Check for a do-while loop, a for-loop with an empty
2144 * initializer part, a labeled statement, a function
2145 * definition, or try/finally.
2147 sn = js_GetSrcNote(jp->script, pc);
2148 todo = -2;
2149 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
2150 case SRC_WHILE:
2151 ++pc;
2152 tail = js_GetSrcNoteOffset(sn, 0) - 1;
2153 LOCAL_ASSERT(pc[tail] == JSOP_IFNE ||
2154 pc[tail] == JSOP_IFNEX);
2155 js_printf(jp, "\tdo {\n");
2156 jp->indent += 4;
2157 DECOMPILE_CODE(pc, tail);
2158 jp->indent -= 4;
2159 js_printf(jp, "\t} while (%s);\n", POP_COND_STR());
2160 pc += tail;
2161 len = js_CodeSpec[*pc].length;
2162 todo = -2;
2163 break;
2165 case SRC_FOR:
2166 rval = "";
2168 do_forloop:
2169 JS_ASSERT(SN_TYPE(sn) == SRC_FOR);
2171 /* Skip the JSOP_NOP or JSOP_POP bytecode. */
2172 pc += JSOP_NOP_LENGTH;
2174 /* Get the cond, next, and loop-closing tail offsets. */
2175 cond = js_GetSrcNoteOffset(sn, 0);
2176 next = js_GetSrcNoteOffset(sn, 1);
2177 tail = js_GetSrcNoteOffset(sn, 2);
2180 * If this loop has a condition, then pc points at a goto
2181 * targeting the condition.
2183 pc2 = pc;
2184 if (cond != tail) {
2185 LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX);
2186 pc2 += (*pc == JSOP_GOTO) ? JSOP_GOTO_LENGTH : JSOP_GOTOX_LENGTH;
2188 LOCAL_ASSERT(tail + GetJumpOffset(pc+tail, pc+tail) == pc2 - pc);
2190 /* Print the keyword and the possibly empty init-part. */
2191 js_printf(jp, "\tfor (%s;", rval);
2193 if (cond != tail) {
2194 /* Decompile the loop condition. */
2195 DECOMPILE_CODE(pc + cond, tail - cond);
2196 js_printf(jp, " %s", POP_STR());
2199 /* Need a semicolon whether or not there was a cond. */
2200 js_puts(jp, ";");
2202 if (next != cond) {
2204 * Decompile the loop updater. It may end in a JSOP_POP
2205 * that we skip; or in a JSOP_POPN that we do not skip,
2206 * followed by a JSOP_NOP (skipped as if it's a POP).
2207 * We cope with the difference between these two cases
2208 * by checking for stack imbalance and popping if there
2209 * is an rval.
2211 uintN saveTop = ss->top;
2213 DECOMPILE_CODE(pc + next, cond - next - JSOP_POP_LENGTH);
2214 LOCAL_ASSERT(ss->top - saveTop <= 1U);
2215 rval = (ss->top == saveTop)
2216 ? ss->sprinter.base + ss->sprinter.offset
2217 : POP_STR();
2218 js_printf(jp, " %s", rval);
2221 /* Do the loop body. */
2222 js_printf(jp, ") {\n");
2223 jp->indent += 4;
2224 next -= pc2 - pc;
2225 DECOMPILE_CODE(pc2, next);
2226 jp->indent -= 4;
2227 js_printf(jp, "\t}\n");
2229 /* Set len so pc skips over the entire loop. */
2230 len = tail + js_CodeSpec[pc[tail]].length;
2231 break;
2233 case SRC_LABEL:
2234 GET_SOURCE_NOTE_ATOM(sn, atom);
2235 jp->indent -= 4;
2236 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2237 if (!rval)
2238 return NULL;
2239 RETRACT(&ss->sprinter, rval);
2240 js_printf(jp, "\t%s:\n", rval);
2241 jp->indent += 4;
2242 break;
2244 case SRC_LABELBRACE:
2245 GET_SOURCE_NOTE_ATOM(sn, atom);
2246 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2247 if (!rval)
2248 return NULL;
2249 RETRACT(&ss->sprinter, rval);
2250 js_printf(jp, "\t%s: {\n", rval);
2251 jp->indent += 4;
2252 break;
2254 case SRC_ENDBRACE:
2255 jp->indent -= 4;
2256 js_printf(jp, "\t}\n");
2257 break;
2259 case SRC_FUNCDEF:
2260 fun = jp->script->getFunction(js_GetSrcNoteOffset(sn, 0));
2261 do_function:
2262 js_puts(jp, "\n");
2263 jp2 = js_NewPrinter(cx, "nested_function", fun,
2264 jp->indent, jp->pretty, jp->grouped,
2265 jp->strict);
2266 if (!jp2)
2267 return NULL;
2268 ok = js_DecompileFunction(jp2);
2269 if (ok && jp2->sprinter.base)
2270 js_puts(jp, jp2->sprinter.base);
2271 js_DestroyPrinter(jp2);
2272 if (!ok)
2273 return NULL;
2274 js_puts(jp, "\n\n");
2275 break;
2277 case SRC_BRACE:
2278 js_printf(jp, "\t{\n");
2279 jp->indent += 4;
2280 len = js_GetSrcNoteOffset(sn, 0);
2281 DECOMPILE_CODE(pc + oplen, len - oplen);
2282 jp->indent -= 4;
2283 js_printf(jp, "\t}\n");
2284 break;
2286 default:;
2288 break;
2290 case JSOP_PUSH:
2291 #if JS_HAS_DESTRUCTURING
2292 sn = js_GetSrcNote(jp->script, pc);
2293 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
2294 pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo);
2295 if (!pc)
2296 return NULL;
2297 LOCAL_ASSERT(*pc == JSOP_POPN);
2298 len = oplen = JSOP_POPN_LENGTH;
2299 goto end_groupassignment;
2301 #endif
2302 /* FALL THROUGH */
2304 case JSOP_BINDNAME:
2305 todo = Sprint(&ss->sprinter, "");
2306 break;
2308 case JSOP_TRY:
2309 js_printf(jp, "\ttry {\n");
2310 jp->indent += 4;
2311 todo = -2;
2312 break;
2314 case JSOP_FINALLY:
2315 jp->indent -= 4;
2316 js_printf(jp, "\t} finally {\n");
2317 jp->indent += 4;
2320 * We push push the pair of exception/restsub cookies to
2321 * simulate the effects [gosub] or control transfer during
2322 * exception capturing on the stack.
2324 todo = Sprint(&ss->sprinter, exception_cookie);
2325 if (todo < 0 || !PushOff(ss, todo, op))
2326 return NULL;
2327 todo = Sprint(&ss->sprinter, retsub_pc_cookie);
2328 break;
2330 case JSOP_RETSUB:
2331 rval = POP_STR();
2332 LOCAL_ASSERT(strcmp(rval, retsub_pc_cookie) == 0);
2333 lval = POP_STR();
2334 LOCAL_ASSERT(strcmp(lval, exception_cookie) == 0);
2335 todo = -2;
2336 break;
2338 case JSOP_GOSUB:
2339 case JSOP_GOSUBX:
2341 * JSOP_GOSUB and GOSUBX have no effect on the decompiler's
2342 * string stack because the next op in bytecode order finds
2343 * the stack balanced by a JSOP_RETSUB executed elsewhere.
2345 todo = -2;
2346 break;
2348 case JSOP_POPN:
2350 uintN newtop, oldtop;
2353 * The compiler models operand stack depth and fixes the stack
2354 * pointer on entry to a catch clause based on its depth model.
2355 * The decompiler must match the code generator's model, which
2356 * is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops.
2358 oldtop = ss->top;
2359 newtop = oldtop - GET_UINT16(pc);
2360 LOCAL_ASSERT(newtop <= oldtop);
2361 todo = -2;
2363 sn = js_GetSrcNote(jp->script, pc);
2364 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
2365 break;
2366 #if JS_HAS_DESTRUCTURING
2367 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
2368 todo = Sprint(&ss->sprinter, "%s[] = [",
2369 VarPrefix(sn));
2370 if (todo < 0)
2371 return NULL;
2372 for (uintN i = newtop; i < oldtop; i++) {
2373 rval = OFF2STR(&ss->sprinter, ss->offsets[i]);
2374 if (Sprint(&ss->sprinter, ss_format,
2375 (i == newtop) ? "" : ", ",
2376 (i == oldtop - 1 && *rval == '\0')
2377 ? ", " : rval) < 0) {
2378 return NULL;
2381 if (SprintPut(&ss->sprinter, "]", 1) < 0)
2382 return NULL;
2385 * If this is an empty group assignment, we have no stack
2386 * budget into which we can push our result string. Adjust
2387 * ss->sprinter.offset so that our consumer can find the
2388 * empty group assignment decompilation.
2390 if (newtop == oldtop) {
2391 ss->sprinter.offset = todo;
2392 } else {
2394 * Kill newtop before the end_groupassignment: label by
2395 * retracting/popping early. Control will either jump
2396 * to do_forloop: or do_letheadbody: or else break from
2397 * our case JSOP_POPN: after the switch (*pc2) below.
2399 LOCAL_ASSERT(newtop < oldtop);
2400 ss->sprinter.offset = GetOff(ss, newtop);
2401 ss->top = newtop;
2404 end_groupassignment:
2405 LOCAL_ASSERT(*pc == JSOP_POPN);
2408 * Thread directly to the next opcode if we can, to handle
2409 * the special cases of a group assignment in the first or
2410 * last part of a for(;;) loop head, or in a let block or
2411 * expression head.
2413 * NB: todo at this point indexes space in ss->sprinter
2414 * that is liable to be overwritten. The code below knows
2415 * exactly how long rval lives, or else copies it down via
2416 * SprintCString.
2418 rval = OFF2STR(&ss->sprinter, todo);
2419 todo = -2;
2420 pc2 = pc + oplen;
2421 if (*pc2 == JSOP_NOP) {
2422 sn = js_GetSrcNote(jp->script, pc2);
2423 if (sn) {
2424 if (SN_TYPE(sn) == SRC_FOR) {
2425 op = JSOP_NOP;
2426 pc = pc2;
2427 goto do_forloop;
2430 if (SN_TYPE(sn) == SRC_DECL) {
2431 if (ss->top == StackDepth(jp->script)) {
2433 * This must be an empty destructuring
2434 * in the head of a let whose body block
2435 * is also empty.
2437 pc = pc2 + JSOP_NOP_LENGTH;
2438 len = js_GetSrcNoteOffset(sn, 0);
2439 LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCK);
2440 js_printf(jp, "\tlet (%s) {\n", rval);
2441 js_printf(jp, "\t}\n");
2442 break;
2444 todo = SprintCString(&ss->sprinter, rval);
2445 if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
2446 return NULL;
2447 op = JSOP_POP;
2448 pc = pc2 + JSOP_NOP_LENGTH;
2449 goto do_letheadbody;
2451 } else {
2453 * An unnannotated NOP following a POPN must be the
2454 * third part of for(;;) loop head. If the POPN's
2455 * immediate operand is 0, then we may have no slot
2456 * on the sprint-stack in which to push our result
2457 * string. In this case the result can be recovered
2458 * at ss->sprinter.base + ss->sprinter.offset.
2460 if (GET_UINT16(pc) == 0)
2461 break;
2462 todo = SprintCString(&ss->sprinter, rval);
2463 saveop = JSOP_NOP;
2468 * If control flow reaches this point with todo still -2,
2469 * just print rval as an expression statement.
2471 if (todo == -2)
2472 js_printf(jp, "\t%s;\n", rval);
2473 break;
2475 #endif
2476 if (newtop < oldtop) {
2477 ss->sprinter.offset = GetOff(ss, newtop);
2478 ss->top = newtop;
2480 break;
2483 case JSOP_EXCEPTION:
2484 /* The catch decompiler handles this op itself. */
2485 LOCAL_ASSERT(JS_FALSE);
2486 break;
2488 case JSOP_POP:
2490 * By default, do not automatically parenthesize when popping
2491 * a stacked expression decompilation. We auto-parenthesize
2492 * only when JSOP_POP is annotated with SRC_PCDELTA, meaning
2493 * comma operator.
2495 op = JSOP_POPV;
2496 /* FALL THROUGH */
2498 case JSOP_POPV:
2499 sn = js_GetSrcNote(jp->script, pc);
2500 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
2501 case SRC_FOR:
2502 /* Force parens around 'in' expression at 'for' front. */
2503 if (ss->opcodes[ss->top-1] == JSOP_IN)
2504 op = JSOP_LSH;
2505 rval = POP_STR();
2506 todo = -2;
2507 goto do_forloop;
2509 case SRC_PCDELTA:
2510 /* Comma operator: use JSOP_POP for correct precedence. */
2511 op = JSOP_POP;
2513 /* Pop and save to avoid blowing stack depth budget. */
2514 lval = JS_strdup(cx, POP_STR());
2515 if (!lval)
2516 return NULL;
2519 * The offset tells distance to the end of the right-hand
2520 * operand of the comma operator.
2522 done = pc + len;
2523 pc += js_GetSrcNoteOffset(sn, 0);
2524 len = 0;
2526 if (!Decompile(ss, done, pc - done, JSOP_POP)) {
2527 cx->free((char *)lval);
2528 return NULL;
2531 /* Pop Decompile result and print comma expression. */
2532 rval = POP_STR();
2533 todo = Sprint(&ss->sprinter, "%s, %s", lval, rval);
2534 cx->free((char *)lval);
2535 break;
2537 case SRC_HIDDEN:
2538 /* Hide this pop, it's from a goto in a with or for/in. */
2539 todo = -2;
2540 break;
2542 case SRC_DECL:
2543 /* This pop is at the end of the let block/expr head. */
2544 pc += JSOP_POP_LENGTH;
2545 #if JS_HAS_DESTRUCTURING
2546 do_letheadbody:
2547 #endif
2548 len = js_GetSrcNoteOffset(sn, 0);
2549 if (pc[len] == JSOP_LEAVEBLOCK) {
2550 js_printf(jp, "\tlet (%s) {\n", POP_STR());
2551 jp->indent += 4;
2552 DECOMPILE_CODE(pc, len);
2553 jp->indent -= 4;
2554 js_printf(jp, "\t}\n");
2555 todo = -2;
2556 } else {
2557 LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCKEXPR);
2559 lval = JS_strdup(cx, PopStr(ss, JSOP_NOP));
2560 if (!lval)
2561 return NULL;
2563 /* Set saveop to reflect what we will push. */
2564 saveop = JSOP_LEAVEBLOCKEXPR;
2565 if (!Decompile(ss, pc, len, saveop)) {
2566 cx->free((char *)lval);
2567 return NULL;
2569 rval = PopStr(ss, JSOP_SETNAME);
2570 todo = Sprint(&ss->sprinter,
2571 (*rval == '{')
2572 ? "let (%s) (%s)"
2573 : "let (%s) %s",
2574 lval, rval);
2575 cx->free((char *)lval);
2577 break;
2579 default:
2580 /* Turn off parens around a yield statement. */
2581 if (ss->opcodes[ss->top-1] == JSOP_YIELD)
2582 op = JSOP_NOP;
2584 rval = POP_STR();
2587 * Don't emit decompiler-pushed strings that are not
2588 * handled by other opcodes. They are pushed onto the
2589 * stack to help model the interpreter stack and should
2590 * not appear in the decompiler's output.
2592 if (*rval != '\0' && (rval[0] != '/' || rval[1] != '*')) {
2593 js_printf(jp,
2594 (*rval == '{' ||
2595 (strncmp(rval, js_function_str, 8) == 0 &&
2596 rval[8] == ' '))
2597 ? "\t(%s);\n"
2598 : "\t%s;\n",
2599 rval);
2600 } else {
2601 LOCAL_ASSERT(*rval == '\0' ||
2602 strcmp(rval, exception_cookie) == 0);
2604 todo = -2;
2605 break;
2607 sn = NULL;
2608 break;
2610 case JSOP_ENTERWITH:
2611 LOCAL_ASSERT(!js_GetSrcNote(jp->script, pc));
2612 rval = POP_STR();
2613 js_printf(jp, "\twith (%s) {\n", rval);
2614 jp->indent += 4;
2615 todo = Sprint(&ss->sprinter, with_cookie);
2616 break;
2618 case JSOP_LEAVEWITH:
2619 sn = js_GetSrcNote(jp->script, pc);
2620 todo = -2;
2621 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
2622 break;
2623 rval = POP_STR();
2624 LOCAL_ASSERT(strcmp(rval, with_cookie) == 0);
2625 jp->indent -= 4;
2626 js_printf(jp, "\t}\n");
2627 break;
2629 case JSOP_ENTERBLOCK:
2631 JSAtom **atomv, *smallv[5];
2632 JSScopeProperty *sprop;
2634 LOAD_OBJECT(0);
2635 argc = OBJ_BLOCK_COUNT(cx, obj);
2636 if ((size_t)argc <= JS_ARRAY_LENGTH(smallv)) {
2637 atomv = smallv;
2638 } else {
2639 atomv = (JSAtom **) cx->malloc(argc * sizeof(JSAtom *));
2640 if (!atomv)
2641 return NULL;
2644 MUST_FLOW_THROUGH("enterblock_out");
2645 #define LOCAL_ASSERT_OUT(expr) LOCAL_ASSERT_CUSTOM(expr, ok = JS_FALSE; \
2646 goto enterblock_out)
2647 for (sprop = obj->scope()->lastProperty(); sprop;
2648 sprop = sprop->parent) {
2649 if (!sprop->hasShortID())
2650 continue;
2651 LOCAL_ASSERT_OUT(sprop->shortid < argc);
2652 atomv[sprop->shortid] = JSID_TO_ATOM(sprop->id);
2654 ok = JS_TRUE;
2655 for (i = 0; i < argc; i++) {
2656 atom = atomv[i];
2657 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2658 if (!rval ||
2659 !PushOff(ss, STR2OFF(&ss->sprinter, rval), op)) {
2660 ok = JS_FALSE;
2661 goto enterblock_out;
2665 sn = js_GetSrcNote(jp->script, pc);
2666 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
2667 #if JS_HAS_BLOCK_SCOPE
2668 case SRC_BRACE:
2669 js_printf(jp, "\t{\n");
2670 jp->indent += 4;
2671 len = js_GetSrcNoteOffset(sn, 0);
2672 ok = Decompile(ss, pc + oplen, len - oplen, JSOP_NOP)
2673 != NULL;
2674 if (!ok)
2675 goto enterblock_out;
2676 jp->indent -= 4;
2677 js_printf(jp, "\t}\n");
2678 break;
2679 #endif
2681 case SRC_CATCH:
2682 jp->indent -= 4;
2683 js_printf(jp, "\t} catch (");
2685 pc2 = pc;
2686 pc += oplen;
2687 LOCAL_ASSERT_OUT(*pc == JSOP_EXCEPTION);
2688 pc += JSOP_EXCEPTION_LENGTH;
2689 todo = Sprint(&ss->sprinter, exception_cookie);
2690 if (todo < 0 || !PushOff(ss, todo, JSOP_EXCEPTION)) {
2691 ok = JS_FALSE;
2692 goto enterblock_out;
2695 if (*pc == JSOP_DUP) {
2696 sn2 = js_GetSrcNote(jp->script, pc);
2697 if (!sn2 || SN_TYPE(sn2) != SRC_DESTRUCT) {
2699 * This is a dup to save the exception for later.
2700 * It is emitted only when the catch head contains
2701 * an exception guard.
2703 LOCAL_ASSERT_OUT(js_GetSrcNoteOffset(sn, 0) != 0);
2704 pc += JSOP_DUP_LENGTH;
2705 todo = Sprint(&ss->sprinter, exception_cookie);
2706 if (todo < 0 ||
2707 !PushOff(ss, todo, JSOP_EXCEPTION)) {
2708 ok = JS_FALSE;
2709 goto enterblock_out;
2714 #if JS_HAS_DESTRUCTURING
2715 if (*pc == JSOP_DUP) {
2716 pc = DecompileDestructuring(ss, pc, endpc);
2717 if (!pc) {
2718 ok = JS_FALSE;
2719 goto enterblock_out;
2721 LOCAL_ASSERT_OUT(*pc == JSOP_POP);
2722 pc += JSOP_POP_LENGTH;
2723 lval = PopStr(ss, JSOP_NOP);
2724 js_puts(jp, lval);
2725 } else {
2726 #endif
2727 LOCAL_ASSERT_OUT(*pc == JSOP_SETLOCALPOP);
2728 i = GET_SLOTNO(pc) - jp->script->nfixed;
2729 pc += JSOP_SETLOCALPOP_LENGTH;
2730 atom = atomv[i - OBJ_BLOCK_DEPTH(cx, obj)];
2731 str = ATOM_TO_STRING(atom);
2732 if (!QuoteString(&jp->sprinter, str, 0)) {
2733 ok = JS_FALSE;
2734 goto enterblock_out;
2736 #if JS_HAS_DESTRUCTURING
2738 #endif
2741 * Pop the exception_cookie (or its dup in the case of a
2742 * guarded catch head) off the stack now.
2744 rval = PopStr(ss, JSOP_NOP);
2745 LOCAL_ASSERT_OUT(strcmp(rval, exception_cookie) == 0);
2747 len = js_GetSrcNoteOffset(sn, 0);
2748 if (len) {
2749 len -= pc - pc2;
2750 LOCAL_ASSERT_OUT(len > 0);
2751 js_printf(jp, " if ");
2752 ok = Decompile(ss, pc, len, JSOP_NOP) != NULL;
2753 if (!ok)
2754 goto enterblock_out;
2755 js_printf(jp, "%s", POP_STR());
2756 pc += len;
2757 LOCAL_ASSERT_OUT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
2758 pc += js_CodeSpec[*pc].length;
2761 js_printf(jp, ") {\n");
2762 jp->indent += 4;
2763 len = 0;
2764 break;
2765 default:
2766 break;
2769 todo = -2;
2771 #undef LOCAL_ASSERT_OUT
2772 enterblock_out:
2773 if (atomv != smallv)
2774 cx->free(atomv);
2775 if (!ok)
2776 return NULL;
2778 break;
2780 case JSOP_LEAVEBLOCK:
2781 case JSOP_LEAVEBLOCKEXPR:
2783 uintN top, depth;
2785 sn = js_GetSrcNote(jp->script, pc);
2786 todo = -2;
2787 if (op == JSOP_LEAVEBLOCKEXPR) {
2788 LOCAL_ASSERT(SN_TYPE(sn) == SRC_PCBASE);
2789 rval = POP_STR();
2790 } else if (sn) {
2791 LOCAL_ASSERT(op == JSOP_LEAVEBLOCK);
2792 if (SN_TYPE(sn) == SRC_HIDDEN)
2793 break;
2796 * This JSOP_LEAVEBLOCK must be for a catch block. If sn's
2797 * offset does not equal the model stack depth, there must
2798 * be a copy of the exception value on the stack due to a
2799 * catch guard (see above, the JSOP_ENTERBLOCK + SRC_CATCH
2800 * case code).
2802 LOCAL_ASSERT(SN_TYPE(sn) == SRC_CATCH);
2803 if ((uintN)js_GetSrcNoteOffset(sn, 0) != ss->top) {
2804 LOCAL_ASSERT((uintN)js_GetSrcNoteOffset(sn, 0)
2805 == ss->top - 1);
2806 rval = POP_STR();
2807 LOCAL_ASSERT(strcmp(rval, exception_cookie) == 0);
2810 top = ss->top;
2811 depth = GET_UINT16(pc);
2812 LOCAL_ASSERT(top >= depth);
2813 top -= depth;
2814 ss->top = top;
2815 ss->sprinter.offset = GetOff(ss, top);
2816 if (op == JSOP_LEAVEBLOCKEXPR)
2817 todo = SprintCString(&ss->sprinter, rval);
2818 break;
2821 case JSOP_GETUPVAR:
2822 case JSOP_CALLUPVAR:
2823 case JSOP_GETUPVAR_DBG:
2824 case JSOP_CALLUPVAR_DBG:
2825 case JSOP_GETDSLOT:
2826 case JSOP_CALLDSLOT:
2828 if (!jp->fun) {
2829 JS_ASSERT(jp->script->savedCallerFun);
2830 jp->fun = jp->script->getFunction(0);
2833 if (!jp->localNames)
2834 jp->localNames = js_GetLocalNameArray(cx, jp->fun, &jp->pool);
2836 uintN index = GET_UINT16(pc);
2837 if (index < jp->fun->u.i.nupvars) {
2838 index += jp->fun->countArgsAndVars();
2839 } else {
2840 JSUpvarArray *uva;
2841 #ifdef DEBUG
2843 * We must be in an eval called from jp->fun, where
2844 * jp->script is the eval-compiled script.
2846 * However, it's possible that a js_Invoke already
2847 * pushed a frame trying to call js_Construct on an
2848 * object that's not a constructor, causing us to be
2849 * called with an intervening frame on the stack.
2851 JSStackFrame *fp = js_GetTopStackFrame(cx);
2852 if (fp) {
2853 while (!(fp->flags & JSFRAME_EVAL))
2854 fp = fp->down;
2855 JS_ASSERT(fp->script == jp->script);
2856 JS_ASSERT(fp->down->fun == jp->fun);
2857 JS_ASSERT(FUN_INTERPRETED(jp->fun));
2858 JS_ASSERT(jp->script != jp->fun->u.i.script);
2859 JS_ASSERT(jp->script->upvarsOffset != 0);
2861 #endif
2862 uva = jp->script->upvars();
2863 index = UPVAR_FRAME_SLOT(uva->vector[index]);
2865 atom = GetArgOrVarAtom(jp, index);
2866 goto do_name;
2869 case JSOP_CALLLOCAL:
2870 case JSOP_GETLOCAL:
2871 if (IsVarSlot(jp, pc, &i)) {
2872 atom = GetArgOrVarAtom(jp, i);
2873 LOCAL_ASSERT(atom);
2874 goto do_name;
2876 LOCAL_ASSERT((uintN)i < ss->top);
2877 sn = js_GetSrcNote(jp->script, pc);
2879 #if JS_HAS_DESTRUCTURING
2880 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
2882 * Distinguish a js_DecompileValueGenerator call that
2883 * targets op alone, from decompilation of a full group
2884 * assignment sequence, triggered by SRC_GROUPASSIGN
2885 * annotating the first JSOP_GETLOCAL in the sequence.
2887 if (endpc - pc > JSOP_GETLOCAL_LENGTH || pc > startpc) {
2888 pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo);
2889 if (!pc)
2890 return NULL;
2891 LOCAL_ASSERT(*pc == JSOP_POPN);
2892 len = oplen = JSOP_POPN_LENGTH;
2893 goto end_groupassignment;
2896 /* Null sn to prevent bogus VarPrefix'ing below. */
2897 sn = NULL;
2899 #endif
2901 rval = GetLocal(ss, i);
2902 todo = Sprint(&ss->sprinter, ss_format, VarPrefix(sn), rval);
2903 break;
2905 case JSOP_SETLOCAL:
2906 case JSOP_SETLOCALPOP:
2907 if (IsVarSlot(jp, pc, &i)) {
2908 atom = GetArgOrVarAtom(jp, i);
2909 LOCAL_ASSERT(atom);
2910 goto do_setname;
2912 lval = GetLocal(ss, i);
2913 rval = POP_STR();
2914 goto do_setlval;
2916 case JSOP_INCLOCAL:
2917 case JSOP_DECLOCAL:
2918 if (IsVarSlot(jp, pc, &i)) {
2919 atom = GetArgOrVarAtom(jp, i);
2920 LOCAL_ASSERT(atom);
2921 goto do_incatom;
2923 lval = GetLocal(ss, i);
2924 goto do_inclval;
2926 case JSOP_LOCALINC:
2927 case JSOP_LOCALDEC:
2928 if (IsVarSlot(jp, pc, &i)) {
2929 atom = GetArgOrVarAtom(jp, i);
2930 LOCAL_ASSERT(atom);
2931 goto do_atominc;
2933 lval = GetLocal(ss, i);
2934 goto do_lvalinc;
2936 case JSOP_RETRVAL:
2937 todo = -2;
2938 break;
2940 case JSOP_RETURN:
2941 LOCAL_ASSERT(jp->fun);
2942 fun = jp->fun;
2943 if (fun->flags & JSFUN_EXPR_CLOSURE) {
2944 /* Turn on parens around comma-expression here. */
2945 op = JSOP_SETNAME;
2946 rval = POP_STR();
2947 js_printf(jp, (*rval == '{') ? "(%s)%s" : ss_format,
2948 rval,
2949 ((fun->flags & JSFUN_LAMBDA) || !fun->atom)
2950 ? ""
2951 : ";");
2952 todo = -2;
2953 break;
2955 /* FALL THROUGH */
2957 case JSOP_SETRVAL:
2958 rval = POP_STR();
2959 if (*rval != '\0')
2960 js_printf(jp, "\t%s %s;\n", js_return_str, rval);
2961 else
2962 js_printf(jp, "\t%s;\n", js_return_str);
2963 todo = -2;
2964 break;
2966 #if JS_HAS_GENERATORS
2967 case JSOP_YIELD:
2968 #if JS_HAS_GENERATOR_EXPRS
2969 if (!ss->inGenExp || !(sn = js_GetSrcNote(jp->script, pc)))
2970 #endif
2972 /* Turn off most parens. */
2973 op = JSOP_SETNAME;
2974 rval = POP_STR();
2975 todo = (*rval != '\0')
2976 ? Sprint(&ss->sprinter,
2977 (strncmp(rval, js_yield_str, 5) == 0 &&
2978 (rval[5] == ' ' || rval[5] == '\0'))
2979 ? "%s (%s)"
2980 : "%s %s",
2981 js_yield_str, rval)
2982 : SprintCString(&ss->sprinter, js_yield_str);
2983 break;
2986 #if JS_HAS_GENERATOR_EXPRS
2987 LOCAL_ASSERT(SN_TYPE(sn) == SRC_HIDDEN);
2988 /* FALL THROUGH */
2989 #endif
2991 case JSOP_ARRAYPUSH:
2993 uintN pos, forpos;
2994 ptrdiff_t start;
2996 /* Turn off most parens. */
2997 op = JSOP_SETNAME;
2999 /* Pop the expression being pushed or yielded. */
3000 rval = POP_STR();
3003 * Skip the for loop head stacked by JSOP_FORLOCAL until we hit
3004 * a block local slot (note empty destructuring patterns result
3005 * in unit-count blocks).
3007 pos = ss->top;
3008 while (pos != 0) {
3009 op = (JSOp) ss->opcodes[--pos];
3010 if (op == JSOP_ENTERBLOCK)
3011 break;
3013 JS_ASSERT(op == JSOP_ENTERBLOCK);
3016 * Here, forpos must index the space before the left-most |for|
3017 * in the single string of accumulated |for| heads and optional
3018 * final |if (condition)|.
3020 forpos = pos + 1;
3021 LOCAL_ASSERT(forpos < ss->top);
3024 * Now move pos downward over the block's local slots. Even an
3025 * empty destructuring pattern has one (dummy) local.
3027 while (ss->opcodes[pos] == JSOP_ENTERBLOCK) {
3028 if (pos == 0)
3029 break;
3030 --pos;
3033 #if JS_HAS_GENERATOR_EXPRS
3034 if (saveop == JSOP_YIELD) {
3036 * Generator expression: decompile just rval followed by
3037 * the string starting at forpos. Leave the result string
3038 * in ss->offsets[0] so it can be recovered by our caller
3039 * (the JSOP_ANONFUNOBJ with SRC_GENEXP case). Bump the
3040 * top of stack to balance yield, which is an expression
3041 * (so has neutral stack balance).
3043 LOCAL_ASSERT(pos == 0);
3044 xval = OFF2STR(&ss->sprinter, ss->offsets[forpos]);
3045 ss->sprinter.offset = PAREN_SLOP;
3046 todo = Sprint(&ss->sprinter, ss_format, rval, xval);
3047 if (todo < 0)
3048 return NULL;
3049 ss->offsets[0] = todo;
3050 ++ss->top;
3051 return pc;
3053 #endif /* JS_HAS_GENERATOR_EXPRS */
3056 * Array comprehension: retract the sprinter to the beginning
3057 * of the array initialiser and decompile "[<rval> for ...]".
3059 JS_ASSERT(jp->script->nfixed + pos == GET_UINT16(pc));
3060 LOCAL_ASSERT(ss->opcodes[pos] == JSOP_NEWINIT);
3062 start = ss->offsets[pos];
3063 LOCAL_ASSERT(ss->sprinter.base[start] == '[' ||
3064 ss->sprinter.base[start] == '#');
3065 LOCAL_ASSERT(forpos < ss->top);
3066 xval = OFF2STR(&ss->sprinter, ss->offsets[forpos]);
3067 lval = OFF2STR(&ss->sprinter, start);
3068 RETRACT(&ss->sprinter, lval);
3070 todo = Sprint(&ss->sprinter, sss_format, lval, rval, xval);
3071 if (todo < 0)
3072 return NULL;
3073 ss->offsets[pos] = todo;
3074 todo = -2;
3075 break;
3077 #endif /* JS_HAS_GENERATORS */
3079 case JSOP_THROWING:
3080 todo = -2;
3081 break;
3083 case JSOP_THROW:
3084 sn = js_GetSrcNote(jp->script, pc);
3085 todo = -2;
3086 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
3087 break;
3088 rval = POP_STR();
3089 js_printf(jp, "\t%s %s;\n", js_throw_str, rval);
3090 break;
3092 case JSOP_ITER:
3093 foreach = (pc[1] & (JSITER_FOREACH | JSITER_KEYVALUE)) ==
3094 JSITER_FOREACH;
3095 todo = -2;
3096 break;
3098 case JSOP_MOREITER:
3099 JS_NOT_REACHED("JSOP_MOREITER");
3100 break;
3102 case JSOP_ENDITER:
3103 sn = js_GetSrcNote(jp->script, pc);
3104 todo = -2;
3105 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
3106 break;
3107 (void) PopOff(ss, op);
3108 break;
3110 case JSOP_GOTO:
3111 case JSOP_GOTOX:
3112 sn = js_GetSrcNote(jp->script, pc);
3113 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
3114 case SRC_FOR_IN:
3116 * The loop back-edge carries +1 stack balance, for the
3117 * flag processed by JSOP_IFNE. We do not decompile the
3118 * JSOP_IFNE, and instead push the left-hand side of 'in'
3119 * after the loop edge in this stack slot (the JSOP_FOR*
3120 * opcodes' decompilers do this pushing).
3122 cond = GetJumpOffset(pc, pc);
3123 next = js_GetSrcNoteOffset(sn, 0);
3124 tail = js_GetSrcNoteOffset(sn, 1);
3125 JS_ASSERT(pc[cond] == JSOP_MOREITER);
3126 DECOMPILE_CODE(pc + oplen, next - oplen);
3127 lval = POP_STR();
3128 LOCAL_ASSERT(ss->top >= 1);
3130 if (ss->inArrayInit || ss->inGenExp) {
3131 rval = POP_STR();
3132 if (ss->top >= 1 && ss->opcodes[ss->top - 1] == JSOP_FORLOCAL) {
3133 ss->sprinter.offset = ss->offsets[ss->top] - PAREN_SLOP;
3134 if (Sprint(&ss->sprinter, " %s (%s in %s)",
3135 foreach ? js_for_each_str : js_for_str,
3136 lval, rval) < 0) {
3137 return NULL;
3141 * Do not AddParentSlop here, as we will push the
3142 * top-most offset again, which will add paren slop
3143 * for us. We must push to balance the stack budget
3144 * when nesting for heads in a comprehension.
3146 todo = ss->offsets[ss->top - 1];
3147 } else {
3148 todo = Sprint(&ss->sprinter, " %s (%s in %s)",
3149 foreach ? js_for_each_str : js_for_str,
3150 lval, rval);
3152 if (todo < 0 || !PushOff(ss, todo, JSOP_FORLOCAL))
3153 return NULL;
3154 DECOMPILE_CODE(pc + next, cond - next);
3155 } else {
3157 * As above, rval or an extension of it must remain
3158 * stacked during loop body decompilation.
3160 rval = GetStr(ss, ss->top - 1);
3161 js_printf(jp, "\t%s (%s in %s) {\n",
3162 foreach ? js_for_each_str : js_for_str,
3163 lval, rval);
3164 jp->indent += 4;
3165 DECOMPILE_CODE(pc + next, cond - next);
3166 jp->indent -= 4;
3167 js_printf(jp, "\t}\n");
3170 pc += tail;
3171 LOCAL_ASSERT(*pc == JSOP_IFNE || *pc == JSOP_IFNEX);
3172 len = js_CodeSpec[*pc].length;
3173 break;
3175 case SRC_WHILE:
3176 cond = GetJumpOffset(pc, pc);
3177 tail = js_GetSrcNoteOffset(sn, 0);
3178 DECOMPILE_CODE(pc + cond, tail - cond);
3179 js_printf(jp, "\twhile (%s) {\n", POP_COND_STR());
3180 jp->indent += 4;
3181 DECOMPILE_CODE(pc + oplen, cond - oplen);
3182 jp->indent -= 4;
3183 js_printf(jp, "\t}\n");
3184 pc += tail;
3185 LOCAL_ASSERT(*pc == JSOP_IFNE || *pc == JSOP_IFNEX);
3186 len = js_CodeSpec[*pc].length;
3187 todo = -2;
3188 break;
3190 case SRC_CONT2LABEL:
3191 GET_SOURCE_NOTE_ATOM(sn, atom);
3192 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3193 if (!rval)
3194 return NULL;
3195 RETRACT(&ss->sprinter, rval);
3196 js_printf(jp, "\tcontinue %s;\n", rval);
3197 break;
3199 case SRC_CONTINUE:
3200 js_printf(jp, "\tcontinue;\n");
3201 break;
3203 case SRC_BREAK2LABEL:
3204 GET_SOURCE_NOTE_ATOM(sn, atom);
3205 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3206 if (!rval)
3207 return NULL;
3208 RETRACT(&ss->sprinter, rval);
3209 js_printf(jp, "\tbreak %s;\n", rval);
3210 break;
3212 case SRC_HIDDEN:
3213 break;
3215 default:
3216 js_printf(jp, "\tbreak;\n");
3217 break;
3219 todo = -2;
3220 break;
3222 case JSOP_IFEQ:
3223 case JSOP_IFEQX:
3225 JSBool elseif = JS_FALSE;
3227 if_again:
3228 len = GetJumpOffset(pc, pc);
3229 sn = js_GetSrcNote(jp->script, pc);
3231 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
3232 case SRC_IF:
3233 case SRC_IF_ELSE:
3234 rval = POP_COND_STR();
3235 if (ss->inArrayInit || ss->inGenExp) {
3236 LOCAL_ASSERT(SN_TYPE(sn) == SRC_IF);
3237 ss->sprinter.offset -= PAREN_SLOP;
3238 if (Sprint(&ss->sprinter, " if (%s)", rval) < 0)
3239 return NULL;
3240 AddParenSlop(ss);
3241 } else {
3242 js_printf(jp,
3243 elseif ? " if (%s) {\n" : "\tif (%s) {\n",
3244 rval);
3245 jp->indent += 4;
3248 if (SN_TYPE(sn) == SRC_IF) {
3249 DECOMPILE_CODE(pc + oplen, len - oplen);
3250 } else {
3251 LOCAL_ASSERT(!ss->inArrayInit && !ss->inGenExp);
3252 tail = js_GetSrcNoteOffset(sn, 0);
3253 DECOMPILE_CODE(pc + oplen, tail - oplen);
3254 jp->indent -= 4;
3255 pc += tail;
3256 LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX);
3257 oplen = js_CodeSpec[*pc].length;
3258 len = GetJumpOffset(pc, pc);
3259 js_printf(jp, "\t} else");
3262 * If the second offset for sn is non-zero, it tells
3263 * the distance from the goto around the else, to the
3264 * ifeq for the if inside the else that forms an "if
3265 * else if" chain. Thus cond spans the condition of
3266 * the second if, so we simply decompile it and start
3267 * over at label if_again.
3269 cond = js_GetSrcNoteOffset(sn, 1);
3270 if (cond != 0) {
3271 cond -= tail;
3272 DECOMPILE_CODE(pc + oplen, cond - oplen);
3273 pc += cond;
3274 oplen = js_CodeSpec[*pc].length;
3275 elseif = JS_TRUE;
3276 goto if_again;
3279 js_printf(jp, " {\n");
3280 jp->indent += 4;
3281 DECOMPILE_CODE(pc + oplen, len - oplen);
3284 if (!ss->inArrayInit && !ss->inGenExp) {
3285 jp->indent -= 4;
3286 js_printf(jp, "\t}\n");
3288 todo = -2;
3289 break;
3291 case SRC_COND:
3292 xval = JS_strdup(cx, POP_STR());
3293 if (!xval)
3294 return NULL;
3295 len = js_GetSrcNoteOffset(sn, 0);
3296 DECOMPILE_CODE(pc + oplen, len - oplen);
3297 lval = JS_strdup(cx, POP_STR());
3298 if (!lval) {
3299 cx->free((void *)xval);
3300 return NULL;
3302 pc += len;
3303 LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX);
3304 oplen = js_CodeSpec[*pc].length;
3305 len = GetJumpOffset(pc, pc);
3306 DECOMPILE_CODE(pc + oplen, len - oplen);
3307 rval = POP_STR();
3308 todo = Sprint(&ss->sprinter, "%s ? %s : %s",
3309 xval, lval, rval);
3310 cx->free((void *)xval);
3311 cx->free((void *)lval);
3312 break;
3314 default:
3315 break;
3317 break;
3320 case JSOP_IFNE:
3321 case JSOP_IFNEX:
3322 LOCAL_ASSERT(0);
3323 break;
3325 case JSOP_OR:
3326 case JSOP_ORX:
3327 xval = "||";
3329 do_logical_connective:
3330 /* Top of stack is the first clause in a disjunction (||). */
3331 lval = JS_strdup(cx, POP_STR());
3332 if (!lval)
3333 return NULL;
3334 done = pc + GetJumpOffset(pc, pc);
3335 pc += len;
3336 len = done - pc;
3337 if (!Decompile(ss, pc, len, op)) {
3338 cx->free((char *)lval);
3339 return NULL;
3341 rval = POP_STR();
3342 if (jp->pretty &&
3343 jp->indent + 4 + strlen(lval) + 4 + strlen(rval) > 75) {
3344 rval = JS_strdup(cx, rval);
3345 if (!rval) {
3346 tail = -1;
3347 } else {
3348 todo = Sprint(&ss->sprinter, "%s %s\n", lval, xval);
3349 tail = Sprint(&ss->sprinter, "%*s%s",
3350 jp->indent + 4, "", rval);
3351 cx->free((char *)rval);
3353 if (tail < 0)
3354 todo = -1;
3355 } else {
3356 todo = Sprint(&ss->sprinter, "%s %s %s", lval, xval, rval);
3358 cx->free((char *)lval);
3359 break;
3361 case JSOP_AND:
3362 case JSOP_ANDX:
3363 xval = "&&";
3364 goto do_logical_connective;
3366 case JSOP_FORARG:
3367 sn = NULL;
3368 i = GET_ARGNO(pc);
3369 goto do_forvarslot;
3371 case JSOP_FORLOCAL:
3372 sn = js_GetSrcNote(jp->script, pc);
3373 if (!IsVarSlot(jp, pc, &i)) {
3374 JS_ASSERT(op == JSOP_FORLOCAL);
3375 todo = Sprint(&ss->sprinter, ss_format, VarPrefix(sn), GetStr(ss, i));
3376 break;
3379 do_forvarslot:
3380 atom = GetArgOrVarAtom(jp, i);
3381 LOCAL_ASSERT(atom);
3382 todo = SprintCString(&ss->sprinter, VarPrefix(sn));
3383 if (todo < 0 || !QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0))
3384 return NULL;
3385 break;
3387 case JSOP_FORNAME:
3388 LOAD_ATOM(0);
3389 sn = js_GetSrcNote(jp->script, pc);
3390 todo = SprintCString(&ss->sprinter, VarPrefix(sn));
3391 if (todo < 0 || !QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0))
3392 return NULL;
3393 break;
3395 case JSOP_FORPROP:
3396 xval = NULL;
3397 LOAD_ATOM(0);
3398 if (!ATOM_IS_IDENTIFIER(atom)) {
3399 xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
3400 (jschar)'\'');
3401 if (!xval)
3402 return NULL;
3404 lval = POP_STR();
3405 if (xval) {
3406 JS_ASSERT(*lval);
3407 todo = Sprint(&ss->sprinter, index_format, lval, xval);
3408 } else {
3409 todo = Sprint(&ss->sprinter, ss_format, lval, *lval ? "." : "");
3410 if (todo < 0)
3411 return NULL;
3412 if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0))
3413 return NULL;
3415 break;
3417 case JSOP_FORELEM:
3418 todo = SprintCString(&ss->sprinter, forelem_cookie);
3419 break;
3421 case JSOP_ENUMELEM:
3422 case JSOP_ENUMCONSTELEM:
3424 * The stack has the object under the (top) index expression.
3425 * The "rval" property id is underneath those two on the stack.
3426 * The for loop body net and gross lengths can now be adjusted
3427 * to account for the length of the indexing expression that
3428 * came after JSOP_FORELEM and before JSOP_ENUMELEM.
3430 atom = NULL;
3431 op = JSOP_NOP; /* turn off parens around xval */
3432 xval = POP_STR();
3433 op = JSOP_GETELEM; /* lval must have high precedence */
3434 lval = POP_STR();
3435 op = saveop;
3436 rval = POP_STR();
3437 LOCAL_ASSERT(strcmp(rval, forelem_cookie) == 0);
3438 if (*xval == '\0') {
3439 todo = SprintCString(&ss->sprinter, lval);
3440 } else {
3441 todo = Sprint(&ss->sprinter,
3442 (JOF_OPMODE(lastop) == JOF_XMLNAME)
3443 ? dot_format
3444 : index_format,
3445 lval, xval);
3447 break;
3449 case JSOP_GETTER:
3450 case JSOP_SETTER:
3451 todo = -2;
3452 break;
3454 case JSOP_DUP2:
3455 rval = GetStr(ss, ss->top-2);
3456 todo = SprintCString(&ss->sprinter, rval);
3457 if (todo < 0 || !PushOff(ss, todo,
3458 (JSOp) ss->opcodes[ss->top-2])) {
3459 return NULL;
3461 /* FALL THROUGH */
3463 case JSOP_DUP:
3464 #if JS_HAS_DESTRUCTURING
3465 sn = js_GetSrcNote(jp->script, pc);
3466 if (sn) {
3467 LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT);
3468 pc = DecompileDestructuring(ss, pc, endpc);
3469 if (!pc)
3470 return NULL;
3471 len = 0;
3472 lval = POP_STR();
3473 op = saveop = JSOP_ENUMELEM;
3474 rval = POP_STR();
3476 if (strcmp(rval, forelem_cookie) == 0) {
3477 todo = Sprint(&ss->sprinter, ss_format,
3478 VarPrefix(sn), lval);
3480 // Skip POP so the SRC_FOR_IN code can pop for itself.
3481 if (*pc == JSOP_POP)
3482 len = JSOP_POP_LENGTH;
3483 } else {
3484 todo = Sprint(&ss->sprinter, "%s%s = %s",
3485 VarPrefix(sn), lval, rval);
3487 break;
3489 #endif
3491 rval = GetStr(ss, ss->top-1);
3492 saveop = (JSOp) ss->opcodes[ss->top-1];
3493 todo = SprintCString(&ss->sprinter, rval);
3494 break;
3496 case JSOP_SETARG:
3497 atom = GetArgOrVarAtom(jp, GET_ARGNO(pc));
3498 LOCAL_ASSERT(atom);
3499 goto do_setname;
3501 case JSOP_SETCONST:
3502 case JSOP_SETNAME:
3503 case JSOP_SETGVAR:
3504 LOAD_ATOM(0);
3506 do_setname:
3507 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3508 if (!lval)
3509 return NULL;
3510 rval = POP_STR();
3511 if (op == JSOP_SETNAME)
3512 (void) PopOff(ss, op);
3514 do_setlval:
3515 sn = js_GetSrcNote(jp->script, pc - 1);
3516 if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
3517 todo = Sprint(&ss->sprinter, "%s %s= %s",
3518 lval,
3519 (lastop == JSOP_GETTER)
3520 ? js_getter_str
3521 : (lastop == JSOP_SETTER)
3522 ? js_setter_str
3523 : CodeToken[lastop],
3524 rval);
3525 } else {
3526 sn = js_GetSrcNote(jp->script, pc);
3527 todo = Sprint(&ss->sprinter, "%s%s = %s",
3528 VarPrefix(sn), lval, rval);
3530 if (op == JSOP_SETLOCALPOP) {
3531 if (!PushOff(ss, todo, saveop))
3532 return NULL;
3533 rval = POP_STR();
3534 LOCAL_ASSERT(*rval != '\0');
3535 js_printf(jp, "\t%s;\n", rval);
3536 todo = -2;
3538 break;
3540 case JSOP_CONCATN:
3542 argc = GET_UINT16(pc);
3543 JS_ASSERT(argc > 0);
3545 js::Vector<char *> argv(cx);
3546 if (!argv.resize(argc))
3547 return NULL;
3549 MUST_FLOW_THROUGH("out");
3550 ok = JS_FALSE;
3552 for (i = argc - 1; i >= 0; i--) {
3553 argv[i] = JS_strdup(cx, POP_STR_PREC(cs->prec + 1));
3554 if (!argv[i])
3555 goto out;
3558 todo = Sprint(&ss->sprinter, "%s", argv[0]);
3559 if (todo < 0)
3560 goto out;
3561 for (i = 1; i < argc; i++) {
3562 if (Sprint(&ss->sprinter, " + %s", argv[i]) < 0)
3563 goto out;
3567 * The only way that our next op could be a JSOP_ADD is
3568 * if we are about to concatenate at least one non-string
3569 * literal. Deal with that here in order to avoid extra
3570 * parentheses (because JSOP_ADD is left-associative).
3572 if (pc[len] == JSOP_ADD)
3573 saveop = JSOP_NOP;
3575 ok = JS_TRUE;
3577 out:
3578 for (i = 0; i < argc; i++)
3579 JS_free(cx, argv[i]);
3580 if (!ok)
3581 return NULL;
3582 break;
3585 case JSOP_NEW:
3586 case JSOP_CALL:
3587 case JSOP_EVAL:
3588 case JSOP_APPLY:
3589 case JSOP_SETCALL:
3590 argc = GET_ARGC(pc);
3591 argv = (char **)
3592 cx->malloc((size_t)(argc + 1) * sizeof *argv);
3593 if (!argv)
3594 return NULL;
3596 op = JSOP_SETNAME;
3597 ok = JS_TRUE;
3598 for (i = argc; i > 0; i--)
3599 argv[i] = JS_strdup(cx, POP_STR());
3601 /* Skip the JSOP_PUSHOBJ-created empty string. */
3602 LOCAL_ASSERT(ss->top >= 2);
3603 (void) PopOff(ss, op);
3606 * Special case: new (x(y)(z)) must be parenthesized like so.
3607 * Same for new (x(y).z) -- contrast with new x(y).z.
3608 * See PROPAGATE_CALLNESS.
3610 op = (JSOp) ss->opcodes[ss->top - 1];
3611 lval = PopStr(ss,
3612 (saveop == JSOP_NEW &&
3613 (op == JSOP_CALL ||
3614 op == JSOP_EVAL ||
3615 op == JSOP_APPLY ||
3616 (js_CodeSpec[op].format & JOF_CALLOP)))
3617 ? JSOP_NAME
3618 : saveop);
3619 op = saveop;
3621 argv[0] = JS_strdup(cx, lval);
3622 if (!argv[0])
3623 ok = JS_FALSE;
3625 lval = "(", rval = ")";
3626 if (op == JSOP_NEW) {
3627 if (argc == 0)
3628 lval = rval = "";
3629 todo = Sprint(&ss->sprinter, "%s %s%s",
3630 js_new_str, argv[0], lval);
3631 } else {
3632 todo = Sprint(&ss->sprinter, ss_format,
3633 argv[0], lval);
3635 if (todo < 0)
3636 ok = JS_FALSE;
3638 for (i = 1; i <= argc; i++) {
3639 if (!argv[i] ||
3640 Sprint(&ss->sprinter, ss_format,
3641 argv[i], (i < argc) ? ", " : "") < 0) {
3642 ok = JS_FALSE;
3643 break;
3646 if (Sprint(&ss->sprinter, rval) < 0)
3647 ok = JS_FALSE;
3649 for (i = 0; i <= argc; i++)
3650 cx->free(argv[i]);
3651 cx->free(argv);
3652 if (!ok)
3653 return NULL;
3654 if (op == JSOP_SETCALL) {
3655 if (!PushOff(ss, todo, op))
3656 return NULL;
3657 todo = Sprint(&ss->sprinter, "");
3659 break;
3661 case JSOP_DELNAME:
3662 LOAD_ATOM(0);
3663 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3664 if (!lval)
3665 return NULL;
3666 RETRACT(&ss->sprinter, lval);
3667 do_delete_lval:
3668 todo = Sprint(&ss->sprinter, "%s %s", js_delete_str, lval);
3669 break;
3671 case JSOP_DELPROP:
3672 GET_ATOM_QUOTE_AND_FMT("%s %s[%s]", "%s %s.%s", rval);
3673 op = JSOP_GETPROP;
3674 lval = POP_STR();
3675 todo = Sprint(&ss->sprinter, fmt, js_delete_str, lval, rval);
3676 break;
3678 case JSOP_DELELEM:
3679 op = JSOP_NOP; /* turn off parens */
3680 xval = POP_STR();
3681 op = JSOP_GETPROP;
3682 lval = POP_STR();
3683 if (*xval == '\0')
3684 goto do_delete_lval;
3685 todo = Sprint(&ss->sprinter,
3686 (JOF_OPMODE(lastop) == JOF_XMLNAME)
3687 ? "%s %s.%s"
3688 : "%s %s[%s]",
3689 js_delete_str, lval, xval);
3690 break;
3692 #if JS_HAS_XML_SUPPORT
3693 case JSOP_DELDESC:
3694 xval = POP_STR();
3695 op = JSOP_GETPROP;
3696 lval = POP_STR();
3697 todo = Sprint(&ss->sprinter, "%s %s..%s",
3698 js_delete_str, lval, xval);
3699 break;
3700 #endif
3702 case JSOP_TYPEOFEXPR:
3703 case JSOP_TYPEOF:
3704 case JSOP_VOID:
3705 rval = POP_STR();
3706 todo = Sprint(&ss->sprinter, "%s %s",
3707 (op == JSOP_VOID) ? js_void_str : js_typeof_str,
3708 rval);
3709 break;
3711 case JSOP_INCARG:
3712 case JSOP_DECARG:
3713 atom = GetArgOrVarAtom(jp, GET_ARGNO(pc));
3714 LOCAL_ASSERT(atom);
3715 goto do_incatom;
3717 case JSOP_INCNAME:
3718 case JSOP_DECNAME:
3719 case JSOP_INCGVAR:
3720 case JSOP_DECGVAR:
3721 LOAD_ATOM(0);
3722 do_incatom:
3723 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3724 if (!lval)
3725 return NULL;
3726 RETRACT(&ss->sprinter, lval);
3727 do_inclval:
3728 todo = Sprint(&ss->sprinter, ss_format,
3729 js_incop_strs[!(cs->format & JOF_INC)], lval);
3730 break;
3732 case JSOP_INCPROP:
3733 case JSOP_DECPROP:
3734 GET_ATOM_QUOTE_AND_FMT(preindex_format, predot_format, rval);
3737 * Force precedence below the numeric literal opcodes, so that
3738 * 42..foo or 10000..toString(16), e.g., decompile with parens
3739 * around the left-hand side of dot.
3741 op = JSOP_GETPROP;
3742 lval = POP_STR();
3743 todo = Sprint(&ss->sprinter, fmt,
3744 js_incop_strs[!(cs->format & JOF_INC)],
3745 lval, rval);
3746 break;
3748 case JSOP_INCELEM:
3749 case JSOP_DECELEM:
3750 op = JSOP_NOP; /* turn off parens */
3751 xval = POP_STR();
3752 op = JSOP_GETELEM;
3753 lval = POP_STR();
3754 if (*xval != '\0') {
3755 todo = Sprint(&ss->sprinter,
3756 (JOF_OPMODE(lastop) == JOF_XMLNAME)
3757 ? predot_format
3758 : preindex_format,
3759 js_incop_strs[!(cs->format & JOF_INC)],
3760 lval, xval);
3761 } else {
3762 todo = Sprint(&ss->sprinter, ss_format,
3763 js_incop_strs[!(cs->format & JOF_INC)], lval);
3765 break;
3767 case JSOP_ARGINC:
3768 case JSOP_ARGDEC:
3769 atom = GetArgOrVarAtom(jp, GET_ARGNO(pc));
3770 LOCAL_ASSERT(atom);
3771 goto do_atominc;
3773 case JSOP_NAMEINC:
3774 case JSOP_NAMEDEC:
3775 case JSOP_GVARINC:
3776 case JSOP_GVARDEC:
3777 LOAD_ATOM(0);
3778 do_atominc:
3779 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3780 if (!lval)
3781 return NULL;
3782 RETRACT(&ss->sprinter, lval);
3783 do_lvalinc:
3784 todo = Sprint(&ss->sprinter, ss_format,
3785 lval, js_incop_strs[!(cs->format & JOF_INC)]);
3786 break;
3788 case JSOP_PROPINC:
3789 case JSOP_PROPDEC:
3790 GET_ATOM_QUOTE_AND_FMT(postindex_format, postdot_format, rval);
3793 * Force precedence below the numeric literal opcodes, so that
3794 * 42..foo or 10000..toString(16), e.g., decompile with parens
3795 * around the left-hand side of dot.
3797 op = JSOP_GETPROP;
3798 lval = POP_STR();
3799 todo = Sprint(&ss->sprinter, fmt, lval, rval,
3800 js_incop_strs[!(cs->format & JOF_INC)]);
3801 break;
3803 case JSOP_ELEMINC:
3804 case JSOP_ELEMDEC:
3805 op = JSOP_NOP; /* turn off parens */
3806 xval = POP_STR();
3807 op = JSOP_GETELEM;
3808 lval = POP_STR();
3809 if (*xval != '\0') {
3810 todo = Sprint(&ss->sprinter,
3811 (JOF_OPMODE(lastop) == JOF_XMLNAME)
3812 ? postdot_format
3813 : postindex_format,
3814 lval, xval,
3815 js_incop_strs[!(cs->format & JOF_INC)]);
3816 } else {
3817 todo = Sprint(&ss->sprinter, ss_format,
3818 lval, js_incop_strs[!(cs->format & JOF_INC)]);
3820 break;
3822 case JSOP_LENGTH:
3823 fmt = dot_format;
3824 rval = js_length_str;
3825 goto do_getprop_lval;
3827 case JSOP_GETPROP2:
3828 op = JSOP_GETPROP;
3829 (void) PopOff(ss, lastop);
3830 /* FALL THROUGH */
3832 case JSOP_CALLPROP:
3833 case JSOP_GETPROP:
3834 case JSOP_GETXPROP:
3835 LOAD_ATOM(0);
3837 do_getprop:
3838 GET_QUOTE_AND_FMT(index_format, dot_format, rval);
3839 do_getprop_lval:
3840 PROPAGATE_CALLNESS();
3841 lval = POP_STR();
3842 todo = Sprint(&ss->sprinter, fmt, lval, rval);
3843 break;
3845 case JSOP_GETTHISPROP:
3846 LOAD_ATOM(0);
3847 GET_QUOTE_AND_FMT(index_format, dot_format, rval);
3848 todo = Sprint(&ss->sprinter, fmt, js_this_str, rval);
3849 break;
3851 case JSOP_GETARGPROP:
3852 /* Get the name of the argument or variable. */
3853 i = GET_ARGNO(pc);
3855 do_getarg_prop:
3856 atom = GetArgOrVarAtom(ss->printer, i);
3857 LOCAL_ASSERT(atom);
3858 LOCAL_ASSERT(ATOM_IS_STRING(atom));
3859 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3860 if (!lval || !PushOff(ss, STR2OFF(&ss->sprinter, lval), op))
3861 return NULL;
3863 /* Get the name of the property. */
3864 LOAD_ATOM(ARGNO_LEN);
3865 goto do_getprop;
3867 case JSOP_GETLOCALPROP:
3868 if (IsVarSlot(jp, pc, &i))
3869 goto do_getarg_prop;
3870 LOCAL_ASSERT((uintN)i < ss->top);
3871 lval = GetLocal(ss, i);
3872 if (!lval)
3873 return NULL;
3874 todo = SprintCString(&ss->sprinter, lval);
3875 if (todo < 0 || !PushOff(ss, todo, op))
3876 return NULL;
3877 LOAD_ATOM(2);
3878 goto do_getprop;
3880 case JSOP_SETPROP:
3881 case JSOP_SETMETHOD:
3882 LOAD_ATOM(0);
3883 GET_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval);
3884 rval = POP_STR();
3887 * Force precedence below the numeric literal opcodes, so that
3888 * 42..foo or 10000..toString(16), e.g., decompile with parens
3889 * around the left-hand side of dot.
3891 op = JSOP_GETPROP;
3892 lval = POP_STR();
3893 sn = js_GetSrcNote(jp->script, pc - 1);
3894 todo = Sprint(&ss->sprinter, fmt, lval, xval,
3895 (sn && SN_TYPE(sn) == SRC_ASSIGNOP)
3896 ? (lastop == JSOP_GETTER)
3897 ? js_getter_str
3898 : (lastop == JSOP_SETTER)
3899 ? js_setter_str
3900 : CodeToken[lastop]
3901 : "",
3902 rval);
3903 break;
3905 case JSOP_GETELEM2:
3906 op = JSOP_GETELEM;
3907 (void) PopOff(ss, lastop);
3908 /* FALL THROUGH */
3910 case JSOP_CALLELEM:
3911 case JSOP_GETELEM:
3912 op = JSOP_NOP; /* turn off parens */
3913 xval = POP_STR();
3914 op = saveop;
3915 PROPAGATE_CALLNESS();
3916 lval = POP_STR();
3917 if (*xval == '\0') {
3918 todo = Sprint(&ss->sprinter, "%s", lval);
3919 } else {
3920 todo = Sprint(&ss->sprinter,
3921 (JOF_OPMODE(lastop) == JOF_XMLNAME)
3922 ? dot_format
3923 : index_format,
3924 lval, xval);
3926 break;
3928 case JSOP_SETELEM:
3929 rval = POP_STR();
3930 op = JSOP_NOP; /* turn off parens */
3931 xval = POP_STR();
3932 cs = &js_CodeSpec[ss->opcodes[ss->top]];
3933 op = JSOP_GETELEM; /* lval must have high precedence */
3934 lval = POP_STR();
3935 op = saveop;
3936 if (*xval == '\0')
3937 goto do_setlval;
3938 sn = js_GetSrcNote(jp->script, pc - 1);
3939 todo = Sprint(&ss->sprinter,
3940 (JOF_MODE(cs->format) == JOF_XMLNAME)
3941 ? "%s.%s %s= %s"
3942 : "%s[%s] %s= %s",
3943 lval, xval,
3944 (sn && SN_TYPE(sn) == SRC_ASSIGNOP)
3945 ? (lastop == JSOP_GETTER)
3946 ? js_getter_str
3947 : (lastop == JSOP_SETTER)
3948 ? js_setter_str
3949 : CodeToken[lastop]
3950 : "",
3951 rval);
3952 break;
3954 case JSOP_ARGSUB:
3955 i = (jsint) GET_ARGNO(pc);
3956 todo = Sprint(&ss->sprinter, "%s[%d]",
3957 js_arguments_str, (int) i);
3958 break;
3960 case JSOP_ARGCNT:
3961 todo = Sprint(&ss->sprinter, dot_format,
3962 js_arguments_str, js_length_str);
3963 break;
3965 case JSOP_CALLARG:
3966 case JSOP_GETARG:
3967 i = GET_ARGNO(pc);
3968 atom = GetArgOrVarAtom(jp, i);
3969 #if JS_HAS_DESTRUCTURING
3970 if (!atom) {
3971 todo = Sprint(&ss->sprinter, "%s[%d]", js_arguments_str, i);
3972 break;
3974 #else
3975 LOCAL_ASSERT(atom);
3976 #endif
3977 goto do_name;
3979 case JSOP_CALLNAME:
3980 case JSOP_NAME:
3981 case JSOP_GETGVAR:
3982 case JSOP_CALLGVAR:
3983 LOAD_ATOM(0);
3984 do_name:
3985 lval = "";
3986 #if JS_HAS_XML_SUPPORT
3987 do_qname:
3988 #endif
3989 sn = js_GetSrcNote(jp->script, pc);
3990 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
3991 inXML ? DONT_ESCAPE : 0);
3992 if (!rval)
3993 return NULL;
3994 RETRACT(&ss->sprinter, rval);
3995 todo = Sprint(&ss->sprinter, sss_format,
3996 VarPrefix(sn), lval, rval);
3997 break;
3999 case JSOP_UINT16:
4000 i = (jsint) GET_UINT16(pc);
4001 goto do_sprint_int;
4003 case JSOP_UINT24:
4004 i = (jsint) GET_UINT24(pc);
4005 goto do_sprint_int;
4007 case JSOP_INT8:
4008 i = GET_INT8(pc);
4009 goto do_sprint_int;
4011 case JSOP_INT32:
4012 i = GET_INT32(pc);
4013 do_sprint_int:
4014 todo = Sprint(&ss->sprinter, "%d", i);
4015 break;
4017 case JSOP_DOUBLE:
4018 GET_DOUBLE_FROM_BYTECODE(jp->script, pc, 0, atom);
4019 val = ATOM_KEY(atom);
4020 LOCAL_ASSERT(JSVAL_IS_DOUBLE(val));
4021 todo = SprintDoubleValue(&ss->sprinter, val, &saveop);
4022 break;
4024 case JSOP_STRING:
4025 LOAD_ATOM(0);
4026 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
4027 inXML ? DONT_ESCAPE : '"');
4028 if (!rval)
4029 return NULL;
4030 todo = STR2OFF(&ss->sprinter, rval);
4031 break;
4033 case JSOP_LAMBDA:
4034 case JSOP_LAMBDA_FC:
4035 case JSOP_LAMBDA_DBGFC:
4036 #if JS_HAS_GENERATOR_EXPRS
4037 sn = js_GetSrcNote(jp->script, pc);
4038 if (sn && SN_TYPE(sn) == SRC_GENEXP) {
4039 void *mark;
4040 jsuword *innerLocalNames, *outerLocalNames;
4041 JSScript *inner, *outer;
4042 SprintStack ss2;
4043 JSFunction *outerfun;
4045 LOAD_FUNCTION(0);
4048 * All allocation when decompiling is LIFO, using malloc
4049 * or, more commonly, arena-allocating from cx->tempPool.
4050 * Therefore after InitSprintStack succeeds, we must
4051 * release to mark before returning.
4053 mark = cx->tempPool.getMark();
4054 if (!fun->hasLocalNames()) {
4055 innerLocalNames = NULL;
4056 } else {
4057 innerLocalNames = js_GetLocalNameArray(cx, fun, &cx->tempPool);
4058 if (!innerLocalNames)
4059 return NULL;
4061 inner = fun->u.i.script;
4062 if (!InitSprintStack(cx, &ss2, jp, StackDepth(inner))) {
4063 cx->tempPool.release(mark);
4064 return NULL;
4066 ss2.inGenExp = JS_TRUE;
4069 * Recursively decompile this generator function as an
4070 * un-parenthesized generator expression. The ss->inGenExp
4071 * special case of JSOP_YIELD shares array comprehension
4072 * decompilation code that leaves the result as the single
4073 * string pushed on ss2.
4075 outer = jp->script;
4076 outerfun = jp->fun;
4077 outerLocalNames = jp->localNames;
4078 LOCAL_ASSERT(JS_UPTRDIFF(pc, outer->code) <= outer->length);
4079 jp->script = inner;
4080 jp->fun = fun;
4081 jp->localNames = innerLocalNames;
4082 ok = Decompile(&ss2, inner->code, inner->length, JSOP_NOP) != NULL;
4083 jp->script = outer;
4084 jp->fun = outerfun;
4085 jp->localNames = outerLocalNames;
4086 if (!ok) {
4087 cx->tempPool.release(mark);
4088 return NULL;
4092 * Advance over this op and its global |this| push, and
4093 * arrange to advance over the call to this lambda.
4095 pc += len;
4096 LOCAL_ASSERT(*pc == JSOP_NULL);
4097 pc += JSOP_NULL_LENGTH;
4098 LOCAL_ASSERT(*pc == JSOP_CALL);
4099 LOCAL_ASSERT(GET_ARGC(pc) == 0);
4100 len = JSOP_CALL_LENGTH;
4103 * Arrange to parenthesize this genexp unless:
4105 * 1. It is the complete expression consumed by a control
4106 * flow bytecode such as JSOP_TABLESWITCH whose syntax
4107 * always parenthesizes the controlling expression.
4108 * 2. It is the sole argument to a function call.
4110 * But if this genexp runs up against endpc, parenthesize
4111 * regardless. (This can happen if we are called from
4112 * DecompileExpression or recursively from case
4113 * JSOP_{NOP,AND,OR}.)
4115 * There's no special case for |if (genexp)| because the
4116 * compiler optimizes that to |if (true)|.
4118 pc2 = pc + len;
4119 op = JSOp(*pc2);
4120 if (op == JSOP_TRACE || op == JSOP_NOP)
4121 pc2 += JSOP_NOP_LENGTH;
4122 LOCAL_ASSERT(pc2 < endpc ||
4123 endpc < outer->code + outer->length);
4124 LOCAL_ASSERT(ss2.top == 1);
4125 ss2.opcodes[0] = JSOP_POP;
4126 if (pc2 == endpc) {
4127 op = JSOP_SETNAME;
4128 } else {
4129 op = (JSOp) *pc2;
4130 op = ((js_CodeSpec[op].format & JOF_PARENHEAD) ||
4131 ((js_CodeSpec[op].format & JOF_INVOKE) && GET_ARGC(pc2) == 1))
4132 ? JSOP_POP
4133 : JSOP_SETNAME;
4136 * Stack this result as if it's a name and not an
4137 * anonymous function, so it doesn't get decompiled as
4138 * a generator function in a getter or setter context.
4139 * The precedence level is the same for JSOP_NAME and
4140 * JSOP_LAMBDA.
4142 LOCAL_ASSERT(js_CodeSpec[JSOP_NAME].prec ==
4143 js_CodeSpec[saveop].prec);
4144 saveop = JSOP_NAME;
4148 * Alas, we have to malloc a copy of the result left on
4149 * the top of ss2 because both ss and ss2 arena-allocate
4150 * from cx's tempPool.
4152 rval = JS_strdup(cx, PopStr(&ss2, op));
4153 cx->tempPool.release(mark);
4154 if (!rval)
4155 return NULL;
4156 todo = SprintCString(&ss->sprinter, rval);
4157 cx->free((void *)rval);
4158 break;
4160 #endif /* JS_HAS_GENERATOR_EXPRS */
4161 /* FALL THROUGH */
4163 LOAD_FUNCTION(0);
4166 * Always parenthesize expression closures. We can't force
4167 * saveop to a low-precedence op to arrange for auto-magic
4168 * parenthesization without confusing getter/setter code
4169 * that checks for JSOP_LAMBDA.
4171 bool grouped = !(fun->flags & JSFUN_EXPR_CLOSURE);
4172 bool strict = jp->script->strictModeCode;
4173 str = js_DecompileToString(cx, "lambda", fun, 0,
4174 false, grouped, strict,
4175 js_DecompileFunction);
4176 if (!str)
4177 return NULL;
4179 sprint_string:
4180 todo = SprintString(&ss->sprinter, str);
4181 break;
4183 case JSOP_CALLEE:
4184 JS_ASSERT(jp->fun && jp->fun->atom);
4185 todo = SprintString(&ss->sprinter, ATOM_TO_STRING(jp->fun->atom));
4186 break;
4188 case JSOP_OBJECT:
4189 LOAD_OBJECT(0);
4190 LOCAL_ASSERT(obj->getClass() == &js_RegExpClass);
4191 goto do_regexp;
4193 case JSOP_REGEXP:
4194 GET_REGEXP_FROM_BYTECODE(jp->script, pc, 0, obj);
4195 do_regexp:
4196 if (!js_regexp_toString(cx, obj, &val))
4197 return NULL;
4198 str = JSVAL_TO_STRING(val);
4199 goto sprint_string;
4201 case JSOP_TABLESWITCH:
4202 case JSOP_TABLESWITCHX:
4204 ptrdiff_t jmplen, off, off2;
4205 jsint j, n, low, high;
4206 TableEntry *table, *tmp;
4208 sn = js_GetSrcNote(jp->script, pc);
4209 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
4210 len = js_GetSrcNoteOffset(sn, 0);
4211 jmplen = (op == JSOP_TABLESWITCH) ? JUMP_OFFSET_LEN
4212 : JUMPX_OFFSET_LEN;
4213 pc2 = pc;
4214 off = GetJumpOffset(pc, pc2);
4215 pc2 += jmplen;
4216 low = GET_JUMP_OFFSET(pc2);
4217 pc2 += JUMP_OFFSET_LEN;
4218 high = GET_JUMP_OFFSET(pc2);
4219 pc2 += JUMP_OFFSET_LEN;
4221 n = high - low + 1;
4222 if (n == 0) {
4223 table = NULL;
4224 j = 0;
4225 ok = JS_TRUE;
4226 } else {
4227 table = (TableEntry *)
4228 cx->malloc((size_t)n * sizeof *table);
4229 if (!table)
4230 return NULL;
4231 for (i = j = 0; i < n; i++) {
4232 table[j].label = NULL;
4233 off2 = GetJumpOffset(pc, pc2);
4234 if (off2) {
4235 sn = js_GetSrcNote(jp->script, pc2);
4236 if (sn) {
4237 LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL);
4238 GET_SOURCE_NOTE_ATOM(sn, table[j].label);
4240 table[j].key = INT_TO_JSVAL(low + i);
4241 table[j].offset = off2;
4242 table[j].order = j;
4243 j++;
4245 pc2 += jmplen;
4247 tmp = (TableEntry *)
4248 cx->malloc((size_t)j * sizeof *table);
4249 if (tmp) {
4250 VOUCH_DOES_NOT_REQUIRE_STACK();
4251 ok = js_MergeSort(table, (size_t)j, sizeof(TableEntry),
4252 CompareOffsets, NULL, tmp);
4253 cx->free(tmp);
4254 } else {
4255 ok = JS_FALSE;
4259 if (ok) {
4260 ok = DecompileSwitch(ss, table, (uintN)j, pc, len, off,
4261 JS_FALSE);
4263 cx->free(table);
4264 if (!ok)
4265 return NULL;
4266 todo = -2;
4267 break;
4270 case JSOP_LOOKUPSWITCH:
4271 case JSOP_LOOKUPSWITCHX:
4273 ptrdiff_t jmplen, off, off2;
4274 jsatomid npairs, k;
4275 TableEntry *table;
4277 sn = js_GetSrcNote(jp->script, pc);
4278 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
4279 len = js_GetSrcNoteOffset(sn, 0);
4280 jmplen = (op == JSOP_LOOKUPSWITCH) ? JUMP_OFFSET_LEN
4281 : JUMPX_OFFSET_LEN;
4282 pc2 = pc;
4283 off = GetJumpOffset(pc, pc2);
4284 pc2 += jmplen;
4285 npairs = GET_UINT16(pc2);
4286 pc2 += UINT16_LEN;
4288 table = (TableEntry *)
4289 cx->malloc((size_t)npairs * sizeof *table);
4290 if (!table)
4291 return NULL;
4292 for (k = 0; k < npairs; k++) {
4293 sn = js_GetSrcNote(jp->script, pc2);
4294 if (sn) {
4295 LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL);
4296 GET_SOURCE_NOTE_ATOM(sn, table[k].label);
4297 } else {
4298 table[k].label = NULL;
4300 JS_GET_SCRIPT_ATOM(jp->script, pc, GET_INDEX(pc2), atom);
4301 pc2 += INDEX_LEN;
4302 off2 = GetJumpOffset(pc, pc2);
4303 pc2 += jmplen;
4304 table[k].key = ATOM_KEY(atom);
4305 table[k].offset = off2;
4308 ok = DecompileSwitch(ss, table, (uintN)npairs, pc, len, off,
4309 JS_FALSE);
4310 cx->free(table);
4311 if (!ok)
4312 return NULL;
4313 todo = -2;
4314 break;
4317 case JSOP_CONDSWITCH:
4319 ptrdiff_t off, off2, caseOff;
4320 jsint ncases;
4321 TableEntry *table;
4323 sn = js_GetSrcNote(jp->script, pc);
4324 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
4325 len = js_GetSrcNoteOffset(sn, 0);
4326 off = js_GetSrcNoteOffset(sn, 1);
4329 * Count the cases using offsets from switch to first case,
4330 * and case to case, stored in srcnote immediates.
4332 pc2 = pc;
4333 off2 = off;
4334 for (ncases = 0; off2 != 0; ncases++) {
4335 pc2 += off2;
4336 LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT ||
4337 *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX);
4338 if (*pc2 == JSOP_DEFAULT || *pc2 == JSOP_DEFAULTX) {
4339 /* End of cases, but count default as a case. */
4340 off2 = 0;
4341 } else {
4342 sn = js_GetSrcNote(jp->script, pc2);
4343 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
4344 off2 = js_GetSrcNoteOffset(sn, 0);
4349 * Allocate table and rescan the cases using their srcnotes,
4350 * stashing each case's delta from switch top in table[i].key,
4351 * and the distance to its statements in table[i].offset.
4353 table = (TableEntry *)
4354 cx->malloc((size_t)ncases * sizeof *table);
4355 if (!table)
4356 return NULL;
4357 pc2 = pc;
4358 off2 = off;
4359 for (i = 0; i < ncases; i++) {
4360 pc2 += off2;
4361 LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT ||
4362 *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX);
4363 caseOff = pc2 - pc;
4364 table[i].key = INT_TO_JSVAL((jsint) caseOff);
4365 table[i].offset = caseOff + GetJumpOffset(pc2, pc2);
4366 if (*pc2 == JSOP_CASE || *pc2 == JSOP_CASEX) {
4367 sn = js_GetSrcNote(jp->script, pc2);
4368 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
4369 off2 = js_GetSrcNoteOffset(sn, 0);
4374 * Find offset of default code by fetching the default offset
4375 * from the end of table. JSOP_CONDSWITCH always has a default
4376 * case at the end.
4378 off = JSVAL_TO_INT(table[ncases-1].key);
4379 pc2 = pc + off;
4380 off += GetJumpOffset(pc2, pc2);
4382 ok = DecompileSwitch(ss, table, (uintN)ncases, pc, len, off,
4383 JS_TRUE);
4384 cx->free(table);
4385 if (!ok)
4386 return NULL;
4387 todo = -2;
4388 break;
4391 case JSOP_CASE:
4392 case JSOP_CASEX:
4394 lval = POP_STR();
4395 if (!lval)
4396 return NULL;
4397 js_printf(jp, "\tcase %s:\n", lval);
4398 todo = -2;
4399 break;
4402 case JSOP_DEFFUN:
4403 case JSOP_DEFFUN_FC:
4404 case JSOP_DEFFUN_DBGFC:
4405 LOAD_FUNCTION(0);
4406 todo = -2;
4407 goto do_function;
4408 break;
4410 case JSOP_TRAP:
4411 saveop = op = JS_GetTrapOpcode(cx, jp->script, pc);
4412 *pc = op;
4413 cs = &js_CodeSpec[op];
4414 len = cs->length;
4415 DECOMPILE_CODE(pc, len);
4416 *pc = JSOP_TRAP;
4417 todo = -2;
4418 break;
4420 case JSOP_HOLE:
4421 todo = SprintPut(&ss->sprinter, "", 0);
4422 break;
4424 case JSOP_NEWARRAY:
4425 argc = GET_UINT16(pc);
4426 LOCAL_ASSERT(ss->top >= (uintN) argc);
4427 if (argc == 0) {
4428 todo = SprintCString(&ss->sprinter, "[]");
4429 break;
4432 argv = (char **) cx->malloc(size_t(argc) * sizeof *argv);
4433 if (!argv)
4434 return NULL;
4436 op = JSOP_SETNAME;
4437 ok = JS_TRUE;
4438 i = argc;
4439 while (i > 0)
4440 argv[--i] = JS_strdup(cx, POP_STR());
4442 todo = SprintCString(&ss->sprinter, "[");
4443 if (todo < 0)
4444 break;
4446 for (i = 0; i < argc; i++) {
4447 if (!argv[i] ||
4448 Sprint(&ss->sprinter, ss_format,
4449 argv[i], (i < argc - 1) ? ", " : "") < 0) {
4450 ok = JS_FALSE;
4451 break;
4455 for (i = 0; i < argc; i++)
4456 cx->free(argv[i]);
4457 cx->free(argv);
4458 if (!ok)
4459 return NULL;
4461 sn = js_GetSrcNote(jp->script, pc);
4462 if (sn && SN_TYPE(sn) == SRC_CONTINUE && SprintCString(&ss->sprinter, ", ") < 0)
4463 return NULL;
4464 if (SprintCString(&ss->sprinter, "]") < 0)
4465 return NULL;
4466 break;
4468 case JSOP_NEWINIT:
4470 i = GET_INT8(pc);
4471 LOCAL_ASSERT(i == JSProto_Array || i == JSProto_Object);
4473 todo = ss->sprinter.offset;
4474 #if JS_HAS_SHARP_VARS
4475 op = (JSOp)pc[len];
4476 if (op == JSOP_SHARPINIT)
4477 op = (JSOp)pc[len += JSOP_SHARPINIT_LENGTH];
4478 if (op == JSOP_DEFSHARP) {
4479 pc += len;
4480 cs = &js_CodeSpec[op];
4481 len = cs->length;
4482 if (Sprint(&ss->sprinter, "#%u=",
4483 (unsigned) (jsint) GET_UINT16(pc + UINT16_LEN))
4484 < 0) {
4485 return NULL;
4488 #endif /* JS_HAS_SHARP_VARS */
4489 if (i == JSProto_Array) {
4490 ++ss->inArrayInit;
4491 if (SprintCString(&ss->sprinter, "[") < 0)
4492 return NULL;
4493 } else {
4494 if (SprintCString(&ss->sprinter, "{") < 0)
4495 return NULL;
4497 break;
4500 case JSOP_ENDINIT:
4502 JSBool inArray;
4504 op = JSOP_NOP; /* turn off parens */
4505 rval = POP_STR();
4506 sn = js_GetSrcNote(jp->script, pc);
4508 /* Skip any #n= prefix to find the opening bracket. */
4509 for (xval = rval; *xval != '[' && *xval != '{'; xval++)
4510 continue;
4511 inArray = (*xval == '[');
4512 if (inArray)
4513 --ss->inArrayInit;
4514 todo = Sprint(&ss->sprinter, "%s%s%c",
4515 rval,
4516 (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "",
4517 inArray ? ']' : '}');
4518 break;
4522 JSBool isFirst;
4523 const char *maybeComma;
4525 case JSOP_INITELEM:
4526 isFirst = (ss->opcodes[ss->top - 3] == JSOP_NEWINIT);
4528 /* Turn off most parens. */
4529 op = JSOP_SETNAME;
4530 rval = POP_STR();
4532 /* Turn off all parens for xval and lval, which we control. */
4533 op = JSOP_NOP;
4534 xval = POP_STR();
4535 lval = POP_STR();
4536 sn = js_GetSrcNote(jp->script, pc);
4538 if (sn && SN_TYPE(sn) == SRC_INITPROP) {
4539 atom = NULL;
4540 goto do_initprop;
4542 maybeComma = isFirst ? "" : ", ";
4543 todo = Sprint(&ss->sprinter, sss_format,
4544 lval,
4545 maybeComma,
4546 rval);
4547 break;
4549 case JSOP_INITPROP:
4550 case JSOP_INITMETHOD:
4551 LOAD_ATOM(0);
4552 xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
4553 jschar(ATOM_IS_IDENTIFIER(atom) ? 0 : '\''));
4554 if (!xval)
4555 return NULL;
4556 isFirst = (ss->opcodes[ss->top - 2] == JSOP_NEWINIT);
4557 rval = POP_STR();
4558 lval = POP_STR();
4559 /* fall through */
4561 do_initprop:
4562 maybeComma = isFirst ? "" : ", ";
4563 if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) {
4564 const char *end = rval + strlen(rval);
4566 if (*rval == '(')
4567 ++rval, --end;
4568 LOCAL_ASSERT(strncmp(rval, js_function_str, 8) == 0);
4569 LOCAL_ASSERT(rval[8] == ' ');
4570 rval += 8 + 1;
4571 LOCAL_ASSERT(*end ? *end == ')' : end[-1] == '}');
4572 todo = Sprint(&ss->sprinter, "%s%s%s %s%s%.*s",
4573 lval,
4574 maybeComma,
4575 (lastop == JSOP_GETTER)
4576 ? js_get_str : js_set_str,
4577 xval,
4578 (rval[0] != '(') ? " " : "",
4579 end - rval, rval);
4580 } else {
4581 todo = Sprint(&ss->sprinter, "%s%s%s: %s",
4582 lval, maybeComma, xval, rval);
4584 break;
4587 #if JS_HAS_SHARP_VARS
4588 case JSOP_DEFSHARP:
4589 i = (jsint) GET_UINT16(pc + UINT16_LEN);
4590 rval = POP_STR();
4591 todo = Sprint(&ss->sprinter, "#%u=%s", (unsigned) i, rval);
4592 break;
4594 case JSOP_USESHARP:
4595 i = (jsint) GET_UINT16(pc + UINT16_LEN);
4596 todo = Sprint(&ss->sprinter, "#%u#", (unsigned) i);
4597 break;
4598 #endif /* JS_HAS_SHARP_VARS */
4600 #if JS_HAS_DEBUGGER_KEYWORD
4601 case JSOP_DEBUGGER:
4602 js_printf(jp, "\tdebugger;\n");
4603 todo = -2;
4604 break;
4605 #endif /* JS_HAS_DEBUGGER_KEYWORD */
4607 #if JS_HAS_XML_SUPPORT
4608 case JSOP_STARTXML:
4609 case JSOP_STARTXMLEXPR:
4610 inXML = op == JSOP_STARTXML;
4611 todo = -2;
4612 break;
4614 case JSOP_DEFXMLNS:
4615 rval = POP_STR();
4616 js_printf(jp, "\t%s %s %s = %s;\n",
4617 js_default_str, js_xml_str, js_namespace_str, rval);
4618 todo = -2;
4619 break;
4621 case JSOP_ANYNAME:
4622 if (pc[JSOP_ANYNAME_LENGTH] == JSOP_TOATTRNAME) {
4623 len += JSOP_TOATTRNAME_LENGTH;
4624 todo = SprintPut(&ss->sprinter, "@*", 2);
4625 } else {
4626 todo = SprintPut(&ss->sprinter, "*", 1);
4628 break;
4630 case JSOP_QNAMEPART:
4631 LOAD_ATOM(0);
4632 if (pc[JSOP_QNAMEPART_LENGTH] == JSOP_TOATTRNAME) {
4633 saveop = JSOP_TOATTRNAME;
4634 len += JSOP_TOATTRNAME_LENGTH;
4635 lval = "@";
4636 goto do_qname;
4638 goto do_name;
4640 case JSOP_QNAMECONST:
4641 LOAD_ATOM(0);
4642 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
4643 if (!rval)
4644 return NULL;
4645 RETRACT(&ss->sprinter, rval);
4646 lval = POP_STR();
4647 todo = Sprint(&ss->sprinter, "%s::%s", lval, rval);
4648 break;
4650 case JSOP_QNAME:
4651 rval = POP_STR();
4652 lval = POP_STR();
4653 todo = Sprint(&ss->sprinter, "%s::[%s]", lval, rval);
4654 break;
4656 case JSOP_TOATTRNAME:
4657 op = JSOP_NOP; /* turn off parens */
4658 rval = POP_STR();
4659 todo = Sprint(&ss->sprinter, "@[%s]", rval);
4660 break;
4662 case JSOP_TOATTRVAL:
4663 todo = -2;
4664 break;
4666 case JSOP_ADDATTRNAME:
4667 rval = POP_STR();
4668 lval = POP_STR();
4669 todo = Sprint(&ss->sprinter, "%s %s", lval, rval);
4670 /* This gets reset by all XML tag expressions. */
4671 quoteAttr = JS_TRUE;
4672 break;
4674 case JSOP_ADDATTRVAL:
4675 rval = POP_STR();
4676 lval = POP_STR();
4677 if (quoteAttr)
4678 todo = Sprint(&ss->sprinter, "%s=\"%s\"", lval, rval);
4679 else
4680 todo = Sprint(&ss->sprinter, "%s=%s", lval, rval);
4681 break;
4683 case JSOP_BINDXMLNAME:
4684 /* Leave the name stacked and push a dummy string. */
4685 todo = Sprint(&ss->sprinter, "");
4686 break;
4688 case JSOP_SETXMLNAME:
4689 /* Pop the r.h.s., the dummy string, and the name. */
4690 rval = POP_STR();
4691 (void) PopOff(ss, op);
4692 lval = POP_STR();
4693 goto do_setlval;
4695 case JSOP_XMLELTEXPR:
4696 case JSOP_XMLTAGEXPR:
4697 todo = Sprint(&ss->sprinter, "{%s}", POP_STR());
4698 inXML = JS_TRUE;
4699 /* If we're an attribute value, we shouldn't quote this. */
4700 quoteAttr = JS_FALSE;
4701 break;
4703 case JSOP_TOXMLLIST:
4704 op = JSOP_NOP; /* turn off parens */
4705 todo = Sprint(&ss->sprinter, "<>%s</>", POP_STR());
4706 inXML = JS_FALSE;
4707 break;
4709 case JSOP_TOXML:
4710 case JSOP_CALLXMLNAME:
4711 case JSOP_XMLNAME:
4712 case JSOP_FILTER:
4713 /* These ops indicate the end of XML expressions. */
4714 inXML = JS_FALSE;
4715 todo = -2;
4716 break;
4718 case JSOP_ENDFILTER:
4719 rval = POP_STR();
4720 PROPAGATE_CALLNESS();
4721 lval = POP_STR();
4722 todo = Sprint(&ss->sprinter, "%s.(%s)", lval, rval);
4723 break;
4725 case JSOP_DESCENDANTS:
4726 rval = POP_STR();
4727 PROPAGATE_CALLNESS();
4728 lval = POP_STR();
4729 todo = Sprint(&ss->sprinter, "%s..%s", lval, rval);
4730 break;
4732 case JSOP_XMLOBJECT:
4733 LOAD_OBJECT(0);
4734 todo = Sprint(&ss->sprinter, "<xml address='%p'>", obj);
4735 break;
4737 case JSOP_XMLCDATA:
4738 LOAD_ATOM(0);
4739 todo = SprintPut(&ss->sprinter, "<![CDATA[", 9);
4740 if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
4741 DONT_ESCAPE))
4742 return NULL;
4743 SprintPut(&ss->sprinter, "]]>", 3);
4744 break;
4746 case JSOP_XMLCOMMENT:
4747 LOAD_ATOM(0);
4748 todo = SprintPut(&ss->sprinter, "<!--", 4);
4749 if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
4750 DONT_ESCAPE))
4751 return NULL;
4752 SprintPut(&ss->sprinter, "-->", 3);
4753 break;
4755 case JSOP_XMLPI:
4756 LOAD_ATOM(0);
4757 rval = JS_strdup(cx, POP_STR());
4758 if (!rval)
4759 return NULL;
4760 todo = SprintPut(&ss->sprinter, "<?", 2);
4761 ok = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0) &&
4762 (*rval == '\0' ||
4763 (SprintPut(&ss->sprinter, " ", 1) >= 0 &&
4764 SprintCString(&ss->sprinter, rval)));
4765 cx->free((char *)rval);
4766 if (!ok)
4767 return NULL;
4768 SprintPut(&ss->sprinter, "?>", 2);
4769 break;
4771 case JSOP_GETFUNNS:
4772 todo = SprintPut(&ss->sprinter, js_function_str, 8);
4773 break;
4774 #endif /* JS_HAS_XML_SUPPORT */
4776 default:
4777 todo = -2;
4778 break;
4782 if (todo < 0) {
4783 /* -2 means "don't push", -1 means reported error. */
4784 if (todo == -1)
4785 return NULL;
4786 } else {
4787 if (!PushOff(ss, todo, saveop))
4788 return NULL;
4791 if (cs->format & JOF_CALLOP) {
4792 todo = Sprint(&ss->sprinter, "");
4793 if (todo < 0 || !PushOff(ss, todo, saveop))
4794 return NULL;
4797 pc += len;
4801 * Undefine local macros.
4803 #undef inXML
4804 #undef DECOMPILE_CODE
4805 #undef NEXT_OP
4806 #undef TOP_STR
4807 #undef POP_STR
4808 #undef POP_STR_PREC
4809 #undef LOCAL_ASSERT
4810 #undef ATOM_IS_IDENTIFIER
4811 #undef GET_QUOTE_AND_FMT
4812 #undef GET_ATOM_QUOTE_AND_FMT
4814 return pc;
4817 static JSBool
4818 DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len,
4819 uintN pcdepth)
4821 uintN depth, i;
4822 SprintStack ss;
4823 JSContext *cx;
4824 void *mark;
4825 JSBool ok;
4826 JSScript *oldscript;
4827 jsbytecode *oldcode, *oldmain, *code;
4828 char *last;
4830 depth = StackDepth(script);
4831 JS_ASSERT(pcdepth <= depth);
4833 /* Initialize a sprinter for use with the offset stack. */
4834 cx = jp->sprinter.context;
4835 mark = cx->tempPool.getMark();
4836 ok = InitSprintStack(cx, &ss, jp, depth);
4837 if (!ok)
4838 goto out;
4841 * If we are called from js_DecompileValueGenerator with a portion of
4842 * script's bytecode that starts with a non-zero model stack depth given
4843 * by pcdepth, attempt to initialize the missing string offsets in ss to
4844 * |spindex| negative indexes from fp->sp for the activation fp in which
4845 * the error arose.
4847 * See js_DecompileValueGenerator for how its |spindex| parameter is used,
4848 * and see also GetOff, which makes use of the ss.offsets[i] < -1 that are
4849 * potentially stored below.
4851 ss.top = pcdepth;
4852 if (pcdepth != 0) {
4853 for (i = 0; i < pcdepth; i++) {
4854 ss.offsets[i] = -2 - (ptrdiff_t)i;
4855 ss.opcodes[i] = *jp->pcstack[i];
4859 /* Call recursive subroutine to do the hard work. */
4860 oldscript = jp->script;
4861 jp->script = script;
4862 oldcode = jp->script->code;
4863 oldmain = jp->script->main;
4864 code = js_UntrapScriptCode(cx, jp->script);
4865 if (code != oldcode) {
4866 jp->script->code = code;
4867 jp->script->main = code + (oldmain - oldcode);
4868 pc = code + (pc - oldcode);
4871 ok = Decompile(&ss, pc, len, JSOP_NOP) != NULL;
4872 if (code != oldcode) {
4873 cx->free(jp->script->code);
4874 jp->script->code = oldcode;
4875 jp->script->main = oldmain;
4877 jp->script = oldscript;
4879 /* If the given code didn't empty the stack, do it now. */
4880 if (ok && ss.top) {
4881 do {
4882 last = OFF2STR(&ss.sprinter, PopOff(&ss, JSOP_POP));
4883 } while (ss.top > pcdepth);
4884 js_printf(jp, "%s", last);
4887 out:
4888 /* Free all temporary stuff allocated under this call. */
4889 cx->tempPool.release(mark);
4890 return ok;
4893 JSBool
4894 js_DecompileScript(JSPrinter *jp, JSScript *script)
4896 return DecompileCode(jp, script, script->code, (uintN)script->length, 0);
4899 JSString *
4900 js_DecompileToString(JSContext *cx, const char *name, JSFunction *fun,
4901 uintN indent, JSBool pretty, JSBool grouped, JSBool strict,
4902 JSDecompilerPtr decompiler)
4904 JSPrinter *jp;
4905 JSString *str;
4907 jp = js_NewPrinter(cx, name, fun, indent, pretty, grouped, strict);
4908 if (!jp)
4909 return NULL;
4910 if (decompiler(jp))
4911 str = js_GetPrinterOutput(jp);
4912 else
4913 str = NULL;
4914 js_DestroyPrinter(jp);
4915 return str;
4918 static const char native_code_str[] = "\t[native code]\n";
4920 JSBool
4921 js_DecompileFunctionBody(JSPrinter *jp)
4923 JSScript *script;
4925 JS_ASSERT(jp->fun);
4926 JS_ASSERT(!jp->script);
4927 if (!FUN_INTERPRETED(jp->fun)) {
4928 js_printf(jp, native_code_str);
4929 return JS_TRUE;
4932 script = jp->fun->u.i.script;
4933 return DecompileCode(jp, script, script->code, (uintN)script->length, 0);
4936 JSBool
4937 js_DecompileFunction(JSPrinter *jp)
4939 JSFunction *fun;
4940 uintN i;
4941 JSAtom *param;
4942 jsbytecode *pc, *endpc;
4943 ptrdiff_t len;
4944 JSBool ok;
4946 fun = jp->fun;
4947 JS_ASSERT(fun);
4948 JS_ASSERT(!jp->script);
4951 * If pretty, conform to ECMA-262 Edition 3, 15.3.4.2, by decompiling a
4952 * FunctionDeclaration. Otherwise, check the JSFUN_LAMBDA flag and force
4953 * an expression by parenthesizing.
4955 if (jp->pretty) {
4956 js_printf(jp, "\t");
4957 } else {
4958 if (!jp->grouped && (fun->flags & JSFUN_LAMBDA))
4959 js_puts(jp, "(");
4961 if (JSFUN_GETTER_TEST(fun->flags))
4962 js_printf(jp, "%s ", js_getter_str);
4963 else if (JSFUN_SETTER_TEST(fun->flags))
4964 js_printf(jp, "%s ", js_setter_str);
4966 js_printf(jp, "%s ", js_function_str);
4967 if (fun->atom && !QuoteString(&jp->sprinter, ATOM_TO_STRING(fun->atom), 0))
4968 return JS_FALSE;
4969 js_puts(jp, "(");
4971 if (!FUN_INTERPRETED(fun)) {
4972 js_printf(jp, ") {\n");
4973 jp->indent += 4;
4974 js_printf(jp, native_code_str);
4975 jp->indent -= 4;
4976 js_printf(jp, "\t}");
4977 } else {
4978 JSScript *script = fun->u.i.script;
4979 #if JS_HAS_DESTRUCTURING
4980 SprintStack ss;
4981 void *mark;
4982 #endif
4984 /* Print the parameters. */
4985 pc = script->main;
4986 endpc = pc + script->length;
4987 ok = JS_TRUE;
4989 /* Skip trace hint if it appears here. */
4990 #if JS_HAS_GENERATORS
4991 if (js_GetOpcode(jp->sprinter.context, script, script->code) != JSOP_GENERATOR)
4992 #endif
4994 JSOp op = js_GetOpcode(jp->sprinter.context, script, pc);
4995 if (op == JSOP_TRACE || op == JSOP_NOP) {
4996 JS_STATIC_ASSERT(JSOP_TRACE_LENGTH == JSOP_NOP_LENGTH);
4997 pc += JSOP_TRACE_LENGTH;
4998 } else {
4999 JS_ASSERT(op == JSOP_STOP); /* empty script singleton */
5003 #if JS_HAS_DESTRUCTURING
5004 ss.printer = NULL;
5005 jp->script = script;
5006 mark = jp->sprinter.context->tempPool.getMark();
5007 #endif
5009 for (i = 0; i < fun->nargs; i++) {
5010 if (i > 0)
5011 js_puts(jp, ", ");
5013 param = GetArgOrVarAtom(jp, i);
5015 #if JS_HAS_DESTRUCTURING
5016 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, JS_FALSE)
5018 if (!param) {
5019 ptrdiff_t todo;
5020 const char *lval;
5022 LOCAL_ASSERT(*pc == JSOP_GETARG);
5023 pc += JSOP_GETARG_LENGTH;
5024 LOCAL_ASSERT(*pc == JSOP_DUP);
5025 if (!ss.printer) {
5026 ok = InitSprintStack(jp->sprinter.context, &ss, jp, StackDepth(script));
5027 if (!ok)
5028 break;
5030 pc = DecompileDestructuring(&ss, pc, endpc);
5031 if (!pc) {
5032 ok = JS_FALSE;
5033 break;
5035 LOCAL_ASSERT(*pc == JSOP_POP);
5036 pc += JSOP_POP_LENGTH;
5037 lval = PopStr(&ss, JSOP_NOP);
5038 todo = SprintCString(&jp->sprinter, lval);
5039 if (todo < 0) {
5040 ok = JS_FALSE;
5041 break;
5043 continue;
5046 #undef LOCAL_ASSERT
5047 #endif
5049 if (!QuoteString(&jp->sprinter, ATOM_TO_STRING(param), 0)) {
5050 ok = JS_FALSE;
5051 break;
5055 #if JS_HAS_DESTRUCTURING
5056 jp->script = NULL;
5057 jp->sprinter.context->tempPool.release(mark);
5058 #endif
5059 if (!ok)
5060 return JS_FALSE;
5061 if (fun->flags & JSFUN_EXPR_CLOSURE) {
5062 js_printf(jp, ") ");
5063 if (fun->u.i.script->strictModeCode && !jp->strict) {
5065 * We have no syntax for strict function expressions;
5066 * at least give a hint.
5068 js_printf(jp, "\t/* use strict */ \n");
5069 jp->strict = true;
5072 } else {
5073 js_printf(jp, ") {\n");
5074 jp->indent += 4;
5075 if (fun->u.i.script->strictModeCode && !jp->strict) {
5076 js_printf(jp, "\t'use strict';\n");
5077 jp->strict = true;
5081 len = script->code + script->length - pc;
5082 ok = DecompileCode(jp, script, pc, (uintN)len, 0);
5083 if (!ok)
5084 return JS_FALSE;
5086 if (!(fun->flags & JSFUN_EXPR_CLOSURE)) {
5087 jp->indent -= 4;
5088 js_printf(jp, "\t}");
5092 if (!jp->pretty && !jp->grouped && (fun->flags & JSFUN_LAMBDA))
5093 js_puts(jp, ")");
5095 return JS_TRUE;
5098 char *
5099 js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
5100 JSString *fallback)
5102 JSStackFrame *fp;
5103 jsbytecode *pc;
5104 JSScript *script;
5105 JSFrameRegs *regs;
5106 intN pcdepth;
5107 jsval *sp, *stackBase;
5108 char *name;
5110 JS_ASSERT(spindex < 0 ||
5111 spindex == JSDVG_IGNORE_STACK ||
5112 spindex == JSDVG_SEARCH_STACK);
5114 fp = js_GetScriptedCaller(cx, NULL);
5115 if (!fp || !fp->regs || !fp->regs->sp)
5116 goto do_fallback;
5118 script = fp->script;
5119 regs = fp->regs;
5120 pc = fp->imacpc ? fp->imacpc : regs->pc;
5121 if (pc < script->main || script->code + script->length <= pc) {
5122 JS_NOT_REACHED("bug");
5123 goto do_fallback;
5126 if (spindex != JSDVG_IGNORE_STACK) {
5127 jsbytecode **pcstack;
5130 * Prepare computing pcstack containing pointers to opcodes that
5131 * populated interpreter's stack with its current content.
5133 pcstack = (jsbytecode **)
5134 cx->malloc(StackDepth(script) * sizeof *pcstack);
5135 if (!pcstack)
5136 return NULL;
5137 pcdepth = ReconstructPCStack(cx, script, pc, pcstack);
5138 if (pcdepth < 0)
5139 goto release_pcstack;
5141 if (spindex != JSDVG_SEARCH_STACK) {
5142 JS_ASSERT(spindex < 0);
5143 pcdepth += spindex;
5144 if (pcdepth < 0)
5145 goto release_pcstack;
5146 pc = pcstack[pcdepth];
5147 } else {
5149 * We search from fp->sp to base to find the most recently
5150 * calculated value matching v under assumption that it is
5151 * it that caused exception, see bug 328664.
5153 stackBase = StackBase(fp);
5154 sp = regs->sp;
5155 do {
5156 if (sp == stackBase) {
5157 pcdepth = -1;
5158 goto release_pcstack;
5160 } while (*--sp != v);
5163 * The value may have come from beyond stackBase + pcdepth,
5164 * meaning that it came from a temporary slot that the
5165 * interpreter uses for GC roots or when JSOP_APPLY extended
5166 * the stack to fit the argument array elements. Only update pc
5167 * if beneath stackBase + pcdepth; otherwise blame existing
5168 * (current) PC.
5170 if (sp < stackBase + pcdepth)
5171 pc = pcstack[sp - stackBase];
5174 release_pcstack:
5175 cx->free(pcstack);
5176 if (pcdepth < 0)
5177 goto do_fallback;
5181 jsbytecode* savepc = regs->pc;
5182 jsbytecode* imacpc = fp->imacpc;
5183 if (imacpc) {
5184 regs->pc = imacpc;
5185 fp->imacpc = NULL;
5189 * FIXME: bug 489843. Stack reconstruction may have returned a pc
5190 * value *inside* an imacro; this would confuse the decompiler.
5192 if (imacpc && size_t(pc - script->code) >= script->length)
5193 name = FAILED_EXPRESSION_DECOMPILER;
5194 else
5195 name = DecompileExpression(cx, script, fp->fun, pc);
5197 if (imacpc) {
5198 regs->pc = savepc;
5199 fp->imacpc = imacpc;
5202 if (name != FAILED_EXPRESSION_DECOMPILER)
5203 return name;
5205 do_fallback:
5206 if (!fallback) {
5207 fallback = js_ValueToSource(cx, v);
5208 if (!fallback)
5209 return NULL;
5211 return js_DeflateString(cx, fallback->chars(), fallback->length());
5214 static char *
5215 DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun,
5216 jsbytecode *pc)
5218 jsbytecode *code, *oldcode, *oldmain;
5219 JSOp op;
5220 const JSCodeSpec *cs;
5221 jsbytecode *begin, *end;
5222 jssrcnote *sn;
5223 ptrdiff_t len;
5224 jsbytecode **pcstack;
5225 intN pcdepth;
5226 JSPrinter *jp;
5227 char *name;
5229 JS_ASSERT(script->main <= pc && pc < script->code + script->length);
5231 pcstack = NULL;
5232 oldcode = script->code;
5233 oldmain = script->main;
5235 MUST_FLOW_THROUGH("out");
5236 code = js_UntrapScriptCode(cx, script);
5237 if (code != oldcode) {
5238 script->code = code;
5239 script->main = code + (oldmain - oldcode);
5240 pc = code + (pc - oldcode);
5243 op = (JSOp) *pc;
5245 /* None of these stack-writing ops generates novel values. */
5246 JS_ASSERT(op != JSOP_CASE && op != JSOP_CASEX &&
5247 op != JSOP_DUP && op != JSOP_DUP2);
5249 /* JSOP_PUSH is used to generate undefined for group assignment holes. */
5250 if (op == JSOP_PUSH) {
5251 name = JS_strdup(cx, js_undefined_str);
5252 goto out;
5256 * |this| could convert to a very long object initialiser, so cite it by
5257 * its keyword name instead.
5259 if (op == JSOP_THIS) {
5260 name = JS_strdup(cx, js_this_str);
5261 goto out;
5265 * JSOP_BINDNAME is special: it generates a value, the base object of a
5266 * reference. But if it is the generating op for a diagnostic produced by
5267 * js_DecompileValueGenerator, the name being bound is irrelevant. Just
5268 * fall back to the base object.
5270 if (op == JSOP_BINDNAME) {
5271 name = FAILED_EXPRESSION_DECOMPILER;
5272 goto out;
5275 /* NAME ops are self-contained, others require left or right context. */
5276 cs = &js_CodeSpec[op];
5277 begin = pc;
5278 end = pc + cs->length;
5279 switch (JOF_MODE(cs->format)) {
5280 case JOF_PROP:
5281 case JOF_ELEM:
5282 case JOF_XMLNAME:
5283 case 0:
5284 sn = js_GetSrcNote(script, pc);
5285 if (!sn) {
5286 name = FAILED_EXPRESSION_DECOMPILER;
5287 goto out;
5289 switch (SN_TYPE(sn)) {
5290 case SRC_PCBASE:
5291 begin -= js_GetSrcNoteOffset(sn, 0);
5292 break;
5293 case SRC_PCDELTA:
5294 end = begin + js_GetSrcNoteOffset(sn, 0);
5295 begin += cs->length;
5296 break;
5297 default:
5298 name = FAILED_EXPRESSION_DECOMPILER;
5299 goto out;
5301 break;
5302 default:;
5304 len = end - begin;
5305 if (len <= 0) {
5306 name = FAILED_EXPRESSION_DECOMPILER;
5307 goto out;
5310 pcstack = (jsbytecode **)
5311 cx->malloc(StackDepth(script) * sizeof *pcstack);
5312 if (!pcstack) {
5313 name = NULL;
5314 goto out;
5317 MUST_FLOW_THROUGH("out");
5318 pcdepth = ReconstructPCStack(cx, script, begin, pcstack);
5319 if (pcdepth < 0) {
5320 name = FAILED_EXPRESSION_DECOMPILER;
5321 goto out;
5324 name = NULL;
5325 jp = js_NewPrinter(cx, "js_DecompileValueGenerator", fun, 0,
5326 false, false, false);
5327 if (jp) {
5328 jp->dvgfence = end;
5329 jp->pcstack = pcstack;
5330 if (DecompileCode(jp, script, begin, (uintN) len, (uintN) pcdepth)) {
5331 name = (jp->sprinter.base) ? jp->sprinter.base : (char *) "";
5332 name = JS_strdup(cx, name);
5334 js_DestroyPrinter(jp);
5337 out:
5338 if (code != oldcode) {
5339 cx->free(script->code);
5340 script->code = oldcode;
5341 script->main = oldmain;
5344 cx->free(pcstack);
5345 return name;
5348 uintN
5349 js_ReconstructStackDepth(JSContext *cx, JSScript *script, jsbytecode *pc)
5351 return ReconstructPCStack(cx, script, pc, NULL);
5354 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1);
5356 static intN
5357 SimulateOp(JSContext *cx, JSScript *script, JSOp op, const JSCodeSpec *cs,
5358 jsbytecode *pc, jsbytecode **pcstack, uintN &pcdepth)
5360 uintN nuses = js_GetStackUses(cs, op, pc);
5361 uintN ndefs = js_GetStackDefs(cx, cs, op, script, pc);
5362 LOCAL_ASSERT(pcdepth >= nuses);
5363 pcdepth -= nuses;
5364 LOCAL_ASSERT(pcdepth + ndefs <= StackDepth(script));
5367 * Fill the slots that the opcode defines withs its pc unless it just
5368 * reshuffles the stack. In the latter case we want to preserve the
5369 * opcode that generated the original value.
5371 switch (op) {
5372 default:
5373 if (pcstack) {
5374 for (uintN i = 0; i != ndefs; ++i)
5375 pcstack[pcdepth + i] = pc;
5377 break;
5379 case JSOP_CASE:
5380 case JSOP_CASEX:
5381 /* Keep the switch value. */
5382 JS_ASSERT(ndefs == 1);
5383 break;
5385 case JSOP_DUP:
5386 JS_ASSERT(ndefs == 2);
5387 if (pcstack)
5388 pcstack[pcdepth + 1] = pcstack[pcdepth];
5389 break;
5391 case JSOP_DUP2:
5392 JS_ASSERT(ndefs == 4);
5393 if (pcstack) {
5394 pcstack[pcdepth + 2] = pcstack[pcdepth];
5395 pcstack[pcdepth + 3] = pcstack[pcdepth + 1];
5397 break;
5399 case JSOP_SWAP:
5400 JS_ASSERT(ndefs == 2);
5401 if (pcstack) {
5402 jsbytecode *tmp = pcstack[pcdepth + 1];
5403 pcstack[pcdepth + 1] = pcstack[pcdepth];
5404 pcstack[pcdepth] = tmp;
5406 break;
5408 pcdepth += ndefs;
5409 return pcdepth;
5412 #ifdef JS_TRACER
5414 #undef LOCAL_ASSERT
5415 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_CUSTOM(expr, goto failure);
5417 static intN
5418 SimulateImacroCFG(JSContext *cx, JSScript *script,
5419 uintN pcdepth, jsbytecode *pc, jsbytecode *target,
5420 jsbytecode **pcstack)
5422 size_t nbytes = StackDepth(script) * sizeof *pcstack;
5423 jsbytecode** tmp_pcstack = (jsbytecode **) cx->malloc(nbytes);
5424 if (!tmp_pcstack)
5425 return -1;
5426 memcpy(tmp_pcstack, pcstack, nbytes);
5428 ptrdiff_t oplen;
5429 for (; pc < target; pc += oplen) {
5430 JSOp op = js_GetOpcode(cx, script, pc);
5431 const JSCodeSpec *cs = &js_CodeSpec[op];
5432 oplen = cs->length;
5433 if (oplen < 0)
5434 oplen = js_GetVariableBytecodeLength(pc);
5436 if (SimulateOp(cx, script, op, cs, pc, tmp_pcstack, pcdepth) < 0)
5437 goto failure;
5439 uint32 type = cs->format & JOF_TYPEMASK;
5440 if (type == JOF_JUMP || type == JOF_JUMPX) {
5441 ptrdiff_t jmpoff = (type == JOF_JUMP) ? GET_JUMP_OFFSET(pc)
5442 : GET_JUMPX_OFFSET(pc);
5443 LOCAL_ASSERT(jmpoff >= 0);
5444 intN tmp_pcdepth = SimulateImacroCFG(cx, script, pcdepth, pc + jmpoff,
5445 target, tmp_pcstack);
5446 if (tmp_pcdepth >= 0) {
5447 pcdepth = uintN(tmp_pcdepth);
5448 goto success;
5451 if (op == JSOP_GOTO || op == JSOP_GOTOX)
5452 goto failure;
5456 if (pc > target)
5457 goto failure;
5459 LOCAL_ASSERT(pc == target);
5461 success:
5462 memcpy(pcstack, tmp_pcstack, nbytes);
5463 cx->free(tmp_pcstack);
5464 return pcdepth;
5466 failure:
5467 cx->free(tmp_pcstack);
5468 return -1;
5471 #undef LOCAL_ASSERT
5472 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1);
5474 static intN
5475 ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *target,
5476 jsbytecode **pcstack);
5478 static intN
5479 ReconstructImacroPCStack(JSContext *cx, JSScript *script,
5480 jsbytecode *imacstart, jsbytecode *target,
5481 jsbytecode **pcstack)
5484 * Begin with a recursive call back to ReconstructPCStack to pick up
5485 * the state-of-the-world at the *start* of the imacro.
5487 JSStackFrame *fp = js_GetScriptedCaller(cx, NULL);
5488 JS_ASSERT(fp->imacpc);
5489 intN pcdepth = ReconstructPCStack(cx, script, fp->imacpc, pcstack);
5490 if (pcdepth < 0)
5491 return pcdepth;
5492 return SimulateImacroCFG(cx, script, pcdepth, imacstart, target, pcstack);
5495 extern jsbytecode* js_GetImacroStart(jsbytecode* pc);
5496 #endif
5498 static intN
5499 ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *target,
5500 jsbytecode **pcstack)
5503 * Walk forward from script->main and compute the stack depth and stack of
5504 * operand-generating opcode PCs in pcstack.
5506 * FIXME: Code to compute oplen copied from js_Disassemble1 and reduced.
5507 * FIXME: Optimize to use last empty-stack sequence point.
5509 #ifdef JS_TRACER
5510 jsbytecode *imacstart = js_GetImacroStart(target);
5512 if (imacstart)
5513 return ReconstructImacroPCStack(cx, script, imacstart, target, pcstack);
5514 #endif
5516 LOCAL_ASSERT(script->main <= target && target < script->code + script->length);
5517 jsbytecode *pc = script->main;
5518 uintN pcdepth = 0;
5519 ptrdiff_t oplen;
5520 for (; pc < target; pc += oplen) {
5521 JSOp op = js_GetOpcode(cx, script, pc);
5522 const JSCodeSpec *cs = &js_CodeSpec[op];
5523 oplen = cs->length;
5524 if (oplen < 0)
5525 oplen = js_GetVariableBytecodeLength(pc);
5528 * A (C ? T : E) expression requires skipping either T (if target is in
5529 * E) or both T and E (if target is after the whole expression) before
5530 * adjusting pcdepth based on the JSOP_IFEQ or JSOP_IFEQX at pc that
5531 * tests condition C. We know that the stack depth can't change from
5532 * what it was with C on top of stack.
5534 jssrcnote *sn = js_GetSrcNote(script, pc);
5535 if (sn && SN_TYPE(sn) == SRC_COND) {
5536 ptrdiff_t jmpoff = js_GetSrcNoteOffset(sn, 0);
5537 if (pc + jmpoff < target) {
5538 pc += jmpoff;
5539 op = js_GetOpcode(cx, script, pc);
5540 JS_ASSERT(op == JSOP_GOTO || op == JSOP_GOTOX);
5541 cs = &js_CodeSpec[op];
5542 oplen = cs->length;
5543 JS_ASSERT(oplen > 0);
5544 ptrdiff_t jmplen = GetJumpOffset(pc, pc);
5545 if (pc + jmplen < target) {
5546 oplen = (uintN) jmplen;
5547 continue;
5551 * Ok, target lies in E. Manually pop C off the model stack,
5552 * since we have moved beyond the IFEQ now.
5554 LOCAL_ASSERT(pcdepth != 0);
5555 --pcdepth;
5559 /* Ignore early-exit code, which is annotated SRC_HIDDEN. */
5560 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
5561 continue;
5563 if (SimulateOp(cx, script, op, cs, pc, pcstack, pcdepth) < 0)
5564 return -1;
5567 LOCAL_ASSERT(pc == target);
5568 return pcdepth;
5570 #undef LOCAL_ASSERT
5573 #undef LOCAL_ASSERT_RV