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

Annotation of /trunk/js/jsopcode.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

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