Bug 543565 - Assertion failure: fp->slots + fp->script->nfixed + js_ReconstructStackD...
[mozilla-central.git] / js / src / jsopcode.cpp
blob476ee70b94777baad81bdfae8c6cb0bc9ccfa2dd
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"
83 * Index limit must stay within 32 bits.
85 JS_STATIC_ASSERT(sizeof(uint32) * JS_BITS_PER_BYTE >= INDEX_LIMIT_LOG2 + 1);
87 /* Verify JSOP_XXX_LENGTH constant definitions. */
88 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
89 JS_STATIC_ASSERT(op##_LENGTH == length);
90 #include "jsopcode.tbl"
91 #undef OPDEF
93 static const char js_incop_strs[][3] = {"++", "--"};
94 static const char js_for_each_str[] = "for each";
96 const JSCodeSpec js_CodeSpec[] = {
97 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
98 {length,nuses,ndefs,prec,format},
99 #include "jsopcode.tbl"
100 #undef OPDEF
103 uintN js_NumCodeSpecs = JS_ARRAY_LENGTH(js_CodeSpec);
106 * Each element of the array is either a source literal associated with JS
107 * bytecode or null.
109 static const char *CodeToken[] = {
110 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
111 token,
112 #include "jsopcode.tbl"
113 #undef OPDEF
116 #if defined(DEBUG) || defined(JS_JIT_SPEW)
118 * Array of JS bytecode names used by DEBUG-only js_Disassemble and by
119 * JIT debug spew.
121 const char *js_CodeName[] = {
122 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
123 name,
124 #include "jsopcode.tbl"
125 #undef OPDEF
127 #endif
129 /************************************************************************/
131 static ptrdiff_t
132 GetJumpOffset(jsbytecode *pc, jsbytecode *pc2)
134 uint32 type;
136 type = JOF_OPTYPE(*pc);
137 if (JOF_TYPE_IS_EXTENDED_JUMP(type))
138 return GET_JUMPX_OFFSET(pc2);
139 return GET_JUMP_OFFSET(pc2);
142 uintN
143 js_GetIndexFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc,
144 ptrdiff_t pcoff)
146 JSOp op;
147 uintN span, base;
149 op = js_GetOpcode(cx, script, pc);
150 JS_ASSERT(js_CodeSpec[op].length >= 1 + pcoff + UINT16_LEN);
153 * We need to detect index base prefix. It presents when resetbase
154 * follows the bytecode.
156 span = js_CodeSpec[op].length;
157 base = 0;
158 if (pc - script->code + span < script->length) {
159 if (pc[span] == JSOP_RESETBASE) {
160 base = GET_INDEXBASE(pc - JSOP_INDEXBASE_LENGTH);
161 } else if (pc[span] == JSOP_RESETBASE0) {
162 JS_ASSERT(JSOP_INDEXBASE1 <= pc[-1] || pc[-1] <= JSOP_INDEXBASE3);
163 base = (pc[-1] - JSOP_INDEXBASE1 + 1) << 16;
166 return base + GET_UINT16(pc + pcoff);
169 uintN
170 js_GetVariableBytecodeLength(jsbytecode *pc)
172 JSOp op;
173 uintN jmplen, ncases;
174 jsint low, high;
176 op = (JSOp) *pc;
177 JS_ASSERT(js_CodeSpec[op].length == -1);
178 switch (op) {
179 case JSOP_TABLESWITCHX:
180 jmplen = JUMPX_OFFSET_LEN;
181 goto do_table;
182 case JSOP_TABLESWITCH:
183 jmplen = JUMP_OFFSET_LEN;
184 do_table:
185 /* Structure: default-jump case-low case-high case1-jump ... */
186 pc += jmplen;
187 low = GET_JUMP_OFFSET(pc);
188 pc += JUMP_OFFSET_LEN;
189 high = GET_JUMP_OFFSET(pc);
190 ncases = (uintN)(high - low + 1);
191 return 1 + jmplen + INDEX_LEN + INDEX_LEN + ncases * jmplen;
193 case JSOP_LOOKUPSWITCHX:
194 jmplen = JUMPX_OFFSET_LEN;
195 goto do_lookup;
196 default:
197 JS_ASSERT(op == JSOP_LOOKUPSWITCH);
198 jmplen = JUMP_OFFSET_LEN;
199 do_lookup:
200 /* Structure: default-jump case-count (case1-value case1-jump) ... */
201 pc += jmplen;
202 ncases = GET_UINT16(pc);
203 return 1 + jmplen + INDEX_LEN + ncases * (INDEX_LEN + jmplen);
207 uintN
208 js_GetVariableStackUses(JSOp op, jsbytecode *pc)
210 JS_ASSERT(*pc == op || *pc == JSOP_TRAP);
211 JS_ASSERT(js_CodeSpec[op].nuses == -1);
212 switch (op) {
213 case JSOP_POPN:
214 return GET_UINT16(pc);
215 case JSOP_CONCATN:
216 return GET_UINT16(pc);
217 case JSOP_LEAVEBLOCK:
218 return GET_UINT16(pc);
219 case JSOP_LEAVEBLOCKEXPR:
220 return GET_UINT16(pc) + 1;
221 case JSOP_NEWARRAY:
222 return GET_UINT16(pc);
223 default:
224 /* stack: fun, this, [argc arguments] */
225 JS_ASSERT(op == JSOP_NEW || op == JSOP_CALL ||
226 op == JSOP_EVAL || op == JSOP_SETCALL ||
227 op == JSOP_APPLY);
228 return 2 + GET_ARGC(pc);
232 uintN
233 js_GetEnterBlockStackDefs(JSContext *cx, JSScript *script, jsbytecode *pc)
235 JSObject *obj;
237 JS_ASSERT(*pc == JSOP_ENTERBLOCK || *pc == JSOP_TRAP);
238 GET_OBJECT_FROM_BYTECODE(script, pc, 0, obj);
239 return OBJ_BLOCK_COUNT(cx, obj);
242 #ifdef DEBUG
244 JS_FRIEND_API(JSBool)
245 js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp)
247 jsbytecode *pc, *end;
248 uintN len;
250 pc = script->code;
251 end = pc + script->length;
252 while (pc < end) {
253 if (pc == script->main)
254 fputs("main:\n", fp);
255 len = js_Disassemble1(cx, script, pc,
256 pc - script->code,
257 lines, fp);
258 if (!len)
259 return JS_FALSE;
260 pc += len;
262 return JS_TRUE;
265 JSBool
266 js_DumpScript(JSContext *cx, JSScript *script)
268 return js_Disassemble(cx, script, true, stdout);
271 const char *
272 ToDisassemblySource(JSContext *cx, jsval v)
274 if (!JSVAL_IS_PRIMITIVE(v)) {
275 JSObject *obj = JSVAL_TO_OBJECT(v);
276 JSClass *clasp = OBJ_GET_CLASS(cx, obj);
278 if (clasp == &js_BlockClass) {
279 char *source = JS_sprintf_append(NULL, "depth %d {", OBJ_BLOCK_DEPTH(cx, obj));
280 for (JSScopeProperty *sprop = OBJ_SCOPE(obj)->lastProperty();
281 sprop;
282 sprop = sprop->parent) {
283 const char *bytes = js_AtomToPrintableString(cx, JSID_TO_ATOM(sprop->id));
284 if (!bytes)
285 return NULL;
286 source = JS_sprintf_append(source, "%s: %d%s",
287 bytes, sprop->shortid,
288 sprop->parent ? ", " : "");
291 source = JS_sprintf_append(source, "}");
292 if (!source)
293 return NULL;
295 JSString *str = JS_NewString(cx, source, strlen(source));
296 if (!str)
297 return NULL;
298 return js_GetStringBytes(cx, str);
301 if (clasp == &js_FunctionClass) {
302 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj);
303 JSString *str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT);
304 if (!str)
305 return NULL;
306 return js_GetStringBytes(cx, str);
309 if (clasp == &js_RegExpClass) {
310 JSAutoTempValueRooter tvr(cx);
311 if (!js_regexp_toString(cx, obj, tvr.addr()))
312 return NULL;
313 return js_GetStringBytes(cx, JSVAL_TO_STRING(tvr.value()));
317 return js_ValueToPrintableSource(cx, v);
320 JS_FRIEND_API(uintN)
321 js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc,
322 uintN loc, JSBool lines, FILE *fp)
324 JSOp op;
325 const JSCodeSpec *cs;
326 ptrdiff_t len, off, jmplen;
327 uint32 type;
328 JSAtom *atom;
329 uintN index;
330 JSObject *obj;
331 jsval v;
332 const char *bytes;
333 jsint i;
335 op = (JSOp)*pc;
336 if (op >= JSOP_LIMIT) {
337 char numBuf1[12], numBuf2[12];
338 JS_snprintf(numBuf1, sizeof numBuf1, "%d", op);
339 JS_snprintf(numBuf2, sizeof numBuf2, "%d", JSOP_LIMIT);
340 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
341 JSMSG_BYTECODE_TOO_BIG, numBuf1, numBuf2);
342 return 0;
344 cs = &js_CodeSpec[op];
345 len = (ptrdiff_t) cs->length;
346 fprintf(fp, "%05u:", loc);
347 if (lines)
348 fprintf(fp, "%4u", JS_PCToLineNumber(cx, script, pc));
349 fprintf(fp, " %s", js_CodeName[op]);
350 type = JOF_TYPE(cs->format);
351 switch (type) {
352 case JOF_BYTE:
353 if (op == JSOP_TRAP) {
354 op = JS_GetTrapOpcode(cx, script, pc);
355 len = (ptrdiff_t) js_CodeSpec[op].length;
357 break;
359 case JOF_JUMP:
360 case JOF_JUMPX:
361 off = GetJumpOffset(pc, pc);
362 fprintf(fp, " %u (%d)", loc + (intN) off, (intN) off);
363 break;
365 case JOF_ATOM:
366 case JOF_OBJECT:
367 case JOF_REGEXP:
368 index = js_GetIndexFromBytecode(cx, script, pc, 0);
369 if (type == JOF_ATOM) {
370 JS_GET_SCRIPT_ATOM(script, pc, index, atom);
371 v = ATOM_KEY(atom);
372 } else {
373 if (type == JOF_OBJECT)
374 obj = script->getObject(index);
375 else
376 obj = script->getRegExp(index);
377 v = OBJECT_TO_JSVAL(obj);
379 bytes = ToDisassemblySource(cx, v);
380 if (!bytes)
381 return 0;
382 fprintf(fp, " %s", bytes);
383 break;
385 case JOF_UINT16PAIR:
386 i = (jsint)GET_UINT16(pc);
387 fprintf(fp, " %d", i);
388 /* FALL THROUGH */
390 case JOF_UINT16:
391 i = (jsint)GET_UINT16(pc);
392 goto print_int;
394 case JOF_TABLESWITCH:
395 case JOF_TABLESWITCHX:
397 jsbytecode *pc2;
398 jsint i, low, high;
400 jmplen = (type == JOF_TABLESWITCH) ? JUMP_OFFSET_LEN
401 : JUMPX_OFFSET_LEN;
402 pc2 = pc;
403 off = GetJumpOffset(pc, pc2);
404 pc2 += jmplen;
405 low = GET_JUMP_OFFSET(pc2);
406 pc2 += JUMP_OFFSET_LEN;
407 high = GET_JUMP_OFFSET(pc2);
408 pc2 += JUMP_OFFSET_LEN;
409 fprintf(fp, " defaultOffset %d low %d high %d", (intN) off, low, high);
410 for (i = low; i <= high; i++) {
411 off = GetJumpOffset(pc, pc2);
412 fprintf(fp, "\n\t%d: %d", i, (intN) off);
413 pc2 += jmplen;
415 len = 1 + pc2 - pc;
416 break;
419 case JOF_LOOKUPSWITCH:
420 case JOF_LOOKUPSWITCHX:
422 jsbytecode *pc2;
423 jsatomid npairs;
425 jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN
426 : JUMPX_OFFSET_LEN;
427 pc2 = pc;
428 off = GetJumpOffset(pc, pc2);
429 pc2 += jmplen;
430 npairs = GET_UINT16(pc2);
431 pc2 += UINT16_LEN;
432 fprintf(fp, " offset %d npairs %u", (intN) off, (uintN) npairs);
433 while (npairs) {
434 JS_GET_SCRIPT_ATOM(script, pc, GET_INDEX(pc2), atom);
435 pc2 += INDEX_LEN;
436 off = GetJumpOffset(pc, pc2);
437 pc2 += jmplen;
439 bytes = ToDisassemblySource(cx, ATOM_KEY(atom));
440 if (!bytes)
441 return 0;
442 fprintf(fp, "\n\t%s: %d", bytes, (intN) off);
443 npairs--;
445 len = 1 + pc2 - pc;
446 break;
449 case JOF_QARG:
450 fprintf(fp, " %u", GET_ARGNO(pc));
451 break;
453 case JOF_LOCAL:
454 fprintf(fp, " %u", GET_SLOTNO(pc));
455 break;
457 case JOF_SLOTATOM:
458 case JOF_SLOTOBJECT:
459 fprintf(fp, " %u", GET_SLOTNO(pc));
460 index = js_GetIndexFromBytecode(cx, script, pc, SLOTNO_LEN);
461 if (type == JOF_SLOTATOM) {
462 JS_GET_SCRIPT_ATOM(script, pc, index, atom);
463 v = ATOM_KEY(atom);
464 } else {
465 obj = script->getObject(index);
466 v = OBJECT_TO_JSVAL(obj);
468 bytes = ToDisassemblySource(cx, v);
469 if (!bytes)
470 return 0;
471 fprintf(fp, " %s", bytes);
472 break;
474 case JOF_UINT24:
475 JS_ASSERT(op == JSOP_UINT24 || op == JSOP_NEWARRAY);
476 i = (jsint)GET_UINT24(pc);
477 goto print_int;
479 case JOF_UINT8:
480 i = pc[1];
481 goto print_int;
483 case JOF_INT8:
484 i = GET_INT8(pc);
485 goto print_int;
487 case JOF_INT32:
488 JS_ASSERT(op == JSOP_INT32);
489 i = GET_INT32(pc);
490 print_int:
491 fprintf(fp, " %d", i);
492 break;
494 default: {
495 char numBuf[12];
496 JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) cs->format);
497 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
498 JSMSG_UNKNOWN_FORMAT, numBuf);
499 return 0;
502 fputs("\n", fp);
503 return len;
506 #endif /* DEBUG */
508 /************************************************************************/
511 * Sprintf, but with unlimited and automatically allocated buffering.
513 typedef struct Sprinter {
514 JSContext *context; /* context executing the decompiler */
515 JSArenaPool *pool; /* string allocation pool */
516 char *base; /* base address of buffer in pool */
517 size_t size; /* size of buffer allocated at base */
518 ptrdiff_t offset; /* offset of next free char in buffer */
519 } Sprinter;
521 #define INIT_SPRINTER(cx, sp, ap, off) \
522 ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0, \
523 (sp)->offset = off)
525 #define OFF2STR(sp,off) ((sp)->base + (off))
526 #define STR2OFF(sp,str) ((str) - (sp)->base)
527 #define RETRACT(sp,str) ((sp)->offset = STR2OFF(sp, str))
529 static JSBool
530 SprintEnsureBuffer(Sprinter *sp, size_t len)
532 ptrdiff_t nb;
533 char *base;
535 nb = (sp->offset + len + 1) - sp->size;
536 if (nb < 0)
537 return JS_TRUE;
538 base = sp->base;
539 if (!base) {
540 JS_ARENA_ALLOCATE_CAST(base, char *, sp->pool, nb);
541 } else {
542 JS_ARENA_GROW_CAST(base, char *, sp->pool, sp->size, nb);
544 if (!base) {
545 js_ReportOutOfScriptQuota(sp->context);
546 return JS_FALSE;
548 sp->base = base;
549 sp->size += nb;
550 return JS_TRUE;
553 static ptrdiff_t
554 SprintPut(Sprinter *sp, const char *s, size_t len)
556 ptrdiff_t offset;
557 char *bp;
559 /* Allocate space for s, including the '\0' at the end. */
560 if (!SprintEnsureBuffer(sp, len))
561 return -1;
563 /* Advance offset and copy s into sp's buffer. */
564 offset = sp->offset;
565 sp->offset += len;
566 bp = sp->base + offset;
567 memmove(bp, s, len);
568 bp[len] = 0;
569 return offset;
572 static ptrdiff_t
573 SprintCString(Sprinter *sp, const char *s)
575 return SprintPut(sp, s, strlen(s));
578 static ptrdiff_t
579 SprintString(Sprinter *sp, JSString *str)
581 const jschar *chars;
582 size_t length, size;
583 ptrdiff_t offset;
585 str->getCharsAndLength(chars, length);
586 if (length == 0)
587 return sp->offset;
589 size = js_GetDeflatedStringLength(sp->context, chars, length);
590 if (size == (size_t)-1 || !SprintEnsureBuffer(sp, size))
591 return -1;
593 offset = sp->offset;
594 sp->offset += size;
595 js_DeflateStringToBuffer(sp->context, chars, length, sp->base + offset,
596 &size);
597 sp->base[sp->offset] = 0;
598 return offset;
602 static ptrdiff_t
603 Sprint(Sprinter *sp, const char *format, ...)
605 va_list ap;
606 char *bp;
607 ptrdiff_t offset;
609 va_start(ap, format);
610 bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */
611 va_end(ap);
612 if (!bp) {
613 JS_ReportOutOfMemory(sp->context);
614 return -1;
616 offset = SprintCString(sp, bp);
617 js_free(bp);
618 return offset;
621 const char js_EscapeMap[] = {
622 '\b', 'b',
623 '\f', 'f',
624 '\n', 'n',
625 '\r', 'r',
626 '\t', 't',
627 '\v', 'v',
628 '"', '"',
629 '\'', '\'',
630 '\\', '\\',
631 '\0', '0'
634 #define DONT_ESCAPE 0x10000
636 static char *
637 QuoteString(Sprinter *sp, JSString *str, uint32 quote)
639 JSBool dontEscape, ok;
640 jschar qc, c;
641 ptrdiff_t off, len;
642 const jschar *s, *t, *z;
643 const char *e;
644 char *bp;
646 /* Sample off first for later return value pointer computation. */
647 dontEscape = (quote & DONT_ESCAPE) != 0;
648 qc = (jschar) quote;
649 off = sp->offset;
650 if (qc && Sprint(sp, "%c", (char)qc) < 0)
651 return NULL;
653 /* Loop control variables: z points at end of string sentinel. */
654 str->getCharsAndEnd(s, z);
655 for (t = s; t < z; s = ++t) {
656 /* Move t forward from s past un-quote-worthy characters. */
657 c = *t;
658 while (JS_ISPRINT(c) && c != qc && c != '\\' && c != '\t' &&
659 !(c >> 8)) {
660 c = *++t;
661 if (t == z)
662 break;
664 len = t - s;
666 /* Allocate space for s, including the '\0' at the end. */
667 if (!SprintEnsureBuffer(sp, len))
668 return NULL;
670 /* Advance sp->offset and copy s into sp's buffer. */
671 bp = sp->base + sp->offset;
672 sp->offset += len;
673 while (--len >= 0)
674 *bp++ = (char) *s++;
675 *bp = '\0';
677 if (t == z)
678 break;
680 /* Use js_EscapeMap, \u, or \x only if necessary. */
681 if (!(c >> 8) && (e = strchr(js_EscapeMap, (int)c)) != NULL) {
682 ok = dontEscape
683 ? Sprint(sp, "%c", (char)c) >= 0
684 : Sprint(sp, "\\%c", e[1]) >= 0;
685 } else {
686 ok = Sprint(sp, (c >> 8) ? "\\u%04X" : "\\x%02X", c) >= 0;
688 if (!ok)
689 return NULL;
692 /* Sprint the closing quote and return the quoted string. */
693 if (qc && Sprint(sp, "%c", (char)qc) < 0)
694 return NULL;
697 * If we haven't Sprint'd anything yet, Sprint an empty string so that
698 * the OFF2STR below gives a valid result.
700 if (off == sp->offset && Sprint(sp, "") < 0)
701 return NULL;
702 return OFF2STR(sp, off);
705 JSString *
706 js_QuoteString(JSContext *cx, JSString *str, jschar quote)
708 void *mark;
709 Sprinter sprinter;
710 char *bytes;
711 JSString *escstr;
713 mark = JS_ARENA_MARK(&cx->tempPool);
714 INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);
715 bytes = QuoteString(&sprinter, str, quote);
716 escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL;
717 JS_ARENA_RELEASE(&cx->tempPool, mark);
718 return escstr;
721 /************************************************************************/
723 struct JSPrinter {
724 Sprinter sprinter; /* base class state */
725 JSArenaPool pool; /* string allocation pool */
726 uintN indent; /* indentation in spaces */
727 bool pretty; /* pretty-print: indent, use newlines */
728 bool grouped; /* in parenthesized expression context */
729 bool strict; /* in code marked strict */
730 JSScript *script; /* script being printed */
731 jsbytecode *dvgfence; /* DecompileExpression fencepost */
732 jsbytecode **pcstack; /* DecompileExpression modeled stack */
733 JSFunction *fun; /* interpreted function */
734 jsuword *localNames; /* argument and variable names */
737 JSPrinter *
738 js_NewPrinter(JSContext *cx, const char *name, JSFunction *fun,
739 uintN indent, JSBool pretty, JSBool grouped, JSBool strict)
741 JSPrinter *jp;
743 jp = (JSPrinter *) cx->malloc(sizeof(JSPrinter));
744 if (!jp)
745 return NULL;
746 INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
747 JS_InitArenaPool(&jp->pool, name, 256, 1, &cx->scriptStackQuota);
748 jp->indent = indent;
749 jp->pretty = pretty;
750 jp->grouped = grouped;
751 jp->strict = strict;
752 jp->script = NULL;
753 jp->dvgfence = NULL;
754 jp->pcstack = NULL;
755 jp->fun = fun;
756 jp->localNames = NULL;
757 if (fun && FUN_INTERPRETED(fun) && fun->hasLocalNames()) {
758 jp->localNames = js_GetLocalNameArray(cx, fun, &jp->pool);
759 if (!jp->localNames) {
760 js_DestroyPrinter(jp);
761 return NULL;
764 return jp;
767 void
768 js_DestroyPrinter(JSPrinter *jp)
770 JS_FinishArenaPool(&jp->pool);
771 jp->sprinter.context->free(jp);
774 JSString *
775 js_GetPrinterOutput(JSPrinter *jp)
777 JSContext *cx;
778 JSString *str;
780 cx = jp->sprinter.context;
781 if (!jp->sprinter.base)
782 return cx->runtime->emptyString;
783 str = JS_NewStringCopyZ(cx, jp->sprinter.base);
784 if (!str)
785 return NULL;
786 JS_FreeArenaPool(&jp->pool);
787 INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
788 return str;
792 * NB: Indexed by SRC_DECL_* defines from jsemit.h.
794 static const char * const var_prefix[] = {"var ", "const ", "let "};
796 static const char *
797 VarPrefix(jssrcnote *sn)
799 if (sn && (SN_TYPE(sn) == SRC_DECL || SN_TYPE(sn) == SRC_GROUPASSIGN)) {
800 ptrdiff_t type = js_GetSrcNoteOffset(sn, 0);
801 if ((uintN)type <= SRC_DECL_LET)
802 return var_prefix[type];
804 return "";
808 js_printf(JSPrinter *jp, const char *format, ...)
810 va_list ap;
811 char *bp, *fp;
812 int cc;
814 if (*format == '\0')
815 return 0;
817 va_start(ap, format);
819 /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */
820 if (*format == '\t') {
821 format++;
822 if (jp->pretty && Sprint(&jp->sprinter, "%*s", jp->indent, "") < 0) {
823 va_end(ap);
824 return -1;
828 /* Suppress newlines (must be once per format, at the end) if not pretty. */
829 fp = NULL;
830 if (!jp->pretty && format[cc = strlen(format) - 1] == '\n') {
831 fp = JS_strdup(jp->sprinter.context, format);
832 if (!fp) {
833 va_end(ap);
834 return -1;
836 fp[cc] = '\0';
837 format = fp;
840 /* Allocate temp space, convert format, and put. */
841 bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */
842 if (fp) {
843 jp->sprinter.context->free(fp);
844 format = NULL;
846 if (!bp) {
847 JS_ReportOutOfMemory(jp->sprinter.context);
848 va_end(ap);
849 return -1;
852 cc = strlen(bp);
853 if (SprintPut(&jp->sprinter, bp, (size_t)cc) < 0)
854 cc = -1;
855 js_free(bp);
857 va_end(ap);
858 return cc;
861 JSBool
862 js_puts(JSPrinter *jp, const char *s)
864 return SprintCString(&jp->sprinter, s) >= 0;
867 /************************************************************************/
869 typedef struct SprintStack {
870 Sprinter sprinter; /* sprinter for postfix to infix buffering */
871 ptrdiff_t *offsets; /* stack of postfix string offsets */
872 jsbytecode *opcodes; /* parallel stack of JS opcodes */
873 uintN top; /* top of stack index */
874 uintN inArrayInit; /* array initialiser/comprehension level */
875 JSBool inGenExp; /* in generator expression */
876 JSPrinter *printer; /* permanent output goes here */
877 } SprintStack;
880 * Find the depth of the operand stack when the interpreter reaches the given
881 * pc in script. pcstack must have space for least script->depth elements. On
882 * return it will contain pointers to opcodes that populated the interpreter's
883 * current operand stack.
885 * This function cannot raise an exception or error. However, due to a risk of
886 * potential bugs when modeling the stack, the function returns -1 if it
887 * detects an inconsistency in the model. Such an inconsistency triggers an
888 * assert in a debug build.
890 static intN
891 ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *pc,
892 jsbytecode **pcstack);
894 #define FAILED_EXPRESSION_DECOMPILER ((char *) 1)
897 * Decompile a part of expression up to the given pc. The function returns
898 * NULL on out-of-memory, or the FAILED_EXPRESSION_DECOMPILER sentinel when
899 * the decompiler fails due to a bug and/or unimplemented feature, or the
900 * decompiled string on success.
902 static char *
903 DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun,
904 jsbytecode *pc);
907 * Get a stacked offset from ss->sprinter.base, or if the stacked value |off|
908 * is negative, fetch the generating pc from printer->pcstack[-2 - off] and
909 * decompile the code that generated the missing value. This is used when
910 * reporting errors, where the model stack will lack |pcdepth| non-negative
911 * offsets (see DecompileExpression and DecompileCode).
913 * If the stacked offset is -1, return 0 to index the NUL padding at the start
914 * of ss->sprinter.base. If this happens, it means there is a decompiler bug
915 * to fix, but it won't violate memory safety.
917 static ptrdiff_t
918 GetOff(SprintStack *ss, uintN i)
920 ptrdiff_t off;
921 jsbytecode *pc;
922 char *bytes;
924 off = ss->offsets[i];
925 if (off >= 0)
926 return off;
928 JS_ASSERT(off <= -2);
929 JS_ASSERT(ss->printer->pcstack);
930 if (off <= -2 && ss->printer->pcstack) {
931 pc = ss->printer->pcstack[-2 - off];
932 bytes = DecompileExpression(ss->sprinter.context, ss->printer->script,
933 ss->printer->fun, pc);
934 if (!bytes)
935 return 0;
936 if (bytes != FAILED_EXPRESSION_DECOMPILER) {
937 off = SprintCString(&ss->sprinter, bytes);
938 if (off < 0)
939 off = 0;
940 ss->offsets[i] = off;
941 ss->sprinter.context->free(bytes);
942 return off;
944 if (!ss->sprinter.base && SprintPut(&ss->sprinter, "", 0) >= 0) {
945 memset(ss->sprinter.base, 0, ss->sprinter.offset);
946 ss->offsets[i] = -1;
949 return 0;
952 static const char *
953 GetStr(SprintStack *ss, uintN i)
955 ptrdiff_t off;
958 * Must call GetOff before using ss->sprinter.base, since it may be null
959 * until bootstrapped by GetOff.
961 off = GetOff(ss, i);
962 return OFF2STR(&ss->sprinter, off);
966 * Gap between stacked strings to allow for insertion of parens and commas
967 * when auto-parenthesizing expressions and decompiling array initialisers
968 * (see the JSOP_NEWARRAY case in Decompile).
970 #define PAREN_SLOP (2 + 1)
973 * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME,
974 * JSOP_SETPROP, and JSOP_SETELEM, respectively. They are never stored in
975 * bytecode, so they don't preempt valid opcodes.
977 #define JSOP_GETPROP2 JSOP_LIMIT
978 #define JSOP_GETELEM2 JSOP_LIMIT + 1
979 JS_STATIC_ASSERT(JSOP_GETELEM2 <= 255);
981 static void
982 AddParenSlop(SprintStack *ss)
984 memset(OFF2STR(&ss->sprinter, ss->sprinter.offset), 0, PAREN_SLOP);
985 ss->sprinter.offset += PAREN_SLOP;
988 static JSBool
989 PushOff(SprintStack *ss, ptrdiff_t off, JSOp op)
991 uintN top;
993 if (!SprintEnsureBuffer(&ss->sprinter, PAREN_SLOP))
994 return JS_FALSE;
996 /* ss->top points to the next free slot; be paranoid about overflow. */
997 top = ss->top;
998 JS_ASSERT(top < StackDepth(ss->printer->script));
999 if (top >= StackDepth(ss->printer->script)) {
1000 JS_ReportOutOfMemory(ss->sprinter.context);
1001 return JS_FALSE;
1004 /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */
1005 ss->offsets[top] = off;
1006 ss->opcodes[top] = jsbytecode((op == JSOP_GETPROP2) ? JSOP_GETPROP
1007 : (op == JSOP_GETELEM2) ? JSOP_GETELEM
1008 : op);
1009 ss->top = ++top;
1010 AddParenSlop(ss);
1011 return JS_TRUE;
1014 static ptrdiff_t
1015 PopOffPrec(SprintStack *ss, uint8 prec)
1017 uintN top;
1018 const JSCodeSpec *topcs;
1019 ptrdiff_t off;
1021 /* ss->top points to the next free slot; be paranoid about underflow. */
1022 top = ss->top;
1023 JS_ASSERT(top != 0);
1024 if (top == 0)
1025 return 0;
1027 ss->top = --top;
1028 off = GetOff(ss, top);
1029 topcs = &js_CodeSpec[ss->opcodes[top]];
1030 if (topcs->prec != 0 && topcs->prec < prec) {
1031 ss->sprinter.offset = ss->offsets[top] = off - 2;
1032 off = Sprint(&ss->sprinter, "(%s)", OFF2STR(&ss->sprinter, off));
1033 } else {
1034 ss->sprinter.offset = off;
1036 return off;
1039 static const char *
1040 PopStrPrec(SprintStack *ss, uint8 prec)
1042 ptrdiff_t off;
1044 off = PopOffPrec(ss, prec);
1045 return OFF2STR(&ss->sprinter, off);
1048 static ptrdiff_t
1049 PopOff(SprintStack *ss, JSOp op)
1051 return PopOffPrec(ss, js_CodeSpec[op].prec);
1054 static const char *
1055 PopStr(SprintStack *ss, JSOp op)
1057 return PopStrPrec(ss, js_CodeSpec[op].prec);
1060 typedef struct TableEntry {
1061 jsval key;
1062 ptrdiff_t offset;
1063 JSAtom *label;
1064 jsint order; /* source order for stable tableswitch sort */
1065 } TableEntry;
1067 static JSBool
1068 CompareOffsets(void *arg, const void *v1, const void *v2, int *result)
1070 ptrdiff_t offset_diff;
1071 const TableEntry *te1 = (const TableEntry *) v1,
1072 *te2 = (const TableEntry *) v2;
1074 offset_diff = te1->offset - te2->offset;
1075 *result = (offset_diff == 0 ? te1->order - te2->order
1076 : offset_diff < 0 ? -1
1077 : 1);
1078 return JS_TRUE;
1081 static ptrdiff_t
1082 SprintDoubleValue(Sprinter *sp, jsval v, JSOp *opp)
1084 jsdouble d;
1085 ptrdiff_t todo;
1086 char *s, buf[DTOSTR_STANDARD_BUFFER_SIZE];
1088 JS_ASSERT(JSVAL_IS_DOUBLE(v));
1089 d = *JSVAL_TO_DOUBLE(v);
1090 if (JSDOUBLE_IS_NEGZERO(d)) {
1091 todo = SprintCString(sp, "-0");
1092 *opp = JSOP_NEG;
1093 } else if (!JSDOUBLE_IS_FINITE(d)) {
1094 /* Don't use Infinity and NaN, they're mutable. */
1095 todo = SprintCString(sp,
1096 JSDOUBLE_IS_NaN(d)
1097 ? "0 / 0"
1098 : (d < 0)
1099 ? "1 / -0"
1100 : "1 / 0");
1101 *opp = JSOP_DIV;
1102 } else {
1103 s = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, d);
1104 if (!s) {
1105 JS_ReportOutOfMemory(sp->context);
1106 return -1;
1108 JS_ASSERT(strcmp(s, js_Infinity_str) &&
1109 (*s != '-' ||
1110 strcmp(s + 1, js_Infinity_str)) &&
1111 strcmp(s, js_NaN_str));
1112 todo = Sprint(sp, s);
1114 return todo;
1117 static jsbytecode *
1118 Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop);
1120 static JSBool
1121 DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength,
1122 jsbytecode *pc, ptrdiff_t switchLength,
1123 ptrdiff_t defaultOffset, JSBool isCondSwitch)
1125 JSContext *cx;
1126 JSPrinter *jp;
1127 ptrdiff_t off, off2, diff, caseExprOff, todo;
1128 char *lval, *rval;
1129 uintN i;
1130 jsval key;
1131 JSString *str;
1133 cx = ss->sprinter.context;
1134 jp = ss->printer;
1136 /* JSOP_CONDSWITCH doesn't pop, unlike JSOP_{LOOKUP,TABLE}SWITCH. */
1137 off = isCondSwitch ? GetOff(ss, ss->top-1) : PopOff(ss, JSOP_NOP);
1138 lval = OFF2STR(&ss->sprinter, off);
1140 js_printf(jp, "\tswitch (%s) {\n", lval);
1142 if (tableLength) {
1143 diff = table[0].offset - defaultOffset;
1144 if (diff > 0) {
1145 jp->indent += 2;
1146 js_printf(jp, "\t%s:\n", js_default_str);
1147 jp->indent += 2;
1148 if (!Decompile(ss, pc + defaultOffset, diff, JSOP_NOP))
1149 return JS_FALSE;
1150 jp->indent -= 4;
1153 caseExprOff = isCondSwitch ? JSOP_CONDSWITCH_LENGTH : 0;
1155 for (i = 0; i < tableLength; i++) {
1156 off = table[i].offset;
1157 off2 = (i + 1 < tableLength) ? table[i + 1].offset : switchLength;
1159 key = table[i].key;
1160 if (isCondSwitch) {
1161 ptrdiff_t nextCaseExprOff;
1164 * key encodes the JSOP_CASE bytecode's offset from switchtop.
1165 * The next case expression follows immediately, unless we are
1166 * at the last case.
1168 nextCaseExprOff = (ptrdiff_t)JSVAL_TO_INT(key);
1169 nextCaseExprOff += js_CodeSpec[pc[nextCaseExprOff]].length;
1170 jp->indent += 2;
1171 if (!Decompile(ss, pc + caseExprOff,
1172 nextCaseExprOff - caseExprOff, JSOP_NOP)) {
1173 return JS_FALSE;
1175 caseExprOff = nextCaseExprOff;
1177 /* Balance the stack as if this JSOP_CASE matched. */
1178 --ss->top;
1179 } else {
1181 * key comes from an atom, not the decompiler, so we need to
1182 * quote it if it's a string literal. But if table[i].label
1183 * is non-null, key was constant-propagated and label is the
1184 * name of the const we should show as the case label. We set
1185 * key to undefined so this identifier is escaped, if required
1186 * by non-ASCII characters, but not quoted, by QuoteString.
1188 todo = -1;
1189 if (table[i].label) {
1190 str = ATOM_TO_STRING(table[i].label);
1191 key = JSVAL_VOID;
1192 } else if (JSVAL_IS_DOUBLE(key)) {
1193 JSOp junk;
1195 todo = SprintDoubleValue(&ss->sprinter, key, &junk);
1196 str = NULL;
1197 } else {
1198 str = js_ValueToString(cx, key);
1199 if (!str)
1200 return JS_FALSE;
1202 if (todo >= 0) {
1203 rval = OFF2STR(&ss->sprinter, todo);
1204 } else {
1205 rval = QuoteString(&ss->sprinter, str, (jschar)
1206 (JSVAL_IS_STRING(key) ? '"' : 0));
1207 if (!rval)
1208 return JS_FALSE;
1210 RETRACT(&ss->sprinter, rval);
1211 jp->indent += 2;
1212 js_printf(jp, "\tcase %s:\n", rval);
1215 jp->indent += 2;
1216 if (off <= defaultOffset && defaultOffset < off2) {
1217 diff = defaultOffset - off;
1218 if (diff != 0) {
1219 if (!Decompile(ss, pc + off, diff, JSOP_NOP))
1220 return JS_FALSE;
1221 off = defaultOffset;
1223 jp->indent -= 2;
1224 js_printf(jp, "\t%s:\n", js_default_str);
1225 jp->indent += 2;
1227 if (!Decompile(ss, pc + off, off2 - off, JSOP_NOP))
1228 return JS_FALSE;
1229 jp->indent -= 4;
1231 /* Re-balance as if last JSOP_CASE or JSOP_DEFAULT mismatched. */
1232 if (isCondSwitch)
1233 ++ss->top;
1237 if (defaultOffset == switchLength) {
1238 jp->indent += 2;
1239 js_printf(jp, "\t%s:;\n", js_default_str);
1240 jp->indent -= 2;
1242 js_printf(jp, "\t}\n");
1244 /* By the end of a JSOP_CONDSWITCH, the discriminant has been popped. */
1245 if (isCondSwitch)
1246 --ss->top;
1247 return JS_TRUE;
1250 #define LOCAL_ASSERT_CUSTOM(expr, BAD_EXIT) \
1251 JS_BEGIN_MACRO \
1252 JS_ASSERT(expr); \
1253 if (!(expr)) { BAD_EXIT; } \
1254 JS_END_MACRO
1256 #define LOCAL_ASSERT_RV(expr, rv) \
1257 LOCAL_ASSERT_CUSTOM(expr, return (rv))
1259 static JSAtom *
1260 GetArgOrVarAtom(JSPrinter *jp, uintN slot)
1262 JSAtom *name;
1264 LOCAL_ASSERT_RV(jp->fun, NULL);
1265 LOCAL_ASSERT_RV(slot < jp->fun->countLocalNames(), NULL);
1266 name = JS_LOCAL_NAME_TO_ATOM(jp->localNames[slot]);
1267 #if !JS_HAS_DESTRUCTURING
1268 LOCAL_ASSERT_RV(name, NULL);
1269 #endif
1270 return name;
1273 const char *
1274 GetLocal(SprintStack *ss, jsint i)
1276 ptrdiff_t off;
1277 JSContext *cx;
1278 JSScript *script;
1279 jsatomid j, n;
1280 JSAtom *atom;
1281 JSObject *obj;
1282 jsint depth, count;
1283 JSScopeProperty *sprop;
1284 const char *rval;
1286 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, "")
1288 off = ss->offsets[i];
1289 if (off >= 0)
1290 return OFF2STR(&ss->sprinter, off);
1293 * We must be called from js_DecompileValueGenerator (via Decompile) when
1294 * dereferencing a local that's undefined or null. Search script->objects
1295 * for the block containing this local by its stack index, i.
1297 * In case of destructuring's use of JSOP_GETLOCAL, however, there may be
1298 * no such local. This could mean no blocks (no script objects at all, or
1299 * none of the script's object literals are blocks), or the stack slot i is
1300 * not in a block. In either case, return GetStr(ss, i).
1302 cx = ss->sprinter.context;
1303 script = ss->printer->script;
1304 if (script->objectsOffset == 0)
1305 return GetStr(ss, i);
1306 for (j = 0, n = script->objects()->length; ; j++) {
1307 if (j == n)
1308 return GetStr(ss, i);
1309 obj = script->getObject(j);
1310 if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) {
1311 depth = OBJ_BLOCK_DEPTH(cx, obj);
1312 count = OBJ_BLOCK_COUNT(cx, obj);
1313 if ((jsuint)(i - depth) < (jsuint)count)
1314 break;
1318 i -= depth;
1319 for (sprop = OBJ_SCOPE(obj)->lastProperty(); sprop; sprop = sprop->parent) {
1320 if (sprop->shortid == i)
1321 break;
1324 LOCAL_ASSERT(sprop && JSID_IS_ATOM(sprop->id));
1325 atom = JSID_TO_ATOM(sprop->id);
1326 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1327 if (!rval)
1328 return NULL;
1329 RETRACT(&ss->sprinter, rval);
1330 return rval;
1332 #undef LOCAL_ASSERT
1335 static JSBool
1336 IsVarSlot(JSPrinter *jp, jsbytecode *pc, jsint *indexp)
1338 uintN slot;
1340 slot = GET_SLOTNO(pc);
1341 if (slot < jp->script->nfixed) {
1342 /* The slot refers to a variable with name stored in jp->localNames. */
1343 *indexp = jp->fun->nargs + slot;
1344 return JS_TRUE;
1347 /* We have a local which index is relative to the stack base. */
1348 slot -= jp->script->nfixed;
1349 JS_ASSERT(slot < StackDepth(jp->script));
1350 *indexp = slot;
1351 return JS_FALSE;
1354 #define LOAD_ATOM(PCOFF) \
1355 GET_ATOM_FROM_BYTECODE(jp->script, pc, PCOFF, atom)
1357 #if JS_HAS_DESTRUCTURING
1359 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
1360 #define LOAD_OP_DATA(pc) (oplen = (cs = &js_CodeSpec[op=(JSOp)*pc])->length)
1362 static jsbytecode *
1363 DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc);
1365 static jsbytecode *
1366 DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
1367 JSBool *hole)
1369 JSContext *cx;
1370 JSPrinter *jp;
1371 JSOp op;
1372 const JSCodeSpec *cs;
1373 uintN oplen;
1374 jsint i;
1375 const char *lval, *xval;
1376 ptrdiff_t todo;
1377 JSAtom *atom;
1379 *hole = JS_FALSE;
1380 cx = ss->sprinter.context;
1381 jp = ss->printer;
1382 LOAD_OP_DATA(pc);
1384 switch (op) {
1385 case JSOP_POP:
1386 *hole = JS_TRUE;
1387 todo = SprintPut(&ss->sprinter, ", ", 2);
1388 break;
1390 case JSOP_DUP:
1391 pc = DecompileDestructuring(ss, pc, endpc);
1392 if (!pc)
1393 return NULL;
1394 if (pc == endpc)
1395 return pc;
1396 LOAD_OP_DATA(pc);
1397 lval = PopStr(ss, JSOP_NOP);
1398 todo = SprintCString(&ss->sprinter, lval);
1399 if (op == JSOP_POPN)
1400 return pc;
1401 LOCAL_ASSERT(*pc == JSOP_POP);
1402 break;
1404 case JSOP_SETARG:
1405 case JSOP_SETGVAR:
1406 case JSOP_SETLOCAL:
1407 LOCAL_ASSERT(pc[oplen] == JSOP_POP || pc[oplen] == JSOP_POPN);
1408 /* FALL THROUGH */
1410 case JSOP_SETLOCALPOP:
1411 atom = NULL;
1412 lval = NULL;
1413 if (op == JSOP_SETARG) {
1414 atom = GetArgOrVarAtom(jp, GET_SLOTNO(pc));
1415 LOCAL_ASSERT(atom);
1416 } else if (op == JSOP_SETGVAR) {
1417 LOAD_ATOM(0);
1418 } else if (IsVarSlot(jp, pc, &i)) {
1419 atom = GetArgOrVarAtom(jp, i);
1420 LOCAL_ASSERT(atom);
1421 } else {
1422 lval = GetLocal(ss, i);
1424 if (atom)
1425 lval = js_AtomToPrintableString(cx, atom);
1426 LOCAL_ASSERT(lval);
1427 todo = SprintCString(&ss->sprinter, lval);
1428 if (op != JSOP_SETLOCALPOP) {
1429 pc += oplen;
1430 if (pc == endpc)
1431 return pc;
1432 LOAD_OP_DATA(pc);
1433 if (op == JSOP_POPN)
1434 return pc;
1435 LOCAL_ASSERT(op == JSOP_POP);
1437 break;
1439 default:
1441 * We may need to auto-parenthesize the left-most value decompiled
1442 * here, so add back PAREN_SLOP temporarily. Then decompile until the
1443 * opcode that would reduce the stack depth to (ss->top-1), which we
1444 * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for
1445 * the nb parameter.
1447 todo = ss->sprinter.offset;
1448 ss->sprinter.offset = todo + PAREN_SLOP;
1449 pc = Decompile(ss, pc, -((intN)ss->top), JSOP_NOP);
1450 if (!pc)
1451 return NULL;
1452 if (pc == endpc)
1453 return pc;
1454 LOAD_OP_DATA(pc);
1455 LOCAL_ASSERT(op == JSOP_ENUMELEM || op == JSOP_ENUMCONSTELEM);
1456 xval = PopStr(ss, JSOP_NOP);
1457 lval = PopStr(ss, JSOP_GETPROP);
1458 ss->sprinter.offset = todo;
1459 if (*lval == '\0') {
1460 /* lval is from JSOP_BINDNAME, so just print xval. */
1461 todo = SprintCString(&ss->sprinter, xval);
1462 } else if (*xval == '\0') {
1463 /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */
1464 todo = SprintCString(&ss->sprinter, lval);
1465 } else {
1466 todo = Sprint(&ss->sprinter,
1467 (JOF_OPMODE(ss->opcodes[ss->top+1]) == JOF_XMLNAME)
1468 ? "%s.%s"
1469 : "%s[%s]",
1470 lval, xval);
1472 break;
1475 if (todo < 0)
1476 return NULL;
1478 LOCAL_ASSERT(pc < endpc);
1479 pc += oplen;
1480 return pc;
1484 * Starting with a SRC_DESTRUCT-annotated JSOP_DUP, decompile a destructuring
1485 * left-hand side object or array initialiser, including nested destructuring
1486 * initialisers. On successful return, the decompilation will be pushed on ss
1487 * and the return value will point to the POP or GROUP bytecode following the
1488 * destructuring expression.
1490 * At any point, if pc is equal to endpc and would otherwise advance, we stop
1491 * immediately and return endpc.
1493 static jsbytecode *
1494 DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc)
1496 ptrdiff_t head;
1497 JSContext *cx;
1498 JSPrinter *jp;
1499 JSOp op, saveop;
1500 const JSCodeSpec *cs;
1501 uintN oplen;
1502 jsint i, lasti;
1503 jsdouble d;
1504 const char *lval;
1505 JSAtom *atom;
1506 jssrcnote *sn;
1507 JSString *str;
1508 JSBool hole;
1510 LOCAL_ASSERT(*pc == JSOP_DUP);
1511 pc += JSOP_DUP_LENGTH;
1514 * Set head so we can rewrite '[' to '{' as needed. Back up PAREN_SLOP
1515 * chars so the destructuring decompilation accumulates contiguously in
1516 * ss->sprinter starting with "[".
1518 head = SprintPut(&ss->sprinter, "[", 1);
1519 if (head < 0 || !PushOff(ss, head, JSOP_NOP))
1520 return NULL;
1521 ss->sprinter.offset -= PAREN_SLOP;
1522 LOCAL_ASSERT(head == ss->sprinter.offset - 1);
1523 LOCAL_ASSERT(*OFF2STR(&ss->sprinter, head) == '[');
1525 cx = ss->sprinter.context;
1526 jp = ss->printer;
1527 lasti = -1;
1529 while (pc < endpc) {
1530 #if JS_HAS_DESTRUCTURING_SHORTHAND
1531 ptrdiff_t nameoff = -1;
1532 #endif
1534 LOAD_OP_DATA(pc);
1535 saveop = op;
1537 switch (op) {
1538 case JSOP_POP:
1539 pc += oplen;
1540 goto out;
1542 /* Handle the optimized number-pushing opcodes. */
1543 case JSOP_ZERO: d = i = 0; goto do_getelem;
1544 case JSOP_ONE: d = i = 1; goto do_getelem;
1545 case JSOP_UINT16: d = i = GET_UINT16(pc); goto do_getelem;
1546 case JSOP_UINT24: d = i = GET_UINT24(pc); goto do_getelem;
1547 case JSOP_INT8: d = i = GET_INT8(pc); goto do_getelem;
1548 case JSOP_INT32: d = i = GET_INT32(pc); goto do_getelem;
1550 case JSOP_DOUBLE:
1551 GET_DOUBLE_FROM_BYTECODE(jp->script, pc, 0, atom);
1552 d = *ATOM_TO_DOUBLE(atom);
1553 LOCAL_ASSERT(JSDOUBLE_IS_FINITE(d) && !JSDOUBLE_IS_NEGZERO(d));
1554 i = (jsint)d;
1556 do_getelem:
1557 sn = js_GetSrcNote(jp->script, pc);
1558 pc += oplen;
1559 if (pc == endpc)
1560 return pc;
1561 LOAD_OP_DATA(pc);
1562 LOCAL_ASSERT(op == JSOP_GETELEM);
1564 /* Distinguish object from array by opcode or source note. */
1565 if (sn && SN_TYPE(sn) == SRC_INITPROP) {
1566 *OFF2STR(&ss->sprinter, head) = '{';
1567 if (Sprint(&ss->sprinter, "%g: ", d) < 0)
1568 return NULL;
1569 } else {
1570 /* Sanity check for the gnarly control flow above. */
1571 LOCAL_ASSERT(i == d);
1573 /* Fill in any holes (holes at the end don't matter). */
1574 while (++lasti < i) {
1575 if (SprintPut(&ss->sprinter, ", ", 2) < 0)
1576 return NULL;
1579 break;
1581 case JSOP_LENGTH:
1582 atom = cx->runtime->atomState.lengthAtom;
1583 goto do_destructure_atom;
1585 case JSOP_CALLPROP:
1586 case JSOP_GETPROP:
1587 LOAD_ATOM(0);
1588 do_destructure_atom:
1589 *OFF2STR(&ss->sprinter, head) = '{';
1590 str = ATOM_TO_STRING(atom);
1591 #if JS_HAS_DESTRUCTURING_SHORTHAND
1592 nameoff = ss->sprinter.offset;
1593 #endif
1594 if (!QuoteString(&ss->sprinter, str,
1595 js_IsIdentifier(str) ? 0 : (jschar)'\'')) {
1596 return NULL;
1598 if (SprintPut(&ss->sprinter, ": ", 2) < 0)
1599 return NULL;
1600 break;
1602 default:
1603 LOCAL_ASSERT(0);
1606 pc += oplen;
1607 if (pc == endpc)
1608 return pc;
1611 * Decompile the left-hand side expression whose bytecode starts at pc
1612 * and continues for a bounded number of bytecodes or stack operations
1613 * (and which in any event stops before endpc).
1615 pc = DecompileDestructuringLHS(ss, pc, endpc, &hole);
1616 if (!pc)
1617 return NULL;
1619 #if JS_HAS_DESTRUCTURING_SHORTHAND
1620 if (nameoff >= 0) {
1621 ptrdiff_t offset, initlen;
1623 offset = ss->sprinter.offset;
1624 LOCAL_ASSERT(*OFF2STR(&ss->sprinter, offset) == '\0');
1625 initlen = offset - nameoff;
1626 LOCAL_ASSERT(initlen >= 4);
1628 /* Early check to rule out odd "name: lval" length. */
1629 if (((size_t)initlen & 1) == 0) {
1630 size_t namelen;
1631 const char *name;
1634 * Even "name: lval" string length: check for "x: x" and the
1635 * like, and apply the shorthand if we can.
1637 namelen = (size_t)(initlen - 2) >> 1;
1638 name = OFF2STR(&ss->sprinter, nameoff);
1639 if (!strncmp(name + namelen, ": ", 2) &&
1640 !strncmp(name, name + namelen + 2, namelen)) {
1641 offset -= namelen + 2;
1642 *OFF2STR(&ss->sprinter, offset) = '\0';
1643 ss->sprinter.offset = offset;
1647 #endif
1649 if (pc == endpc || *pc != JSOP_DUP)
1650 break;
1653 * We should stop if JSOP_DUP is either without notes or its note is
1654 * not SRC_CONTINUE. The former happens when JSOP_DUP duplicates the
1655 * last destructuring reference implementing an op= assignment like in
1656 * '([t] = z).y += x'. In the latter case the note is SRC_DESTRUCT and
1657 * means another destructuring initialiser abuts this one like in
1658 * '[a] = [b] = c'.
1660 sn = js_GetSrcNote(jp->script, pc);
1661 if (!sn)
1662 break;
1663 if (SN_TYPE(sn) != SRC_CONTINUE) {
1664 LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT);
1665 break;
1668 if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
1669 return NULL;
1671 pc += JSOP_DUP_LENGTH;
1674 out:
1675 lval = OFF2STR(&ss->sprinter, head);
1676 if (SprintPut(&ss->sprinter, (*lval == '[') ? "]" : "}", 1) < 0)
1677 return NULL;
1678 return pc;
1681 static jsbytecode *
1682 DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
1683 jssrcnote *sn, ptrdiff_t *todop)
1685 JSOp op;
1686 const JSCodeSpec *cs;
1687 uintN oplen, start, end, i;
1688 ptrdiff_t todo;
1689 JSBool hole;
1690 const char *rval;
1692 LOAD_OP_DATA(pc);
1693 LOCAL_ASSERT(op == JSOP_PUSH || op == JSOP_GETLOCAL);
1695 todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn));
1696 if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
1697 return NULL;
1698 ss->sprinter.offset -= PAREN_SLOP;
1700 for (;;) {
1701 pc += oplen;
1702 if (pc == endpc)
1703 return pc;
1704 pc = DecompileDestructuringLHS(ss, pc, endpc, &hole);
1705 if (!pc)
1706 return NULL;
1707 if (pc == endpc)
1708 return pc;
1709 LOAD_OP_DATA(pc);
1710 if (op != JSOP_PUSH && op != JSOP_GETLOCAL)
1711 break;
1712 if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
1713 return NULL;
1716 LOCAL_ASSERT(op == JSOP_POPN);
1717 if (SprintPut(&ss->sprinter, "] = [", 5) < 0)
1718 return NULL;
1720 end = ss->top - 1;
1721 start = end - GET_UINT16(pc);
1722 for (i = start; i < end; i++) {
1723 rval = GetStr(ss, i);
1724 if (Sprint(&ss->sprinter,
1725 (i == start) ? "%s" : ", %s",
1726 (i == end - 1 && *rval == '\0') ? ", " : rval) < 0) {
1727 return NULL;
1731 if (SprintPut(&ss->sprinter, "]", 1) < 0)
1732 return NULL;
1733 ss->sprinter.offset = ss->offsets[i];
1734 ss->top = start;
1735 *todop = todo;
1736 return pc;
1739 #undef LOCAL_ASSERT
1740 #undef LOAD_OP_DATA
1742 #endif /* JS_HAS_DESTRUCTURING */
1744 static JSBool
1745 InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth)
1747 size_t offsetsz, opcodesz;
1748 void *space;
1750 INIT_SPRINTER(cx, &ss->sprinter, &cx->tempPool, PAREN_SLOP);
1752 /* Allocate the parallel (to avoid padding) offset and opcode stacks. */
1753 offsetsz = depth * sizeof(ptrdiff_t);
1754 opcodesz = depth * sizeof(jsbytecode);
1755 JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz);
1756 if (!space) {
1757 js_ReportOutOfScriptQuota(cx);
1758 return JS_FALSE;
1760 ss->offsets = (ptrdiff_t *) space;
1761 ss->opcodes = (jsbytecode *) ((char *)space + offsetsz);
1763 ss->top = ss->inArrayInit = 0;
1764 ss->inGenExp = JS_FALSE;
1765 ss->printer = jp;
1766 return JS_TRUE;
1770 * If nb is non-negative, decompile nb bytecodes starting at pc. Otherwise
1771 * the decompiler starts at pc and continues until it reaches an opcode for
1772 * which decompiling would result in the stack depth equaling -(nb + 1).
1774 * The nextop parameter is either JSOP_NOP or the "next" opcode in order of
1775 * abstract interpretation (not necessarily physically next in a bytecode
1776 * vector). So nextop is JSOP_POP for the last operand in a comma expression,
1777 * or JSOP_AND for the right operand of &&.
1779 static jsbytecode *
1780 Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
1782 JSContext *cx;
1783 JSPrinter *jp, *jp2;
1784 jsbytecode *startpc, *endpc, *pc2, *done;
1785 ptrdiff_t tail, todo, len, oplen, cond, next;
1786 JSOp op, lastop, saveop;
1787 const JSCodeSpec *cs;
1788 jssrcnote *sn, *sn2;
1789 const char *lval, *rval, *xval, *fmt, *token;
1790 uintN nuses;
1791 jsint i, argc;
1792 char **argv;
1793 JSAtom *atom;
1794 JSObject *obj;
1795 JSFunction *fun;
1796 JSString *str;
1797 JSBool ok;
1798 #if JS_HAS_XML_SUPPORT
1799 JSBool foreach, inXML, quoteAttr;
1800 #else
1801 #define inXML JS_FALSE
1802 #endif
1803 jsval val;
1805 static const char exception_cookie[] = "/*EXCEPTION*/";
1806 static const char retsub_pc_cookie[] = "/*RETSUB_PC*/";
1807 static const char iter_cookie[] = "/*ITER*/";
1808 static const char forelem_cookie[] = "/*FORELEM*/";
1809 static const char with_cookie[] = "/*WITH*/";
1810 static const char dot_format[] = "%s.%s";
1811 static const char index_format[] = "%s[%s]";
1812 static const char predot_format[] = "%s%s.%s";
1813 static const char postdot_format[] = "%s.%s%s";
1814 static const char preindex_format[] = "%s%s[%s]";
1815 static const char postindex_format[] = "%s[%s]%s";
1816 static const char ss_format[] = "%s%s";
1817 static const char sss_format[] = "%s%s%s";
1819 /* Argument and variables decompilation uses the following to share code. */
1820 JS_STATIC_ASSERT(ARGNO_LEN == SLOTNO_LEN);
1823 * Local macros
1825 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
1826 #define DECOMPILE_CODE(pc,nb) if (!Decompile(ss, pc, nb, JSOP_NOP)) return NULL
1827 #define NEXT_OP(pc) (((pc) + (len) == endpc) ? nextop : pc[len])
1828 #define TOP_STR() GetStr(ss, ss->top - 1)
1829 #define POP_STR() PopStr(ss, op)
1830 #define POP_STR_PREC(prec) PopStrPrec(ss, prec)
1833 * Pop a condition expression for if/while. JSOP_IFEQ's precedence forces
1834 * extra parens around assignment, which avoids a strict-mode warning.
1836 #define POP_COND_STR() \
1837 PopStr(ss, (js_CodeSpec[ss->opcodes[ss->top - 1]].format & JOF_SET) \
1838 ? JSOP_IFEQ \
1839 : JSOP_NOP)
1842 * Callers know that ATOM_IS_STRING(atom), and we leave it to the optimizer to
1843 * common ATOM_TO_STRING(atom) here and near the call sites.
1845 #define ATOM_IS_IDENTIFIER(atom) js_IsIdentifier(ATOM_TO_STRING(atom))
1846 #define ATOM_IS_KEYWORD(atom) \
1847 (js_CheckKeyword(ATOM_TO_STRING(atom)->chars(), \
1848 ATOM_TO_STRING(atom)->length()) != TOK_EOF)
1851 * Given an atom already fetched from jp->script's atom map, quote/escape its
1852 * string appropriately into rval, and select fmt from the quoted and unquoted
1853 * alternatives.
1855 #define GET_QUOTE_AND_FMT(qfmt, ufmt, rval) \
1856 JS_BEGIN_MACRO \
1857 jschar quote_; \
1858 if (!ATOM_IS_IDENTIFIER(atom)) { \
1859 quote_ = '\''; \
1860 fmt = qfmt; \
1861 } else { \
1862 quote_ = 0; \
1863 fmt = ufmt; \
1865 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), quote_); \
1866 if (!rval) \
1867 return NULL; \
1868 JS_END_MACRO
1870 #define LOAD_OBJECT(PCOFF) \
1871 GET_OBJECT_FROM_BYTECODE(jp->script, pc, PCOFF, obj)
1873 #define LOAD_FUNCTION(PCOFF) \
1874 GET_FUNCTION_FROM_BYTECODE(jp->script, pc, PCOFF, fun)
1876 #define LOAD_REGEXP(PCOFF) \
1877 GET_REGEXP_FROM_BYTECODE(jp->script, pc, PCOFF, obj)
1879 #define GET_SOURCE_NOTE_ATOM(sn, atom) \
1880 JS_BEGIN_MACRO \
1881 jsatomid atomIndex_ = (jsatomid) js_GetSrcNoteOffset((sn), 0); \
1883 LOCAL_ASSERT(atomIndex_ < jp->script->atomMap.length); \
1884 (atom) = jp->script->atomMap.vector[atomIndex_]; \
1885 JS_END_MACRO
1888 * Get atom from jp->script's atom map, quote/escape its string appropriately
1889 * into rval, and select fmt from the quoted and unquoted alternatives.
1891 #define GET_ATOM_QUOTE_AND_FMT(qfmt, ufmt, rval) \
1892 JS_BEGIN_MACRO \
1893 LOAD_ATOM(0); \
1894 GET_QUOTE_AND_FMT(qfmt, ufmt, rval); \
1895 JS_END_MACRO
1898 * Per spec, new x(y).z means (new x(y))).z. For example new (x(y).z) must
1899 * decompile with the constructor parenthesized, but new x.z should not. The
1900 * normal rules give x(y).z and x.z identical precedence: both are produced by
1901 * JSOP_GETPROP.
1903 * Therefore, we need to know in case JSOP_NEW whether the constructor
1904 * expression contains any unparenthesized function calls. So when building a
1905 * MemberExpression or CallExpression, we set ss->opcodes[n] to JSOP_CALL if
1906 * this is true. x(y).z gets JSOP_CALL, not JSOP_GETPROP.
1908 #define PROPAGATE_CALLNESS() \
1909 JS_BEGIN_MACRO \
1910 if (ss->opcodes[ss->top - 1] == JSOP_CALL) \
1911 saveop = JSOP_CALL; \
1912 JS_END_MACRO
1914 cx = ss->sprinter.context;
1915 JS_CHECK_RECURSION(cx, return NULL);
1917 jp = ss->printer;
1918 startpc = pc;
1919 endpc = (nb < 0) ? jp->script->code + jp->script->length : pc + nb;
1920 tail = -1;
1921 todo = -2; /* NB: different from Sprint() error return. */
1922 saveop = JSOP_NOP;
1923 sn = NULL;
1924 rval = NULL;
1925 #if JS_HAS_XML_SUPPORT
1926 foreach = inXML = quoteAttr = JS_FALSE;
1927 #endif
1929 while (nb < 0 || pc < endpc) {
1931 * Move saveop to lastop so prefixed bytecodes can take special action
1932 * while sharing maximal code. Set op and saveop to the new bytecode,
1933 * use op in POP_STR to trigger automatic parenthesization, but push
1934 * saveop at the bottom of the loop if this op pushes. Thus op may be
1935 * set to nop or otherwise mutated to suppress auto-parens.
1937 lastop = saveop;
1938 op = (JSOp) *pc;
1939 cs = &js_CodeSpec[op];
1940 if (cs->format & JOF_INDEXBASE) {
1942 * The decompiler uses js_GetIndexFromBytecode to get atoms and
1943 * objects and ignores these suffix/prefix bytecodes, thus
1944 * simplifying code that must process JSOP_GETTER/JSOP_SETTER
1945 * prefixes.
1947 pc += cs->length;
1948 if (pc >= endpc)
1949 break;
1950 op = (JSOp) *pc;
1951 cs = &js_CodeSpec[op];
1953 saveop = op;
1954 len = oplen = cs->length;
1955 nuses = js_GetStackUses(cs, op, pc);
1958 * Here it is possible that nuses > ss->top when the op has a hidden
1959 * source note. But when nb < 0 we assume that the caller knows that
1960 * Decompile would never meet such opcodes.
1962 if (nb < 0) {
1963 LOCAL_ASSERT(ss->top >= nuses);
1964 uintN ndefs = js_GetStackDefs(cx, cs, op, jp->script, pc);
1965 if ((uintN) -(nb + 1) == ss->top - nuses + ndefs)
1966 return pc;
1970 * Save source literal associated with JS now before the following
1971 * rewrite changes op. See bug 380197.
1973 token = CodeToken[op];
1975 if (pc + oplen == jp->dvgfence) {
1976 JSStackFrame *fp;
1977 uint32 format, mode, type;
1980 * Rewrite non-get ops to their "get" format if the error is in
1981 * the bytecode at pc, so we don't decompile more than the error
1982 * expression.
1984 fp = js_GetScriptedCaller(cx, NULL);
1985 format = cs->format;
1986 if (((fp && fp->regs && pc == fp->regs->pc) ||
1987 (pc == startpc && nuses != 0)) &&
1988 format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_FOR|JOF_VARPROP)) {
1989 mode = JOF_MODE(format);
1990 if (mode == JOF_NAME) {
1992 * JOF_NAME does not imply JOF_ATOM, so we must check for
1993 * the QARG and QVAR format types, and translate those to
1994 * JSOP_GETARG or JSOP_GETLOCAL appropriately, instead of
1995 * to JSOP_NAME.
1997 type = JOF_TYPE(format);
1998 op = (type == JOF_QARG)
1999 ? JSOP_GETARG
2000 : (type == JOF_LOCAL)
2001 ? JSOP_GETLOCAL
2002 : JSOP_NAME;
2004 JS_ASSERT(js_CodeSpec[op].nuses >= 0);
2005 i = nuses - js_CodeSpec[op].nuses;
2006 while (--i >= 0)
2007 PopOff(ss, JSOP_NOP);
2008 } else {
2010 * We must replace the faulting pc's bytecode with a
2011 * corresponding JSOP_GET* code. For JSOP_SET{PROP,ELEM},
2012 * we must use the "2nd" form of JSOP_GET{PROP,ELEM}, to
2013 * throw away the assignment op's right-hand operand and
2014 * decompile it as if it were a GET of its left-hand
2015 * operand.
2017 if (mode == JOF_PROP) {
2018 op = (JSOp) ((format & JOF_SET)
2019 ? JSOP_GETPROP2
2020 : JSOP_GETPROP);
2021 } else if (mode == JOF_ELEM) {
2022 op = (JSOp) ((format & JOF_SET)
2023 ? JSOP_GETELEM2
2024 : JSOP_GETELEM);
2025 } else {
2027 * Unknown mode (including mode 0) means that op is
2028 * uncategorized for our purposes, so we must write
2029 * per-op special case code here.
2031 switch (op) {
2032 case JSOP_ENUMELEM:
2033 case JSOP_ENUMCONSTELEM:
2034 op = JSOP_GETELEM;
2035 break;
2036 case JSOP_SETCALL:
2037 op = JSOP_CALL;
2038 break;
2039 case JSOP_GETTHISPROP:
2041 * NB: JSOP_GETTHISPROP can't fail due to |this|
2042 * being null or undefined at runtime (beware that
2043 * this may change for ES4). Therefore any error
2044 * resulting from this op must be due to the value
2045 * of the property accessed via |this|, so do not
2046 * rewrite op to JSOP_THIS.
2048 * The next two cases should not change op if
2049 * js_DecompileValueGenerator was called from the
2050 * the property getter. They should rewrite only
2051 * if the base object in the arg/var/local is null
2052 * or undefined. FIXME: bug 431569.
2054 break;
2055 case JSOP_GETARGPROP:
2056 op = JSOP_GETARG;
2057 break;
2058 case JSOP_GETLOCALPROP:
2059 op = JSOP_GETLOCAL;
2060 break;
2061 default:
2062 LOCAL_ASSERT(0);
2068 saveop = op;
2069 if (op >= JSOP_LIMIT) {
2070 switch (op) {
2071 case JSOP_GETPROP2:
2072 saveop = JSOP_GETPROP;
2073 break;
2074 case JSOP_GETELEM2:
2075 saveop = JSOP_GETELEM;
2076 break;
2077 default:;
2080 LOCAL_ASSERT(js_CodeSpec[saveop].length == oplen ||
2081 JOF_TYPE(format) == JOF_SLOTATOM);
2083 jp->dvgfence = NULL;
2086 if (token) {
2087 switch (nuses) {
2088 case 2:
2089 sn = js_GetSrcNote(jp->script, pc);
2090 if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
2092 * Avoid over-parenthesizing y in x op= y based on its
2093 * expansion: x = x op y (replace y by z = w to see the
2094 * problem).
2096 op = (JSOp) pc[oplen];
2097 rval = POP_STR();
2098 lval = POP_STR();
2099 /* Print only the right operand of the assignment-op. */
2100 todo = SprintCString(&ss->sprinter, rval);
2101 op = saveop;
2102 } else if (!inXML) {
2103 rval = POP_STR_PREC(cs->prec + !!(cs->format & JOF_LEFTASSOC));
2104 lval = POP_STR_PREC(cs->prec + !(cs->format & JOF_LEFTASSOC));
2105 todo = Sprint(&ss->sprinter, "%s %s %s",
2106 lval, token, rval);
2107 } else {
2108 /* In XML, just concatenate the two operands. */
2109 LOCAL_ASSERT(op == JSOP_ADD);
2110 rval = POP_STR();
2111 lval = POP_STR();
2112 todo = Sprint(&ss->sprinter, ss_format, lval, rval);
2114 break;
2116 case 1:
2117 rval = POP_STR();
2118 todo = Sprint(&ss->sprinter, ss_format, token, rval);
2119 break;
2121 case 0:
2122 todo = SprintCString(&ss->sprinter, token);
2123 break;
2125 default:
2126 todo = -2;
2127 break;
2129 } else {
2130 switch (op) {
2131 case JSOP_NOP:
2133 * Check for a do-while loop, a for-loop with an empty
2134 * initializer part, a labeled statement, a function
2135 * definition, or try/finally.
2137 sn = js_GetSrcNote(jp->script, pc);
2138 todo = -2;
2139 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
2140 case SRC_WHILE:
2141 ++pc;
2142 tail = js_GetSrcNoteOffset(sn, 0) - 1;
2143 LOCAL_ASSERT(pc[tail] == JSOP_IFNE ||
2144 pc[tail] == JSOP_IFNEX);
2145 js_printf(jp, "\tdo {\n");
2146 jp->indent += 4;
2147 DECOMPILE_CODE(pc, tail);
2148 jp->indent -= 4;
2149 js_printf(jp, "\t} while (%s);\n", POP_COND_STR());
2150 pc += tail;
2151 len = js_CodeSpec[*pc].length;
2152 todo = -2;
2153 break;
2155 case SRC_FOR:
2156 rval = "";
2158 do_forloop:
2159 JS_ASSERT(SN_TYPE(sn) == SRC_FOR);
2161 /* Skip the JSOP_NOP or JSOP_POP bytecode. */
2162 pc += JSOP_NOP_LENGTH;
2164 /* Get the cond, next, and loop-closing tail offsets. */
2165 cond = js_GetSrcNoteOffset(sn, 0);
2166 next = js_GetSrcNoteOffset(sn, 1);
2167 tail = js_GetSrcNoteOffset(sn, 2);
2170 * If this loop has a condition, then pc points at a goto
2171 * targeting the condition.
2173 pc2 = pc;
2174 if (cond != tail) {
2175 LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX);
2176 pc2 += (*pc == JSOP_GOTO) ? JSOP_GOTO_LENGTH : JSOP_GOTOX_LENGTH;
2178 LOCAL_ASSERT(tail + GetJumpOffset(pc+tail, pc+tail) == pc2 - pc);
2180 /* Print the keyword and the possibly empty init-part. */
2181 js_printf(jp, "\tfor (%s;", rval);
2183 if (cond != tail) {
2184 /* Decompile the loop condition. */
2185 DECOMPILE_CODE(pc + cond, tail - cond);
2186 js_printf(jp, " %s", POP_STR());
2189 /* Need a semicolon whether or not there was a cond. */
2190 js_puts(jp, ";");
2192 if (next != cond) {
2194 * Decompile the loop updater. It may end in a JSOP_POP
2195 * that we skip; or in a JSOP_POPN that we do not skip,
2196 * followed by a JSOP_NOP (skipped as if it's a POP).
2197 * We cope with the difference between these two cases
2198 * by checking for stack imbalance and popping if there
2199 * is an rval.
2201 uintN saveTop = ss->top;
2203 DECOMPILE_CODE(pc + next, cond - next - JSOP_POP_LENGTH);
2204 LOCAL_ASSERT(ss->top - saveTop <= 1U);
2205 rval = (ss->top == saveTop)
2206 ? ss->sprinter.base + ss->sprinter.offset
2207 : POP_STR();
2208 js_printf(jp, " %s", rval);
2211 /* Do the loop body. */
2212 js_printf(jp, ") {\n");
2213 jp->indent += 4;
2214 next -= pc2 - pc;
2215 DECOMPILE_CODE(pc2, next);
2216 jp->indent -= 4;
2217 js_printf(jp, "\t}\n");
2219 /* Set len so pc skips over the entire loop. */
2220 len = tail + js_CodeSpec[pc[tail]].length;
2221 break;
2223 case SRC_LABEL:
2224 GET_SOURCE_NOTE_ATOM(sn, atom);
2225 jp->indent -= 4;
2226 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2227 if (!rval)
2228 return NULL;
2229 RETRACT(&ss->sprinter, rval);
2230 js_printf(jp, "\t%s:\n", rval);
2231 jp->indent += 4;
2232 break;
2234 case SRC_LABELBRACE:
2235 GET_SOURCE_NOTE_ATOM(sn, atom);
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_ENDBRACE:
2245 jp->indent -= 4;
2246 js_printf(jp, "\t}\n");
2247 break;
2249 case SRC_FUNCDEF:
2250 fun = jp->script->getFunction(js_GetSrcNoteOffset(sn, 0));
2251 do_function:
2252 js_puts(jp, "\n");
2253 jp2 = js_NewPrinter(cx, "nested_function", fun,
2254 jp->indent, jp->pretty, jp->grouped,
2255 jp->strict);
2256 if (!jp2)
2257 return NULL;
2258 ok = js_DecompileFunction(jp2);
2259 if (ok && jp2->sprinter.base)
2260 js_puts(jp, jp2->sprinter.base);
2261 js_DestroyPrinter(jp2);
2262 if (!ok)
2263 return NULL;
2264 js_puts(jp, "\n\n");
2265 break;
2267 case SRC_BRACE:
2268 js_printf(jp, "\t{\n");
2269 jp->indent += 4;
2270 len = js_GetSrcNoteOffset(sn, 0);
2271 DECOMPILE_CODE(pc + oplen, len - oplen);
2272 jp->indent -= 4;
2273 js_printf(jp, "\t}\n");
2274 break;
2276 default:;
2278 break;
2280 case JSOP_PUSH:
2281 #if JS_HAS_DESTRUCTURING
2282 sn = js_GetSrcNote(jp->script, pc);
2283 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
2284 pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo);
2285 if (!pc)
2286 return NULL;
2287 LOCAL_ASSERT(*pc == JSOP_POPN);
2288 len = oplen = JSOP_POPN_LENGTH;
2289 goto end_groupassignment;
2291 #endif
2292 /* FALL THROUGH */
2294 case JSOP_BINDNAME:
2295 todo = Sprint(&ss->sprinter, "");
2296 break;
2298 case JSOP_TRY:
2299 js_printf(jp, "\ttry {\n");
2300 jp->indent += 4;
2301 todo = -2;
2302 break;
2304 case JSOP_FINALLY:
2305 jp->indent -= 4;
2306 js_printf(jp, "\t} finally {\n");
2307 jp->indent += 4;
2310 * We push push the pair of exception/restsub cookies to
2311 * simulate the effects [gosub] or control transfer during
2312 * exception capturing on the stack.
2314 todo = Sprint(&ss->sprinter, exception_cookie);
2315 if (todo < 0 || !PushOff(ss, todo, op))
2316 return NULL;
2317 todo = Sprint(&ss->sprinter, retsub_pc_cookie);
2318 break;
2320 case JSOP_RETSUB:
2321 rval = POP_STR();
2322 LOCAL_ASSERT(strcmp(rval, retsub_pc_cookie) == 0);
2323 lval = POP_STR();
2324 LOCAL_ASSERT(strcmp(lval, exception_cookie) == 0);
2325 todo = -2;
2326 break;
2328 case JSOP_GOSUB:
2329 case JSOP_GOSUBX:
2331 * JSOP_GOSUB and GOSUBX have no effect on the decompiler's
2332 * string stack because the next op in bytecode order finds
2333 * the stack balanced by a JSOP_RETSUB executed elsewhere.
2335 todo = -2;
2336 break;
2338 case JSOP_POPN:
2340 uintN newtop, oldtop;
2343 * The compiler models operand stack depth and fixes the stack
2344 * pointer on entry to a catch clause based on its depth model.
2345 * The decompiler must match the code generator's model, which
2346 * is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops.
2348 oldtop = ss->top;
2349 newtop = oldtop - GET_UINT16(pc);
2350 LOCAL_ASSERT(newtop <= oldtop);
2351 todo = -2;
2353 sn = js_GetSrcNote(jp->script, pc);
2354 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
2355 break;
2356 #if JS_HAS_DESTRUCTURING
2357 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
2358 todo = Sprint(&ss->sprinter, "%s[] = [",
2359 VarPrefix(sn));
2360 if (todo < 0)
2361 return NULL;
2362 for (uintN i = newtop; i < oldtop; i++) {
2363 rval = OFF2STR(&ss->sprinter, ss->offsets[i]);
2364 if (Sprint(&ss->sprinter, ss_format,
2365 (i == newtop) ? "" : ", ",
2366 (i == oldtop - 1 && *rval == '\0')
2367 ? ", " : rval) < 0) {
2368 return NULL;
2371 if (SprintPut(&ss->sprinter, "]", 1) < 0)
2372 return NULL;
2375 * If this is an empty group assignment, we have no stack
2376 * budget into which we can push our result string. Adjust
2377 * ss->sprinter.offset so that our consumer can find the
2378 * empty group assignment decompilation.
2380 if (newtop == oldtop) {
2381 ss->sprinter.offset = todo;
2382 } else {
2384 * Kill newtop before the end_groupassignment: label by
2385 * retracting/popping early. Control will either jump
2386 * to do_forloop: or do_letheadbody: or else break from
2387 * our case JSOP_POPN: after the switch (*pc2) below.
2389 LOCAL_ASSERT(newtop < oldtop);
2390 ss->sprinter.offset = GetOff(ss, newtop);
2391 ss->top = newtop;
2394 end_groupassignment:
2395 LOCAL_ASSERT(*pc == JSOP_POPN);
2398 * Thread directly to the next opcode if we can, to handle
2399 * the special cases of a group assignment in the first or
2400 * last part of a for(;;) loop head, or in a let block or
2401 * expression head.
2403 * NB: todo at this point indexes space in ss->sprinter
2404 * that is liable to be overwritten. The code below knows
2405 * exactly how long rval lives, or else copies it down via
2406 * SprintCString.
2408 rval = OFF2STR(&ss->sprinter, todo);
2409 todo = -2;
2410 pc2 = pc + oplen;
2411 if (*pc2 == JSOP_NOP) {
2412 sn = js_GetSrcNote(jp->script, pc2);
2413 if (sn) {
2414 if (SN_TYPE(sn) == SRC_FOR) {
2415 op = JSOP_NOP;
2416 pc = pc2;
2417 goto do_forloop;
2420 if (SN_TYPE(sn) == SRC_DECL) {
2421 if (ss->top == StackDepth(jp->script)) {
2423 * This must be an empty destructuring
2424 * in the head of a let whose body block
2425 * is also empty.
2427 pc = pc2 + JSOP_NOP_LENGTH;
2428 len = js_GetSrcNoteOffset(sn, 0);
2429 LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCK);
2430 js_printf(jp, "\tlet (%s) {\n", rval);
2431 js_printf(jp, "\t}\n");
2432 break;
2434 todo = SprintCString(&ss->sprinter, rval);
2435 if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
2436 return NULL;
2437 op = JSOP_POP;
2438 pc = pc2 + JSOP_NOP_LENGTH;
2439 goto do_letheadbody;
2441 } else {
2443 * An unnannotated NOP following a POPN must be the
2444 * third part of for(;;) loop head. If the POPN's
2445 * immediate operand is 0, then we may have no slot
2446 * on the sprint-stack in which to push our result
2447 * string. In this case the result can be recovered
2448 * at ss->sprinter.base + ss->sprinter.offset.
2450 if (GET_UINT16(pc) == 0)
2451 break;
2452 todo = SprintCString(&ss->sprinter, rval);
2453 saveop = JSOP_NOP;
2458 * If control flow reaches this point with todo still -2,
2459 * just print rval as an expression statement.
2461 if (todo == -2)
2462 js_printf(jp, "\t%s;\n", rval);
2463 break;
2465 #endif
2466 if (newtop < oldtop) {
2467 ss->sprinter.offset = GetOff(ss, newtop);
2468 ss->top = newtop;
2470 break;
2473 case JSOP_EXCEPTION:
2474 /* The catch decompiler handles this op itself. */
2475 LOCAL_ASSERT(JS_FALSE);
2476 break;
2478 case JSOP_POP:
2480 * By default, do not automatically parenthesize when popping
2481 * a stacked expression decompilation. We auto-parenthesize
2482 * only when JSOP_POP is annotated with SRC_PCDELTA, meaning
2483 * comma operator.
2485 op = JSOP_POPV;
2486 /* FALL THROUGH */
2488 case JSOP_POPV:
2489 sn = js_GetSrcNote(jp->script, pc);
2490 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
2491 case SRC_FOR:
2492 /* Force parens around 'in' expression at 'for' front. */
2493 if (ss->opcodes[ss->top-1] == JSOP_IN)
2494 op = JSOP_LSH;
2495 rval = POP_STR();
2496 todo = -2;
2497 goto do_forloop;
2499 case SRC_PCDELTA:
2500 /* Comma operator: use JSOP_POP for correct precedence. */
2501 op = JSOP_POP;
2503 /* Pop and save to avoid blowing stack depth budget. */
2504 lval = JS_strdup(cx, POP_STR());
2505 if (!lval)
2506 return NULL;
2509 * The offset tells distance to the end of the right-hand
2510 * operand of the comma operator.
2512 done = pc + len;
2513 pc += js_GetSrcNoteOffset(sn, 0);
2514 len = 0;
2516 if (!Decompile(ss, done, pc - done, JSOP_POP)) {
2517 cx->free((char *)lval);
2518 return NULL;
2521 /* Pop Decompile result and print comma expression. */
2522 rval = POP_STR();
2523 todo = Sprint(&ss->sprinter, "%s, %s", lval, rval);
2524 cx->free((char *)lval);
2525 break;
2527 case SRC_HIDDEN:
2529 * Hide this pop. Don't adjust our stack depth model if
2530 * it's from a goto in a with or for/in.
2532 todo = -2;
2533 if (lastop == JSOP_UNBRAND)
2534 (void) POP_STR();
2535 break;
2537 case SRC_DECL:
2538 /* This pop is at the end of the let block/expr head. */
2539 pc += JSOP_POP_LENGTH;
2540 #if JS_HAS_DESTRUCTURING
2541 do_letheadbody:
2542 #endif
2543 len = js_GetSrcNoteOffset(sn, 0);
2544 if (pc[len] == JSOP_LEAVEBLOCK) {
2545 js_printf(jp, "\tlet (%s) {\n", POP_STR());
2546 jp->indent += 4;
2547 DECOMPILE_CODE(pc, len);
2548 jp->indent -= 4;
2549 js_printf(jp, "\t}\n");
2550 todo = -2;
2551 } else {
2552 LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCKEXPR);
2554 lval = JS_strdup(cx, PopStr(ss, JSOP_NOP));
2555 if (!lval)
2556 return NULL;
2558 /* Set saveop to reflect what we will push. */
2559 saveop = JSOP_LEAVEBLOCKEXPR;
2560 if (!Decompile(ss, pc, len, saveop)) {
2561 cx->free((char *)lval);
2562 return NULL;
2564 rval = PopStr(ss, JSOP_SETNAME);
2565 todo = Sprint(&ss->sprinter,
2566 (*rval == '{')
2567 ? "let (%s) (%s)"
2568 : "let (%s) %s",
2569 lval, rval);
2570 cx->free((char *)lval);
2572 break;
2574 default:
2575 /* Turn off parens around a yield statement. */
2576 if (ss->opcodes[ss->top-1] == JSOP_YIELD)
2577 op = JSOP_NOP;
2579 rval = POP_STR();
2582 * Don't emit decompiler-pushed strings that are not
2583 * handled by other opcodes. They are pushed onto the
2584 * stack to help model the interpreter stack and should
2585 * not appear in the decompiler's output.
2587 if (*rval != '\0' && (rval[0] != '/' || rval[1] != '*')) {
2588 js_printf(jp,
2589 (*rval == '{' ||
2590 (strncmp(rval, js_function_str, 8) == 0 &&
2591 rval[8] == ' '))
2592 ? "\t(%s);\n"
2593 : "\t%s;\n",
2594 rval);
2595 } else {
2596 LOCAL_ASSERT(*rval == '\0' ||
2597 strcmp(rval, exception_cookie) == 0);
2599 todo = -2;
2600 break;
2602 sn = NULL;
2603 break;
2605 case JSOP_ENTERWITH:
2606 LOCAL_ASSERT(!js_GetSrcNote(jp->script, pc));
2607 rval = POP_STR();
2608 js_printf(jp, "\twith (%s) {\n", rval);
2609 jp->indent += 4;
2610 todo = Sprint(&ss->sprinter, with_cookie);
2611 break;
2613 case JSOP_LEAVEWITH:
2614 sn = js_GetSrcNote(jp->script, pc);
2615 todo = -2;
2616 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
2617 break;
2618 rval = POP_STR();
2619 LOCAL_ASSERT(strcmp(rval, with_cookie) == 0);
2620 jp->indent -= 4;
2621 js_printf(jp, "\t}\n");
2622 break;
2624 case JSOP_ENTERBLOCK:
2626 JSAtom **atomv, *smallv[5];
2627 JSScopeProperty *sprop;
2629 LOAD_OBJECT(0);
2630 argc = OBJ_BLOCK_COUNT(cx, obj);
2631 if ((size_t)argc <= JS_ARRAY_LENGTH(smallv)) {
2632 atomv = smallv;
2633 } else {
2634 atomv = (JSAtom **) cx->malloc(argc * sizeof(JSAtom *));
2635 if (!atomv)
2636 return NULL;
2639 MUST_FLOW_THROUGH("enterblock_out");
2640 #define LOCAL_ASSERT_OUT(expr) LOCAL_ASSERT_CUSTOM(expr, ok = JS_FALSE; \
2641 goto enterblock_out)
2642 for (sprop = OBJ_SCOPE(obj)->lastProperty(); sprop;
2643 sprop = sprop->parent) {
2644 if (!(sprop->flags & SPROP_HAS_SHORTID))
2645 continue;
2646 LOCAL_ASSERT_OUT(sprop->shortid < argc);
2647 atomv[sprop->shortid] = JSID_TO_ATOM(sprop->id);
2649 ok = JS_TRUE;
2650 for (i = 0; i < argc; i++) {
2651 atom = atomv[i];
2652 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2653 if (!rval ||
2654 !PushOff(ss, STR2OFF(&ss->sprinter, rval), op)) {
2655 ok = JS_FALSE;
2656 goto enterblock_out;
2660 sn = js_GetSrcNote(jp->script, pc);
2661 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
2662 #if JS_HAS_BLOCK_SCOPE
2663 case SRC_BRACE:
2664 js_printf(jp, "\t{\n");
2665 jp->indent += 4;
2666 len = js_GetSrcNoteOffset(sn, 0);
2667 ok = Decompile(ss, pc + oplen, len - oplen, JSOP_NOP)
2668 != NULL;
2669 if (!ok)
2670 goto enterblock_out;
2671 jp->indent -= 4;
2672 js_printf(jp, "\t}\n");
2673 break;
2674 #endif
2676 case SRC_CATCH:
2677 jp->indent -= 4;
2678 js_printf(jp, "\t} catch (");
2680 pc2 = pc;
2681 pc += oplen;
2682 LOCAL_ASSERT_OUT(*pc == JSOP_EXCEPTION);
2683 pc += JSOP_EXCEPTION_LENGTH;
2684 todo = Sprint(&ss->sprinter, exception_cookie);
2685 if (todo < 0 || !PushOff(ss, todo, JSOP_EXCEPTION)) {
2686 ok = JS_FALSE;
2687 goto enterblock_out;
2690 if (*pc == JSOP_DUP) {
2691 sn2 = js_GetSrcNote(jp->script, pc);
2692 if (!sn2 || SN_TYPE(sn2) != SRC_DESTRUCT) {
2694 * This is a dup to save the exception for later.
2695 * It is emitted only when the catch head contains
2696 * an exception guard.
2698 LOCAL_ASSERT_OUT(js_GetSrcNoteOffset(sn, 0) != 0);
2699 pc += JSOP_DUP_LENGTH;
2700 todo = Sprint(&ss->sprinter, exception_cookie);
2701 if (todo < 0 ||
2702 !PushOff(ss, todo, JSOP_EXCEPTION)) {
2703 ok = JS_FALSE;
2704 goto enterblock_out;
2709 #if JS_HAS_DESTRUCTURING
2710 if (*pc == JSOP_DUP) {
2711 pc = DecompileDestructuring(ss, pc, endpc);
2712 if (!pc) {
2713 ok = JS_FALSE;
2714 goto enterblock_out;
2716 LOCAL_ASSERT_OUT(*pc == JSOP_POP);
2717 pc += JSOP_POP_LENGTH;
2718 lval = PopStr(ss, JSOP_NOP);
2719 js_puts(jp, lval);
2720 } else {
2721 #endif
2722 LOCAL_ASSERT_OUT(*pc == JSOP_SETLOCALPOP);
2723 i = GET_SLOTNO(pc) - jp->script->nfixed;
2724 pc += JSOP_SETLOCALPOP_LENGTH;
2725 atom = atomv[i - OBJ_BLOCK_DEPTH(cx, obj)];
2726 str = ATOM_TO_STRING(atom);
2727 if (!QuoteString(&jp->sprinter, str, 0)) {
2728 ok = JS_FALSE;
2729 goto enterblock_out;
2731 #if JS_HAS_DESTRUCTURING
2733 #endif
2736 * Pop the exception_cookie (or its dup in the case of a
2737 * guarded catch head) off the stack now.
2739 rval = PopStr(ss, JSOP_NOP);
2740 LOCAL_ASSERT_OUT(strcmp(rval, exception_cookie) == 0);
2742 len = js_GetSrcNoteOffset(sn, 0);
2743 if (len) {
2744 len -= pc - pc2;
2745 LOCAL_ASSERT_OUT(len > 0);
2746 js_printf(jp, " if ");
2747 ok = Decompile(ss, pc, len, JSOP_NOP) != NULL;
2748 if (!ok)
2749 goto enterblock_out;
2750 js_printf(jp, "%s", POP_STR());
2751 pc += len;
2752 LOCAL_ASSERT_OUT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
2753 pc += js_CodeSpec[*pc].length;
2756 js_printf(jp, ") {\n");
2757 jp->indent += 4;
2758 len = 0;
2759 break;
2760 default:
2761 break;
2764 todo = -2;
2766 #undef LOCAL_ASSERT_OUT
2767 enterblock_out:
2768 if (atomv != smallv)
2769 cx->free(atomv);
2770 if (!ok)
2771 return NULL;
2773 break;
2775 case JSOP_LEAVEBLOCK:
2776 case JSOP_LEAVEBLOCKEXPR:
2778 uintN top, depth;
2780 sn = js_GetSrcNote(jp->script, pc);
2781 todo = -2;
2782 if (op == JSOP_LEAVEBLOCKEXPR) {
2783 LOCAL_ASSERT(SN_TYPE(sn) == SRC_PCBASE);
2784 rval = POP_STR();
2785 } else if (sn) {
2786 LOCAL_ASSERT(op == JSOP_LEAVEBLOCK);
2787 if (SN_TYPE(sn) == SRC_HIDDEN)
2788 break;
2791 * This JSOP_LEAVEBLOCK must be for a catch block. If sn's
2792 * offset does not equal the model stack depth, there must
2793 * be a copy of the exception value on the stack due to a
2794 * catch guard (see above, the JSOP_ENTERBLOCK + SRC_CATCH
2795 * case code).
2797 LOCAL_ASSERT(SN_TYPE(sn) == SRC_CATCH);
2798 if ((uintN)js_GetSrcNoteOffset(sn, 0) != ss->top) {
2799 LOCAL_ASSERT((uintN)js_GetSrcNoteOffset(sn, 0)
2800 == ss->top - 1);
2801 rval = POP_STR();
2802 LOCAL_ASSERT(strcmp(rval, exception_cookie) == 0);
2805 top = ss->top;
2806 depth = GET_UINT16(pc);
2807 LOCAL_ASSERT(top >= depth);
2808 top -= depth;
2809 ss->top = top;
2810 ss->sprinter.offset = GetOff(ss, top);
2811 if (op == JSOP_LEAVEBLOCKEXPR)
2812 todo = SprintCString(&ss->sprinter, rval);
2813 break;
2816 case JSOP_GETUPVAR:
2817 case JSOP_CALLUPVAR:
2818 case JSOP_GETUPVAR_DBG:
2819 case JSOP_CALLUPVAR_DBG:
2820 case JSOP_GETDSLOT:
2821 case JSOP_CALLDSLOT:
2823 if (!jp->fun) {
2824 JS_ASSERT(jp->script->savedCallerFun);
2825 jp->fun = jp->script->getFunction(0);
2828 if (!jp->localNames)
2829 jp->localNames = js_GetLocalNameArray(cx, jp->fun, &jp->pool);
2831 uintN index = GET_UINT16(pc);
2832 if (index < jp->fun->u.i.nupvars) {
2833 index += jp->fun->countArgsAndVars();
2834 } else {
2835 JSUpvarArray *uva;
2836 #ifdef DEBUG
2838 * We must be in an eval called from jp->fun, where
2839 * jp->script is the eval-compiled script.
2841 * However, it's possible that a js_Invoke already
2842 * pushed a frame trying to call js_Construct on an
2843 * object that's not a constructor, causing us to be
2844 * called with an intervening frame on the stack.
2846 JSStackFrame *fp = js_GetTopStackFrame(cx);
2847 if (fp) {
2848 while (!(fp->flags & JSFRAME_EVAL))
2849 fp = fp->down;
2850 JS_ASSERT(fp->script == jp->script);
2851 JS_ASSERT(fp->down->fun == jp->fun);
2852 JS_ASSERT(FUN_INTERPRETED(jp->fun));
2853 JS_ASSERT(jp->script != jp->fun->u.i.script);
2854 JS_ASSERT(jp->script->upvarsOffset != 0);
2856 #endif
2857 uva = jp->script->upvars();
2858 index = UPVAR_FRAME_SLOT(uva->vector[index]);
2860 atom = GetArgOrVarAtom(jp, index);
2861 goto do_name;
2864 case JSOP_CALLLOCAL:
2865 case JSOP_GETLOCAL:
2866 if (IsVarSlot(jp, pc, &i)) {
2867 atom = GetArgOrVarAtom(jp, i);
2868 LOCAL_ASSERT(atom);
2869 goto do_name;
2871 LOCAL_ASSERT((uintN)i < ss->top);
2872 sn = js_GetSrcNote(jp->script, pc);
2874 #if JS_HAS_DESTRUCTURING
2875 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
2877 * Distinguish a js_DecompileValueGenerator call that
2878 * targets op alone, from decompilation of a full group
2879 * assignment sequence, triggered by SRC_GROUPASSIGN
2880 * annotating the first JSOP_GETLOCAL in the sequence.
2882 if (endpc - pc > JSOP_GETLOCAL_LENGTH || pc > startpc) {
2883 pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo);
2884 if (!pc)
2885 return NULL;
2886 LOCAL_ASSERT(*pc == JSOP_POPN);
2887 len = oplen = JSOP_POPN_LENGTH;
2888 goto end_groupassignment;
2891 /* Null sn to prevent bogus VarPrefix'ing below. */
2892 sn = NULL;
2894 #endif
2896 rval = GetLocal(ss, i);
2897 todo = Sprint(&ss->sprinter, ss_format, VarPrefix(sn), rval);
2898 break;
2900 case JSOP_SETLOCAL:
2901 case JSOP_SETLOCALPOP:
2902 if (IsVarSlot(jp, pc, &i)) {
2903 atom = GetArgOrVarAtom(jp, i);
2904 LOCAL_ASSERT(atom);
2905 goto do_setname;
2907 lval = GetLocal(ss, i);
2908 rval = POP_STR();
2909 goto do_setlval;
2911 case JSOP_INCLOCAL:
2912 case JSOP_DECLOCAL:
2913 if (IsVarSlot(jp, pc, &i)) {
2914 atom = GetArgOrVarAtom(jp, i);
2915 LOCAL_ASSERT(atom);
2916 goto do_incatom;
2918 lval = GetLocal(ss, i);
2919 goto do_inclval;
2921 case JSOP_LOCALINC:
2922 case JSOP_LOCALDEC:
2923 if (IsVarSlot(jp, pc, &i)) {
2924 atom = GetArgOrVarAtom(jp, i);
2925 LOCAL_ASSERT(atom);
2926 goto do_atominc;
2928 lval = GetLocal(ss, i);
2929 goto do_lvalinc;
2931 case JSOP_RETRVAL:
2932 todo = -2;
2933 break;
2935 case JSOP_RETURN:
2936 LOCAL_ASSERT(jp->fun);
2937 fun = jp->fun;
2938 if (fun->flags & JSFUN_EXPR_CLOSURE) {
2939 /* Turn on parens around comma-expression here. */
2940 op = JSOP_SETNAME;
2941 rval = POP_STR();
2942 js_printf(jp, (*rval == '{') ? "(%s)%s" : ss_format,
2943 rval,
2944 ((fun->flags & JSFUN_LAMBDA) || !fun->atom)
2945 ? ""
2946 : ";");
2947 todo = -2;
2948 break;
2950 /* FALL THROUGH */
2952 case JSOP_SETRVAL:
2953 rval = POP_STR();
2954 if (*rval != '\0')
2955 js_printf(jp, "\t%s %s;\n", js_return_str, rval);
2956 else
2957 js_printf(jp, "\t%s;\n", js_return_str);
2958 todo = -2;
2959 break;
2961 #if JS_HAS_GENERATORS
2962 case JSOP_YIELD:
2963 #if JS_HAS_GENERATOR_EXPRS
2964 if (!ss->inGenExp || !(sn = js_GetSrcNote(jp->script, pc)))
2965 #endif
2967 /* Turn off most parens. */
2968 op = JSOP_SETNAME;
2969 rval = POP_STR();
2970 todo = (*rval != '\0')
2971 ? Sprint(&ss->sprinter,
2972 (strncmp(rval, js_yield_str, 5) == 0 &&
2973 (rval[5] == ' ' || rval[5] == '\0'))
2974 ? "%s (%s)"
2975 : "%s %s",
2976 js_yield_str, rval)
2977 : SprintCString(&ss->sprinter, js_yield_str);
2978 break;
2981 #if JS_HAS_GENERATOR_EXPRS
2982 LOCAL_ASSERT(SN_TYPE(sn) == SRC_HIDDEN);
2983 /* FALL THROUGH */
2984 #endif
2986 case JSOP_ARRAYPUSH:
2988 uintN pos, forpos;
2989 ptrdiff_t start;
2991 /* Turn off most parens. */
2992 op = JSOP_SETNAME;
2994 /* Pop the expression being pushed or yielded. */
2995 rval = POP_STR();
2998 * Skip the for loop head stacked by JSOP_FORLOCAL until we hit
2999 * a block local slot (note empty destructuring patterns result
3000 * in unit-count blocks).
3002 pos = ss->top;
3003 while (pos != 0) {
3004 op = (JSOp) ss->opcodes[--pos];
3005 if (op == JSOP_ENTERBLOCK)
3006 break;
3008 JS_ASSERT(op == JSOP_ENTERBLOCK);
3011 * Here, forpos must index the space before the left-most |for|
3012 * in the single string of accumulated |for| heads and optional
3013 * final |if (condition)|.
3015 forpos = pos + 2;
3016 LOCAL_ASSERT(forpos < ss->top);
3019 * Now move pos downward over the block's local slots. Even an
3020 * empty destructuring pattern has one (dummy) local.
3022 while (ss->opcodes[pos] == JSOP_ENTERBLOCK) {
3023 if (pos == 0)
3024 break;
3025 --pos;
3028 #if JS_HAS_GENERATOR_EXPRS
3029 if (saveop == JSOP_YIELD) {
3031 * Generator expression: decompile just rval followed by
3032 * the string starting at forpos. Leave the result string
3033 * in ss->offsets[0] so it can be recovered by our caller
3034 * (the JSOP_ANONFUNOBJ with SRC_GENEXP case). Bump the
3035 * top of stack to balance yield, which is an expression
3036 * (so has neutral stack balance).
3038 LOCAL_ASSERT(pos == 0);
3039 xval = OFF2STR(&ss->sprinter, ss->offsets[forpos]);
3040 ss->sprinter.offset = PAREN_SLOP;
3041 todo = Sprint(&ss->sprinter, ss_format, rval, xval);
3042 if (todo < 0)
3043 return NULL;
3044 ss->offsets[0] = todo;
3045 ++ss->top;
3046 return pc;
3048 #endif /* JS_HAS_GENERATOR_EXPRS */
3051 * Array comprehension: retract the sprinter to the beginning
3052 * of the array initialiser and decompile "[<rval> for ...]".
3054 JS_ASSERT(jp->script->nfixed + pos == GET_UINT16(pc));
3055 LOCAL_ASSERT(ss->opcodes[pos] == JSOP_NEWINIT);
3057 start = ss->offsets[pos];
3058 LOCAL_ASSERT(ss->sprinter.base[start] == '[' ||
3059 ss->sprinter.base[start] == '#');
3060 LOCAL_ASSERT(forpos < ss->top);
3061 xval = OFF2STR(&ss->sprinter, ss->offsets[forpos]);
3062 lval = OFF2STR(&ss->sprinter, start);
3063 RETRACT(&ss->sprinter, lval);
3065 todo = Sprint(&ss->sprinter, sss_format, lval, rval, xval);
3066 if (todo < 0)
3067 return NULL;
3068 ss->offsets[pos] = todo;
3069 todo = -2;
3070 break;
3072 #endif /* JS_HAS_GENERATORS */
3074 case JSOP_THROWING:
3075 todo = -2;
3076 break;
3078 case JSOP_THROW:
3079 sn = js_GetSrcNote(jp->script, pc);
3080 todo = -2;
3081 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
3082 break;
3083 rval = POP_STR();
3084 js_printf(jp, "\t%s %s;\n", js_throw_str, rval);
3085 break;
3087 case JSOP_ITER:
3088 foreach = (pc[1] & (JSITER_FOREACH | JSITER_KEYVALUE)) ==
3089 JSITER_FOREACH;
3090 todo = SprintCString(&ss->sprinter, iter_cookie);
3091 break;
3093 case JSOP_NEXTITER:
3094 JS_NOT_REACHED("JSOP_NEXTITER");
3095 break;
3097 case JSOP_ENDITER:
3098 sn = js_GetSrcNote(jp->script, pc);
3099 todo = -2;
3100 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
3101 break;
3102 (void) PopOff(ss, op);
3103 (void) PopOff(ss, op);
3104 break;
3106 case JSOP_GOTO:
3107 case JSOP_GOTOX:
3108 sn = js_GetSrcNote(jp->script, pc);
3109 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
3110 case SRC_FOR_IN:
3112 * The loop back-edge carries +1 stack balance, for the
3113 * flag processed by JSOP_IFNE. We do not decompile the
3114 * JSOP_IFNE, and instead push the left-hand side of 'in'
3115 * after the loop edge in this stack slot (the JSOP_FOR*
3116 * opcodes' decompilers do this pushing).
3118 cond = GetJumpOffset(pc, pc);
3119 next = js_GetSrcNoteOffset(sn, 0);
3120 tail = js_GetSrcNoteOffset(sn, 1);
3121 JS_ASSERT(pc[cond] == JSOP_NEXTITER);
3122 DECOMPILE_CODE(pc + oplen, next - oplen);
3123 lval = POP_STR();
3124 LOCAL_ASSERT(ss->top >= 2);
3126 if (ss->inArrayInit || ss->inGenExp) {
3127 (void) PopOff(ss, JSOP_NOP);
3128 rval = TOP_STR();
3129 if (ss->top >= 2 && ss->opcodes[ss->top - 2] == JSOP_FORLOCAL) {
3130 ss->sprinter.offset = ss->offsets[ss->top - 1] - PAREN_SLOP;
3131 if (Sprint(&ss->sprinter, " %s (%s in %s)",
3132 foreach ? js_for_each_str : js_for_str,
3133 lval, rval) < 0) {
3134 return NULL;
3138 * Do not AddParentSlop here, as we will push the
3139 * top-most offset again, which will add paren slop
3140 * for us. We must push to balance the stack budget
3141 * when nesting for heads in a comprehension.
3143 todo = ss->offsets[ss->top - 1];
3144 } else {
3145 todo = Sprint(&ss->sprinter, " %s (%s in %s)",
3146 foreach ? js_for_each_str : js_for_str,
3147 lval, rval);
3149 if (todo < 0 || !PushOff(ss, todo, JSOP_FORLOCAL))
3150 return NULL;
3151 DECOMPILE_CODE(pc + next, cond - next);
3152 } else {
3154 * As above, rval or an extension of it must remain
3155 * stacked during loop body decompilation.
3157 rval = GetStr(ss, ss->top - 2);
3158 js_printf(jp, "\t%s (%s in %s) {\n",
3159 foreach ? js_for_each_str : js_for_str,
3160 lval, rval);
3161 jp->indent += 4;
3162 DECOMPILE_CODE(pc + next, cond - next);
3163 jp->indent -= 4;
3164 js_printf(jp, "\t}\n");
3167 pc += tail;
3168 LOCAL_ASSERT(*pc == JSOP_IFNE || *pc == JSOP_IFNEX);
3169 len = js_CodeSpec[*pc].length;
3170 break;
3172 case SRC_WHILE:
3173 cond = GetJumpOffset(pc, pc);
3174 tail = js_GetSrcNoteOffset(sn, 0);
3175 DECOMPILE_CODE(pc + cond, tail - cond);
3176 js_printf(jp, "\twhile (%s) {\n", POP_COND_STR());
3177 jp->indent += 4;
3178 DECOMPILE_CODE(pc + oplen, cond - oplen);
3179 jp->indent -= 4;
3180 js_printf(jp, "\t}\n");
3181 pc += tail;
3182 LOCAL_ASSERT(*pc == JSOP_IFNE || *pc == JSOP_IFNEX);
3183 len = js_CodeSpec[*pc].length;
3184 todo = -2;
3185 break;
3187 case SRC_CONT2LABEL:
3188 GET_SOURCE_NOTE_ATOM(sn, atom);
3189 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3190 if (!rval)
3191 return NULL;
3192 RETRACT(&ss->sprinter, rval);
3193 js_printf(jp, "\tcontinue %s;\n", rval);
3194 break;
3196 case SRC_CONTINUE:
3197 js_printf(jp, "\tcontinue;\n");
3198 break;
3200 case SRC_BREAK2LABEL:
3201 GET_SOURCE_NOTE_ATOM(sn, atom);
3202 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3203 if (!rval)
3204 return NULL;
3205 RETRACT(&ss->sprinter, rval);
3206 js_printf(jp, "\tbreak %s;\n", rval);
3207 break;
3209 case SRC_HIDDEN:
3210 break;
3212 default:
3213 js_printf(jp, "\tbreak;\n");
3214 break;
3216 todo = -2;
3217 break;
3219 case JSOP_IFEQ:
3220 case JSOP_IFEQX:
3222 JSBool elseif = JS_FALSE;
3224 if_again:
3225 len = GetJumpOffset(pc, pc);
3226 sn = js_GetSrcNote(jp->script, pc);
3228 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
3229 case SRC_IF:
3230 case SRC_IF_ELSE:
3231 rval = POP_COND_STR();
3232 if (ss->inArrayInit || ss->inGenExp) {
3233 LOCAL_ASSERT(SN_TYPE(sn) == SRC_IF);
3234 ss->sprinter.offset -= PAREN_SLOP;
3235 if (Sprint(&ss->sprinter, " if (%s)", rval) < 0)
3236 return NULL;
3237 AddParenSlop(ss);
3238 } else {
3239 js_printf(jp,
3240 elseif ? " if (%s) {\n" : "\tif (%s) {\n",
3241 rval);
3242 jp->indent += 4;
3245 if (SN_TYPE(sn) == SRC_IF) {
3246 DECOMPILE_CODE(pc + oplen, len - oplen);
3247 } else {
3248 LOCAL_ASSERT(!ss->inArrayInit && !ss->inGenExp);
3249 tail = js_GetSrcNoteOffset(sn, 0);
3250 DECOMPILE_CODE(pc + oplen, tail - oplen);
3251 jp->indent -= 4;
3252 pc += tail;
3253 LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX);
3254 oplen = js_CodeSpec[*pc].length;
3255 len = GetJumpOffset(pc, pc);
3256 js_printf(jp, "\t} else");
3259 * If the second offset for sn is non-zero, it tells
3260 * the distance from the goto around the else, to the
3261 * ifeq for the if inside the else that forms an "if
3262 * else if" chain. Thus cond spans the condition of
3263 * the second if, so we simply decompile it and start
3264 * over at label if_again.
3266 cond = js_GetSrcNoteOffset(sn, 1);
3267 if (cond != 0) {
3268 DECOMPILE_CODE(pc + oplen, cond - oplen);
3269 pc += cond;
3270 elseif = JS_TRUE;
3271 goto if_again;
3274 js_printf(jp, " {\n");
3275 jp->indent += 4;
3276 DECOMPILE_CODE(pc + oplen, len - oplen);
3279 if (!ss->inArrayInit && !ss->inGenExp) {
3280 jp->indent -= 4;
3281 js_printf(jp, "\t}\n");
3283 todo = -2;
3284 break;
3286 case SRC_COND:
3287 xval = JS_strdup(cx, POP_STR());
3288 if (!xval)
3289 return NULL;
3290 len = js_GetSrcNoteOffset(sn, 0);
3291 DECOMPILE_CODE(pc + oplen, len - oplen);
3292 lval = JS_strdup(cx, POP_STR());
3293 if (!lval) {
3294 cx->free((void *)xval);
3295 return NULL;
3297 pc += len;
3298 LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX);
3299 oplen = js_CodeSpec[*pc].length;
3300 len = GetJumpOffset(pc, pc);
3301 DECOMPILE_CODE(pc + oplen, len - oplen);
3302 rval = POP_STR();
3303 todo = Sprint(&ss->sprinter, "%s ? %s : %s",
3304 xval, lval, rval);
3305 cx->free((void *)xval);
3306 cx->free((void *)lval);
3307 break;
3309 default:
3310 break;
3312 break;
3315 case JSOP_IFNE:
3316 case JSOP_IFNEX:
3317 LOCAL_ASSERT(0);
3318 break;
3320 case JSOP_OR:
3321 case JSOP_ORX:
3322 xval = "||";
3324 do_logical_connective:
3325 /* Top of stack is the first clause in a disjunction (||). */
3326 lval = JS_strdup(cx, POP_STR());
3327 if (!lval)
3328 return NULL;
3329 done = pc + GetJumpOffset(pc, pc);
3330 pc += len;
3331 len = done - pc;
3332 if (!Decompile(ss, pc, len, op)) {
3333 cx->free((char *)lval);
3334 return NULL;
3336 rval = POP_STR();
3337 if (jp->pretty &&
3338 jp->indent + 4 + strlen(lval) + 4 + strlen(rval) > 75) {
3339 rval = JS_strdup(cx, rval);
3340 if (!rval) {
3341 tail = -1;
3342 } else {
3343 todo = Sprint(&ss->sprinter, "%s %s\n", lval, xval);
3344 tail = Sprint(&ss->sprinter, "%*s%s",
3345 jp->indent + 4, "", rval);
3346 cx->free((char *)rval);
3348 if (tail < 0)
3349 todo = -1;
3350 } else {
3351 todo = Sprint(&ss->sprinter, "%s %s %s", lval, xval, rval);
3353 cx->free((char *)lval);
3354 break;
3356 case JSOP_AND:
3357 case JSOP_ANDX:
3358 xval = "&&";
3359 goto do_logical_connective;
3361 case JSOP_FORARG:
3362 sn = NULL;
3363 i = GET_ARGNO(pc);
3364 goto do_forvarslot;
3366 case JSOP_FORLOCAL:
3367 sn = js_GetSrcNote(jp->script, pc);
3368 if (!IsVarSlot(jp, pc, &i)) {
3369 JS_ASSERT(op == JSOP_FORLOCAL);
3370 todo = Sprint(&ss->sprinter, ss_format, VarPrefix(sn), GetStr(ss, i));
3371 break;
3374 do_forvarslot:
3375 atom = GetArgOrVarAtom(jp, i);
3376 LOCAL_ASSERT(atom);
3377 todo = SprintCString(&ss->sprinter, VarPrefix(sn));
3378 if (todo < 0 || !QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0))
3379 return NULL;
3380 break;
3382 case JSOP_FORNAME:
3383 LOAD_ATOM(0);
3384 sn = js_GetSrcNote(jp->script, pc);
3385 todo = SprintCString(&ss->sprinter, VarPrefix(sn));
3386 if (todo < 0 || !QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0))
3387 return NULL;
3388 break;
3390 case JSOP_FORPROP:
3391 xval = NULL;
3392 LOAD_ATOM(0);
3393 if (!ATOM_IS_IDENTIFIER(atom)) {
3394 xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
3395 (jschar)'\'');
3396 if (!xval)
3397 return NULL;
3399 lval = POP_STR();
3400 if (xval) {
3401 JS_ASSERT(*lval);
3402 todo = Sprint(&ss->sprinter, index_format, lval, xval);
3403 } else {
3404 todo = Sprint(&ss->sprinter, ss_format, lval, *lval ? "." : "");
3405 if (todo < 0)
3406 return NULL;
3407 if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0))
3408 return NULL;
3410 break;
3412 case JSOP_FORELEM:
3413 todo = SprintCString(&ss->sprinter, forelem_cookie);
3414 break;
3416 case JSOP_ENUMELEM:
3417 case JSOP_ENUMCONSTELEM:
3419 * The stack has the object under the (top) index expression.
3420 * The "rval" property id is underneath those two on the stack.
3421 * The for loop body net and gross lengths can now be adjusted
3422 * to account for the length of the indexing expression that
3423 * came after JSOP_FORELEM and before JSOP_ENUMELEM.
3425 atom = NULL;
3426 op = JSOP_NOP; /* turn off parens around xval */
3427 xval = POP_STR();
3428 op = JSOP_GETELEM; /* lval must have high precedence */
3429 lval = POP_STR();
3430 op = saveop;
3431 rval = POP_STR();
3432 LOCAL_ASSERT(strcmp(rval, forelem_cookie) == 0);
3433 if (*xval == '\0') {
3434 todo = SprintCString(&ss->sprinter, lval);
3435 } else {
3436 todo = Sprint(&ss->sprinter,
3437 (JOF_OPMODE(lastop) == JOF_XMLNAME)
3438 ? dot_format
3439 : index_format,
3440 lval, xval);
3442 break;
3444 #if JS_HAS_GETTER_SETTER
3445 case JSOP_GETTER:
3446 case JSOP_SETTER:
3447 todo = -2;
3448 break;
3449 #endif
3451 case JSOP_DUP2:
3452 rval = GetStr(ss, ss->top-2);
3453 todo = SprintCString(&ss->sprinter, rval);
3454 if (todo < 0 || !PushOff(ss, todo,
3455 (JSOp) ss->opcodes[ss->top-2])) {
3456 return NULL;
3458 /* FALL THROUGH */
3460 case JSOP_DUP:
3461 #if JS_HAS_DESTRUCTURING
3462 sn = js_GetSrcNote(jp->script, pc);
3463 if (sn) {
3464 LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT);
3465 pc = DecompileDestructuring(ss, pc, endpc);
3466 if (!pc)
3467 return NULL;
3468 len = 0;
3469 lval = POP_STR();
3470 op = saveop = JSOP_ENUMELEM;
3471 rval = POP_STR();
3473 if (strcmp(rval, forelem_cookie) == 0) {
3474 todo = Sprint(&ss->sprinter, ss_format,
3475 VarPrefix(sn), lval);
3477 // Skip POP so the SRC_FOR_IN code can pop for itself.
3478 if (*pc == JSOP_POP)
3479 len = JSOP_POP_LENGTH;
3480 } else {
3481 todo = Sprint(&ss->sprinter, "%s%s = %s",
3482 VarPrefix(sn), lval, rval);
3484 break;
3486 #endif
3488 rval = GetStr(ss, ss->top-1);
3489 saveop = (JSOp) ss->opcodes[ss->top-1];
3490 todo = SprintCString(&ss->sprinter, rval);
3491 break;
3493 case JSOP_SETARG:
3494 atom = GetArgOrVarAtom(jp, GET_ARGNO(pc));
3495 LOCAL_ASSERT(atom);
3496 goto do_setname;
3498 case JSOP_SETCONST:
3499 case JSOP_SETNAME:
3500 case JSOP_SETGVAR:
3501 LOAD_ATOM(0);
3503 do_setname:
3504 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3505 if (!lval)
3506 return NULL;
3507 rval = POP_STR();
3508 if (op == JSOP_SETNAME)
3509 (void) PopOff(ss, op);
3511 do_setlval:
3512 sn = js_GetSrcNote(jp->script, pc - 1);
3513 if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
3514 todo = Sprint(&ss->sprinter, "%s %s= %s",
3515 lval,
3516 (lastop == JSOP_GETTER)
3517 ? js_getter_str
3518 : (lastop == JSOP_SETTER)
3519 ? js_setter_str
3520 : CodeToken[lastop],
3521 rval);
3522 } else {
3523 sn = js_GetSrcNote(jp->script, pc);
3524 todo = Sprint(&ss->sprinter, "%s%s = %s",
3525 VarPrefix(sn), lval, rval);
3527 if (op == JSOP_SETLOCALPOP) {
3528 if (!PushOff(ss, todo, saveop))
3529 return NULL;
3530 rval = POP_STR();
3531 LOCAL_ASSERT(*rval != '\0');
3532 js_printf(jp, "\t%s;\n", rval);
3533 todo = -2;
3535 break;
3537 case JSOP_CONCATN:
3539 argc = GET_UINT16(pc);
3540 JS_ASSERT(argc > 0);
3542 js::Vector<char *> argv(cx);
3543 if (!argv.resize(argc))
3544 return NULL;
3546 MUST_FLOW_THROUGH("out");
3547 ok = JS_FALSE;
3549 for (i = argc - 1; i >= 0; i--) {
3550 argv[i] = JS_strdup(cx, POP_STR());
3551 if (!argv[i])
3552 goto out;
3555 todo = Sprint(&ss->sprinter, "%s", argv[0]);
3556 if (todo < 0)
3557 goto out;
3558 for (i = 1; i < argc; i++) {
3559 if (Sprint(&ss->sprinter, " + %s", argv[i]) < 0)
3560 goto out;
3564 * The only way that our next op could be a JSOP_ADD is
3565 * if we are about to concatenate at least one non-string
3566 * literal. Deal with that here in order to avoid extra
3567 * parentheses (because JSOP_ADD is left-associative).
3569 if (pc[len] == JSOP_ADD)
3570 saveop = JSOP_NOP;
3572 ok = JS_TRUE;
3574 out:
3575 for (i = 0; i < argc; i++)
3576 JS_free(cx, argv[i]);
3577 if (!ok)
3578 return NULL;
3579 break;
3582 case JSOP_NEW:
3583 case JSOP_CALL:
3584 case JSOP_EVAL:
3585 case JSOP_APPLY:
3586 case JSOP_SETCALL:
3587 argc = GET_ARGC(pc);
3588 argv = (char **)
3589 cx->malloc((size_t)(argc + 1) * sizeof *argv);
3590 if (!argv)
3591 return NULL;
3593 op = JSOP_SETNAME;
3594 ok = JS_TRUE;
3595 for (i = argc; i > 0; i--)
3596 argv[i] = JS_strdup(cx, POP_STR());
3598 /* Skip the JSOP_PUSHOBJ-created empty string. */
3599 LOCAL_ASSERT(ss->top >= 2);
3600 (void) PopOff(ss, op);
3603 * Special case: new (x(y)(z)) must be parenthesized like so.
3604 * Same for new (x(y).z) -- contrast with new x(y).z.
3605 * See PROPAGATE_CALLNESS.
3607 op = (JSOp) ss->opcodes[ss->top - 1];
3608 lval = PopStr(ss,
3609 (saveop == JSOP_NEW &&
3610 (op == JSOP_CALL ||
3611 op == JSOP_EVAL ||
3612 op == JSOP_APPLY ||
3613 (js_CodeSpec[op].format & JOF_CALLOP)))
3614 ? JSOP_NAME
3615 : saveop);
3616 op = saveop;
3618 argv[0] = JS_strdup(cx, lval);
3619 if (!argv[0])
3620 ok = JS_FALSE;
3622 lval = "(", rval = ")";
3623 if (op == JSOP_NEW) {
3624 if (argc == 0)
3625 lval = rval = "";
3626 todo = Sprint(&ss->sprinter, "%s %s%s",
3627 js_new_str, argv[0], lval);
3628 } else {
3629 todo = Sprint(&ss->sprinter, ss_format,
3630 argv[0], lval);
3632 if (todo < 0)
3633 ok = JS_FALSE;
3635 for (i = 1; i <= argc; i++) {
3636 if (!argv[i] ||
3637 Sprint(&ss->sprinter, ss_format,
3638 argv[i], (i < argc) ? ", " : "") < 0) {
3639 ok = JS_FALSE;
3640 break;
3643 if (Sprint(&ss->sprinter, rval) < 0)
3644 ok = JS_FALSE;
3646 for (i = 0; i <= argc; i++)
3647 cx->free(argv[i]);
3648 cx->free(argv);
3649 if (!ok)
3650 return NULL;
3651 if (op == JSOP_SETCALL) {
3652 if (!PushOff(ss, todo, op))
3653 return NULL;
3654 todo = Sprint(&ss->sprinter, "");
3656 break;
3658 case JSOP_DELNAME:
3659 LOAD_ATOM(0);
3660 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3661 if (!lval)
3662 return NULL;
3663 RETRACT(&ss->sprinter, lval);
3664 do_delete_lval:
3665 todo = Sprint(&ss->sprinter, "%s %s", js_delete_str, lval);
3666 break;
3668 case JSOP_DELPROP:
3669 GET_ATOM_QUOTE_AND_FMT("%s %s[%s]", "%s %s.%s", rval);
3670 op = JSOP_GETPROP;
3671 lval = POP_STR();
3672 todo = Sprint(&ss->sprinter, fmt, js_delete_str, lval, rval);
3673 break;
3675 case JSOP_DELELEM:
3676 op = JSOP_NOP; /* turn off parens */
3677 xval = POP_STR();
3678 op = JSOP_GETPROP;
3679 lval = POP_STR();
3680 if (*xval == '\0')
3681 goto do_delete_lval;
3682 todo = Sprint(&ss->sprinter,
3683 (JOF_OPMODE(lastop) == JOF_XMLNAME)
3684 ? "%s %s.%s"
3685 : "%s %s[%s]",
3686 js_delete_str, lval, xval);
3687 break;
3689 #if JS_HAS_XML_SUPPORT
3690 case JSOP_DELDESC:
3691 xval = POP_STR();
3692 op = JSOP_GETPROP;
3693 lval = POP_STR();
3694 todo = Sprint(&ss->sprinter, "%s %s..%s",
3695 js_delete_str, lval, xval);
3696 break;
3697 #endif
3699 case JSOP_TYPEOFEXPR:
3700 case JSOP_TYPEOF:
3701 case JSOP_VOID:
3702 rval = POP_STR();
3703 todo = Sprint(&ss->sprinter, "%s %s",
3704 (op == JSOP_VOID) ? js_void_str : js_typeof_str,
3705 rval);
3706 break;
3708 case JSOP_INCARG:
3709 case JSOP_DECARG:
3710 atom = GetArgOrVarAtom(jp, GET_ARGNO(pc));
3711 LOCAL_ASSERT(atom);
3712 goto do_incatom;
3714 case JSOP_INCNAME:
3715 case JSOP_DECNAME:
3716 case JSOP_INCGVAR:
3717 case JSOP_DECGVAR:
3718 LOAD_ATOM(0);
3719 do_incatom:
3720 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3721 if (!lval)
3722 return NULL;
3723 RETRACT(&ss->sprinter, lval);
3724 do_inclval:
3725 todo = Sprint(&ss->sprinter, ss_format,
3726 js_incop_strs[!(cs->format & JOF_INC)], lval);
3727 break;
3729 case JSOP_INCPROP:
3730 case JSOP_DECPROP:
3731 GET_ATOM_QUOTE_AND_FMT(preindex_format, predot_format, rval);
3734 * Force precedence below the numeric literal opcodes, so that
3735 * 42..foo or 10000..toString(16), e.g., decompile with parens
3736 * around the left-hand side of dot.
3738 op = JSOP_GETPROP;
3739 lval = POP_STR();
3740 todo = Sprint(&ss->sprinter, fmt,
3741 js_incop_strs[!(cs->format & JOF_INC)],
3742 lval, rval);
3743 break;
3745 case JSOP_INCELEM:
3746 case JSOP_DECELEM:
3747 op = JSOP_NOP; /* turn off parens */
3748 xval = POP_STR();
3749 op = JSOP_GETELEM;
3750 lval = POP_STR();
3751 if (*xval != '\0') {
3752 todo = Sprint(&ss->sprinter,
3753 (JOF_OPMODE(lastop) == JOF_XMLNAME)
3754 ? predot_format
3755 : preindex_format,
3756 js_incop_strs[!(cs->format & JOF_INC)],
3757 lval, xval);
3758 } else {
3759 todo = Sprint(&ss->sprinter, ss_format,
3760 js_incop_strs[!(cs->format & JOF_INC)], lval);
3762 break;
3764 case JSOP_ARGINC:
3765 case JSOP_ARGDEC:
3766 atom = GetArgOrVarAtom(jp, GET_ARGNO(pc));
3767 LOCAL_ASSERT(atom);
3768 goto do_atominc;
3770 case JSOP_NAMEINC:
3771 case JSOP_NAMEDEC:
3772 case JSOP_GVARINC:
3773 case JSOP_GVARDEC:
3774 LOAD_ATOM(0);
3775 do_atominc:
3776 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3777 if (!lval)
3778 return NULL;
3779 RETRACT(&ss->sprinter, lval);
3780 do_lvalinc:
3781 todo = Sprint(&ss->sprinter, ss_format,
3782 lval, js_incop_strs[!(cs->format & JOF_INC)]);
3783 break;
3785 case JSOP_PROPINC:
3786 case JSOP_PROPDEC:
3787 GET_ATOM_QUOTE_AND_FMT(postindex_format, postdot_format, rval);
3790 * Force precedence below the numeric literal opcodes, so that
3791 * 42..foo or 10000..toString(16), e.g., decompile with parens
3792 * around the left-hand side of dot.
3794 op = JSOP_GETPROP;
3795 lval = POP_STR();
3796 todo = Sprint(&ss->sprinter, fmt, lval, rval,
3797 js_incop_strs[!(cs->format & JOF_INC)]);
3798 break;
3800 case JSOP_ELEMINC:
3801 case JSOP_ELEMDEC:
3802 op = JSOP_NOP; /* turn off parens */
3803 xval = POP_STR();
3804 op = JSOP_GETELEM;
3805 lval = POP_STR();
3806 if (*xval != '\0') {
3807 todo = Sprint(&ss->sprinter,
3808 (JOF_OPMODE(lastop) == JOF_XMLNAME)
3809 ? postdot_format
3810 : postindex_format,
3811 lval, xval,
3812 js_incop_strs[!(cs->format & JOF_INC)]);
3813 } else {
3814 todo = Sprint(&ss->sprinter, ss_format,
3815 lval, js_incop_strs[!(cs->format & JOF_INC)]);
3817 break;
3819 case JSOP_LENGTH:
3820 fmt = dot_format;
3821 rval = js_length_str;
3822 goto do_getprop_lval;
3824 case JSOP_GETPROP2:
3825 op = JSOP_GETPROP;
3826 (void) PopOff(ss, lastop);
3827 /* FALL THROUGH */
3829 case JSOP_CALLPROP:
3830 case JSOP_GETPROP:
3831 case JSOP_GETXPROP:
3832 LOAD_ATOM(0);
3834 do_getprop:
3835 GET_QUOTE_AND_FMT(index_format, dot_format, rval);
3836 do_getprop_lval:
3837 PROPAGATE_CALLNESS();
3838 lval = POP_STR();
3839 todo = Sprint(&ss->sprinter, fmt, lval, rval);
3840 break;
3842 case JSOP_GETTHISPROP:
3843 LOAD_ATOM(0);
3844 GET_QUOTE_AND_FMT(index_format, dot_format, rval);
3845 todo = Sprint(&ss->sprinter, fmt, js_this_str, rval);
3846 break;
3848 case JSOP_GETARGPROP:
3849 /* Get the name of the argument or variable. */
3850 i = GET_ARGNO(pc);
3852 do_getarg_prop:
3853 atom = GetArgOrVarAtom(ss->printer, i);
3854 LOCAL_ASSERT(atom);
3855 LOCAL_ASSERT(ATOM_IS_STRING(atom));
3856 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3857 if (!lval || !PushOff(ss, STR2OFF(&ss->sprinter, lval), op))
3858 return NULL;
3860 /* Get the name of the property. */
3861 LOAD_ATOM(ARGNO_LEN);
3862 goto do_getprop;
3864 case JSOP_GETLOCALPROP:
3865 if (IsVarSlot(jp, pc, &i))
3866 goto do_getarg_prop;
3867 LOCAL_ASSERT((uintN)i < ss->top);
3868 lval = GetLocal(ss, i);
3869 if (!lval)
3870 return NULL;
3871 todo = SprintCString(&ss->sprinter, lval);
3872 if (todo < 0 || !PushOff(ss, todo, op))
3873 return NULL;
3874 LOAD_ATOM(2);
3875 goto do_getprop;
3877 case JSOP_SETPROP:
3878 case JSOP_SETMETHOD:
3879 LOAD_ATOM(0);
3880 GET_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval);
3881 rval = POP_STR();
3884 * Force precedence below the numeric literal opcodes, so that
3885 * 42..foo or 10000..toString(16), e.g., decompile with parens
3886 * around the left-hand side of dot.
3888 op = JSOP_GETPROP;
3889 lval = POP_STR();
3890 sn = js_GetSrcNote(jp->script, pc - 1);
3891 todo = Sprint(&ss->sprinter, fmt, lval, xval,
3892 (sn && SN_TYPE(sn) == SRC_ASSIGNOP)
3893 ? (lastop == JSOP_GETTER)
3894 ? js_getter_str
3895 : (lastop == JSOP_SETTER)
3896 ? js_setter_str
3897 : CodeToken[lastop]
3898 : "",
3899 rval);
3900 break;
3902 case JSOP_GETELEM2:
3903 op = JSOP_GETELEM;
3904 (void) PopOff(ss, lastop);
3905 /* FALL THROUGH */
3907 case JSOP_CALLELEM:
3908 case JSOP_GETELEM:
3909 op = JSOP_NOP; /* turn off parens */
3910 xval = POP_STR();
3911 op = saveop;
3912 PROPAGATE_CALLNESS();
3913 lval = POP_STR();
3914 if (*xval == '\0') {
3915 todo = Sprint(&ss->sprinter, "%s", lval);
3916 } else {
3917 todo = Sprint(&ss->sprinter,
3918 (JOF_OPMODE(lastop) == JOF_XMLNAME)
3919 ? dot_format
3920 : index_format,
3921 lval, xval);
3923 break;
3925 case JSOP_SETELEM:
3926 rval = POP_STR();
3927 op = JSOP_NOP; /* turn off parens */
3928 xval = POP_STR();
3929 cs = &js_CodeSpec[ss->opcodes[ss->top]];
3930 op = JSOP_GETELEM; /* lval must have high precedence */
3931 lval = POP_STR();
3932 op = saveop;
3933 if (*xval == '\0')
3934 goto do_setlval;
3935 sn = js_GetSrcNote(jp->script, pc - 1);
3936 todo = Sprint(&ss->sprinter,
3937 (JOF_MODE(cs->format) == JOF_XMLNAME)
3938 ? "%s.%s %s= %s"
3939 : "%s[%s] %s= %s",
3940 lval, xval,
3941 (sn && SN_TYPE(sn) == SRC_ASSIGNOP)
3942 ? (lastop == JSOP_GETTER)
3943 ? js_getter_str
3944 : (lastop == JSOP_SETTER)
3945 ? js_setter_str
3946 : CodeToken[lastop]
3947 : "",
3948 rval);
3949 break;
3951 case JSOP_ARGSUB:
3952 i = (jsint) GET_ARGNO(pc);
3953 todo = Sprint(&ss->sprinter, "%s[%d]",
3954 js_arguments_str, (int) i);
3955 break;
3957 case JSOP_ARGCNT:
3958 todo = Sprint(&ss->sprinter, dot_format,
3959 js_arguments_str, js_length_str);
3960 break;
3962 case JSOP_CALLARG:
3963 case JSOP_GETARG:
3964 i = GET_ARGNO(pc);
3965 atom = GetArgOrVarAtom(jp, i);
3966 #if JS_HAS_DESTRUCTURING
3967 if (!atom) {
3968 todo = Sprint(&ss->sprinter, "%s[%d]", js_arguments_str, i);
3969 break;
3971 #else
3972 LOCAL_ASSERT(atom);
3973 #endif
3974 goto do_name;
3976 case JSOP_CALLNAME:
3977 case JSOP_NAME:
3978 case JSOP_GETGVAR:
3979 case JSOP_CALLGVAR:
3980 LOAD_ATOM(0);
3981 do_name:
3982 lval = "";
3983 #if JS_HAS_XML_SUPPORT
3984 do_qname:
3985 #endif
3986 sn = js_GetSrcNote(jp->script, pc);
3987 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
3988 inXML ? DONT_ESCAPE : 0);
3989 if (!rval)
3990 return NULL;
3991 RETRACT(&ss->sprinter, rval);
3992 todo = Sprint(&ss->sprinter, sss_format,
3993 VarPrefix(sn), lval, rval);
3994 break;
3996 case JSOP_UINT16:
3997 i = (jsint) GET_UINT16(pc);
3998 goto do_sprint_int;
4000 case JSOP_UINT24:
4001 i = (jsint) GET_UINT24(pc);
4002 goto do_sprint_int;
4004 case JSOP_INT8:
4005 i = GET_INT8(pc);
4006 goto do_sprint_int;
4008 case JSOP_INT32:
4009 i = GET_INT32(pc);
4010 do_sprint_int:
4011 todo = Sprint(&ss->sprinter, "%d", i);
4012 break;
4014 case JSOP_DOUBLE:
4015 GET_DOUBLE_FROM_BYTECODE(jp->script, pc, 0, atom);
4016 val = ATOM_KEY(atom);
4017 LOCAL_ASSERT(JSVAL_IS_DOUBLE(val));
4018 todo = SprintDoubleValue(&ss->sprinter, val, &saveop);
4019 break;
4021 case JSOP_STRING:
4022 LOAD_ATOM(0);
4023 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
4024 inXML ? DONT_ESCAPE : '"');
4025 if (!rval)
4026 return NULL;
4027 todo = STR2OFF(&ss->sprinter, rval);
4028 break;
4030 case JSOP_LAMBDA:
4031 case JSOP_LAMBDA_FC:
4032 case JSOP_LAMBDA_DBGFC:
4033 #if JS_HAS_GENERATOR_EXPRS
4034 sn = js_GetSrcNote(jp->script, pc);
4035 if (sn && SN_TYPE(sn) == SRC_GENEXP) {
4036 void *mark;
4037 jsuword *innerLocalNames, *outerLocalNames;
4038 JSScript *inner, *outer;
4039 SprintStack ss2;
4040 JSFunction *outerfun;
4042 LOAD_FUNCTION(0);
4045 * All allocation when decompiling is LIFO, using malloc
4046 * or, more commonly, arena-allocating from cx->tempPool.
4047 * Therefore after InitSprintStack succeeds, we must
4048 * release to mark before returning.
4050 mark = JS_ARENA_MARK(&cx->tempPool);
4051 if (!fun->hasLocalNames()) {
4052 innerLocalNames = NULL;
4053 } else {
4054 innerLocalNames = js_GetLocalNameArray(cx, fun, &cx->tempPool);
4055 if (!innerLocalNames)
4056 return NULL;
4058 inner = fun->u.i.script;
4059 if (!InitSprintStack(cx, &ss2, jp, StackDepth(inner))) {
4060 JS_ARENA_RELEASE(&cx->tempPool, mark);
4061 return NULL;
4063 ss2.inGenExp = JS_TRUE;
4066 * Recursively decompile this generator function as an
4067 * un-parenthesized generator expression. The ss->inGenExp
4068 * special case of JSOP_YIELD shares array comprehension
4069 * decompilation code that leaves the result as the single
4070 * string pushed on ss2.
4072 outer = jp->script;
4073 outerfun = jp->fun;
4074 outerLocalNames = jp->localNames;
4075 LOCAL_ASSERT(JS_UPTRDIFF(pc, outer->code) <= outer->length);
4076 jp->script = inner;
4077 jp->fun = fun;
4078 jp->localNames = innerLocalNames;
4079 ok = Decompile(&ss2, inner->code, inner->length, JSOP_NOP) != NULL;
4080 jp->script = outer;
4081 jp->fun = outerfun;
4082 jp->localNames = outerLocalNames;
4083 if (!ok) {
4084 JS_ARENA_RELEASE(&cx->tempPool, mark);
4085 return NULL;
4089 * Advance over this op and its global |this| push, and
4090 * arrange to advance over the call to this lambda.
4092 pc += len;
4093 LOCAL_ASSERT(*pc == JSOP_NULL);
4094 pc += JSOP_NULL_LENGTH;
4095 LOCAL_ASSERT(*pc == JSOP_CALL);
4096 LOCAL_ASSERT(GET_ARGC(pc) == 0);
4097 len = JSOP_CALL_LENGTH;
4100 * Arrange to parenthesize this genexp unless:
4102 * 1. It is the complete expression consumed by a control
4103 * flow bytecode such as JSOP_TABLESWITCH whose syntax
4104 * always parenthesizes the controlling expression.
4105 * 2. It is the sole argument to a function call.
4107 * But if this genexp runs up against endpc, parenthesize
4108 * regardless. (This can happen if we are called from
4109 * DecompileExpression or recursively from case
4110 * JSOP_{NOP,AND,OR}.)
4112 * There's no special case for |if (genexp)| because the
4113 * compiler optimizes that to |if (true)|.
4115 pc2 = pc + len;
4116 op = JSOp(*pc2);
4117 if (op == JSOP_TRACE || op == JSOP_NOP)
4118 pc2 += JSOP_NOP_LENGTH;
4119 LOCAL_ASSERT(pc2 < endpc ||
4120 endpc < outer->code + outer->length);
4121 LOCAL_ASSERT(ss2.top == 1);
4122 ss2.opcodes[0] = JSOP_POP;
4123 if (pc2 == endpc) {
4124 op = JSOP_SETNAME;
4125 } else {
4126 op = (JSOp) *pc2;
4127 op = ((js_CodeSpec[op].format & JOF_PARENHEAD) ||
4128 ((js_CodeSpec[op].format & JOF_INVOKE) && GET_ARGC(pc2) == 1))
4129 ? JSOP_POP
4130 : JSOP_SETNAME;
4133 * Stack this result as if it's a name and not an
4134 * anonymous function, so it doesn't get decompiled as
4135 * a generator function in a getter or setter context.
4136 * The precedence level is the same for JSOP_NAME and
4137 * JSOP_LAMBDA.
4139 LOCAL_ASSERT(js_CodeSpec[JSOP_NAME].prec ==
4140 js_CodeSpec[saveop].prec);
4141 saveop = JSOP_NAME;
4145 * Alas, we have to malloc a copy of the result left on
4146 * the top of ss2 because both ss and ss2 arena-allocate
4147 * from cx's tempPool.
4149 rval = JS_strdup(cx, PopStr(&ss2, op));
4150 JS_ARENA_RELEASE(&cx->tempPool, mark);
4151 if (!rval)
4152 return NULL;
4153 todo = SprintCString(&ss->sprinter, rval);
4154 cx->free((void *)rval);
4155 break;
4157 #endif /* JS_HAS_GENERATOR_EXPRS */
4158 /* FALL THROUGH */
4160 LOAD_FUNCTION(0);
4163 * Always parenthesize expression closures. We can't force
4164 * saveop to a low-precedence op to arrange for auto-magic
4165 * parenthesization without confusing getter/setter code
4166 * that checks for JSOP_LAMBDA.
4168 bool grouped = !(fun->flags & JSFUN_EXPR_CLOSURE);
4169 bool strict = jp->script->strictModeCode;
4170 str = js_DecompileToString(cx, "lambda", fun, 0,
4171 false, grouped, strict,
4172 js_DecompileFunction);
4173 if (!str)
4174 return NULL;
4176 sprint_string:
4177 todo = SprintString(&ss->sprinter, str);
4178 break;
4180 case JSOP_CALLEE:
4181 JS_ASSERT(jp->fun && jp->fun->atom);
4182 todo = SprintString(&ss->sprinter, ATOM_TO_STRING(jp->fun->atom));
4183 break;
4185 case JSOP_OBJECT:
4186 LOAD_OBJECT(0);
4187 LOCAL_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass);
4188 goto do_regexp;
4190 case JSOP_REGEXP:
4191 GET_REGEXP_FROM_BYTECODE(jp->script, pc, 0, obj);
4192 do_regexp:
4193 if (!js_regexp_toString(cx, obj, &val))
4194 return NULL;
4195 str = JSVAL_TO_STRING(val);
4196 goto sprint_string;
4198 case JSOP_TABLESWITCH:
4199 case JSOP_TABLESWITCHX:
4201 ptrdiff_t jmplen, off, off2;
4202 jsint j, n, low, high;
4203 TableEntry *table, *tmp;
4205 sn = js_GetSrcNote(jp->script, pc);
4206 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
4207 len = js_GetSrcNoteOffset(sn, 0);
4208 jmplen = (op == JSOP_TABLESWITCH) ? JUMP_OFFSET_LEN
4209 : JUMPX_OFFSET_LEN;
4210 pc2 = pc;
4211 off = GetJumpOffset(pc, pc2);
4212 pc2 += jmplen;
4213 low = GET_JUMP_OFFSET(pc2);
4214 pc2 += JUMP_OFFSET_LEN;
4215 high = GET_JUMP_OFFSET(pc2);
4216 pc2 += JUMP_OFFSET_LEN;
4218 n = high - low + 1;
4219 if (n == 0) {
4220 table = NULL;
4221 j = 0;
4222 ok = JS_TRUE;
4223 } else {
4224 table = (TableEntry *)
4225 cx->malloc((size_t)n * sizeof *table);
4226 if (!table)
4227 return NULL;
4228 for (i = j = 0; i < n; i++) {
4229 table[j].label = NULL;
4230 off2 = GetJumpOffset(pc, pc2);
4231 if (off2) {
4232 sn = js_GetSrcNote(jp->script, pc2);
4233 if (sn) {
4234 LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL);
4235 GET_SOURCE_NOTE_ATOM(sn, table[j].label);
4237 table[j].key = INT_TO_JSVAL(low + i);
4238 table[j].offset = off2;
4239 table[j].order = j;
4240 j++;
4242 pc2 += jmplen;
4244 tmp = (TableEntry *)
4245 cx->malloc((size_t)j * sizeof *table);
4246 if (tmp) {
4247 VOUCH_DOES_NOT_REQUIRE_STACK();
4248 ok = js_MergeSort(table, (size_t)j, sizeof(TableEntry),
4249 CompareOffsets, NULL, tmp);
4250 cx->free(tmp);
4251 } else {
4252 ok = JS_FALSE;
4256 if (ok) {
4257 ok = DecompileSwitch(ss, table, (uintN)j, pc, len, off,
4258 JS_FALSE);
4260 cx->free(table);
4261 if (!ok)
4262 return NULL;
4263 todo = -2;
4264 break;
4267 case JSOP_LOOKUPSWITCH:
4268 case JSOP_LOOKUPSWITCHX:
4270 ptrdiff_t jmplen, off, off2;
4271 jsatomid npairs, k;
4272 TableEntry *table;
4274 sn = js_GetSrcNote(jp->script, pc);
4275 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
4276 len = js_GetSrcNoteOffset(sn, 0);
4277 jmplen = (op == JSOP_LOOKUPSWITCH) ? JUMP_OFFSET_LEN
4278 : JUMPX_OFFSET_LEN;
4279 pc2 = pc;
4280 off = GetJumpOffset(pc, pc2);
4281 pc2 += jmplen;
4282 npairs = GET_UINT16(pc2);
4283 pc2 += UINT16_LEN;
4285 table = (TableEntry *)
4286 cx->malloc((size_t)npairs * sizeof *table);
4287 if (!table)
4288 return NULL;
4289 for (k = 0; k < npairs; k++) {
4290 sn = js_GetSrcNote(jp->script, pc2);
4291 if (sn) {
4292 LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL);
4293 GET_SOURCE_NOTE_ATOM(sn, table[k].label);
4294 } else {
4295 table[k].label = NULL;
4297 JS_GET_SCRIPT_ATOM(jp->script, pc, GET_INDEX(pc2), atom);
4298 pc2 += INDEX_LEN;
4299 off2 = GetJumpOffset(pc, pc2);
4300 pc2 += jmplen;
4301 table[k].key = ATOM_KEY(atom);
4302 table[k].offset = off2;
4305 ok = DecompileSwitch(ss, table, (uintN)npairs, pc, len, off,
4306 JS_FALSE);
4307 cx->free(table);
4308 if (!ok)
4309 return NULL;
4310 todo = -2;
4311 break;
4314 case JSOP_CONDSWITCH:
4316 ptrdiff_t off, off2, caseOff;
4317 jsint ncases;
4318 TableEntry *table;
4320 sn = js_GetSrcNote(jp->script, pc);
4321 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
4322 len = js_GetSrcNoteOffset(sn, 0);
4323 off = js_GetSrcNoteOffset(sn, 1);
4326 * Count the cases using offsets from switch to first case,
4327 * and case to case, stored in srcnote immediates.
4329 pc2 = pc;
4330 off2 = off;
4331 for (ncases = 0; off2 != 0; ncases++) {
4332 pc2 += off2;
4333 LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT ||
4334 *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX);
4335 if (*pc2 == JSOP_DEFAULT || *pc2 == JSOP_DEFAULTX) {
4336 /* End of cases, but count default as a case. */
4337 off2 = 0;
4338 } else {
4339 sn = js_GetSrcNote(jp->script, pc2);
4340 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
4341 off2 = js_GetSrcNoteOffset(sn, 0);
4346 * Allocate table and rescan the cases using their srcnotes,
4347 * stashing each case's delta from switch top in table[i].key,
4348 * and the distance to its statements in table[i].offset.
4350 table = (TableEntry *)
4351 cx->malloc((size_t)ncases * sizeof *table);
4352 if (!table)
4353 return NULL;
4354 pc2 = pc;
4355 off2 = off;
4356 for (i = 0; i < ncases; i++) {
4357 pc2 += off2;
4358 LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT ||
4359 *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX);
4360 caseOff = pc2 - pc;
4361 table[i].key = INT_TO_JSVAL((jsint) caseOff);
4362 table[i].offset = caseOff + GetJumpOffset(pc2, pc2);
4363 if (*pc2 == JSOP_CASE || *pc2 == JSOP_CASEX) {
4364 sn = js_GetSrcNote(jp->script, pc2);
4365 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
4366 off2 = js_GetSrcNoteOffset(sn, 0);
4371 * Find offset of default code by fetching the default offset
4372 * from the end of table. JSOP_CONDSWITCH always has a default
4373 * case at the end.
4375 off = JSVAL_TO_INT(table[ncases-1].key);
4376 pc2 = pc + off;
4377 off += GetJumpOffset(pc2, pc2);
4379 ok = DecompileSwitch(ss, table, (uintN)ncases, pc, len, off,
4380 JS_TRUE);
4381 cx->free(table);
4382 if (!ok)
4383 return NULL;
4384 todo = -2;
4385 break;
4388 case JSOP_CASE:
4389 case JSOP_CASEX:
4391 lval = POP_STR();
4392 if (!lval)
4393 return NULL;
4394 js_printf(jp, "\tcase %s:\n", lval);
4395 todo = -2;
4396 break;
4399 case JSOP_DEFFUN:
4400 case JSOP_DEFFUN_FC:
4401 case JSOP_DEFFUN_DBGFC:
4402 LOAD_FUNCTION(0);
4403 todo = -2;
4404 goto do_function;
4405 break;
4407 case JSOP_TRAP:
4408 saveop = op = JS_GetTrapOpcode(cx, jp->script, pc);
4409 *pc = op;
4410 cs = &js_CodeSpec[op];
4411 len = cs->length;
4412 DECOMPILE_CODE(pc, len);
4413 *pc = JSOP_TRAP;
4414 todo = -2;
4415 break;
4417 case JSOP_HOLE:
4418 todo = SprintPut(&ss->sprinter, "", 0);
4419 break;
4421 case JSOP_NEWARRAY:
4422 argc = GET_UINT16(pc);
4423 LOCAL_ASSERT(ss->top >= (uintN) argc);
4424 if (argc == 0) {
4425 todo = SprintCString(&ss->sprinter, "[]");
4426 break;
4429 argv = (char **) cx->malloc(size_t(argc) * sizeof *argv);
4430 if (!argv)
4431 return NULL;
4433 op = JSOP_SETNAME;
4434 ok = JS_TRUE;
4435 i = argc;
4436 while (i > 0)
4437 argv[--i] = JS_strdup(cx, POP_STR());
4439 todo = SprintCString(&ss->sprinter, "[");
4440 if (todo < 0)
4441 break;
4443 for (i = 0; i < argc; i++) {
4444 if (!argv[i] ||
4445 Sprint(&ss->sprinter, ss_format,
4446 argv[i], (i < argc - 1) ? ", " : "") < 0) {
4447 ok = JS_FALSE;
4448 break;
4452 for (i = 0; i < argc; i++)
4453 cx->free(argv[i]);
4454 cx->free(argv);
4455 if (!ok)
4456 return NULL;
4458 sn = js_GetSrcNote(jp->script, pc);
4459 if (sn && SN_TYPE(sn) == SRC_CONTINUE && SprintCString(&ss->sprinter, ", ") < 0)
4460 return NULL;
4461 if (SprintCString(&ss->sprinter, "]") < 0)
4462 return NULL;
4463 break;
4465 case JSOP_NEWINIT:
4467 i = GET_INT8(pc);
4468 LOCAL_ASSERT(i == JSProto_Array || i == JSProto_Object);
4470 todo = ss->sprinter.offset;
4471 #if JS_HAS_SHARP_VARS
4472 op = (JSOp)pc[len];
4473 if (op == JSOP_SHARPINIT)
4474 op = (JSOp)pc[len += JSOP_SHARPINIT_LENGTH];
4475 if (op == JSOP_DEFSHARP) {
4476 pc += len;
4477 cs = &js_CodeSpec[op];
4478 len = cs->length;
4479 if (Sprint(&ss->sprinter, "#%u=",
4480 (unsigned) (jsint) GET_UINT16(pc + UINT16_LEN))
4481 < 0) {
4482 return NULL;
4485 #endif /* JS_HAS_SHARP_VARS */
4486 if (i == JSProto_Array) {
4487 ++ss->inArrayInit;
4488 if (SprintCString(&ss->sprinter, "[") < 0)
4489 return NULL;
4490 } else {
4491 if (SprintCString(&ss->sprinter, "{") < 0)
4492 return NULL;
4494 break;
4497 case JSOP_ENDINIT:
4499 JSBool inArray;
4501 op = JSOP_NOP; /* turn off parens */
4502 rval = POP_STR();
4503 sn = js_GetSrcNote(jp->script, pc);
4505 /* Skip any #n= prefix to find the opening bracket. */
4506 for (xval = rval; *xval != '[' && *xval != '{'; xval++)
4507 continue;
4508 inArray = (*xval == '[');
4509 if (inArray)
4510 --ss->inArrayInit;
4511 todo = Sprint(&ss->sprinter, "%s%s%c",
4512 rval,
4513 (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "",
4514 inArray ? ']' : '}');
4515 break;
4519 JSBool isFirst;
4520 const char *maybeComma;
4522 case JSOP_INITELEM:
4523 isFirst = (ss->opcodes[ss->top - 3] == JSOP_NEWINIT);
4525 /* Turn off most parens. */
4526 op = JSOP_SETNAME;
4527 rval = POP_STR();
4529 /* Turn off all parens for xval and lval, which we control. */
4530 op = JSOP_NOP;
4531 xval = POP_STR();
4532 lval = POP_STR();
4533 sn = js_GetSrcNote(jp->script, pc);
4535 if (sn && SN_TYPE(sn) == SRC_INITPROP) {
4536 atom = NULL;
4537 goto do_initprop;
4539 maybeComma = isFirst ? "" : ", ";
4540 todo = Sprint(&ss->sprinter, sss_format,
4541 lval,
4542 maybeComma,
4543 rval);
4544 break;
4546 case JSOP_INITPROP:
4547 case JSOP_INITMETHOD:
4548 LOAD_ATOM(0);
4549 xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
4550 (jschar)
4551 (ATOM_IS_IDENTIFIER(atom) ? 0 : '\''));
4552 if (!xval)
4553 return NULL;
4554 isFirst = (ss->opcodes[ss->top - 2] == JSOP_NEWINIT);
4555 rval = POP_STR();
4556 lval = POP_STR();
4557 /* fall through */
4559 do_initprop:
4560 maybeComma = isFirst ? "" : ", ";
4561 #ifdef OLD_GETTER_SETTER
4562 todo = Sprint(&ss->sprinter, "%s%s%s%s%s%s%s:%s",
4563 lval,
4564 maybeComma,
4565 xval,
4566 (lastop == JSOP_GETTER || lastop == JSOP_SETTER)
4567 ? " " : "",
4568 (lastop == JSOP_GETTER) ? js_getter_str :
4569 (lastop == JSOP_SETTER) ? js_setter_str :
4571 rval);
4572 #else
4573 if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) {
4574 if (!atom ||
4575 !ATOM_IS_STRING(atom) ||
4576 !ATOM_IS_IDENTIFIER(atom) ||
4577 ATOM_IS_KEYWORD(atom) ||
4578 (ss->opcodes[ss->top+1] != JSOP_LAMBDA &&
4579 ss->opcodes[ss->top+1] != JSOP_LAMBDA_FC)) {
4580 todo = Sprint(&ss->sprinter, "%s%s%s %s: %s",
4581 lval,
4582 maybeComma,
4583 xval,
4584 (lastop == JSOP_GETTER) ? js_getter_str :
4585 (lastop == JSOP_SETTER) ? js_setter_str :
4587 rval);
4588 } else {
4589 const char *end = rval + strlen(rval);
4591 if (*rval == '(')
4592 ++rval, --end;
4593 LOCAL_ASSERT(strncmp(rval, js_function_str, 8) == 0);
4594 LOCAL_ASSERT(rval[8] == ' ');
4595 rval += 8 + 1;
4596 LOCAL_ASSERT(*end ? *end == ')' : end[-1] == '}');
4597 todo = Sprint(&ss->sprinter, "%s%s%s %s%s%.*s",
4598 lval,
4599 maybeComma,
4600 (lastop == JSOP_GETTER)
4601 ? js_get_str : js_set_str,
4602 xval,
4603 (rval[0] != '(') ? " " : "",
4604 end - rval, rval);
4606 } else {
4607 todo = Sprint(&ss->sprinter, "%s%s%s: %s",
4608 lval, maybeComma, xval, rval);
4610 #endif
4611 break;
4614 #if JS_HAS_SHARP_VARS
4615 case JSOP_DEFSHARP:
4616 i = (jsint) GET_UINT16(pc + UINT16_LEN);
4617 rval = POP_STR();
4618 todo = Sprint(&ss->sprinter, "#%u=%s", (unsigned) i, rval);
4619 break;
4621 case JSOP_USESHARP:
4622 i = (jsint) GET_UINT16(pc + UINT16_LEN);
4623 todo = Sprint(&ss->sprinter, "#%u#", (unsigned) i);
4624 break;
4625 #endif /* JS_HAS_SHARP_VARS */
4627 #if JS_HAS_DEBUGGER_KEYWORD
4628 case JSOP_DEBUGGER:
4629 js_printf(jp, "\tdebugger;\n");
4630 todo = -2;
4631 break;
4632 #endif /* JS_HAS_DEBUGGER_KEYWORD */
4634 #if JS_HAS_XML_SUPPORT
4635 case JSOP_STARTXML:
4636 case JSOP_STARTXMLEXPR:
4637 inXML = op == JSOP_STARTXML;
4638 todo = -2;
4639 break;
4641 case JSOP_DEFXMLNS:
4642 rval = POP_STR();
4643 js_printf(jp, "\t%s %s %s = %s;\n",
4644 js_default_str, js_xml_str, js_namespace_str, rval);
4645 todo = -2;
4646 break;
4648 case JSOP_ANYNAME:
4649 if (pc[JSOP_ANYNAME_LENGTH] == JSOP_TOATTRNAME) {
4650 len += JSOP_TOATTRNAME_LENGTH;
4651 todo = SprintPut(&ss->sprinter, "@*", 2);
4652 } else {
4653 todo = SprintPut(&ss->sprinter, "*", 1);
4655 break;
4657 case JSOP_QNAMEPART:
4658 LOAD_ATOM(0);
4659 if (pc[JSOP_QNAMEPART_LENGTH] == JSOP_TOATTRNAME) {
4660 saveop = JSOP_TOATTRNAME;
4661 len += JSOP_TOATTRNAME_LENGTH;
4662 lval = "@";
4663 goto do_qname;
4665 goto do_name;
4667 case JSOP_QNAMECONST:
4668 LOAD_ATOM(0);
4669 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
4670 if (!rval)
4671 return NULL;
4672 RETRACT(&ss->sprinter, rval);
4673 lval = POP_STR();
4674 todo = Sprint(&ss->sprinter, "%s::%s", lval, rval);
4675 break;
4677 case JSOP_QNAME:
4678 rval = POP_STR();
4679 lval = POP_STR();
4680 todo = Sprint(&ss->sprinter, "%s::[%s]", lval, rval);
4681 break;
4683 case JSOP_TOATTRNAME:
4684 op = JSOP_NOP; /* turn off parens */
4685 rval = POP_STR();
4686 todo = Sprint(&ss->sprinter, "@[%s]", rval);
4687 break;
4689 case JSOP_TOATTRVAL:
4690 todo = -2;
4691 break;
4693 case JSOP_ADDATTRNAME:
4694 rval = POP_STR();
4695 lval = POP_STR();
4696 todo = Sprint(&ss->sprinter, "%s %s", lval, rval);
4697 /* This gets reset by all XML tag expressions. */
4698 quoteAttr = JS_TRUE;
4699 break;
4701 case JSOP_ADDATTRVAL:
4702 rval = POP_STR();
4703 lval = POP_STR();
4704 if (quoteAttr)
4705 todo = Sprint(&ss->sprinter, "%s=\"%s\"", lval, rval);
4706 else
4707 todo = Sprint(&ss->sprinter, "%s=%s", lval, rval);
4708 break;
4710 case JSOP_BINDXMLNAME:
4711 /* Leave the name stacked and push a dummy string. */
4712 todo = Sprint(&ss->sprinter, "");
4713 break;
4715 case JSOP_SETXMLNAME:
4716 /* Pop the r.h.s., the dummy string, and the name. */
4717 rval = POP_STR();
4718 (void) PopOff(ss, op);
4719 lval = POP_STR();
4720 goto do_setlval;
4722 case JSOP_XMLELTEXPR:
4723 case JSOP_XMLTAGEXPR:
4724 todo = Sprint(&ss->sprinter, "{%s}", POP_STR());
4725 inXML = JS_TRUE;
4726 /* If we're an attribute value, we shouldn't quote this. */
4727 quoteAttr = JS_FALSE;
4728 break;
4730 case JSOP_TOXMLLIST:
4731 op = JSOP_NOP; /* turn off parens */
4732 todo = Sprint(&ss->sprinter, "<>%s</>", POP_STR());
4733 inXML = JS_FALSE;
4734 break;
4736 case JSOP_TOXML:
4737 case JSOP_CALLXMLNAME:
4738 case JSOP_XMLNAME:
4739 case JSOP_FILTER:
4740 /* These ops indicate the end of XML expressions. */
4741 inXML = JS_FALSE;
4742 todo = -2;
4743 break;
4745 case JSOP_ENDFILTER:
4746 rval = POP_STR();
4747 PROPAGATE_CALLNESS();
4748 lval = POP_STR();
4749 todo = Sprint(&ss->sprinter, "%s.(%s)", lval, rval);
4750 break;
4752 case JSOP_DESCENDANTS:
4753 rval = POP_STR();
4754 PROPAGATE_CALLNESS();
4755 lval = POP_STR();
4756 todo = Sprint(&ss->sprinter, "%s..%s", lval, rval);
4757 break;
4759 case JSOP_XMLOBJECT:
4760 LOAD_OBJECT(0);
4761 todo = Sprint(&ss->sprinter, "<xml address='%p'>", obj);
4762 break;
4764 case JSOP_XMLCDATA:
4765 LOAD_ATOM(0);
4766 todo = SprintPut(&ss->sprinter, "<![CDATA[", 9);
4767 if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
4768 DONT_ESCAPE))
4769 return NULL;
4770 SprintPut(&ss->sprinter, "]]>", 3);
4771 break;
4773 case JSOP_XMLCOMMENT:
4774 LOAD_ATOM(0);
4775 todo = SprintPut(&ss->sprinter, "<!--", 4);
4776 if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
4777 DONT_ESCAPE))
4778 return NULL;
4779 SprintPut(&ss->sprinter, "-->", 3);
4780 break;
4782 case JSOP_XMLPI:
4783 LOAD_ATOM(0);
4784 rval = JS_strdup(cx, POP_STR());
4785 if (!rval)
4786 return NULL;
4787 todo = SprintPut(&ss->sprinter, "<?", 2);
4788 ok = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0) &&
4789 (*rval == '\0' ||
4790 (SprintPut(&ss->sprinter, " ", 1) >= 0 &&
4791 SprintCString(&ss->sprinter, rval)));
4792 cx->free((char *)rval);
4793 if (!ok)
4794 return NULL;
4795 SprintPut(&ss->sprinter, "?>", 2);
4796 break;
4798 case JSOP_GETFUNNS:
4799 todo = SprintPut(&ss->sprinter, js_function_str, 8);
4800 break;
4801 #endif /* JS_HAS_XML_SUPPORT */
4803 default:
4804 todo = -2;
4805 break;
4809 if (todo < 0) {
4810 /* -2 means "don't push", -1 means reported error. */
4811 if (todo == -1)
4812 return NULL;
4813 } else {
4814 if (!PushOff(ss, todo, saveop))
4815 return NULL;
4818 if (cs->format & JOF_CALLOP) {
4819 todo = Sprint(&ss->sprinter, "");
4820 if (todo < 0 || !PushOff(ss, todo, saveop))
4821 return NULL;
4824 pc += len;
4828 * Undefine local macros.
4830 #undef inXML
4831 #undef DECOMPILE_CODE
4832 #undef NEXT_OP
4833 #undef TOP_STR
4834 #undef POP_STR
4835 #undef POP_STR_PREC
4836 #undef LOCAL_ASSERT
4837 #undef ATOM_IS_IDENTIFIER
4838 #undef GET_QUOTE_AND_FMT
4839 #undef GET_ATOM_QUOTE_AND_FMT
4841 return pc;
4844 static JSBool
4845 DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len,
4846 uintN pcdepth)
4848 uintN depth, i;
4849 SprintStack ss;
4850 JSContext *cx;
4851 void *mark;
4852 JSBool ok;
4853 JSScript *oldscript;
4854 jsbytecode *oldcode, *oldmain, *code;
4855 char *last;
4857 depth = StackDepth(script);
4858 JS_ASSERT(pcdepth <= depth);
4860 /* Initialize a sprinter for use with the offset stack. */
4861 cx = jp->sprinter.context;
4862 mark = JS_ARENA_MARK(&cx->tempPool);
4863 ok = InitSprintStack(cx, &ss, jp, depth);
4864 if (!ok)
4865 goto out;
4868 * If we are called from js_DecompileValueGenerator with a portion of
4869 * script's bytecode that starts with a non-zero model stack depth given
4870 * by pcdepth, attempt to initialize the missing string offsets in ss to
4871 * |spindex| negative indexes from fp->sp for the activation fp in which
4872 * the error arose.
4874 * See js_DecompileValueGenerator for how its |spindex| parameter is used,
4875 * and see also GetOff, which makes use of the ss.offsets[i] < -1 that are
4876 * potentially stored below.
4878 ss.top = pcdepth;
4879 if (pcdepth != 0) {
4880 for (i = 0; i < pcdepth; i++) {
4881 ss.offsets[i] = -2 - (ptrdiff_t)i;
4882 ss.opcodes[i] = *jp->pcstack[i];
4886 /* Call recursive subroutine to do the hard work. */
4887 oldscript = jp->script;
4888 jp->script = script;
4889 oldcode = jp->script->code;
4890 oldmain = jp->script->main;
4891 code = js_UntrapScriptCode(cx, jp->script);
4892 if (code != oldcode) {
4893 jp->script->code = code;
4894 jp->script->main = code + (oldmain - oldcode);
4895 pc = code + (pc - oldcode);
4898 ok = Decompile(&ss, pc, len, JSOP_NOP) != NULL;
4899 if (code != oldcode) {
4900 cx->free(jp->script->code);
4901 jp->script->code = oldcode;
4902 jp->script->main = oldmain;
4904 jp->script = oldscript;
4906 /* If the given code didn't empty the stack, do it now. */
4907 if (ok && ss.top) {
4908 do {
4909 last = OFF2STR(&ss.sprinter, PopOff(&ss, JSOP_POP));
4910 } while (ss.top > pcdepth);
4911 js_printf(jp, "%s", last);
4914 out:
4915 /* Free all temporary stuff allocated under this call. */
4916 JS_ARENA_RELEASE(&cx->tempPool, mark);
4917 return ok;
4920 JSBool
4921 js_DecompileScript(JSPrinter *jp, JSScript *script)
4923 return DecompileCode(jp, script, script->code, (uintN)script->length, 0);
4926 JSString *
4927 js_DecompileToString(JSContext *cx, const char *name, JSFunction *fun,
4928 uintN indent, JSBool pretty, JSBool grouped, JSBool strict,
4929 JSDecompilerPtr decompiler)
4931 JSPrinter *jp;
4932 JSString *str;
4934 jp = js_NewPrinter(cx, name, fun, indent, pretty, grouped, strict);
4935 if (!jp)
4936 return NULL;
4937 if (decompiler(jp))
4938 str = js_GetPrinterOutput(jp);
4939 else
4940 str = NULL;
4941 js_DestroyPrinter(jp);
4942 return str;
4945 static const char native_code_str[] = "\t[native code]\n";
4947 JSBool
4948 js_DecompileFunctionBody(JSPrinter *jp)
4950 JSScript *script;
4952 JS_ASSERT(jp->fun);
4953 JS_ASSERT(!jp->script);
4954 if (!FUN_INTERPRETED(jp->fun)) {
4955 js_printf(jp, native_code_str);
4956 return JS_TRUE;
4959 script = jp->fun->u.i.script;
4960 return DecompileCode(jp, script, script->code, (uintN)script->length, 0);
4963 JSBool
4964 js_DecompileFunction(JSPrinter *jp)
4966 JSFunction *fun;
4967 uintN i;
4968 JSAtom *param;
4969 jsbytecode *pc, *endpc;
4970 ptrdiff_t len;
4971 JSBool ok;
4973 fun = jp->fun;
4974 JS_ASSERT(fun);
4975 JS_ASSERT(!jp->script);
4978 * If pretty, conform to ECMA-262 Edition 3, 15.3.4.2, by decompiling a
4979 * FunctionDeclaration. Otherwise, check the JSFUN_LAMBDA flag and force
4980 * an expression by parenthesizing.
4982 if (jp->pretty) {
4983 js_printf(jp, "\t");
4984 } else {
4985 if (!jp->grouped && (fun->flags & JSFUN_LAMBDA))
4986 js_puts(jp, "(");
4988 if (JSFUN_GETTER_TEST(fun->flags))
4989 js_printf(jp, "%s ", js_getter_str);
4990 else if (JSFUN_SETTER_TEST(fun->flags))
4991 js_printf(jp, "%s ", js_setter_str);
4993 js_printf(jp, "%s ", js_function_str);
4994 if (fun->atom && !QuoteString(&jp->sprinter, ATOM_TO_STRING(fun->atom), 0))
4995 return JS_FALSE;
4996 js_puts(jp, "(");
4998 if (!FUN_INTERPRETED(fun)) {
4999 js_printf(jp, ") {\n");
5000 jp->indent += 4;
5001 js_printf(jp, native_code_str);
5002 jp->indent -= 4;
5003 js_printf(jp, "\t}");
5004 } else {
5005 JSScript *script = fun->u.i.script;
5006 #if JS_HAS_DESTRUCTURING
5007 SprintStack ss;
5008 void *mark;
5009 #endif
5011 /* Print the parameters. */
5012 pc = script->main;
5013 endpc = pc + script->length;
5014 ok = JS_TRUE;
5016 /* Skip trace hint if it appears here. */
5017 #if JS_HAS_GENERATORS
5018 if (js_GetOpcode(jp->sprinter.context, script, script->code) != JSOP_GENERATOR)
5019 #endif
5021 JSOp op = js_GetOpcode(jp->sprinter.context, script, pc);
5022 if (op == JSOP_TRACE || op == JSOP_NOP) {
5023 JS_STATIC_ASSERT(JSOP_TRACE_LENGTH == JSOP_NOP_LENGTH);
5024 pc += JSOP_TRACE_LENGTH;
5025 } else {
5026 JS_ASSERT(op == JSOP_STOP); /* empty script singleton */
5030 #if JS_HAS_DESTRUCTURING
5031 ss.printer = NULL;
5032 jp->script = script;
5033 mark = JS_ARENA_MARK(&jp->sprinter.context->tempPool);
5034 #endif
5036 for (i = 0; i < fun->nargs; i++) {
5037 if (i > 0)
5038 js_puts(jp, ", ");
5040 param = GetArgOrVarAtom(jp, i);
5042 #if JS_HAS_DESTRUCTURING
5043 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, JS_FALSE)
5045 if (!param) {
5046 ptrdiff_t todo;
5047 const char *lval;
5049 LOCAL_ASSERT(*pc == JSOP_GETARG);
5050 pc += JSOP_GETARG_LENGTH;
5051 LOCAL_ASSERT(*pc == JSOP_DUP);
5052 if (!ss.printer) {
5053 ok = InitSprintStack(jp->sprinter.context, &ss, jp, StackDepth(script));
5054 if (!ok)
5055 break;
5057 pc = DecompileDestructuring(&ss, pc, endpc);
5058 if (!pc) {
5059 ok = JS_FALSE;
5060 break;
5062 LOCAL_ASSERT(*pc == JSOP_POP);
5063 pc += JSOP_POP_LENGTH;
5064 lval = PopStr(&ss, JSOP_NOP);
5065 todo = SprintCString(&jp->sprinter, lval);
5066 if (todo < 0) {
5067 ok = JS_FALSE;
5068 break;
5070 continue;
5073 #undef LOCAL_ASSERT
5074 #endif
5076 if (!QuoteString(&jp->sprinter, ATOM_TO_STRING(param), 0)) {
5077 ok = JS_FALSE;
5078 break;
5082 #if JS_HAS_DESTRUCTURING
5083 jp->script = NULL;
5084 JS_ARENA_RELEASE(&jp->sprinter.context->tempPool, mark);
5085 #endif
5086 if (!ok)
5087 return JS_FALSE;
5088 if (fun->flags & JSFUN_EXPR_CLOSURE) {
5089 js_printf(jp, ") ");
5090 if (fun->u.i.script->strictModeCode && !jp->strict) {
5092 * We have no syntax for strict function expressions;
5093 * at least give a hint.
5095 js_printf(jp, "\t/* use strict */ \n");
5096 jp->strict = true;
5099 } else {
5100 js_printf(jp, ") {\n");
5101 jp->indent += 4;
5102 if (fun->u.i.script->strictModeCode && !jp->strict) {
5103 js_printf(jp, "\t'use strict';\n");
5104 jp->strict = true;
5108 len = script->code + script->length - pc;
5109 ok = DecompileCode(jp, script, pc, (uintN)len, 0);
5110 if (!ok)
5111 return JS_FALSE;
5113 if (!(fun->flags & JSFUN_EXPR_CLOSURE)) {
5114 jp->indent -= 4;
5115 js_printf(jp, "\t}");
5119 if (!jp->pretty && !jp->grouped && (fun->flags & JSFUN_LAMBDA))
5120 js_puts(jp, ")");
5122 return JS_TRUE;
5125 char *
5126 js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
5127 JSString *fallback)
5129 JSStackFrame *fp;
5130 jsbytecode *pc;
5131 JSScript *script;
5132 JSFrameRegs *regs;
5133 intN pcdepth;
5134 jsval *sp, *stackBase;
5135 char *name;
5137 JS_ASSERT(spindex < 0 ||
5138 spindex == JSDVG_IGNORE_STACK ||
5139 spindex == JSDVG_SEARCH_STACK);
5141 fp = js_GetScriptedCaller(cx, NULL);
5142 if (!fp || !fp->regs || !fp->regs->sp)
5143 goto do_fallback;
5145 script = fp->script;
5146 regs = fp->regs;
5147 pc = fp->imacpc ? fp->imacpc : regs->pc;
5148 if (pc < script->main || script->code + script->length <= pc) {
5149 JS_NOT_REACHED("bug");
5150 goto do_fallback;
5153 if (spindex != JSDVG_IGNORE_STACK) {
5154 jsbytecode **pcstack;
5157 * Prepare computing pcstack containing pointers to opcodes that
5158 * populated interpreter's stack with its current content.
5160 pcstack = (jsbytecode **)
5161 cx->malloc(StackDepth(script) * sizeof *pcstack);
5162 if (!pcstack)
5163 return NULL;
5164 pcdepth = ReconstructPCStack(cx, script, pc, pcstack);
5165 if (pcdepth < 0)
5166 goto release_pcstack;
5168 if (spindex != JSDVG_SEARCH_STACK) {
5169 JS_ASSERT(spindex < 0);
5170 pcdepth += spindex;
5171 if (pcdepth < 0)
5172 goto release_pcstack;
5173 pc = pcstack[pcdepth];
5174 } else {
5176 * We search from fp->sp to base to find the most recently
5177 * calculated value matching v under assumption that it is
5178 * it that caused exception, see bug 328664.
5180 stackBase = StackBase(fp);
5181 sp = regs->sp;
5182 do {
5183 if (sp == stackBase) {
5184 pcdepth = -1;
5185 goto release_pcstack;
5187 } while (*--sp != v);
5190 * The value may have come from beyond stackBase + pcdepth,
5191 * meaning that it came from a temporary slot that the
5192 * interpreter uses for GC roots or when JSOP_APPLY extended
5193 * the stack to fit the argument array elements. Only update pc
5194 * if beneath stackBase + pcdepth; otherwise blame existing
5195 * (current) PC.
5197 if (sp < stackBase + pcdepth)
5198 pc = pcstack[sp - stackBase];
5201 release_pcstack:
5202 cx->free(pcstack);
5203 if (pcdepth < 0)
5204 goto do_fallback;
5208 jsbytecode* savepc = regs->pc;
5209 jsbytecode* imacpc = fp->imacpc;
5210 if (imacpc) {
5211 regs->pc = imacpc;
5212 fp->imacpc = NULL;
5216 * FIXME: bug 489843. Stack reconstruction may have returned a pc
5217 * value *inside* an imacro; this would confuse the decompiler.
5219 if (imacpc && size_t(pc - script->code) >= script->length)
5220 name = FAILED_EXPRESSION_DECOMPILER;
5221 else
5222 name = DecompileExpression(cx, script, fp->fun, pc);
5224 if (imacpc) {
5225 regs->pc = savepc;
5226 fp->imacpc = imacpc;
5229 if (name != FAILED_EXPRESSION_DECOMPILER)
5230 return name;
5232 do_fallback:
5233 if (!fallback) {
5234 fallback = js_ValueToSource(cx, v);
5235 if (!fallback)
5236 return NULL;
5238 return js_DeflateString(cx, fallback->chars(), fallback->length());
5241 static char *
5242 DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun,
5243 jsbytecode *pc)
5245 jsbytecode *code, *oldcode, *oldmain;
5246 JSOp op;
5247 const JSCodeSpec *cs;
5248 jsbytecode *begin, *end;
5249 jssrcnote *sn;
5250 ptrdiff_t len;
5251 jsbytecode **pcstack;
5252 intN pcdepth;
5253 JSPrinter *jp;
5254 char *name;
5256 JS_ASSERT(script->main <= pc && pc < script->code + script->length);
5258 pcstack = NULL;
5259 oldcode = script->code;
5260 oldmain = script->main;
5262 MUST_FLOW_THROUGH("out");
5263 code = js_UntrapScriptCode(cx, script);
5264 if (code != oldcode) {
5265 script->code = code;
5266 script->main = code + (oldmain - oldcode);
5267 pc = code + (pc - oldcode);
5270 op = (JSOp) *pc;
5272 /* None of these stack-writing ops generates novel values. */
5273 JS_ASSERT(op != JSOP_CASE && op != JSOP_CASEX &&
5274 op != JSOP_DUP && op != JSOP_DUP2);
5276 /* JSOP_PUSH is used to generate undefined for group assignment holes. */
5277 if (op == JSOP_PUSH) {
5278 name = JS_strdup(cx, js_undefined_str);
5279 goto out;
5283 * |this| could convert to a very long object initialiser, so cite it by
5284 * its keyword name instead.
5286 if (op == JSOP_THIS) {
5287 name = JS_strdup(cx, js_this_str);
5288 goto out;
5292 * JSOP_BINDNAME is special: it generates a value, the base object of a
5293 * reference. But if it is the generating op for a diagnostic produced by
5294 * js_DecompileValueGenerator, the name being bound is irrelevant. Just
5295 * fall back to the base object.
5297 if (op == JSOP_BINDNAME) {
5298 name = FAILED_EXPRESSION_DECOMPILER;
5299 goto out;
5302 /* NAME ops are self-contained, others require left or right context. */
5303 cs = &js_CodeSpec[op];
5304 begin = pc;
5305 end = pc + cs->length;
5306 switch (JOF_MODE(cs->format)) {
5307 case JOF_PROP:
5308 case JOF_ELEM:
5309 case JOF_XMLNAME:
5310 case 0:
5311 sn = js_GetSrcNote(script, pc);
5312 if (!sn) {
5313 name = FAILED_EXPRESSION_DECOMPILER;
5314 goto out;
5316 switch (SN_TYPE(sn)) {
5317 case SRC_PCBASE:
5318 begin -= js_GetSrcNoteOffset(sn, 0);
5319 break;
5320 case SRC_PCDELTA:
5321 end = begin + js_GetSrcNoteOffset(sn, 0);
5322 begin += cs->length;
5323 break;
5324 default:
5325 name = FAILED_EXPRESSION_DECOMPILER;
5326 goto out;
5328 break;
5329 default:;
5331 len = end - begin;
5332 if (len <= 0) {
5333 name = FAILED_EXPRESSION_DECOMPILER;
5334 goto out;
5337 pcstack = (jsbytecode **)
5338 cx->malloc(StackDepth(script) * sizeof *pcstack);
5339 if (!pcstack) {
5340 name = NULL;
5341 goto out;
5344 MUST_FLOW_THROUGH("out");
5345 pcdepth = ReconstructPCStack(cx, script, begin, pcstack);
5346 if (pcdepth < 0) {
5347 name = FAILED_EXPRESSION_DECOMPILER;
5348 goto out;
5351 name = NULL;
5352 jp = js_NewPrinter(cx, "js_DecompileValueGenerator", fun, 0,
5353 false, false, false);
5354 if (jp) {
5355 jp->dvgfence = end;
5356 jp->pcstack = pcstack;
5357 if (DecompileCode(jp, script, begin, (uintN) len, (uintN) pcdepth)) {
5358 name = (jp->sprinter.base) ? jp->sprinter.base : (char *) "";
5359 name = JS_strdup(cx, name);
5361 js_DestroyPrinter(jp);
5364 out:
5365 if (code != oldcode) {
5366 cx->free(script->code);
5367 script->code = oldcode;
5368 script->main = oldmain;
5371 cx->free(pcstack);
5372 return name;
5375 uintN
5376 js_ReconstructStackDepth(JSContext *cx, JSScript *script, jsbytecode *pc)
5378 return ReconstructPCStack(cx, script, pc, NULL);
5381 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1);
5383 static intN
5384 SimulateOp(JSContext *cx, JSScript *script, JSOp op, const JSCodeSpec *cs,
5385 jsbytecode *pc, jsbytecode **pcstack, uintN &pcdepth)
5387 uintN nuses = js_GetStackUses(cs, op, pc);
5388 uintN ndefs = js_GetStackDefs(cx, cs, op, script, pc);
5389 LOCAL_ASSERT(pcdepth >= nuses);
5390 pcdepth -= nuses;
5391 LOCAL_ASSERT(pcdepth + ndefs <= StackDepth(script));
5394 * Fill the slots that the opcode defines withs its pc unless it just
5395 * reshuffles the stack. In the latter case we want to preserve the
5396 * opcode that generated the original value.
5398 switch (op) {
5399 default:
5400 if (pcstack) {
5401 for (uintN i = 0; i != ndefs; ++i)
5402 pcstack[pcdepth + i] = pc;
5404 break;
5406 case JSOP_CASE:
5407 case JSOP_CASEX:
5408 /* Keep the switch value. */
5409 JS_ASSERT(ndefs == 1);
5410 break;
5412 case JSOP_DUP:
5413 JS_ASSERT(ndefs == 2);
5414 if (pcstack)
5415 pcstack[pcdepth + 1] = pcstack[pcdepth];
5416 break;
5418 case JSOP_DUP2:
5419 JS_ASSERT(ndefs == 4);
5420 if (pcstack) {
5421 pcstack[pcdepth + 2] = pcstack[pcdepth];
5422 pcstack[pcdepth + 3] = pcstack[pcdepth + 1];
5424 break;
5426 case JSOP_SWAP:
5427 JS_ASSERT(ndefs == 2);
5428 if (pcstack) {
5429 jsbytecode *tmp = pcstack[pcdepth + 1];
5430 pcstack[pcdepth + 1] = pcstack[pcdepth];
5431 pcstack[pcdepth] = tmp;
5433 break;
5435 pcdepth += ndefs;
5436 return pcdepth;
5439 #ifdef JS_TRACER
5441 #undef LOCAL_ASSERT
5442 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_CUSTOM(expr, goto failure);
5444 static intN
5445 SimulateImacroCFG(JSContext *cx, JSScript *script,
5446 uintN pcdepth, jsbytecode *pc, jsbytecode *target,
5447 jsbytecode **pcstack)
5449 size_t nbytes = StackDepth(script) * sizeof *pcstack;
5450 jsbytecode** tmp_pcstack = (jsbytecode **) cx->malloc(nbytes);
5451 if (!tmp_pcstack)
5452 return -1;
5453 memcpy(tmp_pcstack, pcstack, nbytes);
5455 ptrdiff_t oplen;
5456 for (; pc < target; pc += oplen) {
5457 JSOp op = js_GetOpcode(cx, script, pc);
5458 const JSCodeSpec *cs = &js_CodeSpec[op];
5459 oplen = cs->length;
5460 if (oplen < 0)
5461 oplen = js_GetVariableBytecodeLength(pc);
5463 if (SimulateOp(cx, script, op, cs, pc, tmp_pcstack, pcdepth) < 0)
5464 goto failure;
5466 uint32 type = cs->format & JOF_TYPEMASK;
5467 if (type == JOF_JUMP || type == JOF_JUMPX) {
5468 ptrdiff_t jmpoff = (type == JOF_JUMP) ? GET_JUMP_OFFSET(pc)
5469 : GET_JUMPX_OFFSET(pc);
5470 LOCAL_ASSERT(jmpoff >= 0);
5471 intN tmp_pcdepth = SimulateImacroCFG(cx, script, pcdepth, pc + jmpoff,
5472 target, tmp_pcstack);
5473 if (tmp_pcdepth >= 0) {
5474 pcdepth = uintN(tmp_pcdepth);
5475 goto success;
5478 if (op == JSOP_GOTO || op == JSOP_GOTOX)
5479 goto failure;
5483 if (pc > target)
5484 goto failure;
5486 LOCAL_ASSERT(pc == target);
5488 success:
5489 memcpy(pcstack, tmp_pcstack, nbytes);
5490 cx->free(tmp_pcstack);
5491 return pcdepth;
5493 failure:
5494 cx->free(tmp_pcstack);
5495 return -1;
5498 #undef LOCAL_ASSERT
5499 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1);
5501 static intN
5502 ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *target,
5503 jsbytecode **pcstack);
5505 static intN
5506 ReconstructImacroPCStack(JSContext *cx, JSScript *script,
5507 jsbytecode *imacstart, jsbytecode *target,
5508 jsbytecode **pcstack)
5511 * Begin with a recursive call back to ReconstructPCStack to pick up
5512 * the state-of-the-world at the *start* of the imacro.
5514 JSStackFrame *fp = js_GetScriptedCaller(cx, NULL);
5515 JS_ASSERT(fp->imacpc);
5516 intN pcdepth = ReconstructPCStack(cx, script, fp->imacpc, pcstack);
5517 if (pcdepth < 0)
5518 return pcdepth;
5519 return SimulateImacroCFG(cx, script, pcdepth, imacstart, target, pcstack);
5522 extern jsbytecode* js_GetImacroStart(jsbytecode* pc);
5523 #endif
5525 static intN
5526 ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *target,
5527 jsbytecode **pcstack)
5530 * Walk forward from script->main and compute the stack depth and stack of
5531 * operand-generating opcode PCs in pcstack.
5533 * FIXME: Code to compute oplen copied from js_Disassemble1 and reduced.
5534 * FIXME: Optimize to use last empty-stack sequence point.
5536 #ifdef JS_TRACER
5537 jsbytecode *imacstart = js_GetImacroStart(target);
5539 if (imacstart)
5540 return ReconstructImacroPCStack(cx, script, imacstart, target, pcstack);
5541 #endif
5543 LOCAL_ASSERT(script->main <= target && target < script->code + script->length);
5544 jsbytecode *pc = script->main;
5545 uintN pcdepth = 0;
5546 ptrdiff_t oplen;
5547 for (; pc < target; pc += oplen) {
5548 JSOp op = js_GetOpcode(cx, script, pc);
5549 const JSCodeSpec *cs = &js_CodeSpec[op];
5550 oplen = cs->length;
5551 if (oplen < 0)
5552 oplen = js_GetVariableBytecodeLength(pc);
5555 * A (C ? T : E) expression requires skipping either T (if target is in
5556 * E) or both T and E (if target is after the whole expression) before
5557 * adjusting pcdepth based on the JSOP_IFEQ or JSOP_IFEQX at pc that
5558 * tests condition C. We know that the stack depth can't change from
5559 * what it was with C on top of stack.
5561 jssrcnote *sn = js_GetSrcNote(script, pc);
5562 if (sn && SN_TYPE(sn) == SRC_COND) {
5563 ptrdiff_t jmpoff = js_GetSrcNoteOffset(sn, 0);
5564 if (pc + jmpoff < target) {
5565 pc += jmpoff;
5566 op = js_GetOpcode(cx, script, pc);
5567 JS_ASSERT(op == JSOP_GOTO || op == JSOP_GOTOX);
5568 cs = &js_CodeSpec[op];
5569 oplen = cs->length;
5570 JS_ASSERT(oplen > 0);
5571 ptrdiff_t jmplen = GetJumpOffset(pc, pc);
5572 if (pc + jmplen < target) {
5573 oplen = (uintN) jmplen;
5574 continue;
5578 * Ok, target lies in E. Manually pop C off the model stack,
5579 * since we have moved beyond the IFEQ now.
5581 LOCAL_ASSERT(pcdepth != 0);
5582 --pcdepth;
5587 * Ignore early-exit code, which is SRC_HIDDEN, but do not ignore the
5588 * hidden POP that sometimes appears after an UNBRAND. See bug 543565.
5590 if (sn && SN_TYPE(sn) == SRC_HIDDEN &&
5591 (op != JSOP_POP || js_GetOpcode(cx, script, pc - 1) != JSOP_UNBRAND)) {
5592 continue;
5595 if (SimulateOp(cx, script, op, cs, pc, pcstack, pcdepth) < 0)
5596 return -1;
5599 LOCAL_ASSERT(pc == target);
5600 return pcdepth;
5602 #undef LOCAL_ASSERT
5605 #undef LOCAL_ASSERT_RV