1 /* -*- Mode: C; tab-width: 8; 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"
77 #include "jsautooplen.h"
79 /* Verify JSOP_XXX_LENGTH constant definitions. */
80 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
81 JS_STATIC_ASSERT(op##_LENGTH == length);
82 #include "jsopcode.tbl"
85 static const char js_incop_strs
[][3] = {"++", "--"};
86 static const char js_for_each_str
[] = "for each";
88 const JSCodeSpec js_CodeSpec
[] = {
89 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
90 {length,nuses,ndefs,prec,format},
91 #include "jsopcode.tbl"
95 uintN js_NumCodeSpecs
= JS_ARRAY_LENGTH(js_CodeSpec
);
98 * Each element of the array is either a source literal associated with JS
101 static const char *CodeToken
[] = {
102 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
104 #include "jsopcode.tbl"
108 #if defined(DEBUG) || defined(JS_JIT_SPEW)
110 * Array of JS bytecode names used by DEBUG-only js_Disassemble and by
113 const char *js_CodeName
[] = {
114 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
116 #include "jsopcode.tbl"
121 /************************************************************************/
124 GetJumpOffset(jsbytecode
*pc
, jsbytecode
*pc2
)
128 type
= JOF_OPTYPE(*pc
);
129 if (JOF_TYPE_IS_EXTENDED_JUMP(type
))
130 return GET_JUMPX_OFFSET(pc2
);
131 return GET_JUMP_OFFSET(pc2
);
135 js_GetIndexFromBytecode(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
,
143 op
= JS_GetTrapOpcode(cx
, script
, pc
);
144 JS_ASSERT(js_CodeSpec
[op
].length
>= 1 + pcoff
+ UINT16_LEN
);
147 * We need to detect index base prefix. It presents when resetbase
148 * follows the bytecode.
150 span
= js_CodeSpec
[op
].length
;
152 if (pc
- script
->code
+ span
< script
->length
) {
153 if (pc
[span
] == JSOP_RESETBASE
) {
154 base
= GET_INDEXBASE(pc
- JSOP_INDEXBASE_LENGTH
);
155 } else if (pc
[span
] == JSOP_RESETBASE0
) {
156 JS_ASSERT(JSOP_INDEXBASE1
<= pc
[-1] || pc
[-1] <= JSOP_INDEXBASE3
);
157 base
= (pc
[-1] - JSOP_INDEXBASE1
+ 1) << 16;
160 return base
+ GET_UINT16(pc
+ pcoff
);
164 js_GetVariableBytecodeLength(jsbytecode
*pc
)
167 uintN jmplen
, ncases
;
171 JS_ASSERT(js_CodeSpec
[op
].length
== -1);
173 case JSOP_TABLESWITCHX
:
174 jmplen
= JUMPX_OFFSET_LEN
;
176 case JSOP_TABLESWITCH
:
177 jmplen
= JUMP_OFFSET_LEN
;
179 /* Structure: default-jump case-low case-high case1-jump ... */
181 low
= GET_JUMP_OFFSET(pc
);
182 pc
+= JUMP_OFFSET_LEN
;
183 high
= GET_JUMP_OFFSET(pc
);
184 ncases
= (uintN
)(high
- low
+ 1);
185 return 1 + jmplen
+ INDEX_LEN
+ INDEX_LEN
+ ncases
* jmplen
;
187 case JSOP_LOOKUPSWITCHX
:
188 jmplen
= JUMPX_OFFSET_LEN
;
191 JS_ASSERT(op
== JSOP_LOOKUPSWITCH
);
192 jmplen
= JUMP_OFFSET_LEN
;
194 /* Structure: default-jump case-count (case1-value case1-jump) ... */
196 ncases
= GET_UINT16(pc
);
197 return 1 + jmplen
+ INDEX_LEN
+ ncases
* (INDEX_LEN
+ jmplen
);
202 js_GetVariableStackUseLength(JSOp op
, jsbytecode
*pc
)
204 JS_ASSERT(*pc
== op
|| *pc
== JSOP_TRAP
);
205 JS_ASSERT(js_CodeSpec
[op
].nuses
== -1);
208 return GET_UINT16(pc
);
209 case JSOP_LEAVEBLOCK
:
210 return GET_UINT16(pc
);
211 case JSOP_LEAVEBLOCKEXPR
:
212 return GET_UINT16(pc
) + 1;
214 return GET_UINT24(pc
);
216 /* stack: fun, this, [argc arguments] */
217 JS_ASSERT(op
== JSOP_NEW
|| op
== JSOP_CALL
||
218 op
== JSOP_EVAL
|| op
== JSOP_SETCALL
||
220 return 2 + GET_ARGC(pc
);
226 JS_FRIEND_API(JSBool
)
227 js_Disassemble(JSContext
*cx
, JSScript
*script
, JSBool lines
, FILE *fp
)
229 jsbytecode
*pc
, *end
;
233 end
= pc
+ script
->length
;
235 if (pc
== script
->main
)
236 fputs("main:\n", fp
);
237 len
= js_Disassemble1(cx
, script
, pc
,
238 PTRDIFF(pc
, script
->code
, jsbytecode
),
248 ToDisassemblySource(JSContext
*cx
, jsval v
)
251 JSScopeProperty
*sprop
;
256 if (!JSVAL_IS_PRIMITIVE(v
)) {
257 obj
= JSVAL_TO_OBJECT(v
);
258 if (OBJ_GET_CLASS(cx
, obj
) == &js_BlockClass
) {
259 source
= JS_sprintf_append(NULL
, "depth %d {",
260 OBJ_BLOCK_DEPTH(cx
, obj
));
261 for (sprop
= OBJ_SCOPE(obj
)->lastProp
; sprop
;
262 sprop
= sprop
->parent
) {
263 bytes
= js_AtomToPrintableString(cx
, JSID_TO_ATOM(sprop
->id
));
266 source
= JS_sprintf_append(source
, "%s: %d%s",
267 bytes
, sprop
->shortid
,
268 sprop
->parent
? ", " : "");
270 source
= JS_sprintf_append(source
, "}");
273 str
= JS_NewString(cx
, source
, strlen(source
));
276 return js_GetStringBytes(cx
, str
);
279 return js_ValueToPrintableSource(cx
, v
);
283 js_Disassemble1(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
,
284 uintN loc
, JSBool lines
, FILE *fp
)
287 const JSCodeSpec
*cs
;
288 ptrdiff_t len
, off
, jmplen
;
298 if (op
>= JSOP_LIMIT
) {
299 char numBuf1
[12], numBuf2
[12];
300 JS_snprintf(numBuf1
, sizeof numBuf1
, "%d", op
);
301 JS_snprintf(numBuf2
, sizeof numBuf2
, "%d", JSOP_LIMIT
);
302 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
303 JSMSG_BYTECODE_TOO_BIG
, numBuf1
, numBuf2
);
306 cs
= &js_CodeSpec
[op
];
307 len
= (ptrdiff_t) cs
->length
;
308 fprintf(fp
, "%05u:", loc
);
310 fprintf(fp
, "%4u", JS_PCToLineNumber(cx
, script
, pc
));
311 fprintf(fp
, " %s", js_CodeName
[op
]);
312 type
= JOF_TYPE(cs
->format
);
315 if (op
== JSOP_TRAP
) {
316 op
= JS_GetTrapOpcode(cx
, script
, pc
);
317 len
= (ptrdiff_t) js_CodeSpec
[op
].length
;
323 off
= GetJumpOffset(pc
, pc
);
324 fprintf(fp
, " %u (%d)", loc
+ (intN
) off
, (intN
) off
);
330 index
= js_GetIndexFromBytecode(cx
, script
, pc
, 0);
331 if (type
== JOF_ATOM
) {
332 JS_GET_SCRIPT_ATOM(script
, index
, atom
);
335 if (type
== JOF_OBJECT
)
336 JS_GET_SCRIPT_OBJECT(script
, index
, obj
);
338 JS_GET_SCRIPT_REGEXP(script
, index
, obj
);
339 v
= OBJECT_TO_JSVAL(obj
);
341 bytes
= ToDisassemblySource(cx
, v
);
344 fprintf(fp
, " %s", bytes
);
348 i
= (jsint
)GET_UINT16(pc
);
351 case JOF_TABLESWITCH
:
352 case JOF_TABLESWITCHX
:
357 jmplen
= (type
== JOF_TABLESWITCH
) ? JUMP_OFFSET_LEN
360 off
= GetJumpOffset(pc
, pc2
);
362 low
= GET_JUMP_OFFSET(pc2
);
363 pc2
+= JUMP_OFFSET_LEN
;
364 high
= GET_JUMP_OFFSET(pc2
);
365 pc2
+= JUMP_OFFSET_LEN
;
366 fprintf(fp
, " defaultOffset %d low %d high %d", (intN
) off
, low
, high
);
367 for (i
= low
; i
<= high
; i
++) {
368 off
= GetJumpOffset(pc
, pc2
);
369 fprintf(fp
, "\n\t%d: %d", i
, (intN
) off
);
376 case JOF_LOOKUPSWITCH
:
377 case JOF_LOOKUPSWITCHX
:
382 jmplen
= (type
== JOF_LOOKUPSWITCH
) ? JUMP_OFFSET_LEN
385 off
= GetJumpOffset(pc
, pc2
);
387 npairs
= GET_UINT16(pc2
);
389 fprintf(fp
, " offset %d npairs %u", (intN
) off
, (uintN
) npairs
);
391 JS_GET_SCRIPT_ATOM(script
, GET_INDEX(pc2
), atom
);
393 off
= GetJumpOffset(pc
, pc2
);
396 bytes
= ToDisassemblySource(cx
, ATOM_KEY(atom
));
399 fprintf(fp
, "\n\t%s: %d", bytes
, (intN
) off
);
407 fprintf(fp
, " %u", GET_ARGNO(pc
));
411 fprintf(fp
, " %u", GET_SLOTNO(pc
));
416 fprintf(fp
, " %u", GET_SLOTNO(pc
));
417 index
= js_GetIndexFromBytecode(cx
, script
, pc
, SLOTNO_LEN
);
418 if (type
== JOF_SLOTATOM
) {
419 JS_GET_SCRIPT_ATOM(script
, index
, atom
);
422 JS_GET_SCRIPT_OBJECT(script
, index
, obj
);
423 v
= OBJECT_TO_JSVAL(obj
);
425 bytes
= ToDisassemblySource(cx
, v
);
428 fprintf(fp
, " %s", bytes
);
432 JS_ASSERT(op
== JSOP_UINT24
|| op
== JSOP_NEWARRAY
);
433 i
= (jsint
)GET_UINT24(pc
);
445 JS_ASSERT(op
== JSOP_INT32
);
448 fprintf(fp
, " %d", i
);
453 JS_snprintf(numBuf
, sizeof numBuf
, "%lx", (unsigned long) cs
->format
);
454 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
455 JSMSG_UNKNOWN_FORMAT
, numBuf
);
465 /************************************************************************/
468 * Sprintf, but with unlimited and automatically allocated buffering.
470 typedef struct Sprinter
{
471 JSContext
*context
; /* context executing the decompiler */
472 JSArenaPool
*pool
; /* string allocation pool */
473 char *base
; /* base address of buffer in pool */
474 size_t size
; /* size of buffer allocated at base */
475 ptrdiff_t offset
; /* offset of next free char in buffer */
478 #define INIT_SPRINTER(cx, sp, ap, off) \
479 ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0, \
482 #define OFF2STR(sp,off) ((sp)->base + (off))
483 #define STR2OFF(sp,str) ((str) - (sp)->base)
484 #define RETRACT(sp,str) ((sp)->offset = STR2OFF(sp, str))
487 SprintEnsureBuffer(Sprinter
*sp
, size_t len
)
492 nb
= (sp
->offset
+ len
+ 1) - sp
->size
;
497 JS_ARENA_ALLOCATE_CAST(base
, char *, sp
->pool
, nb
);
499 JS_ARENA_GROW_CAST(base
, char *, sp
->pool
, sp
->size
, nb
);
502 js_ReportOutOfScriptQuota(sp
->context
);
511 SprintPut(Sprinter
*sp
, const char *s
, size_t len
)
516 /* Allocate space for s, including the '\0' at the end. */
517 if (!SprintEnsureBuffer(sp
, len
))
520 /* Advance offset and copy s into sp's buffer. */
523 bp
= sp
->base
+ offset
;
530 SprintCString(Sprinter
*sp
, const char *s
)
532 return SprintPut(sp
, s
, strlen(s
));
536 SprintString(Sprinter
*sp
, JSString
*str
)
542 JSSTRING_CHARS_AND_LENGTH(str
, chars
, length
);
546 size
= js_GetDeflatedStringLength(sp
->context
, chars
, length
);
547 if (size
== (size_t)-1 || !SprintEnsureBuffer(sp
, size
))
552 js_DeflateStringToBuffer(sp
->context
, chars
, length
, sp
->base
+ offset
,
554 sp
->base
[sp
->offset
] = 0;
560 Sprint(Sprinter
*sp
, const char *format
, ...)
566 va_start(ap
, format
);
567 bp
= JS_vsmprintf(format
, ap
); /* XXX vsaprintf */
570 JS_ReportOutOfMemory(sp
->context
);
573 offset
= SprintCString(sp
, bp
);
578 const char js_EscapeMap
[] = {
591 #define DONT_ESCAPE 0x10000
594 QuoteString(Sprinter
*sp
, JSString
*str
, uint32 quote
)
596 JSBool dontEscape
, ok
;
599 const jschar
*s
, *t
, *z
;
603 /* Sample off first for later return value pointer computation. */
604 dontEscape
= (quote
& DONT_ESCAPE
) != 0;
607 if (qc
&& Sprint(sp
, "%c", (char)qc
) < 0)
610 /* Loop control variables: z points at end of string sentinel. */
611 JSSTRING_CHARS_AND_END(str
, s
, z
);
612 for (t
= s
; t
< z
; s
= ++t
) {
613 /* Move t forward from s past un-quote-worthy characters. */
615 while (JS_ISPRINT(c
) && c
!= qc
&& c
!= '\\' && c
!= '\t' &&
621 len
= PTRDIFF(t
, s
, jschar
);
623 /* Allocate space for s, including the '\0' at the end. */
624 if (!SprintEnsureBuffer(sp
, len
))
627 /* Advance sp->offset and copy s into sp's buffer. */
628 bp
= sp
->base
+ sp
->offset
;
637 /* Use js_EscapeMap, \u, or \x only if necessary. */
638 if (!(c
>> 8) && (e
= strchr(js_EscapeMap
, (int)c
)) != NULL
) {
640 ? Sprint(sp
, "%c", (char)c
) >= 0
641 : Sprint(sp
, "\\%c", e
[1]) >= 0;
643 ok
= Sprint(sp
, (c
>> 8) ? "\\u%04X" : "\\x%02X", c
) >= 0;
649 /* Sprint the closing quote and return the quoted string. */
650 if (qc
&& Sprint(sp
, "%c", (char)qc
) < 0)
654 * If we haven't Sprint'd anything yet, Sprint an empty string so that
655 * the OFF2STR below gives a valid result.
657 if (off
== sp
->offset
&& Sprint(sp
, "") < 0)
659 return OFF2STR(sp
, off
);
663 js_QuoteString(JSContext
*cx
, JSString
*str
, jschar quote
)
670 mark
= JS_ARENA_MARK(&cx
->tempPool
);
671 INIT_SPRINTER(cx
, &sprinter
, &cx
->tempPool
, 0);
672 bytes
= QuoteString(&sprinter
, str
, quote
);
673 escstr
= bytes
? JS_NewStringCopyZ(cx
, bytes
) : NULL
;
674 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
678 /************************************************************************/
681 Sprinter sprinter
; /* base class state */
682 JSArenaPool pool
; /* string allocation pool */
683 uintN indent
; /* indentation in spaces */
684 JSPackedBool pretty
; /* pretty-print: indent, use newlines */
685 JSPackedBool grouped
; /* in parenthesized expression context */
686 JSScript
*script
; /* script being printed */
687 jsbytecode
*dvgfence
; /* DecompileExpression fencepost */
688 jsbytecode
**pcstack
; /* DecompileExpression modeled stack */
689 JSFunction
*fun
; /* interpreted function */
690 jsuword
*localNames
; /* argument and variable names */
694 * Hack another flag, a la JS_DONT_PRETTY_PRINT, into uintN indent parameters
695 * to functions such as js_DecompileFunction and js_NewPrinter. This time, as
696 * opposed to JS_DONT_PRETTY_PRINT back in the dark ages, we can assume that a
697 * uintN is at least 32 bits.
699 #define JS_IN_GROUP_CONTEXT 0x10000
702 JS_NEW_PRINTER(JSContext
*cx
, const char *name
, JSFunction
*fun
,
703 uintN indent
, JSBool pretty
)
707 jp
= (JSPrinter
*) JS_malloc(cx
, sizeof(JSPrinter
));
710 INIT_SPRINTER(cx
, &jp
->sprinter
, &jp
->pool
, 0);
711 JS_INIT_ARENA_POOL(&jp
->pool
, name
, 256, 1, &cx
->scriptStackQuota
);
712 jp
->indent
= indent
& ~JS_IN_GROUP_CONTEXT
;
714 jp
->grouped
= (indent
& JS_IN_GROUP_CONTEXT
) != 0;
719 jp
->localNames
= NULL
;
720 if (fun
&& FUN_INTERPRETED(fun
) && JS_GET_LOCAL_NAME_COUNT(fun
)) {
721 jp
->localNames
= js_GetLocalNameArray(cx
, fun
, &jp
->pool
);
722 if (!jp
->localNames
) {
723 js_DestroyPrinter(jp
);
731 js_DestroyPrinter(JSPrinter
*jp
)
733 JS_FinishArenaPool(&jp
->pool
);
734 JS_free(jp
->sprinter
.context
, jp
);
738 js_GetPrinterOutput(JSPrinter
*jp
)
743 cx
= jp
->sprinter
.context
;
744 if (!jp
->sprinter
.base
)
745 return cx
->runtime
->emptyString
;
746 str
= JS_NewStringCopyZ(cx
, jp
->sprinter
.base
);
749 JS_FreeArenaPool(&jp
->pool
);
750 INIT_SPRINTER(cx
, &jp
->sprinter
, &jp
->pool
, 0);
755 * NB: Indexed by SRC_DECL_* defines from jsemit.h.
757 static const char * const var_prefix
[] = {"var ", "const ", "let "};
760 VarPrefix(jssrcnote
*sn
)
762 if (sn
&& (SN_TYPE(sn
) == SRC_DECL
|| SN_TYPE(sn
) == SRC_GROUPASSIGN
)) {
763 ptrdiff_t type
= js_GetSrcNoteOffset(sn
, 0);
764 if ((uintN
)type
<= SRC_DECL_LET
)
765 return var_prefix
[type
];
771 js_printf(JSPrinter
*jp
, const char *format
, ...)
780 va_start(ap
, format
);
782 /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */
783 if (*format
== '\t') {
785 if (jp
->pretty
&& Sprint(&jp
->sprinter
, "%*s", jp
->indent
, "") < 0)
789 /* Suppress newlines (must be once per format, at the end) if not pretty. */
791 if (!jp
->pretty
&& format
[cc
= strlen(format
) - 1] == '\n') {
792 fp
= JS_strdup(jp
->sprinter
.context
, format
);
799 /* Allocate temp space, convert format, and put. */
800 bp
= JS_vsmprintf(format
, ap
); /* XXX vsaprintf */
802 JS_free(jp
->sprinter
.context
, fp
);
806 JS_ReportOutOfMemory(jp
->sprinter
.context
);
811 if (SprintPut(&jp
->sprinter
, bp
, (size_t)cc
) < 0)
820 js_puts(JSPrinter
*jp
, const char *s
)
822 return SprintCString(&jp
->sprinter
, s
) >= 0;
825 /************************************************************************/
827 typedef struct SprintStack
{
828 Sprinter sprinter
; /* sprinter for postfix to infix buffering */
829 ptrdiff_t *offsets
; /* stack of postfix string offsets */
830 jsbytecode
*opcodes
; /* parallel stack of JS opcodes */
831 uintN top
; /* top of stack index */
832 uintN inArrayInit
; /* array initialiser/comprehension level */
833 JSBool inGenExp
; /* in generator expression */
834 JSPrinter
*printer
; /* permanent output goes here */
838 * Find the depth of the operand stack when the interpreter reaches the given
839 * pc in script. pcstack must have space for least script->depth elements. On
840 * return it will contain pointers to opcodes that populated the interpreter's
841 * current operand stack.
843 * This function cannot raise an exception or error. However, due to a risk of
844 * potential bugs when modeling the stack, the function returns -1 if it
845 * detects an inconsistency in the model. Such an inconsistency triggers an
846 * assert in a debug build.
849 ReconstructPCStack(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
,
850 jsbytecode
**pcstack
);
852 #define FAILED_EXPRESSION_DECOMPILER ((char *) 1)
855 * Decompile a part of expression up to the given pc. The function returns
856 * NULL on out-of-memory, or the FAILED_EXPRESSION_DECOMPILER sentinel when
857 * the decompiler fails due to a bug and/or unimplemented feature, or the
858 * decompiled string on success.
861 DecompileExpression(JSContext
*cx
, JSScript
*script
, JSFunction
*fun
,
865 * Get a stacked offset from ss->sprinter.base, or if the stacked value |off|
866 * is negative, fetch the generating pc from printer->pcstack[-2 - off] and
867 * decompile the code that generated the missing value. This is used when
868 * reporting errors, where the model stack will lack |pcdepth| non-negative
869 * offsets (see DecompileExpression and DecompileCode).
871 * If the stacked offset is -1, return 0 to index the NUL padding at the start
872 * of ss->sprinter.base. If this happens, it means there is a decompiler bug
873 * to fix, but it won't violate memory safety.
876 GetOff(SprintStack
*ss
, uintN i
)
882 off
= ss
->offsets
[i
];
886 JS_ASSERT(off
<= -2);
887 JS_ASSERT(ss
->printer
->pcstack
);
888 if (off
< -2 && ss
->printer
->pcstack
) {
889 pc
= ss
->printer
->pcstack
[-2 - off
];
890 bytes
= DecompileExpression(ss
->sprinter
.context
, ss
->printer
->script
,
891 ss
->printer
->fun
, pc
);
894 if (bytes
!= FAILED_EXPRESSION_DECOMPILER
) {
895 off
= SprintCString(&ss
->sprinter
, bytes
);
898 ss
->offsets
[i
] = off
;
899 JS_free(ss
->sprinter
.context
, bytes
);
902 if (!ss
->sprinter
.base
&& SprintPut(&ss
->sprinter
, "", 0) >= 0) {
903 memset(ss
->sprinter
.base
, 0, ss
->sprinter
.offset
);
911 GetStr(SprintStack
*ss
, uintN i
)
916 * Must call GetOff before using ss->sprinter.base, since it may be null
917 * until bootstrapped by GetOff.
920 return OFF2STR(&ss
->sprinter
, off
);
924 * Gap between stacked strings to allow for insertion of parens and commas
925 * when auto-parenthesizing expressions and decompiling array initialisers
926 * (see the JSOP_NEWARRAY case in Decompile).
928 #define PAREN_SLOP (2 + 1)
931 * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME,
932 * JSOP_SETPROP, and JSOP_SETELEM, respectively. They are never stored in
933 * bytecode, so they don't preempt valid opcodes.
935 #define JSOP_GETPROP2 256
936 #define JSOP_GETELEM2 257
939 AddParenSlop(SprintStack
*ss
)
941 memset(OFF2STR(&ss
->sprinter
, ss
->sprinter
.offset
), 0, PAREN_SLOP
);
942 ss
->sprinter
.offset
+= PAREN_SLOP
;
946 PushOff(SprintStack
*ss
, ptrdiff_t off
, JSOp op
)
950 if (!SprintEnsureBuffer(&ss
->sprinter
, PAREN_SLOP
))
953 /* ss->top points to the next free slot; be paranoid about overflow. */
955 JS_ASSERT(top
< StackDepth(ss
->printer
->script
));
956 if (top
>= StackDepth(ss
->printer
->script
)) {
957 JS_ReportOutOfMemory(ss
->sprinter
.context
);
961 /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */
962 ss
->offsets
[top
] = off
;
963 ss
->opcodes
[top
] = (op
== JSOP_GETPROP2
) ? JSOP_GETPROP
964 : (op
== JSOP_GETELEM2
) ? JSOP_GETELEM
972 PopOffPrec(SprintStack
*ss
, uint8 prec
)
975 const JSCodeSpec
*topcs
;
978 /* ss->top points to the next free slot; be paranoid about underflow. */
985 off
= GetOff(ss
, top
);
986 topcs
= &js_CodeSpec
[ss
->opcodes
[top
]];
987 if (topcs
->prec
!= 0 && topcs
->prec
< prec
) {
988 ss
->sprinter
.offset
= ss
->offsets
[top
] = off
- 2;
989 off
= Sprint(&ss
->sprinter
, "(%s)", OFF2STR(&ss
->sprinter
, off
));
991 ss
->sprinter
.offset
= off
;
997 PopStrPrec(SprintStack
*ss
, uint8 prec
)
1001 off
= PopOffPrec(ss
, prec
);
1002 return OFF2STR(&ss
->sprinter
, off
);
1006 PopOff(SprintStack
*ss
, JSOp op
)
1008 return PopOffPrec(ss
, js_CodeSpec
[op
].prec
);
1012 PopStr(SprintStack
*ss
, JSOp op
)
1014 return PopStrPrec(ss
, js_CodeSpec
[op
].prec
);
1017 typedef struct TableEntry
{
1021 jsint order
; /* source order for stable tableswitch sort */
1025 CompareOffsets(void *arg
, const void *v1
, const void *v2
, int *result
)
1027 ptrdiff_t offset_diff
;
1028 const TableEntry
*te1
= (const TableEntry
*) v1
,
1029 *te2
= (const TableEntry
*) v2
;
1031 offset_diff
= te1
->offset
- te2
->offset
;
1032 *result
= (offset_diff
== 0 ? te1
->order
- te2
->order
1033 : offset_diff
< 0 ? -1
1039 SprintDoubleValue(Sprinter
*sp
, jsval v
, JSOp
*opp
)
1043 char *s
, buf
[DTOSTR_STANDARD_BUFFER_SIZE
];
1045 JS_ASSERT(JSVAL_IS_DOUBLE(v
));
1046 d
= *JSVAL_TO_DOUBLE(v
);
1047 if (JSDOUBLE_IS_NEGZERO(d
)) {
1048 todo
= SprintCString(sp
, "-0");
1050 } else if (!JSDOUBLE_IS_FINITE(d
)) {
1051 /* Don't use Infinity and NaN, they're mutable. */
1052 todo
= SprintCString(sp
,
1060 s
= JS_dtostr(buf
, sizeof buf
, DTOSTR_STANDARD
, 0, d
);
1062 JS_ReportOutOfMemory(sp
->context
);
1065 JS_ASSERT(strcmp(s
, js_Infinity_str
) &&
1067 strcmp(s
+ 1, js_Infinity_str
)) &&
1068 strcmp(s
, js_NaN_str
));
1069 todo
= Sprint(sp
, s
);
1075 Decompile(SprintStack
*ss
, jsbytecode
*pc
, intN nb
, JSOp nextop
);
1078 DecompileSwitch(SprintStack
*ss
, TableEntry
*table
, uintN tableLength
,
1079 jsbytecode
*pc
, ptrdiff_t switchLength
,
1080 ptrdiff_t defaultOffset
, JSBool isCondSwitch
)
1084 ptrdiff_t off
, off2
, diff
, caseExprOff
, todo
;
1090 cx
= ss
->sprinter
.context
;
1093 /* JSOP_CONDSWITCH doesn't pop, unlike JSOP_{LOOKUP,TABLE}SWITCH. */
1094 off
= isCondSwitch
? GetOff(ss
, ss
->top
-1) : PopOff(ss
, JSOP_NOP
);
1095 lval
= OFF2STR(&ss
->sprinter
, off
);
1097 js_printf(jp
, "\tswitch (%s) {\n", lval
);
1100 diff
= table
[0].offset
- defaultOffset
;
1103 js_printf(jp
, "\t%s:\n", js_default_str
);
1105 if (!Decompile(ss
, pc
+ defaultOffset
, diff
, JSOP_NOP
))
1110 caseExprOff
= isCondSwitch
? JSOP_CONDSWITCH_LENGTH
: 0;
1112 for (i
= 0; i
< tableLength
; i
++) {
1113 off
= table
[i
].offset
;
1114 off2
= (i
+ 1 < tableLength
) ? table
[i
+ 1].offset
: switchLength
;
1118 ptrdiff_t nextCaseExprOff
;
1121 * key encodes the JSOP_CASE bytecode's offset from switchtop.
1122 * The next case expression follows immediately, unless we are
1125 nextCaseExprOff
= (ptrdiff_t)JSVAL_TO_INT(key
);
1126 nextCaseExprOff
+= js_CodeSpec
[pc
[nextCaseExprOff
]].length
;
1128 if (!Decompile(ss
, pc
+ caseExprOff
,
1129 nextCaseExprOff
- caseExprOff
, JSOP_NOP
)) {
1132 caseExprOff
= nextCaseExprOff
;
1134 /* Balance the stack as if this JSOP_CASE matched. */
1138 * key comes from an atom, not the decompiler, so we need to
1139 * quote it if it's a string literal. But if table[i].label
1140 * is non-null, key was constant-propagated and label is the
1141 * name of the const we should show as the case label. We set
1142 * key to undefined so this identifier is escaped, if required
1143 * by non-ASCII characters, but not quoted, by QuoteString.
1146 if (table
[i
].label
) {
1147 str
= ATOM_TO_STRING(table
[i
].label
);
1149 } else if (JSVAL_IS_DOUBLE(key
)) {
1152 todo
= SprintDoubleValue(&ss
->sprinter
, key
, &junk
);
1155 str
= js_ValueToString(cx
, key
);
1160 rval
= OFF2STR(&ss
->sprinter
, todo
);
1162 rval
= QuoteString(&ss
->sprinter
, str
, (jschar
)
1163 (JSVAL_IS_STRING(key
) ? '"' : 0));
1167 RETRACT(&ss
->sprinter
, rval
);
1169 js_printf(jp
, "\tcase %s:\n", rval
);
1173 if (off
<= defaultOffset
&& defaultOffset
< off2
) {
1174 diff
= defaultOffset
- off
;
1176 if (!Decompile(ss
, pc
+ off
, diff
, JSOP_NOP
))
1178 off
= defaultOffset
;
1181 js_printf(jp
, "\t%s:\n", js_default_str
);
1184 if (!Decompile(ss
, pc
+ off
, off2
- off
, JSOP_NOP
))
1188 /* Re-balance as if last JSOP_CASE or JSOP_DEFAULT mismatched. */
1194 if (defaultOffset
== switchLength
) {
1196 js_printf(jp
, "\t%s:;\n", js_default_str
);
1199 js_printf(jp
, "\t}\n");
1201 /* By the end of a JSOP_CONDSWITCH, the discriminant has been popped. */
1207 #define LOCAL_ASSERT_CUSTOM(expr, BAD_EXIT) \
1210 if (!(expr)) { BAD_EXIT; } \
1213 #define LOCAL_ASSERT_RV(expr, rv) \
1214 LOCAL_ASSERT_CUSTOM(expr, return (rv))
1217 GetArgOrVarAtom(JSPrinter
*jp
, uintN slot
)
1221 LOCAL_ASSERT_RV(jp
->fun
, NULL
);
1222 LOCAL_ASSERT_RV(slot
< (uintN
) JS_GET_LOCAL_NAME_COUNT(jp
->fun
), NULL
);
1223 name
= JS_LOCAL_NAME_TO_ATOM(jp
->localNames
[slot
]);
1224 #if !JS_HAS_DESTRUCTURING
1225 LOCAL_ASSERT_RV(name
, NULL
);
1231 GetLocal(SprintStack
*ss
, jsint i
)
1240 JSScopeProperty
*sprop
;
1243 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, "")
1245 off
= ss
->offsets
[i
];
1247 return OFF2STR(&ss
->sprinter
, off
);
1250 * We must be called from js_DecompileValueGenerator (via Decompile) when
1251 * dereferencing a local that's undefined or null. Search script->objects
1252 * for the block containing this local by its stack index, i.
1254 cx
= ss
->sprinter
.context
;
1255 script
= ss
->printer
->script
;
1256 LOCAL_ASSERT(script
->objectsOffset
!= 0);
1257 for (j
= 0, n
= JS_SCRIPT_OBJECTS(script
)->length
; ; j
++) {
1258 LOCAL_ASSERT(j
< n
);
1259 JS_GET_SCRIPT_OBJECT(script
, j
, obj
);
1260 if (OBJ_GET_CLASS(cx
, obj
) == &js_BlockClass
) {
1261 depth
= OBJ_BLOCK_DEPTH(cx
, obj
);
1262 count
= OBJ_BLOCK_COUNT(cx
, obj
);
1263 if ((jsuint
)(i
- depth
) < (jsuint
)count
)
1269 for (sprop
= OBJ_SCOPE(obj
)->lastProp
; sprop
; sprop
= sprop
->parent
) {
1270 if (sprop
->shortid
== i
)
1274 LOCAL_ASSERT(sprop
&& JSID_IS_ATOM(sprop
->id
));
1275 atom
= JSID_TO_ATOM(sprop
->id
);
1276 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
1279 RETRACT(&ss
->sprinter
, rval
);
1286 IsVarSlot(JSPrinter
*jp
, jsbytecode
*pc
, jsint
*indexp
)
1290 slot
= GET_SLOTNO(pc
);
1291 if (slot
< jp
->script
->nfixed
) {
1292 /* The slot refers to a variable with name stored in jp->localNames. */
1293 *indexp
= jp
->fun
->nargs
+ slot
;
1297 /* We have a local which index is relative to the stack base. */
1298 slot
-= jp
->script
->nfixed
;
1299 JS_ASSERT(slot
< StackDepth(jp
->script
));
1304 #if JS_HAS_DESTRUCTURING
1306 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
1307 #define LOAD_OP_DATA(pc) (oplen = (cs = &js_CodeSpec[op=(JSOp)*pc])->length)
1310 DecompileDestructuring(SprintStack
*ss
, jsbytecode
*pc
, jsbytecode
*endpc
);
1313 DecompileDestructuringLHS(SprintStack
*ss
, jsbytecode
*pc
, jsbytecode
*endpc
,
1319 const JSCodeSpec
*cs
;
1322 const char *lval
, *xval
;
1327 cx
= ss
->sprinter
.context
;
1334 todo
= SprintPut(&ss
->sprinter
, ", ", 2);
1338 pc
= DecompileDestructuring(ss
, pc
, endpc
);
1344 lval
= PopStr(ss
, JSOP_NOP
);
1345 todo
= SprintCString(&ss
->sprinter
, lval
);
1346 if (op
== JSOP_POPN
)
1348 LOCAL_ASSERT(*pc
== JSOP_POP
);
1354 LOCAL_ASSERT(pc
[oplen
] == JSOP_POP
|| pc
[oplen
] == JSOP_POPN
);
1357 case JSOP_SETLOCALPOP
:
1360 if (op
== JSOP_SETARG
) {
1361 atom
= GetArgOrVarAtom(jp
, GET_SLOTNO(pc
));
1363 } else if (op
== JSOP_SETGVAR
) {
1364 GET_ATOM_FROM_BYTECODE(jp
->script
, pc
, 0, atom
);
1365 } else if (IsVarSlot(jp
, pc
, &i
)) {
1366 atom
= GetArgOrVarAtom(jp
, i
);
1369 lval
= GetLocal(ss
, i
);
1372 lval
= js_AtomToPrintableString(cx
, atom
);
1374 todo
= SprintCString(&ss
->sprinter
, lval
);
1375 if (op
!= JSOP_SETLOCALPOP
) {
1380 if (op
== JSOP_POPN
)
1382 LOCAL_ASSERT(op
== JSOP_POP
);
1388 * We may need to auto-parenthesize the left-most value decompiled
1389 * here, so add back PAREN_SLOP temporarily. Then decompile until the
1390 * opcode that would reduce the stack depth to (ss->top-1), which we
1391 * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for
1394 todo
= ss
->sprinter
.offset
;
1395 ss
->sprinter
.offset
= todo
+ PAREN_SLOP
;
1396 pc
= Decompile(ss
, pc
, -((intN
)ss
->top
), JSOP_NOP
);
1402 LOCAL_ASSERT(op
== JSOP_ENUMELEM
|| op
== JSOP_ENUMCONSTELEM
);
1403 xval
= PopStr(ss
, JSOP_NOP
);
1404 lval
= PopStr(ss
, JSOP_GETPROP
);
1405 ss
->sprinter
.offset
= todo
;
1406 if (*lval
== '\0') {
1407 /* lval is from JSOP_BINDNAME, so just print xval. */
1408 todo
= SprintCString(&ss
->sprinter
, xval
);
1409 } else if (*xval
== '\0') {
1410 /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */
1411 todo
= SprintCString(&ss
->sprinter
, lval
);
1413 todo
= Sprint(&ss
->sprinter
,
1414 (JOF_OPMODE(ss
->opcodes
[ss
->top
+1]) == JOF_XMLNAME
)
1425 LOCAL_ASSERT(pc
< endpc
);
1431 * Starting with a SRC_DESTRUCT-annotated JSOP_DUP, decompile a destructuring
1432 * left-hand side object or array initialiser, including nested destructuring
1433 * initialisers. On successful return, the decompilation will be pushed on ss
1434 * and the return value will point to the POP or GROUP bytecode following the
1435 * destructuring expression.
1437 * At any point, if pc is equal to endpc and would otherwise advance, we stop
1438 * immediately and return endpc.
1441 DecompileDestructuring(SprintStack
*ss
, jsbytecode
*pc
, jsbytecode
*endpc
)
1447 const JSCodeSpec
*cs
;
1457 LOCAL_ASSERT(*pc
== JSOP_DUP
);
1458 pc
+= JSOP_DUP_LENGTH
;
1461 * Set head so we can rewrite '[' to '{' as needed. Back up PAREN_SLOP
1462 * chars so the destructuring decompilation accumulates contiguously in
1463 * ss->sprinter starting with "[".
1465 head
= SprintPut(&ss
->sprinter
, "[", 1);
1466 if (head
< 0 || !PushOff(ss
, head
, JSOP_NOP
))
1468 ss
->sprinter
.offset
-= PAREN_SLOP
;
1469 LOCAL_ASSERT(head
== ss
->sprinter
.offset
- 1);
1470 LOCAL_ASSERT(*OFF2STR(&ss
->sprinter
, head
) == '[');
1472 cx
= ss
->sprinter
.context
;
1476 while (pc
< endpc
) {
1477 #if JS_HAS_DESTRUCTURING_SHORTHAND
1478 ptrdiff_t nameoff
= -1;
1489 /* Handle the optimized number-pushing opcodes. */
1490 case JSOP_ZERO
: d
= i
= 0; goto do_getelem
;
1491 case JSOP_ONE
: d
= i
= 1; goto do_getelem
;
1492 case JSOP_UINT16
: d
= i
= GET_UINT16(pc
); goto do_getelem
;
1493 case JSOP_UINT24
: d
= i
= GET_UINT24(pc
); goto do_getelem
;
1494 case JSOP_INT8
: d
= i
= GET_INT8(pc
); goto do_getelem
;
1495 case JSOP_INT32
: d
= i
= GET_INT32(pc
); goto do_getelem
;
1498 GET_ATOM_FROM_BYTECODE(jp
->script
, pc
, 0, atom
);
1499 d
= *ATOM_TO_DOUBLE(atom
);
1500 LOCAL_ASSERT(JSDOUBLE_IS_FINITE(d
) && !JSDOUBLE_IS_NEGZERO(d
));
1504 sn
= js_GetSrcNote(jp
->script
, pc
);
1509 LOCAL_ASSERT(op
== JSOP_GETELEM
);
1511 /* Distinguish object from array by opcode or source note. */
1512 if (sn
&& SN_TYPE(sn
) == SRC_INITPROP
) {
1513 *OFF2STR(&ss
->sprinter
, head
) = '{';
1514 if (Sprint(&ss
->sprinter
, "%g: ", d
) < 0)
1517 /* Sanity check for the gnarly control flow above. */
1518 LOCAL_ASSERT(i
== d
);
1520 /* Fill in any holes (holes at the end don't matter). */
1521 while (++lasti
< i
) {
1522 if (SprintPut(&ss
->sprinter
, ", ", 2) < 0)
1529 atom
= cx
->runtime
->atomState
.lengthAtom
;
1530 goto do_destructure_atom
;
1534 GET_ATOM_FROM_BYTECODE(jp
->script
, pc
, 0, atom
);
1535 do_destructure_atom
:
1536 *OFF2STR(&ss
->sprinter
, head
) = '{';
1537 str
= ATOM_TO_STRING(atom
);
1538 #if JS_HAS_DESTRUCTURING_SHORTHAND
1539 nameoff
= ss
->sprinter
.offset
;
1541 if (!QuoteString(&ss
->sprinter
, str
,
1542 js_IsIdentifier(str
) ? 0 : (jschar
)'\'')) {
1545 if (SprintPut(&ss
->sprinter
, ": ", 2) < 0)
1558 * Decompile the left-hand side expression whose bytecode starts at pc
1559 * and continues for a bounded number of bytecodes or stack operations
1560 * (and which in any event stops before endpc).
1562 pc
= DecompileDestructuringLHS(ss
, pc
, endpc
, &hole
);
1566 #if JS_HAS_DESTRUCTURING_SHORTHAND
1568 ptrdiff_t offset
, initlen
;
1570 offset
= ss
->sprinter
.offset
;
1571 LOCAL_ASSERT(*OFF2STR(&ss
->sprinter
, offset
) == '\0');
1572 initlen
= offset
- nameoff
;
1573 LOCAL_ASSERT(initlen
>= 4);
1575 /* Early check to rule out odd "name: lval" length. */
1576 if (((size_t)initlen
& 1) == 0) {
1581 * Even "name: lval" string length: check for "x: x" and the
1582 * like, and apply the shorthand if we can.
1584 namelen
= (size_t)(initlen
- 2) >> 1;
1585 name
= OFF2STR(&ss
->sprinter
, nameoff
);
1586 if (!strncmp(name
+ namelen
, ": ", 2) &&
1587 !strncmp(name
, name
+ namelen
+ 2, namelen
)) {
1588 offset
-= namelen
+ 2;
1589 *OFF2STR(&ss
->sprinter
, offset
) = '\0';
1590 ss
->sprinter
.offset
= offset
;
1596 if (pc
== endpc
|| *pc
!= JSOP_DUP
)
1600 * Check for SRC_DESTRUCT on this JSOP_DUP, which would mean another
1601 * destructuring initialiser abuts this one, and we should stop. This
1602 * happens with source of the form '[a] = [b] = c'.
1604 sn
= js_GetSrcNote(jp
->script
, pc
);
1605 if (sn
&& SN_TYPE(sn
) == SRC_DESTRUCT
)
1608 if (!hole
&& SprintPut(&ss
->sprinter
, ", ", 2) < 0)
1611 pc
+= JSOP_DUP_LENGTH
;
1615 lval
= OFF2STR(&ss
->sprinter
, head
);
1616 if (SprintPut(&ss
->sprinter
, (*lval
== '[') ? "]" : "}", 1) < 0)
1622 DecompileGroupAssignment(SprintStack
*ss
, jsbytecode
*pc
, jsbytecode
*endpc
,
1623 jssrcnote
*sn
, ptrdiff_t *todop
)
1626 const JSCodeSpec
*cs
;
1627 uintN oplen
, start
, end
, i
;
1633 LOCAL_ASSERT(op
== JSOP_PUSH
|| op
== JSOP_GETLOCAL
);
1635 todo
= Sprint(&ss
->sprinter
, "%s[", VarPrefix(sn
));
1636 if (todo
< 0 || !PushOff(ss
, todo
, JSOP_NOP
))
1638 ss
->sprinter
.offset
-= PAREN_SLOP
;
1644 pc
= DecompileDestructuringLHS(ss
, pc
, endpc
, &hole
);
1650 if (op
!= JSOP_PUSH
&& op
!= JSOP_GETLOCAL
)
1652 if (!hole
&& SprintPut(&ss
->sprinter
, ", ", 2) < 0)
1656 LOCAL_ASSERT(op
== JSOP_POPN
);
1657 if (SprintPut(&ss
->sprinter
, "] = [", 5) < 0)
1661 start
= end
- GET_UINT16(pc
);
1662 for (i
= start
; i
< end
; i
++) {
1663 rval
= GetStr(ss
, i
);
1664 if (Sprint(&ss
->sprinter
,
1665 (i
== start
) ? "%s" : ", %s",
1666 (i
== end
- 1 && *rval
== '\0') ? ", " : rval
) < 0) {
1671 if (SprintPut(&ss
->sprinter
, "]", 1) < 0)
1673 ss
->sprinter
.offset
= ss
->offsets
[i
];
1682 #endif /* JS_HAS_DESTRUCTURING */
1685 InitSprintStack(JSContext
*cx
, SprintStack
*ss
, JSPrinter
*jp
, uintN depth
)
1687 size_t offsetsz
, opcodesz
;
1690 INIT_SPRINTER(cx
, &ss
->sprinter
, &cx
->tempPool
, PAREN_SLOP
);
1692 /* Allocate the parallel (to avoid padding) offset and opcode stacks. */
1693 offsetsz
= depth
* sizeof(ptrdiff_t);
1694 opcodesz
= depth
* sizeof(jsbytecode
);
1695 JS_ARENA_ALLOCATE(space
, &cx
->tempPool
, offsetsz
+ opcodesz
);
1697 js_ReportOutOfScriptQuota(cx
);
1700 ss
->offsets
= (ptrdiff_t *) space
;
1701 ss
->opcodes
= (jsbytecode
*) ((char *)space
+ offsetsz
);
1703 ss
->top
= ss
->inArrayInit
= 0;
1704 ss
->inGenExp
= JS_FALSE
;
1710 * If nb is non-negative, decompile nb bytecodes starting at pc. Otherwise
1711 * the decompiler starts at pc and continues until it reaches an opcode for
1712 * which decompiling would result in the stack depth equaling -(nb + 1).
1714 * The nextop parameter is either JSOP_NOP or the "next" opcode in order of
1715 * abstract interpretation (not necessarily physically next in a bytecode
1716 * vector). So nextop is JSOP_POP for the last operand in a comma expression,
1717 * or JSOP_AND for the right operand of &&.
1720 Decompile(SprintStack
*ss
, jsbytecode
*pc
, intN nb
, JSOp nextop
)
1723 JSPrinter
*jp
, *jp2
;
1724 jsbytecode
*startpc
, *endpc
, *pc2
, *done
;
1725 ptrdiff_t tail
, todo
, len
, oplen
, cond
, next
;
1726 JSOp op
, lastop
, saveop
;
1727 const JSCodeSpec
*cs
;
1728 jssrcnote
*sn
, *sn2
;
1729 const char *lval
, *rval
, *xval
, *fmt
, *token
;
1737 #if JS_HAS_XML_SUPPORT
1738 JSBool foreach
, inXML
, quoteAttr
;
1740 #define inXML JS_FALSE
1744 static const char exception_cookie
[] = "/*EXCEPTION*/";
1745 static const char retsub_pc_cookie
[] = "/*RETSUB_PC*/";
1746 static const char iter_cookie
[] = "/*ITER*/";
1747 static const char forelem_cookie
[] = "/*FORELEM*/";
1748 static const char with_cookie
[] = "/*WITH*/";
1749 static const char dot_format
[] = "%s.%s";
1750 static const char index_format
[] = "%s[%s]";
1751 static const char predot_format
[] = "%s%s.%s";
1752 static const char postdot_format
[] = "%s.%s%s";
1753 static const char preindex_format
[] = "%s%s[%s]";
1754 static const char postindex_format
[] = "%s[%s]%s";
1755 static const char ss_format
[] = "%s%s";
1756 static const char sss_format
[] = "%s%s%s";
1758 /* Argument and variables decompilation uses the following to share code. */
1759 JS_STATIC_ASSERT(ARGNO_LEN
== SLOTNO_LEN
);
1764 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
1765 #define DECOMPILE_CODE(pc,nb) if (!Decompile(ss, pc, nb, JSOP_NOP)) return NULL
1766 #define NEXT_OP(pc) (((pc) + (len) == endpc) ? nextop : pc[len])
1767 #define TOP_STR() GetStr(ss, ss->top - 1)
1768 #define POP_STR() PopStr(ss, op)
1769 #define POP_STR_PREC(prec) PopStrPrec(ss, prec)
1772 * Pop a condition expression for if/for/while. JSOP_IFEQ's precedence forces
1773 * extra parens around assignment, which avoids a strict-mode warning.
1775 #define POP_COND_STR() \
1776 PopStr(ss, (js_CodeSpec[ss->opcodes[ss->top - 1]].format & JOF_SET) \
1781 * Callers know that ATOM_IS_STRING(atom), and we leave it to the optimizer to
1782 * common ATOM_TO_STRING(atom) here and near the call sites.
1784 #define ATOM_IS_IDENTIFIER(atom) js_IsIdentifier(ATOM_TO_STRING(atom))
1785 #define ATOM_IS_KEYWORD(atom) \
1786 (js_CheckKeyword(JSSTRING_CHARS(ATOM_TO_STRING(atom)), \
1787 JSSTRING_LENGTH(ATOM_TO_STRING(atom))) != TOK_EOF)
1790 * Given an atom already fetched from jp->script's atom map, quote/escape its
1791 * string appropriately into rval, and select fmt from the quoted and unquoted
1794 #define GET_QUOTE_AND_FMT(qfmt, ufmt, rval) \
1797 if (!ATOM_IS_IDENTIFIER(atom)) { \
1804 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), quote_); \
1809 #define LOAD_ATOM(PCOFF) \
1810 GET_ATOM_FROM_BYTECODE(jp->script, pc, PCOFF, atom)
1812 #define LOAD_OBJECT(PCOFF) \
1813 GET_OBJECT_FROM_BYTECODE(jp->script, pc, PCOFF, obj)
1815 #define LOAD_FUNCTION(PCOFF) \
1816 GET_FUNCTION_FROM_BYTECODE(jp->script, pc, PCOFF, fun)
1818 #define LOAD_REGEXP(PCOFF) \
1819 GET_REGEXP_FROM_BYTECODE(jp->script, pc, PCOFF, obj)
1821 #define GET_SOURCE_NOTE_ATOM(sn, atom) \
1823 jsatomid atomIndex_ = (jsatomid) js_GetSrcNoteOffset((sn), 0); \
1825 LOCAL_ASSERT(atomIndex_ < jp->script->atomMap.length); \
1826 (atom) = jp->script->atomMap.vector[atomIndex_]; \
1830 * Get atom from jp->script's atom map, quote/escape its string appropriately
1831 * into rval, and select fmt from the quoted and unquoted alternatives.
1833 #define GET_ATOM_QUOTE_AND_FMT(qfmt, ufmt, rval) \
1836 GET_QUOTE_AND_FMT(qfmt, ufmt, rval); \
1840 * Per spec, new x(y).z means (new x(y))).z. For example new (x(y).z) must
1841 * decompile with the constructor parenthesized, but new x.z should not. The
1842 * normal rules give x(y).z and x.z identical precedence: both are produced by
1845 * Therefore, we need to know in case JSOP_NEW whether the constructor
1846 * expression contains any unparenthesized function calls. So when building a
1847 * MemberExpression or CallExpression, we set ss->opcodes[n] to JSOP_CALL if
1848 * this is true. x(y).z gets JSOP_CALL, not JSOP_GETPROP.
1850 #define PROPAGATE_CALLNESS() \
1852 if (ss->opcodes[ss->top - 1] == JSOP_CALL) \
1853 saveop = JSOP_CALL; \
1856 cx
= ss
->sprinter
.context
;
1857 JS_CHECK_RECURSION(cx
, return NULL
);
1861 endpc
= (nb
< 0) ? jp
->script
->code
+ jp
->script
->length
: pc
+ nb
;
1863 todo
= -2; /* NB: different from Sprint() error return. */
1867 #if JS_HAS_XML_SUPPORT
1868 foreach
= inXML
= quoteAttr
= JS_FALSE
;
1871 while (nb
< 0 || pc
< endpc
) {
1873 * Move saveop to lastop so prefixed bytecodes can take special action
1874 * while sharing maximal code. Set op and saveop to the new bytecode,
1875 * use op in POP_STR to trigger automatic parenthesization, but push
1876 * saveop at the bottom of the loop if this op pushes. Thus op may be
1877 * set to nop or otherwise mutated to suppress auto-parens.
1881 cs
= &js_CodeSpec
[op
];
1882 if (cs
->format
& JOF_INDEXBASE
) {
1884 * The decompiler uses js_GetIndexFromBytecode to get atoms and
1885 * objects and ignores these suffix/prefix bytecodes, thus
1886 * simplifying code that must process JSOP_GETTER/JSOP_SETTER
1893 cs
= &js_CodeSpec
[op
];
1896 len
= oplen
= cs
->length
;
1898 if (nb
< 0 && -(nb
+ 1) == (intN
)ss
->top
- cs
->nuses
+ cs
->ndefs
)
1902 * Save source literal associated with JS now before the following
1903 * rewrite changes op. See bug 380197.
1905 token
= CodeToken
[op
];
1907 if (pc
+ oplen
== jp
->dvgfence
) {
1909 uint32 format
, mode
, type
;
1912 * Rewrite non-get ops to their "get" format if the error is in
1913 * the bytecode at pc, so we don't decompile more than the error
1916 fp
= js_GetScriptedCaller(cx
, NULL
);
1917 format
= cs
->format
;
1918 if (((fp
&& fp
->regs
&& pc
== fp
->regs
->pc
) ||
1919 (pc
== startpc
&& cs
->nuses
!= 0)) &&
1920 format
& (JOF_SET
|JOF_DEL
|JOF_INCDEC
|JOF_FOR
|JOF_VARPROP
)) {
1921 mode
= JOF_MODE(format
);
1922 if (mode
== JOF_NAME
) {
1924 * JOF_NAME does not imply JOF_ATOM, so we must check for
1925 * the QARG and QVAR format types, and translate those to
1926 * JSOP_GETARG or JSOP_GETLOCAL appropriately, instead of
1929 type
= JOF_TYPE(format
);
1930 op
= (type
== JOF_QARG
)
1932 : (type
== JOF_LOCAL
)
1936 i
= cs
->nuses
- js_CodeSpec
[op
].nuses
;
1938 PopOff(ss
, JSOP_NOP
);
1941 * We must replace the faulting pc's bytecode with a
1942 * corresponding JSOP_GET* code. For JSOP_SET{PROP,ELEM},
1943 * we must use the "2nd" form of JSOP_GET{PROP,ELEM}, to
1944 * throw away the assignment op's right-hand operand and
1945 * decompile it as if it were a GET of its left-hand
1948 if (mode
== JOF_PROP
) {
1949 op
= (JSOp
) ((format
& JOF_SET
)
1952 } else if (mode
== JOF_ELEM
) {
1953 op
= (JSOp
) ((format
& JOF_SET
)
1958 * Unknown mode (including mode 0) means that op is
1959 * uncategorized for our purposes, so we must write
1960 * per-op special case code here.
1964 case JSOP_ENUMCONSTELEM
:
1967 #if JS_HAS_LVALUE_RETURN
1972 case JSOP_GETTHISPROP
:
1974 * NB: JSOP_GETTHISPROP can't fail due to |this|
1975 * being null or undefined at runtime (beware that
1976 * this may change for ES4). Therefore any error
1977 * resulting from this op must be due to the value
1978 * of the property accessed via |this|, so do not
1979 * rewrite op to JSOP_THIS.
1981 * The next two cases should not change op if
1982 * js_DecompileValueGenerator was called from the
1983 * the property getter. They should rewrite only
1984 * if the base object in the arg/var/local is null
1985 * or undefined. FIXME: bug 431569.
1988 case JSOP_GETARGPROP
:
1991 case JSOP_GETLOCALPROP
:
2002 if (op
>= JSOP_LIMIT
) {
2005 saveop
= JSOP_GETPROP
;
2008 saveop
= JSOP_GETELEM
;
2013 LOCAL_ASSERT(js_CodeSpec
[saveop
].length
== oplen
||
2014 JOF_TYPE(format
) == JOF_SLOTATOM
);
2016 jp
->dvgfence
= NULL
;
2020 switch (cs
->nuses
) {
2022 sn
= js_GetSrcNote(jp
->script
, pc
);
2023 if (sn
&& SN_TYPE(sn
) == SRC_ASSIGNOP
) {
2025 * Avoid over-parenthesizing y in x op= y based on its
2026 * expansion: x = x op y (replace y by z = w to see the
2029 op
= (JSOp
) pc
[oplen
];
2032 /* Print only the right operand of the assignment-op. */
2033 todo
= SprintCString(&ss
->sprinter
, rval
);
2035 } else if (!inXML
) {
2036 rval
= POP_STR_PREC(cs
->prec
+ !!(cs
->format
& JOF_LEFTASSOC
));
2037 lval
= POP_STR_PREC(cs
->prec
+ !(cs
->format
& JOF_LEFTASSOC
));
2038 todo
= Sprint(&ss
->sprinter
, "%s %s %s",
2041 /* In XML, just concatenate the two operands. */
2042 LOCAL_ASSERT(op
== JSOP_ADD
);
2045 todo
= Sprint(&ss
->sprinter
, ss_format
, lval
, rval
);
2051 todo
= Sprint(&ss
->sprinter
, ss_format
, token
, rval
);
2055 todo
= SprintCString(&ss
->sprinter
, token
);
2066 * Check for a do-while loop, a for-loop with an empty
2067 * initializer part, a labeled statement, a function
2068 * definition, or try/finally.
2070 sn
= js_GetSrcNote(jp
->script
, pc
);
2072 switch (sn
? SN_TYPE(sn
) : SRC_NULL
) {
2075 tail
= js_GetSrcNoteOffset(sn
, 0) - 1;
2076 LOCAL_ASSERT(pc
[tail
] == JSOP_IFNE
||
2077 pc
[tail
] == JSOP_IFNEX
);
2078 js_printf(jp
, "\tdo {\n");
2080 DECOMPILE_CODE(pc
, tail
);
2082 js_printf(jp
, "\t} while (%s);\n", POP_COND_STR());
2084 len
= js_CodeSpec
[*pc
].length
;
2092 JS_ASSERT(SN_TYPE(sn
) == SRC_FOR
);
2094 /* Skip the JSOP_NOP or JSOP_POP bytecode. */
2095 pc
+= JSOP_NOP_LENGTH
;
2097 /* Get the cond, next, and loop-closing tail offsets. */
2098 cond
= js_GetSrcNoteOffset(sn
, 0);
2099 next
= js_GetSrcNoteOffset(sn
, 1);
2100 tail
= js_GetSrcNoteOffset(sn
, 2);
2103 * If this loop has a condition, then pc points at a goto
2104 * targeting the condition.
2108 LOCAL_ASSERT(*pc
== JSOP_GOTO
|| *pc
== JSOP_GOTOX
);
2109 pc2
+= (*pc
== JSOP_GOTO
) ? JSOP_GOTO_LENGTH
: JSOP_GOTOX_LENGTH
;
2111 LOCAL_ASSERT(tail
+ GetJumpOffset(pc
+tail
, pc
+tail
) == pc2
- pc
);
2113 /* Print the keyword and the possibly empty init-part. */
2114 js_printf(jp
, "\tfor (%s;", rval
);
2117 /* Decompile the loop condition. */
2118 DECOMPILE_CODE(pc
+ cond
, tail
- cond
);
2119 js_printf(jp
, " %s", POP_STR());
2122 /* Need a semicolon whether or not there was a cond. */
2127 * Decompile the loop updater. It may end in a JSOP_POP
2128 * that we skip; or in a JSOP_POPN that we do not skip,
2129 * followed by a JSOP_NOP (skipped as if it's a POP).
2130 * We cope with the difference between these two cases
2131 * by checking for stack imbalance and popping if there
2134 uintN saveTop
= ss
->top
;
2136 DECOMPILE_CODE(pc
+ next
, cond
- next
- JSOP_POP_LENGTH
);
2137 LOCAL_ASSERT(ss
->top
- saveTop
<= 1U);
2138 rval
= (ss
->top
== saveTop
)
2139 ? ss
->sprinter
.base
+ ss
->sprinter
.offset
2141 js_printf(jp
, " %s", rval
);
2144 /* Do the loop body. */
2145 js_printf(jp
, ") {\n");
2148 DECOMPILE_CODE(pc2
, next
);
2150 js_printf(jp
, "\t}\n");
2152 /* Set len so pc skips over the entire loop. */
2153 len
= tail
+ js_CodeSpec
[pc
[tail
]].length
;
2157 GET_SOURCE_NOTE_ATOM(sn
, atom
);
2159 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
2162 RETRACT(&ss
->sprinter
, rval
);
2163 js_printf(jp
, "\t%s:\n", rval
);
2167 case SRC_LABELBRACE
:
2168 GET_SOURCE_NOTE_ATOM(sn
, atom
);
2169 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
2172 RETRACT(&ss
->sprinter
, rval
);
2173 js_printf(jp
, "\t%s: {\n", rval
);
2179 js_printf(jp
, "\t}\n");
2183 JS_GET_SCRIPT_FUNCTION(jp
->script
,
2184 js_GetSrcNoteOffset(sn
, 0),
2188 jp2
= JS_NEW_PRINTER(cx
, "nested_function", fun
,
2189 jp
->indent
, jp
->pretty
);
2192 ok
= js_DecompileFunction(jp2
);
2193 if (ok
&& jp2
->sprinter
.base
)
2194 js_puts(jp
, jp2
->sprinter
.base
);
2195 js_DestroyPrinter(jp2
);
2198 js_puts(jp
, "\n\n");
2202 js_printf(jp
, "\t{\n");
2204 len
= js_GetSrcNoteOffset(sn
, 0);
2205 DECOMPILE_CODE(pc
+ oplen
, len
- oplen
);
2207 js_printf(jp
, "\t}\n");
2215 #if JS_HAS_DESTRUCTURING
2216 sn
= js_GetSrcNote(jp
->script
, pc
);
2217 if (sn
&& SN_TYPE(sn
) == SRC_GROUPASSIGN
) {
2218 pc
= DecompileGroupAssignment(ss
, pc
, endpc
, sn
, &todo
);
2221 LOCAL_ASSERT(*pc
== JSOP_POPN
);
2222 len
= oplen
= JSOP_POPN_LENGTH
;
2223 goto end_groupassignment
;
2229 todo
= Sprint(&ss
->sprinter
, "");
2233 js_printf(jp
, "\ttry {\n");
2240 js_printf(jp
, "\t} finally {\n");
2244 * We push push the pair of exception/restsub cookies to
2245 * simulate the effects [gosub] or control transfer during
2246 * exception capturing on the stack.
2248 todo
= Sprint(&ss
->sprinter
, exception_cookie
);
2249 if (todo
< 0 || !PushOff(ss
, todo
, op
))
2251 todo
= Sprint(&ss
->sprinter
, retsub_pc_cookie
);
2256 LOCAL_ASSERT(strcmp(rval
, retsub_pc_cookie
) == 0);
2258 LOCAL_ASSERT(strcmp(lval
, exception_cookie
) == 0);
2265 * JSOP_GOSUB and GOSUBX have no effect on the decompiler's
2266 * string stack because the next op in bytecode order finds
2267 * the stack balanced by a JSOP_RETSUB executed elsewhere.
2274 uintN newtop
, oldtop
;
2277 * The compiler models operand stack depth and fixes the stack
2278 * pointer on entry to a catch clause based on its depth model.
2279 * The decompiler must match the code generator's model, which
2280 * is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops.
2283 newtop
= oldtop
- GET_UINT16(pc
);
2284 LOCAL_ASSERT(newtop
<= oldtop
);
2287 sn
= js_GetSrcNote(jp
->script
, pc
);
2288 if (sn
&& SN_TYPE(sn
) == SRC_HIDDEN
)
2290 #if JS_HAS_DESTRUCTURING
2291 if (sn
&& SN_TYPE(sn
) == SRC_GROUPASSIGN
) {
2292 todo
= Sprint(&ss
->sprinter
, "%s[] = [",
2296 for (uintN i
= newtop
; i
< oldtop
; i
++) {
2297 rval
= OFF2STR(&ss
->sprinter
, ss
->offsets
[i
]);
2298 if (Sprint(&ss
->sprinter
, ss_format
,
2299 (i
== newtop
) ? "" : ", ",
2300 (i
== oldtop
- 1 && *rval
== '\0')
2301 ? ", " : rval
) < 0) {
2305 if (SprintPut(&ss
->sprinter
, "]", 1) < 0)
2309 * If this is an empty group assignment, we have no stack
2310 * budget into which we can push our result string. Adjust
2311 * ss->sprinter.offset so that our consumer can find the
2312 * empty group assignment decompilation.
2314 if (newtop
== oldtop
) {
2315 ss
->sprinter
.offset
= todo
;
2318 * Kill newtop before the end_groupassignment: label by
2319 * retracting/popping early. Control will either jump
2320 * to do_forloop: or do_letheadbody: or else break from
2321 * our case JSOP_POPN: after the switch (*pc2) below.
2323 LOCAL_ASSERT(newtop
< oldtop
);
2324 ss
->sprinter
.offset
= GetOff(ss
, newtop
);
2328 end_groupassignment
:
2329 LOCAL_ASSERT(*pc
== JSOP_POPN
);
2332 * Thread directly to the next opcode if we can, to handle
2333 * the special cases of a group assignment in the first or
2334 * last part of a for(;;) loop head, or in a let block or
2337 * NB: todo at this point indexes space in ss->sprinter
2338 * that is liable to be overwritten. The code below knows
2339 * exactly how long rval lives, or else copies it down via
2342 rval
= OFF2STR(&ss
->sprinter
, todo
);
2345 if (*pc2
== JSOP_NOP
) {
2346 sn
= js_GetSrcNote(jp
->script
, pc2
);
2348 if (SN_TYPE(sn
) == SRC_FOR
) {
2354 if (SN_TYPE(sn
) == SRC_DECL
) {
2355 if (ss
->top
== StackDepth(jp
->script
)) {
2357 * This must be an empty destructuring
2358 * in the head of a let whose body block
2361 pc
= pc2
+ JSOP_NOP_LENGTH
;
2362 len
= js_GetSrcNoteOffset(sn
, 0);
2363 LOCAL_ASSERT(pc
[len
] == JSOP_LEAVEBLOCK
);
2364 js_printf(jp
, "\tlet (%s) {\n", rval
);
2365 js_printf(jp
, "\t}\n");
2368 todo
= SprintCString(&ss
->sprinter
, rval
);
2369 if (todo
< 0 || !PushOff(ss
, todo
, JSOP_NOP
))
2372 pc
= pc2
+ JSOP_NOP_LENGTH
;
2373 goto do_letheadbody
;
2377 * An unnannotated NOP following a POPN must be the
2378 * third part of for(;;) loop head. If the POPN's
2379 * immediate operand is 0, then we may have no slot
2380 * on the sprint-stack in which to push our result
2381 * string. In this case the result can be recovered
2382 * at ss->sprinter.base + ss->sprinter.offset.
2384 if (GET_UINT16(pc
) == 0)
2386 todo
= SprintCString(&ss
->sprinter
, rval
);
2392 * If control flow reaches this point with todo still -2,
2393 * just print rval as an expression statement.
2396 js_printf(jp
, "\t%s;\n", rval
);
2400 if (newtop
< oldtop
) {
2401 ss
->sprinter
.offset
= GetOff(ss
, newtop
);
2407 case JSOP_EXCEPTION
:
2408 /* The catch decompiler handles this op itself. */
2409 LOCAL_ASSERT(JS_FALSE
);
2414 * By default, do not automatically parenthesize when popping
2415 * a stacked expression decompilation. We auto-parenthesize
2416 * only when JSOP_POP is annotated with SRC_PCDELTA, meaning
2423 sn
= js_GetSrcNote(jp
->script
, pc
);
2424 switch (sn
? SN_TYPE(sn
) : SRC_NULL
) {
2426 /* Force parens around 'in' expression at 'for' front. */
2427 if (ss
->opcodes
[ss
->top
-1] == JSOP_IN
)
2434 /* Comma operator: use JSOP_POP for correct precedence. */
2437 /* Pop and save to avoid blowing stack depth budget. */
2438 lval
= JS_strdup(cx
, POP_STR());
2443 * The offset tells distance to the end of the right-hand
2444 * operand of the comma operator.
2447 pc
+= js_GetSrcNoteOffset(sn
, 0);
2450 if (!Decompile(ss
, done
, pc
- done
, JSOP_POP
)) {
2451 JS_free(cx
, (char *)lval
);
2455 /* Pop Decompile result and print comma expression. */
2457 todo
= Sprint(&ss
->sprinter
, "%s, %s", lval
, rval
);
2458 JS_free(cx
, (char *)lval
);
2462 /* Hide this pop, it's from a goto in a with or for/in. */
2467 /* This pop is at the end of the let block/expr head. */
2468 pc
+= JSOP_POP_LENGTH
;
2469 #if JS_HAS_DESTRUCTURING
2472 len
= js_GetSrcNoteOffset(sn
, 0);
2473 if (pc
[len
] == JSOP_LEAVEBLOCK
) {
2474 js_printf(jp
, "\tlet (%s) {\n", POP_STR());
2476 DECOMPILE_CODE(pc
, len
);
2478 js_printf(jp
, "\t}\n");
2481 LOCAL_ASSERT(pc
[len
] == JSOP_LEAVEBLOCKEXPR
);
2483 lval
= JS_strdup(cx
, PopStr(ss
, JSOP_NOP
));
2487 /* Set saveop to reflect what we will push. */
2488 saveop
= JSOP_LEAVEBLOCKEXPR
;
2489 if (!Decompile(ss
, pc
, len
, saveop
)) {
2490 JS_free(cx
, (char *)lval
);
2493 rval
= PopStr(ss
, JSOP_SETNAME
);
2494 todo
= Sprint(&ss
->sprinter
,
2499 JS_free(cx
, (char *)lval
);
2504 /* Turn off parens around a yield statement. */
2505 if (ss
->opcodes
[ss
->top
-1] == JSOP_YIELD
)
2511 * Don't emit decompiler-pushed strings that are not
2512 * handled by other opcodes. They are pushed onto the
2513 * stack to help model the interpreter stack and should
2514 * not appear in the decompiler's output.
2516 if (*rval
!= '\0' && (rval
[0] != '/' || rval
[1] != '*')) {
2519 (strncmp(rval
, js_function_str
, 8) == 0 &&
2525 LOCAL_ASSERT(*rval
== '\0' ||
2526 strcmp(rval
, exception_cookie
) == 0);
2534 case JSOP_ENTERWITH
:
2535 LOCAL_ASSERT(!js_GetSrcNote(jp
->script
, pc
));
2537 js_printf(jp
, "\twith (%s) {\n", rval
);
2539 todo
= Sprint(&ss
->sprinter
, with_cookie
);
2542 case JSOP_LEAVEWITH
:
2543 sn
= js_GetSrcNote(jp
->script
, pc
);
2545 if (sn
&& SN_TYPE(sn
) == SRC_HIDDEN
)
2548 LOCAL_ASSERT(strcmp(rval
, with_cookie
) == 0);
2550 js_printf(jp
, "\t}\n");
2553 case JSOP_ENTERBLOCK
:
2555 JSAtom
**atomv
, *smallv
[5];
2556 JSScopeProperty
*sprop
;
2559 argc
= OBJ_BLOCK_COUNT(cx
, obj
);
2560 if ((size_t)argc
<= JS_ARRAY_LENGTH(smallv
)) {
2563 atomv
= (JSAtom
**) JS_malloc(cx
, argc
* sizeof(JSAtom
*));
2568 MUST_FLOW_THROUGH("enterblock_out");
2569 #define LOCAL_ASSERT_OUT(expr) LOCAL_ASSERT_CUSTOM(expr, ok = JS_FALSE; \
2570 goto enterblock_out)
2571 for (sprop
= OBJ_SCOPE(obj
)->lastProp
; sprop
;
2572 sprop
= sprop
->parent
) {
2573 if (!(sprop
->flags
& SPROP_HAS_SHORTID
))
2575 LOCAL_ASSERT_OUT(sprop
->shortid
< argc
);
2576 atomv
[sprop
->shortid
] = JSID_TO_ATOM(sprop
->id
);
2579 for (i
= 0; i
< argc
; i
++) {
2581 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
2583 !PushOff(ss
, STR2OFF(&ss
->sprinter
, rval
), op
)) {
2585 goto enterblock_out
;
2589 sn
= js_GetSrcNote(jp
->script
, pc
);
2590 switch (sn
? SN_TYPE(sn
) : SRC_NULL
) {
2591 #if JS_HAS_BLOCK_SCOPE
2593 js_printf(jp
, "\t{\n");
2595 len
= js_GetSrcNoteOffset(sn
, 0);
2596 ok
= Decompile(ss
, pc
+ oplen
, len
- oplen
, JSOP_NOP
)
2599 goto enterblock_out
;
2601 js_printf(jp
, "\t}\n");
2607 js_printf(jp
, "\t} catch (");
2611 LOCAL_ASSERT_OUT(*pc
== JSOP_EXCEPTION
);
2612 pc
+= JSOP_EXCEPTION_LENGTH
;
2613 todo
= Sprint(&ss
->sprinter
, exception_cookie
);
2614 if (todo
< 0 || !PushOff(ss
, todo
, JSOP_EXCEPTION
)) {
2616 goto enterblock_out
;
2619 if (*pc
== JSOP_DUP
) {
2620 sn2
= js_GetSrcNote(jp
->script
, pc
);
2621 if (!sn2
|| SN_TYPE(sn2
) != SRC_DESTRUCT
) {
2623 * This is a dup to save the exception for later.
2624 * It is emitted only when the catch head contains
2625 * an exception guard.
2627 LOCAL_ASSERT_OUT(js_GetSrcNoteOffset(sn
, 0) != 0);
2628 pc
+= JSOP_DUP_LENGTH
;
2629 todo
= Sprint(&ss
->sprinter
, exception_cookie
);
2631 !PushOff(ss
, todo
, JSOP_EXCEPTION
)) {
2633 goto enterblock_out
;
2638 #if JS_HAS_DESTRUCTURING
2639 if (*pc
== JSOP_DUP
) {
2640 pc
= DecompileDestructuring(ss
, pc
, endpc
);
2643 goto enterblock_out
;
2645 LOCAL_ASSERT_OUT(*pc
== JSOP_POP
);
2646 pc
+= JSOP_POP_LENGTH
;
2647 lval
= PopStr(ss
, JSOP_NOP
);
2651 LOCAL_ASSERT_OUT(*pc
== JSOP_SETLOCALPOP
);
2652 i
= GET_SLOTNO(pc
) - jp
->script
->nfixed
;
2653 pc
+= JSOP_SETLOCALPOP_LENGTH
;
2654 atom
= atomv
[i
- OBJ_BLOCK_DEPTH(cx
, obj
)];
2655 str
= ATOM_TO_STRING(atom
);
2656 if (!QuoteString(&jp
->sprinter
, str
, 0)) {
2658 goto enterblock_out
;
2660 #if JS_HAS_DESTRUCTURING
2665 * Pop the exception_cookie (or its dup in the case of a
2666 * guarded catch head) off the stack now.
2668 rval
= PopStr(ss
, JSOP_NOP
);
2669 LOCAL_ASSERT_OUT(strcmp(rval
, exception_cookie
) == 0);
2671 len
= js_GetSrcNoteOffset(sn
, 0);
2673 len
-= PTRDIFF(pc
, pc2
, jsbytecode
);
2674 LOCAL_ASSERT_OUT(len
> 0);
2675 js_printf(jp
, " if ");
2676 ok
= Decompile(ss
, pc
, len
, JSOP_NOP
) != NULL
;
2678 goto enterblock_out
;
2679 js_printf(jp
, "%s", POP_STR());
2681 LOCAL_ASSERT_OUT(*pc
== JSOP_IFEQ
|| *pc
== JSOP_IFEQX
);
2682 pc
+= js_CodeSpec
[*pc
].length
;
2685 js_printf(jp
, ") {\n");
2695 #undef LOCAL_ASSERT_OUT
2697 if (atomv
!= smallv
)
2704 case JSOP_LEAVEBLOCK
:
2705 case JSOP_LEAVEBLOCKEXPR
:
2709 sn
= js_GetSrcNote(jp
->script
, pc
);
2711 if (op
== JSOP_LEAVEBLOCKEXPR
) {
2712 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_PCBASE
);
2715 LOCAL_ASSERT(op
== JSOP_LEAVEBLOCK
);
2716 if (SN_TYPE(sn
) == SRC_HIDDEN
)
2720 * This JSOP_LEAVEBLOCK must be for a catch block. If sn's
2721 * offset does not equal the model stack depth, there must
2722 * be a copy of the exception value on the stack due to a
2723 * catch guard (see above, the JSOP_ENTERBLOCK + SRC_CATCH
2726 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_CATCH
);
2727 if ((uintN
)js_GetSrcNoteOffset(sn
, 0) != ss
->top
) {
2728 LOCAL_ASSERT((uintN
)js_GetSrcNoteOffset(sn
, 0)
2731 LOCAL_ASSERT(strcmp(rval
, exception_cookie
) == 0);
2735 depth
= GET_UINT16(pc
);
2736 LOCAL_ASSERT(top
>= depth
);
2739 ss
->sprinter
.offset
= GetOff(ss
, top
);
2740 if (op
== JSOP_LEAVEBLOCKEXPR
)
2741 todo
= SprintCString(&ss
->sprinter
, rval
);
2745 case JSOP_CALLUPVAR
:
2749 JS_GET_SCRIPT_FUNCTION(jp
->script
, 0, jp
->fun
);
2751 if (!jp
->localNames
)
2752 jp
->localNames
= js_GetLocalNameArray(cx
, jp
->fun
, &jp
->pool
);
2754 i
= JS_UPVAR_LOCAL_NAME_START(jp
->fun
) + GET_UINT16(pc
);
2755 if (i
>= JS_GET_LOCAL_NAME_COUNT(jp
->fun
)) {
2759 * We must be in an eval called from jp->fun, where
2760 * jp->script is the eval-compiled script.
2762 * However, it's possible that a js_Invoke already
2763 * pushed a frame trying to call js_Construct on an
2764 * object that's not a constructor, causing us to be
2765 * called with an intervening frame on the stack.
2767 JSStackFrame
*fp
= js_GetTopStackFrame(cx
);
2769 while (!(fp
->flags
& JSFRAME_EVAL
))
2771 JS_ASSERT(fp
->script
== jp
->script
);
2772 JS_ASSERT(fp
->down
->fun
== jp
->fun
);
2773 JS_ASSERT(FUN_INTERPRETED(jp
->fun
));
2774 JS_ASSERT(jp
->script
!= jp
->fun
->u
.i
.script
);
2775 JS_ASSERT(jp
->script
->upvarsOffset
!= 0);
2778 uva
= JS_SCRIPT_UPVARS(jp
->script
);
2780 i
= UPVAR_FRAME_SLOT(uva
->vector
[i
]);
2782 atom
= GetArgOrVarAtom(jp
, i
);
2785 case JSOP_CALLLOCAL
:
2787 if (IsVarSlot(jp
, pc
, &i
)) {
2788 atom
= GetArgOrVarAtom(jp
, i
);
2792 LOCAL_ASSERT((uintN
)i
< ss
->top
);
2793 sn
= js_GetSrcNote(jp
->script
, pc
);
2795 #if JS_HAS_DESTRUCTURING
2796 if (sn
&& SN_TYPE(sn
) == SRC_GROUPASSIGN
) {
2797 pc
= DecompileGroupAssignment(ss
, pc
, endpc
, sn
, &todo
);
2800 LOCAL_ASSERT(*pc
== JSOP_POPN
);
2801 len
= oplen
= JSOP_POPN_LENGTH
;
2802 goto end_groupassignment
;
2806 rval
= GetLocal(ss
, i
);
2807 todo
= Sprint(&ss
->sprinter
, ss_format
, VarPrefix(sn
), rval
);
2811 case JSOP_SETLOCALPOP
:
2812 if (IsVarSlot(jp
, pc
, &i
)) {
2813 atom
= GetArgOrVarAtom(jp
, i
);
2817 lval
= GetLocal(ss
, i
);
2823 if (IsVarSlot(jp
, pc
, &i
)) {
2824 atom
= GetArgOrVarAtom(jp
, i
);
2828 lval
= GetLocal(ss
, i
);
2833 if (IsVarSlot(jp
, pc
, &i
)) {
2834 atom
= GetArgOrVarAtom(jp
, i
);
2838 lval
= GetLocal(ss
, i
);
2846 LOCAL_ASSERT(jp
->fun
);
2848 if (fun
->flags
& JSFUN_EXPR_CLOSURE
) {
2849 /* Turn on parens around comma-expression here. */
2852 js_printf(jp
, (*rval
== '{') ? "(%s)%s" : ss_format
,
2854 ((fun
->flags
& JSFUN_LAMBDA
) || !fun
->atom
)
2865 js_printf(jp
, "\t%s %s;\n", js_return_str
, rval
);
2867 js_printf(jp
, "\t%s;\n", js_return_str
);
2871 #if JS_HAS_GENERATORS
2873 #if JS_HAS_GENERATOR_EXPRS
2874 if (!ss
->inGenExp
|| !(sn
= js_GetSrcNote(jp
->script
, pc
)))
2877 /* Turn off most parens. */
2880 todo
= (*rval
!= '\0')
2881 ? Sprint(&ss
->sprinter
,
2882 (strncmp(rval
, js_yield_str
, 5) == 0 &&
2883 (rval
[5] == ' ' || rval
[5] == '\0'))
2887 : SprintCString(&ss
->sprinter
, js_yield_str
);
2890 #if JS_HAS_GENERATOR_EXPRS
2891 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_HIDDEN
);
2895 case JSOP_ARRAYPUSH
:
2900 /* Turn off most parens. */
2903 /* Pop the expression being pushed or yielded. */
2907 * Skip the for loop head stacked by JSOP_FORLOCAL until we hit
2908 * a block local slot (note empty destructuring patterns result
2909 * in unit-count blocks).
2913 op
= (JSOp
) ss
->opcodes
[--pos
];
2914 if (op
== JSOP_ENTERBLOCK
)
2917 JS_ASSERT(op
== JSOP_ENTERBLOCK
);
2920 * Here, forpos must index the space before the left-most |for|
2921 * in the single string of accumulated |for| heads and optional
2922 * final |if (condition)|.
2925 LOCAL_ASSERT(forpos
< ss
->top
);
2928 * Now move pos downward over the block's local slots. Even an
2929 * empty destructuring pattern has one (dummy) local.
2931 while (ss
->opcodes
[pos
] == JSOP_ENTERBLOCK
) {
2936 JS_ASSERT_IF(saveop
== JSOP_ARRAYPUSH
,
2937 jp
->script
->nfixed
+ pos
== GET_UINT16(pc
));
2939 #if JS_HAS_GENERATOR_EXPRS
2940 if (saveop
== JSOP_YIELD
) {
2942 * Generator expression: decompile just rval followed by
2943 * the string starting at forpos. Leave the result string
2944 * in ss->offsets[0] so it can be recovered by our caller
2945 * (the JSOP_ANONFUNOBJ with SRC_GENEXP case). Bump the
2946 * top of stack to balance yield, which is an expression
2947 * (so has neutral stack balance).
2949 LOCAL_ASSERT(pos
== 0);
2950 xval
= OFF2STR(&ss
->sprinter
, ss
->offsets
[forpos
]);
2951 ss
->sprinter
.offset
= PAREN_SLOP
;
2952 todo
= Sprint(&ss
->sprinter
, ss_format
, rval
, xval
);
2955 ss
->offsets
[0] = todo
;
2959 #endif /* JS_HAS_GENERATOR_EXPRS */
2962 * Array comprehension: retract the sprinter to the beginning
2963 * of the array initialiser and decompile "[<rval> for ...]".
2965 LOCAL_ASSERT(ss
->opcodes
[pos
] == JSOP_NEWINIT
);
2966 start
= ss
->offsets
[pos
];
2967 LOCAL_ASSERT(ss
->sprinter
.base
[start
] == '[' ||
2968 ss
->sprinter
.base
[start
] == '#');
2969 LOCAL_ASSERT(forpos
< ss
->top
);
2970 xval
= OFF2STR(&ss
->sprinter
, ss
->offsets
[forpos
]);
2971 lval
= OFF2STR(&ss
->sprinter
, start
);
2972 RETRACT(&ss
->sprinter
, lval
);
2974 todo
= Sprint(&ss
->sprinter
, sss_format
, lval
, rval
, xval
);
2977 ss
->offsets
[pos
] = todo
;
2988 sn
= js_GetSrcNote(jp
->script
, pc
);
2990 if (sn
&& SN_TYPE(sn
) == SRC_HIDDEN
)
2993 js_printf(jp
, "\t%s %s;\n", js_throw_str
, rval
);
2997 foreach
= (pc
[1] & (JSITER_FOREACH
| JSITER_KEYVALUE
)) ==
2999 todo
= SprintCString(&ss
->sprinter
, iter_cookie
);
3003 JS_NOT_REACHED("JSOP_NEXTITER");
3007 sn
= js_GetSrcNote(jp
->script
, pc
);
3009 if (sn
&& SN_TYPE(sn
) == SRC_HIDDEN
)
3011 (void) PopOff(ss
, op
);
3012 (void) PopOff(ss
, op
);
3017 sn
= js_GetSrcNote(jp
->script
, pc
);
3018 switch (sn
? SN_TYPE(sn
) : SRC_NULL
) {
3021 * The loop back-edge carries +1 stack balance, for the
3022 * flag processed by JSOP_IFNE. We do not decompile the
3023 * JSOP_IFNE, and instead push the left-hand side of 'in'
3024 * after the loop edge in this stack slot (the JSOP_FOR*
3025 * opcodes' decompilers do this pushing).
3027 cond
= GetJumpOffset(pc
, pc
);
3028 next
= js_GetSrcNoteOffset(sn
, 0);
3029 tail
= js_GetSrcNoteOffset(sn
, 1);
3030 JS_ASSERT(pc
[cond
] == JSOP_NEXTITER
);
3031 DECOMPILE_CODE(pc
+ oplen
, next
- oplen
);
3033 if (ss
->inArrayInit
|| ss
->inGenExp
) {
3034 (void) PopOff(ss
, JSOP_NOP
);
3036 LOCAL_ASSERT(ss
->top
>= 2);
3037 if (ss
->opcodes
[ss
->top
- 2] == JSOP_FORLOCAL
) {
3038 ss
->sprinter
.offset
= ss
->offsets
[ss
->top
- 1] - PAREN_SLOP
;
3039 if (Sprint(&ss
->sprinter
, " %s (%s in %s)",
3040 foreach
? js_for_each_str
: js_for_str
,
3046 * Do not AddParentSlop here, as we will push the
3047 * top-most offset again, which will add paren slop
3048 * for us. We must push to balance the stack budget
3049 * when nesting for heads in a comprehension.
3051 todo
= ss
->offsets
[ss
->top
- 1];
3053 LOCAL_ASSERT(ss
->opcodes
[ss
->top
- 2] == JSOP_ENTERBLOCK
);
3054 todo
= Sprint(&ss
->sprinter
, " %s (%s in %s)",
3055 foreach
? js_for_each_str
: js_for_str
,
3058 if (todo
< 0 || !PushOff(ss
, todo
, JSOP_FORLOCAL
))
3060 DECOMPILE_CODE(pc
+ next
, cond
- next
);
3063 * As above, rval or an extension of it must remain
3064 * stacked during loop body decompilation.
3066 rval
= GetStr(ss
, ss
->top
- 2);
3067 js_printf(jp
, "\t%s (%s in %s) {\n",
3068 foreach
? js_for_each_str
: js_for_str
,
3071 DECOMPILE_CODE(pc
+ next
, cond
- next
);
3073 js_printf(jp
, "\t}\n");
3076 LOCAL_ASSERT(*pc
== JSOP_IFNE
|| *pc
== JSOP_IFNEX
);
3077 len
= js_CodeSpec
[*pc
].length
;
3081 cond
= GetJumpOffset(pc
, pc
);
3082 tail
= js_GetSrcNoteOffset(sn
, 0);
3083 DECOMPILE_CODE(pc
+ cond
, tail
- cond
);
3084 js_printf(jp
, "\twhile (%s) {\n", POP_COND_STR());
3086 DECOMPILE_CODE(pc
+ oplen
, cond
- oplen
);
3088 js_printf(jp
, "\t}\n");
3090 LOCAL_ASSERT(*pc
== JSOP_IFNE
|| *pc
== JSOP_IFNEX
);
3091 len
= js_CodeSpec
[*pc
].length
;
3095 case SRC_CONT2LABEL
:
3096 GET_SOURCE_NOTE_ATOM(sn
, atom
);
3097 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3100 RETRACT(&ss
->sprinter
, rval
);
3101 js_printf(jp
, "\tcontinue %s;\n", rval
);
3105 js_printf(jp
, "\tcontinue;\n");
3108 case SRC_BREAK2LABEL
:
3109 GET_SOURCE_NOTE_ATOM(sn
, atom
);
3110 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3113 RETRACT(&ss
->sprinter
, rval
);
3114 js_printf(jp
, "\tbreak %s;\n", rval
);
3121 js_printf(jp
, "\tbreak;\n");
3130 JSBool elseif
= JS_FALSE
;
3133 len
= GetJumpOffset(pc
, pc
);
3134 sn
= js_GetSrcNote(jp
->script
, pc
);
3136 switch (sn
? SN_TYPE(sn
) : SRC_NULL
) {
3139 rval
= POP_COND_STR();
3140 if (ss
->inArrayInit
|| ss
->inGenExp
) {
3141 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_IF
);
3142 ss
->sprinter
.offset
-= PAREN_SLOP
;
3143 if (Sprint(&ss
->sprinter
, " if (%s)", rval
) < 0)
3148 elseif
? " if (%s) {\n" : "\tif (%s) {\n",
3153 if (SN_TYPE(sn
) == SRC_IF
) {
3154 DECOMPILE_CODE(pc
+ oplen
, len
- oplen
);
3156 LOCAL_ASSERT(!ss
->inArrayInit
&& !ss
->inGenExp
);
3157 tail
= js_GetSrcNoteOffset(sn
, 0);
3158 DECOMPILE_CODE(pc
+ oplen
, tail
- oplen
);
3161 LOCAL_ASSERT(*pc
== JSOP_GOTO
|| *pc
== JSOP_GOTOX
);
3162 oplen
= js_CodeSpec
[*pc
].length
;
3163 len
= GetJumpOffset(pc
, pc
);
3164 js_printf(jp
, "\t} else");
3167 * If the second offset for sn is non-zero, it tells
3168 * the distance from the goto around the else, to the
3169 * ifeq for the if inside the else that forms an "if
3170 * else if" chain. Thus cond spans the condition of
3171 * the second if, so we simply decompile it and start
3172 * over at label if_again.
3174 cond
= js_GetSrcNoteOffset(sn
, 1);
3176 DECOMPILE_CODE(pc
+ oplen
, cond
- oplen
);
3182 js_printf(jp
, " {\n");
3184 DECOMPILE_CODE(pc
+ oplen
, len
- oplen
);
3187 if (!ss
->inArrayInit
&& !ss
->inGenExp
) {
3189 js_printf(jp
, "\t}\n");
3195 xval
= JS_strdup(cx
, POP_STR());
3198 len
= js_GetSrcNoteOffset(sn
, 0);
3199 DECOMPILE_CODE(pc
+ oplen
, len
- oplen
);
3200 lval
= JS_strdup(cx
, POP_STR());
3202 JS_free(cx
, (void *)xval
);
3206 LOCAL_ASSERT(*pc
== JSOP_GOTO
|| *pc
== JSOP_GOTOX
);
3207 oplen
= js_CodeSpec
[*pc
].length
;
3208 len
= GetJumpOffset(pc
, pc
);
3209 DECOMPILE_CODE(pc
+ oplen
, len
- oplen
);
3211 todo
= Sprint(&ss
->sprinter
, "%s ? %s : %s",
3213 JS_free(cx
, (void *)xval
);
3214 JS_free(cx
, (void *)lval
);
3232 do_logical_connective
:
3233 /* Top of stack is the first clause in a disjunction (||). */
3234 lval
= JS_strdup(cx
, POP_STR());
3237 done
= pc
+ GetJumpOffset(pc
, pc
);
3239 len
= PTRDIFF(done
, pc
, jsbytecode
);
3240 if (!Decompile(ss
, pc
, len
, op
)) {
3241 JS_free(cx
, (char *)lval
);
3246 jp
->indent
+ 4 + strlen(lval
) + 4 + strlen(rval
) > 75) {
3247 rval
= JS_strdup(cx
, rval
);
3251 todo
= Sprint(&ss
->sprinter
, "%s %s\n", lval
, xval
);
3252 tail
= Sprint(&ss
->sprinter
, "%*s%s",
3253 jp
->indent
+ 4, "", rval
);
3254 JS_free(cx
, (char *)rval
);
3259 todo
= Sprint(&ss
->sprinter
, "%s %s %s", lval
, xval
, rval
);
3261 JS_free(cx
, (char *)lval
);
3267 goto do_logical_connective
;
3275 sn
= js_GetSrcNote(jp
->script
, pc
);
3276 if (!IsVarSlot(jp
, pc
, &i
)) {
3277 JS_ASSERT(op
== JSOP_FORLOCAL
);
3278 todo
= Sprint(&ss
->sprinter
, ss_format
, VarPrefix(sn
), GetStr(ss
, i
));
3283 atom
= GetArgOrVarAtom(jp
, i
);
3285 todo
= SprintCString(&ss
->sprinter
, VarPrefix(sn
));
3286 if (todo
< 0 || !QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0))
3292 sn
= js_GetSrcNote(jp
->script
, pc
);
3293 todo
= SprintCString(&ss
->sprinter
, VarPrefix(sn
));
3294 if (todo
< 0 || !QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0))
3301 if (!ATOM_IS_IDENTIFIER(atom
)) {
3302 xval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
3310 todo
= Sprint(&ss
->sprinter
, index_format
, lval
, xval
);
3312 todo
= Sprint(&ss
->sprinter
, ss_format
, lval
, *lval
? "." : "");
3315 if (!QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0))
3321 todo
= SprintCString(&ss
->sprinter
, forelem_cookie
);
3325 case JSOP_ENUMCONSTELEM
:
3327 * The stack has the object under the (top) index expression.
3328 * The "rval" property id is underneath those two on the stack.
3329 * The for loop body net and gross lengths can now be adjusted
3330 * to account for the length of the indexing expression that
3331 * came after JSOP_FORELEM and before JSOP_ENUMELEM.
3334 op
= JSOP_NOP
; /* turn off parens around xval */
3336 op
= JSOP_GETELEM
; /* lval must have high precedence */
3340 LOCAL_ASSERT(strcmp(rval
, forelem_cookie
) == 0);
3341 if (*xval
== '\0') {
3342 todo
= SprintCString(&ss
->sprinter
, lval
);
3344 todo
= Sprint(&ss
->sprinter
,
3345 (JOF_OPMODE(lastop
) == JOF_XMLNAME
)
3352 #if JS_HAS_GETTER_SETTER
3360 rval
= GetStr(ss
, ss
->top
-2);
3361 todo
= SprintCString(&ss
->sprinter
, rval
);
3362 if (todo
< 0 || !PushOff(ss
, todo
,
3363 (JSOp
) ss
->opcodes
[ss
->top
-2])) {
3369 #if JS_HAS_DESTRUCTURING
3370 sn
= js_GetSrcNote(jp
->script
, pc
);
3372 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_DESTRUCT
);
3373 pc
= DecompileDestructuring(ss
, pc
, endpc
);
3378 op
= saveop
= JSOP_ENUMELEM
;
3381 if (strcmp(rval
, forelem_cookie
) == 0) {
3382 todo
= Sprint(&ss
->sprinter
, ss_format
,
3383 VarPrefix(sn
), lval
);
3385 // Skip POP so the SRC_FOR_IN code can pop for itself.
3386 if (*pc
== JSOP_POP
)
3387 len
= JSOP_POP_LENGTH
;
3389 todo
= Sprint(&ss
->sprinter
, "%s%s = %s",
3390 VarPrefix(sn
), lval
, rval
);
3396 rval
= GetStr(ss
, ss
->top
-1);
3397 saveop
= (JSOp
) ss
->opcodes
[ss
->top
-1];
3398 todo
= SprintCString(&ss
->sprinter
, rval
);
3402 atom
= GetArgOrVarAtom(jp
, GET_ARGNO(pc
));
3412 lval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3416 if (op
== JSOP_SETNAME
)
3417 (void) PopOff(ss
, op
);
3420 sn
= js_GetSrcNote(jp
->script
, pc
- 1);
3421 if (sn
&& SN_TYPE(sn
) == SRC_ASSIGNOP
) {
3422 todo
= Sprint(&ss
->sprinter
, "%s %s= %s",
3424 (lastop
== JSOP_GETTER
)
3426 : (lastop
== JSOP_SETTER
)
3428 : CodeToken
[lastop
],
3431 sn
= js_GetSrcNote(jp
->script
, pc
);
3432 todo
= Sprint(&ss
->sprinter
, "%s%s = %s",
3433 VarPrefix(sn
), lval
, rval
);
3435 if (op
== JSOP_SETLOCALPOP
) {
3436 if (!PushOff(ss
, todo
, saveop
))
3439 LOCAL_ASSERT(*rval
!= '\0');
3440 js_printf(jp
, "\t%s;\n", rval
);
3449 #if JS_HAS_LVALUE_RETURN
3452 /* Turn off most parens (all if there's only one argument). */
3453 argc
= GET_ARGC(pc
);
3454 op
= (argc
== 1) ? JSOP_NOP
: JSOP_SETNAME
;
3456 JS_malloc(cx
, (size_t)(argc
+ 1) * sizeof *argv
);
3461 for (i
= argc
; i
> 0; i
--) {
3462 argv
[i
] = JS_strdup(cx
, POP_STR());
3467 /* Skip the JSOP_PUSHOBJ-created empty string. */
3468 LOCAL_ASSERT(ss
->top
>= 2);
3469 (void) PopOff(ss
, op
);
3472 * Special case: new (x(y)(z)) must be parenthesized like so.
3473 * Same for new (x(y).z) -- contrast with new x(y).z.
3474 * See PROPAGATE_CALLNESS.
3476 op
= (JSOp
) ss
->opcodes
[ss
->top
-1];
3478 (saveop
== JSOP_NEW
&&
3482 (js_CodeSpec
[op
].format
& JOF_CALLOP
)))
3487 argv
[0] = JS_strdup(cx
, lval
);
3491 lval
= "(", rval
= ")";
3492 if (op
== JSOP_NEW
) {
3495 todo
= Sprint(&ss
->sprinter
, "%s %s%s",
3496 js_new_str
, argv
[0], lval
);
3498 todo
= Sprint(&ss
->sprinter
, ss_format
,
3504 for (i
= 1; i
<= argc
; i
++) {
3506 Sprint(&ss
->sprinter
, ss_format
,
3507 argv
[i
], (i
< argc
) ? ", " : "") < 0) {
3512 if (Sprint(&ss
->sprinter
, rval
) < 0)
3515 for (i
= 0; i
<= argc
; i
++) {
3517 JS_free(cx
, argv
[i
]);
3522 #if JS_HAS_LVALUE_RETURN
3523 if (op
== JSOP_SETCALL
) {
3524 if (!PushOff(ss
, todo
, op
))
3526 todo
= Sprint(&ss
->sprinter
, "");
3533 lval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3536 RETRACT(&ss
->sprinter
, lval
);
3538 todo
= Sprint(&ss
->sprinter
, "%s %s", js_delete_str
, lval
);
3542 GET_ATOM_QUOTE_AND_FMT("%s %s[%s]", "%s %s.%s", rval
);
3545 todo
= Sprint(&ss
->sprinter
, fmt
, js_delete_str
, lval
, rval
);
3549 op
= JSOP_NOP
; /* turn off parens */
3554 goto do_delete_lval
;
3555 todo
= Sprint(&ss
->sprinter
,
3556 (JOF_OPMODE(lastop
) == JOF_XMLNAME
)
3559 js_delete_str
, lval
, xval
);
3562 #if JS_HAS_XML_SUPPORT
3567 todo
= Sprint(&ss
->sprinter
, "%s %s..%s",
3568 js_delete_str
, lval
, xval
);
3572 case JSOP_TYPEOFEXPR
:
3576 todo
= Sprint(&ss
->sprinter
, "%s %s",
3577 (op
== JSOP_VOID
) ? js_void_str
: js_typeof_str
,
3583 atom
= GetArgOrVarAtom(jp
, GET_ARGNO(pc
));
3593 lval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3596 RETRACT(&ss
->sprinter
, lval
);
3598 todo
= Sprint(&ss
->sprinter
, ss_format
,
3599 js_incop_strs
[!(cs
->format
& JOF_INC
)], lval
);
3604 GET_ATOM_QUOTE_AND_FMT(preindex_format
, predot_format
, rval
);
3607 * Force precedence below the numeric literal opcodes, so that
3608 * 42..foo or 10000..toString(16), e.g., decompile with parens
3609 * around the left-hand side of dot.
3613 todo
= Sprint(&ss
->sprinter
, fmt
,
3614 js_incop_strs
[!(cs
->format
& JOF_INC
)],
3620 op
= JSOP_NOP
; /* turn off parens */
3624 if (*xval
!= '\0') {
3625 todo
= Sprint(&ss
->sprinter
,
3626 (JOF_OPMODE(lastop
) == JOF_XMLNAME
)
3629 js_incop_strs
[!(cs
->format
& JOF_INC
)],
3632 todo
= Sprint(&ss
->sprinter
, ss_format
,
3633 js_incop_strs
[!(cs
->format
& JOF_INC
)], lval
);
3639 atom
= GetArgOrVarAtom(jp
, GET_ARGNO(pc
));
3649 lval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3652 RETRACT(&ss
->sprinter
, lval
);
3654 todo
= Sprint(&ss
->sprinter
, ss_format
,
3655 lval
, js_incop_strs
[!(cs
->format
& JOF_INC
)]);
3660 GET_ATOM_QUOTE_AND_FMT(postindex_format
, postdot_format
, rval
);
3663 * Force precedence below the numeric literal opcodes, so that
3664 * 42..foo or 10000..toString(16), e.g., decompile with parens
3665 * around the left-hand side of dot.
3669 todo
= Sprint(&ss
->sprinter
, fmt
, lval
, rval
,
3670 js_incop_strs
[!(cs
->format
& JOF_INC
)]);
3675 op
= JSOP_NOP
; /* turn off parens */
3679 if (*xval
!= '\0') {
3680 todo
= Sprint(&ss
->sprinter
,
3681 (JOF_OPMODE(lastop
) == JOF_XMLNAME
)
3685 js_incop_strs
[!(cs
->format
& JOF_INC
)]);
3687 todo
= Sprint(&ss
->sprinter
, ss_format
,
3688 lval
, js_incop_strs
[!(cs
->format
& JOF_INC
)]);
3694 rval
= js_length_str
;
3695 goto do_getprop_lval
;
3699 (void) PopOff(ss
, lastop
);
3708 GET_QUOTE_AND_FMT(index_format
, dot_format
, rval
);
3710 PROPAGATE_CALLNESS();
3712 todo
= Sprint(&ss
->sprinter
, fmt
, lval
, rval
);
3715 case JSOP_GETTHISPROP
:
3717 GET_QUOTE_AND_FMT(index_format
, dot_format
, rval
);
3718 todo
= Sprint(&ss
->sprinter
, fmt
, js_this_str
, rval
);
3721 case JSOP_GETARGPROP
:
3722 /* Get the name of the argument or variable. */
3726 atom
= GetArgOrVarAtom(ss
->printer
, i
);
3728 LOCAL_ASSERT(ATOM_IS_STRING(atom
));
3729 lval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3730 if (!lval
|| !PushOff(ss
, STR2OFF(&ss
->sprinter
, lval
), op
))
3733 /* Get the name of the property. */
3734 LOAD_ATOM(ARGNO_LEN
);
3737 case JSOP_GETLOCALPROP
:
3738 if (IsVarSlot(jp
, pc
, &i
))
3739 goto do_getarg_prop
;
3740 LOCAL_ASSERT((uintN
)i
< ss
->top
);
3741 lval
= GetLocal(ss
, i
);
3744 todo
= SprintCString(&ss
->sprinter
, lval
);
3745 if (todo
< 0 || !PushOff(ss
, todo
, op
))
3752 GET_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval
);
3756 * Force precedence below the numeric literal opcodes, so that
3757 * 42..foo or 10000..toString(16), e.g., decompile with parens
3758 * around the left-hand side of dot.
3762 sn
= js_GetSrcNote(jp
->script
, pc
- 1);
3763 todo
= Sprint(&ss
->sprinter
, fmt
, lval
, xval
,
3764 (sn
&& SN_TYPE(sn
) == SRC_ASSIGNOP
)
3765 ? (lastop
== JSOP_GETTER
)
3767 : (lastop
== JSOP_SETTER
)
3776 (void) PopOff(ss
, lastop
);
3781 op
= JSOP_NOP
; /* turn off parens */
3784 PROPAGATE_CALLNESS();
3786 if (*xval
== '\0') {
3787 todo
= Sprint(&ss
->sprinter
, "%s", lval
);
3789 todo
= Sprint(&ss
->sprinter
,
3790 (JOF_OPMODE(lastop
) == JOF_XMLNAME
)
3799 op
= JSOP_NOP
; /* turn off parens */
3801 cs
= &js_CodeSpec
[ss
->opcodes
[ss
->top
]];
3802 op
= JSOP_GETELEM
; /* lval must have high precedence */
3807 sn
= js_GetSrcNote(jp
->script
, pc
- 1);
3808 todo
= Sprint(&ss
->sprinter
,
3809 (JOF_MODE(cs
->format
) == JOF_XMLNAME
)
3813 (sn
&& SN_TYPE(sn
) == SRC_ASSIGNOP
)
3814 ? (lastop
== JSOP_GETTER
)
3816 : (lastop
== JSOP_SETTER
)
3824 i
= (jsint
) GET_ARGNO(pc
);
3825 todo
= Sprint(&ss
->sprinter
, "%s[%d]",
3826 js_arguments_str
, (int) i
);
3830 todo
= Sprint(&ss
->sprinter
, dot_format
,
3831 js_arguments_str
, js_length_str
);
3837 atom
= GetArgOrVarAtom(jp
, i
);
3838 #if JS_HAS_DESTRUCTURING
3840 todo
= Sprint(&ss
->sprinter
, "%s[%d]", js_arguments_str
, i
);
3855 #if JS_HAS_XML_SUPPORT
3858 sn
= js_GetSrcNote(jp
->script
, pc
);
3859 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
3860 inXML
? DONT_ESCAPE
: 0);
3863 RETRACT(&ss
->sprinter
, rval
);
3864 todo
= Sprint(&ss
->sprinter
, sss_format
,
3865 VarPrefix(sn
), lval
, rval
);
3869 i
= (jsint
) GET_UINT16(pc
);
3873 i
= (jsint
) GET_UINT24(pc
);
3883 todo
= Sprint(&ss
->sprinter
, "%d", i
);
3888 val
= ATOM_KEY(atom
);
3889 LOCAL_ASSERT(JSVAL_IS_DOUBLE(val
));
3890 todo
= SprintDoubleValue(&ss
->sprinter
, val
, &saveop
);
3895 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
3896 inXML
? DONT_ESCAPE
: '"');
3899 todo
= STR2OFF(&ss
->sprinter
, rval
);
3902 case JSOP_ANONFUNOBJ
:
3903 #if JS_HAS_GENERATOR_EXPRS
3904 sn
= js_GetSrcNote(jp
->script
, pc
);
3905 if (sn
&& SN_TYPE(sn
) == SRC_GENEXP
) {
3906 JSScript
*inner
, *outer
;
3911 inner
= fun
->u
.i
.script
;
3914 * All allocation when decompiling is LIFO, using malloc
3915 * or, more commonly, arena-alloocating from cx->tempPool.
3916 * After InitSprintStack succeeds, we must release to mark
3919 mark
= JS_ARENA_MARK(&cx
->tempPool
);
3920 if (!InitSprintStack(cx
, &ss2
, jp
, StackDepth(inner
)))
3922 ss2
.inGenExp
= JS_TRUE
;
3925 * Recursively decompile this generator function as an
3926 * un-parenthesized generator expression. The ss->inGenExp
3927 * special case of JSOP_YIELD shares array comprehension
3928 * decompilation code that leaves the result as the single
3929 * string pushed on ss2.
3932 LOCAL_ASSERT(JS_UPTRDIFF(pc
, outer
->code
) <= outer
->length
);
3934 if (!Decompile(&ss2
, inner
->code
, inner
->length
, JSOP_NOP
)) {
3935 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
3941 * Advance over this op and its global |this| push, and
3942 * arrange to advance over the call to this lambda.
3945 LOCAL_ASSERT(*pc
== JSOP_NULL
|| *pc
== JSOP_NULLTHIS
);
3946 pc
+= JSOP_NULL_LENGTH
;
3947 LOCAL_ASSERT(*pc
== JSOP_CALL
);
3948 LOCAL_ASSERT(GET_ARGC(pc
) == 0);
3949 len
= JSOP_CALL_LENGTH
;
3952 * Arrange to parenthesize this genexp unless:
3954 * 1. It is the complete expression consumed by a control
3955 * flow bytecode such as JSOP_TABLESWITCH whose syntax
3956 * always parenthesizes the controlling expression.
3957 * 2. It is the condition of a loop other than a for (;;).
3958 * 3. It is the sole argument to a function call.
3959 * 4. It is the condition of an if statement and not of a
3962 * But (first, before anything else) always parenthesize
3963 * if this genexp runs up against endpc and the next op is
3964 * not a loop condition (JSOP_IFNE*) opcode. In such cases,
3965 * this Decompile activation has been recursively called by
3966 * a comma operator, &&, or || bytecode.
3969 LOCAL_ASSERT(pc2
< endpc
||
3970 endpc
< outer
->code
+ outer
->length
);
3971 LOCAL_ASSERT(ss2
.top
== 1);
3972 ss2
.opcodes
[0] = JSOP_POP
;
3974 (JSOp
) *endpc
!= JSOP_IFNE
&&
3975 (JSOp
) *endpc
!= JSOP_IFNEX
) {
3979 op
= ((js_CodeSpec
[op
].format
& JOF_PARENHEAD
) ||
3980 ((js_CodeSpec
[op
].format
& JOF_INVOKE
) &&
3981 GET_ARGC(pc2
) == 1) ||
3982 ((op
== JSOP_IFEQ
|| op
== JSOP_IFEQX
) &&
3983 (sn2
= js_GetSrcNote(outer
, pc2
)) &&
3984 SN_TYPE(sn2
) != SRC_COND
))
3989 * Stack this result as if it's a name and not an
3990 * anonymous function, so it doesn't get decompiled as
3991 * a generator function in a getter or setter context.
3992 * The precedence level is the same for JSOP_NAME and
3995 LOCAL_ASSERT(js_CodeSpec
[JSOP_NAME
].prec
==
3996 js_CodeSpec
[saveop
].prec
);
4001 * Alas, we have to malloc a copy of the result left on
4002 * the top of ss2 because both ss and ss2 arena-allocate
4003 * from cx's tempPool.
4005 rval
= JS_strdup(cx
, PopStr(&ss2
, op
));
4006 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
4009 todo
= SprintCString(&ss
->sprinter
, rval
);
4010 JS_free(cx
, (void *)rval
);
4013 #endif /* JS_HAS_GENERATOR_EXPRS */
4016 case JSOP_NAMEDFUNOBJ
:
4019 uintN indent
= JS_DONT_PRETTY_PRINT
;
4022 * Always parenthesize expression closures. We can't force
4023 * saveop to a low-precedence op to arrange for auto-magic
4024 * parenthesization without confusing getter/setter code
4025 * that checks for JSOP_ANONFUNOBJ and JSOP_NAMEDFUNOBJ.
4027 if (!(fun
->flags
& JSFUN_EXPR_CLOSURE
))
4028 indent
|= JS_IN_GROUP_CONTEXT
;
4029 str
= JS_DecompileFunction(cx
, fun
, indent
);
4034 todo
= SprintString(&ss
->sprinter
, str
);
4039 LOCAL_ASSERT(OBJ_GET_CLASS(cx
, obj
) == &js_RegExpClass
);
4043 GET_REGEXP_FROM_BYTECODE(jp
->script
, pc
, 0, obj
);
4045 if (!js_regexp_toString(cx
, obj
, &val
))
4047 str
= JSVAL_TO_STRING(val
);
4050 case JSOP_TABLESWITCH
:
4051 case JSOP_TABLESWITCHX
:
4053 ptrdiff_t jmplen
, off
, off2
;
4054 jsint j
, n
, low
, high
;
4055 TableEntry
*table
, *tmp
;
4057 sn
= js_GetSrcNote(jp
->script
, pc
);
4058 LOCAL_ASSERT(sn
&& SN_TYPE(sn
) == SRC_SWITCH
);
4059 len
= js_GetSrcNoteOffset(sn
, 0);
4060 jmplen
= (op
== JSOP_TABLESWITCH
) ? JUMP_OFFSET_LEN
4063 off
= GetJumpOffset(pc
, pc2
);
4065 low
= GET_JUMP_OFFSET(pc2
);
4066 pc2
+= JUMP_OFFSET_LEN
;
4067 high
= GET_JUMP_OFFSET(pc2
);
4068 pc2
+= JUMP_OFFSET_LEN
;
4076 table
= (TableEntry
*)
4077 JS_malloc(cx
, (size_t)n
* sizeof *table
);
4080 for (i
= j
= 0; i
< n
; i
++) {
4081 table
[j
].label
= NULL
;
4082 off2
= GetJumpOffset(pc
, pc2
);
4084 sn
= js_GetSrcNote(jp
->script
, pc2
);
4086 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_LABEL
);
4087 GET_SOURCE_NOTE_ATOM(sn
, table
[j
].label
);
4089 table
[j
].key
= INT_TO_JSVAL(low
+ i
);
4090 table
[j
].offset
= off2
;
4096 tmp
= (TableEntry
*)
4097 JS_malloc(cx
, (size_t)j
* sizeof *table
);
4099 ok
= js_MergeSort(table
, (size_t)j
, sizeof(TableEntry
),
4100 CompareOffsets
, NULL
, tmp
);
4108 ok
= DecompileSwitch(ss
, table
, (uintN
)j
, pc
, len
, off
,
4118 case JSOP_LOOKUPSWITCH
:
4119 case JSOP_LOOKUPSWITCHX
:
4121 ptrdiff_t jmplen
, off
, off2
;
4125 sn
= js_GetSrcNote(jp
->script
, pc
);
4126 LOCAL_ASSERT(sn
&& SN_TYPE(sn
) == SRC_SWITCH
);
4127 len
= js_GetSrcNoteOffset(sn
, 0);
4128 jmplen
= (op
== JSOP_LOOKUPSWITCH
) ? JUMP_OFFSET_LEN
4131 off
= GetJumpOffset(pc
, pc2
);
4133 npairs
= GET_UINT16(pc2
);
4136 table
= (TableEntry
*)
4137 JS_malloc(cx
, (size_t)npairs
* sizeof *table
);
4140 for (k
= 0; k
< npairs
; k
++) {
4141 sn
= js_GetSrcNote(jp
->script
, pc2
);
4143 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_LABEL
);
4144 GET_SOURCE_NOTE_ATOM(sn
, table
[k
].label
);
4146 table
[k
].label
= NULL
;
4148 JS_GET_SCRIPT_ATOM(jp
->script
, GET_INDEX(pc2
), atom
);
4150 off2
= GetJumpOffset(pc
, pc2
);
4152 table
[k
].key
= ATOM_KEY(atom
);
4153 table
[k
].offset
= off2
;
4156 ok
= DecompileSwitch(ss
, table
, (uintN
)npairs
, pc
, len
, off
,
4165 case JSOP_CONDSWITCH
:
4167 ptrdiff_t off
, off2
, caseOff
;
4171 sn
= js_GetSrcNote(jp
->script
, pc
);
4172 LOCAL_ASSERT(sn
&& SN_TYPE(sn
) == SRC_SWITCH
);
4173 len
= js_GetSrcNoteOffset(sn
, 0);
4174 off
= js_GetSrcNoteOffset(sn
, 1);
4177 * Count the cases using offsets from switch to first case,
4178 * and case to case, stored in srcnote immediates.
4182 for (ncases
= 0; off2
!= 0; ncases
++) {
4184 LOCAL_ASSERT(*pc2
== JSOP_CASE
|| *pc2
== JSOP_DEFAULT
||
4185 *pc2
== JSOP_CASEX
|| *pc2
== JSOP_DEFAULTX
);
4186 if (*pc2
== JSOP_DEFAULT
|| *pc2
== JSOP_DEFAULTX
) {
4187 /* End of cases, but count default as a case. */
4190 sn
= js_GetSrcNote(jp
->script
, pc2
);
4191 LOCAL_ASSERT(sn
&& SN_TYPE(sn
) == SRC_PCDELTA
);
4192 off2
= js_GetSrcNoteOffset(sn
, 0);
4197 * Allocate table and rescan the cases using their srcnotes,
4198 * stashing each case's delta from switch top in table[i].key,
4199 * and the distance to its statements in table[i].offset.
4201 table
= (TableEntry
*)
4202 JS_malloc(cx
, (size_t)ncases
* sizeof *table
);
4207 for (i
= 0; i
< ncases
; i
++) {
4209 LOCAL_ASSERT(*pc2
== JSOP_CASE
|| *pc2
== JSOP_DEFAULT
||
4210 *pc2
== JSOP_CASEX
|| *pc2
== JSOP_DEFAULTX
);
4212 table
[i
].key
= INT_TO_JSVAL((jsint
) caseOff
);
4213 table
[i
].offset
= caseOff
+ GetJumpOffset(pc2
, pc2
);
4214 if (*pc2
== JSOP_CASE
|| *pc2
== JSOP_CASEX
) {
4215 sn
= js_GetSrcNote(jp
->script
, pc2
);
4216 LOCAL_ASSERT(sn
&& SN_TYPE(sn
) == SRC_PCDELTA
);
4217 off2
= js_GetSrcNoteOffset(sn
, 0);
4222 * Find offset of default code by fetching the default offset
4223 * from the end of table. JSOP_CONDSWITCH always has a default
4226 off
= JSVAL_TO_INT(table
[ncases
-1].key
);
4228 off
+= GetJumpOffset(pc2
, pc2
);
4230 ok
= DecompileSwitch(ss
, table
, (uintN
)ncases
, pc
, len
, off
,
4245 js_printf(jp
, "\tcase %s:\n", lval
);
4257 saveop
= op
= JS_GetTrapOpcode(cx
, jp
->script
, pc
);
4259 cs
= &js_CodeSpec
[op
];
4261 DECOMPILE_CODE(pc
, len
);
4267 todo
= SprintPut(&ss
->sprinter
, "", 0);
4273 char *base
, *from
, *to
;
4276 * All operands are stacked and ready for in-place formatting.
4277 * We know that PAREN_SLOP is 3 here, and take advantage of it
4278 * to avoid strdup'ing.
4280 argc
= GET_UINT24(pc
);
4281 LOCAL_ASSERT(ss
->top
>= (uintN
) argc
);
4282 sn
= js_GetSrcNote(jp
->script
, pc
);
4284 todo
= Sprint(&ss
->sprinter
, "[%s]",
4285 (sn
&& SN_TYPE(sn
) == SRC_CONTINUE
)
4290 off
= GetOff(ss
, ss
->top
);
4291 LOCAL_ASSERT(off
>= PAREN_SLOP
);
4292 base
= OFF2STR(&ss
->sprinter
, off
);
4296 /* Move to the next string that had been stacked. */
4297 from
= OFF2STR(&ss
->sprinter
, off
);
4298 todo
= strlen(from
);
4299 memmove(to
, from
, todo
);
4302 !(sn
&& SN_TYPE(sn
) == SRC_CONTINUE
)) {
4307 off
= GetOff(ss
, ss
->top
+ i
);
4309 LOCAL_ASSERT(to
- base
< ss
->sprinter
.offset
- PAREN_SLOP
);
4313 ss
->sprinter
.offset
= STR2OFF(&ss
->sprinter
, to
);
4314 todo
= STR2OFF(&ss
->sprinter
, base
);
4322 LOCAL_ASSERT(i
== JSProto_Array
|| i
== JSProto_Object
);
4324 todo
= ss
->sprinter
.offset
;
4325 #if JS_HAS_SHARP_VARS
4327 if (op
== JSOP_DEFSHARP
) {
4329 cs
= &js_CodeSpec
[op
];
4331 if (Sprint(&ss
->sprinter
, "#%u=",
4332 (unsigned) (jsint
) GET_UINT16(pc
)) < 0)
4335 #endif /* JS_HAS_SHARP_VARS */
4336 if (i
== JSProto_Array
) {
4338 if (SprintCString(&ss
->sprinter
, "[") < 0)
4341 if (SprintCString(&ss
->sprinter
, "{") < 0)
4351 op
= JSOP_NOP
; /* turn off parens */
4353 sn
= js_GetSrcNote(jp
->script
, pc
);
4355 /* Skip any #n= prefix to find the opening bracket. */
4356 for (xval
= rval
; *xval
!= '[' && *xval
!= '{'; xval
++)
4358 inArray
= (*xval
== '[');
4361 todo
= Sprint(&ss
->sprinter
, "%s%s%c",
4363 (sn
&& SN_TYPE(sn
) == SRC_CONTINUE
) ? ", " : "",
4364 inArray
? ']' : '}');
4370 const char *maybeComma
;
4373 isFirst
= (ss
->opcodes
[ss
->top
- 3] == JSOP_NEWINIT
);
4375 /* Turn off most parens. */
4379 /* Turn off all parens for xval and lval, which we control. */
4383 sn
= js_GetSrcNote(jp
->script
, pc
);
4385 if (sn
&& SN_TYPE(sn
) == SRC_INITPROP
) {
4389 maybeComma
= isFirst
? "" : ", ";
4390 todo
= Sprint(&ss
->sprinter
, sss_format
,
4398 xval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
4400 (ATOM_IS_IDENTIFIER(atom
) ? 0 : '\''));
4403 isFirst
= (ss
->opcodes
[ss
->top
- 2] == JSOP_NEWINIT
);
4409 maybeComma
= isFirst
? "" : ", ";
4410 #ifdef OLD_GETTER_SETTER
4411 todo
= Sprint(&ss
->sprinter
, "%s%s%s%s%s%s%s:%s",
4415 (lastop
== JSOP_GETTER
|| lastop
== JSOP_SETTER
)
4417 (lastop
== JSOP_GETTER
) ? js_getter_str
:
4418 (lastop
== JSOP_SETTER
) ? js_setter_str
:
4422 if (lastop
== JSOP_GETTER
|| lastop
== JSOP_SETTER
) {
4424 !ATOM_IS_STRING(atom
) ||
4425 !ATOM_IS_IDENTIFIER(atom
) ||
4426 ATOM_IS_KEYWORD(atom
) ||
4427 (ss
->opcodes
[ss
->top
+1] != JSOP_ANONFUNOBJ
&&
4428 ss
->opcodes
[ss
->top
+1] != JSOP_NAMEDFUNOBJ
)) {
4429 todo
= Sprint(&ss
->sprinter
, "%s%s%s %s: %s",
4433 (lastop
== JSOP_GETTER
) ? js_getter_str
:
4434 (lastop
== JSOP_SETTER
) ? js_setter_str
:
4438 const char *end
= rval
+ strlen(rval
);
4442 LOCAL_ASSERT(strncmp(rval
, js_function_str
, 8) == 0);
4443 LOCAL_ASSERT(rval
[8] == ' ');
4445 LOCAL_ASSERT(*end
? *end
== ')' : end
[-1] == '}');
4446 todo
= Sprint(&ss
->sprinter
, "%s%s%s %s%s%.*s",
4449 (lastop
== JSOP_GETTER
)
4450 ? js_get_str
: js_set_str
,
4452 (rval
[0] != '(') ? " " : "",
4456 todo
= Sprint(&ss
->sprinter
, "%s%s%s: %s",
4457 lval
, maybeComma
, xval
, rval
);
4463 #if JS_HAS_SHARP_VARS
4465 i
= (jsint
) GET_UINT16(pc
);
4467 todo
= Sprint(&ss
->sprinter
, "#%u=%s", (unsigned) i
, rval
);
4471 i
= (jsint
) GET_UINT16(pc
);
4472 todo
= Sprint(&ss
->sprinter
, "#%u#", (unsigned) i
);
4474 #endif /* JS_HAS_SHARP_VARS */
4476 #if JS_HAS_DEBUGGER_KEYWORD
4478 js_printf(jp
, "\tdebugger;\n");
4481 #endif /* JS_HAS_DEBUGGER_KEYWORD */
4483 #if JS_HAS_XML_SUPPORT
4485 case JSOP_STARTXMLEXPR
:
4486 inXML
= op
== JSOP_STARTXML
;
4492 js_printf(jp
, "\t%s %s %s = %s;\n",
4493 js_default_str
, js_xml_str
, js_namespace_str
, rval
);
4498 if (pc
[JSOP_ANYNAME_LENGTH
] == JSOP_TOATTRNAME
) {
4499 len
+= JSOP_TOATTRNAME_LENGTH
;
4500 todo
= SprintPut(&ss
->sprinter
, "@*", 2);
4502 todo
= SprintPut(&ss
->sprinter
, "*", 1);
4506 case JSOP_QNAMEPART
:
4508 if (pc
[JSOP_QNAMEPART_LENGTH
] == JSOP_TOATTRNAME
) {
4509 saveop
= JSOP_TOATTRNAME
;
4510 len
+= JSOP_TOATTRNAME_LENGTH
;
4516 case JSOP_QNAMECONST
:
4518 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
4521 RETRACT(&ss
->sprinter
, rval
);
4523 todo
= Sprint(&ss
->sprinter
, "%s::%s", lval
, rval
);
4529 todo
= Sprint(&ss
->sprinter
, "%s::[%s]", lval
, rval
);
4532 case JSOP_TOATTRNAME
:
4533 op
= JSOP_NOP
; /* turn off parens */
4535 todo
= Sprint(&ss
->sprinter
, "@[%s]", rval
);
4538 case JSOP_TOATTRVAL
:
4542 case JSOP_ADDATTRNAME
:
4545 todo
= Sprint(&ss
->sprinter
, "%s %s", lval
, rval
);
4546 /* This gets reset by all XML tag expressions. */
4547 quoteAttr
= JS_TRUE
;
4550 case JSOP_ADDATTRVAL
:
4554 todo
= Sprint(&ss
->sprinter
, "%s=\"%s\"", lval
, rval
);
4556 todo
= Sprint(&ss
->sprinter
, "%s=%s", lval
, rval
);
4559 case JSOP_BINDXMLNAME
:
4560 /* Leave the name stacked and push a dummy string. */
4561 todo
= Sprint(&ss
->sprinter
, "");
4564 case JSOP_SETXMLNAME
:
4565 /* Pop the r.h.s., the dummy string, and the name. */
4567 (void) PopOff(ss
, op
);
4571 case JSOP_XMLELTEXPR
:
4572 case JSOP_XMLTAGEXPR
:
4573 todo
= Sprint(&ss
->sprinter
, "{%s}", POP_STR());
4575 /* If we're an attribute value, we shouldn't quote this. */
4576 quoteAttr
= JS_FALSE
;
4579 case JSOP_TOXMLLIST
:
4580 op
= JSOP_NOP
; /* turn off parens */
4581 todo
= Sprint(&ss
->sprinter
, "<>%s</>", POP_STR());
4586 case JSOP_CALLXMLNAME
:
4589 /* These ops indicate the end of XML expressions. */
4594 case JSOP_ENDFILTER
:
4596 PROPAGATE_CALLNESS();
4598 todo
= Sprint(&ss
->sprinter
, "%s.(%s)", lval
, rval
);
4601 case JSOP_DESCENDANTS
:
4603 PROPAGATE_CALLNESS();
4605 todo
= Sprint(&ss
->sprinter
, "%s..%s", lval
, rval
);
4608 case JSOP_XMLOBJECT
:
4610 todo
= Sprint(&ss
->sprinter
, "<xml address='%p'>", obj
);
4615 todo
= SprintPut(&ss
->sprinter
, "<![CDATA[", 9);
4616 if (!QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
4619 SprintPut(&ss
->sprinter
, "]]>", 3);
4622 case JSOP_XMLCOMMENT
:
4624 todo
= SprintPut(&ss
->sprinter
, "<!--", 4);
4625 if (!QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
4628 SprintPut(&ss
->sprinter
, "-->", 3);
4633 rval
= JS_strdup(cx
, POP_STR());
4636 todo
= SprintPut(&ss
->sprinter
, "<?", 2);
4637 ok
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0) &&
4639 (SprintPut(&ss
->sprinter
, " ", 1) >= 0 &&
4640 SprintCString(&ss
->sprinter
, rval
)));
4641 JS_free(cx
, (char *)rval
);
4644 SprintPut(&ss
->sprinter
, "?>", 2);
4648 todo
= SprintPut(&ss
->sprinter
, js_function_str
, 8);
4650 #endif /* JS_HAS_XML_SUPPORT */
4659 /* -2 means "don't push", -1 means reported error. */
4663 if (!PushOff(ss
, todo
, saveop
))
4667 if (cs
->format
& JOF_CALLOP
) {
4668 todo
= Sprint(&ss
->sprinter
, "");
4669 if (todo
< 0 || !PushOff(ss
, todo
, saveop
))
4677 * Undefine local macros.
4680 #undef DECOMPILE_CODE
4686 #undef ATOM_IS_IDENTIFIER
4687 #undef GET_QUOTE_AND_FMT
4688 #undef GET_ATOM_QUOTE_AND_FMT
4694 DecompileCode(JSPrinter
*jp
, JSScript
*script
, jsbytecode
*pc
, uintN len
,
4702 JSScript
*oldscript
;
4703 jsbytecode
*oldcode
, *oldmain
, *code
;
4706 depth
= StackDepth(script
);
4707 JS_ASSERT(pcdepth
<= depth
);
4709 /* Initialize a sprinter for use with the offset stack. */
4710 cx
= jp
->sprinter
.context
;
4711 mark
= JS_ARENA_MARK(&cx
->tempPool
);
4712 ok
= InitSprintStack(cx
, &ss
, jp
, depth
);
4717 * If we are called from js_DecompileValueGenerator with a portion of
4718 * script's bytecode that starts with a non-zero model stack depth given
4719 * by pcdepth, attempt to initialize the missing string offsets in ss to
4720 * |spindex| negative indexes from fp->sp for the activation fp in which
4723 * See js_DecompileValueGenerator for how its |spindex| parameter is used,
4724 * and see also GetOff, which makes use of the ss.offsets[i] < -1 that are
4725 * potentially stored below.
4729 for (i
= 0; i
< pcdepth
; i
++) {
4730 ss
.offsets
[i
] = -2 - (ptrdiff_t)i
;
4731 ss
.opcodes
[i
] = *jp
->pcstack
[i
];
4735 /* Call recursive subroutine to do the hard work. */
4736 oldscript
= jp
->script
;
4737 jp
->script
= script
;
4738 oldcode
= jp
->script
->code
;
4739 oldmain
= jp
->script
->main
;
4740 code
= js_UntrapScriptCode(cx
, jp
->script
);
4741 if (code
!= oldcode
) {
4742 jp
->script
->code
= code
;
4743 jp
->script
->main
= code
+ (oldmain
- oldcode
);
4744 pc
= code
+ (pc
- oldcode
);
4747 ok
= Decompile(&ss
, pc
, len
, JSOP_NOP
) != NULL
;
4748 if (code
!= oldcode
) {
4749 JS_free(cx
, jp
->script
->code
);
4750 jp
->script
->code
= oldcode
;
4751 jp
->script
->main
= oldmain
;
4753 jp
->script
= oldscript
;
4755 /* If the given code didn't empty the stack, do it now. */
4758 last
= OFF2STR(&ss
.sprinter
, PopOff(&ss
, JSOP_POP
));
4759 } while (ss
.top
> pcdepth
);
4760 js_printf(jp
, "%s", last
);
4764 /* Free all temporary stuff allocated under this call. */
4765 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
4770 js_DecompileScript(JSPrinter
*jp
, JSScript
*script
)
4772 return DecompileCode(jp
, script
, script
->code
, (uintN
)script
->length
, 0);
4775 static const char native_code_str
[] = "\t[native code]\n";
4778 js_DecompileFunctionBody(JSPrinter
*jp
)
4783 JS_ASSERT(!jp
->script
);
4784 if (!FUN_INTERPRETED(jp
->fun
)) {
4785 js_printf(jp
, native_code_str
);
4789 script
= jp
->fun
->u
.i
.script
;
4790 return DecompileCode(jp
, script
, script
->code
, (uintN
)script
->length
, 0);
4794 js_DecompileFunction(JSPrinter
*jp
)
4799 jsbytecode
*pc
, *endpc
;
4805 JS_ASSERT(!jp
->script
);
4808 * If pretty, conform to ECMA-262 Edition 3, 15.3.4.2, by decompiling a
4809 * FunctionDeclaration. Otherwise, check the JSFUN_LAMBDA flag and force
4810 * an expression by parenthesizing.
4813 js_printf(jp
, "\t");
4815 if (!jp
->grouped
&& (fun
->flags
& JSFUN_LAMBDA
))
4818 if (JSFUN_GETTER_TEST(fun
->flags
))
4819 js_printf(jp
, "%s ", js_getter_str
);
4820 else if (JSFUN_SETTER_TEST(fun
->flags
))
4821 js_printf(jp
, "%s ", js_setter_str
);
4823 js_printf(jp
, "%s ", js_function_str
);
4824 if (fun
->atom
&& !QuoteString(&jp
->sprinter
, ATOM_TO_STRING(fun
->atom
), 0))
4828 if (!FUN_INTERPRETED(fun
)) {
4829 js_printf(jp
, ") {\n");
4831 js_printf(jp
, native_code_str
);
4833 js_printf(jp
, "\t}");
4835 #ifdef JS_HAS_DESTRUCTURING
4840 /* Print the parameters. */
4841 pc
= fun
->u
.i
.script
->main
;
4842 endpc
= pc
+ fun
->u
.i
.script
->length
;
4845 #ifdef JS_HAS_DESTRUCTURING
4846 /* Skip JSOP_GENERATOR in case of destructuring parameters. */
4847 if (*pc
== JSOP_GENERATOR
)
4848 pc
+= JSOP_GENERATOR_LENGTH
;
4851 jp
->script
= fun
->u
.i
.script
;
4852 mark
= JS_ARENA_MARK(&jp
->sprinter
.context
->tempPool
);
4855 for (i
= 0; i
< fun
->nargs
; i
++) {
4859 param
= GetArgOrVarAtom(jp
, i
);
4861 #if JS_HAS_DESTRUCTURING
4862 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, JS_FALSE)
4868 LOCAL_ASSERT(*pc
== JSOP_GETARG
);
4869 pc
+= JSOP_GETARG_LENGTH
;
4870 LOCAL_ASSERT(*pc
== JSOP_DUP
);
4872 ok
= InitSprintStack(jp
->sprinter
.context
, &ss
, jp
,
4873 StackDepth(fun
->u
.i
.script
));
4877 pc
= DecompileDestructuring(&ss
, pc
, endpc
);
4882 LOCAL_ASSERT(*pc
== JSOP_POP
);
4883 pc
+= JSOP_POP_LENGTH
;
4884 lval
= PopStr(&ss
, JSOP_NOP
);
4885 todo
= SprintCString(&jp
->sprinter
, lval
);
4896 if (!QuoteString(&jp
->sprinter
, ATOM_TO_STRING(param
), 0)) {
4902 #ifdef JS_HAS_DESTRUCTURING
4904 JS_ARENA_RELEASE(&jp
->sprinter
.context
->tempPool
, mark
);
4908 if (fun
->flags
& JSFUN_EXPR_CLOSURE
) {
4909 js_printf(jp
, ") ");
4911 js_printf(jp
, ") {\n");
4915 len
= fun
->u
.i
.script
->code
+ fun
->u
.i
.script
->length
- pc
;
4916 ok
= DecompileCode(jp
, fun
->u
.i
.script
, pc
, (uintN
)len
, 0);
4920 if (!(fun
->flags
& JSFUN_EXPR_CLOSURE
)) {
4922 js_printf(jp
, "\t}");
4926 if (!jp
->pretty
&& !jp
->grouped
&& (fun
->flags
& JSFUN_LAMBDA
))
4933 js_DecompileValueGenerator(JSContext
*cx
, intN spindex
, jsval v
,
4941 jsval
*sp
, *stackBase
;
4944 JS_ASSERT(spindex
< 0 ||
4945 spindex
== JSDVG_IGNORE_STACK
||
4946 spindex
== JSDVG_SEARCH_STACK
);
4948 for (fp
= cx
->fp
; fp
&& !fp
->script
; fp
= fp
->down
)
4950 if (!fp
|| !fp
->regs
|| !fp
->regs
->sp
)
4953 script
= fp
->script
;
4955 pc
= fp
->imacpc
? fp
->imacpc
: regs
->pc
;
4956 if (pc
< script
->main
|| script
->code
+ script
->length
<= pc
) {
4957 JS_NOT_REACHED("bug");
4961 if (spindex
!= JSDVG_IGNORE_STACK
) {
4962 jsbytecode
**pcstack
;
4965 * Prepare computing pcstack containing pointers to opcodes that
4966 * populated interpreter's stack with its current content.
4968 pcstack
= (jsbytecode
**)
4969 JS_malloc(cx
, StackDepth(script
) * sizeof *pcstack
);
4972 pcdepth
= ReconstructPCStack(cx
, script
, pc
, pcstack
);
4974 goto release_pcstack
;
4976 if (spindex
!= JSDVG_SEARCH_STACK
) {
4977 JS_ASSERT(spindex
< 0);
4980 goto release_pcstack
;
4981 pc
= pcstack
[pcdepth
];
4984 * We search from fp->sp to base to find the most recently
4985 * calculated value matching v under assumption that it is
4986 * it that caused exception, see bug 328664.
4988 stackBase
= StackBase(fp
);
4991 if (sp
== stackBase
) {
4993 goto release_pcstack
;
4995 } while (*--sp
!= v
);
4997 if (sp
>= stackBase
+ pcdepth
) {
4999 * The value comes from a temporary slot that the interpreter
5000 * uses for GC roots or when JSOP_APPLY extended the stack to
5001 * fit the argument array elements. Assume that it is the
5002 * current PC that caused the exception.
5004 pc
= fp
->imacpc
? fp
->imacpc
: regs
->pc
;
5006 pc
= pcstack
[sp
- stackBase
];
5011 JS_free(cx
, pcstack
);
5017 jsbytecode
* savepc
= regs
->pc
;
5018 jsbytecode
* imacpc
= fp
->imacpc
;
5023 name
= DecompileExpression(cx
, script
, fp
->fun
, pc
);
5026 fp
->imacpc
= imacpc
;
5029 if (name
!= FAILED_EXPRESSION_DECOMPILER
)
5034 fallback
= js_ValueToSource(cx
, v
);
5038 return js_DeflateString(cx
, JSSTRING_CHARS(fallback
),
5039 JSSTRING_LENGTH(fallback
));
5043 DecompileExpression(JSContext
*cx
, JSScript
*script
, JSFunction
*fun
,
5046 jsbytecode
*code
, *oldcode
, *oldmain
;
5048 const JSCodeSpec
*cs
;
5049 jsbytecode
*begin
, *end
;
5052 jsbytecode
**pcstack
;
5057 JS_ASSERT(script
->main
<= pc
&& pc
< script
->code
+ script
->length
);
5060 oldcode
= script
->code
;
5061 oldmain
= script
->main
;
5063 MUST_FLOW_THROUGH("out");
5064 code
= js_UntrapScriptCode(cx
, script
);
5065 if (code
!= oldcode
) {
5066 script
->code
= code
;
5067 script
->main
= code
+ (oldmain
- oldcode
);
5068 pc
= code
+ (pc
- oldcode
);
5073 /* None of these stack-writing ops generates novel values. */
5074 JS_ASSERT(op
!= JSOP_CASE
&& op
!= JSOP_CASEX
&&
5075 op
!= JSOP_DUP
&& op
!= JSOP_DUP2
);
5078 * |this| could convert to a very long object initialiser, so cite it by
5079 * its keyword name instead.
5081 if (op
== JSOP_THIS
) {
5082 name
= JS_strdup(cx
, js_this_str
);
5087 * JSOP_BINDNAME is special: it generates a value, the base object of a
5088 * reference. But if it is the generating op for a diagnostic produced by
5089 * js_DecompileValueGenerator, the name being bound is irrelevant. Just
5090 * fall back to the base object.
5092 if (op
== JSOP_BINDNAME
) {
5093 name
= FAILED_EXPRESSION_DECOMPILER
;
5097 /* NAME ops are self-contained, others require left or right context. */
5098 cs
= &js_CodeSpec
[op
];
5100 end
= pc
+ cs
->length
;
5101 switch (JOF_MODE(cs
->format
)) {
5106 sn
= js_GetSrcNote(script
, pc
);
5108 name
= FAILED_EXPRESSION_DECOMPILER
;
5111 switch (SN_TYPE(sn
)) {
5113 begin
-= js_GetSrcNoteOffset(sn
, 0);
5116 end
= begin
+ js_GetSrcNoteOffset(sn
, 0);
5117 begin
+= cs
->length
;
5120 name
= FAILED_EXPRESSION_DECOMPILER
;
5126 len
= PTRDIFF(end
, begin
, jsbytecode
);
5128 name
= FAILED_EXPRESSION_DECOMPILER
;
5132 pcstack
= (jsbytecode
**)
5133 JS_malloc(cx
, StackDepth(script
) * sizeof *pcstack
);
5139 MUST_FLOW_THROUGH("out");
5140 pcdepth
= ReconstructPCStack(cx
, script
, begin
, pcstack
);
5142 name
= FAILED_EXPRESSION_DECOMPILER
;
5147 jp
= JS_NEW_PRINTER(cx
, "js_DecompileValueGenerator", fun
, 0, JS_FALSE
);
5150 jp
->pcstack
= pcstack
;
5151 if (DecompileCode(jp
, script
, begin
, (uintN
) len
, (uintN
) pcdepth
)) {
5152 name
= (jp
->sprinter
.base
) ? jp
->sprinter
.base
: (char *) "";
5153 name
= JS_strdup(cx
, name
);
5155 js_DestroyPrinter(jp
);
5159 if (code
!= oldcode
) {
5160 JS_free(cx
, script
->code
);
5161 script
->code
= oldcode
;
5162 script
->main
= oldmain
;
5165 JS_free(cx
, pcstack
);
5170 js_ReconstructStackDepth(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
)
5172 return ReconstructPCStack(cx
, script
, pc
, NULL
);
5176 ReconstructPCStack(JSContext
*cx
, JSScript
*script
, jsbytecode
*target
,
5177 jsbytecode
**pcstack
)
5179 intN pcdepth
, nuses
, ndefs
;
5182 const JSCodeSpec
*cs
;
5187 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1);
5190 * Walk forward from script->main and compute the stack depth and stack of
5191 * operand-generating opcode PCs in pcstack.
5193 * FIXME: Code to compute oplen copied from js_Disassemble1 and reduced.
5194 * FIXME: Optimize to use last empty-stack sequence point.
5196 LOCAL_ASSERT(script
->main
<= target
&& target
< script
->code
+ script
->length
);
5198 for (pc
= script
->main
; pc
< target
; pc
+= oplen
) {
5200 if (op
== JSOP_TRAP
)
5201 op
= JS_GetTrapOpcode(cx
, script
, pc
);
5202 cs
= &js_CodeSpec
[op
];
5205 oplen
= js_GetVariableBytecodeLength(pc
);
5208 * A (C ? T : E) expression requires skipping either T (if target is in
5209 * E) or both T and E (if target is after the whole expression) before
5210 * adjusting pcdepth based on the JSOP_IFEQ or JSOP_IFEQX at pc that
5211 * tests condition C. We know that the stack depth can't change from
5212 * what it was with C on top of stack.
5214 sn
= js_GetSrcNote(script
, pc
);
5215 if (sn
&& SN_TYPE(sn
) == SRC_COND
) {
5216 ptrdiff_t jmpoff
, jmplen
;
5218 jmpoff
= js_GetSrcNoteOffset(sn
, 0);
5219 if (pc
+ jmpoff
< target
) {
5222 JS_ASSERT(op
== JSOP_GOTO
|| op
== JSOP_GOTOX
);
5223 cs
= &js_CodeSpec
[op
];
5225 JS_ASSERT(oplen
> 0);
5226 jmplen
= GetJumpOffset(pc
, pc
);
5227 if (pc
+ jmplen
< target
) {
5228 oplen
= (uintN
) jmplen
;
5233 * Ok, target lies in E. Manually pop C off the model stack,
5234 * since we have moved beyond the IFEQ now.
5237 LOCAL_ASSERT(pcdepth
>= 0);
5241 if (sn
&& SN_TYPE(sn
) == SRC_HIDDEN
)
5246 nuses
= js_GetVariableStackUseLength(op
, pc
);
5252 JS_ASSERT(op
== JSOP_ENTERBLOCK
);
5253 GET_OBJECT_FROM_BYTECODE(script
, pc
, 0, obj
);
5254 JS_ASSERT(OBJ_BLOCK_DEPTH(cx
, obj
) == pcdepth
);
5255 ndefs
= OBJ_BLOCK_COUNT(cx
, obj
);
5259 LOCAL_ASSERT(pcdepth
>= 0);
5260 LOCAL_ASSERT((uintN
)(pcdepth
+ ndefs
) <= StackDepth(script
));
5263 * Fill the slots that the opcode defines withs its pc unless it just
5264 * reshuffle the stack. In the latter case we want to preserve the
5265 * opcode that generated the original value.
5270 for (i
= 0; i
!= ndefs
; ++i
)
5271 pcstack
[pcdepth
+ i
] = pc
;
5277 /* Keep the switch value. */
5278 JS_ASSERT(ndefs
== 1);
5282 JS_ASSERT(ndefs
== 2);
5284 pcstack
[pcdepth
+ 1] = pcstack
[pcdepth
];
5288 JS_ASSERT(ndefs
== 4);
5290 pcstack
[pcdepth
+ 2] = pcstack
[pcdepth
];
5291 pcstack
[pcdepth
+ 3] = pcstack
[pcdepth
+ 1];
5297 LOCAL_ASSERT(pc
== target
);
5303 #undef LOCAL_ASSERT_RV