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

Annotation of /trunk/js/jsopcode.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 585 - (hide annotations)
Sun Sep 12 15:13:23 2010 UTC (9 years, 3 months ago) by siliconforks
File size: 194652 byte(s)
Update to SpiderMonkey from Firefox 3.6.9.

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