1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set sw=4 ts=8 et tw=78:
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 "jsconfig.h"
62 #include "jsdbgapi.h"
63 #include "jsemit.h"
64 #include "jsfun.h"
65 #include "jslock.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
74 #if JS_HAS_DESTRUCTURING
75 # include "jsnum.h"
76 #endif
77
78 static const char js_incop_strs[][3] = {"++", "--"};
79
80 /* Pollute the namespace locally for MSVC Win16, but not for WatCom. */
81 #ifdef __WINDOWS_386__
82 #ifdef FAR
83 #undef FAR
84 #endif
85 #else /* !__WINDOWS_386__ */
86 #ifndef FAR
87 #define FAR
88 #endif
89 #endif /* !__WINDOWS_386__ */
90
91 const JSCodeSpec FAR js_CodeSpec[] = {
92 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
93 {name,token,length,nuses,ndefs,prec,format},
94 #include "jsopcode.tbl"
95 #undef OPDEF
96 };
97
98 uintN js_NumCodeSpecs = sizeof (js_CodeSpec) / sizeof js_CodeSpec[0];
99
100 /************************************************************************/
101
102 static ptrdiff_t
GetJumpOffset(jsbytecode * pc,jsbytecode * pc2)103 GetJumpOffset(jsbytecode *pc, jsbytecode *pc2)
104 {
105 uint32 type;
106
107 type = (js_CodeSpec[*pc].format & JOF_TYPEMASK);
108 if (JOF_TYPE_IS_EXTENDED_JUMP(type))
109 return GET_JUMPX_OFFSET(pc2);
110 return GET_JUMP_OFFSET(pc2);
111 }
112
113 #ifdef DEBUG
114
115 JS_FRIEND_API(JSBool)
js_Disassemble(JSContext * cx,JSScript * script,JSBool lines,FILE * fp)116 js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp)
117 {
118 jsbytecode *pc, *end;
119 uintN len;
120
121 pc = script->code;
122 end = pc + script->length;
123 while (pc < end) {
124 if (pc == script->main)
125 fputs("main:\n", fp);
126 len = js_Disassemble1(cx, script, pc,
127 PTRDIFF(pc, script->code, jsbytecode),
128 lines, fp);
129 if (!len)
130 return JS_FALSE;
131 pc += len;
132 }
133 return JS_TRUE;
134 }
135
136 const char *
ToDisassemblySource(JSContext * cx,jsval v)137 ToDisassemblySource(JSContext *cx, jsval v)
138 {
139 JSObject *obj;
140 JSScopeProperty *sprop;
141 char *source;
142 const char *bytes;
143 JSString *str;
144
145 if (!JSVAL_IS_PRIMITIVE(v)) {
146 obj = JSVAL_TO_OBJECT(v);
147 if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) {
148 source = JS_sprintf_append(NULL, "depth %d {",
149 OBJ_BLOCK_DEPTH(cx, obj));
150 for (sprop = OBJ_SCOPE(obj)->lastProp; sprop;
151 sprop = sprop->parent) {
152 bytes = js_AtomToPrintableString(cx, JSID_TO_ATOM(sprop->id));
153 if (!bytes)
154 return NULL;
155 source = JS_sprintf_append(source, "%s: %d%s",
156 bytes, sprop->shortid,
157 sprop->parent ? ", " : "");
158 }
159 source = JS_sprintf_append(source, "}");
160 if (!source)
161 return NULL;
162 str = JS_NewString(cx, source, strlen(source));
163 if (!str)
164 return NULL;
165 return JS_GetStringBytes(str);
166 }
167 }
168 return js_ValueToPrintableSource(cx, v);
169 }
170
171 JS_FRIEND_API(uintN)
js_Disassemble1(JSContext * cx,JSScript * script,jsbytecode * pc,uintN loc,JSBool lines,FILE * fp)172 js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
173 JSBool lines, FILE *fp)
174 {
175 JSOp op;
176 const JSCodeSpec *cs;
177 ptrdiff_t len, off, jmplen;
178 uint32 type;
179 JSAtom *atom;
180 const char *bytes;
181
182 op = (JSOp)*pc;
183 if (op >= JSOP_LIMIT) {
184 char numBuf1[12], numBuf2[12];
185 JS_snprintf(numBuf1, sizeof numBuf1, "%d", op);
186 JS_snprintf(numBuf2, sizeof numBuf2, "%d", JSOP_LIMIT);
187 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
188 JSMSG_BYTECODE_TOO_BIG, numBuf1, numBuf2);
189 return 0;
190 }
191 cs = &js_CodeSpec[op];
192 len = (ptrdiff_t) cs->length;
193 fprintf(fp, "%05u:", loc);
194 if (lines)
195 fprintf(fp, "%4u", JS_PCToLineNumber(cx, script, pc));
196 fprintf(fp, " %s", cs->name);
197 type = cs->format & JOF_TYPEMASK;
198 switch (type) {
199 case JOF_BYTE:
200 if (op == JSOP_TRAP) {
201 op = JS_GetTrapOpcode(cx, script, pc);
202 if (op == JSOP_LIMIT)
203 return 0;
204 len = (ptrdiff_t) js_CodeSpec[op].length;
205 }
206 break;
207
208 case JOF_JUMP:
209 case JOF_JUMPX:
210 off = GetJumpOffset(pc, pc);
211 fprintf(fp, " %u (%d)", loc + off, off);
212 break;
213
214 case JOF_CONST:
215 atom = GET_ATOM(cx, script, pc);
216 bytes = ToDisassemblySource(cx, ATOM_KEY(atom));
217 if (!bytes)
218 return 0;
219 fprintf(fp, " %s", bytes);
220 break;
221
222 case JOF_UINT16:
223 case JOF_LOCAL:
224 fprintf(fp, " %u", GET_UINT16(pc));
225 break;
226
227 case JOF_TABLESWITCH:
228 case JOF_TABLESWITCHX:
229 {
230 jsbytecode *pc2;
231 jsint i, low, high;
232
233 jmplen = (type == JOF_TABLESWITCH) ? JUMP_OFFSET_LEN
234 : JUMPX_OFFSET_LEN;
235 pc2 = pc;
236 off = GetJumpOffset(pc, pc2);
237 pc2 += jmplen;
238 low = GET_JUMP_OFFSET(pc2);
239 pc2 += JUMP_OFFSET_LEN;
240 high = GET_JUMP_OFFSET(pc2);
241 pc2 += JUMP_OFFSET_LEN;
242 fprintf(fp, " defaultOffset %d low %d high %d", off, low, high);
243 for (i = low; i <= high; i++) {
244 off = GetJumpOffset(pc, pc2);
245 fprintf(fp, "\n\t%d: %d", i, off);
246 pc2 += jmplen;
247 }
248 len = 1 + pc2 - pc;
249 break;
250 }
251
252 case JOF_LOOKUPSWITCH:
253 case JOF_LOOKUPSWITCHX:
254 {
255 jsbytecode *pc2;
256 jsatomid npairs;
257
258 jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN
259 : JUMPX_OFFSET_LEN;
260 pc2 = pc;
261 off = GetJumpOffset(pc, pc2);
262 pc2 += jmplen;
263 npairs = GET_ATOM_INDEX(pc2);
264 pc2 += ATOM_INDEX_LEN;
265 fprintf(fp, " offset %d npairs %u", off, (uintN) npairs);
266 while (npairs) {
267 atom = GET_ATOM(cx, script, pc2);
268 pc2 += ATOM_INDEX_LEN;
269 off = GetJumpOffset(pc, pc2);
270 pc2 += jmplen;
271
272 bytes = ToDisassemblySource(cx, ATOM_KEY(atom));
273 if (!bytes)
274 return 0;
275 fprintf(fp, "\n\t%s: %d", bytes, off);
276 npairs--;
277 }
278 len = 1 + pc2 - pc;
279 break;
280 }
281
282 case JOF_QARG:
283 fprintf(fp, " %u", GET_ARGNO(pc));
284 break;
285
286 case JOF_QVAR:
287 fprintf(fp, " %u", GET_VARNO(pc));
288 break;
289
290 case JOF_INDEXCONST:
291 fprintf(fp, " %u", GET_VARNO(pc));
292 pc += VARNO_LEN;
293 atom = GET_ATOM(cx, script, pc);
294 bytes = ToDisassemblySource(cx, ATOM_KEY(atom));
295 if (!bytes)
296 return 0;
297 fprintf(fp, " %s", bytes);
298 break;
299
300 case JOF_UINT24:
301 if (op == JSOP_FINDNAME) {
302 /* Special case to avoid a JOF_FINDNAME just for this op. */
303 atom = js_GetAtom(cx, &script->atomMap, GET_UINT24(pc));
304 bytes = ToDisassemblySource(cx, ATOM_KEY(atom));
305 if (!bytes)
306 return 0;
307 fprintf(fp, " %s", bytes);
308 break;
309 }
310
311 JS_ASSERT(op == JSOP_UINT24 || op == JSOP_LITERAL);
312 fprintf(fp, " %u", GET_UINT24(pc));
313 break;
314
315 case JOF_LITOPX:
316 atom = js_GetAtom(cx, &script->atomMap, GET_LITERAL_INDEX(pc));
317 bytes = ToDisassemblySource(cx, ATOM_KEY(atom));
318 if (!bytes)
319 return 0;
320
321 /*
322 * Bytecode: JSOP_LITOPX <uint24> op [<varno> if JSOP_DEFLOCALFUN].
323 * Advance pc to point at op.
324 */
325 pc += 1 + LITERAL_INDEX_LEN;
326 op = *pc;
327 cs = &js_CodeSpec[op];
328 fprintf(fp, " %s op %s", bytes, cs->name);
329 if ((cs->format & JOF_TYPEMASK) == JOF_INDEXCONST)
330 fprintf(fp, " %u", GET_VARNO(pc));
331
332 /*
333 * Set len to advance pc to skip op and any other immediates (namely,
334 * <varno> if JSOP_DEFLOCALFUN).
335 */
336 JS_ASSERT(cs->length > ATOM_INDEX_LEN);
337 len = cs->length - ATOM_INDEX_LEN;
338 break;
339
340 default: {
341 char numBuf[12];
342 JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) cs->format);
343 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
344 JSMSG_UNKNOWN_FORMAT, numBuf);
345 return 0;
346 }
347 }
348 fputs("\n", fp);
349 return len;
350 }
351
352 #endif /* DEBUG */
353
354 /************************************************************************/
355
356 /*
357 * Sprintf, but with unlimited and automatically allocated buffering.
358 */
359 typedef struct Sprinter {
360 JSContext *context; /* context executing the decompiler */
361 JSArenaPool *pool; /* string allocation pool */
362 char *base; /* base address of buffer in pool */
363 size_t size; /* size of buffer allocated at base */
364 ptrdiff_t offset; /* offset of next free char in buffer */
365 } Sprinter;
366
367 #define INIT_SPRINTER(cx, sp, ap, off) \
368 ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0, \
369 (sp)->offset = off)
370
371 #define OFF2STR(sp,off) ((sp)->base + (off))
372 #define STR2OFF(sp,str) ((str) - (sp)->base)
373 #define RETRACT(sp,str) ((sp)->offset = STR2OFF(sp, str))
374
375 static JSBool
SprintAlloc(Sprinter * sp,size_t nb)376 SprintAlloc(Sprinter *sp, size_t nb)
377 {
378 char *base;
379
380 base = sp->base;
381 if (!base) {
382 JS_ARENA_ALLOCATE_CAST(base, char *, sp->pool, nb);
383 } else {
384 JS_ARENA_GROW_CAST(base, char *, sp->pool, sp->size, nb);
385 }
386 if (!base) {
387 JS_ReportOutOfMemory(sp->context);
388 return JS_FALSE;
389 }
390 sp->base = base;
391 sp->size += nb;
392 return JS_TRUE;
393 }
394
395 static ptrdiff_t
SprintPut(Sprinter * sp,const char * s,size_t len)396 SprintPut(Sprinter *sp, const char *s, size_t len)
397 {
398 ptrdiff_t nb, offset;
399 char *bp;
400
401 /* Allocate space for s, including the '\0' at the end. */
402 nb = (sp->offset + len + 1) - sp->size;
403 if (nb > 0 && !SprintAlloc(sp, nb))
404 return -1;
405
406 /* Advance offset and copy s into sp's buffer. */
407 offset = sp->offset;
408 sp->offset += len;
409 bp = sp->base + offset;
410 memmove(bp, s, len);
411 bp[len] = 0;
412 return offset;
413 }
414
415 static ptrdiff_t
SprintCString(Sprinter * sp,const char * s)416 SprintCString(Sprinter *sp, const char *s)
417 {
418 return SprintPut(sp, s, strlen(s));
419 }
420
421 static ptrdiff_t
Sprint(Sprinter * sp,const char * format,...)422 Sprint(Sprinter *sp, const char *format, ...)
423 {
424 va_list ap;
425 char *bp;
426 ptrdiff_t offset;
427
428 va_start(ap, format);
429 bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */
430 va_end(ap);
431 if (!bp) {
432 JS_ReportOutOfMemory(sp->context);
433 return -1;
434 }
435 offset = SprintCString(sp, bp);
436 free(bp);
437 return offset;
438 }
439
440 const jschar js_EscapeMap[] = {
441 '\b', 'b',
442 '\f', 'f',
443 '\n', 'n',
444 '\r', 'r',
445 '\t', 't',
446 '\v', 'v',
447 '"', '"',
448 '\'', '\'',
449 '\\', '\\',
450 0
451 };
452
453 #define DONT_ESCAPE 0x10000
454
455 static char *
QuoteString(Sprinter * sp,JSString * str,uint32 quote)456 QuoteString(Sprinter *sp, JSString *str, uint32 quote)
457 {
458 JSBool dontEscape, ok;
459 jschar qc, c;
460 ptrdiff_t off, len, nb;
461 const jschar *s, *t, *u, *z;
462 char *bp;
463
464 /* Sample off first for later return value pointer computation. */
465 dontEscape = (quote & DONT_ESCAPE) != 0;
466 qc = (jschar) quote;
467 off = sp->offset;
468 if (qc && Sprint(sp, "%c", (char)qc) < 0)
469 return NULL;
470
471 /* Loop control variables: z points at end of string sentinel. */
472 s = JSSTRING_CHARS(str);
473 z = s + JSSTRING_LENGTH(str);
474 for (t = s; t < z; s = ++t) {
475 /* Move t forward from s past un-quote-worthy characters. */
476 c = *t;
477 while (JS_ISPRINT(c) && c != qc && c != '\\' && !(c >> 8)) {
478 c = *++t;
479 if (t == z)
480 break;
481 }
482 len = PTRDIFF(t, s, jschar);
483
484 /* Allocate space for s, including the '\0' at the end. */
485 nb = (sp->offset + len + 1) - sp->size;
486 if (nb > 0 && !SprintAlloc(sp, nb))
487 return NULL;
488
489 /* Advance sp->offset and copy s into sp's buffer. */
490 bp = sp->base + sp->offset;
491 sp->offset += len;
492 while (--len >= 0)
493 *bp++ = (char) *s++;
494 *bp = '\0';
495
496 if (t == z)
497 break;
498
499 /* Use js_EscapeMap, \u, or \x only if necessary. */
500 if ((u = js_strchr(js_EscapeMap, c)) != NULL) {
501 ok = dontEscape
502 ? Sprint(sp, "%c", (char)c) >= 0
503 : Sprint(sp, "\\%c", (char)u[1]) >= 0;
504 } else {
505 #ifdef JS_C_STRINGS_ARE_UTF8
506 /* If this is a surrogate pair, make sure to print the pair. */
507 if (c >= 0xD800 && c <= 0xDBFF) {
508 jschar buffer[3];
509 buffer[0] = c;
510 buffer[1] = *++t;
511 buffer[2] = 0;
512 if (t == z) {
513 char numbuf[10];
514 JS_snprintf(numbuf, sizeof numbuf, "0x%x", c);
515 JS_ReportErrorFlagsAndNumber(sp->context, JSREPORT_ERROR,
516 js_GetErrorMessage, NULL,
517 JSMSG_BAD_SURROGATE_CHAR,
518 numbuf);
519 ok = JS_FALSE;
520 break;
521 }
522 ok = Sprint(sp, "%hs", buffer) >= 0;
523 } else {
524 /* Print as UTF-8 string. */
525 ok = Sprint(sp, "%hc", c) >= 0;
526 }
527 #else
528 /* Use \uXXXX or \xXX if the string can't be displayed as UTF-8. */
529 ok = Sprint(sp, (c >> 8) ? "\\u%04X" : "\\x%02X", c) >= 0;
530 #endif
531 }
532 if (!ok)
533 return NULL;
534 }
535
536 /* Sprint the closing quote and return the quoted string. */
537 if (qc && Sprint(sp, "%c", (char)qc) < 0)
538 return NULL;
539
540 /*
541 * If we haven't Sprint'd anything yet, Sprint an empty string so that
542 * the OFF2STR below gives a valid result.
543 */
544 if (off == sp->offset && Sprint(sp, "") < 0)
545 return NULL;
546 return OFF2STR(sp, off);
547 }
548
549 JSString *
js_QuoteString(JSContext * cx,JSString * str,jschar quote)550 js_QuoteString(JSContext *cx, JSString *str, jschar quote)
551 {
552 void *mark;
553 Sprinter sprinter;
554 char *bytes;
555 JSString *escstr;
556
557 mark = JS_ARENA_MARK(&cx->tempPool);
558 INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0);
559 bytes = QuoteString(&sprinter, str, quote);
560 escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL;
561 JS_ARENA_RELEASE(&cx->tempPool, mark);
562 return escstr;
563 }
564
565 /************************************************************************/
566
567 #if JS_HAS_BLOCK_SCOPE
568 typedef enum JSBraceState {
569 ALWAYS_BRACE,
570 MAYBE_BRACE,
571 DONT_BRACE
572 } JSBraceState;
573 #endif
574
575 struct JSPrinter {
576 Sprinter sprinter; /* base class state */
577 JSArenaPool pool; /* string allocation pool */
578 uintN indent; /* indentation in spaces */
579 JSPackedBool pretty; /* pretty-print: indent, use newlines */
580 JSPackedBool grouped; /* in parenthesized expression context */
581 JSScript *script; /* script being printed */
582 jsbytecode *dvgfence; /* js_DecompileValueGenerator fencepost */
583 JSScope *scope; /* script function scope */
584 #if JS_HAS_BLOCK_SCOPE
585 JSBraceState braceState; /* remove braces around let declaration */
586 ptrdiff_t spaceOffset; /* -1 or offset of space before maybe-{ */
587 #endif
588 };
589
590 /*
591 * Hack another flag, a la JS_DONT_PRETTY_PRINT, into uintN indent parameters
592 * to functions such as js_DecompileFunction and js_NewPrinter. This time, as
593 * opposed to JS_DONT_PRETTY_PRINT back in the dark ages, we can assume that a
594 * uintN is at least 32 bits.
595 */
596 #define JS_IN_GROUP_CONTEXT 0x10000
597
598 JSPrinter *
js_NewPrinter(JSContext * cx,const char * name,uintN indent,JSBool pretty)599 js_NewPrinter(JSContext *cx, const char *name, uintN indent, JSBool pretty)
600 {
601 JSPrinter *jp;
602
603 jp = (JSPrinter *) JS_malloc(cx, sizeof(JSPrinter));
604 if (!jp)
605 return NULL;
606 INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
607 JS_InitArenaPool(&jp->pool, name, 256, 1);
608 jp->indent = indent & ~JS_IN_GROUP_CONTEXT;
609 jp->pretty = pretty;
610 jp->grouped = (indent & JS_IN_GROUP_CONTEXT) != 0;
611 jp->script = NULL;
612 jp->dvgfence = NULL;
613 jp->scope = NULL;
614 #if JS_HAS_BLOCK_SCOPE
615 jp->braceState = ALWAYS_BRACE;
616 jp->spaceOffset = -1;
617 #endif
618 return jp;
619 }
620
621 void
js_DestroyPrinter(JSPrinter * jp)622 js_DestroyPrinter(JSPrinter *jp)
623 {
624 JS_FinishArenaPool(&jp->pool);
625 JS_free(jp->sprinter.context, jp);
626 }
627
628 JSString *
js_GetPrinterOutput(JSPrinter * jp)629 js_GetPrinterOutput(JSPrinter *jp)
630 {
631 JSContext *cx;
632 JSString *str;
633
634 cx = jp->sprinter.context;
635 if (!jp->sprinter.base)
636 return cx->runtime->emptyString;
637 str = JS_NewStringCopyZ(cx, jp->sprinter.base);
638 if (!str)
639 return NULL;
640 JS_FreeArenaPool(&jp->pool);
641 INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
642 return str;
643 }
644
645 #if !JS_HAS_BLOCK_SCOPE
646 # define SET_MAYBE_BRACE(jp) jp
647 # define CLEAR_MAYBE_BRACE(jp) jp
648 #else
649 # define SET_MAYBE_BRACE(jp) ((jp)->braceState = MAYBE_BRACE, (jp))
650 # define CLEAR_MAYBE_BRACE(jp) ((jp)->braceState = ALWAYS_BRACE, (jp))
651
652 static void
SetDontBrace(JSPrinter * jp)653 SetDontBrace(JSPrinter *jp)
654 {
655 ptrdiff_t offset;
656 const char *bp;
657
658 /* When not pretty-printing, newline after brace is chopped. */
659 JS_ASSERT(jp->spaceOffset < 0);
660 offset = jp->sprinter.offset - (jp->pretty ? 3 : 2);
661
662 /* The shortest case is "if (x) {". */
663 JS_ASSERT(offset >= 6);
664 bp = jp->sprinter.base;
665 if (bp[offset+0] == ' ' && bp[offset+1] == '{') {
666 JS_ASSERT(!jp->pretty || bp[offset+2] == '\n');
667 jp->spaceOffset = offset;
668 jp->braceState = DONT_BRACE;
669 }
670 }
671 #endif
672
673 int
js_printf(JSPrinter * jp,const char * format,...)674 js_printf(JSPrinter *jp, const char *format, ...)
675 {
676 va_list ap;
677 char *bp, *fp;
678 int cc;
679
680 if (*format == '\0')
681 return 0;
682
683 va_start(ap, format);
684
685 /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */
686 if (*format == '\t') {
687 format++;
688
689 #if JS_HAS_BLOCK_SCOPE
690 if (*format == '}' && jp->braceState != ALWAYS_BRACE) {
691 JSBraceState braceState;
692
693 braceState = jp->braceState;
694 jp->braceState = ALWAYS_BRACE;
695 if (braceState == DONT_BRACE) {
696 ptrdiff_t offset, delta, from;
697
698 JS_ASSERT(format[1] == '\n' || format[1] == ' ');
699 offset = jp->spaceOffset;
700 JS_ASSERT(offset >= 6);
701
702 /* Replace " {\n" at the end of jp->sprinter with "\n". */
703 bp = jp->sprinter.base;
704 if (bp[offset+0] == ' ' && bp[offset+1] == '{') {
705 delta = 2;
706 if (jp->pretty) {
707 /* If pretty, we don't have to worry about 'else'. */
708 JS_ASSERT(bp[offset+2] == '\n');
709 } else if (bp[offset-1] != ')') {
710 /* Must keep ' ' to avoid 'dolet' or 'elselet'. */
711 ++offset;
712 delta = 1;
713 }
714
715 from = offset + delta;
716 memmove(bp + offset, bp + from, jp->sprinter.offset - from);
717 jp->sprinter.offset -= delta;
718 jp->spaceOffset = -1;
719
720 format += 2;
721 if (*format == '\0')
722 return 0;
723 }
724 }
725 }
726 #endif
727
728 if (jp->pretty && Sprint(&jp->sprinter, "%*s", jp->indent, "") < 0)
729 return -1;
730 }
731
732 /* Suppress newlines (must be once per format, at the end) if not pretty. */
733 fp = NULL;
734 if (!jp->pretty && format[cc = strlen(format) - 1] == '\n') {
735 fp = JS_strdup(jp->sprinter.context, format);
736 if (!fp)
737 return -1;
738 fp[cc] = '\0';
739 format = fp;
740 }
741
742 /* Allocate temp space, convert format, and put. */
743 bp = JS_vsmprintf(format, ap); /* XXX vsaprintf */
744 if (fp) {
745 JS_free(jp->sprinter.context, fp);
746 format = NULL;
747 }
748 if (!bp) {
749 JS_ReportOutOfMemory(jp->sprinter.context);
750 return -1;
751 }
752
753 cc = strlen(bp);
754 if (SprintPut(&jp->sprinter, bp, (size_t)cc) < 0)
755 cc = -1;
756 free(bp);
757
758 va_end(ap);
759 return cc;
760 }
761
762 JSBool
js_puts(JSPrinter * jp,const char * s)763 js_puts(JSPrinter *jp, const char *s)
764 {
765 return SprintCString(&jp->sprinter, s) >= 0;
766 }
767
768 /************************************************************************/
769
770 typedef struct SprintStack {
771 Sprinter sprinter; /* sprinter for postfix to infix buffering */
772 ptrdiff_t *offsets; /* stack of postfix string offsets */
773 jsbytecode *opcodes; /* parallel stack of JS opcodes */
774 uintN top; /* top of stack index */
775 uintN inArrayInit; /* array initialiser/comprehension level */
776 JSPrinter *printer; /* permanent output goes here */
777 } SprintStack;
778
779 /*
780 * Get a stacked offset from ss->sprinter.base, or if the stacked value |off|
781 * is negative, lazily fetch the generating pc at |spindex = 1 + off| and try
782 * to decompile the code that generated the missing value. This is used when
783 * reporting errors, where the model stack will lack |pcdepth| non-negative
784 * offsets (see js_DecompileValueGenerator and js_DecompileCode).
785 *
786 * If the stacked offset is -1, return 0 to index the NUL padding at the start
787 * of ss->sprinter.base. If this happens, it means there is a decompiler bug
788 * to fix, but it won't violate memory safety.
789 */
790 static ptrdiff_t
GetOff(SprintStack * ss,uintN i)791 GetOff(SprintStack *ss, uintN i)
792 {
793 ptrdiff_t off;
794 JSString *str;
795
796 off = ss->offsets[i];
797 if (off < 0) {
798 #if defined DEBUG_brendan || defined DEBUG_mrbkap || defined DEBUG_crowder
799 JS_ASSERT(off < -1);
800 #endif
801 if (++off == 0) {
802 if (!ss->sprinter.base && SprintPut(&ss->sprinter, "", 0) >= 0)
803 memset(ss->sprinter.base, 0, ss->sprinter.offset);
804 return 0;
805 }
806
807 str = js_DecompileValueGenerator(ss->sprinter.context, off,
808 JSVAL_NULL, NULL);
809 if (!str)
810 return 0;
811 off = SprintCString(&ss->sprinter, JS_GetStringBytes(str));
812 if (off < 0)
813 off = 0;
814 ss->offsets[i] = off;
815 }
816 return off;
817 }
818
819 static const char *
GetStr(SprintStack * ss,uintN i)820 GetStr(SprintStack *ss, uintN i)
821 {
822 ptrdiff_t off;
823
824 /*
825 * Must call GetOff before using ss->sprinter.base, since it may be null
826 * until bootstrapped by GetOff.
827 */
828 off = GetOff(ss, i);
829 return OFF2STR(&ss->sprinter, off);
830 }
831
832 /* Gap between stacked strings to allow for insertion of parens and commas. */
833 #define PAREN_SLOP (2 + 1)
834
835 /*
836 * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME,
837 * JSOP_SETPROP, and JSOP_SETELEM, respectively. They are never stored in
838 * bytecode, so they don't preempt valid opcodes.
839 */
840 #define JSOP_GETPROP2 256
841 #define JSOP_GETELEM2 257
842
843 static JSBool
PushOff(SprintStack * ss,ptrdiff_t off,JSOp op)844 PushOff(SprintStack *ss, ptrdiff_t off, JSOp op)
845 {
846 uintN top;
847
848 if (!SprintAlloc(&ss->sprinter, PAREN_SLOP))
849 return JS_FALSE;
850
851 /* ss->top points to the next free slot; be paranoid about overflow. */
852 top = ss->top;
853 JS_ASSERT(top < ss->printer->script->depth);
854 if (top >= ss->printer->script->depth) {
855 JS_ReportOutOfMemory(ss->sprinter.context);
856 return JS_FALSE;
857 }
858
859 /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */
860 ss->offsets[top] = off;
861 ss->opcodes[top] = (op == JSOP_GETPROP2) ? JSOP_GETPROP
862 : (op == JSOP_GETELEM2) ? JSOP_GETELEM
863 : (jsbytecode) op;
864 ss->top = ++top;
865 memset(OFF2STR(&ss->sprinter, ss->sprinter.offset), 0, PAREN_SLOP);
866 ss->sprinter.offset += PAREN_SLOP;
867 return JS_TRUE;
868 }
869
870 static ptrdiff_t
PopOff(SprintStack * ss,JSOp op)871 PopOff(SprintStack *ss, JSOp op)
872 {
873 uintN top;
874 const JSCodeSpec *cs, *topcs;
875 ptrdiff_t off;
876
877 /* ss->top points to the next free slot; be paranoid about underflow. */
878 top = ss->top;
879 JS_ASSERT(top != 0);
880 if (top == 0)
881 return 0;
882
883 ss->top = --top;
884 off = GetOff(ss, top);
885 topcs = &js_CodeSpec[ss->opcodes[top]];
886 cs = &js_CodeSpec[op];
887 if (topcs->prec != 0 && topcs->prec < cs->prec) {
888 ss->sprinter.offset = ss->offsets[top] = off - 2;
889 off = Sprint(&ss->sprinter, "(%s)", OFF2STR(&ss->sprinter, off));
890 } else {
891 ss->sprinter.offset = off;
892 }
893 return off;
894 }
895
896 static const char *
PopStr(SprintStack * ss,JSOp op)897 PopStr(SprintStack *ss, JSOp op)
898 {
899 ptrdiff_t off;
900
901 off = PopOff(ss, op);
902 return OFF2STR(&ss->sprinter, off);
903 }
904
905 typedef struct TableEntry {
906 jsval key;
907 ptrdiff_t offset;
908 JSAtom *label;
909 jsint order; /* source order for stable tableswitch sort */
910 } TableEntry;
911
912 static JSBool
CompareOffsets(void * arg,const void * v1,const void * v2,int * result)913 CompareOffsets(void *arg, const void *v1, const void *v2, int *result)
914 {
915 ptrdiff_t offset_diff;
916 const TableEntry *te1 = (const TableEntry *) v1,
917 *te2 = (const TableEntry *) v2;
918
919 offset_diff = te1->offset - te2->offset;
920 *result = (offset_diff == 0 ? te1->order - te2->order
921 : offset_diff < 0 ? -1
922 : 1);
923 return JS_TRUE;
924 }
925
926 static jsbytecode *
927 Decompile(SprintStack *ss, jsbytecode *pc, intN nb);
928
929 static JSBool
DecompileSwitch(SprintStack * ss,TableEntry * table,uintN tableLength,jsbytecode * pc,ptrdiff_t switchLength,ptrdiff_t defaultOffset,JSBool isCondSwitch)930 DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength,
931 jsbytecode *pc, ptrdiff_t switchLength,
932 ptrdiff_t defaultOffset, JSBool isCondSwitch)
933 {
934 JSContext *cx;
935 JSPrinter *jp;
936 ptrdiff_t off, off2, diff, caseExprOff;
937 char *lval, *rval;
938 uintN i;
939 jsval key;
940 JSString *str;
941
942 cx = ss->sprinter.context;
943 jp = ss->printer;
944
945 /* JSOP_CONDSWITCH doesn't pop, unlike JSOP_{LOOKUP,TABLE}SWITCH. */
946 off = isCondSwitch ? GetOff(ss, ss->top-1) : PopOff(ss, JSOP_NOP);
947 lval = OFF2STR(&ss->sprinter, off);
948
949 js_printf(CLEAR_MAYBE_BRACE(jp), "\tswitch (%s) {\n", lval);
950
951 if (tableLength) {
952 diff = table[0].offset - defaultOffset;
953 if (diff > 0) {
954 jp->indent += 2;
955 js_printf(jp, "\t%s:\n", js_default_str);
956 jp->indent += 2;
957 if (!Decompile(ss, pc + defaultOffset, diff))
958 return JS_FALSE;
959 jp->indent -= 4;
960 }
961
962 caseExprOff = isCondSwitch ? JSOP_CONDSWITCH_LENGTH : 0;
963
964 for (i = 0; i < tableLength; i++) {
965 off = table[i].offset;
966 off2 = (i + 1 < tableLength) ? table[i + 1].offset : switchLength;
967
968 key = table[i].key;
969 if (isCondSwitch) {
970 ptrdiff_t nextCaseExprOff;
971
972 /*
973 * key encodes the JSOP_CASE bytecode's offset from switchtop.
974 * The next case expression follows immediately, unless we are
975 * at the last case.
976 */
977 nextCaseExprOff = (ptrdiff_t)JSVAL_TO_INT(key);
978 nextCaseExprOff += js_CodeSpec[pc[nextCaseExprOff]].length;
979 jp->indent += 2;
980 if (!Decompile(ss, pc + caseExprOff,
981 nextCaseExprOff - caseExprOff)) {
982 return JS_FALSE;
983 }
984 caseExprOff = nextCaseExprOff;
985
986 /* Balance the stack as if this JSOP_CASE matched. */
987 --ss->top;
988 } else {
989 /*
990 * key comes from an atom, not the decompiler, so we need to
991 * quote it if it's a string literal. But if table[i].label
992 * is non-null, key was constant-propagated and label is the
993 * name of the const we should show as the case label. We set
994 * key to undefined so this identifier is escaped, if required
995 * by non-ASCII characters, but not quoted, by QuoteString.
996 */
997 if (table[i].label) {
998 str = ATOM_TO_STRING(table[i].label);
999 key = JSVAL_VOID;
1000 } else {
1001 str = js_ValueToString(cx, key);
1002 if (!str)
1003 return JS_FALSE;
1004 }
1005 rval = QuoteString(&ss->sprinter, str,
1006 (jschar)(JSVAL_IS_STRING(key) ? '"' : 0));
1007 if (!rval)
1008 return JS_FALSE;
1009 RETRACT(&ss->sprinter, rval);
1010 jp->indent += 2;
1011 js_printf(jp, "\tcase %s:\n", rval);
1012 }
1013
1014 jp->indent += 2;
1015 if (off <= defaultOffset && defaultOffset < off2) {
1016 diff = defaultOffset - off;
1017 if (diff != 0) {
1018 if (!Decompile(ss, pc + off, diff))
1019 return JS_FALSE;
1020 off = defaultOffset;
1021 }
1022 jp->indent -= 2;
1023 js_printf(jp, "\t%s:\n", js_default_str);
1024 jp->indent += 2;
1025 }
1026 if (!Decompile(ss, pc + off, off2 - off))
1027 return JS_FALSE;
1028 jp->indent -= 4;
1029
1030 /* Re-balance as if last JSOP_CASE or JSOP_DEFAULT mismatched. */
1031 if (isCondSwitch)
1032 ++ss->top;
1033 }
1034 }
1035
1036 if (defaultOffset == switchLength) {
1037 jp->indent += 2;
1038 js_printf(jp, "\t%s:;\n", js_default_str);
1039 jp->indent -= 2;
1040 }
1041 js_printf(jp, "\t}\n");
1042
1043 /* By the end of a JSOP_CONDSWITCH, the discriminant has been popped. */
1044 if (isCondSwitch)
1045 --ss->top;
1046 return JS_TRUE;
1047 }
1048
1049 static JSAtom *
GetSlotAtom(JSPrinter * jp,JSPropertyOp getter,uintN slot)1050 GetSlotAtom(JSPrinter *jp, JSPropertyOp getter, uintN slot)
1051 {
1052 JSScope *scope;
1053 JSScopeProperty *sprop;
1054 JSObject *obj, *proto;
1055
1056 scope = jp->scope;
1057 while (scope) {
1058 for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
1059 if (sprop->getter != getter)
1060 continue;
1061 JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
1062 JS_ASSERT(JSID_IS_ATOM(sprop->id));
1063 if ((uintN) sprop->shortid == slot)
1064 return JSID_TO_ATOM(sprop->id);
1065 }
1066 obj = scope->object;
1067 if (!obj)
1068 break;
1069 proto = OBJ_GET_PROTO(jp->sprinter.context, obj);
1070 if (!proto)
1071 break;
1072 scope = OBJ_SCOPE(proto);
1073 }
1074 return NULL;
1075 }
1076
1077 /*
1078 * NB: Indexed by SRC_DECL_* defines from jsemit.h.
1079 */
1080 static const char * const var_prefix[] = {"var ", "const ", "let "};
1081
1082 static const char *
VarPrefix(jssrcnote * sn)1083 VarPrefix(jssrcnote *sn)
1084 {
1085 if (sn && (SN_TYPE(sn) == SRC_DECL || SN_TYPE(sn) == SRC_GROUPASSIGN)) {
1086 ptrdiff_t type = js_GetSrcNoteOffset(sn, 0);
1087 if ((uintN)type <= SRC_DECL_LET)
1088 return var_prefix[type];
1089 }
1090 return "";
1091 }
1092 #define LOCAL_ASSERT_RV(expr, rv) \
1093 JS_BEGIN_MACRO \
1094 JS_ASSERT(expr); \
1095 if (!(expr)) return (rv); \
1096 JS_END_MACRO
1097
1098 const char *
GetLocal(SprintStack * ss,jsint i)1099 GetLocal(SprintStack *ss, jsint i)
1100 {
1101 ptrdiff_t off;
1102 JSContext *cx;
1103 JSScript *script;
1104 jsatomid j, n;
1105 JSAtom *atom;
1106 JSObject *obj;
1107 jsint depth, count;
1108 JSScopeProperty *sprop;
1109 const char *rval;
1110
1111 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, "")
1112
1113 off = ss->offsets[i];
1114 if (off >= 0)
1115 return OFF2STR(&ss->sprinter, off);
1116
1117 /*
1118 * We must be called from js_DecompileValueGenerator (via Decompile) when
1119 * dereferencing a local that's undefined or null. Search script->atomMap
1120 * for the block containing this local by its stack index, i.
1121 */
1122 cx = ss->sprinter.context;
1123 script = ss->printer->script;
1124 for (j = 0, n = script->atomMap.length; j < n; j++) {
1125 atom = script->atomMap.vector[j];
1126 if (ATOM_IS_OBJECT(atom)) {
1127 obj = ATOM_TO_OBJECT(atom);
1128 if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) {
1129 depth = OBJ_BLOCK_DEPTH(cx, obj);
1130 count = OBJ_BLOCK_COUNT(cx, obj);
1131 if ((jsuint)(i - depth) < (jsuint)count)
1132 break;
1133 }
1134 }
1135 }
1136
1137 LOCAL_ASSERT(j < n);
1138 i -= depth;
1139 for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) {
1140 if (sprop->shortid == i)
1141 break;
1142 }
1143
1144 LOCAL_ASSERT(sprop && JSID_IS_ATOM(sprop->id));
1145 atom = JSID_TO_ATOM(sprop->id);
1146 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1147 if (!rval)
1148 return NULL;
1149 RETRACT(&ss->sprinter, rval);
1150 return rval;
1151
1152 #undef LOCAL_ASSERT
1153 }
1154
1155 #if JS_HAS_DESTRUCTURING
1156
1157 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
1158 #define LOAD_OP_DATA(pc) (oplen = (cs = &js_CodeSpec[op = *pc])->length)
1159
1160 static jsbytecode *
1161 DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc);
1162
1163 static jsbytecode *
DecompileDestructuringLHS(SprintStack * ss,jsbytecode * pc,jsbytecode * endpc,JSBool * hole)1164 DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
1165 JSBool *hole)
1166 {
1167 JSContext *cx;
1168 JSPrinter *jp;
1169 JSOp op;
1170 const JSCodeSpec *cs;
1171 uintN oplen, i;
1172 const char *lval, *xval;
1173 ptrdiff_t todo;
1174 JSAtom *atom;
1175
1176 *hole = JS_FALSE;
1177 cx = ss->sprinter.context;
1178 jp = ss->printer;
1179 LOAD_OP_DATA(pc);
1180
1181 switch (op) {
1182 case JSOP_POP:
1183 *hole = JS_TRUE;
1184 todo = SprintPut(&ss->sprinter, ", ", 2);
1185 break;
1186
1187 case JSOP_DUP:
1188 pc = DecompileDestructuring(ss, pc, endpc);
1189 if (!pc)
1190 return NULL;
1191 if (pc == endpc)
1192 return pc;
1193 LOAD_OP_DATA(pc);
1194 lval = PopStr(ss, JSOP_NOP);
1195 todo = SprintCString(&ss->sprinter, lval);
1196 if (op == JSOP_SETSP)
1197 return pc;
1198 LOCAL_ASSERT(*pc == JSOP_POP);
1199 break;
1200
1201 case JSOP_SETARG:
1202 case JSOP_SETVAR:
1203 case JSOP_SETGVAR:
1204 case JSOP_SETLOCAL:
1205 LOCAL_ASSERT(pc[oplen] == JSOP_POP || pc[oplen] == JSOP_SETSP);
1206 /* FALL THROUGH */
1207
1208 case JSOP_SETLOCALPOP:
1209 i = GET_UINT16(pc);
1210 atom = NULL;
1211 lval = NULL;
1212 if (op == JSOP_SETARG)
1213 atom = GetSlotAtom(jp, js_GetArgument, i);
1214 else if (op == JSOP_SETVAR)
1215 atom = GetSlotAtom(jp, js_GetLocalVariable, i);
1216 else if (op == JSOP_SETGVAR)
1217 atom = GET_ATOM(cx, jp->script, pc);
1218 else
1219 lval = GetLocal(ss, i);
1220 if (atom)
1221 lval = js_AtomToPrintableString(cx, atom);
1222 LOCAL_ASSERT(lval);
1223 todo = SprintCString(&ss->sprinter, lval);
1224 if (op != JSOP_SETLOCALPOP) {
1225 pc += oplen;
1226 if (pc == endpc)
1227 return pc;
1228 LOAD_OP_DATA(pc);
1229 if (op == JSOP_SETSP)
1230 return pc;
1231 LOCAL_ASSERT(op == JSOP_POP);
1232 }
1233 break;
1234
1235 default:
1236 /*
1237 * We may need to auto-parenthesize the left-most value decompiled
1238 * here, so add back PAREN_SLOP temporarily. Then decompile until the
1239 * opcode that would reduce the stack depth to (ss->top-1), which we
1240 * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for
1241 * the nb parameter.
1242 */
1243 todo = ss->sprinter.offset;
1244 ss->sprinter.offset = todo + PAREN_SLOP;
1245 pc = Decompile(ss, pc, -ss->top);
1246 if (!pc)
1247 return NULL;
1248 if (pc == endpc)
1249 return pc;
1250 LOAD_OP_DATA(pc);
1251 LOCAL_ASSERT(op == JSOP_ENUMELEM || op == JSOP_ENUMCONSTELEM);
1252 xval = PopStr(ss, JSOP_NOP);
1253 lval = PopStr(ss, JSOP_GETPROP);
1254 ss->sprinter.offset = todo;
1255 if (*lval == '\0') {
1256 /* lval is from JSOP_BINDNAME, so just print xval. */
1257 todo = SprintCString(&ss->sprinter, xval);
1258 } else if (*xval == '\0') {
1259 /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */
1260 todo = SprintCString(&ss->sprinter, lval);
1261 } else {
1262 todo = Sprint(&ss->sprinter,
1263 (js_CodeSpec[ss->opcodes[ss->top+1]].format
1264 & JOF_XMLNAME)
1265 ? "%s.%s"
1266 : "%s[%s]",
1267 lval, xval);
1268 }
1269 break;
1270 }
1271
1272 if (todo < 0)
1273 return NULL;
1274
1275 LOCAL_ASSERT(pc < endpc);
1276 pc += oplen;
1277 return pc;
1278 }
1279
1280 /*
1281 * Starting with a SRC_DESTRUCT-annotated JSOP_DUP, decompile a destructuring
1282 * left-hand side object or array initialiser, including nested destructuring
1283 * initialisers. On successful return, the decompilation will be pushed on ss
1284 * and the return value will point to the POP or GROUP bytecode following the
1285 * destructuring expression.
1286 *
1287 * At any point, if pc is equal to endpc and would otherwise advance, we stop
1288 * immediately and return endpc.
1289 */
1290 static jsbytecode *
DecompileDestructuring(SprintStack * ss,jsbytecode * pc,jsbytecode * endpc)1291 DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc)
1292 {
1293 ptrdiff_t head, todo;
1294 JSContext *cx;
1295 JSPrinter *jp;
1296 JSOp op, saveop;
1297 const JSCodeSpec *cs;
1298 uintN oplen;
1299 jsint i, lasti;
1300 jsdouble d;
1301 const char *lval;
1302 jsbytecode *pc2;
1303 jsatomid atomIndex;
1304 JSAtom *atom;
1305 jssrcnote *sn;
1306 JSString *str;
1307 JSBool hole;
1308
1309 LOCAL_ASSERT(*pc == JSOP_DUP);
1310 pc += JSOP_DUP_LENGTH;
1311
1312 /*
1313 * Set head so we can rewrite '[' to '{' as needed. Back up PAREN_SLOP
1314 * chars so the destructuring decompilation accumulates contiguously in
1315 * ss->sprinter starting with "[".
1316 */
1317 head = SprintPut(&ss->sprinter, "[", 1);
1318 if (head < 0 || !PushOff(ss, head, JSOP_NOP))
1319 return NULL;
1320 ss->sprinter.offset -= PAREN_SLOP;
1321 LOCAL_ASSERT(head == ss->sprinter.offset - 1);
1322 LOCAL_ASSERT(*OFF2STR(&ss->sprinter, head) == '[');
1323
1324 cx = ss->sprinter.context;
1325 jp = ss->printer;
1326 lasti = -1;
1327
1328 while (pc < endpc) {
1329 LOAD_OP_DATA(pc);
1330 saveop = op;
1331
1332 switch (op) {
1333 case JSOP_POP:
1334 pc += oplen;
1335 goto out;
1336
1337 /* Handle the optimized number-pushing opcodes. */
1338 case JSOP_ZERO: d = i = 0; goto do_getelem;
1339 case JSOP_ONE: d = i = 1; goto do_getelem;
1340 case JSOP_UINT16: d = i = GET_UINT16(pc); goto do_getelem;
1341 case JSOP_UINT24: d = i = GET_UINT24(pc); goto do_getelem;
1342
1343 /* Handle the extended literal form of JSOP_NUMBER. */
1344 case JSOP_LITOPX:
1345 atomIndex = GET_LITERAL_INDEX(pc);
1346 pc2 = pc + 1 + LITERAL_INDEX_LEN;
1347 op = *pc2;
1348 LOCAL_ASSERT(op == JSOP_NUMBER);
1349 goto do_getatom;
1350
1351 case JSOP_NUMBER:
1352 atomIndex = GET_ATOM_INDEX(pc);
1353
1354 do_getatom:
1355 atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex);
1356 d = *ATOM_TO_DOUBLE(atom);
1357 LOCAL_ASSERT(JSDOUBLE_IS_FINITE(d) && !JSDOUBLE_IS_NEGZERO(d));
1358 i = (jsint)d;
1359
1360 do_getelem:
1361 sn = js_GetSrcNote(jp->script, pc);
1362 pc += oplen;
1363 if (pc == endpc)
1364 return pc;
1365 LOAD_OP_DATA(pc);
1366 LOCAL_ASSERT(op == JSOP_GETELEM);
1367
1368 /* Distinguish object from array by opcode or source note. */
1369 if (saveop == JSOP_LITERAL ||
1370 (sn && SN_TYPE(sn) == SRC_INITPROP)) {
1371 *OFF2STR(&ss->sprinter, head) = '{';
1372 if (Sprint(&ss->sprinter, "%g: ", d) < 0)
1373 return NULL;
1374 } else {
1375 /* Sanity check for the gnarly control flow above. */
1376 LOCAL_ASSERT(i == d);
1377
1378 /* Fill in any holes (holes at the end don't matter). */
1379 while (++lasti < i) {
1380 if (SprintPut(&ss->sprinter, ", ", 2) < 0)
1381 return NULL;
1382 }
1383 }
1384 break;
1385
1386 case JSOP_LITERAL:
1387 atomIndex = GET_LITERAL_INDEX(pc);
1388 goto do_getatom;
1389
1390 case JSOP_GETPROP:
1391 *OFF2STR(&ss->sprinter, head) = '{';
1392 atom = GET_ATOM(cx, jp->script, pc);
1393 str = ATOM_TO_STRING(atom);
1394 if (!QuoteString(&ss->sprinter, str,
1395 js_IsIdentifier(str) ? 0 : (jschar)'\'')) {
1396 return NULL;
1397 }
1398 if (SprintPut(&ss->sprinter, ": ", 2) < 0)
1399 return NULL;
1400 break;
1401
1402 default:
1403 LOCAL_ASSERT(0);
1404 }
1405
1406 pc += oplen;
1407 if (pc == endpc)
1408 return pc;
1409
1410 /*
1411 * Decompile the left-hand side expression whose bytecode starts at pc
1412 * and continues for a bounded number of bytecodes or stack operations
1413 * (and which in any event stops before endpc).
1414 */
1415 pc = DecompileDestructuringLHS(ss, pc, endpc, &hole);
1416 if (!pc)
1417 return NULL;
1418 if (pc == endpc || *pc != JSOP_DUP)
1419 break;
1420
1421 /*
1422 * Check for SRC_DESTRUCT on this JSOP_DUP, which would mean another
1423 * destructuring initialiser abuts this one, and we should stop. This
1424 * happens with source of the form '[a] = [b] = c'.
1425 */
1426 sn = js_GetSrcNote(jp->script, pc);
1427 if (sn && SN_TYPE(sn) == SRC_DESTRUCT)
1428 break;
1429
1430 if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
1431 return NULL;
1432
1433 pc += JSOP_DUP_LENGTH;
1434 }
1435
1436 out:
1437 lval = OFF2STR(&ss->sprinter, head);
1438 todo = SprintPut(&ss->sprinter, (*lval == '[') ? "]" : "}", 1);
1439 if (todo < 0)
1440 return NULL;
1441 return pc;
1442 }
1443
1444 static jsbytecode *
DecompileGroupAssignment(SprintStack * ss,jsbytecode * pc,jsbytecode * endpc,jssrcnote * sn,ptrdiff_t * todop)1445 DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
1446 jssrcnote *sn, ptrdiff_t *todop)
1447 {
1448 JSOp op;
1449 const JSCodeSpec *cs;
1450 uintN oplen, start, end, i;
1451 ptrdiff_t todo;
1452 JSBool hole;
1453 const char *rval;
1454
1455 LOAD_OP_DATA(pc);
1456 LOCAL_ASSERT(op == JSOP_PUSH || op == JSOP_GETLOCAL);
1457
1458 todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn));
1459 if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
1460 return NULL;
1461 ss->sprinter.offset -= PAREN_SLOP;
1462
1463 for (;;) {
1464 pc += oplen;
1465 if (pc == endpc)
1466 return pc;
1467 pc = DecompileDestructuringLHS(ss, pc, endpc, &hole);
1468 if (!pc)
1469 return NULL;
1470 if (pc == endpc)
1471 return pc;
1472 LOAD_OP_DATA(pc);
1473 if (op != JSOP_PUSH && op != JSOP_GETLOCAL)
1474 break;
1475 if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
1476 return NULL;
1477 }
1478
1479 LOCAL_ASSERT(op == JSOP_SETSP);
1480 if (SprintPut(&ss->sprinter, "] = [", 5) < 0)
1481 return NULL;
1482
1483 start = GET_UINT16(pc);
1484 end = ss->top - 1;
1485 for (i = start; i < end; i++) {
1486 rval = GetStr(ss, i);
1487 if (Sprint(&ss->sprinter, "%s%s",
1488 (i == start) ? "" : ", ",
1489 (i == end - 1 && *rval == '\0') ? ", " : rval) < 0) {
1490 return NULL;
1491 }
1492 }
1493
1494 if (SprintPut(&ss->sprinter, "]", 1) < 0)
1495 return NULL;
1496 ss->sprinter.offset = ss->offsets[i];
1497 ss->top = start;
1498 *todop = todo;
1499 return pc;
1500 }
1501
1502 #undef LOCAL_ASSERT
1503 #undef LOAD_OP_DATA
1504
1505 #endif /* JS_HAS_DESTRUCTURING */
1506
1507 /*
1508 * If nb is non-negative, decompile nb bytecodes starting at pc. Otherwise
1509 * the decompiler starts at pc and continues until it reaches an opcode for
1510 * which decompiling would result in the stack depth equaling -(nb + 1).
1511 */
1512 static jsbytecode *
Decompile(SprintStack * ss,jsbytecode * pc,intN nb)1513 Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
1514 {
1515 JSContext *cx;
1516 JSPrinter *jp, *jp2;
1517 jsbytecode *startpc, *endpc, *pc2, *done, *forelem_tail, *forelem_done;
1518 ptrdiff_t tail, todo, len, oplen, cond, next;
1519 JSOp op, lastop, saveop;
1520 const JSCodeSpec *cs;
1521 jssrcnote *sn, *sn2;
1522 const char *lval, *rval, *xval, *fmt;
1523 jsint i, argc;
1524 char **argv;
1525 jsatomid atomIndex;
1526 JSAtom *atom;
1527 JSObject *obj;
1528 JSFunction *fun;
1529 JSString *str;
1530 JSBool ok;
1531 #if JS_HAS_XML_SUPPORT
1532 JSBool foreach, inXML, quoteAttr;
1533 #else
1534 #define inXML JS_FALSE
1535 #endif
1536 jsval val;
1537 int stackDummy;
1538
1539 static const char exception_cookie[] = "/*EXCEPTION*/";
1540 static const char retsub_pc_cookie[] = "/*RETSUB_PC*/";
1541 static const char forelem_cookie[] = "/*FORELEM*/";
1542 static const char with_cookie[] = "/*WITH*/";
1543 static const char dot_format[] = "%s.%s";
1544 static const char index_format[] = "%s[%s]";
1545 static const char predot_format[] = "%s%s.%s";
1546 static const char postdot_format[] = "%s.%s%s";
1547 static const char preindex_format[] = "%s%s[%s]";
1548 static const char postindex_format[] = "%s[%s]%s";
1549 static const char ss_format[] = "%s%s";
1550
1551 /*
1552 * Local macros
1553 */
1554 #define DECOMPILE_CODE(pc,nb) if (!Decompile(ss, pc, nb)) return NULL
1555 #define POP_STR() PopStr(ss, op)
1556 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, JS_FALSE)
1557
1558 /*
1559 * Callers know that ATOM_IS_STRING(atom), and we leave it to the optimizer to
1560 * common ATOM_TO_STRING(atom) here and near the call sites.
1561 */
1562 #define ATOM_IS_IDENTIFIER(atom) js_IsIdentifier(ATOM_TO_STRING(atom))
1563 #define ATOM_IS_KEYWORD(atom) \
1564 (js_CheckKeyword(JSSTRING_CHARS(ATOM_TO_STRING(atom)), \
1565 JSSTRING_LENGTH(ATOM_TO_STRING(atom))) != TOK_EOF)
1566
1567 /*
1568 * Given an atom already fetched from jp->script's atom map, quote/escape its
1569 * string appropriately into rval, and select fmt from the quoted and unquoted
1570 * alternatives.
1571 */
1572 #define GET_QUOTE_AND_FMT(qfmt, ufmt, rval) \
1573 JS_BEGIN_MACRO \
1574 jschar quote_; \
1575 if (!ATOM_IS_IDENTIFIER(atom)) { \
1576 quote_ = '\''; \
1577 fmt = qfmt; \
1578 } else { \
1579 quote_ = 0; \
1580 fmt = ufmt; \
1581 } \
1582 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), quote_); \
1583 if (!rval) \
1584 return NULL; \
1585 JS_END_MACRO
1586
1587 /*
1588 * Get atom from jp->script's atom map, quote/escape its string appropriately
1589 * into rval, and select fmt from the quoted and unquoted alternatives.
1590 */
1591 #define GET_ATOM_QUOTE_AND_FMT(qfmt, ufmt, rval) \
1592 JS_BEGIN_MACRO \
1593 atom = GET_ATOM(cx, jp->script, pc); \
1594 GET_QUOTE_AND_FMT(qfmt, ufmt, rval); \
1595 JS_END_MACRO
1596
1597 cx = ss->sprinter.context;
1598 if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
1599 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
1600 return NULL;
1601 }
1602
1603 jp = ss->printer;
1604 startpc = pc;
1605 endpc = (nb < 0) ? jp->script->code + jp->script->length : pc + nb;
1606 forelem_tail = forelem_done = NULL;
1607 tail = -1;
1608 todo = -2; /* NB: different from Sprint() error return. */
1609 saveop = JSOP_NOP;
1610 sn = NULL;
1611 rval = NULL;
1612 #if JS_HAS_XML_SUPPORT
1613 foreach = inXML = quoteAttr = JS_FALSE;
1614 #endif
1615
1616 while (nb < 0 || pc < endpc) {
1617 /*
1618 * Move saveop to lastop so prefixed bytecodes can take special action
1619 * while sharing maximal code. Set op and saveop to the new bytecode,
1620 * use op in POP_STR to trigger automatic parenthesization, but push
1621 * saveop at the bottom of the loop if this op pushes. Thus op may be
1622 * set to nop or otherwise mutated to suppress auto-parens.
1623 */
1624 lastop = saveop;
1625 op = saveop = (JSOp) *pc;
1626 cs = &js_CodeSpec[saveop];
1627 len = oplen = cs->length;
1628
1629 if (nb < 0 && -(nb + 1) == (intN)ss->top - cs->nuses + cs->ndefs)
1630 return pc;
1631
1632 if (pc + oplen == jp->dvgfence) {
1633 JSStackFrame *fp;
1634 uint32 format, mode, type;
1635
1636 /*
1637 * Rewrite non-get ops to their "get" format if the error is in
1638 * the bytecode at pc, so we don't decompile more than the error
1639 * expression.
1640 */
1641 for (fp = cx->fp; fp && !fp->script; fp = fp->down)
1642 continue;
1643 format = cs->format;
1644 if (((fp && pc == fp->pc) ||
1645 (pc == startpc && cs->nuses != 0)) &&
1646 format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_IMPORT|JOF_FOR)) {
1647 mode = (format & JOF_MODEMASK);
1648 if (mode == JOF_NAME) {
1649 /*
1650 * JOF_NAME does not imply JOF_CONST, so we must check for
1651 * the QARG and QVAR format types, and translate those to
1652 * JSOP_GETARG or JSOP_GETVAR appropriately, instead of to
1653 * JSOP_NAME.
1654 */
1655 type = format & JOF_TYPEMASK;
1656 op = (type == JOF_QARG)
1657 ? JSOP_GETARG
1658 : (type == JOF_QVAR)
1659 ? JSOP_GETVAR
1660 : (type == JOF_LOCAL)
1661 ? JSOP_GETLOCAL
1662 : JSOP_NAME;
1663
1664 i = cs->nuses - js_CodeSpec[op].nuses;
1665 while (--i >= 0)
1666 PopOff(ss, JSOP_NOP);
1667 } else {
1668 /*
1669 * We must replace the faulting pc's bytecode with a
1670 * corresponding JSOP_GET* code. For JSOP_SET{PROP,ELEM},
1671 * we must use the "2nd" form of JSOP_GET{PROP,ELEM}, to
1672 * throw away the assignment op's right-hand operand and
1673 * decompile it as if it were a GET of its left-hand
1674 * operand.
1675 */
1676 if (mode == JOF_PROP) {
1677 op = (format & JOF_SET) ? JSOP_GETPROP2 : JSOP_GETPROP;
1678 } else if (mode == JOF_ELEM) {
1679 op = (format & JOF_SET) ? JSOP_GETELEM2 : JSOP_GETELEM;
1680 } else {
1681 /*
1682 * Zero mode means precisely that op is uncategorized
1683 * for our purposes, so we must write per-op special
1684 * case code here.
1685 */
1686 switch (op) {
1687 case JSOP_ENUMELEM:
1688 case JSOP_ENUMCONSTELEM:
1689 op = JSOP_GETELEM;
1690 break;
1691 #if JS_HAS_LVALUE_RETURN
1692 case JSOP_SETCALL:
1693 op = JSOP_CALL;
1694 break;
1695 #endif
1696 default:
1697 LOCAL_ASSERT(0);
1698 }
1699 }
1700 }
1701 }
1702
1703 saveop = op;
1704 if (op >= JSOP_LIMIT) {
1705 switch (op) {
1706 case JSOP_GETPROP2:
1707 saveop = JSOP_GETPROP;
1708 break;
1709 case JSOP_GETELEM2:
1710 saveop = JSOP_GETELEM;
1711 break;
1712 default:;
1713 }
1714 }
1715 LOCAL_ASSERT(js_CodeSpec[saveop].length == oplen);
1716
1717 jp->dvgfence = NULL;
1718 }
1719
1720 if (cs->token) {
1721 switch (cs->nuses) {
1722 case 2:
1723 sn = js_GetSrcNote(jp->script, pc);
1724 if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
1725 /*
1726 * Avoid over-parenthesizing y in x op= y based on its
1727 * expansion: x = x op y (replace y by z = w to see the
1728 * problem).
1729 */
1730 op = pc[oplen];
1731 LOCAL_ASSERT(op != saveop);
1732 }
1733 rval = POP_STR();
1734 lval = POP_STR();
1735 if (op != saveop) {
1736 /* Print only the right operand of the assignment-op. */
1737 todo = SprintCString(&ss->sprinter, rval);
1738 op = saveop;
1739 } else if (!inXML) {
1740 todo = Sprint(&ss->sprinter, "%s %s %s",
1741 lval, cs->token, rval);
1742 } else {
1743 /* In XML, just concatenate the two operands. */
1744 LOCAL_ASSERT(op == JSOP_ADD);
1745 todo = Sprint(&ss->sprinter, ss_format, lval, rval);
1746 }
1747 break;
1748
1749 case 1:
1750 rval = POP_STR();
1751 todo = Sprint(&ss->sprinter, ss_format, cs->token, rval);
1752 break;
1753
1754 case 0:
1755 todo = SprintCString(&ss->sprinter, cs->token);
1756 break;
1757
1758 default:
1759 todo = -2;
1760 break;
1761 }
1762 } else {
1763 switch (op) {
1764 #define BEGIN_LITOPX_CASE(OP) \
1765 case OP: \
1766 atomIndex = GET_ATOM_INDEX(pc); \
1767 do_##OP: \
1768 atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex);
1769
1770 #define END_LITOPX_CASE \
1771 break;
1772
1773 case JSOP_NOP:
1774 /*
1775 * Check for a do-while loop, a for-loop with an empty
1776 * initializer part, a labeled statement, a function
1777 * definition, or try/finally.
1778 */
1779 sn = js_GetSrcNote(jp->script, pc);
1780 todo = -2;
1781 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
1782 case SRC_WHILE:
1783 js_printf(SET_MAYBE_BRACE(jp), "\tdo {\n");
1784 jp->indent += 4;
1785 break;
1786
1787 case SRC_FOR:
1788 rval = "";
1789
1790 do_forloop:
1791 /* Skip the JSOP_NOP or JSOP_POP bytecode. */
1792 pc++;
1793
1794 /* Get the cond, next, and loop-closing tail offsets. */
1795 cond = js_GetSrcNoteOffset(sn, 0);
1796 next = js_GetSrcNoteOffset(sn, 1);
1797 tail = js_GetSrcNoteOffset(sn, 2);
1798 LOCAL_ASSERT(tail + GetJumpOffset(pc+tail, pc+tail) == 0);
1799
1800 /* Print the keyword and the possibly empty init-part. */
1801 js_printf(jp, "\tfor (%s;", rval);
1802
1803 if (pc[cond] == JSOP_IFEQ || pc[cond] == JSOP_IFEQX) {
1804 /* Decompile the loop condition. */
1805 DECOMPILE_CODE(pc, cond);
1806 js_printf(jp, " %s", POP_STR());
1807 }
1808
1809 /* Need a semicolon whether or not there was a cond. */
1810 js_puts(jp, ";");
1811
1812 if (pc[next] != JSOP_GOTO && pc[next] != JSOP_GOTOX) {
1813 /* Decompile the loop updater. */
1814 DECOMPILE_CODE(pc + next, tail - next - 1);
1815 js_printf(jp, " %s", POP_STR());
1816 }
1817
1818 /* Do the loop body. */
1819 js_printf(SET_MAYBE_BRACE(jp), ") {\n");
1820 jp->indent += 4;
1821 oplen = (cond) ? js_CodeSpec[pc[cond]].length : 0;
1822 DECOMPILE_CODE(pc + cond + oplen, next - cond - oplen);
1823 jp->indent -= 4;
1824 js_printf(jp, "\t}\n");
1825
1826 /* Set len so pc skips over the entire loop. */
1827 len = tail + js_CodeSpec[pc[tail]].length;
1828 break;
1829
1830 case SRC_LABEL:
1831 atom = js_GetAtom(cx, &jp->script->atomMap,
1832 (jsatomid) js_GetSrcNoteOffset(sn, 0));
1833 jp->indent -= 4;
1834 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1835 if (!rval)
1836 return NULL;
1837 RETRACT(&ss->sprinter, rval);
1838 js_printf(CLEAR_MAYBE_BRACE(jp), "\t%s:\n", rval);
1839 jp->indent += 4;
1840 break;
1841
1842 case SRC_LABELBRACE:
1843 atom = js_GetAtom(cx, &jp->script->atomMap,
1844 (jsatomid) js_GetSrcNoteOffset(sn, 0));
1845 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
1846 if (!rval)
1847 return NULL;
1848 RETRACT(&ss->sprinter, rval);
1849 js_printf(CLEAR_MAYBE_BRACE(jp), "\t%s: {\n", rval);
1850 jp->indent += 4;
1851 break;
1852
1853 case SRC_ENDBRACE:
1854 jp->indent -= 4;
1855 js_printf(jp, "\t}\n");
1856 break;
1857
1858 case SRC_FUNCDEF:
1859 atom = js_GetAtom(cx, &jp->script->atomMap,
1860 (jsatomid) js_GetSrcNoteOffset(sn, 0));
1861 LOCAL_ASSERT(ATOM_IS_OBJECT(atom));
1862 do_function:
1863 obj = ATOM_TO_OBJECT(atom);
1864 fun = (JSFunction *) JS_GetPrivate(cx, obj);
1865 jp2 = js_NewPrinter(cx, JS_GetFunctionName(fun),
1866 jp->indent, jp->pretty);
1867 if (!jp2)
1868 return NULL;
1869 jp2->scope = jp->scope;
1870 js_puts(jp2, "\n");
1871 ok = js_DecompileFunction(jp2, fun);
1872 if (ok) {
1873 js_puts(jp2, "\n");
1874 str = js_GetPrinterOutput(jp2);
1875 if (str)
1876 js_printf(jp, "%s\n", JS_GetStringBytes(str));
1877 else
1878 ok = JS_FALSE;
1879 }
1880 js_DestroyPrinter(jp2);
1881 if (!ok)
1882 return NULL;
1883
1884 break;
1885
1886 case SRC_BRACE:
1887 js_printf(CLEAR_MAYBE_BRACE(jp), "\t{\n");
1888 jp->indent += 4;
1889 len = js_GetSrcNoteOffset(sn, 0);
1890 DECOMPILE_CODE(pc + oplen, len - oplen);
1891 jp->indent -= 4;
1892 js_printf(jp, "\t}\n");
1893 break;
1894
1895 default:;
1896 }
1897 break;
1898
1899 case JSOP_GROUP:
1900 cs = &js_CodeSpec[lastop];
1901 if ((cs->prec != 0 &&
1902 cs->prec == js_CodeSpec[pc[JSOP_GROUP_LENGTH]].prec) ||
1903 pc[JSOP_GROUP_LENGTH] == JSOP_PUSHOBJ ||
1904 pc[JSOP_GROUP_LENGTH] == JSOP_DUP) {
1905 /*
1906 * Force parens if this JSOP_GROUP forced re-association
1907 * against precedence, or if this is a call or constructor
1908 * expression, or if it is destructured (JSOP_DUP).
1909 *
1910 * This is necessary to handle the operator new grammar,
1911 * by which new x(y).z means (new x(y))).z. For example
1912 * new (x(y).z) must decompile with the constructor
1913 * parenthesized, but normal precedence has JSOP_GETPROP
1914 * (for the final .z) higher than JSOP_NEW. In general,
1915 * if the call or constructor expression is parenthesized,
1916 * we preserve parens.
1917 */
1918 op = JSOP_NAME;
1919 rval = POP_STR();
1920 todo = SprintCString(&ss->sprinter, rval);
1921 } else {
1922 /*
1923 * Don't explicitly parenthesize -- just fix the top
1924 * opcode so that the auto-parens magic in PopOff can do
1925 * its thing.
1926 */
1927 LOCAL_ASSERT(ss->top != 0);
1928 ss->opcodes[ss->top-1] = saveop = lastop;
1929 todo = -2;
1930 }
1931 break;
1932
1933 case JSOP_STARTITER:
1934 todo = -2;
1935 break;
1936
1937 case JSOP_PUSH:
1938 #if JS_HAS_DESTRUCTURING
1939 sn = js_GetSrcNote(jp->script, pc);
1940 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
1941 pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo);
1942 if (!pc)
1943 return NULL;
1944 LOCAL_ASSERT(*pc == JSOP_SETSP);
1945 len = oplen = JSOP_SETSP_LENGTH;
1946 goto end_groupassignment;
1947 }
1948 #endif
1949 /* FALL THROUGH */
1950
1951 case JSOP_PUSHOBJ:
1952 case JSOP_BINDNAME:
1953 do_JSOP_BINDNAME:
1954 todo = Sprint(&ss->sprinter, "");
1955 break;
1956
1957 case JSOP_TRY:
1958 js_printf(CLEAR_MAYBE_BRACE(jp), "\ttry {\n");
1959 jp->indent += 4;
1960 todo = -2;
1961 break;
1962
1963 case JSOP_FINALLY:
1964 jp->indent -= 4;
1965 js_printf(CLEAR_MAYBE_BRACE(jp), "\t} finally {\n");
1966 jp->indent += 4;
1967
1968 /*
1969 * We must push an empty string placeholder for gosub's return
1970 * address, popped by JSOP_RETSUB and counted by script->depth
1971 * but not by ss->top (see JSOP_SETSP, below).
1972 */
1973 todo = Sprint(&ss->sprinter, exception_cookie);
1974 if (todo < 0 || !PushOff(ss, todo, op))
1975 return NULL;
1976 todo = Sprint(&ss->sprinter, retsub_pc_cookie);
1977 break;
1978
1979 case JSOP_RETSUB:
1980 rval = POP_STR();
1981 LOCAL_ASSERT(strcmp(rval, retsub_pc_cookie) == 0);
1982 lval = POP_STR();
1983 LOCAL_ASSERT(strcmp(lval, exception_cookie) == 0);
1984 todo = -2;
1985 break;
1986
1987 case JSOP_SWAP:
1988 /*
1989 * We don't generate this opcode currently, and previously we
1990 * did not need to decompile it. If old, serialized bytecode
1991 * uses it still, we should fall through and set todo = -2.
1992 */
1993 /* FALL THROUGH */
1994
1995 case JSOP_GOSUB:
1996 case JSOP_GOSUBX:
1997 /*
1998 * JSOP_GOSUB and GOSUBX have no effect on the decompiler's
1999 * string stack because the next op in bytecode order finds
2000 * the stack balanced by a JSOP_RETSUB executed elsewhere.
2001 */
2002 todo = -2;
2003 break;
2004
2005 case JSOP_SETSP:
2006 {
2007 uintN newtop, oldtop, i;
2008
2009 /*
2010 * The compiler models operand stack depth and fixes the stack
2011 * pointer on entry to a catch clause based on its depth model.
2012 * The decompiler must match the code generator's model, which
2013 * is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops.
2014 */
2015 newtop = (uintN) GET_UINT16(pc);
2016 oldtop = ss->top;
2017 LOCAL_ASSERT(newtop <= oldtop);
2018 todo = -2;
2019
2020 #if JS_HAS_DESTRUCTURING
2021 sn = js_GetSrcNote(jp->script, pc);
2022 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
2023 todo = Sprint(&ss->sprinter, "%s[] = [",
2024 VarPrefix(sn));
2025 if (todo < 0)
2026 return NULL;
2027 for (i = newtop; i < oldtop; i++) {
2028 rval = OFF2STR(&ss->sprinter, ss->offsets[i]);
2029 if (Sprint(&ss->sprinter, ss_format,
2030 (i == newtop) ? "" : ", ",
2031 (i == oldtop - 1 && *rval == '\0')
2032 ? ", " : rval) < 0) {
2033 return NULL;
2034 }
2035 }
2036 if (SprintPut(&ss->sprinter, "]", 1) < 0)
2037 return NULL;
2038
2039 /*
2040 * Kill newtop before the end_groupassignment: label by
2041 * retracting/popping early. Control will either jump to
2042 * do_forloop: or do_letheadbody: or else break from our
2043 * case JSOP_SETSP: after the switch (*pc2) below.
2044 */
2045 if (newtop < oldtop) {
2046 ss->sprinter.offset = GetOff(ss, newtop);
2047 ss->top = newtop;
2048 }
2049
2050 end_groupassignment:
2051 /*
2052 * Thread directly to the next opcode if we can, to handle
2053 * the special cases of a group assignment in the first or
2054 * last part of a for(;;) loop head, or in a let block or
2055 * expression head.
2056 *
2057 * NB: todo at this point indexes space in ss->sprinter
2058 * that is liable to be overwritten. The code below knows
2059 * exactly how long rval lives, or else copies it down via
2060 * SprintCString.
2061 */
2062 rval = OFF2STR(&ss->sprinter, todo);
2063 todo = -2;
2064 pc2 = pc + oplen;
2065 switch (*pc2) {
2066 case JSOP_NOP:
2067 /* First part of for(;;) or let block/expr head. */
2068 sn = js_GetSrcNote(jp->script, pc2);
2069 if (sn) {
2070 if (SN_TYPE(sn) == SRC_FOR) {
2071 pc = pc2;
2072 goto do_forloop;
2073 }
2074 if (SN_TYPE(sn) == SRC_DECL) {
2075 if (ss->top == jp->script->depth) {
2076 /*
2077 * This must be an empty destructuring
2078 * in the head of a let whose body block
2079 * is also empty.
2080 */
2081 pc = pc2 + 1;
2082 len = js_GetSrcNoteOffset(sn, 0);
2083 LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCK);
2084 js_printf(jp, "\tlet (%s) {\n", rval);
2085 js_printf(jp, "\t}\n");
2086 goto end_setsp;
2087 }
2088 todo = SprintCString(&ss->sprinter, rval);
2089 if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
2090 return NULL;
2091 op = JSOP_POP;
2092 pc = pc2 + 1;
2093 goto do_letheadbody;
2094 }
2095 }
2096 break;
2097
2098 case JSOP_GOTO:
2099 case JSOP_GOTOX:
2100 /* Third part of for(;;) loop head. */
2101 cond = GetJumpOffset(pc2, pc2);
2102 sn = js_GetSrcNote(jp->script, pc2 + cond - 1);
2103 if (sn && SN_TYPE(sn) == SRC_FOR) {
2104 todo = SprintCString(&ss->sprinter, rval);
2105 saveop = JSOP_NOP;
2106 }
2107 break;
2108 }
2109
2110 /*
2111 * If control flow reaches this point with todo still -2,
2112 * just print rval as an expression statement.
2113 */
2114 if (todo == -2)
2115 js_printf(jp, "\t%s;\n", rval);
2116 end_setsp:
2117 break;
2118 }
2119 #endif
2120 if (newtop < oldtop) {
2121 ss->sprinter.offset = GetOff(ss, newtop);
2122 ss->top = newtop;
2123 }
2124 break;
2125 }
2126
2127 case JSOP_EXCEPTION:
2128 /* The catch decompiler handles this op itself. */
2129 LOCAL_ASSERT(JS_FALSE);
2130 break;
2131
2132 case JSOP_POP:
2133 /*
2134 * By default, do not automatically parenthesize when popping
2135 * a stacked expression decompilation. We auto-parenthesize
2136 * only when JSOP_POP is annotated with SRC_PCDELTA, meaning
2137 * comma operator.
2138 */
2139 op = JSOP_POPV;
2140 /* FALL THROUGH */
2141
2142 case JSOP_POPV:
2143 sn = js_GetSrcNote(jp->script, pc);
2144 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
2145 case SRC_FOR:
2146 /* Force parens around 'in' expression at 'for' front. */
2147 if (ss->opcodes[ss->top-1] == JSOP_IN)
2148 op = JSOP_LSH;
2149 rval = POP_STR();
2150 todo = -2;
2151 goto do_forloop;
2152
2153 case SRC_PCDELTA:
2154 /* Comma operator: use JSOP_POP for correct precedence. */
2155 op = JSOP_POP;
2156
2157 /* Pop and save to avoid blowing stack depth budget. */
2158 lval = JS_strdup(cx, POP_STR());
2159 if (!lval)
2160 return NULL;
2161
2162 /*
2163 * The offset tells distance to the end of the right-hand
2164 * operand of the comma operator.
2165 */
2166 done = pc + len;
2167 pc += js_GetSrcNoteOffset(sn, 0);
2168 len = 0;
2169
2170 if (!Decompile(ss, done, pc - done)) {
2171 JS_free(cx, (char *)lval);
2172 return NULL;
2173 }
2174
2175 /* Pop Decompile result and print comma expression. */
2176 rval = POP_STR();
2177 todo = Sprint(&ss->sprinter, "%s, %s", lval, rval);
2178 JS_free(cx, (char *)lval);
2179 break;
2180
2181 case SRC_HIDDEN:
2182 /* Hide this pop, it's from a goto in a with or for/in. */
2183 todo = -2;
2184 break;
2185
2186 case SRC_DECL:
2187 /* This pop is at the end of the let block/expr head. */
2188 pc += JSOP_POP_LENGTH;
2189 #if JS_HAS_DESTRUCTURING
2190 do_letheadbody:
2191 #endif
2192 len = js_GetSrcNoteOffset(sn, 0);
2193 if (pc[len] == JSOP_LEAVEBLOCK) {
2194 js_printf(CLEAR_MAYBE_BRACE(jp), "\tlet (%s) {\n",
2195 POP_STR());
2196 jp->indent += 4;
2197 DECOMPILE_CODE(pc, len);
2198 jp->indent -= 4;
2199 js_printf(jp, "\t}\n");
2200 todo = -2;
2201 } else {
2202 LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCKEXPR);
2203
2204 lval = JS_strdup(cx, POP_STR());
2205 if (!lval)
2206 return NULL;
2207
2208 if (!Decompile(ss, pc, len)) {
2209 JS_free(cx, (char *)lval);
2210 return NULL;
2211 }
2212 rval = POP_STR();
2213 todo = Sprint(&ss->sprinter,
2214 (*rval == '{')
2215 ? "let (%s) (%s)"
2216 : "let (%s) %s",
2217 lval, rval);
2218 JS_free(cx, (char *)lval);
2219 }
2220 break;
2221
2222 default:
2223 /* Turn off parens around a yield statement. */
2224 if (ss->opcodes[ss->top-1] == JSOP_YIELD)
2225 op = JSOP_NOP;
2226
2227 rval = POP_STR();
2228 if (*rval != '\0') {
2229 #if JS_HAS_BLOCK_SCOPE
2230 /*
2231 * If a let declaration is the only child of a control
2232 * structure that does not require braces, it must not
2233 * be braced. If it were braced explicitly, it would
2234 * be bracketed by JSOP_ENTERBLOCK/JSOP_LEAVEBLOCK.
2235 */
2236 if (jp->braceState == MAYBE_BRACE &&
2237 pc + JSOP_POP_LENGTH == endpc &&
2238 !strncmp(rval, var_prefix[SRC_DECL_LET], 4) &&
2239 rval[4] != '(') {
2240 SetDontBrace(jp);
2241 }
2242 #endif
2243 js_printf(jp,
2244 (*rval == '{' ||
2245 (strncmp(rval, js_function_str, 8) == 0 &&
2246 rval[8] == ' '))
2247 ? "\t(%s);\n"
2248 : "\t%s;\n",
2249 rval);
2250 }
2251 todo = -2;
2252 break;
2253 }
2254 break;
2255
2256 case JSOP_POP2:
2257 case JSOP_ENDITER:
2258 sn = js_GetSrcNote(jp->script, pc);
2259 todo = -2;
2260 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
2261 break;
2262 (void) PopOff(ss, op);
2263 if (op == JSOP_POP2)
2264 (void) PopOff(ss, op);
2265 break;
2266
2267 case JSOP_ENTERWITH:
2268 LOCAL_ASSERT(!js_GetSrcNote(jp->script, pc));
2269 rval = POP_STR();
2270 js_printf(SET_MAYBE_BRACE(jp), "\twith (%s) {\n", rval);
2271 jp->indent += 4;
2272 todo = Sprint(&ss->sprinter, with_cookie);
2273 break;
2274
2275 case JSOP_LEAVEWITH:
2276 sn = js_GetSrcNote(jp->script, pc);
2277 todo = -2;
2278 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
2279 break;
2280 rval = POP_STR();
2281 LOCAL_ASSERT(strcmp(rval, with_cookie) == 0);
2282 jp->indent -= 4;
2283 js_printf(jp, "\t}\n");
2284 break;
2285
2286 BEGIN_LITOPX_CASE(JSOP_ENTERBLOCK)
2287 {
2288 JSAtom **atomv, *smallv[5];
2289 JSScopeProperty *sprop;
2290
2291 obj = ATOM_TO_OBJECT(atom);
2292 argc = OBJ_BLOCK_COUNT(cx, obj);
2293 if ((size_t)argc <= sizeof smallv / sizeof smallv[0]) {
2294 atomv = smallv;
2295 } else {
2296 atomv = (JSAtom **) JS_malloc(cx, argc * sizeof(JSAtom *));
2297 if (!atomv)
2298 return NULL;
2299 }
2300
2301 /* From here on, control must flow through enterblock_out. */
2302 for (sprop = OBJ_SCOPE(obj)->lastProp; sprop;
2303 sprop = sprop->parent) {
2304 if (!(sprop->flags & SPROP_HAS_SHORTID))
2305 continue;
2306 LOCAL_ASSERT(sprop->shortid < argc);
2307 atomv[sprop->shortid] = JSID_TO_ATOM(sprop->id);
2308 }
2309 ok = JS_TRUE;
2310 for (i = 0; i < argc; i++) {
2311 atom = atomv[i];
2312 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2313 if (!rval ||
2314 !PushOff(ss, STR2OFF(&ss->sprinter, rval), op)) {
2315 ok = JS_FALSE;
2316 goto enterblock_out;
2317 }
2318 }
2319
2320 sn = js_GetSrcNote(jp->script, pc);
2321 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
2322 #if JS_HAS_BLOCK_SCOPE
2323 case SRC_BRACE:
2324 js_printf(CLEAR_MAYBE_BRACE(jp), "\t{\n");
2325 jp->indent += 4;
2326 len = js_GetSrcNoteOffset(sn, 0);
2327 ok = Decompile(ss, pc + oplen, len - oplen) != NULL;
2328 if (!ok)
2329 goto enterblock_out;
2330 jp->indent -= 4;
2331 js_printf(jp, "\t}\n");
2332 break;
2333 #endif
2334
2335 case SRC_CATCH:
2336 jp->indent -= 4;
2337 js_printf(CLEAR_MAYBE_BRACE(jp), "\t} catch (");
2338
2339 pc2 = pc;
2340 pc += oplen;
2341 LOCAL_ASSERT(*pc == JSOP_EXCEPTION);
2342 pc += JSOP_EXCEPTION_LENGTH;
2343 if (*pc == JSOP_DUP) {
2344 sn2 = js_GetSrcNote(jp->script, pc);
2345 if (sn2 && SN_TYPE(sn2) == SRC_HIDDEN) {
2346 /*
2347 * This is a hidden dup to save the exception for
2348 * later. It must exist only when the catch has
2349 * an exception guard.
2350 */
2351 LOCAL_ASSERT(js_GetSrcNoteOffset(sn, 0) != 0);
2352 pc += JSOP_DUP_LENGTH;
2353 }
2354 }
2355 #if JS_HAS_DESTRUCTURING
2356 if (*pc == JSOP_DUP) {
2357 pc = DecompileDestructuring(ss, pc, endpc);
2358 if (!pc) {
2359 ok = JS_FALSE;
2360 goto enterblock_out;
2361 }
2362 LOCAL_ASSERT(*pc == JSOP_POP);
2363 pc += JSOP_POP_LENGTH;
2364 lval = PopStr(ss, JSOP_NOP);
2365 js_puts(jp, lval);
2366 } else {
2367 #endif
2368 LOCAL_ASSERT(*pc == JSOP_SETLOCALPOP);
2369 i = GET_UINT16(pc);
2370 pc += JSOP_SETLOCALPOP_LENGTH;
2371 atom = atomv[i - OBJ_BLOCK_DEPTH(cx, obj)];
2372 str = ATOM_TO_STRING(atom);
2373 if (!QuoteString(&jp->sprinter, str, 0)) {
2374 ok = JS_FALSE;
2375 goto enterblock_out;
2376 }
2377 #if JS_HAS_DESTRUCTURING
2378 }
2379 #endif
2380
2381 len = js_GetSrcNoteOffset(sn, 0);
2382 if (len) {
2383 len -= PTRDIFF(pc, pc2, jsbytecode);
2384 LOCAL_ASSERT(len > 0);
2385 js_printf(jp, " if ");
2386 ok = Decompile(ss, pc, len) != NULL;
2387 if (!ok)
2388 goto enterblock_out;
2389 js_printf(jp, "%s", POP_STR());
2390 pc += len;
2391 LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
2392 pc += js_CodeSpec[*pc].length;
2393 }
2394
2395 js_printf(jp, ") {\n");
2396 jp->indent += 4;
2397 len = 0;
2398 break;
2399 }
2400
2401 todo = -2;
2402
2403 enterblock_out:
2404 if (atomv != smallv)
2405 JS_free(cx, atomv);
2406 if (!ok)
2407 return NULL;
2408 }
2409 END_LITOPX_CASE
2410
2411 case JSOP_LEAVEBLOCK:
2412 case JSOP_LEAVEBLOCKEXPR:
2413 {
2414 uintN top, depth;
2415
2416 sn = js_GetSrcNote(jp->script, pc);
2417 todo = -2;
2418 if (op == JSOP_LEAVEBLOCKEXPR) {
2419 LOCAL_ASSERT(SN_TYPE(sn) == SRC_PCBASE);
2420 rval = POP_STR();
2421 } else if (sn) {
2422 LOCAL_ASSERT(op == JSOP_LEAVEBLOCK);
2423 if (SN_TYPE(sn) == SRC_HIDDEN)
2424 break;
2425 LOCAL_ASSERT(SN_TYPE(sn) == SRC_CATCH);
2426 LOCAL_ASSERT((uintN)js_GetSrcNoteOffset(sn, 0) == ss->top);
2427 }
2428 top = ss->top;
2429 depth = GET_UINT16(pc);
2430 LOCAL_ASSERT(top >= depth);
2431 top -= depth;
2432 ss->top = top;
2433 ss->sprinter.offset = GetOff(ss, top);
2434 if (op == JSOP_LEAVEBLOCKEXPR)
2435 todo = SprintCString(&ss->sprinter, rval);
2436 break;
2437 }
2438
2439 case JSOP_GETLOCAL:
2440 i = GET_UINT16(pc);
2441 sn = js_GetSrcNote(jp->script, pc);
2442 LOCAL_ASSERT((uintN)i < ss->top);
2443 rval = GetLocal(ss, i);
2444
2445 #if JS_HAS_DESTRUCTURING
2446 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
2447 pc = DecompileGroupAssignment(ss, pc, endpc, sn, &todo);
2448 if (!pc)
2449 return NULL;
2450 LOCAL_ASSERT(*pc == JSOP_SETSP);
2451 len = oplen = JSOP_SETSP_LENGTH;
2452 goto end_groupassignment;
2453 }
2454 #endif
2455
2456 todo = Sprint(&ss->sprinter, ss_format, VarPrefix(sn), rval);
2457 break;
2458
2459 case JSOP_SETLOCAL:
2460 case JSOP_SETLOCALPOP:
2461 i = GET_UINT16(pc);
2462 lval = GetStr(ss, i);
2463 rval = POP_STR();
2464 goto do_setlval;
2465
2466 case JSOP_INCLOCAL:
2467 case JSOP_DECLOCAL:
2468 i = GET_UINT16(pc);
2469 lval = GetLocal(ss, i);
2470 goto do_inclval;
2471
2472 case JSOP_LOCALINC:
2473 case JSOP_LOCALDEC:
2474 i = GET_UINT16(pc);
2475 lval = GetLocal(ss, i);
2476 goto do_lvalinc;
2477
2478 case JSOP_FORLOCAL:
2479 i = GET_UINT16(pc);
2480 lval = GetStr(ss, i);
2481 atom = NULL;
2482 goto do_forlvalinloop;
2483
2484 case JSOP_RETRVAL:
2485 todo = -2;
2486 break;
2487
2488 case JSOP_SETRVAL:
2489 case JSOP_RETURN:
2490 rval = POP_STR();
2491 if (*rval != '\0')
2492 js_printf(jp, "\t%s %s;\n", js_return_str, rval);
2493 else
2494 js_printf(jp, "\t%s;\n", js_return_str);
2495 todo = -2;
2496 break;
2497
2498 #if JS_HAS_GENERATORS
2499 case JSOP_YIELD:
2500 op = JSOP_SETNAME; /* turn off most parens */
2501 rval = POP_STR();
2502 todo = (*rval != '\0')
2503 ? Sprint(&ss->sprinter,
2504 (strncmp(rval, js_yield_str, 5) == 0 &&
2505 (rval[5] == ' ' || rval[5] == '\0'))
2506 ? "%s (%s)"
2507 : "%s %s",
2508 js_yield_str, rval)
2509 : SprintCString(&ss->sprinter, js_yield_str);
2510 break;
2511
2512 case JSOP_ARRAYPUSH:
2513 {
2514 uintN pos, blockpos, startpos;
2515 ptrdiff_t start;
2516
2517 rval = POP_STR();
2518 pos = ss->top;
2519 while ((op = ss->opcodes[--pos]) != JSOP_ENTERBLOCK &&
2520 op != JSOP_NEWINIT) {
2521 LOCAL_ASSERT(pos != 0);
2522 }
2523 blockpos = pos;
2524 while (ss->opcodes[pos] == JSOP_ENTERBLOCK) {
2525 if (pos == 0)
2526 break;
2527 --pos;
2528 }
2529 LOCAL_ASSERT(ss->opcodes[pos] == JSOP_NEWINIT);
2530 startpos = pos;
2531 start = ss->offsets[pos];
2532 LOCAL_ASSERT(ss->sprinter.base[start] == '[' ||
2533 ss->sprinter.base[start] == '#');
2534 pos = blockpos;
2535 while (ss->opcodes[++pos] == JSOP_STARTITER)
2536 LOCAL_ASSERT(pos < ss->top);
2537 LOCAL_ASSERT(pos < ss->top);
2538 xval = OFF2STR(&ss->sprinter, ss->offsets[pos]);
2539 lval = OFF2STR(&ss->sprinter, start);
2540 RETRACT(&ss->sprinter, lval);
2541 todo = Sprint(&ss->sprinter, "%s%s%.*s",
2542 lval, rval, rval - xval, xval);
2543 if (todo < 0)
2544 return NULL;
2545 ss->offsets[startpos] = todo;
2546 todo = -2;
2547 break;
2548 }
2549 #endif
2550
2551 case JSOP_THROWING:
2552 todo = -2;
2553 break;
2554
2555 case JSOP_THROW:
2556 sn = js_GetSrcNote(jp->script, pc);
2557 todo = -2;
2558 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
2559 break;
2560 rval = POP_STR();
2561 js_printf(jp, "\t%s %s;\n", cs->name, rval);
2562 break;
2563
2564 case JSOP_GOTO:
2565 case JSOP_GOTOX:
2566 sn = js_GetSrcNote(jp->script, pc);
2567 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
2568 case SRC_CONT2LABEL:
2569 atom = js_GetAtom(cx, &jp->script->atomMap,
2570 (jsatomid) js_GetSrcNoteOffset(sn, 0));
2571 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2572 if (!rval)
2573 return NULL;
2574 RETRACT(&ss->sprinter, rval);
2575 js_printf(jp, "\tcontinue %s;\n", rval);
2576 break;
2577 case SRC_CONTINUE:
2578 js_printf(jp, "\tcontinue;\n");
2579 break;
2580 case SRC_BREAK2LABEL:
2581 atom = js_GetAtom(cx, &jp->script->atomMap,
2582 (jsatomid) js_GetSrcNoteOffset(sn, 0));
2583 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2584 if (!rval)
2585 return NULL;
2586 RETRACT(&ss->sprinter, rval);
2587 js_printf(jp, "\tbreak %s;\n", rval);
2588 break;
2589 case SRC_HIDDEN:
2590 break;
2591 default:
2592 js_printf(jp, "\tbreak;\n");
2593 break;
2594 }
2595 todo = -2;
2596 break;
2597
2598 case JSOP_IFEQ:
2599 case JSOP_IFEQX:
2600 {
2601 JSBool elseif = JS_FALSE;
2602
2603 if_again:
2604 len = GetJumpOffset(pc, pc);
2605 sn = js_GetSrcNote(jp->script, pc);
2606
2607 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
2608 case SRC_IF:
2609 case SRC_IF_ELSE:
2610 op = JSOP_NOP; /* turn off parens */
2611 rval = POP_STR();
2612 if (ss->inArrayInit) {
2613 LOCAL_ASSERT(SN_TYPE(sn) == SRC_IF);
2614 if (Sprint(&ss->sprinter, " if (%s)", rval) < 0)
2615 return NULL;
2616 } else {
2617 js_printf(SET_MAYBE_BRACE(jp),
2618 elseif ? " if (%s) {\n" : "\tif (%s) {\n",
2619 rval);
2620 jp->indent += 4;
2621 }
2622
2623 if (SN_TYPE(sn) == SRC_IF) {
2624 DECOMPILE_CODE(pc + oplen, len - oplen);
2625 } else {
2626 LOCAL_ASSERT(!ss->inArrayInit);
2627 tail = js_GetSrcNoteOffset(sn, 0);
2628 DECOMPILE_CODE(pc + oplen, tail - oplen);
2629 jp->indent -= 4;
2630 pc += tail;
2631 LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX);
2632 oplen = js_CodeSpec[*pc].length;
2633 len = GetJumpOffset(pc, pc);
2634 js_printf(jp, "\t} else");
2635
2636 /*
2637 * If the second offset for sn is non-zero, it tells
2638 * the distance from the goto around the else, to the
2639 * ifeq for the if inside the else that forms an "if
2640 * else if" chain. Thus cond spans the condition of
2641 * the second if, so we simply decompile it and start
2642 * over at label if_again.
2643 */
2644 cond = js_GetSrcNoteOffset(sn, 1);
2645 if (cond != 0) {
2646 DECOMPILE_CODE(pc + oplen, cond - oplen);
2647 pc += cond;
2648 elseif = JS_TRUE;
2649 goto if_again;
2650 }
2651
2652 js_printf(SET_MAYBE_BRACE(jp), " {\n");
2653 jp->indent += 4;
2654 DECOMPILE_CODE(pc + oplen, len - oplen);
2655 }
2656
2657 if (!ss->inArrayInit) {
2658 jp->indent -= 4;
2659 js_printf(jp, "\t}\n");
2660 }
2661 todo = -2;
2662 break;
2663
2664 case SRC_WHILE:
2665 rval = POP_STR();
2666 js_printf(SET_MAYBE_BRACE(jp), "\twhile (%s) {\n", rval);
2667 jp->indent += 4;
2668 tail = js_GetSrcNoteOffset(sn, 0);
2669 DECOMPILE_CODE(pc + oplen, tail - oplen);
2670 jp->indent -= 4;
2671 js_printf(jp, "\t}\n");
2672 todo = -2;
2673 break;
2674
2675 case SRC_COND:
2676 xval = JS_strdup(cx, POP_STR());
2677 if (!xval)
2678 return NULL;
2679 len = js_GetSrcNoteOffset(sn, 0);
2680 DECOMPILE_CODE(pc + oplen, len - oplen);
2681 lval = JS_strdup(cx, POP_STR());
2682 if (!lval) {
2683 JS_free(cx, (void *)xval);
2684 return NULL;
2685 }
2686 pc += len;
2687 LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX);
2688 oplen = js_CodeSpec[*pc].length;
2689 len = GetJumpOffset(pc, pc);
2690 DECOMPILE_CODE(pc + oplen, len - oplen);
2691 rval = POP_STR();
2692 todo = Sprint(&ss->sprinter, "%s ? %s : %s",
2693 xval, lval, rval);
2694 JS_free(cx, (void *)xval);
2695 JS_free(cx, (void *)lval);
2696 break;
2697
2698 default:
2699 break;
2700 }
2701 break;
2702 }
2703
2704 case JSOP_IFNE:
2705 case JSOP_IFNEX:
2706 /* Currently, this must be a do-while loop's upward branch. */
2707 jp->indent -= 4;
2708 js_printf(jp, "\t} while (%s);\n", POP_STR());
2709 todo = -2;
2710 break;
2711
2712 case JSOP_OR:
2713 case JSOP_ORX:
2714 xval = "||";
2715
2716 do_logical_connective:
2717 /* Top of stack is the first clause in a disjunction (||). */
2718 lval = JS_strdup(cx, POP_STR());
2719 if (!lval)
2720 return NULL;
2721 done = pc + GetJumpOffset(pc, pc);
2722 pc += len;
2723 len = PTRDIFF(done, pc, jsbytecode);
2724 DECOMPILE_CODE(pc, len);
2725 rval = POP_STR();
2726 if (jp->pretty &&
2727 jp->indent + 4 + strlen(lval) + 4 + strlen(rval) > 75) {
2728 rval = JS_strdup(cx, rval);
2729 if (!rval) {
2730 tail = -1;
2731 } else {
2732 todo = Sprint(&ss->sprinter, "%s %s\n", lval, xval);
2733 tail = Sprint(&ss->sprinter, "%*s%s",
2734 jp->indent + 4, "", rval);
2735 JS_free(cx, (char *)rval);
2736 }
2737 if (tail < 0)
2738 todo = -1;
2739 } else {
2740 todo = Sprint(&ss->sprinter, "%s %s %s", lval, xval, rval);
2741 }
2742 JS_free(cx, (char *)lval);
2743 break;
2744
2745 case JSOP_AND:
2746 case JSOP_ANDX:
2747 xval = "&&";
2748 goto do_logical_connective;
2749
2750 case JSOP_FORARG:
2751 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
2752 LOCAL_ASSERT(atom);
2753 goto do_fornameinloop;
2754
2755 case JSOP_FORVAR:
2756 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
2757 LOCAL_ASSERT(atom);
2758 goto do_fornameinloop;
2759
2760 case JSOP_FORNAME:
2761 atom = GET_ATOM(cx, jp->script, pc);
2762
2763 do_fornameinloop:
2764 lval = "";
2765 do_forlvalinloop:
2766 sn = js_GetSrcNote(jp->script, pc);
2767 xval = NULL;
2768 goto do_forinloop;
2769
2770 case JSOP_FORPROP:
2771 xval = NULL;
2772 atom = GET_ATOM(cx, jp->script, pc);
2773 if (!ATOM_IS_IDENTIFIER(atom)) {
2774 xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
2775 (jschar)'\'');
2776 if (!xval)
2777 return NULL;
2778 atom = NULL;
2779 }
2780 lval = POP_STR();
2781 sn = NULL;
2782
2783 do_forinloop:
2784 pc += oplen;
2785 LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
2786 oplen = js_CodeSpec[*pc].length;
2787 len = GetJumpOffset(pc, pc);
2788 sn2 = js_GetSrcNote(jp->script, pc);
2789 tail = js_GetSrcNoteOffset(sn2, 0);
2790
2791 do_forinhead:
2792 if (!atom && xval) {
2793 /*
2794 * If xval is not a dummy empty string, we have to strdup
2795 * it to save it from being clobbered by the first Sprint
2796 * below. Standard dumb decompiler operating procedure!
2797 */
2798 if (*xval == '\0') {
2799 xval = NULL;
2800 } else {
2801 xval = JS_strdup(cx, xval);
2802 if (!xval)
2803 return NULL;
2804 }
2805 }
2806
2807 #if JS_HAS_XML_SUPPORT
2808 if (foreach) {
2809 foreach = JS_FALSE;
2810 todo = Sprint(&ss->sprinter, "for %s (%s%s",
2811 js_each_str, VarPrefix(sn), lval);
2812 } else
2813 #endif
2814 {
2815 todo = Sprint(&ss->sprinter, "for (%s%s",
2816 VarPrefix(sn), lval);
2817 }
2818 if (atom) {
2819 if (*lval && SprintPut(&ss->sprinter, ".", 1) < 0)
2820 return NULL;
2821 xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2822 if (!xval)
2823 return NULL;
2824 } else if (xval) {
2825 LOCAL_ASSERT(*xval != '\0');
2826 ok = (Sprint(&ss->sprinter,
2827 (js_CodeSpec[lastop].format & JOF_XMLNAME)
2828 ? ".%s"
2829 : "[%s]",
2830 xval)
2831 >= 0);
2832 JS_free(cx, (char *)xval);
2833 if (!ok)
2834 return NULL;
2835 }
2836 if (todo < 0)
2837 return NULL;
2838
2839 lval = OFF2STR(&ss->sprinter, todo);
2840 rval = GetStr(ss, ss->top-1);
2841 RETRACT(&ss->sprinter, rval);
2842 if (ss->inArrayInit) {
2843 todo = Sprint(&ss->sprinter, " %s in %s)", lval, rval);
2844 if (todo < 0)
2845 return NULL;
2846 ss->offsets[ss->top-1] = todo;
2847 ss->sprinter.offset += PAREN_SLOP;
2848 DECOMPILE_CODE(pc + oplen, tail - oplen);
2849 } else {
2850 js_printf(SET_MAYBE_BRACE(jp), "\t%s in %s) {\n",
2851 lval, rval);
2852 jp->indent += 4;
2853 DECOMPILE_CODE(pc + oplen, tail - oplen);
2854 jp->indent -= 4;
2855 js_printf(jp, "\t}\n");
2856 }
2857 todo = -2;
2858 break;
2859
2860 case JSOP_FORELEM:
2861 pc++;
2862 LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
2863 len = js_CodeSpec[*pc].length;
2864
2865 /*
2866 * Arrange for the JSOP_ENUMELEM case to set tail for use by
2867 * do_forinhead: code that uses on it to find the loop-closing
2868 * jump (whatever its format, normal or extended), in order to
2869 * bound the recursively decompiled loop body.
2870 */
2871 sn = js_GetSrcNote(jp->script, pc);
2872 LOCAL_ASSERT(!forelem_tail);
2873 forelem_tail = pc + js_GetSrcNoteOffset(sn, 0);
2874
2875 /*
2876 * This gets a little wacky. Only the length of the for loop
2877 * body PLUS the element-indexing expression is known here, so
2878 * we pass the after-loop pc to the JSOP_ENUMELEM case, which
2879 * is immediately below, to decompile that helper bytecode via
2880 * the 'forelem_done' local.
2881 *
2882 * Since a for..in loop can't nest in the head of another for
2883 * loop, we can use forelem_{tail,done} singletons to remember
2884 * state from JSOP_FORELEM to JSOP_ENUMELEM, thence (via goto)
2885 * to label do_forinhead.
2886 */
2887 LOCAL_ASSERT(!forelem_done);
2888 forelem_done = pc + GetJumpOffset(pc, pc);
2889
2890 /* Our net stack balance after forelem;ifeq is +1. */
2891 todo = SprintCString(&ss->sprinter, forelem_cookie);
2892 break;
2893
2894 case JSOP_ENUMELEM:
2895 case JSOP_ENUMCONSTELEM:
2896 /*
2897 * The stack has the object under the (top) index expression.
2898 * The "rval" property id is underneath those two on the stack.
2899 * The for loop body net and gross lengths can now be adjusted
2900 * to account for the length of the indexing expression that
2901 * came after JSOP_FORELEM and before JSOP_ENUMELEM.
2902 */
2903 atom = NULL;
2904 xval = POP_STR();
2905 op = JSOP_GETELEM; /* lval must have high precedence */
2906 lval = POP_STR();
2907 op = saveop;
2908 rval = POP_STR();
2909 LOCAL_ASSERT(strcmp(rval, forelem_cookie) == 0);
2910 LOCAL_ASSERT(forelem_tail > pc);
2911 tail = forelem_tail - pc;
2912 forelem_tail = NULL;
2913 LOCAL_ASSERT(forelem_done > pc);
2914 len = forelem_done - pc;
2915 forelem_done = NULL;
2916 goto do_forinhead;
2917
2918 #if JS_HAS_GETTER_SETTER
2919 case JSOP_GETTER:
2920 case JSOP_SETTER:
2921 todo = -2;
2922 break;
2923 #endif
2924
2925 case JSOP_DUP2:
2926 rval = GetStr(ss, ss->top-2);
2927 todo = SprintCString(&ss->sprinter, rval);
2928 if (todo < 0 || !PushOff(ss, todo, ss->opcodes[ss->top-2]))
2929 return NULL;
2930 /* FALL THROUGH */
2931
2932 case JSOP_DUP:
2933 #if JS_HAS_DESTRUCTURING
2934 sn = js_GetSrcNote(jp->script, pc);
2935 if (sn) {
2936 LOCAL_ASSERT(SN_TYPE(sn) == SRC_DESTRUCT);
2937 pc = DecompileDestructuring(ss, pc, endpc);
2938 if (!pc)
2939 return NULL;
2940 len = 0;
2941 lval = POP_STR();
2942 op = saveop = JSOP_ENUMELEM;
2943 rval = POP_STR();
2944
2945 if (strcmp(rval, forelem_cookie) == 0) {
2946 LOCAL_ASSERT(forelem_tail > pc);
2947 tail = forelem_tail - pc;
2948 forelem_tail = NULL;
2949 LOCAL_ASSERT(forelem_done > pc);
2950 len = forelem_done - pc;
2951 forelem_done = NULL;
2952 xval = NULL;
2953 atom = NULL;
2954
2955 /*
2956 * Null sn if this is a 'for (var [k, v] = i in o)'
2957 * loop, because 'var [k, v = i;' has already been
2958 * hoisted.
2959 */
2960 if (js_GetSrcNoteOffset(sn, 0) == SRC_DECL_VAR)
2961 sn = NULL;
2962 goto do_forinhead;
2963 }
2964
2965 todo = Sprint(&ss->sprinter, "%s%s = %s",
2966 VarPrefix(sn), lval, rval);
2967 break;
2968 }
2969 #endif
2970
2971 rval = GetStr(ss, ss->top-1);
2972 saveop = ss->opcodes[ss->top-1];
2973 todo = SprintCString(&ss->sprinter, rval);
2974 break;
2975
2976 case JSOP_SETARG:
2977 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
2978 LOCAL_ASSERT(atom);
2979 goto do_setname;
2980
2981 case JSOP_SETVAR:
2982 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
2983 LOCAL_ASSERT(atom);
2984 goto do_setname;
2985
2986 case JSOP_SETCONST:
2987 case JSOP_SETNAME:
2988 case JSOP_SETGVAR:
2989 atomIndex = GET_ATOM_INDEX(pc);
2990
2991 do_JSOP_SETCONST:
2992 atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex);
2993
2994 do_setname:
2995 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
2996 if (!lval)
2997 return NULL;
2998 rval = POP_STR();
2999 if (op == JSOP_SETNAME)
3000 (void) PopOff(ss, op);
3001
3002 do_setlval:
3003 sn = js_GetSrcNote(jp->script, pc - 1);
3004 if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
3005 todo = Sprint(&ss->sprinter, "%s %s= %s",
3006 lval,
3007 (lastop == JSOP_GETTER)
3008 ? js_getter_str
3009 : (lastop == JSOP_SETTER)
3010 ? js_setter_str
3011 : js_CodeSpec[lastop].token,
3012 rval);
3013 } else {
3014 sn = js_GetSrcNote(jp->script, pc);
3015 todo = Sprint(&ss->sprinter, "%s%s = %s",
3016 VarPrefix(sn), lval, rval);
3017 }
3018 if (op == JSOP_SETLOCALPOP) {
3019 if (!PushOff(ss, todo, saveop))
3020 return NULL;
3021 rval = POP_STR();
3022 LOCAL_ASSERT(*rval != '\0');
3023 js_printf(jp, "\t%s;\n", rval);
3024 todo = -2;
3025 }
3026 break;
3027
3028 case JSOP_NEW:
3029 case JSOP_CALL:
3030 case JSOP_EVAL:
3031 #if JS_HAS_LVALUE_RETURN
3032 case JSOP_SETCALL:
3033 #endif
3034 op = JSOP_SETNAME; /* turn off most parens */
3035 argc = GET_ARGC(pc);
3036 argv = (char **)
3037 JS_malloc(cx, (size_t)(argc + 1) * sizeof *argv);
3038 if (!argv)
3039 return NULL;
3040
3041 ok = JS_TRUE;
3042 for (i = argc; i > 0; i--) {
3043 argv[i] = JS_strdup(cx, POP_STR());
3044 if (!argv[i]) {
3045 ok = JS_FALSE;
3046 break;
3047 }
3048 }
3049
3050 /* Skip the JSOP_PUSHOBJ-created empty string. */
3051 LOCAL_ASSERT(ss->top >= 2);
3052 (void) PopOff(ss, op);
3053
3054 op = saveop;
3055 argv[0] = JS_strdup(cx, POP_STR());
3056 if (!argv[i])
3057 ok = JS_FALSE;
3058
3059 lval = "(", rval = ")";
3060 if (op == JSOP_NEW) {
3061 if (argc == 0)
3062 lval = rval = "";
3063 todo = Sprint(&ss->sprinter, "%s %s%s",
3064 js_new_str, argv[0], lval);
3065 } else {
3066 todo = Sprint(&ss->sprinter, ss_format,
3067 argv[0], lval);
3068 }
3069 if (todo < 0)
3070 ok = JS_FALSE;
3071
3072 for (i = 1; i <= argc; i++) {
3073 if (!argv[i] ||
3074 Sprint(&ss->sprinter, ss_format,
3075 argv[i], (i < argc) ? ", " : "") < 0) {
3076 ok = JS_FALSE;
3077 break;
3078 }
3079 }
3080 if (Sprint(&ss->sprinter, rval) < 0)
3081 ok = JS_FALSE;
3082
3083 for (i = 0; i <= argc; i++) {
3084 if (argv[i])
3085 JS_free(cx, argv[i]);
3086 }
3087 JS_free(cx, argv);
3088 if (!ok)
3089 return NULL;
3090 #if JS_HAS_LVALUE_RETURN
3091 if (op == JSOP_SETCALL) {
3092 if (!PushOff(ss, todo, op))
3093 return NULL;
3094 todo = Sprint(&ss->sprinter, "");
3095 }
3096 #endif
3097 break;
3098
3099 case JSOP_DELNAME:
3100 atom = GET_ATOM(cx, jp->script, pc);
3101 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3102 if (!lval)
3103 return NULL;
3104 RETRACT(&ss->sprinter, lval);
3105 do_delete_lval:
3106 todo = Sprint(&ss->sprinter, "%s %s", js_delete_str, lval);
3107 break;
3108
3109 case JSOP_DELPROP:
3110 GET_ATOM_QUOTE_AND_FMT("%s %s[%s]", "%s %s.%s", rval);
3111 lval = POP_STR();
3112 todo = Sprint(&ss->sprinter, fmt, js_delete_str, lval, rval);
3113 break;
3114
3115 case JSOP_DELELEM:
3116 op = JSOP_NOP; /* turn off parens */
3117 xval = POP_STR();
3118 op = saveop;
3119 lval = POP_STR();
3120 if (*xval == '\0')
3121 goto do_delete_lval;
3122 todo = Sprint(&ss->sprinter,
3123 (js_CodeSpec[lastop].format & JOF_XMLNAME)
3124 ? "%s %s.%s"
3125 : "%s %s[%s]",
3126 js_delete_str, lval, xval);
3127 break;
3128
3129 #if JS_HAS_XML_SUPPORT
3130 case JSOP_DELDESC:
3131 xval = POP_STR();
3132 lval = POP_STR();
3133 todo = Sprint(&ss->sprinter, "%s %s..%s",
3134 js_delete_str, lval, xval);
3135 break;
3136 #endif
3137
3138 case JSOP_TYPEOFEXPR:
3139 case JSOP_TYPEOF:
3140 case JSOP_VOID:
3141 rval = POP_STR();
3142 todo = Sprint(&ss->sprinter, "%s %s", cs->name, rval);
3143 break;
3144
3145 case JSOP_INCARG:
3146 case JSOP_DECARG:
3147 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
3148 LOCAL_ASSERT(atom);
3149 goto do_incatom;
3150
3151 case JSOP_INCVAR:
3152 case JSOP_DECVAR:
3153 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
3154 LOCAL_ASSERT(atom);
3155 goto do_incatom;
3156
3157 case JSOP_INCNAME:
3158 case JSOP_DECNAME:
3159 case JSOP_INCGVAR:
3160 case JSOP_DECGVAR:
3161 atom = GET_ATOM(cx, jp->script, pc);
3162 do_incatom:
3163 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3164 if (!lval)
3165 return NULL;
3166 RETRACT(&ss->sprinter, lval);
3167 do_inclval:
3168 todo = Sprint(&ss->sprinter, ss_format,
3169 js_incop_strs[!(cs->format & JOF_INC)], lval);
3170 break;
3171
3172 case JSOP_INCPROP:
3173 case JSOP_DECPROP:
3174 GET_ATOM_QUOTE_AND_FMT(preindex_format, predot_format, rval);
3175
3176 /*
3177 * Force precedence below the numeric literal opcodes, so that
3178 * 42..foo or 10000..toString(16), e.g., decompile with parens
3179 * around the left-hand side of dot.
3180 */
3181 op = JSOP_GETPROP;
3182 lval = POP_STR();
3183 todo = Sprint(&ss->sprinter, fmt,
3184 js_incop_strs[!(cs->format & JOF_INC)],
3185 lval, rval);
3186 break;
3187
3188 case JSOP_INCELEM:
3189 case JSOP_DECELEM:
3190 op = JSOP_NOP; /* turn off parens */
3191 xval = POP_STR();
3192 op = JSOP_GETELEM;
3193 lval = POP_STR();
3194 if (*xval != '\0') {
3195 todo = Sprint(&ss->sprinter,
3196 (js_CodeSpec[lastop].format & JOF_XMLNAME)
3197 ? predot_format
3198 : preindex_format,
3199 js_incop_strs[!(cs->format & JOF_INC)],
3200 lval, xval);
3201 } else {
3202 todo = Sprint(&ss->sprinter, ss_format,
3203 js_incop_strs[!(cs->format & JOF_INC)], lval);
3204 }
3205 break;
3206
3207 case JSOP_ARGINC:
3208 case JSOP_ARGDEC:
3209 atom = GetSlotAtom(jp, js_GetArgument, GET_ARGNO(pc));
3210 LOCAL_ASSERT(atom);
3211 goto do_atominc;
3212
3213 case JSOP_VARINC:
3214 case JSOP_VARDEC:
3215 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
3216 LOCAL_ASSERT(atom);
3217 goto do_atominc;
3218
3219 case JSOP_NAMEINC:
3220 case JSOP_NAMEDEC:
3221 case JSOP_GVARINC:
3222 case JSOP_GVARDEC:
3223 atom = GET_ATOM(cx, jp->script, pc);
3224 do_atominc:
3225 lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3226 if (!lval)
3227 return NULL;
3228 RETRACT(&ss->sprinter, lval);
3229 do_lvalinc:
3230 todo = Sprint(&ss->sprinter, ss_format,
3231 lval, js_incop_strs[!(cs->format & JOF_INC)]);
3232 break;
3233
3234 case JSOP_PROPINC:
3235 case JSOP_PROPDEC:
3236 GET_ATOM_QUOTE_AND_FMT(postindex_format, postdot_format, rval);
3237
3238 /*
3239 * Force precedence below the numeric literal opcodes, so that
3240 * 42..foo or 10000..toString(16), e.g., decompile with parens
3241 * around the left-hand side of dot.
3242 */
3243 op = JSOP_GETPROP;
3244 lval = POP_STR();
3245 todo = Sprint(&ss->sprinter, fmt, lval, rval,
3246 js_incop_strs[!(cs->format & JOF_INC)]);
3247 break;
3248
3249 case JSOP_ELEMINC:
3250 case JSOP_ELEMDEC:
3251 op = JSOP_NOP; /* turn off parens */
3252 xval = POP_STR();
3253 op = JSOP_GETELEM;
3254 lval = POP_STR();
3255 if (*xval != '\0') {
3256 todo = Sprint(&ss->sprinter,
3257 (js_CodeSpec[lastop].format & JOF_XMLNAME)
3258 ? postdot_format
3259 : postindex_format,
3260 lval, xval,
3261 js_incop_strs[!(cs->format & JOF_INC)]);
3262 } else {
3263 todo = Sprint(&ss->sprinter, ss_format,
3264 lval, js_incop_strs[!(cs->format & JOF_INC)]);
3265 }
3266 break;
3267
3268 case JSOP_GETPROP2:
3269 op = JSOP_GETPROP;
3270 (void) PopOff(ss, lastop);
3271 /* FALL THROUGH */
3272
3273 case JSOP_GETPROP:
3274 case JSOP_GETXPROP:
3275 atom = GET_ATOM(cx, jp->script, pc);
3276
3277 do_getprop:
3278 GET_QUOTE_AND_FMT(index_format, dot_format, rval);
3279
3280 do_getprop_lval:
3281 lval = POP_STR();
3282 todo = Sprint(&ss->sprinter, fmt, lval, rval);
3283 break;
3284
3285 #if JS_HAS_XML_SUPPORT
3286 BEGIN_LITOPX_CASE(JSOP_GETMETHOD)
3287 sn = js_GetSrcNote(jp->script, pc);
3288 if (sn && SN_TYPE(sn) == SRC_PCBASE)
3289 goto do_getprop;
3290 GET_QUOTE_AND_FMT("%s.function::[%s]", "%s.function::%s", rval);
3291 goto do_getprop_lval;
3292
3293 BEGIN_LITOPX_CASE(JSOP_SETMETHOD)
3294 sn = js_GetSrcNote(jp->script, pc);
3295 if (sn && SN_TYPE(sn) == SRC_PCBASE)
3296 goto do_setprop;
3297 GET_QUOTE_AND_FMT("%s.function::[%s] %s= %s",
3298 "%s.function::%s %s= %s",
3299 xval);
3300 goto do_setprop_rval;
3301 #endif
3302
3303 case JSOP_SETPROP:
3304 atom = GET_ATOM(cx, jp->script, pc);
3305
3306 do_setprop:
3307 GET_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval);
3308
3309 do_setprop_rval:
3310 rval = POP_STR();
3311
3312 /*
3313 * Force precedence below the numeric literal opcodes, so that
3314 * 42..foo or 10000..toString(16), e.g., decompile with parens
3315 * around the left-hand side of dot.
3316 */
3317 op = JSOP_GETPROP;
3318 lval = POP_STR();
3319 sn = js_GetSrcNote(jp->script, pc - 1);
3320 todo = Sprint(&ss->sprinter, fmt, lval, xval,
3321 (sn && SN_TYPE(sn) == SRC_ASSIGNOP)
3322 ? (lastop == JSOP_GETTER)
3323 ? js_getter_str
3324 : (lastop == JSOP_SETTER)
3325 ? js_setter_str
3326 : js_CodeSpec[lastop].token
3327 : "",
3328 rval);
3329 break;
3330
3331 case JSOP_GETELEM2:
3332 op = JSOP_GETELEM;
3333 (void) PopOff(ss, lastop);
3334 /* FALL THROUGH */
3335
3336 case JSOP_GETELEM:
3337 case JSOP_GETXELEM:
3338 op = JSOP_NOP; /* turn off parens */
3339 xval = POP_STR();
3340 op = saveop;
3341 lval = POP_STR();
3342 if (*xval == '\0') {
3343 todo = Sprint(&ss->sprinter, "%s", lval);
3344 } else {
3345 todo = Sprint(&ss->sprinter,
3346 (js_CodeSpec[lastop].format & JOF_XMLNAME)
3347 ? dot_format
3348 : index_format,
3349 lval, xval);
3350 }
3351 break;
3352
3353 case JSOP_SETELEM:
3354 rval = POP_STR();
3355 op = JSOP_NOP; /* turn off parens */
3356 xval = POP_STR();
3357 cs = &js_CodeSpec[ss->opcodes[ss->top]];
3358 op = JSOP_GETELEM; /* lval must have high precedence */
3359 lval = POP_STR();
3360 op = saveop;
3361 if (*xval == '\0')
3362 goto do_setlval;
3363 sn = js_GetSrcNote(jp->script, pc - 1);
3364 todo = Sprint(&ss->sprinter,
3365 (cs->format & JOF_XMLNAME)
3366 ? "%s.%s %s= %s"
3367 : "%s[%s] %s= %s",
3368 lval, xval,
3369 (sn && SN_TYPE(sn) == SRC_ASSIGNOP)
3370 ? (lastop == JSOP_GETTER)
3371 ? js_getter_str
3372 : (lastop == JSOP_SETTER)
3373 ? js_setter_str
3374 : js_CodeSpec[lastop].token
3375 : "",
3376 rval);
3377 break;
3378
3379 case JSOP_ARGSUB:
3380 i = (jsint) GET_ATOM_INDEX(pc);
3381 todo = Sprint(&ss->sprinter, "%s[%d]",
3382 js_arguments_str, (int) i);
3383 break;
3384
3385 case JSOP_ARGCNT:
3386 todo = Sprint(&ss->sprinter, dot_format,
3387 js_arguments_str, js_length_str);
3388 break;
3389
3390 case JSOP_GETARG:
3391 i = GET_ARGNO(pc);
3392 atom = GetSlotAtom(jp, js_GetArgument, i);
3393 #if JS_HAS_DESTRUCTURING
3394 if (!atom) {
3395 todo = Sprint(&ss->sprinter, "%s[%d]", js_arguments_str, i);
3396 break;
3397 }
3398 #else
3399 LOCAL_ASSERT(atom);
3400 #endif
3401 goto do_name;
3402
3403 case JSOP_GETVAR:
3404 atom = GetSlotAtom(jp, js_GetLocalVariable, GET_VARNO(pc));
3405 LOCAL_ASSERT(atom);
3406 goto do_name;
3407
3408 case JSOP_NAME:
3409 case JSOP_GETGVAR:
3410 atom = GET_ATOM(cx, jp->script, pc);
3411 do_name:
3412 lval = "";
3413 do_qname:
3414 sn = js_GetSrcNote(jp->script, pc);
3415 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3416 if (!rval)
3417 return NULL;
3418 RETRACT(&ss->sprinter, rval);
3419 todo = Sprint(&ss->sprinter, "%s%s%s",
3420 VarPrefix(sn), lval, rval);
3421 break;
3422
3423 case JSOP_UINT16:
3424 i = (jsint) GET_ATOM_INDEX(pc);
3425 goto do_sprint_int;
3426
3427 case JSOP_UINT24:
3428 i = (jsint) GET_UINT24(pc);
3429 do_sprint_int:
3430 todo = Sprint(&ss->sprinter, "%u", (unsigned) i);
3431 break;
3432
3433 case JSOP_LITERAL:
3434 atomIndex = GET_LITERAL_INDEX(pc);
3435 goto do_JSOP_STRING;
3436
3437 case JSOP_FINDNAME:
3438 atomIndex = GET_LITERAL_INDEX(pc);
3439 todo = Sprint(&ss->sprinter, "");
3440 if (todo < 0 || !PushOff(ss, todo, op))
3441 return NULL;
3442 atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex);
3443 goto do_name;
3444
3445 case JSOP_LITOPX:
3446 atomIndex = GET_LITERAL_INDEX(pc);
3447 pc2 = pc + 1 + LITERAL_INDEX_LEN;
3448 op = saveop = *pc2;
3449 pc += len - (1 + ATOM_INDEX_LEN);
3450 cs = &js_CodeSpec[op];
3451 len = cs->length;
3452 switch (op) {
3453 case JSOP_ANONFUNOBJ: goto do_JSOP_ANONFUNOBJ;
3454 case JSOP_BINDNAME: goto do_JSOP_BINDNAME;
3455 case JSOP_CLOSURE: goto do_JSOP_CLOSURE;
3456 #if JS_HAS_EXPORT_IMPORT
3457 case JSOP_EXPORTNAME: goto do_JSOP_EXPORTNAME;
3458 #endif
3459 #if JS_HAS_XML_SUPPORT
3460 case JSOP_GETMETHOD: goto do_JSOP_GETMETHOD;
3461 case JSOP_SETMETHOD: goto do_JSOP_SETMETHOD;
3462 #endif
3463 case JSOP_NAMEDFUNOBJ: goto do_JSOP_NAMEDFUNOBJ;
3464 case JSOP_NUMBER: goto do_JSOP_NUMBER;
3465 case JSOP_OBJECT: goto do_JSOP_OBJECT;
3466 #if JS_HAS_XML_SUPPORT
3467 case JSOP_QNAMECONST: goto do_JSOP_QNAMECONST;
3468 case JSOP_QNAMEPART: goto do_JSOP_QNAMEPART;
3469 #endif
3470 case JSOP_REGEXP: goto do_JSOP_REGEXP;
3471 case JSOP_SETCONST: goto do_JSOP_SETCONST;
3472 case JSOP_STRING: goto do_JSOP_STRING;
3473 #if JS_HAS_XML_SUPPORT
3474 case JSOP_XMLCDATA: goto do_JSOP_XMLCDATA;
3475 case JSOP_XMLCOMMENT: goto do_JSOP_XMLCOMMENT;
3476 case JSOP_XMLOBJECT: goto do_JSOP_XMLOBJECT;
3477 case JSOP_XMLPI: goto do_JSOP_XMLPI;
3478 #endif
3479 case JSOP_ENTERBLOCK: goto do_JSOP_ENTERBLOCK;
3480 default: LOCAL_ASSERT(0);
3481 }
3482 /* NOTREACHED */
3483 break;
3484
3485 BEGIN_LITOPX_CASE(JSOP_NUMBER)
3486 val = ATOM_KEY(atom);
3487 if (JSVAL_IS_INT(val)) {
3488 long ival = (long)JSVAL_TO_INT(val);
3489 todo = Sprint(&ss->sprinter, "%ld", ival);
3490 } else {
3491 char buf[DTOSTR_STANDARD_BUFFER_SIZE];
3492 char *numStr = JS_dtostr(buf, sizeof buf, DTOSTR_STANDARD,
3493 0, *JSVAL_TO_DOUBLE(val));
3494 if (!numStr) {
3495 JS_ReportOutOfMemory(cx);
3496 return NULL;
3497 }
3498 todo = Sprint(&ss->sprinter, numStr);
3499 }
3500 END_LITOPX_CASE
3501
3502 BEGIN_LITOPX_CASE(JSOP_STRING)
3503 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
3504 inXML ? DONT_ESCAPE : '"');
3505 if (!rval)
3506 return NULL;
3507 todo = STR2OFF(&ss->sprinter, rval);
3508 END_LITOPX_CASE
3509
3510 case JSOP_OBJECT:
3511 case JSOP_REGEXP:
3512 case JSOP_ANONFUNOBJ:
3513 case JSOP_NAMEDFUNOBJ:
3514 atomIndex = GET_ATOM_INDEX(pc);
3515
3516 do_JSOP_OBJECT:
3517 do_JSOP_REGEXP:
3518 do_JSOP_ANONFUNOBJ:
3519 do_JSOP_NAMEDFUNOBJ:
3520 atom = js_GetAtom(cx, &jp->script->atomMap, atomIndex);
3521 if (op == JSOP_OBJECT || op == JSOP_REGEXP) {
3522 if (!js_regexp_toString(cx, ATOM_TO_OBJECT(atom), 0, NULL,
3523 &val)) {
3524 return NULL;
3525 }
3526 } else {
3527 if (!js_fun_toString(cx, ATOM_TO_OBJECT(atom),
3528 JS_IN_GROUP_CONTEXT |
3529 JS_DONT_PRETTY_PRINT,
3530 0, NULL, &val)) {
3531 return NULL;
3532 }
3533 }
3534 str = JSVAL_TO_STRING(val);
3535 todo = SprintPut(&ss->sprinter, JS_GetStringBytes(str),
3536 JSSTRING_LENGTH(str));
3537 break;
3538
3539 case JSOP_TABLESWITCH:
3540 case JSOP_TABLESWITCHX:
3541 {
3542 ptrdiff_t jmplen, off, off2;
3543 jsint j, n, low, high;
3544 TableEntry *table, pivot;
3545
3546 sn = js_GetSrcNote(jp->script, pc);
3547 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
3548 len = js_GetSrcNoteOffset(sn, 0);
3549 jmplen = (op == JSOP_TABLESWITCH) ? JUMP_OFFSET_LEN
3550 : JUMPX_OFFSET_LEN;
3551 pc2 = pc;
3552 off = GetJumpOffset(pc, pc2);
3553 pc2 += jmplen;
3554 low = GET_JUMP_OFFSET(pc2);
3555 pc2 += JUMP_OFFSET_LEN;
3556 high = GET_JUMP_OFFSET(pc2);
3557 pc2 += JUMP_OFFSET_LEN;
3558
3559 n = high - low + 1;
3560 if (n == 0) {
3561 table = NULL;
3562 j = 0;
3563 } else {
3564 table = (TableEntry *)
3565 JS_malloc(cx, (size_t)n * sizeof *table);
3566 if (!table)
3567 return NULL;
3568 for (i = j = 0; i < n; i++) {
3569 table[j].label = NULL;
3570 off2 = GetJumpOffset(pc, pc2);
3571 if (off2) {
3572 sn = js_GetSrcNote(jp->script, pc2);
3573 if (sn) {
3574 LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL);
3575 table[j].label =
3576 js_GetAtom(cx, &jp->script->atomMap,
3577 (jsatomid)
3578 js_GetSrcNoteOffset(sn, 0));
3579 }
3580 table[j].key = INT_TO_JSVAL(low + i);
3581 table[j].offset = off2;
3582 table[j].order = j;
3583 j++;
3584 }
3585 pc2 += jmplen;
3586 }
3587 js_HeapSort(table, (size_t) j, &pivot, sizeof(TableEntry),
3588 CompareOffsets, NULL);
3589 }
3590
3591 ok = DecompileSwitch(ss, table, (uintN)j, pc, len, off,
3592 JS_FALSE);
3593 JS_free(cx, table);
3594 if (!ok)
3595 return NULL;
3596 todo = -2;
3597 break;
3598 }
3599
3600 case JSOP_LOOKUPSWITCH:
3601 case JSOP_LOOKUPSWITCHX:
3602 {
3603 ptrdiff_t jmplen, off, off2;
3604 jsatomid npairs, k;
3605 TableEntry *table;
3606
3607 sn = js_GetSrcNote(jp->script, pc);
3608 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
3609 len = js_GetSrcNoteOffset(sn, 0);
3610 jmplen = (op == JSOP_LOOKUPSWITCH) ? JUMP_OFFSET_LEN
3611 : JUMPX_OFFSET_LEN;
3612 pc2 = pc;
3613 off = GetJumpOffset(pc, pc2);
3614 pc2 += jmplen;
3615 npairs = GET_ATOM_INDEX(pc2);
3616 pc2 += ATOM_INDEX_LEN;
3617
3618 table = (TableEntry *)
3619 JS_malloc(cx, (size_t)npairs * sizeof *table);
3620 if (!table)
3621 return NULL;
3622 for (k = 0; k < npairs; k++) {
3623 sn = js_GetSrcNote(jp->script, pc2);
3624 if (sn) {
3625 LOCAL_ASSERT(SN_TYPE(sn) == SRC_LABEL);
3626 table[k].label =
3627 js_GetAtom(cx, &jp->script->atomMap, (jsatomid)
3628 js_GetSrcNoteOffset(sn, 0));
3629 } else {
3630 table[k].label = NULL;
3631 }
3632 atom = GET_ATOM(cx, jp->script, pc2);
3633 pc2 += ATOM_INDEX_LEN;
3634 off2 = GetJumpOffset(pc, pc2);
3635 pc2 += jmplen;
3636 table[k].key = ATOM_KEY(atom);
3637 table[k].offset = off2;
3638 }
3639
3640 ok = DecompileSwitch(ss, table, (uintN)npairs, pc, len, off,
3641 JS_FALSE);
3642 JS_free(cx, table);
3643 if (!ok)
3644 return NULL;
3645 todo = -2;
3646 break;
3647 }
3648
3649 case JSOP_CONDSWITCH:
3650 {
3651 ptrdiff_t off, off2, caseOff;
3652 jsint ncases;
3653 TableEntry *table;
3654
3655 sn = js_GetSrcNote(jp->script, pc);
3656 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_SWITCH);
3657 len = js_GetSrcNoteOffset(sn, 0);
3658 off = js_GetSrcNoteOffset(sn, 1);
3659
3660 /*
3661 * Count the cases using offsets from switch to first case,
3662 * and case to case, stored in srcnote immediates.
3663 */
3664 pc2 = pc;
3665 off2 = off;
3666 for (ncases = 0; off2 != 0; ncases++) {
3667 pc2 += off2;
3668 LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT ||
3669 *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX);
3670 if (*pc2 == JSOP_DEFAULT || *pc2 == JSOP_DEFAULTX) {
3671 /* End of cases, but count default as a case. */
3672 off2 = 0;
3673 } else {
3674 sn = js_GetSrcNote(jp->script, pc2);
3675 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
3676 off2 = js_GetSrcNoteOffset(sn, 0);
3677 }
3678 }
3679
3680 /*
3681 * Allocate table and rescan the cases using their srcnotes,
3682 * stashing each case's delta from switch top in table[i].key,
3683 * and the distance to its statements in table[i].offset.
3684 */
3685 table = (TableEntry *)
3686 JS_malloc(cx, (size_t)ncases * sizeof *table);
3687 if (!table)
3688 return NULL;
3689 pc2 = pc;
3690 off2 = off;
3691 for (i = 0; i < ncases; i++) {
3692 pc2 += off2;
3693 LOCAL_ASSERT(*pc2 == JSOP_CASE || *pc2 == JSOP_DEFAULT ||
3694 *pc2 == JSOP_CASEX || *pc2 == JSOP_DEFAULTX);
3695 caseOff = pc2 - pc;
3696 table[i].key = INT_TO_JSVAL((jsint) caseOff);
3697 table[i].offset = caseOff + GetJumpOffset(pc2, pc2);
3698 if (*pc2 == JSOP_CASE || *pc2 == JSOP_CASEX) {
3699 sn = js_GetSrcNote(jp->script, pc2);
3700 LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_PCDELTA);
3701 off2 = js_GetSrcNoteOffset(sn, 0);
3702 }
3703 }
3704
3705 /*
3706 * Find offset of default code by fetching the default offset
3707 * from the end of table. JSOP_CONDSWITCH always has a default
3708 * case at the end.
3709 */
3710 off = JSVAL_TO_INT(table[ncases-1].key);
3711 pc2 = pc + off;
3712 off += GetJumpOffset(pc2, pc2);
3713
3714 ok = DecompileSwitch(ss, table, (uintN)ncases, pc, len, off,
3715 JS_TRUE);
3716 JS_free(cx, table);
3717 if (!ok)
3718 return NULL;
3719 todo = -2;
3720 break;
3721 }
3722
3723 case JSOP_CASE:
3724 case JSOP_CASEX:
3725 {
3726 lval = POP_STR();
3727 if (!lval)
3728 return NULL;
3729 js_printf(jp, "\tcase %s:\n", lval);
3730 todo = -2;
3731 break;
3732 }
3733
3734 case JSOP_NEW_EQ:
3735 case JSOP_NEW_NE:
3736 rval = POP_STR();
3737 lval = POP_STR();
3738 todo = Sprint(&ss->sprinter, "%s %c== %s",
3739 lval, (op == JSOP_NEW_EQ) ? '=' : '!', rval);
3740 break;
3741
3742 BEGIN_LITOPX_CASE(JSOP_CLOSURE)
3743 LOCAL_ASSERT(ATOM_IS_OBJECT(atom));
3744 todo = -2;
3745 goto do_function;
3746 END_LITOPX_CASE
3747
3748 #if JS_HAS_EXPORT_IMPORT
3749 case JSOP_EXPORTALL:
3750 js_printf(jp, "\texport *;\n");
3751 todo = -2;
3752 break;
3753
3754 BEGIN_LITOPX_CASE(JSOP_EXPORTNAME)
3755 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3756 if (!rval)
3757 return NULL;
3758 RETRACT(&ss->sprinter, rval);
3759 js_printf(jp, "\texport %s;\n", rval);
3760 todo = -2;
3761 END_LITOPX_CASE
3762
3763 case JSOP_IMPORTALL:
3764 lval = POP_STR();
3765 js_printf(jp, "\timport %s.*;\n", lval);
3766 todo = -2;
3767 break;
3768
3769 case JSOP_IMPORTPROP:
3770 do_importprop:
3771 GET_ATOM_QUOTE_AND_FMT("\timport %s[%s];\n",
3772 "\timport %s.%s;\n",
3773 rval);
3774 lval = POP_STR();
3775 js_printf(jp, fmt, lval, rval);
3776 todo = -2;
3777 break;
3778
3779 case JSOP_IMPORTELEM:
3780 xval = POP_STR();
3781 op = JSOP_GETELEM;
3782 if (js_CodeSpec[lastop].format & JOF_XMLNAME)
3783 goto do_importprop;
3784 lval = POP_STR();
3785 js_printf(jp, "\timport %s[%s];\n", lval, xval);
3786 todo = -2;
3787 break;
3788 #endif /* JS_HAS_EXPORT_IMPORT */
3789
3790 case JSOP_TRAP:
3791 op = JS_GetTrapOpcode(cx, jp->script, pc);
3792 if (op == JSOP_LIMIT)
3793 return NULL;
3794 saveop = op;
3795 *pc = op;
3796 cs = &js_CodeSpec[op];
3797 len = cs->length;
3798 DECOMPILE_CODE(pc, len);
3799 *pc = JSOP_TRAP;
3800 todo = -2;
3801 break;
3802
3803 case JSOP_NEWINIT:
3804 {
3805 JSBool isArray;
3806
3807 LOCAL_ASSERT(ss->top >= 2);
3808 (void) PopOff(ss, op);
3809 lval = POP_STR();
3810 isArray = (*lval == 'A');
3811 todo = ss->sprinter.offset;
3812 #if JS_HAS_SHARP_VARS
3813 op = (JSOp)pc[len];
3814 if (op == JSOP_DEFSHARP) {
3815 pc += len;
3816 cs = &js_CodeSpec[op];
3817 len = cs->length;
3818 i = (jsint) GET_ATOM_INDEX(pc);
3819 if (Sprint(&ss->sprinter, "#%u=", (unsigned) i) < 0)
3820 return NULL;
3821 }
3822 #endif /* JS_HAS_SHARP_VARS */
3823 if (isArray) {
3824 ++ss->inArrayInit;
3825 if (SprintCString(&ss->sprinter, "[") < 0)
3826 return NULL;
3827 } else {
3828 if (SprintCString(&ss->sprinter, "{") < 0)
3829 return NULL;
3830 }
3831 break;
3832 }
3833
3834 case JSOP_ENDINIT:
3835 op = JSOP_NOP; /* turn off parens */
3836 rval = POP_STR();
3837 sn = js_GetSrcNote(jp->script, pc);
3838
3839 /* Skip any #n= prefix to find the opening bracket. */
3840 for (xval = rval; *xval != '[' && *xval != '{'; xval++)
3841 continue;
3842 if (*xval == '[')
3843 --ss->inArrayInit;
3844 todo = Sprint(&ss->sprinter, "%s%s%c",
3845 rval,
3846 (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "",
3847 (*xval == '[') ? ']' : '}');
3848 break;
3849
3850 case JSOP_INITPROP:
3851 atom = GET_ATOM(cx, jp->script, pc);
3852 xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom),
3853 (jschar)
3854 (ATOM_IS_IDENTIFIER(atom) ? 0 : '\''));
3855 if (!xval)
3856 return NULL;
3857 rval = POP_STR();
3858 lval = POP_STR();
3859 do_initprop:
3860 #ifdef OLD_GETTER_SETTER
3861 todo = Sprint(&ss->sprinter, "%s%s%s%s%s:%s",
3862 lval,
3863 (lval[1] != '\0') ? ", " : "",
3864 xval,
3865 (lastop == JSOP_GETTER || lastop == JSOP_SETTER)
3866 ? " " : "",
3867 (lastop == JSOP_GETTER) ? js_getter_str :
3868 (lastop == JSOP_SETTER) ? js_setter_str :
3869 "",
3870 rval);
3871 #else
3872 if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) {
3873 if (!atom || !ATOM_IS_STRING(atom) ||
3874 !ATOM_IS_IDENTIFIER(atom) ||
3875 ATOM_IS_KEYWORD(atom) ||
3876 ((ss->opcodes[ss->top+1] != JSOP_ANONFUNOBJ ||
3877 strncmp(rval, js_function_str, 8) != 0) &&
3878 ss->opcodes[ss->top+1] != JSOP_NAMEDFUNOBJ)) {
3879 todo = Sprint(&ss->sprinter, "%s%s%s%s%s:%s", lval,
3880 (lval[1] != '\0') ? ", " : "", xval,
3881 (lastop == JSOP_GETTER ||
3882 lastop == JSOP_SETTER)
3883 ? " " : "",
3884 (lastop == JSOP_GETTER) ? js_getter_str :
3885 (lastop == JSOP_SETTER) ? js_setter_str :
3886 "",
3887 rval);
3888 } else {
3889 rval += 8 + 1;
3890 LOCAL_ASSERT(rval[strlen(rval)-1] == '}');
3891 todo = Sprint(&ss->sprinter, "%s%s%s %s%s",
3892 lval,
3893 (lval[1] != '\0') ? ", " : "",
3894 (lastop == JSOP_GETTER)
3895 ? js_get_str : js_set_str,
3896 xval,
3897 rval);
3898 }
3899 } else {
3900 todo = Sprint(&ss->sprinter, "%s%s%s:%s",
3901 lval,
3902 (lval[1] != '\0') ? ", " : "",
3903 xval,
3904 rval);
3905 }
3906 #endif
3907 break;
3908
3909 case JSOP_INITELEM:
3910 rval = POP_STR();
3911 xval = POP_STR();
3912 lval = POP_STR();
3913 sn = js_GetSrcNote(jp->script, pc);
3914 if (sn && SN_TYPE(sn) == SRC_INITPROP) {
3915 atom = NULL;
3916 goto do_initprop;
3917 }
3918 todo = Sprint(&ss->sprinter, "%s%s%s",
3919 lval,
3920 (lval[1] != '\0' || *xval != '0') ? ", " : "",
3921 rval);
3922 break;
3923
3924 #if JS_HAS_SHARP_VARS
3925 case JSOP_DEFSHARP:
3926 i = (jsint) GET_ATOM_INDEX(pc);
3927 rval = POP_STR();
3928 todo = Sprint(&ss->sprinter, "#%u=%s", (unsigned) i, rval);
3929 break;
3930
3931 case JSOP_USESHARP:
3932 i = (jsint) GET_ATOM_INDEX(pc);
3933 todo = Sprint(&ss->sprinter, "#%u#", (unsigned) i);
3934 break;
3935 #endif /* JS_HAS_SHARP_VARS */
3936
3937 #if JS_HAS_DEBUGGER_KEYWORD
3938 case JSOP_DEBUGGER:
3939 js_printf(jp, "\tdebugger;\n");
3940 todo = -2;
3941 break;
3942 #endif /* JS_HAS_DEBUGGER_KEYWORD */
3943
3944 #if JS_HAS_XML_SUPPORT
3945 case JSOP_STARTXML:
3946 case JSOP_STARTXMLEXPR:
3947 inXML = op == JSOP_STARTXML;
3948 todo = -2;
3949 break;
3950
3951 case JSOP_DEFXMLNS:
3952 rval = POP_STR();
3953 js_printf(jp, "\t%s %s %s = %s;\n",
3954 js_default_str, js_xml_str, js_namespace_str, rval);
3955 todo = -2;
3956 break;
3957
3958 case JSOP_ANYNAME:
3959 if (pc[JSOP_ANYNAME_LENGTH] == JSOP_TOATTRNAME) {
3960 len += JSOP_TOATTRNAME_LENGTH;
3961 todo = SprintPut(&ss->sprinter, "@*", 2);
3962 } else {
3963 todo = SprintPut(&ss->sprinter, "*", 1);
3964 }
3965 break;
3966
3967 BEGIN_LITOPX_CASE(JSOP_QNAMEPART)
3968 if (pc[JSOP_QNAMEPART_LENGTH] == JSOP_TOATTRNAME) {
3969 saveop = JSOP_TOATTRNAME;
3970 len += JSOP_TOATTRNAME_LENGTH;
3971 lval = "@";
3972 goto do_qname;
3973 }
3974 goto do_name;
3975 END_LITOPX_CASE
3976
3977 BEGIN_LITOPX_CASE(JSOP_QNAMECONST)
3978 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
3979 if (!rval)
3980 return NULL;
3981 RETRACT(&ss->sprinter, rval);
3982 lval = POP_STR();
3983 todo = Sprint(&ss->sprinter, "%s::%s", lval, rval);
3984 END_LITOPX_CASE
3985
3986 case JSOP_QNAME:
3987 rval = POP_STR();
3988 lval = POP_STR();
3989 todo = Sprint(&ss->sprinter, "%s::[%s]", lval, rval);
3990 break;
3991
3992 case JSOP_TOATTRNAME:
3993 op = JSOP_NOP; /* turn off parens */
3994 rval = POP_STR();
3995 todo = Sprint(&ss->sprinter, "@[%s]", rval);
3996 break;
3997
3998 case JSOP_TOATTRVAL:
3999 todo = -2;
4000 break;
4001
4002 case JSOP_ADDATTRNAME:
4003 rval = POP_STR();
4004 lval = POP_STR();
4005 todo = Sprint(&ss->sprinter, "%s %s", lval, rval);
4006 /* This gets reset by all XML tag expressions. */
4007 quoteAttr = JS_TRUE;
4008 break;
4009
4010 case JSOP_ADDATTRVAL:
4011 rval = POP_STR();
4012 lval = POP_STR();
4013 if (quoteAttr)
4014 todo = Sprint(&ss->sprinter, "%s=\"%s\"", lval, rval);
4015 else
4016 todo = Sprint(&ss->sprinter, "%s=%s", lval, rval);
4017 break;
4018
4019 case JSOP_BINDXMLNAME:
4020 /* Leave the name stacked and push a dummy string. */
4021 todo = Sprint(&ss->sprinter, "");
4022 break;
4023
4024 case JSOP_SETXMLNAME:
4025 /* Pop the r.h.s., the dummy string, and the name. */
4026 rval = POP_STR();
4027 (void) PopOff(ss, op);
4028 lval = POP_STR();
4029 goto do_setlval;
4030
4031 case JSOP_XMLELTEXPR:
4032 case JSOP_XMLTAGEXPR:
4033 todo = Sprint(&ss->sprinter, "{%s}", POP_STR());
4034 inXML = JS_TRUE;
4035 /* If we're an attribute value, we shouldn't quote this. */
4036 quoteAttr = JS_FALSE;
4037 break;
4038
4039 case JSOP_TOXMLLIST:
4040 op = JSOP_NOP; /* turn off parens */
4041 todo = Sprint(&ss->sprinter, "<>%s</>", POP_STR());
4042 inXML = JS_FALSE;
4043 break;
4044
4045 case JSOP_FOREACH:
4046 foreach = JS_TRUE;
4047 todo = -2;
4048 break;
4049
4050 case JSOP_TOXML:
4051 inXML = JS_FALSE;
4052 /* FALL THROUGH */
4053
4054 case JSOP_XMLNAME:
4055 case JSOP_FILTER:
4056 /* Conversion and prefix ops do nothing in the decompiler. */
4057 todo = -2;
4058 break;
4059
4060 case JSOP_ENDFILTER:
4061 rval = POP_STR();
4062 lval = POP_STR();
4063 todo = Sprint(&ss->sprinter, "%s.(%s)", lval, rval);
4064 break;
4065
4066 case JSOP_DESCENDANTS:
4067 rval = POP_STR();
4068 lval = POP_STR();
4069 todo = Sprint(&ss->sprinter, "%s..%s", lval, rval);
4070 break;
4071
4072 BEGIN_LITOPX_CASE(JSOP_XMLOBJECT)
4073 todo = Sprint(&ss->sprinter, "<xml address='%p'>",
4074 ATOM_TO_OBJECT(atom));
4075 END_LITOPX_CASE
4076
4077 BEGIN_LITOPX_CASE(JSOP_XMLCDATA)
4078 todo = SprintPut(&ss->sprinter, "<![CDATA[", 9);
4079 if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0))
4080 return NULL;
4081 SprintPut(&ss->sprinter, "]]>", 3);
4082 END_LITOPX_CASE
4083
4084 BEGIN_LITOPX_CASE(JSOP_XMLCOMMENT)
4085 todo = SprintPut(&ss->sprinter, "<!--", 4);
4086 if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0))
4087 return NULL;
4088 SprintPut(&ss->sprinter, "-->", 3);
4089 END_LITOPX_CASE
4090
4091 BEGIN_LITOPX_CASE(JSOP_XMLPI)
4092 rval = JS_strdup(cx, POP_STR());
4093 if (!rval)
4094 return NULL;
4095 todo = SprintPut(&ss->sprinter, "<?", 2);
4096 ok = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0) &&
4097 (*rval == '\0' ||
4098 (SprintPut(&ss->sprinter, " ", 1) >= 0 &&
4099 SprintCString(&ss->sprinter, rval)));
4100 JS_free(cx, (char *)rval);
4101 if (!ok)
4102 return NULL;
4103 SprintPut(&ss->sprinter, "?>", 2);
4104 END_LITOPX_CASE
4105
4106 case JSOP_GETFUNNS:
4107 todo = SprintPut(&ss->sprinter, js_function_str, 8);
4108 break;
4109 #endif /* JS_HAS_XML_SUPPORT */
4110
4111 default:
4112 todo = -2;
4113 break;
4114
4115 #undef BEGIN_LITOPX_CASE
4116 #undef END_LITOPX_CASE
4117 }
4118 }
4119
4120 if (todo < 0) {
4121 /* -2 means "don't push", -1 means reported error. */
4122 if (todo == -1)
4123 return NULL;
4124 } else {
4125 if (!PushOff(ss, todo, saveop))
4126 return NULL;
4127 }
4128 pc += len;
4129 }
4130
4131 /*
4132 * Undefine local macros.
4133 */
4134 #undef inXML
4135 #undef DECOMPILE_CODE
4136 #undef POP_STR
4137 #undef LOCAL_ASSERT
4138 #undef ATOM_IS_IDENTIFIER
4139 #undef GET_QUOTE_AND_FMT
4140 #undef GET_ATOM_QUOTE_AND_FMT
4141
4142 return pc;
4143 }
4144
4145 static JSBool
InitSprintStack(JSContext * cx,SprintStack * ss,JSPrinter * jp,uintN depth)4146 InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth)
4147 {
4148 size_t offsetsz, opcodesz;
4149 void *space;
4150
4151 INIT_SPRINTER(cx, &ss->sprinter, &cx->tempPool, PAREN_SLOP);
4152
4153 /* Allocate the parallel (to avoid padding) offset and opcode stacks. */
4154 offsetsz = depth * sizeof(ptrdiff_t);
4155 opcodesz = depth * sizeof(jsbytecode);
4156 JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz);
4157 if (!space)
4158 return JS_FALSE;
4159 ss->offsets = (ptrdiff_t *) space;
4160 ss->opcodes = (jsbytecode *) ((char *)space + offsetsz);
4161
4162 ss->top = ss->inArrayInit = 0;
4163 ss->printer = jp;
4164 return JS_TRUE;
4165 }
4166
4167 JSBool
js_DecompileCode(JSPrinter * jp,JSScript * script,jsbytecode * pc,uintN len,uintN pcdepth)4168 js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len,
4169 uintN pcdepth)
4170 {
4171 uintN depth, i;
4172 SprintStack ss;
4173 JSContext *cx;
4174 void *mark;
4175 JSBool ok;
4176 JSScript *oldscript;
4177 char *last;
4178
4179 depth = script->depth;
4180 JS_ASSERT(pcdepth <= depth);
4181
4182 /* Initialize a sprinter for use with the offset stack. */
4183 cx = jp->sprinter.context;
4184 mark = JS_ARENA_MARK(&cx->tempPool);
4185 ok = InitSprintStack(cx, &ss, jp, depth);
4186 if (!ok)
4187 goto out;
4188
4189 /*
4190 * If we are called from js_DecompileValueGenerator with a portion of
4191 * script's bytecode that starts with a non-zero model stack depth given
4192 * by pcdepth, attempt to initialize the missing string offsets in ss to
4193 * |spindex| negative indexes from fp->sp for the activation fp in which
4194 * the error arose.
4195 *
4196 * See js_DecompileValueGenerator for how its |spindex| parameter is used,
4197 * and see also GetOff, which makes use of the ss.offsets[i] < -1 that are
4198 * potentially stored below.
4199 */
4200 ss.top = pcdepth;
4201 if (pcdepth != 0) {
4202 JSStackFrame *fp;
4203 ptrdiff_t top;
4204
4205 for (fp = cx->fp; fp && !fp->script; fp = fp->down)
4206 continue;
4207 top = fp ? fp->sp - fp->spbase : 0;
4208 for (i = 0; i < pcdepth; i++) {
4209 ss.offsets[i] = -1;
4210 ss.opcodes[i] = JSOP_NOP;
4211 }
4212 if (fp && fp->pc == pc && (uintN)top == pcdepth) {
4213 for (i = 0; i < pcdepth; i++) {
4214 ptrdiff_t off;
4215 jsbytecode *genpc;
4216
4217 off = (intN)i - (intN)depth;
4218 genpc = (jsbytecode *) fp->spbase[off];
4219 if (JS_UPTRDIFF(genpc, script->code) < script->length) {
4220 ss.offsets[i] += (ptrdiff_t)i - top;
4221 ss.opcodes[i] = *genpc;
4222 }
4223 }
4224 }
4225 }
4226
4227 /* Call recursive subroutine to do the hard work. */
4228 oldscript = jp->script;
4229 jp->script = script;
4230 ok = Decompile(&ss, pc, len) != NULL;
4231 jp->script = oldscript;
4232
4233 /* If the given code didn't empty the stack, do it now. */
4234 if (ss.top) {
4235 do {
4236 last = OFF2STR(&ss.sprinter, PopOff(&ss, JSOP_POP));
4237 } while (ss.top > pcdepth);
4238 js_printf(jp, "%s", last);
4239 }
4240
4241 out:
4242 /* Free all temporary stuff allocated under this call. */
4243 JS_ARENA_RELEASE(&cx->tempPool, mark);
4244 return ok;
4245 }
4246
4247 JSBool
js_DecompileScript(JSPrinter * jp,JSScript * script)4248 js_DecompileScript(JSPrinter *jp, JSScript *script)
4249 {
4250 return js_DecompileCode(jp, script, script->code, (uintN)script->length, 0);
4251 }
4252
4253 static const char native_code_str[] = "\t[native code]\n";
4254
4255 JSBool
js_DecompileFunctionBody(JSPrinter * jp,JSFunction * fun)4256 js_DecompileFunctionBody(JSPrinter *jp, JSFunction *fun)
4257 {
4258 JSScript *script;
4259 JSScope *scope, *save;
4260 JSBool ok;
4261
4262 if (!FUN_INTERPRETED(fun)) {
4263 js_printf(jp, native_code_str);
4264 return JS_TRUE;
4265 }
4266 script = fun->u.i.script;
4267 scope = fun->object ? OBJ_SCOPE(fun->object) : NULL;
4268 save = jp->scope;
4269 jp->scope = scope;
4270 ok = js_DecompileCode(jp, script, script->code, (uintN)script->length, 0);
4271 jp->scope = save;
4272 return ok;
4273 }
4274
4275 JSBool
js_DecompileFunction(JSPrinter * jp,JSFunction * fun)4276 js_DecompileFunction(JSPrinter *jp, JSFunction *fun)
4277 {
4278 JSContext *cx;
4279 uintN i, nargs, indent;
4280 void *mark;
4281 JSAtom **params;
4282 JSScope *scope, *oldscope;
4283 JSScopeProperty *sprop;
4284 jsbytecode *pc, *endpc;
4285 ptrdiff_t len;
4286 JSBool ok;
4287
4288 /*
4289 * If pretty, conform to ECMA-262 Edition 3, 15.3.4.2, by decompiling a
4290 * FunctionDeclaration. Otherwise, check the JSFUN_LAMBDA flag and force
4291 * an expression by parenthesizing.
4292 */
4293 if (jp->pretty) {
4294 js_printf(jp, "\t");
4295 } else {
4296 if (!jp->grouped && (fun->flags & JSFUN_LAMBDA))
4297 js_puts(jp, "(");
4298 }
4299 if (JSFUN_GETTER_TEST(fun->flags))
4300 js_printf(jp, "%s ", js_getter_str);
4301 else if (JSFUN_SETTER_TEST(fun->flags))
4302 js_printf(jp, "%s ", js_setter_str);
4303
4304 js_printf(jp, "%s ", js_function_str);
4305 if (fun->atom && !QuoteString(&jp->sprinter, ATOM_TO_STRING(fun->atom), 0))
4306 return JS_FALSE;
4307 js_puts(jp, "(");
4308
4309 if (FUN_INTERPRETED(fun) && fun->object) {
4310 size_t paramsize;
4311 #ifdef JS_HAS_DESTRUCTURING
4312 SprintStack ss;
4313 JSScript *oldscript;
4314 #endif
4315
4316 /*
4317 * Print the parameters.
4318 *
4319 * This code is complicated by the need to handle duplicate parameter
4320 * names, as required by ECMA (bah!). A duplicate parameter is stored
4321 * as another node with the same id (the parameter name) but different
4322 * shortid (the argument index) along the property tree ancestor line
4323 * starting at SCOPE_LAST_PROP(scope). Only the last duplicate param
4324 * is mapped by the scope's hash table.
4325 */
4326 cx = jp->sprinter.context;
4327 nargs = fun->nargs;
4328 mark = JS_ARENA_MARK(&cx->tempPool);
4329 paramsize = nargs * sizeof(JSAtom *);
4330 JS_ARENA_ALLOCATE_CAST(params, JSAtom **, &cx->tempPool, paramsize);
4331 if (!params) {
4332 JS_ReportOutOfMemory(cx);
4333 return JS_FALSE;
4334 }
4335
4336 memset(params, 0, paramsize);
4337 scope = OBJ_SCOPE(fun->object);
4338 for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
4339 if (sprop->getter != js_GetArgument)
4340 continue;
4341 JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
4342 JS_ASSERT((uint16) sprop->shortid < nargs);
4343 JS_ASSERT(JSID_IS_ATOM(sprop->id));
4344 params[(uint16) sprop->shortid] = JSID_TO_ATOM(sprop->id);
4345 }
4346
4347 pc = fun->u.i.script->main;
4348 endpc = pc + fun->u.i.script->length;
4349 ok = JS_TRUE;
4350
4351 #ifdef JS_HAS_DESTRUCTURING
4352 /* Skip JSOP_GENERATOR in case of destructuring parameters. */
4353 if (*pc == JSOP_GENERATOR)
4354 pc += JSOP_GENERATOR_LENGTH;
4355
4356 ss.printer = NULL;
4357 oldscript = jp->script;
4358 jp->script = fun->u.i.script;
4359 oldscope = jp->scope;
4360 jp->scope = scope;
4361 #endif
4362
4363 for (i = 0; i < nargs; i++) {
4364 if (i > 0)
4365 js_puts(jp, ", ");
4366
4367 #if JS_HAS_DESTRUCTURING
4368 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, JS_FALSE)
4369
4370 if (!params[i]) {
4371 ptrdiff_t todo;
4372 const char *lval;
4373
4374 LOCAL_ASSERT(*pc == JSOP_GETARG);
4375 pc += JSOP_GETARG_LENGTH;
4376 LOCAL_ASSERT(*pc == JSOP_DUP);
4377 if (!ss.printer) {
4378 ok = InitSprintStack(cx, &ss, jp, fun->u.i.script->depth);
4379 if (!ok)
4380 break;
4381 }
4382 pc = DecompileDestructuring(&ss, pc, endpc);
4383 if (!pc) {
4384 ok = JS_FALSE;
4385 break;
4386 }
4387 LOCAL_ASSERT(*pc == JSOP_POP);
4388 pc += JSOP_POP_LENGTH;
4389 lval = PopStr(&ss, JSOP_NOP);
4390 todo = SprintCString(&jp->sprinter, lval);
4391 if (todo < 0) {
4392 ok = JS_FALSE;
4393 break;
4394 }
4395 continue;
4396 }
4397
4398 #undef LOCAL_ASSERT
4399 #endif
4400
4401 if (!QuoteString(&jp->sprinter, ATOM_TO_STRING(params[i]), 0)) {
4402 ok = JS_FALSE;
4403 break;
4404 }
4405 }
4406
4407 #ifdef JS_HAS_DESTRUCTURING
4408 jp->script = oldscript;
4409 jp->scope = oldscope;
4410 #endif
4411 JS_ARENA_RELEASE(&cx->tempPool, mark);
4412 if (!ok)
4413 return JS_FALSE;
4414 #ifdef __GNUC__
4415 } else {
4416 scope = NULL;
4417 pc = NULL;
4418 #endif
4419 }
4420
4421 js_printf(jp, ") {\n");
4422 indent = jp->indent;
4423 jp->indent += 4;
4424 if (FUN_INTERPRETED(fun) && fun->object) {
4425 oldscope = jp->scope;
4426 jp->scope = scope;
4427 len = fun->u.i.script->code + fun->u.i.script->length - pc;
4428 ok = js_DecompileCode(jp, fun->u.i.script, pc, (uintN)len, 0);
4429 jp->scope = oldscope;
4430 if (!ok) {
4431 jp->indent = indent;
4432 return JS_FALSE;
4433 }
4434 } else {
4435 js_printf(jp, native_code_str);
4436 }
4437 jp->indent -= 4;
4438 js_printf(jp, "\t}");
4439
4440 if (!jp->pretty) {
4441 if (!jp->grouped && (fun->flags & JSFUN_LAMBDA))
4442 js_puts(jp, ")");
4443 }
4444 return JS_TRUE;
4445 }
4446
4447 #undef LOCAL_ASSERT_RV
4448
4449 JSString *
js_DecompileValueGenerator(JSContext * cx,intN spindex,jsval v,JSString * fallback)4450 js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
4451 JSString *fallback)
4452 {
4453 JSStackFrame *fp, *down;
4454 jsbytecode *pc, *begin, *end;
4455 jsval *sp, *spbase, *base, *limit;
4456 intN depth, pcdepth;
4457 JSScript *script;
4458 JSOp op;
4459 const JSCodeSpec *cs;
4460 jssrcnote *sn;
4461 ptrdiff_t len, oplen;
4462 JSPrinter *jp;
4463 JSString *name;
4464
4465 for (fp = cx->fp; fp && !fp->script; fp = fp->down)
4466 continue;
4467 if (!fp)
4468 goto do_fallback;
4469
4470 /* Try to find sp's generating pc depth slots under it on the stack. */
4471 pc = fp->pc;
4472 sp = fp->sp;
4473 spbase = fp->spbase;
4474 if ((uintN)(sp - spbase) > fp->script->depth) {
4475 /*
4476 * Preparing to make an internal invocation, using an argv stack
4477 * segment pushed just above fp's operand stack space. Such an argv
4478 * stack has no generating pc "basement", so we must fall back.
4479 */
4480 goto do_fallback;
4481 }
4482
4483 if (spindex == JSDVG_SEARCH_STACK) {
4484 if (!pc) {
4485 /*
4486 * Current frame is native: look under it for a scripted call
4487 * in which a decompilable bytecode string that generated the
4488 * value as an actual argument might exist.
4489 */
4490 JS_ASSERT(!fp->script && !(fp->fun && FUN_INTERPRETED(fp->fun)));
4491 down = fp->down;
4492 if (!down)
4493 goto do_fallback;
4494 script = down->script;
4495 spbase = down->spbase;
4496 base = fp->argv;
4497 limit = base + fp->argc;
4498 } else {
4499 /*
4500 * This should be a script activation, either a top-level
4501 * script or a scripted function. But be paranoid about calls
4502 * to js_DecompileValueGenerator from code that hasn't fully
4503 * initialized a (default-all-zeroes) frame.
4504 */
4505 script = fp->script;
4506 spbase = base = fp->spbase;
4507 limit = fp->sp;
4508 }
4509
4510 /*
4511 * Pure paranoia about default-zeroed frames being active while
4512 * js_DecompileValueGenerator is called. It can't hurt much now;
4513 * error reporting performance is not an issue.
4514 */
4515 if (!script || !base || !limit)
4516 goto do_fallback;
4517
4518 /*
4519 * Try to find operand-generating pc depth slots below sp.
4520 *
4521 * In the native case, we know the arguments have generating pc's
4522 * under them, on account of fp->down->script being non-null: all
4523 * compiled scripts get depth slots for generating pc's allocated
4524 * upon activation, at the top of js_Interpret.
4525 *
4526 * In the script or scripted function case, the same reasoning
4527 * applies to fp rather than to fp->down.
4528 *
4529 * We search from limit to base to find the most recently calculated
4530 * value matching v under assumption that it is it that caused
4531 * exception, see bug 328664.
4532 */
4533 for (sp = limit;;) {
4534 if (sp <= base)
4535 goto do_fallback;
4536 --sp;
4537 if (*sp == v) {
4538 depth = (intN)script->depth;
4539 sp -= depth;
4540 pc = (jsbytecode *) *sp;
4541 break;
4542 }
4543 }
4544 } else {
4545 /*
4546 * At this point, pc may or may not be null, i.e., we could be in
4547 * a script activation, or we could be in a native frame that was
4548 * called by another native function. Check pc and script.
4549 */
4550 if (!pc)
4551 goto do_fallback;
4552 script = fp->script;
4553 if (!script)
4554 goto do_fallback;
4555
4556 if (spindex != JSDVG_IGNORE_STACK) {
4557 JS_ASSERT(spindex < 0);
4558 depth = (intN)script->depth;
4559 #if !JS_HAS_NO_SUCH_METHOD
4560 JS_ASSERT(-depth <= spindex);
4561 #endif
4562 spindex -= depth;
4563
4564 base = (jsval *) cx->stackPool.current->base;
4565 limit = (jsval *) cx->stackPool.current->avail;
4566 sp = fp->sp + spindex;
4567 if (JS_UPTRDIFF(sp, base) < JS_UPTRDIFF(limit, base))
4568 pc = (jsbytecode *) *sp;
4569 }
4570 }
4571
4572 /*
4573 * Again, be paranoid, this time about possibly loading an invalid pc
4574 * from fp->sp[-(1+depth)].
4575 */
4576 if (JS_UPTRDIFF(pc, script->code) >= (jsuword)script->length) {
4577 pc = fp->pc;
4578 if (!pc)
4579 goto do_fallback;
4580 }
4581 op = (JSOp) *pc;
4582 if (op == JSOP_TRAP)
4583 op = JS_GetTrapOpcode(cx, script, pc);
4584
4585 /* None of these stack-writing ops generates novel values. */
4586 JS_ASSERT(op != JSOP_CASE && op != JSOP_CASEX &&
4587 op != JSOP_DUP && op != JSOP_DUP2 &&
4588 op != JSOP_SWAP);
4589
4590 /*
4591 * |this| could convert to a very long object initialiser, so cite it by
4592 * its keyword name instead.
4593 */
4594 if (op == JSOP_THIS)
4595 return JS_NewStringCopyZ(cx, js_this_str);
4596
4597 /*
4598 * JSOP_BINDNAME is special: it generates a value, the base object of a
4599 * reference. But if it is the generating op for a diagnostic produced by
4600 * js_DecompileValueGenerator, the name being bound is irrelevant. Just
4601 * fall back to the base object.
4602 */
4603 if (op == JSOP_BINDNAME)
4604 goto do_fallback;
4605
4606 /* NAME ops are self-contained, others require left or right context. */
4607 cs = &js_CodeSpec[op];
4608 begin = pc;
4609 end = pc + cs->length;
4610 if ((cs->format & JOF_MODEMASK) != JOF_NAME) {
4611 JSSrcNoteType noteType;
4612
4613 sn = js_GetSrcNote(script, pc);
4614 if (!sn)
4615 goto do_fallback;
4616 noteType = SN_TYPE(sn);
4617 if (noteType == SRC_PCBASE) {
4618 begin -= js_GetSrcNoteOffset(sn, 0);
4619 } else if (noteType == SRC_PCDELTA) {
4620 end = begin + js_GetSrcNoteOffset(sn, 0);
4621 begin += cs->length;
4622 } else {
4623 goto do_fallback;
4624 }
4625 }
4626 len = PTRDIFF(end, begin, jsbytecode);
4627 if (len <= 0)
4628 goto do_fallback;
4629
4630 /*
4631 * Walk forward from script->main and compute starting stack depth.
4632 * FIXME: Code to compute oplen copied from js_Disassemble1 and reduced.
4633 * FIXME: Optimize to use last empty-stack sequence point.
4634 */
4635 pcdepth = 0;
4636 for (pc = script->main; pc < begin; pc += oplen) {
4637 jsbytecode *pc2;
4638 uint32 type;
4639 intN nuses, ndefs;
4640
4641 /* Let pc2 be non-null only for JSOP_LITOPX. */
4642 pc2 = NULL;
4643 op = (JSOp) *pc;
4644 if (op == JSOP_TRAP)
4645 op = JS_GetTrapOpcode(cx, script, pc);
4646 cs = &js_CodeSpec[op];
4647 oplen = cs->length;
4648
4649 if (op == JSOP_SETSP) {
4650 pcdepth = GET_UINT16(pc);
4651 continue;
4652 }
4653
4654 /*
4655 * A (C ? T : E) expression requires skipping either T (if begin is in
4656 * E) or both T and E (if begin is after the whole expression) before
4657 * adjusting pcdepth based on the JSOP_IFEQ or JSOP_IFEQX at pc that
4658 * tests condition C. We know that the stack depth can't change from
4659 * what it was with C on top of stack.
4660 */
4661 sn = js_GetSrcNote(script, pc);
4662 if (sn && SN_TYPE(sn) == SRC_COND) {
4663 ptrdiff_t jmpoff, jmplen;
4664
4665 jmpoff = js_GetSrcNoteOffset(sn, 0);
4666 if (pc + jmpoff < begin) {
4667 pc += jmpoff;
4668 op = *pc;
4669 JS_ASSERT(op == JSOP_GOTO || op == JSOP_GOTOX);
4670 cs = &js_CodeSpec[op];
4671 oplen = cs->length;
4672 jmplen = GetJumpOffset(pc, pc);
4673 if (pc + jmplen < begin) {
4674 oplen = (uintN) jmplen;
4675 continue;
4676 }
4677
4678 /*
4679 * Ok, begin lies in E. Manually pop C off the model stack,
4680 * since we have moved beyond the IFEQ now.
4681 */
4682 --pcdepth;
4683 }
4684 }
4685
4686 type = cs->format & JOF_TYPEMASK;
4687 switch (type) {
4688 case JOF_TABLESWITCH:
4689 case JOF_TABLESWITCHX:
4690 {
4691 jsint jmplen, i, low, high;
4692
4693 jmplen = (type == JOF_TABLESWITCH) ? JUMP_OFFSET_LEN
4694 : JUMPX_OFFSET_LEN;
4695 pc2 = pc;
4696 pc2 += jmplen;
4697 low = GET_JUMP_OFFSET(pc2);
4698 pc2 += JUMP_OFFSET_LEN;
4699 high = GET_JUMP_OFFSET(pc2);
4700 pc2 += JUMP_OFFSET_LEN;
4701 for (i = low; i <= high; i++)
4702 pc2 += jmplen;
4703 oplen = 1 + pc2 - pc;
4704 break;
4705 }
4706
4707 case JOF_LOOKUPSWITCH:
4708 case JOF_LOOKUPSWITCHX:
4709 {
4710 jsint jmplen;
4711 jsbytecode *pc2;
4712 jsatomid npairs;
4713
4714 jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN
4715 : JUMPX_OFFSET_LEN;
4716 pc2 = pc;
4717 pc2 += jmplen;
4718 npairs = GET_ATOM_INDEX(pc2);
4719 pc2 += ATOM_INDEX_LEN;
4720 while (npairs) {
4721 pc2 += ATOM_INDEX_LEN;
4722 pc2 += jmplen;
4723 npairs--;
4724 }
4725 oplen = 1 + pc2 - pc;
4726 break;
4727 }
4728
4729 case JOF_LITOPX:
4730 pc2 = pc + 1 + LITERAL_INDEX_LEN;
4731 op = *pc2;
4732 cs = &js_CodeSpec[op];
4733 JS_ASSERT(cs->length > ATOM_INDEX_LEN);
4734 oplen += cs->length - (1 + ATOM_INDEX_LEN);
4735 break;
4736
4737 default:;
4738 }
4739
4740 if (sn && SN_TYPE(sn) == SRC_HIDDEN)
4741 continue;
4742
4743 nuses = cs->nuses;
4744 if (nuses < 0) {
4745 /* Call opcode pushes [callee, this, argv...]. */
4746 nuses = 2 + GET_ARGC(pc);
4747 } else if (op == JSOP_RETSUB) {
4748 /* Pop [exception or hole, retsub pc-index]. */
4749 JS_ASSERT(nuses == 0);
4750 nuses = 2;
4751 } else if (op == JSOP_LEAVEBLOCK || op == JSOP_LEAVEBLOCKEXPR) {
4752 JS_ASSERT(nuses == 0);
4753 nuses = GET_UINT16(pc);
4754 }
4755 pcdepth -= nuses;
4756 JS_ASSERT(pcdepth >= 0);
4757
4758 ndefs = cs->ndefs;
4759 if (op == JSOP_FINALLY) {
4760 /* Push [exception or hole, retsub pc-index]. */
4761 JS_ASSERT(ndefs == 0);
4762 ndefs = 2;
4763 } else if (op == JSOP_ENTERBLOCK) {
4764 jsatomid atomIndex;
4765 JSAtom *atom;
4766 JSObject *obj;
4767
4768 JS_ASSERT(ndefs == 0);
4769 atomIndex = pc2 ? GET_LITERAL_INDEX(pc) : GET_ATOM_INDEX(pc);
4770 atom = js_GetAtom(cx, &script->atomMap, atomIndex);
4771 obj = ATOM_TO_OBJECT(atom);
4772 JS_ASSERT(OBJ_BLOCK_DEPTH(cx, obj) == pcdepth);
4773 ndefs = OBJ_BLOCK_COUNT(cx, obj);
4774 }
4775 pcdepth += ndefs;
4776 }
4777
4778 name = NULL;
4779 jp = js_NewPrinter(cx, "js_DecompileValueGenerator", 0, JS_FALSE);
4780 if (jp) {
4781 if (fp->fun && fp->fun->object) {
4782 JS_ASSERT(OBJ_IS_NATIVE(fp->fun->object));
4783 jp->scope = OBJ_SCOPE(fp->fun->object);
4784 }
4785 jp->dvgfence = end;
4786 if (js_DecompileCode(jp, script, begin, (uintN)len, (uintN)pcdepth))
4787 name = js_GetPrinterOutput(jp);
4788 js_DestroyPrinter(jp);
4789 }
4790 return name;
4791
4792 do_fallback:
4793 return fallback ? fallback : js_ValueToSource(cx, v);
4794 }
4795