/[jscoverage]/trunk/js/jsopcode.cpp
ViewVC logotype

Contents of /trunk/js/jsopcode.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 585 - (show annotations)
Sun Sep 12 15:13:23 2010 UTC (9 years ago) by siliconforks
File size: 194652 byte(s)
Update to SpiderMonkey from Firefox 3.6.9.

<
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set sw=4 ts=8 et tw=99:
3 *
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
19 *
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
24 *
25 * Contributor(s):
26 *
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
38 *
39 * ***** END LICENSE BLOCK ***** */
40
41 /*
42 * JS bytecode descriptors, disassemblers, and decompilers.
43 */
44 #ifdef HAVE_MEMORY_H
45 #include <memory.h>
46 #endif
47 #include <stdarg.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include "jstypes.h"
52 #include "jsstdint.h"
53 #include "jsarena.h" /* Added by JSIFY */
54 #include "jsutil.h" /* Added by JSIFY */
55 #include "jsdtoa.h"
56 #include "jsprf.h"
57 #include "jsapi.h"
58 #include "jsarray.h"
59 #include "jsatom.h"
60 #include "jscntxt.h"
61 #include "jsversion.h"
62 #include "jsdbgapi.h"
63 #include "jsemit.h"
64 #include "jsfun.h"
65 #include "jsiter.h"
66 #include "jsnum.h"
67 #include "jsobj.h"
68 #include "jsopcode.h"
69 #include "jsregexp.h"
70 #include "jsscan.h"
71 #include "jsscope.h"
72 #include "jsscript.h"
73 #include "jsstr.h"
74 #include "jsstaticcheck.h"
75 #include "jstracer.h"
76 #include "jsvector.h"
77
78 #include "jsscriptinlines.h"
79
80 #include "jsautooplen.h"
81
82 /*
83 * Index limit must stay within 32 bits.
84 */
85 JS_STATIC_ASSERT(sizeof(uint32) * JS_BITS_PER_BYTE >= INDEX_LIMIT_LOG2 + 1);
86
87 /* Verify JSOP_XXX_LENGTH constant definitions. */
88 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
89 JS_STATIC_ASSERT(op##_LENGTH == length);
90 #include "jsopcode.tbl"
91 #undef OPDEF
92
93 static const char js_incop_strs[][3] = {"++", "--"};
94 static const char js_for_each_str[] = "for each";
95
96 const JSCodeSpec js_CodeSpec[] = {
97 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
98 {length,nuses,ndefs,prec,format},
99 #include "jsopcode.tbl"
100 #undef OPDEF
101 };
102
103 uintN js_NumCodeSpecs = JS_ARRAY_LENGTH(js_CodeSpec);
104
105 /*
106 * Each element of the array is either a source literal associated with JS
107 * bytecode or null.
108 */
109 static const char *CodeToken[] = {
110 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
111 token,
112 #include "jsopcode.tbl"
113 #undef OPDEF
114 };
115
116 #if defined(DEBUG) || defined(JS_JIT_SPEW)
117 /*
118 * Array of JS bytecode names used by DEBUG-only js_Disassemble and by
119 * JIT debug spew.
120 */
121 const char *js_CodeName[] = {
122 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
123 name,
124 #include "jsopcode.tbl"
125 #undef OPDEF
126 };
127 #endif
128
129 /************************************************************************/
130
131 static ptrdiff_t
132 GetJumpOffset(jsbytecode *pc, jsbytecode *pc2)
133 {
134 uint32 type;
135
136 type = JOF_OPTYPE(*pc);
137 if (JOF_TYPE_IS_EXTENDED_JUMP(type))
138 return GET_JUMPX_OFFSET(pc2);
139 return GET_JUMP_OFFSET(pc2);
140 }
141
142 uintN
143 js_GetIndexFromBytecode(JSContext *cx, JSScript *script, jsbytecode *pc,
144 ptrdiff_t pcoff)
145 {
146 JSOp op;
147 uintN span, base;
148
149 op = js_GetOpcode(cx, script, pc);
150 JS_ASSERT(js_CodeSpec[op].length >= 1 + pcoff + UINT16_LEN);
151
152 /*
153 * We need to detect index base prefix. It presents when resetbase
154 * follows the bytecode.
155 */
156 span = js_CodeSpec[op].length;
157 base = 0;
158 if (pc - script->code + span < script->length) {
159 if (pc[span] == JSOP_RESETBASE) {
160 base = GET_INDEXBASE(pc - JSOP_INDEXBASE_LENGTH);
161 } else if (pc[span] == JSOP_RESETBASE0) {
162 JS_ASSERT(JSOP_INDEXBASE1 <= pc[-1] || pc[-1] <= JSOP_INDEXBASE3);
163 base = (pc[-1] - JSOP_INDEXBASE1 + 1) << 16;
164 }
165 }
166 return base + GET_UINT16(pc + pcoff);
167 }
168
169 uintN
170 js_GetVariableBytecodeLength(jsbytecode *pc)
171 {
172 JSOp op;
173 uintN jmplen, ncases;
174 jsint low, high;
175
176 op = (JSOp) *pc;
177 JS_ASSERT(js_CodeSpec[op].length == -1);
178 switch (op) {
179 case JSOP_TABLESWITCHX:
180 jmplen = JUMPX_OFFSET_LEN;
181 goto do_table;
182 case JSOP_TABLESWITCH:
183 jmplen = JUMP_OFFSET_LEN;
184 do_table:
185 /* Structure: default-jump case-low case-high case1-jump ... */
186 pc += jmplen;
187 low = GET_JUMP_OFFSET(pc);
188 pc += JUMP_OFFSET_LEN;
189 high = GET_JUMP_OFFSET(pc);
190 ncases = (uintN)(high - low + 1);
191 return 1 + jmplen + INDEX_LEN + INDEX_LEN + ncases * jmplen;
192
193 case JSOP_LOOKUPSWITCHX:
194 jmplen = JUMPX_OFFSET_LEN;
195 goto do_lookup;
196 default:
197 JS_ASSERT(op == JSOP_LOOKUPSWITCH);
198 jmplen = JUMP_OFFSET_LEN;
199 do_lookup:
200 /* Structure: default-jump case-count (case1-value case1-jump) ... */
201 pc += jmplen;
202 ncases = GET_UINT16(pc);
203 return 1 + jmplen + INDEX_LEN + ncases * (INDEX_LEN + jmplen);
204 }
205 }
206
207 uintN
208 js_GetVariableStackUses(JSOp op, jsbytecode *pc)
209 {
210 JS_ASSERT(*pc == op || *pc == JSOP_TRAP);
211 JS_ASSERT(js_CodeSpec[op].nuses == -1);
212 switch (op) {
213 case JSOP_POPN:
214 return GET_UINT16(pc);
215 case JSOP_CONCATN:
216 return GET_UINT16(pc);
217 case JSOP_LEAVEBLOCK:
218 return GET_UINT16(pc);
219 case JSOP_LEAVEBLOCKEXPR:
220 return GET_UINT16(pc) + 1;
221 case JSOP_NEWARRAY:
222 return GET_UINT16(pc);
223 default:
224 /* stack: fun, this, [argc arguments] */
225 JS_ASSERT(op == JSOP_NEW || op == JSOP_CALL ||
226 op == JSOP_EVAL || op == JSOP_SETCALL ||
227 op == JSOP_APPLY);
228 return 2 + GET_ARGC(pc);
229 }
230 }
231
232 uintN
233 js_GetEnterBlockStackDefs(JSContext *cx, JSScript *script, jsbytecode *pc)
234 {
235 JSObject *obj;
236
237 JS_ASSERT(*pc == JSOP_ENTERBLOCK || *pc == JSOP_TRAP);
238 GET_OBJECT_FROM_BYTECODE(script, pc, 0, obj);
239 return OBJ_BLOCK_COUNT(cx, obj);
240 }
241
242 #ifdef DEBUG
243
244 JS_FRIEND_API(JSBool)
245 js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp)
246 {
247 jsbytecode *pc, *end;
248 uintN len;
249
250 pc = script->code;
251 end = pc + script->length;
252 while (pc < end) {
253 if (pc == script->main)
254 fputs("main:\n", fp);
255 len = js_Disassemble1(cx, script, pc,
256 pc - script->code,
257 lines, fp);
258 if (!len)
259 return JS_FALSE;
260 pc += len;
261 }
262 return JS_TRUE;
263 }
264
265 JSBool
266 js_DumpScript(JSContext *cx, JSScript *script)
267 {
268 return js_Disassemble(cx, script, true, stdout);
269 }
270
271 const char *
272 ToDisassemblySource(JSContext *cx, jsval v)
273 {
274 if (!JSVAL_IS_PRIMITIVE(v)) {
275 JSObject *obj = JSVAL_TO_OBJECT(v);
276 JSClass *clasp = OBJ_GET_CLASS(cx, obj);
277
278 if (clasp == &js_BlockClass) {
279 char *source = JS_sprintf_append(NULL, "depth %d {", OBJ_BLOCK_DEPTH(cx, obj));
280 for (JSScopeProperty *sprop = OBJ_SCOPE(obj)->lastProp;
281 sprop;
282 sprop = sprop->parent) {
283 const char *bytes = js_AtomToPrintableString(cx, JSID_TO_ATOM(sprop->id));
284 if (!bytes)
285 return NULL;
286 source = JS_sprintf_append(source, "%s: %d%s",
287 bytes, sprop->shortid,
288 sprop->parent ? ", " : "");
289 }
290
291 source = JS_sprintf_append(source, "}");
292 if (!source)
293 return NULL;
294
295 JSString *str = JS_NewString(cx, source, strlen(source));
296 if (!str)
297 return NULL;
298 return js_GetStringBytes(cx, str);
299 }
300
301 if (clasp == &js_FunctionClass) {
302 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj);
303 JSString *str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT);
304 if (!str)
305 return NULL;
306 return js_GetStringBytes(cx, str);
307 }
308
309 if (clasp == &js_RegExpClass) {
310 JSAutoTempValueRooter tvr(cx);
311 if (!js_regexp_toString(cx, obj, tvr.addr()))
312 return NULL;
313 return js_GetStringBytes(cx, JSVAL_TO_STRING(tvr.value()));
314 }
315 }
316
317 return js_ValueToPrintableSource(cx, v);
318 }
319
320 JS_FRIEND_API(uintN)
321 js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc,
322 uintN loc, JSBool lines, FILE *fp)
323 {
324 JSOp op;
325 const JSCodeSpec *cs;
326 ptrdiff_t len, off, jmplen;
327 uint32 type;
328 JSAtom *atom;
329 uintN index;
330 JSObject *obj;
331 jsval v;
332 const char *bytes;
333 jsint i;
334
335 op = (JSOp)*pc;
336 if (op >= JSOP_LIMIT) {
337 char numBuf1[12], numBuf2[12];
338 JS_snprintf(numBuf1, sizeof numBuf1, "%d", op);
339 JS_snprintf(numBuf2, sizeof numBuf2, "%d", JSOP_LIMIT);
340 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
341 JSMSG_BYTECODE_TOO_BIG, numBuf1, numBuf2);
342 return 0;
343 }
344 cs = &js_CodeSpec[op];
345 len = (ptrdiff_t) cs->length;
346 fprintf(fp, "%05u:", loc);
347 if (lines)
348 fprintf(fp, "%4u", JS_PCToLineNumber(cx, script, pc));
349 fprintf(fp, " %s", js_CodeName[op]);
350 type = JOF_TYPE(cs->format);
351 switch (type) {
352 case JOF_BYTE:
353 if (op == JSOP_TRAP) {
354 op = JS_GetTrapOpcode(cx, script, pc);
355 len = (ptrdiff_t) js_CodeSpec[op].length;
356 }
357 break;
358
359 case JOF_JUMP:
360 case JOF_JUMPX:
361 off = GetJumpOffset(pc, pc);
362 fprintf(fp, " %u (%d)", loc + (intN) off, (intN) off);
363 break;
364
365 case JOF_ATOM:
366 case JOF_OBJECT:
367 case JOF_REGEXP:
368 index = js_GetIndexFromBytecode(cx, script, pc, 0);
369 if (type == JOF_ATOM) {
370 JS_GET_SCRIPT_ATOM(script, pc, index, atom);
371 v = ATOM_KEY(atom);
372 } else {
373 if (type == JOF_OBJECT)
374 obj = script->getObject(index);
375 else
376 obj = script->getRegExp(index);
377 v = OBJECT_TO_JSVAL(obj);
378 }
379 bytes = ToDisassemblySource(cx, v);
380 if (!bytes)
381 return 0;
382 fprintf(fp, " %s", bytes);
383 break;
384
385 case JOF_UINT16:
386 i = (jsint)GET_UINT16(pc);
387 goto print_int;
388
389 case JOF_TABLESWITCH:
390 case JOF_TABLESWITCHX:
391 {
392 jsbytecode *pc2;
393 jsint i, low, high;
394
395 jmplen = (type == JOF_TABLESWITCH) ? JUMP_OFFSET_LEN
396 : JUMPX_OFFSET_LEN;
397 pc2 = pc;
398 off = GetJumpOffset(pc, pc2);
399 pc2 += jmplen;
400 low = GET_JUMP_OFFSET(pc2);
401 pc2 += JUMP_OFFSET_LEN;
402 high = GET_JUMP_OFFSET(pc2);
403 pc2 += JUMP_OFFSET_LEN;
404 fprintf(fp, " defaultOffset %d low %d high %d", (intN) off, low, high);
405 for (i = low; i <= high; i++) {
406 off = GetJumpOffset(pc, pc2);
407 fprintf(fp, "\n\t%d: %d", i, (intN) off);
408 pc2 += jmplen;
409 }
410 len = 1 + pc2 - pc;
411 break;
412 }
413
414 case JOF_LOOKUPSWITCH:
415 case JOF_LOOKUPSWITCHX:
416 {
417 jsbytecode *pc2;
418 jsatomid npairs;
419
420 jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN
421 : JUMPX_OFFSET_LEN;
422 pc2 = pc;
423 off = GetJumpOffset(pc, pc2);
424 pc2 += jmplen;
425 npairs = GET_UINT16(pc2);
426 pc2 += UINT16_LEN;
427 fprintf(fp, " offset %d npairs %u", (intN) off, (uintN) npairs);
428 while (npairs) {
429 JS_GET_SCRIPT_ATOM(script, pc, GET_INDEX(pc2), atom);
430 pc2 += INDEX_LEN;
431 off = GetJumpOffset(pc, pc2);
432 pc2 += jmplen;
433
434 bytes = ToDisassemblySource(cx, ATOM_KEY(atom));
435 if (!bytes)
436 return 0;
437 fprintf(fp, "\n\t%s: %d", bytes, (intN) off);
438 npairs--;
439 }
440 len = 1 + pc2 - pc;
441 break;
442 }
443
444 case JOF_QARG:
445 fprintf(fp, " %u", GET_ARGNO(pc));
446 break;
447
448 case JOF_LOCAL:
449 fprintf(fp, " %u", GET_SLOTNO(pc));
450 break;
451
452 case JOF_SLOTATOM:
453 case JOF_SLOTOBJECT:
454 fprintf(fp, " %u", GET_SLOTNO(pc));
455 index = js_GetIndexFromBytecode(cx, script, pc, SLOTNO_LEN);
456 if (type == JOF_SLOTATOM) {
457 JS_GET_SCRIPT_ATOM(script, pc, index, atom);
458 v = ATOM_KEY(atom);
459 } else {
460 obj = script->getObject(index);
461 v = OBJECT_TO_JSVAL(obj);
462 }
463 bytes = ToDisassemblySource(cx, v);
464 if (!bytes)
465 return 0;
466 fprintf(fp, " %s", bytes);
467 break;
468
469 case JOF_UINT24:
470 JS_ASSERT(op == JSOP_UINT24 || op == JSOP_NEWARRAY);
471 i = (jsint)GET_UINT24(pc);
472 goto print_int;
473
474 case JOF_UINT8:
475 i = pc[1];
476 goto print_int;
477
478 case JOF_INT8:
479 i = GET_INT8(pc);
480 goto print_int;
481
482 case JOF_INT32:
483 JS_ASSERT(op == JSOP_INT32);
484 i = GET_INT32(pc);
485 print_int:
486 fprintf(fp, " %d", i);
487 break;
488
489 default: {
490 char numBuf[12];
491 JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) cs->format);
492 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
493 JSMSG_UNKNOWN_FORMAT, numBuf);
494 return 0;
495 }
496 }
497 fputs("\n", fp);
498 return len;
499 }
500
501 #endif /* DEBUG */
502
503 /************************************************************************/
504
505 /*
506 * Sprintf, but with unlimited and automatically allocated buffering.
507 */
508 typedef struct Sprinter {
509 JSContext *context; /* context executing the decompiler */
510 JSArenaPool *pool; /* string allocation pool */
511 char *base; /* base address of buffer in pool */
512 size_t size; /* size of buffer allocated at base */
513 ptrdiff_t offset; /* offset of next free char in buffer */
514 } Sprinter;
515
516 #define INIT_SPRINTER(cx, sp, ap, off) \
517 ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0, \
518 (sp)->offset = off)
519
520 #define OFF2STR(sp,off) ((sp)->base + (off))
521 #define STR2OFF(sp,str) ((str) - (sp)->base)
522 #define RETRACT(sp,str) ((sp)->offset = STR2OFF(sp, str))
523
524 static JSBool
525 SprintEnsureBuffer(Sprinter *sp, size_t len)
526 {
527 ptrdiff_t nb;
528 char *base;
529
530 nb = (sp->offset + len + 1) - sp->size;
531 if (nb < 0)
532 return JS_TRUE;
533 base = sp->base;
534 if (!base) {
535 JS_ARENA_ALLOCATE_CAST(base, char *, sp->pool, nb);
536 } else {
537 JS_ARENA_GROW_CAST(base, char *, sp->pool, sp->size, nb);
538 }
539 if (!base) {
540 js_ReportOutOfScriptQuota(sp->context);
541 return JS_FALSE;
542 }
543 sp->base = base;
544 sp->size += nb;
545 return JS_TRUE;
546 }
547
548 static ptrdiff_t
549 SprintPut(Sprinter *sp, const char *s, size_t len)
550 {
551 ptrdiff_t offset = sp->size; /* save old size */
552 char *bp = sp->base; /* save old base */
553
554 /* Allocate space for s, including the '\0' at the end. */
555 if (!SprintEnsureBuffer(sp, len))
556 return -1;
557
558 if (sp->base != bp && /* buffer was realloc'ed */
559 s >= bp && s < bp + offset) { /* s was within the buffer */
560 s = sp->base + (s - bp); /* this is where it lives now */
561 }
562
563 /* Advance offset and copy s into sp's buffer. */
564 offset = sp->offset;
565 sp->offset += len;
566 bp = sp->base + offset;
567 memmove(bp, s, len);
568 bp[len] = 0;
569 return offset;
570 }
571
572 static ptrdiff_t
573 SprintCString(Sprinter *sp, const char *s)
574 {
575 return SprintPut(sp, s, strlen(s));
576 }
577
578 static ptrdiff_t
579 SprintString(Sprinter *sp, JSString *str)
580 {
581 const jschar *chars;
582 size_t length, size;
583 ptrdiff_t offset;
584
585 str->getCharsAndLength(chars, length);
586 if (length == 0)
587 return sp->offset;
588
589 size = js_GetDeflatedStringLength(sp->context, chars, length);
590 if (size == (size_t)-1 || !SprintEnsureBuffer(sp, size))
591 return -1;
592
593 offset = sp->offset;
594 sp->offset += size;
595 js_DeflateStringToBuffer(sp->context, chars, length, sp->base + offset,
596 &size);
597 sp->base[sp->offset] = 0;
598 return offset;
599 }
600
601
602 static ptrdiff_t
603 Sprint(Sprinter *sp, const char *format, ...)
604 {
605 va_list ap;
606 char *bp;
607 ptrdiff_t offset;
608
609 va_start(ap, format);
610 bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */
611 va_end(ap);
612 if (!bp) {
613 JS_ReportOutOfMemory(sp->context);
614 return -1;
615 }
616 offset = SprintCString(sp, bp);
617 js_free(bp);
618 return offset;
619 }
620
621 const char js_EscapeMap[] = {
622 '\b', 'b',
623 '\f', 'f',
624 '\n', 'n',
625 '\r', 'r',
626 '\t', 't',
627 '\v', 'v',
628 '"', '"',
629 '\'', '\'',
630 '\\', '\\',
631 '\0', '0'
632 };
633
634 #define DONT_ESCAPE 0x10000
635
636 static char *
637 QuoteString(Sprinter *sp, JSString *str, uint32 quote)
638 {
639 JSBool dontEscape, ok;
640 jschar qc, c;
641 ptrdiff_t off, len;
642 const jschar *s, *t, *z;
643 const char *e;
644 char *bp;
645
646 /* Sample off first for later return value pointer computation. */
647 dontEscape = (quote & DONT_ESCAPE) != 0;
648 qc = (jschar) quote;
649 off = sp->offset;
650 if (qc && Sprint(sp, "%c", (char)qc) < 0)
651 return NULL;
652
653 /* Loop control variables: z points at end of string sentinel. */
654 str->getCharsAndEnd(s, z);
655 for (t = s; t < z; s = ++t) {
656 /* Move t forward from s past un-quote-worthy characters. */
657 c = *t;
658 while (JS_ISPRINT(c) && c != qc && c != '\\' && c != '\t' &&
659 !(c >> 8)) {
660 c = *++t;
661 if (t == z)
662 break;
663 }
664 len = t - s;
665
666 /* Allocate space for s, including the '\0' at the end. */
667 if (!SprintEnsureBuffer(sp, len))
668 return NULL;
669
670 /* Advance sp->offset and copy s into sp's buffer. */
671 bp = sp->base + sp->offset;
672 sp->offset += len;
673 while (--len >= 0)
674 *bp++ = (char) *s++;
675 *bp = '\0';
676
677 if (t == z)
678 break;
679
680 /* Use js_EscapeMap, \u, or \x only if necessary. */
681 if (!(c >> 8) && (e = strchr(js_EscapeMap, (int)c)) != NULL) {
682 ok = dontEscape
683 ? Sprint(sp, "%c", (char)c) >= 0
684 : Sprint(sp, "\\%c", e[1]) >= 0;
685 } else {
686 ok = Sprint(sp, (c >> 8) ? "\\u%04X" : "\\x%02X", c) >= 0;
687 }
688 if (!ok)
689 return NULL;
690 }
691
692 /* Sprint the closing quote and return the quoted string. */
693 if (qc && Sprint(sp, "%c", (char)qc) < 0)
694 return NULL;
695
696 /*
697 * If we haven't Sprint'd anything yet, Sprint an empty string so that
698 * the OFF2STR below gives a valid result.
699 */
700 if (off == sp->offset && Sprint(sp, "") < 0)
701 return NULL;
702 return OFF2STR(sp, off);
703 }
704
705 JSString *
706 js_QuoteString(JSContext *cx, JSString *str, jschar quote)
707 {
708 void *mark;
709 Sprinter sprinter;
710 char *bytes;
711 JSString *escstr;
712
713 mark = JS_ARENA_MARK(&cx->tempPool);
714 INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);
715 bytes = QuoteString(&sprinter, str, quote);
716 escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL;
717 JS_ARENA_RELEASE(&cx->tempPool, mark);
718 return escstr;
719 }
720
721 /************************************************************************/
722
723 struct JSPrinter {
724 Sprinter sprinter; /* base class state */
725 JSArenaPool pool; /* string allocation pool */
726 uintN indent; /* indentation in spaces */
727 JSPackedBool pretty; /* pretty-print: indent, use newlines */
728 JSPackedBool grouped; /* in parenthesized expression context */
729 JSScript *script; /* script being printed */
730 jsbytecode *dvgfence; /* DecompileExpression fencepost */
731 jsbytecode **pcstack; /* DecompileExpression modeled stack */
732 JSFunction *fun; /* interpreted function */
733 jsuword *localNames; /* argument and variable names */
734 };
735
736 /*
737 * Hack another flag, a la JS_DONT_PRETTY_PRINT, into uintN indent parameters
738 * to functions such as js_DecompileFunction and js_NewPrinter. This time, as
739 * opposed to JS_DONT_PRETTY_PRINT back in the dark ages, we can assume that a
740 * uintN is at least 32 bits.
741 */
742 #define JS_IN_GROUP_CONTEXT 0x10000
743
744 JSPrinter *
745 JS_NEW_PRINTER(JSContext *cx, const char *name, JSFunction *fun,
746 uintN indent, JSBool pretty)
747 {
748 JSPrinter *jp;
749
750 jp = (JSPrinter *) cx->malloc(sizeof(JSPrinter));
751 if (!jp)
752 return NULL;
753 INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
754 JS_INIT_ARENA_POOL(&jp->pool, name, 256, 1, &cx->scriptStackQuota);
755 jp->indent = indent & ~JS_IN_GROUP_CONTEXT;
756 jp->pretty = pretty;
757 jp->grouped = (indent & JS_IN_GROUP_CONTEXT) != 0;
758 jp->script = NULL;
759 jp->dvgfence = NULL;
760 jp->pcstack = NULL;
761 jp->fun = fun;
762 jp->localNames = NULL;
763 if (fun && FUN_INTERPRETED(fun) && fun->hasLocalNames()) {
764 jp->localNames = js_GetLocalNameArray(cx, fun, &jp->pool);
765 if (!jp->localNames) {
766 js_DestroyPrinter(jp);
767 return NULL;
768 }
769 }
770 return jp;
771 }
772
773 void
774 js_DestroyPrinter(JSPrinter *jp)
775 {
776 JS_FinishArenaPool(&jp->pool);
777 jp->sprinter.context->free(jp);
778 }
779
780 JSString *
781 js_GetPrinterOutput(JSPrinter *jp)
782 {
783 JSContext *cx;
784 JSString *str;
785
786 cx = jp->sprinter.context;
787 if (!jp->sprinter.base)
788 return cx->runtime->emptyString;
789 str = JS_NewStringCopyZ(cx, jp->sprinter.base);
790 if (!str)
791 return NULL;
792 JS_FreeArenaPool(&jp->pool);
793 INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
794 return str;
795 }
796
797 /*
798 * NB: Indexed by SRC_DECL_* defines from jsemit.h.
799 */
800 static const char * const var_prefix[] = {"var ", "const ", "let "};
801
802 static const char *
803 VarPrefix(jssrcnote *sn)
804 {
805 if (sn && (SN_TYPE(sn) == SRC_DECL || SN_TYPE(sn) == SRC_GROUPASSIGN)) {
806 ptrdiff_t type = js_GetSrcNoteOffset(sn, 0);
807 if ((uintN)type <= SRC_DECL_LET)
808 return var_prefix[type];
809 }
810 return "";
811 }
812
813 int
814 js_printf(JSPrinter *jp, const char *format, ...)
815 {
816 va_list ap;
817 char *bp, *fp;
818 int cc;
819
820 if (*format == '\0')
821 return 0;
822
823 va_start(ap, format);
824
825 /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */
826 if (*format == '\t') {
827 format++;
828 if (jp->pretty && Sprint(&jp->sprinter, "%*s", jp->indent, "") < 0)
829 return -1;
830 }
831
832 /* Suppress newlines (must be once per format, at the end) if not pretty. */
833 fp = NULL;
834 if (!jp->pretty && format[cc = strlen(format) - 1] == '\n') {
835 fp = JS_strdup(jp->sprinter.context, format);
836 if (!fp)
837 return -1;
838 fp[cc] = '\0';
839 format = fp;
840 }
841
842 /* Allocate temp space, convert format, and put. */
843 bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */
844 if (fp) {
845 jp->sprinter.context->free(fp);
846 format = NULL;
847 }
848 if (!bp) {
849 JS_ReportOutOfMemory(jp->sprinter.context);
850 return -1;
851 }
852
853 cc = strlen(bp);
854 if (SprintPut(&jp->sprinter, bp, (size_t)cc) < 0)
855 cc = -1;
856 js_free(bp);
857
858 va_end(ap);
859 return cc;
860 }
861
862 JSBool
863 js_puts(JSPrinter *jp, const char *s)
864 {
865 return SprintCString(&jp->sprinter, s) >= 0;
866 }
867
868 /************************************************************************/
869
870 typedef struct SprintStack {
871 Sprinter sprinter; /* sprinter for postfix to infix buffering */
872 ptrdiff_t *offsets; /* stack of postfix string offsets */
873 jsbytecode *opcodes; /* parallel stack of JS opcodes */
874 uintN top; /* top of stack index */
875 uintN inArrayInit; /* array initialiser/comprehension level */
876 JSBool inGenExp; /* in generator expression */
877 JSPrinter *printer; /* permanent output goes here */
878 } SprintStack;
879
880 /*
881 * Find the depth of the operand stack when the interpreter reaches the given
882 * pc in script. pcstack must have space for least script->depth elements. On
883 * return it will contain pointers to opcodes that populated the interpreter's
884 * current operand stack.
885 *
886 * This function cannot raise an exception or error. However, due to a risk of
887 * potential bugs when modeling the stack, the function returns -1 if it
888 * detects an inconsistency in the model. Such an inconsistency triggers an
889 * assert in a debug build.
890 */
891 static intN
892 ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *pc,
893 jsbytecode **pcstack);
894
895 #define FAILED_EXPRESSION_DECOMPILER ((char *) 1)
896
897 /*
898 * Decompile a part of expression up to the given pc. The function returns
899 * NULL on out-of-memory, or the FAILED_EXPRESSION_DECOMPILER sentinel when
900 * the decompiler fails due to a bug and/or unimplemented feature, or the
901 * decompiled string on success.
902 */
903 static char *
904 DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun,
905 jsbytecode *pc);
906
907 /*
908 * Get a stacked offset from ss->sprinter.base, or if the stacked value |off|
909 * is negative, fetch the generating pc from printer->pcstack[-2 - off] and
910 * decompile the code that generated the missing value. This is used when
911 * reporting errors, where the model stack will lack |pcdepth| non-negative
912 * offsets (see DecompileExpression and DecompileCode).
913 *
914 * If the stacked offset is -1, return 0 to index the NUL padding at the start
915 * of ss->sprinter.base. If this happens, it means there is a decompiler bug
916 * to fix, but it won't violate memory safety.
917 */
918 static ptrdiff_t
919 GetOff(SprintStack *ss, uintN i)
920 {
921 ptrdiff_t off;
922 jsbytecode *pc;
923 char *bytes;
924
925 off = ss->offsets[i];
926 if (off >= 0)
927 return off;
928
929 JS_ASSERT(off <= -2);
930 JS_ASSERT(ss->printer->pcstack);
931 if (off <= -2 && ss->printer->pcstack) {
932 pc = ss->printer->pcstack[-2 - off];
933 bytes = DecompileExpression(ss->sprinter.context, ss->printer->script,
934 ss->printer->fun, pc);
935 if (!bytes)
936 return 0;
937 if (bytes != FAILED_EXPRESSION_DECOMPILER) {
938 off = SprintCString(&ss->sprinter, bytes);
939 if (off < 0)
940 off = 0;
941 ss->offsets[i] = off;
942 ss->sprinter.context->free(bytes);
943 return off;
944 }
945 if (!ss->sprinter.base && SprintPut(&ss->sprinter, "", 0) >= 0) {
946 memset(ss->sprinter.base, 0, ss->sprinter.offset);
947 ss->offsets[i] = -1;
948 }
949 }
950 return 0;
951 }
952
953 static const char *
954 GetStr(SprintStack *ss, uintN i)
955 {
956 ptrdiff_t off;
957
958 /*
959 * Must call GetOff before using ss->sprinter.base, since it may be null
960 * until bootstrapped by GetOff.
961 */
962 off = GetOff(ss, i);
963 return OFF2STR(&ss->sprinter, off);
964 }
965
966 /*
967 * Gap between stacked strings to allow for insertion of parens and commas
968 * when auto-parenthesizing expressions and decompiling array initialisers
969 * (see the JSOP_NEWARRAY case in Decompile).
970 */
971 #define PAREN_SLOP (2 + 1)
972
973 /*
974 * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME,
975 * JSOP_SETPROP, and JSOP_SETELEM, respectively. They are never stored in
976 * bytecode, so they don't preempt valid opcodes.
977 */
978 #define JSOP_GETPROP2 JSOP_LIMIT
979 #define JSOP_GETELEM2 JSOP_LIMIT + 1
980 JS_STATIC_ASSERT(JSOP_GETELEM2 <= 255);
981
982 static void
983 AddParenSlop(SprintStack *ss)
984 {
985 memset(OFF2STR(&ss->sprinter, ss->sprinter.offset), 0, PAREN_SLOP);
986 ss->sprinter.offset += PAREN_SLOP;
987 }
988
989 static JSBool
990 PushOff(SprintStack *ss, ptrdiff_t off, JSOp op)
991 {
992 uintN top;
993
994 if (!SprintEnsureBuffer(&ss->sprinter, PAREN_SLOP))
995 return JS_FALSE;
996
997 /* ss->top points to the next free slot; be paranoid about overflow. */
998 top = ss->top;
999 JS_ASSERT(top < StackDepth(ss->printer->script));
1000 if (top >= StackDepth(ss->printer->script)) {
1001 JS_ReportOutOfMemory(ss->sprinter.context);
1002 return JS_FALSE;
1003 }
1004
1005 /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */
1006 ss->offsets[top] = off;
1007 ss->opcodes[top] = jsbytecode((op == JSOP_GETPROP2) ? JSOP_GETPROP
1008 : (op == JSOP_GETELEM2) ? JSOP_GETELEM
1009 : op);
1010 ss->top = ++top;
1011 AddParenSlop(ss);
1012 return JS_TRUE;
1013 }
1014
1015 static ptrdiff_t
1016 PopOffPrec(SprintStack *ss, uint8 prec)
1017 {
1018 uintN top;
1019 const JSCodeSpec *topcs;
1020 ptrdiff_t off;
1021
1022 /* ss->top points to the next free slot; be paranoid about underflow. */
1023 top = ss->top;
1024 JS_ASSERT(top != 0);
1025 if (top == 0)
1026 return 0;
1027
1028 ss->top = --top;
1029 off = GetOff(ss, top);
1030 topcs = &js_CodeSpec[ss->opcodes[top]];
1031 if (topcs->prec != 0 && topcs->prec < prec) {
1032 ss->sprinter.offset = ss->offsets[top] = off - 2;
1033 off = Sprint(&ss->sprinter, "(%s)", OFF2STR(&ss->sprinter, off));
1034 } else {
1035 ss->sprinter.offset = off;
1036 }
1037 return off;
1038 }
1039
1040 static const char *
1041 PopStrPrec(SprintStack *ss, uint8 prec)
1042 {
1043 ptrdiff_t off;
1044
1045 off = PopOffPrec(ss, prec);
1046 return OFF2STR(&ss->sprinter, off);
1047 }
1048
1049 static ptrdiff_t
1050 PopOff(SprintStack *ss, JSOp op)
1051 {
1052 return PopOffPrec(ss, js_CodeSpec[op].prec);
1053 }
1054
1055 static const char *
1056 PopStr(SprintStack *ss, JSOp op)
1057 {
1058 return PopStrPrec(ss, js_CodeSpec[op].prec);
1059 }
1060
1061 typedef struct TableEntry {
1062 jsval key;
1063 ptrdiff_t offset;
1064 JSAtom *label;
1065 jsint order; /* source order for stable tableswitch sort */
1066 } TableEntry;
1067
1068 static JSBool
1069 CompareOffsets(void *arg, const void *v1, const void *v2, int *result)
1070 {
1071 ptrdiff_t offset_diff;
1072 const TableEntry *te1 = (const TableEntry *) v1,
1073 *te2 = (const TableEntry *) v2;
1074
1075 offset_diff = te1->offset - te2->offset;
1076 *result = (offset_diff == 0 ? te1->order - te2->order
1077 : offset_diff < 0 ? -1
1078 : 1);
1079 return JS_TRUE;
1080 }
1081
1082 static ptrdiff_t
1083 SprintDoubleValue(Sprinter *sp, jsval v, JSOp *opp)
1084 {
1085 jsdouble d;
1086 ptrdiff_t todo;
1087 char *s, buf[DTOSTR_STANDARD_BUFFER_SIZE];
1088
1089 JS_ASSERT(JSVAL_IS_DOUBLE(v));
1090 d = *JSVAL_TO_DOUBLE(v);
1091 if (JSDOUBLE_IS_NEGZERO(d)) {
1092 todo = SprintCString(sp, "-0");
1093 *opp = JSOP_NEG;
1094 } else if (!JSDOUBLE_IS_FINITE(d)) {
1095 /* Don't use Infinity and NaN, they're mutable. */
1096 todo = SprintCString(sp,
1097 JSDOUBLE_IS_NaN(d)
1098 ? "0 / 0"
1099 : (d < 0)
1100 ? "1 / -0"
1101 : "1 / 0");
1102 *opp = JSOP_DIV;
1103 } else {
1104 s = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD, 0, d);
1105 if (!s) {
1106 JS_ReportOutOfMemory(sp->context);
1107 return -1;
1108 }
1109 JS_ASSERT(strcmp(s, js_Infinity_str) &&
1110 (*s != '-' ||
1111 strcmp(s + 1, js_Infinity_str)) &&
1112 strcmp(s, js_NaN_str));
1113 todo = Sprint(sp, s);
1114 }
1115 return todo;
1116 }
1117
1118 static jsbytecode *
1119 Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop);
1120
1121 static JSBool
1122 DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength,
1123 jsbytecode *pc, ptrdiff_t switchLength,
1124 ptrdiff_t defaultOffset, JSBool isCondSwitch)
1125 {
1126 JSContext *cx;
1127 JSPrinter *jp;
1128 ptrdiff_t off, off2, diff, caseExprOff, todo;
1129 char *lval, *rval;
1130 uintN i;
1131 jsval key;
1132 JSString *str;
1133
1134 cx = ss->sprinter.context;
1135 jp = ss->printer;
1136
1137 /* JSOP_CONDSWITCH doesn't pop, unlike JSOP_{LOOKUP,TABLE}SWITCH. */
1138 off = isCondSwitch ? GetOff(ss, ss->top-1) : PopOff(ss, JSOP_NOP);
1139 lval = OFF2STR(&ss->sprinter, off);
1140
1141 js_printf(jp, "\tswitch (%s) {\n", lval);
1142
1143 if (tableLength) {
1144 diff = table[0].offset - defaultOffset;
1145 if (diff > 0) {
1146 jp->indent += 2;
1147 js_printf(jp, "\t%s:\n", js_default_str);
1148 jp->indent += 2;
1149 if (!Decompile(ss, pc + defaultOffset, diff, JSOP_NOP))
1150 return JS_FALSE;
1151 jp->indent -= 4;
1152 }
1153
1154 caseExprOff = isCondSwitch ? JSOP_CONDSWITCH_LENGTH : 0;
1155
1156 for (i = 0; i < tableLength; i++) {
1157 off = table[i].offset;
1158 off2 = (i + 1 < tableLength) ? table[i + 1].offset : switchLength;
1159
1160 key = table[i].key;
1161 if (isCondSwitch) {
1162 ptrdiff_t nextCaseExprOff;
1163
1164 /*
1165 * key encodes the JSOP_CASE bytecode's offset from switchtop.
1166 * The next case expression follows immediately, unless we are
1167 * at the last case.
1168 */
1169 nextCaseExprOff = (ptrdiff_t)JSVAL_TO_INT(key);
1170 nextCaseExprOff += js_CodeSpec[pc[nextCaseExprOff]].length;
1171 jp->indent += 2;
1172 if (!Decompile(ss, pc + caseExprOff,
1173 nextCaseExprOff - caseExprOff, JSOP_NOP)) {
1174 return JS_FALSE;
1175 }
1176 caseExprOff = nextCaseExprOff;
1177
1178 /* Balance the stack as if this JSOP_CASE matched. */
1179 --ss->top;
1180 } else {
1181 /*
1182 * key comes from an atom, not the decompiler, so we need to
1183 * quote it if it's a string literal. But if table[i].label
1184 * is non-null, key was constant-propagated and label is the
1185 * name of the const we should show as the case label. We set
1186 * key to undefined so this identifier is escaped, if required
1187 * by non-ASCII characters, but not quoted, by QuoteString.
1188 */
1189 todo = -1;
1190 if (table[i].label) {
1191 str = ATOM_TO_STRING(table[i].label);
1192 key = JSVAL_VOID;
1193 } else if (JSVAL_IS_DOUBLE(key)) {
1194 JSOp junk;
1195
1196 todo = SprintDoubleValue(&ss->sprinter, key, &junk);
1197 str = NULL;
1198 } else {
1199 str = js_ValueToString(cx, key);
1200 if (!str)
1201 return JS_FALSE;
1202 }
1203 if (todo >= 0) {
1204 rval = OFF2STR(&ss->sprinter, todo);
1205 } else {
1206 rval = QuoteString(&ss->sprinter, str, (jschar)
1207 (JSVAL_IS_STRING(key) ? '"' : 0));
1208 if (!rval)
1209 return JS_FALSE;
1210 }
1211 RETRACT(&ss->sprinter, rval);
1212 jp->indent += 2;
1213 js_printf(jp, "\tcase %s:\n", rval);
1214 }
1215
1216 jp->indent += 2;
1217 if (off <= defaultOffset && defaultOffset < off2) {
1218 diff = defaultOffset - off;
1219 if (diff != 0) {
1220 if (!Decompile(ss, pc + off, diff, JSOP_NOP))
1221 return JS_FALSE;
1222 off = defaultOffset;
1223 }
1224 jp->indent -= 2;
1225 js_printf(jp, "\t%s:\n", js_default_str);
1226 jp->indent += 2;
1227 }
1228 if (!Decompile(ss, pc + off, off2 - off, JSOP_NOP))
1229 return JS_FALSE;
1230 jp->indent -= 4;
1231
1232 /* Re-balance as if last JSOP_CASE or JSOP_DEFAULT mismatched. */
1233 if (isCondSwitch)
1234 ++ss->top;
1235 }
1236 }
1237
1238 if (defaultOffset == switchLength) {
1239 jp->indent += 2;
1240 js_printf(jp, "\t%s:;\n", js_default_str);
1241 jp->indent -= 2;
1242 }
1243 js_printf(jp, "\t}\n");
1244
1245 /* By the end of a JSOP_CONDSWITCH, the discriminant has been popped. */
1246 if (isCondSwitch)
1247 --ss->top;
1248 return JS_TRUE;
1249 }
1250
1251 #define LOCAL_ASSERT_CUSTOM(expr, BAD_EXIT) \
1252 JS_BEGIN_MACRO \
1253 JS_ASSERT(expr); \
1254 if (!(expr)) { BAD_EXIT; } \
1255 JS_END_MACRO
1256
1257 #define LOCAL_ASSERT_RV(expr, rv) \
1258 LOCAL_ASSERT_CUSTOM(expr, return (rv))
1259
1260 static JSAtom *
1261 GetArgOrVarAtom(JSPrinter *jp, uintN slot)
1262 {
1263 JSAtom *name;
1264
1265 LOCAL_ASSERT_RV(jp->fun, NULL);
1266 LOCAL_ASSERT_RV(slot < jp->fun->countLocalNames(), NULL);
1267 name = JS_LOCAL_NAME_TO_ATOM(jp->localNames[slot]);
1268 #if !JS_HAS_DESTRUCTURING
1269 LOCAL_ASSERT_RV(name, NULL);
1270 #endif
1271 return name;
1272 }
1273
1274 const char *
1275 GetLocal(SprintStack *ss, jsint i)
1276 {
1277 ptrdiff_t off;
1278 JSContext *cx;
1279 JSScript *script;
1280 jsatomid j, n;
1281 JSAtom *atom;
1282 JSObject *obj;
1283 jsint depth, count;
1284 JSScopeProperty *sprop;
1285 const char *rval;
1286
1287 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, "")
1288
1289 off = ss->offsets[i];
1290 if (off >= 0)
1291 return OFF2STR(&ss->sprinter, off);
1292
1293 /*
1294 * We must be called from js_DecompileValueGenerator (via Decompile) when
1295 * dereferencing a local that's undefined or null. Search script->objects
1296 * for the block containing this local by its stack index, i.
1297 *
1298 * In case of destructuring's use of JSOP_GETLOCAL, however, there may be
1299 * no such local. This could mean no blocks (no script objects at all, or
1300 * none of the script's object literals are blocks), or the stack slot i is
1301 * not in a block. In either case, return GetStr(ss, i).
1302 */
1303 cx = ss->sprinter.context;
1304 script = ss->printer->script;
1305 if (script->objectsOffset == 0)
1306 return GetStr(ss, i);
1307 for (j = 0, n = script->objects()->length; ; j++) {
1308 if (j == n)
1309 return GetStr(ss, i);
1310 obj = script->getObject(j);
1311 if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) {
1312 depth = OBJ_BLOCK_DEPTH(cx, obj);
1313 count = OBJ_BLOCK_COUNT(cx, obj);
1314 if ((jsuint)(i - depth) < (jsuint)count)
1315 break;
1316 }
1317 }
1318
1319 i -= depth;
1320 for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) {
1321 if (sprop->shortid == i)
1322 break;
1323 }
1324
1325 LOCAL_ASSERT(sprop && JSID_IS_ATOM(sprop->id));
1326 atom = JSID_TO_ATOM(sprop->id);
1327 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1328 if (!rval)
1329 return NULL;
1330 RETRACT(&ss->sprinter, rval);
1331 return rval;
1332
1333 #undef LOCAL_ASSERT
1334 }
1335
1336 static JSBool
1337 IsVarSlot(JSPrinter *jp, jsbytecode *pc, jsint *indexp)
1338 {
1339 uintN slot;
1340
1341 slot = GET_SLOTNO(pc);
1342 if (slot < jp->script->nfixed) {
1343 /* The slot refers to a variable with name stored in jp->localNames. */
1344 *indexp = jp->fun->nargs + slot;
1345 return JS_TRUE;
1346 }
1347
1348 /* We have a local which index is relative to the stack base. */
1349 slot -= jp->script->nfixed;
1350 JS_ASSERT(slot < StackDepth(jp->script));
1351 *indexp = slot;
1352 return JS_FALSE;
1353 }
1354
1355 #define LOAD_ATOM(PCOFF) \
1356 GET_ATOM_FROM_BYTECODE(jp->script, pc, PCOFF, atom)
1357
1358 #if JS_HAS_DESTRUCTURING
1359
1360 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
1361 #define LOAD_OP_DATA(pc) (oplen = (cs = &js_CodeSpec[op=(JSOp)*pc])->length)
1362
1363 static jsbytecode *
1364 DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc);
1365
1366 static jsbytecode *
1367 DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
1368 JSBool *hole)
1369 {
1370 JSContext *cx;
1371 JSPrinter *jp;
1372 JSOp op;
1373 const JSCodeSpec *cs;
1374 uintN oplen;
1375 jsint i;
1376 const char *lval, *xval;
1377 ptrdiff_t todo;
1378 JSAtom *atom;
1379
1380 *hole = JS_FALSE;
1381 cx = ss->sprinter.context;
1382 jp = ss->printer;
1383 LOAD_OP_DATA(pc);
1384
1385 switch (op) {
1386 case JSOP_POP:
1387 *hole = JS_TRUE;
1388 todo = SprintPut(&ss->sprinter, ", ", 2);
1389 break;
1390
1391 case JSOP_DUP:
1392 pc = DecompileDestructuring(ss, pc, endpc);
1393 if (!pc)
1394 return NULL;
1395 if (pc == endpc)
1396 return pc;
1397 LOAD_OP_DATA(pc);
1398 lval = PopStr(ss, JSOP_NOP);
1399 todo = SprintCString(&ss->sprinter, lval);
1400 if (op == JSOP_POPN)
1401 return pc;
1402 LOCAL_ASSERT(*pc == JSOP_POP);
1403 break;
1404
1405 case JSOP_SETARG:
1406 case JSOP_SETGVAR:
1407 case JSOP_SETLOCAL:
1408 LOCAL_ASSERT(pc[oplen] == JSOP_POP || pc[oplen] == JSOP_POPN);
1409 /* FALL THROUGH */
1410
1411 case JSOP_SETLOCALPOP:
1412 atom = NULL;
1413 lval = NULL;
1414 if (op == JSOP_SETARG) {
1415 atom = GetArgOrVarAtom(jp, GET_SLOTNO(pc));
1416 LOCAL_ASSERT(atom);
1417 } else if (op == JSOP_SETGVAR) {
1418 LOAD_ATOM(0);
1419 } else if (IsVarSlot(jp, pc, &i)) {
1420 atom = GetArgOrVarAtom(jp, i);
1421 LOCAL_ASSERT(atom);
1422 } else {
1423 lval = GetLocal(ss, i);
1424 }
1425 if (atom)
1426 lval = js_AtomToPrintableString(cx, atom);
1427 LOCAL_ASSERT(lval);
1428 todo = SprintCString(&ss->sprinter, lval);
1429 if (op != JSOP_SETLOCALPOP) {
1430 pc += oplen;
1431 if (pc == endpc)
1432 return pc;
1433 LOAD_OP_DATA(pc);
1434 if (op == JSOP_POPN)
1435 return pc;
1436 LOCAL_ASSERT(op == JSOP_POP);
1437 }
1438 break;
1439
1440 default:
1441 /*
1442 * We may need to auto-parenthesize the left-most value decompiled
1443 * here, so add back PAREN_SLOP temporarily. Then decompile until the
1444 * opcode that would reduce the stack depth to (ss->top-1), which we
1445 * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for
1446 * the nb parameter.
1447 */
1448 todo = ss->sprinter.offset;
1449 ss->sprinter.offset = todo + PAREN_SLOP;
1450 pc = Decompile(ss, pc, -((intN)ss->top), JSOP_NOP);
1451 if (!pc)
1452 return NULL;
1453 if (pc == endpc)
1454 return pc;
1455 LOAD_OP_DATA(pc);
1456 LOCAL_ASSERT(op == JSOP_ENUMELEM || op == JSOP_ENUMCONSTELEM);
1457 xval = PopStr(ss, JSOP_NOP);
1458 lval = PopStr(ss, JSOP_GETPROP);
1459 ss->sprinter.offset = todo;
1460 if (*lval == '\0') {
1461 /* lval is from JSOP_BINDNAME, so just print xval. */
1462 todo = SprintCString(&ss->sprinter, xval);
1463 } else if (*xval == '\0') {
1464 /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */
1465 todo = SprintCString(&ss->sprinter, lval);
1466 } else {
1467 todo = Sprint(&ss->sprinter,
1468 (JOF_OPMODE(ss->opcodes[ss->top+1]) == JOF_XMLNAME)
1469 ? "%s.%s"
1470 : "%s[%s]",
1471 lval, xval);
1472 }
1473 break;
1474 }
1475
1476 if (todo < 0)
1477 return NULL;
1478
1479 LOCAL_ASSERT(pc < endpc);
1480 pc += oplen;
1481 return pc;
1482 }
1483
1484 /*
1485 * Starting with a SRC_DESTRUCT-annotated JSOP_DUP, decompile a destructuring
1486 * left-hand side object or array initialiser, including nested destructuring
1487 * initialisers. On successful return, the decompilation will be pushed on ss
1488 * and the return value will point to the POP or GROUP bytecode following the
1489 * destructuring expression.
1490 *
1491 * At any point, if pc is equal to endpc and would otherwise advance, we stop
1492 * immediately and return endpc.
1493 */
1494 static jsbytecode *
1495 DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc)
1496 {
1497 ptrdiff_t head;
1498 JSContext *cx;
1499 JSPrinter *jp;
1500 JSOp op, saveop;
1501 const JSCodeSpec *cs;
1502 uintN oplen;
1503 jsint i, lasti;
1504 jsdouble d;
1505 const char *lval;
1506 JSAtom *atom;
1507 jssrcnote *sn;
1508 JSString *str;
1509 JSBool hole;
1510
1511 LOCAL_ASSERT(*pc == JSOP_DUP);
1512 pc += JSOP_DUP_LENGTH;
1513
1514 /*
1515 * Set head so we can rewrite '[' to '{' as needed. Back up PAREN_SLOP
1516 * chars so the destructuring decompilation accumulates contiguously in
1517 * ss->sprinter starting with "[".
1518 */
1519 head = SprintPut(&ss->sprinter, "[", 1);
1520 if (head < 0 || !PushOff(ss, head, JSOP_NOP))
1521 return NULL;
1522 ss->sprinter.offset -= PAREN_SLOP;
1523 LOCAL_ASSERT(head == ss->sprinter.offset - 1);
1524 LOCAL_ASSERT(*OFF2STR(&ss->sprinter, head) == '[');
1525
1526 cx = ss->sprinter.context;
1527 jp = ss->printer;
1528 lasti = -1;
1529
1530 while (pc < endpc) {
1531 #if JS_HAS_DESTRUCTURING_SHORTHAND
1532 ptrdiff_t nameoff = -1;
1533 #endif
1534
1535 LOAD_OP_DATA(pc);
1536 saveop = op;
1537
1538 switch (op) {
1539 case JSOP_POP:
1540 pc += oplen;
1541 goto out;
1542
1543 /* Handle the optimized number-pushing opcodes. */
1544 case JSOP_ZERO: d = i = 0; goto do_getelem;
1545 case JSOP_ONE: d = i = 1; goto do_getelem;
1546 case JSOP_UINT16: d = i = GET_UINT16(pc); goto do_getelem;
1547 case JSOP_UINT24: d = i = GET_UINT24(pc); goto do_getelem;
1548 case JSOP_INT8: d = i = GET_INT8(pc); goto do_getelem;
1549 case JSOP_INT32: d = i = GET_INT32(pc); goto do_getelem;
1550
1551 case JSOP_DOUBLE:
1552 GET_DOUBLE_FROM_BYTECODE(jp->script, pc, 0, atom);
1553 d = *ATOM_TO_DOUBLE(atom);
1554 LOCAL_ASSERT(JSDOUBLE_IS_FINITE(d) && !JSDOUBLE_IS_NEGZERO(d));
1555 i = (jsint)d;
1556
1557 do_getelem:
1558 sn = js_GetSrcNote(jp->script, pc);
1559 pc += oplen;
1560 if (pc == endpc)
1561 return pc;
1562 LOAD_OP_DATA(pc);
1563 LOCAL_ASSERT(op == JSOP_GETELEM);
1564
1565 /* Distinguish object from array by opcode or source note. */
1566 if (sn && SN_TYPE(sn) == SRC_INITPROP) {
1567 *OFF2STR(&ss->sprinter, head) = '{';
1568 if (Sprint(&ss->sprinter, "%g: ", d) < 0)
1569 return NULL;
1570 } else {
1571 /* Sanity check for the gnarly control flow above. */
1572 LOCAL_ASSERT(i == d);
1573
1574 /* Fill in any holes (holes at the end don't matter). */
1575 while (++lasti < i) {
1576 if (SprintPut(&ss->sprinter, ", ", 2) < 0)
1577 return NULL;
1578 }
1579 }
1580 break;
1581
1582 case JSOP_LENGTH:
1583 atom = cx->runtime->atomState.lengthAtom;
1584 goto do_destructure_atom;
1585
1586 case JSOP_CALLPROP:
1587 case JSOP_GETPROP:
1588 LOAD_ATOM(0);
1589 do_destructure_atom:
1590 *OFF2STR(&ss->sprinter, head) = '{';
1591 str = ATOM_TO_STRING(atom);
1592 #if JS_HAS_DESTRUCTURING_SHORTHAND
1593 nameoff = ss->sprinter.offset;
1594 #endif
1595 if (!QuoteString(&ss->sprinter, str,
1596 js_IsIdentifier(str) ? 0 : (jschar)'\'')) {
1597 return NULL;
1598 }
1599 if (SprintPut(&ss->sprinter, ": ", 2) < 0)
1600 return NULL;
1601 break;
1602
1603 default:
1604 LOCAL_ASSERT(0);
1605 }
1606
1607 pc += oplen;
1608 if (pc == endpc)
1609 return pc;
1610
1611 /*
1612 * Decompile the left-hand side expression whose bytecode starts at pc
1613 * and continues for a bounded number of bytecodes or stack operations
1614 * (and which in any event stops before endpc).
1615 */
1616 pc = DecompileDestructuringLHS(ss, pc, endpc, &hole);
1617 if (!pc)
1618 return NULL;
1619
1620 #if JS_HAS_DESTRUCTURING_SHORTHAND
1621 if (nameoff >= 0) {
1622 ptrdiff_t offset, initlen;
1623
1624 offset = ss->sprinter.offset;
1625 LOCAL_ASSERT(*OFF2STR(&ss->sprinter, offset) == '\0');
1626 initlen = offset - nameoff;
1627 LOCAL_ASSERT(initlen >= 4);
1628
1629 /* Early check to rule out odd "name: lval" length. */
1630 if (((size_t)initlen & 1) == 0) {
1631 size_t namelen;
1632 const char *name;
1633
1634 /*
1635 * Even "name: lval" string length: check for "x: x" and the
1636 * like, and apply the shorthand if we can.
1637 */
1638 namelen = (size_t)(initlen - 2) >> 1;
1639 name = OFF2STR(&ss->sprinter, nameoff);
1640 if (!strncmp(name + namelen, ": ", 2) &&
1641 !strncmp(name, name + namelen + 2, namelen)) {
1642 offset -= namelen + 2;
1643 *OFF2STR(&ss->sprinter, offset) = '\0';
1644 ss->sprinter.offset = offset;
1645 }
1646 }
1647 }
1648 #endif
1649
1650 if (pc == endpc || *pc != JSOP_DUP)
1651 break;
1652
1653 /*
1654 * We should stop if JSOP_DUP is either without notes or its note is
1655 * not SRC_CONTINUE. The former happens when JSOP_DUP duplicates the
1656 * last destructuring reference implementing an op= assignment like in
1657 * '([t] = z).y += x'. In the latter case the note is SRC_DESTRUCT and
1658 * means another destructuring initialiser abuts this one like in
1659 * '[a] = [b] = c'.
1660 */
1661 sn = js_GetSrcNote(jp->script, pc);
1662 if (!sn)
1663 break;
1664 if (SN_TYPE(sn) != SRC_CONTINUE) {
1665 LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT);
1666 break;
1667 }
1668
1669 if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
1670 return NULL;
1671
1672 pc += JSOP_DUP_LENGTH;
1673 }
1674
1675 out:
1676 lval = OFF2STR(&ss->sprinter, head);
1677 if (SprintPut(&ss->sprinter, (*lval == '[') ? "]" : "}", 1) < 0)
1678 return NULL;
1679 return pc;
1680 }
1681
1682 static jsbytecode *
1683 DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
1684 jssrcnote *sn, ptrdiff_t *todop)
1685 {
1686 JSOp op;
1687 const JSCodeSpec *cs;
1688 uintN oplen, start, end, i;
1689 ptrdiff_t todo;
1690 JSBool hole;
1691 const char *rval;
1692
1693 LOAD_OP_DATA(pc);
1694 LOCAL_ASSERT(op == JSOP_PUSH || op == JSOP_GETLOCAL);
1695
1696 todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn));
1697 if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
1698 return NULL;
1699 ss->sprinter.offset -= PAREN_SLOP;
1700
1701 for (;;) {
1702 pc += oplen;
1703 if (pc == endpc)
1704 return pc;
1705 pc = DecompileDestructuringLHS(ss, pc, endpc, &hole);
1706 if (!pc)
1707 return NULL;
1708 if (pc == endpc)
1709 return pc;
1710 LOAD_OP_DATA(pc);
1711 if (op != JSOP_PUSH && op != JSOP_GETLOCAL)
1712 break;
1713 if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
1714 return NULL;
1715 }
1716
1717 LOCAL_ASSERT(op == JSOP_POPN);
1718 if (SprintPut(&ss->sprinter, "] = [", 5) < 0)
1719 return NULL;
1720
1721 end = ss->top - 1;
1722 start = end - GET_UINT16(pc);
1723 for (i = start; i < end; i++) {
1724 rval = GetStr(ss, i);
1725 if (Sprint(&ss->sprinter,
1726 (i == start) ? "%s" : ", %s",
1727 (i == end - 1 && *rval == '\0') ? ", " : rval) < 0) {
1728 return NULL;
1729 }
1730 }
1731
1732 if (SprintPut(&ss->sprinter, "]", 1) < 0)
1733 return NULL;
1734 ss->sprinter.offset = ss->offsets[i];
1735 ss->top = start;
1736 *todop = todo;
1737 return pc;
1738 }
1739
1740 #undef LOCAL_ASSERT
1741 #undef LOAD_OP_DATA
1742
1743 #endif /* JS_HAS_DESTRUCTURING */
1744
1745 static JSBool
1746 InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth)
1747 {
1748 size_t offsetsz, opcodesz;
1749 void *space;
1750
1751 INIT_SPRINTER(cx, &ss->sprinter, &cx->tempPool, PAREN_SLOP);
1752
1753 /* Allocate the parallel (to avoid padding) offset and opcode stacks. */
1754 offsetsz = depth * sizeof(ptrdiff_t);
1755 opcodesz = depth * sizeof(jsbytecode);
1756 JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz);
1757 if (!space) {
1758 js_ReportOutOfScriptQuota(cx);
1759 return JS_FALSE;
1760 }
1761 ss->offsets = (ptrdiff_t *) space;
1762 ss->opcodes = (jsbytecode *) ((char *)space + offsetsz);
1763
1764 ss->top = ss->inArrayInit = 0;
1765 ss->inGenExp = JS_FALSE;
1766 ss->printer = jp;
1767 return JS_TRUE;
1768 }
1769
1770 /*
1771 * If nb is non-negative, decompile nb bytecodes starting at pc. Otherwise
1772 * the decompiler starts at pc and continues until it reaches an opcode for
1773 * which decompiling would result in the stack depth equaling -(nb + 1).
1774 *
1775 * The nextop parameter is either JSOP_NOP or the "next" opcode in order of
1776 * abstract interpretation (not necessarily physically next in a bytecode
1777 * vector). So nextop is JSOP_POP for the last operand in a comma expression,
1778 * or JSOP_AND for the right operand of &&.
1779 */
1780 static jsbytecode *
1781 Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
1782 {
1783 JSContext *cx;
1784 JSPrinter *jp, *jp2;
1785 jsbytecode *startpc, *endpc, *pc2, *done;
1786 ptrdiff_t tail, todo, len, oplen, cond, next;
1787 JSOp op, lastop, saveop;
1788 const JSCodeSpec *cs;
1789 jssrcnote *sn, *sn2;
1790 const char *lval, *rval, *xval, *fmt, *token;
1791 uintN nuses;
1792 jsint i, argc;
1793 char **argv;
1794 JSAtom *atom;
1795 JSObject *obj;
1796 JSFunction *fun;
1797 JSString *str;
1798 JSBool ok;
1799 #if JS_HAS_XML_SUPPORT
1800 JSBool foreach, inXML, quoteAttr;
1801 #else
1802 #define inXML JS_FALSE
1803 #endif
1804 jsval val;
1805
1806 static const char exception_cookie[] = "/*EXCEPTION*/";
1807 static const char retsub_pc_cookie[] = "/*RETSUB_PC*/";
1808 static const char iter_cookie[] = "/*ITER*/";
1809 static const char forelem_cookie[] = "/*FORELEM*/";
1810 static const char with_cookie[] = "/*WITH*/";
1811 static const char dot_format[] = "%s.%s";
1812 static const char index_format[] = "%s[%s]";
1813 static const char predot_format[] = "%s%s.%s";
1814 static const char postdot_format[] = "%s.%s%s";
1815 static const char preindex_format[] = "%s%s[%s]";
1816 static const char postindex_format[] = "%s[%s]%s";
1817 static const char ss_format[] = "%s%s";
1818 static const char sss_format[] = "%s%s%s";
1819
1820 /* Argument and variables decompilation uses the following to share code. */
1821 JS_STATIC_ASSERT(ARGNO_LEN == SLOTNO_LEN);
1822
1823 /*
1824 * Local macros
1825 */
1826 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
1827 #define DECOMPILE_CODE(pc,nb) if (!Decompile(ss, pc, nb, JSOP_NOP)) return NULL
1828 #define NEXT_OP(pc) (((pc) + (len) == endpc) ? nextop : pc[len])
1829 #define TOP_STR() GetStr(ss, ss->top - 1)
1830 #define POP_STR() PopStr(ss, op)
1831 #define POP_STR_PREC(prec) PopStrPrec(ss, prec)
1832
1833 /*
1834 * Pop a condition expression for if/while. JSOP_IFEQ's precedence forces
1835 * extra parens around assignment, which avoids a strict-mode warning.
1836 */
1837 #define POP_COND_STR() \
1838 PopStr(ss, (js_CodeSpec[ss->opcodes[ss->top - 1]].format & JOF_SET) \
1839 ? JSOP_IFEQ \
1840 : JSOP_NOP)
1841
1842 /*
1843 * Callers know that ATOM_IS_STRING(atom), and we leave it to the optimizer to
1844 * common ATOM_TO_STRING(atom) here and near the call sites.
1845 */
1846 #define ATOM_IS_IDENTIFIER(atom) js_IsIdentifier(ATOM_TO_STRING(atom))
1847 #define ATOM_IS_KEYWORD(atom) \
1848 (js_CheckKeyword(ATOM_TO_STRING(atom)->chars(), \
1849 ATOM_TO_STRING(atom)->length()) != TOK_EOF)
1850
1851 /*
1852 * Given an atom already fetched from jp->script's atom map, quote/escape its
1853 * string appropriately into rval, and select fmt from the quoted and unquoted
1854 * alternatives.
1855 */
1856 #define GET_QUOTE_AND_FMT(qfmt, ufmt, rval) \
1857 JS_BEGIN_MACRO \
1858 jschar quote_; \
1859 if (!ATOM_IS_IDENTIFIER(atom)) { \
1860 quote_ = '\''; \
1861 fmt = qfmt; \
1862 } else { \
1863 quote_ = 0; \
1864 fmt = ufmt; \
1865 } \
1866 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), quote_); \
1867 if (!rval) \
1868 return NULL; \
1869 JS_END_MACRO
1870
1871 #define LOAD_OBJECT(PCOFF) \
1872 GET_OBJECT_FROM_BYTECODE(jp->script, pc, PCOFF, obj)
1873
1874 #define LOAD_FUNCTION(PCOFF) \
1875 GET_FUNCTION_FROM_BYTECODE(jp->script, pc, PCOFF, fun)
1876
1877 #define LOAD_REGEXP(PCOFF) \
1878 GET_REGEXP_FROM_BYTECODE(jp->script, pc, PCOFF, obj)
1879
1880 #define GET_SOURCE_NOTE_ATOM(sn, atom) \
1881 JS_BEGIN_MACRO \
1882 jsatomid atomIndex_ = (jsatomid) js_GetSrcNoteOffset((sn), 0); \
1883 \
1884 LOCAL_ASSERT(atomIndex_ < jp->script->atomMap.length); \
1885 (atom) = jp->script->atomMap.vector[atomIndex_]; \
1886 JS_END_MACRO
1887
1888 /*
1889 * Get atom from jp->script's atom map, quote/escape its string appropriately
1890 * into rval, and select fmt from the quoted and unquoted alternatives.
1891 */
1892 #define GET_ATOM_QUOTE_AND_FMT(qfmt, ufmt, rval) \
1893 JS_BEGIN_MACRO \
1894 LOAD_ATOM(0); \
1895 GET_QUOTE_AND_FMT(qfmt, ufmt, rval); \
1896 JS_END_MACRO
1897
1898 /*
1899 * Per spec, new x(y).z means (new x(y))).z. For example new (x(y).z) must
1900 * decompile with the constructor parenthesized, but new x.z should not. The
1901 * normal rules give x(y).z and x.z identical precedence: both are produced by
1902 * JSOP_GETPROP.
1903 *
1904 * Therefore, we need to know in case JSOP_NEW whether the constructor
1905 * expression contains any unparenthesized function calls. So when building a
1906 * MemberExpression or CallExpression, we set ss->opcodes[n] to JSOP_CALL if
1907 * this is true. x(y).z gets JSOP_CALL, not JSOP_GETPROP.
1908 */
1909 #define PROPAGATE_CALLNESS() \
1910 JS_BEGIN_MACRO \
1911 if (ss->opcodes[ss->top - 1] == JSOP_CALL) \
1912 saveop = JSOP_CALL; \
1913 JS_END_MACRO
1914
1915 cx = ss->sprinter.context;
1916 JS_CHECK_RECURSION(cx, return NULL);
1917
1918 jp = ss->printer;
1919 startpc = pc;
1920 endpc = (nb < 0) ? jp->script->code + jp->script->length : pc + nb;
1921 tail = -1;
1922 todo = -2; /* NB: different from Sprint() error return. */
1923 saveop = JSOP_NOP;
1924 sn = NULL;
1925 rval = NULL;
1926 #if JS_HAS_XML_SUPPORT
1927 foreach = inXML = quoteAttr = JS_FALSE;
1928 #endif
1929
1930 while (nb < 0 || pc < endpc) {
1931 /*
1932 * Move saveop to lastop so prefixed bytecodes can take special action
1933 * while sharing maximal code. Set op and saveop to the new bytecode,
1934 * use op in POP_STR to trigger automatic parenthesization, but push
1935 * saveop at the bottom of the loop if this op pushes. Thus op may be
1936 * set to nop or otherwise mutated to suppress auto-parens.
1937 */
1938 lastop = saveop;
1939 op = (JSOp) *pc;
1940 cs = &js_CodeSpec[op];
1941 if (cs->format & JOF_INDEXBASE) {
1942 /*
1943 * The decompiler uses js_GetIndexFromBytecode to get atoms and
1944 * objects and ignores these suffix/prefix bytecodes, thus
1945 * simplifying code that must process JSOP_GETTER/JSOP_SETTER
1946 * prefixes.
1947 */
1948 pc += cs->length;
1949 if (pc >= endpc)
1950 break;
1951 op = (JSOp) *pc;
1952 cs = &js_CodeSpec[op];
1953 }
1954 saveop = op;
1955 len = oplen = cs->length;
1956 nuses = js_GetStackUses(cs, op, pc);
1957
1958 /*
1959 * Here it is possible that nuses > ss->top when the op has a hidden
1960 * source note. But when nb < 0 we assume that the caller knows that
1961 * Decompile would never meet such opcodes.
1962 */
1963 if (nb < 0) {
1964 LOCAL_ASSERT(ss->top >= nuses);
1965 uintN ndefs = js_GetStackDefs(cx, cs, op, jp->script, pc);
1966 if ((uintN) -(nb + 1) == ss->top - nuses + ndefs)
1967 return pc;
1968 }
1969
1970 /*
1971 * Save source literal associated with JS now before the following
1972 * rewrite changes op. See bug 380197.
1973 */
1974 token = CodeToken[op];
1975
1976 if (pc + oplen == jp->dvgfence) {
1977 JSStackFrame *fp;
1978 uint32 format, mode, type;
1979
1980 /*
1981 * Rewrite non-get ops to their "get" format if the error is in
1982 * the bytecode at pc, so we don't decompile more than the error
1983 * expression.
1984 */
1985 fp = js_GetScriptedCaller(cx, NULL);
1986 format = cs->format;
1987 if (((fp && fp->regs && pc == fp->regs->pc) ||
1988 (pc == startpc && nuses != 0)) &&
1989 format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_FOR|JOF_VARPROP)) {
1990 mode = JOF_MODE(format);
1991 if (mode == JOF_NAME) {
1992 /*
1993 * JOF_NAME does not imply JOF_ATOM, so we must check for
1994 * the QARG and QVAR format types, and translate those to
1995 * JSOP_GETARG or JSOP_GETLOCAL appropriately, instead of
1996 * to JSOP_NAME.
1997 */
1998 type = JOF_TYPE(format);
1999 op = (type == JOF_QARG)
2000 ? JSOP_GETARG
2001 : (type == JOF_LOCAL)
2002 ? JSOP_GETLOCAL
2003 : JSOP_NAME;
2004
2005 JS_ASSERT(js_CodeSpec[op].nuses >= 0);
2006 i = nuses - js_CodeSpec[op].nuses;
2007 while (--i >= 0)
2008 PopOff(ss, JSOP_NOP);
2009 } else {
2010 /*
2011 * We must replace the faulting pc's bytecode with a
2012 * corresponding JSOP_GET* code. For JSOP_SET{PROP,ELEM},
2013 * we must use the "2nd" form of JSOP_GET{PROP,ELEM}, to
2014 * throw away the assignment op's right-hand operand and
2015 * decompile it as if it were a GET of its left-hand
2016 * operand.
2017 */
2018 if (mode == JOF_PROP) {
2019 op = (JSOp) ((format & JOF_SET)
2020 ? JSOP_GETPROP2
2021 : JSOP_GETPROP);
2022 } else if (mode == JOF_ELEM) {
2023 op = (JSOp) ((format & JOF_SET)
2024 ? JSOP_GETELEM2
2025 : JSOP_GETELEM);
2026 } else {
2027 /*
2028 * Unknown mode (including mode 0) means that op is
2029 * uncategorized for our purposes, so we must write
2030 * per-op special case code here.
2031 */
2032 switch (op) {
2033 case JSOP_ENUMELEM:
2034 case JSOP_ENUMCONSTELEM:
2035 op = JSOP_GETELEM;
2036 break;
2037 #if JS_HAS_LVALUE_RETURN
2038 case JSOP_SETCALL:
2039 op = JSOP_CALL;
2040 break;
2041 #endif
2042 case JSOP_GETTHISPROP:
2043 /*
2044 * NB: JSOP_GETTHISPROP can't fail due to |this|
2045 * being null or undefined at runtime (beware that
2046 * this may change for ES4). Therefore any error
2047 * resulting from this op must be due to the value
2048 * of the property accessed via |this|, so do not
2049 * rewrite op to JSOP_THIS.
2050 *
2051 * The next two cases should not change op if
2052 * js_DecompileValueGenerator was called from the
2053 * the property getter. They should rewrite only
2054 * if the base object in the arg/var/local is null
2055 * or undefined. FIXME: bug 431569.
2056 */
2057 break;
2058 case JSOP_GETARGPROP:
2059 op = JSOP_GETARG;
2060 break;
2061 case JSOP_GETLOCALPROP:
2062 op = JSOP_GETLOCAL;
2063 break;
2064 default:
2065 LOCAL_ASSERT(0);
2066 }
2067 }
2068 }
2069 }
2070
2071 saveop = op;
2072 if (op >= JSOP_LIMIT) {
2073 switch (op) {
2074 case JSOP_GETPROP2:
2075 saveop = JSOP_GETPROP;
2076 break;
2077 case JSOP_GETELEM2:
2078 saveop = JSOP_GETELEM;
2079 break;
2080 default:;
2081 }
2082 }
2083 LOCAL_ASSERT(js_CodeSpec[saveop].length == oplen ||
2084 JOF_TYPE(format) == JOF_SLOTATOM);
2085
2086 jp->dvgfence = NULL;
2087 }
2088
2089 if (token) {
2090 switch (nuses) {
2091 case 2:
2092 sn = js_GetSrcNote(jp->script, pc);
2093 if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
2094 /*
2095 * Avoid over-parenthesizing y in x op= y based on its
2096 * expansion: x = x op y (replace y by z = w to see the
2097 * problem).
2098 */
2099 op = (JSOp) pc[oplen];
2100 rval = POP_STR();
2101 lval = POP_STR();
2102 /* Print only the right operand of the assignment-op. */
2103 todo = SprintCString(&ss->sprinter, rval);
2104 op = saveop;
2105 } else if (!inXML) {
2106 rval = POP_STR_PREC(cs->prec + !!(cs->format & JOF_LEFTASSOC));
2107 lval = POP_STR_PREC(cs->prec + !(cs->format & JOF_LEFTASSOC));
2108 todo = Sprint(&ss->sprinter, "%s %s %s",
2109 lval, token, rval);
2110 } else {
2111 /* In XML, just concatenate the two operands. */
2112 LOCAL_ASSERT(op == JSOP_ADD);
2113 rval = POP_STR();
2114 lval = POP_STR();
2115 todo = Sprint(&ss->sprinter, ss_format, lval, rval);
2116 }
2117 break;
2118
2119 case 1:
2120 rval = POP_STR();
2121 todo = Sprint(&ss->sprinter, ss_format, token, rval);
2122 break;
2123
2124 case 0:
2125 todo = SprintCString(&ss->sprinter, token);
2126 break;
2127
2128 default:
2129 todo = -2;
2130 break;
2131 }
2132 } else {
2133 switch (op) {
2134 case JSOP_NOP:
2135 /*
2136 * Check for a do-while loop, a for-loop with an empty
2137 * initializer part, a labeled statement, a function
2138 * definition, or try/finally.
2139 */
2140 sn = js_GetSrcNote(jp->script, pc);
2141 todo = -2;
2142 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
2143 case SRC_WHILE:
2144 ++pc;
2145 tail = js_GetSrcNoteOffset(sn, 0) - 1;
2146 LOCAL_ASSERT(pc[tail] == JSOP_IFNE ||
2147 pc[tail] == JSOP_IFNEX);
2148 js_printf(jp, "\tdo {\n");
2149 jp->indent += 4;
2150 DECOMPILE_CODE(pc, tail);
2151 jp->indent -= 4;
2152 js_printf(jp, "\t} while (%s);\n", POP_COND_STR());
2153 pc += tail;
2154 len = js_CodeSpec[*pc].length;
2155 todo = -2;
2156 break;
2157
2158 case SRC_FOR:
2159 rval = "";
2160
2161 do_forloop:
2162 JS_ASSERT(SN_TYPE(sn) == SRC_FOR);
2163
2164 /* Skip the JSOP_NOP or JSOP_POP bytecode. */
2165 pc += JSOP_NOP_LENGTH;
2166
2167 /* Get the cond, next, and loop-closing tail offsets. */
2168 cond = js_GetSrcNoteOffset(sn, 0);
2169 next = js_GetSrcNoteOffset(sn, 1);
2170 tail = js_GetSrcNoteOffset(sn, 2);
2171
2172 /*
2173 * If this loop has a condition, then pc points at a goto
2174 * targeting the condition.
2175 */
2176 pc2 = pc;
2177 if (cond != tail) {
2178 LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX);
2179 pc2 += (*pc == JSOP_GOTO) ? JSOP_GOTO_LENGTH : JSOP_GOTOX_LENGTH;
2180 }
2181 LOCAL_ASSERT(tail + GetJumpOffset(pc+tail, pc+tail) == pc2 - pc);
2182
2183 /* Print the keyword and the possibly empty init-part. */
2184 js_printf(jp, "\tfor (%s;", rval);
2185
2186 if (cond != tail) {
2187 /* Decompile the loop condition. */
2188 DECOMPILE_CODE(pc + cond, tail - cond);
2189 js_printf(jp, " %s", POP_STR());
2190 }
2191
2192 /* Need a semicolon whether or not there was a cond. */
2193 js_puts(jp, ";");
2194
2195 if (next != cond) {
2196 /*
2197 * Decompile the loop updater. It may end in a JSOP_POP
2198 * that we skip; or in a JSOP_POPN that we do not skip,
2199 * followed by a JSOP_NOP (skipped as if it's a POP).
2200 * We cope with the difference between these two cases
2201 * by checking for stack imbalance and popping if there
2202 * is an rval.
2203 */
2204 uintN saveTop = ss->top;
2205
2206 DECOMPILE_CODE(pc + next, cond - next - JSOP_POP_LENGTH);
2207 LOCAL_ASSERT(ss->top - saveTop <= 1U);
2208 rval = (ss->top == saveTop)
2209 ? ss->sprinter.base + ss->sprinter.offset
2210 : POP_STR();
2211 js_printf(jp, " %s", rval);
2212 }
2213
2214 /* Do the loop body. */
2215 js_printf(jp, ") {\n");
2216 jp->indent += 4;
2217 next -= pc2 - pc;
2218 DECOMPILE_CODE(pc2, next);
2219 jp->indent -= 4;
2220 js_printf(jp, "\t}\n");
2221
2222 /* Set len so pc skips over the entire loop. */
2223 len = tail + js_CodeSpec[pc[tail]].length;
2224 break;
2225
2226 case SRC_LABEL:
2227 GET_SOURCE_NOTE_ATOM(sn, atom);
2228 jp->indent -= 4;
2229 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2230 if (!rval)
2231 return NULL;
2232 RETRACT(&ss->sprinter, rval);
2233 js_printf(jp, "\t%s:\n", rval);
2234 jp->indent += 4;
2235 break;
2236
2237 case SRC_LABELBRACE:
2238 GET_SOURCE_NOTE_ATOM(sn, atom);
2239 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2240 if (!rval)
2241 return NULL;
2242 RETRACT(&ss->sprinter, rval);
2243 js_printf(jp, "\t%s: {\n", rval);
2244 jp->indent += 4;
2245 break;
2246
2247 case SRC_ENDBRACE:
2248 jp->indent -= 4;
2249 js_printf(jp, "\t}\n");
2250 break;
2251
2252 case SRC_FUNCDEF:
2253 fun = jp->script->getFunction(js_GetSrcNoteOffset(sn, 0));
2254 do_function:
2255 js_puts(jp, "\n");
2256 jp2 = JS_NEW_PRINTER(cx, "nested_function", fun,
2257 jp->indent, jp->pretty);
2258 if (!jp2)
2259 return NULL;
2260 ok = js_DecompileFunction(jp2);
2261 if (ok && jp2->sprinter.base)
2262 js_puts(jp, jp2->sprinter.base);
2263 js_DestroyPrinter(jp2);
2264 if (!ok)
2265 return NULL;
2266 js_puts(jp, "\n\n");
2267 break;
2268
2269 case SRC_BRACE:
2270 js_printf(jp, "\t{\n");
2271 jp->indent += 4;
2272 len = js_GetSrcNoteOffset(sn, 0);
2273 DECOMPILE_CODE(pc + oplen, len - oplen);
2274 jp->indent -= 4;
2275 js_printf(jp, "\t}\n");
2276 break;
2277
2278 default:;
2279 }
2280 break;
2281
2282 case JSOP_PUSH:
2283 #if JS_HAS_DESTRUCTURING
2284 sn = js_GetSrcNote(jp->script, pc);
2285 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
2286 pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo);
2287 if (!pc)
2288 return NULL;
2289 LOCAL_ASSERT(*pc == JSOP_POPN);
2290 len = oplen = JSOP_POPN_LENGTH;
2291 goto end_groupassignment;
2292 }
2293 #endif
2294 /* FALL THROUGH */
2295
2296 case JSOP_BINDNAME:
2297 todo = Sprint(&ss->sprinter, "");
2298 break;
2299
2300 case JSOP_TRY:
2301 js_printf(jp, "\ttry {\n");
2302 jp->indent += 4;
2303 todo = -2;
2304 break;
2305
2306 case JSOP_FINALLY:
2307 jp->indent -= 4;
2308 js_printf(jp, "\t} finally {\n");
2309 jp->indent += 4;
2310
2311 /*
2312 * We push push the pair of exception/restsub cookies to
2313 * simulate the effects [gosub] or control transfer during
2314 * exception capturing on the stack.
2315 */
2316 todo = Sprint(&ss->sprinter, exception_cookie);
2317 if (todo < 0 || !PushOff(ss, todo, op))
2318 return NULL;
2319 todo = Sprint(&ss->sprinter, retsub_pc_cookie);
2320 break;
2321
2322 case JSOP_RETSUB:
2323 rval = POP_STR();
2324 LOCAL_ASSERT(strcmp(rval, retsub_pc_cookie) == 0);
2325 lval = POP_STR();
2326 LOCAL_ASSERT(strcmp(lval, exception_cookie) == 0);
2327 todo = -2;
2328 break;
2329
2330 case JSOP_GOSUB:
2331 case JSOP_GOSUBX:
2332 /*
2333 * JSOP_GOSUB and GOSUBX have no effect on the decompiler's
2334 * string stack because the next op in bytecode order finds
2335 * the stack balanced by a JSOP_RETSUB executed elsewhere.
2336 */
2337 todo = -2;
2338 break;
2339
2340 case JSOP_POPN:
2341 {
2342 uintN newtop, oldtop;
2343
2344 /*
2345 * The compiler models operand stack depth and fixes the stack
2346 * pointer on entry to a catch clause based on its depth model.
2347 * The decompiler must match the code generator's model, which
2348 * is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops.
2349 */
2350 oldtop = ss->top;
2351 newtop = oldtop - GET_UINT16(pc);
2352 LOCAL_ASSERT(newtop <= oldtop);
2353 todo = -2;
2354
2355 sn = js_GetSrcNote(jp->script, pc);
2356 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
2357 break;
2358 #if JS_HAS_DESTRUCTURING
2359 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
2360 todo = Sprint(&ss->sprinter, "%s[] = [",
2361 VarPrefix(sn));
2362 if (todo < 0)
2363 return NULL;
2364 for (uintN i = newtop; i < oldtop; i++) {
2365 rval = OFF2STR(&ss->sprinter, ss->offsets[i]);
2366 if (Sprint(&ss->sprinter, ss_format,
2367 (i == newtop) ? "" : ", ",
2368 (i == oldtop - 1 && *rval == '\0')
2369 ? ", " : rval) < 0) {
2370 return NULL;
2371 }
2372 }
2373 if (SprintPut(&ss->sprinter, "]", 1) < 0)
2374 return NULL;
2375
2376 /*
2377 * If this is an empty group assignment, we have no stack
2378 * budget into which we can push our result string. Adjust
2379 * ss->sprinter.offset so that our consumer can find the
2380 * empty group assignment decompilation.
2381 */
2382 if (newtop == oldtop) {
2383 ss->sprinter.offset = todo;
2384 } else {
2385 /*
2386 * Kill newtop before the end_groupassignment: label by
2387 * retracting/popping early. Control will either jump
2388 * to do_forloop: or do_letheadbody: or else break from
2389 * our case JSOP_POPN: after the switch (*pc2) below.
2390 */
2391 LOCAL_ASSERT(newtop < oldtop);
2392 ss->sprinter.offset = GetOff(ss, newtop);
2393 ss->top = newtop;
2394 }
2395
2396 end_groupassignment:
2397 LOCAL_ASSERT(*pc == JSOP_POPN);
2398
2399 /*
2400 * Thread directly to the next opcode if we can, to handle
2401 * the special cases of a group assignment in the first or
2402 * last part of a for(;;) loop head, or in a let block or
2403 * expression head.
2404 *
2405 * NB: todo at this point indexes space in ss->sprinter
2406 * that is liable to be overwritten. The code below knows
2407 * exactly how long rval lives, or else copies it down via
2408 * SprintCString.
2409 */
2410 rval = OFF2STR(&ss->sprinter, todo);
2411 todo = -2;
2412 pc2 = pc + oplen;
2413 if (*pc2 == JSOP_NOP) {
2414 sn = js_GetSrcNote(jp->script, pc2);
2415 if (sn) {
2416 if (SN_TYPE(sn) == SRC_FOR) {
2417 op = JSOP_NOP;
2418 pc = pc2;
2419 goto do_forloop;
2420 }
2421
2422 if (SN_TYPE(sn) == SRC_DECL) {
2423 if (ss->top == StackDepth(jp->script)) {
2424 /*
2425 * This must be an empty destructuring
2426 * in the head of a let whose body block
2427 * is also empty.
2428 */
2429 pc = pc2 + JSOP_NOP_LENGTH;
2430 len = js_GetSrcNoteOffset(sn, 0);
2431 LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCK);
2432 js_printf(jp, "\tlet (%s) {\n", rval);
2433 js_printf(jp, "\t}\n");
2434 break;
2435 }
2436 todo = SprintCString(&ss->sprinter, rval);
2437 if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
2438 return NULL;
2439 op = JSOP_POP;
2440 pc = pc2 + JSOP_NOP_LENGTH;
2441 goto do_letheadbody;
2442 }
2443 } else {
2444 /*
2445 * An unnannotated NOP following a POPN must be the
2446 * third part of for(;;) loop head. If the POPN's
2447 * immediate operand is 0, then we may have no slot
2448 * on the sprint-stack in which to push our result
2449 * string. In this case the result can be recovered
2450 * at ss->sprinter.base + ss->sprinter.offset.
2451 */
2452 if (GET_UINT16(pc) == 0)
2453 break;
2454 todo = SprintCString(&ss->sprinter, rval);
2455 saveop = JSOP_NOP;
2456 }
2457 }
2458
2459 /*
2460 * If control flow reaches this point with todo still -2,
2461 * just print rval as an expression statement.
2462 */
2463 if (todo == -2)
2464 js_printf(jp, "\t%s;\n", rval);
2465 break;
2466 }
2467 #endif
2468 if (newtop < oldtop) {
2469 ss->sprinter.offset = GetOff(ss, newtop);
2470 ss->top = newtop;
2471 }
2472 break;
2473 }
2474
2475 case JSOP_EXCEPTION:
2476 /* The catch decompiler handles this op itself. */
2477 LOCAL_ASSERT(JS_FALSE);
2478 break;
2479
2480 case JSOP_POP:
2481 /*
2482 * By default, do not automatically parenthesize when popping
2483 * a stacked expression decompilation. We auto-parenthesize
2484 * only when JSOP_POP is annotated with SRC_PCDELTA, meaning
2485 * comma operator.
2486 */
2487 op = JSOP_POPV;
2488 /* FALL THROUGH */
2489
2490 case JSOP_POPV:
2491 sn = js_GetSrcNote(jp->script, pc);
2492 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
2493 case SRC_FOR:
2494 /* Force parens around 'in' expression at 'for' front. */
2495 if (ss->opcodes[ss->top-1] == JSOP_IN)
2496 op = JSOP_LSH;
2497 rval = POP_STR();
2498 todo = -2;
2499 goto do_forloop;
2500
2501 case SRC_PCDELTA:
2502 /* Comma operator: use JSOP_POP for correct precedence. */
2503 op = JSOP_POP;
2504
2505 /* Pop and save to avoid blowing stack depth budget. */
2506 lval = JS_strdup(cx, POP_STR());
2507 if (!lval)
2508 return NULL;
2509
2510 /*
2511 * The offset tells distance to the end of the right-hand
2512 * operand of the comma operator.
2513 */
2514 done = pc + len;
2515 pc += js_GetSrcNoteOffset(sn, 0);
2516 len = 0;
2517
2518 if (!Decompile(ss, done, pc - done, JSOP_POP)) {
2519 cx->free((char *)lval);
2520 return NULL;
2521 }
2522
2523 /* Pop Decompile result and print comma expression. */
2524 rval = POP_STR();
2525 todo = Sprint(&ss->sprinter, "%s, %s", lval, rval);
2526 cx->free((char *)lval);
2527 break;
2528
2529 case SRC_HIDDEN:
2530 /* Hide this pop, it's from a goto in a with or for/in. */
2531 todo = -2;
2532 break;
2533
2534 case SRC_DECL:
2535 /* This pop is at the end of the let block/expr head. */
2536 pc += JSOP_POP_LENGTH;
2537 #if JS_HAS_DESTRUCTURING
2538 do_letheadbody:
2539 #endif
2540 len = js_GetSrcNoteOffset(sn, 0);
2541 if (pc[len] == JSOP_LEAVEBLOCK) {
2542 js_printf(jp, "\tlet (%s) {\n", POP_STR());
2543 jp->indent += 4;
2544 DECOMPILE_CODE(pc, len);
2545 jp->indent -= 4;
2546 js_printf(jp, "\t}\n");
2547 todo = -2;
2548 } else {
2549 LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCKEXPR);
2550
2551 lval = JS_strdup(cx, PopStr(ss, JSOP_NOP));
2552 if (!lval)
2553 return NULL;
2554
2555 /* Set saveop to reflect what we will push. */
2556 saveop = JSOP_LEAVEBLOCKEXPR;
2557 if (!Decompile(ss, pc, len, saveop)) {
2558 cx->free((char *)lval);
2559 return NULL;
2560 }
2561 rval = PopStr(ss, JSOP_SETNAME);
2562 todo = Sprint(&ss->sprinter,
2563 (*rval == '{')
2564 ? "let (%s) (%s)"
2565 : "let (%s) %s",
2566 lval, rval);
2567 cx->free((char *)lval);
2568 }
2569 break;
2570
2571 default:
2572 /* Turn off parens around a yield statement. */
2573 if (ss->opcodes[ss->top-1] == JSOP_YIELD)
2574 op = JSOP_NOP;
2575
2576 rval = POP_STR();
2577
2578 /*
2579 * Don't emit decompiler-pushed strings that are not
2580 * handled by other opcodes. They are pushed onto the
2581 * stack to help model the interpreter stack and should
2582 * not appear in the decompiler's output.
2583 */
2584 if (*rval != '\0' && (rval[0] != '/' || rval[1] != '*')) {
2585 js_printf(jp,
2586 (*rval == '{' ||
2587 (strncmp(rval, js_function_str, 8) == 0 &&
2588 rval[8] == ' '))
2589 ? "\t(%s);\n"
2590 : "\t%s;\n",
2591 rval);
2592 } else {
2593 LOCAL_ASSERT(*rval == '\0' ||
2594 strcmp(rval, exception_cookie) == 0);
2595 }
2596 todo = -2;
2597 break;
2598 }
2599 sn = NULL;
2600 break;
2601
2602 case JSOP_ENTERWITH:
2603 LOCAL_ASSERT(!js_GetSrcNote(jp->script, pc));
2604 rval = POP_STR();
2605 js_printf(jp, "\twith (%s) {\n", rval);
2606 jp->indent += 4;
2607 todo = Sprint(&ss->sprinter, with_cookie);
2608 break;
2609
2610 case JSOP_LEAVEWITH:
2611 sn = js_GetSrcNote(jp->script, pc);
2612 todo = -2;
2613 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
2614 break;
2615 rval = POP_STR();
2616 LOCAL_ASSERT(strcmp(rval, with_cookie) == 0);
2617 jp->indent -= 4;
2618 js_printf(jp, "\t}\n");
2619 break;
2620
2621 case JSOP_ENTERBLOCK:
2622 {
2623 JSAtom **atomv, *smallv[5];
2624 JSScopeProperty *sprop;
2625
2626 LOAD_OBJECT(0);
2627 argc = OBJ_BLOCK_COUNT(cx, obj);
2628 if ((size_t)argc <= JS_ARRAY_LENGTH(smallv)) {
2629 atomv = smallv;
2630 } else {
2631 atomv = (JSAtom **) cx->malloc(argc * sizeof(JSAtom *));
2632 if (!atomv)
2633 return NULL;
2634 }
2635
2636 MUST_FLOW_THROUGH("enterblock_out");
2637 #define LOCAL_ASSERT_OUT(expr) LOCAL_ASSERT_CUSTOM(expr, ok = JS_FALSE; \
2638 goto enterblock_out)
2639 for (sprop = OBJ_SCOPE(obj)->lastProp; sprop;
2640 sprop = sprop->parent) {
2641 if (!(sprop->flags & SPROP_HAS_SHORTID))
2642 continue;
2643 LOCAL_ASSERT_OUT(sprop->shortid < argc);
2644 atomv[sprop->shortid] = JSID_TO_ATOM(sprop->id);
2645 }
2646 ok = JS_TRUE;
2647 for (i = 0; i < argc; i++) {
2648 atom = atomv[i];
2649 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2650 if (!rval ||
2651 !PushOff(ss, STR2OFF(&ss->sprinter, rval), op)) {
2652 ok = JS_FALSE;
2653 goto enterblock_out;
2654 }
2655 }
2656
2657 sn = js_GetSrcNote(jp->script, pc);
2658 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
2659 #if JS_HAS_BLOCK_SCOPE
2660 case SRC_BRACE:
2661 js_printf(jp, "\t{\n");
2662 jp->indent += 4;
2663 len = js_GetSrcNoteOffset(sn, 0);
2664 ok = Decompile(ss, pc + oplen, len - oplen, JSOP_NOP)
2665 != NULL;
2666 if (!ok)
2667 goto enterblock_out;
2668 jp->indent -= 4;
2669 js_printf(jp, "\t}\n");
2670 break;
2671 #endif
2672
2673 case SRC_CATCH:
2674 jp->indent -= 4;
2675 js_printf(jp, "\t} catch (");
2676
2677 pc2 = pc;
2678 pc += oplen;
2679 LOCAL_ASSERT_OUT(*pc == JSOP_EXCEPTION);
2680 pc += JSOP_EXCEPTION_LENGTH;
2681 todo = Sprint(&ss->sprinter, exception_cookie);
2682 if (todo < 0 || !PushOff(ss, todo, JSOP_EXCEPTION)) {
2683 ok = JS_FALSE;
2684 goto enterblock_out;
2685 }
2686
2687 if (*pc == JSOP_DUP) {
2688 sn2 = js_GetSrcNote(jp->script, pc);
2689 if (!sn2 || SN_TYPE(sn2) != SRC_DESTRUCT) {
2690 /*
2691 * This is a dup to save the exception for later.
2692 * It is emitted only when the catch head contains
2693 * an exception guard.
2694 */
2695 LOCAL_ASSERT_OUT(js_GetSrcNoteOffset(sn, 0) != 0);
2696 pc += JSOP_DUP_LENGTH;
2697 todo = Sprint(&ss->sprinter, exception_cookie);
2698 if (todo < 0 ||
2699 !PushOff(ss, todo, JSOP_EXCEPTION)) {
2700 ok = JS_FALSE;
2701 goto enterblock_out;
2702 }
2703 }
2704 }
2705
2706 #if JS_HAS_DESTRUCTURING
2707 if (*pc == JSOP_DUP) {
2708 pc = DecompileDestructuring(ss, pc, endpc);
2709 if (!pc) {
2710 ok = JS_FALSE;
2711 goto enterblock_out;
2712 }
2713 LOCAL_ASSERT_OUT(*pc == JSOP_POP);
2714 pc += JSOP_POP_LENGTH;
2715 lval = PopStr(ss, JSOP_NOP);
2716 js_puts(jp, lval);
2717 } else {
2718 #endif
2719 LOCAL_ASSERT_OUT(*pc == JSOP_SETLOCALPOP);
2720 i = GET_SLOTNO(pc) - jp->script->nfixed;
2721 pc += JSOP_SETLOCALPOP_LENGTH;
2722 atom = atomv[i - OBJ_BLOCK_DEPTH(cx, obj)];
2723 str = ATOM_TO_STRING(atom);
2724 if (!QuoteString(&jp->sprinter, str, 0)) {
2725 ok = JS_FALSE;
2726 goto enterblock_out;
2727 }
2728 #if JS_HAS_DESTRUCTURING
2729 }
2730 #endif
2731
2732 /*
2733 * Pop the exception_cookie (or its dup in the case of a
2734 * guarded catch head) off the stack now.
2735 */
2736 rval = PopStr(ss, JSOP_NOP);
2737 LOCAL_ASSERT_OUT(strcmp(rval, exception_cookie) == 0);
2738
2739 len = js_GetSrcNoteOffset(sn, 0);
2740 if (len) {
2741 len -= pc - pc2;
2742 LOCAL_ASSERT_OUT(len > 0);
2743 js_printf(jp, " if ");
2744 ok = Decompile(ss, pc, len, JSOP_NOP) != NULL;
2745 if (!ok)
2746 goto enterblock_out;
2747 js_printf(jp, "%s", POP_STR());
2748 pc += len;
2749 LOCAL_ASSERT_OUT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
2750 pc += js_CodeSpec[*pc].length;
2751 }
2752
2753 js_printf(jp, ") {\n");
2754 jp->indent += 4;
2755 len = 0;
2756 break;
2757 default:
2758 break;
2759 }
2760
2761 todo = -2;
2762
2763 #undef LOCAL_ASSERT_OUT
2764 enterblock_out:
2765 if (atomv != smallv)
2766 cx->free(atomv);
2767 if (!ok)
2768 return NULL;
2769 }
2770 break;
2771
2772 case JSOP_LEAVEBLOCK:
2773 case JSOP_LEAVEBLOCKEXPR:
2774 {
2775 uintN top, depth;
2776
2777 sn = js_GetSrcNote(jp->script, pc);
2778 todo = -2;
2779 if (op == JSOP_LEAVEBLOCKEXPR) {
2780 LOCAL_ASSERT(SN_TYPE(sn) == SRC_PCBASE);
2781 rval = POP_STR();
2782 } else if (sn) {
2783 LOCAL_ASSERT(op == JSOP_LEAVEBLOCK);
2784 if (SN_TYPE(sn) == SRC_HIDDEN)
2785 break;
2786
2787 /*
2788 * This JSOP_LEAVEBLOCK must be for a catch block. If sn's
2789 * offset does not equal the model stack depth, there must
2790 * be a copy of the exception value on the stack due to a
2791 * catch guard (see above, the JSOP_ENTERBLOCK + SRC_CATCH
2792 * case code).
2793 */
2794 LOCAL_ASSERT(SN_TYPE(sn) == SRC_CATCH);
2795 if ((uintN)js_GetSrcNoteOffset(sn, 0) != ss->top) {
2796 LOCAL_ASSERT((uintN)js_GetSrcNoteOffset(sn, 0)
2797 == ss->top - 1);
2798 rval = POP_STR();
2799 LOCAL_ASSERT(strcmp(rval, exception_cookie) == 0);
2800 }
2801 }
2802 top = ss->top;
2803 depth = GET_UINT16(pc);
2804 LOCAL_ASSERT(top >= depth);
2805 top -= depth;
2806 ss->top = top;
2807 ss->sprinter.offset = GetOff(ss, top);
2808 if (op == JSOP_LEAVEBLOCKEXPR)
2809 todo = SprintCString(&ss->sprinter, rval);
2810 break;
2811 }
2812
2813 case JSOP_GETUPVAR:
2814 case JSOP_CALLUPVAR:
2815 case JSOP_GETUPVAR_DBG:
2816 case JSOP_CALLUPVAR_DBG:
2817 case JSOP_GETDSLOT:
2818 case JSOP_CALLDSLOT:
2819 {
2820 if (!jp->fun) {
2821 JS_ASSERT(jp->script->flags & JSSF_SAVED_CALLER_FUN);
2822 jp->fun = jp->script->getFunction(0);
2823 }
2824
2825 if (!jp->localNames)
2826 jp->localNames = js_GetLocalNameArray(cx, jp->fun, &jp->pool);
2827
2828 uintN index = GET_UINT16(pc);
2829 if (index < jp->fun->u.i.nupvars) {
2830 index += jp->fun->countArgsAndVars();
2831 } else {
2832 JSUpvarArray *uva;
2833 #ifdef DEBUG
2834 /*
2835 * We must be in an eval called from jp->fun, where
2836 * jp->script is the eval-compiled script.
2837 *
2838 * However, it's possible that a js_Invoke already
2839 * pushed a frame trying to call js_Construct on an
2840 * object that's not a constructor, causing us to be
2841 * called with an intervening frame on the stack.
2842 */
2843 JSStackFrame *fp = js_GetTopStackFrame(cx);
2844 if (fp) {
2845 while (!(fp->flags & JSFRAME_EVAL))
2846 fp = fp->down;
2847 JS_ASSERT(fp->script == jp->script);
2848 JS_ASSERT(fp->down->fun == jp->fun);
2849 JS_ASSERT(FUN_INTERPRETED(jp->fun));
2850 JS_ASSERT(jp->script != jp->fun->u.i.script);
2851 JS_ASSERT(jp->script->upvarsOffset != 0);
2852 }
2853 #endif
2854 uva = jp->script->upvars();
2855 index = UPVAR_FRAME_SLOT(uva->vector[index]);
2856 }
2857 atom = GetArgOrVarAtom(jp, index);
2858 goto do_name;
2859 }
2860
2861 case JSOP_CALLLOCAL:
2862 case JSOP_GETLOCAL:
2863 if (IsVarSlot(jp, pc, &i)) {
2864 atom = GetArgOrVarAtom(jp, i);
2865 LOCAL_ASSERT(atom);
2866 goto do_name;
2867 }
2868 LOCAL_ASSERT((uintN)i < ss->top);
2869 sn = js_GetSrcNote(jp->script, pc);
2870
2871 #if JS_HAS_DESTRUCTURING
2872 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
2873 /*
2874 * Distinguish a js_DecompileValueGenerator call that
2875 * targets op alone, from decompilation of a full group
2876 * assignment sequence, triggered by SRC_GROUPASSIGN
2877 * annotating the first JSOP_GETLOCAL in the sequence.
2878 */
2879 if (endpc - pc > JSOP_GETLOCAL_LENGTH || pc > startpc) {
2880 pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo);
2881 if (!pc)
2882 return NULL;
2883 LOCAL_ASSERT(*pc == JSOP_POPN);
2884 len = oplen = JSOP_POPN_LENGTH;
2885 goto end_groupassignment;
2886 }
2887
2888 /* Null sn to prevent bogus VarPrefix'ing below. */
2889 sn = NULL;
2890 }
2891 #endif
2892
2893 rval = GetLocal(ss, i);
2894 todo = Sprint(&ss->sprinter, ss_format, VarPrefix(sn), rval);
2895 break;
2896
2897 case JSOP_SETLOCAL:
2898 case JSOP_SETLOCALPOP:
2899 if (IsVarSlot(jp, pc, &i)) {
2900 atom = GetArgOrVarAtom(jp, i);
2901 LOCAL_ASSERT(atom);
2902 goto do_setname;
2903 }
2904 lval = GetLocal(ss, i);
2905 rval = POP_STR();
2906 goto do_setlval;
2907
2908 case JSOP_INCLOCAL:
2909 case JSOP_DECLOCAL:
2910 if (IsVarSlot(jp, pc, &i)) {
2911 atom = GetArgOrVarAtom(jp, i);
2912 LOCAL_ASSERT(atom);
2913 goto do_incatom;
2914 }
2915 lval = GetLocal(ss, i);
2916 goto do_inclval;
2917
2918 case JSOP_LOCALINC:
2919 case JSOP_LOCALDEC:
2920 if (IsVarSlot(jp, pc, &i)) {
2921 atom = GetArgOrVarAtom(jp, i);
2922 LOCAL_ASSERT(atom);
2923 goto do_atominc;
2924 }
2925 lval = GetLocal(ss, i);
2926 goto do_lvalinc;
2927
2928 case JSOP_RETRVAL:
2929 todo = -2;
2930 break;
2931
2932 case JSOP_RETURN:
2933 LOCAL_ASSERT(jp->fun);
2934 fun = jp->fun;
2935 if (fun->flags & JSFUN_EXPR_CLOSURE) {
2936 /* Turn on parens around comma-expression here. */
2937 op = JSOP_SETNAME;
2938 rval = POP_STR();
2939 js_printf(jp, (*rval == '{') ? "(%s)%s" : ss_format,
2940 rval,
2941 ((fun->flags & JSFUN_LAMBDA) || !fun->atom)
2942 ? ""
2943 : ";");
2944 todo = -2;
2945 break;
2946 }
2947 /* FALL THROUGH */
2948
2949 case JSOP_SETRVAL:
2950 rval = POP_STR();
2951 if (*rval != '\0')
2952 js_printf(jp, "\t%s %s;\n", js_return_str, rval);
2953 else
2954 js_printf(jp, "\t%s;\n", js_return_str);
2955 todo = -2;
2956 break;
2957
2958 #if JS_HAS_GENERATORS
2959 case JSOP_YIELD:
2960 #if JS_HAS_GENERATOR_EXPRS
2961 if (!ss->inGenExp || !(sn = js_GetSrcNote(jp->script, pc)))
2962 #endif
2963 {
2964 /* Turn off most parens. */
2965 op = JSOP_SETNAME;
2966 rval = POP_STR();
2967 todo = (*rval != '\0')
2968 ? Sprint(&ss->sprinter,
2969 (strncmp(rval, js_yield_str, 5) == 0 &&
2970 (rval[5] == ' ' || rval[5] == '\0'))
2971 ? "%s (%s)"
2972 : "%s %s",
2973 js_yield_str, rval)
2974 : SprintCString(&ss->sprinter, js_yield_str);
2975 break;
2976 }
2977
2978 #if JS_HAS_GENERATOR_EXPRS
2979 LOCAL_ASSERT(SN_TYPE(sn) == SRC_HIDDEN);
2980 /* FALL THROUGH */
2981 #endif
2982
2983 case JSOP_ARRAYPUSH:
2984 {
2985 uintN pos, forpos;
2986 ptrdiff_t start;
2987
2988 /* Turn off most parens. */
2989 op = JSOP_SETNAME;
2990
2991 /* Pop the expression being pushed or yielded. */
2992 rval = POP_STR();
2993
2994 /*
2995 * Skip the for loop head stacked by JSOP_FORLOCAL until we hit
2996 * a block local slot (note empty destructuring patterns result
2997 * in unit-count blocks).
2998 */
2999 pos = ss->top;
3000 while (pos != 0) {
3001 op = (JSOp) ss->opcodes[--pos];
3002 if (op == JSOP_ENTERBLOCK)
3003 break;
3004 }
3005 JS_ASSERT(op == JSOP_ENTERBLOCK);
3006
3007 /*
3008 * Here, forpos must index the space before the left-most |for|
3009 * in the single string of accumulated |for| heads and optional
3010 * final |if (condition)|.
3011 */
3012 forpos = pos + 2;
3013 LOCAL_ASSERT(forpos < ss->top);
3014
3015 /*
3016 * Now move pos downward over the block's local slots. Even an
3017 * empty destructuring pattern has one (dummy) local.
3018 */
3019 while (ss->opcodes[pos] == JSOP_ENTERBLOCK) {
3020 if (pos == 0)
3021 break;
3022 --pos;
3023 }
3024
3025 #if JS_HAS_GENERATOR_EXPRS
3026 if (saveop == JSOP_YIELD) {
3027 /*
3028 * Generator expression: decompile just rval followed by
3029 * the string starting at forpos. Leave the result string
3030 * in ss->offsets[0] so it can be recovered by our caller
3031 * (the JSOP_ANONFUNOBJ with SRC_GENEXP case). Bump the
3032 * top of stack to balance yield, which is an expression
3033 * (so has neutral stack balance).
3034 */
3035 LOCAL_ASSERT(pos == 0);
3036 xval = OFF2STR(&ss->sprinter, ss->offsets[forpos]);
3037 ss->sprinter.offset = PAREN_SLOP;
3038 todo = Sprint(&ss->sprinter, ss_format, rval, xval);
3039 if (todo < 0)
3040 return NULL;
3041 ss->offsets[0] = todo;
3042 ++ss->top;
3043 return pc;
3044 }
3045 #endif /* JS_HAS_GENERATOR_EXPRS */
3046
3047 /*
3048 * Array comprehension: retract the sprinter to the beginning
3049 * of the array initialiser and decompile "[<rval> for ...]".
3050 */
3051 JS_ASSERT(jp->script->nfixed + pos == GET_UINT16(pc));
3052 LOCAL_ASSERT(ss->opcodes[pos] == JSOP_NEWINIT);
3053
3054 start = ss->offsets[pos];
3055 LOCAL_ASSERT(ss->sprinter.base[start] == '[' ||
3056 ss->sprinter.base[start] == '#');
3057 LOCAL_ASSERT(forpos < ss->top);
3058 xval = OFF2STR(&ss->sprinter, ss->offsets[forpos]);
3059 lval = OFF2STR(&ss->sprinter, start);
3060 RETRACT(&ss->sprinter, lval);
3061
3062 todo = Sprint(&ss->sprinter, sss_format, lval, rval, xval);
3063 if (todo < 0)
3064 return NULL;
3065 ss->offsets[pos] = todo;
3066 todo = -2;
3067 break;
3068 }
3069 #endif /* JS_HAS_GENERATORS */
3070
3071 case JSOP_THROWING:
3072 todo = -2;
3073 break;
3074
3075 case JSOP_THROW:
3076 sn = js_GetSrcNote(jp->script, pc);
3077 todo = -2;
3078 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
3079 break;
3080 rval = POP_STR();
3081 js_printf(jp, "\t%s %s;\n", js_throw_str, rval);
3082 break;
3083
3084 case JSOP_ITER:
3085 foreach = (pc[1] & (JSITER_FOREACH | JSITER_KEYVALUE)) ==
3086 JSITER_FOREACH;
3087 todo = SprintCString(&ss->sprinter, iter_cookie);
3088 break;
3089
3090 case JSOP_NEXTITER:
3091 JS_NOT_REACHED("JSOP_NEXTITER");
3092 break;
3093
3094 case JSOP_ENDITER:
3095 sn = js_GetSrcNote(jp->script, pc);
3096 todo = -2;
3097 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
3098 break;
3099 (void) PopOff(ss, op);
3100 (void) PopOff(ss, op);
3101 break;
3102
3103 case JSOP_GOTO:
3104 case JSOP_GOTOX:
3105 sn = js_GetSrcNote(jp->script, pc);
3106 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
3107 case SRC_FOR_IN:
3108 /*
3109 * The loop back-edge carries +1 stack balance, for the
3110 * flag processed by JSOP_IFNE. We do not decompile the
3111 * JSOP_IFNE, and instead push the left-hand side of 'in'
3112 * after the loop edge in this stack slot (the JSOP_FOR*
3113 * opcodes' decompilers do this pushing).
3114 */
3115 cond = GetJumpOffset(pc, pc);
3116 next = js_GetSrcNoteOffset(sn, 0);
3117 tail = js_GetSrcNoteOffset(sn, 1);
3118 JS_ASSERT(pc[cond] == JSOP_NEXTITER);
3119 DECOMPILE_CODE(pc + oplen, next - oplen);
3120 lval = POP_STR();
3121 LOCAL_ASSERT(ss->top >= 2);
3122
3123 if (ss->inArrayInit || ss->inGenExp) {
3124 (void) PopOff(ss, JSOP_NOP);
3125 rval = TOP_STR();
3126 if (ss->top >= 2 && ss->opcodes[ss->top - 2] == JSOP_FORLOCAL) {
3127 ss->sprinter.offset = ss->offsets[ss->top - 1] - PAREN_SLOP;
3128 if (Sprint(&ss->sprinter, " %s (%s in %s)",
3129 foreach ? js_for_each_str : js_for_str,
3130 lval, rval) < 0) {
3131 return NULL;
3132 }
3133
3134 /*
3135 * Do not AddParentSlop here, as we will push the
3136 * top-most offset again, which will add paren slop
3137 * for us. We must push to balance the stack budget
3138 * when nesting for heads in a comprehension.
3139 */
3140 todo = ss->offsets[ss->top - 1];
3141 } else {
3142 todo = Sprint(&ss->sprinter, " %s (%s in %s)",
3143 foreach ? js_for_each_str : js_for_str,
3144 lval, rval);
3145 }
3146 if (todo < 0 || !PushOff(ss, todo, JSOP_FORLOCAL))
3147 return NULL;
3148 DECOMPILE_CODE(pc + next, cond - next);
3149 } else {
3150 /*
3151 * As above, rval or an extension of it must remain
3152 * stacked during loop body decompilation.
3153 */
3154 rval = GetStr(ss, ss->top - 2);
3155 js_printf(jp, "\t%s (%s in %s) {\n",
3156 foreach ? js_for_each_str : js_for_str,
3157 lval, rval);
3158 jp->indent += 4;
3159 DECOMPILE_CODE(pc + next, cond - next);
3160 jp->indent -= 4;
3161 js_printf(jp, "\t}\n");
3162 }
3163
3164 pc += tail;
3165 LOCAL_ASSERT(*pc == JSOP_IFNE || *pc == JSOP_IFNEX);
3166 len = js_CodeSpec[*pc].length;
3167 break;
3168
3169 case SRC_WHILE:
3170 cond = GetJumpOffset(pc, pc);
3171 tail = js_GetSrcNoteOffset(sn, 0);
3172 DECOMPILE_CODE(pc + cond, tail - cond);
3173 js_printf(jp, "\twhile (%s) {\n", POP_COND_STR());
3174 jp->indent += 4;
3175 DECOMPILE_CODE(pc + oplen, cond - oplen);
3176 jp->indent -= 4;
3177 js_printf(jp, "\t}\n");
3178 pc += tail;
3179 LOCAL_ASSERT(*pc == JSOP_IFNE || *pc == JSOP_IFNEX);
3180 len = js_CodeSpec[*pc].length;
3181 todo = -2;
3182 break;
3183
3184 case SRC_CONT2LABEL:
3185 GET_SOURCE_NOTE_ATOM(sn, atom);
3186 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3187 if (!rval)
3188 return NULL;
3189 RETRACT(&ss->sprinter, rval);
3190 js_printf(jp, "\tcontinue %s;\n", rval);
3191 break;
3192
3193 case SRC_CONTINUE:
3194 js_printf(jp, "\tcontinue;\n");
3195 break;
3196
3197 case SRC_BREAK2LABEL:
3198 GET_SOURCE_NOTE_ATOM(sn, atom);
3199 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3200 if (!rval)
3201 return NULL;
3202 RETRACT(&ss->sprinter, rval);
3203 js_printf(jp, "\tbreak %s;\n", rval);
3204 break;
3205
3206 case SRC_HIDDEN:
3207 break;
3208
3209 default:
3210 js_printf(jp, "\tbreak;\n");
3211 break;
3212 }
3213 todo = -2;
3214 break;
3215
3216 case JSOP_IFEQ:
3217 case JSOP_IFEQX:
3218 {
3219 JSBool elseif = JS_FALSE;
3220
3221 if_again:
3222 len = GetJumpOffset(pc, pc);
3223 sn = js_GetSrcNote(jp->script, pc);
3224
3225 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
3226 case SRC_IF:
3227 case SRC_IF_ELSE:
3228 rval = POP_COND_STR();
3229 if (ss->inArrayInit || ss->inGenExp) {
3230 LOCAL_ASSERT(SN_TYPE(sn) == SRC_IF);
3231 ss->sprinter.offset -= PAREN_SLOP;
3232 if (Sprint(&ss->sprinter, " if (%s)", rval) < 0)
3233 return NULL;
3234 AddParenSlop(ss);
3235 } else {
3236 js_printf(jp,
3237 elseif ? " if (%s) {\n" : "\tif (%s) {\n",
3238 rval);
3239 jp->indent += 4;
3240 }
3241
3242 if (SN_TYPE(sn) == SRC_IF) {
3243 DECOMPILE_CODE(pc + oplen, len - oplen);
3244 } else {
3245 LOCAL_ASSERT(!ss->inArrayInit && !ss->inGenExp);
3246 tail = js_GetSrcNoteOffset(sn, 0);
3247 DECOMPILE_CODE(pc + oplen, tail - oplen);
3248