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"
83 * Index limit must stay within 32 bits.
85 JS_STATIC_ASSERT(sizeof(uint32
) * JS_BITS_PER_BYTE
>= INDEX_LIMIT_LOG2
+ 1);
87 /* Verify JSOP_XXX_LENGTH constant definitions. */
88 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
89 JS_STATIC_ASSERT(op##_LENGTH == length);
90 #include "jsopcode.tbl"
93 static const char js_incop_strs
[][3] = {"++", "--"};
94 static const char js_for_each_str
[] = "for each";
96 const JSCodeSpec js_CodeSpec
[] = {
97 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
98 {length,nuses,ndefs,prec,format},
99 #include "jsopcode.tbl"
103 uintN js_NumCodeSpecs
= JS_ARRAY_LENGTH(js_CodeSpec
);
106 * Each element of the array is either a source literal associated with JS
109 static const char *CodeToken
[] = {
110 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
112 #include "jsopcode.tbl"
116 #if defined(DEBUG) || defined(JS_JIT_SPEW)
118 * Array of JS bytecode names used by DEBUG-only js_Disassemble and by
121 const char *js_CodeName
[] = {
122 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
124 #include "jsopcode.tbl"
129 /************************************************************************/
132 GetJumpOffset(jsbytecode
*pc
, jsbytecode
*pc2
)
136 type
= JOF_OPTYPE(*pc
);
137 if (JOF_TYPE_IS_EXTENDED_JUMP(type
))
138 return GET_JUMPX_OFFSET(pc2
);
139 return GET_JUMP_OFFSET(pc2
);
143 js_GetIndexFromBytecode(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
,
149 op
= js_GetOpcode(cx
, script
, pc
);
150 JS_ASSERT(js_CodeSpec
[op
].length
>= 1 + pcoff
+ UINT16_LEN
);
153 * We need to detect index base prefix. It presents when resetbase
154 * follows the bytecode.
156 span
= js_CodeSpec
[op
].length
;
158 if (pc
- script
->code
+ span
< script
->length
) {
159 if (pc
[span
] == JSOP_RESETBASE
) {
160 base
= GET_INDEXBASE(pc
- JSOP_INDEXBASE_LENGTH
);
161 } else if (pc
[span
] == JSOP_RESETBASE0
) {
162 JS_ASSERT(JSOP_INDEXBASE1
<= pc
[-1] || pc
[-1] <= JSOP_INDEXBASE3
);
163 base
= (pc
[-1] - JSOP_INDEXBASE1
+ 1) << 16;
166 return base
+ GET_UINT16(pc
+ pcoff
);
170 js_GetVariableBytecodeLength(jsbytecode
*pc
)
173 uintN jmplen
, ncases
;
177 JS_ASSERT(js_CodeSpec
[op
].length
== -1);
179 case JSOP_TABLESWITCHX
:
180 jmplen
= JUMPX_OFFSET_LEN
;
182 case JSOP_TABLESWITCH
:
183 jmplen
= JUMP_OFFSET_LEN
;
185 /* Structure: default-jump case-low case-high case1-jump ... */
187 low
= GET_JUMP_OFFSET(pc
);
188 pc
+= JUMP_OFFSET_LEN
;
189 high
= GET_JUMP_OFFSET(pc
);
190 ncases
= (uintN
)(high
- low
+ 1);
191 return 1 + jmplen
+ INDEX_LEN
+ INDEX_LEN
+ ncases
* jmplen
;
193 case JSOP_LOOKUPSWITCHX
:
194 jmplen
= JUMPX_OFFSET_LEN
;
197 JS_ASSERT(op
== JSOP_LOOKUPSWITCH
);
198 jmplen
= JUMP_OFFSET_LEN
;
200 /* Structure: default-jump case-count (case1-value case1-jump) ... */
202 ncases
= GET_UINT16(pc
);
203 return 1 + jmplen
+ INDEX_LEN
+ ncases
* (INDEX_LEN
+ jmplen
);
208 js_GetVariableStackUses(JSOp op
, jsbytecode
*pc
)
210 JS_ASSERT(*pc
== op
|| *pc
== JSOP_TRAP
);
211 JS_ASSERT(js_CodeSpec
[op
].nuses
== -1);
214 return GET_UINT16(pc
);
216 return GET_UINT16(pc
);
217 case JSOP_LEAVEBLOCK
:
218 return GET_UINT16(pc
);
219 case JSOP_LEAVEBLOCKEXPR
:
220 return GET_UINT16(pc
) + 1;
222 return GET_UINT16(pc
);
224 /* stack: fun, this, [argc arguments] */
225 JS_ASSERT(op
== JSOP_NEW
|| op
== JSOP_CALL
||
226 op
== JSOP_EVAL
|| op
== JSOP_SETCALL
||
228 return 2 + GET_ARGC(pc
);
233 js_GetEnterBlockStackDefs(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
)
237 JS_ASSERT(*pc
== JSOP_ENTERBLOCK
|| *pc
== JSOP_TRAP
);
238 GET_OBJECT_FROM_BYTECODE(script
, pc
, 0, obj
);
239 return OBJ_BLOCK_COUNT(cx
, obj
);
244 JS_FRIEND_API(JSBool
)
245 js_Disassemble(JSContext
*cx
, JSScript
*script
, JSBool lines
, FILE *fp
)
247 jsbytecode
*pc
, *end
;
251 end
= pc
+ script
->length
;
253 if (pc
== script
->main
)
254 fputs("main:\n", fp
);
255 len
= js_Disassemble1(cx
, script
, pc
,
266 js_DumpScript(JSContext
*cx
, JSScript
*script
)
268 return js_Disassemble(cx
, script
, true, stdout
);
272 ToDisassemblySource(JSContext
*cx
, jsval v
)
274 if (!JSVAL_IS_PRIMITIVE(v
)) {
275 JSObject
*obj
= JSVAL_TO_OBJECT(v
);
276 JSClass
*clasp
= OBJ_GET_CLASS(cx
, obj
);
278 if (clasp
== &js_BlockClass
) {
279 char *source
= JS_sprintf_append(NULL
, "depth %d {", OBJ_BLOCK_DEPTH(cx
, obj
));
280 for (JSScopeProperty
*sprop
= OBJ_SCOPE(obj
)->lastProperty();
282 sprop
= sprop
->parent
) {
283 const char *bytes
= js_AtomToPrintableString(cx
, JSID_TO_ATOM(sprop
->id
));
286 source
= JS_sprintf_append(source
, "%s: %d%s",
287 bytes
, sprop
->shortid
,
288 sprop
->parent
? ", " : "");
291 source
= JS_sprintf_append(source
, "}");
295 JSString
*str
= JS_NewString(cx
, source
, strlen(source
));
298 return js_GetStringBytes(cx
, str
);
301 if (clasp
== &js_FunctionClass
) {
302 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, obj
);
303 JSString
*str
= JS_DecompileFunction(cx
, fun
, JS_DONT_PRETTY_PRINT
);
306 return js_GetStringBytes(cx
, str
);
309 if (clasp
== &js_RegExpClass
) {
310 JSAutoTempValueRooter
tvr(cx
);
311 if (!js_regexp_toString(cx
, obj
, tvr
.addr()))
313 return js_GetStringBytes(cx
, JSVAL_TO_STRING(tvr
.value()));
317 return js_ValueToPrintableSource(cx
, v
);
321 js_Disassemble1(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
,
322 uintN loc
, JSBool lines
, FILE *fp
)
325 const JSCodeSpec
*cs
;
326 ptrdiff_t len
, off
, jmplen
;
336 if (op
>= JSOP_LIMIT
) {
337 char numBuf1
[12], numBuf2
[12];
338 JS_snprintf(numBuf1
, sizeof numBuf1
, "%d", op
);
339 JS_snprintf(numBuf2
, sizeof numBuf2
, "%d", JSOP_LIMIT
);
340 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
341 JSMSG_BYTECODE_TOO_BIG
, numBuf1
, numBuf2
);
344 cs
= &js_CodeSpec
[op
];
345 len
= (ptrdiff_t) cs
->length
;
346 fprintf(fp
, "%05u:", loc
);
348 fprintf(fp
, "%4u", JS_PCToLineNumber(cx
, script
, pc
));
349 fprintf(fp
, " %s", js_CodeName
[op
]);
350 type
= JOF_TYPE(cs
->format
);
353 if (op
== JSOP_TRAP
) {
354 op
= JS_GetTrapOpcode(cx
, script
, pc
);
355 len
= (ptrdiff_t) js_CodeSpec
[op
].length
;
361 off
= GetJumpOffset(pc
, pc
);
362 fprintf(fp
, " %u (%d)", loc
+ (intN
) off
, (intN
) off
);
368 index
= js_GetIndexFromBytecode(cx
, script
, pc
, 0);
369 if (type
== JOF_ATOM
) {
370 JS_GET_SCRIPT_ATOM(script
, pc
, index
, atom
);
373 if (type
== JOF_OBJECT
)
374 obj
= script
->getObject(index
);
376 obj
= script
->getRegExp(index
);
377 v
= OBJECT_TO_JSVAL(obj
);
379 bytes
= ToDisassemblySource(cx
, v
);
382 fprintf(fp
, " %s", bytes
);
386 i
= (jsint
)GET_UINT16(pc
);
387 fprintf(fp
, " %d", i
);
391 i
= (jsint
)GET_UINT16(pc
);
394 case JOF_TABLESWITCH
:
395 case JOF_TABLESWITCHX
:
400 jmplen
= (type
== JOF_TABLESWITCH
) ? JUMP_OFFSET_LEN
403 off
= GetJumpOffset(pc
, pc2
);
405 low
= GET_JUMP_OFFSET(pc2
);
406 pc2
+= JUMP_OFFSET_LEN
;
407 high
= GET_JUMP_OFFSET(pc2
);
408 pc2
+= JUMP_OFFSET_LEN
;
409 fprintf(fp
, " defaultOffset %d low %d high %d", (intN
) off
, low
, high
);
410 for (i
= low
; i
<= high
; i
++) {
411 off
= GetJumpOffset(pc
, pc2
);
412 fprintf(fp
, "\n\t%d: %d", i
, (intN
) off
);
419 case JOF_LOOKUPSWITCH
:
420 case JOF_LOOKUPSWITCHX
:
425 jmplen
= (type
== JOF_LOOKUPSWITCH
) ? JUMP_OFFSET_LEN
428 off
= GetJumpOffset(pc
, pc2
);
430 npairs
= GET_UINT16(pc2
);
432 fprintf(fp
, " offset %d npairs %u", (intN
) off
, (uintN
) npairs
);
434 JS_GET_SCRIPT_ATOM(script
, pc
, GET_INDEX(pc2
), atom
);
436 off
= GetJumpOffset(pc
, pc2
);
439 bytes
= ToDisassemblySource(cx
, ATOM_KEY(atom
));
442 fprintf(fp
, "\n\t%s: %d", bytes
, (intN
) off
);
450 fprintf(fp
, " %u", GET_ARGNO(pc
));
454 fprintf(fp
, " %u", GET_SLOTNO(pc
));
459 fprintf(fp
, " %u", GET_SLOTNO(pc
));
460 index
= js_GetIndexFromBytecode(cx
, script
, pc
, SLOTNO_LEN
);
461 if (type
== JOF_SLOTATOM
) {
462 JS_GET_SCRIPT_ATOM(script
, pc
, index
, atom
);
465 obj
= script
->getObject(index
);
466 v
= OBJECT_TO_JSVAL(obj
);
468 bytes
= ToDisassemblySource(cx
, v
);
471 fprintf(fp
, " %s", bytes
);
475 JS_ASSERT(op
== JSOP_UINT24
|| op
== JSOP_NEWARRAY
);
476 i
= (jsint
)GET_UINT24(pc
);
488 JS_ASSERT(op
== JSOP_INT32
);
491 fprintf(fp
, " %d", i
);
496 JS_snprintf(numBuf
, sizeof numBuf
, "%lx", (unsigned long) cs
->format
);
497 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
498 JSMSG_UNKNOWN_FORMAT
, numBuf
);
508 /************************************************************************/
511 * Sprintf, but with unlimited and automatically allocated buffering.
513 typedef struct Sprinter
{
514 JSContext
*context
; /* context executing the decompiler */
515 JSArenaPool
*pool
; /* string allocation pool */
516 char *base
; /* base address of buffer in pool */
517 size_t size
; /* size of buffer allocated at base */
518 ptrdiff_t offset
; /* offset of next free char in buffer */
521 #define INIT_SPRINTER(cx, sp, ap, off) \
522 ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0, \
525 #define OFF2STR(sp,off) ((sp)->base + (off))
526 #define STR2OFF(sp,str) ((str) - (sp)->base)
527 #define RETRACT(sp,str) ((sp)->offset = STR2OFF(sp, str))
530 SprintEnsureBuffer(Sprinter
*sp
, size_t len
)
535 nb
= (sp
->offset
+ len
+ 1) - sp
->size
;
540 JS_ARENA_ALLOCATE_CAST(base
, char *, sp
->pool
, nb
);
542 JS_ARENA_GROW_CAST(base
, char *, sp
->pool
, sp
->size
, nb
);
545 js_ReportOutOfScriptQuota(sp
->context
);
554 SprintPut(Sprinter
*sp
, const char *s
, size_t len
)
559 /* Allocate space for s, including the '\0' at the end. */
560 if (!SprintEnsureBuffer(sp
, len
))
563 /* Advance offset and copy s into sp's buffer. */
566 bp
= sp
->base
+ offset
;
573 SprintCString(Sprinter
*sp
, const char *s
)
575 return SprintPut(sp
, s
, strlen(s
));
579 SprintString(Sprinter
*sp
, JSString
*str
)
585 str
->getCharsAndLength(chars
, length
);
589 size
= js_GetDeflatedStringLength(sp
->context
, chars
, length
);
590 if (size
== (size_t)-1 || !SprintEnsureBuffer(sp
, size
))
595 js_DeflateStringToBuffer(sp
->context
, chars
, length
, sp
->base
+ offset
,
597 sp
->base
[sp
->offset
] = 0;
603 Sprint(Sprinter
*sp
, const char *format
, ...)
609 va_start(ap
, format
);
610 bp
= JS_vsmprintf(format
, ap
); /* XXX vsaprintf */
613 JS_ReportOutOfMemory(sp
->context
);
616 offset
= SprintCString(sp
, bp
);
621 const char js_EscapeMap
[] = {
634 #define DONT_ESCAPE 0x10000
637 QuoteString(Sprinter
*sp
, JSString
*str
, uint32 quote
)
639 JSBool dontEscape
, ok
;
642 const jschar
*s
, *t
, *z
;
646 /* Sample off first for later return value pointer computation. */
647 dontEscape
= (quote
& DONT_ESCAPE
) != 0;
650 if (qc
&& Sprint(sp
, "%c", (char)qc
) < 0)
653 /* Loop control variables: z points at end of string sentinel. */
654 str
->getCharsAndEnd(s
, z
);
655 for (t
= s
; t
< z
; s
= ++t
) {
656 /* Move t forward from s past un-quote-worthy characters. */
658 while (JS_ISPRINT(c
) && c
!= qc
&& c
!= '\\' && c
!= '\t' &&
666 /* Allocate space for s, including the '\0' at the end. */
667 if (!SprintEnsureBuffer(sp
, len
))
670 /* Advance sp->offset and copy s into sp's buffer. */
671 bp
= sp
->base
+ sp
->offset
;
680 /* Use js_EscapeMap, \u, or \x only if necessary. */
681 if (!(c
>> 8) && (e
= strchr(js_EscapeMap
, (int)c
)) != NULL
) {
683 ? Sprint(sp
, "%c", (char)c
) >= 0
684 : Sprint(sp
, "\\%c", e
[1]) >= 0;
686 ok
= Sprint(sp
, (c
>> 8) ? "\\u%04X" : "\\x%02X", c
) >= 0;
692 /* Sprint the closing quote and return the quoted string. */
693 if (qc
&& Sprint(sp
, "%c", (char)qc
) < 0)
697 * If we haven't Sprint'd anything yet, Sprint an empty string so that
698 * the OFF2STR below gives a valid result.
700 if (off
== sp
->offset
&& Sprint(sp
, "") < 0)
702 return OFF2STR(sp
, off
);
706 js_QuoteString(JSContext
*cx
, JSString
*str
, jschar quote
)
713 mark
= JS_ARENA_MARK(&cx
->tempPool
);
714 INIT_SPRINTER(cx
, &sprinter
, &cx
->tempPool
, 0);
715 bytes
= QuoteString(&sprinter
, str
, quote
);
716 escstr
= bytes
? JS_NewStringCopyZ(cx
, bytes
) : NULL
;
717 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
721 /************************************************************************/
724 Sprinter sprinter
; /* base class state */
725 JSArenaPool pool
; /* string allocation pool */
726 uintN indent
; /* indentation in spaces */
727 bool pretty
; /* pretty-print: indent, use newlines */
728 bool grouped
; /* in parenthesized expression context */
729 bool strict
; /* in code marked strict */
730 JSScript
*script
; /* script being printed */
731 jsbytecode
*dvgfence
; /* DecompileExpression fencepost */
732 jsbytecode
**pcstack
; /* DecompileExpression modeled stack */
733 JSFunction
*fun
; /* interpreted function */
734 jsuword
*localNames
; /* argument and variable names */
738 js_NewPrinter(JSContext
*cx
, const char *name
, JSFunction
*fun
,
739 uintN indent
, JSBool pretty
, JSBool grouped
, JSBool strict
)
743 jp
= (JSPrinter
*) cx
->malloc(sizeof(JSPrinter
));
746 INIT_SPRINTER(cx
, &jp
->sprinter
, &jp
->pool
, 0);
747 JS_InitArenaPool(&jp
->pool
, name
, 256, 1, &cx
->scriptStackQuota
);
750 jp
->grouped
= grouped
;
756 jp
->localNames
= NULL
;
757 if (fun
&& FUN_INTERPRETED(fun
) && fun
->hasLocalNames()) {
758 jp
->localNames
= js_GetLocalNameArray(cx
, fun
, &jp
->pool
);
759 if (!jp
->localNames
) {
760 js_DestroyPrinter(jp
);
768 js_DestroyPrinter(JSPrinter
*jp
)
770 JS_FinishArenaPool(&jp
->pool
);
771 jp
->sprinter
.context
->free(jp
);
775 js_GetPrinterOutput(JSPrinter
*jp
)
780 cx
= jp
->sprinter
.context
;
781 if (!jp
->sprinter
.base
)
782 return cx
->runtime
->emptyString
;
783 str
= JS_NewStringCopyZ(cx
, jp
->sprinter
.base
);
786 JS_FreeArenaPool(&jp
->pool
);
787 INIT_SPRINTER(cx
, &jp
->sprinter
, &jp
->pool
, 0);
792 * NB: Indexed by SRC_DECL_* defines from jsemit.h.
794 static const char * const var_prefix
[] = {"var ", "const ", "let "};
797 VarPrefix(jssrcnote
*sn
)
799 if (sn
&& (SN_TYPE(sn
) == SRC_DECL
|| SN_TYPE(sn
) == SRC_GROUPASSIGN
)) {
800 ptrdiff_t type
= js_GetSrcNoteOffset(sn
, 0);
801 if ((uintN
)type
<= SRC_DECL_LET
)
802 return var_prefix
[type
];
808 js_printf(JSPrinter
*jp
, const char *format
, ...)
817 va_start(ap
, format
);
819 /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */
820 if (*format
== '\t') {
822 if (jp
->pretty
&& Sprint(&jp
->sprinter
, "%*s", jp
->indent
, "") < 0) {
828 /* Suppress newlines (must be once per format, at the end) if not pretty. */
830 if (!jp
->pretty
&& format
[cc
= strlen(format
) - 1] == '\n') {
831 fp
= JS_strdup(jp
->sprinter
.context
, format
);
840 /* Allocate temp space, convert format, and put. */
841 bp
= JS_vsmprintf(format
, ap
); /* XXX vsaprintf */
843 jp
->sprinter
.context
->free(fp
);
847 JS_ReportOutOfMemory(jp
->sprinter
.context
);
853 if (SprintPut(&jp
->sprinter
, bp
, (size_t)cc
) < 0)
862 js_puts(JSPrinter
*jp
, const char *s
)
864 return SprintCString(&jp
->sprinter
, s
) >= 0;
867 /************************************************************************/
869 typedef struct SprintStack
{
870 Sprinter sprinter
; /* sprinter for postfix to infix buffering */
871 ptrdiff_t *offsets
; /* stack of postfix string offsets */
872 jsbytecode
*opcodes
; /* parallel stack of JS opcodes */
873 uintN top
; /* top of stack index */
874 uintN inArrayInit
; /* array initialiser/comprehension level */
875 JSBool inGenExp
; /* in generator expression */
876 JSPrinter
*printer
; /* permanent output goes here */
880 * Find the depth of the operand stack when the interpreter reaches the given
881 * pc in script. pcstack must have space for least script->depth elements. On
882 * return it will contain pointers to opcodes that populated the interpreter's
883 * current operand stack.
885 * This function cannot raise an exception or error. However, due to a risk of
886 * potential bugs when modeling the stack, the function returns -1 if it
887 * detects an inconsistency in the model. Such an inconsistency triggers an
888 * assert in a debug build.
891 ReconstructPCStack(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
,
892 jsbytecode
**pcstack
);
894 #define FAILED_EXPRESSION_DECOMPILER ((char *) 1)
897 * Decompile a part of expression up to the given pc. The function returns
898 * NULL on out-of-memory, or the FAILED_EXPRESSION_DECOMPILER sentinel when
899 * the decompiler fails due to a bug and/or unimplemented feature, or the
900 * decompiled string on success.
903 DecompileExpression(JSContext
*cx
, JSScript
*script
, JSFunction
*fun
,
907 * Get a stacked offset from ss->sprinter.base, or if the stacked value |off|
908 * is negative, fetch the generating pc from printer->pcstack[-2 - off] and
909 * decompile the code that generated the missing value. This is used when
910 * reporting errors, where the model stack will lack |pcdepth| non-negative
911 * offsets (see DecompileExpression and DecompileCode).
913 * If the stacked offset is -1, return 0 to index the NUL padding at the start
914 * of ss->sprinter.base. If this happens, it means there is a decompiler bug
915 * to fix, but it won't violate memory safety.
918 GetOff(SprintStack
*ss
, uintN i
)
924 off
= ss
->offsets
[i
];
928 JS_ASSERT(off
<= -2);
929 JS_ASSERT(ss
->printer
->pcstack
);
930 if (off
<= -2 && ss
->printer
->pcstack
) {
931 pc
= ss
->printer
->pcstack
[-2 - off
];
932 bytes
= DecompileExpression(ss
->sprinter
.context
, ss
->printer
->script
,
933 ss
->printer
->fun
, pc
);
936 if (bytes
!= FAILED_EXPRESSION_DECOMPILER
) {
937 off
= SprintCString(&ss
->sprinter
, bytes
);
940 ss
->offsets
[i
] = off
;
941 ss
->sprinter
.context
->free(bytes
);
944 if (!ss
->sprinter
.base
&& SprintPut(&ss
->sprinter
, "", 0) >= 0) {
945 memset(ss
->sprinter
.base
, 0, ss
->sprinter
.offset
);
953 GetStr(SprintStack
*ss
, uintN i
)
958 * Must call GetOff before using ss->sprinter.base, since it may be null
959 * until bootstrapped by GetOff.
962 return OFF2STR(&ss
->sprinter
, off
);
966 * Gap between stacked strings to allow for insertion of parens and commas
967 * when auto-parenthesizing expressions and decompiling array initialisers
968 * (see the JSOP_NEWARRAY case in Decompile).
970 #define PAREN_SLOP (2 + 1)
973 * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME,
974 * JSOP_SETPROP, and JSOP_SETELEM, respectively. They are never stored in
975 * bytecode, so they don't preempt valid opcodes.
977 #define JSOP_GETPROP2 JSOP_LIMIT
978 #define JSOP_GETELEM2 JSOP_LIMIT + 1
979 JS_STATIC_ASSERT(JSOP_GETELEM2
<= 255);
982 AddParenSlop(SprintStack
*ss
)
984 memset(OFF2STR(&ss
->sprinter
, ss
->sprinter
.offset
), 0, PAREN_SLOP
);
985 ss
->sprinter
.offset
+= PAREN_SLOP
;
989 PushOff(SprintStack
*ss
, ptrdiff_t off
, JSOp op
)
993 if (!SprintEnsureBuffer(&ss
->sprinter
, PAREN_SLOP
))
996 /* ss->top points to the next free slot; be paranoid about overflow. */
998 JS_ASSERT(top
< StackDepth(ss
->printer
->script
));
999 if (top
>= StackDepth(ss
->printer
->script
)) {
1000 JS_ReportOutOfMemory(ss
->sprinter
.context
);
1004 /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */
1005 ss
->offsets
[top
] = off
;
1006 ss
->opcodes
[top
] = jsbytecode((op
== JSOP_GETPROP2
) ? JSOP_GETPROP
1007 : (op
== JSOP_GETELEM2
) ? JSOP_GETELEM
1015 PopOffPrec(SprintStack
*ss
, uint8 prec
)
1018 const JSCodeSpec
*topcs
;
1021 /* ss->top points to the next free slot; be paranoid about underflow. */
1023 JS_ASSERT(top
!= 0);
1028 off
= GetOff(ss
, top
);
1029 topcs
= &js_CodeSpec
[ss
->opcodes
[top
]];
1030 if (topcs
->prec
!= 0 && topcs
->prec
< prec
) {
1031 ss
->sprinter
.offset
= ss
->offsets
[top
] = off
- 2;
1032 off
= Sprint(&ss
->sprinter
, "(%s)", OFF2STR(&ss
->sprinter
, off
));
1034 ss
->sprinter
.offset
= off
;
1040 PopStrPrec(SprintStack
*ss
, uint8 prec
)
1044 off
= PopOffPrec(ss
, prec
);
1045 return OFF2STR(&ss
->sprinter
, off
);
1049 PopOff(SprintStack
*ss
, JSOp op
)
1051 return PopOffPrec(ss
, js_CodeSpec
[op
].prec
);
1055 PopStr(SprintStack
*ss
, JSOp op
)
1057 return PopStrPrec(ss
, js_CodeSpec
[op
].prec
);
1060 typedef struct TableEntry
{
1064 jsint order
; /* source order for stable tableswitch sort */
1068 CompareOffsets(void *arg
, const void *v1
, const void *v2
, int *result
)
1070 ptrdiff_t offset_diff
;
1071 const TableEntry
*te1
= (const TableEntry
*) v1
,
1072 *te2
= (const TableEntry
*) v2
;
1074 offset_diff
= te1
->offset
- te2
->offset
;
1075 *result
= (offset_diff
== 0 ? te1
->order
- te2
->order
1076 : offset_diff
< 0 ? -1
1082 SprintDoubleValue(Sprinter
*sp
, jsval v
, JSOp
*opp
)
1086 char *s
, buf
[DTOSTR_STANDARD_BUFFER_SIZE
];
1088 JS_ASSERT(JSVAL_IS_DOUBLE(v
));
1089 d
= *JSVAL_TO_DOUBLE(v
);
1090 if (JSDOUBLE_IS_NEGZERO(d
)) {
1091 todo
= SprintCString(sp
, "-0");
1093 } else if (!JSDOUBLE_IS_FINITE(d
)) {
1094 /* Don't use Infinity and NaN, they're mutable. */
1095 todo
= SprintCString(sp
,
1103 s
= JS_dtostr(buf
, sizeof buf
, DTOSTR_STANDARD
, 0, d
);
1105 JS_ReportOutOfMemory(sp
->context
);
1108 JS_ASSERT(strcmp(s
, js_Infinity_str
) &&
1110 strcmp(s
+ 1, js_Infinity_str
)) &&
1111 strcmp(s
, js_NaN_str
));
1112 todo
= Sprint(sp
, s
);
1118 Decompile(SprintStack
*ss
, jsbytecode
*pc
, intN nb
, JSOp nextop
);
1121 DecompileSwitch(SprintStack
*ss
, TableEntry
*table
, uintN tableLength
,
1122 jsbytecode
*pc
, ptrdiff_t switchLength
,
1123 ptrdiff_t defaultOffset
, JSBool isCondSwitch
)
1127 ptrdiff_t off
, off2
, diff
, caseExprOff
, todo
;
1133 cx
= ss
->sprinter
.context
;
1136 /* JSOP_CONDSWITCH doesn't pop, unlike JSOP_{LOOKUP,TABLE}SWITCH. */
1137 off
= isCondSwitch
? GetOff(ss
, ss
->top
-1) : PopOff(ss
, JSOP_NOP
);
1138 lval
= OFF2STR(&ss
->sprinter
, off
);
1140 js_printf(jp
, "\tswitch (%s) {\n", lval
);
1143 diff
= table
[0].offset
- defaultOffset
;
1146 js_printf(jp
, "\t%s:\n", js_default_str
);
1148 if (!Decompile(ss
, pc
+ defaultOffset
, diff
, JSOP_NOP
))
1153 caseExprOff
= isCondSwitch
? JSOP_CONDSWITCH_LENGTH
: 0;
1155 for (i
= 0; i
< tableLength
; i
++) {
1156 off
= table
[i
].offset
;
1157 off2
= (i
+ 1 < tableLength
) ? table
[i
+ 1].offset
: switchLength
;
1161 ptrdiff_t nextCaseExprOff
;
1164 * key encodes the JSOP_CASE bytecode's offset from switchtop.
1165 * The next case expression follows immediately, unless we are
1168 nextCaseExprOff
= (ptrdiff_t)JSVAL_TO_INT(key
);
1169 nextCaseExprOff
+= js_CodeSpec
[pc
[nextCaseExprOff
]].length
;
1171 if (!Decompile(ss
, pc
+ caseExprOff
,
1172 nextCaseExprOff
- caseExprOff
, JSOP_NOP
)) {
1175 caseExprOff
= nextCaseExprOff
;
1177 /* Balance the stack as if this JSOP_CASE matched. */
1181 * key comes from an atom, not the decompiler, so we need to
1182 * quote it if it's a string literal. But if table[i].label
1183 * is non-null, key was constant-propagated and label is the
1184 * name of the const we should show as the case label. We set
1185 * key to undefined so this identifier is escaped, if required
1186 * by non-ASCII characters, but not quoted, by QuoteString.
1189 if (table
[i
].label
) {
1190 str
= ATOM_TO_STRING(table
[i
].label
);
1192 } else if (JSVAL_IS_DOUBLE(key
)) {
1195 todo
= SprintDoubleValue(&ss
->sprinter
, key
, &junk
);
1198 str
= js_ValueToString(cx
, key
);
1203 rval
= OFF2STR(&ss
->sprinter
, todo
);
1205 rval
= QuoteString(&ss
->sprinter
, str
, (jschar
)
1206 (JSVAL_IS_STRING(key
) ? '"' : 0));
1210 RETRACT(&ss
->sprinter
, rval
);
1212 js_printf(jp
, "\tcase %s:\n", rval
);
1216 if (off
<= defaultOffset
&& defaultOffset
< off2
) {
1217 diff
= defaultOffset
- off
;
1219 if (!Decompile(ss
, pc
+ off
, diff
, JSOP_NOP
))
1221 off
= defaultOffset
;
1224 js_printf(jp
, "\t%s:\n", js_default_str
);
1227 if (!Decompile(ss
, pc
+ off
, off2
- off
, JSOP_NOP
))
1231 /* Re-balance as if last JSOP_CASE or JSOP_DEFAULT mismatched. */
1237 if (defaultOffset
== switchLength
) {
1239 js_printf(jp
, "\t%s:;\n", js_default_str
);
1242 js_printf(jp
, "\t}\n");
1244 /* By the end of a JSOP_CONDSWITCH, the discriminant has been popped. */
1250 #define LOCAL_ASSERT_CUSTOM(expr, BAD_EXIT) \
1253 if (!(expr)) { BAD_EXIT; } \
1256 #define LOCAL_ASSERT_RV(expr, rv) \
1257 LOCAL_ASSERT_CUSTOM(expr, return (rv))
1260 GetArgOrVarAtom(JSPrinter
*jp
, uintN slot
)
1264 LOCAL_ASSERT_RV(jp
->fun
, NULL
);
1265 LOCAL_ASSERT_RV(slot
< jp
->fun
->countLocalNames(), NULL
);
1266 name
= JS_LOCAL_NAME_TO_ATOM(jp
->localNames
[slot
]);
1267 #if !JS_HAS_DESTRUCTURING
1268 LOCAL_ASSERT_RV(name
, NULL
);
1274 GetLocal(SprintStack
*ss
, jsint i
)
1283 JSScopeProperty
*sprop
;
1286 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, "")
1288 off
= ss
->offsets
[i
];
1290 return OFF2STR(&ss
->sprinter
, off
);
1293 * We must be called from js_DecompileValueGenerator (via Decompile) when
1294 * dereferencing a local that's undefined or null. Search script->objects
1295 * for the block containing this local by its stack index, i.
1297 * In case of destructuring's use of JSOP_GETLOCAL, however, there may be
1298 * no such local. This could mean no blocks (no script objects at all, or
1299 * none of the script's object literals are blocks), or the stack slot i is
1300 * not in a block. In either case, return GetStr(ss, i).
1302 cx
= ss
->sprinter
.context
;
1303 script
= ss
->printer
->script
;
1304 if (script
->objectsOffset
== 0)
1305 return GetStr(ss
, i
);
1306 for (j
= 0, n
= script
->objects()->length
; ; j
++) {
1308 return GetStr(ss
, i
);
1309 obj
= script
->getObject(j
);
1310 if (OBJ_GET_CLASS(cx
, obj
) == &js_BlockClass
) {
1311 depth
= OBJ_BLOCK_DEPTH(cx
, obj
);
1312 count
= OBJ_BLOCK_COUNT(cx
, obj
);
1313 if ((jsuint
)(i
- depth
) < (jsuint
)count
)
1319 for (sprop
= OBJ_SCOPE(obj
)->lastProperty(); sprop
; sprop
= sprop
->parent
) {
1320 if (sprop
->shortid
== i
)
1324 LOCAL_ASSERT(sprop
&& JSID_IS_ATOM(sprop
->id
));
1325 atom
= JSID_TO_ATOM(sprop
->id
);
1326 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
1329 RETRACT(&ss
->sprinter
, rval
);
1336 IsVarSlot(JSPrinter
*jp
, jsbytecode
*pc
, jsint
*indexp
)
1340 slot
= GET_SLOTNO(pc
);
1341 if (slot
< jp
->script
->nfixed
) {
1342 /* The slot refers to a variable with name stored in jp->localNames. */
1343 *indexp
= jp
->fun
->nargs
+ slot
;
1347 /* We have a local which index is relative to the stack base. */
1348 slot
-= jp
->script
->nfixed
;
1349 JS_ASSERT(slot
< StackDepth(jp
->script
));
1354 #define LOAD_ATOM(PCOFF) \
1355 GET_ATOM_FROM_BYTECODE(jp->script, pc, PCOFF, atom)
1357 #if JS_HAS_DESTRUCTURING
1359 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
1360 #define LOAD_OP_DATA(pc) (oplen = (cs = &js_CodeSpec[op=(JSOp)*pc])->length)
1363 DecompileDestructuring(SprintStack
*ss
, jsbytecode
*pc
, jsbytecode
*endpc
);
1366 DecompileDestructuringLHS(SprintStack
*ss
, jsbytecode
*pc
, jsbytecode
*endpc
,
1372 const JSCodeSpec
*cs
;
1375 const char *lval
, *xval
;
1380 cx
= ss
->sprinter
.context
;
1387 todo
= SprintPut(&ss
->sprinter
, ", ", 2);
1391 pc
= DecompileDestructuring(ss
, pc
, endpc
);
1397 lval
= PopStr(ss
, JSOP_NOP
);
1398 todo
= SprintCString(&ss
->sprinter
, lval
);
1399 if (op
== JSOP_POPN
)
1401 LOCAL_ASSERT(*pc
== JSOP_POP
);
1407 LOCAL_ASSERT(pc
[oplen
] == JSOP_POP
|| pc
[oplen
] == JSOP_POPN
);
1410 case JSOP_SETLOCALPOP
:
1413 if (op
== JSOP_SETARG
) {
1414 atom
= GetArgOrVarAtom(jp
, GET_SLOTNO(pc
));
1416 } else if (op
== JSOP_SETGVAR
) {
1418 } else if (IsVarSlot(jp
, pc
, &i
)) {
1419 atom
= GetArgOrVarAtom(jp
, i
);
1422 lval
= GetLocal(ss
, i
);
1425 lval
= js_AtomToPrintableString(cx
, atom
);
1427 todo
= SprintCString(&ss
->sprinter
, lval
);
1428 if (op
!= JSOP_SETLOCALPOP
) {
1433 if (op
== JSOP_POPN
)
1435 LOCAL_ASSERT(op
== JSOP_POP
);
1441 * We may need to auto-parenthesize the left-most value decompiled
1442 * here, so add back PAREN_SLOP temporarily. Then decompile until the
1443 * opcode that would reduce the stack depth to (ss->top-1), which we
1444 * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for
1447 todo
= ss
->sprinter
.offset
;
1448 ss
->sprinter
.offset
= todo
+ PAREN_SLOP
;
1449 pc
= Decompile(ss
, pc
, -((intN
)ss
->top
), JSOP_NOP
);
1455 LOCAL_ASSERT(op
== JSOP_ENUMELEM
|| op
== JSOP_ENUMCONSTELEM
);
1456 xval
= PopStr(ss
, JSOP_NOP
);
1457 lval
= PopStr(ss
, JSOP_GETPROP
);
1458 ss
->sprinter
.offset
= todo
;
1459 if (*lval
== '\0') {
1460 /* lval is from JSOP_BINDNAME, so just print xval. */
1461 todo
= SprintCString(&ss
->sprinter
, xval
);
1462 } else if (*xval
== '\0') {
1463 /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */
1464 todo
= SprintCString(&ss
->sprinter
, lval
);
1466 todo
= Sprint(&ss
->sprinter
,
1467 (JOF_OPMODE(ss
->opcodes
[ss
->top
+1]) == JOF_XMLNAME
)
1478 LOCAL_ASSERT(pc
< endpc
);
1484 * Starting with a SRC_DESTRUCT-annotated JSOP_DUP, decompile a destructuring
1485 * left-hand side object or array initialiser, including nested destructuring
1486 * initialisers. On successful return, the decompilation will be pushed on ss
1487 * and the return value will point to the POP or GROUP bytecode following the
1488 * destructuring expression.
1490 * At any point, if pc is equal to endpc and would otherwise advance, we stop
1491 * immediately and return endpc.
1494 DecompileDestructuring(SprintStack
*ss
, jsbytecode
*pc
, jsbytecode
*endpc
)
1500 const JSCodeSpec
*cs
;
1510 LOCAL_ASSERT(*pc
== JSOP_DUP
);
1511 pc
+= JSOP_DUP_LENGTH
;
1514 * Set head so we can rewrite '[' to '{' as needed. Back up PAREN_SLOP
1515 * chars so the destructuring decompilation accumulates contiguously in
1516 * ss->sprinter starting with "[".
1518 head
= SprintPut(&ss
->sprinter
, "[", 1);
1519 if (head
< 0 || !PushOff(ss
, head
, JSOP_NOP
))
1521 ss
->sprinter
.offset
-= PAREN_SLOP
;
1522 LOCAL_ASSERT(head
== ss
->sprinter
.offset
- 1);
1523 LOCAL_ASSERT(*OFF2STR(&ss
->sprinter
, head
) == '[');
1525 cx
= ss
->sprinter
.context
;
1529 while (pc
< endpc
) {
1530 #if JS_HAS_DESTRUCTURING_SHORTHAND
1531 ptrdiff_t nameoff
= -1;
1542 /* Handle the optimized number-pushing opcodes. */
1543 case JSOP_ZERO
: d
= i
= 0; goto do_getelem
;
1544 case JSOP_ONE
: d
= i
= 1; goto do_getelem
;
1545 case JSOP_UINT16
: d
= i
= GET_UINT16(pc
); goto do_getelem
;
1546 case JSOP_UINT24
: d
= i
= GET_UINT24(pc
); goto do_getelem
;
1547 case JSOP_INT8
: d
= i
= GET_INT8(pc
); goto do_getelem
;
1548 case JSOP_INT32
: d
= i
= GET_INT32(pc
); goto do_getelem
;
1551 GET_DOUBLE_FROM_BYTECODE(jp
->script
, pc
, 0, atom
);
1552 d
= *ATOM_TO_DOUBLE(atom
);
1553 LOCAL_ASSERT(JSDOUBLE_IS_FINITE(d
) && !JSDOUBLE_IS_NEGZERO(d
));
1557 sn
= js_GetSrcNote(jp
->script
, pc
);
1562 LOCAL_ASSERT(op
== JSOP_GETELEM
);
1564 /* Distinguish object from array by opcode or source note. */
1565 if (sn
&& SN_TYPE(sn
) == SRC_INITPROP
) {
1566 *OFF2STR(&ss
->sprinter
, head
) = '{';
1567 if (Sprint(&ss
->sprinter
, "%g: ", d
) < 0)
1570 /* Sanity check for the gnarly control flow above. */
1571 LOCAL_ASSERT(i
== d
);
1573 /* Fill in any holes (holes at the end don't matter). */
1574 while (++lasti
< i
) {
1575 if (SprintPut(&ss
->sprinter
, ", ", 2) < 0)
1582 atom
= cx
->runtime
->atomState
.lengthAtom
;
1583 goto do_destructure_atom
;
1588 do_destructure_atom
:
1589 *OFF2STR(&ss
->sprinter
, head
) = '{';
1590 str
= ATOM_TO_STRING(atom
);
1591 #if JS_HAS_DESTRUCTURING_SHORTHAND
1592 nameoff
= ss
->sprinter
.offset
;
1594 if (!QuoteString(&ss
->sprinter
, str
,
1595 js_IsIdentifier(str
) ? 0 : (jschar
)'\'')) {
1598 if (SprintPut(&ss
->sprinter
, ": ", 2) < 0)
1611 * Decompile the left-hand side expression whose bytecode starts at pc
1612 * and continues for a bounded number of bytecodes or stack operations
1613 * (and which in any event stops before endpc).
1615 pc
= DecompileDestructuringLHS(ss
, pc
, endpc
, &hole
);
1619 #if JS_HAS_DESTRUCTURING_SHORTHAND
1621 ptrdiff_t offset
, initlen
;
1623 offset
= ss
->sprinter
.offset
;
1624 LOCAL_ASSERT(*OFF2STR(&ss
->sprinter
, offset
) == '\0');
1625 initlen
= offset
- nameoff
;
1626 LOCAL_ASSERT(initlen
>= 4);
1628 /* Early check to rule out odd "name: lval" length. */
1629 if (((size_t)initlen
& 1) == 0) {
1634 * Even "name: lval" string length: check for "x: x" and the
1635 * like, and apply the shorthand if we can.
1637 namelen
= (size_t)(initlen
- 2) >> 1;
1638 name
= OFF2STR(&ss
->sprinter
, nameoff
);
1639 if (!strncmp(name
+ namelen
, ": ", 2) &&
1640 !strncmp(name
, name
+ namelen
+ 2, namelen
)) {
1641 offset
-= namelen
+ 2;
1642 *OFF2STR(&ss
->sprinter
, offset
) = '\0';
1643 ss
->sprinter
.offset
= offset
;
1649 if (pc
== endpc
|| *pc
!= JSOP_DUP
)
1653 * We should stop if JSOP_DUP is either without notes or its note is
1654 * not SRC_CONTINUE. The former happens when JSOP_DUP duplicates the
1655 * last destructuring reference implementing an op= assignment like in
1656 * '([t] = z).y += x'. In the latter case the note is SRC_DESTRUCT and
1657 * means another destructuring initialiser abuts this one like in
1660 sn
= js_GetSrcNote(jp
->script
, pc
);
1663 if (SN_TYPE(sn
) != SRC_CONTINUE
) {
1664 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_DESTRUCT
);
1668 if (!hole
&& SprintPut(&ss
->sprinter
, ", ", 2) < 0)
1671 pc
+= JSOP_DUP_LENGTH
;
1675 lval
= OFF2STR(&ss
->sprinter
, head
);
1676 if (SprintPut(&ss
->sprinter
, (*lval
== '[') ? "]" : "}", 1) < 0)
1682 DecompileGroupAssignment(SprintStack
*ss
, jsbytecode
*pc
, jsbytecode
*endpc
,
1683 jssrcnote
*sn
, ptrdiff_t *todop
)
1686 const JSCodeSpec
*cs
;
1687 uintN oplen
, start
, end
, i
;
1693 LOCAL_ASSERT(op
== JSOP_PUSH
|| op
== JSOP_GETLOCAL
);
1695 todo
= Sprint(&ss
->sprinter
, "%s[", VarPrefix(sn
));
1696 if (todo
< 0 || !PushOff(ss
, todo
, JSOP_NOP
))
1698 ss
->sprinter
.offset
-= PAREN_SLOP
;
1704 pc
= DecompileDestructuringLHS(ss
, pc
, endpc
, &hole
);
1710 if (op
!= JSOP_PUSH
&& op
!= JSOP_GETLOCAL
)
1712 if (!hole
&& SprintPut(&ss
->sprinter
, ", ", 2) < 0)
1716 LOCAL_ASSERT(op
== JSOP_POPN
);
1717 if (SprintPut(&ss
->sprinter
, "] = [", 5) < 0)
1721 start
= end
- GET_UINT16(pc
);
1722 for (i
= start
; i
< end
; i
++) {
1723 rval
= GetStr(ss
, i
);
1724 if (Sprint(&ss
->sprinter
,
1725 (i
== start
) ? "%s" : ", %s",
1726 (i
== end
- 1 && *rval
== '\0') ? ", " : rval
) < 0) {
1731 if (SprintPut(&ss
->sprinter
, "]", 1) < 0)
1733 ss
->sprinter
.offset
= ss
->offsets
[i
];
1742 #endif /* JS_HAS_DESTRUCTURING */
1745 InitSprintStack(JSContext
*cx
, SprintStack
*ss
, JSPrinter
*jp
, uintN depth
)
1747 size_t offsetsz
, opcodesz
;
1750 INIT_SPRINTER(cx
, &ss
->sprinter
, &cx
->tempPool
, PAREN_SLOP
);
1752 /* Allocate the parallel (to avoid padding) offset and opcode stacks. */
1753 offsetsz
= depth
* sizeof(ptrdiff_t);
1754 opcodesz
= depth
* sizeof(jsbytecode
);
1755 JS_ARENA_ALLOCATE(space
, &cx
->tempPool
, offsetsz
+ opcodesz
);
1757 js_ReportOutOfScriptQuota(cx
);
1760 ss
->offsets
= (ptrdiff_t *) space
;
1761 ss
->opcodes
= (jsbytecode
*) ((char *)space
+ offsetsz
);
1763 ss
->top
= ss
->inArrayInit
= 0;
1764 ss
->inGenExp
= JS_FALSE
;
1770 * If nb is non-negative, decompile nb bytecodes starting at pc. Otherwise
1771 * the decompiler starts at pc and continues until it reaches an opcode for
1772 * which decompiling would result in the stack depth equaling -(nb + 1).
1774 * The nextop parameter is either JSOP_NOP or the "next" opcode in order of
1775 * abstract interpretation (not necessarily physically next in a bytecode
1776 * vector). So nextop is JSOP_POP for the last operand in a comma expression,
1777 * or JSOP_AND for the right operand of &&.
1780 Decompile(SprintStack
*ss
, jsbytecode
*pc
, intN nb
, JSOp nextop
)
1783 JSPrinter
*jp
, *jp2
;
1784 jsbytecode
*startpc
, *endpc
, *pc2
, *done
;
1785 ptrdiff_t tail
, todo
, len
, oplen
, cond
, next
;
1786 JSOp op
, lastop
, saveop
;
1787 const JSCodeSpec
*cs
;
1788 jssrcnote
*sn
, *sn2
;
1789 const char *lval
, *rval
, *xval
, *fmt
, *token
;
1798 #if JS_HAS_XML_SUPPORT
1799 JSBool foreach
, inXML
, quoteAttr
;
1801 #define inXML JS_FALSE
1805 static const char exception_cookie
[] = "/*EXCEPTION*/";
1806 static const char retsub_pc_cookie
[] = "/*RETSUB_PC*/";
1807 static const char iter_cookie
[] = "/*ITER*/";
1808 static const char forelem_cookie
[] = "/*FORELEM*/";
1809 static const char with_cookie
[] = "/*WITH*/";
1810 static const char dot_format
[] = "%s.%s";
1811 static const char index_format
[] = "%s[%s]";
1812 static const char predot_format
[] = "%s%s.%s";
1813 static const char postdot_format
[] = "%s.%s%s";
1814 static const char preindex_format
[] = "%s%s[%s]";
1815 static const char postindex_format
[] = "%s[%s]%s";
1816 static const char ss_format
[] = "%s%s";
1817 static const char sss_format
[] = "%s%s%s";
1819 /* Argument and variables decompilation uses the following to share code. */
1820 JS_STATIC_ASSERT(ARGNO_LEN
== SLOTNO_LEN
);
1825 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
1826 #define DECOMPILE_CODE(pc,nb) if (!Decompile(ss, pc, nb, JSOP_NOP)) return NULL
1827 #define NEXT_OP(pc) (((pc) + (len) == endpc) ? nextop : pc[len])
1828 #define TOP_STR() GetStr(ss, ss->top - 1)
1829 #define POP_STR() PopStr(ss, op)
1830 #define POP_STR_PREC(prec) PopStrPrec(ss, prec)
1833 * Pop a condition expression for if/while. JSOP_IFEQ's precedence forces
1834 * extra parens around assignment, which avoids a strict-mode warning.
1836 #define POP_COND_STR() \
1837 PopStr(ss, (js_CodeSpec[ss->opcodes[ss->top - 1]].format & JOF_SET) \
1842 * Callers know that ATOM_IS_STRING(atom), and we leave it to the optimizer to
1843 * common ATOM_TO_STRING(atom) here and near the call sites.
1845 #define ATOM_IS_IDENTIFIER(atom) js_IsIdentifier(ATOM_TO_STRING(atom))
1846 #define ATOM_IS_KEYWORD(atom) \
1847 (js_CheckKeyword(ATOM_TO_STRING(atom)->chars(), \
1848 ATOM_TO_STRING(atom)->length()) != TOK_EOF)
1851 * Given an atom already fetched from jp->script's atom map, quote/escape its
1852 * string appropriately into rval, and select fmt from the quoted and unquoted
1855 #define GET_QUOTE_AND_FMT(qfmt, ufmt, rval) \
1858 if (!ATOM_IS_IDENTIFIER(atom)) { \
1865 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), quote_); \
1870 #define LOAD_OBJECT(PCOFF) \
1871 GET_OBJECT_FROM_BYTECODE(jp->script, pc, PCOFF, obj)
1873 #define LOAD_FUNCTION(PCOFF) \
1874 GET_FUNCTION_FROM_BYTECODE(jp->script, pc, PCOFF, fun)
1876 #define LOAD_REGEXP(PCOFF) \
1877 GET_REGEXP_FROM_BYTECODE(jp->script, pc, PCOFF, obj)
1879 #define GET_SOURCE_NOTE_ATOM(sn, atom) \
1881 jsatomid atomIndex_ = (jsatomid) js_GetSrcNoteOffset((sn), 0); \
1883 LOCAL_ASSERT(atomIndex_ < jp->script->atomMap.length); \
1884 (atom) = jp->script->atomMap.vector[atomIndex_]; \
1888 * Get atom from jp->script's atom map, quote/escape its string appropriately
1889 * into rval, and select fmt from the quoted and unquoted alternatives.
1891 #define GET_ATOM_QUOTE_AND_FMT(qfmt, ufmt, rval) \
1894 GET_QUOTE_AND_FMT(qfmt, ufmt, rval); \
1898 * Per spec, new x(y).z means (new x(y))).z. For example new (x(y).z) must
1899 * decompile with the constructor parenthesized, but new x.z should not. The
1900 * normal rules give x(y).z and x.z identical precedence: both are produced by
1903 * Therefore, we need to know in case JSOP_NEW whether the constructor
1904 * expression contains any unparenthesized function calls. So when building a
1905 * MemberExpression or CallExpression, we set ss->opcodes[n] to JSOP_CALL if
1906 * this is true. x(y).z gets JSOP_CALL, not JSOP_GETPROP.
1908 #define PROPAGATE_CALLNESS() \
1910 if (ss->opcodes[ss->top - 1] == JSOP_CALL) \
1911 saveop = JSOP_CALL; \
1914 cx
= ss
->sprinter
.context
;
1915 JS_CHECK_RECURSION(cx
, return NULL
);
1919 endpc
= (nb
< 0) ? jp
->script
->code
+ jp
->script
->length
: pc
+ nb
;
1921 todo
= -2; /* NB: different from Sprint() error return. */
1925 #if JS_HAS_XML_SUPPORT
1926 foreach
= inXML
= quoteAttr
= JS_FALSE
;
1929 while (nb
< 0 || pc
< endpc
) {
1931 * Move saveop to lastop so prefixed bytecodes can take special action
1932 * while sharing maximal code. Set op and saveop to the new bytecode,
1933 * use op in POP_STR to trigger automatic parenthesization, but push
1934 * saveop at the bottom of the loop if this op pushes. Thus op may be
1935 * set to nop or otherwise mutated to suppress auto-parens.
1939 cs
= &js_CodeSpec
[op
];
1940 if (cs
->format
& JOF_INDEXBASE
) {
1942 * The decompiler uses js_GetIndexFromBytecode to get atoms and
1943 * objects and ignores these suffix/prefix bytecodes, thus
1944 * simplifying code that must process JSOP_GETTER/JSOP_SETTER
1951 cs
= &js_CodeSpec
[op
];
1954 len
= oplen
= cs
->length
;
1955 nuses
= js_GetStackUses(cs
, op
, pc
);
1958 * Here it is possible that nuses > ss->top when the op has a hidden
1959 * source note. But when nb < 0 we assume that the caller knows that
1960 * Decompile would never meet such opcodes.
1963 LOCAL_ASSERT(ss
->top
>= nuses
);
1964 uintN ndefs
= js_GetStackDefs(cx
, cs
, op
, jp
->script
, pc
);
1965 if ((uintN
) -(nb
+ 1) == ss
->top
- nuses
+ ndefs
)
1970 * Save source literal associated with JS now before the following
1971 * rewrite changes op. See bug 380197.
1973 token
= CodeToken
[op
];
1975 if (pc
+ oplen
== jp
->dvgfence
) {
1977 uint32 format
, mode
, type
;
1980 * Rewrite non-get ops to their "get" format if the error is in
1981 * the bytecode at pc, so we don't decompile more than the error
1984 fp
= js_GetScriptedCaller(cx
, NULL
);
1985 format
= cs
->format
;
1986 if (((fp
&& fp
->regs
&& pc
== fp
->regs
->pc
) ||
1987 (pc
== startpc
&& nuses
!= 0)) &&
1988 format
& (JOF_SET
|JOF_DEL
|JOF_INCDEC
|JOF_FOR
|JOF_VARPROP
)) {
1989 mode
= JOF_MODE(format
);
1990 if (mode
== JOF_NAME
) {
1992 * JOF_NAME does not imply JOF_ATOM, so we must check for
1993 * the QARG and QVAR format types, and translate those to
1994 * JSOP_GETARG or JSOP_GETLOCAL appropriately, instead of
1997 type
= JOF_TYPE(format
);
1998 op
= (type
== JOF_QARG
)
2000 : (type
== JOF_LOCAL
)
2004 JS_ASSERT(js_CodeSpec
[op
].nuses
>= 0);
2005 i
= nuses
- js_CodeSpec
[op
].nuses
;
2007 PopOff(ss
, JSOP_NOP
);
2010 * We must replace the faulting pc's bytecode with a
2011 * corresponding JSOP_GET* code. For JSOP_SET{PROP,ELEM},
2012 * we must use the "2nd" form of JSOP_GET{PROP,ELEM}, to
2013 * throw away the assignment op's right-hand operand and
2014 * decompile it as if it were a GET of its left-hand
2017 if (mode
== JOF_PROP
) {
2018 op
= (JSOp
) ((format
& JOF_SET
)
2021 } else if (mode
== JOF_ELEM
) {
2022 op
= (JSOp
) ((format
& JOF_SET
)
2027 * Unknown mode (including mode 0) means that op is
2028 * uncategorized for our purposes, so we must write
2029 * per-op special case code here.
2033 case JSOP_ENUMCONSTELEM
:
2039 case JSOP_GETTHISPROP
:
2041 * NB: JSOP_GETTHISPROP can't fail due to |this|
2042 * being null or undefined at runtime (beware that
2043 * this may change for ES4). Therefore any error
2044 * resulting from this op must be due to the value
2045 * of the property accessed via |this|, so do not
2046 * rewrite op to JSOP_THIS.
2048 * The next two cases should not change op if
2049 * js_DecompileValueGenerator was called from the
2050 * the property getter. They should rewrite only
2051 * if the base object in the arg/var/local is null
2052 * or undefined. FIXME: bug 431569.
2055 case JSOP_GETARGPROP
:
2058 case JSOP_GETLOCALPROP
:
2069 if (op
>= JSOP_LIMIT
) {
2072 saveop
= JSOP_GETPROP
;
2075 saveop
= JSOP_GETELEM
;
2080 LOCAL_ASSERT(js_CodeSpec
[saveop
].length
== oplen
||
2081 JOF_TYPE(format
) == JOF_SLOTATOM
);
2083 jp
->dvgfence
= NULL
;
2089 sn
= js_GetSrcNote(jp
->script
, pc
);
2090 if (sn
&& SN_TYPE(sn
) == SRC_ASSIGNOP
) {
2092 * Avoid over-parenthesizing y in x op= y based on its
2093 * expansion: x = x op y (replace y by z = w to see the
2096 op
= (JSOp
) pc
[oplen
];
2099 /* Print only the right operand of the assignment-op. */
2100 todo
= SprintCString(&ss
->sprinter
, rval
);
2102 } else if (!inXML
) {
2103 rval
= POP_STR_PREC(cs
->prec
+ !!(cs
->format
& JOF_LEFTASSOC
));
2104 lval
= POP_STR_PREC(cs
->prec
+ !(cs
->format
& JOF_LEFTASSOC
));
2105 todo
= Sprint(&ss
->sprinter
, "%s %s %s",
2108 /* In XML, just concatenate the two operands. */
2109 LOCAL_ASSERT(op
== JSOP_ADD
);
2112 todo
= Sprint(&ss
->sprinter
, ss_format
, lval
, rval
);
2118 todo
= Sprint(&ss
->sprinter
, ss_format
, token
, rval
);
2122 todo
= SprintCString(&ss
->sprinter
, token
);
2133 * Check for a do-while loop, a for-loop with an empty
2134 * initializer part, a labeled statement, a function
2135 * definition, or try/finally.
2137 sn
= js_GetSrcNote(jp
->script
, pc
);
2139 switch (sn
? SN_TYPE(sn
) : SRC_NULL
) {
2142 tail
= js_GetSrcNoteOffset(sn
, 0) - 1;
2143 LOCAL_ASSERT(pc
[tail
] == JSOP_IFNE
||
2144 pc
[tail
] == JSOP_IFNEX
);
2145 js_printf(jp
, "\tdo {\n");
2147 DECOMPILE_CODE(pc
, tail
);
2149 js_printf(jp
, "\t} while (%s);\n", POP_COND_STR());
2151 len
= js_CodeSpec
[*pc
].length
;
2159 JS_ASSERT(SN_TYPE(sn
) == SRC_FOR
);
2161 /* Skip the JSOP_NOP or JSOP_POP bytecode. */
2162 pc
+= JSOP_NOP_LENGTH
;
2164 /* Get the cond, next, and loop-closing tail offsets. */
2165 cond
= js_GetSrcNoteOffset(sn
, 0);
2166 next
= js_GetSrcNoteOffset(sn
, 1);
2167 tail
= js_GetSrcNoteOffset(sn
, 2);
2170 * If this loop has a condition, then pc points at a goto
2171 * targeting the condition.
2175 LOCAL_ASSERT(*pc
== JSOP_GOTO
|| *pc
== JSOP_GOTOX
);
2176 pc2
+= (*pc
== JSOP_GOTO
) ? JSOP_GOTO_LENGTH
: JSOP_GOTOX_LENGTH
;
2178 LOCAL_ASSERT(tail
+ GetJumpOffset(pc
+tail
, pc
+tail
) == pc2
- pc
);
2180 /* Print the keyword and the possibly empty init-part. */
2181 js_printf(jp
, "\tfor (%s;", rval
);
2184 /* Decompile the loop condition. */
2185 DECOMPILE_CODE(pc
+ cond
, tail
- cond
);
2186 js_printf(jp
, " %s", POP_STR());
2189 /* Need a semicolon whether or not there was a cond. */
2194 * Decompile the loop updater. It may end in a JSOP_POP
2195 * that we skip; or in a JSOP_POPN that we do not skip,
2196 * followed by a JSOP_NOP (skipped as if it's a POP).
2197 * We cope with the difference between these two cases
2198 * by checking for stack imbalance and popping if there
2201 uintN saveTop
= ss
->top
;
2203 DECOMPILE_CODE(pc
+ next
, cond
- next
- JSOP_POP_LENGTH
);
2204 LOCAL_ASSERT(ss
->top
- saveTop
<= 1U);
2205 rval
= (ss
->top
== saveTop
)
2206 ? ss
->sprinter
.base
+ ss
->sprinter
.offset
2208 js_printf(jp
, " %s", rval
);
2211 /* Do the loop body. */
2212 js_printf(jp
, ") {\n");
2215 DECOMPILE_CODE(pc2
, next
);
2217 js_printf(jp
, "\t}\n");
2219 /* Set len so pc skips over the entire loop. */
2220 len
= tail
+ js_CodeSpec
[pc
[tail
]].length
;
2224 GET_SOURCE_NOTE_ATOM(sn
, atom
);
2226 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
2229 RETRACT(&ss
->sprinter
, rval
);
2230 js_printf(jp
, "\t%s:\n", rval
);
2234 case SRC_LABELBRACE
:
2235 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
);
2246 js_printf(jp
, "\t}\n");
2250 fun
= jp
->script
->getFunction(js_GetSrcNoteOffset(sn
, 0));
2253 jp2
= js_NewPrinter(cx
, "nested_function", fun
,
2254 jp
->indent
, jp
->pretty
, jp
->grouped
,
2258 ok
= js_DecompileFunction(jp2
);
2259 if (ok
&& jp2
->sprinter
.base
)
2260 js_puts(jp
, jp2
->sprinter
.base
);
2261 js_DestroyPrinter(jp2
);
2264 js_puts(jp
, "\n\n");
2268 js_printf(jp
, "\t{\n");
2270 len
= js_GetSrcNoteOffset(sn
, 0);
2271 DECOMPILE_CODE(pc
+ oplen
, len
- oplen
);
2273 js_printf(jp
, "\t}\n");
2281 #if JS_HAS_DESTRUCTURING
2282 sn
= js_GetSrcNote(jp
->script
, pc
);
2283 if (sn
&& SN_TYPE(sn
) == SRC_GROUPASSIGN
) {
2284 pc
= DecompileGroupAssignment(ss
, pc
, endpc
, sn
, &todo
);
2287 LOCAL_ASSERT(*pc
== JSOP_POPN
);
2288 len
= oplen
= JSOP_POPN_LENGTH
;
2289 goto end_groupassignment
;
2295 todo
= Sprint(&ss
->sprinter
, "");
2299 js_printf(jp
, "\ttry {\n");
2306 js_printf(jp
, "\t} finally {\n");
2310 * We push push the pair of exception/restsub cookies to
2311 * simulate the effects [gosub] or control transfer during
2312 * exception capturing on the stack.
2314 todo
= Sprint(&ss
->sprinter
, exception_cookie
);
2315 if (todo
< 0 || !PushOff(ss
, todo
, op
))
2317 todo
= Sprint(&ss
->sprinter
, retsub_pc_cookie
);
2322 LOCAL_ASSERT(strcmp(rval
, retsub_pc_cookie
) == 0);
2324 LOCAL_ASSERT(strcmp(lval
, exception_cookie
) == 0);
2331 * JSOP_GOSUB and GOSUBX have no effect on the decompiler's
2332 * string stack because the next op in bytecode order finds
2333 * the stack balanced by a JSOP_RETSUB executed elsewhere.
2340 uintN newtop
, oldtop
;
2343 * The compiler models operand stack depth and fixes the stack
2344 * pointer on entry to a catch clause based on its depth model.
2345 * The decompiler must match the code generator's model, which
2346 * is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops.
2349 newtop
= oldtop
- GET_UINT16(pc
);
2350 LOCAL_ASSERT(newtop
<= oldtop
);
2353 sn
= js_GetSrcNote(jp
->script
, pc
);
2354 if (sn
&& SN_TYPE(sn
) == SRC_HIDDEN
)
2356 #if JS_HAS_DESTRUCTURING
2357 if (sn
&& SN_TYPE(sn
) == SRC_GROUPASSIGN
) {
2358 todo
= Sprint(&ss
->sprinter
, "%s[] = [",
2362 for (uintN i
= newtop
; i
< oldtop
; i
++) {
2363 rval
= OFF2STR(&ss
->sprinter
, ss
->offsets
[i
]);
2364 if (Sprint(&ss
->sprinter
, ss_format
,
2365 (i
== newtop
) ? "" : ", ",
2366 (i
== oldtop
- 1 && *rval
== '\0')
2367 ? ", " : rval
) < 0) {
2371 if (SprintPut(&ss
->sprinter
, "]", 1) < 0)
2375 * If this is an empty group assignment, we have no stack
2376 * budget into which we can push our result string. Adjust
2377 * ss->sprinter.offset so that our consumer can find the
2378 * empty group assignment decompilation.
2380 if (newtop
== oldtop
) {
2381 ss
->sprinter
.offset
= todo
;
2384 * Kill newtop before the end_groupassignment: label by
2385 * retracting/popping early. Control will either jump
2386 * to do_forloop: or do_letheadbody: or else break from
2387 * our case JSOP_POPN: after the switch (*pc2) below.
2389 LOCAL_ASSERT(newtop
< oldtop
);
2390 ss
->sprinter
.offset
= GetOff(ss
, newtop
);
2394 end_groupassignment
:
2395 LOCAL_ASSERT(*pc
== JSOP_POPN
);
2398 * Thread directly to the next opcode if we can, to handle
2399 * the special cases of a group assignment in the first or
2400 * last part of a for(;;) loop head, or in a let block or
2403 * NB: todo at this point indexes space in ss->sprinter
2404 * that is liable to be overwritten. The code below knows
2405 * exactly how long rval lives, or else copies it down via
2408 rval
= OFF2STR(&ss
->sprinter
, todo
);
2411 if (*pc2
== JSOP_NOP
) {
2412 sn
= js_GetSrcNote(jp
->script
, pc2
);
2414 if (SN_TYPE(sn
) == SRC_FOR
) {
2420 if (SN_TYPE(sn
) == SRC_DECL
) {
2421 if (ss
->top
== StackDepth(jp
->script
)) {
2423 * This must be an empty destructuring
2424 * in the head of a let whose body block
2427 pc
= pc2
+ JSOP_NOP_LENGTH
;
2428 len
= js_GetSrcNoteOffset(sn
, 0);
2429 LOCAL_ASSERT(pc
[len
] == JSOP_LEAVEBLOCK
);
2430 js_printf(jp
, "\tlet (%s) {\n", rval
);
2431 js_printf(jp
, "\t}\n");
2434 todo
= SprintCString(&ss
->sprinter
, rval
);
2435 if (todo
< 0 || !PushOff(ss
, todo
, JSOP_NOP
))
2438 pc
= pc2
+ JSOP_NOP_LENGTH
;
2439 goto do_letheadbody
;
2443 * An unnannotated NOP following a POPN must be the
2444 * third part of for(;;) loop head. If the POPN's
2445 * immediate operand is 0, then we may have no slot
2446 * on the sprint-stack in which to push our result
2447 * string. In this case the result can be recovered
2448 * at ss->sprinter.base + ss->sprinter.offset.
2450 if (GET_UINT16(pc
) == 0)
2452 todo
= SprintCString(&ss
->sprinter
, rval
);
2458 * If control flow reaches this point with todo still -2,
2459 * just print rval as an expression statement.
2462 js_printf(jp
, "\t%s;\n", rval
);
2466 if (newtop
< oldtop
) {
2467 ss
->sprinter
.offset
= GetOff(ss
, newtop
);
2473 case JSOP_EXCEPTION
:
2474 /* The catch decompiler handles this op itself. */
2475 LOCAL_ASSERT(JS_FALSE
);
2480 * By default, do not automatically parenthesize when popping
2481 * a stacked expression decompilation. We auto-parenthesize
2482 * only when JSOP_POP is annotated with SRC_PCDELTA, meaning
2489 sn
= js_GetSrcNote(jp
->script
, pc
);
2490 switch (sn
? SN_TYPE(sn
) : SRC_NULL
) {
2492 /* Force parens around 'in' expression at 'for' front. */
2493 if (ss
->opcodes
[ss
->top
-1] == JSOP_IN
)
2500 /* Comma operator: use JSOP_POP for correct precedence. */
2503 /* Pop and save to avoid blowing stack depth budget. */
2504 lval
= JS_strdup(cx
, POP_STR());
2509 * The offset tells distance to the end of the right-hand
2510 * operand of the comma operator.
2513 pc
+= js_GetSrcNoteOffset(sn
, 0);
2516 if (!Decompile(ss
, done
, pc
- done
, JSOP_POP
)) {
2517 cx
->free((char *)lval
);
2521 /* Pop Decompile result and print comma expression. */
2523 todo
= Sprint(&ss
->sprinter
, "%s, %s", lval
, rval
);
2524 cx
->free((char *)lval
);
2529 * Hide this pop. Don't adjust our stack depth model if
2530 * it's from a goto in a with or for/in.
2533 if (lastop
== JSOP_UNBRAND
)
2538 /* This pop is at the end of the let block/expr head. */
2539 pc
+= JSOP_POP_LENGTH
;
2540 #if JS_HAS_DESTRUCTURING
2543 len
= js_GetSrcNoteOffset(sn
, 0);
2544 if (pc
[len
] == JSOP_LEAVEBLOCK
) {
2545 js_printf(jp
, "\tlet (%s) {\n", POP_STR());
2547 DECOMPILE_CODE(pc
, len
);
2549 js_printf(jp
, "\t}\n");
2552 LOCAL_ASSERT(pc
[len
] == JSOP_LEAVEBLOCKEXPR
);
2554 lval
= JS_strdup(cx
, PopStr(ss
, JSOP_NOP
));
2558 /* Set saveop to reflect what we will push. */
2559 saveop
= JSOP_LEAVEBLOCKEXPR
;
2560 if (!Decompile(ss
, pc
, len
, saveop
)) {
2561 cx
->free((char *)lval
);
2564 rval
= PopStr(ss
, JSOP_SETNAME
);
2565 todo
= Sprint(&ss
->sprinter
,
2570 cx
->free((char *)lval
);
2575 /* Turn off parens around a yield statement. */
2576 if (ss
->opcodes
[ss
->top
-1] == JSOP_YIELD
)
2582 * Don't emit decompiler-pushed strings that are not
2583 * handled by other opcodes. They are pushed onto the
2584 * stack to help model the interpreter stack and should
2585 * not appear in the decompiler's output.
2587 if (*rval
!= '\0' && (rval
[0] != '/' || rval
[1] != '*')) {
2590 (strncmp(rval
, js_function_str
, 8) == 0 &&
2596 LOCAL_ASSERT(*rval
== '\0' ||
2597 strcmp(rval
, exception_cookie
) == 0);
2605 case JSOP_ENTERWITH
:
2606 LOCAL_ASSERT(!js_GetSrcNote(jp
->script
, pc
));
2608 js_printf(jp
, "\twith (%s) {\n", rval
);
2610 todo
= Sprint(&ss
->sprinter
, with_cookie
);
2613 case JSOP_LEAVEWITH
:
2614 sn
= js_GetSrcNote(jp
->script
, pc
);
2616 if (sn
&& SN_TYPE(sn
) == SRC_HIDDEN
)
2619 LOCAL_ASSERT(strcmp(rval
, with_cookie
) == 0);
2621 js_printf(jp
, "\t}\n");
2624 case JSOP_ENTERBLOCK
:
2626 JSAtom
**atomv
, *smallv
[5];
2627 JSScopeProperty
*sprop
;
2630 argc
= OBJ_BLOCK_COUNT(cx
, obj
);
2631 if ((size_t)argc
<= JS_ARRAY_LENGTH(smallv
)) {
2634 atomv
= (JSAtom
**) cx
->malloc(argc
* sizeof(JSAtom
*));
2639 MUST_FLOW_THROUGH("enterblock_out");
2640 #define LOCAL_ASSERT_OUT(expr) LOCAL_ASSERT_CUSTOM(expr, ok = JS_FALSE; \
2641 goto enterblock_out)
2642 for (sprop
= OBJ_SCOPE(obj
)->lastProperty(); sprop
;
2643 sprop
= sprop
->parent
) {
2644 if (!(sprop
->flags
& SPROP_HAS_SHORTID
))
2646 LOCAL_ASSERT_OUT(sprop
->shortid
< argc
);
2647 atomv
[sprop
->shortid
] = JSID_TO_ATOM(sprop
->id
);
2650 for (i
= 0; i
< argc
; i
++) {
2652 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
2654 !PushOff(ss
, STR2OFF(&ss
->sprinter
, rval
), op
)) {
2656 goto enterblock_out
;
2660 sn
= js_GetSrcNote(jp
->script
, pc
);
2661 switch (sn
? SN_TYPE(sn
) : SRC_NULL
) {
2662 #if JS_HAS_BLOCK_SCOPE
2664 js_printf(jp
, "\t{\n");
2666 len
= js_GetSrcNoteOffset(sn
, 0);
2667 ok
= Decompile(ss
, pc
+ oplen
, len
- oplen
, JSOP_NOP
)
2670 goto enterblock_out
;
2672 js_printf(jp
, "\t}\n");
2678 js_printf(jp
, "\t} catch (");
2682 LOCAL_ASSERT_OUT(*pc
== JSOP_EXCEPTION
);
2683 pc
+= JSOP_EXCEPTION_LENGTH
;
2684 todo
= Sprint(&ss
->sprinter
, exception_cookie
);
2685 if (todo
< 0 || !PushOff(ss
, todo
, JSOP_EXCEPTION
)) {
2687 goto enterblock_out
;
2690 if (*pc
== JSOP_DUP
) {
2691 sn2
= js_GetSrcNote(jp
->script
, pc
);
2692 if (!sn2
|| SN_TYPE(sn2
) != SRC_DESTRUCT
) {
2694 * This is a dup to save the exception for later.
2695 * It is emitted only when the catch head contains
2696 * an exception guard.
2698 LOCAL_ASSERT_OUT(js_GetSrcNoteOffset(sn
, 0) != 0);
2699 pc
+= JSOP_DUP_LENGTH
;
2700 todo
= Sprint(&ss
->sprinter
, exception_cookie
);
2702 !PushOff(ss
, todo
, JSOP_EXCEPTION
)) {
2704 goto enterblock_out
;
2709 #if JS_HAS_DESTRUCTURING
2710 if (*pc
== JSOP_DUP
) {
2711 pc
= DecompileDestructuring(ss
, pc
, endpc
);
2714 goto enterblock_out
;
2716 LOCAL_ASSERT_OUT(*pc
== JSOP_POP
);
2717 pc
+= JSOP_POP_LENGTH
;
2718 lval
= PopStr(ss
, JSOP_NOP
);
2722 LOCAL_ASSERT_OUT(*pc
== JSOP_SETLOCALPOP
);
2723 i
= GET_SLOTNO(pc
) - jp
->script
->nfixed
;
2724 pc
+= JSOP_SETLOCALPOP_LENGTH
;
2725 atom
= atomv
[i
- OBJ_BLOCK_DEPTH(cx
, obj
)];
2726 str
= ATOM_TO_STRING(atom
);
2727 if (!QuoteString(&jp
->sprinter
, str
, 0)) {
2729 goto enterblock_out
;
2731 #if JS_HAS_DESTRUCTURING
2736 * Pop the exception_cookie (or its dup in the case of a
2737 * guarded catch head) off the stack now.
2739 rval
= PopStr(ss
, JSOP_NOP
);
2740 LOCAL_ASSERT_OUT(strcmp(rval
, exception_cookie
) == 0);
2742 len
= js_GetSrcNoteOffset(sn
, 0);
2745 LOCAL_ASSERT_OUT(len
> 0);
2746 js_printf(jp
, " if ");
2747 ok
= Decompile(ss
, pc
, len
, JSOP_NOP
) != NULL
;
2749 goto enterblock_out
;
2750 js_printf(jp
, "%s", POP_STR());
2752 LOCAL_ASSERT_OUT(*pc
== JSOP_IFEQ
|| *pc
== JSOP_IFEQX
);
2753 pc
+= js_CodeSpec
[*pc
].length
;
2756 js_printf(jp
, ") {\n");
2766 #undef LOCAL_ASSERT_OUT
2768 if (atomv
!= smallv
)
2775 case JSOP_LEAVEBLOCK
:
2776 case JSOP_LEAVEBLOCKEXPR
:
2780 sn
= js_GetSrcNote(jp
->script
, pc
);
2782 if (op
== JSOP_LEAVEBLOCKEXPR
) {
2783 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_PCBASE
);
2786 LOCAL_ASSERT(op
== JSOP_LEAVEBLOCK
);
2787 if (SN_TYPE(sn
) == SRC_HIDDEN
)
2791 * This JSOP_LEAVEBLOCK must be for a catch block. If sn's
2792 * offset does not equal the model stack depth, there must
2793 * be a copy of the exception value on the stack due to a
2794 * catch guard (see above, the JSOP_ENTERBLOCK + SRC_CATCH
2797 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_CATCH
);
2798 if ((uintN
)js_GetSrcNoteOffset(sn
, 0) != ss
->top
) {
2799 LOCAL_ASSERT((uintN
)js_GetSrcNoteOffset(sn
, 0)
2802 LOCAL_ASSERT(strcmp(rval
, exception_cookie
) == 0);
2806 depth
= GET_UINT16(pc
);
2807 LOCAL_ASSERT(top
>= depth
);
2810 ss
->sprinter
.offset
= GetOff(ss
, top
);
2811 if (op
== JSOP_LEAVEBLOCKEXPR
)
2812 todo
= SprintCString(&ss
->sprinter
, rval
);
2817 case JSOP_CALLUPVAR
:
2818 case JSOP_GETUPVAR_DBG
:
2819 case JSOP_CALLUPVAR_DBG
:
2821 case JSOP_CALLDSLOT
:
2824 JS_ASSERT(jp
->script
->savedCallerFun
);
2825 jp
->fun
= jp
->script
->getFunction(0);
2828 if (!jp
->localNames
)
2829 jp
->localNames
= js_GetLocalNameArray(cx
, jp
->fun
, &jp
->pool
);
2831 uintN index
= GET_UINT16(pc
);
2832 if (index
< jp
->fun
->u
.i
.nupvars
) {
2833 index
+= jp
->fun
->countArgsAndVars();
2838 * We must be in an eval called from jp->fun, where
2839 * jp->script is the eval-compiled script.
2841 * However, it's possible that a js_Invoke already
2842 * pushed a frame trying to call js_Construct on an
2843 * object that's not a constructor, causing us to be
2844 * called with an intervening frame on the stack.
2846 JSStackFrame
*fp
= js_GetTopStackFrame(cx
);
2848 while (!(fp
->flags
& JSFRAME_EVAL
))
2850 JS_ASSERT(fp
->script
== jp
->script
);
2851 JS_ASSERT(fp
->down
->fun
== jp
->fun
);
2852 JS_ASSERT(FUN_INTERPRETED(jp
->fun
));
2853 JS_ASSERT(jp
->script
!= jp
->fun
->u
.i
.script
);
2854 JS_ASSERT(jp
->script
->upvarsOffset
!= 0);
2857 uva
= jp
->script
->upvars();
2858 index
= UPVAR_FRAME_SLOT(uva
->vector
[index
]);
2860 atom
= GetArgOrVarAtom(jp
, index
);
2864 case JSOP_CALLLOCAL
:
2866 if (IsVarSlot(jp
, pc
, &i
)) {
2867 atom
= GetArgOrVarAtom(jp
, i
);
2871 LOCAL_ASSERT((uintN
)i
< ss
->top
);
2872 sn
= js_GetSrcNote(jp
->script
, pc
);
2874 #if JS_HAS_DESTRUCTURING
2875 if (sn
&& SN_TYPE(sn
) == SRC_GROUPASSIGN
) {
2877 * Distinguish a js_DecompileValueGenerator call that
2878 * targets op alone, from decompilation of a full group
2879 * assignment sequence, triggered by SRC_GROUPASSIGN
2880 * annotating the first JSOP_GETLOCAL in the sequence.
2882 if (endpc
- pc
> JSOP_GETLOCAL_LENGTH
|| pc
> startpc
) {
2883 pc
= DecompileGroupAssignment(ss
, pc
, endpc
, sn
, &todo
);
2886 LOCAL_ASSERT(*pc
== JSOP_POPN
);
2887 len
= oplen
= JSOP_POPN_LENGTH
;
2888 goto end_groupassignment
;
2891 /* Null sn to prevent bogus VarPrefix'ing below. */
2896 rval
= GetLocal(ss
, i
);
2897 todo
= Sprint(&ss
->sprinter
, ss_format
, VarPrefix(sn
), rval
);
2901 case JSOP_SETLOCALPOP
:
2902 if (IsVarSlot(jp
, pc
, &i
)) {
2903 atom
= GetArgOrVarAtom(jp
, i
);
2907 lval
= GetLocal(ss
, i
);
2913 if (IsVarSlot(jp
, pc
, &i
)) {
2914 atom
= GetArgOrVarAtom(jp
, i
);
2918 lval
= GetLocal(ss
, i
);
2923 if (IsVarSlot(jp
, pc
, &i
)) {
2924 atom
= GetArgOrVarAtom(jp
, i
);
2928 lval
= GetLocal(ss
, i
);
2936 LOCAL_ASSERT(jp
->fun
);
2938 if (fun
->flags
& JSFUN_EXPR_CLOSURE
) {
2939 /* Turn on parens around comma-expression here. */
2942 js_printf(jp
, (*rval
== '{') ? "(%s)%s" : ss_format
,
2944 ((fun
->flags
& JSFUN_LAMBDA
) || !fun
->atom
)
2955 js_printf(jp
, "\t%s %s;\n", js_return_str
, rval
);
2957 js_printf(jp
, "\t%s;\n", js_return_str
);
2961 #if JS_HAS_GENERATORS
2963 #if JS_HAS_GENERATOR_EXPRS
2964 if (!ss
->inGenExp
|| !(sn
= js_GetSrcNote(jp
->script
, pc
)))
2967 /* Turn off most parens. */
2970 todo
= (*rval
!= '\0')
2971 ? Sprint(&ss
->sprinter
,
2972 (strncmp(rval
, js_yield_str
, 5) == 0 &&
2973 (rval
[5] == ' ' || rval
[5] == '\0'))
2977 : SprintCString(&ss
->sprinter
, js_yield_str
);
2981 #if JS_HAS_GENERATOR_EXPRS
2982 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_HIDDEN
);
2986 case JSOP_ARRAYPUSH
:
2991 /* Turn off most parens. */
2994 /* Pop the expression being pushed or yielded. */
2998 * Skip the for loop head stacked by JSOP_FORLOCAL until we hit
2999 * a block local slot (note empty destructuring patterns result
3000 * in unit-count blocks).
3004 op
= (JSOp
) ss
->opcodes
[--pos
];
3005 if (op
== JSOP_ENTERBLOCK
)
3008 JS_ASSERT(op
== JSOP_ENTERBLOCK
);
3011 * Here, forpos must index the space before the left-most |for|
3012 * in the single string of accumulated |for| heads and optional
3013 * final |if (condition)|.
3016 LOCAL_ASSERT(forpos
< ss
->top
);
3019 * Now move pos downward over the block's local slots. Even an
3020 * empty destructuring pattern has one (dummy) local.
3022 while (ss
->opcodes
[pos
] == JSOP_ENTERBLOCK
) {
3028 #if JS_HAS_GENERATOR_EXPRS
3029 if (saveop
== JSOP_YIELD
) {
3031 * Generator expression: decompile just rval followed by
3032 * the string starting at forpos. Leave the result string
3033 * in ss->offsets[0] so it can be recovered by our caller
3034 * (the JSOP_ANONFUNOBJ with SRC_GENEXP case). Bump the
3035 * top of stack to balance yield, which is an expression
3036 * (so has neutral stack balance).
3038 LOCAL_ASSERT(pos
== 0);
3039 xval
= OFF2STR(&ss
->sprinter
, ss
->offsets
[forpos
]);
3040 ss
->sprinter
.offset
= PAREN_SLOP
;
3041 todo
= Sprint(&ss
->sprinter
, ss_format
, rval
, xval
);
3044 ss
->offsets
[0] = todo
;
3048 #endif /* JS_HAS_GENERATOR_EXPRS */
3051 * Array comprehension: retract the sprinter to the beginning
3052 * of the array initialiser and decompile "[<rval> for ...]".
3054 JS_ASSERT(jp
->script
->nfixed
+ pos
== GET_UINT16(pc
));
3055 LOCAL_ASSERT(ss
->opcodes
[pos
] == JSOP_NEWINIT
);
3057 start
= ss
->offsets
[pos
];
3058 LOCAL_ASSERT(ss
->sprinter
.base
[start
] == '[' ||
3059 ss
->sprinter
.base
[start
] == '#');
3060 LOCAL_ASSERT(forpos
< ss
->top
);
3061 xval
= OFF2STR(&ss
->sprinter
, ss
->offsets
[forpos
]);
3062 lval
= OFF2STR(&ss
->sprinter
, start
);
3063 RETRACT(&ss
->sprinter
, lval
);
3065 todo
= Sprint(&ss
->sprinter
, sss_format
, lval
, rval
, xval
);
3068 ss
->offsets
[pos
] = todo
;
3072 #endif /* JS_HAS_GENERATORS */
3079 sn
= js_GetSrcNote(jp
->script
, pc
);
3081 if (sn
&& SN_TYPE(sn
) == SRC_HIDDEN
)
3084 js_printf(jp
, "\t%s %s;\n", js_throw_str
, rval
);
3088 foreach
= (pc
[1] & (JSITER_FOREACH
| JSITER_KEYVALUE
)) ==
3090 todo
= SprintCString(&ss
->sprinter
, iter_cookie
);
3094 JS_NOT_REACHED("JSOP_NEXTITER");
3098 sn
= js_GetSrcNote(jp
->script
, pc
);
3100 if (sn
&& SN_TYPE(sn
) == SRC_HIDDEN
)
3102 (void) PopOff(ss
, op
);
3103 (void) PopOff(ss
, op
);
3108 sn
= js_GetSrcNote(jp
->script
, pc
);
3109 switch (sn
? SN_TYPE(sn
) : SRC_NULL
) {
3112 * The loop back-edge carries +1 stack balance, for the
3113 * flag processed by JSOP_IFNE. We do not decompile the
3114 * JSOP_IFNE, and instead push the left-hand side of 'in'
3115 * after the loop edge in this stack slot (the JSOP_FOR*
3116 * opcodes' decompilers do this pushing).
3118 cond
= GetJumpOffset(pc
, pc
);
3119 next
= js_GetSrcNoteOffset(sn
, 0);
3120 tail
= js_GetSrcNoteOffset(sn
, 1);
3121 JS_ASSERT(pc
[cond
] == JSOP_NEXTITER
);
3122 DECOMPILE_CODE(pc
+ oplen
, next
- oplen
);
3124 LOCAL_ASSERT(ss
->top
>= 2);
3126 if (ss
->inArrayInit
|| ss
->inGenExp
) {
3127 (void) PopOff(ss
, JSOP_NOP
);
3129 if (ss
->top
>= 2 && ss
->opcodes
[ss
->top
- 2] == JSOP_FORLOCAL
) {
3130 ss
->sprinter
.offset
= ss
->offsets
[ss
->top
- 1] - PAREN_SLOP
;
3131 if (Sprint(&ss
->sprinter
, " %s (%s in %s)",
3132 foreach
? js_for_each_str
: js_for_str
,
3138 * Do not AddParentSlop here, as we will push the
3139 * top-most offset again, which will add paren slop
3140 * for us. We must push to balance the stack budget
3141 * when nesting for heads in a comprehension.
3143 todo
= ss
->offsets
[ss
->top
- 1];
3145 todo
= Sprint(&ss
->sprinter
, " %s (%s in %s)",
3146 foreach
? js_for_each_str
: js_for_str
,
3149 if (todo
< 0 || !PushOff(ss
, todo
, JSOP_FORLOCAL
))
3151 DECOMPILE_CODE(pc
+ next
, cond
- next
);
3154 * As above, rval or an extension of it must remain
3155 * stacked during loop body decompilation.
3157 rval
= GetStr(ss
, ss
->top
- 2);
3158 js_printf(jp
, "\t%s (%s in %s) {\n",
3159 foreach
? js_for_each_str
: js_for_str
,
3162 DECOMPILE_CODE(pc
+ next
, cond
- next
);
3164 js_printf(jp
, "\t}\n");
3168 LOCAL_ASSERT(*pc
== JSOP_IFNE
|| *pc
== JSOP_IFNEX
);
3169 len
= js_CodeSpec
[*pc
].length
;
3173 cond
= GetJumpOffset(pc
, pc
);
3174 tail
= js_GetSrcNoteOffset(sn
, 0);
3175 DECOMPILE_CODE(pc
+ cond
, tail
- cond
);
3176 js_printf(jp
, "\twhile (%s) {\n", POP_COND_STR());
3178 DECOMPILE_CODE(pc
+ oplen
, cond
- oplen
);
3180 js_printf(jp
, "\t}\n");
3182 LOCAL_ASSERT(*pc
== JSOP_IFNE
|| *pc
== JSOP_IFNEX
);
3183 len
= js_CodeSpec
[*pc
].length
;
3187 case SRC_CONT2LABEL
:
3188 GET_SOURCE_NOTE_ATOM(sn
, atom
);
3189 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3192 RETRACT(&ss
->sprinter
, rval
);
3193 js_printf(jp
, "\tcontinue %s;\n", rval
);
3197 js_printf(jp
, "\tcontinue;\n");
3200 case SRC_BREAK2LABEL
:
3201 GET_SOURCE_NOTE_ATOM(sn
, atom
);
3202 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3205 RETRACT(&ss
->sprinter
, rval
);
3206 js_printf(jp
, "\tbreak %s;\n", rval
);
3213 js_printf(jp
, "\tbreak;\n");
3222 JSBool elseif
= JS_FALSE
;
3225 len
= GetJumpOffset(pc
, pc
);
3226 sn
= js_GetSrcNote(jp
->script
, pc
);
3228 switch (sn
? SN_TYPE(sn
) : SRC_NULL
) {
3231 rval
= POP_COND_STR();
3232 if (ss
->inArrayInit
|| ss
->inGenExp
) {
3233 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_IF
);
3234 ss
->sprinter
.offset
-= PAREN_SLOP
;
3235 if (Sprint(&ss
->sprinter
, " if (%s)", rval
) < 0)
3240 elseif
? " if (%s) {\n" : "\tif (%s) {\n",
3245 if (SN_TYPE(sn
) == SRC_IF
) {
3246 DECOMPILE_CODE(pc
+ oplen
, len
- oplen
);
3248 LOCAL_ASSERT(!ss
->inArrayInit
&& !ss
->inGenExp
);
3249 tail
= js_GetSrcNoteOffset(sn
, 0);
3250 DECOMPILE_CODE(pc
+ oplen
, tail
- oplen
);
3253 LOCAL_ASSERT(*pc
== JSOP_GOTO
|| *pc
== JSOP_GOTOX
);
3254 oplen
= js_CodeSpec
[*pc
].length
;
3255 len
= GetJumpOffset(pc
, pc
);
3256 js_printf(jp
, "\t} else");
3259 * If the second offset for sn is non-zero, it tells
3260 * the distance from the goto around the else, to the
3261 * ifeq for the if inside the else that forms an "if
3262 * else if" chain. Thus cond spans the condition of
3263 * the second if, so we simply decompile it and start
3264 * over at label if_again.
3266 cond
= js_GetSrcNoteOffset(sn
, 1);
3268 DECOMPILE_CODE(pc
+ oplen
, cond
- oplen
);
3274 js_printf(jp
, " {\n");
3276 DECOMPILE_CODE(pc
+ oplen
, len
- oplen
);
3279 if (!ss
->inArrayInit
&& !ss
->inGenExp
) {
3281 js_printf(jp
, "\t}\n");
3287 xval
= JS_strdup(cx
, POP_STR());
3290 len
= js_GetSrcNoteOffset(sn
, 0);
3291 DECOMPILE_CODE(pc
+ oplen
, len
- oplen
);
3292 lval
= JS_strdup(cx
, POP_STR());
3294 cx
->free((void *)xval
);
3298 LOCAL_ASSERT(*pc
== JSOP_GOTO
|| *pc
== JSOP_GOTOX
);
3299 oplen
= js_CodeSpec
[*pc
].length
;
3300 len
= GetJumpOffset(pc
, pc
);
3301 DECOMPILE_CODE(pc
+ oplen
, len
- oplen
);
3303 todo
= Sprint(&ss
->sprinter
, "%s ? %s : %s",
3305 cx
->free((void *)xval
);
3306 cx
->free((void *)lval
);
3324 do_logical_connective
:
3325 /* Top of stack is the first clause in a disjunction (||). */
3326 lval
= JS_strdup(cx
, POP_STR());
3329 done
= pc
+ GetJumpOffset(pc
, pc
);
3332 if (!Decompile(ss
, pc
, len
, op
)) {
3333 cx
->free((char *)lval
);
3338 jp
->indent
+ 4 + strlen(lval
) + 4 + strlen(rval
) > 75) {
3339 rval
= JS_strdup(cx
, rval
);
3343 todo
= Sprint(&ss
->sprinter
, "%s %s\n", lval
, xval
);
3344 tail
= Sprint(&ss
->sprinter
, "%*s%s",
3345 jp
->indent
+ 4, "", rval
);
3346 cx
->free((char *)rval
);
3351 todo
= Sprint(&ss
->sprinter
, "%s %s %s", lval
, xval
, rval
);
3353 cx
->free((char *)lval
);
3359 goto do_logical_connective
;
3367 sn
= js_GetSrcNote(jp
->script
, pc
);
3368 if (!IsVarSlot(jp
, pc
, &i
)) {
3369 JS_ASSERT(op
== JSOP_FORLOCAL
);
3370 todo
= Sprint(&ss
->sprinter
, ss_format
, VarPrefix(sn
), GetStr(ss
, i
));
3375 atom
= GetArgOrVarAtom(jp
, i
);
3377 todo
= SprintCString(&ss
->sprinter
, VarPrefix(sn
));
3378 if (todo
< 0 || !QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0))
3384 sn
= js_GetSrcNote(jp
->script
, pc
);
3385 todo
= SprintCString(&ss
->sprinter
, VarPrefix(sn
));
3386 if (todo
< 0 || !QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0))
3393 if (!ATOM_IS_IDENTIFIER(atom
)) {
3394 xval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
3402 todo
= Sprint(&ss
->sprinter
, index_format
, lval
, xval
);
3404 todo
= Sprint(&ss
->sprinter
, ss_format
, lval
, *lval
? "." : "");
3407 if (!QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0))
3413 todo
= SprintCString(&ss
->sprinter
, forelem_cookie
);
3417 case JSOP_ENUMCONSTELEM
:
3419 * The stack has the object under the (top) index expression.
3420 * The "rval" property id is underneath those two on the stack.
3421 * The for loop body net and gross lengths can now be adjusted
3422 * to account for the length of the indexing expression that
3423 * came after JSOP_FORELEM and before JSOP_ENUMELEM.
3426 op
= JSOP_NOP
; /* turn off parens around xval */
3428 op
= JSOP_GETELEM
; /* lval must have high precedence */
3432 LOCAL_ASSERT(strcmp(rval
, forelem_cookie
) == 0);
3433 if (*xval
== '\0') {
3434 todo
= SprintCString(&ss
->sprinter
, lval
);
3436 todo
= Sprint(&ss
->sprinter
,
3437 (JOF_OPMODE(lastop
) == JOF_XMLNAME
)
3444 #if JS_HAS_GETTER_SETTER
3452 rval
= GetStr(ss
, ss
->top
-2);
3453 todo
= SprintCString(&ss
->sprinter
, rval
);
3454 if (todo
< 0 || !PushOff(ss
, todo
,
3455 (JSOp
) ss
->opcodes
[ss
->top
-2])) {
3461 #if JS_HAS_DESTRUCTURING
3462 sn
= js_GetSrcNote(jp
->script
, pc
);
3464 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_DESTRUCT
);
3465 pc
= DecompileDestructuring(ss
, pc
, endpc
);
3470 op
= saveop
= JSOP_ENUMELEM
;
3473 if (strcmp(rval
, forelem_cookie
) == 0) {
3474 todo
= Sprint(&ss
->sprinter
, ss_format
,
3475 VarPrefix(sn
), lval
);
3477 // Skip POP so the SRC_FOR_IN code can pop for itself.
3478 if (*pc
== JSOP_POP
)
3479 len
= JSOP_POP_LENGTH
;
3481 todo
= Sprint(&ss
->sprinter
, "%s%s = %s",
3482 VarPrefix(sn
), lval
, rval
);
3488 rval
= GetStr(ss
, ss
->top
-1);
3489 saveop
= (JSOp
) ss
->opcodes
[ss
->top
-1];
3490 todo
= SprintCString(&ss
->sprinter
, rval
);
3494 atom
= GetArgOrVarAtom(jp
, GET_ARGNO(pc
));
3504 lval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3508 if (op
== JSOP_SETNAME
)
3509 (void) PopOff(ss
, op
);
3512 sn
= js_GetSrcNote(jp
->script
, pc
- 1);
3513 if (sn
&& SN_TYPE(sn
) == SRC_ASSIGNOP
) {
3514 todo
= Sprint(&ss
->sprinter
, "%s %s= %s",
3516 (lastop
== JSOP_GETTER
)
3518 : (lastop
== JSOP_SETTER
)
3520 : CodeToken
[lastop
],
3523 sn
= js_GetSrcNote(jp
->script
, pc
);
3524 todo
= Sprint(&ss
->sprinter
, "%s%s = %s",
3525 VarPrefix(sn
), lval
, rval
);
3527 if (op
== JSOP_SETLOCALPOP
) {
3528 if (!PushOff(ss
, todo
, saveop
))
3531 LOCAL_ASSERT(*rval
!= '\0');
3532 js_printf(jp
, "\t%s;\n", rval
);
3539 argc
= GET_UINT16(pc
);
3540 JS_ASSERT(argc
> 0);
3542 js::Vector
<char *> argv(cx
);
3543 if (!argv
.resize(argc
))
3546 MUST_FLOW_THROUGH("out");
3549 for (i
= argc
- 1; i
>= 0; i
--) {
3550 argv
[i
] = JS_strdup(cx
, POP_STR());
3555 todo
= Sprint(&ss
->sprinter
, "%s", argv
[0]);
3558 for (i
= 1; i
< argc
; i
++) {
3559 if (Sprint(&ss
->sprinter
, " + %s", argv
[i
]) < 0)
3564 * The only way that our next op could be a JSOP_ADD is
3565 * if we are about to concatenate at least one non-string
3566 * literal. Deal with that here in order to avoid extra
3567 * parentheses (because JSOP_ADD is left-associative).
3569 if (pc
[len
] == JSOP_ADD
)
3575 for (i
= 0; i
< argc
; i
++)
3576 JS_free(cx
, argv
[i
]);
3587 argc
= GET_ARGC(pc
);
3589 cx
->malloc((size_t)(argc
+ 1) * sizeof *argv
);
3595 for (i
= argc
; i
> 0; i
--)
3596 argv
[i
] = JS_strdup(cx
, POP_STR());
3598 /* Skip the JSOP_PUSHOBJ-created empty string. */
3599 LOCAL_ASSERT(ss
->top
>= 2);
3600 (void) PopOff(ss
, op
);
3603 * Special case: new (x(y)(z)) must be parenthesized like so.
3604 * Same for new (x(y).z) -- contrast with new x(y).z.
3605 * See PROPAGATE_CALLNESS.
3607 op
= (JSOp
) ss
->opcodes
[ss
->top
- 1];
3609 (saveop
== JSOP_NEW
&&
3613 (js_CodeSpec
[op
].format
& JOF_CALLOP
)))
3618 argv
[0] = JS_strdup(cx
, lval
);
3622 lval
= "(", rval
= ")";
3623 if (op
== JSOP_NEW
) {
3626 todo
= Sprint(&ss
->sprinter
, "%s %s%s",
3627 js_new_str
, argv
[0], lval
);
3629 todo
= Sprint(&ss
->sprinter
, ss_format
,
3635 for (i
= 1; i
<= argc
; i
++) {
3637 Sprint(&ss
->sprinter
, ss_format
,
3638 argv
[i
], (i
< argc
) ? ", " : "") < 0) {
3643 if (Sprint(&ss
->sprinter
, rval
) < 0)
3646 for (i
= 0; i
<= argc
; i
++)
3651 if (op
== JSOP_SETCALL
) {
3652 if (!PushOff(ss
, todo
, op
))
3654 todo
= Sprint(&ss
->sprinter
, "");
3660 lval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3663 RETRACT(&ss
->sprinter
, lval
);
3665 todo
= Sprint(&ss
->sprinter
, "%s %s", js_delete_str
, lval
);
3669 GET_ATOM_QUOTE_AND_FMT("%s %s[%s]", "%s %s.%s", rval
);
3672 todo
= Sprint(&ss
->sprinter
, fmt
, js_delete_str
, lval
, rval
);
3676 op
= JSOP_NOP
; /* turn off parens */
3681 goto do_delete_lval
;
3682 todo
= Sprint(&ss
->sprinter
,
3683 (JOF_OPMODE(lastop
) == JOF_XMLNAME
)
3686 js_delete_str
, lval
, xval
);
3689 #if JS_HAS_XML_SUPPORT
3694 todo
= Sprint(&ss
->sprinter
, "%s %s..%s",
3695 js_delete_str
, lval
, xval
);
3699 case JSOP_TYPEOFEXPR
:
3703 todo
= Sprint(&ss
->sprinter
, "%s %s",
3704 (op
== JSOP_VOID
) ? js_void_str
: js_typeof_str
,
3710 atom
= GetArgOrVarAtom(jp
, GET_ARGNO(pc
));
3720 lval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3723 RETRACT(&ss
->sprinter
, lval
);
3725 todo
= Sprint(&ss
->sprinter
, ss_format
,
3726 js_incop_strs
[!(cs
->format
& JOF_INC
)], lval
);
3731 GET_ATOM_QUOTE_AND_FMT(preindex_format
, predot_format
, rval
);
3734 * Force precedence below the numeric literal opcodes, so that
3735 * 42..foo or 10000..toString(16), e.g., decompile with parens
3736 * around the left-hand side of dot.
3740 todo
= Sprint(&ss
->sprinter
, fmt
,
3741 js_incop_strs
[!(cs
->format
& JOF_INC
)],
3747 op
= JSOP_NOP
; /* turn off parens */
3751 if (*xval
!= '\0') {
3752 todo
= Sprint(&ss
->sprinter
,
3753 (JOF_OPMODE(lastop
) == JOF_XMLNAME
)
3756 js_incop_strs
[!(cs
->format
& JOF_INC
)],
3759 todo
= Sprint(&ss
->sprinter
, ss_format
,
3760 js_incop_strs
[!(cs
->format
& JOF_INC
)], lval
);
3766 atom
= GetArgOrVarAtom(jp
, GET_ARGNO(pc
));
3776 lval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3779 RETRACT(&ss
->sprinter
, lval
);
3781 todo
= Sprint(&ss
->sprinter
, ss_format
,
3782 lval
, js_incop_strs
[!(cs
->format
& JOF_INC
)]);
3787 GET_ATOM_QUOTE_AND_FMT(postindex_format
, postdot_format
, rval
);
3790 * Force precedence below the numeric literal opcodes, so that
3791 * 42..foo or 10000..toString(16), e.g., decompile with parens
3792 * around the left-hand side of dot.
3796 todo
= Sprint(&ss
->sprinter
, fmt
, lval
, rval
,
3797 js_incop_strs
[!(cs
->format
& JOF_INC
)]);
3802 op
= JSOP_NOP
; /* turn off parens */
3806 if (*xval
!= '\0') {
3807 todo
= Sprint(&ss
->sprinter
,
3808 (JOF_OPMODE(lastop
) == JOF_XMLNAME
)
3812 js_incop_strs
[!(cs
->format
& JOF_INC
)]);
3814 todo
= Sprint(&ss
->sprinter
, ss_format
,
3815 lval
, js_incop_strs
[!(cs
->format
& JOF_INC
)]);
3821 rval
= js_length_str
;
3822 goto do_getprop_lval
;
3826 (void) PopOff(ss
, lastop
);
3835 GET_QUOTE_AND_FMT(index_format
, dot_format
, rval
);
3837 PROPAGATE_CALLNESS();
3839 todo
= Sprint(&ss
->sprinter
, fmt
, lval
, rval
);
3842 case JSOP_GETTHISPROP
:
3844 GET_QUOTE_AND_FMT(index_format
, dot_format
, rval
);
3845 todo
= Sprint(&ss
->sprinter
, fmt
, js_this_str
, rval
);
3848 case JSOP_GETARGPROP
:
3849 /* Get the name of the argument or variable. */
3853 atom
= GetArgOrVarAtom(ss
->printer
, i
);
3855 LOCAL_ASSERT(ATOM_IS_STRING(atom
));
3856 lval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3857 if (!lval
|| !PushOff(ss
, STR2OFF(&ss
->sprinter
, lval
), op
))
3860 /* Get the name of the property. */
3861 LOAD_ATOM(ARGNO_LEN
);
3864 case JSOP_GETLOCALPROP
:
3865 if (IsVarSlot(jp
, pc
, &i
))
3866 goto do_getarg_prop
;
3867 LOCAL_ASSERT((uintN
)i
< ss
->top
);
3868 lval
= GetLocal(ss
, i
);
3871 todo
= SprintCString(&ss
->sprinter
, lval
);
3872 if (todo
< 0 || !PushOff(ss
, todo
, op
))
3878 case JSOP_SETMETHOD
:
3880 GET_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval
);
3884 * Force precedence below the numeric literal opcodes, so that
3885 * 42..foo or 10000..toString(16), e.g., decompile with parens
3886 * around the left-hand side of dot.
3890 sn
= js_GetSrcNote(jp
->script
, pc
- 1);
3891 todo
= Sprint(&ss
->sprinter
, fmt
, lval
, xval
,
3892 (sn
&& SN_TYPE(sn
) == SRC_ASSIGNOP
)
3893 ? (lastop
== JSOP_GETTER
)
3895 : (lastop
== JSOP_SETTER
)
3904 (void) PopOff(ss
, lastop
);
3909 op
= JSOP_NOP
; /* turn off parens */
3912 PROPAGATE_CALLNESS();
3914 if (*xval
== '\0') {
3915 todo
= Sprint(&ss
->sprinter
, "%s", lval
);
3917 todo
= Sprint(&ss
->sprinter
,
3918 (JOF_OPMODE(lastop
) == JOF_XMLNAME
)
3927 op
= JSOP_NOP
; /* turn off parens */
3929 cs
= &js_CodeSpec
[ss
->opcodes
[ss
->top
]];
3930 op
= JSOP_GETELEM
; /* lval must have high precedence */
3935 sn
= js_GetSrcNote(jp
->script
, pc
- 1);
3936 todo
= Sprint(&ss
->sprinter
,
3937 (JOF_MODE(cs
->format
) == JOF_XMLNAME
)
3941 (sn
&& SN_TYPE(sn
) == SRC_ASSIGNOP
)
3942 ? (lastop
== JSOP_GETTER
)
3944 : (lastop
== JSOP_SETTER
)
3952 i
= (jsint
) GET_ARGNO(pc
);
3953 todo
= Sprint(&ss
->sprinter
, "%s[%d]",
3954 js_arguments_str
, (int) i
);
3958 todo
= Sprint(&ss
->sprinter
, dot_format
,
3959 js_arguments_str
, js_length_str
);
3965 atom
= GetArgOrVarAtom(jp
, i
);
3966 #if JS_HAS_DESTRUCTURING
3968 todo
= Sprint(&ss
->sprinter
, "%s[%d]", js_arguments_str
, i
);
3983 #if JS_HAS_XML_SUPPORT
3986 sn
= js_GetSrcNote(jp
->script
, pc
);
3987 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
3988 inXML
? DONT_ESCAPE
: 0);
3991 RETRACT(&ss
->sprinter
, rval
);
3992 todo
= Sprint(&ss
->sprinter
, sss_format
,
3993 VarPrefix(sn
), lval
, rval
);
3997 i
= (jsint
) GET_UINT16(pc
);
4001 i
= (jsint
) GET_UINT24(pc
);
4011 todo
= Sprint(&ss
->sprinter
, "%d", i
);
4015 GET_DOUBLE_FROM_BYTECODE(jp
->script
, pc
, 0, atom
);
4016 val
= ATOM_KEY(atom
);
4017 LOCAL_ASSERT(JSVAL_IS_DOUBLE(val
));
4018 todo
= SprintDoubleValue(&ss
->sprinter
, val
, &saveop
);
4023 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
4024 inXML
? DONT_ESCAPE
: '"');
4027 todo
= STR2OFF(&ss
->sprinter
, rval
);
4031 case JSOP_LAMBDA_FC
:
4032 case JSOP_LAMBDA_DBGFC
:
4033 #if JS_HAS_GENERATOR_EXPRS
4034 sn
= js_GetSrcNote(jp
->script
, pc
);
4035 if (sn
&& SN_TYPE(sn
) == SRC_GENEXP
) {
4037 jsuword
*innerLocalNames
, *outerLocalNames
;
4038 JSScript
*inner
, *outer
;
4040 JSFunction
*outerfun
;
4045 * All allocation when decompiling is LIFO, using malloc
4046 * or, more commonly, arena-allocating from cx->tempPool.
4047 * Therefore after InitSprintStack succeeds, we must
4048 * release to mark before returning.
4050 mark
= JS_ARENA_MARK(&cx
->tempPool
);
4051 if (!fun
->hasLocalNames()) {
4052 innerLocalNames
= NULL
;
4054 innerLocalNames
= js_GetLocalNameArray(cx
, fun
, &cx
->tempPool
);
4055 if (!innerLocalNames
)
4058 inner
= fun
->u
.i
.script
;
4059 if (!InitSprintStack(cx
, &ss2
, jp
, StackDepth(inner
))) {
4060 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
4063 ss2
.inGenExp
= JS_TRUE
;
4066 * Recursively decompile this generator function as an
4067 * un-parenthesized generator expression. The ss->inGenExp
4068 * special case of JSOP_YIELD shares array comprehension
4069 * decompilation code that leaves the result as the single
4070 * string pushed on ss2.
4074 outerLocalNames
= jp
->localNames
;
4075 LOCAL_ASSERT(JS_UPTRDIFF(pc
, outer
->code
) <= outer
->length
);
4078 jp
->localNames
= innerLocalNames
;
4079 ok
= Decompile(&ss2
, inner
->code
, inner
->length
, JSOP_NOP
) != NULL
;
4082 jp
->localNames
= outerLocalNames
;
4084 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
4089 * Advance over this op and its global |this| push, and
4090 * arrange to advance over the call to this lambda.
4093 LOCAL_ASSERT(*pc
== JSOP_NULL
);
4094 pc
+= JSOP_NULL_LENGTH
;
4095 LOCAL_ASSERT(*pc
== JSOP_CALL
);
4096 LOCAL_ASSERT(GET_ARGC(pc
) == 0);
4097 len
= JSOP_CALL_LENGTH
;
4100 * Arrange to parenthesize this genexp unless:
4102 * 1. It is the complete expression consumed by a control
4103 * flow bytecode such as JSOP_TABLESWITCH whose syntax
4104 * always parenthesizes the controlling expression.
4105 * 2. It is the sole argument to a function call.
4107 * But if this genexp runs up against endpc, parenthesize
4108 * regardless. (This can happen if we are called from
4109 * DecompileExpression or recursively from case
4110 * JSOP_{NOP,AND,OR}.)
4112 * There's no special case for |if (genexp)| because the
4113 * compiler optimizes that to |if (true)|.
4117 if (op
== JSOP_TRACE
|| op
== JSOP_NOP
)
4118 pc2
+= JSOP_NOP_LENGTH
;
4119 LOCAL_ASSERT(pc2
< endpc
||
4120 endpc
< outer
->code
+ outer
->length
);
4121 LOCAL_ASSERT(ss2
.top
== 1);
4122 ss2
.opcodes
[0] = JSOP_POP
;
4127 op
= ((js_CodeSpec
[op
].format
& JOF_PARENHEAD
) ||
4128 ((js_CodeSpec
[op
].format
& JOF_INVOKE
) && GET_ARGC(pc2
) == 1))
4133 * Stack this result as if it's a name and not an
4134 * anonymous function, so it doesn't get decompiled as
4135 * a generator function in a getter or setter context.
4136 * The precedence level is the same for JSOP_NAME and
4139 LOCAL_ASSERT(js_CodeSpec
[JSOP_NAME
].prec
==
4140 js_CodeSpec
[saveop
].prec
);
4145 * Alas, we have to malloc a copy of the result left on
4146 * the top of ss2 because both ss and ss2 arena-allocate
4147 * from cx's tempPool.
4149 rval
= JS_strdup(cx
, PopStr(&ss2
, op
));
4150 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
4153 todo
= SprintCString(&ss
->sprinter
, rval
);
4154 cx
->free((void *)rval
);
4157 #endif /* JS_HAS_GENERATOR_EXPRS */
4163 * Always parenthesize expression closures. We can't force
4164 * saveop to a low-precedence op to arrange for auto-magic
4165 * parenthesization without confusing getter/setter code
4166 * that checks for JSOP_LAMBDA.
4168 bool grouped
= !(fun
->flags
& JSFUN_EXPR_CLOSURE
);
4169 bool strict
= jp
->script
->strictModeCode
;
4170 str
= js_DecompileToString(cx
, "lambda", fun
, 0,
4171 false, grouped
, strict
,
4172 js_DecompileFunction
);
4177 todo
= SprintString(&ss
->sprinter
, str
);
4181 JS_ASSERT(jp
->fun
&& jp
->fun
->atom
);
4182 todo
= SprintString(&ss
->sprinter
, ATOM_TO_STRING(jp
->fun
->atom
));
4187 LOCAL_ASSERT(OBJ_GET_CLASS(cx
, obj
) == &js_RegExpClass
);
4191 GET_REGEXP_FROM_BYTECODE(jp
->script
, pc
, 0, obj
);
4193 if (!js_regexp_toString(cx
, obj
, &val
))
4195 str
= JSVAL_TO_STRING(val
);
4198 case JSOP_TABLESWITCH
:
4199 case JSOP_TABLESWITCHX
:
4201 ptrdiff_t jmplen
, off
, off2
;
4202 jsint j
, n
, low
, high
;
4203 TableEntry
*table
, *tmp
;
4205 sn
= js_GetSrcNote(jp
->script
, pc
);
4206 LOCAL_ASSERT(sn
&& SN_TYPE(sn
) == SRC_SWITCH
);
4207 len
= js_GetSrcNoteOffset(sn
, 0);
4208 jmplen
= (op
== JSOP_TABLESWITCH
) ? JUMP_OFFSET_LEN
4211 off
= GetJumpOffset(pc
, pc2
);
4213 low
= GET_JUMP_OFFSET(pc2
);
4214 pc2
+= JUMP_OFFSET_LEN
;
4215 high
= GET_JUMP_OFFSET(pc2
);
4216 pc2
+= JUMP_OFFSET_LEN
;
4224 table
= (TableEntry
*)
4225 cx
->malloc((size_t)n
* sizeof *table
);
4228 for (i
= j
= 0; i
< n
; i
++) {
4229 table
[j
].label
= NULL
;
4230 off2
= GetJumpOffset(pc
, pc2
);
4232 sn
= js_GetSrcNote(jp
->script
, pc2
);
4234 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_LABEL
);
4235 GET_SOURCE_NOTE_ATOM(sn
, table
[j
].label
);
4237 table
[j
].key
= INT_TO_JSVAL(low
+ i
);
4238 table
[j
].offset
= off2
;
4244 tmp
= (TableEntry
*)
4245 cx
->malloc((size_t)j
* sizeof *table
);
4247 VOUCH_DOES_NOT_REQUIRE_STACK();
4248 ok
= js_MergeSort(table
, (size_t)j
, sizeof(TableEntry
),
4249 CompareOffsets
, NULL
, tmp
);
4257 ok
= DecompileSwitch(ss
, table
, (uintN
)j
, pc
, len
, off
,
4267 case JSOP_LOOKUPSWITCH
:
4268 case JSOP_LOOKUPSWITCHX
:
4270 ptrdiff_t jmplen
, off
, off2
;
4274 sn
= js_GetSrcNote(jp
->script
, pc
);
4275 LOCAL_ASSERT(sn
&& SN_TYPE(sn
) == SRC_SWITCH
);
4276 len
= js_GetSrcNoteOffset(sn
, 0);
4277 jmplen
= (op
== JSOP_LOOKUPSWITCH
) ? JUMP_OFFSET_LEN
4280 off
= GetJumpOffset(pc
, pc2
);
4282 npairs
= GET_UINT16(pc2
);
4285 table
= (TableEntry
*)
4286 cx
->malloc((size_t)npairs
* sizeof *table
);
4289 for (k
= 0; k
< npairs
; k
++) {
4290 sn
= js_GetSrcNote(jp
->script
, pc2
);
4292 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_LABEL
);
4293 GET_SOURCE_NOTE_ATOM(sn
, table
[k
].label
);
4295 table
[k
].label
= NULL
;
4297 JS_GET_SCRIPT_ATOM(jp
->script
, pc
, GET_INDEX(pc2
), atom
);
4299 off2
= GetJumpOffset(pc
, pc2
);
4301 table
[k
].key
= ATOM_KEY(atom
);
4302 table
[k
].offset
= off2
;
4305 ok
= DecompileSwitch(ss
, table
, (uintN
)npairs
, pc
, len
, off
,
4314 case JSOP_CONDSWITCH
:
4316 ptrdiff_t off
, off2
, caseOff
;
4320 sn
= js_GetSrcNote(jp
->script
, pc
);
4321 LOCAL_ASSERT(sn
&& SN_TYPE(sn
) == SRC_SWITCH
);
4322 len
= js_GetSrcNoteOffset(sn
, 0);
4323 off
= js_GetSrcNoteOffset(sn
, 1);
4326 * Count the cases using offsets from switch to first case,
4327 * and case to case, stored in srcnote immediates.
4331 for (ncases
= 0; off2
!= 0; ncases
++) {
4333 LOCAL_ASSERT(*pc2
== JSOP_CASE
|| *pc2
== JSOP_DEFAULT
||
4334 *pc2
== JSOP_CASEX
|| *pc2
== JSOP_DEFAULTX
);
4335 if (*pc2
== JSOP_DEFAULT
|| *pc2
== JSOP_DEFAULTX
) {
4336 /* End of cases, but count default as a case. */
4339 sn
= js_GetSrcNote(jp
->script
, pc2
);
4340 LOCAL_ASSERT(sn
&& SN_TYPE(sn
) == SRC_PCDELTA
);
4341 off2
= js_GetSrcNoteOffset(sn
, 0);
4346 * Allocate table and rescan the cases using their srcnotes,
4347 * stashing each case's delta from switch top in table[i].key,
4348 * and the distance to its statements in table[i].offset.
4350 table
= (TableEntry
*)
4351 cx
->malloc((size_t)ncases
* sizeof *table
);
4356 for (i
= 0; i
< ncases
; i
++) {
4358 LOCAL_ASSERT(*pc2
== JSOP_CASE
|| *pc2
== JSOP_DEFAULT
||
4359 *pc2
== JSOP_CASEX
|| *pc2
== JSOP_DEFAULTX
);
4361 table
[i
].key
= INT_TO_JSVAL((jsint
) caseOff
);
4362 table
[i
].offset
= caseOff
+ GetJumpOffset(pc2
, pc2
);
4363 if (*pc2
== JSOP_CASE
|| *pc2
== JSOP_CASEX
) {
4364 sn
= js_GetSrcNote(jp
->script
, pc2
);
4365 LOCAL_ASSERT(sn
&& SN_TYPE(sn
) == SRC_PCDELTA
);
4366 off2
= js_GetSrcNoteOffset(sn
, 0);
4371 * Find offset of default code by fetching the default offset
4372 * from the end of table. JSOP_CONDSWITCH always has a default
4375 off
= JSVAL_TO_INT(table
[ncases
-1].key
);
4377 off
+= GetJumpOffset(pc2
, pc2
);
4379 ok
= DecompileSwitch(ss
, table
, (uintN
)ncases
, pc
, len
, off
,
4394 js_printf(jp
, "\tcase %s:\n", lval
);
4400 case JSOP_DEFFUN_FC
:
4401 case JSOP_DEFFUN_DBGFC
:
4408 saveop
= op
= JS_GetTrapOpcode(cx
, jp
->script
, pc
);
4410 cs
= &js_CodeSpec
[op
];
4412 DECOMPILE_CODE(pc
, len
);
4418 todo
= SprintPut(&ss
->sprinter
, "", 0);
4422 argc
= GET_UINT16(pc
);
4423 LOCAL_ASSERT(ss
->top
>= (uintN
) argc
);
4425 todo
= SprintCString(&ss
->sprinter
, "[]");
4429 argv
= (char **) cx
->malloc(size_t(argc
) * sizeof *argv
);
4437 argv
[--i
] = JS_strdup(cx
, POP_STR());
4439 todo
= SprintCString(&ss
->sprinter
, "[");
4443 for (i
= 0; i
< argc
; i
++) {
4445 Sprint(&ss
->sprinter
, ss_format
,
4446 argv
[i
], (i
< argc
- 1) ? ", " : "") < 0) {
4452 for (i
= 0; i
< argc
; i
++)
4458 sn
= js_GetSrcNote(jp
->script
, pc
);
4459 if (sn
&& SN_TYPE(sn
) == SRC_CONTINUE
&& SprintCString(&ss
->sprinter
, ", ") < 0)
4461 if (SprintCString(&ss
->sprinter
, "]") < 0)
4468 LOCAL_ASSERT(i
== JSProto_Array
|| i
== JSProto_Object
);
4470 todo
= ss
->sprinter
.offset
;
4471 #if JS_HAS_SHARP_VARS
4473 if (op
== JSOP_SHARPINIT
)
4474 op
= (JSOp
)pc
[len
+= JSOP_SHARPINIT_LENGTH
];
4475 if (op
== JSOP_DEFSHARP
) {
4477 cs
= &js_CodeSpec
[op
];
4479 if (Sprint(&ss
->sprinter
, "#%u=",
4480 (unsigned) (jsint
) GET_UINT16(pc
+ UINT16_LEN
))
4485 #endif /* JS_HAS_SHARP_VARS */
4486 if (i
== JSProto_Array
) {
4488 if (SprintCString(&ss
->sprinter
, "[") < 0)
4491 if (SprintCString(&ss
->sprinter
, "{") < 0)
4501 op
= JSOP_NOP
; /* turn off parens */
4503 sn
= js_GetSrcNote(jp
->script
, pc
);
4505 /* Skip any #n= prefix to find the opening bracket. */
4506 for (xval
= rval
; *xval
!= '[' && *xval
!= '{'; xval
++)
4508 inArray
= (*xval
== '[');
4511 todo
= Sprint(&ss
->sprinter
, "%s%s%c",
4513 (sn
&& SN_TYPE(sn
) == SRC_CONTINUE
) ? ", " : "",
4514 inArray
? ']' : '}');
4520 const char *maybeComma
;
4523 isFirst
= (ss
->opcodes
[ss
->top
- 3] == JSOP_NEWINIT
);
4525 /* Turn off most parens. */
4529 /* Turn off all parens for xval and lval, which we control. */
4533 sn
= js_GetSrcNote(jp
->script
, pc
);
4535 if (sn
&& SN_TYPE(sn
) == SRC_INITPROP
) {
4539 maybeComma
= isFirst
? "" : ", ";
4540 todo
= Sprint(&ss
->sprinter
, sss_format
,
4547 case JSOP_INITMETHOD
:
4549 xval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
4551 (ATOM_IS_IDENTIFIER(atom
) ? 0 : '\''));
4554 isFirst
= (ss
->opcodes
[ss
->top
- 2] == JSOP_NEWINIT
);
4560 maybeComma
= isFirst
? "" : ", ";
4561 #ifdef OLD_GETTER_SETTER
4562 todo
= Sprint(&ss
->sprinter
, "%s%s%s%s%s%s%s:%s",
4566 (lastop
== JSOP_GETTER
|| lastop
== JSOP_SETTER
)
4568 (lastop
== JSOP_GETTER
) ? js_getter_str
:
4569 (lastop
== JSOP_SETTER
) ? js_setter_str
:
4573 if (lastop
== JSOP_GETTER
|| lastop
== JSOP_SETTER
) {
4575 !ATOM_IS_STRING(atom
) ||
4576 !ATOM_IS_IDENTIFIER(atom
) ||
4577 ATOM_IS_KEYWORD(atom
) ||
4578 (ss
->opcodes
[ss
->top
+1] != JSOP_LAMBDA
&&
4579 ss
->opcodes
[ss
->top
+1] != JSOP_LAMBDA_FC
)) {
4580 todo
= Sprint(&ss
->sprinter
, "%s%s%s %s: %s",
4584 (lastop
== JSOP_GETTER
) ? js_getter_str
:
4585 (lastop
== JSOP_SETTER
) ? js_setter_str
:
4589 const char *end
= rval
+ strlen(rval
);
4593 LOCAL_ASSERT(strncmp(rval
, js_function_str
, 8) == 0);
4594 LOCAL_ASSERT(rval
[8] == ' ');
4596 LOCAL_ASSERT(*end
? *end
== ')' : end
[-1] == '}');
4597 todo
= Sprint(&ss
->sprinter
, "%s%s%s %s%s%.*s",
4600 (lastop
== JSOP_GETTER
)
4601 ? js_get_str
: js_set_str
,
4603 (rval
[0] != '(') ? " " : "",
4607 todo
= Sprint(&ss
->sprinter
, "%s%s%s: %s",
4608 lval
, maybeComma
, xval
, rval
);
4614 #if JS_HAS_SHARP_VARS
4616 i
= (jsint
) GET_UINT16(pc
+ UINT16_LEN
);
4618 todo
= Sprint(&ss
->sprinter
, "#%u=%s", (unsigned) i
, rval
);
4622 i
= (jsint
) GET_UINT16(pc
+ UINT16_LEN
);
4623 todo
= Sprint(&ss
->sprinter
, "#%u#", (unsigned) i
);
4625 #endif /* JS_HAS_SHARP_VARS */
4627 #if JS_HAS_DEBUGGER_KEYWORD
4629 js_printf(jp
, "\tdebugger;\n");
4632 #endif /* JS_HAS_DEBUGGER_KEYWORD */
4634 #if JS_HAS_XML_SUPPORT
4636 case JSOP_STARTXMLEXPR
:
4637 inXML
= op
== JSOP_STARTXML
;
4643 js_printf(jp
, "\t%s %s %s = %s;\n",
4644 js_default_str
, js_xml_str
, js_namespace_str
, rval
);
4649 if (pc
[JSOP_ANYNAME_LENGTH
] == JSOP_TOATTRNAME
) {
4650 len
+= JSOP_TOATTRNAME_LENGTH
;
4651 todo
= SprintPut(&ss
->sprinter
, "@*", 2);
4653 todo
= SprintPut(&ss
->sprinter
, "*", 1);
4657 case JSOP_QNAMEPART
:
4659 if (pc
[JSOP_QNAMEPART_LENGTH
] == JSOP_TOATTRNAME
) {
4660 saveop
= JSOP_TOATTRNAME
;
4661 len
+= JSOP_TOATTRNAME_LENGTH
;
4667 case JSOP_QNAMECONST
:
4669 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
4672 RETRACT(&ss
->sprinter
, rval
);
4674 todo
= Sprint(&ss
->sprinter
, "%s::%s", lval
, rval
);
4680 todo
= Sprint(&ss
->sprinter
, "%s::[%s]", lval
, rval
);
4683 case JSOP_TOATTRNAME
:
4684 op
= JSOP_NOP
; /* turn off parens */
4686 todo
= Sprint(&ss
->sprinter
, "@[%s]", rval
);
4689 case JSOP_TOATTRVAL
:
4693 case JSOP_ADDATTRNAME
:
4696 todo
= Sprint(&ss
->sprinter
, "%s %s", lval
, rval
);
4697 /* This gets reset by all XML tag expressions. */
4698 quoteAttr
= JS_TRUE
;
4701 case JSOP_ADDATTRVAL
:
4705 todo
= Sprint(&ss
->sprinter
, "%s=\"%s\"", lval
, rval
);
4707 todo
= Sprint(&ss
->sprinter
, "%s=%s", lval
, rval
);
4710 case JSOP_BINDXMLNAME
:
4711 /* Leave the name stacked and push a dummy string. */
4712 todo
= Sprint(&ss
->sprinter
, "");
4715 case JSOP_SETXMLNAME
:
4716 /* Pop the r.h.s., the dummy string, and the name. */
4718 (void) PopOff(ss
, op
);
4722 case JSOP_XMLELTEXPR
:
4723 case JSOP_XMLTAGEXPR
:
4724 todo
= Sprint(&ss
->sprinter
, "{%s}", POP_STR());
4726 /* If we're an attribute value, we shouldn't quote this. */
4727 quoteAttr
= JS_FALSE
;
4730 case JSOP_TOXMLLIST
:
4731 op
= JSOP_NOP
; /* turn off parens */
4732 todo
= Sprint(&ss
->sprinter
, "<>%s</>", POP_STR());
4737 case JSOP_CALLXMLNAME
:
4740 /* These ops indicate the end of XML expressions. */
4745 case JSOP_ENDFILTER
:
4747 PROPAGATE_CALLNESS();
4749 todo
= Sprint(&ss
->sprinter
, "%s.(%s)", lval
, rval
);
4752 case JSOP_DESCENDANTS
:
4754 PROPAGATE_CALLNESS();
4756 todo
= Sprint(&ss
->sprinter
, "%s..%s", lval
, rval
);
4759 case JSOP_XMLOBJECT
:
4761 todo
= Sprint(&ss
->sprinter
, "<xml address='%p'>", obj
);
4766 todo
= SprintPut(&ss
->sprinter
, "<![CDATA[", 9);
4767 if (!QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
4770 SprintPut(&ss
->sprinter
, "]]>", 3);
4773 case JSOP_XMLCOMMENT
:
4775 todo
= SprintPut(&ss
->sprinter
, "<!--", 4);
4776 if (!QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
4779 SprintPut(&ss
->sprinter
, "-->", 3);
4784 rval
= JS_strdup(cx
, POP_STR());
4787 todo
= SprintPut(&ss
->sprinter
, "<?", 2);
4788 ok
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0) &&
4790 (SprintPut(&ss
->sprinter
, " ", 1) >= 0 &&
4791 SprintCString(&ss
->sprinter
, rval
)));
4792 cx
->free((char *)rval
);
4795 SprintPut(&ss
->sprinter
, "?>", 2);
4799 todo
= SprintPut(&ss
->sprinter
, js_function_str
, 8);
4801 #endif /* JS_HAS_XML_SUPPORT */
4810 /* -2 means "don't push", -1 means reported error. */
4814 if (!PushOff(ss
, todo
, saveop
))
4818 if (cs
->format
& JOF_CALLOP
) {
4819 todo
= Sprint(&ss
->sprinter
, "");
4820 if (todo
< 0 || !PushOff(ss
, todo
, saveop
))
4828 * Undefine local macros.
4831 #undef DECOMPILE_CODE
4837 #undef ATOM_IS_IDENTIFIER
4838 #undef GET_QUOTE_AND_FMT
4839 #undef GET_ATOM_QUOTE_AND_FMT
4845 DecompileCode(JSPrinter
*jp
, JSScript
*script
, jsbytecode
*pc
, uintN len
,
4853 JSScript
*oldscript
;
4854 jsbytecode
*oldcode
, *oldmain
, *code
;
4857 depth
= StackDepth(script
);
4858 JS_ASSERT(pcdepth
<= depth
);
4860 /* Initialize a sprinter for use with the offset stack. */
4861 cx
= jp
->sprinter
.context
;
4862 mark
= JS_ARENA_MARK(&cx
->tempPool
);
4863 ok
= InitSprintStack(cx
, &ss
, jp
, depth
);
4868 * If we are called from js_DecompileValueGenerator with a portion of
4869 * script's bytecode that starts with a non-zero model stack depth given
4870 * by pcdepth, attempt to initialize the missing string offsets in ss to
4871 * |spindex| negative indexes from fp->sp for the activation fp in which
4874 * See js_DecompileValueGenerator for how its |spindex| parameter is used,
4875 * and see also GetOff, which makes use of the ss.offsets[i] < -1 that are
4876 * potentially stored below.
4880 for (i
= 0; i
< pcdepth
; i
++) {
4881 ss
.offsets
[i
] = -2 - (ptrdiff_t)i
;
4882 ss
.opcodes
[i
] = *jp
->pcstack
[i
];
4886 /* Call recursive subroutine to do the hard work. */
4887 oldscript
= jp
->script
;
4888 jp
->script
= script
;
4889 oldcode
= jp
->script
->code
;
4890 oldmain
= jp
->script
->main
;
4891 code
= js_UntrapScriptCode(cx
, jp
->script
);
4892 if (code
!= oldcode
) {
4893 jp
->script
->code
= code
;
4894 jp
->script
->main
= code
+ (oldmain
- oldcode
);
4895 pc
= code
+ (pc
- oldcode
);
4898 ok
= Decompile(&ss
, pc
, len
, JSOP_NOP
) != NULL
;
4899 if (code
!= oldcode
) {
4900 cx
->free(jp
->script
->code
);
4901 jp
->script
->code
= oldcode
;
4902 jp
->script
->main
= oldmain
;
4904 jp
->script
= oldscript
;
4906 /* If the given code didn't empty the stack, do it now. */
4909 last
= OFF2STR(&ss
.sprinter
, PopOff(&ss
, JSOP_POP
));
4910 } while (ss
.top
> pcdepth
);
4911 js_printf(jp
, "%s", last
);
4915 /* Free all temporary stuff allocated under this call. */
4916 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
4921 js_DecompileScript(JSPrinter
*jp
, JSScript
*script
)
4923 return DecompileCode(jp
, script
, script
->code
, (uintN
)script
->length
, 0);
4927 js_DecompileToString(JSContext
*cx
, const char *name
, JSFunction
*fun
,
4928 uintN indent
, JSBool pretty
, JSBool grouped
, JSBool strict
,
4929 JSDecompilerPtr decompiler
)
4934 jp
= js_NewPrinter(cx
, name
, fun
, indent
, pretty
, grouped
, strict
);
4938 str
= js_GetPrinterOutput(jp
);
4941 js_DestroyPrinter(jp
);
4945 static const char native_code_str
[] = "\t[native code]\n";
4948 js_DecompileFunctionBody(JSPrinter
*jp
)
4953 JS_ASSERT(!jp
->script
);
4954 if (!FUN_INTERPRETED(jp
->fun
)) {
4955 js_printf(jp
, native_code_str
);
4959 script
= jp
->fun
->u
.i
.script
;
4960 return DecompileCode(jp
, script
, script
->code
, (uintN
)script
->length
, 0);
4964 js_DecompileFunction(JSPrinter
*jp
)
4969 jsbytecode
*pc
, *endpc
;
4975 JS_ASSERT(!jp
->script
);
4978 * If pretty, conform to ECMA-262 Edition 3, 15.3.4.2, by decompiling a
4979 * FunctionDeclaration. Otherwise, check the JSFUN_LAMBDA flag and force
4980 * an expression by parenthesizing.
4983 js_printf(jp
, "\t");
4985 if (!jp
->grouped
&& (fun
->flags
& JSFUN_LAMBDA
))
4988 if (JSFUN_GETTER_TEST(fun
->flags
))
4989 js_printf(jp
, "%s ", js_getter_str
);
4990 else if (JSFUN_SETTER_TEST(fun
->flags
))
4991 js_printf(jp
, "%s ", js_setter_str
);
4993 js_printf(jp
, "%s ", js_function_str
);
4994 if (fun
->atom
&& !QuoteString(&jp
->sprinter
, ATOM_TO_STRING(fun
->atom
), 0))
4998 if (!FUN_INTERPRETED(fun
)) {
4999 js_printf(jp
, ") {\n");
5001 js_printf(jp
, native_code_str
);
5003 js_printf(jp
, "\t}");
5005 JSScript
*script
= fun
->u
.i
.script
;
5006 #if JS_HAS_DESTRUCTURING
5011 /* Print the parameters. */
5013 endpc
= pc
+ script
->length
;
5016 /* Skip trace hint if it appears here. */
5017 #if JS_HAS_GENERATORS
5018 if (js_GetOpcode(jp
->sprinter
.context
, script
, script
->code
) != JSOP_GENERATOR
)
5021 JSOp op
= js_GetOpcode(jp
->sprinter
.context
, script
, pc
);
5022 if (op
== JSOP_TRACE
|| op
== JSOP_NOP
) {
5023 JS_STATIC_ASSERT(JSOP_TRACE_LENGTH
== JSOP_NOP_LENGTH
);
5024 pc
+= JSOP_TRACE_LENGTH
;
5026 JS_ASSERT(op
== JSOP_STOP
); /* empty script singleton */
5030 #if JS_HAS_DESTRUCTURING
5032 jp
->script
= script
;
5033 mark
= JS_ARENA_MARK(&jp
->sprinter
.context
->tempPool
);
5036 for (i
= 0; i
< fun
->nargs
; i
++) {
5040 param
= GetArgOrVarAtom(jp
, i
);
5042 #if JS_HAS_DESTRUCTURING
5043 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, JS_FALSE)
5049 LOCAL_ASSERT(*pc
== JSOP_GETARG
);
5050 pc
+= JSOP_GETARG_LENGTH
;
5051 LOCAL_ASSERT(*pc
== JSOP_DUP
);
5053 ok
= InitSprintStack(jp
->sprinter
.context
, &ss
, jp
, StackDepth(script
));
5057 pc
= DecompileDestructuring(&ss
, pc
, endpc
);
5062 LOCAL_ASSERT(*pc
== JSOP_POP
);
5063 pc
+= JSOP_POP_LENGTH
;
5064 lval
= PopStr(&ss
, JSOP_NOP
);
5065 todo
= SprintCString(&jp
->sprinter
, lval
);
5076 if (!QuoteString(&jp
->sprinter
, ATOM_TO_STRING(param
), 0)) {
5082 #if JS_HAS_DESTRUCTURING
5084 JS_ARENA_RELEASE(&jp
->sprinter
.context
->tempPool
, mark
);
5088 if (fun
->flags
& JSFUN_EXPR_CLOSURE
) {
5089 js_printf(jp
, ") ");
5090 if (fun
->u
.i
.script
->strictModeCode
&& !jp
->strict
) {
5092 * We have no syntax for strict function expressions;
5093 * at least give a hint.
5095 js_printf(jp
, "\t/* use strict */ \n");
5100 js_printf(jp
, ") {\n");
5102 if (fun
->u
.i
.script
->strictModeCode
&& !jp
->strict
) {
5103 js_printf(jp
, "\t'use strict';\n");
5108 len
= script
->code
+ script
->length
- pc
;
5109 ok
= DecompileCode(jp
, script
, pc
, (uintN
)len
, 0);
5113 if (!(fun
->flags
& JSFUN_EXPR_CLOSURE
)) {
5115 js_printf(jp
, "\t}");
5119 if (!jp
->pretty
&& !jp
->grouped
&& (fun
->flags
& JSFUN_LAMBDA
))
5126 js_DecompileValueGenerator(JSContext
*cx
, intN spindex
, jsval v
,
5134 jsval
*sp
, *stackBase
;
5137 JS_ASSERT(spindex
< 0 ||
5138 spindex
== JSDVG_IGNORE_STACK
||
5139 spindex
== JSDVG_SEARCH_STACK
);
5141 fp
= js_GetScriptedCaller(cx
, NULL
);
5142 if (!fp
|| !fp
->regs
|| !fp
->regs
->sp
)
5145 script
= fp
->script
;
5147 pc
= fp
->imacpc
? fp
->imacpc
: regs
->pc
;
5148 if (pc
< script
->main
|| script
->code
+ script
->length
<= pc
) {
5149 JS_NOT_REACHED("bug");
5153 if (spindex
!= JSDVG_IGNORE_STACK
) {
5154 jsbytecode
**pcstack
;
5157 * Prepare computing pcstack containing pointers to opcodes that
5158 * populated interpreter's stack with its current content.
5160 pcstack
= (jsbytecode
**)
5161 cx
->malloc(StackDepth(script
) * sizeof *pcstack
);
5164 pcdepth
= ReconstructPCStack(cx
, script
, pc
, pcstack
);
5166 goto release_pcstack
;
5168 if (spindex
!= JSDVG_SEARCH_STACK
) {
5169 JS_ASSERT(spindex
< 0);
5172 goto release_pcstack
;
5173 pc
= pcstack
[pcdepth
];
5176 * We search from fp->sp to base to find the most recently
5177 * calculated value matching v under assumption that it is
5178 * it that caused exception, see bug 328664.
5180 stackBase
= StackBase(fp
);
5183 if (sp
== stackBase
) {
5185 goto release_pcstack
;
5187 } while (*--sp
!= v
);
5190 * The value may have come from beyond stackBase + pcdepth,
5191 * meaning that it came from a temporary slot that the
5192 * interpreter uses for GC roots or when JSOP_APPLY extended
5193 * the stack to fit the argument array elements. Only update pc
5194 * if beneath stackBase + pcdepth; otherwise blame existing
5197 if (sp
< stackBase
+ pcdepth
)
5198 pc
= pcstack
[sp
- stackBase
];
5208 jsbytecode
* savepc
= regs
->pc
;
5209 jsbytecode
* imacpc
= fp
->imacpc
;
5216 * FIXME: bug 489843. Stack reconstruction may have returned a pc
5217 * value *inside* an imacro; this would confuse the decompiler.
5219 if (imacpc
&& size_t(pc
- script
->code
) >= script
->length
)
5220 name
= FAILED_EXPRESSION_DECOMPILER
;
5222 name
= DecompileExpression(cx
, script
, fp
->fun
, pc
);
5226 fp
->imacpc
= imacpc
;
5229 if (name
!= FAILED_EXPRESSION_DECOMPILER
)
5234 fallback
= js_ValueToSource(cx
, v
);
5238 return js_DeflateString(cx
, fallback
->chars(), fallback
->length());
5242 DecompileExpression(JSContext
*cx
, JSScript
*script
, JSFunction
*fun
,
5245 jsbytecode
*code
, *oldcode
, *oldmain
;
5247 const JSCodeSpec
*cs
;
5248 jsbytecode
*begin
, *end
;
5251 jsbytecode
**pcstack
;
5256 JS_ASSERT(script
->main
<= pc
&& pc
< script
->code
+ script
->length
);
5259 oldcode
= script
->code
;
5260 oldmain
= script
->main
;
5262 MUST_FLOW_THROUGH("out");
5263 code
= js_UntrapScriptCode(cx
, script
);
5264 if (code
!= oldcode
) {
5265 script
->code
= code
;
5266 script
->main
= code
+ (oldmain
- oldcode
);
5267 pc
= code
+ (pc
- oldcode
);
5272 /* None of these stack-writing ops generates novel values. */
5273 JS_ASSERT(op
!= JSOP_CASE
&& op
!= JSOP_CASEX
&&
5274 op
!= JSOP_DUP
&& op
!= JSOP_DUP2
);
5276 /* JSOP_PUSH is used to generate undefined for group assignment holes. */
5277 if (op
== JSOP_PUSH
) {
5278 name
= JS_strdup(cx
, js_undefined_str
);
5283 * |this| could convert to a very long object initialiser, so cite it by
5284 * its keyword name instead.
5286 if (op
== JSOP_THIS
) {
5287 name
= JS_strdup(cx
, js_this_str
);
5292 * JSOP_BINDNAME is special: it generates a value, the base object of a
5293 * reference. But if it is the generating op for a diagnostic produced by
5294 * js_DecompileValueGenerator, the name being bound is irrelevant. Just
5295 * fall back to the base object.
5297 if (op
== JSOP_BINDNAME
) {
5298 name
= FAILED_EXPRESSION_DECOMPILER
;
5302 /* NAME ops are self-contained, others require left or right context. */
5303 cs
= &js_CodeSpec
[op
];
5305 end
= pc
+ cs
->length
;
5306 switch (JOF_MODE(cs
->format
)) {
5311 sn
= js_GetSrcNote(script
, pc
);
5313 name
= FAILED_EXPRESSION_DECOMPILER
;
5316 switch (SN_TYPE(sn
)) {
5318 begin
-= js_GetSrcNoteOffset(sn
, 0);
5321 end
= begin
+ js_GetSrcNoteOffset(sn
, 0);
5322 begin
+= cs
->length
;
5325 name
= FAILED_EXPRESSION_DECOMPILER
;
5333 name
= FAILED_EXPRESSION_DECOMPILER
;
5337 pcstack
= (jsbytecode
**)
5338 cx
->malloc(StackDepth(script
) * sizeof *pcstack
);
5344 MUST_FLOW_THROUGH("out");
5345 pcdepth
= ReconstructPCStack(cx
, script
, begin
, pcstack
);
5347 name
= FAILED_EXPRESSION_DECOMPILER
;
5352 jp
= js_NewPrinter(cx
, "js_DecompileValueGenerator", fun
, 0,
5353 false, false, false);
5356 jp
->pcstack
= pcstack
;
5357 if (DecompileCode(jp
, script
, begin
, (uintN
) len
, (uintN
) pcdepth
)) {
5358 name
= (jp
->sprinter
.base
) ? jp
->sprinter
.base
: (char *) "";
5359 name
= JS_strdup(cx
, name
);
5361 js_DestroyPrinter(jp
);
5365 if (code
!= oldcode
) {
5366 cx
->free(script
->code
);
5367 script
->code
= oldcode
;
5368 script
->main
= oldmain
;
5376 js_ReconstructStackDepth(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
)
5378 return ReconstructPCStack(cx
, script
, pc
, NULL
);
5381 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1);
5384 SimulateOp(JSContext
*cx
, JSScript
*script
, JSOp op
, const JSCodeSpec
*cs
,
5385 jsbytecode
*pc
, jsbytecode
**pcstack
, uintN
&pcdepth
)
5387 uintN nuses
= js_GetStackUses(cs
, op
, pc
);
5388 uintN ndefs
= js_GetStackDefs(cx
, cs
, op
, script
, pc
);
5389 LOCAL_ASSERT(pcdepth
>= nuses
);
5391 LOCAL_ASSERT(pcdepth
+ ndefs
<= StackDepth(script
));
5394 * Fill the slots that the opcode defines withs its pc unless it just
5395 * reshuffles the stack. In the latter case we want to preserve the
5396 * opcode that generated the original value.
5401 for (uintN i
= 0; i
!= ndefs
; ++i
)
5402 pcstack
[pcdepth
+ i
] = pc
;
5408 /* Keep the switch value. */
5409 JS_ASSERT(ndefs
== 1);
5413 JS_ASSERT(ndefs
== 2);
5415 pcstack
[pcdepth
+ 1] = pcstack
[pcdepth
];
5419 JS_ASSERT(ndefs
== 4);
5421 pcstack
[pcdepth
+ 2] = pcstack
[pcdepth
];
5422 pcstack
[pcdepth
+ 3] = pcstack
[pcdepth
+ 1];
5427 JS_ASSERT(ndefs
== 2);
5429 jsbytecode
*tmp
= pcstack
[pcdepth
+ 1];
5430 pcstack
[pcdepth
+ 1] = pcstack
[pcdepth
];
5431 pcstack
[pcdepth
] = tmp
;
5442 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_CUSTOM(expr, goto failure);
5445 SimulateImacroCFG(JSContext
*cx
, JSScript
*script
,
5446 uintN pcdepth
, jsbytecode
*pc
, jsbytecode
*target
,
5447 jsbytecode
**pcstack
)
5449 size_t nbytes
= StackDepth(script
) * sizeof *pcstack
;
5450 jsbytecode
** tmp_pcstack
= (jsbytecode
**) cx
->malloc(nbytes
);
5453 memcpy(tmp_pcstack
, pcstack
, nbytes
);
5456 for (; pc
< target
; pc
+= oplen
) {
5457 JSOp op
= js_GetOpcode(cx
, script
, pc
);
5458 const JSCodeSpec
*cs
= &js_CodeSpec
[op
];
5461 oplen
= js_GetVariableBytecodeLength(pc
);
5463 if (SimulateOp(cx
, script
, op
, cs
, pc
, tmp_pcstack
, pcdepth
) < 0)
5466 uint32 type
= cs
->format
& JOF_TYPEMASK
;
5467 if (type
== JOF_JUMP
|| type
== JOF_JUMPX
) {
5468 ptrdiff_t jmpoff
= (type
== JOF_JUMP
) ? GET_JUMP_OFFSET(pc
)
5469 : GET_JUMPX_OFFSET(pc
);
5470 LOCAL_ASSERT(jmpoff
>= 0);
5471 intN tmp_pcdepth
= SimulateImacroCFG(cx
, script
, pcdepth
, pc
+ jmpoff
,
5472 target
, tmp_pcstack
);
5473 if (tmp_pcdepth
>= 0) {
5474 pcdepth
= uintN(tmp_pcdepth
);
5478 if (op
== JSOP_GOTO
|| op
== JSOP_GOTOX
)
5486 LOCAL_ASSERT(pc
== target
);
5489 memcpy(pcstack
, tmp_pcstack
, nbytes
);
5490 cx
->free(tmp_pcstack
);
5494 cx
->free(tmp_pcstack
);
5499 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1);
5502 ReconstructPCStack(JSContext
*cx
, JSScript
*script
, jsbytecode
*target
,
5503 jsbytecode
**pcstack
);
5506 ReconstructImacroPCStack(JSContext
*cx
, JSScript
*script
,
5507 jsbytecode
*imacstart
, jsbytecode
*target
,
5508 jsbytecode
**pcstack
)
5511 * Begin with a recursive call back to ReconstructPCStack to pick up
5512 * the state-of-the-world at the *start* of the imacro.
5514 JSStackFrame
*fp
= js_GetScriptedCaller(cx
, NULL
);
5515 JS_ASSERT(fp
->imacpc
);
5516 intN pcdepth
= ReconstructPCStack(cx
, script
, fp
->imacpc
, pcstack
);
5519 return SimulateImacroCFG(cx
, script
, pcdepth
, imacstart
, target
, pcstack
);
5522 extern jsbytecode
* js_GetImacroStart(jsbytecode
* pc
);
5526 ReconstructPCStack(JSContext
*cx
, JSScript
*script
, jsbytecode
*target
,
5527 jsbytecode
**pcstack
)
5530 * Walk forward from script->main and compute the stack depth and stack of
5531 * operand-generating opcode PCs in pcstack.
5533 * FIXME: Code to compute oplen copied from js_Disassemble1 and reduced.
5534 * FIXME: Optimize to use last empty-stack sequence point.
5537 jsbytecode
*imacstart
= js_GetImacroStart(target
);
5540 return ReconstructImacroPCStack(cx
, script
, imacstart
, target
, pcstack
);
5543 LOCAL_ASSERT(script
->main
<= target
&& target
< script
->code
+ script
->length
);
5544 jsbytecode
*pc
= script
->main
;
5547 for (; pc
< target
; pc
+= oplen
) {
5548 JSOp op
= js_GetOpcode(cx
, script
, pc
);
5549 const JSCodeSpec
*cs
= &js_CodeSpec
[op
];
5552 oplen
= js_GetVariableBytecodeLength(pc
);
5555 * A (C ? T : E) expression requires skipping either T (if target is in
5556 * E) or both T and E (if target is after the whole expression) before
5557 * adjusting pcdepth based on the JSOP_IFEQ or JSOP_IFEQX at pc that
5558 * tests condition C. We know that the stack depth can't change from
5559 * what it was with C on top of stack.
5561 jssrcnote
*sn
= js_GetSrcNote(script
, pc
);
5562 if (sn
&& SN_TYPE(sn
) == SRC_COND
) {
5563 ptrdiff_t jmpoff
= js_GetSrcNoteOffset(sn
, 0);
5564 if (pc
+ jmpoff
< target
) {
5566 op
= js_GetOpcode(cx
, script
, pc
);
5567 JS_ASSERT(op
== JSOP_GOTO
|| op
== JSOP_GOTOX
);
5568 cs
= &js_CodeSpec
[op
];
5570 JS_ASSERT(oplen
> 0);
5571 ptrdiff_t jmplen
= GetJumpOffset(pc
, pc
);
5572 if (pc
+ jmplen
< target
) {
5573 oplen
= (uintN
) jmplen
;
5578 * Ok, target lies in E. Manually pop C off the model stack,
5579 * since we have moved beyond the IFEQ now.
5581 LOCAL_ASSERT(pcdepth
!= 0);
5587 * Ignore early-exit code, which is SRC_HIDDEN, but do not ignore the
5588 * hidden POP that sometimes appears after an UNBRAND. See bug 543565.
5590 if (sn
&& SN_TYPE(sn
) == SRC_HIDDEN
&&
5591 (op
!= JSOP_POP
|| js_GetOpcode(cx
, script
, pc
- 1) != JSOP_UNBRAND
)) {
5595 if (SimulateOp(cx
, script
, op
, cs
, pc
, pcstack
, pcdepth
) < 0)
5599 LOCAL_ASSERT(pc
== target
);
5605 #undef LOCAL_ASSERT_RV