1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set sw=4 ts=8 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
17 * The Original Code is Mozilla Communicator client code, released
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.
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.
39 * ***** END LICENSE BLOCK ***** */
42 * JS bytecode descriptors, disassemblers, and decompilers.
53 #include "jsarena.h" /* Added by JSIFY */
54 #include "jsutil.h" /* Added by JSIFY */
61 #include "jsversion.h"
74 #include "jsstaticcheck.h"
78 #include "jsscriptinlines.h"
80 #include "jsautooplen.h"
85 * Index limit must stay within 32 bits.
87 JS_STATIC_ASSERT(sizeof(uint32
) * JS_BITS_PER_BYTE
>= INDEX_LIMIT_LOG2
+ 1);
89 /* Verify JSOP_XXX_LENGTH constant definitions. */
90 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
91 JS_STATIC_ASSERT(op##_LENGTH == length);
92 #include "jsopcode.tbl"
95 static const char js_incop_strs
[][3] = {"++", "--"};
96 static const char js_for_each_str
[] = "for each";
98 const JSCodeSpec js_CodeSpec
[] = {
99 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
100 {length,nuses,ndefs,prec,format},
101 #include "jsopcode.tbl"
105 uintN js_NumCodeSpecs
= JS_ARRAY_LENGTH(js_CodeSpec
);
108 * Each element of the array is either a source literal associated with JS
111 static const char *CodeToken
[] = {
112 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
114 #include "jsopcode.tbl"
118 #if defined(DEBUG) || defined(JS_JIT_SPEW)
120 * Array of JS bytecode names used by DEBUG-only js_Disassemble and by
123 const char *js_CodeName
[] = {
124 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
126 #include "jsopcode.tbl"
131 /************************************************************************/
134 GetJumpOffset(jsbytecode
*pc
, jsbytecode
*pc2
)
138 type
= JOF_OPTYPE(*pc
);
139 if (JOF_TYPE_IS_EXTENDED_JUMP(type
))
140 return GET_JUMPX_OFFSET(pc2
);
141 return GET_JUMP_OFFSET(pc2
);
145 js_GetIndexFromBytecode(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
,
151 op
= js_GetOpcode(cx
, script
, pc
);
152 JS_ASSERT(js_CodeSpec
[op
].length
>= 1 + pcoff
+ UINT16_LEN
);
155 * We need to detect index base prefix. It presents when resetbase
156 * follows the bytecode.
158 span
= js_CodeSpec
[op
].length
;
160 if (pc
- script
->code
+ span
< script
->length
) {
161 if (pc
[span
] == JSOP_RESETBASE
) {
162 base
= GET_INDEXBASE(pc
- JSOP_INDEXBASE_LENGTH
);
163 } else if (pc
[span
] == JSOP_RESETBASE0
) {
164 JS_ASSERT(JSOP_INDEXBASE1
<= pc
[-1] || pc
[-1] <= JSOP_INDEXBASE3
);
165 base
= (pc
[-1] - JSOP_INDEXBASE1
+ 1) << 16;
168 return base
+ GET_UINT16(pc
+ pcoff
);
172 js_GetVariableBytecodeLength(jsbytecode
*pc
)
175 uintN jmplen
, ncases
;
179 JS_ASSERT(js_CodeSpec
[op
].length
== -1);
181 case JSOP_TABLESWITCHX
:
182 jmplen
= JUMPX_OFFSET_LEN
;
184 case JSOP_TABLESWITCH
:
185 jmplen
= JUMP_OFFSET_LEN
;
187 /* Structure: default-jump case-low case-high case1-jump ... */
189 low
= GET_JUMP_OFFSET(pc
);
190 pc
+= JUMP_OFFSET_LEN
;
191 high
= GET_JUMP_OFFSET(pc
);
192 ncases
= (uintN
)(high
- low
+ 1);
193 return 1 + jmplen
+ INDEX_LEN
+ INDEX_LEN
+ ncases
* jmplen
;
195 case JSOP_LOOKUPSWITCHX
:
196 jmplen
= JUMPX_OFFSET_LEN
;
199 JS_ASSERT(op
== JSOP_LOOKUPSWITCH
);
200 jmplen
= JUMP_OFFSET_LEN
;
202 /* Structure: default-jump case-count (case1-value case1-jump) ... */
204 ncases
= GET_UINT16(pc
);
205 return 1 + jmplen
+ INDEX_LEN
+ ncases
* (INDEX_LEN
+ jmplen
);
210 js_GetVariableStackUses(JSOp op
, jsbytecode
*pc
)
212 JS_ASSERT(*pc
== op
|| *pc
== JSOP_TRAP
);
213 JS_ASSERT(js_CodeSpec
[op
].nuses
== -1);
216 return GET_UINT16(pc
);
218 return GET_UINT16(pc
);
219 case JSOP_LEAVEBLOCK
:
220 return GET_UINT16(pc
);
221 case JSOP_LEAVEBLOCKEXPR
:
222 return GET_UINT16(pc
) + 1;
224 return GET_UINT16(pc
);
226 /* stack: fun, this, [argc arguments] */
227 JS_ASSERT(op
== JSOP_NEW
|| op
== JSOP_CALL
||
228 op
== JSOP_EVAL
|| op
== JSOP_SETCALL
||
230 return 2 + GET_ARGC(pc
);
235 js_GetEnterBlockStackDefs(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
)
239 JS_ASSERT(*pc
== JSOP_ENTERBLOCK
|| *pc
== JSOP_TRAP
);
240 GET_OBJECT_FROM_BYTECODE(script
, pc
, 0, obj
);
241 return OBJ_BLOCK_COUNT(cx
, obj
);
246 JS_FRIEND_API(JSBool
)
247 js_Disassemble(JSContext
*cx
, JSScript
*script
, JSBool lines
, FILE *fp
)
249 jsbytecode
*pc
, *end
;
253 end
= pc
+ script
->length
;
255 if (pc
== script
->main
)
256 fputs("main:\n", fp
);
257 len
= js_Disassemble1(cx
, script
, pc
,
268 js_DumpScript(JSContext
*cx
, JSScript
*script
)
270 return js_Disassemble(cx
, script
, true, stdout
);
274 ToDisassemblySource(JSContext
*cx
, jsval v
)
276 if (!JSVAL_IS_PRIMITIVE(v
)) {
277 JSObject
*obj
= JSVAL_TO_OBJECT(v
);
278 JSClass
*clasp
= obj
->getClass();
280 if (clasp
== &js_BlockClass
) {
281 char *source
= JS_sprintf_append(NULL
, "depth %d {", OBJ_BLOCK_DEPTH(cx
, obj
));
282 for (JSScopeProperty
*sprop
= obj
->scope()->lastProperty();
284 sprop
= sprop
->parent
) {
285 const char *bytes
= js_AtomToPrintableString(cx
, JSID_TO_ATOM(sprop
->id
));
288 source
= JS_sprintf_append(source
, "%s: %d%s",
289 bytes
, sprop
->shortid
,
290 sprop
->parent
? ", " : "");
293 source
= JS_sprintf_append(source
, "}");
297 JSString
*str
= JS_NewString(cx
, source
, strlen(source
));
300 return js_GetStringBytes(cx
, str
);
303 if (clasp
== &js_FunctionClass
) {
304 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, obj
);
305 JSString
*str
= JS_DecompileFunction(cx
, fun
, JS_DONT_PRETTY_PRINT
);
308 return js_GetStringBytes(cx
, str
);
311 if (clasp
== &js_RegExpClass
) {
312 AutoValueRooter
tvr(cx
);
313 if (!js_regexp_toString(cx
, obj
, tvr
.addr()))
315 return js_GetStringBytes(cx
, JSVAL_TO_STRING(tvr
.value()));
319 return js_ValueToPrintableSource(cx
, v
);
323 js_Disassemble1(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
,
324 uintN loc
, JSBool lines
, FILE *fp
)
327 const JSCodeSpec
*cs
;
328 ptrdiff_t len
, off
, jmplen
;
338 if (op
>= JSOP_LIMIT
) {
339 char numBuf1
[12], numBuf2
[12];
340 JS_snprintf(numBuf1
, sizeof numBuf1
, "%d", op
);
341 JS_snprintf(numBuf2
, sizeof numBuf2
, "%d", JSOP_LIMIT
);
342 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
343 JSMSG_BYTECODE_TOO_BIG
, numBuf1
, numBuf2
);
346 cs
= &js_CodeSpec
[op
];
347 len
= (ptrdiff_t) cs
->length
;
348 fprintf(fp
, "%05u:", loc
);
350 fprintf(fp
, "%4u", JS_PCToLineNumber(cx
, script
, pc
));
351 fprintf(fp
, " %s", js_CodeName
[op
]);
352 type
= JOF_TYPE(cs
->format
);
355 if (op
== JSOP_TRAP
) {
356 op
= JS_GetTrapOpcode(cx
, script
, pc
);
357 len
= (ptrdiff_t) js_CodeSpec
[op
].length
;
363 off
= GetJumpOffset(pc
, pc
);
364 fprintf(fp
, " %u (%d)", loc
+ (intN
) off
, (intN
) off
);
370 index
= js_GetIndexFromBytecode(cx
, script
, pc
, 0);
371 if (type
== JOF_ATOM
) {
372 JS_GET_SCRIPT_ATOM(script
, pc
, index
, atom
);
375 if (type
== JOF_OBJECT
)
376 obj
= script
->getObject(index
);
378 obj
= script
->getRegExp(index
);
379 v
= OBJECT_TO_JSVAL(obj
);
381 bytes
= ToDisassemblySource(cx
, v
);
384 fprintf(fp
, " %s", bytes
);
388 i
= (jsint
)GET_UINT16(pc
);
389 fprintf(fp
, " %d", i
);
393 i
= (jsint
)GET_UINT16(pc
);
396 case JOF_TABLESWITCH
:
397 case JOF_TABLESWITCHX
:
402 jmplen
= (type
== JOF_TABLESWITCH
) ? JUMP_OFFSET_LEN
405 off
= GetJumpOffset(pc
, pc2
);
407 low
= GET_JUMP_OFFSET(pc2
);
408 pc2
+= JUMP_OFFSET_LEN
;
409 high
= GET_JUMP_OFFSET(pc2
);
410 pc2
+= JUMP_OFFSET_LEN
;
411 fprintf(fp
, " defaultOffset %d low %d high %d", (intN
) off
, low
, high
);
412 for (i
= low
; i
<= high
; i
++) {
413 off
= GetJumpOffset(pc
, pc2
);
414 fprintf(fp
, "\n\t%d: %d", i
, (intN
) off
);
421 case JOF_LOOKUPSWITCH
:
422 case JOF_LOOKUPSWITCHX
:
427 jmplen
= (type
== JOF_LOOKUPSWITCH
) ? JUMP_OFFSET_LEN
430 off
= GetJumpOffset(pc
, pc2
);
432 npairs
= GET_UINT16(pc2
);
434 fprintf(fp
, " offset %d npairs %u", (intN
) off
, (uintN
) npairs
);
436 JS_GET_SCRIPT_ATOM(script
, pc
, GET_INDEX(pc2
), atom
);
438 off
= GetJumpOffset(pc
, pc2
);
441 bytes
= ToDisassemblySource(cx
, ATOM_KEY(atom
));
444 fprintf(fp
, "\n\t%s: %d", bytes
, (intN
) off
);
452 fprintf(fp
, " %u", GET_ARGNO(pc
));
456 fprintf(fp
, " %u", GET_SLOTNO(pc
));
461 fprintf(fp
, " %u", GET_SLOTNO(pc
));
462 index
= js_GetIndexFromBytecode(cx
, script
, pc
, SLOTNO_LEN
);
463 if (type
== JOF_SLOTATOM
) {
464 JS_GET_SCRIPT_ATOM(script
, pc
, index
, atom
);
467 obj
= script
->getObject(index
);
468 v
= OBJECT_TO_JSVAL(obj
);
470 bytes
= ToDisassemblySource(cx
, v
);
473 fprintf(fp
, " %s", bytes
);
477 JS_ASSERT(op
== JSOP_UINT24
|| op
== JSOP_NEWARRAY
);
478 i
= (jsint
)GET_UINT24(pc
);
490 JS_ASSERT(op
== JSOP_INT32
);
493 fprintf(fp
, " %d", i
);
498 JS_snprintf(numBuf
, sizeof numBuf
, "%lx", (unsigned long) cs
->format
);
499 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
500 JSMSG_UNKNOWN_FORMAT
, numBuf
);
510 /************************************************************************/
513 * Sprintf, but with unlimited and automatically allocated buffering.
515 typedef struct Sprinter
{
516 JSContext
*context
; /* context executing the decompiler */
517 JSArenaPool
*pool
; /* string allocation pool */
518 char *base
; /* base address of buffer in pool */
519 size_t size
; /* size of buffer allocated at base */
520 ptrdiff_t offset
; /* offset of next free char in buffer */
523 #define INIT_SPRINTER(cx, sp, ap, off) \
524 ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0, \
527 #define OFF2STR(sp,off) ((sp)->base + (off))
528 #define STR2OFF(sp,str) ((str) - (sp)->base)
529 #define RETRACT(sp,str) ((sp)->offset = STR2OFF(sp, str))
532 SprintEnsureBuffer(Sprinter
*sp
, size_t len
)
537 nb
= (sp
->offset
+ len
+ 1) - sp
->size
;
542 sp
->pool
->allocateCast
<char *>(base
, nb
);
544 sp
->pool
->growCast
<char *>(base
, sp
->size
, nb
);
547 js_ReportOutOfScriptQuota(sp
->context
);
556 SprintPut(Sprinter
*sp
, const char *s
, size_t len
)
558 ptrdiff_t offset
= sp
->size
; /* save old size */
559 char *bp
= sp
->base
; /* save old base */
561 /* Allocate space for s, including the '\0' at the end. */
562 if (!SprintEnsureBuffer(sp
, len
))
565 if (sp
->base
!= bp
&& /* buffer was realloc'ed */
566 s
>= bp
&& s
< bp
+ offset
) { /* s was within the buffer */
567 s
= sp
->base
+ (s
- bp
); /* this is where it lives now */
570 /* Advance offset and copy s into sp's buffer. */
573 bp
= sp
->base
+ offset
;
580 SprintCString(Sprinter
*sp
, const char *s
)
582 return SprintPut(sp
, s
, strlen(s
));
586 SprintString(Sprinter
*sp
, JSString
*str
)
592 str
->getCharsAndLength(chars
, length
);
596 size
= js_GetDeflatedStringLength(sp
->context
, chars
, length
);
597 if (size
== (size_t)-1 || !SprintEnsureBuffer(sp
, size
))
602 js_DeflateStringToBuffer(sp
->context
, chars
, length
, sp
->base
+ offset
,
604 sp
->base
[sp
->offset
] = 0;
610 Sprint(Sprinter
*sp
, const char *format
, ...)
616 va_start(ap
, format
);
617 bp
= JS_vsmprintf(format
, ap
); /* XXX vsaprintf */
620 JS_ReportOutOfMemory(sp
->context
);
623 offset
= SprintCString(sp
, bp
);
628 const char js_EscapeMap
[] = {
641 #define DONT_ESCAPE 0x10000
644 QuoteString(Sprinter
*sp
, JSString
*str
, uint32 quote
)
646 JSBool dontEscape
, ok
;
649 const jschar
*s
, *t
, *z
;
653 /* Sample off first for later return value pointer computation. */
654 dontEscape
= (quote
& DONT_ESCAPE
) != 0;
657 if (qc
&& Sprint(sp
, "%c", (char)qc
) < 0)
660 /* Loop control variables: z points at end of string sentinel. */
661 str
->getCharsAndEnd(s
, z
);
662 for (t
= s
; t
< z
; s
= ++t
) {
663 /* Move t forward from s past un-quote-worthy characters. */
665 while (JS_ISPRINT(c
) && c
!= qc
&& c
!= '\\' && c
!= '\t' &&
673 /* Allocate space for s, including the '\0' at the end. */
674 if (!SprintEnsureBuffer(sp
, len
))
677 /* Advance sp->offset and copy s into sp's buffer. */
678 bp
= sp
->base
+ sp
->offset
;
687 /* Use js_EscapeMap, \u, or \x only if necessary. */
688 if (!(c
>> 8) && (e
= strchr(js_EscapeMap
, (int)c
)) != NULL
) {
690 ? Sprint(sp
, "%c", (char)c
) >= 0
691 : Sprint(sp
, "\\%c", e
[1]) >= 0;
693 ok
= Sprint(sp
, (c
>> 8) ? "\\u%04X" : "\\x%02X", c
) >= 0;
699 /* Sprint the closing quote and return the quoted string. */
700 if (qc
&& Sprint(sp
, "%c", (char)qc
) < 0)
704 * If we haven't Sprint'd anything yet, Sprint an empty string so that
705 * the OFF2STR below gives a valid result.
707 if (off
== sp
->offset
&& Sprint(sp
, "") < 0)
709 return OFF2STR(sp
, off
);
713 js_QuoteString(JSContext
*cx
, JSString
*str
, jschar quote
)
720 mark
= cx
->tempPool
.getMark();
721 INIT_SPRINTER(cx
, &sprinter
, &cx
->tempPool
, 0);
722 bytes
= QuoteString(&sprinter
, str
, quote
);
723 escstr
= bytes
? JS_NewStringCopyZ(cx
, bytes
) : NULL
;
724 cx
->tempPool
.release(mark
);
728 /************************************************************************/
731 Sprinter sprinter
; /* base class state */
732 JSArenaPool pool
; /* string allocation pool */
733 uintN indent
; /* indentation in spaces */
734 bool pretty
; /* pretty-print: indent, use newlines */
735 bool grouped
; /* in parenthesized expression context */
736 bool strict
; /* in code marked strict */
737 JSScript
*script
; /* script being printed */
738 jsbytecode
*dvgfence
; /* DecompileExpression fencepost */
739 jsbytecode
**pcstack
; /* DecompileExpression modeled stack */
740 JSFunction
*fun
; /* interpreted function */
741 jsuword
*localNames
; /* argument and variable names */
745 js_NewPrinter(JSContext
*cx
, const char *name
, JSFunction
*fun
,
746 uintN indent
, JSBool pretty
, JSBool grouped
, JSBool strict
)
750 jp
= (JSPrinter
*) cx
->malloc(sizeof(JSPrinter
));
753 INIT_SPRINTER(cx
, &jp
->sprinter
, &jp
->pool
, 0);
754 jp
->pool
.init(name
, 256, 1, &cx
->scriptStackQuota
);
756 jp
->pretty
= !!pretty
;
757 jp
->grouped
= !!grouped
;
758 jp
->strict
= !!strict
;
763 jp
->localNames
= NULL
;
764 if (fun
&& FUN_INTERPRETED(fun
) && fun
->hasLocalNames()) {
765 jp
->localNames
= js_GetLocalNameArray(cx
, fun
, &jp
->pool
);
766 if (!jp
->localNames
) {
767 js_DestroyPrinter(jp
);
775 js_DestroyPrinter(JSPrinter
*jp
)
778 jp
->sprinter
.context
->free(jp
);
782 js_GetPrinterOutput(JSPrinter
*jp
)
787 cx
= jp
->sprinter
.context
;
788 if (!jp
->sprinter
.base
)
789 return cx
->runtime
->emptyString
;
790 str
= JS_NewStringCopyZ(cx
, jp
->sprinter
.base
);
794 INIT_SPRINTER(cx
, &jp
->sprinter
, &jp
->pool
, 0);
799 * NB: Indexed by SRC_DECL_* defines from jsemit.h.
801 static const char * const var_prefix
[] = {"var ", "const ", "let "};
804 VarPrefix(jssrcnote
*sn
)
806 if (sn
&& (SN_TYPE(sn
) == SRC_DECL
|| SN_TYPE(sn
) == SRC_GROUPASSIGN
)) {
807 ptrdiff_t type
= js_GetSrcNoteOffset(sn
, 0);
808 if ((uintN
)type
<= SRC_DECL_LET
)
809 return var_prefix
[type
];
815 js_printf(JSPrinter
*jp
, const char *format
, ...)
824 va_start(ap
, format
);
826 /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */
827 if (*format
== '\t') {
829 if (jp
->pretty
&& Sprint(&jp
->sprinter
, "%*s", jp
->indent
, "") < 0) {
835 /* Suppress newlines (must be once per format, at the end) if not pretty. */
837 if (!jp
->pretty
&& format
[cc
= strlen(format
) - 1] == '\n') {
838 fp
= JS_strdup(jp
->sprinter
.context
, format
);
847 /* Allocate temp space, convert format, and put. */
848 bp
= JS_vsmprintf(format
, ap
); /* XXX vsaprintf */
850 jp
->sprinter
.context
->free(fp
);
854 JS_ReportOutOfMemory(jp
->sprinter
.context
);
860 if (SprintPut(&jp
->sprinter
, bp
, (size_t)cc
) < 0)
869 js_puts(JSPrinter
*jp
, const char *s
)
871 return SprintCString(&jp
->sprinter
, s
) >= 0;
874 /************************************************************************/
876 typedef struct SprintStack
{
877 Sprinter sprinter
; /* sprinter for postfix to infix buffering */
878 ptrdiff_t *offsets
; /* stack of postfix string offsets */
879 jsbytecode
*opcodes
; /* parallel stack of JS opcodes */
880 uintN top
; /* top of stack index */
881 uintN inArrayInit
; /* array initialiser/comprehension level */
882 JSBool inGenExp
; /* in generator expression */
883 JSPrinter
*printer
; /* permanent output goes here */
887 * Find the depth of the operand stack when the interpreter reaches the given
888 * pc in script. pcstack must have space for least script->depth elements. On
889 * return it will contain pointers to opcodes that populated the interpreter's
890 * current operand stack.
892 * This function cannot raise an exception or error. However, due to a risk of
893 * potential bugs when modeling the stack, the function returns -1 if it
894 * detects an inconsistency in the model. Such an inconsistency triggers an
895 * assert in a debug build.
898 ReconstructPCStack(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
,
899 jsbytecode
**pcstack
);
901 #define FAILED_EXPRESSION_DECOMPILER ((char *) 1)
904 * Decompile a part of expression up to the given pc. The function returns
905 * NULL on out-of-memory, or the FAILED_EXPRESSION_DECOMPILER sentinel when
906 * the decompiler fails due to a bug and/or unimplemented feature, or the
907 * decompiled string on success.
910 DecompileExpression(JSContext
*cx
, JSScript
*script
, JSFunction
*fun
,
914 * Get a stacked offset from ss->sprinter.base, or if the stacked value |off|
915 * is negative, fetch the generating pc from printer->pcstack[-2 - off] and
916 * decompile the code that generated the missing value. This is used when
917 * reporting errors, where the model stack will lack |pcdepth| non-negative
918 * offsets (see DecompileExpression and DecompileCode).
920 * If the stacked offset is -1, return 0 to index the NUL padding at the start
921 * of ss->sprinter.base. If this happens, it means there is a decompiler bug
922 * to fix, but it won't violate memory safety.
925 GetOff(SprintStack
*ss
, uintN i
)
931 off
= ss
->offsets
[i
];
935 JS_ASSERT(off
<= -2);
936 JS_ASSERT(ss
->printer
->pcstack
);
937 if (off
<= -2 && ss
->printer
->pcstack
) {
938 pc
= ss
->printer
->pcstack
[-2 - off
];
939 bytes
= DecompileExpression(ss
->sprinter
.context
, ss
->printer
->script
,
940 ss
->printer
->fun
, pc
);
943 if (bytes
!= FAILED_EXPRESSION_DECOMPILER
) {
944 off
= SprintCString(&ss
->sprinter
, bytes
);
947 ss
->offsets
[i
] = off
;
948 ss
->sprinter
.context
->free(bytes
);
951 if (!ss
->sprinter
.base
&& SprintPut(&ss
->sprinter
, "", 0) >= 0) {
952 memset(ss
->sprinter
.base
, 0, ss
->sprinter
.offset
);
960 GetStr(SprintStack
*ss
, uintN i
)
965 * Must call GetOff before using ss->sprinter.base, since it may be null
966 * until bootstrapped by GetOff.
969 return OFF2STR(&ss
->sprinter
, off
);
973 * Gap between stacked strings to allow for insertion of parens and commas
974 * when auto-parenthesizing expressions and decompiling array initialisers
975 * (see the JSOP_NEWARRAY case in Decompile).
977 #define PAREN_SLOP (2 + 1)
980 * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME,
981 * JSOP_SETPROP, and JSOP_SETELEM, respectively. They are never stored in
982 * bytecode, so they don't preempt valid opcodes.
984 #define JSOP_GETPROP2 JSOP_LIMIT
985 #define JSOP_GETELEM2 JSOP_LIMIT + 1
986 JS_STATIC_ASSERT(JSOP_GETELEM2
<= 255);
989 AddParenSlop(SprintStack
*ss
)
991 memset(OFF2STR(&ss
->sprinter
, ss
->sprinter
.offset
), 0, PAREN_SLOP
);
992 ss
->sprinter
.offset
+= PAREN_SLOP
;
996 PushOff(SprintStack
*ss
, ptrdiff_t off
, JSOp op
)
1000 if (!SprintEnsureBuffer(&ss
->sprinter
, PAREN_SLOP
))
1003 /* ss->top points to the next free slot; be paranoid about overflow. */
1005 JS_ASSERT(top
< StackDepth(ss
->printer
->script
));
1006 if (top
>= StackDepth(ss
->printer
->script
)) {
1007 JS_ReportOutOfMemory(ss
->sprinter
.context
);
1011 /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */
1012 ss
->offsets
[top
] = off
;
1013 ss
->opcodes
[top
] = jsbytecode((op
== JSOP_GETPROP2
) ? JSOP_GETPROP
1014 : (op
== JSOP_GETELEM2
) ? JSOP_GETELEM
1022 PopOffPrec(SprintStack
*ss
, uint8 prec
)
1025 const JSCodeSpec
*topcs
;
1028 /* ss->top points to the next free slot; be paranoid about underflow. */
1030 JS_ASSERT(top
!= 0);
1035 off
= GetOff(ss
, top
);
1036 topcs
= &js_CodeSpec
[ss
->opcodes
[top
]];
1037 if (topcs
->prec
!= 0 && topcs
->prec
< prec
) {
1038 ss
->sprinter
.offset
= ss
->offsets
[top
] = off
- 2;
1039 off
= Sprint(&ss
->sprinter
, "(%s)", OFF2STR(&ss
->sprinter
, off
));
1041 ss
->sprinter
.offset
= off
;
1047 PopStrPrec(SprintStack
*ss
, uint8 prec
)
1051 off
= PopOffPrec(ss
, prec
);
1052 return OFF2STR(&ss
->sprinter
, off
);
1056 PopOff(SprintStack
*ss
, JSOp op
)
1058 return PopOffPrec(ss
, js_CodeSpec
[op
].prec
);
1062 PopStr(SprintStack
*ss
, JSOp op
)
1064 return PopStrPrec(ss
, js_CodeSpec
[op
].prec
);
1067 typedef struct TableEntry
{
1071 jsint order
; /* source order for stable tableswitch sort */
1075 CompareOffsets(void *arg
, const void *v1
, const void *v2
, int *result
)
1077 ptrdiff_t offset_diff
;
1078 const TableEntry
*te1
= (const TableEntry
*) v1
,
1079 *te2
= (const TableEntry
*) v2
;
1081 offset_diff
= te1
->offset
- te2
->offset
;
1082 *result
= (offset_diff
== 0 ? te1
->order
- te2
->order
1083 : offset_diff
< 0 ? -1
1089 SprintDoubleValue(Sprinter
*sp
, jsval v
, JSOp
*opp
)
1093 char *s
, buf
[DTOSTR_STANDARD_BUFFER_SIZE
];
1095 JS_ASSERT(JSVAL_IS_DOUBLE(v
));
1096 d
= *JSVAL_TO_DOUBLE(v
);
1097 if (JSDOUBLE_IS_NEGZERO(d
)) {
1098 todo
= SprintCString(sp
, "-0");
1100 } else if (!JSDOUBLE_IS_FINITE(d
)) {
1101 /* Don't use Infinity and NaN, they're mutable. */
1102 todo
= SprintCString(sp
,
1110 s
= js_dtostr(JS_THREAD_DATA(sp
->context
)->dtoaState
, buf
, sizeof buf
,
1111 DTOSTR_STANDARD
, 0, d
);
1113 JS_ReportOutOfMemory(sp
->context
);
1116 JS_ASSERT(strcmp(s
, js_Infinity_str
) &&
1118 strcmp(s
+ 1, js_Infinity_str
)) &&
1119 strcmp(s
, js_NaN_str
));
1120 todo
= Sprint(sp
, s
);
1126 Decompile(SprintStack
*ss
, jsbytecode
*pc
, intN nb
, JSOp nextop
);
1129 DecompileSwitch(SprintStack
*ss
, TableEntry
*table
, uintN tableLength
,
1130 jsbytecode
*pc
, ptrdiff_t switchLength
,
1131 ptrdiff_t defaultOffset
, JSBool isCondSwitch
)
1135 ptrdiff_t off
, off2
, diff
, caseExprOff
, todo
;
1141 cx
= ss
->sprinter
.context
;
1144 /* JSOP_CONDSWITCH doesn't pop, unlike JSOP_{LOOKUP,TABLE}SWITCH. */
1145 off
= isCondSwitch
? GetOff(ss
, ss
->top
-1) : PopOff(ss
, JSOP_NOP
);
1146 lval
= OFF2STR(&ss
->sprinter
, off
);
1148 js_printf(jp
, "\tswitch (%s) {\n", lval
);
1151 diff
= table
[0].offset
- defaultOffset
;
1154 js_printf(jp
, "\t%s:\n", js_default_str
);
1156 if (!Decompile(ss
, pc
+ defaultOffset
, diff
, JSOP_NOP
))
1161 caseExprOff
= isCondSwitch
? JSOP_CONDSWITCH_LENGTH
: 0;
1163 for (i
= 0; i
< tableLength
; i
++) {
1164 off
= table
[i
].offset
;
1165 off2
= (i
+ 1 < tableLength
) ? table
[i
+ 1].offset
: switchLength
;
1169 ptrdiff_t nextCaseExprOff
;
1172 * key encodes the JSOP_CASE bytecode's offset from switchtop.
1173 * The next case expression follows immediately, unless we are
1176 nextCaseExprOff
= (ptrdiff_t)JSVAL_TO_INT(key
);
1177 nextCaseExprOff
+= js_CodeSpec
[pc
[nextCaseExprOff
]].length
;
1179 if (!Decompile(ss
, pc
+ caseExprOff
,
1180 nextCaseExprOff
- caseExprOff
, JSOP_NOP
)) {
1183 caseExprOff
= nextCaseExprOff
;
1185 /* Balance the stack as if this JSOP_CASE matched. */
1189 * key comes from an atom, not the decompiler, so we need to
1190 * quote it if it's a string literal. But if table[i].label
1191 * is non-null, key was constant-propagated and label is the
1192 * name of the const we should show as the case label. We set
1193 * key to undefined so this identifier is escaped, if required
1194 * by non-ASCII characters, but not quoted, by QuoteString.
1197 if (table
[i
].label
) {
1198 str
= ATOM_TO_STRING(table
[i
].label
);
1200 } else if (JSVAL_IS_DOUBLE(key
)) {
1203 todo
= SprintDoubleValue(&ss
->sprinter
, key
, &junk
);
1206 str
= js_ValueToString(cx
, key
);
1211 rval
= OFF2STR(&ss
->sprinter
, todo
);
1213 rval
= QuoteString(&ss
->sprinter
, str
, (jschar
)
1214 (JSVAL_IS_STRING(key
) ? '"' : 0));
1218 RETRACT(&ss
->sprinter
, rval
);
1220 js_printf(jp
, "\tcase %s:\n", rval
);
1224 if (off
<= defaultOffset
&& defaultOffset
< off2
) {
1225 diff
= defaultOffset
- off
;
1227 if (!Decompile(ss
, pc
+ off
, diff
, JSOP_NOP
))
1229 off
= defaultOffset
;
1232 js_printf(jp
, "\t%s:\n", js_default_str
);
1235 if (!Decompile(ss
, pc
+ off
, off2
- off
, JSOP_NOP
))
1239 /* Re-balance as if last JSOP_CASE or JSOP_DEFAULT mismatched. */
1245 if (defaultOffset
== switchLength
) {
1247 js_printf(jp
, "\t%s:;\n", js_default_str
);
1250 js_printf(jp
, "\t}\n");
1252 /* By the end of a JSOP_CONDSWITCH, the discriminant has been popped. */
1258 #define LOCAL_ASSERT_CUSTOM(expr, BAD_EXIT) \
1261 if (!(expr)) { BAD_EXIT; } \
1264 #define LOCAL_ASSERT_RV(expr, rv) \
1265 LOCAL_ASSERT_CUSTOM(expr, return (rv))
1268 GetArgOrVarAtom(JSPrinter
*jp
, uintN slot
)
1272 LOCAL_ASSERT_RV(jp
->fun
, NULL
);
1273 LOCAL_ASSERT_RV(slot
< jp
->fun
->countLocalNames(), NULL
);
1274 name
= JS_LOCAL_NAME_TO_ATOM(jp
->localNames
[slot
]);
1275 #if !JS_HAS_DESTRUCTURING
1276 LOCAL_ASSERT_RV(name
, NULL
);
1282 GetLocal(SprintStack
*ss
, jsint i
)
1291 JSScopeProperty
*sprop
;
1294 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, "")
1296 off
= ss
->offsets
[i
];
1298 return OFF2STR(&ss
->sprinter
, off
);
1301 * We must be called from js_DecompileValueGenerator (via Decompile) when
1302 * dereferencing a local that's undefined or null. Search script->objects
1303 * for the block containing this local by its stack index, i.
1305 * In case of destructuring's use of JSOP_GETLOCAL, however, there may be
1306 * no such local. This could mean no blocks (no script objects at all, or
1307 * none of the script's object literals are blocks), or the stack slot i is
1308 * not in a block. In either case, return GetStr(ss, i).
1310 cx
= ss
->sprinter
.context
;
1311 script
= ss
->printer
->script
;
1312 if (script
->objectsOffset
== 0)
1313 return GetStr(ss
, i
);
1314 for (j
= 0, n
= script
->objects()->length
; ; j
++) {
1316 return GetStr(ss
, i
);
1317 obj
= script
->getObject(j
);
1318 if (obj
->getClass() == &js_BlockClass
) {
1319 depth
= OBJ_BLOCK_DEPTH(cx
, obj
);
1320 count
= OBJ_BLOCK_COUNT(cx
, obj
);
1321 if ((jsuint
)(i
- depth
) < (jsuint
)count
)
1327 for (sprop
= obj
->scope()->lastProperty(); sprop
; sprop
= sprop
->parent
) {
1328 if (sprop
->shortid
== i
)
1332 LOCAL_ASSERT(sprop
&& JSID_IS_ATOM(sprop
->id
));
1333 atom
= JSID_TO_ATOM(sprop
->id
);
1334 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
1337 RETRACT(&ss
->sprinter
, rval
);
1344 IsVarSlot(JSPrinter
*jp
, jsbytecode
*pc
, jsint
*indexp
)
1348 slot
= GET_SLOTNO(pc
);
1349 if (slot
< jp
->script
->nfixed
) {
1350 /* The slot refers to a variable with name stored in jp->localNames. */
1351 *indexp
= jp
->fun
->nargs
+ slot
;
1355 /* We have a local which index is relative to the stack base. */
1356 slot
-= jp
->script
->nfixed
;
1357 JS_ASSERT(slot
< StackDepth(jp
->script
));
1362 #define LOAD_ATOM(PCOFF) \
1363 GET_ATOM_FROM_BYTECODE(jp->script, pc, PCOFF, atom)
1365 #if JS_HAS_DESTRUCTURING
1367 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
1368 #define LOAD_OP_DATA(pc) (oplen = (cs = &js_CodeSpec[op=(JSOp)*pc])->length)
1371 DecompileDestructuring(SprintStack
*ss
, jsbytecode
*pc
, jsbytecode
*endpc
);
1374 DecompileDestructuringLHS(SprintStack
*ss
, jsbytecode
*pc
, jsbytecode
*endpc
,
1380 const JSCodeSpec
*cs
;
1383 const char *lval
, *xval
;
1388 cx
= ss
->sprinter
.context
;
1395 todo
= SprintPut(&ss
->sprinter
, ", ", 2);
1399 pc
= DecompileDestructuring(ss
, pc
, endpc
);
1405 lval
= PopStr(ss
, JSOP_NOP
);
1406 todo
= SprintCString(&ss
->sprinter
, lval
);
1407 if (op
== JSOP_POPN
)
1409 LOCAL_ASSERT(*pc
== JSOP_POP
);
1415 LOCAL_ASSERT(pc
[oplen
] == JSOP_POP
|| pc
[oplen
] == JSOP_POPN
);
1418 case JSOP_SETLOCALPOP
:
1421 if (op
== JSOP_SETARG
) {
1422 atom
= GetArgOrVarAtom(jp
, GET_SLOTNO(pc
));
1424 } else if (op
== JSOP_SETGVAR
) {
1426 } else if (IsVarSlot(jp
, pc
, &i
)) {
1427 atom
= GetArgOrVarAtom(jp
, i
);
1430 lval
= GetLocal(ss
, i
);
1433 lval
= js_AtomToPrintableString(cx
, atom
);
1435 todo
= SprintCString(&ss
->sprinter
, lval
);
1436 if (op
!= JSOP_SETLOCALPOP
) {
1441 if (op
== JSOP_POPN
)
1443 LOCAL_ASSERT(op
== JSOP_POP
);
1449 * We may need to auto-parenthesize the left-most value decompiled
1450 * here, so add back PAREN_SLOP temporarily. Then decompile until the
1451 * opcode that would reduce the stack depth to (ss->top-1), which we
1452 * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for
1455 todo
= ss
->sprinter
.offset
;
1456 ss
->sprinter
.offset
= todo
+ PAREN_SLOP
;
1457 pc
= Decompile(ss
, pc
, -((intN
)ss
->top
), JSOP_NOP
);
1463 LOCAL_ASSERT(op
== JSOP_ENUMELEM
|| op
== JSOP_ENUMCONSTELEM
);
1464 xval
= PopStr(ss
, JSOP_NOP
);
1465 lval
= PopStr(ss
, JSOP_GETPROP
);
1466 ss
->sprinter
.offset
= todo
;
1467 if (*lval
== '\0') {
1468 /* lval is from JSOP_BINDNAME, so just print xval. */
1469 todo
= SprintCString(&ss
->sprinter
, xval
);
1470 } else if (*xval
== '\0') {
1471 /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */
1472 todo
= SprintCString(&ss
->sprinter
, lval
);
1474 todo
= Sprint(&ss
->sprinter
,
1475 (JOF_OPMODE(ss
->opcodes
[ss
->top
+1]) == JOF_XMLNAME
)
1486 LOCAL_ASSERT(pc
< endpc
);
1492 * Starting with a SRC_DESTRUCT-annotated JSOP_DUP, decompile a destructuring
1493 * left-hand side object or array initialiser, including nested destructuring
1494 * initialisers. On successful return, the decompilation will be pushed on ss
1495 * and the return value will point to the POP or GROUP bytecode following the
1496 * destructuring expression.
1498 * At any point, if pc is equal to endpc and would otherwise advance, we stop
1499 * immediately and return endpc.
1502 DecompileDestructuring(SprintStack
*ss
, jsbytecode
*pc
, jsbytecode
*endpc
)
1508 const JSCodeSpec
*cs
;
1518 LOCAL_ASSERT(*pc
== JSOP_DUP
);
1519 pc
+= JSOP_DUP_LENGTH
;
1522 * Set head so we can rewrite '[' to '{' as needed. Back up PAREN_SLOP
1523 * chars so the destructuring decompilation accumulates contiguously in
1524 * ss->sprinter starting with "[".
1526 head
= SprintPut(&ss
->sprinter
, "[", 1);
1527 if (head
< 0 || !PushOff(ss
, head
, JSOP_NOP
))
1529 ss
->sprinter
.offset
-= PAREN_SLOP
;
1530 LOCAL_ASSERT(head
== ss
->sprinter
.offset
- 1);
1531 LOCAL_ASSERT(*OFF2STR(&ss
->sprinter
, head
) == '[');
1533 cx
= ss
->sprinter
.context
;
1537 while (pc
< endpc
) {
1538 #if JS_HAS_DESTRUCTURING_SHORTHAND
1539 ptrdiff_t nameoff
= -1;
1550 /* Handle the optimized number-pushing opcodes. */
1551 case JSOP_ZERO
: d
= i
= 0; goto do_getelem
;
1552 case JSOP_ONE
: d
= i
= 1; goto do_getelem
;
1553 case JSOP_UINT16
: d
= i
= GET_UINT16(pc
); goto do_getelem
;
1554 case JSOP_UINT24
: d
= i
= GET_UINT24(pc
); goto do_getelem
;
1555 case JSOP_INT8
: d
= i
= GET_INT8(pc
); goto do_getelem
;
1556 case JSOP_INT32
: d
= i
= GET_INT32(pc
); goto do_getelem
;
1559 GET_DOUBLE_FROM_BYTECODE(jp
->script
, pc
, 0, atom
);
1560 d
= *ATOM_TO_DOUBLE(atom
);
1561 LOCAL_ASSERT(JSDOUBLE_IS_FINITE(d
) && !JSDOUBLE_IS_NEGZERO(d
));
1565 sn
= js_GetSrcNote(jp
->script
, pc
);
1570 LOCAL_ASSERT(op
== JSOP_GETELEM
);
1572 /* Distinguish object from array by opcode or source note. */
1573 if (sn
&& SN_TYPE(sn
) == SRC_INITPROP
) {
1574 *OFF2STR(&ss
->sprinter
, head
) = '{';
1575 if (Sprint(&ss
->sprinter
, "%g: ", d
) < 0)
1578 /* Sanity check for the gnarly control flow above. */
1579 LOCAL_ASSERT(i
== d
);
1581 /* Fill in any holes (holes at the end don't matter). */
1582 while (++lasti
< i
) {
1583 if (SprintPut(&ss
->sprinter
, ", ", 2) < 0)
1590 atom
= cx
->runtime
->atomState
.lengthAtom
;
1591 goto do_destructure_atom
;
1596 do_destructure_atom
:
1597 *OFF2STR(&ss
->sprinter
, head
) = '{';
1598 str
= ATOM_TO_STRING(atom
);
1599 #if JS_HAS_DESTRUCTURING_SHORTHAND
1600 nameoff
= ss
->sprinter
.offset
;
1602 if (!QuoteString(&ss
->sprinter
, str
,
1603 js_IsIdentifier(str
) ? 0 : (jschar
)'\'')) {
1606 if (SprintPut(&ss
->sprinter
, ": ", 2) < 0)
1619 * Decompile the left-hand side expression whose bytecode starts at pc
1620 * and continues for a bounded number of bytecodes or stack operations
1621 * (and which in any event stops before endpc).
1623 pc
= DecompileDestructuringLHS(ss
, pc
, endpc
, &hole
);
1627 #if JS_HAS_DESTRUCTURING_SHORTHAND
1629 ptrdiff_t offset
, initlen
;
1631 offset
= ss
->sprinter
.offset
;
1632 LOCAL_ASSERT(*OFF2STR(&ss
->sprinter
, offset
) == '\0');
1633 initlen
= offset
- nameoff
;
1634 LOCAL_ASSERT(initlen
>= 4);
1636 /* Early check to rule out odd "name: lval" length. */
1637 if (((size_t)initlen
& 1) == 0) {
1642 * Even "name: lval" string length: check for "x: x" and the
1643 * like, and apply the shorthand if we can.
1645 namelen
= (size_t)(initlen
- 2) >> 1;
1646 name
= OFF2STR(&ss
->sprinter
, nameoff
);
1647 if (!strncmp(name
+ namelen
, ": ", 2) &&
1648 !strncmp(name
, name
+ namelen
+ 2, namelen
)) {
1649 offset
-= namelen
+ 2;
1650 *OFF2STR(&ss
->sprinter
, offset
) = '\0';
1651 ss
->sprinter
.offset
= offset
;
1657 if (pc
== endpc
|| *pc
!= JSOP_DUP
)
1661 * We should stop if JSOP_DUP is either without notes or its note is
1662 * not SRC_CONTINUE. The former happens when JSOP_DUP duplicates the
1663 * last destructuring reference implementing an op= assignment like in
1664 * '([t] = z).y += x'. In the latter case the note is SRC_DESTRUCT and
1665 * means another destructuring initialiser abuts this one like in
1668 sn
= js_GetSrcNote(jp
->script
, pc
);
1671 if (SN_TYPE(sn
) != SRC_CONTINUE
) {
1672 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_DESTRUCT
);
1676 if (!hole
&& SprintPut(&ss
->sprinter
, ", ", 2) < 0)
1679 pc
+= JSOP_DUP_LENGTH
;
1683 lval
= OFF2STR(&ss
->sprinter
, head
);
1684 if (SprintPut(&ss
->sprinter
, (*lval
== '[') ? "]" : "}", 1) < 0)
1690 DecompileGroupAssignment(SprintStack
*ss
, jsbytecode
*pc
, jsbytecode
*endpc
,
1691 jssrcnote
*sn
, ptrdiff_t *todop
)
1694 const JSCodeSpec
*cs
;
1695 uintN oplen
, start
, end
, i
;
1701 LOCAL_ASSERT(op
== JSOP_PUSH
|| op
== JSOP_GETLOCAL
);
1703 todo
= Sprint(&ss
->sprinter
, "%s[", VarPrefix(sn
));
1704 if (todo
< 0 || !PushOff(ss
, todo
, JSOP_NOP
))
1706 ss
->sprinter
.offset
-= PAREN_SLOP
;
1712 pc
= DecompileDestructuringLHS(ss
, pc
, endpc
, &hole
);
1718 if (op
!= JSOP_PUSH
&& op
!= JSOP_GETLOCAL
)
1720 if (!hole
&& SprintPut(&ss
->sprinter
, ", ", 2) < 0)
1724 LOCAL_ASSERT(op
== JSOP_POPN
);
1725 if (SprintPut(&ss
->sprinter
, "] = [", 5) < 0)
1729 start
= end
- GET_UINT16(pc
);
1730 for (i
= start
; i
< end
; i
++) {
1731 rval
= GetStr(ss
, i
);
1732 if (Sprint(&ss
->sprinter
,
1733 (i
== start
) ? "%s" : ", %s",
1734 (i
== end
- 1 && *rval
== '\0') ? ", " : rval
) < 0) {
1739 if (SprintPut(&ss
->sprinter
, "]", 1) < 0)
1741 ss
->sprinter
.offset
= ss
->offsets
[i
];
1750 #endif /* JS_HAS_DESTRUCTURING */
1753 InitSprintStack(JSContext
*cx
, SprintStack
*ss
, JSPrinter
*jp
, uintN depth
)
1755 size_t offsetsz
, opcodesz
;
1758 INIT_SPRINTER(cx
, &ss
->sprinter
, &cx
->tempPool
, PAREN_SLOP
);
1760 /* Allocate the parallel (to avoid padding) offset and opcode stacks. */
1761 offsetsz
= depth
* sizeof(ptrdiff_t);
1762 opcodesz
= depth
* sizeof(jsbytecode
);
1763 cx
->tempPool
.allocate(space
, offsetsz
+ opcodesz
);
1765 js_ReportOutOfScriptQuota(cx
);
1768 ss
->offsets
= (ptrdiff_t *) space
;
1769 ss
->opcodes
= (jsbytecode
*) ((char *)space
+ offsetsz
);
1771 ss
->top
= ss
->inArrayInit
= 0;
1772 ss
->inGenExp
= JS_FALSE
;
1778 * If nb is non-negative, decompile nb bytecodes starting at pc. Otherwise
1779 * the decompiler starts at pc and continues until it reaches an opcode for
1780 * which decompiling would result in the stack depth equaling -(nb + 1).
1782 * The nextop parameter is either JSOP_NOP or the "next" opcode in order of
1783 * abstract interpretation (not necessarily physically next in a bytecode
1784 * vector). So nextop is JSOP_POP for the last operand in a comma expression,
1785 * or JSOP_AND for the right operand of &&.
1788 Decompile(SprintStack
*ss
, jsbytecode
*pc
, intN nb
, JSOp nextop
)
1791 JSPrinter
*jp
, *jp2
;
1792 jsbytecode
*startpc
, *endpc
, *pc2
, *done
;
1793 ptrdiff_t tail
, todo
, len
, oplen
, cond
, next
;
1794 JSOp op
, lastop
, saveop
;
1795 const JSCodeSpec
*cs
;
1796 jssrcnote
*sn
, *sn2
;
1797 const char *lval
, *rval
, *xval
, *fmt
, *token
;
1806 #if JS_HAS_XML_SUPPORT
1807 JSBool foreach
, inXML
, quoteAttr
;
1809 #define inXML JS_FALSE
1813 static const char exception_cookie
[] = "/*EXCEPTION*/";
1814 static const char retsub_pc_cookie
[] = "/*RETSUB_PC*/";
1815 static const char forelem_cookie
[] = "/*FORELEM*/";
1816 static const char with_cookie
[] = "/*WITH*/";
1817 static const char dot_format
[] = "%s.%s";
1818 static const char index_format
[] = "%s[%s]";
1819 static const char predot_format
[] = "%s%s.%s";
1820 static const char postdot_format
[] = "%s.%s%s";
1821 static const char preindex_format
[] = "%s%s[%s]";
1822 static const char postindex_format
[] = "%s[%s]%s";
1823 static const char ss_format
[] = "%s%s";
1824 static const char sss_format
[] = "%s%s%s";
1826 /* Argument and variables decompilation uses the following to share code. */
1827 JS_STATIC_ASSERT(ARGNO_LEN
== SLOTNO_LEN
);
1832 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
1833 #define DECOMPILE_CODE(pc,nb) if (!Decompile(ss, pc, nb, JSOP_NOP)) return NULL
1834 #define NEXT_OP(pc) (((pc) + (len) == endpc) ? nextop : pc[len])
1835 #define TOP_STR() GetStr(ss, ss->top - 1)
1836 #define POP_STR() PopStr(ss, op)
1837 #define POP_STR_PREC(prec) PopStrPrec(ss, prec)
1840 * Pop a condition expression for if/while. JSOP_IFEQ's precedence forces
1841 * extra parens around assignment, which avoids a strict-mode warning.
1843 #define POP_COND_STR() \
1844 PopStr(ss, (js_CodeSpec[ss->opcodes[ss->top - 1]].format & JOF_SET) \
1849 * Callers know that ATOM_IS_STRING(atom), and we leave it to the optimizer to
1850 * common ATOM_TO_STRING(atom) here and near the call sites.
1852 #define ATOM_IS_IDENTIFIER(atom) js_IsIdentifier(ATOM_TO_STRING(atom))
1853 #define ATOM_IS_KEYWORD(atom) \
1854 (js_CheckKeyword(ATOM_TO_STRING(atom)->chars(), \
1855 ATOM_TO_STRING(atom)->length()) != TOK_EOF)
1858 * Given an atom already fetched from jp->script's atom map, quote/escape its
1859 * string appropriately into rval, and select fmt from the quoted and unquoted
1862 #define GET_QUOTE_AND_FMT(qfmt, ufmt, rval) \
1865 if (!ATOM_IS_IDENTIFIER(atom)) { \
1872 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), quote_); \
1877 #define LOAD_OBJECT(PCOFF) \
1878 GET_OBJECT_FROM_BYTECODE(jp->script, pc, PCOFF, obj)
1880 #define LOAD_FUNCTION(PCOFF) \
1881 GET_FUNCTION_FROM_BYTECODE(jp->script, pc, PCOFF, fun)
1883 #define LOAD_REGEXP(PCOFF) \
1884 GET_REGEXP_FROM_BYTECODE(jp->script, pc, PCOFF, obj)
1886 #define GET_SOURCE_NOTE_ATOM(sn, atom) \
1888 jsatomid atomIndex_ = (jsatomid) js_GetSrcNoteOffset((sn), 0); \
1890 LOCAL_ASSERT(atomIndex_ < jp->script->atomMap.length); \
1891 (atom) = jp->script->atomMap.vector[atomIndex_]; \
1895 * Get atom from jp->script's atom map, quote/escape its string appropriately
1896 * into rval, and select fmt from the quoted and unquoted alternatives.
1898 #define GET_ATOM_QUOTE_AND_FMT(qfmt, ufmt, rval) \
1901 GET_QUOTE_AND_FMT(qfmt, ufmt, rval); \
1905 * Per spec, new x(y).z means (new x(y))).z. For example new (x(y).z) must
1906 * decompile with the constructor parenthesized, but new x.z should not. The
1907 * normal rules give x(y).z and x.z identical precedence: both are produced by
1910 * Therefore, we need to know in case JSOP_NEW whether the constructor
1911 * expression contains any unparenthesized function calls. So when building a
1912 * MemberExpression or CallExpression, we set ss->opcodes[n] to JSOP_CALL if
1913 * this is true. x(y).z gets JSOP_CALL, not JSOP_GETPROP.
1915 #define PROPAGATE_CALLNESS() \
1917 if (ss->opcodes[ss->top - 1] == JSOP_CALL || \
1918 ss->opcodes[ss->top - 1] == JSOP_EVAL || \
1919 ss->opcodes[ss->top - 1] == JSOP_APPLY) { \
1920 saveop = JSOP_CALL; \
1924 cx
= ss
->sprinter
.context
;
1925 JS_CHECK_RECURSION(cx
, return NULL
);
1929 endpc
= (nb
< 0) ? jp
->script
->code
+ jp
->script
->length
: pc
+ nb
;
1931 todo
= -2; /* NB: different from Sprint() error return. */
1935 #if JS_HAS_XML_SUPPORT
1936 foreach
= inXML
= quoteAttr
= JS_FALSE
;
1939 while (nb
< 0 || pc
< endpc
) {
1941 * Move saveop to lastop so prefixed bytecodes can take special action
1942 * while sharing maximal code. Set op and saveop to the new bytecode,
1943 * use op in POP_STR to trigger automatic parenthesization, but push
1944 * saveop at the bottom of the loop if this op pushes. Thus op may be
1945 * set to nop or otherwise mutated to suppress auto-parens.
1949 cs
= &js_CodeSpec
[op
];
1950 if (cs
->format
& JOF_INDEXBASE
) {
1952 * The decompiler uses js_GetIndexFromBytecode to get atoms and
1953 * objects and ignores these suffix/prefix bytecodes, thus
1954 * simplifying code that must process JSOP_GETTER/JSOP_SETTER
1961 cs
= &js_CodeSpec
[op
];
1964 len
= oplen
= cs
->length
;
1965 nuses
= js_GetStackUses(cs
, op
, pc
);
1968 * Here it is possible that nuses > ss->top when the op has a hidden
1969 * source note. But when nb < 0 we assume that the caller knows that
1970 * Decompile would never meet such opcodes.
1973 LOCAL_ASSERT(ss
->top
>= nuses
);
1974 uintN ndefs
= js_GetStackDefs(cx
, cs
, op
, jp
->script
, pc
);
1975 if ((uintN
) -(nb
+ 1) == ss
->top
- nuses
+ ndefs
)
1980 * Save source literal associated with JS now before the following
1981 * rewrite changes op. See bug 380197.
1983 token
= CodeToken
[op
];
1985 if (pc
+ oplen
== jp
->dvgfence
) {
1987 uint32 format
, mode
, type
;
1990 * Rewrite non-get ops to their "get" format if the error is in
1991 * the bytecode at pc, so we don't decompile more than the error
1994 fp
= js_GetScriptedCaller(cx
, NULL
);
1995 format
= cs
->format
;
1996 if (((fp
&& fp
->regs
&& pc
== fp
->regs
->pc
) ||
1997 (pc
== startpc
&& nuses
!= 0)) &&
1998 format
& (JOF_SET
|JOF_DEL
|JOF_INCDEC
|JOF_FOR
|JOF_VARPROP
)) {
1999 mode
= JOF_MODE(format
);
2000 if (mode
== JOF_NAME
) {
2002 * JOF_NAME does not imply JOF_ATOM, so we must check for
2003 * the QARG and QVAR format types, and translate those to
2004 * JSOP_GETARG or JSOP_GETLOCAL appropriately, instead of
2007 type
= JOF_TYPE(format
);
2008 op
= (type
== JOF_QARG
)
2010 : (type
== JOF_LOCAL
)
2014 JS_ASSERT(js_CodeSpec
[op
].nuses
>= 0);
2015 i
= nuses
- js_CodeSpec
[op
].nuses
;
2017 PopOff(ss
, JSOP_NOP
);
2020 * We must replace the faulting pc's bytecode with a
2021 * corresponding JSOP_GET* code. For JSOP_SET{PROP,ELEM},
2022 * we must use the "2nd" form of JSOP_GET{PROP,ELEM}, to
2023 * throw away the assignment op's right-hand operand and
2024 * decompile it as if it were a GET of its left-hand
2027 if (mode
== JOF_PROP
) {
2028 op
= (JSOp
) ((format
& JOF_SET
)
2031 } else if (mode
== JOF_ELEM
) {
2032 op
= (JSOp
) ((format
& JOF_SET
)
2037 * Unknown mode (including mode 0) means that op is
2038 * uncategorized for our purposes, so we must write
2039 * per-op special case code here.
2043 case JSOP_ENUMCONSTELEM
:
2049 case JSOP_GETTHISPROP
:
2051 * NB: JSOP_GETTHISPROP can't fail due to |this|
2052 * being null or undefined at runtime (beware that
2053 * this may change for ES4). Therefore any error
2054 * resulting from this op must be due to the value
2055 * of the property accessed via |this|, so do not
2056 * rewrite op to JSOP_THIS.
2058 * The next two cases should not change op if
2059 * js_DecompileValueGenerator was called from the
2060 * the property getter. They should rewrite only
2061 * if the base object in the arg/var/local is null
2062 * or undefined. FIXME: bug 431569.
2065 case JSOP_GETARGPROP
:
2068 case JSOP_GETLOCALPROP
:
2079 if (op
>= JSOP_LIMIT
) {
2082 saveop
= JSOP_GETPROP
;
2085 saveop
= JSOP_GETELEM
;
2090 LOCAL_ASSERT(js_CodeSpec
[saveop
].length
== oplen
||
2091 JOF_TYPE(format
) == JOF_SLOTATOM
);
2093 jp
->dvgfence
= NULL
;
2099 sn
= js_GetSrcNote(jp
->script
, pc
);
2100 if (sn
&& SN_TYPE(sn
) == SRC_ASSIGNOP
) {
2102 * Avoid over-parenthesizing y in x op= y based on its
2103 * expansion: x = x op y (replace y by z = w to see the
2106 op
= (JSOp
) pc
[oplen
];
2109 /* Print only the right operand of the assignment-op. */
2110 todo
= SprintCString(&ss
->sprinter
, rval
);
2112 } else if (!inXML
) {
2113 rval
= POP_STR_PREC(cs
->prec
+ !!(cs
->format
& JOF_LEFTASSOC
));
2114 lval
= POP_STR_PREC(cs
->prec
+ !(cs
->format
& JOF_LEFTASSOC
));
2115 todo
= Sprint(&ss
->sprinter
, "%s %s %s",
2118 /* In XML, just concatenate the two operands. */
2119 LOCAL_ASSERT(op
== JSOP_ADD
);
2122 todo
= Sprint(&ss
->sprinter
, ss_format
, lval
, rval
);
2128 todo
= Sprint(&ss
->sprinter
, ss_format
, token
, rval
);
2132 todo
= SprintCString(&ss
->sprinter
, token
);
2143 * Check for a do-while loop, a for-loop with an empty
2144 * initializer part, a labeled statement, a function
2145 * definition, or try/finally.
2147 sn
= js_GetSrcNote(jp
->script
, pc
);
2149 switch (sn
? SN_TYPE(sn
) : SRC_NULL
) {
2152 tail
= js_GetSrcNoteOffset(sn
, 0) - 1;
2153 LOCAL_ASSERT(pc
[tail
] == JSOP_IFNE
||
2154 pc
[tail
] == JSOP_IFNEX
);
2155 js_printf(jp
, "\tdo {\n");
2157 DECOMPILE_CODE(pc
, tail
);
2159 js_printf(jp
, "\t} while (%s);\n", POP_COND_STR());
2161 len
= js_CodeSpec
[*pc
].length
;
2169 JS_ASSERT(SN_TYPE(sn
) == SRC_FOR
);
2171 /* Skip the JSOP_NOP or JSOP_POP bytecode. */
2172 pc
+= JSOP_NOP_LENGTH
;
2174 /* Get the cond, next, and loop-closing tail offsets. */
2175 cond
= js_GetSrcNoteOffset(sn
, 0);
2176 next
= js_GetSrcNoteOffset(sn
, 1);
2177 tail
= js_GetSrcNoteOffset(sn
, 2);
2180 * If this loop has a condition, then pc points at a goto
2181 * targeting the condition.
2185 LOCAL_ASSERT(*pc
== JSOP_GOTO
|| *pc
== JSOP_GOTOX
);
2186 pc2
+= (*pc
== JSOP_GOTO
) ? JSOP_GOTO_LENGTH
: JSOP_GOTOX_LENGTH
;
2188 LOCAL_ASSERT(tail
+ GetJumpOffset(pc
+tail
, pc
+tail
) == pc2
- pc
);
2190 /* Print the keyword and the possibly empty init-part. */
2191 js_printf(jp
, "\tfor (%s;", rval
);
2194 /* Decompile the loop condition. */
2195 DECOMPILE_CODE(pc
+ cond
, tail
- cond
);
2196 js_printf(jp
, " %s", POP_STR());
2199 /* Need a semicolon whether or not there was a cond. */
2204 * Decompile the loop updater. It may end in a JSOP_POP
2205 * that we skip; or in a JSOP_POPN that we do not skip,
2206 * followed by a JSOP_NOP (skipped as if it's a POP).
2207 * We cope with the difference between these two cases
2208 * by checking for stack imbalance and popping if there
2211 uintN saveTop
= ss
->top
;
2213 DECOMPILE_CODE(pc
+ next
, cond
- next
- JSOP_POP_LENGTH
);
2214 LOCAL_ASSERT(ss
->top
- saveTop
<= 1U);
2215 rval
= (ss
->top
== saveTop
)
2216 ? ss
->sprinter
.base
+ ss
->sprinter
.offset
2218 js_printf(jp
, " %s", rval
);
2221 /* Do the loop body. */
2222 js_printf(jp
, ") {\n");
2225 DECOMPILE_CODE(pc2
, next
);
2227 js_printf(jp
, "\t}\n");
2229 /* Set len so pc skips over the entire loop. */
2230 len
= tail
+ js_CodeSpec
[pc
[tail
]].length
;
2234 GET_SOURCE_NOTE_ATOM(sn
, atom
);
2236 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
2239 RETRACT(&ss
->sprinter
, rval
);
2240 js_printf(jp
, "\t%s:\n", rval
);
2244 case SRC_LABELBRACE
:
2245 GET_SOURCE_NOTE_ATOM(sn
, atom
);
2246 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
2249 RETRACT(&ss
->sprinter
, rval
);
2250 js_printf(jp
, "\t%s: {\n", rval
);
2256 js_printf(jp
, "\t}\n");
2260 fun
= jp
->script
->getFunction(js_GetSrcNoteOffset(sn
, 0));
2263 jp2
= js_NewPrinter(cx
, "nested_function", fun
,
2264 jp
->indent
, jp
->pretty
, jp
->grouped
,
2268 ok
= js_DecompileFunction(jp2
);
2269 if (ok
&& jp2
->sprinter
.base
)
2270 js_puts(jp
, jp2
->sprinter
.base
);
2271 js_DestroyPrinter(jp2
);
2274 js_puts(jp
, "\n\n");
2278 js_printf(jp
, "\t{\n");
2280 len
= js_GetSrcNoteOffset(sn
, 0);
2281 DECOMPILE_CODE(pc
+ oplen
, len
- oplen
);
2283 js_printf(jp
, "\t}\n");
2291 #if JS_HAS_DESTRUCTURING
2292 sn
= js_GetSrcNote(jp
->script
, pc
);
2293 if (sn
&& SN_TYPE(sn
) == SRC_GROUPASSIGN
) {
2294 pc
= DecompileGroupAssignment(ss
, pc
, endpc
, sn
, &todo
);
2297 LOCAL_ASSERT(*pc
== JSOP_POPN
);
2298 len
= oplen
= JSOP_POPN_LENGTH
;
2299 goto end_groupassignment
;
2305 todo
= Sprint(&ss
->sprinter
, "");
2309 js_printf(jp
, "\ttry {\n");
2316 js_printf(jp
, "\t} finally {\n");
2320 * We push push the pair of exception/restsub cookies to
2321 * simulate the effects [gosub] or control transfer during
2322 * exception capturing on the stack.
2324 todo
= Sprint(&ss
->sprinter
, exception_cookie
);
2325 if (todo
< 0 || !PushOff(ss
, todo
, op
))
2327 todo
= Sprint(&ss
->sprinter
, retsub_pc_cookie
);
2332 LOCAL_ASSERT(strcmp(rval
, retsub_pc_cookie
) == 0);
2334 LOCAL_ASSERT(strcmp(lval
, exception_cookie
) == 0);
2341 * JSOP_GOSUB and GOSUBX have no effect on the decompiler's
2342 * string stack because the next op in bytecode order finds
2343 * the stack balanced by a JSOP_RETSUB executed elsewhere.
2350 uintN newtop
, oldtop
;
2353 * The compiler models operand stack depth and fixes the stack
2354 * pointer on entry to a catch clause based on its depth model.
2355 * The decompiler must match the code generator's model, which
2356 * is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops.
2359 newtop
= oldtop
- GET_UINT16(pc
);
2360 LOCAL_ASSERT(newtop
<= oldtop
);
2363 sn
= js_GetSrcNote(jp
->script
, pc
);
2364 if (sn
&& SN_TYPE(sn
) == SRC_HIDDEN
)
2366 #if JS_HAS_DESTRUCTURING
2367 if (sn
&& SN_TYPE(sn
) == SRC_GROUPASSIGN
) {
2368 todo
= Sprint(&ss
->sprinter
, "%s[] = [",
2372 for (uintN i
= newtop
; i
< oldtop
; i
++) {
2373 rval
= OFF2STR(&ss
->sprinter
, ss
->offsets
[i
]);
2374 if (Sprint(&ss
->sprinter
, ss_format
,
2375 (i
== newtop
) ? "" : ", ",
2376 (i
== oldtop
- 1 && *rval
== '\0')
2377 ? ", " : rval
) < 0) {
2381 if (SprintPut(&ss
->sprinter
, "]", 1) < 0)
2385 * If this is an empty group assignment, we have no stack
2386 * budget into which we can push our result string. Adjust
2387 * ss->sprinter.offset so that our consumer can find the
2388 * empty group assignment decompilation.
2390 if (newtop
== oldtop
) {
2391 ss
->sprinter
.offset
= todo
;
2394 * Kill newtop before the end_groupassignment: label by
2395 * retracting/popping early. Control will either jump
2396 * to do_forloop: or do_letheadbody: or else break from
2397 * our case JSOP_POPN: after the switch (*pc2) below.
2399 LOCAL_ASSERT(newtop
< oldtop
);
2400 ss
->sprinter
.offset
= GetOff(ss
, newtop
);
2404 end_groupassignment
:
2405 LOCAL_ASSERT(*pc
== JSOP_POPN
);
2408 * Thread directly to the next opcode if we can, to handle
2409 * the special cases of a group assignment in the first or
2410 * last part of a for(;;) loop head, or in a let block or
2413 * NB: todo at this point indexes space in ss->sprinter
2414 * that is liable to be overwritten. The code below knows
2415 * exactly how long rval lives, or else copies it down via
2418 rval
= OFF2STR(&ss
->sprinter
, todo
);
2421 if (*pc2
== JSOP_NOP
) {
2422 sn
= js_GetSrcNote(jp
->script
, pc2
);
2424 if (SN_TYPE(sn
) == SRC_FOR
) {
2430 if (SN_TYPE(sn
) == SRC_DECL
) {
2431 if (ss
->top
== StackDepth(jp
->script
)) {
2433 * This must be an empty destructuring
2434 * in the head of a let whose body block
2437 pc
= pc2
+ JSOP_NOP_LENGTH
;
2438 len
= js_GetSrcNoteOffset(sn
, 0);
2439 LOCAL_ASSERT(pc
[len
] == JSOP_LEAVEBLOCK
);
2440 js_printf(jp
, "\tlet (%s) {\n", rval
);
2441 js_printf(jp
, "\t}\n");
2444 todo
= SprintCString(&ss
->sprinter
, rval
);
2445 if (todo
< 0 || !PushOff(ss
, todo
, JSOP_NOP
))
2448 pc
= pc2
+ JSOP_NOP_LENGTH
;
2449 goto do_letheadbody
;
2453 * An unnannotated NOP following a POPN must be the
2454 * third part of for(;;) loop head. If the POPN's
2455 * immediate operand is 0, then we may have no slot
2456 * on the sprint-stack in which to push our result
2457 * string. In this case the result can be recovered
2458 * at ss->sprinter.base + ss->sprinter.offset.
2460 if (GET_UINT16(pc
) == 0)
2462 todo
= SprintCString(&ss
->sprinter
, rval
);
2468 * If control flow reaches this point with todo still -2,
2469 * just print rval as an expression statement.
2472 js_printf(jp
, "\t%s;\n", rval
);
2476 if (newtop
< oldtop
) {
2477 ss
->sprinter
.offset
= GetOff(ss
, newtop
);
2483 case JSOP_EXCEPTION
:
2484 /* The catch decompiler handles this op itself. */
2485 LOCAL_ASSERT(JS_FALSE
);
2490 * By default, do not automatically parenthesize when popping
2491 * a stacked expression decompilation. We auto-parenthesize
2492 * only when JSOP_POP is annotated with SRC_PCDELTA, meaning
2499 sn
= js_GetSrcNote(jp
->script
, pc
);
2500 switch (sn
? SN_TYPE(sn
) : SRC_NULL
) {
2502 /* Force parens around 'in' expression at 'for' front. */
2503 if (ss
->opcodes
[ss
->top
-1] == JSOP_IN
)
2510 /* Comma operator: use JSOP_POP for correct precedence. */
2513 /* Pop and save to avoid blowing stack depth budget. */
2514 lval
= JS_strdup(cx
, POP_STR());
2519 * The offset tells distance to the end of the right-hand
2520 * operand of the comma operator.
2523 pc
+= js_GetSrcNoteOffset(sn
, 0);
2526 if (!Decompile(ss
, done
, pc
- done
, JSOP_POP
)) {
2527 cx
->free((char *)lval
);
2531 /* Pop Decompile result and print comma expression. */
2533 todo
= Sprint(&ss
->sprinter
, "%s, %s", lval
, rval
);
2534 cx
->free((char *)lval
);
2538 /* Hide this pop, it's from a goto in a with or for/in. */
2543 /* This pop is at the end of the let block/expr head. */
2544 pc
+= JSOP_POP_LENGTH
;
2545 #if JS_HAS_DESTRUCTURING
2548 len
= js_GetSrcNoteOffset(sn
, 0);
2549 if (pc
[len
] == JSOP_LEAVEBLOCK
) {
2550 js_printf(jp
, "\tlet (%s) {\n", POP_STR());
2552 DECOMPILE_CODE(pc
, len
);
2554 js_printf(jp
, "\t}\n");
2557 LOCAL_ASSERT(pc
[len
] == JSOP_LEAVEBLOCKEXPR
);
2559 lval
= JS_strdup(cx
, PopStr(ss
, JSOP_NOP
));
2563 /* Set saveop to reflect what we will push. */
2564 saveop
= JSOP_LEAVEBLOCKEXPR
;
2565 if (!Decompile(ss
, pc
, len
, saveop
)) {
2566 cx
->free((char *)lval
);
2569 rval
= PopStr(ss
, JSOP_SETNAME
);
2570 todo
= Sprint(&ss
->sprinter
,
2575 cx
->free((char *)lval
);
2580 /* Turn off parens around a yield statement. */
2581 if (ss
->opcodes
[ss
->top
-1] == JSOP_YIELD
)
2587 * Don't emit decompiler-pushed strings that are not
2588 * handled by other opcodes. They are pushed onto the
2589 * stack to help model the interpreter stack and should
2590 * not appear in the decompiler's output.
2592 if (*rval
!= '\0' && (rval
[0] != '/' || rval
[1] != '*')) {
2595 (strncmp(rval
, js_function_str
, 8) == 0 &&
2601 LOCAL_ASSERT(*rval
== '\0' ||
2602 strcmp(rval
, exception_cookie
) == 0);
2610 case JSOP_ENTERWITH
:
2611 LOCAL_ASSERT(!js_GetSrcNote(jp
->script
, pc
));
2613 js_printf(jp
, "\twith (%s) {\n", rval
);
2615 todo
= Sprint(&ss
->sprinter
, with_cookie
);
2618 case JSOP_LEAVEWITH
:
2619 sn
= js_GetSrcNote(jp
->script
, pc
);
2621 if (sn
&& SN_TYPE(sn
) == SRC_HIDDEN
)
2624 LOCAL_ASSERT(strcmp(rval
, with_cookie
) == 0);
2626 js_printf(jp
, "\t}\n");
2629 case JSOP_ENTERBLOCK
:
2631 JSAtom
**atomv
, *smallv
[5];
2632 JSScopeProperty
*sprop
;
2635 argc
= OBJ_BLOCK_COUNT(cx
, obj
);
2636 if ((size_t)argc
<= JS_ARRAY_LENGTH(smallv
)) {
2639 atomv
= (JSAtom
**) cx
->malloc(argc
* sizeof(JSAtom
*));
2644 MUST_FLOW_THROUGH("enterblock_out");
2645 #define LOCAL_ASSERT_OUT(expr) LOCAL_ASSERT_CUSTOM(expr, ok = JS_FALSE; \
2646 goto enterblock_out)
2647 for (sprop
= obj
->scope()->lastProperty(); sprop
;
2648 sprop
= sprop
->parent
) {
2649 if (!sprop
->hasShortID())
2651 LOCAL_ASSERT_OUT(sprop
->shortid
< argc
);
2652 atomv
[sprop
->shortid
] = JSID_TO_ATOM(sprop
->id
);
2655 for (i
= 0; i
< argc
; i
++) {
2657 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
2659 !PushOff(ss
, STR2OFF(&ss
->sprinter
, rval
), op
)) {
2661 goto enterblock_out
;
2665 sn
= js_GetSrcNote(jp
->script
, pc
);
2666 switch (sn
? SN_TYPE(sn
) : SRC_NULL
) {
2667 #if JS_HAS_BLOCK_SCOPE
2669 js_printf(jp
, "\t{\n");
2671 len
= js_GetSrcNoteOffset(sn
, 0);
2672 ok
= Decompile(ss
, pc
+ oplen
, len
- oplen
, JSOP_NOP
)
2675 goto enterblock_out
;
2677 js_printf(jp
, "\t}\n");
2683 js_printf(jp
, "\t} catch (");
2687 LOCAL_ASSERT_OUT(*pc
== JSOP_EXCEPTION
);
2688 pc
+= JSOP_EXCEPTION_LENGTH
;
2689 todo
= Sprint(&ss
->sprinter
, exception_cookie
);
2690 if (todo
< 0 || !PushOff(ss
, todo
, JSOP_EXCEPTION
)) {
2692 goto enterblock_out
;
2695 if (*pc
== JSOP_DUP
) {
2696 sn2
= js_GetSrcNote(jp
->script
, pc
);
2697 if (!sn2
|| SN_TYPE(sn2
) != SRC_DESTRUCT
) {
2699 * This is a dup to save the exception for later.
2700 * It is emitted only when the catch head contains
2701 * an exception guard.
2703 LOCAL_ASSERT_OUT(js_GetSrcNoteOffset(sn
, 0) != 0);
2704 pc
+= JSOP_DUP_LENGTH
;
2705 todo
= Sprint(&ss
->sprinter
, exception_cookie
);
2707 !PushOff(ss
, todo
, JSOP_EXCEPTION
)) {
2709 goto enterblock_out
;
2714 #if JS_HAS_DESTRUCTURING
2715 if (*pc
== JSOP_DUP
) {
2716 pc
= DecompileDestructuring(ss
, pc
, endpc
);
2719 goto enterblock_out
;
2721 LOCAL_ASSERT_OUT(*pc
== JSOP_POP
);
2722 pc
+= JSOP_POP_LENGTH
;
2723 lval
= PopStr(ss
, JSOP_NOP
);
2727 LOCAL_ASSERT_OUT(*pc
== JSOP_SETLOCALPOP
);
2728 i
= GET_SLOTNO(pc
) - jp
->script
->nfixed
;
2729 pc
+= JSOP_SETLOCALPOP_LENGTH
;
2730 atom
= atomv
[i
- OBJ_BLOCK_DEPTH(cx
, obj
)];
2731 str
= ATOM_TO_STRING(atom
);
2732 if (!QuoteString(&jp
->sprinter
, str
, 0)) {
2734 goto enterblock_out
;
2736 #if JS_HAS_DESTRUCTURING
2741 * Pop the exception_cookie (or its dup in the case of a
2742 * guarded catch head) off the stack now.
2744 rval
= PopStr(ss
, JSOP_NOP
);
2745 LOCAL_ASSERT_OUT(strcmp(rval
, exception_cookie
) == 0);
2747 len
= js_GetSrcNoteOffset(sn
, 0);
2750 LOCAL_ASSERT_OUT(len
> 0);
2751 js_printf(jp
, " if ");
2752 ok
= Decompile(ss
, pc
, len
, JSOP_NOP
) != NULL
;
2754 goto enterblock_out
;
2755 js_printf(jp
, "%s", POP_STR());
2757 LOCAL_ASSERT_OUT(*pc
== JSOP_IFEQ
|| *pc
== JSOP_IFEQX
);
2758 pc
+= js_CodeSpec
[*pc
].length
;
2761 js_printf(jp
, ") {\n");
2771 #undef LOCAL_ASSERT_OUT
2773 if (atomv
!= smallv
)
2780 case JSOP_LEAVEBLOCK
:
2781 case JSOP_LEAVEBLOCKEXPR
:
2785 sn
= js_GetSrcNote(jp
->script
, pc
);
2787 if (op
== JSOP_LEAVEBLOCKEXPR
) {
2788 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_PCBASE
);
2791 LOCAL_ASSERT(op
== JSOP_LEAVEBLOCK
);
2792 if (SN_TYPE(sn
) == SRC_HIDDEN
)
2796 * This JSOP_LEAVEBLOCK must be for a catch block. If sn's
2797 * offset does not equal the model stack depth, there must
2798 * be a copy of the exception value on the stack due to a
2799 * catch guard (see above, the JSOP_ENTERBLOCK + SRC_CATCH
2802 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_CATCH
);
2803 if ((uintN
)js_GetSrcNoteOffset(sn
, 0) != ss
->top
) {
2804 LOCAL_ASSERT((uintN
)js_GetSrcNoteOffset(sn
, 0)
2807 LOCAL_ASSERT(strcmp(rval
, exception_cookie
) == 0);
2811 depth
= GET_UINT16(pc
);
2812 LOCAL_ASSERT(top
>= depth
);
2815 ss
->sprinter
.offset
= GetOff(ss
, top
);
2816 if (op
== JSOP_LEAVEBLOCKEXPR
)
2817 todo
= SprintCString(&ss
->sprinter
, rval
);
2822 case JSOP_CALLUPVAR
:
2823 case JSOP_GETUPVAR_DBG
:
2824 case JSOP_CALLUPVAR_DBG
:
2826 case JSOP_CALLDSLOT
:
2829 JS_ASSERT(jp
->script
->savedCallerFun
);
2830 jp
->fun
= jp
->script
->getFunction(0);
2833 if (!jp
->localNames
)
2834 jp
->localNames
= js_GetLocalNameArray(cx
, jp
->fun
, &jp
->pool
);
2836 uintN index
= GET_UINT16(pc
);
2837 if (index
< jp
->fun
->u
.i
.nupvars
) {
2838 index
+= jp
->fun
->countArgsAndVars();
2843 * We must be in an eval called from jp->fun, where
2844 * jp->script is the eval-compiled script.
2846 * However, it's possible that a js_Invoke already
2847 * pushed a frame trying to call js_Construct on an
2848 * object that's not a constructor, causing us to be
2849 * called with an intervening frame on the stack.
2851 JSStackFrame
*fp
= js_GetTopStackFrame(cx
);
2853 while (!(fp
->flags
& JSFRAME_EVAL
))
2855 JS_ASSERT(fp
->script
== jp
->script
);
2856 JS_ASSERT(fp
->down
->fun
== jp
->fun
);
2857 JS_ASSERT(FUN_INTERPRETED(jp
->fun
));
2858 JS_ASSERT(jp
->script
!= jp
->fun
->u
.i
.script
);
2859 JS_ASSERT(jp
->script
->upvarsOffset
!= 0);
2862 uva
= jp
->script
->upvars();
2863 index
= UPVAR_FRAME_SLOT(uva
->vector
[index
]);
2865 atom
= GetArgOrVarAtom(jp
, index
);
2869 case JSOP_CALLLOCAL
:
2871 if (IsVarSlot(jp
, pc
, &i
)) {
2872 atom
= GetArgOrVarAtom(jp
, i
);
2876 LOCAL_ASSERT((uintN
)i
< ss
->top
);
2877 sn
= js_GetSrcNote(jp
->script
, pc
);
2879 #if JS_HAS_DESTRUCTURING
2880 if (sn
&& SN_TYPE(sn
) == SRC_GROUPASSIGN
) {
2882 * Distinguish a js_DecompileValueGenerator call that
2883 * targets op alone, from decompilation of a full group
2884 * assignment sequence, triggered by SRC_GROUPASSIGN
2885 * annotating the first JSOP_GETLOCAL in the sequence.
2887 if (endpc
- pc
> JSOP_GETLOCAL_LENGTH
|| pc
> startpc
) {
2888 pc
= DecompileGroupAssignment(ss
, pc
, endpc
, sn
, &todo
);
2891 LOCAL_ASSERT(*pc
== JSOP_POPN
);
2892 len
= oplen
= JSOP_POPN_LENGTH
;
2893 goto end_groupassignment
;
2896 /* Null sn to prevent bogus VarPrefix'ing below. */
2901 rval
= GetLocal(ss
, i
);
2902 todo
= Sprint(&ss
->sprinter
, ss_format
, VarPrefix(sn
), rval
);
2906 case JSOP_SETLOCALPOP
:
2907 if (IsVarSlot(jp
, pc
, &i
)) {
2908 atom
= GetArgOrVarAtom(jp
, i
);
2912 lval
= GetLocal(ss
, i
);
2918 if (IsVarSlot(jp
, pc
, &i
)) {
2919 atom
= GetArgOrVarAtom(jp
, i
);
2923 lval
= GetLocal(ss
, i
);
2928 if (IsVarSlot(jp
, pc
, &i
)) {
2929 atom
= GetArgOrVarAtom(jp
, i
);
2933 lval
= GetLocal(ss
, i
);
2941 LOCAL_ASSERT(jp
->fun
);
2943 if (fun
->flags
& JSFUN_EXPR_CLOSURE
) {
2944 /* Turn on parens around comma-expression here. */
2947 js_printf(jp
, (*rval
== '{') ? "(%s)%s" : ss_format
,
2949 ((fun
->flags
& JSFUN_LAMBDA
) || !fun
->atom
)
2960 js_printf(jp
, "\t%s %s;\n", js_return_str
, rval
);
2962 js_printf(jp
, "\t%s;\n", js_return_str
);
2966 #if JS_HAS_GENERATORS
2968 #if JS_HAS_GENERATOR_EXPRS
2969 if (!ss
->inGenExp
|| !(sn
= js_GetSrcNote(jp
->script
, pc
)))
2972 /* Turn off most parens. */
2975 todo
= (*rval
!= '\0')
2976 ? Sprint(&ss
->sprinter
,
2977 (strncmp(rval
, js_yield_str
, 5) == 0 &&
2978 (rval
[5] == ' ' || rval
[5] == '\0'))
2982 : SprintCString(&ss
->sprinter
, js_yield_str
);
2986 #if JS_HAS_GENERATOR_EXPRS
2987 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_HIDDEN
);
2991 case JSOP_ARRAYPUSH
:
2996 /* Turn off most parens. */
2999 /* Pop the expression being pushed or yielded. */
3003 * Skip the for loop head stacked by JSOP_FORLOCAL until we hit
3004 * a block local slot (note empty destructuring patterns result
3005 * in unit-count blocks).
3009 op
= (JSOp
) ss
->opcodes
[--pos
];
3010 if (op
== JSOP_ENTERBLOCK
)
3013 JS_ASSERT(op
== JSOP_ENTERBLOCK
);
3016 * Here, forpos must index the space before the left-most |for|
3017 * in the single string of accumulated |for| heads and optional
3018 * final |if (condition)|.
3021 LOCAL_ASSERT(forpos
< ss
->top
);
3024 * Now move pos downward over the block's local slots. Even an
3025 * empty destructuring pattern has one (dummy) local.
3027 while (ss
->opcodes
[pos
] == JSOP_ENTERBLOCK
) {
3033 #if JS_HAS_GENERATOR_EXPRS
3034 if (saveop
== JSOP_YIELD
) {
3036 * Generator expression: decompile just rval followed by
3037 * the string starting at forpos. Leave the result string
3038 * in ss->offsets[0] so it can be recovered by our caller
3039 * (the JSOP_ANONFUNOBJ with SRC_GENEXP case). Bump the
3040 * top of stack to balance yield, which is an expression
3041 * (so has neutral stack balance).
3043 LOCAL_ASSERT(pos
== 0);
3044 xval
= OFF2STR(&ss
->sprinter
, ss
->offsets
[forpos
]);
3045 ss
->sprinter
.offset
= PAREN_SLOP
;
3046 todo
= Sprint(&ss
->sprinter
, ss_format
, rval
, xval
);
3049 ss
->offsets
[0] = todo
;
3053 #endif /* JS_HAS_GENERATOR_EXPRS */
3056 * Array comprehension: retract the sprinter to the beginning
3057 * of the array initialiser and decompile "[<rval> for ...]".
3059 JS_ASSERT(jp
->script
->nfixed
+ pos
== GET_UINT16(pc
));
3060 LOCAL_ASSERT(ss
->opcodes
[pos
] == JSOP_NEWINIT
);
3062 start
= ss
->offsets
[pos
];
3063 LOCAL_ASSERT(ss
->sprinter
.base
[start
] == '[' ||
3064 ss
->sprinter
.base
[start
] == '#');
3065 LOCAL_ASSERT(forpos
< ss
->top
);
3066 xval
= OFF2STR(&ss
->sprinter
, ss
->offsets
[forpos
]);
3067 lval
= OFF2STR(&ss
->sprinter
, start
);
3068 RETRACT(&ss
->sprinter
, lval
);
3070 todo
= Sprint(&ss
->sprinter
, sss_format
, lval
, rval
, xval
);
3073 ss
->offsets
[pos
] = todo
;
3077 #endif /* JS_HAS_GENERATORS */
3084 sn
= js_GetSrcNote(jp
->script
, pc
);
3086 if (sn
&& SN_TYPE(sn
) == SRC_HIDDEN
)
3089 js_printf(jp
, "\t%s %s;\n", js_throw_str
, rval
);
3093 foreach
= (pc
[1] & (JSITER_FOREACH
| JSITER_KEYVALUE
)) ==
3099 JS_NOT_REACHED("JSOP_MOREITER");
3103 sn
= js_GetSrcNote(jp
->script
, pc
);
3105 if (sn
&& SN_TYPE(sn
) == SRC_HIDDEN
)
3107 (void) PopOff(ss
, op
);
3112 sn
= js_GetSrcNote(jp
->script
, pc
);
3113 switch (sn
? SN_TYPE(sn
) : SRC_NULL
) {
3116 * The loop back-edge carries +1 stack balance, for the
3117 * flag processed by JSOP_IFNE. We do not decompile the
3118 * JSOP_IFNE, and instead push the left-hand side of 'in'
3119 * after the loop edge in this stack slot (the JSOP_FOR*
3120 * opcodes' decompilers do this pushing).
3122 cond
= GetJumpOffset(pc
, pc
);
3123 next
= js_GetSrcNoteOffset(sn
, 0);
3124 tail
= js_GetSrcNoteOffset(sn
, 1);
3125 JS_ASSERT(pc
[cond
] == JSOP_MOREITER
);
3126 DECOMPILE_CODE(pc
+ oplen
, next
- oplen
);
3128 LOCAL_ASSERT(ss
->top
>= 1);
3130 if (ss
->inArrayInit
|| ss
->inGenExp
) {
3132 if (ss
->top
>= 1 && ss
->opcodes
[ss
->top
- 1] == JSOP_FORLOCAL
) {
3133 ss
->sprinter
.offset
= ss
->offsets
[ss
->top
] - PAREN_SLOP
;
3134 if (Sprint(&ss
->sprinter
, " %s (%s in %s)",
3135 foreach
? js_for_each_str
: js_for_str
,
3141 * Do not AddParentSlop here, as we will push the
3142 * top-most offset again, which will add paren slop
3143 * for us. We must push to balance the stack budget
3144 * when nesting for heads in a comprehension.
3146 todo
= ss
->offsets
[ss
->top
- 1];
3148 todo
= Sprint(&ss
->sprinter
, " %s (%s in %s)",
3149 foreach
? js_for_each_str
: js_for_str
,
3152 if (todo
< 0 || !PushOff(ss
, todo
, JSOP_FORLOCAL
))
3154 DECOMPILE_CODE(pc
+ next
, cond
- next
);
3157 * As above, rval or an extension of it must remain
3158 * stacked during loop body decompilation.
3160 rval
= GetStr(ss
, ss
->top
- 1);
3161 js_printf(jp
, "\t%s (%s in %s) {\n",
3162 foreach
? js_for_each_str
: js_for_str
,
3165 DECOMPILE_CODE(pc
+ next
, cond
- next
);
3167 js_printf(jp
, "\t}\n");
3171 LOCAL_ASSERT(*pc
== JSOP_IFNE
|| *pc
== JSOP_IFNEX
);
3172 len
= js_CodeSpec
[*pc
].length
;
3176 cond
= GetJumpOffset(pc
, pc
);
3177 tail
= js_GetSrcNoteOffset(sn
, 0);
3178 DECOMPILE_CODE(pc
+ cond
, tail
- cond
);
3179 js_printf(jp
, "\twhile (%s) {\n", POP_COND_STR());
3181 DECOMPILE_CODE(pc
+ oplen
, cond
- oplen
);
3183 js_printf(jp
, "\t}\n");
3185 LOCAL_ASSERT(*pc
== JSOP_IFNE
|| *pc
== JSOP_IFNEX
);
3186 len
= js_CodeSpec
[*pc
].length
;
3190 case SRC_CONT2LABEL
:
3191 GET_SOURCE_NOTE_ATOM(sn
, atom
);
3192 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3195 RETRACT(&ss
->sprinter
, rval
);
3196 js_printf(jp
, "\tcontinue %s;\n", rval
);
3200 js_printf(jp
, "\tcontinue;\n");
3203 case SRC_BREAK2LABEL
:
3204 GET_SOURCE_NOTE_ATOM(sn
, atom
);
3205 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3208 RETRACT(&ss
->sprinter
, rval
);
3209 js_printf(jp
, "\tbreak %s;\n", rval
);
3216 js_printf(jp
, "\tbreak;\n");
3225 JSBool elseif
= JS_FALSE
;
3228 len
= GetJumpOffset(pc
, pc
);
3229 sn
= js_GetSrcNote(jp
->script
, pc
);
3231 switch (sn
? SN_TYPE(sn
) : SRC_NULL
) {
3234 rval
= POP_COND_STR();
3235 if (ss
->inArrayInit
|| ss
->inGenExp
) {
3236 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_IF
);
3237 ss
->sprinter
.offset
-= PAREN_SLOP
;
3238 if (Sprint(&ss
->sprinter
, " if (%s)", rval
) < 0)
3243 elseif
? " if (%s) {\n" : "\tif (%s) {\n",
3248 if (SN_TYPE(sn
) == SRC_IF
) {
3249 DECOMPILE_CODE(pc
+ oplen
, len
- oplen
);
3251 LOCAL_ASSERT(!ss
->inArrayInit
&& !ss
->inGenExp
);
3252 tail
= js_GetSrcNoteOffset(sn
, 0);
3253 DECOMPILE_CODE(pc
+ oplen
, tail
- oplen
);
3256 LOCAL_ASSERT(*pc
== JSOP_GOTO
|| *pc
== JSOP_GOTOX
);
3257 oplen
= js_CodeSpec
[*pc
].length
;
3258 len
= GetJumpOffset(pc
, pc
);
3259 js_printf(jp
, "\t} else");
3262 * If the second offset for sn is non-zero, it tells
3263 * the distance from the goto around the else, to the
3264 * ifeq for the if inside the else that forms an "if
3265 * else if" chain. Thus cond spans the condition of
3266 * the second if, so we simply decompile it and start
3267 * over at label if_again.
3269 cond
= js_GetSrcNoteOffset(sn
, 1);
3272 DECOMPILE_CODE(pc
+ oplen
, cond
- oplen
);
3274 oplen
= js_CodeSpec
[*pc
].length
;
3279 js_printf(jp
, " {\n");
3281 DECOMPILE_CODE(pc
+ oplen
, len
- oplen
);
3284 if (!ss
->inArrayInit
&& !ss
->inGenExp
) {
3286 js_printf(jp
, "\t}\n");
3292 xval
= JS_strdup(cx
, POP_STR());
3295 len
= js_GetSrcNoteOffset(sn
, 0);
3296 DECOMPILE_CODE(pc
+ oplen
, len
- oplen
);
3297 lval
= JS_strdup(cx
, POP_STR());
3299 cx
->free((void *)xval
);
3303 LOCAL_ASSERT(*pc
== JSOP_GOTO
|| *pc
== JSOP_GOTOX
);
3304 oplen
= js_CodeSpec
[*pc
].length
;
3305 len
= GetJumpOffset(pc
, pc
);
3306 DECOMPILE_CODE(pc
+ oplen
, len
- oplen
);
3308 todo
= Sprint(&ss
->sprinter
, "%s ? %s : %s",
3310 cx
->free((void *)xval
);
3311 cx
->free((void *)lval
);
3329 do_logical_connective
:
3330 /* Top of stack is the first clause in a disjunction (||). */
3331 lval
= JS_strdup(cx
, POP_STR());
3334 done
= pc
+ GetJumpOffset(pc
, pc
);
3337 if (!Decompile(ss
, pc
, len
, op
)) {
3338 cx
->free((char *)lval
);
3343 jp
->indent
+ 4 + strlen(lval
) + 4 + strlen(rval
) > 75) {
3344 rval
= JS_strdup(cx
, rval
);
3348 todo
= Sprint(&ss
->sprinter
, "%s %s\n", lval
, xval
);
3349 tail
= Sprint(&ss
->sprinter
, "%*s%s",
3350 jp
->indent
+ 4, "", rval
);
3351 cx
->free((char *)rval
);
3356 todo
= Sprint(&ss
->sprinter
, "%s %s %s", lval
, xval
, rval
);
3358 cx
->free((char *)lval
);
3364 goto do_logical_connective
;
3372 sn
= js_GetSrcNote(jp
->script
, pc
);
3373 if (!IsVarSlot(jp
, pc
, &i
)) {
3374 JS_ASSERT(op
== JSOP_FORLOCAL
);
3375 todo
= Sprint(&ss
->sprinter
, ss_format
, VarPrefix(sn
), GetStr(ss
, i
));
3380 atom
= GetArgOrVarAtom(jp
, i
);
3382 todo
= SprintCString(&ss
->sprinter
, VarPrefix(sn
));
3383 if (todo
< 0 || !QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0))
3389 sn
= js_GetSrcNote(jp
->script
, pc
);
3390 todo
= SprintCString(&ss
->sprinter
, VarPrefix(sn
));
3391 if (todo
< 0 || !QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0))
3398 if (!ATOM_IS_IDENTIFIER(atom
)) {
3399 xval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
3407 todo
= Sprint(&ss
->sprinter
, index_format
, lval
, xval
);
3409 todo
= Sprint(&ss
->sprinter
, ss_format
, lval
, *lval
? "." : "");
3412 if (!QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0))
3418 todo
= SprintCString(&ss
->sprinter
, forelem_cookie
);
3422 case JSOP_ENUMCONSTELEM
:
3424 * The stack has the object under the (top) index expression.
3425 * The "rval" property id is underneath those two on the stack.
3426 * The for loop body net and gross lengths can now be adjusted
3427 * to account for the length of the indexing expression that
3428 * came after JSOP_FORELEM and before JSOP_ENUMELEM.
3431 op
= JSOP_NOP
; /* turn off parens around xval */
3433 op
= JSOP_GETELEM
; /* lval must have high precedence */
3437 LOCAL_ASSERT(strcmp(rval
, forelem_cookie
) == 0);
3438 if (*xval
== '\0') {
3439 todo
= SprintCString(&ss
->sprinter
, lval
);
3441 todo
= Sprint(&ss
->sprinter
,
3442 (JOF_OPMODE(lastop
) == JOF_XMLNAME
)
3455 rval
= GetStr(ss
, ss
->top
-2);
3456 todo
= SprintCString(&ss
->sprinter
, rval
);
3457 if (todo
< 0 || !PushOff(ss
, todo
,
3458 (JSOp
) ss
->opcodes
[ss
->top
-2])) {
3464 #if JS_HAS_DESTRUCTURING
3465 sn
= js_GetSrcNote(jp
->script
, pc
);
3467 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_DESTRUCT
);
3468 pc
= DecompileDestructuring(ss
, pc
, endpc
);
3473 op
= saveop
= JSOP_ENUMELEM
;
3476 if (strcmp(rval
, forelem_cookie
) == 0) {
3477 todo
= Sprint(&ss
->sprinter
, ss_format
,
3478 VarPrefix(sn
), lval
);
3480 // Skip POP so the SRC_FOR_IN code can pop for itself.
3481 if (*pc
== JSOP_POP
)
3482 len
= JSOP_POP_LENGTH
;
3484 todo
= Sprint(&ss
->sprinter
, "%s%s = %s",
3485 VarPrefix(sn
), lval
, rval
);
3491 rval
= GetStr(ss
, ss
->top
-1);
3492 saveop
= (JSOp
) ss
->opcodes
[ss
->top
-1];
3493 todo
= SprintCString(&ss
->sprinter
, rval
);
3497 atom
= GetArgOrVarAtom(jp
, GET_ARGNO(pc
));
3507 lval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3511 if (op
== JSOP_SETNAME
)
3512 (void) PopOff(ss
, op
);
3515 sn
= js_GetSrcNote(jp
->script
, pc
- 1);
3516 if (sn
&& SN_TYPE(sn
) == SRC_ASSIGNOP
) {
3517 todo
= Sprint(&ss
->sprinter
, "%s %s= %s",
3519 (lastop
== JSOP_GETTER
)
3521 : (lastop
== JSOP_SETTER
)
3523 : CodeToken
[lastop
],
3526 sn
= js_GetSrcNote(jp
->script
, pc
);
3527 todo
= Sprint(&ss
->sprinter
, "%s%s = %s",
3528 VarPrefix(sn
), lval
, rval
);
3530 if (op
== JSOP_SETLOCALPOP
) {
3531 if (!PushOff(ss
, todo
, saveop
))
3534 LOCAL_ASSERT(*rval
!= '\0');
3535 js_printf(jp
, "\t%s;\n", rval
);
3542 argc
= GET_UINT16(pc
);
3543 JS_ASSERT(argc
> 0);
3545 js::Vector
<char *> argv(cx
);
3546 if (!argv
.resize(argc
))
3549 MUST_FLOW_THROUGH("out");
3552 for (i
= argc
- 1; i
>= 0; i
--) {
3553 argv
[i
] = JS_strdup(cx
, POP_STR_PREC(cs
->prec
+ 1));
3558 todo
= Sprint(&ss
->sprinter
, "%s", argv
[0]);
3561 for (i
= 1; i
< argc
; i
++) {
3562 if (Sprint(&ss
->sprinter
, " + %s", argv
[i
]) < 0)
3567 * The only way that our next op could be a JSOP_ADD is
3568 * if we are about to concatenate at least one non-string
3569 * literal. Deal with that here in order to avoid extra
3570 * parentheses (because JSOP_ADD is left-associative).
3572 if (pc
[len
] == JSOP_ADD
)
3578 for (i
= 0; i
< argc
; i
++)
3579 JS_free(cx
, argv
[i
]);
3590 argc
= GET_ARGC(pc
);
3592 cx
->malloc((size_t)(argc
+ 1) * sizeof *argv
);
3598 for (i
= argc
; i
> 0; i
--)
3599 argv
[i
] = JS_strdup(cx
, POP_STR());
3601 /* Skip the JSOP_PUSHOBJ-created empty string. */
3602 LOCAL_ASSERT(ss
->top
>= 2);
3603 (void) PopOff(ss
, op
);
3606 * Special case: new (x(y)(z)) must be parenthesized like so.
3607 * Same for new (x(y).z) -- contrast with new x(y).z.
3608 * See PROPAGATE_CALLNESS.
3610 op
= (JSOp
) ss
->opcodes
[ss
->top
- 1];
3612 (saveop
== JSOP_NEW
&&
3616 (js_CodeSpec
[op
].format
& JOF_CALLOP
)))
3621 argv
[0] = JS_strdup(cx
, lval
);
3625 lval
= "(", rval
= ")";
3626 if (op
== JSOP_NEW
) {
3629 todo
= Sprint(&ss
->sprinter
, "%s %s%s",
3630 js_new_str
, argv
[0], lval
);
3632 todo
= Sprint(&ss
->sprinter
, ss_format
,
3638 for (i
= 1; i
<= argc
; i
++) {
3640 Sprint(&ss
->sprinter
, ss_format
,
3641 argv
[i
], (i
< argc
) ? ", " : "") < 0) {
3646 if (Sprint(&ss
->sprinter
, rval
) < 0)
3649 for (i
= 0; i
<= argc
; i
++)
3654 if (op
== JSOP_SETCALL
) {
3655 if (!PushOff(ss
, todo
, op
))
3657 todo
= Sprint(&ss
->sprinter
, "");
3663 lval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3666 RETRACT(&ss
->sprinter
, lval
);
3668 todo
= Sprint(&ss
->sprinter
, "%s %s", js_delete_str
, lval
);
3672 GET_ATOM_QUOTE_AND_FMT("%s %s[%s]", "%s %s.%s", rval
);
3675 todo
= Sprint(&ss
->sprinter
, fmt
, js_delete_str
, lval
, rval
);
3679 op
= JSOP_NOP
; /* turn off parens */
3684 goto do_delete_lval
;
3685 todo
= Sprint(&ss
->sprinter
,
3686 (JOF_OPMODE(lastop
) == JOF_XMLNAME
)
3689 js_delete_str
, lval
, xval
);
3692 #if JS_HAS_XML_SUPPORT
3697 todo
= Sprint(&ss
->sprinter
, "%s %s..%s",
3698 js_delete_str
, lval
, xval
);
3702 case JSOP_TYPEOFEXPR
:
3706 todo
= Sprint(&ss
->sprinter
, "%s %s",
3707 (op
== JSOP_VOID
) ? js_void_str
: js_typeof_str
,
3713 atom
= GetArgOrVarAtom(jp
, GET_ARGNO(pc
));
3723 lval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3726 RETRACT(&ss
->sprinter
, lval
);
3728 todo
= Sprint(&ss
->sprinter
, ss_format
,
3729 js_incop_strs
[!(cs
->format
& JOF_INC
)], lval
);
3734 GET_ATOM_QUOTE_AND_FMT(preindex_format
, predot_format
, rval
);
3737 * Force precedence below the numeric literal opcodes, so that
3738 * 42..foo or 10000..toString(16), e.g., decompile with parens
3739 * around the left-hand side of dot.
3743 todo
= Sprint(&ss
->sprinter
, fmt
,
3744 js_incop_strs
[!(cs
->format
& JOF_INC
)],
3750 op
= JSOP_NOP
; /* turn off parens */
3754 if (*xval
!= '\0') {
3755 todo
= Sprint(&ss
->sprinter
,
3756 (JOF_OPMODE(lastop
) == JOF_XMLNAME
)
3759 js_incop_strs
[!(cs
->format
& JOF_INC
)],
3762 todo
= Sprint(&ss
->sprinter
, ss_format
,
3763 js_incop_strs
[!(cs
->format
& JOF_INC
)], lval
);
3769 atom
= GetArgOrVarAtom(jp
, GET_ARGNO(pc
));
3779 lval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3782 RETRACT(&ss
->sprinter
, lval
);
3784 todo
= Sprint(&ss
->sprinter
, ss_format
,
3785 lval
, js_incop_strs
[!(cs
->format
& JOF_INC
)]);
3790 GET_ATOM_QUOTE_AND_FMT(postindex_format
, postdot_format
, rval
);
3793 * Force precedence below the numeric literal opcodes, so that
3794 * 42..foo or 10000..toString(16), e.g., decompile with parens
3795 * around the left-hand side of dot.
3799 todo
= Sprint(&ss
->sprinter
, fmt
, lval
, rval
,
3800 js_incop_strs
[!(cs
->format
& JOF_INC
)]);
3805 op
= JSOP_NOP
; /* turn off parens */
3809 if (*xval
!= '\0') {
3810 todo
= Sprint(&ss
->sprinter
,
3811 (JOF_OPMODE(lastop
) == JOF_XMLNAME
)
3815 js_incop_strs
[!(cs
->format
& JOF_INC
)]);
3817 todo
= Sprint(&ss
->sprinter
, ss_format
,
3818 lval
, js_incop_strs
[!(cs
->format
& JOF_INC
)]);
3824 rval
= js_length_str
;
3825 goto do_getprop_lval
;
3829 (void) PopOff(ss
, lastop
);
3838 GET_QUOTE_AND_FMT(index_format
, dot_format
, rval
);
3840 PROPAGATE_CALLNESS();
3842 todo
= Sprint(&ss
->sprinter
, fmt
, lval
, rval
);
3845 case JSOP_GETTHISPROP
:
3847 GET_QUOTE_AND_FMT(index_format
, dot_format
, rval
);
3848 todo
= Sprint(&ss
->sprinter
, fmt
, js_this_str
, rval
);
3851 case JSOP_GETARGPROP
:
3852 /* Get the name of the argument or variable. */
3856 atom
= GetArgOrVarAtom(ss
->printer
, i
);
3858 LOCAL_ASSERT(ATOM_IS_STRING(atom
));
3859 lval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3860 if (!lval
|| !PushOff(ss
, STR2OFF(&ss
->sprinter
, lval
), op
))
3863 /* Get the name of the property. */
3864 LOAD_ATOM(ARGNO_LEN
);
3867 case JSOP_GETLOCALPROP
:
3868 if (IsVarSlot(jp
, pc
, &i
))
3869 goto do_getarg_prop
;
3870 LOCAL_ASSERT((uintN
)i
< ss
->top
);
3871 lval
= GetLocal(ss
, i
);
3874 todo
= SprintCString(&ss
->sprinter
, lval
);
3875 if (todo
< 0 || !PushOff(ss
, todo
, op
))
3881 case JSOP_SETMETHOD
:
3883 GET_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval
);
3887 * Force precedence below the numeric literal opcodes, so that
3888 * 42..foo or 10000..toString(16), e.g., decompile with parens
3889 * around the left-hand side of dot.
3893 sn
= js_GetSrcNote(jp
->script
, pc
- 1);
3894 todo
= Sprint(&ss
->sprinter
, fmt
, lval
, xval
,
3895 (sn
&& SN_TYPE(sn
) == SRC_ASSIGNOP
)
3896 ? (lastop
== JSOP_GETTER
)
3898 : (lastop
== JSOP_SETTER
)
3907 (void) PopOff(ss
, lastop
);
3912 op
= JSOP_NOP
; /* turn off parens */
3915 PROPAGATE_CALLNESS();
3917 if (*xval
== '\0') {
3918 todo
= Sprint(&ss
->sprinter
, "%s", lval
);
3920 todo
= Sprint(&ss
->sprinter
,
3921 (JOF_OPMODE(lastop
) == JOF_XMLNAME
)
3930 op
= JSOP_NOP
; /* turn off parens */
3932 cs
= &js_CodeSpec
[ss
->opcodes
[ss
->top
]];
3933 op
= JSOP_GETELEM
; /* lval must have high precedence */
3938 sn
= js_GetSrcNote(jp
->script
, pc
- 1);
3939 todo
= Sprint(&ss
->sprinter
,
3940 (JOF_MODE(cs
->format
) == JOF_XMLNAME
)
3944 (sn
&& SN_TYPE(sn
) == SRC_ASSIGNOP
)
3945 ? (lastop
== JSOP_GETTER
)
3947 : (lastop
== JSOP_SETTER
)
3955 i
= (jsint
) GET_ARGNO(pc
);
3956 todo
= Sprint(&ss
->sprinter
, "%s[%d]",
3957 js_arguments_str
, (int) i
);
3961 todo
= Sprint(&ss
->sprinter
, dot_format
,
3962 js_arguments_str
, js_length_str
);
3968 atom
= GetArgOrVarAtom(jp
, i
);
3969 #if JS_HAS_DESTRUCTURING
3971 todo
= Sprint(&ss
->sprinter
, "%s[%d]", js_arguments_str
, i
);
3986 #if JS_HAS_XML_SUPPORT
3989 sn
= js_GetSrcNote(jp
->script
, pc
);
3990 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
3991 inXML
? DONT_ESCAPE
: 0);
3994 RETRACT(&ss
->sprinter
, rval
);
3995 todo
= Sprint(&ss
->sprinter
, sss_format
,
3996 VarPrefix(sn
), lval
, rval
);
4000 i
= (jsint
) GET_UINT16(pc
);
4004 i
= (jsint
) GET_UINT24(pc
);
4014 todo
= Sprint(&ss
->sprinter
, "%d", i
);
4018 GET_DOUBLE_FROM_BYTECODE(jp
->script
, pc
, 0, atom
);
4019 val
= ATOM_KEY(atom
);
4020 LOCAL_ASSERT(JSVAL_IS_DOUBLE(val
));
4021 todo
= SprintDoubleValue(&ss
->sprinter
, val
, &saveop
);
4026 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
4027 inXML
? DONT_ESCAPE
: '"');
4030 todo
= STR2OFF(&ss
->sprinter
, rval
);
4034 case JSOP_LAMBDA_FC
:
4035 case JSOP_LAMBDA_DBGFC
:
4036 #if JS_HAS_GENERATOR_EXPRS
4037 sn
= js_GetSrcNote(jp
->script
, pc
);
4038 if (sn
&& SN_TYPE(sn
) == SRC_GENEXP
) {
4040 jsuword
*innerLocalNames
, *outerLocalNames
;
4041 JSScript
*inner
, *outer
;
4043 JSFunction
*outerfun
;
4048 * All allocation when decompiling is LIFO, using malloc
4049 * or, more commonly, arena-allocating from cx->tempPool.
4050 * Therefore after InitSprintStack succeeds, we must
4051 * release to mark before returning.
4053 mark
= cx
->tempPool
.getMark();
4054 if (!fun
->hasLocalNames()) {
4055 innerLocalNames
= NULL
;
4057 innerLocalNames
= js_GetLocalNameArray(cx
, fun
, &cx
->tempPool
);
4058 if (!innerLocalNames
)
4061 inner
= fun
->u
.i
.script
;
4062 if (!InitSprintStack(cx
, &ss2
, jp
, StackDepth(inner
))) {
4063 cx
->tempPool
.release(mark
);
4066 ss2
.inGenExp
= JS_TRUE
;
4069 * Recursively decompile this generator function as an
4070 * un-parenthesized generator expression. The ss->inGenExp
4071 * special case of JSOP_YIELD shares array comprehension
4072 * decompilation code that leaves the result as the single
4073 * string pushed on ss2.
4077 outerLocalNames
= jp
->localNames
;
4078 LOCAL_ASSERT(JS_UPTRDIFF(pc
, outer
->code
) <= outer
->length
);
4081 jp
->localNames
= innerLocalNames
;
4082 ok
= Decompile(&ss2
, inner
->code
, inner
->length
, JSOP_NOP
) != NULL
;
4085 jp
->localNames
= outerLocalNames
;
4087 cx
->tempPool
.release(mark
);
4092 * Advance over this op and its global |this| push, and
4093 * arrange to advance over the call to this lambda.
4096 LOCAL_ASSERT(*pc
== JSOP_NULL
);
4097 pc
+= JSOP_NULL_LENGTH
;
4098 LOCAL_ASSERT(*pc
== JSOP_CALL
);
4099 LOCAL_ASSERT(GET_ARGC(pc
) == 0);
4100 len
= JSOP_CALL_LENGTH
;
4103 * Arrange to parenthesize this genexp unless:
4105 * 1. It is the complete expression consumed by a control
4106 * flow bytecode such as JSOP_TABLESWITCH whose syntax
4107 * always parenthesizes the controlling expression.
4108 * 2. It is the sole argument to a function call.
4110 * But if this genexp runs up against endpc, parenthesize
4111 * regardless. (This can happen if we are called from
4112 * DecompileExpression or recursively from case
4113 * JSOP_{NOP,AND,OR}.)
4115 * There's no special case for |if (genexp)| because the
4116 * compiler optimizes that to |if (true)|.
4120 if (op
== JSOP_TRACE
|| op
== JSOP_NOP
)
4121 pc2
+= JSOP_NOP_LENGTH
;
4122 LOCAL_ASSERT(pc2
< endpc
||
4123 endpc
< outer
->code
+ outer
->length
);
4124 LOCAL_ASSERT(ss2
.top
== 1);
4125 ss2
.opcodes
[0] = JSOP_POP
;
4130 op
= ((js_CodeSpec
[op
].format
& JOF_PARENHEAD
) ||
4131 ((js_CodeSpec
[op
].format
& JOF_INVOKE
) && GET_ARGC(pc2
) == 1))
4136 * Stack this result as if it's a name and not an
4137 * anonymous function, so it doesn't get decompiled as
4138 * a generator function in a getter or setter context.
4139 * The precedence level is the same for JSOP_NAME and
4142 LOCAL_ASSERT(js_CodeSpec
[JSOP_NAME
].prec
==
4143 js_CodeSpec
[saveop
].prec
);
4148 * Alas, we have to malloc a copy of the result left on
4149 * the top of ss2 because both ss and ss2 arena-allocate
4150 * from cx's tempPool.
4152 rval
= JS_strdup(cx
, PopStr(&ss2
, op
));
4153 cx
->tempPool
.release(mark
);
4156 todo
= SprintCString(&ss
->sprinter
, rval
);
4157 cx
->free((void *)rval
);
4160 #endif /* JS_HAS_GENERATOR_EXPRS */
4166 * Always parenthesize expression closures. We can't force
4167 * saveop to a low-precedence op to arrange for auto-magic
4168 * parenthesization without confusing getter/setter code
4169 * that checks for JSOP_LAMBDA.
4171 bool grouped
= !(fun
->flags
& JSFUN_EXPR_CLOSURE
);
4172 bool strict
= jp
->script
->strictModeCode
;
4173 str
= js_DecompileToString(cx
, "lambda", fun
, 0,
4174 false, grouped
, strict
,
4175 js_DecompileFunction
);
4180 todo
= SprintString(&ss
->sprinter
, str
);
4184 JS_ASSERT(jp
->fun
&& jp
->fun
->atom
);
4185 todo
= SprintString(&ss
->sprinter
, ATOM_TO_STRING(jp
->fun
->atom
));
4190 LOCAL_ASSERT(obj
->getClass() == &js_RegExpClass
);
4194 GET_REGEXP_FROM_BYTECODE(jp
->script
, pc
, 0, obj
);
4196 if (!js_regexp_toString(cx
, obj
, &val
))
4198 str
= JSVAL_TO_STRING(val
);
4201 case JSOP_TABLESWITCH
:
4202 case JSOP_TABLESWITCHX
:
4204 ptrdiff_t jmplen
, off
, off2
;
4205 jsint j
, n
, low
, high
;
4206 TableEntry
*table
, *tmp
;
4208 sn
= js_GetSrcNote(jp
->script
, pc
);
4209 LOCAL_ASSERT(sn
&& SN_TYPE(sn
) == SRC_SWITCH
);
4210 len
= js_GetSrcNoteOffset(sn
, 0);
4211 jmplen
= (op
== JSOP_TABLESWITCH
) ? JUMP_OFFSET_LEN
4214 off
= GetJumpOffset(pc
, pc2
);
4216 low
= GET_JUMP_OFFSET(pc2
);
4217 pc2
+= JUMP_OFFSET_LEN
;
4218 high
= GET_JUMP_OFFSET(pc2
);
4219 pc2
+= JUMP_OFFSET_LEN
;
4227 table
= (TableEntry
*)
4228 cx
->malloc((size_t)n
* sizeof *table
);
4231 for (i
= j
= 0; i
< n
; i
++) {
4232 table
[j
].label
= NULL
;
4233 off2
= GetJumpOffset(pc
, pc2
);
4235 sn
= js_GetSrcNote(jp
->script
, pc2
);
4237 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_LABEL
);
4238 GET_SOURCE_NOTE_ATOM(sn
, table
[j
].label
);
4240 table
[j
].key
= INT_TO_JSVAL(low
+ i
);
4241 table
[j
].offset
= off2
;
4247 tmp
= (TableEntry
*)
4248 cx
->malloc((size_t)j
* sizeof *table
);
4250 VOUCH_DOES_NOT_REQUIRE_STACK();
4251 ok
= js_MergeSort(table
, (size_t)j
, sizeof(TableEntry
),
4252 CompareOffsets
, NULL
, tmp
);
4260 ok
= DecompileSwitch(ss
, table
, (uintN
)j
, pc
, len
, off
,
4270 case JSOP_LOOKUPSWITCH
:
4271 case JSOP_LOOKUPSWITCHX
:
4273 ptrdiff_t jmplen
, off
, off2
;
4277 sn
= js_GetSrcNote(jp
->script
, pc
);
4278 LOCAL_ASSERT(sn
&& SN_TYPE(sn
) == SRC_SWITCH
);
4279 len
= js_GetSrcNoteOffset(sn
, 0);
4280 jmplen
= (op
== JSOP_LOOKUPSWITCH
) ? JUMP_OFFSET_LEN
4283 off
= GetJumpOffset(pc
, pc2
);
4285 npairs
= GET_UINT16(pc2
);
4288 table
= (TableEntry
*)
4289 cx
->malloc((size_t)npairs
* sizeof *table
);
4292 for (k
= 0; k
< npairs
; k
++) {
4293 sn
= js_GetSrcNote(jp
->script
, pc2
);
4295 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_LABEL
);
4296 GET_SOURCE_NOTE_ATOM(sn
, table
[k
].label
);
4298 table
[k
].label
= NULL
;
4300 JS_GET_SCRIPT_ATOM(jp
->script
, pc
, GET_INDEX(pc2
), atom
);
4302 off2
= GetJumpOffset(pc
, pc2
);
4304 table
[k
].key
= ATOM_KEY(atom
);
4305 table
[k
].offset
= off2
;
4308 ok
= DecompileSwitch(ss
, table
, (uintN
)npairs
, pc
, len
, off
,
4317 case JSOP_CONDSWITCH
:
4319 ptrdiff_t off
, off2
, caseOff
;
4323 sn
= js_GetSrcNote(jp
->script
, pc
);
4324 LOCAL_ASSERT(sn
&& SN_TYPE(sn
) == SRC_SWITCH
);
4325 len
= js_GetSrcNoteOffset(sn
, 0);
4326 off
= js_GetSrcNoteOffset(sn
, 1);
4329 * Count the cases using offsets from switch to first case,
4330 * and case to case, stored in srcnote immediates.
4334 for (ncases
= 0; off2
!= 0; ncases
++) {
4336 LOCAL_ASSERT(*pc2
== JSOP_CASE
|| *pc2
== JSOP_DEFAULT
||
4337 *pc2
== JSOP_CASEX
|| *pc2
== JSOP_DEFAULTX
);
4338 if (*pc2
== JSOP_DEFAULT
|| *pc2
== JSOP_DEFAULTX
) {
4339 /* End of cases, but count default as a case. */
4342 sn
= js_GetSrcNote(jp
->script
, pc2
);
4343 LOCAL_ASSERT(sn
&& SN_TYPE(sn
) == SRC_PCDELTA
);
4344 off2
= js_GetSrcNoteOffset(sn
, 0);
4349 * Allocate table and rescan the cases using their srcnotes,
4350 * stashing each case's delta from switch top in table[i].key,
4351 * and the distance to its statements in table[i].offset.
4353 table
= (TableEntry
*)
4354 cx
->malloc((size_t)ncases
* sizeof *table
);
4359 for (i
= 0; i
< ncases
; i
++) {
4361 LOCAL_ASSERT(*pc2
== JSOP_CASE
|| *pc2
== JSOP_DEFAULT
||
4362 *pc2
== JSOP_CASEX
|| *pc2
== JSOP_DEFAULTX
);
4364 table
[i
].key
= INT_TO_JSVAL((jsint
) caseOff
);
4365 table
[i
].offset
= caseOff
+ GetJumpOffset(pc2
, pc2
);
4366 if (*pc2
== JSOP_CASE
|| *pc2
== JSOP_CASEX
) {
4367 sn
= js_GetSrcNote(jp
->script
, pc2
);
4368 LOCAL_ASSERT(sn
&& SN_TYPE(sn
) == SRC_PCDELTA
);
4369 off2
= js_GetSrcNoteOffset(sn
, 0);
4374 * Find offset of default code by fetching the default offset
4375 * from the end of table. JSOP_CONDSWITCH always has a default
4378 off
= JSVAL_TO_INT(table
[ncases
-1].key
);
4380 off
+= GetJumpOffset(pc2
, pc2
);
4382 ok
= DecompileSwitch(ss
, table
, (uintN
)ncases
, pc
, len
, off
,
4397 js_printf(jp
, "\tcase %s:\n", lval
);
4403 case JSOP_DEFFUN_FC
:
4404 case JSOP_DEFFUN_DBGFC
:
4411 saveop
= op
= JS_GetTrapOpcode(cx
, jp
->script
, pc
);
4413 cs
= &js_CodeSpec
[op
];
4415 DECOMPILE_CODE(pc
, len
);
4421 todo
= SprintPut(&ss
->sprinter
, "", 0);
4425 argc
= GET_UINT16(pc
);
4426 LOCAL_ASSERT(ss
->top
>= (uintN
) argc
);
4428 todo
= SprintCString(&ss
->sprinter
, "[]");
4432 argv
= (char **) cx
->malloc(size_t(argc
) * sizeof *argv
);
4440 argv
[--i
] = JS_strdup(cx
, POP_STR());
4442 todo
= SprintCString(&ss
->sprinter
, "[");
4446 for (i
= 0; i
< argc
; i
++) {
4448 Sprint(&ss
->sprinter
, ss_format
,
4449 argv
[i
], (i
< argc
- 1) ? ", " : "") < 0) {
4455 for (i
= 0; i
< argc
; i
++)
4461 sn
= js_GetSrcNote(jp
->script
, pc
);
4462 if (sn
&& SN_TYPE(sn
) == SRC_CONTINUE
&& SprintCString(&ss
->sprinter
, ", ") < 0)
4464 if (SprintCString(&ss
->sprinter
, "]") < 0)
4471 LOCAL_ASSERT(i
== JSProto_Array
|| i
== JSProto_Object
);
4473 todo
= ss
->sprinter
.offset
;
4474 #if JS_HAS_SHARP_VARS
4476 if (op
== JSOP_SHARPINIT
)
4477 op
= (JSOp
)pc
[len
+= JSOP_SHARPINIT_LENGTH
];
4478 if (op
== JSOP_DEFSHARP
) {
4480 cs
= &js_CodeSpec
[op
];
4482 if (Sprint(&ss
->sprinter
, "#%u=",
4483 (unsigned) (jsint
) GET_UINT16(pc
+ UINT16_LEN
))
4488 #endif /* JS_HAS_SHARP_VARS */
4489 if (i
== JSProto_Array
) {
4491 if (SprintCString(&ss
->sprinter
, "[") < 0)
4494 if (SprintCString(&ss
->sprinter
, "{") < 0)
4504 op
= JSOP_NOP
; /* turn off parens */
4506 sn
= js_GetSrcNote(jp
->script
, pc
);
4508 /* Skip any #n= prefix to find the opening bracket. */
4509 for (xval
= rval
; *xval
!= '[' && *xval
!= '{'; xval
++)
4511 inArray
= (*xval
== '[');
4514 todo
= Sprint(&ss
->sprinter
, "%s%s%c",
4516 (sn
&& SN_TYPE(sn
) == SRC_CONTINUE
) ? ", " : "",
4517 inArray
? ']' : '}');
4523 const char *maybeComma
;
4526 isFirst
= (ss
->opcodes
[ss
->top
- 3] == JSOP_NEWINIT
);
4528 /* Turn off most parens. */
4532 /* Turn off all parens for xval and lval, which we control. */
4536 sn
= js_GetSrcNote(jp
->script
, pc
);
4538 if (sn
&& SN_TYPE(sn
) == SRC_INITPROP
) {
4542 maybeComma
= isFirst
? "" : ", ";
4543 todo
= Sprint(&ss
->sprinter
, sss_format
,
4550 case JSOP_INITMETHOD
:
4552 xval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
4553 jschar(ATOM_IS_IDENTIFIER(atom
) ? 0 : '\''));
4556 isFirst
= (ss
->opcodes
[ss
->top
- 2] == JSOP_NEWINIT
);
4562 maybeComma
= isFirst
? "" : ", ";
4563 if (lastop
== JSOP_GETTER
|| lastop
== JSOP_SETTER
) {
4564 const char *end
= rval
+ strlen(rval
);
4568 LOCAL_ASSERT(strncmp(rval
, js_function_str
, 8) == 0);
4569 LOCAL_ASSERT(rval
[8] == ' ');
4571 LOCAL_ASSERT(*end
? *end
== ')' : end
[-1] == '}');
4572 todo
= Sprint(&ss
->sprinter
, "%s%s%s %s%s%.*s",
4575 (lastop
== JSOP_GETTER
)
4576 ? js_get_str
: js_set_str
,
4578 (rval
[0] != '(') ? " " : "",
4581 todo
= Sprint(&ss
->sprinter
, "%s%s%s: %s",
4582 lval
, maybeComma
, xval
, rval
);
4587 #if JS_HAS_SHARP_VARS
4589 i
= (jsint
) GET_UINT16(pc
+ UINT16_LEN
);
4591 todo
= Sprint(&ss
->sprinter
, "#%u=%s", (unsigned) i
, rval
);
4595 i
= (jsint
) GET_UINT16(pc
+ UINT16_LEN
);
4596 todo
= Sprint(&ss
->sprinter
, "#%u#", (unsigned) i
);
4598 #endif /* JS_HAS_SHARP_VARS */
4600 #if JS_HAS_DEBUGGER_KEYWORD
4602 js_printf(jp
, "\tdebugger;\n");
4605 #endif /* JS_HAS_DEBUGGER_KEYWORD */
4607 #if JS_HAS_XML_SUPPORT
4609 case JSOP_STARTXMLEXPR
:
4610 inXML
= op
== JSOP_STARTXML
;
4616 js_printf(jp
, "\t%s %s %s = %s;\n",
4617 js_default_str
, js_xml_str
, js_namespace_str
, rval
);
4622 if (pc
[JSOP_ANYNAME_LENGTH
] == JSOP_TOATTRNAME
) {
4623 len
+= JSOP_TOATTRNAME_LENGTH
;
4624 todo
= SprintPut(&ss
->sprinter
, "@*", 2);
4626 todo
= SprintPut(&ss
->sprinter
, "*", 1);
4630 case JSOP_QNAMEPART
:
4632 if (pc
[JSOP_QNAMEPART_LENGTH
] == JSOP_TOATTRNAME
) {
4633 saveop
= JSOP_TOATTRNAME
;
4634 len
+= JSOP_TOATTRNAME_LENGTH
;
4640 case JSOP_QNAMECONST
:
4642 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
4645 RETRACT(&ss
->sprinter
, rval
);
4647 todo
= Sprint(&ss
->sprinter
, "%s::%s", lval
, rval
);
4653 todo
= Sprint(&ss
->sprinter
, "%s::[%s]", lval
, rval
);
4656 case JSOP_TOATTRNAME
:
4657 op
= JSOP_NOP
; /* turn off parens */
4659 todo
= Sprint(&ss
->sprinter
, "@[%s]", rval
);
4662 case JSOP_TOATTRVAL
:
4666 case JSOP_ADDATTRNAME
:
4669 todo
= Sprint(&ss
->sprinter
, "%s %s", lval
, rval
);
4670 /* This gets reset by all XML tag expressions. */
4671 quoteAttr
= JS_TRUE
;
4674 case JSOP_ADDATTRVAL
:
4678 todo
= Sprint(&ss
->sprinter
, "%s=\"%s\"", lval
, rval
);
4680 todo
= Sprint(&ss
->sprinter
, "%s=%s", lval
, rval
);
4683 case JSOP_BINDXMLNAME
:
4684 /* Leave the name stacked and push a dummy string. */
4685 todo
= Sprint(&ss
->sprinter
, "");
4688 case JSOP_SETXMLNAME
:
4689 /* Pop the r.h.s., the dummy string, and the name. */
4691 (void) PopOff(ss
, op
);
4695 case JSOP_XMLELTEXPR
:
4696 case JSOP_XMLTAGEXPR
:
4697 todo
= Sprint(&ss
->sprinter
, "{%s}", POP_STR());
4699 /* If we're an attribute value, we shouldn't quote this. */
4700 quoteAttr
= JS_FALSE
;
4703 case JSOP_TOXMLLIST
:
4704 op
= JSOP_NOP
; /* turn off parens */
4705 todo
= Sprint(&ss
->sprinter
, "<>%s</>", POP_STR());
4710 case JSOP_CALLXMLNAME
:
4713 /* These ops indicate the end of XML expressions. */
4718 case JSOP_ENDFILTER
:
4720 PROPAGATE_CALLNESS();
4722 todo
= Sprint(&ss
->sprinter
, "%s.(%s)", lval
, rval
);
4725 case JSOP_DESCENDANTS
:
4727 PROPAGATE_CALLNESS();
4729 todo
= Sprint(&ss
->sprinter
, "%s..%s", lval
, rval
);
4732 case JSOP_XMLOBJECT
:
4734 todo
= Sprint(&ss
->sprinter
, "<xml address='%p'>", obj
);
4739 todo
= SprintPut(&ss
->sprinter
, "<![CDATA[", 9);
4740 if (!QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
4743 SprintPut(&ss
->sprinter
, "]]>", 3);
4746 case JSOP_XMLCOMMENT
:
4748 todo
= SprintPut(&ss
->sprinter
, "<!--", 4);
4749 if (!QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
4752 SprintPut(&ss
->sprinter
, "-->", 3);
4757 rval
= JS_strdup(cx
, POP_STR());
4760 todo
= SprintPut(&ss
->sprinter
, "<?", 2);
4761 ok
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0) &&
4763 (SprintPut(&ss
->sprinter
, " ", 1) >= 0 &&
4764 SprintCString(&ss
->sprinter
, rval
)));
4765 cx
->free((char *)rval
);
4768 SprintPut(&ss
->sprinter
, "?>", 2);
4772 todo
= SprintPut(&ss
->sprinter
, js_function_str
, 8);
4774 #endif /* JS_HAS_XML_SUPPORT */
4783 /* -2 means "don't push", -1 means reported error. */
4787 if (!PushOff(ss
, todo
, saveop
))
4791 if (cs
->format
& JOF_CALLOP
) {
4792 todo
= Sprint(&ss
->sprinter
, "");
4793 if (todo
< 0 || !PushOff(ss
, todo
, saveop
))
4801 * Undefine local macros.
4804 #undef DECOMPILE_CODE
4810 #undef ATOM_IS_IDENTIFIER
4811 #undef GET_QUOTE_AND_FMT
4812 #undef GET_ATOM_QUOTE_AND_FMT
4818 DecompileCode(JSPrinter
*jp
, JSScript
*script
, jsbytecode
*pc
, uintN len
,
4826 JSScript
*oldscript
;
4827 jsbytecode
*oldcode
, *oldmain
, *code
;
4830 depth
= StackDepth(script
);
4831 JS_ASSERT(pcdepth
<= depth
);
4833 /* Initialize a sprinter for use with the offset stack. */
4834 cx
= jp
->sprinter
.context
;
4835 mark
= cx
->tempPool
.getMark();
4836 ok
= InitSprintStack(cx
, &ss
, jp
, depth
);
4841 * If we are called from js_DecompileValueGenerator with a portion of
4842 * script's bytecode that starts with a non-zero model stack depth given
4843 * by pcdepth, attempt to initialize the missing string offsets in ss to
4844 * |spindex| negative indexes from fp->sp for the activation fp in which
4847 * See js_DecompileValueGenerator for how its |spindex| parameter is used,
4848 * and see also GetOff, which makes use of the ss.offsets[i] < -1 that are
4849 * potentially stored below.
4853 for (i
= 0; i
< pcdepth
; i
++) {
4854 ss
.offsets
[i
] = -2 - (ptrdiff_t)i
;
4855 ss
.opcodes
[i
] = *jp
->pcstack
[i
];
4859 /* Call recursive subroutine to do the hard work. */
4860 oldscript
= jp
->script
;
4861 jp
->script
= script
;
4862 oldcode
= jp
->script
->code
;
4863 oldmain
= jp
->script
->main
;
4864 code
= js_UntrapScriptCode(cx
, jp
->script
);
4865 if (code
!= oldcode
) {
4866 jp
->script
->code
= code
;
4867 jp
->script
->main
= code
+ (oldmain
- oldcode
);
4868 pc
= code
+ (pc
- oldcode
);
4871 ok
= Decompile(&ss
, pc
, len
, JSOP_NOP
) != NULL
;
4872 if (code
!= oldcode
) {
4873 cx
->free(jp
->script
->code
);
4874 jp
->script
->code
= oldcode
;
4875 jp
->script
->main
= oldmain
;
4877 jp
->script
= oldscript
;
4879 /* If the given code didn't empty the stack, do it now. */
4882 last
= OFF2STR(&ss
.sprinter
, PopOff(&ss
, JSOP_POP
));
4883 } while (ss
.top
> pcdepth
);
4884 js_printf(jp
, "%s", last
);
4888 /* Free all temporary stuff allocated under this call. */
4889 cx
->tempPool
.release(mark
);
4894 js_DecompileScript(JSPrinter
*jp
, JSScript
*script
)
4896 return DecompileCode(jp
, script
, script
->code
, (uintN
)script
->length
, 0);
4900 js_DecompileToString(JSContext
*cx
, const char *name
, JSFunction
*fun
,
4901 uintN indent
, JSBool pretty
, JSBool grouped
, JSBool strict
,
4902 JSDecompilerPtr decompiler
)
4907 jp
= js_NewPrinter(cx
, name
, fun
, indent
, pretty
, grouped
, strict
);
4911 str
= js_GetPrinterOutput(jp
);
4914 js_DestroyPrinter(jp
);
4918 static const char native_code_str
[] = "\t[native code]\n";
4921 js_DecompileFunctionBody(JSPrinter
*jp
)
4926 JS_ASSERT(!jp
->script
);
4927 if (!FUN_INTERPRETED(jp
->fun
)) {
4928 js_printf(jp
, native_code_str
);
4932 script
= jp
->fun
->u
.i
.script
;
4933 return DecompileCode(jp
, script
, script
->code
, (uintN
)script
->length
, 0);
4937 js_DecompileFunction(JSPrinter
*jp
)
4942 jsbytecode
*pc
, *endpc
;
4948 JS_ASSERT(!jp
->script
);
4951 * If pretty, conform to ECMA-262 Edition 3, 15.3.4.2, by decompiling a
4952 * FunctionDeclaration. Otherwise, check the JSFUN_LAMBDA flag and force
4953 * an expression by parenthesizing.
4956 js_printf(jp
, "\t");
4958 if (!jp
->grouped
&& (fun
->flags
& JSFUN_LAMBDA
))
4961 if (JSFUN_GETTER_TEST(fun
->flags
))
4962 js_printf(jp
, "%s ", js_getter_str
);
4963 else if (JSFUN_SETTER_TEST(fun
->flags
))
4964 js_printf(jp
, "%s ", js_setter_str
);
4966 js_printf(jp
, "%s ", js_function_str
);
4967 if (fun
->atom
&& !QuoteString(&jp
->sprinter
, ATOM_TO_STRING(fun
->atom
), 0))
4971 if (!FUN_INTERPRETED(fun
)) {
4972 js_printf(jp
, ") {\n");
4974 js_printf(jp
, native_code_str
);
4976 js_printf(jp
, "\t}");
4978 JSScript
*script
= fun
->u
.i
.script
;
4979 #if JS_HAS_DESTRUCTURING
4984 /* Print the parameters. */
4986 endpc
= pc
+ script
->length
;
4989 /* Skip trace hint if it appears here. */
4990 #if JS_HAS_GENERATORS
4991 if (js_GetOpcode(jp
->sprinter
.context
, script
, script
->code
) != JSOP_GENERATOR
)
4994 JSOp op
= js_GetOpcode(jp
->sprinter
.context
, script
, pc
);
4995 if (op
== JSOP_TRACE
|| op
== JSOP_NOP
) {
4996 JS_STATIC_ASSERT(JSOP_TRACE_LENGTH
== JSOP_NOP_LENGTH
);
4997 pc
+= JSOP_TRACE_LENGTH
;
4999 JS_ASSERT(op
== JSOP_STOP
); /* empty script singleton */
5003 #if JS_HAS_DESTRUCTURING
5005 jp
->script
= script
;
5006 mark
= jp
->sprinter
.context
->tempPool
.getMark();
5009 for (i
= 0; i
< fun
->nargs
; i
++) {
5013 param
= GetArgOrVarAtom(jp
, i
);
5015 #if JS_HAS_DESTRUCTURING
5016 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, JS_FALSE)
5022 LOCAL_ASSERT(*pc
== JSOP_GETARG
);
5023 pc
+= JSOP_GETARG_LENGTH
;
5024 LOCAL_ASSERT(*pc
== JSOP_DUP
);
5026 ok
= InitSprintStack(jp
->sprinter
.context
, &ss
, jp
, StackDepth(script
));
5030 pc
= DecompileDestructuring(&ss
, pc
, endpc
);
5035 LOCAL_ASSERT(*pc
== JSOP_POP
);
5036 pc
+= JSOP_POP_LENGTH
;
5037 lval
= PopStr(&ss
, JSOP_NOP
);
5038 todo
= SprintCString(&jp
->sprinter
, lval
);
5049 if (!QuoteString(&jp
->sprinter
, ATOM_TO_STRING(param
), 0)) {
5055 #if JS_HAS_DESTRUCTURING
5057 jp
->sprinter
.context
->tempPool
.release(mark
);
5061 if (fun
->flags
& JSFUN_EXPR_CLOSURE
) {
5062 js_printf(jp
, ") ");
5063 if (fun
->u
.i
.script
->strictModeCode
&& !jp
->strict
) {
5065 * We have no syntax for strict function expressions;
5066 * at least give a hint.
5068 js_printf(jp
, "\t/* use strict */ \n");
5073 js_printf(jp
, ") {\n");
5075 if (fun
->u
.i
.script
->strictModeCode
&& !jp
->strict
) {
5076 js_printf(jp
, "\t'use strict';\n");
5081 len
= script
->code
+ script
->length
- pc
;
5082 ok
= DecompileCode(jp
, script
, pc
, (uintN
)len
, 0);
5086 if (!(fun
->flags
& JSFUN_EXPR_CLOSURE
)) {
5088 js_printf(jp
, "\t}");
5092 if (!jp
->pretty
&& !jp
->grouped
&& (fun
->flags
& JSFUN_LAMBDA
))
5099 js_DecompileValueGenerator(JSContext
*cx
, intN spindex
, jsval v
,
5107 jsval
*sp
, *stackBase
;
5110 JS_ASSERT(spindex
< 0 ||
5111 spindex
== JSDVG_IGNORE_STACK
||
5112 spindex
== JSDVG_SEARCH_STACK
);
5114 fp
= js_GetScriptedCaller(cx
, NULL
);
5115 if (!fp
|| !fp
->regs
|| !fp
->regs
->sp
)
5118 script
= fp
->script
;
5120 pc
= fp
->imacpc
? fp
->imacpc
: regs
->pc
;
5121 if (pc
< script
->main
|| script
->code
+ script
->length
<= pc
) {
5122 JS_NOT_REACHED("bug");
5126 if (spindex
!= JSDVG_IGNORE_STACK
) {
5127 jsbytecode
**pcstack
;
5130 * Prepare computing pcstack containing pointers to opcodes that
5131 * populated interpreter's stack with its current content.
5133 pcstack
= (jsbytecode
**)
5134 cx
->malloc(StackDepth(script
) * sizeof *pcstack
);
5137 pcdepth
= ReconstructPCStack(cx
, script
, pc
, pcstack
);
5139 goto release_pcstack
;
5141 if (spindex
!= JSDVG_SEARCH_STACK
) {
5142 JS_ASSERT(spindex
< 0);
5145 goto release_pcstack
;
5146 pc
= pcstack
[pcdepth
];
5149 * We search from fp->sp to base to find the most recently
5150 * calculated value matching v under assumption that it is
5151 * it that caused exception, see bug 328664.
5153 stackBase
= StackBase(fp
);
5156 if (sp
== stackBase
) {
5158 goto release_pcstack
;
5160 } while (*--sp
!= v
);
5163 * The value may have come from beyond stackBase + pcdepth,
5164 * meaning that it came from a temporary slot that the
5165 * interpreter uses for GC roots or when JSOP_APPLY extended
5166 * the stack to fit the argument array elements. Only update pc
5167 * if beneath stackBase + pcdepth; otherwise blame existing
5170 if (sp
< stackBase
+ pcdepth
)
5171 pc
= pcstack
[sp
- stackBase
];
5181 jsbytecode
* savepc
= regs
->pc
;
5182 jsbytecode
* imacpc
= fp
->imacpc
;
5189 * FIXME: bug 489843. Stack reconstruction may have returned a pc
5190 * value *inside* an imacro; this would confuse the decompiler.
5192 if (imacpc
&& size_t(pc
- script
->code
) >= script
->length
)
5193 name
= FAILED_EXPRESSION_DECOMPILER
;
5195 name
= DecompileExpression(cx
, script
, fp
->fun
, pc
);
5199 fp
->imacpc
= imacpc
;
5202 if (name
!= FAILED_EXPRESSION_DECOMPILER
)
5207 fallback
= js_ValueToSource(cx
, v
);
5211 return js_DeflateString(cx
, fallback
->chars(), fallback
->length());
5215 DecompileExpression(JSContext
*cx
, JSScript
*script
, JSFunction
*fun
,
5218 jsbytecode
*code
, *oldcode
, *oldmain
;
5220 const JSCodeSpec
*cs
;
5221 jsbytecode
*begin
, *end
;
5224 jsbytecode
**pcstack
;
5229 JS_ASSERT(script
->main
<= pc
&& pc
< script
->code
+ script
->length
);
5232 oldcode
= script
->code
;
5233 oldmain
= script
->main
;
5235 MUST_FLOW_THROUGH("out");
5236 code
= js_UntrapScriptCode(cx
, script
);
5237 if (code
!= oldcode
) {
5238 script
->code
= code
;
5239 script
->main
= code
+ (oldmain
- oldcode
);
5240 pc
= code
+ (pc
- oldcode
);
5245 /* None of these stack-writing ops generates novel values. */
5246 JS_ASSERT(op
!= JSOP_CASE
&& op
!= JSOP_CASEX
&&
5247 op
!= JSOP_DUP
&& op
!= JSOP_DUP2
);
5249 /* JSOP_PUSH is used to generate undefined for group assignment holes. */
5250 if (op
== JSOP_PUSH
) {
5251 name
= JS_strdup(cx
, js_undefined_str
);
5256 * |this| could convert to a very long object initialiser, so cite it by
5257 * its keyword name instead.
5259 if (op
== JSOP_THIS
) {
5260 name
= JS_strdup(cx
, js_this_str
);
5265 * JSOP_BINDNAME is special: it generates a value, the base object of a
5266 * reference. But if it is the generating op for a diagnostic produced by
5267 * js_DecompileValueGenerator, the name being bound is irrelevant. Just
5268 * fall back to the base object.
5270 if (op
== JSOP_BINDNAME
) {
5271 name
= FAILED_EXPRESSION_DECOMPILER
;
5275 /* NAME ops are self-contained, others require left or right context. */
5276 cs
= &js_CodeSpec
[op
];
5278 end
= pc
+ cs
->length
;
5279 switch (JOF_MODE(cs
->format
)) {
5284 sn
= js_GetSrcNote(script
, pc
);
5286 name
= FAILED_EXPRESSION_DECOMPILER
;
5289 switch (SN_TYPE(sn
)) {
5291 begin
-= js_GetSrcNoteOffset(sn
, 0);
5294 end
= begin
+ js_GetSrcNoteOffset(sn
, 0);
5295 begin
+= cs
->length
;
5298 name
= FAILED_EXPRESSION_DECOMPILER
;
5306 name
= FAILED_EXPRESSION_DECOMPILER
;
5310 pcstack
= (jsbytecode
**)
5311 cx
->malloc(StackDepth(script
) * sizeof *pcstack
);
5317 MUST_FLOW_THROUGH("out");
5318 pcdepth
= ReconstructPCStack(cx
, script
, begin
, pcstack
);
5320 name
= FAILED_EXPRESSION_DECOMPILER
;
5325 jp
= js_NewPrinter(cx
, "js_DecompileValueGenerator", fun
, 0,
5326 false, false, false);
5329 jp
->pcstack
= pcstack
;
5330 if (DecompileCode(jp
, script
, begin
, (uintN
) len
, (uintN
) pcdepth
)) {
5331 name
= (jp
->sprinter
.base
) ? jp
->sprinter
.base
: (char *) "";
5332 name
= JS_strdup(cx
, name
);
5334 js_DestroyPrinter(jp
);
5338 if (code
!= oldcode
) {
5339 cx
->free(script
->code
);
5340 script
->code
= oldcode
;
5341 script
->main
= oldmain
;
5349 js_ReconstructStackDepth(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
)
5351 return ReconstructPCStack(cx
, script
, pc
, NULL
);
5354 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1);
5357 SimulateOp(JSContext
*cx
, JSScript
*script
, JSOp op
, const JSCodeSpec
*cs
,
5358 jsbytecode
*pc
, jsbytecode
**pcstack
, uintN
&pcdepth
)
5360 uintN nuses
= js_GetStackUses(cs
, op
, pc
);
5361 uintN ndefs
= js_GetStackDefs(cx
, cs
, op
, script
, pc
);
5362 LOCAL_ASSERT(pcdepth
>= nuses
);
5364 LOCAL_ASSERT(pcdepth
+ ndefs
<= StackDepth(script
));
5367 * Fill the slots that the opcode defines withs its pc unless it just
5368 * reshuffles the stack. In the latter case we want to preserve the
5369 * opcode that generated the original value.
5374 for (uintN i
= 0; i
!= ndefs
; ++i
)
5375 pcstack
[pcdepth
+ i
] = pc
;
5381 /* Keep the switch value. */
5382 JS_ASSERT(ndefs
== 1);
5386 JS_ASSERT(ndefs
== 2);
5388 pcstack
[pcdepth
+ 1] = pcstack
[pcdepth
];
5392 JS_ASSERT(ndefs
== 4);
5394 pcstack
[pcdepth
+ 2] = pcstack
[pcdepth
];
5395 pcstack
[pcdepth
+ 3] = pcstack
[pcdepth
+ 1];
5400 JS_ASSERT(ndefs
== 2);
5402 jsbytecode
*tmp
= pcstack
[pcdepth
+ 1];
5403 pcstack
[pcdepth
+ 1] = pcstack
[pcdepth
];
5404 pcstack
[pcdepth
] = tmp
;
5415 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_CUSTOM(expr, goto failure);
5418 SimulateImacroCFG(JSContext
*cx
, JSScript
*script
,
5419 uintN pcdepth
, jsbytecode
*pc
, jsbytecode
*target
,
5420 jsbytecode
**pcstack
)
5422 size_t nbytes
= StackDepth(script
) * sizeof *pcstack
;
5423 jsbytecode
** tmp_pcstack
= (jsbytecode
**) cx
->malloc(nbytes
);
5426 memcpy(tmp_pcstack
, pcstack
, nbytes
);
5429 for (; pc
< target
; pc
+= oplen
) {
5430 JSOp op
= js_GetOpcode(cx
, script
, pc
);
5431 const JSCodeSpec
*cs
= &js_CodeSpec
[op
];
5434 oplen
= js_GetVariableBytecodeLength(pc
);
5436 if (SimulateOp(cx
, script
, op
, cs
, pc
, tmp_pcstack
, pcdepth
) < 0)
5439 uint32 type
= cs
->format
& JOF_TYPEMASK
;
5440 if (type
== JOF_JUMP
|| type
== JOF_JUMPX
) {
5441 ptrdiff_t jmpoff
= (type
== JOF_JUMP
) ? GET_JUMP_OFFSET(pc
)
5442 : GET_JUMPX_OFFSET(pc
);
5443 LOCAL_ASSERT(jmpoff
>= 0);
5444 intN tmp_pcdepth
= SimulateImacroCFG(cx
, script
, pcdepth
, pc
+ jmpoff
,
5445 target
, tmp_pcstack
);
5446 if (tmp_pcdepth
>= 0) {
5447 pcdepth
= uintN(tmp_pcdepth
);
5451 if (op
== JSOP_GOTO
|| op
== JSOP_GOTOX
)
5459 LOCAL_ASSERT(pc
== target
);
5462 memcpy(pcstack
, tmp_pcstack
, nbytes
);
5463 cx
->free(tmp_pcstack
);
5467 cx
->free(tmp_pcstack
);
5472 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1);
5475 ReconstructPCStack(JSContext
*cx
, JSScript
*script
, jsbytecode
*target
,
5476 jsbytecode
**pcstack
);
5479 ReconstructImacroPCStack(JSContext
*cx
, JSScript
*script
,
5480 jsbytecode
*imacstart
, jsbytecode
*target
,
5481 jsbytecode
**pcstack
)
5484 * Begin with a recursive call back to ReconstructPCStack to pick up
5485 * the state-of-the-world at the *start* of the imacro.
5487 JSStackFrame
*fp
= js_GetScriptedCaller(cx
, NULL
);
5488 JS_ASSERT(fp
->imacpc
);
5489 intN pcdepth
= ReconstructPCStack(cx
, script
, fp
->imacpc
, pcstack
);
5492 return SimulateImacroCFG(cx
, script
, pcdepth
, imacstart
, target
, pcstack
);
5495 extern jsbytecode
* js_GetImacroStart(jsbytecode
* pc
);
5499 ReconstructPCStack(JSContext
*cx
, JSScript
*script
, jsbytecode
*target
,
5500 jsbytecode
**pcstack
)
5503 * Walk forward from script->main and compute the stack depth and stack of
5504 * operand-generating opcode PCs in pcstack.
5506 * FIXME: Code to compute oplen copied from js_Disassemble1 and reduced.
5507 * FIXME: Optimize to use last empty-stack sequence point.
5510 jsbytecode
*imacstart
= js_GetImacroStart(target
);
5513 return ReconstructImacroPCStack(cx
, script
, imacstart
, target
, pcstack
);
5516 LOCAL_ASSERT(script
->main
<= target
&& target
< script
->code
+ script
->length
);
5517 jsbytecode
*pc
= script
->main
;
5520 for (; pc
< target
; pc
+= oplen
) {
5521 JSOp op
= js_GetOpcode(cx
, script
, pc
);
5522 const JSCodeSpec
*cs
= &js_CodeSpec
[op
];
5525 oplen
= js_GetVariableBytecodeLength(pc
);
5528 * A (C ? T : E) expression requires skipping either T (if target is in
5529 * E) or both T and E (if target is after the whole expression) before
5530 * adjusting pcdepth based on the JSOP_IFEQ or JSOP_IFEQX at pc that
5531 * tests condition C. We know that the stack depth can't change from
5532 * what it was with C on top of stack.
5534 jssrcnote
*sn
= js_GetSrcNote(script
, pc
);
5535 if (sn
&& SN_TYPE(sn
) == SRC_COND
) {
5536 ptrdiff_t jmpoff
= js_GetSrcNoteOffset(sn
, 0);
5537 if (pc
+ jmpoff
< target
) {
5539 op
= js_GetOpcode(cx
, script
, pc
);
5540 JS_ASSERT(op
== JSOP_GOTO
|| op
== JSOP_GOTOX
);
5541 cs
= &js_CodeSpec
[op
];
5543 JS_ASSERT(oplen
> 0);
5544 ptrdiff_t jmplen
= GetJumpOffset(pc
, pc
);
5545 if (pc
+ jmplen
< target
) {
5546 oplen
= (uintN
) jmplen
;
5551 * Ok, target lies in E. Manually pop C off the model stack,
5552 * since we have moved beyond the IFEQ now.
5554 LOCAL_ASSERT(pcdepth
!= 0);
5559 /* Ignore early-exit code, which is annotated SRC_HIDDEN. */
5560 if (sn
&& SN_TYPE(sn
) == SRC_HIDDEN
)
5563 if (SimulateOp(cx
, script
, op
, cs
, pc
, pcstack
, pcdepth
) < 0)
5567 LOCAL_ASSERT(pc
== target
);
5573 #undef LOCAL_ASSERT_RV