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

Annotation of /trunk/js/jsopcode.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

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