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

Contents of /trunk/js/jsopcode.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 507 - (show annotations)
Sun Jan 10 07:23:34 2010 UTC (9 years, 11 months ago) by siliconforks
File size: 194364 byte(s)
Update SpiderMonkey from Firefox 3.6rc1.

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