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

Contents of /trunk/js/jsopcode.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 460 - (show annotations)
Sat Sep 26 23:15:22 2009 UTC (9 years, 10 months ago) by siliconforks
File size: 193317 byte(s)
Upgrade to SpiderMonkey from Firefox 3.5.3.

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