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

Annotation of /trunk/js/jsopcode.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 507 - (hide annotations)
Sun Jan 10 07:23:34 2010 UTC (9 years, 7 months ago) by siliconforks
File size: 194364 byte(s)
Update SpiderMonkey from Firefox 3.6rc1.

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