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

Contents of /trunk/js/jsopcode.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 399 - (show annotations)
Tue Dec 9 03:37:47 2008 UTC (10 years, 11 months ago) by siliconforks
File size: 187625 byte(s)
Use SpiderMonkey from Firefox 3.1b2.

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