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

Contents of /trunk/js/jsopcode.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 332 - (show annotations)
Thu Oct 23 19:03:33 2008 UTC (10 years, 10 months ago) by siliconforks
File size: 186696 byte(s)
Add SpiderMonkey from Firefox 3.1b1.

The following directories and files were removed:
correct/, correct.js
liveconnect/
nanojit/
t/
v8/
vprof/
xpconnect/
all JavaScript files (Y.js, call.js, if.js, math-partial-sums.js, md5.js, perfect.js, trace-test.js, trace.js)


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