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

Annotation of /trunk/js/jsopcode.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

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


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