Bug 463806 - [PATCH][@font-face] Downloaded font activation on Mac may fail due to...
[wine-gecko.git] / js / src / jsopcode.cpp
blob3a89b75bedd3b3cb2c630c6dddf371939ccdbe65
1 /* -*- Mode: C; tab-width: 8; 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 #include "jsstddef.h"
45 #ifdef HAVE_MEMORY_H
46 #include <memory.h>
47 #endif
48 #include <stdarg.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include "jstypes.h"
53 #include "jsarena.h" /* Added by JSIFY */
54 #include "jsutil.h" /* Added by JSIFY */
55 #include "jsdtoa.h"
56 #include "jsprf.h"
57 #include "jsapi.h"
58 #include "jsarray.h"
59 #include "jsatom.h"
60 #include "jscntxt.h"
61 #include "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"
77 #include "jsautooplen.h"
79 /* Verify JSOP_XXX_LENGTH constant definitions. */
80 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
81 JS_STATIC_ASSERT(op##_LENGTH == length);
82 #include "jsopcode.tbl"
83 #undef OPDEF
85 static const char js_incop_strs[][3] = {"++", "--"};
86 static const char js_for_each_str[] = "for each";
88 const JSCodeSpec js_CodeSpec[] = {
89 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
90 {length,nuses,ndefs,prec,format},
91 #include "jsopcode.tbl"
92 #undef OPDEF
95 uintN js_NumCodeSpecs = JS_ARRAY_LENGTH(js_CodeSpec);
98 * Each element of the array is either a source literal associated with JS
99 * bytecode or null.
101 static const char *CodeToken[] = {
102 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
103 token,
104 #include "jsopcode.tbl"
105 #undef OPDEF
108 #if defined(DEBUG) || defined(JS_JIT_SPEW)
110 * Array of JS bytecode names used by DEBUG-only js_Disassemble and by
111 * JIT debug spew.
113 const char *js_CodeName[] = {
114 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
115 name,
116 #include "jsopcode.tbl"
117 #undef OPDEF
119 #endif
121 /************************************************************************/
123 static ptrdiff_t
124 GetJumpOffset(jsbytecode *pc, jsbytecode *pc2)
126 uint32 type;
128 type = JOF_OPTYPE(*pc);
129 if (JOF_TYPE_IS_EXTENDED_JUMP(type))
130 return GET_JUMPX_OFFSET(pc2);
131 return GET_JUMP_OFFSET(pc2);
134 uintN
135 js_GetIndexFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc,
136 ptrdiff_t pcoff)
138 JSOp op;
139 uintN span, base;
141 op = (JSOp)*pc;
142 if (op == JSOP_TRAP)
143 op = JS_GetTrapOpcode(cx, script, pc);
144 JS_ASSERT(js_CodeSpec[op].length >= 1 + pcoff + UINT16_LEN);
147 * We need to detect index base prefix. It presents when resetbase
148 * follows the bytecode.
150 span = js_CodeSpec[op].length;
151 base = 0;
152 if (pc - script->code + span < script->length) {
153 if (pc[span] == JSOP_RESETBASE) {
154 base = GET_INDEXBASE(pc - JSOP_INDEXBASE_LENGTH);
155 } else if (pc[span] == JSOP_RESETBASE0) {
156 JS_ASSERT(JSOP_INDEXBASE1 <= pc[-1] || pc[-1] <= JSOP_INDEXBASE3);
157 base = (pc[-1] - JSOP_INDEXBASE1 + 1) << 16;
160 return base + GET_UINT16(pc + pcoff);
163 uintN
164 js_GetVariableBytecodeLength(jsbytecode *pc)
166 JSOp op;
167 uintN jmplen, ncases;
168 jsint low, high;
170 op = (JSOp) *pc;
171 JS_ASSERT(js_CodeSpec[op].length == -1);
172 switch (op) {
173 case JSOP_TABLESWITCHX:
174 jmplen = JUMPX_OFFSET_LEN;
175 goto do_table;
176 case JSOP_TABLESWITCH:
177 jmplen = JUMP_OFFSET_LEN;
178 do_table:
179 /* Structure: default-jump case-low case-high case1-jump ... */
180 pc += jmplen;
181 low = GET_JUMP_OFFSET(pc);
182 pc += JUMP_OFFSET_LEN;
183 high = GET_JUMP_OFFSET(pc);
184 ncases = (uintN)(high - low + 1);
185 return 1 + jmplen + INDEX_LEN + INDEX_LEN + ncases * jmplen;
187 case JSOP_LOOKUPSWITCHX:
188 jmplen = JUMPX_OFFSET_LEN;
189 goto do_lookup;
190 default:
191 JS_ASSERT(op == JSOP_LOOKUPSWITCH);
192 jmplen = JUMP_OFFSET_LEN;
193 do_lookup:
194 /* Structure: default-jump case-count (case1-value case1-jump) ... */
195 pc += jmplen;
196 ncases = GET_UINT16(pc);
197 return 1 + jmplen + INDEX_LEN + ncases * (INDEX_LEN + jmplen);
201 uintN
202 js_GetVariableStackUseLength(JSOp op, jsbytecode *pc)
204 JS_ASSERT(*pc == op || *pc == JSOP_TRAP);
205 JS_ASSERT(js_CodeSpec[op].nuses == -1);
206 switch (op) {
207 case JSOP_POPN:
208 return GET_UINT16(pc);
209 case JSOP_LEAVEBLOCK:
210 return GET_UINT16(pc);
211 case JSOP_LEAVEBLOCKEXPR:
212 return GET_UINT16(pc) + 1;
213 case JSOP_NEWARRAY:
214 return GET_UINT24(pc);
215 default:
216 /* stack: fun, this, [argc arguments] */
217 JS_ASSERT(op == JSOP_NEW || op == JSOP_CALL ||
218 op == JSOP_EVAL || op == JSOP_SETCALL ||
219 op == JSOP_APPLY);
220 return 2 + GET_ARGC(pc);
224 #ifdef DEBUG
226 JS_FRIEND_API(JSBool)
227 js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp)
229 jsbytecode *pc, *end;
230 uintN len;
232 pc = script->code;
233 end = pc + script->length;
234 while (pc < end) {
235 if (pc == script->main)
236 fputs("main:\n", fp);
237 len = js_Disassemble1(cx, script, pc,
238 PTRDIFF(pc, script->code, jsbytecode),
239 lines, fp);
240 if (!len)
241 return JS_FALSE;
242 pc += len;
244 return JS_TRUE;
247 const char *
248 ToDisassemblySource(JSContext *cx, jsval v)
250 JSObject *obj;
251 JSScopeProperty *sprop;
252 char *source;
253 const char *bytes;
254 JSString *str;
256 if (!JSVAL_IS_PRIMITIVE(v)) {
257 obj = JSVAL_TO_OBJECT(v);
258 if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) {
259 source = JS_sprintf_append(NULL, "depth %d {",
260 OBJ_BLOCK_DEPTH(cx, obj));
261 for (sprop = OBJ_SCOPE(obj)->lastProp; sprop;
262 sprop = sprop->parent) {
263 bytes = js_AtomToPrintableString(cx, JSID_TO_ATOM(sprop->id));
264 if (!bytes)
265 return NULL;
266 source = JS_sprintf_append(source, "%s: %d%s",
267 bytes, sprop->shortid,
268 sprop->parent ? ", " : "");
270 source = JS_sprintf_append(source, "}");
271 if (!source)
272 return NULL;
273 str = JS_NewString(cx, source, strlen(source));
274 if (!str)
275 return NULL;
276 return js_GetStringBytes(cx, str);
279 return js_ValueToPrintableSource(cx, v);
282 JS_FRIEND_API(uintN)
283 js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc,
284 uintN loc, JSBool lines, FILE *fp)
286 JSOp op;
287 const JSCodeSpec *cs;
288 ptrdiff_t len, off, jmplen;
289 uint32 type;
290 JSAtom *atom;
291 uintN index;
292 JSObject *obj;
293 jsval v;
294 const char *bytes;
295 jsint i;
297 op = (JSOp)*pc;
298 if (op >= JSOP_LIMIT) {
299 char numBuf1[12], numBuf2[12];
300 JS_snprintf(numBuf1, sizeof numBuf1, "%d", op);
301 JS_snprintf(numBuf2, sizeof numBuf2, "%d", JSOP_LIMIT);
302 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
303 JSMSG_BYTECODE_TOO_BIG, numBuf1, numBuf2);
304 return 0;
306 cs = &js_CodeSpec[op];
307 len = (ptrdiff_t) cs->length;
308 fprintf(fp, "%05u:", loc);
309 if (lines)
310 fprintf(fp, "%4u", JS_PCToLineNumber(cx, script, pc));
311 fprintf(fp, " %s", js_CodeName[op]);
312 type = JOF_TYPE(cs->format);
313 switch (type) {
314 case JOF_BYTE:
315 if (op == JSOP_TRAP) {
316 op = JS_GetTrapOpcode(cx, script, pc);
317 len = (ptrdiff_t) js_CodeSpec[op].length;
319 break;
321 case JOF_JUMP:
322 case JOF_JUMPX:
323 off = GetJumpOffset(pc, pc);
324 fprintf(fp, " %u (%d)", loc + (intN) off, (intN) off);
325 break;
327 case JOF_ATOM:
328 case JOF_OBJECT:
329 case JOF_REGEXP:
330 index = js_GetIndexFromBytecode(cx, script, pc, 0);
331 if (type == JOF_ATOM) {
332 JS_GET_SCRIPT_ATOM(script, index, atom);
333 v = ATOM_KEY(atom);
334 } else {
335 if (type == JOF_OBJECT)
336 JS_GET_SCRIPT_OBJECT(script, index, obj);
337 else
338 JS_GET_SCRIPT_REGEXP(script, index, obj);
339 v = OBJECT_TO_JSVAL(obj);
341 bytes = ToDisassemblySource(cx, v);
342 if (!bytes)
343 return 0;
344 fprintf(fp, " %s", bytes);
345 break;
347 case JOF_UINT16:
348 i = (jsint)GET_UINT16(pc);
349 goto print_int;
351 case JOF_TABLESWITCH:
352 case JOF_TABLESWITCHX:
354 jsbytecode *pc2;
355 jsint i, low, high;
357 jmplen = (type == JOF_TABLESWITCH) ? JUMP_OFFSET_LEN
358 : JUMPX_OFFSET_LEN;
359 pc2 = pc;
360 off = GetJumpOffset(pc, pc2);
361 pc2 += jmplen;
362 low = GET_JUMP_OFFSET(pc2);
363 pc2 += JUMP_OFFSET_LEN;
364 high = GET_JUMP_OFFSET(pc2);
365 pc2 += JUMP_OFFSET_LEN;
366 fprintf(fp, " defaultOffset %d low %d high %d", (intN) off, low, high);
367 for (i = low; i <= high; i++) {
368 off = GetJumpOffset(pc, pc2);
369 fprintf(fp, "\n\t%d: %d", i, (intN) off);
370 pc2 += jmplen;
372 len = 1 + pc2 - pc;
373 break;
376 case JOF_LOOKUPSWITCH:
377 case JOF_LOOKUPSWITCHX:
379 jsbytecode *pc2;
380 jsatomid npairs;
382 jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN
383 : JUMPX_OFFSET_LEN;
384 pc2 = pc;
385 off = GetJumpOffset(pc, pc2);
386 pc2 += jmplen;
387 npairs = GET_UINT16(pc2);
388 pc2 += UINT16_LEN;
389 fprintf(fp, " offset %d npairs %u", (intN) off, (uintN) npairs);
390 while (npairs) {
391 JS_GET_SCRIPT_ATOM(script, GET_INDEX(pc2), atom);
392 pc2 += INDEX_LEN;
393 off = GetJumpOffset(pc, pc2);
394 pc2 += jmplen;
396 bytes = ToDisassemblySource(cx, ATOM_KEY(atom));
397 if (!bytes)
398 return 0;
399 fprintf(fp, "\n\t%s: %d", bytes, (intN) off);
400 npairs--;
402 len = 1 + pc2 - pc;
403 break;
406 case JOF_QARG:
407 fprintf(fp, " %u", GET_ARGNO(pc));
408 break;
410 case JOF_LOCAL:
411 fprintf(fp, " %u", GET_SLOTNO(pc));
412 break;
414 case JOF_SLOTATOM:
415 case JOF_SLOTOBJECT:
416 fprintf(fp, " %u", GET_SLOTNO(pc));
417 index = js_GetIndexFromBytecode(cx, script, pc, SLOTNO_LEN);
418 if (type == JOF_SLOTATOM) {
419 JS_GET_SCRIPT_ATOM(script, index, atom);
420 v = ATOM_KEY(atom);
421 } else {
422 JS_GET_SCRIPT_OBJECT(script, index, obj);
423 v = OBJECT_TO_JSVAL(obj);
425 bytes = ToDisassemblySource(cx, v);
426 if (!bytes)
427 return 0;
428 fprintf(fp, " %s", bytes);
429 break;
431 case JOF_UINT24:
432 JS_ASSERT(op == JSOP_UINT24 || op == JSOP_NEWARRAY);
433 i = (jsint)GET_UINT24(pc);
434 goto print_int;
436 case JOF_UINT8:
437 i = pc[1];
438 goto print_int;
440 case JOF_INT8:
441 i = GET_INT8(pc);
442 goto print_int;
444 case JOF_INT32:
445 JS_ASSERT(op == JSOP_INT32);
446 i = GET_INT32(pc);
447 print_int:
448 fprintf(fp, " %d", i);
449 break;
451 default: {
452 char numBuf[12];
453 JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) cs->format);
454 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
455 JSMSG_UNKNOWN_FORMAT, numBuf);
456 return 0;
459 fputs("\n", fp);
460 return len;
463 #endif /* DEBUG */
465 /************************************************************************/
468 * Sprintf, but with unlimited and automatically allocated buffering.
470 typedef struct Sprinter {
471 JSContext *context; /* context executing the decompiler */
472 JSArenaPool *pool; /* string allocation pool */
473 char *base; /* base address of buffer in pool */
474 size_t size; /* size of buffer allocated at base */
475 ptrdiff_t offset; /* offset of next free char in buffer */
476 } Sprinter;
478 #define INIT_SPRINTER(cx, sp, ap, off) \
479 ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0, \
480 (sp)->offset = off)
482 #define OFF2STR(sp,off) ((sp)->base + (off))
483 #define STR2OFF(sp,str) ((str) - (sp)->base)
484 #define RETRACT(sp,str) ((sp)->offset = STR2OFF(sp, str))
486 static JSBool
487 SprintEnsureBuffer(Sprinter *sp, size_t len)
489 ptrdiff_t nb;
490 char *base;
492 nb = (sp->offset + len + 1) - sp->size;
493 if (nb < 0)
494 return JS_TRUE;
495 base = sp->base;
496 if (!base) {
497 JS_ARENA_ALLOCATE_CAST(base, char *, sp->pool, nb);
498 } else {
499 JS_ARENA_GROW_CAST(base, char *, sp->pool, sp->size, nb);
501 if (!base) {
502 js_ReportOutOfScriptQuota(sp->context);
503 return JS_FALSE;
505 sp->base = base;
506 sp->size += nb;
507 return JS_TRUE;
510 static ptrdiff_t
511 SprintPut(Sprinter *sp, const char *s, size_t len)
513 ptrdiff_t offset;
514 char *bp;
516 /* Allocate space for s, including the '\0' at the end. */
517 if (!SprintEnsureBuffer(sp, len))
518 return -1;
520 /* Advance offset and copy s into sp's buffer. */
521 offset = sp->offset;
522 sp->offset += len;
523 bp = sp->base + offset;
524 memmove(bp, s, len);
525 bp[len] = 0;
526 return offset;
529 static ptrdiff_t
530 SprintCString(Sprinter *sp, const char *s)
532 return SprintPut(sp, s, strlen(s));
535 static ptrdiff_t
536 SprintString(Sprinter *sp, JSString *str)
538 jschar *chars;
539 size_t length, size;
540 ptrdiff_t offset;
542 JSSTRING_CHARS_AND_LENGTH(str, chars, length);
543 if (length == 0)
544 return sp->offset;
546 size = js_GetDeflatedStringLength(sp->context, chars, length);
547 if (size == (size_t)-1 || !SprintEnsureBuffer(sp, size))
548 return -1;
550 offset = sp->offset;
551 sp->offset += size;
552 js_DeflateStringToBuffer(sp->context, chars, length, sp->base + offset,
553 &size);
554 sp->base[sp->offset] = 0;
555 return offset;
559 static ptrdiff_t
560 Sprint(Sprinter *sp, const char *format, ...)
562 va_list ap;
563 char *bp;
564 ptrdiff_t offset;
566 va_start(ap, format);
567 bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */
568 va_end(ap);
569 if (!bp) {
570 JS_ReportOutOfMemory(sp->context);
571 return -1;
573 offset = SprintCString(sp, bp);
574 free(bp);
575 return offset;
578 const char js_EscapeMap[] = {
579 '\b', 'b',
580 '\f', 'f',
581 '\n', 'n',
582 '\r', 'r',
583 '\t', 't',
584 '\v', 'v',
585 '"', '"',
586 '\'', '\'',
587 '\\', '\\',
588 '\0', '0'
591 #define DONT_ESCAPE 0x10000
593 static char *
594 QuoteString(Sprinter *sp, JSString *str, uint32 quote)
596 JSBool dontEscape, ok;
597 jschar qc, c;
598 ptrdiff_t off, len;
599 const jschar *s, *t, *z;
600 const char *e;
601 char *bp;
603 /* Sample off first for later return value pointer computation. */
604 dontEscape = (quote & DONT_ESCAPE) != 0;
605 qc = (jschar) quote;
606 off = sp->offset;
607 if (qc && Sprint(sp, "%c", (char)qc) < 0)
608 return NULL;
610 /* Loop control variables: z points at end of string sentinel. */
611 JSSTRING_CHARS_AND_END(str, s, z);
612 for (t = s; t < z; s = ++t) {
613 /* Move t forward from s past un-quote-worthy characters. */
614 c = *t;
615 while (JS_ISPRINT(c) && c != qc && c != '\\' && c != '\t' &&
616 !(c >> 8)) {
617 c = *++t;
618 if (t == z)
619 break;
621 len = PTRDIFF(t, s, jschar);
623 /* Allocate space for s, including the '\0' at the end. */
624 if (!SprintEnsureBuffer(sp, len))
625 return NULL;
627 /* Advance sp->offset and copy s into sp's buffer. */
628 bp = sp->base + sp->offset;
629 sp->offset += len;
630 while (--len >= 0)
631 *bp++ = (char) *s++;
632 *bp = '\0';
634 if (t == z)
635 break;
637 /* Use js_EscapeMap, \u, or \x only if necessary. */
638 if (!(c >> 8) && (e = strchr(js_EscapeMap, (int)c)) != NULL) {
639 ok = dontEscape
640 ? Sprint(sp, "%c", (char)c) >= 0
641 : Sprint(sp, "\\%c", e[1]) >= 0;
642 } else {
643 ok = Sprint(sp, (c >> 8) ? "\\u%04X" : "\\x%02X", c) >= 0;
645 if (!ok)
646 return NULL;
649 /* Sprint the closing quote and return the quoted string. */
650 if (qc && Sprint(sp, "%c", (char)qc) < 0)
651 return NULL;
654 * If we haven't Sprint'd anything yet, Sprint an empty string so that
655 * the OFF2STR below gives a valid result.
657 if (off == sp->offset && Sprint(sp, "") < 0)
658 return NULL;
659 return OFF2STR(sp, off);
662 JSString *
663 js_QuoteString(JSContext *cx, JSString *str, jschar quote)
665 void *mark;
666 Sprinter sprinter;
667 char *bytes;
668 JSString *escstr;
670 mark = JS_ARENA_MARK(&cx->tempPool);
671 INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);
672 bytes = QuoteString(&sprinter, str, quote);
673 escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL;
674 JS_ARENA_RELEASE(&cx->tempPool, mark);
675 return escstr;
678 /************************************************************************/
680 struct JSPrinter {
681 Sprinter sprinter; /* base class state */
682 JSArenaPool pool; /* string allocation pool */
683 uintN indent; /* indentation in spaces */
684 JSPackedBool pretty; /* pretty-print: indent, use newlines */
685 JSPackedBool grouped; /* in parenthesized expression context */
686 JSScript *script; /* script being printed */
687 jsbytecode *dvgfence; /* DecompileExpression fencepost */
688 jsbytecode **pcstack; /* DecompileExpression modeled stack */
689 JSFunction *fun; /* interpreted function */
690 jsuword *localNames; /* argument and variable names */
694 * Hack another flag, a la JS_DONT_PRETTY_PRINT, into uintN indent parameters
695 * to functions such as js_DecompileFunction and js_NewPrinter. This time, as
696 * opposed to JS_DONT_PRETTY_PRINT back in the dark ages, we can assume that a
697 * uintN is at least 32 bits.
699 #define JS_IN_GROUP_CONTEXT 0x10000
701 JSPrinter *
702 JS_NEW_PRINTER(JSContext *cx, const char *name, JSFunction *fun,
703 uintN indent, JSBool pretty)
705 JSPrinter *jp;
707 jp = (JSPrinter *) JS_malloc(cx, sizeof(JSPrinter));
708 if (!jp)
709 return NULL;
710 INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
711 JS_INIT_ARENA_POOL(&jp->pool, name, 256, 1, &cx->scriptStackQuota);
712 jp->indent = indent & ~JS_IN_GROUP_CONTEXT;
713 jp->pretty = pretty;
714 jp->grouped = (indent & JS_IN_GROUP_CONTEXT) != 0;
715 jp->script = NULL;
716 jp->dvgfence = NULL;
717 jp->pcstack = NULL;
718 jp->fun = fun;
719 jp->localNames = NULL;
720 if (fun && FUN_INTERPRETED(fun) && JS_GET_LOCAL_NAME_COUNT(fun)) {
721 jp->localNames = js_GetLocalNameArray(cx, fun, &jp->pool);
722 if (!jp->localNames) {
723 js_DestroyPrinter(jp);
724 return NULL;
727 return jp;
730 void
731 js_DestroyPrinter(JSPrinter *jp)
733 JS_FinishArenaPool(&jp->pool);
734 JS_free(jp->sprinter.context, jp);
737 JSString *
738 js_GetPrinterOutput(JSPrinter *jp)
740 JSContext *cx;
741 JSString *str;
743 cx = jp->sprinter.context;
744 if (!jp->sprinter.base)
745 return cx->runtime->emptyString;
746 str = JS_NewStringCopyZ(cx, jp->sprinter.base);
747 if (!str)
748 return NULL;
749 JS_FreeArenaPool(&jp->pool);
750 INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
751 return str;
755 * NB: Indexed by SRC_DECL_* defines from jsemit.h.
757 static const char * const var_prefix[] = {"var ", "const ", "let "};
759 static const char *
760 VarPrefix(jssrcnote *sn)
762 if (sn && (SN_TYPE(sn) == SRC_DECL || SN_TYPE(sn) == SRC_GROUPASSIGN)) {
763 ptrdiff_t type = js_GetSrcNoteOffset(sn, 0);
764 if ((uintN)type <= SRC_DECL_LET)
765 return var_prefix[type];
767 return "";
771 js_printf(JSPrinter *jp, const char *format, ...)
773 va_list ap;
774 char *bp, *fp;
775 int cc;
777 if (*format == '\0')
778 return 0;
780 va_start(ap, format);
782 /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */
783 if (*format == '\t') {
784 format++;
785 if (jp->pretty && Sprint(&jp->sprinter, "%*s", jp->indent, "") < 0)
786 return -1;
789 /* Suppress newlines (must be once per format, at the end) if not pretty. */
790 fp = NULL;
791 if (!jp->pretty && format[cc = strlen(format) - 1] == '\n') {
792 fp = JS_strdup(jp->sprinter.context, format);
793 if (!fp)
794 return -1;
795 fp[cc] = '\0';
796 format = fp;
799 /* Allocate temp space, convert format, and put. */
800 bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */
801 if (fp) {
802 JS_free(jp->sprinter.context, fp);
803 format = NULL;
805 if (!bp) {
806 JS_ReportOutOfMemory(jp->sprinter.context);
807 return -1;
810 cc = strlen(bp);
811 if (SprintPut(&jp->sprinter, bp, (size_t)cc) < 0)
812 cc = -1;
813 free(bp);
815 va_end(ap);
816 return cc;
819 JSBool
820 js_puts(JSPrinter *jp, const char *s)
822 return SprintCString(&jp->sprinter, s) >= 0;
825 /************************************************************************/
827 typedef struct SprintStack {
828 Sprinter sprinter; /* sprinter for postfix to infix buffering */
829 ptrdiff_t *offsets; /* stack of postfix string offsets */
830 jsbytecode *opcodes; /* parallel stack of JS opcodes */
831 uintN top; /* top of stack index */
832 uintN inArrayInit; /* array initialiser/comprehension level */
833 JSBool inGenExp; /* in generator expression */
834 JSPrinter *printer; /* permanent output goes here */
835 } SprintStack;
838 * Find the depth of the operand stack when the interpreter reaches the given
839 * pc in script. pcstack must have space for least script->depth elements. On
840 * return it will contain pointers to opcodes that populated the interpreter's
841 * current operand stack.
843 * This function cannot raise an exception or error. However, due to a risk of
844 * potential bugs when modeling the stack, the function returns -1 if it
845 * detects an inconsistency in the model. Such an inconsistency triggers an
846 * assert in a debug build.
848 static intN
849 ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *pc,
850 jsbytecode **pcstack);
852 #define FAILED_EXPRESSION_DECOMPILER ((char *) 1)
855 * Decompile a part of expression up to the given pc. The function returns
856 * NULL on out-of-memory, or the FAILED_EXPRESSION_DECOMPILER sentinel when
857 * the decompiler fails due to a bug and/or unimplemented feature, or the
858 * decompiled string on success.
860 static char *
861 DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun,
862 jsbytecode *pc);
865 * Get a stacked offset from ss->sprinter.base, or if the stacked value |off|
866 * is negative, fetch the generating pc from printer->pcstack[-2 - off] and
867 * decompile the code that generated the missing value. This is used when
868 * reporting errors, where the model stack will lack |pcdepth| non-negative
869 * offsets (see DecompileExpression and DecompileCode).
871 * If the stacked offset is -1, return 0 to index the NUL padding at the start
872 * of ss->sprinter.base. If this happens, it means there is a decompiler bug
873 * to fix, but it won't violate memory safety.
875 static ptrdiff_t
876 GetOff(SprintStack *ss, uintN i)
878 ptrdiff_t off;
879 jsbytecode *pc;
880 char *bytes;
882 off = ss->offsets[i];
883 if (off >= 0)
884 return off;
886 JS_ASSERT(off <= -2);
887 JS_ASSERT(ss->printer->pcstack);
888 if (off < -2 && ss->printer->pcstack) {
889 pc = ss->printer->pcstack[-2 - off];
890 bytes = DecompileExpression(ss->sprinter.context, ss->printer->script,
891 ss->printer->fun, pc);
892 if (!bytes)
893 return 0;
894 if (bytes != FAILED_EXPRESSION_DECOMPILER) {
895 off = SprintCString(&ss->sprinter, bytes);
896 if (off < 0)
897 off = 0;
898 ss->offsets[i] = off;
899 JS_free(ss->sprinter.context, bytes);
900 return off;
902 if (!ss->sprinter.base && SprintPut(&ss->sprinter, "", 0) >= 0) {
903 memset(ss->sprinter.base, 0, ss->sprinter.offset);
904 ss->offsets[i] = -1;
907 return 0;
910 static const char *
911 GetStr(SprintStack *ss, uintN i)
913 ptrdiff_t off;
916 * Must call GetOff before using ss->sprinter.base, since it may be null
917 * until bootstrapped by GetOff.
919 off = GetOff(ss, i);
920 return OFF2STR(&ss->sprinter, off);
924 * Gap between stacked strings to allow for insertion of parens and commas
925 * when auto-parenthesizing expressions and decompiling array initialisers
926 * (see the JSOP_NEWARRAY case in Decompile).
928 #define PAREN_SLOP (2 + 1)
931 * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME,
932 * JSOP_SETPROP, and JSOP_SETELEM, respectively. They are never stored in
933 * bytecode, so they don't preempt valid opcodes.
935 #define JSOP_GETPROP2 256
936 #define JSOP_GETELEM2 257
938 static void
939 AddParenSlop(SprintStack *ss)
941 memset(OFF2STR(&ss->sprinter, ss->sprinter.offset), 0, PAREN_SLOP);
942 ss->sprinter.offset += PAREN_SLOP;
945 static JSBool
946 PushOff(SprintStack *ss, ptrdiff_t off, JSOp op)
948 uintN top;
950 if (!SprintEnsureBuffer(&ss->sprinter, PAREN_SLOP))
951 return JS_FALSE;
953 /* ss->top points to the next free slot; be paranoid about overflow. */
954 top = ss->top;
955 JS_ASSERT(top < StackDepth(ss->printer->script));
956 if (top >= StackDepth(ss->printer->script)) {
957 JS_ReportOutOfMemory(ss->sprinter.context);
958 return JS_FALSE;
961 /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */
962 ss->offsets[top] = off;
963 ss->opcodes[top] = (op == JSOP_GETPROP2) ? JSOP_GETPROP
964 : (op == JSOP_GETELEM2) ? JSOP_GETELEM
965 : (jsbytecode) op;
966 ss->top = ++top;
967 AddParenSlop(ss);
968 return JS_TRUE;
971 static ptrdiff_t
972 PopOffPrec(SprintStack *ss, uint8 prec)
974 uintN top;
975 const JSCodeSpec *topcs;
976 ptrdiff_t off;
978 /* ss->top points to the next free slot; be paranoid about underflow. */
979 top = ss->top;
980 JS_ASSERT(top != 0);
981 if (top == 0)
982 return 0;
984 ss->top = --top;
985 off = GetOff(ss, top);
986 topcs = &js_CodeSpec[ss->opcodes[top]];
987 if (topcs->prec != 0 && topcs->prec < prec) {
988 ss->sprinter.offset = ss->offsets[top] = off - 2;
989 off = Sprint(&ss->sprinter, "(%s)", OFF2STR(&ss->sprinter, off));
990 } else {
991 ss->sprinter.offset = off;
993 return off;
996 static const char *
997 PopStrPrec(SprintStack *ss, uint8 prec)
999 ptrdiff_t off;
1001 off = PopOffPrec(ss, prec);
1002 return OFF2STR(&ss->sprinter, off);
1005 static ptrdiff_t
1006 PopOff(SprintStack *ss, JSOp op)
1008 return PopOffPrec(ss, js_CodeSpec[op].prec);
1011 static const char *
1012 PopStr(SprintStack *ss, JSOp op)
1014 return PopStrPrec(ss, js_CodeSpec[op].prec);
1017 typedef struct TableEntry {
1018 jsval key;
1019 ptrdiff_t offset;
1020 JSAtom *label;
1021 jsint order; /* source order for stable tableswitch sort */
1022 } TableEntry;
1024 static JSBool
1025 CompareOffsets(void *arg, const void *v1, const void *v2, int *result)
1027 ptrdiff_t offset_diff;
1028 const TableEntry *te1 = (const TableEntry *) v1,
1029 *te2 = (const TableEntry *) v2;
1031 offset_diff = te1->offset - te2->offset;
1032 *result = (offset_diff == 0 ? te1->order - te2->order
1033 : offset_diff < 0 ? -1
1034 : 1);
1035 return JS_TRUE;
1038 static ptrdiff_t
1039 SprintDoubleValue(Sprinter *sp, jsval v, JSOp *opp)
1041 jsdouble d;
1042 ptrdiff_t todo;
1043 char *s, buf[DTOSTR_STANDARD_BUFFER_SIZE];
1045 JS_ASSERT(JSVAL_IS_DOUBLE(v));
1046 d = *JSVAL_TO_DOUBLE(v);
1047 if (JSDOUBLE_IS_NEGZERO(d)) {
1048 todo = SprintCString(sp, "-0");
1049 *opp = JSOP_NEG;
1050 } else if (!JSDOUBLE_IS_FINITE(d)) {
1051 /* Don't use Infinity and NaN, they're mutable. */
1052 todo = SprintCString(sp,
1053 JSDOUBLE_IS_NaN(d)
1054 ? "0 / 0"
1055 : (d < 0)
1056 ? "1 / -0"
1057 : "1 / 0");
1058 *opp = JSOP_DIV;
1059 } else {
1060 s = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, d);
1061 if (!s) {
1062 JS_ReportOutOfMemory(sp->context);
1063 return -1;
1065 JS_ASSERT(strcmp(s, js_Infinity_str) &&
1066 (*s != '-' ||
1067 strcmp(s + 1, js_Infinity_str)) &&
1068 strcmp(s, js_NaN_str));
1069 todo = Sprint(sp, s);
1071 return todo;
1074 static jsbytecode *
1075 Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop);
1077 static JSBool
1078 DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength,
1079 jsbytecode *pc, ptrdiff_t switchLength,
1080 ptrdiff_t defaultOffset, JSBool isCondSwitch)
1082 JSContext *cx;
1083 JSPrinter *jp;
1084 ptrdiff_t off, off2, diff, caseExprOff, todo;
1085 char *lval, *rval;
1086 uintN i;
1087 jsval key;
1088 JSString *str;
1090 cx = ss->sprinter.context;
1091 jp = ss->printer;
1093 /* JSOP_CONDSWITCH doesn't pop, unlike JSOP_{LOOKUP,TABLE}SWITCH. */
1094 off = isCondSwitch ? GetOff(ss, ss->top-1) : PopOff(ss, JSOP_NOP);
1095 lval = OFF2STR(&ss->sprinter, off);
1097 js_printf(jp, "\tswitch (%s) {\n", lval);
1099 if (tableLength) {
1100 diff = table[0].offset - defaultOffset;
1101 if (diff > 0) {
1102 jp->indent += 2;
1103 js_printf(jp, "\t%s:\n", js_default_str);
1104 jp->indent += 2;
1105 if (!Decompile(ss, pc + defaultOffset, diff, JSOP_NOP))
1106 return JS_FALSE;
1107 jp->indent -= 4;
1110 caseExprOff = isCondSwitch ? JSOP_CONDSWITCH_LENGTH : 0;
1112 for (i = 0; i < tableLength; i++) {
1113 off = table[i].offset;
1114 off2 = (i + 1 < tableLength) ? table[i + 1].offset : switchLength;
1116 key = table[i].key;
1117 if (isCondSwitch) {
1118 ptrdiff_t nextCaseExprOff;
1121 * key encodes the JSOP_CASE bytecode's offset from switchtop.
1122 * The next case expression follows immediately, unless we are
1123 * at the last case.
1125 nextCaseExprOff = (ptrdiff_t)JSVAL_TO_INT(key);
1126 nextCaseExprOff += js_CodeSpec[pc[nextCaseExprOff]].length;
1127 jp->indent += 2;
1128 if (!Decompile(ss, pc + caseExprOff,
1129 nextCaseExprOff - caseExprOff, JSOP_NOP)) {
1130 return JS_FALSE;
1132 caseExprOff = nextCaseExprOff;
1134 /* Balance the stack as if this JSOP_CASE matched. */
1135 --ss->top;
1136 } else {
1138 * key comes from an atom, not the decompiler, so we need to
1139 * quote it if it's a string literal. But if table[i].label
1140 * is non-null, key was constant-propagated and label is the
1141 * name of the const we should show as the case label. We set
1142 * key to undefined so this identifier is escaped, if required
1143 * by non-ASCII characters, but not quoted, by QuoteString.
1145 todo = -1;
1146 if (table[i].label) {
1147 str = ATOM_TO_STRING(table[i].label);
1148 key = JSVAL_VOID;
1149 } else if (JSVAL_IS_DOUBLE(key)) {
1150 JSOp junk;
1152 todo = SprintDoubleValue(&ss->sprinter, key, &junk);
1153 str = NULL;
1154 } else {
1155 str = js_ValueToString(cx, key);
1156 if (!str)
1157 return JS_FALSE;
1159 if (todo >= 0) {
1160 rval = OFF2STR(&ss->sprinter, todo);
1161 } else {
1162 rval = QuoteString(&ss->sprinter, str, (jschar)
1163 (JSVAL_IS_STRING(key) ? '"' : 0));
1164 if (!rval)
1165 return JS_FALSE;
1167 RETRACT(&ss->sprinter, rval);
1168 jp->indent += 2;
1169 js_printf(jp, "\tcase %s:\n", rval);
1172 jp->indent += 2;
1173 if (off <= defaultOffset && defaultOffset < off2) {
1174 diff = defaultOffset - off;
1175 if (diff != 0) {
1176 if (!Decompile(ss, pc + off, diff, JSOP_NOP))
1177 return JS_FALSE;
1178 off = defaultOffset;
1180 jp->indent -= 2;
1181 js_printf(jp, "\t%s:\n", js_default_str);
1182 jp->indent += 2;
1184 if (!Decompile(ss, pc + off, off2 - off, JSOP_NOP))
1185 return JS_FALSE;
1186 jp->indent -= 4;
1188 /* Re-balance as if last JSOP_CASE or JSOP_DEFAULT mismatched. */
1189 if (isCondSwitch)
1190 ++ss->top;
1194 if (defaultOffset == switchLength) {
1195 jp->indent += 2;
1196 js_printf(jp, "\t%s:;\n", js_default_str);
1197 jp->indent -= 2;
1199 js_printf(jp, "\t}\n");
1201 /* By the end of a JSOP_CONDSWITCH, the discriminant has been popped. */
1202 if (isCondSwitch)
1203 --ss->top;
1204 return JS_TRUE;
1207 #define LOCAL_ASSERT_CUSTOM(expr, BAD_EXIT) \
1208 JS_BEGIN_MACRO \
1209 JS_ASSERT(expr); \
1210 if (!(expr)) { BAD_EXIT; } \
1211 JS_END_MACRO
1213 #define LOCAL_ASSERT_RV(expr, rv) \
1214 LOCAL_ASSERT_CUSTOM(expr, return (rv))
1216 static JSAtom *
1217 GetArgOrVarAtom(JSPrinter *jp, uintN slot)
1219 JSAtom *name;
1221 LOCAL_ASSERT_RV(jp->fun, NULL);
1222 LOCAL_ASSERT_RV(slot < (uintN) JS_GET_LOCAL_NAME_COUNT(jp->fun), NULL);
1223 name = JS_LOCAL_NAME_TO_ATOM(jp->localNames[slot]);
1224 #if !JS_HAS_DESTRUCTURING
1225 LOCAL_ASSERT_RV(name, NULL);
1226 #endif
1227 return name;
1230 const char *
1231 GetLocal(SprintStack *ss, jsint i)
1233 ptrdiff_t off;
1234 JSContext *cx;
1235 JSScript *script;
1236 jsatomid j, n;
1237 JSAtom *atom;
1238 JSObject *obj;
1239 jsint depth, count;
1240 JSScopeProperty *sprop;
1241 const char *rval;
1243 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, "")
1245 off = ss->offsets[i];
1246 if (off >= 0)
1247 return OFF2STR(&ss->sprinter, off);
1250 * We must be called from js_DecompileValueGenerator (via Decompile) when
1251 * dereferencing a local that's undefined or null. Search script->objects
1252 * for the block containing this local by its stack index, i.
1254 cx = ss->sprinter.context;
1255 script = ss->printer->script;
1256 LOCAL_ASSERT(script->objectsOffset != 0);
1257 for (j = 0, n = JS_SCRIPT_OBJECTS(script)->length; ; j++) {
1258 LOCAL_ASSERT(j < n);
1259 JS_GET_SCRIPT_OBJECT(script, j, obj);
1260 if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) {
1261 depth = OBJ_BLOCK_DEPTH(cx, obj);
1262 count = OBJ_BLOCK_COUNT(cx, obj);
1263 if ((jsuint)(i - depth) < (jsuint)count)
1264 break;
1268 i -= depth;
1269 for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) {
1270 if (sprop->shortid == i)
1271 break;
1274 LOCAL_ASSERT(sprop && JSID_IS_ATOM(sprop->id));
1275 atom = JSID_TO_ATOM(sprop->id);
1276 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1277 if (!rval)
1278 return NULL;
1279 RETRACT(&ss->sprinter, rval);
1280 return rval;
1282 #undef LOCAL_ASSERT
1285 static JSBool
1286 IsVarSlot(JSPrinter *jp, jsbytecode *pc, jsint *indexp)
1288 uintN slot;
1290 slot = GET_SLOTNO(pc);
1291 if (slot < jp->script->nfixed) {
1292 /* The slot refers to a variable with name stored in jp->localNames. */
1293 *indexp = jp->fun->nargs + slot;
1294 return JS_TRUE;
1297 /* We have a local which index is relative to the stack base. */
1298 slot -= jp->script->nfixed;
1299 JS_ASSERT(slot < StackDepth(jp->script));
1300 *indexp = slot;
1301 return JS_FALSE;
1304 #if JS_HAS_DESTRUCTURING
1306 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
1307 #define LOAD_OP_DATA(pc) (oplen = (cs = &js_CodeSpec[op=(JSOp)*pc])->length)
1309 static jsbytecode *
1310 DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc);
1312 static jsbytecode *
1313 DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
1314 JSBool *hole)
1316 JSContext *cx;
1317 JSPrinter *jp;
1318 JSOp op;
1319 const JSCodeSpec *cs;
1320 uintN oplen;
1321 jsint i;
1322 const char *lval, *xval;
1323 ptrdiff_t todo;
1324 JSAtom *atom;
1326 *hole = JS_FALSE;
1327 cx = ss->sprinter.context;
1328 jp = ss->printer;
1329 LOAD_OP_DATA(pc);
1331 switch (op) {
1332 case JSOP_POP:
1333 *hole = JS_TRUE;
1334 todo = SprintPut(&ss->sprinter, ", ", 2);
1335 break;
1337 case JSOP_DUP:
1338 pc = DecompileDestructuring(ss, pc, endpc);
1339 if (!pc)
1340 return NULL;
1341 if (pc == endpc)
1342 return pc;
1343 LOAD_OP_DATA(pc);
1344 lval = PopStr(ss, JSOP_NOP);
1345 todo = SprintCString(&ss->sprinter, lval);
1346 if (op == JSOP_POPN)
1347 return pc;
1348 LOCAL_ASSERT(*pc == JSOP_POP);
1349 break;
1351 case JSOP_SETARG:
1352 case JSOP_SETGVAR:
1353 case JSOP_SETLOCAL:
1354 LOCAL_ASSERT(pc[oplen] == JSOP_POP || pc[oplen] == JSOP_POPN);
1355 /* FALL THROUGH */
1357 case JSOP_SETLOCALPOP:
1358 atom = NULL;
1359 lval = NULL;
1360 if (op == JSOP_SETARG) {
1361 atom = GetArgOrVarAtom(jp, GET_SLOTNO(pc));
1362 LOCAL_ASSERT(atom);
1363 } else if (op == JSOP_SETGVAR) {
1364 GET_ATOM_FROM_BYTECODE(jp->script, pc, 0, atom);
1365 } else if (IsVarSlot(jp, pc, &i)) {
1366 atom = GetArgOrVarAtom(jp, i);
1367 LOCAL_ASSERT(atom);
1368 } else {
1369 lval = GetLocal(ss, i);
1371 if (atom)
1372 lval = js_AtomToPrintableString(cx, atom);
1373 LOCAL_ASSERT(lval);
1374 todo = SprintCString(&ss->sprinter, lval);
1375 if (op != JSOP_SETLOCALPOP) {
1376 pc += oplen;
1377 if (pc == endpc)
1378 return pc;
1379 LOAD_OP_DATA(pc);
1380 if (op == JSOP_POPN)
1381 return pc;
1382 LOCAL_ASSERT(op == JSOP_POP);
1384 break;
1386 default:
1388 * We may need to auto-parenthesize the left-most value decompiled
1389 * here, so add back PAREN_SLOP temporarily. Then decompile until the
1390 * opcode that would reduce the stack depth to (ss->top-1), which we
1391 * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for
1392 * the nb parameter.
1394 todo = ss->sprinter.offset;
1395 ss->sprinter.offset = todo + PAREN_SLOP;
1396 pc = Decompile(ss, pc, -((intN)ss->top), JSOP_NOP);
1397 if (!pc)
1398 return NULL;
1399 if (pc == endpc)
1400 return pc;
1401 LOAD_OP_DATA(pc);
1402 LOCAL_ASSERT(op == JSOP_ENUMELEM || op == JSOP_ENUMCONSTELEM);
1403 xval = PopStr(ss, JSOP_NOP);
1404 lval = PopStr(ss, JSOP_GETPROP);
1405 ss->sprinter.offset = todo;
1406 if (*lval == '\0') {
1407 /* lval is from JSOP_BINDNAME, so just print xval. */
1408 todo = SprintCString(&ss->sprinter, xval);
1409 } else if (*xval == '\0') {
1410 /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */
1411 todo = SprintCString(&ss->sprinter, lval);
1412 } else {
1413 todo = Sprint(&ss->sprinter,
1414 (JOF_OPMODE(ss->opcodes[ss->top+1]) == JOF_XMLNAME)
1415 ? "%s.%s"
1416 : "%s[%s]",
1417 lval, xval);
1419 break;
1422 if (todo < 0)
1423 return NULL;
1425 LOCAL_ASSERT(pc < endpc);
1426 pc += oplen;
1427 return pc;
1431 * Starting with a SRC_DESTRUCT-annotated JSOP_DUP, decompile a destructuring
1432 * left-hand side object or array initialiser, including nested destructuring
1433 * initialisers. On successful return, the decompilation will be pushed on ss
1434 * and the return value will point to the POP or GROUP bytecode following the
1435 * destructuring expression.
1437 * At any point, if pc is equal to endpc and would otherwise advance, we stop
1438 * immediately and return endpc.
1440 static jsbytecode *
1441 DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc)
1443 ptrdiff_t head;
1444 JSContext *cx;
1445 JSPrinter *jp;
1446 JSOp op, saveop;
1447 const JSCodeSpec *cs;
1448 uintN oplen;
1449 jsint i, lasti;
1450 jsdouble d;
1451 const char *lval;
1452 JSAtom *atom;
1453 jssrcnote *sn;
1454 JSString *str;
1455 JSBool hole;
1457 LOCAL_ASSERT(*pc == JSOP_DUP);
1458 pc += JSOP_DUP_LENGTH;
1461 * Set head so we can rewrite '[' to '{' as needed. Back up PAREN_SLOP
1462 * chars so the destructuring decompilation accumulates contiguously in
1463 * ss->sprinter starting with "[".
1465 head = SprintPut(&ss->sprinter, "[", 1);
1466 if (head < 0 || !PushOff(ss, head, JSOP_NOP))
1467 return NULL;
1468 ss->sprinter.offset -= PAREN_SLOP;
1469 LOCAL_ASSERT(head == ss->sprinter.offset - 1);
1470 LOCAL_ASSERT(*OFF2STR(&ss->sprinter, head) == '[');
1472 cx = ss->sprinter.context;
1473 jp = ss->printer;
1474 lasti = -1;
1476 while (pc < endpc) {
1477 #if JS_HAS_DESTRUCTURING_SHORTHAND
1478 ptrdiff_t nameoff = -1;
1479 #endif
1481 LOAD_OP_DATA(pc);
1482 saveop = op;
1484 switch (op) {
1485 case JSOP_POP:
1486 pc += oplen;
1487 goto out;
1489 /* Handle the optimized number-pushing opcodes. */
1490 case JSOP_ZERO: d = i = 0; goto do_getelem;
1491 case JSOP_ONE: d = i = 1; goto do_getelem;
1492 case JSOP_UINT16: d = i = GET_UINT16(pc); goto do_getelem;
1493 case JSOP_UINT24: d = i = GET_UINT24(pc); goto do_getelem;
1494 case JSOP_INT8: d = i = GET_INT8(pc); goto do_getelem;
1495 case JSOP_INT32: d = i = GET_INT32(pc); goto do_getelem;
1497 case JSOP_DOUBLE:
1498 GET_ATOM_FROM_BYTECODE(jp->script, pc, 0, atom);
1499 d = *ATOM_TO_DOUBLE(atom);
1500 LOCAL_ASSERT(JSDOUBLE_IS_FINITE(d) && !JSDOUBLE_IS_NEGZERO(d));
1501 i = (jsint)d;
1503 do_getelem:
1504 sn = js_GetSrcNote(jp->script, pc);
1505 pc += oplen;
1506 if (pc == endpc)
1507 return pc;
1508 LOAD_OP_DATA(pc);
1509 LOCAL_ASSERT(op == JSOP_GETELEM);
1511 /* Distinguish object from array by opcode or source note. */
1512 if (sn && SN_TYPE(sn) == SRC_INITPROP) {
1513 *OFF2STR(&ss->sprinter, head) = '{';
1514 if (Sprint(&ss->sprinter, "%g: ", d) < 0)
1515 return NULL;
1516 } else {
1517 /* Sanity check for the gnarly control flow above. */
1518 LOCAL_ASSERT(i == d);
1520 /* Fill in any holes (holes at the end don't matter). */
1521 while (++lasti < i) {
1522 if (SprintPut(&ss->sprinter, ", ", 2) < 0)
1523 return NULL;
1526 break;
1528 case JSOP_LENGTH:
1529 atom = cx->runtime->atomState.lengthAtom;
1530 goto do_destructure_atom;
1532 case JSOP_CALLPROP:
1533 case JSOP_GETPROP:
1534 GET_ATOM_FROM_BYTECODE(jp->script, pc, 0, atom);
1535 do_destructure_atom:
1536 *OFF2STR(&ss->sprinter, head) = '{';
1537 str = ATOM_TO_STRING(atom);
1538 #if JS_HAS_DESTRUCTURING_SHORTHAND
1539 nameoff = ss->sprinter.offset;
1540 #endif
1541 if (!QuoteString(&ss->sprinter, str,
1542 js_IsIdentifier(str) ? 0 : (jschar)'\'')) {
1543 return NULL;
1545 if (SprintPut(&ss->sprinter, ": ", 2) < 0)
1546 return NULL;
1547 break;
1549 default:
1550 LOCAL_ASSERT(0);
1553 pc += oplen;
1554 if (pc == endpc)
1555 return pc;
1558 * Decompile the left-hand side expression whose bytecode starts at pc
1559 * and continues for a bounded number of bytecodes or stack operations
1560 * (and which in any event stops before endpc).
1562 pc = DecompileDestructuringLHS(ss, pc, endpc, &hole);
1563 if (!pc)
1564 return NULL;
1566 #if JS_HAS_DESTRUCTURING_SHORTHAND
1567 if (nameoff >= 0) {
1568 ptrdiff_t offset, initlen;
1570 offset = ss->sprinter.offset;
1571 LOCAL_ASSERT(*OFF2STR(&ss->sprinter, offset) == '\0');
1572 initlen = offset - nameoff;
1573 LOCAL_ASSERT(initlen >= 4);
1575 /* Early check to rule out odd "name: lval" length. */
1576 if (((size_t)initlen & 1) == 0) {
1577 size_t namelen;
1578 const char *name;
1581 * Even "name: lval" string length: check for "x: x" and the
1582 * like, and apply the shorthand if we can.
1584 namelen = (size_t)(initlen - 2) >> 1;
1585 name = OFF2STR(&ss->sprinter, nameoff);
1586 if (!strncmp(name + namelen, ": ", 2) &&
1587 !strncmp(name, name + namelen + 2, namelen)) {
1588 offset -= namelen + 2;
1589 *OFF2STR(&ss->sprinter, offset) = '\0';
1590 ss->sprinter.offset = offset;
1594 #endif
1596 if (pc == endpc || *pc != JSOP_DUP)
1597 break;
1600 * Check for SRC_DESTRUCT on this JSOP_DUP, which would mean another
1601 * destructuring initialiser abuts this one, and we should stop. This
1602 * happens with source of the form '[a] = [b] = c'.
1604 sn = js_GetSrcNote(jp->script, pc);
1605 if (sn && SN_TYPE(sn) == SRC_DESTRUCT)
1606 break;
1608 if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
1609 return NULL;
1611 pc += JSOP_DUP_LENGTH;
1614 out:
1615 lval = OFF2STR(&ss->sprinter, head);
1616 if (SprintPut(&ss->sprinter, (*lval == '[') ? "]" : "}", 1) < 0)
1617 return NULL;
1618 return pc;
1621 static jsbytecode *
1622 DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
1623 jssrcnote *sn, ptrdiff_t *todop)
1625 JSOp op;
1626 const JSCodeSpec *cs;
1627 uintN oplen, start, end, i;
1628 ptrdiff_t todo;
1629 JSBool hole;
1630 const char *rval;
1632 LOAD_OP_DATA(pc);
1633 LOCAL_ASSERT(op == JSOP_PUSH || op == JSOP_GETLOCAL);
1635 todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn));
1636 if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
1637 return NULL;
1638 ss->sprinter.offset -= PAREN_SLOP;
1640 for (;;) {
1641 pc += oplen;
1642 if (pc == endpc)
1643 return pc;
1644 pc = DecompileDestructuringLHS(ss, pc, endpc, &hole);
1645 if (!pc)
1646 return NULL;
1647 if (pc == endpc)
1648 return pc;
1649 LOAD_OP_DATA(pc);
1650 if (op != JSOP_PUSH && op != JSOP_GETLOCAL)
1651 break;
1652 if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
1653 return NULL;
1656 LOCAL_ASSERT(op == JSOP_POPN);
1657 if (SprintPut(&ss->sprinter, "] = [", 5) < 0)
1658 return NULL;
1660 end = ss->top - 1;
1661 start = end - GET_UINT16(pc);
1662 for (i = start; i < end; i++) {
1663 rval = GetStr(ss, i);
1664 if (Sprint(&ss->sprinter,
1665 (i == start) ? "%s" : ", %s",
1666 (i == end - 1 && *rval == '\0') ? ", " : rval) < 0) {
1667 return NULL;
1671 if (SprintPut(&ss->sprinter, "]", 1) < 0)
1672 return NULL;
1673 ss->sprinter.offset = ss->offsets[i];
1674 ss->top = start;
1675 *todop = todo;
1676 return pc;
1679 #undef LOCAL_ASSERT
1680 #undef LOAD_OP_DATA
1682 #endif /* JS_HAS_DESTRUCTURING */
1684 static JSBool
1685 InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth)
1687 size_t offsetsz, opcodesz;
1688 void *space;
1690 INIT_SPRINTER(cx, &ss->sprinter, &cx->tempPool, PAREN_SLOP);
1692 /* Allocate the parallel (to avoid padding) offset and opcode stacks. */
1693 offsetsz = depth * sizeof(ptrdiff_t);
1694 opcodesz = depth * sizeof(jsbytecode);
1695 JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz);
1696 if (!space) {
1697 js_ReportOutOfScriptQuota(cx);
1698 return JS_FALSE;
1700 ss->offsets = (ptrdiff_t *) space;
1701 ss->opcodes = (jsbytecode *) ((char *)space + offsetsz);
1703 ss->top = ss->inArrayInit = 0;
1704 ss->inGenExp = JS_FALSE;
1705 ss->printer = jp;
1706 return JS_TRUE;
1710 * If nb is non-negative, decompile nb bytecodes starting at pc. Otherwise
1711 * the decompiler starts at pc and continues until it reaches an opcode for
1712 * which decompiling would result in the stack depth equaling -(nb + 1).
1714 * The nextop parameter is either JSOP_NOP or the "next" opcode in order of
1715 * abstract interpretation (not necessarily physically next in a bytecode
1716 * vector). So nextop is JSOP_POP for the last operand in a comma expression,
1717 * or JSOP_AND for the right operand of &&.
1719 static jsbytecode *
1720 Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
1722 JSContext *cx;
1723 JSPrinter *jp, *jp2;
1724 jsbytecode *startpc, *endpc, *pc2, *done;
1725 ptrdiff_t tail, todo, len, oplen, cond, next;
1726 JSOp op, lastop, saveop;
1727 const JSCodeSpec *cs;
1728 jssrcnote *sn, *sn2;
1729 const char *lval, *rval, *xval, *fmt, *token;
1730 jsint i, argc;
1731 char **argv;
1732 JSAtom *atom;
1733 JSObject *obj;
1734 JSFunction *fun;
1735 JSString *str;
1736 JSBool ok;
1737 #if JS_HAS_XML_SUPPORT
1738 JSBool foreach, inXML, quoteAttr;
1739 #else
1740 #define inXML JS_FALSE
1741 #endif
1742 jsval val;
1744 static const char exception_cookie[] = "/*EXCEPTION*/";
1745 static const char retsub_pc_cookie[] = "/*RETSUB_PC*/";
1746 static const char iter_cookie[] = "/*ITER*/";
1747 static const char forelem_cookie[] = "/*FORELEM*/";
1748 static const char with_cookie[] = "/*WITH*/";
1749 static const char dot_format[] = "%s.%s";
1750 static const char index_format[] = "%s[%s]";
1751 static const char predot_format[] = "%s%s.%s";
1752 static const char postdot_format[] = "%s.%s%s";
1753 static const char preindex_format[] = "%s%s[%s]";
1754 static const char postindex_format[] = "%s[%s]%s";
1755 static const char ss_format[] = "%s%s";
1756 static const char sss_format[] = "%s%s%s";
1758 /* Argument and variables decompilation uses the following to share code. */
1759 JS_STATIC_ASSERT(ARGNO_LEN == SLOTNO_LEN);
1762 * Local macros
1764 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
1765 #define DECOMPILE_CODE(pc,nb) if (!Decompile(ss, pc, nb, JSOP_NOP)) return NULL
1766 #define NEXT_OP(pc) (((pc) + (len) == endpc) ? nextop : pc[len])
1767 #define TOP_STR() GetStr(ss, ss->top - 1)
1768 #define POP_STR() PopStr(ss, op)
1769 #define POP_STR_PREC(prec) PopStrPrec(ss, prec)
1772 * Pop a condition expression for if/for/while. JSOP_IFEQ's precedence forces
1773 * extra parens around assignment, which avoids a strict-mode warning.
1775 #define POP_COND_STR() \
1776 PopStr(ss, (js_CodeSpec[ss->opcodes[ss->top - 1]].format & JOF_SET) \
1777 ? JSOP_IFEQ \
1778 : JSOP_NOP)
1781 * Callers know that ATOM_IS_STRING(atom), and we leave it to the optimizer to
1782 * common ATOM_TO_STRING(atom) here and near the call sites.
1784 #define ATOM_IS_IDENTIFIER(atom) js_IsIdentifier(ATOM_TO_STRING(atom))
1785 #define ATOM_IS_KEYWORD(atom) \
1786 (js_CheckKeyword(JSSTRING_CHARS(ATOM_TO_STRING(atom)), \
1787 JSSTRING_LENGTH(ATOM_TO_STRING(atom))) != TOK_EOF)
1790 * Given an atom already fetched from jp->script's atom map, quote/escape its
1791 * string appropriately into rval, and select fmt from the quoted and unquoted
1792 * alternatives.
1794 #define GET_QUOTE_AND_FMT(qfmt, ufmt, rval) \
1795 JS_BEGIN_MACRO \
1796 jschar quote_; \
1797 if (!ATOM_IS_IDENTIFIER(atom)) { \
1798 quote_ = '\''; \
1799 fmt = qfmt; \
1800 } else { \
1801 quote_ = 0; \
1802 fmt = ufmt; \
1804 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), quote_); \
1805 if (!rval) \
1806 return NULL; \
1807 JS_END_MACRO
1809 #define LOAD_ATOM(PCOFF) \
1810 GET_ATOM_FROM_BYTECODE(jp->script, pc, PCOFF, atom)
1812 #define LOAD_OBJECT(PCOFF) \
1813 GET_OBJECT_FROM_BYTECODE(jp->script, pc, PCOFF, obj)
1815 #define LOAD_FUNCTION(PCOFF) \
1816 GET_FUNCTION_FROM_BYTECODE(jp->script, pc, PCOFF, fun)
1818 #define LOAD_REGEXP(PCOFF) \
1819 GET_REGEXP_FROM_BYTECODE(jp->script, pc, PCOFF, obj)
1821 #define GET_SOURCE_NOTE_ATOM(sn, atom) \
1822 JS_BEGIN_MACRO \
1823 jsatomid atomIndex_ = (jsatomid) js_GetSrcNoteOffset((sn), 0); \
1825 LOCAL_ASSERT(atomIndex_ < jp->script->atomMap.length); \
1826 (atom) = jp->script->atomMap.vector[atomIndex_]; \
1827 JS_END_MACRO
1830 * Get atom from jp->script's atom map, quote/escape its string appropriately
1831 * into rval, and select fmt from the quoted and unquoted alternatives.
1833 #define GET_ATOM_QUOTE_AND_FMT(qfmt, ufmt, rval) \
1834 JS_BEGIN_MACRO \
1835 LOAD_ATOM(0); \
1836 GET_QUOTE_AND_FMT(qfmt, ufmt, rval); \
1837 JS_END_MACRO
1840 * Per spec, new x(y).z means (new x(y))).z. For example new (x(y).z) must
1841 * decompile with the constructor parenthesized, but new x.z should not. The
1842 * normal rules give x(y).z and x.z identical precedence: both are produced by
1843 * JSOP_GETPROP.
1845 * Therefore, we need to know in case JSOP_NEW whether the constructor
1846 * expression contains any unparenthesized function calls. So when building a
1847 * MemberExpression or CallExpression, we set ss->opcodes[n] to JSOP_CALL if
1848 * this is true. x(y).z gets JSOP_CALL, not JSOP_GETPROP.
1850 #define PROPAGATE_CALLNESS() \
1851 JS_BEGIN_MACRO \
1852 if (ss->opcodes[ss->top - 1] == JSOP_CALL) \
1853 saveop = JSOP_CALL; \
1854 JS_END_MACRO
1856 cx = ss->sprinter.context;
1857 JS_CHECK_RECURSION(cx, return NULL);
1859 jp = ss->printer;
1860 startpc = pc;
1861 endpc = (nb < 0) ? jp->script->code + jp->script->length : pc + nb;
1862 tail = -1;
1863 todo = -2; /* NB: different from Sprint() error return. */
1864 saveop = JSOP_NOP;
1865 sn = NULL;
1866 rval = NULL;
1867 #if JS_HAS_XML_SUPPORT
1868 foreach = inXML = quoteAttr = JS_FALSE;
1869 #endif
1871 while (nb < 0 || pc < endpc) {
1873 * Move saveop to lastop so prefixed bytecodes can take special action
1874 * while sharing maximal code. Set op and saveop to the new bytecode,
1875 * use op in POP_STR to trigger automatic parenthesization, but push
1876 * saveop at the bottom of the loop if this op pushes. Thus op may be
1877 * set to nop or otherwise mutated to suppress auto-parens.
1879 lastop = saveop;
1880 op = (JSOp) *pc;
1881 cs = &js_CodeSpec[op];
1882 if (cs->format & JOF_INDEXBASE) {
1884 * The decompiler uses js_GetIndexFromBytecode to get atoms and
1885 * objects and ignores these suffix/prefix bytecodes, thus
1886 * simplifying code that must process JSOP_GETTER/JSOP_SETTER
1887 * prefixes.
1889 pc += cs->length;
1890 if (pc >= endpc)
1891 break;
1892 op = (JSOp) *pc;
1893 cs = &js_CodeSpec[op];
1895 saveop = op;
1896 len = oplen = cs->length;
1898 if (nb < 0 && -(nb + 1) == (intN)ss->top - cs->nuses + cs->ndefs)
1899 return pc;
1902 * Save source literal associated with JS now before the following
1903 * rewrite changes op. See bug 380197.
1905 token = CodeToken[op];
1907 if (pc + oplen == jp->dvgfence) {
1908 JSStackFrame *fp;
1909 uint32 format, mode, type;
1912 * Rewrite non-get ops to their "get" format if the error is in
1913 * the bytecode at pc, so we don't decompile more than the error
1914 * expression.
1916 fp = js_GetScriptedCaller(cx, NULL);
1917 format = cs->format;
1918 if (((fp && fp->regs && pc == fp->regs->pc) ||
1919 (pc == startpc && cs->nuses != 0)) &&
1920 format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_FOR|JOF_VARPROP)) {
1921 mode = JOF_MODE(format);
1922 if (mode == JOF_NAME) {
1924 * JOF_NAME does not imply JOF_ATOM, so we must check for
1925 * the QARG and QVAR format types, and translate those to
1926 * JSOP_GETARG or JSOP_GETLOCAL appropriately, instead of
1927 * to JSOP_NAME.
1929 type = JOF_TYPE(format);
1930 op = (type == JOF_QARG)
1931 ? JSOP_GETARG
1932 : (type == JOF_LOCAL)
1933 ? JSOP_GETLOCAL
1934 : JSOP_NAME;
1936 i = cs->nuses - js_CodeSpec[op].nuses;
1937 while (--i >= 0)
1938 PopOff(ss, JSOP_NOP);
1939 } else {
1941 * We must replace the faulting pc's bytecode with a
1942 * corresponding JSOP_GET* code. For JSOP_SET{PROP,ELEM},
1943 * we must use the "2nd" form of JSOP_GET{PROP,ELEM}, to
1944 * throw away the assignment op's right-hand operand and
1945 * decompile it as if it were a GET of its left-hand
1946 * operand.
1948 if (mode == JOF_PROP) {
1949 op = (JSOp) ((format & JOF_SET)
1950 ? JSOP_GETPROP2
1951 : JSOP_GETPROP);
1952 } else if (mode == JOF_ELEM) {
1953 op = (JSOp) ((format & JOF_SET)
1954 ? JSOP_GETELEM2
1955 : JSOP_GETELEM);
1956 } else {
1958 * Unknown mode (including mode 0) means that op is
1959 * uncategorized for our purposes, so we must write
1960 * per-op special case code here.
1962 switch (op) {
1963 case JSOP_ENUMELEM:
1964 case JSOP_ENUMCONSTELEM:
1965 op = JSOP_GETELEM;
1966 break;
1967 #if JS_HAS_LVALUE_RETURN
1968 case JSOP_SETCALL:
1969 op = JSOP_CALL;
1970 break;
1971 #endif
1972 case JSOP_GETTHISPROP:
1974 * NB: JSOP_GETTHISPROP can't fail due to |this|
1975 * being null or undefined at runtime (beware that
1976 * this may change for ES4). Therefore any error
1977 * resulting from this op must be due to the value
1978 * of the property accessed via |this|, so do not
1979 * rewrite op to JSOP_THIS.
1981 * The next two cases should not change op if
1982 * js_DecompileValueGenerator was called from the
1983 * the property getter. They should rewrite only
1984 * if the base object in the arg/var/local is null
1985 * or undefined. FIXME: bug 431569.
1987 break;
1988 case JSOP_GETARGPROP:
1989 op = JSOP_GETARG;
1990 break;
1991 case JSOP_GETLOCALPROP:
1992 op = JSOP_GETLOCAL;
1993 break;
1994 default:
1995 LOCAL_ASSERT(0);
2001 saveop = op;
2002 if (op >= JSOP_LIMIT) {
2003 switch (op) {
2004 case JSOP_GETPROP2:
2005 saveop = JSOP_GETPROP;
2006 break;
2007 case JSOP_GETELEM2:
2008 saveop = JSOP_GETELEM;
2009 break;
2010 default:;
2013 LOCAL_ASSERT(js_CodeSpec[saveop].length == oplen ||
2014 JOF_TYPE(format) == JOF_SLOTATOM);
2016 jp->dvgfence = NULL;
2019 if (token) {
2020 switch (cs->nuses) {
2021 case 2:
2022 sn = js_GetSrcNote(jp->script, pc);
2023 if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
2025 * Avoid over-parenthesizing y in x op= y based on its
2026 * expansion: x = x op y (replace y by z = w to see the
2027 * problem).
2029 op = (JSOp) pc[oplen];
2030 rval = POP_STR();
2031 lval = POP_STR();
2032 /* Print only the right operand of the assignment-op. */
2033 todo = SprintCString(&ss->sprinter, rval);
2034 op = saveop;
2035 } else if (!inXML) {
2036 rval = POP_STR_PREC(cs->prec + !!(cs->format & JOF_LEFTASSOC));
2037 lval = POP_STR_PREC(cs->prec + !(cs->format & JOF_LEFTASSOC));
2038 todo = Sprint(&ss->sprinter, "%s %s %s",
2039 lval, token, rval);
2040 } else {
2041 /* In XML, just concatenate the two operands. */
2042 LOCAL_ASSERT(op == JSOP_ADD);
2043 rval = POP_STR();
2044 lval = POP_STR();
2045 todo = Sprint(&ss->sprinter, ss_format, lval, rval);
2047 break;
2049 case 1:
2050 rval = POP_STR();
2051 todo = Sprint(&ss->sprinter, ss_format, token, rval);
2052 break;
2054 case 0:
2055 todo = SprintCString(&ss->sprinter, token);
2056 break;
2058 default:
2059 todo = -2;
2060 break;
2062 } else {
2063 switch (op) {
2064 case JSOP_NOP:
2066 * Check for a do-while loop, a for-loop with an empty
2067 * initializer part, a labeled statement, a function
2068 * definition, or try/finally.
2070 sn = js_GetSrcNote(jp->script, pc);
2071 todo = -2;
2072 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
2073 case SRC_WHILE:
2074 ++pc;
2075 tail = js_GetSrcNoteOffset(sn, 0) - 1;
2076 LOCAL_ASSERT(pc[tail] == JSOP_IFNE ||
2077 pc[tail] == JSOP_IFNEX);
2078 js_printf(jp, "\tdo {\n");
2079 jp->indent += 4;
2080 DECOMPILE_CODE(pc, tail);
2081 jp->indent -= 4;
2082 js_printf(jp, "\t} while (%s);\n", POP_COND_STR());
2083 pc += tail;
2084 len = js_CodeSpec[*pc].length;
2085 todo = -2;
2086 break;
2088 case SRC_FOR:
2089 rval = "";
2091 do_forloop:
2092 JS_ASSERT(SN_TYPE(sn) == SRC_FOR);
2094 /* Skip the JSOP_NOP or JSOP_POP bytecode. */
2095 pc += JSOP_NOP_LENGTH;
2097 /* Get the cond, next, and loop-closing tail offsets. */
2098 cond = js_GetSrcNoteOffset(sn, 0);
2099 next = js_GetSrcNoteOffset(sn, 1);
2100 tail = js_GetSrcNoteOffset(sn, 2);
2103 * If this loop has a condition, then pc points at a goto
2104 * targeting the condition.
2106 pc2 = pc;
2107 if (cond != tail) {
2108 LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX);
2109 pc2 += (*pc == JSOP_GOTO) ? JSOP_GOTO_LENGTH : JSOP_GOTOX_LENGTH;
2111 LOCAL_ASSERT(tail + GetJumpOffset(pc+tail, pc+tail) == pc2 - pc);
2113 /* Print the keyword and the possibly empty init-part. */
2114 js_printf(jp, "\tfor (%s;", rval);
2116 if (cond != tail) {
2117 /* Decompile the loop condition. */
2118 DECOMPILE_CODE(pc + cond, tail - cond);
2119 js_printf(jp, " %s", POP_STR());
2122 /* Need a semicolon whether or not there was a cond. */
2123 js_puts(jp, ";");
2125 if (next != cond) {
2127 * Decompile the loop updater. It may end in a JSOP_POP
2128 * that we skip; or in a JSOP_POPN that we do not skip,
2129 * followed by a JSOP_NOP (skipped as if it's a POP).
2130 * We cope with the difference between these two cases
2131 * by checking for stack imbalance and popping if there
2132 * is an rval.
2134 uintN saveTop = ss->top;
2136 DECOMPILE_CODE(pc + next, cond - next - JSOP_POP_LENGTH);
2137 LOCAL_ASSERT(ss->top - saveTop <= 1U);
2138 rval = (ss->top == saveTop)
2139 ? ss->sprinter.base + ss->sprinter.offset
2140 : POP_STR();
2141 js_printf(jp, " %s", rval);
2144 /* Do the loop body. */
2145 js_printf(jp, ") {\n");
2146 jp->indent += 4;
2147 next -= pc2 - pc;
2148 DECOMPILE_CODE(pc2, next);
2149 jp->indent -= 4;
2150 js_printf(jp, "\t}\n");
2152 /* Set len so pc skips over the entire loop. */
2153 len = tail + js_CodeSpec[pc[tail]].length;
2154 break;
2156 case SRC_LABEL:
2157 GET_SOURCE_NOTE_ATOM(sn, atom);
2158 jp->indent -= 4;
2159 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2160 if (!rval)
2161 return NULL;
2162 RETRACT(&ss->sprinter, rval);
2163 js_printf(jp, "\t%s:\n", rval);
2164 jp->indent += 4;
2165 break;
2167 case SRC_LABELBRACE:
2168 GET_SOURCE_NOTE_ATOM(sn, atom);
2169 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2170 if (!rval)
2171 return NULL;
2172 RETRACT(&ss->sprinter, rval);
2173 js_printf(jp, "\t%s: {\n", rval);
2174 jp->indent += 4;
2175 break;
2177 case SRC_ENDBRACE:
2178 jp->indent -= 4;
2179 js_printf(jp, "\t}\n");
2180 break;
2182 case SRC_FUNCDEF:
2183 JS_GET_SCRIPT_FUNCTION(jp->script,
2184 js_GetSrcNoteOffset(sn, 0),
2185 fun);
2186 do_function:
2187 js_puts(jp, "\n");
2188 jp2 = JS_NEW_PRINTER(cx, "nested_function", fun,
2189 jp->indent, jp->pretty);
2190 if (!jp2)
2191 return NULL;
2192 ok = js_DecompileFunction(jp2);
2193 if (ok && jp2->sprinter.base)
2194 js_puts(jp, jp2->sprinter.base);
2195 js_DestroyPrinter(jp2);
2196 if (!ok)
2197 return NULL;
2198 js_puts(jp, "\n\n");
2199 break;
2201 case SRC_BRACE:
2202 js_printf(jp, "\t{\n");
2203 jp->indent += 4;
2204 len = js_GetSrcNoteOffset(sn, 0);
2205 DECOMPILE_CODE(pc + oplen, len - oplen);
2206 jp->indent -= 4;
2207 js_printf(jp, "\t}\n");
2208 break;
2210 default:;
2212 break;
2214 case JSOP_PUSH:
2215 #if JS_HAS_DESTRUCTURING
2216 sn = js_GetSrcNote(jp->script, pc);
2217 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
2218 pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo);
2219 if (!pc)
2220 return NULL;
2221 LOCAL_ASSERT(*pc == JSOP_POPN);
2222 len = oplen = JSOP_POPN_LENGTH;
2223 goto end_groupassignment;
2225 #endif
2226 /* FALL THROUGH */
2228 case JSOP_BINDNAME:
2229 todo = Sprint(&ss->sprinter, "");
2230 break;
2232 case JSOP_TRY:
2233 js_printf(jp, "\ttry {\n");
2234 jp->indent += 4;
2235 todo = -2;
2236 break;
2238 case JSOP_FINALLY:
2239 jp->indent -= 4;
2240 js_printf(jp, "\t} finally {\n");
2241 jp->indent += 4;
2244 * We push push the pair of exception/restsub cookies to
2245 * simulate the effects [gosub] or control transfer during
2246 * exception capturing on the stack.
2248 todo = Sprint(&ss->sprinter, exception_cookie);
2249 if (todo < 0 || !PushOff(ss, todo, op))
2250 return NULL;
2251 todo = Sprint(&ss->sprinter, retsub_pc_cookie);
2252 break;
2254 case JSOP_RETSUB:
2255 rval = POP_STR();
2256 LOCAL_ASSERT(strcmp(rval, retsub_pc_cookie) == 0);
2257 lval = POP_STR();
2258 LOCAL_ASSERT(strcmp(lval, exception_cookie) == 0);
2259 todo = -2;
2260 break;
2262 case JSOP_GOSUB:
2263 case JSOP_GOSUBX:
2265 * JSOP_GOSUB and GOSUBX have no effect on the decompiler's
2266 * string stack because the next op in bytecode order finds
2267 * the stack balanced by a JSOP_RETSUB executed elsewhere.
2269 todo = -2;
2270 break;
2272 case JSOP_POPN:
2274 uintN newtop, oldtop;
2277 * The compiler models operand stack depth and fixes the stack
2278 * pointer on entry to a catch clause based on its depth model.
2279 * The decompiler must match the code generator's model, which
2280 * is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops.
2282 oldtop = ss->top;
2283 newtop = oldtop - GET_UINT16(pc);
2284 LOCAL_ASSERT(newtop <= oldtop);
2285 todo = -2;
2287 sn = js_GetSrcNote(jp->script, pc);
2288 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
2289 break;
2290 #if JS_HAS_DESTRUCTURING
2291 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
2292 todo = Sprint(&ss->sprinter, "%s[] = [",
2293 VarPrefix(sn));
2294 if (todo < 0)
2295 return NULL;
2296 for (uintN i = newtop; i < oldtop; i++) {
2297 rval = OFF2STR(&ss->sprinter, ss->offsets[i]);
2298 if (Sprint(&ss->sprinter, ss_format,
2299 (i == newtop) ? "" : ", ",
2300 (i == oldtop - 1 && *rval == '\0')
2301 ? ", " : rval) < 0) {
2302 return NULL;
2305 if (SprintPut(&ss->sprinter, "]", 1) < 0)
2306 return NULL;
2309 * If this is an empty group assignment, we have no stack
2310 * budget into which we can push our result string. Adjust
2311 * ss->sprinter.offset so that our consumer can find the
2312 * empty group assignment decompilation.
2314 if (newtop == oldtop) {
2315 ss->sprinter.offset = todo;
2316 } else {
2318 * Kill newtop before the end_groupassignment: label by
2319 * retracting/popping early. Control will either jump
2320 * to do_forloop: or do_letheadbody: or else break from
2321 * our case JSOP_POPN: after the switch (*pc2) below.
2323 LOCAL_ASSERT(newtop < oldtop);
2324 ss->sprinter.offset = GetOff(ss, newtop);
2325 ss->top = newtop;
2328 end_groupassignment:
2329 LOCAL_ASSERT(*pc == JSOP_POPN);
2332 * Thread directly to the next opcode if we can, to handle
2333 * the special cases of a group assignment in the first or
2334 * last part of a for(;;) loop head, or in a let block or
2335 * expression head.
2337 * NB: todo at this point indexes space in ss->sprinter
2338 * that is liable to be overwritten. The code below knows
2339 * exactly how long rval lives, or else copies it down via
2340 * SprintCString.
2342 rval = OFF2STR(&ss->sprinter, todo);
2343 todo = -2;
2344 pc2 = pc + oplen;
2345 if (*pc2 == JSOP_NOP) {
2346 sn = js_GetSrcNote(jp->script, pc2);
2347 if (sn) {
2348 if (SN_TYPE(sn) == SRC_FOR) {
2349 op = JSOP_NOP;
2350 pc = pc2;
2351 goto do_forloop;
2354 if (SN_TYPE(sn) == SRC_DECL) {
2355 if (ss->top == StackDepth(jp->script)) {
2357 * This must be an empty destructuring
2358 * in the head of a let whose body block
2359 * is also empty.
2361 pc = pc2 + JSOP_NOP_LENGTH;
2362 len = js_GetSrcNoteOffset(sn, 0);
2363 LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCK);
2364 js_printf(jp, "\tlet (%s) {\n", rval);
2365 js_printf(jp, "\t}\n");
2366 break;
2368 todo = SprintCString(&ss->sprinter, rval);
2369 if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
2370 return NULL;
2371 op = JSOP_POP;
2372 pc = pc2 + JSOP_NOP_LENGTH;
2373 goto do_letheadbody;
2375 } else {
2377 * An unnannotated NOP following a POPN must be the
2378 * third part of for(;;) loop head. If the POPN's
2379 * immediate operand is 0, then we may have no slot
2380 * on the sprint-stack in which to push our result
2381 * string. In this case the result can be recovered
2382 * at ss->sprinter.base + ss->sprinter.offset.
2384 if (GET_UINT16(pc) == 0)
2385 break;
2386 todo = SprintCString(&ss->sprinter, rval);
2387 saveop = JSOP_NOP;
2392 * If control flow reaches this point with todo still -2,
2393 * just print rval as an expression statement.
2395 if (todo == -2)
2396 js_printf(jp, "\t%s;\n", rval);
2397 break;
2399 #endif
2400 if (newtop < oldtop) {
2401 ss->sprinter.offset = GetOff(ss, newtop);
2402 ss->top = newtop;
2404 break;
2407 case JSOP_EXCEPTION:
2408 /* The catch decompiler handles this op itself. */
2409 LOCAL_ASSERT(JS_FALSE);
2410 break;
2412 case JSOP_POP:
2414 * By default, do not automatically parenthesize when popping
2415 * a stacked expression decompilation. We auto-parenthesize
2416 * only when JSOP_POP is annotated with SRC_PCDELTA, meaning
2417 * comma operator.
2419 op = JSOP_POPV;
2420 /* FALL THROUGH */
2422 case JSOP_POPV:
2423 sn = js_GetSrcNote(jp->script, pc);
2424 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
2425 case SRC_FOR:
2426 /* Force parens around 'in' expression at 'for' front. */
2427 if (ss->opcodes[ss->top-1] == JSOP_IN)
2428 op = JSOP_LSH;
2429 rval = POP_STR();
2430 todo = -2;
2431 goto do_forloop;
2433 case SRC_PCDELTA:
2434 /* Comma operator: use JSOP_POP for correct precedence. */
2435 op = JSOP_POP;
2437 /* Pop and save to avoid blowing stack depth budget. */
2438 lval = JS_strdup(cx, POP_STR());
2439 if (!lval)
2440 return NULL;
2443 * The offset tells distance to the end of the right-hand
2444 * operand of the comma operator.
2446 done = pc + len;
2447 pc += js_GetSrcNoteOffset(sn, 0);
2448 len = 0;
2450 if (!Decompile(ss, done, pc - done, JSOP_POP)) {
2451 JS_free(cx, (char *)lval);
2452 return NULL;
2455 /* Pop Decompile result and print comma expression. */
2456 rval = POP_STR();
2457 todo = Sprint(&ss->sprinter, "%s, %s", lval, rval);
2458 JS_free(cx, (char *)lval);
2459 break;
2461 case SRC_HIDDEN:
2462 /* Hide this pop, it's from a goto in a with or for/in. */
2463 todo = -2;
2464 break;
2466 case SRC_DECL:
2467 /* This pop is at the end of the let block/expr head. */
2468 pc += JSOP_POP_LENGTH;
2469 #if JS_HAS_DESTRUCTURING
2470 do_letheadbody:
2471 #endif
2472 len = js_GetSrcNoteOffset(sn, 0);
2473 if (pc[len] == JSOP_LEAVEBLOCK) {
2474 js_printf(jp, "\tlet (%s) {\n", POP_STR());
2475 jp->indent += 4;
2476 DECOMPILE_CODE(pc, len);
2477 jp->indent -= 4;
2478 js_printf(jp, "\t}\n");
2479 todo = -2;
2480 } else {
2481 LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCKEXPR);
2483 lval = JS_strdup(cx, PopStr(ss, JSOP_NOP));
2484 if (!lval)
2485 return NULL;
2487 /* Set saveop to reflect what we will push. */
2488 saveop = JSOP_LEAVEBLOCKEXPR;
2489 if (!Decompile(ss, pc, len, saveop)) {
2490 JS_free(cx, (char *)lval);
2491 return NULL;
2493 rval = PopStr(ss, JSOP_SETNAME);
2494 todo = Sprint(&ss->sprinter,
2495 (*rval == '{')
2496 ? "let (%s) (%s)"
2497 : "let (%s) %s",
2498 lval, rval);
2499 JS_free(cx, (char *)lval);
2501 break;
2503 default:
2504 /* Turn off parens around a yield statement. */
2505 if (ss->opcodes[ss->top-1] == JSOP_YIELD)
2506 op = JSOP_NOP;
2508 rval = POP_STR();
2511 * Don't emit decompiler-pushed strings that are not
2512 * handled by other opcodes. They are pushed onto the
2513 * stack to help model the interpreter stack and should
2514 * not appear in the decompiler's output.
2516 if (*rval != '\0' && (rval[0] != '/' || rval[1] != '*')) {
2517 js_printf(jp,
2518 (*rval == '{' ||
2519 (strncmp(rval, js_function_str, 8) == 0 &&
2520 rval[8] == ' '))
2521 ? "\t(%s);\n"
2522 : "\t%s;\n",
2523 rval);
2524 } else {
2525 LOCAL_ASSERT(*rval == '\0' ||
2526 strcmp(rval, exception_cookie) == 0);
2528 todo = -2;
2529 break;
2531 sn = NULL;
2532 break;
2534 case JSOP_ENTERWITH:
2535 LOCAL_ASSERT(!js_GetSrcNote(jp->script, pc));
2536 rval = POP_STR();
2537 js_printf(jp, "\twith (%s) {\n", rval);
2538 jp->indent += 4;
2539 todo = Sprint(&ss->sprinter, with_cookie);
2540 break;
2542 case JSOP_LEAVEWITH:
2543 sn = js_GetSrcNote(jp->script, pc);
2544 todo = -2;
2545 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
2546 break;
2547 rval = POP_STR();
2548 LOCAL_ASSERT(strcmp(rval, with_cookie) == 0);
2549 jp->indent -= 4;
2550 js_printf(jp, "\t}\n");
2551 break;
2553 case JSOP_ENTERBLOCK:
2555 JSAtom **atomv, *smallv[5];
2556 JSScopeProperty *sprop;
2558 LOAD_OBJECT(0);
2559 argc = OBJ_BLOCK_COUNT(cx, obj);
2560 if ((size_t)argc <= JS_ARRAY_LENGTH(smallv)) {
2561 atomv = smallv;
2562 } else {
2563 atomv = (JSAtom **) JS_malloc(cx, argc * sizeof(JSAtom *));
2564 if (!atomv)
2565 return NULL;
2568 MUST_FLOW_THROUGH("enterblock_out");
2569 #define LOCAL_ASSERT_OUT(expr) LOCAL_ASSERT_CUSTOM(expr, ok = JS_FALSE; \
2570 goto enterblock_out)
2571 for (sprop = OBJ_SCOPE(obj)->lastProp; sprop;
2572 sprop = sprop->parent) {
2573 if (!(sprop->flags & SPROP_HAS_SHORTID))
2574 continue;
2575 LOCAL_ASSERT_OUT(sprop->shortid < argc);
2576 atomv[sprop->shortid] = JSID_TO_ATOM(sprop->id);
2578 ok = JS_TRUE;
2579 for (i = 0; i < argc; i++) {
2580 atom = atomv[i];
2581 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2582 if (!rval ||
2583 !PushOff(ss, STR2OFF(&ss->sprinter, rval), op)) {
2584 ok = JS_FALSE;
2585 goto enterblock_out;
2589 sn = js_GetSrcNote(jp->script, pc);
2590 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
2591 #if JS_HAS_BLOCK_SCOPE
2592 case SRC_BRACE:
2593 js_printf(jp, "\t{\n");
2594 jp->indent += 4;
2595 len = js_GetSrcNoteOffset(sn, 0);
2596 ok = Decompile(ss, pc + oplen, len - oplen, JSOP_NOP)
2597 != NULL;
2598 if (!ok)
2599 goto enterblock_out;
2600 jp->indent -= 4;
2601 js_printf(jp, "\t}\n");
2602 break;
2603 #endif
2605 case SRC_CATCH:
2606 jp->indent -= 4;
2607 js_printf(jp, "\t} catch (");
2609 pc2 = pc;
2610 pc += oplen;
2611 LOCAL_ASSERT_OUT(*pc == JSOP_EXCEPTION);
2612 pc += JSOP_EXCEPTION_LENGTH;
2613 todo = Sprint(&ss->sprinter, exception_cookie);
2614 if (todo < 0 || !PushOff(ss, todo, JSOP_EXCEPTION)) {
2615 ok = JS_FALSE;
2616 goto enterblock_out;
2619 if (*pc == JSOP_DUP) {
2620 sn2 = js_GetSrcNote(jp->script, pc);
2621 if (!sn2 || SN_TYPE(sn2) != SRC_DESTRUCT) {
2623 * This is a dup to save the exception for later.
2624 * It is emitted only when the catch head contains
2625 * an exception guard.
2627 LOCAL_ASSERT_OUT(js_GetSrcNoteOffset(sn, 0) != 0);
2628 pc += JSOP_DUP_LENGTH;
2629 todo = Sprint(&ss->sprinter, exception_cookie);
2630 if (todo < 0 ||
2631 !PushOff(ss, todo, JSOP_EXCEPTION)) {
2632 ok = JS_FALSE;
2633 goto enterblock_out;
2638 #if JS_HAS_DESTRUCTURING
2639 if (*pc == JSOP_DUP) {
2640 pc = DecompileDestructuring(ss, pc, endpc);
2641 if (!pc) {
2642 ok = JS_FALSE;
2643 goto enterblock_out;
2645 LOCAL_ASSERT_OUT(*pc == JSOP_POP);
2646 pc += JSOP_POP_LENGTH;
2647 lval = PopStr(ss, JSOP_NOP);
2648 js_puts(jp, lval);
2649 } else {
2650 #endif
2651 LOCAL_ASSERT_OUT(*pc == JSOP_SETLOCALPOP);
2652 i = GET_SLOTNO(pc) - jp->script->nfixed;
2653 pc += JSOP_SETLOCALPOP_LENGTH;
2654 atom = atomv[i - OBJ_BLOCK_DEPTH(cx, obj)];
2655 str = ATOM_TO_STRING(atom);
2656 if (!QuoteString(&jp->sprinter, str, 0)) {
2657 ok = JS_FALSE;
2658 goto enterblock_out;
2660 #if JS_HAS_DESTRUCTURING
2662 #endif
2665 * Pop the exception_cookie (or its dup in the case of a
2666 * guarded catch head) off the stack now.
2668 rval = PopStr(ss, JSOP_NOP);
2669 LOCAL_ASSERT_OUT(strcmp(rval, exception_cookie) == 0);
2671 len = js_GetSrcNoteOffset(sn, 0);
2672 if (len) {
2673 len -= PTRDIFF(pc, pc2, jsbytecode);
2674 LOCAL_ASSERT_OUT(len > 0);
2675 js_printf(jp, " if ");
2676 ok = Decompile(ss, pc, len, JSOP_NOP) != NULL;
2677 if (!ok)
2678 goto enterblock_out;
2679 js_printf(jp, "%s", POP_STR());
2680 pc += len;
2681 LOCAL_ASSERT_OUT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
2682 pc += js_CodeSpec[*pc].length;
2685 js_printf(jp, ") {\n");
2686 jp->indent += 4;
2687 len = 0;
2688 break;
2689 default:
2690 break;
2693 todo = -2;
2695 #undef LOCAL_ASSERT_OUT
2696 enterblock_out:
2697 if (atomv != smallv)
2698 JS_free(cx, atomv);
2699 if (!ok)
2700 return NULL;
2702 break;
2704 case JSOP_LEAVEBLOCK:
2705 case JSOP_LEAVEBLOCKEXPR:
2707 uintN top, depth;
2709 sn = js_GetSrcNote(jp->script, pc);
2710 todo = -2;
2711 if (op == JSOP_LEAVEBLOCKEXPR) {
2712 LOCAL_ASSERT(SN_TYPE(sn) == SRC_PCBASE);
2713 rval = POP_STR();
2714 } else if (sn) {
2715 LOCAL_ASSERT(op == JSOP_LEAVEBLOCK);
2716 if (SN_TYPE(sn) == SRC_HIDDEN)
2717 break;
2720 * This JSOP_LEAVEBLOCK must be for a catch block. If sn's
2721 * offset does not equal the model stack depth, there must
2722 * be a copy of the exception value on the stack due to a
2723 * catch guard (see above, the JSOP_ENTERBLOCK + SRC_CATCH
2724 * case code).
2726 LOCAL_ASSERT(SN_TYPE(sn) == SRC_CATCH);
2727 if ((uintN)js_GetSrcNoteOffset(sn, 0) != ss->top) {
2728 LOCAL_ASSERT((uintN)js_GetSrcNoteOffset(sn, 0)
2729 == ss->top - 1);
2730 rval = POP_STR();
2731 LOCAL_ASSERT(strcmp(rval, exception_cookie) == 0);
2734 top = ss->top;
2735 depth = GET_UINT16(pc);
2736 LOCAL_ASSERT(top >= depth);
2737 top -= depth;
2738 ss->top = top;
2739 ss->sprinter.offset = GetOff(ss, top);
2740 if (op == JSOP_LEAVEBLOCKEXPR)
2741 todo = SprintCString(&ss->sprinter, rval);
2742 break;
2745 case JSOP_CALLUPVAR:
2746 case JSOP_GETUPVAR:
2748 if (!jp->fun)
2749 JS_GET_SCRIPT_FUNCTION(jp->script, 0, jp->fun);
2751 if (!jp->localNames)
2752 jp->localNames = js_GetLocalNameArray(cx, jp->fun, &jp->pool);
2754 i = JS_UPVAR_LOCAL_NAME_START(jp->fun) + GET_UINT16(pc);
2755 if (i >= JS_GET_LOCAL_NAME_COUNT(jp->fun)) {
2756 JSUpvarArray *uva;
2757 #ifdef DEBUG
2759 * We must be in an eval called from jp->fun, where
2760 * jp->script is the eval-compiled script.
2762 * However, it's possible that a js_Invoke already
2763 * pushed a frame trying to call js_Construct on an
2764 * object that's not a constructor, causing us to be
2765 * called with an intervening frame on the stack.
2767 JSStackFrame *fp = js_GetTopStackFrame(cx);
2768 if (fp) {
2769 while (!(fp->flags & JSFRAME_EVAL))
2770 fp = fp->down;
2771 JS_ASSERT(fp->script == jp->script);
2772 JS_ASSERT(fp->down->fun == jp->fun);
2773 JS_ASSERT(FUN_INTERPRETED(jp->fun));
2774 JS_ASSERT(jp->script != jp->fun->u.i.script);
2775 JS_ASSERT(jp->script->upvarsOffset != 0);
2777 #endif
2778 uva = JS_SCRIPT_UPVARS(jp->script);
2779 i = GET_UINT16(pc);
2780 i = UPVAR_FRAME_SLOT(uva->vector[i]);
2782 atom = GetArgOrVarAtom(jp, i);
2783 goto do_name;
2785 case JSOP_CALLLOCAL:
2786 case JSOP_GETLOCAL:
2787 if (IsVarSlot(jp, pc, &i)) {
2788 atom = GetArgOrVarAtom(jp, i);
2789 LOCAL_ASSERT(atom);
2790 goto do_name;
2792 LOCAL_ASSERT((uintN)i < ss->top);
2793 sn = js_GetSrcNote(jp->script, pc);
2795 #if JS_HAS_DESTRUCTURING
2796 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
2797 pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo);
2798 if (!pc)
2799 return NULL;
2800 LOCAL_ASSERT(*pc == JSOP_POPN);
2801 len = oplen = JSOP_POPN_LENGTH;
2802 goto end_groupassignment;
2804 #endif
2806 rval = GetLocal(ss, i);
2807 todo = Sprint(&ss->sprinter, ss_format, VarPrefix(sn), rval);
2808 break;
2810 case JSOP_SETLOCAL:
2811 case JSOP_SETLOCALPOP:
2812 if (IsVarSlot(jp, pc, &i)) {
2813 atom = GetArgOrVarAtom(jp, i);
2814 LOCAL_ASSERT(atom);
2815 goto do_setname;
2817 lval = GetLocal(ss, i);
2818 rval = POP_STR();
2819 goto do_setlval;
2821 case JSOP_INCLOCAL:
2822 case JSOP_DECLOCAL:
2823 if (IsVarSlot(jp, pc, &i)) {
2824 atom = GetArgOrVarAtom(jp, i);
2825 LOCAL_ASSERT(atom);
2826 goto do_incatom;
2828 lval = GetLocal(ss, i);
2829 goto do_inclval;
2831 case JSOP_LOCALINC:
2832 case JSOP_LOCALDEC:
2833 if (IsVarSlot(jp, pc, &i)) {
2834 atom = GetArgOrVarAtom(jp, i);
2835 LOCAL_ASSERT(atom);
2836 goto do_atominc;
2838 lval = GetLocal(ss, i);
2839 goto do_lvalinc;
2841 case JSOP_RETRVAL:
2842 todo = -2;
2843 break;
2845 case JSOP_RETURN:
2846 LOCAL_ASSERT(jp->fun);
2847 fun = jp->fun;
2848 if (fun->flags & JSFUN_EXPR_CLOSURE) {
2849 /* Turn on parens around comma-expression here. */
2850 op = JSOP_SETNAME;
2851 rval = POP_STR();
2852 js_printf(jp, (*rval == '{') ? "(%s)%s" : ss_format,
2853 rval,
2854 ((fun->flags & JSFUN_LAMBDA) || !fun->atom)
2855 ? ""
2856 : ";");
2857 todo = -2;
2858 break;
2860 /* FALL THROUGH */
2862 case JSOP_SETRVAL:
2863 rval = POP_STR();
2864 if (*rval != '\0')
2865 js_printf(jp, "\t%s %s;\n", js_return_str, rval);
2866 else
2867 js_printf(jp, "\t%s;\n", js_return_str);
2868 todo = -2;
2869 break;
2871 #if JS_HAS_GENERATORS
2872 case JSOP_YIELD:
2873 #if JS_HAS_GENERATOR_EXPRS
2874 if (!ss->inGenExp || !(sn = js_GetSrcNote(jp->script, pc)))
2875 #endif
2877 /* Turn off most parens. */
2878 op = JSOP_SETNAME;
2879 rval = POP_STR();
2880 todo = (*rval != '\0')
2881 ? Sprint(&ss->sprinter,
2882 (strncmp(rval, js_yield_str, 5) == 0 &&
2883 (rval[5] == ' ' || rval[5] == '\0'))
2884 ? "%s (%s)"
2885 : "%s %s",
2886 js_yield_str, rval)
2887 : SprintCString(&ss->sprinter, js_yield_str);
2888 break;
2890 #if JS_HAS_GENERATOR_EXPRS
2891 LOCAL_ASSERT(SN_TYPE(sn) == SRC_HIDDEN);
2892 /* FALL THROUGH */
2893 #endif
2895 case JSOP_ARRAYPUSH:
2897 uintN pos, forpos;
2898 ptrdiff_t start;
2900 /* Turn off most parens. */
2901 op = JSOP_SETNAME;
2903 /* Pop the expression being pushed or yielded. */
2904 rval = POP_STR();
2907 * Skip the for loop head stacked by JSOP_FORLOCAL until we hit
2908 * a block local slot (note empty destructuring patterns result
2909 * in unit-count blocks).
2911 pos = ss->top;
2912 while (pos != 0) {
2913 op = (JSOp) ss->opcodes[--pos];
2914 if (op == JSOP_ENTERBLOCK)
2915 break;
2917 JS_ASSERT(op == JSOP_ENTERBLOCK);
2920 * Here, forpos must index the space before the left-most |for|
2921 * in the single string of accumulated |for| heads and optional
2922 * final |if (condition)|.
2924 forpos = pos + 2;
2925 LOCAL_ASSERT(forpos < ss->top);
2928 * Now move pos downward over the block's local slots. Even an
2929 * empty destructuring pattern has one (dummy) local.
2931 while (ss->opcodes[pos] == JSOP_ENTERBLOCK) {
2932 if (pos == 0)
2933 break;
2934 --pos;
2936 JS_ASSERT_IF(saveop == JSOP_ARRAYPUSH,
2937 jp->script->nfixed + pos == GET_UINT16(pc));
2939 #if JS_HAS_GENERATOR_EXPRS
2940 if (saveop == JSOP_YIELD) {
2942 * Generator expression: decompile just rval followed by
2943 * the string starting at forpos. Leave the result string
2944 * in ss->offsets[0] so it can be recovered by our caller
2945 * (the JSOP_ANONFUNOBJ with SRC_GENEXP case). Bump the
2946 * top of stack to balance yield, which is an expression
2947 * (so has neutral stack balance).
2949 LOCAL_ASSERT(pos == 0);
2950 xval = OFF2STR(&ss->sprinter, ss->offsets[forpos]);
2951 ss->sprinter.offset = PAREN_SLOP;
2952 todo = Sprint(&ss->sprinter, ss_format, rval, xval);
2953 if (todo < 0)
2954 return NULL;
2955 ss->offsets[0] = todo;
2956 ++ss->top;
2957 return pc;
2959 #endif /* JS_HAS_GENERATOR_EXPRS */
2962 * Array comprehension: retract the sprinter to the beginning
2963 * of the array initialiser and decompile "[<rval> for ...]".
2965 LOCAL_ASSERT(ss->opcodes[pos] == JSOP_NEWINIT);
2966 start = ss->offsets[pos];
2967 LOCAL_ASSERT(ss->sprinter.base[start] == '[' ||
2968 ss->sprinter.base[start] == '#');
2969 LOCAL_ASSERT(forpos < ss->top);
2970 xval = OFF2STR(&ss->sprinter, ss->offsets[forpos]);
2971 lval = OFF2STR(&ss->sprinter, start);
2972 RETRACT(&ss->sprinter, lval);
2974 todo = Sprint(&ss->sprinter, sss_format, lval, rval, xval);
2975 if (todo < 0)
2976 return NULL;
2977 ss->offsets[pos] = todo;
2978 todo = -2;
2979 break;
2981 #endif
2983 case JSOP_THROWING:
2984 todo = -2;
2985 break;
2987 case JSOP_THROW:
2988 sn = js_GetSrcNote(jp->script, pc);
2989 todo = -2;
2990 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
2991 break;
2992 rval = POP_STR();
2993 js_printf(jp, "\t%s %s;\n", js_throw_str, rval);
2994 break;
2996 case JSOP_ITER:
2997 foreach = (pc[1] & (JSITER_FOREACH | JSITER_KEYVALUE)) ==
2998 JSITER_FOREACH;
2999 todo = SprintCString(&ss->sprinter, iter_cookie);
3000 break;
3002 case JSOP_NEXTITER:
3003 JS_NOT_REACHED("JSOP_NEXTITER");
3004 break;
3006 case JSOP_ENDITER:
3007 sn = js_GetSrcNote(jp->script, pc);
3008 todo = -2;
3009 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
3010 break;
3011 (void) PopOff(ss, op);
3012 (void) PopOff(ss, op);
3013 break;
3015 case JSOP_GOTO:
3016 case JSOP_GOTOX:
3017 sn = js_GetSrcNote(jp->script, pc);
3018 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
3019 case SRC_FOR_IN:
3021 * The loop back-edge carries +1 stack balance, for the
3022 * flag processed by JSOP_IFNE. We do not decompile the
3023 * JSOP_IFNE, and instead push the left-hand side of 'in'
3024 * after the loop edge in this stack slot (the JSOP_FOR*
3025 * opcodes' decompilers do this pushing).
3027 cond = GetJumpOffset(pc, pc);
3028 next = js_GetSrcNoteOffset(sn, 0);
3029 tail = js_GetSrcNoteOffset(sn, 1);
3030 JS_ASSERT(pc[cond] == JSOP_NEXTITER);
3031 DECOMPILE_CODE(pc + oplen, next - oplen);
3032 lval = POP_STR();
3033 if (ss->inArrayInit || ss->inGenExp) {
3034 (void) PopOff(ss, JSOP_NOP);
3035 rval = TOP_STR();
3036 LOCAL_ASSERT(ss->top >= 2);
3037 if (ss->opcodes[ss->top - 2] == JSOP_FORLOCAL) {
3038 ss->sprinter.offset = ss->offsets[ss->top - 1] - PAREN_SLOP;
3039 if (Sprint(&ss->sprinter, " %s (%s in %s)",
3040 foreach ? js_for_each_str : js_for_str,
3041 lval, rval) < 0) {
3042 return NULL;
3046 * Do not AddParentSlop here, as we will push the
3047 * top-most offset again, which will add paren slop
3048 * for us. We must push to balance the stack budget
3049 * when nesting for heads in a comprehension.
3051 todo = ss->offsets[ss->top - 1];
3052 } else {
3053 LOCAL_ASSERT(ss->opcodes[ss->top - 2] == JSOP_ENTERBLOCK);
3054 todo = Sprint(&ss->sprinter, " %s (%s in %s)",
3055 foreach ? js_for_each_str : js_for_str,
3056 lval, rval);
3058 if (todo < 0 || !PushOff(ss, todo, JSOP_FORLOCAL))
3059 return NULL;
3060 DECOMPILE_CODE(pc + next, cond - next);
3061 } else {
3063 * As above, rval or an extension of it must remain
3064 * stacked during loop body decompilation.
3066 rval = GetStr(ss, ss->top - 2);
3067 js_printf(jp, "\t%s (%s in %s) {\n",
3068 foreach ? js_for_each_str : js_for_str,
3069 lval, rval);
3070 jp->indent += 4;
3071 DECOMPILE_CODE(pc + next, cond - next);
3072 jp->indent -= 4;
3073 js_printf(jp, "\t}\n");
3075 pc += tail;
3076 LOCAL_ASSERT(*pc == JSOP_IFNE || *pc == JSOP_IFNEX);
3077 len = js_CodeSpec[*pc].length;
3078 break;
3080 case SRC_WHILE:
3081 cond = GetJumpOffset(pc, pc);
3082 tail = js_GetSrcNoteOffset(sn, 0);
3083 DECOMPILE_CODE(pc + cond, tail - cond);
3084 js_printf(jp, "\twhile (%s) {\n", POP_COND_STR());
3085 jp->indent += 4;
3086 DECOMPILE_CODE(pc + oplen, cond - oplen);
3087 jp->indent -= 4;
3088 js_printf(jp, "\t}\n");
3089 pc += tail;
3090 LOCAL_ASSERT(*pc == JSOP_IFNE || *pc == JSOP_IFNEX);
3091 len = js_CodeSpec[*pc].length;
3092 todo = -2;
3093 break;
3095 case SRC_CONT2LABEL:
3096 GET_SOURCE_NOTE_ATOM(sn, atom);
3097 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3098 if (!rval)
3099 return NULL;
3100 RETRACT(&ss->sprinter, rval);
3101 js_printf(jp, "\tcontinue %s;\n", rval);
3102 break;
3104 case SRC_CONTINUE:
3105 js_printf(jp, "\tcontinue;\n");
3106 break;
3108 case SRC_BREAK2LABEL:
3109 GET_SOURCE_NOTE_ATOM(sn, atom);
3110 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3111 if (!rval)
3112 return NULL;
3113 RETRACT(&ss->sprinter, rval);
3114 js_printf(jp, "\tbreak %s;\n", rval);
3115 break;
3117 case SRC_HIDDEN:
3118 break;
3120 default:
3121 js_printf(jp, "\tbreak;\n");
3122 break;
3124 todo = -2;
3125 break;
3127 case JSOP_IFEQ:
3128 case JSOP_IFEQX:
3130 JSBool elseif = JS_FALSE;
3132 if_again:
3133 len = GetJumpOffset(pc, pc);
3134 sn = js_GetSrcNote(jp->script, pc);
3136 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
3137 case SRC_IF:
3138 case SRC_IF_ELSE:
3139 rval = POP_COND_STR();
3140 if (ss->inArrayInit || ss->inGenExp) {
3141 LOCAL_ASSERT(SN_TYPE(sn) == SRC_IF);
3142 ss->sprinter.offset -= PAREN_SLOP;
3143 if (Sprint(&ss->sprinter, " if (%s)", rval) < 0)
3144 return NULL;
3145 AddParenSlop(ss);
3146 } else {
3147 js_printf(jp,
3148 elseif ? " if (%s) {\n" : "\tif (%s) {\n",
3149 rval);
3150 jp->indent += 4;
3153 if (SN_TYPE(sn) == SRC_IF) {
3154 DECOMPILE_CODE(pc + oplen, len - oplen);
3155 } else {
3156 LOCAL_ASSERT(!ss->inArrayInit && !ss->inGenExp);
3157 tail = js_GetSrcNoteOffset(sn, 0);
3158 DECOMPILE_CODE(pc + oplen, tail - oplen);
3159 jp->indent -= 4;
3160 pc += tail;
3161 LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX);
3162 oplen = js_CodeSpec[*pc].length;
3163 len = GetJumpOffset(pc, pc);
3164 js_printf(jp, "\t} else");
3167 * If the second offset for sn is non-zero, it tells
3168 * the distance from the goto around the else, to the
3169 * ifeq for the if inside the else that forms an "if
3170 * else if" chain. Thus cond spans the condition of
3171 * the second if, so we simply decompile it and start
3172 * over at label if_again.
3174 cond = js_GetSrcNoteOffset(sn, 1);
3175 if (cond != 0) {
3176 DECOMPILE_CODE(pc + oplen, cond - oplen);
3177 pc += cond;
3178 elseif = JS_TRUE;
3179 goto if_again;
3182 js_printf(jp, " {\n");
3183 jp->indent += 4;
3184 DECOMPILE_CODE(pc + oplen, len - oplen);
3187 if (!ss->inArrayInit && !ss->inGenExp) {
3188 jp->indent -= 4;
3189 js_printf(jp, "\t}\n");
3191 todo = -2;
3192 break;
3194 case SRC_COND:
3195 xval = JS_strdup(cx, POP_STR());
3196 if (!xval)
3197 return NULL;
3198 len = js_GetSrcNoteOffset(sn, 0);
3199 DECOMPILE_CODE(pc + oplen, len - oplen);
3200 lval = JS_strdup(cx, POP_STR());
3201 if (!lval) {
3202 JS_free(cx, (void *)xval);
3203 return NULL;
3205 pc += len;
3206 LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX);
3207 oplen = js_CodeSpec[*pc].length;
3208 len = GetJumpOffset(pc, pc);
3209 DECOMPILE_CODE(pc + oplen, len - oplen);
3210 rval = POP_STR();
3211 todo = Sprint(&ss->sprinter, "%s ? %s : %s",
3212 xval, lval, rval);
3213 JS_free(cx, (void *)xval);
3214 JS_free(cx, (void *)lval);
3215 break;
3217 default:
3218 break;
3220 break;
3223 case JSOP_IFNE:
3224 case JSOP_IFNEX:
3225 LOCAL_ASSERT(0);
3226 break;
3228 case JSOP_OR:
3229 case JSOP_ORX:
3230 xval = "||";
3232 do_logical_connective:
3233 /* Top of stack is the first clause in a disjunction (||). */
3234 lval = JS_strdup(cx, POP_STR());
3235 if (!lval)
3236 return NULL;
3237 done = pc + GetJumpOffset(pc, pc);
3238 pc += len;
3239 len = PTRDIFF(done, pc, jsbytecode);
3240 if (!Decompile(ss, pc, len, op)) {
3241 JS_free(cx, (char *)lval);
3242 return NULL;
3244 rval = POP_STR();
3245 if (jp->pretty &&
3246 jp->indent + 4 + strlen(lval) + 4 + strlen(rval) > 75) {
3247 rval = JS_strdup(cx, rval);
3248 if (!rval) {
3249 tail = -1;
3250 } else {
3251 todo = Sprint(&ss->sprinter, "%s %s\n", lval, xval);
3252 tail = Sprint(&ss->sprinter, "%*s%s",
3253 jp->indent + 4, "", rval);
3254 JS_free(cx, (char *)rval);
3256 if (tail < 0)
3257 todo = -1;
3258 } else {
3259 todo = Sprint(&ss->sprinter, "%s %s %s", lval, xval, rval);
3261 JS_free(cx, (char *)lval);
3262 break;
3264 case JSOP_AND:
3265 case JSOP_ANDX:
3266 xval = "&&";
3267 goto do_logical_connective;
3269 case JSOP_FORARG:
3270 sn = NULL;
3271 i = GET_ARGNO(pc);
3272 goto do_forvarslot;
3274 case JSOP_FORLOCAL:
3275 sn = js_GetSrcNote(jp->script, pc);
3276 if (!IsVarSlot(jp, pc, &i)) {
3277 JS_ASSERT(op == JSOP_FORLOCAL);
3278 todo = Sprint(&ss->sprinter, ss_format, VarPrefix(sn), GetStr(ss, i));
3279 break;
3282 do_forvarslot:
3283 atom = GetArgOrVarAtom(jp, i);
3284 LOCAL_ASSERT(atom);
3285 todo = SprintCString(&ss->sprinter, VarPrefix(sn));
3286 if (todo < 0 || !QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0))
3287 return NULL;
3288 break;
3290 case JSOP_FORNAME:
3291 LOAD_ATOM(0);
3292 sn = js_GetSrcNote(jp->script, pc);
3293 todo = SprintCString(&ss->sprinter, VarPrefix(sn));
3294 if (todo < 0 || !QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0))
3295 return NULL;
3296 break;
3298 case JSOP_FORPROP:
3299 xval = NULL;
3300 LOAD_ATOM(0);
3301 if (!ATOM_IS_IDENTIFIER(atom)) {
3302 xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
3303 (jschar)'\'');
3304 if (!xval)
3305 return NULL;
3307 lval = POP_STR();
3308 if (xval) {
3309 JS_ASSERT(*lval);
3310 todo = Sprint(&ss->sprinter, index_format, lval, xval);
3311 } else {
3312 todo = Sprint(&ss->sprinter, ss_format, lval, *lval ? "." : "");
3313 if (todo < 0)
3314 return NULL;
3315 if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0))
3316 return NULL;
3318 break;
3320 case JSOP_FORELEM:
3321 todo = SprintCString(&ss->sprinter, forelem_cookie);
3322 break;
3324 case JSOP_ENUMELEM:
3325 case JSOP_ENUMCONSTELEM:
3327 * The stack has the object under the (top) index expression.
3328 * The "rval" property id is underneath those two on the stack.
3329 * The for loop body net and gross lengths can now be adjusted
3330 * to account for the length of the indexing expression that
3331 * came after JSOP_FORELEM and before JSOP_ENUMELEM.
3333 atom = NULL;
3334 op = JSOP_NOP; /* turn off parens around xval */
3335 xval = POP_STR();
3336 op = JSOP_GETELEM; /* lval must have high precedence */
3337 lval = POP_STR();
3338 op = saveop;
3339 rval = POP_STR();
3340 LOCAL_ASSERT(strcmp(rval, forelem_cookie) == 0);
3341 if (*xval == '\0') {
3342 todo = SprintCString(&ss->sprinter, lval);
3343 } else {
3344 todo = Sprint(&ss->sprinter,
3345 (JOF_OPMODE(lastop) == JOF_XMLNAME)
3346 ? dot_format
3347 : index_format,
3348 lval, xval);
3350 break;
3352 #if JS_HAS_GETTER_SETTER
3353 case JSOP_GETTER:
3354 case JSOP_SETTER:
3355 todo = -2;
3356 break;
3357 #endif
3359 case JSOP_DUP2:
3360 rval = GetStr(ss, ss->top-2);
3361 todo = SprintCString(&ss->sprinter, rval);
3362 if (todo < 0 || !PushOff(ss, todo,
3363 (JSOp) ss->opcodes[ss->top-2])) {
3364 return NULL;
3366 /* FALL THROUGH */
3368 case JSOP_DUP:
3369 #if JS_HAS_DESTRUCTURING
3370 sn = js_GetSrcNote(jp->script, pc);
3371 if (sn) {
3372 LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT);
3373 pc = DecompileDestructuring(ss, pc, endpc);
3374 if (!pc)
3375 return NULL;
3376 len = 0;
3377 lval = POP_STR();
3378 op = saveop = JSOP_ENUMELEM;
3379 rval = POP_STR();
3381 if (strcmp(rval, forelem_cookie) == 0) {
3382 todo = Sprint(&ss->sprinter, ss_format,
3383 VarPrefix(sn), lval);
3385 // Skip POP so the SRC_FOR_IN code can pop for itself.
3386 if (*pc == JSOP_POP)
3387 len = JSOP_POP_LENGTH;
3388 } else {
3389 todo = Sprint(&ss->sprinter, "%s%s = %s",
3390 VarPrefix(sn), lval, rval);
3392 break;
3394 #endif
3396 rval = GetStr(ss, ss->top-1);
3397 saveop = (JSOp) ss->opcodes[ss->top-1];
3398 todo = SprintCString(&ss->sprinter, rval);
3399 break;
3401 case JSOP_SETARG:
3402 atom = GetArgOrVarAtom(jp, GET_ARGNO(pc));
3403 LOCAL_ASSERT(atom);
3404 goto do_setname;
3406 case JSOP_SETCONST:
3407 case JSOP_SETNAME:
3408 case JSOP_SETGVAR:
3409 LOAD_ATOM(0);
3411 do_setname:
3412 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3413 if (!lval)
3414 return NULL;
3415 rval = POP_STR();
3416 if (op == JSOP_SETNAME)
3417 (void) PopOff(ss, op);
3419 do_setlval:
3420 sn = js_GetSrcNote(jp->script, pc - 1);
3421 if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
3422 todo = Sprint(&ss->sprinter, "%s %s= %s",
3423 lval,
3424 (lastop == JSOP_GETTER)
3425 ? js_getter_str
3426 : (lastop == JSOP_SETTER)
3427 ? js_setter_str
3428 : CodeToken[lastop],
3429 rval);
3430 } else {
3431 sn = js_GetSrcNote(jp->script, pc);
3432 todo = Sprint(&ss->sprinter, "%s%s = %s",
3433 VarPrefix(sn), lval, rval);
3435 if (op == JSOP_SETLOCALPOP) {
3436 if (!PushOff(ss, todo, saveop))
3437 return NULL;
3438 rval = POP_STR();
3439 LOCAL_ASSERT(*rval != '\0');
3440 js_printf(jp, "\t%s;\n", rval);
3441 todo = -2;
3443 break;
3445 case JSOP_NEW:
3446 case JSOP_CALL:
3447 case JSOP_EVAL:
3448 case JSOP_APPLY:
3449 #if JS_HAS_LVALUE_RETURN
3450 case JSOP_SETCALL:
3451 #endif
3452 /* Turn off most parens (all if there's only one argument). */
3453 argc = GET_ARGC(pc);
3454 op = (argc == 1) ? JSOP_NOP : JSOP_SETNAME;
3455 argv = (char **)
3456 JS_malloc(cx, (size_t)(argc + 1) * sizeof *argv);
3457 if (!argv)
3458 return NULL;
3460 ok = JS_TRUE;
3461 for (i = argc; i > 0; i--) {
3462 argv[i] = JS_strdup(cx, POP_STR());
3463 if (!argv[i])
3464 ok = JS_FALSE;
3467 /* Skip the JSOP_PUSHOBJ-created empty string. */
3468 LOCAL_ASSERT(ss->top >= 2);
3469 (void) PopOff(ss, op);
3472 * Special case: new (x(y)(z)) must be parenthesized like so.
3473 * Same for new (x(y).z) -- contrast with new x(y).z.
3474 * See PROPAGATE_CALLNESS.
3476 op = (JSOp) ss->opcodes[ss->top-1];
3477 lval = PopStr(ss,
3478 (saveop == JSOP_NEW &&
3479 (op == JSOP_CALL ||
3480 op == JSOP_EVAL ||
3481 op == JSOP_APPLY ||
3482 (js_CodeSpec[op].format & JOF_CALLOP)))
3483 ? JSOP_NAME
3484 : saveop);
3485 op = saveop;
3487 argv[0] = JS_strdup(cx, lval);
3488 if (!argv[i])
3489 ok = JS_FALSE;
3491 lval = "(", rval = ")";
3492 if (op == JSOP_NEW) {
3493 if (argc == 0)
3494 lval = rval = "";
3495 todo = Sprint(&ss->sprinter, "%s %s%s",
3496 js_new_str, argv[0], lval);
3497 } else {
3498 todo = Sprint(&ss->sprinter, ss_format,
3499 argv[0], lval);
3501 if (todo < 0)
3502 ok = JS_FALSE;
3504 for (i = 1; i <= argc; i++) {
3505 if (!argv[i] ||
3506 Sprint(&ss->sprinter, ss_format,
3507 argv[i], (i < argc) ? ", " : "") < 0) {
3508 ok = JS_FALSE;
3509 break;
3512 if (Sprint(&ss->sprinter, rval) < 0)
3513 ok = JS_FALSE;
3515 for (i = 0; i <= argc; i++) {
3516 if (argv[i])
3517 JS_free(cx, argv[i]);
3519 JS_free(cx, argv);
3520 if (!ok)
3521 return NULL;
3522 #if JS_HAS_LVALUE_RETURN
3523 if (op == JSOP_SETCALL) {
3524 if (!PushOff(ss, todo, op))
3525 return NULL;
3526 todo = Sprint(&ss->sprinter, "");
3528 #endif
3529 break;
3531 case JSOP_DELNAME:
3532 LOAD_ATOM(0);
3533 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3534 if (!lval)
3535 return NULL;
3536 RETRACT(&ss->sprinter, lval);
3537 do_delete_lval:
3538 todo = Sprint(&ss->sprinter, "%s %s", js_delete_str, lval);
3539 break;
3541 case JSOP_DELPROP:
3542 GET_ATOM_QUOTE_AND_FMT("%s %s[%s]", "%s %s.%s", rval);
3543 op = JSOP_GETPROP;
3544 lval = POP_STR();
3545 todo = Sprint(&ss->sprinter, fmt, js_delete_str, lval, rval);
3546 break;
3548 case JSOP_DELELEM:
3549 op = JSOP_NOP; /* turn off parens */
3550 xval = POP_STR();
3551 op = JSOP_GETPROP;
3552 lval = POP_STR();
3553 if (*xval == '\0')
3554 goto do_delete_lval;
3555 todo = Sprint(&ss->sprinter,
3556 (JOF_OPMODE(lastop) == JOF_XMLNAME)
3557 ? "%s %s.%s"
3558 : "%s %s[%s]",
3559 js_delete_str, lval, xval);
3560 break;
3562 #if JS_HAS_XML_SUPPORT
3563 case JSOP_DELDESC:
3564 xval = POP_STR();
3565 op = JSOP_GETPROP;
3566 lval = POP_STR();
3567 todo = Sprint(&ss->sprinter, "%s %s..%s",
3568 js_delete_str, lval, xval);
3569 break;
3570 #endif
3572 case JSOP_TYPEOFEXPR:
3573 case JSOP_TYPEOF:
3574 case JSOP_VOID:
3575 rval = POP_STR();
3576 todo = Sprint(&ss->sprinter, "%s %s",
3577 (op == JSOP_VOID) ? js_void_str : js_typeof_str,
3578 rval);
3579 break;
3581 case JSOP_INCARG:
3582 case JSOP_DECARG:
3583 atom = GetArgOrVarAtom(jp, GET_ARGNO(pc));
3584 LOCAL_ASSERT(atom);
3585 goto do_incatom;
3587 case JSOP_INCNAME:
3588 case JSOP_DECNAME:
3589 case JSOP_INCGVAR:
3590 case JSOP_DECGVAR:
3591 LOAD_ATOM(0);
3592 do_incatom:
3593 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3594 if (!lval)
3595 return NULL;
3596 RETRACT(&ss->sprinter, lval);
3597 do_inclval:
3598 todo = Sprint(&ss->sprinter, ss_format,
3599 js_incop_strs[!(cs->format & JOF_INC)], lval);
3600 break;
3602 case JSOP_INCPROP:
3603 case JSOP_DECPROP:
3604 GET_ATOM_QUOTE_AND_FMT(preindex_format, predot_format, rval);
3607 * Force precedence below the numeric literal opcodes, so that
3608 * 42..foo or 10000..toString(16), e.g., decompile with parens
3609 * around the left-hand side of dot.
3611 op = JSOP_GETPROP;
3612 lval = POP_STR();
3613 todo = Sprint(&ss->sprinter, fmt,
3614 js_incop_strs[!(cs->format & JOF_INC)],
3615 lval, rval);
3616 break;
3618 case JSOP_INCELEM:
3619 case JSOP_DECELEM:
3620 op = JSOP_NOP; /* turn off parens */
3621 xval = POP_STR();
3622 op = JSOP_GETELEM;
3623 lval = POP_STR();
3624 if (*xval != '\0') {
3625 todo = Sprint(&ss->sprinter,
3626 (JOF_OPMODE(lastop) == JOF_XMLNAME)
3627 ? predot_format
3628 : preindex_format,
3629 js_incop_strs[!(cs->format & JOF_INC)],
3630 lval, xval);
3631 } else {
3632 todo = Sprint(&ss->sprinter, ss_format,
3633 js_incop_strs[!(cs->format & JOF_INC)], lval);
3635 break;
3637 case JSOP_ARGINC:
3638 case JSOP_ARGDEC:
3639 atom = GetArgOrVarAtom(jp, GET_ARGNO(pc));
3640 LOCAL_ASSERT(atom);
3641 goto do_atominc;
3643 case JSOP_NAMEINC:
3644 case JSOP_NAMEDEC:
3645 case JSOP_GVARINC:
3646 case JSOP_GVARDEC:
3647 LOAD_ATOM(0);
3648 do_atominc:
3649 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3650 if (!lval)
3651 return NULL;
3652 RETRACT(&ss->sprinter, lval);
3653 do_lvalinc:
3654 todo = Sprint(&ss->sprinter, ss_format,
3655 lval, js_incop_strs[!(cs->format & JOF_INC)]);
3656 break;
3658 case JSOP_PROPINC:
3659 case JSOP_PROPDEC:
3660 GET_ATOM_QUOTE_AND_FMT(postindex_format, postdot_format, rval);
3663 * Force precedence below the numeric literal opcodes, so that
3664 * 42..foo or 10000..toString(16), e.g., decompile with parens
3665 * around the left-hand side of dot.
3667 op = JSOP_GETPROP;
3668 lval = POP_STR();
3669 todo = Sprint(&ss->sprinter, fmt, lval, rval,
3670 js_incop_strs[!(cs->format & JOF_INC)]);
3671 break;
3673 case JSOP_ELEMINC:
3674 case JSOP_ELEMDEC:
3675 op = JSOP_NOP; /* turn off parens */
3676 xval = POP_STR();
3677 op = JSOP_GETELEM;
3678 lval = POP_STR();
3679 if (*xval != '\0') {
3680 todo = Sprint(&ss->sprinter,
3681 (JOF_OPMODE(lastop) == JOF_XMLNAME)
3682 ? postdot_format
3683 : postindex_format,
3684 lval, xval,
3685 js_incop_strs[!(cs->format & JOF_INC)]);
3686 } else {
3687 todo = Sprint(&ss->sprinter, ss_format,
3688 lval, js_incop_strs[!(cs->format & JOF_INC)]);
3690 break;
3692 case JSOP_LENGTH:
3693 fmt = dot_format;
3694 rval = js_length_str;
3695 goto do_getprop_lval;
3697 case JSOP_GETPROP2:
3698 op = JSOP_GETPROP;
3699 (void) PopOff(ss, lastop);
3700 /* FALL THROUGH */
3702 case JSOP_CALLPROP:
3703 case JSOP_GETPROP:
3704 case JSOP_GETXPROP:
3705 LOAD_ATOM(0);
3707 do_getprop:
3708 GET_QUOTE_AND_FMT(index_format, dot_format, rval);
3709 do_getprop_lval:
3710 PROPAGATE_CALLNESS();
3711 lval = POP_STR();
3712 todo = Sprint(&ss->sprinter, fmt, lval, rval);
3713 break;
3715 case JSOP_GETTHISPROP:
3716 LOAD_ATOM(0);
3717 GET_QUOTE_AND_FMT(index_format, dot_format, rval);
3718 todo = Sprint(&ss->sprinter, fmt, js_this_str, rval);
3719 break;
3721 case JSOP_GETARGPROP:
3722 /* Get the name of the argument or variable. */
3723 i = GET_ARGNO(pc);
3725 do_getarg_prop:
3726 atom = GetArgOrVarAtom(ss->printer, i);
3727 LOCAL_ASSERT(atom);
3728 LOCAL_ASSERT(ATOM_IS_STRING(atom));
3729 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3730 if (!lval || !PushOff(ss, STR2OFF(&ss->sprinter, lval), op))
3731 return NULL;
3733 /* Get the name of the property. */
3734 LOAD_ATOM(ARGNO_LEN);
3735 goto do_getprop;
3737 case JSOP_GETLOCALPROP:
3738 if (IsVarSlot(jp, pc, &i))
3739 goto do_getarg_prop;
3740 LOCAL_ASSERT((uintN)i < ss->top);
3741 lval = GetLocal(ss, i);
3742 if (!lval)
3743 return NULL;
3744 todo = SprintCString(&ss->sprinter, lval);
3745 if (todo < 0 || !PushOff(ss, todo, op))
3746 return NULL;
3747 LOAD_ATOM(2);
3748 goto do_getprop;
3750 case JSOP_SETPROP:
3751 LOAD_ATOM(0);
3752 GET_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval);
3753 rval = POP_STR();
3756 * Force precedence below the numeric literal opcodes, so that
3757 * 42..foo or 10000..toString(16), e.g., decompile with parens
3758 * around the left-hand side of dot.
3760 op = JSOP_GETPROP;
3761 lval = POP_STR();
3762 sn = js_GetSrcNote(jp->script, pc - 1);
3763 todo = Sprint(&ss->sprinter, fmt, lval, xval,
3764 (sn && SN_TYPE(sn) == SRC_ASSIGNOP)
3765 ? (lastop == JSOP_GETTER)
3766 ? js_getter_str
3767 : (lastop == JSOP_SETTER)
3768 ? js_setter_str
3769 : CodeToken[lastop]
3770 : "",
3771 rval);
3772 break;
3774 case JSOP_GETELEM2:
3775 op = JSOP_GETELEM;
3776 (void) PopOff(ss, lastop);
3777 /* FALL THROUGH */
3779 case JSOP_CALLELEM:
3780 case JSOP_GETELEM:
3781 op = JSOP_NOP; /* turn off parens */
3782 xval = POP_STR();
3783 op = saveop;
3784 PROPAGATE_CALLNESS();
3785 lval = POP_STR();
3786 if (*xval == '\0') {
3787 todo = Sprint(&ss->sprinter, "%s", lval);
3788 } else {
3789 todo = Sprint(&ss->sprinter,
3790 (JOF_OPMODE(lastop) == JOF_XMLNAME)
3791 ? dot_format
3792 : index_format,
3793 lval, xval);
3795 break;
3797 case JSOP_SETELEM:
3798 rval = POP_STR();
3799 op = JSOP_NOP; /* turn off parens */
3800 xval = POP_STR();
3801 cs = &js_CodeSpec[ss->opcodes[ss->top]];
3802 op = JSOP_GETELEM; /* lval must have high precedence */
3803 lval = POP_STR();
3804 op = saveop;
3805 if (*xval == '\0')
3806 goto do_setlval;
3807 sn = js_GetSrcNote(jp->script, pc - 1);
3808 todo = Sprint(&ss->sprinter,
3809 (JOF_MODE(cs->format) == JOF_XMLNAME)
3810 ? "%s.%s %s= %s"
3811 : "%s[%s] %s= %s",
3812 lval, xval,
3813 (sn && SN_TYPE(sn) == SRC_ASSIGNOP)
3814 ? (lastop == JSOP_GETTER)
3815 ? js_getter_str
3816 : (lastop == JSOP_SETTER)
3817 ? js_setter_str
3818 : CodeToken[lastop]
3819 : "",
3820 rval);
3821 break;
3823 case JSOP_ARGSUB:
3824 i = (jsint) GET_ARGNO(pc);
3825 todo = Sprint(&ss->sprinter, "%s[%d]",
3826 js_arguments_str, (int) i);
3827 break;
3829 case JSOP_ARGCNT:
3830 todo = Sprint(&ss->sprinter, dot_format,
3831 js_arguments_str, js_length_str);
3832 break;
3834 case JSOP_CALLARG:
3835 case JSOP_GETARG:
3836 i = GET_ARGNO(pc);
3837 atom = GetArgOrVarAtom(jp, i);
3838 #if JS_HAS_DESTRUCTURING
3839 if (!atom) {
3840 todo = Sprint(&ss->sprinter, "%s[%d]", js_arguments_str, i);
3841 break;
3843 #else
3844 LOCAL_ASSERT(atom);
3845 #endif
3846 goto do_name;
3848 case JSOP_CALLNAME:
3849 case JSOP_NAME:
3850 case JSOP_GETGVAR:
3851 case JSOP_CALLGVAR:
3852 LOAD_ATOM(0);
3853 do_name:
3854 lval = "";
3855 #if JS_HAS_XML_SUPPORT
3856 do_qname:
3857 #endif
3858 sn = js_GetSrcNote(jp->script, pc);
3859 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
3860 inXML ? DONT_ESCAPE : 0);
3861 if (!rval)
3862 return NULL;
3863 RETRACT(&ss->sprinter, rval);
3864 todo = Sprint(&ss->sprinter, sss_format,
3865 VarPrefix(sn), lval, rval);
3866 break;
3868 case JSOP_UINT16:
3869 i = (jsint) GET_UINT16(pc);
3870 goto do_sprint_int;
3872 case JSOP_UINT24:
3873 i = (jsint) GET_UINT24(pc);
3874 goto do_sprint_int;
3876 case JSOP_INT8:
3877 i = GET_INT8(pc);
3878 goto do_sprint_int;
3880 case JSOP_INT32:
3881 i = GET_INT32(pc);
3882 do_sprint_int:
3883 todo = Sprint(&ss->sprinter, "%d", i);
3884 break;
3886 case JSOP_DOUBLE:
3887 LOAD_ATOM(0);
3888 val = ATOM_KEY(atom);
3889 LOCAL_ASSERT(JSVAL_IS_DOUBLE(val));
3890 todo = SprintDoubleValue(&ss->sprinter, val, &saveop);
3891 break;
3893 case JSOP_STRING:
3894 LOAD_ATOM(0);
3895 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
3896 inXML ? DONT_ESCAPE : '"');
3897 if (!rval)
3898 return NULL;
3899 todo = STR2OFF(&ss->sprinter, rval);
3900 break;
3902 case JSOP_ANONFUNOBJ:
3903 #if JS_HAS_GENERATOR_EXPRS
3904 sn = js_GetSrcNote(jp->script, pc);
3905 if (sn && SN_TYPE(sn) == SRC_GENEXP) {
3906 JSScript *inner, *outer;
3907 void *mark;
3908 SprintStack ss2;
3910 LOAD_FUNCTION(0);
3911 inner = fun->u.i.script;
3914 * All allocation when decompiling is LIFO, using malloc
3915 * or, more commonly, arena-alloocating from cx->tempPool.
3916 * After InitSprintStack succeeds, we must release to mark
3917 * before returning.
3919 mark = JS_ARENA_MARK(&cx->tempPool);
3920 if (!InitSprintStack(cx, &ss2, jp, StackDepth(inner)))
3921 return NULL;
3922 ss2.inGenExp = JS_TRUE;
3925 * Recursively decompile this generator function as an
3926 * un-parenthesized generator expression. The ss->inGenExp
3927 * special case of JSOP_YIELD shares array comprehension
3928 * decompilation code that leaves the result as the single
3929 * string pushed on ss2.
3931 outer = jp->script;
3932 LOCAL_ASSERT(JS_UPTRDIFF(pc, outer->code) <= outer->length);
3933 jp->script = inner;
3934 if (!Decompile(&ss2, inner->code, inner->length, JSOP_NOP)) {
3935 JS_ARENA_RELEASE(&cx->tempPool, mark);
3936 return NULL;
3938 jp->script = outer;
3941 * Advance over this op and its global |this| push, and
3942 * arrange to advance over the call to this lambda.
3944 pc += len;
3945 LOCAL_ASSERT(*pc == JSOP_NULL || *pc == JSOP_NULLTHIS);
3946 pc += JSOP_NULL_LENGTH;
3947 LOCAL_ASSERT(*pc == JSOP_CALL);
3948 LOCAL_ASSERT(GET_ARGC(pc) == 0);
3949 len = JSOP_CALL_LENGTH;
3952 * Arrange to parenthesize this genexp unless:
3954 * 1. It is the complete expression consumed by a control
3955 * flow bytecode such as JSOP_TABLESWITCH whose syntax
3956 * always parenthesizes the controlling expression.
3957 * 2. It is the condition of a loop other than a for (;;).
3958 * 3. It is the sole argument to a function call.
3959 * 4. It is the condition of an if statement and not of a
3960 * ?: expression.
3962 * But (first, before anything else) always parenthesize
3963 * if this genexp runs up against endpc and the next op is
3964 * not a loop condition (JSOP_IFNE*) opcode. In such cases,
3965 * this Decompile activation has been recursively called by
3966 * a comma operator, &&, or || bytecode.
3968 pc2 = pc + len;
3969 LOCAL_ASSERT(pc2 < endpc ||
3970 endpc < outer->code + outer->length);
3971 LOCAL_ASSERT(ss2.top == 1);
3972 ss2.opcodes[0] = JSOP_POP;
3973 if (pc2 == endpc &&
3974 (JSOp) *endpc != JSOP_IFNE &&
3975 (JSOp) *endpc != JSOP_IFNEX) {
3976 op = JSOP_SETNAME;
3977 } else {
3978 op = (JSOp) *pc2;
3979 op = ((js_CodeSpec[op].format & JOF_PARENHEAD) ||
3980 ((js_CodeSpec[op].format & JOF_INVOKE) &&
3981 GET_ARGC(pc2) == 1) ||
3982 ((op == JSOP_IFEQ || op == JSOP_IFEQX) &&
3983 (sn2 = js_GetSrcNote(outer, pc2)) &&
3984 SN_TYPE(sn2) != SRC_COND))
3985 ? JSOP_POP
3986 : JSOP_SETNAME;
3989 * Stack this result as if it's a name and not an
3990 * anonymous function, so it doesn't get decompiled as
3991 * a generator function in a getter or setter context.
3992 * The precedence level is the same for JSOP_NAME and
3993 * JSOP_ANONFUNOBJ.
3995 LOCAL_ASSERT(js_CodeSpec[JSOP_NAME].prec ==
3996 js_CodeSpec[saveop].prec);
3997 saveop = JSOP_NAME;
4001 * Alas, we have to malloc a copy of the result left on
4002 * the top of ss2 because both ss and ss2 arena-allocate
4003 * from cx's tempPool.
4005 rval = JS_strdup(cx, PopStr(&ss2, op));
4006 JS_ARENA_RELEASE(&cx->tempPool, mark);
4007 if (!rval)
4008 return NULL;
4009 todo = SprintCString(&ss->sprinter, rval);
4010 JS_free(cx, (void *)rval);
4011 break;
4013 #endif /* JS_HAS_GENERATOR_EXPRS */
4014 /* FALL THROUGH */
4016 case JSOP_NAMEDFUNOBJ:
4017 LOAD_FUNCTION(0);
4019 uintN indent = JS_DONT_PRETTY_PRINT;
4022 * Always parenthesize expression closures. We can't force
4023 * saveop to a low-precedence op to arrange for auto-magic
4024 * parenthesization without confusing getter/setter code
4025 * that checks for JSOP_ANONFUNOBJ and JSOP_NAMEDFUNOBJ.
4027 if (!(fun->flags & JSFUN_EXPR_CLOSURE))
4028 indent |= JS_IN_GROUP_CONTEXT;
4029 str = JS_DecompileFunction(cx, fun, indent);
4030 if (!str)
4031 return NULL;
4033 sprint_string:
4034 todo = SprintString(&ss->sprinter, str);
4035 break;
4037 case JSOP_OBJECT:
4038 LOAD_OBJECT(0);
4039 LOCAL_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass);
4040 goto do_regexp;
4042 case JSOP_REGEXP:
4043 GET_REGEXP_FROM_BYTECODE(jp->script, pc, 0, obj);
4044 do_regexp:
4045 if (!js_regexp_toString(cx, obj, &val))
4046 return NULL;
4047 str = JSVAL_TO_STRING(val);
4048 goto sprint_string;
4050 case JSOP_TABLESWITCH:
4051 case JSOP_TABLESWITCHX:
4053 ptrdiff_t jmplen, off, off2;
4054 jsint j, n, low, high;
4055 TableEntry *table, *tmp;
4057 sn = js_GetSrcNote(jp->script, pc);
4058 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
4059 len = js_GetSrcNoteOffset(sn, 0);
4060 jmplen = (op == JSOP_TABLESWITCH) ? JUMP_OFFSET_LEN
4061 : JUMPX_OFFSET_LEN;
4062 pc2 = pc;
4063 off = GetJumpOffset(pc, pc2);
4064 pc2 += jmplen;
4065 low = GET_JUMP_OFFSET(pc2);
4066 pc2 += JUMP_OFFSET_LEN;
4067 high = GET_JUMP_OFFSET(pc2);
4068 pc2 += JUMP_OFFSET_LEN;
4070 n = high - low + 1;
4071 if (n == 0) {
4072 table = NULL;
4073 j = 0;
4074 ok = JS_TRUE;
4075 } else {
4076 table = (TableEntry *)
4077 JS_malloc(cx, (size_t)n * sizeof *table);
4078 if (!table)
4079 return NULL;
4080 for (i = j = 0; i < n; i++) {
4081 table[j].label = NULL;
4082 off2 = GetJumpOffset(pc, pc2);
4083 if (off2) {
4084 sn = js_GetSrcNote(jp->script, pc2);
4085 if (sn) {
4086 LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL);
4087 GET_SOURCE_NOTE_ATOM(sn, table[j].label);
4089 table[j].key = INT_TO_JSVAL(low + i);
4090 table[j].offset = off2;
4091 table[j].order = j;
4092 j++;
4094 pc2 += jmplen;
4096 tmp = (TableEntry *)
4097 JS_malloc(cx, (size_t)j * sizeof *table);
4098 if (tmp) {
4099 ok = js_MergeSort(table, (size_t)j, sizeof(TableEntry),
4100 CompareOffsets, NULL, tmp);
4101 JS_free(cx, tmp);
4102 } else {
4103 ok = JS_FALSE;
4107 if (ok) {
4108 ok = DecompileSwitch(ss, table, (uintN)j, pc, len, off,
4109 JS_FALSE);
4111 JS_free(cx, table);
4112 if (!ok)
4113 return NULL;
4114 todo = -2;
4115 break;
4118 case JSOP_LOOKUPSWITCH:
4119 case JSOP_LOOKUPSWITCHX:
4121 ptrdiff_t jmplen, off, off2;
4122 jsatomid npairs, k;
4123 TableEntry *table;
4125 sn = js_GetSrcNote(jp->script, pc);
4126 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
4127 len = js_GetSrcNoteOffset(sn, 0);
4128 jmplen = (op == JSOP_LOOKUPSWITCH) ? JUMP_OFFSET_LEN
4129 : JUMPX_OFFSET_LEN;
4130 pc2 = pc;
4131 off = GetJumpOffset(pc, pc2);
4132 pc2 += jmplen;
4133 npairs = GET_UINT16(pc2);
4134 pc2 += UINT16_LEN;
4136 table = (TableEntry *)
4137 JS_malloc(cx, (size_t)npairs * sizeof *table);
4138 if (!table)
4139 return NULL;
4140 for (k = 0; k < npairs; k++) {
4141 sn = js_GetSrcNote(jp->script, pc2);
4142 if (sn) {
4143 LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL);
4144 GET_SOURCE_NOTE_ATOM(sn, table[k].label);
4145 } else {
4146 table[k].label = NULL;
4148 JS_GET_SCRIPT_ATOM(jp->script, GET_INDEX(pc2), atom);
4149 pc2 += INDEX_LEN;
4150 off2 = GetJumpOffset(pc, pc2);
4151 pc2 += jmplen;
4152 table[k].key = ATOM_KEY(atom);
4153 table[k].offset = off2;
4156 ok = DecompileSwitch(ss, table, (uintN)npairs, pc, len, off,
4157 JS_FALSE);
4158 JS_free(cx, table);
4159 if (!ok)
4160 return NULL;
4161 todo = -2;
4162 break;
4165 case JSOP_CONDSWITCH:
4167 ptrdiff_t off, off2, caseOff;
4168 jsint ncases;
4169 TableEntry *table;
4171 sn = js_GetSrcNote(jp->script, pc);
4172 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
4173 len = js_GetSrcNoteOffset(sn, 0);
4174 off = js_GetSrcNoteOffset(sn, 1);
4177 * Count the cases using offsets from switch to first case,
4178 * and case to case, stored in srcnote immediates.
4180 pc2 = pc;
4181 off2 = off;
4182 for (ncases = 0; off2 != 0; ncases++) {
4183 pc2 += off2;
4184 LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT ||
4185 *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX);
4186 if (*pc2 == JSOP_DEFAULT || *pc2 == JSOP_DEFAULTX) {
4187 /* End of cases, but count default as a case. */
4188 off2 = 0;
4189 } else {
4190 sn = js_GetSrcNote(jp->script, pc2);
4191 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
4192 off2 = js_GetSrcNoteOffset(sn, 0);
4197 * Allocate table and rescan the cases using their srcnotes,
4198 * stashing each case's delta from switch top in table[i].key,
4199 * and the distance to its statements in table[i].offset.
4201 table = (TableEntry *)
4202 JS_malloc(cx, (size_t)ncases * sizeof *table);
4203 if (!table)
4204 return NULL;
4205 pc2 = pc;
4206 off2 = off;
4207 for (i = 0; i < ncases; i++) {
4208 pc2 += off2;
4209 LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT ||
4210 *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX);
4211 caseOff = pc2 - pc;
4212 table[i].key = INT_TO_JSVAL((jsint) caseOff);
4213 table[i].offset = caseOff + GetJumpOffset(pc2, pc2);
4214 if (*pc2 == JSOP_CASE || *pc2 == JSOP_CASEX) {
4215 sn = js_GetSrcNote(jp->script, pc2);
4216 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
4217 off2 = js_GetSrcNoteOffset(sn, 0);
4222 * Find offset of default code by fetching the default offset
4223 * from the end of table. JSOP_CONDSWITCH always has a default
4224 * case at the end.
4226 off = JSVAL_TO_INT(table[ncases-1].key);
4227 pc2 = pc + off;
4228 off += GetJumpOffset(pc2, pc2);
4230 ok = DecompileSwitch(ss, table, (uintN)ncases, pc, len, off,
4231 JS_TRUE);
4232 JS_free(cx, table);
4233 if (!ok)
4234 return NULL;
4235 todo = -2;
4236 break;
4239 case JSOP_CASE:
4240 case JSOP_CASEX:
4242 lval = POP_STR();
4243 if (!lval)
4244 return NULL;
4245 js_printf(jp, "\tcase %s:\n", lval);
4246 todo = -2;
4247 break;
4250 case JSOP_DEFFUN:
4251 LOAD_FUNCTION(0);
4252 todo = -2;
4253 goto do_function;
4254 break;
4256 case JSOP_TRAP:
4257 saveop = op = JS_GetTrapOpcode(cx, jp->script, pc);
4258 *pc = op;
4259 cs = &js_CodeSpec[op];
4260 len = cs->length;
4261 DECOMPILE_CODE(pc, len);
4262 *pc = JSOP_TRAP;
4263 todo = -2;
4264 break;
4266 case JSOP_HOLE:
4267 todo = SprintPut(&ss->sprinter, "", 0);
4268 break;
4270 case JSOP_NEWARRAY:
4272 ptrdiff_t off;
4273 char *base, *from, *to;
4276 * All operands are stacked and ready for in-place formatting.
4277 * We know that PAREN_SLOP is 3 here, and take advantage of it
4278 * to avoid strdup'ing.
4280 argc = GET_UINT24(pc);
4281 LOCAL_ASSERT(ss->top >= (uintN) argc);
4282 sn = js_GetSrcNote(jp->script, pc);
4283 if (argc == 0) {
4284 todo = Sprint(&ss->sprinter, "[%s]",
4285 (sn && SN_TYPE(sn) == SRC_CONTINUE)
4286 ? ", "
4287 : "");
4288 } else {
4289 ss->top -= argc;
4290 off = GetOff(ss, ss->top);
4291 LOCAL_ASSERT(off >= PAREN_SLOP);
4292 base = OFF2STR(&ss->sprinter, off);
4293 to = base + 1;
4294 i = 0;
4295 for (;;) {
4296 /* Move to the next string that had been stacked. */
4297 from = OFF2STR(&ss->sprinter, off);
4298 todo = strlen(from);
4299 memmove(to, from, todo);
4300 to += todo;
4301 if (++i == argc &&
4302 !(sn && SN_TYPE(sn) == SRC_CONTINUE)) {
4303 break;
4305 *to++ = ',';
4306 *to++ = ' ';
4307 off = GetOff(ss, ss->top + i);
4309 LOCAL_ASSERT(to - base < ss->sprinter.offset - PAREN_SLOP);
4310 *base = '[';
4311 *to++ = ']';
4312 *to = '\0';
4313 ss->sprinter.offset = STR2OFF(&ss->sprinter, to);
4314 todo = STR2OFF(&ss->sprinter, base);
4316 break;
4319 case JSOP_NEWINIT:
4321 i = GET_INT8(pc);
4322 LOCAL_ASSERT(i == JSProto_Array || i == JSProto_Object);
4324 todo = ss->sprinter.offset;
4325 #if JS_HAS_SHARP_VARS
4326 op = (JSOp)pc[len];
4327 if (op == JSOP_DEFSHARP) {
4328 pc += len;
4329 cs = &js_CodeSpec[op];
4330 len = cs->length;
4331 if (Sprint(&ss->sprinter, "#%u=",
4332 (unsigned) (jsint) GET_UINT16(pc)) < 0)
4333 return NULL;
4335 #endif /* JS_HAS_SHARP_VARS */
4336 if (i == JSProto_Array) {
4337 ++ss->inArrayInit;
4338 if (SprintCString(&ss->sprinter, "[") < 0)
4339 return NULL;
4340 } else {
4341 if (SprintCString(&ss->sprinter, "{") < 0)
4342 return NULL;
4344 break;
4347 case JSOP_ENDINIT:
4349 JSBool inArray;
4351 op = JSOP_NOP; /* turn off parens */
4352 rval = POP_STR();
4353 sn = js_GetSrcNote(jp->script, pc);
4355 /* Skip any #n= prefix to find the opening bracket. */
4356 for (xval = rval; *xval != '[' && *xval != '{'; xval++)
4357 continue;
4358 inArray = (*xval == '[');
4359 if (inArray)
4360 --ss->inArrayInit;
4361 todo = Sprint(&ss->sprinter, "%s%s%c",
4362 rval,
4363 (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "",
4364 inArray ? ']' : '}');
4365 break;
4369 JSBool isFirst;
4370 const char *maybeComma;
4372 case JSOP_INITELEM:
4373 isFirst = (ss->opcodes[ss->top - 3] == JSOP_NEWINIT);
4375 /* Turn off most parens. */
4376 op = JSOP_SETNAME;
4377 rval = POP_STR();
4379 /* Turn off all parens for xval and lval, which we control. */
4380 op = JSOP_NOP;
4381 xval = POP_STR();
4382 lval = POP_STR();
4383 sn = js_GetSrcNote(jp->script, pc);
4385 if (sn && SN_TYPE(sn) == SRC_INITPROP) {
4386 atom = NULL;
4387 goto do_initprop;
4389 maybeComma = isFirst ? "" : ", ";
4390 todo = Sprint(&ss->sprinter, sss_format,
4391 lval,
4392 maybeComma,
4393 rval);
4394 break;
4396 case JSOP_INITPROP:
4397 LOAD_ATOM(0);
4398 xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
4399 (jschar)
4400 (ATOM_IS_IDENTIFIER(atom) ? 0 : '\''));
4401 if (!xval)
4402 return NULL;
4403 isFirst = (ss->opcodes[ss->top - 2] == JSOP_NEWINIT);
4404 rval = POP_STR();
4405 lval = POP_STR();
4406 /* fall through */
4408 do_initprop:
4409 maybeComma = isFirst ? "" : ", ";
4410 #ifdef OLD_GETTER_SETTER
4411 todo = Sprint(&ss->sprinter, "%s%s%s%s%s%s%s:%s",
4412 lval,
4413 maybeComma,
4414 xval,
4415 (lastop == JSOP_GETTER || lastop == JSOP_SETTER)
4416 ? " " : "",
4417 (lastop == JSOP_GETTER) ? js_getter_str :
4418 (lastop == JSOP_SETTER) ? js_setter_str :
4420 rval);
4421 #else
4422 if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) {
4423 if (!atom ||
4424 !ATOM_IS_STRING(atom) ||
4425 !ATOM_IS_IDENTIFIER(atom) ||
4426 ATOM_IS_KEYWORD(atom) ||
4427 (ss->opcodes[ss->top+1] != JSOP_ANONFUNOBJ &&
4428 ss->opcodes[ss->top+1] != JSOP_NAMEDFUNOBJ)) {
4429 todo = Sprint(&ss->sprinter, "%s%s%s %s: %s",
4430 lval,
4431 maybeComma,
4432 xval,
4433 (lastop == JSOP_GETTER) ? js_getter_str :
4434 (lastop == JSOP_SETTER) ? js_setter_str :
4436 rval);
4437 } else {
4438 const char *end = rval + strlen(rval);
4440 if (*rval == '(')
4441 ++rval, --end;
4442 LOCAL_ASSERT(strncmp(rval, js_function_str, 8) == 0);
4443 LOCAL_ASSERT(rval[8] == ' ');
4444 rval += 8 + 1;
4445 LOCAL_ASSERT(*end ? *end == ')' : end[-1] == '}');
4446 todo = Sprint(&ss->sprinter, "%s%s%s %s%s%.*s",
4447 lval,
4448 maybeComma,
4449 (lastop == JSOP_GETTER)
4450 ? js_get_str : js_set_str,
4451 xval,
4452 (rval[0] != '(') ? " " : "",
4453 end - rval, rval);
4455 } else {
4456 todo = Sprint(&ss->sprinter, "%s%s%s: %s",
4457 lval, maybeComma, xval, rval);
4459 #endif
4460 break;
4463 #if JS_HAS_SHARP_VARS
4464 case JSOP_DEFSHARP:
4465 i = (jsint) GET_UINT16(pc);
4466 rval = POP_STR();
4467 todo = Sprint(&ss->sprinter, "#%u=%s", (unsigned) i, rval);
4468 break;
4470 case JSOP_USESHARP:
4471 i = (jsint) GET_UINT16(pc);
4472 todo = Sprint(&ss->sprinter, "#%u#", (unsigned) i);
4473 break;
4474 #endif /* JS_HAS_SHARP_VARS */
4476 #if JS_HAS_DEBUGGER_KEYWORD
4477 case JSOP_DEBUGGER:
4478 js_printf(jp, "\tdebugger;\n");
4479 todo = -2;
4480 break;
4481 #endif /* JS_HAS_DEBUGGER_KEYWORD */
4483 #if JS_HAS_XML_SUPPORT
4484 case JSOP_STARTXML:
4485 case JSOP_STARTXMLEXPR:
4486 inXML = op == JSOP_STARTXML;
4487 todo = -2;
4488 break;
4490 case JSOP_DEFXMLNS:
4491 rval = POP_STR();
4492 js_printf(jp, "\t%s %s %s = %s;\n",
4493 js_default_str, js_xml_str, js_namespace_str, rval);
4494 todo = -2;
4495 break;
4497 case JSOP_ANYNAME:
4498 if (pc[JSOP_ANYNAME_LENGTH] == JSOP_TOATTRNAME) {
4499 len += JSOP_TOATTRNAME_LENGTH;
4500 todo = SprintPut(&ss->sprinter, "@*", 2);
4501 } else {
4502 todo = SprintPut(&ss->sprinter, "*", 1);
4504 break;
4506 case JSOP_QNAMEPART:
4507 LOAD_ATOM(0);
4508 if (pc[JSOP_QNAMEPART_LENGTH] == JSOP_TOATTRNAME) {
4509 saveop = JSOP_TOATTRNAME;
4510 len += JSOP_TOATTRNAME_LENGTH;
4511 lval = "@";
4512 goto do_qname;
4514 goto do_name;
4516 case JSOP_QNAMECONST:
4517 LOAD_ATOM(0);
4518 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
4519 if (!rval)
4520 return NULL;
4521 RETRACT(&ss->sprinter, rval);
4522 lval = POP_STR();
4523 todo = Sprint(&ss->sprinter, "%s::%s", lval, rval);
4524 break;
4526 case JSOP_QNAME:
4527 rval = POP_STR();
4528 lval = POP_STR();
4529 todo = Sprint(&ss->sprinter, "%s::[%s]", lval, rval);
4530 break;
4532 case JSOP_TOATTRNAME:
4533 op = JSOP_NOP; /* turn off parens */
4534 rval = POP_STR();
4535 todo = Sprint(&ss->sprinter, "@[%s]", rval);
4536 break;
4538 case JSOP_TOATTRVAL:
4539 todo = -2;
4540 break;
4542 case JSOP_ADDATTRNAME:
4543 rval = POP_STR();
4544 lval = POP_STR();
4545 todo = Sprint(&ss->sprinter, "%s %s", lval, rval);
4546 /* This gets reset by all XML tag expressions. */
4547 quoteAttr = JS_TRUE;
4548 break;
4550 case JSOP_ADDATTRVAL:
4551 rval = POP_STR();
4552 lval = POP_STR();
4553 if (quoteAttr)
4554 todo = Sprint(&ss->sprinter, "%s=\"%s\"", lval, rval);
4555 else
4556 todo = Sprint(&ss->sprinter, "%s=%s", lval, rval);
4557 break;
4559 case JSOP_BINDXMLNAME:
4560 /* Leave the name stacked and push a dummy string. */
4561 todo = Sprint(&ss->sprinter, "");
4562 break;
4564 case JSOP_SETXMLNAME:
4565 /* Pop the r.h.s., the dummy string, and the name. */
4566 rval = POP_STR();
4567 (void) PopOff(ss, op);
4568 lval = POP_STR();
4569 goto do_setlval;
4571 case JSOP_XMLELTEXPR:
4572 case JSOP_XMLTAGEXPR:
4573 todo = Sprint(&ss->sprinter, "{%s}", POP_STR());
4574 inXML = JS_TRUE;
4575 /* If we're an attribute value, we shouldn't quote this. */
4576 quoteAttr = JS_FALSE;
4577 break;
4579 case JSOP_TOXMLLIST:
4580 op = JSOP_NOP; /* turn off parens */
4581 todo = Sprint(&ss->sprinter, "<>%s</>", POP_STR());
4582 inXML = JS_FALSE;
4583 break;
4585 case JSOP_TOXML:
4586 case JSOP_CALLXMLNAME:
4587 case JSOP_XMLNAME:
4588 case JSOP_FILTER:
4589 /* These ops indicate the end of XML expressions. */
4590 inXML = JS_FALSE;
4591 todo = -2;
4592 break;
4594 case JSOP_ENDFILTER:
4595 rval = POP_STR();
4596 PROPAGATE_CALLNESS();
4597 lval = POP_STR();
4598 todo = Sprint(&ss->sprinter, "%s.(%s)", lval, rval);
4599 break;
4601 case JSOP_DESCENDANTS:
4602 rval = POP_STR();
4603 PROPAGATE_CALLNESS();
4604 lval = POP_STR();
4605 todo = Sprint(&ss->sprinter, "%s..%s", lval, rval);
4606 break;
4608 case JSOP_XMLOBJECT:
4609 LOAD_OBJECT(0);
4610 todo = Sprint(&ss->sprinter, "<xml address='%p'>", obj);
4611 break;
4613 case JSOP_XMLCDATA:
4614 LOAD_ATOM(0);
4615 todo = SprintPut(&ss->sprinter, "<![CDATA[", 9);
4616 if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
4617 DONT_ESCAPE))
4618 return NULL;
4619 SprintPut(&ss->sprinter, "]]>", 3);
4620 break;
4622 case JSOP_XMLCOMMENT:
4623 LOAD_ATOM(0);
4624 todo = SprintPut(&ss->sprinter, "<!--", 4);
4625 if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
4626 DONT_ESCAPE))
4627 return NULL;
4628 SprintPut(&ss->sprinter, "-->", 3);
4629 break;
4631 case JSOP_XMLPI:
4632 LOAD_ATOM(0);
4633 rval = JS_strdup(cx, POP_STR());
4634 if (!rval)
4635 return NULL;
4636 todo = SprintPut(&ss->sprinter, "<?", 2);
4637 ok = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0) &&
4638 (*rval == '\0' ||
4639 (SprintPut(&ss->sprinter, " ", 1) >= 0 &&
4640 SprintCString(&ss->sprinter, rval)));
4641 JS_free(cx, (char *)rval);
4642 if (!ok)
4643 return NULL;
4644 SprintPut(&ss->sprinter, "?>", 2);
4645 break;
4647 case JSOP_GETFUNNS:
4648 todo = SprintPut(&ss->sprinter, js_function_str, 8);
4649 break;
4650 #endif /* JS_HAS_XML_SUPPORT */
4652 default:
4653 todo = -2;
4654 break;
4658 if (todo < 0) {
4659 /* -2 means "don't push", -1 means reported error. */
4660 if (todo == -1)
4661 return NULL;
4662 } else {
4663 if (!PushOff(ss, todo, saveop))
4664 return NULL;
4667 if (cs->format & JOF_CALLOP) {
4668 todo = Sprint(&ss->sprinter, "");
4669 if (todo < 0 || !PushOff(ss, todo, saveop))
4670 return NULL;
4673 pc += len;
4677 * Undefine local macros.
4679 #undef inXML
4680 #undef DECOMPILE_CODE
4681 #undef NEXT_OP
4682 #undef TOP_STR
4683 #undef POP_STR
4684 #undef POP_STR_PREC
4685 #undef LOCAL_ASSERT
4686 #undef ATOM_IS_IDENTIFIER
4687 #undef GET_QUOTE_AND_FMT
4688 #undef GET_ATOM_QUOTE_AND_FMT
4690 return pc;
4693 static JSBool
4694 DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len,
4695 uintN pcdepth)
4697 uintN depth, i;
4698 SprintStack ss;
4699 JSContext *cx;
4700 void *mark;
4701 JSBool ok;
4702 JSScript *oldscript;
4703 jsbytecode *oldcode, *oldmain, *code;
4704 char *last;
4706 depth = StackDepth(script);
4707 JS_ASSERT(pcdepth <= depth);
4709 /* Initialize a sprinter for use with the offset stack. */
4710 cx = jp->sprinter.context;
4711 mark = JS_ARENA_MARK(&cx->tempPool);
4712 ok = InitSprintStack(cx, &ss, jp, depth);
4713 if (!ok)
4714 goto out;
4717 * If we are called from js_DecompileValueGenerator with a portion of
4718 * script's bytecode that starts with a non-zero model stack depth given
4719 * by pcdepth, attempt to initialize the missing string offsets in ss to
4720 * |spindex| negative indexes from fp->sp for the activation fp in which
4721 * the error arose.
4723 * See js_DecompileValueGenerator for how its |spindex| parameter is used,
4724 * and see also GetOff, which makes use of the ss.offsets[i] < -1 that are
4725 * potentially stored below.
4727 ss.top = pcdepth;
4728 if (pcdepth != 0) {
4729 for (i = 0; i < pcdepth; i++) {
4730 ss.offsets[i] = -2 - (ptrdiff_t)i;
4731 ss.opcodes[i] = *jp->pcstack[i];
4735 /* Call recursive subroutine to do the hard work. */
4736 oldscript = jp->script;
4737 jp->script = script;
4738 oldcode = jp->script->code;
4739 oldmain = jp->script->main;
4740 code = js_UntrapScriptCode(cx, jp->script);
4741 if (code != oldcode) {
4742 jp->script->code = code;
4743 jp->script->main = code + (oldmain - oldcode);
4744 pc = code + (pc - oldcode);
4747 ok = Decompile(&ss, pc, len, JSOP_NOP) != NULL;
4748 if (code != oldcode) {
4749 JS_free(cx, jp->script->code);
4750 jp->script->code = oldcode;
4751 jp->script->main = oldmain;
4753 jp->script = oldscript;
4755 /* If the given code didn't empty the stack, do it now. */
4756 if (ok && ss.top) {
4757 do {
4758 last = OFF2STR(&ss.sprinter, PopOff(&ss, JSOP_POP));
4759 } while (ss.top > pcdepth);
4760 js_printf(jp, "%s", last);
4763 out:
4764 /* Free all temporary stuff allocated under this call. */
4765 JS_ARENA_RELEASE(&cx->tempPool, mark);
4766 return ok;
4769 JSBool
4770 js_DecompileScript(JSPrinter *jp, JSScript *script)
4772 return DecompileCode(jp, script, script->code, (uintN)script->length, 0);
4775 static const char native_code_str[] = "\t[native code]\n";
4777 JSBool
4778 js_DecompileFunctionBody(JSPrinter *jp)
4780 JSScript *script;
4782 JS_ASSERT(jp->fun);
4783 JS_ASSERT(!jp->script);
4784 if (!FUN_INTERPRETED(jp->fun)) {
4785 js_printf(jp, native_code_str);
4786 return JS_TRUE;
4789 script = jp->fun->u.i.script;
4790 return DecompileCode(jp, script, script->code, (uintN)script->length, 0);
4793 JSBool
4794 js_DecompileFunction(JSPrinter *jp)
4796 JSFunction *fun;
4797 uintN i;
4798 JSAtom *param;
4799 jsbytecode *pc, *endpc;
4800 ptrdiff_t len;
4801 JSBool ok;
4803 fun = jp->fun;
4804 JS_ASSERT(fun);
4805 JS_ASSERT(!jp->script);
4808 * If pretty, conform to ECMA-262 Edition 3, 15.3.4.2, by decompiling a
4809 * FunctionDeclaration. Otherwise, check the JSFUN_LAMBDA flag and force
4810 * an expression by parenthesizing.
4812 if (jp->pretty) {
4813 js_printf(jp, "\t");
4814 } else {
4815 if (!jp->grouped && (fun->flags & JSFUN_LAMBDA))
4816 js_puts(jp, "(");
4818 if (JSFUN_GETTER_TEST(fun->flags))
4819 js_printf(jp, "%s ", js_getter_str);
4820 else if (JSFUN_SETTER_TEST(fun->flags))
4821 js_printf(jp, "%s ", js_setter_str);
4823 js_printf(jp, "%s ", js_function_str);
4824 if (fun->atom && !QuoteString(&jp->sprinter, ATOM_TO_STRING(fun->atom), 0))
4825 return JS_FALSE;
4826 js_puts(jp, "(");
4828 if (!FUN_INTERPRETED(fun)) {
4829 js_printf(jp, ") {\n");
4830 jp->indent += 4;
4831 js_printf(jp, native_code_str);
4832 jp->indent -= 4;
4833 js_printf(jp, "\t}");
4834 } else {
4835 #ifdef JS_HAS_DESTRUCTURING
4836 SprintStack ss;
4837 void *mark;
4838 #endif
4840 /* Print the parameters. */
4841 pc = fun->u.i.script->main;
4842 endpc = pc + fun->u.i.script->length;
4843 ok = JS_TRUE;
4845 #ifdef JS_HAS_DESTRUCTURING
4846 /* Skip JSOP_GENERATOR in case of destructuring parameters. */
4847 if (*pc == JSOP_GENERATOR)
4848 pc += JSOP_GENERATOR_LENGTH;
4850 ss.printer = NULL;
4851 jp->script = fun->u.i.script;
4852 mark = JS_ARENA_MARK(&jp->sprinter.context->tempPool);
4853 #endif
4855 for (i = 0; i < fun->nargs; i++) {
4856 if (i > 0)
4857 js_puts(jp, ", ");
4859 param = GetArgOrVarAtom(jp, i);
4861 #if JS_HAS_DESTRUCTURING
4862 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, JS_FALSE)
4864 if (!param) {
4865 ptrdiff_t todo;
4866 const char *lval;
4868 LOCAL_ASSERT(*pc == JSOP_GETARG);
4869 pc += JSOP_GETARG_LENGTH;
4870 LOCAL_ASSERT(*pc == JSOP_DUP);
4871 if (!ss.printer) {
4872 ok = InitSprintStack(jp->sprinter.context, &ss, jp,
4873 StackDepth(fun->u.i.script));
4874 if (!ok)
4875 break;
4877 pc = DecompileDestructuring(&ss, pc, endpc);
4878 if (!pc) {
4879 ok = JS_FALSE;
4880 break;
4882 LOCAL_ASSERT(*pc == JSOP_POP);
4883 pc += JSOP_POP_LENGTH;
4884 lval = PopStr(&ss, JSOP_NOP);
4885 todo = SprintCString(&jp->sprinter, lval);
4886 if (todo < 0) {
4887 ok = JS_FALSE;
4888 break;
4890 continue;
4893 #undef LOCAL_ASSERT
4894 #endif
4896 if (!QuoteString(&jp->sprinter, ATOM_TO_STRING(param), 0)) {
4897 ok = JS_FALSE;
4898 break;
4902 #ifdef JS_HAS_DESTRUCTURING
4903 jp->script = NULL;
4904 JS_ARENA_RELEASE(&jp->sprinter.context->tempPool, mark);
4905 #endif
4906 if (!ok)
4907 return JS_FALSE;
4908 if (fun->flags & JSFUN_EXPR_CLOSURE) {
4909 js_printf(jp, ") ");
4910 } else {
4911 js_printf(jp, ") {\n");
4912 jp->indent += 4;
4915 len = fun->u.i.script->code + fun->u.i.script->length - pc;
4916 ok = DecompileCode(jp, fun->u.i.script, pc, (uintN)len, 0);
4917 if (!ok)
4918 return JS_FALSE;
4920 if (!(fun->flags & JSFUN_EXPR_CLOSURE)) {
4921 jp->indent -= 4;
4922 js_printf(jp, "\t}");
4926 if (!jp->pretty && !jp->grouped && (fun->flags & JSFUN_LAMBDA))
4927 js_puts(jp, ")");
4929 return JS_TRUE;
4932 char *
4933 js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
4934 JSString *fallback)
4936 JSStackFrame *fp;
4937 jsbytecode *pc;
4938 JSScript *script;
4939 JSFrameRegs *regs;
4940 intN pcdepth;
4941 jsval *sp, *stackBase;
4942 char *name;
4944 JS_ASSERT(spindex < 0 ||
4945 spindex == JSDVG_IGNORE_STACK ||
4946 spindex == JSDVG_SEARCH_STACK);
4948 for (fp = cx->fp; fp && !fp->script; fp = fp->down)
4949 continue;
4950 if (!fp || !fp->regs || !fp->regs->sp)
4951 goto do_fallback;
4953 script = fp->script;
4954 regs = fp->regs;
4955 pc = fp->imacpc ? fp->imacpc : regs->pc;
4956 if (pc < script->main || script->code + script->length <= pc) {
4957 JS_NOT_REACHED("bug");
4958 goto do_fallback;
4961 if (spindex != JSDVG_IGNORE_STACK) {
4962 jsbytecode **pcstack;
4965 * Prepare computing pcstack containing pointers to opcodes that
4966 * populated interpreter's stack with its current content.
4968 pcstack = (jsbytecode **)
4969 JS_malloc(cx, StackDepth(script) * sizeof *pcstack);
4970 if (!pcstack)
4971 return NULL;
4972 pcdepth = ReconstructPCStack(cx, script, pc, pcstack);
4973 if (pcdepth < 0)
4974 goto release_pcstack;
4976 if (spindex != JSDVG_SEARCH_STACK) {
4977 JS_ASSERT(spindex < 0);
4978 pcdepth += spindex;
4979 if (pcdepth < 0)
4980 goto release_pcstack;
4981 pc = pcstack[pcdepth];
4982 } else {
4984 * We search from fp->sp to base to find the most recently
4985 * calculated value matching v under assumption that it is
4986 * it that caused exception, see bug 328664.
4988 stackBase = StackBase(fp);
4989 sp = regs->sp;
4990 do {
4991 if (sp == stackBase) {
4992 pcdepth = -1;
4993 goto release_pcstack;
4995 } while (*--sp != v);
4997 if (sp >= stackBase + pcdepth) {
4999 * The value comes from a temporary slot that the interpreter
5000 * uses for GC roots or when JSOP_APPLY extended the stack to
5001 * fit the argument array elements. Assume that it is the
5002 * current PC that caused the exception.
5004 pc = fp->imacpc ? fp->imacpc : regs->pc;
5005 } else {
5006 pc = pcstack[sp - stackBase];
5010 release_pcstack:
5011 JS_free(cx, pcstack);
5012 if (pcdepth < 0)
5013 goto do_fallback;
5017 jsbytecode* savepc = regs->pc;
5018 jsbytecode* imacpc = fp->imacpc;
5019 if (imacpc) {
5020 regs->pc = imacpc;
5021 fp->imacpc = NULL;
5023 name = DecompileExpression(cx, script, fp->fun, pc);
5024 if (imacpc) {
5025 regs->pc = savepc;
5026 fp->imacpc = imacpc;
5029 if (name != FAILED_EXPRESSION_DECOMPILER)
5030 return name;
5032 do_fallback:
5033 if (!fallback) {
5034 fallback = js_ValueToSource(cx, v);
5035 if (!fallback)
5036 return NULL;
5038 return js_DeflateString(cx, JSSTRING_CHARS(fallback),
5039 JSSTRING_LENGTH(fallback));
5042 static char *
5043 DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun,
5044 jsbytecode *pc)
5046 jsbytecode *code, *oldcode, *oldmain;
5047 JSOp op;
5048 const JSCodeSpec *cs;
5049 jsbytecode *begin, *end;
5050 jssrcnote *sn;
5051 ptrdiff_t len;
5052 jsbytecode **pcstack;
5053 intN pcdepth;
5054 JSPrinter *jp;
5055 char *name;
5057 JS_ASSERT(script->main <= pc && pc < script->code + script->length);
5059 pcstack = NULL;
5060 oldcode = script->code;
5061 oldmain = script->main;
5063 MUST_FLOW_THROUGH("out");
5064 code = js_UntrapScriptCode(cx, script);
5065 if (code != oldcode) {
5066 script->code = code;
5067 script->main = code + (oldmain - oldcode);
5068 pc = code + (pc - oldcode);
5071 op = (JSOp) *pc;
5073 /* None of these stack-writing ops generates novel values. */
5074 JS_ASSERT(op != JSOP_CASE && op != JSOP_CASEX &&
5075 op != JSOP_DUP && op != JSOP_DUP2);
5078 * |this| could convert to a very long object initialiser, so cite it by
5079 * its keyword name instead.
5081 if (op == JSOP_THIS) {
5082 name = JS_strdup(cx, js_this_str);
5083 goto out;
5087 * JSOP_BINDNAME is special: it generates a value, the base object of a
5088 * reference. But if it is the generating op for a diagnostic produced by
5089 * js_DecompileValueGenerator, the name being bound is irrelevant. Just
5090 * fall back to the base object.
5092 if (op == JSOP_BINDNAME) {
5093 name = FAILED_EXPRESSION_DECOMPILER;
5094 goto out;
5097 /* NAME ops are self-contained, others require left or right context. */
5098 cs = &js_CodeSpec[op];
5099 begin = pc;
5100 end = pc + cs->length;
5101 switch (JOF_MODE(cs->format)) {
5102 case JOF_PROP:
5103 case JOF_ELEM:
5104 case JOF_XMLNAME:
5105 case 0:
5106 sn = js_GetSrcNote(script, pc);
5107 if (!sn) {
5108 name = FAILED_EXPRESSION_DECOMPILER;
5109 goto out;
5111 switch (SN_TYPE(sn)) {
5112 case SRC_PCBASE:
5113 begin -= js_GetSrcNoteOffset(sn, 0);
5114 break;
5115 case SRC_PCDELTA:
5116 end = begin + js_GetSrcNoteOffset(sn, 0);
5117 begin += cs->length;
5118 break;
5119 default:
5120 name = FAILED_EXPRESSION_DECOMPILER;
5121 goto out;
5123 break;
5124 default:;
5126 len = PTRDIFF(end, begin, jsbytecode);
5127 if (len <= 0) {
5128 name = FAILED_EXPRESSION_DECOMPILER;
5129 goto out;
5132 pcstack = (jsbytecode **)
5133 JS_malloc(cx, StackDepth(script) * sizeof *pcstack);
5134 if (!pcstack) {
5135 name = NULL;
5136 goto out;
5139 MUST_FLOW_THROUGH("out");
5140 pcdepth = ReconstructPCStack(cx, script, begin, pcstack);
5141 if (pcdepth < 0) {
5142 name = FAILED_EXPRESSION_DECOMPILER;
5143 goto out;
5146 name = NULL;
5147 jp = JS_NEW_PRINTER(cx, "js_DecompileValueGenerator", fun, 0, JS_FALSE);
5148 if (jp) {
5149 jp->dvgfence = end;
5150 jp->pcstack = pcstack;
5151 if (DecompileCode(jp, script, begin, (uintN) len, (uintN) pcdepth)) {
5152 name = (jp->sprinter.base) ? jp->sprinter.base : (char *) "";
5153 name = JS_strdup(cx, name);
5155 js_DestroyPrinter(jp);
5158 out:
5159 if (code != oldcode) {
5160 JS_free(cx, script->code);
5161 script->code = oldcode;
5162 script->main = oldmain;
5165 JS_free(cx, pcstack);
5166 return name;
5169 uintN
5170 js_ReconstructStackDepth(JSContext *cx, JSScript *script, jsbytecode *pc)
5172 return ReconstructPCStack(cx, script, pc, NULL);
5175 static intN
5176 ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *target,
5177 jsbytecode **pcstack)
5179 intN pcdepth, nuses, ndefs;
5180 jsbytecode *pc;
5181 JSOp op;
5182 const JSCodeSpec *cs;
5183 ptrdiff_t oplen;
5184 jssrcnote *sn;
5185 intN i;
5187 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1);
5190 * Walk forward from script->main and compute the stack depth and stack of
5191 * operand-generating opcode PCs in pcstack.
5193 * FIXME: Code to compute oplen copied from js_Disassemble1 and reduced.
5194 * FIXME: Optimize to use last empty-stack sequence point.
5196 LOCAL_ASSERT(script->main <= target && target < script->code + script->length);
5197 pcdepth = 0;
5198 for (pc = script->main; pc < target; pc += oplen) {
5199 op = (JSOp) *pc;
5200 if (op == JSOP_TRAP)
5201 op = JS_GetTrapOpcode(cx, script, pc);
5202 cs = &js_CodeSpec[op];
5203 oplen = cs->length;
5204 if (oplen < 0)
5205 oplen = js_GetVariableBytecodeLength(pc);
5208 * A (C ? T : E) expression requires skipping either T (if target is in
5209 * E) or both T and E (if target is after the whole expression) before
5210 * adjusting pcdepth based on the JSOP_IFEQ or JSOP_IFEQX at pc that
5211 * tests condition C. We know that the stack depth can't change from
5212 * what it was with C on top of stack.
5214 sn = js_GetSrcNote(script, pc);
5215 if (sn && SN_TYPE(sn) == SRC_COND) {
5216 ptrdiff_t jmpoff, jmplen;
5218 jmpoff = js_GetSrcNoteOffset(sn, 0);
5219 if (pc + jmpoff < target) {
5220 pc += jmpoff;
5221 op = (JSOp) *pc;
5222 JS_ASSERT(op == JSOP_GOTO || op == JSOP_GOTOX);
5223 cs = &js_CodeSpec[op];
5224 oplen = cs->length;
5225 JS_ASSERT(oplen > 0);
5226 jmplen = GetJumpOffset(pc, pc);
5227 if (pc + jmplen < target) {
5228 oplen = (uintN) jmplen;
5229 continue;
5233 * Ok, target lies in E. Manually pop C off the model stack,
5234 * since we have moved beyond the IFEQ now.
5236 --pcdepth;
5237 LOCAL_ASSERT(pcdepth >= 0);
5241 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
5242 continue;
5244 nuses = cs->nuses;
5245 if (nuses < 0)
5246 nuses = js_GetVariableStackUseLength(op, pc);
5248 ndefs = cs->ndefs;
5249 if (ndefs < 0) {
5250 JSObject *obj;
5252 JS_ASSERT(op == JSOP_ENTERBLOCK);
5253 GET_OBJECT_FROM_BYTECODE(script, pc, 0, obj);
5254 JS_ASSERT(OBJ_BLOCK_DEPTH(cx, obj) == pcdepth);
5255 ndefs = OBJ_BLOCK_COUNT(cx, obj);
5258 pcdepth -= nuses;
5259 LOCAL_ASSERT(pcdepth >= 0);
5260 LOCAL_ASSERT((uintN)(pcdepth + ndefs) <= StackDepth(script));
5263 * Fill the slots that the opcode defines withs its pc unless it just
5264 * reshuffle the stack. In the latter case we want to preserve the
5265 * opcode that generated the original value.
5267 switch (op) {
5268 default:
5269 if (pcstack) {
5270 for (i = 0; i != ndefs; ++i)
5271 pcstack[pcdepth + i] = pc;
5273 break;
5275 case JSOP_CASE:
5276 case JSOP_CASEX:
5277 /* Keep the switch value. */
5278 JS_ASSERT(ndefs == 1);
5279 break;
5281 case JSOP_DUP:
5282 JS_ASSERT(ndefs == 2);
5283 if (pcstack)
5284 pcstack[pcdepth + 1] = pcstack[pcdepth];
5285 break;
5287 case JSOP_DUP2:
5288 JS_ASSERT(ndefs == 4);
5289 if (pcstack) {
5290 pcstack[pcdepth + 2] = pcstack[pcdepth];
5291 pcstack[pcdepth + 3] = pcstack[pcdepth + 1];
5293 break;
5295 pcdepth += ndefs;
5297 LOCAL_ASSERT(pc == target);
5298 return pcdepth;
5300 #undef LOCAL_ASSERT
5303 #undef LOCAL_ASSERT_RV