1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set sw=4 ts=8 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is Mozilla Communicator client code, released
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
42 * JS bytecode descriptors, disassemblers, and decompilers.
60 #include "jsversion.h"
73 #include "jsstaticcheck.h"
77 #include "jsinterpinlines.h"
78 #include "jsobjinlines.h"
79 #include "jsscriptinlines.h"
80 #include "jscntxtinlines.h"
82 #include "jsautooplen.h"
85 using namespace js::gc
;
88 * Index limit must stay within 32 bits.
90 JS_STATIC_ASSERT(sizeof(uint32
) * JS_BITS_PER_BYTE
>= INDEX_LIMIT_LOG2
+ 1);
92 /* Verify JSOP_XXX_LENGTH constant definitions. */
93 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
94 JS_STATIC_ASSERT(op##_LENGTH == length);
95 #include "jsopcode.tbl"
98 static const char js_incop_strs
[][3] = {"++", "--"};
99 static const char js_for_each_str
[] = "for each";
101 const JSCodeSpec js_CodeSpec
[] = {
102 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
103 {length,nuses,ndefs,prec,format},
104 #include "jsopcode.tbl"
108 uintN js_NumCodeSpecs
= JS_ARRAY_LENGTH(js_CodeSpec
);
111 * Each element of the array is either a source literal associated with JS
114 static const char *CodeToken
[] = {
115 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
117 #include "jsopcode.tbl"
121 #if defined(DEBUG) || defined(JS_JIT_SPEW) || defined(JS_METHODJIT_SPEW)
123 * Array of JS bytecode names used by DEBUG-only js_Disassemble and by
126 const char *js_CodeName
[] = {
127 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
129 #include "jsopcode.tbl"
134 /************************************************************************/
137 GetJumpOffset(jsbytecode
*pc
, jsbytecode
*pc2
)
141 type
= JOF_OPTYPE(*pc
);
142 if (JOF_TYPE_IS_EXTENDED_JUMP(type
))
143 return GET_JUMPX_OFFSET(pc2
);
144 return GET_JUMP_OFFSET(pc2
);
148 js_GetIndexFromBytecode(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
,
154 op
= js_GetOpcode(cx
, script
, pc
);
155 JS_ASSERT(js_CodeSpec
[op
].length
>= 1 + pcoff
+ UINT16_LEN
);
158 * We need to detect index base prefix. It presents when resetbase
159 * follows the bytecode.
161 span
= js_CodeSpec
[op
].length
;
163 if (pc
- script
->code
+ span
< script
->length
) {
164 if (pc
[span
] == JSOP_RESETBASE
) {
165 base
= GET_INDEXBASE(pc
- JSOP_INDEXBASE_LENGTH
);
166 } else if (pc
[span
] == JSOP_RESETBASE0
) {
167 JS_ASSERT(JSOP_INDEXBASE1
<= pc
[-1] || pc
[-1] <= JSOP_INDEXBASE3
);
168 base
= (pc
[-1] - JSOP_INDEXBASE1
+ 1) << 16;
171 return base
+ GET_UINT16(pc
+ pcoff
);
175 js_GetVariableBytecodeLength(jsbytecode
*pc
)
178 uintN jmplen
, ncases
;
182 JS_ASSERT(js_CodeSpec
[op
].length
== -1);
184 case JSOP_TABLESWITCHX
:
185 jmplen
= JUMPX_OFFSET_LEN
;
187 case JSOP_TABLESWITCH
:
188 jmplen
= JUMP_OFFSET_LEN
;
190 /* Structure: default-jump case-low case-high case1-jump ... */
192 low
= GET_JUMP_OFFSET(pc
);
193 pc
+= JUMP_OFFSET_LEN
;
194 high
= GET_JUMP_OFFSET(pc
);
195 ncases
= (uintN
)(high
- low
+ 1);
196 return 1 + jmplen
+ INDEX_LEN
+ INDEX_LEN
+ ncases
* jmplen
;
198 case JSOP_LOOKUPSWITCHX
:
199 jmplen
= JUMPX_OFFSET_LEN
;
202 JS_ASSERT(op
== JSOP_LOOKUPSWITCH
);
203 jmplen
= JUMP_OFFSET_LEN
;
205 /* Structure: default-jump case-count (case1-value case1-jump) ... */
207 ncases
= GET_UINT16(pc
);
208 return 1 + jmplen
+ INDEX_LEN
+ ncases
* (INDEX_LEN
+ jmplen
);
213 js_GetVariableStackUses(JSOp op
, jsbytecode
*pc
)
215 JS_ASSERT(*pc
== op
|| *pc
== JSOP_TRAP
);
216 JS_ASSERT(js_CodeSpec
[op
].nuses
== -1);
219 return GET_UINT16(pc
);
220 case JSOP_LEAVEBLOCK
:
221 return GET_UINT16(pc
);
222 case JSOP_LEAVEBLOCKEXPR
:
223 return GET_UINT16(pc
) + 1;
225 /* stack: fun, this, [argc arguments] */
226 JS_ASSERT(op
== JSOP_NEW
|| op
== JSOP_CALL
|| op
== JSOP_EVAL
||
227 op
== JSOP_FUNCALL
|| op
== JSOP_FUNAPPLY
);
228 return 2 + GET_ARGC(pc
);
233 js_GetEnterBlockStackDefs(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
)
237 JS_ASSERT(*pc
== JSOP_ENTERBLOCK
|| *pc
== JSOP_TRAP
);
238 GET_OBJECT_FROM_BYTECODE(script
, pc
, 0, obj
);
239 return OBJ_BLOCK_COUNT(cx
, obj
);
242 class AutoScriptUntrapper
{
249 AutoScriptUntrapper(JSContext
*cx
, JSScript
*script
, jsbytecode
**pc
)
250 : cx(cx
), script(script
), origPC(*pc
)
252 jsbytecode
*newCode
= js_UntrapScriptCode(cx
, script
);
253 if (newCode
== script
->code
) {
257 script
->main
+= newCode
- script
->code
;
258 *pc
= newPC
= origPC
+ (newCode
- script
->code
);
259 script
->code
= newCode
;
262 ~AutoScriptUntrapper()
264 ptrdiff_t delta
= newPC
- origPC
;
266 jsbytecode
*oldCode
= script
->code
- delta
;
267 cx
->free(script
->code
);
268 script
->code
= oldCode
;
269 script
->main
-= delta
;
276 /* If pc != NULL, includes a prefix indicating whether the PC is at the current line. */
277 JS_FRIEND_API(JSBool
)
278 js_DisassembleAtPC(JSContext
*cx
, JSScript
*script
, JSBool lines
, FILE *fp
, jsbytecode
*pc
)
280 jsbytecode
*next
, *end
;
284 end
= next
+ script
->length
;
286 if (next
== script
->main
)
287 fputs("main:\n", fp
);
294 len
= js_Disassemble1(cx
, script
, next
,
304 JS_FRIEND_API(JSBool
)
305 js_Disassemble(JSContext
*cx
, JSScript
*script
, JSBool lines
, FILE *fp
)
307 return js_DisassembleAtPC(cx
, script
, lines
, fp
, NULL
);
310 JS_FRIEND_API(JSBool
)
311 js_DumpPC(JSContext
*cx
)
313 return js_DisassembleAtPC(cx
, cx
->fp()->script(), true, stdout
, cx
->regs
->pc
);
317 js_DumpScript(JSContext
*cx
, JSScript
*script
)
319 return js_Disassemble(cx
, script
, true, stdout
);
323 ToDisassemblySource(JSContext
*cx
, jsval v
, JSAutoByteString
*bytes
)
325 if (!JSVAL_IS_PRIMITIVE(v
)) {
326 JSObject
*obj
= JSVAL_TO_OBJECT(v
);
327 Class
*clasp
= obj
->getClass();
329 if (clasp
== &js_BlockClass
) {
330 char *source
= JS_sprintf_append(NULL
, "depth %d {", OBJ_BLOCK_DEPTH(cx
, obj
));
334 Shape::Range r
= obj
->lastProperty()->all();
336 const Shape
&shape
= r
.front();
337 JSAutoByteString bytes
;
338 if (!js_AtomToPrintableString(cx
, JSID_TO_ATOM(shape
.id
), &bytes
))
342 source
= JS_sprintf_append(source
, "%s: %d%s",
343 bytes
.ptr(), shape
.shortid
,
344 !r
.empty() ? ", " : "");
349 source
= JS_sprintf_append(source
, "}");
352 bytes
->initBytes(source
);
356 if (clasp
== &js_FunctionClass
) {
357 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, obj
);
358 JSString
*str
= JS_DecompileFunction(cx
, fun
, JS_DONT_PRETTY_PRINT
);
361 return bytes
->encode(cx
, str
);
364 if (clasp
== &js_RegExpClass
) {
365 AutoValueRooter
tvr(cx
);
366 if (!js_regexp_toString(cx
, obj
, tvr
.addr()))
368 return bytes
->encode(cx
, JSVAL_TO_STRING(Jsvalify(tvr
.value())));
372 return !!js_ValueToPrintable(cx
, Valueify(v
), bytes
, true);
376 js_Disassemble1(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
,
377 uintN loc
, JSBool lines
, FILE *fp
)
380 const JSCodeSpec
*cs
;
381 ptrdiff_t len
, off
, jmplen
;
389 AutoScriptUntrapper
untrapper(cx
, script
, &pc
);
392 if (op
>= JSOP_LIMIT
) {
393 char numBuf1
[12], numBuf2
[12];
394 JS_snprintf(numBuf1
, sizeof numBuf1
, "%d", op
);
395 JS_snprintf(numBuf2
, sizeof numBuf2
, "%d", JSOP_LIMIT
);
396 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
397 JSMSG_BYTECODE_TOO_BIG
, numBuf1
, numBuf2
);
400 cs
= &js_CodeSpec
[op
];
401 len
= (ptrdiff_t) cs
->length
;
402 fprintf(fp
, "%05u:", loc
);
404 fprintf(fp
, "%4u", JS_PCToLineNumber(cx
, script
, pc
));
405 fprintf(fp
, " %s", js_CodeName
[op
]);
406 type
= JOF_TYPE(cs
->format
);
413 off
= GetJumpOffset(pc
, pc
);
414 fprintf(fp
, " %u (%d)", loc
+ (intN
) off
, (intN
) off
);
420 index
= js_GetIndexFromBytecode(cx
, script
, pc
, 0);
421 if (type
== JOF_ATOM
) {
422 if (op
== JSOP_DOUBLE
) {
423 v
= Jsvalify(script
->getConst(index
));
425 JS_GET_SCRIPT_ATOM(script
, pc
, index
, atom
);
426 v
= ATOM_TO_JSVAL(atom
);
429 if (type
== JOF_OBJECT
)
430 obj
= script
->getObject(index
);
432 obj
= script
->getRegExp(index
);
433 v
= OBJECT_TO_JSVAL(obj
);
436 JSAutoByteString bytes
;
437 if (!ToDisassemblySource(cx
, v
, &bytes
))
439 fprintf(fp
, " %s", bytes
.ptr());
444 atom
= script
->getGlobalAtom(GET_SLOTNO(pc
));
445 v
= ATOM_TO_JSVAL(atom
);
447 JSAutoByteString bytes
;
448 if (!ToDisassemblySource(cx
, v
, &bytes
))
450 fprintf(fp
, " %s", bytes
.ptr());
455 i
= (jsint
)GET_UINT16(pc
);
456 fprintf(fp
, " %d", i
);
461 i
= (jsint
)GET_UINT16(pc
);
464 case JOF_TABLESWITCH
:
465 case JOF_TABLESWITCHX
:
470 jmplen
= (type
== JOF_TABLESWITCH
) ? JUMP_OFFSET_LEN
473 off
= GetJumpOffset(pc
, pc2
);
475 low
= GET_JUMP_OFFSET(pc2
);
476 pc2
+= JUMP_OFFSET_LEN
;
477 high
= GET_JUMP_OFFSET(pc2
);
478 pc2
+= JUMP_OFFSET_LEN
;
479 fprintf(fp
, " defaultOffset %d low %d high %d", (intN
) off
, low
, high
);
480 for (i
= low
; i
<= high
; i
++) {
481 off
= GetJumpOffset(pc
, pc2
);
482 fprintf(fp
, "\n\t%d: %d", i
, (intN
) off
);
489 case JOF_LOOKUPSWITCH
:
490 case JOF_LOOKUPSWITCHX
:
495 jmplen
= (type
== JOF_LOOKUPSWITCH
) ? JUMP_OFFSET_LEN
498 off
= GetJumpOffset(pc
, pc2
);
500 npairs
= GET_UINT16(pc2
);
502 fprintf(fp
, " offset %d npairs %u", (intN
) off
, (uintN
) npairs
);
504 uint16 constIndex
= GET_INDEX(pc2
);
506 off
= GetJumpOffset(pc
, pc2
);
509 JSAutoByteString bytes
;
510 if (!ToDisassemblySource(cx
, Jsvalify(script
->getConst(constIndex
)), &bytes
))
512 fprintf(fp
, "\n\t%s: %d", bytes
.ptr(), (intN
) off
);
520 fprintf(fp
, " %u", GET_ARGNO(pc
));
524 fprintf(fp
, " %u", GET_SLOTNO(pc
));
528 case JOF_SLOTOBJECT
: {
529 fprintf(fp
, " %u", GET_SLOTNO(pc
));
530 index
= js_GetIndexFromBytecode(cx
, script
, pc
, SLOTNO_LEN
);
531 if (type
== JOF_SLOTATOM
) {
532 JS_GET_SCRIPT_ATOM(script
, pc
, index
, atom
);
533 v
= ATOM_TO_JSVAL(atom
);
535 obj
= script
->getObject(index
);
536 v
= OBJECT_TO_JSVAL(obj
);
539 JSAutoByteString bytes
;
540 if (!ToDisassemblySource(cx
, v
, &bytes
))
542 fprintf(fp
, " %s", bytes
.ptr());
547 JS_ASSERT(op
== JSOP_UINT24
|| op
== JSOP_NEWARRAY
);
548 i
= (jsint
)GET_UINT24(pc
);
560 JS_ASSERT(op
== JSOP_INT32
);
563 fprintf(fp
, " %d", i
);
568 JS_snprintf(numBuf
, sizeof numBuf
, "%lx", (unsigned long) cs
->format
);
569 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
570 JSMSG_UNKNOWN_FORMAT
, numBuf
);
580 /************************************************************************/
583 * Sprintf, but with unlimited and automatically allocated buffering.
585 typedef struct Sprinter
{
586 JSContext
*context
; /* context executing the decompiler */
587 JSArenaPool
*pool
; /* string allocation pool */
588 char *base
; /* base address of buffer in pool */
589 size_t size
; /* size of buffer allocated at base */
590 ptrdiff_t offset
; /* offset of next free char in buffer */
593 #define INIT_SPRINTER(cx, sp, ap, off) \
594 ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0, \
597 #define OFF2STR(sp,off) ((sp)->base + (off))
598 #define STR2OFF(sp,str) ((str) - (sp)->base)
599 #define RETRACT(sp,str) ((sp)->offset = STR2OFF(sp, str))
602 SprintEnsureBuffer(Sprinter
*sp
, size_t len
)
607 nb
= (sp
->offset
+ len
+ 1) - sp
->size
;
612 JS_ARENA_ALLOCATE_CAST(base
, char *, sp
->pool
, nb
);
614 JS_ARENA_GROW_CAST(base
, char *, sp
->pool
, sp
->size
, nb
);
617 js_ReportOutOfScriptQuota(sp
->context
);
626 SprintPut(Sprinter
*sp
, const char *s
, size_t len
)
628 ptrdiff_t offset
= sp
->size
; /* save old size */
629 char *bp
= sp
->base
; /* save old base */
631 /* Allocate space for s, including the '\0' at the end. */
632 if (!SprintEnsureBuffer(sp
, len
))
635 if (sp
->base
!= bp
&& /* buffer was realloc'ed */
636 s
>= bp
&& s
< bp
+ offset
) { /* s was within the buffer */
637 s
= sp
->base
+ (s
- bp
); /* this is where it lives now */
640 /* Advance offset and copy s into sp's buffer. */
643 bp
= sp
->base
+ offset
;
650 SprintCString(Sprinter
*sp
, const char *s
)
652 return SprintPut(sp
, s
, strlen(s
));
656 SprintString(Sprinter
*sp
, JSString
*str
)
658 size_t length
= str
->length();
659 const jschar
*chars
= str
->getChars(sp
->context
);
663 size_t size
= js_GetDeflatedStringLength(sp
->context
, chars
, length
);
664 if (size
== (size_t)-1 || !SprintEnsureBuffer(sp
, size
))
667 ptrdiff_t offset
= sp
->offset
;
669 js_DeflateStringToBuffer(sp
->context
, chars
, length
, sp
->base
+ offset
,
671 sp
->base
[sp
->offset
] = 0;
677 Sprint(Sprinter
*sp
, const char *format
, ...)
683 va_start(ap
, format
);
684 bp
= JS_vsmprintf(format
, ap
); /* XXX vsaprintf */
687 JS_ReportOutOfMemory(sp
->context
);
690 offset
= SprintCString(sp
, bp
);
695 const char js_EscapeMap
[] = {
708 #define DONT_ESCAPE 0x10000
711 QuoteString(Sprinter
*sp
, JSString
*str
, uint32 quote
)
713 /* Sample off first for later return value pointer computation. */
714 JSBool dontEscape
= (quote
& DONT_ESCAPE
) != 0;
715 jschar qc
= (jschar
) quote
;
716 ptrdiff_t off
= sp
->offset
;
717 if (qc
&& Sprint(sp
, "%c", (char)qc
) < 0)
720 const jschar
*s
= str
->getChars(sp
->context
);
723 const jschar
*z
= s
+ str
->length();
725 /* Loop control variables: z points at end of string sentinel. */
726 for (const jschar
*t
= s
; t
< z
; s
= ++t
) {
727 /* Move t forward from s past un-quote-worthy characters. */
729 while (JS_ISPRINT(c
) && c
!= qc
&& c
!= '\\' && c
!= '\t' &&
735 ptrdiff_t len
= t
- s
;
737 /* Allocate space for s, including the '\0' at the end. */
738 if (!SprintEnsureBuffer(sp
, len
))
741 /* Advance sp->offset and copy s into sp's buffer. */
742 char *bp
= sp
->base
+ sp
->offset
;
751 /* Use js_EscapeMap, \u, or \x only if necessary. */
754 if (!(c
>> 8) && (e
= strchr(js_EscapeMap
, (int)c
)) != NULL
) {
756 ? Sprint(sp
, "%c", (char)c
) >= 0
757 : Sprint(sp
, "\\%c", e
[1]) >= 0;
760 * Use \x only if the high byte is 0 and we're in a quoted string,
761 * because ECMA-262 allows only \u, not \x, in Unicode identifiers
764 ok
= Sprint(sp
, (qc
&& !(c
>> 8)) ? "\\x%02X" : "\\u%04X", c
) >= 0;
770 /* Sprint the closing quote and return the quoted string. */
771 if (qc
&& Sprint(sp
, "%c", (char)qc
) < 0)
775 * If we haven't Sprint'd anything yet, Sprint an empty string so that
776 * the OFF2STR below gives a valid result.
778 if (off
== sp
->offset
&& Sprint(sp
, "") < 0)
780 return OFF2STR(sp
, off
);
784 js_QuoteString(JSContext
*cx
, JSString
*str
, jschar quote
)
791 mark
= JS_ARENA_MARK(&cx
->tempPool
);
792 INIT_SPRINTER(cx
, &sprinter
, &cx
->tempPool
, 0);
793 bytes
= QuoteString(&sprinter
, str
, quote
);
794 escstr
= bytes
? JS_NewStringCopyZ(cx
, bytes
) : NULL
;
795 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
799 /************************************************************************/
802 Sprinter sprinter
; /* base class state */
803 JSArenaPool pool
; /* string allocation pool */
804 uintN indent
; /* indentation in spaces */
805 bool pretty
; /* pretty-print: indent, use newlines */
806 bool grouped
; /* in parenthesized expression context */
807 bool strict
; /* in code marked strict */
808 JSScript
*script
; /* script being printed */
809 jsbytecode
*dvgfence
; /* DecompileExpression fencepost */
810 jsbytecode
**pcstack
; /* DecompileExpression modeled stack */
811 JSFunction
*fun
; /* interpreted function */
812 jsuword
*localNames
; /* argument and variable names */
816 js_NewPrinter(JSContext
*cx
, const char *name
, JSFunction
*fun
,
817 uintN indent
, JSBool pretty
, JSBool grouped
, JSBool strict
)
821 jp
= (JSPrinter
*) cx
->malloc(sizeof(JSPrinter
));
824 INIT_SPRINTER(cx
, &jp
->sprinter
, &jp
->pool
, 0);
825 JS_InitArenaPool(&jp
->pool
, name
, 256, 1, &cx
->scriptStackQuota
);
827 jp
->pretty
= !!pretty
;
828 jp
->grouped
= !!grouped
;
829 jp
->strict
= !!strict
;
834 jp
->localNames
= NULL
;
835 if (fun
&& fun
->isInterpreted() && fun
->script()->bindings
.hasLocalNames()) {
836 jp
->localNames
= fun
->script()->bindings
.getLocalNameArray(cx
, &jp
->pool
);
837 if (!jp
->localNames
) {
838 js_DestroyPrinter(jp
);
846 js_DestroyPrinter(JSPrinter
*jp
)
848 JS_FinishArenaPool(&jp
->pool
);
849 jp
->sprinter
.context
->free(jp
);
853 js_GetPrinterOutput(JSPrinter
*jp
)
858 cx
= jp
->sprinter
.context
;
859 if (!jp
->sprinter
.base
)
860 return cx
->runtime
->emptyString
;
861 str
= JS_NewStringCopyZ(cx
, jp
->sprinter
.base
);
864 JS_FreeArenaPool(&jp
->pool
);
865 INIT_SPRINTER(cx
, &jp
->sprinter
, &jp
->pool
, 0);
870 * NB: Indexed by SRC_DECL_* defines from jsemit.h.
872 static const char * const var_prefix
[] = {"var ", "const ", "let "};
875 VarPrefix(jssrcnote
*sn
)
877 if (sn
&& (SN_TYPE(sn
) == SRC_DECL
|| SN_TYPE(sn
) == SRC_GROUPASSIGN
)) {
878 ptrdiff_t type
= js_GetSrcNoteOffset(sn
, 0);
879 if ((uintN
)type
<= SRC_DECL_LET
)
880 return var_prefix
[type
];
886 js_printf(JSPrinter
*jp
, const char *format
, ...)
895 va_start(ap
, format
);
897 /* If pretty-printing, expand magic tab into a run of jp->indent spaces. */
898 if (*format
== '\t') {
900 if (jp
->pretty
&& Sprint(&jp
->sprinter
, "%*s", jp
->indent
, "") < 0) {
906 /* Suppress newlines (must be once per format, at the end) if not pretty. */
908 if (!jp
->pretty
&& format
[cc
= strlen(format
) - 1] == '\n') {
909 fp
= JS_strdup(jp
->sprinter
.context
, format
);
918 /* Allocate temp space, convert format, and put. */
919 bp
= JS_vsmprintf(format
, ap
); /* XXX vsaprintf */
921 jp
->sprinter
.context
->free(fp
);
925 JS_ReportOutOfMemory(jp
->sprinter
.context
);
931 if (SprintPut(&jp
->sprinter
, bp
, (size_t)cc
) < 0)
940 js_puts(JSPrinter
*jp
, const char *s
)
942 return SprintCString(&jp
->sprinter
, s
) >= 0;
945 /************************************************************************/
947 typedef struct SprintStack
{
948 Sprinter sprinter
; /* sprinter for postfix to infix buffering */
949 ptrdiff_t *offsets
; /* stack of postfix string offsets */
950 jsbytecode
*opcodes
; /* parallel stack of JS opcodes */
951 uintN top
; /* top of stack index */
952 uintN inArrayInit
; /* array initialiser/comprehension level */
953 JSBool inGenExp
; /* in generator expression */
954 JSPrinter
*printer
; /* permanent output goes here */
958 * Find the depth of the operand stack when the interpreter reaches the given
959 * pc in script. pcstack must have space for least script->depth elements. On
960 * return it will contain pointers to opcodes that populated the interpreter's
961 * current operand stack.
963 * This function cannot raise an exception or error. However, due to a risk of
964 * potential bugs when modeling the stack, the function returns -1 if it
965 * detects an inconsistency in the model. Such an inconsistency triggers an
966 * assert in a debug build.
969 ReconstructPCStack(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
,
970 jsbytecode
**pcstack
);
972 #define FAILED_EXPRESSION_DECOMPILER ((char *) 1)
975 * Decompile a part of expression up to the given pc. The function returns
976 * NULL on out-of-memory, or the FAILED_EXPRESSION_DECOMPILER sentinel when
977 * the decompiler fails due to a bug and/or unimplemented feature, or the
978 * decompiled string on success.
981 DecompileExpression(JSContext
*cx
, JSScript
*script
, JSFunction
*fun
,
985 * Get a stacked offset from ss->sprinter.base, or if the stacked value |off|
986 * is negative, fetch the generating pc from printer->pcstack[-2 - off] and
987 * decompile the code that generated the missing value. This is used when
988 * reporting errors, where the model stack will lack |pcdepth| non-negative
989 * offsets (see DecompileExpression and DecompileCode).
991 * If the stacked offset is -1, return 0 to index the NUL padding at the start
992 * of ss->sprinter.base. If this happens, it means there is a decompiler bug
993 * to fix, but it won't violate memory safety.
996 GetOff(SprintStack
*ss
, uintN i
)
1002 off
= ss
->offsets
[i
];
1006 JS_ASSERT(off
<= -2);
1007 JS_ASSERT(ss
->printer
->pcstack
);
1008 if (off
<= -2 && ss
->printer
->pcstack
) {
1009 pc
= ss
->printer
->pcstack
[-2 - off
];
1010 bytes
= DecompileExpression(ss
->sprinter
.context
, ss
->printer
->script
,
1011 ss
->printer
->fun
, pc
);
1014 if (bytes
!= FAILED_EXPRESSION_DECOMPILER
) {
1015 off
= SprintCString(&ss
->sprinter
, bytes
);
1018 ss
->offsets
[i
] = off
;
1019 ss
->sprinter
.context
->free(bytes
);
1022 if (!ss
->sprinter
.base
&& SprintPut(&ss
->sprinter
, "", 0) >= 0) {
1023 memset(ss
->sprinter
.base
, 0, ss
->sprinter
.offset
);
1024 ss
->offsets
[i
] = -1;
1031 GetStr(SprintStack
*ss
, uintN i
)
1036 * Must call GetOff before using ss->sprinter.base, since it may be null
1037 * until bootstrapped by GetOff.
1039 off
= GetOff(ss
, i
);
1040 return OFF2STR(&ss
->sprinter
, off
);
1044 * Gap between stacked strings to allow for insertion of parens and commas
1045 * when auto-parenthesizing expressions and decompiling array initialisers.
1047 #define PAREN_SLOP (2 + 1)
1049 /* Fake opcodes (see jsopcode.h) must not overflow unsigned 8-bit space. */
1050 JS_STATIC_ASSERT(JSOP_FAKE_LIMIT
<= 255);
1053 AddParenSlop(SprintStack
*ss
)
1055 memset(OFF2STR(&ss
->sprinter
, ss
->sprinter
.offset
), 0, PAREN_SLOP
);
1056 ss
->sprinter
.offset
+= PAREN_SLOP
;
1060 PushOff(SprintStack
*ss
, ptrdiff_t off
, JSOp op
)
1064 if (!SprintEnsureBuffer(&ss
->sprinter
, PAREN_SLOP
))
1067 /* ss->top points to the next free slot; be paranoid about overflow. */
1069 JS_ASSERT(top
< StackDepth(ss
->printer
->script
));
1070 if (top
>= StackDepth(ss
->printer
->script
)) {
1071 JS_ReportOutOfMemory(ss
->sprinter
.context
);
1075 /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */
1076 ss
->offsets
[top
] = off
;
1077 ss
->opcodes
[top
] = jsbytecode((op
== JSOP_GETPROP2
) ? JSOP_GETPROP
1078 : (op
== JSOP_GETELEM2
) ? JSOP_GETELEM
1086 PopOffPrec(SprintStack
*ss
, uint8 prec
)
1089 const JSCodeSpec
*topcs
;
1092 /* ss->top points to the next free slot; be paranoid about underflow. */
1094 JS_ASSERT(top
!= 0);
1099 off
= GetOff(ss
, top
);
1100 topcs
= &js_CodeSpec
[ss
->opcodes
[top
]];
1101 if (topcs
->prec
!= 0 && topcs
->prec
< prec
) {
1102 ss
->sprinter
.offset
= ss
->offsets
[top
] = off
- 2;
1103 off
= Sprint(&ss
->sprinter
, "(%s)", OFF2STR(&ss
->sprinter
, off
));
1105 ss
->sprinter
.offset
= off
;
1111 PopStrPrec(SprintStack
*ss
, uint8 prec
)
1115 off
= PopOffPrec(ss
, prec
);
1116 return OFF2STR(&ss
->sprinter
, off
);
1120 PopOff(SprintStack
*ss
, JSOp op
)
1122 return PopOffPrec(ss
, js_CodeSpec
[op
].prec
);
1126 PopStr(SprintStack
*ss
, JSOp op
)
1128 return PopStrPrec(ss
, js_CodeSpec
[op
].prec
);
1132 IsInitializerOp(unsigned char op
)
1134 return op
== JSOP_NEWINIT
|| op
== JSOP_NEWARRAY
|| op
== JSOP_NEWOBJECT
;
1137 typedef struct TableEntry
{
1141 jsint order
; /* source order for stable tableswitch sort */
1145 CompareOffsets(void *arg
, const void *v1
, const void *v2
, int *result
)
1147 ptrdiff_t offset_diff
;
1148 const TableEntry
*te1
= (const TableEntry
*) v1
,
1149 *te2
= (const TableEntry
*) v2
;
1151 offset_diff
= te1
->offset
- te2
->offset
;
1152 *result
= (offset_diff
== 0 ? te1
->order
- te2
->order
1153 : offset_diff
< 0 ? -1
1159 SprintDoubleValue(Sprinter
*sp
, jsval v
, JSOp
*opp
)
1165 JS_ASSERT(JSVAL_IS_DOUBLE(v
));
1166 d
= JSVAL_TO_DOUBLE(v
);
1167 if (JSDOUBLE_IS_NEGZERO(d
)) {
1168 todo
= SprintCString(sp
, "-0");
1170 } else if (!JSDOUBLE_IS_FINITE(d
)) {
1171 /* Don't use Infinity and NaN, they're mutable. */
1172 todo
= SprintCString(sp
,
1181 s
= NumberToCString(sp
->context
, &cbuf
, d
);
1183 JS_ReportOutOfMemory(sp
->context
);
1186 JS_ASSERT(strcmp(s
, js_Infinity_str
) &&
1188 strcmp(s
+ 1, js_Infinity_str
)) &&
1189 strcmp(s
, js_NaN_str
));
1190 todo
= Sprint(sp
, s
);
1196 Decompile(SprintStack
*ss
, jsbytecode
*pc
, intN nb
, JSOp nextop
);
1199 DecompileSwitch(SprintStack
*ss
, TableEntry
*table
, uintN tableLength
,
1200 jsbytecode
*pc
, ptrdiff_t switchLength
,
1201 ptrdiff_t defaultOffset
, JSBool isCondSwitch
)
1205 ptrdiff_t off
, off2
, diff
, caseExprOff
, todo
;
1211 cx
= ss
->sprinter
.context
;
1214 /* JSOP_CONDSWITCH doesn't pop, unlike JSOP_{LOOKUP,TABLE}SWITCH. */
1215 off
= isCondSwitch
? GetOff(ss
, ss
->top
-1) : PopOff(ss
, JSOP_NOP
);
1216 lval
= OFF2STR(&ss
->sprinter
, off
);
1218 js_printf(jp
, "\tswitch (%s) {\n", lval
);
1221 diff
= table
[0].offset
- defaultOffset
;
1224 js_printf(jp
, "\t%s:\n", js_default_str
);
1226 if (!Decompile(ss
, pc
+ defaultOffset
, diff
, JSOP_NOP
))
1231 caseExprOff
= isCondSwitch
? JSOP_CONDSWITCH_LENGTH
: 0;
1233 for (i
= 0; i
< tableLength
; i
++) {
1234 off
= table
[i
].offset
;
1235 off2
= (i
+ 1 < tableLength
) ? table
[i
+ 1].offset
: switchLength
;
1239 ptrdiff_t nextCaseExprOff
;
1242 * key encodes the JSOP_CASE bytecode's offset from switchtop.
1243 * The next case expression follows immediately, unless we are
1246 nextCaseExprOff
= (ptrdiff_t)JSVAL_TO_INT(key
);
1247 nextCaseExprOff
+= js_CodeSpec
[pc
[nextCaseExprOff
]].length
;
1249 if (!Decompile(ss
, pc
+ caseExprOff
,
1250 nextCaseExprOff
- caseExprOff
, JSOP_NOP
)) {
1253 caseExprOff
= nextCaseExprOff
;
1255 /* Balance the stack as if this JSOP_CASE matched. */
1259 * key comes from an atom, not the decompiler, so we need to
1260 * quote it if it's a string literal. But if table[i].label
1261 * is non-null, key was constant-propagated and label is the
1262 * name of the const we should show as the case label. We set
1263 * key to undefined so this identifier is escaped, if required
1264 * by non-ASCII characters, but not quoted, by QuoteString.
1267 if (table
[i
].label
) {
1268 str
= ATOM_TO_STRING(table
[i
].label
);
1270 } else if (JSVAL_IS_DOUBLE(key
)) {
1273 todo
= SprintDoubleValue(&ss
->sprinter
, key
, &junk
);
1276 str
= js_ValueToString(cx
, Valueify(key
));
1281 rval
= OFF2STR(&ss
->sprinter
, todo
);
1283 rval
= QuoteString(&ss
->sprinter
, str
, (jschar
)
1284 (JSVAL_IS_STRING(key
) ? '"' : 0));
1288 RETRACT(&ss
->sprinter
, rval
);
1290 js_printf(jp
, "\tcase %s:\n", rval
);
1294 if (off
<= defaultOffset
&& defaultOffset
< off2
) {
1295 diff
= defaultOffset
- off
;
1297 if (!Decompile(ss
, pc
+ off
, diff
, JSOP_NOP
))
1299 off
= defaultOffset
;
1302 js_printf(jp
, "\t%s:\n", js_default_str
);
1305 if (!Decompile(ss
, pc
+ off
, off2
- off
, JSOP_NOP
))
1309 /* Re-balance as if last JSOP_CASE or JSOP_DEFAULT mismatched. */
1315 if (defaultOffset
== switchLength
) {
1317 js_printf(jp
, "\t%s:;\n", js_default_str
);
1320 js_printf(jp
, "\t}\n");
1322 /* By the end of a JSOP_CONDSWITCH, the discriminant has been popped. */
1328 #define LOCAL_ASSERT_CUSTOM(expr, BAD_EXIT) \
1331 if (!(expr)) { BAD_EXIT; } \
1334 #define LOCAL_ASSERT_RV(expr, rv) \
1335 LOCAL_ASSERT_CUSTOM(expr, return (rv))
1338 GetArgOrVarAtom(JSPrinter
*jp
, uintN slot
)
1342 LOCAL_ASSERT_RV(jp
->fun
, NULL
);
1343 LOCAL_ASSERT_RV(slot
< jp
->fun
->script()->bindings
.countLocalNames(), NULL
);
1344 name
= JS_LOCAL_NAME_TO_ATOM(jp
->localNames
[slot
]);
1345 #if !JS_HAS_DESTRUCTURING
1346 LOCAL_ASSERT_RV(name
, NULL
);
1352 GetLocal(SprintStack
*ss
, jsint i
)
1354 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, "")
1356 ptrdiff_t off
= ss
->offsets
[i
];
1358 return OFF2STR(&ss
->sprinter
, off
);
1361 * We must be called from js_DecompileValueGenerator (via Decompile) when
1362 * dereferencing a local that's undefined or null. Search script->objects
1363 * for the block containing this local by its stack index, i.
1365 * In case of destructuring's use of JSOP_GETLOCAL, however, there may be
1366 * no such local. This could mean no blocks (no script objects at all, or
1367 * none of the script's object literals are blocks), or the stack slot i is
1368 * not in a block. In either case, return GetStr(ss, i).
1370 JSScript
*script
= ss
->printer
->script
;
1371 if (!JSScript::isValidOffset(script
->objectsOffset
))
1372 return GetStr(ss
, i
);
1374 for (jsatomid j
= 0, n
= script
->objects()->length
; j
!= n
; j
++) {
1375 JSObject
*obj
= script
->getObject(j
);
1376 if (obj
->isBlock()) {
1377 jsint depth
= OBJ_BLOCK_DEPTH(cx
, obj
);
1378 jsint count
= OBJ_BLOCK_COUNT(cx
, obj
);
1380 if (jsuint(i
- depth
) < jsuint(count
)) {
1381 jsint slot
= i
- depth
;
1383 for (Shape::Range
r(obj
->lastProperty()); !r
.empty(); r
.popFront()) {
1384 const Shape
&shape
= r
.front();
1386 if (shape
.shortid
== slot
) {
1387 LOCAL_ASSERT(JSID_IS_ATOM(shape
.id
));
1389 JSAtom
*atom
= JSID_TO_ATOM(shape
.id
);
1390 const char *rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
1394 RETRACT(&ss
->sprinter
, rval
);
1404 return GetStr(ss
, i
);
1410 IsVarSlot(JSPrinter
*jp
, jsbytecode
*pc
, jsint
*indexp
)
1414 slot
= GET_SLOTNO(pc
);
1415 if (slot
< jp
->script
->nfixed
) {
1416 /* The slot refers to a variable with name stored in jp->localNames. */
1417 *indexp
= jp
->fun
->nargs
+ slot
;
1421 /* We have a local which index is relative to the stack base. */
1422 slot
-= jp
->script
->nfixed
;
1423 JS_ASSERT(slot
< StackDepth(jp
->script
));
1428 #define LOAD_ATOM(PCOFF) \
1429 GET_ATOM_FROM_BYTECODE(jp->script, pc, PCOFF, atom)
1431 #if JS_HAS_DESTRUCTURING
1433 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
1434 #define LOAD_OP_DATA(pc) (oplen = (cs = &js_CodeSpec[op=(JSOp)*pc])->length)
1437 DecompileDestructuring(SprintStack
*ss
, jsbytecode
*pc
, jsbytecode
*endpc
);
1440 DecompileDestructuringLHS(SprintStack
*ss
, jsbytecode
*pc
, jsbytecode
*endpc
,
1446 const JSCodeSpec
*cs
;
1449 const char *lval
, *xval
;
1454 cx
= ss
->sprinter
.context
;
1461 todo
= SprintPut(&ss
->sprinter
, ", ", 2);
1465 pc
= DecompileDestructuring(ss
, pc
, endpc
);
1471 lval
= PopStr(ss
, JSOP_NOP
);
1472 todo
= SprintCString(&ss
->sprinter
, lval
);
1473 if (op
== JSOP_POPN
)
1475 LOCAL_ASSERT(*pc
== JSOP_POP
);
1480 LOCAL_ASSERT(pc
[oplen
] == JSOP_POP
|| pc
[oplen
] == JSOP_POPN
);
1483 case JSOP_SETLOCALPOP
:
1486 if (op
== JSOP_SETARG
) {
1487 atom
= GetArgOrVarAtom(jp
, GET_SLOTNO(pc
));
1489 } else if (IsVarSlot(jp
, pc
, &i
)) {
1490 atom
= GetArgOrVarAtom(jp
, i
);
1493 lval
= GetLocal(ss
, i
);
1496 JSAutoByteString bytes
;
1498 lval
= js_AtomToPrintableString(cx
, atom
, &bytes
);
1500 todo
= SprintCString(&ss
->sprinter
, lval
);
1502 if (op
!= JSOP_SETLOCALPOP
) {
1507 if (op
== JSOP_POPN
)
1509 LOCAL_ASSERT(op
== JSOP_POP
);
1515 * We may need to auto-parenthesize the left-most value decompiled
1516 * here, so add back PAREN_SLOP temporarily. Then decompile until the
1517 * opcode that would reduce the stack depth to (ss->top-1), which we
1518 * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for
1521 todo
= ss
->sprinter
.offset
;
1522 ss
->sprinter
.offset
= todo
+ PAREN_SLOP
;
1523 pc
= Decompile(ss
, pc
, -((intN
)ss
->top
), JSOP_NOP
);
1529 LOCAL_ASSERT(op
== JSOP_ENUMELEM
|| op
== JSOP_ENUMCONSTELEM
);
1530 xval
= PopStr(ss
, JSOP_NOP
);
1531 lval
= PopStr(ss
, JSOP_GETPROP
);
1532 ss
->sprinter
.offset
= todo
;
1533 if (*lval
== '\0') {
1534 /* lval is from JSOP_BINDNAME, so just print xval. */
1535 todo
= SprintCString(&ss
->sprinter
, xval
);
1536 } else if (*xval
== '\0') {
1537 /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */
1538 todo
= SprintCString(&ss
->sprinter
, lval
);
1540 todo
= Sprint(&ss
->sprinter
,
1541 (JOF_OPMODE(ss
->opcodes
[ss
->top
+1]) == JOF_XMLNAME
)
1552 LOCAL_ASSERT(pc
< endpc
);
1558 * Starting with a SRC_DESTRUCT-annotated JSOP_DUP, decompile a destructuring
1559 * left-hand side object or array initialiser, including nested destructuring
1560 * initialisers. On successful return, the decompilation will be pushed on ss
1561 * and the return value will point to the POP or GROUP bytecode following the
1562 * destructuring expression.
1564 * At any point, if pc is equal to endpc and would otherwise advance, we stop
1565 * immediately and return endpc.
1568 DecompileDestructuring(SprintStack
*ss
, jsbytecode
*pc
, jsbytecode
*endpc
)
1574 const JSCodeSpec
*cs
;
1583 LOCAL_ASSERT(*pc
== JSOP_DUP
);
1584 pc
+= JSOP_DUP_LENGTH
;
1587 * Set head so we can rewrite '[' to '{' as needed. Back up PAREN_SLOP
1588 * chars so the destructuring decompilation accumulates contiguously in
1589 * ss->sprinter starting with "[".
1591 head
= SprintPut(&ss
->sprinter
, "[", 1);
1592 if (head
< 0 || !PushOff(ss
, head
, JSOP_NOP
))
1594 ss
->sprinter
.offset
-= PAREN_SLOP
;
1595 LOCAL_ASSERT(head
== ss
->sprinter
.offset
- 1);
1596 LOCAL_ASSERT(*OFF2STR(&ss
->sprinter
, head
) == '[');
1598 cx
= ss
->sprinter
.context
;
1602 while (pc
< endpc
) {
1603 #if JS_HAS_DESTRUCTURING_SHORTHAND
1604 ptrdiff_t nameoff
= -1;
1615 /* Handle the optimized number-pushing opcodes. */
1616 case JSOP_ZERO
: d
= i
= 0; goto do_getelem
;
1617 case JSOP_ONE
: d
= i
= 1; goto do_getelem
;
1618 case JSOP_UINT16
: d
= i
= GET_UINT16(pc
); goto do_getelem
;
1619 case JSOP_UINT24
: d
= i
= GET_UINT24(pc
); goto do_getelem
;
1620 case JSOP_INT8
: d
= i
= GET_INT8(pc
); goto do_getelem
;
1621 case JSOP_INT32
: d
= i
= GET_INT32(pc
); goto do_getelem
;
1624 GET_DOUBLE_FROM_BYTECODE(jp
->script
, pc
, 0, d
);
1625 LOCAL_ASSERT(JSDOUBLE_IS_FINITE(d
) && !JSDOUBLE_IS_NEGZERO(d
));
1629 sn
= js_GetSrcNote(jp
->script
, pc
);
1634 LOCAL_ASSERT(op
== JSOP_GETELEM
);
1636 /* Distinguish object from array by opcode or source note. */
1637 if (sn
&& SN_TYPE(sn
) == SRC_INITPROP
) {
1638 *OFF2STR(&ss
->sprinter
, head
) = '{';
1639 if (Sprint(&ss
->sprinter
, "%g: ", d
) < 0)
1642 /* Sanity check for the gnarly control flow above. */
1643 LOCAL_ASSERT(i
== d
);
1645 /* Fill in any holes (holes at the end don't matter). */
1646 while (++lasti
< i
) {
1647 if (SprintPut(&ss
->sprinter
, ", ", 2) < 0)
1654 atom
= cx
->runtime
->atomState
.lengthAtom
;
1655 goto do_destructure_atom
;
1660 do_destructure_atom
:
1662 *OFF2STR(&ss
->sprinter
, head
) = '{';
1663 #if JS_HAS_DESTRUCTURING_SHORTHAND
1664 nameoff
= ss
->sprinter
.offset
;
1666 if (!QuoteString(&ss
->sprinter
, atom
,
1667 js_IsIdentifier(atom
) ? 0 : (jschar
)'\'')) {
1670 if (SprintPut(&ss
->sprinter
, ": ", 2) < 0)
1684 * Decompile the left-hand side expression whose bytecode starts at pc
1685 * and continues for a bounded number of bytecodes or stack operations
1686 * (and which in any event stops before endpc).
1688 pc
= DecompileDestructuringLHS(ss
, pc
, endpc
, &hole
);
1692 #if JS_HAS_DESTRUCTURING_SHORTHAND
1694 ptrdiff_t offset
, initlen
;
1696 offset
= ss
->sprinter
.offset
;
1697 LOCAL_ASSERT(*OFF2STR(&ss
->sprinter
, offset
) == '\0');
1698 initlen
= offset
- nameoff
;
1699 LOCAL_ASSERT(initlen
>= 4);
1701 /* Early check to rule out odd "name: lval" length. */
1702 if (((size_t)initlen
& 1) == 0) {
1707 * Even "name: lval" string length: check for "x: x" and the
1708 * like, and apply the shorthand if we can.
1710 namelen
= (size_t)(initlen
- 2) >> 1;
1711 name
= OFF2STR(&ss
->sprinter
, nameoff
);
1712 if (!strncmp(name
+ namelen
, ": ", 2) &&
1713 !strncmp(name
, name
+ namelen
+ 2, namelen
)) {
1714 offset
-= namelen
+ 2;
1715 *OFF2STR(&ss
->sprinter
, offset
) = '\0';
1716 ss
->sprinter
.offset
= offset
;
1722 if (pc
== endpc
|| *pc
!= JSOP_DUP
)
1726 * We should stop if JSOP_DUP is either without notes or its note is
1727 * not SRC_CONTINUE. The former happens when JSOP_DUP duplicates the
1728 * last destructuring reference implementing an op= assignment like in
1729 * '([t] = z).y += x'. In the latter case the note is SRC_DESTRUCT and
1730 * means another destructuring initialiser abuts this one like in
1733 sn
= js_GetSrcNote(jp
->script
, pc
);
1736 if (SN_TYPE(sn
) != SRC_CONTINUE
) {
1737 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_DESTRUCT
);
1741 if (!hole
&& SprintPut(&ss
->sprinter
, ", ", 2) < 0)
1744 pc
+= JSOP_DUP_LENGTH
;
1748 lval
= OFF2STR(&ss
->sprinter
, head
);
1749 if (SprintPut(&ss
->sprinter
, (*lval
== '[') ? "]" : "}", 1) < 0)
1755 DecompileGroupAssignment(SprintStack
*ss
, jsbytecode
*pc
, jsbytecode
*endpc
,
1756 jssrcnote
*sn
, ptrdiff_t *todop
)
1759 const JSCodeSpec
*cs
;
1760 uintN oplen
, start
, end
, i
;
1766 LOCAL_ASSERT(op
== JSOP_PUSH
|| op
== JSOP_GETLOCAL
);
1768 todo
= Sprint(&ss
->sprinter
, "%s[", VarPrefix(sn
));
1769 if (todo
< 0 || !PushOff(ss
, todo
, JSOP_NOP
))
1771 ss
->sprinter
.offset
-= PAREN_SLOP
;
1777 pc
= DecompileDestructuringLHS(ss
, pc
, endpc
, &hole
);
1783 if (op
!= JSOP_PUSH
&& op
!= JSOP_GETLOCAL
)
1785 if (!hole
&& SprintPut(&ss
->sprinter
, ", ", 2) < 0)
1789 LOCAL_ASSERT(op
== JSOP_POPN
);
1790 if (SprintPut(&ss
->sprinter
, "] = [", 5) < 0)
1794 start
= end
- GET_UINT16(pc
);
1795 for (i
= start
; i
< end
; i
++) {
1796 rval
= GetStr(ss
, i
);
1797 if (Sprint(&ss
->sprinter
,
1798 (i
== start
) ? "%s" : ", %s",
1799 (i
== end
- 1 && *rval
== '\0') ? ", " : rval
) < 0) {
1804 if (SprintPut(&ss
->sprinter
, "]", 1) < 0)
1806 ss
->sprinter
.offset
= ss
->offsets
[i
];
1815 #endif /* JS_HAS_DESTRUCTURING */
1818 InitSprintStack(JSContext
*cx
, SprintStack
*ss
, JSPrinter
*jp
, uintN depth
)
1820 size_t offsetsz
, opcodesz
;
1823 INIT_SPRINTER(cx
, &ss
->sprinter
, &cx
->tempPool
, PAREN_SLOP
);
1825 /* Allocate the parallel (to avoid padding) offset and opcode stacks. */
1826 offsetsz
= depth
* sizeof(ptrdiff_t);
1827 opcodesz
= depth
* sizeof(jsbytecode
);
1828 JS_ARENA_ALLOCATE(space
, &cx
->tempPool
, offsetsz
+ opcodesz
);
1830 js_ReportOutOfScriptQuota(cx
);
1833 ss
->offsets
= (ptrdiff_t *) space
;
1834 ss
->opcodes
= (jsbytecode
*) ((char *)space
+ offsetsz
);
1836 ss
->top
= ss
->inArrayInit
= 0;
1837 ss
->inGenExp
= JS_FALSE
;
1843 * If nb is non-negative, decompile nb bytecodes starting at pc. Otherwise
1844 * the decompiler starts at pc and continues until it reaches an opcode for
1845 * which decompiling would result in the stack depth equaling -(nb + 1).
1847 * The nextop parameter is either JSOP_NOP or the "next" opcode in order of
1848 * abstract interpretation (not necessarily physically next in a bytecode
1849 * vector). So nextop is JSOP_POP for the last operand in a comma expression,
1850 * or JSOP_AND for the right operand of &&.
1853 Decompile(SprintStack
*ss
, jsbytecode
*pc
, intN nb
, JSOp nextop
)
1856 JSPrinter
*jp
, *jp2
;
1857 jsbytecode
*startpc
, *endpc
, *pc2
, *done
;
1858 ptrdiff_t tail
, todo
, len
, oplen
, cond
, next
;
1859 JSOp op
, lastop
, saveop
;
1860 const JSCodeSpec
*cs
;
1861 jssrcnote
*sn
, *sn2
;
1862 const char *lval
, *rval
, *xval
, *fmt
, *token
;
1871 #if JS_HAS_XML_SUPPORT
1872 JSBool foreach
, inXML
, quoteAttr
;
1874 #define inXML JS_FALSE
1878 static const char exception_cookie
[] = "/*EXCEPTION*/";
1879 static const char retsub_pc_cookie
[] = "/*RETSUB_PC*/";
1880 static const char forelem_cookie
[] = "/*FORELEM*/";
1881 static const char with_cookie
[] = "/*WITH*/";
1882 static const char dot_format
[] = "%s.%s";
1883 static const char index_format
[] = "%s[%s]";
1884 static const char predot_format
[] = "%s%s.%s";
1885 static const char postdot_format
[] = "%s.%s%s";
1886 static const char preindex_format
[] = "%s%s[%s]";
1887 static const char postindex_format
[] = "%s[%s]%s";
1888 static const char ss_format
[] = "%s%s";
1889 static const char sss_format
[] = "%s%s%s";
1891 /* Argument and variables decompilation uses the following to share code. */
1892 JS_STATIC_ASSERT(ARGNO_LEN
== SLOTNO_LEN
);
1897 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
1898 #define DECOMPILE_CODE(pc,nb) if (!Decompile(ss, pc, nb, JSOP_NOP)) return NULL
1899 #define NEXT_OP(pc) (((pc) + (len) == endpc) ? nextop : pc[len])
1900 #define TOP_STR() GetStr(ss, ss->top - 1)
1901 #define POP_STR() PopStr(ss, op)
1902 #define POP_STR_PREC(prec) PopStrPrec(ss, prec)
1905 * Pop a condition expression for if/while. JSOP_IFEQ's precedence forces
1906 * extra parens around assignment, which avoids a strict-mode warning.
1908 #define POP_COND_STR() \
1909 PopStr(ss, (js_CodeSpec[ss->opcodes[ss->top - 1]].format & JOF_SET) \
1913 #define ATOM_IS_IDENTIFIER(atom) js_IsIdentifier(atom)
1916 * Given an atom already fetched from jp->script's atom map, quote/escape its
1917 * string appropriately into rval, and select fmt from the quoted and unquoted
1920 #define GET_QUOTE_AND_FMT(qfmt, ufmt, rval) \
1923 if (!ATOM_IS_IDENTIFIER(atom)) { \
1930 rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), quote_); \
1935 #define LOAD_OBJECT(PCOFF) \
1936 GET_OBJECT_FROM_BYTECODE(jp->script, pc, PCOFF, obj)
1938 #define LOAD_FUNCTION(PCOFF) \
1939 GET_FUNCTION_FROM_BYTECODE(jp->script, pc, PCOFF, fun)
1941 #define LOAD_REGEXP(PCOFF) \
1942 GET_REGEXP_FROM_BYTECODE(jp->script, pc, PCOFF, obj)
1944 #define GET_SOURCE_NOTE_ATOM(sn, atom) \
1946 jsatomid atomIndex_ = (jsatomid) js_GetSrcNoteOffset((sn), 0); \
1948 LOCAL_ASSERT(atomIndex_ < jp->script->atomMap.length); \
1949 (atom) = jp->script->atomMap.vector[atomIndex_]; \
1953 * Get atom from jp->script's atom map, quote/escape its string appropriately
1954 * into rval, and select fmt from the quoted and unquoted alternatives.
1956 #define GET_ATOM_QUOTE_AND_FMT(qfmt, ufmt, rval) \
1959 GET_QUOTE_AND_FMT(qfmt, ufmt, rval); \
1963 * Per spec, new x(y).z means (new x(y))).z. For example new (x(y).z) must
1964 * decompile with the constructor parenthesized, but new x.z should not. The
1965 * normal rules give x(y).z and x.z identical precedence: both are produced by
1968 * Therefore, we need to know in case JSOP_NEW whether the constructor
1969 * expression contains any unparenthesized function calls. So when building a
1970 * MemberExpression or CallExpression, we set ss->opcodes[n] to JSOP_CALL if
1971 * this is true. x(y).z gets JSOP_CALL, not JSOP_GETPROP.
1973 #define PROPAGATE_CALLNESS() \
1975 if (ss->opcodes[ss->top - 1] == JSOP_CALL || \
1976 ss->opcodes[ss->top - 1] == JSOP_EVAL || \
1977 ss->opcodes[ss->top - 1] == JSOP_FUNCALL || \
1978 ss->opcodes[ss->top - 1] == JSOP_FUNAPPLY) { \
1979 saveop = JSOP_CALL; \
1983 cx
= ss
->sprinter
.context
;
1984 JS_CHECK_RECURSION(cx
, return NULL
);
1988 endpc
= (nb
< 0) ? jp
->script
->code
+ jp
->script
->length
: pc
+ nb
;
1990 todo
= -2; /* NB: different from Sprint() error return. */
1994 #if JS_HAS_XML_SUPPORT
1995 foreach
= inXML
= quoteAttr
= JS_FALSE
;
1998 while (nb
< 0 || pc
< endpc
) {
2000 * Move saveop to lastop so prefixed bytecodes can take special action
2001 * while sharing maximal code. Set op and saveop to the new bytecode,
2002 * use op in POP_STR to trigger automatic parenthesization, but push
2003 * saveop at the bottom of the loop if this op pushes. Thus op may be
2004 * set to nop or otherwise mutated to suppress auto-parens.
2008 cs
= &js_CodeSpec
[op
];
2009 if (cs
->format
& JOF_INDEXBASE
) {
2011 * The decompiler uses js_GetIndexFromBytecode to get atoms and
2012 * objects and ignores these suffix/prefix bytecodes, thus
2013 * simplifying code that must process JSOP_GETTER/JSOP_SETTER
2020 cs
= &js_CodeSpec
[op
];
2023 len
= oplen
= cs
->length
;
2024 nuses
= js_GetStackUses(cs
, op
, pc
);
2027 * Here it is possible that nuses > ss->top when the op has a hidden
2028 * source note. But when nb < 0 we assume that the caller knows that
2029 * Decompile would never meet such opcodes.
2032 LOCAL_ASSERT(ss
->top
>= nuses
);
2033 uintN ndefs
= js_GetStackDefs(cx
, cs
, op
, jp
->script
, pc
);
2034 if ((uintN
) -(nb
+ 1) == ss
->top
- nuses
+ ndefs
)
2039 * Save source literal associated with JS now before the following
2040 * rewrite changes op. See bug 380197.
2042 token
= CodeToken
[op
];
2044 if (pc
+ oplen
== jp
->dvgfence
) {
2046 uint32 format
, mode
, type
;
2049 * Rewrite non-get ops to their "get" format if the error is in
2050 * the bytecode at pc, so we don't decompile more than the error
2053 fp
= js_GetScriptedCaller(cx
, NULL
);
2054 format
= cs
->format
;
2055 if (((fp
&& pc
== fp
->pc(cx
)) ||
2056 (pc
== startpc
&& nuses
!= 0)) &&
2057 format
& (JOF_SET
|JOF_DEL
|JOF_INCDEC
|JOF_FOR
|JOF_VARPROP
)) {
2058 mode
= JOF_MODE(format
);
2059 if (mode
== JOF_NAME
) {
2061 * JOF_NAME does not imply JOF_ATOM, so we must check for
2062 * the QARG and QVAR format types, and translate those to
2063 * JSOP_GETARG or JSOP_GETLOCAL appropriately, instead of
2066 type
= JOF_TYPE(format
);
2067 op
= (type
== JOF_QARG
)
2069 : (type
== JOF_LOCAL
)
2073 JS_ASSERT(js_CodeSpec
[op
].nuses
>= 0);
2074 i
= nuses
- js_CodeSpec
[op
].nuses
;
2076 PopOff(ss
, JSOP_NOP
);
2079 * We must replace the faulting pc's bytecode with a
2080 * corresponding JSOP_GET* code. For JSOP_SET{PROP,ELEM},
2081 * we must use the "2nd" form of JSOP_GET{PROP,ELEM}, to
2082 * throw away the assignment op's right-hand operand and
2083 * decompile it as if it were a GET of its left-hand
2086 if (mode
== JOF_PROP
) {
2087 op
= (JSOp
) ((format
& JOF_SET
)
2090 } else if (mode
== JOF_ELEM
) {
2091 op
= (JSOp
) ((format
& JOF_SET
)
2096 * Unknown mode (including mode 0) means that op is
2097 * uncategorized for our purposes, so we must write
2098 * per-op special case code here.
2102 case JSOP_ENUMCONSTELEM
:
2105 case JSOP_GETTHISPROP
:
2107 * NB: JSOP_GETTHISPROP can't fail due to |this|
2108 * being null or undefined at runtime (beware that
2109 * this may change for ES4). Therefore any error
2110 * resulting from this op must be due to the value
2111 * of the property accessed via |this|, so do not
2112 * rewrite op to JSOP_THIS.
2114 * The next two cases should not change op if
2115 * js_DecompileValueGenerator was called from the
2116 * the property getter. They should rewrite only
2117 * if the base object in the arg/var/local is null
2118 * or undefined. FIXME: bug 431569.
2121 case JSOP_GETARGPROP
:
2124 case JSOP_GETLOCALPROP
:
2127 case JSOP_SETXMLNAME
:
2128 op
= JSOp(JSOP_GETELEM2
);
2138 if (op
>= JSOP_LIMIT
) {
2139 if (op
== JSOP_GETPROP2
)
2140 saveop
= JSOP_GETPROP
;
2141 else if (op
== JSOP_GETELEM2
)
2142 saveop
= JSOP_GETELEM
;
2144 LOCAL_ASSERT(js_CodeSpec
[saveop
].length
== oplen
||
2145 JOF_TYPE(format
) == JOF_SLOTATOM
);
2147 jp
->dvgfence
= NULL
;
2153 sn
= js_GetSrcNote(jp
->script
, pc
);
2154 if (sn
&& SN_TYPE(sn
) == SRC_ASSIGNOP
) {
2156 * Avoid over-parenthesizing y in x op= y based on its
2157 * expansion: x = x op y (replace y by z = w to see the
2160 op
= (JSOp
) pc
[oplen
];
2163 /* Print only the right operand of the assignment-op. */
2164 todo
= SprintCString(&ss
->sprinter
, rval
);
2166 } else if (!inXML
) {
2167 rval
= POP_STR_PREC(cs
->prec
+ !!(cs
->format
& JOF_LEFTASSOC
));
2168 lval
= POP_STR_PREC(cs
->prec
+ !(cs
->format
& JOF_LEFTASSOC
));
2169 todo
= Sprint(&ss
->sprinter
, "%s %s %s",
2172 /* In XML, just concatenate the two operands. */
2173 LOCAL_ASSERT(op
== JSOP_ADD
);
2176 todo
= Sprint(&ss
->sprinter
, ss_format
, lval
, rval
);
2182 todo
= Sprint(&ss
->sprinter
, ss_format
, token
, rval
);
2186 todo
= SprintCString(&ss
->sprinter
, token
);
2197 * Check for a do-while loop, a for-loop with an empty
2198 * initializer part, a labeled statement, a function
2199 * definition, or try/finally.
2201 sn
= js_GetSrcNote(jp
->script
, pc
);
2203 switch (sn
? SN_TYPE(sn
) : SRC_NULL
) {
2206 tail
= js_GetSrcNoteOffset(sn
, 0) - 1;
2207 LOCAL_ASSERT(pc
[tail
] == JSOP_IFNE
||
2208 pc
[tail
] == JSOP_IFNEX
);
2209 js_printf(jp
, "\tdo {\n");
2211 DECOMPILE_CODE(pc
, tail
);
2213 js_printf(jp
, "\t} while (%s);\n", POP_COND_STR());
2215 len
= js_CodeSpec
[*pc
].length
;
2223 JS_ASSERT(SN_TYPE(sn
) == SRC_FOR
);
2225 /* Skip the JSOP_NOP or JSOP_POP bytecode. */
2226 pc
+= JSOP_NOP_LENGTH
;
2228 /* Get the cond, next, and loop-closing tail offsets. */
2229 cond
= js_GetSrcNoteOffset(sn
, 0);
2230 next
= js_GetSrcNoteOffset(sn
, 1);
2231 tail
= js_GetSrcNoteOffset(sn
, 2);
2234 * If this loop has a condition, then pc points at a goto
2235 * targeting the condition.
2239 LOCAL_ASSERT(*pc
== JSOP_GOTO
|| *pc
== JSOP_GOTOX
);
2240 pc2
+= (*pc
== JSOP_GOTO
) ? JSOP_GOTO_LENGTH
: JSOP_GOTOX_LENGTH
;
2242 LOCAL_ASSERT(tail
+ GetJumpOffset(pc
+tail
, pc
+tail
) == pc2
- pc
);
2244 /* Print the keyword and the possibly empty init-part. */
2245 js_printf(jp
, "\tfor (%s;", rval
);
2248 /* Decompile the loop condition. */
2249 DECOMPILE_CODE(pc
+ cond
, tail
- cond
);
2250 js_printf(jp
, " %s", POP_STR());
2253 /* Need a semicolon whether or not there was a cond. */
2258 * Decompile the loop updater. It may end in a JSOP_POP
2259 * that we skip; or in a JSOP_POPN that we do not skip,
2260 * followed by a JSOP_NOP (skipped as if it's a POP).
2261 * We cope with the difference between these two cases
2262 * by checking for stack imbalance and popping if there
2265 uintN saveTop
= ss
->top
;
2267 DECOMPILE_CODE(pc
+ next
, cond
- next
- JSOP_POP_LENGTH
);
2268 LOCAL_ASSERT(ss
->top
- saveTop
<= 1U);
2269 rval
= (ss
->top
== saveTop
)
2270 ? ss
->sprinter
.base
+ ss
->sprinter
.offset
2272 js_printf(jp
, " %s", rval
);
2275 /* Do the loop body. */
2276 js_printf(jp
, ") {\n");
2279 DECOMPILE_CODE(pc2
, next
);
2281 js_printf(jp
, "\t}\n");
2283 /* Set len so pc skips over the entire loop. */
2284 len
= tail
+ js_CodeSpec
[pc
[tail
]].length
;
2288 GET_SOURCE_NOTE_ATOM(sn
, atom
);
2290 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
2293 RETRACT(&ss
->sprinter
, rval
);
2294 js_printf(jp
, "\t%s:\n", rval
);
2298 case SRC_LABELBRACE
:
2299 GET_SOURCE_NOTE_ATOM(sn
, atom
);
2300 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
2303 RETRACT(&ss
->sprinter
, rval
);
2304 js_printf(jp
, "\t%s: {\n", rval
);
2310 js_printf(jp
, "\t}\n");
2314 fun
= jp
->script
->getFunction(js_GetSrcNoteOffset(sn
, 0));
2317 jp2
= js_NewPrinter(cx
, "nested_function", fun
,
2318 jp
->indent
, jp
->pretty
, jp
->grouped
,
2322 ok
= js_DecompileFunction(jp2
);
2323 if (ok
&& jp2
->sprinter
.base
)
2324 js_puts(jp
, jp2
->sprinter
.base
);
2325 js_DestroyPrinter(jp2
);
2328 js_puts(jp
, "\n\n");
2332 js_printf(jp
, "\t{\n");
2334 len
= js_GetSrcNoteOffset(sn
, 0);
2335 DECOMPILE_CODE(pc
+ oplen
, len
- oplen
);
2337 js_printf(jp
, "\t}\n");
2345 #if JS_HAS_DESTRUCTURING
2346 sn
= js_GetSrcNote(jp
->script
, pc
);
2347 if (sn
&& SN_TYPE(sn
) == SRC_GROUPASSIGN
) {
2348 pc
= DecompileGroupAssignment(ss
, pc
, endpc
, sn
, &todo
);
2351 LOCAL_ASSERT(*pc
== JSOP_POPN
);
2352 len
= oplen
= JSOP_POPN_LENGTH
;
2353 goto end_groupassignment
;
2359 case JSOP_BINDGNAME
:
2360 todo
= Sprint(&ss
->sprinter
, "");
2364 js_printf(jp
, "\ttry {\n");
2371 js_printf(jp
, "\t} finally {\n");
2375 * We push push the pair of exception/restsub cookies to
2376 * simulate the effects [gosub] or control transfer during
2377 * exception capturing on the stack.
2379 todo
= Sprint(&ss
->sprinter
, exception_cookie
);
2380 if (todo
< 0 || !PushOff(ss
, todo
, op
))
2382 todo
= Sprint(&ss
->sprinter
, retsub_pc_cookie
);
2387 LOCAL_ASSERT(strcmp(rval
, retsub_pc_cookie
) == 0);
2389 LOCAL_ASSERT(strcmp(lval
, exception_cookie
) == 0);
2396 * JSOP_GOSUB and GOSUBX have no effect on the decompiler's
2397 * string stack because the next op in bytecode order finds
2398 * the stack balanced by a JSOP_RETSUB executed elsewhere.
2405 uintN newtop
, oldtop
;
2408 * The compiler models operand stack depth and fixes the stack
2409 * pointer on entry to a catch clause based on its depth model.
2410 * The decompiler must match the code generator's model, which
2411 * is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops.
2414 newtop
= oldtop
- GET_UINT16(pc
);
2415 LOCAL_ASSERT(newtop
<= oldtop
);
2418 sn
= js_GetSrcNote(jp
->script
, pc
);
2419 if (sn
&& SN_TYPE(sn
) == SRC_HIDDEN
)
2421 #if JS_HAS_DESTRUCTURING
2422 if (sn
&& SN_TYPE(sn
) == SRC_GROUPASSIGN
) {
2423 todo
= Sprint(&ss
->sprinter
, "%s[] = [",
2427 for (uintN i
= newtop
; i
< oldtop
; i
++) {
2428 rval
= OFF2STR(&ss
->sprinter
, ss
->offsets
[i
]);
2429 if (Sprint(&ss
->sprinter
, ss_format
,
2430 (i
== newtop
) ? "" : ", ",
2431 (i
== oldtop
- 1 && *rval
== '\0')
2432 ? ", " : rval
) < 0) {
2436 if (SprintPut(&ss
->sprinter
, "]", 1) < 0)
2440 * If this is an empty group assignment, we have no stack
2441 * budget into which we can push our result string. Adjust
2442 * ss->sprinter.offset so that our consumer can find the
2443 * empty group assignment decompilation.
2445 if (newtop
== oldtop
) {
2446 ss
->sprinter
.offset
= todo
;
2449 * Kill newtop before the end_groupassignment: label by
2450 * retracting/popping early. Control will either jump
2451 * to do_forloop: or do_letheadbody: or else break from
2452 * our case JSOP_POPN: after the switch (*pc2) below.
2454 LOCAL_ASSERT(newtop
< oldtop
);
2455 ss
->sprinter
.offset
= GetOff(ss
, newtop
);
2459 end_groupassignment
:
2460 LOCAL_ASSERT(*pc
== JSOP_POPN
);
2463 * Thread directly to the next opcode if we can, to handle
2464 * the special cases of a group assignment in the first or
2465 * last part of a for(;;) loop head, or in a let block or
2468 * NB: todo at this point indexes space in ss->sprinter
2469 * that is liable to be overwritten. The code below knows
2470 * exactly how long rval lives, or else copies it down via
2473 rval
= OFF2STR(&ss
->sprinter
, todo
);
2476 if (*pc2
== JSOP_NOP
) {
2477 sn
= js_GetSrcNote(jp
->script
, pc2
);
2479 if (SN_TYPE(sn
) == SRC_FOR
) {
2485 if (SN_TYPE(sn
) == SRC_DECL
) {
2486 if (ss
->top
== StackDepth(jp
->script
)) {
2488 * This must be an empty destructuring
2489 * in the head of a let whose body block
2492 pc
= pc2
+ JSOP_NOP_LENGTH
;
2493 len
= js_GetSrcNoteOffset(sn
, 0);
2494 LOCAL_ASSERT(pc
[len
] == JSOP_LEAVEBLOCK
);
2495 js_printf(jp
, "\tlet (%s) {\n", rval
);
2496 js_printf(jp
, "\t}\n");
2499 todo
= SprintCString(&ss
->sprinter
, rval
);
2500 if (todo
< 0 || !PushOff(ss
, todo
, JSOP_NOP
))
2503 pc
= pc2
+ JSOP_NOP_LENGTH
;
2504 goto do_letheadbody
;
2508 * An unnannotated NOP following a POPN must be the
2509 * third part of for(;;) loop head. If the POPN's
2510 * immediate operand is 0, then we may have no slot
2511 * on the sprint-stack in which to push our result
2512 * string. In this case the result can be recovered
2513 * at ss->sprinter.base + ss->sprinter.offset.
2515 if (GET_UINT16(pc
) == 0)
2517 todo
= SprintCString(&ss
->sprinter
, rval
);
2523 * If control flow reaches this point with todo still -2,
2524 * just print rval as an expression statement.
2527 js_printf(jp
, "\t%s;\n", rval
);
2531 if (newtop
< oldtop
) {
2532 ss
->sprinter
.offset
= GetOff(ss
, newtop
);
2538 case JSOP_EXCEPTION
:
2539 /* The catch decompiler handles this op itself. */
2540 LOCAL_ASSERT(JS_FALSE
);
2545 * By default, do not automatically parenthesize when popping
2546 * a stacked expression decompilation. We auto-parenthesize
2547 * only when JSOP_POP is annotated with SRC_PCDELTA, meaning
2554 sn
= js_GetSrcNote(jp
->script
, pc
);
2555 switch (sn
? SN_TYPE(sn
) : SRC_NULL
) {
2557 /* Force parens around 'in' expression at 'for' front. */
2558 if (ss
->opcodes
[ss
->top
-1] == JSOP_IN
)
2565 /* Comma operator: use JSOP_POP for correct precedence. */
2568 /* Pop and save to avoid blowing stack depth budget. */
2569 lval
= JS_strdup(cx
, POP_STR());
2574 * The offset tells distance to the end of the right-hand
2575 * operand of the comma operator.
2578 pc
+= js_GetSrcNoteOffset(sn
, 0);
2581 if (!Decompile(ss
, done
, pc
- done
, JSOP_POP
)) {
2582 cx
->free((char *)lval
);
2586 /* Pop Decompile result and print comma expression. */
2588 todo
= Sprint(&ss
->sprinter
, "%s, %s", lval
, rval
);
2589 cx
->free((char *)lval
);
2593 /* Hide this pop, it's from a goto in a with or for/in. */
2598 /* This pop is at the end of the let block/expr head. */
2599 pc
+= JSOP_POP_LENGTH
;
2600 #if JS_HAS_DESTRUCTURING
2603 len
= js_GetSrcNoteOffset(sn
, 0);
2604 if (pc
[len
] == JSOP_LEAVEBLOCK
) {
2605 js_printf(jp
, "\tlet (%s) {\n", POP_STR());
2607 DECOMPILE_CODE(pc
, len
);
2609 js_printf(jp
, "\t}\n");
2612 LOCAL_ASSERT(pc
[len
] == JSOP_LEAVEBLOCKEXPR
);
2614 lval
= JS_strdup(cx
, PopStr(ss
, JSOP_NOP
));
2618 /* Set saveop to reflect what we will push. */
2619 saveop
= JSOP_LEAVEBLOCKEXPR
;
2620 if (!Decompile(ss
, pc
, len
, saveop
)) {
2621 cx
->free((char *)lval
);
2624 rval
= PopStr(ss
, JSOP_SETNAME
);
2625 todo
= Sprint(&ss
->sprinter
,
2630 cx
->free((char *)lval
);
2635 /* Turn off parens around a yield statement. */
2636 if (ss
->opcodes
[ss
->top
-1] == JSOP_YIELD
)
2642 * Don't emit decompiler-pushed strings that are not
2643 * handled by other opcodes. They are pushed onto the
2644 * stack to help model the interpreter stack and should
2645 * not appear in the decompiler's output.
2647 if (*rval
!= '\0' && (rval
[0] != '/' || rval
[1] != '*')) {
2650 (strncmp(rval
, js_function_str
, 8) == 0 &&
2656 LOCAL_ASSERT(*rval
== '\0' ||
2657 strcmp(rval
, exception_cookie
) == 0);
2665 case JSOP_ENTERWITH
:
2666 LOCAL_ASSERT(!js_GetSrcNote(jp
->script
, pc
));
2668 js_printf(jp
, "\twith (%s) {\n", rval
);
2670 todo
= Sprint(&ss
->sprinter
, with_cookie
);
2673 case JSOP_LEAVEWITH
:
2674 sn
= js_GetSrcNote(jp
->script
, pc
);
2676 if (sn
&& SN_TYPE(sn
) == SRC_HIDDEN
)
2679 LOCAL_ASSERT(strcmp(rval
, with_cookie
) == 0);
2681 js_printf(jp
, "\t}\n");
2684 case JSOP_ENTERBLOCK
:
2686 JSAtom
**atomv
, *smallv
[5];
2689 argc
= OBJ_BLOCK_COUNT(cx
, obj
);
2690 if ((size_t)argc
<= JS_ARRAY_LENGTH(smallv
)) {
2693 atomv
= (JSAtom
**) cx
->malloc(argc
* sizeof(JSAtom
*));
2698 MUST_FLOW_THROUGH("enterblock_out");
2699 #define LOCAL_ASSERT_OUT(expr) LOCAL_ASSERT_CUSTOM(expr, ok = JS_FALSE; \
2700 goto enterblock_out)
2701 for (Shape::Range r
= obj
->lastProperty()->all(); !r
.empty(); r
.popFront()) {
2702 const Shape
&shape
= r
.front();
2704 if (!shape
.hasShortID())
2706 LOCAL_ASSERT_OUT(shape
.shortid
< argc
);
2707 atomv
[shape
.shortid
] = JSID_TO_ATOM(shape
.id
);
2710 for (i
= 0; i
< argc
; i
++) {
2712 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
2714 !PushOff(ss
, STR2OFF(&ss
->sprinter
, rval
), op
)) {
2716 goto enterblock_out
;
2720 sn
= js_GetSrcNote(jp
->script
, pc
);
2721 switch (sn
? SN_TYPE(sn
) : SRC_NULL
) {
2722 #if JS_HAS_BLOCK_SCOPE
2724 js_printf(jp
, "\t{\n");
2726 len
= js_GetSrcNoteOffset(sn
, 0);
2727 ok
= Decompile(ss
, pc
+ oplen
, len
- oplen
, JSOP_NOP
)
2730 goto enterblock_out
;
2732 js_printf(jp
, "\t}\n");
2738 js_printf(jp
, "\t} catch (");
2742 LOCAL_ASSERT_OUT(*pc
== JSOP_EXCEPTION
);
2743 pc
+= JSOP_EXCEPTION_LENGTH
;
2744 todo
= Sprint(&ss
->sprinter
, exception_cookie
);
2745 if (todo
< 0 || !PushOff(ss
, todo
, JSOP_EXCEPTION
)) {
2747 goto enterblock_out
;
2750 if (*pc
== JSOP_DUP
) {
2751 sn2
= js_GetSrcNote(jp
->script
, pc
);
2752 if (!sn2
|| SN_TYPE(sn2
) != SRC_DESTRUCT
) {
2754 * This is a dup to save the exception for later.
2755 * It is emitted only when the catch head contains
2756 * an exception guard.
2758 LOCAL_ASSERT_OUT(js_GetSrcNoteOffset(sn
, 0) != 0);
2759 pc
+= JSOP_DUP_LENGTH
;
2760 todo
= Sprint(&ss
->sprinter
, exception_cookie
);
2762 !PushOff(ss
, todo
, JSOP_EXCEPTION
)) {
2764 goto enterblock_out
;
2769 #if JS_HAS_DESTRUCTURING
2770 if (*pc
== JSOP_DUP
) {
2771 pc
= DecompileDestructuring(ss
, pc
, endpc
);
2774 goto enterblock_out
;
2776 LOCAL_ASSERT_OUT(*pc
== JSOP_POP
);
2777 pc
+= JSOP_POP_LENGTH
;
2778 lval
= PopStr(ss
, JSOP_NOP
);
2782 LOCAL_ASSERT_OUT(*pc
== JSOP_SETLOCALPOP
);
2783 i
= GET_SLOTNO(pc
) - jp
->script
->nfixed
;
2784 pc
+= JSOP_SETLOCALPOP_LENGTH
;
2785 atom
= atomv
[i
- OBJ_BLOCK_DEPTH(cx
, obj
)];
2786 str
= ATOM_TO_STRING(atom
);
2787 if (!QuoteString(&jp
->sprinter
, str
, 0)) {
2789 goto enterblock_out
;
2791 #if JS_HAS_DESTRUCTURING
2796 * Pop the exception_cookie (or its dup in the case of a
2797 * guarded catch head) off the stack now.
2799 rval
= PopStr(ss
, JSOP_NOP
);
2800 LOCAL_ASSERT_OUT(strcmp(rval
, exception_cookie
) == 0);
2802 len
= js_GetSrcNoteOffset(sn
, 0);
2805 LOCAL_ASSERT_OUT(len
> 0);
2806 js_printf(jp
, " if ");
2807 ok
= Decompile(ss
, pc
, len
, JSOP_NOP
) != NULL
;
2809 goto enterblock_out
;
2810 js_printf(jp
, "%s", POP_STR());
2812 LOCAL_ASSERT_OUT(*pc
== JSOP_IFEQ
|| *pc
== JSOP_IFEQX
);
2813 pc
+= js_CodeSpec
[*pc
].length
;
2816 js_printf(jp
, ") {\n");
2826 #undef LOCAL_ASSERT_OUT
2828 if (atomv
!= smallv
)
2835 case JSOP_LEAVEBLOCK
:
2836 case JSOP_LEAVEBLOCKEXPR
:
2840 sn
= js_GetSrcNote(jp
->script
, pc
);
2842 if (op
== JSOP_LEAVEBLOCKEXPR
) {
2843 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_PCBASE
);
2846 LOCAL_ASSERT(op
== JSOP_LEAVEBLOCK
);
2847 if (SN_TYPE(sn
) == SRC_HIDDEN
)
2851 * This JSOP_LEAVEBLOCK must be for a catch block. If sn's
2852 * offset does not equal the model stack depth, there must
2853 * be a copy of the exception value on the stack due to a
2854 * catch guard (see above, the JSOP_ENTERBLOCK + SRC_CATCH
2857 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_CATCH
);
2858 if ((uintN
)js_GetSrcNoteOffset(sn
, 0) != ss
->top
) {
2859 LOCAL_ASSERT((uintN
)js_GetSrcNoteOffset(sn
, 0)
2862 LOCAL_ASSERT(strcmp(rval
, exception_cookie
) == 0);
2866 depth
= GET_UINT16(pc
);
2867 LOCAL_ASSERT(top
>= depth
);
2870 ss
->sprinter
.offset
= GetOff(ss
, top
);
2871 if (op
== JSOP_LEAVEBLOCKEXPR
)
2872 todo
= SprintCString(&ss
->sprinter
, rval
);
2876 case JSOP_GETUPVAR_DBG
:
2877 case JSOP_CALLUPVAR_DBG
:
2878 case JSOP_GETFCSLOT
:
2879 case JSOP_CALLFCSLOT
:
2882 JS_ASSERT(jp
->script
->savedCallerFun
);
2883 jp
->fun
= jp
->script
->getFunction(0);
2886 if (!jp
->localNames
) {
2887 JS_ASSERT(fun
== jp
->fun
);
2889 jp
->fun
->script()->bindings
.getLocalNameArray(cx
, &jp
->pool
);
2892 uintN index
= GET_UINT16(pc
);
2893 if (index
< jp
->fun
->script()->bindings
.countUpvars()) {
2894 index
+= jp
->fun
->script()->bindings
.countArgsAndVars();
2899 * We must be in an eval called from jp->fun, where
2900 * jp->script is the eval-compiled script.
2902 * However, it's possible that a js_Invoke already
2903 * pushed a frame trying to call Construct on an
2904 * object that's not a constructor, causing us to be
2905 * called with an intervening frame on the stack.
2907 JSStackFrame
*fp
= js_GetTopStackFrame(cx
);
2909 while (!fp
->isEvalFrame())
2911 JS_ASSERT(fp
->script() == jp
->script
);
2912 JS_ASSERT(fp
->prev()->fun() == jp
->fun
);
2913 JS_ASSERT(jp
->fun
->isInterpreted());
2914 JS_ASSERT(jp
->script
!= jp
->fun
->script());
2915 JS_ASSERT(JSScript::isValidOffset(jp
->script
->upvarsOffset
));
2918 uva
= jp
->script
->upvars();
2919 index
= uva
->vector
[index
].slot();
2921 atom
= GetArgOrVarAtom(jp
, index
);
2925 case JSOP_CALLLOCAL
:
2927 if (IsVarSlot(jp
, pc
, &i
)) {
2928 atom
= GetArgOrVarAtom(jp
, i
);
2932 LOCAL_ASSERT((uintN
)i
< ss
->top
);
2933 sn
= js_GetSrcNote(jp
->script
, pc
);
2935 #if JS_HAS_DESTRUCTURING
2936 if (sn
&& SN_TYPE(sn
) == SRC_GROUPASSIGN
) {
2938 * Distinguish a js_DecompileValueGenerator call that
2939 * targets op alone, from decompilation of a full group
2940 * assignment sequence, triggered by SRC_GROUPASSIGN
2941 * annotating the first JSOP_GETLOCAL in the sequence.
2943 if (endpc
- pc
> JSOP_GETLOCAL_LENGTH
|| pc
> startpc
) {
2944 pc
= DecompileGroupAssignment(ss
, pc
, endpc
, sn
, &todo
);
2947 LOCAL_ASSERT(*pc
== JSOP_POPN
);
2948 len
= oplen
= JSOP_POPN_LENGTH
;
2949 goto end_groupassignment
;
2952 /* Null sn to prevent bogus VarPrefix'ing below. */
2957 rval
= GetLocal(ss
, i
);
2958 todo
= Sprint(&ss
->sprinter
, ss_format
, VarPrefix(sn
), rval
);
2962 case JSOP_SETLOCALPOP
:
2963 if (IsVarSlot(jp
, pc
, &i
)) {
2964 atom
= GetArgOrVarAtom(jp
, i
);
2968 lval
= GetLocal(ss
, i
);
2974 if (IsVarSlot(jp
, pc
, &i
)) {
2975 atom
= GetArgOrVarAtom(jp
, i
);
2979 lval
= GetLocal(ss
, i
);
2984 if (IsVarSlot(jp
, pc
, &i
)) {
2985 atom
= GetArgOrVarAtom(jp
, i
);
2989 lval
= GetLocal(ss
, i
);
2997 LOCAL_ASSERT(jp
->fun
);
2999 if (fun
->flags
& JSFUN_EXPR_CLOSURE
) {
3000 /* Turn on parens around comma-expression here. */
3003 js_printf(jp
, (*rval
== '{') ? "(%s)%s" : ss_format
,
3005 ((fun
->flags
& JSFUN_LAMBDA
) || !fun
->atom
)
3016 js_printf(jp
, "\t%s %s;\n", js_return_str
, rval
);
3018 js_printf(jp
, "\t%s;\n", js_return_str
);
3022 #if JS_HAS_GENERATORS
3024 #if JS_HAS_GENERATOR_EXPRS
3025 if (!ss
->inGenExp
|| !(sn
= js_GetSrcNote(jp
->script
, pc
)))
3028 /* Turn off most parens. */
3031 todo
= (*rval
!= '\0')
3032 ? Sprint(&ss
->sprinter
,
3033 (strncmp(rval
, js_yield_str
, 5) == 0 &&
3034 (rval
[5] == ' ' || rval
[5] == '\0'))
3038 : SprintCString(&ss
->sprinter
, js_yield_str
);
3042 #if JS_HAS_GENERATOR_EXPRS
3043 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_HIDDEN
);
3047 case JSOP_ARRAYPUSH
:
3052 /* Turn off most parens. */
3055 /* Pop the expression being pushed or yielded. */
3059 * Skip the for loop head stacked by JSOP_FORLOCAL until we hit
3060 * a block local slot (note empty destructuring patterns result
3061 * in unit-count blocks).
3065 op
= (JSOp
) ss
->opcodes
[--pos
];
3066 if (op
== JSOP_ENTERBLOCK
)
3069 JS_ASSERT(op
== JSOP_ENTERBLOCK
);
3072 * Here, forpos must index the space before the left-most |for|
3073 * in the single string of accumulated |for| heads and optional
3074 * final |if (condition)|.
3077 LOCAL_ASSERT(forpos
< ss
->top
);
3080 * Now move pos downward over the block's local slots. Even an
3081 * empty destructuring pattern has one (dummy) local.
3083 while (ss
->opcodes
[pos
] == JSOP_ENTERBLOCK
) {
3089 #if JS_HAS_GENERATOR_EXPRS
3090 if (saveop
== JSOP_YIELD
) {
3092 * Generator expression: decompile just rval followed by
3093 * the string starting at forpos. Leave the result string
3094 * in ss->offsets[0] so it can be recovered by our caller
3095 * (the JSOP_ANONFUNOBJ with SRC_GENEXP case). Bump the
3096 * top of stack to balance yield, which is an expression
3097 * (so has neutral stack balance).
3099 LOCAL_ASSERT(pos
== 0);
3100 xval
= OFF2STR(&ss
->sprinter
, ss
->offsets
[forpos
]);
3101 ss
->sprinter
.offset
= PAREN_SLOP
;
3102 todo
= Sprint(&ss
->sprinter
, ss_format
, rval
, xval
);
3105 ss
->offsets
[0] = todo
;
3109 #endif /* JS_HAS_GENERATOR_EXPRS */
3112 * Array comprehension: retract the sprinter to the beginning
3113 * of the array initialiser and decompile "[<rval> for ...]".
3115 JS_ASSERT(jp
->script
->nfixed
+ pos
== GET_UINT16(pc
));
3116 LOCAL_ASSERT(ss
->opcodes
[pos
] == JSOP_NEWINIT
);
3118 start
= ss
->offsets
[pos
];
3119 LOCAL_ASSERT(ss
->sprinter
.base
[start
] == '[' ||
3120 ss
->sprinter
.base
[start
] == '#');
3121 LOCAL_ASSERT(forpos
< ss
->top
);
3122 xval
= OFF2STR(&ss
->sprinter
, ss
->offsets
[forpos
]);
3123 lval
= OFF2STR(&ss
->sprinter
, start
);
3124 RETRACT(&ss
->sprinter
, lval
);
3126 todo
= Sprint(&ss
->sprinter
, sss_format
, lval
, rval
, xval
);
3129 ss
->offsets
[pos
] = todo
;
3133 #endif /* JS_HAS_GENERATORS */
3140 sn
= js_GetSrcNote(jp
->script
, pc
);
3142 if (sn
&& SN_TYPE(sn
) == SRC_HIDDEN
)
3145 js_printf(jp
, "\t%s %s;\n", js_throw_str
, rval
);
3149 foreach
= (pc
[1] & (JSITER_FOREACH
| JSITER_KEYVALUE
)) ==
3155 JS_NOT_REACHED("JSOP_MOREITER");
3159 sn
= js_GetSrcNote(jp
->script
, pc
);
3161 if (sn
&& SN_TYPE(sn
) == SRC_HIDDEN
)
3163 (void) PopOff(ss
, op
);
3168 sn
= js_GetSrcNote(jp
->script
, pc
);
3169 switch (sn
? SN_TYPE(sn
) : SRC_NULL
) {
3172 * The loop back-edge carries +1 stack balance, for the
3173 * flag processed by JSOP_IFNE. We do not decompile the
3174 * JSOP_IFNE, and instead push the left-hand side of 'in'
3175 * after the loop edge in this stack slot (the JSOP_FOR*
3176 * opcodes' decompilers do this pushing).
3178 cond
= GetJumpOffset(pc
, pc
);
3179 next
= js_GetSrcNoteOffset(sn
, 0);
3180 tail
= js_GetSrcNoteOffset(sn
, 1);
3181 JS_ASSERT(pc
[cond
] == JSOP_MOREITER
);
3182 DECOMPILE_CODE(pc
+ oplen
, next
- oplen
);
3184 LOCAL_ASSERT(ss
->top
>= 1);
3186 if (ss
->inArrayInit
|| ss
->inGenExp
) {
3188 if (ss
->top
>= 1 && ss
->opcodes
[ss
->top
- 1] == JSOP_FORLOCAL
) {
3189 ss
->sprinter
.offset
= ss
->offsets
[ss
->top
] - PAREN_SLOP
;
3190 if (Sprint(&ss
->sprinter
, " %s (%s in %s)",
3191 foreach
? js_for_each_str
: js_for_str
,
3197 * Do not AddParentSlop here, as we will push the
3198 * top-most offset again, which will add paren slop
3199 * for us. We must push to balance the stack budget
3200 * when nesting for heads in a comprehension.
3202 todo
= ss
->offsets
[ss
->top
- 1];
3204 todo
= Sprint(&ss
->sprinter
, " %s (%s in %s)",
3205 foreach
? js_for_each_str
: js_for_str
,
3208 if (todo
< 0 || !PushOff(ss
, todo
, JSOP_FORLOCAL
))
3210 DECOMPILE_CODE(pc
+ next
, cond
- next
);
3213 * As above, rval or an extension of it must remain
3214 * stacked during loop body decompilation.
3216 rval
= GetStr(ss
, ss
->top
- 1);
3217 js_printf(jp
, "\t%s (%s in %s) {\n",
3218 foreach
? js_for_each_str
: js_for_str
,
3221 DECOMPILE_CODE(pc
+ next
, cond
- next
);
3223 js_printf(jp
, "\t}\n");
3227 LOCAL_ASSERT(*pc
== JSOP_IFNE
|| *pc
== JSOP_IFNEX
);
3228 len
= js_CodeSpec
[*pc
].length
;
3232 cond
= GetJumpOffset(pc
, pc
);
3233 tail
= js_GetSrcNoteOffset(sn
, 0);
3234 DECOMPILE_CODE(pc
+ cond
, tail
- cond
);
3235 js_printf(jp
, "\twhile (%s) {\n", POP_COND_STR());
3237 DECOMPILE_CODE(pc
+ oplen
, cond
- oplen
);
3239 js_printf(jp
, "\t}\n");
3241 LOCAL_ASSERT(*pc
== JSOP_IFNE
|| *pc
== JSOP_IFNEX
);
3242 len
= js_CodeSpec
[*pc
].length
;
3246 case SRC_CONT2LABEL
:
3247 GET_SOURCE_NOTE_ATOM(sn
, atom
);
3248 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3251 RETRACT(&ss
->sprinter
, rval
);
3252 js_printf(jp
, "\tcontinue %s;\n", rval
);
3256 js_printf(jp
, "\tcontinue;\n");
3259 case SRC_BREAK2LABEL
:
3260 GET_SOURCE_NOTE_ATOM(sn
, atom
);
3261 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3264 RETRACT(&ss
->sprinter
, rval
);
3265 js_printf(jp
, "\tbreak %s;\n", rval
);
3272 js_printf(jp
, "\tbreak;\n");
3281 JSBool elseif
= JS_FALSE
;
3284 len
= GetJumpOffset(pc
, pc
);
3285 sn
= js_GetSrcNote(jp
->script
, pc
);
3287 switch (sn
? SN_TYPE(sn
) : SRC_NULL
) {
3290 rval
= POP_COND_STR();
3291 if (ss
->inArrayInit
|| ss
->inGenExp
) {
3292 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_IF
);
3293 ss
->sprinter
.offset
-= PAREN_SLOP
;
3294 if (Sprint(&ss
->sprinter
, " if (%s)", rval
) < 0)
3299 elseif
? " if (%s) {\n" : "\tif (%s) {\n",
3304 if (SN_TYPE(sn
) == SRC_IF
) {
3305 DECOMPILE_CODE(pc
+ oplen
, len
- oplen
);
3307 LOCAL_ASSERT(!ss
->inArrayInit
&& !ss
->inGenExp
);
3308 tail
= js_GetSrcNoteOffset(sn
, 0);
3309 DECOMPILE_CODE(pc
+ oplen
, tail
- oplen
);
3312 LOCAL_ASSERT(*pc
== JSOP_GOTO
|| *pc
== JSOP_GOTOX
);
3313 oplen
= js_CodeSpec
[*pc
].length
;
3314 len
= GetJumpOffset(pc
, pc
);
3315 js_printf(jp
, "\t} else");
3318 * If the second offset for sn is non-zero, it tells
3319 * the distance from the goto around the else, to the
3320 * ifeq for the if inside the else that forms an "if
3321 * else if" chain. Thus cond spans the condition of
3322 * the second if, so we simply decompile it and start
3323 * over at label if_again.
3325 cond
= js_GetSrcNoteOffset(sn
, 1);
3328 DECOMPILE_CODE(pc
+ oplen
, cond
- oplen
);
3330 oplen
= js_CodeSpec
[*pc
].length
;
3335 js_printf(jp
, " {\n");
3337 DECOMPILE_CODE(pc
+ oplen
, len
- oplen
);
3340 if (!ss
->inArrayInit
&& !ss
->inGenExp
) {
3342 js_printf(jp
, "\t}\n");
3348 xval
= JS_strdup(cx
, POP_STR());
3351 len
= js_GetSrcNoteOffset(sn
, 0);
3352 DECOMPILE_CODE(pc
+ oplen
, len
- oplen
);
3353 lval
= JS_strdup(cx
, POP_STR());
3355 cx
->free((void *)xval
);
3359 LOCAL_ASSERT(*pc
== JSOP_GOTO
|| *pc
== JSOP_GOTOX
);
3360 oplen
= js_CodeSpec
[*pc
].length
;
3361 len
= GetJumpOffset(pc
, pc
);
3362 DECOMPILE_CODE(pc
+ oplen
, len
- oplen
);
3364 todo
= Sprint(&ss
->sprinter
, "%s ? %s : %s",
3366 cx
->free((void *)xval
);
3367 cx
->free((void *)lval
);
3385 do_logical_connective
:
3386 /* Top of stack is the first clause in a disjunction (||). */
3387 lval
= JS_strdup(cx
, POP_STR());
3390 done
= pc
+ GetJumpOffset(pc
, pc
);
3393 if (!Decompile(ss
, pc
, len
, op
)) {
3394 cx
->free((char *)lval
);
3399 jp
->indent
+ 4 + strlen(lval
) + 4 + strlen(rval
) > 75) {
3400 rval
= JS_strdup(cx
, rval
);
3404 todo
= Sprint(&ss
->sprinter
, "%s %s\n", lval
, xval
);
3405 tail
= Sprint(&ss
->sprinter
, "%*s%s",
3406 jp
->indent
+ 4, "", rval
);
3407 cx
->free((char *)rval
);
3412 todo
= Sprint(&ss
->sprinter
, "%s %s %s", lval
, xval
, rval
);
3414 cx
->free((char *)lval
);
3420 goto do_logical_connective
;
3428 sn
= js_GetSrcNote(jp
->script
, pc
);
3429 if (!IsVarSlot(jp
, pc
, &i
)) {
3430 JS_ASSERT(op
== JSOP_FORLOCAL
);
3431 todo
= Sprint(&ss
->sprinter
, ss_format
, VarPrefix(sn
), GetStr(ss
, i
));
3436 atom
= GetArgOrVarAtom(jp
, i
);
3438 todo
= SprintCString(&ss
->sprinter
, VarPrefix(sn
));
3439 if (todo
< 0 || !QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0))
3447 sn
= js_GetSrcNote(jp
->script
, pc
);
3448 todo
= SprintCString(&ss
->sprinter
, VarPrefix(sn
));
3449 if (todo
< 0 || !QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0))
3456 if (!ATOM_IS_IDENTIFIER(atom
)) {
3457 xval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
3465 todo
= Sprint(&ss
->sprinter
, index_format
, lval
, xval
);
3467 todo
= Sprint(&ss
->sprinter
, ss_format
, lval
, *lval
? "." : "");
3470 if (!QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0))
3476 todo
= SprintCString(&ss
->sprinter
, forelem_cookie
);
3480 case JSOP_ENUMCONSTELEM
:
3482 * The stack has the object under the (top) index expression.
3483 * The "rval" property id is underneath those two on the stack.
3484 * The for loop body net and gross lengths can now be adjusted
3485 * to account for the length of the indexing expression that
3486 * came after JSOP_FORELEM and before JSOP_ENUMELEM.
3489 op
= JSOP_NOP
; /* turn off parens around xval */
3491 op
= JSOP_GETELEM
; /* lval must have high precedence */
3495 LOCAL_ASSERT(strcmp(rval
, forelem_cookie
) == 0);
3496 if (*xval
== '\0') {
3497 todo
= SprintCString(&ss
->sprinter
, lval
);
3499 todo
= Sprint(&ss
->sprinter
,
3500 (JOF_OPMODE(lastop
) == JOF_XMLNAME
)
3513 rval
= GetStr(ss
, ss
->top
-2);
3514 todo
= SprintCString(&ss
->sprinter
, rval
);
3515 if (todo
< 0 || !PushOff(ss
, todo
,
3516 (JSOp
) ss
->opcodes
[ss
->top
-2])) {
3522 #if JS_HAS_DESTRUCTURING
3523 sn
= js_GetSrcNote(jp
->script
, pc
);
3525 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_DESTRUCT
);
3526 pc
= DecompileDestructuring(ss
, pc
, endpc
);
3531 op
= saveop
= JSOP_ENUMELEM
;
3534 if (strcmp(rval
, forelem_cookie
) == 0) {
3535 todo
= Sprint(&ss
->sprinter
, ss_format
,
3536 VarPrefix(sn
), lval
);
3538 // Skip POP so the SRC_FOR_IN code can pop for itself.
3539 if (*pc
== JSOP_POP
)
3540 len
= JSOP_POP_LENGTH
;
3542 todo
= Sprint(&ss
->sprinter
, "%s%s = %s",
3543 VarPrefix(sn
), lval
, rval
);
3549 rval
= GetStr(ss
, ss
->top
-1);
3550 saveop
= (JSOp
) ss
->opcodes
[ss
->top
-1];
3551 todo
= SprintCString(&ss
->sprinter
, rval
);
3555 atom
= GetArgOrVarAtom(jp
, GET_ARGNO(pc
));
3565 lval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3569 if (op
== JSOP_SETNAME
|| op
== JSOP_SETGNAME
)
3570 (void) PopOff(ss
, op
);
3573 sn
= js_GetSrcNote(jp
->script
, pc
- 1);
3574 if (sn
&& SN_TYPE(sn
) == SRC_ASSIGNOP
) {
3575 todo
= Sprint(&ss
->sprinter
, "%s %s= %s",
3577 (lastop
== JSOP_GETTER
)
3579 : (lastop
== JSOP_SETTER
)
3581 : CodeToken
[lastop
],
3584 sn
= js_GetSrcNote(jp
->script
, pc
);
3585 todo
= Sprint(&ss
->sprinter
, "%s%s = %s",
3586 VarPrefix(sn
), lval
, rval
);
3588 if (op
== JSOP_SETLOCALPOP
) {
3589 if (!PushOff(ss
, todo
, saveop
))
3592 LOCAL_ASSERT(*rval
!= '\0');
3593 js_printf(jp
, "\t%s;\n", rval
);
3603 argc
= GET_ARGC(pc
);
3605 cx
->malloc((size_t)(argc
+ 1) * sizeof *argv
);
3611 for (i
= argc
; i
> 0; i
--)
3612 argv
[i
] = JS_strdup(cx
, POP_STR());
3614 /* Skip the JSOP_PUSHOBJ-created empty string. */
3615 LOCAL_ASSERT(ss
->top
>= 2);
3616 (void) PopOff(ss
, op
);
3619 * Special case: new (x(y)(z)) must be parenthesized like so.
3620 * Same for new (x(y).z) -- contrast with new x(y).z.
3621 * See PROPAGATE_CALLNESS.
3623 op
= (JSOp
) ss
->opcodes
[ss
->top
- 1];
3625 (saveop
== JSOP_NEW
&&
3628 op
== JSOP_FUNCALL
||
3629 op
== JSOP_FUNAPPLY
||
3630 (js_CodeSpec
[op
].format
& JOF_CALLOP
)))
3635 argv
[0] = JS_strdup(cx
, lval
);
3639 lval
= "(", rval
= ")";
3640 if (op
== JSOP_NEW
) {
3643 todo
= Sprint(&ss
->sprinter
, "%s %s%s",
3644 js_new_str
, argv
[0], lval
);
3646 todo
= Sprint(&ss
->sprinter
, ss_format
,
3652 for (i
= 1; i
<= argc
; i
++) {
3654 Sprint(&ss
->sprinter
, ss_format
,
3655 argv
[i
], (i
< argc
) ? ", " : "") < 0) {
3660 if (Sprint(&ss
->sprinter
, rval
) < 0)
3663 for (i
= 0; i
<= argc
; i
++)
3671 todo
= Sprint(&ss
->sprinter
, "");
3676 lval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3679 RETRACT(&ss
->sprinter
, lval
);
3681 todo
= Sprint(&ss
->sprinter
, "%s %s", js_delete_str
, lval
);
3685 GET_ATOM_QUOTE_AND_FMT("%s %s[%s]", "%s %s.%s", rval
);
3688 todo
= Sprint(&ss
->sprinter
, fmt
, js_delete_str
, lval
, rval
);
3692 op
= JSOP_NOP
; /* turn off parens */
3697 goto do_delete_lval
;
3698 todo
= Sprint(&ss
->sprinter
,
3699 (JOF_OPMODE(lastop
) == JOF_XMLNAME
)
3702 js_delete_str
, lval
, xval
);
3705 #if JS_HAS_XML_SUPPORT
3710 todo
= Sprint(&ss
->sprinter
, "%s %s..%s",
3711 js_delete_str
, lval
, xval
);
3715 case JSOP_TYPEOFEXPR
:
3719 todo
= Sprint(&ss
->sprinter
, "%s %s",
3720 (op
== JSOP_VOID
) ? js_void_str
: js_typeof_str
,
3726 atom
= GetArgOrVarAtom(jp
, GET_ARGNO(pc
));
3736 lval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3739 RETRACT(&ss
->sprinter
, lval
);
3741 todo
= Sprint(&ss
->sprinter
, ss_format
,
3742 js_incop_strs
[!(cs
->format
& JOF_INC
)], lval
);
3747 GET_ATOM_QUOTE_AND_FMT(preindex_format
, predot_format
, rval
);
3750 * Force precedence below the numeric literal opcodes, so that
3751 * 42..foo or 10000..toString(16), e.g., decompile with parens
3752 * around the left-hand side of dot.
3756 todo
= Sprint(&ss
->sprinter
, fmt
,
3757 js_incop_strs
[!(cs
->format
& JOF_INC
)],
3763 op
= JSOP_NOP
; /* turn off parens */
3767 if (*xval
!= '\0') {
3768 todo
= Sprint(&ss
->sprinter
,
3769 (JOF_OPMODE(lastop
) == JOF_XMLNAME
)
3772 js_incop_strs
[!(cs
->format
& JOF_INC
)],
3775 todo
= Sprint(&ss
->sprinter
, ss_format
,
3776 js_incop_strs
[!(cs
->format
& JOF_INC
)], lval
);
3782 atom
= GetArgOrVarAtom(jp
, GET_ARGNO(pc
));
3792 lval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3795 RETRACT(&ss
->sprinter
, lval
);
3797 todo
= Sprint(&ss
->sprinter
, ss_format
,
3798 lval
, js_incop_strs
[!(cs
->format
& JOF_INC
)]);
3803 GET_ATOM_QUOTE_AND_FMT(postindex_format
, postdot_format
, rval
);
3806 * Force precedence below the numeric literal opcodes, so that
3807 * 42..foo or 10000..toString(16), e.g., decompile with parens
3808 * around the left-hand side of dot.
3812 todo
= Sprint(&ss
->sprinter
, fmt
, lval
, rval
,
3813 js_incop_strs
[!(cs
->format
& JOF_INC
)]);
3818 op
= JSOP_NOP
; /* turn off parens */
3822 if (*xval
!= '\0') {
3823 todo
= Sprint(&ss
->sprinter
,
3824 (JOF_OPMODE(lastop
) == JOF_XMLNAME
)
3828 js_incop_strs
[!(cs
->format
& JOF_INC
)]);
3830 todo
= Sprint(&ss
->sprinter
, ss_format
,
3831 lval
, js_incop_strs
[!(cs
->format
& JOF_INC
)]);
3837 rval
= js_length_str
;
3838 goto do_getprop_lval
;
3842 (void) PopOff(ss
, lastop
);
3851 GET_QUOTE_AND_FMT(index_format
, dot_format
, rval
);
3853 PROPAGATE_CALLNESS();
3855 todo
= Sprint(&ss
->sprinter
, fmt
, lval
, rval
);
3858 case JSOP_GETTHISPROP
:
3860 GET_QUOTE_AND_FMT(index_format
, dot_format
, rval
);
3861 todo
= Sprint(&ss
->sprinter
, fmt
, js_this_str
, rval
);
3864 case JSOP_GETARGPROP
:
3865 /* Get the name of the argument or variable. */
3869 atom
= GetArgOrVarAtom(ss
->printer
, i
);
3871 lval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
3872 if (!lval
|| !PushOff(ss
, STR2OFF(&ss
->sprinter
, lval
), op
))
3875 /* Get the name of the property. */
3876 LOAD_ATOM(ARGNO_LEN
);
3879 case JSOP_GETLOCALPROP
:
3880 if (IsVarSlot(jp
, pc
, &i
))
3881 goto do_getarg_prop
;
3882 LOCAL_ASSERT((uintN
)i
< ss
->top
);
3883 lval
= GetLocal(ss
, i
);
3886 todo
= SprintCString(&ss
->sprinter
, lval
);
3887 if (todo
< 0 || !PushOff(ss
, todo
, op
))
3893 case JSOP_SETMETHOD
:
3895 GET_QUOTE_AND_FMT("%s[%s] %s= %s", "%s.%s %s= %s", xval
);
3899 * Force precedence below the numeric literal opcodes, so that
3900 * 42..foo or 10000..toString(16), e.g., decompile with parens
3901 * around the left-hand side of dot.
3905 sn
= js_GetSrcNote(jp
->script
, pc
- 1);
3906 todo
= Sprint(&ss
->sprinter
, fmt
, lval
, xval
,
3907 (sn
&& SN_TYPE(sn
) == SRC_ASSIGNOP
)
3908 ? (lastop
== JSOP_GETTER
)
3910 : (lastop
== JSOP_SETTER
)
3919 (void) PopOff(ss
, lastop
);
3924 op
= JSOP_NOP
; /* turn off parens */
3927 PROPAGATE_CALLNESS();
3929 if (*xval
== '\0') {
3930 todo
= Sprint(&ss
->sprinter
, "%s", lval
);
3932 todo
= Sprint(&ss
->sprinter
,
3933 (JOF_OPMODE(lastop
) == JOF_XMLNAME
)
3942 op
= JSOP_NOP
; /* turn off parens */
3944 cs
= &js_CodeSpec
[ss
->opcodes
[ss
->top
]];
3945 op
= JSOP_GETELEM
; /* lval must have high precedence */
3950 sn
= js_GetSrcNote(jp
->script
, pc
- 1);
3951 todo
= Sprint(&ss
->sprinter
,
3952 (JOF_MODE(cs
->format
) == JOF_XMLNAME
)
3956 (sn
&& SN_TYPE(sn
) == SRC_ASSIGNOP
)
3957 ? (lastop
== JSOP_GETTER
)
3959 : (lastop
== JSOP_SETTER
)
3967 i
= (jsint
) GET_ARGNO(pc
);
3968 todo
= Sprint(&ss
->sprinter
, "%s[%d]",
3969 js_arguments_str
, (int) i
);
3973 todo
= Sprint(&ss
->sprinter
, dot_format
,
3974 js_arguments_str
, js_length_str
);
3980 atom
= GetArgOrVarAtom(jp
, i
);
3981 #if JS_HAS_DESTRUCTURING
3983 todo
= Sprint(&ss
->sprinter
, "%s[%d]", js_arguments_str
, i
);
3991 case JSOP_CALLGLOBAL
:
3992 case JSOP_GETGLOBAL
:
3993 atom
= jp
->script
->getGlobalAtom(GET_SLOTNO(pc
));
3999 case JSOP_CALLGNAME
:
4003 #if JS_HAS_XML_SUPPORT
4006 sn
= js_GetSrcNote(jp
->script
, pc
);
4007 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
4008 inXML
? DONT_ESCAPE
: 0);
4011 RETRACT(&ss
->sprinter
, rval
);
4012 todo
= Sprint(&ss
->sprinter
, sss_format
,
4013 VarPrefix(sn
), lval
, rval
);
4017 i
= (jsint
) GET_UINT16(pc
);
4021 i
= (jsint
) GET_UINT24(pc
);
4031 todo
= Sprint(&ss
->sprinter
, "%d", i
);
4037 GET_DOUBLE_FROM_BYTECODE(jp
->script
, pc
, 0, d
);
4038 val
= DOUBLE_TO_JSVAL(d
);
4039 todo
= SprintDoubleValue(&ss
->sprinter
, val
, &saveop
);
4045 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
4046 inXML
? DONT_ESCAPE
: '"');
4049 todo
= STR2OFF(&ss
->sprinter
, rval
);
4053 case JSOP_LAMBDA_FC
:
4054 case JSOP_LAMBDA_DBGFC
:
4055 #if JS_HAS_GENERATOR_EXPRS
4056 sn
= js_GetSrcNote(jp
->script
, pc
);
4057 if (sn
&& SN_TYPE(sn
) == SRC_GENEXP
) {
4059 jsuword
*innerLocalNames
, *outerLocalNames
;
4060 JSScript
*inner
, *outer
;
4062 JSFunction
*outerfun
;
4067 * All allocation when decompiling is LIFO, using malloc
4068 * or, more commonly, arena-allocating from cx->tempPool.
4069 * Therefore after InitSprintStack succeeds, we must
4070 * release to mark before returning.
4072 mark
= JS_ARENA_MARK(&cx
->tempPool
);
4073 if (fun
->script()->bindings
.hasLocalNames()) {
4075 fun
->script()->bindings
.getLocalNameArray(cx
, &cx
->tempPool
);
4076 if (!innerLocalNames
)
4079 innerLocalNames
= NULL
;
4081 inner
= fun
->script();
4082 if (!InitSprintStack(cx
, &ss2
, jp
, StackDepth(inner
))) {
4083 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
4086 ss2
.inGenExp
= JS_TRUE
;
4089 * Recursively decompile this generator function as an
4090 * un-parenthesized generator expression. The ss->inGenExp
4091 * special case of JSOP_YIELD shares array comprehension
4092 * decompilation code that leaves the result as the single
4093 * string pushed on ss2.
4097 outerLocalNames
= jp
->localNames
;
4098 LOCAL_ASSERT(JS_UPTRDIFF(pc
, outer
->code
) <= outer
->length
);
4101 jp
->localNames
= innerLocalNames
;
4104 * Decompile only the main bytecode, to avoid tripping over
4105 * new prolog ops that have stack effects.
4107 ok
= Decompile(&ss2
, inner
->main
,
4108 inner
->length
- (inner
->main
- inner
->code
),
4113 jp
->localNames
= outerLocalNames
;
4115 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
4120 * Advance over this op and its global |this| push, and
4121 * arrange to advance over the call to this lambda.
4124 if (*pc
== JSOP_BLOCKCHAIN
) {
4125 pc
+= JSOP_BLOCKCHAIN_LENGTH
;
4127 LOCAL_ASSERT(*pc
== JSOP_NULLBLOCKCHAIN
);
4128 pc
+= JSOP_NULLBLOCKCHAIN_LENGTH
;
4130 LOCAL_ASSERT(*pc
== JSOP_PUSH
);
4131 pc
+= JSOP_PUSH_LENGTH
;
4132 LOCAL_ASSERT(*pc
== JSOP_CALL
);
4133 LOCAL_ASSERT(GET_ARGC(pc
) == 0);
4134 len
= JSOP_CALL_LENGTH
;
4137 * Arrange to parenthesize this genexp unless:
4139 * 1. It is the complete expression consumed by a control
4140 * flow bytecode such as JSOP_TABLESWITCH whose syntax
4141 * always parenthesizes the controlling expression.
4142 * 2. It is the sole argument to a function call.
4144 * But if this genexp runs up against endpc, parenthesize
4145 * regardless. (This can happen if we are called from
4146 * DecompileExpression or recursively from case
4147 * JSOP_{NOP,AND,OR}.)
4149 * There's no special case for |if (genexp)| because the
4150 * compiler optimizes that to |if (true)|.
4154 if (op
== JSOP_TRACE
|| op
== JSOP_NOP
)
4155 pc2
+= JSOP_NOP_LENGTH
;
4156 LOCAL_ASSERT(pc2
< endpc
||
4157 endpc
< outer
->code
+ outer
->length
);
4158 LOCAL_ASSERT(ss2
.top
== 1);
4159 ss2
.opcodes
[0] = JSOP_POP
;
4164 op
= ((js_CodeSpec
[op
].format
& JOF_PARENHEAD
) ||
4165 ((js_CodeSpec
[op
].format
& JOF_INVOKE
) && GET_ARGC(pc2
) == 1))
4170 * Stack this result as if it's a name and not an
4171 * anonymous function, so it doesn't get decompiled as
4172 * a generator function in a getter or setter context.
4173 * The precedence level is the same for JSOP_NAME and
4176 LOCAL_ASSERT(js_CodeSpec
[JSOP_NAME
].prec
==
4177 js_CodeSpec
[saveop
].prec
);
4182 * Alas, we have to malloc a copy of the result left on
4183 * the top of ss2 because both ss and ss2 arena-allocate
4184 * from cx's tempPool.
4186 rval
= JS_strdup(cx
, PopStr(&ss2
, op
));
4187 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
4190 todo
= SprintCString(&ss
->sprinter
, rval
);
4191 cx
->free((void *)rval
);
4194 #endif /* JS_HAS_GENERATOR_EXPRS */
4200 * Always parenthesize expression closures. We can't force
4201 * saveop to a low-precedence op to arrange for auto-magic
4202 * parenthesization without confusing getter/setter code
4203 * that checks for JSOP_LAMBDA.
4205 bool grouped
= !(fun
->flags
& JSFUN_EXPR_CLOSURE
);
4206 bool strict
= jp
->script
->strictModeCode
;
4207 str
= js_DecompileToString(cx
, "lambda", fun
, 0,
4208 false, grouped
, strict
,
4209 js_DecompileFunction
);
4214 todo
= SprintString(&ss
->sprinter
, str
);
4218 JS_ASSERT(jp
->fun
&& jp
->fun
->atom
);
4219 todo
= SprintString(&ss
->sprinter
, ATOM_TO_STRING(jp
->fun
->atom
));
4224 str
= js_ValueToSource(cx
, ObjectValue(*obj
));
4230 GET_REGEXP_FROM_BYTECODE(jp
->script
, pc
, 0, obj
);
4231 if (!js_regexp_toString(cx
, obj
, Valueify(&val
)))
4233 str
= JSVAL_TO_STRING(val
);
4236 case JSOP_TABLESWITCH
:
4237 case JSOP_TABLESWITCHX
:
4239 ptrdiff_t jmplen
, off
, off2
;
4240 jsint j
, n
, low
, high
;
4241 TableEntry
*table
, *tmp
;
4243 sn
= js_GetSrcNote(jp
->script
, pc
);
4244 LOCAL_ASSERT(sn
&& SN_TYPE(sn
) == SRC_SWITCH
);
4245 len
= js_GetSrcNoteOffset(sn
, 0);
4246 jmplen
= (op
== JSOP_TABLESWITCH
) ? JUMP_OFFSET_LEN
4249 off
= GetJumpOffset(pc
, pc2
);
4251 low
= GET_JUMP_OFFSET(pc2
);
4252 pc2
+= JUMP_OFFSET_LEN
;
4253 high
= GET_JUMP_OFFSET(pc2
);
4254 pc2
+= JUMP_OFFSET_LEN
;
4262 table
= (TableEntry
*)
4263 cx
->malloc((size_t)n
* sizeof *table
);
4266 for (i
= j
= 0; i
< n
; i
++) {
4267 table
[j
].label
= NULL
;
4268 off2
= GetJumpOffset(pc
, pc2
);
4270 sn
= js_GetSrcNote(jp
->script
, pc2
);
4272 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_LABEL
);
4273 GET_SOURCE_NOTE_ATOM(sn
, table
[j
].label
);
4275 table
[j
].key
= INT_TO_JSVAL(low
+ i
);
4276 table
[j
].offset
= off2
;
4282 tmp
= (TableEntry
*)
4283 cx
->malloc((size_t)j
* sizeof *table
);
4285 VOUCH_DOES_NOT_REQUIRE_STACK();
4286 ok
= js_MergeSort(table
, (size_t)j
, sizeof(TableEntry
),
4287 CompareOffsets
, NULL
, tmp
,
4288 JS_SORTING_GENERIC
);
4296 ok
= DecompileSwitch(ss
, table
, (uintN
)j
, pc
, len
, off
,
4306 case JSOP_LOOKUPSWITCH
:
4307 case JSOP_LOOKUPSWITCHX
:
4309 ptrdiff_t jmplen
, off
, off2
;
4313 sn
= js_GetSrcNote(jp
->script
, pc
);
4314 LOCAL_ASSERT(sn
&& SN_TYPE(sn
) == SRC_SWITCH
);
4315 len
= js_GetSrcNoteOffset(sn
, 0);
4316 jmplen
= (op
== JSOP_LOOKUPSWITCH
) ? JUMP_OFFSET_LEN
4319 off
= GetJumpOffset(pc
, pc2
);
4321 npairs
= GET_UINT16(pc2
);
4324 table
= (TableEntry
*)
4325 cx
->malloc((size_t)npairs
* sizeof *table
);
4328 for (k
= 0; k
< npairs
; k
++) {
4329 sn
= js_GetSrcNote(jp
->script
, pc2
);
4331 LOCAL_ASSERT(SN_TYPE(sn
) == SRC_LABEL
);
4332 GET_SOURCE_NOTE_ATOM(sn
, table
[k
].label
);
4334 table
[k
].label
= NULL
;
4336 uint16 constIndex
= GET_INDEX(pc2
);
4338 off2
= GetJumpOffset(pc
, pc2
);
4340 table
[k
].key
= Jsvalify(jp
->script
->getConst(constIndex
));
4341 table
[k
].offset
= off2
;
4344 ok
= DecompileSwitch(ss
, table
, (uintN
)npairs
, pc
, len
, off
,
4353 case JSOP_CONDSWITCH
:
4355 ptrdiff_t off
, off2
, caseOff
;
4359 sn
= js_GetSrcNote(jp
->script
, pc
);
4360 LOCAL_ASSERT(sn
&& SN_TYPE(sn
) == SRC_SWITCH
);
4361 len
= js_GetSrcNoteOffset(sn
, 0);
4362 off
= js_GetSrcNoteOffset(sn
, 1);
4365 * Count the cases using offsets from switch to first case,
4366 * and case to case, stored in srcnote immediates.
4370 for (ncases
= 0; off2
!= 0; ncases
++) {
4372 LOCAL_ASSERT(*pc2
== JSOP_CASE
|| *pc2
== JSOP_DEFAULT
||
4373 *pc2
== JSOP_CASEX
|| *pc2
== JSOP_DEFAULTX
);
4374 if (*pc2
== JSOP_DEFAULT
|| *pc2
== JSOP_DEFAULTX
) {
4375 /* End of cases, but count default as a case. */
4378 sn
= js_GetSrcNote(jp
->script
, pc2
);
4379 LOCAL_ASSERT(sn
&& SN_TYPE(sn
) == SRC_PCDELTA
);
4380 off2
= js_GetSrcNoteOffset(sn
, 0);
4385 * Allocate table and rescan the cases using their srcnotes,
4386 * stashing each case's delta from switch top in table[i].key,
4387 * and the distance to its statements in table[i].offset.
4389 table
= (TableEntry
*)
4390 cx
->malloc((size_t)ncases
* sizeof *table
);
4395 for (i
= 0; i
< ncases
; i
++) {
4397 LOCAL_ASSERT(*pc2
== JSOP_CASE
|| *pc2
== JSOP_DEFAULT
||
4398 *pc2
== JSOP_CASEX
|| *pc2
== JSOP_DEFAULTX
);
4400 table
[i
].key
= INT_TO_JSVAL((jsint
) caseOff
);
4401 table
[i
].offset
= caseOff
+ GetJumpOffset(pc2
, pc2
);
4402 if (*pc2
== JSOP_CASE
|| *pc2
== JSOP_CASEX
) {
4403 sn
= js_GetSrcNote(jp
->script
, pc2
);
4404 LOCAL_ASSERT(sn
&& SN_TYPE(sn
) == SRC_PCDELTA
);
4405 off2
= js_GetSrcNoteOffset(sn
, 0);
4410 * Find offset of default code by fetching the default offset
4411 * from the end of table. JSOP_CONDSWITCH always has a default
4414 off
= JSVAL_TO_INT(table
[ncases
-1].key
);
4416 off
+= GetJumpOffset(pc2
, pc2
);
4418 ok
= DecompileSwitch(ss
, table
, (uintN
)ncases
, pc
, len
, off
,
4433 js_printf(jp
, "\tcase %s:\n", lval
);
4439 case JSOP_DEFFUN_FC
:
4440 case JSOP_DEFFUN_DBGFC
:
4447 saveop
= op
= JS_GetTrapOpcode(cx
, jp
->script
, pc
);
4449 cs
= &js_CodeSpec
[op
];
4451 DECOMPILE_CODE(pc
, len
);
4457 todo
= SprintPut(&ss
->sprinter
, "", 0);
4463 LOCAL_ASSERT(i
== JSProto_Array
|| i
== JSProto_Object
);
4465 todo
= ss
->sprinter
.offset
;
4466 #if JS_HAS_SHARP_VARS
4468 if (op
== JSOP_SHARPINIT
)
4469 op
= (JSOp
)pc
[len
+= JSOP_SHARPINIT_LENGTH
];
4470 if (op
== JSOP_DEFSHARP
) {
4472 cs
= &js_CodeSpec
[op
];
4474 if (Sprint(&ss
->sprinter
, "#%u=",
4475 (unsigned) (jsint
) GET_UINT16(pc
+ UINT16_LEN
))
4480 #endif /* JS_HAS_SHARP_VARS */
4481 if (i
== JSProto_Array
) {
4483 if (SprintCString(&ss
->sprinter
, "[") < 0)
4486 if (SprintCString(&ss
->sprinter
, "{") < 0)
4494 todo
= ss
->sprinter
.offset
;
4496 if (SprintCString(&ss
->sprinter
, "[") < 0)
4501 case JSOP_NEWOBJECT
:
4503 todo
= ss
->sprinter
.offset
;
4504 if (SprintCString(&ss
->sprinter
, "{") < 0)
4513 op
= JSOP_NOP
; /* turn off parens */
4515 sn
= js_GetSrcNote(jp
->script
, pc
);
4517 /* Skip any #n= prefix to find the opening bracket. */
4518 for (xval
= rval
; *xval
!= '[' && *xval
!= '{'; xval
++)
4520 inArray
= (*xval
== '[');
4523 todo
= Sprint(&ss
->sprinter
, "%s%s%c",
4525 (sn
&& SN_TYPE(sn
) == SRC_CONTINUE
) ? ", " : "",
4526 inArray
? ']' : '}');
4532 const char *maybeComma
;
4535 isFirst
= IsInitializerOp(ss
->opcodes
[ss
->top
- 3]);
4537 /* Turn off most parens. */
4541 /* Turn off all parens for xval and lval, which we control. */
4545 sn
= js_GetSrcNote(jp
->script
, pc
);
4547 if (sn
&& SN_TYPE(sn
) == SRC_INITPROP
) {
4551 maybeComma
= isFirst
? "" : ", ";
4552 todo
= Sprint(&ss
->sprinter
, sss_format
,
4559 case JSOP_INITMETHOD
:
4561 xval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
4562 jschar(ATOM_IS_IDENTIFIER(atom
) ? 0 : '\''));
4565 isFirst
= IsInitializerOp(ss
->opcodes
[ss
->top
- 2]);
4571 maybeComma
= isFirst
? "" : ", ";
4572 if (lastop
== JSOP_GETTER
|| lastop
== JSOP_SETTER
) {
4573 const char *end
= rval
+ strlen(rval
);
4577 LOCAL_ASSERT(strncmp(rval
, js_function_str
, 8) == 0);
4578 LOCAL_ASSERT(rval
[8] == ' ');
4580 LOCAL_ASSERT(*end
? *end
== ')' : end
[-1] == '}');
4581 todo
= Sprint(&ss
->sprinter
, "%s%s%s %s%s%.*s",
4584 (lastop
== JSOP_GETTER
)
4585 ? js_get_str
: js_set_str
,
4587 (rval
[0] != '(') ? " " : "",
4590 todo
= Sprint(&ss
->sprinter
, "%s%s%s: %s",
4591 lval
, maybeComma
, xval
, rval
);
4596 #if JS_HAS_SHARP_VARS
4598 i
= (jsint
) GET_UINT16(pc
+ UINT16_LEN
);
4600 todo
= Sprint(&ss
->sprinter
, "#%u=%s", (unsigned) i
, rval
);
4604 i
= (jsint
) GET_UINT16(pc
+ UINT16_LEN
);
4605 todo
= Sprint(&ss
->sprinter
, "#%u#", (unsigned) i
);
4607 #endif /* JS_HAS_SHARP_VARS */
4610 js_printf(jp
, "\tdebugger;\n");
4614 #if JS_HAS_XML_SUPPORT
4616 case JSOP_STARTXMLEXPR
:
4617 inXML
= op
== JSOP_STARTXML
;
4623 js_printf(jp
, "\t%s %s %s = %s;\n",
4624 js_default_str
, js_xml_str
, js_namespace_str
, rval
);
4629 if (pc
[JSOP_ANYNAME_LENGTH
] == JSOP_TOATTRNAME
) {
4630 len
+= JSOP_TOATTRNAME_LENGTH
;
4631 todo
= SprintPut(&ss
->sprinter
, "@*", 2);
4633 todo
= SprintPut(&ss
->sprinter
, "*", 1);
4637 case JSOP_QNAMEPART
:
4639 if (pc
[JSOP_QNAMEPART_LENGTH
] == JSOP_TOATTRNAME
) {
4640 saveop
= JSOP_TOATTRNAME
;
4641 len
+= JSOP_TOATTRNAME_LENGTH
;
4647 case JSOP_QNAMECONST
:
4649 rval
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0);
4652 RETRACT(&ss
->sprinter
, rval
);
4654 todo
= Sprint(&ss
->sprinter
, "%s::%s", lval
, rval
);
4660 todo
= Sprint(&ss
->sprinter
, "%s::[%s]", lval
, rval
);
4663 case JSOP_TOATTRNAME
:
4664 op
= JSOP_NOP
; /* turn off parens */
4666 todo
= Sprint(&ss
->sprinter
, "@[%s]", rval
);
4669 case JSOP_TOATTRVAL
:
4673 case JSOP_ADDATTRNAME
:
4676 todo
= Sprint(&ss
->sprinter
, "%s %s", lval
, rval
);
4677 /* This gets reset by all XML tag expressions. */
4678 quoteAttr
= JS_TRUE
;
4681 case JSOP_ADDATTRVAL
:
4685 todo
= Sprint(&ss
->sprinter
, "%s=\"%s\"", lval
, rval
);
4687 todo
= Sprint(&ss
->sprinter
, "%s=%s", lval
, rval
);
4690 case JSOP_BINDXMLNAME
:
4691 /* Leave the name stacked and push a dummy string. */
4692 todo
= Sprint(&ss
->sprinter
, "");
4695 case JSOP_SETXMLNAME
:
4696 /* Pop the r.h.s., the dummy string, and the name. */
4698 (void) PopOff(ss
, op
);
4702 case JSOP_XMLELTEXPR
:
4703 case JSOP_XMLTAGEXPR
:
4704 todo
= Sprint(&ss
->sprinter
, "{%s}", POP_STR());
4706 /* If we're an attribute value, we shouldn't quote this. */
4707 quoteAttr
= JS_FALSE
;
4710 case JSOP_TOXMLLIST
:
4711 op
= JSOP_NOP
; /* turn off parens */
4712 todo
= Sprint(&ss
->sprinter
, "<>%s</>", POP_STR());
4717 case JSOP_CALLXMLNAME
:
4720 /* These ops indicate the end of XML expressions. */
4725 case JSOP_ENDFILTER
:
4727 PROPAGATE_CALLNESS();
4729 todo
= Sprint(&ss
->sprinter
, "%s.(%s)", lval
, rval
);
4732 case JSOP_DESCENDANTS
:
4734 PROPAGATE_CALLNESS();
4736 todo
= Sprint(&ss
->sprinter
, "%s..%s", lval
, rval
);
4741 todo
= SprintPut(&ss
->sprinter
, "<![CDATA[", 9);
4742 if (!QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
4745 SprintPut(&ss
->sprinter
, "]]>", 3);
4748 case JSOP_XMLCOMMENT
:
4750 todo
= SprintPut(&ss
->sprinter
, "<!--", 4);
4751 if (!QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
),
4754 SprintPut(&ss
->sprinter
, "-->", 3);
4759 rval
= JS_strdup(cx
, POP_STR());
4762 todo
= SprintPut(&ss
->sprinter
, "<?", 2);
4763 ok
= QuoteString(&ss
->sprinter
, ATOM_TO_STRING(atom
), 0) &&
4765 (SprintPut(&ss
->sprinter
, " ", 1) >= 0 &&
4766 SprintCString(&ss
->sprinter
, rval
)));
4767 cx
->free((char *)rval
);
4770 SprintPut(&ss
->sprinter
, "?>", 2);
4774 todo
= SprintPut(&ss
->sprinter
, js_function_str
, 8);
4776 #endif /* JS_HAS_XML_SUPPORT */
4785 /* -2 means "don't push", -1 means reported error. */
4789 if (!PushOff(ss
, todo
, saveop
))
4793 if (cs
->format
& JOF_CALLOP
) {
4794 todo
= Sprint(&ss
->sprinter
, "");
4795 if (todo
< 0 || !PushOff(ss
, todo
, saveop
))
4803 * Undefine local macros.
4806 #undef DECOMPILE_CODE
4812 #undef ATOM_IS_IDENTIFIER
4813 #undef GET_QUOTE_AND_FMT
4814 #undef GET_ATOM_QUOTE_AND_FMT
4820 DecompileCode(JSPrinter
*jp
, JSScript
*script
, jsbytecode
*pc
, uintN len
,
4828 JSScript
*oldscript
;
4831 depth
= StackDepth(script
);
4832 JS_ASSERT(pcdepth
<= depth
);
4833 cx
= jp
->sprinter
.context
;
4835 AutoScriptUntrapper
untrapper(cx
, script
, &pc
);
4837 /* Initialize a sprinter for use with the offset stack. */
4838 mark
= JS_ARENA_MARK(&cx
->tempPool
);
4839 ok
= InitSprintStack(cx
, &ss
, jp
, depth
);
4844 * If we are called from js_DecompileValueGenerator with a portion of
4845 * script's bytecode that starts with a non-zero model stack depth given
4846 * by pcdepth, attempt to initialize the missing string offsets in ss to
4847 * |spindex| negative indexes from fp->sp for the activation fp in which
4850 * See js_DecompileValueGenerator for how its |spindex| parameter is used,
4851 * and see also GetOff, which makes use of the ss.offsets[i] < -1 that are
4852 * potentially stored below.
4856 for (i
= 0; i
< pcdepth
; i
++) {
4857 ss
.offsets
[i
] = -2 - (ptrdiff_t)i
;
4858 ss
.opcodes
[i
] = *jp
->pcstack
[i
];
4862 /* Call recursive subroutine to do the hard work. */
4863 oldscript
= jp
->script
;
4864 jp
->script
= script
;
4865 ok
= Decompile(&ss
, pc
, len
, JSOP_NOP
) != NULL
;
4866 jp
->script
= oldscript
;
4868 /* If the given code didn't empty the stack, do it now. */
4871 last
= OFF2STR(&ss
.sprinter
, PopOff(&ss
, JSOP_POP
));
4872 } while (ss
.top
> pcdepth
);
4873 js_printf(jp
, "%s", last
);
4877 /* Free all temporary stuff allocated under this call. */
4878 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
4883 * Decompile a function body, expression closure expression, or complete
4884 * script. Start at |pc|; go to the end of |script|. Include a directive
4885 * prologue, if appropriate.
4888 DecompileBody(JSPrinter
*jp
, JSScript
*script
, jsbytecode
*pc
)
4890 /* Print a strict mode code directive, if needed. */
4891 if (script
->strictModeCode
&& !jp
->strict
) {
4892 if (jp
->fun
&& (jp
->fun
->flags
& JSFUN_EXPR_CLOSURE
)) {
4894 * We have no syntax for strict function expressions;
4895 * at least give a hint.
4897 js_printf(jp
, "\t/* use strict */ \n");
4899 js_printf(jp
, "\t\"use strict\";\n");
4904 jsbytecode
*end
= script
->code
+ script
->length
;
4905 return DecompileCode(jp
, script
, pc
, end
- pc
, 0);
4909 js_DecompileScript(JSPrinter
*jp
, JSScript
*script
)
4911 return DecompileBody(jp
, script
, script
->code
);
4915 js_DecompileToString(JSContext
*cx
, const char *name
, JSFunction
*fun
,
4916 uintN indent
, JSBool pretty
, JSBool grouped
, JSBool strict
,
4917 JSDecompilerPtr decompiler
)
4922 jp
= js_NewPrinter(cx
, name
, fun
, indent
, pretty
, grouped
, strict
);
4926 str
= js_GetPrinterOutput(jp
);
4929 js_DestroyPrinter(jp
);
4933 static const char native_code_str
[] = "\t[native code]\n";
4936 js_DecompileFunctionBody(JSPrinter
*jp
)
4941 JS_ASSERT(!jp
->script
);
4942 if (!FUN_INTERPRETED(jp
->fun
)) {
4943 js_printf(jp
, native_code_str
);
4947 script
= jp
->fun
->u
.i
.script
;
4948 return DecompileBody(jp
, script
, script
->code
);
4952 js_DecompileFunction(JSPrinter
*jp
)
4957 jsbytecode
*pc
, *endpc
;
4962 JS_ASSERT(!jp
->script
);
4965 * If pretty, conform to ECMA-262 Edition 3, 15.3.4.2, by decompiling a
4966 * FunctionDeclaration. Otherwise, check the JSFUN_LAMBDA flag and force
4967 * an expression by parenthesizing.
4970 js_printf(jp
, "\t");
4972 if (!jp
->grouped
&& (fun
->flags
& JSFUN_LAMBDA
))
4976 js_printf(jp
, "%s ", js_function_str
);
4977 if (fun
->atom
&& !QuoteString(&jp
->sprinter
, ATOM_TO_STRING(fun
->atom
), 0))
4981 if (!FUN_INTERPRETED(fun
)) {
4982 js_printf(jp
, ") {\n");
4984 js_printf(jp
, native_code_str
);
4986 js_printf(jp
, "\t}");
4988 JSScript
*script
= fun
->u
.i
.script
;
4989 #if JS_HAS_DESTRUCTURING
4994 /* Print the parameters. */
4996 AutoScriptUntrapper
untrapper(jp
->sprinter
.context
, script
, &pc
);
4997 endpc
= pc
+ script
->length
;
5000 #if JS_HAS_DESTRUCTURING
5002 jp
->script
= script
;
5003 mark
= JS_ARENA_MARK(&jp
->sprinter
.context
->tempPool
);
5006 for (i
= 0; i
< fun
->nargs
; i
++) {
5010 param
= GetArgOrVarAtom(jp
, i
);
5012 #if JS_HAS_DESTRUCTURING
5013 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, JS_FALSE)
5019 LOCAL_ASSERT(*pc
== JSOP_GETARG
);
5020 pc
+= JSOP_GETARG_LENGTH
;
5021 LOCAL_ASSERT(*pc
== JSOP_DUP
);
5023 ok
= InitSprintStack(jp
->sprinter
.context
, &ss
, jp
, StackDepth(script
));
5027 pc
= DecompileDestructuring(&ss
, pc
, endpc
);
5032 LOCAL_ASSERT(*pc
== JSOP_POP
);
5033 pc
+= JSOP_POP_LENGTH
;
5034 lval
= PopStr(&ss
, JSOP_NOP
);
5035 todo
= SprintCString(&jp
->sprinter
, lval
);
5046 if (!QuoteString(&jp
->sprinter
, ATOM_TO_STRING(param
), 0)) {
5052 #if JS_HAS_DESTRUCTURING
5054 JS_ARENA_RELEASE(&jp
->sprinter
.context
->tempPool
, mark
);
5058 js_printf(jp
, ") ");
5059 if (!(fun
->flags
& JSFUN_EXPR_CLOSURE
)) {
5060 js_printf(jp
, "{\n");
5064 ok
= DecompileBody(jp
, script
, pc
);
5068 if (!(fun
->flags
& JSFUN_EXPR_CLOSURE
)) {
5070 js_printf(jp
, "\t}");
5074 if (!jp
->pretty
&& !jp
->grouped
&& (fun
->flags
& JSFUN_LAMBDA
))
5081 js_DecompileValueGenerator(JSContext
*cx
, intN spindex
, jsval v_in
,
5088 Value v
= Valueify(v_in
);
5090 JS_ASSERT(spindex
< 0 ||
5091 spindex
== JSDVG_IGNORE_STACK
||
5092 spindex
== JSDVG_SEARCH_STACK
);
5096 if (!cx
->regs
|| !cx
->regs
->fp
|| !cx
->regs
->fp
->isScriptFrame())
5100 script
= fp
->script();
5101 pc
= fp
->hasImacropc() ? fp
->imacropc() : cx
->regs
->pc
;
5102 JS_ASSERT(script
->code
<= pc
&& pc
< script
->code
+ script
->length
);
5104 if (pc
< script
->main
)
5107 if (spindex
!= JSDVG_IGNORE_STACK
) {
5108 jsbytecode
**pcstack
;
5111 * Prepare computing pcstack containing pointers to opcodes that
5112 * populated interpreter's stack with its current content.
5114 pcstack
= (jsbytecode
**)
5115 cx
->malloc(StackDepth(script
) * sizeof *pcstack
);
5118 intN pcdepth
= ReconstructPCStack(cx
, script
, pc
, pcstack
);
5120 goto release_pcstack
;
5122 if (spindex
!= JSDVG_SEARCH_STACK
) {
5123 JS_ASSERT(spindex
< 0);
5126 goto release_pcstack
;
5127 pc
= pcstack
[pcdepth
];
5130 * We search from fp->sp to base to find the most recently
5131 * calculated value matching v under assumption that it is
5132 * it that caused exception, see bug 328664.
5134 Value
*stackBase
= fp
->base();
5135 Value
*sp
= cx
->regs
->sp
;
5137 if (sp
== stackBase
) {
5139 goto release_pcstack
;
5141 } while (*--sp
!= v
);
5144 * The value may have come from beyond stackBase + pcdepth, meaning
5145 * that it came from a temporary slot pushed by the interpreter or
5146 * arguments pushed for an Invoke call. Only update pc if beneath
5147 * stackBase + pcdepth. If above, the value may or may not be
5148 * produced by the current pc. Since it takes a fairly contrived
5149 * combination of calls to produce a situation where this is not
5150 * what we want, we just use the current pc.
5152 if (sp
< stackBase
+ pcdepth
)
5153 pc
= pcstack
[sp
- stackBase
];
5163 jsbytecode
* basepc
= cx
->regs
->pc
;
5164 jsbytecode
* savedImacropc
= fp
->maybeImacropc();
5165 if (savedImacropc
) {
5166 JS_ASSERT(cx
->hasfp());
5167 cx
->regs
->pc
= savedImacropc
;
5168 fp
->clearImacropc();
5172 * FIXME: bug 489843. Stack reconstruction may have returned a pc
5173 * value *inside* an imacro; this would confuse the decompiler.
5176 if (savedImacropc
&& size_t(pc
- script
->code
) >= script
->length
)
5177 name
= FAILED_EXPRESSION_DECOMPILER
;
5179 name
= DecompileExpression(cx
, script
, fp
->maybeFun(), pc
);
5181 if (savedImacropc
) {
5182 JS_ASSERT(cx
->hasfp());
5183 cx
->regs
->pc
= basepc
;
5184 fp
->setImacropc(savedImacropc
);
5187 if (name
!= FAILED_EXPRESSION_DECOMPILER
)
5193 fallback
= js_ValueToSource(cx
, v
);
5197 size_t length
= fallback
->length();
5198 const jschar
*chars
= fallback
->getChars(cx
);
5201 return js_DeflateString(cx
, chars
, length
);
5205 DecompileExpression(JSContext
*cx
, JSScript
*script
, JSFunction
*fun
,
5209 const JSCodeSpec
*cs
;
5210 jsbytecode
*begin
, *end
;
5213 jsbytecode
**pcstack
;
5218 JS_ASSERT(script
->code
<= pc
&& pc
< script
->code
+ script
->length
);
5221 AutoScriptUntrapper
untrapper(cx
, script
, &pc
);
5224 /* None of these stack-writing ops generates novel values. */
5225 JS_ASSERT(op
!= JSOP_CASE
&& op
!= JSOP_CASEX
&&
5226 op
!= JSOP_DUP
&& op
!= JSOP_DUP2
);
5228 /* JSOP_PUSH is used to generate undefined for group assignment holes. */
5229 if (op
== JSOP_PUSH
) {
5230 name
= JS_strdup(cx
, js_undefined_str
);
5235 * |this| could convert to a very long object initialiser, so cite it by
5236 * its keyword name instead.
5238 if (op
== JSOP_THIS
) {
5239 name
= JS_strdup(cx
, js_this_str
);
5244 * JSOP_BINDNAME is special: it generates a value, the base object of a
5245 * reference. But if it is the generating op for a diagnostic produced by
5246 * js_DecompileValueGenerator, the name being bound is irrelevant. Just
5247 * fall back to the base object.
5249 if (op
== JSOP_BINDNAME
) {
5250 name
= FAILED_EXPRESSION_DECOMPILER
;
5254 /* NAME ops are self-contained, others require left or right context. */
5255 cs
= &js_CodeSpec
[op
];
5257 end
= pc
+ cs
->length
;
5258 switch (JOF_MODE(cs
->format
)) {
5263 sn
= js_GetSrcNote(script
, pc
);
5265 name
= FAILED_EXPRESSION_DECOMPILER
;
5268 switch (SN_TYPE(sn
)) {
5270 begin
-= js_GetSrcNoteOffset(sn
, 0);
5273 end
= begin
+ js_GetSrcNoteOffset(sn
, 0);
5274 begin
+= cs
->length
;
5277 name
= FAILED_EXPRESSION_DECOMPILER
;
5285 name
= FAILED_EXPRESSION_DECOMPILER
;
5289 pcstack
= (jsbytecode
**)
5290 cx
->malloc(StackDepth(script
) * sizeof *pcstack
);
5296 MUST_FLOW_THROUGH("out");
5297 pcdepth
= ReconstructPCStack(cx
, script
, begin
, pcstack
);
5299 name
= FAILED_EXPRESSION_DECOMPILER
;
5304 jp
= js_NewPrinter(cx
, "js_DecompileValueGenerator", fun
, 0,
5305 false, false, false);
5308 jp
->pcstack
= pcstack
;
5309 if (DecompileCode(jp
, script
, begin
, (uintN
) len
, (uintN
) pcdepth
)) {
5310 name
= (jp
->sprinter
.base
) ? jp
->sprinter
.base
: (char *) "";
5311 name
= JS_strdup(cx
, name
);
5313 js_DestroyPrinter(jp
);
5322 js_ReconstructStackDepth(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
)
5324 return ReconstructPCStack(cx
, script
, pc
, NULL
);
5327 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1);
5330 SimulateOp(JSContext
*cx
, JSScript
*script
, JSOp op
, const JSCodeSpec
*cs
,
5331 jsbytecode
*pc
, jsbytecode
**pcstack
, uintN
&pcdepth
)
5333 uintN nuses
= js_GetStackUses(cs
, op
, pc
);
5334 uintN ndefs
= js_GetStackDefs(cx
, cs
, op
, script
, pc
);
5335 LOCAL_ASSERT(pcdepth
>= nuses
);
5337 LOCAL_ASSERT(pcdepth
+ ndefs
<= StackDepth(script
));
5340 * Fill the slots that the opcode defines withs its pc unless it just
5341 * reshuffles the stack. In the latter case we want to preserve the
5342 * opcode that generated the original value.
5347 for (uintN i
= 0; i
!= ndefs
; ++i
)
5348 pcstack
[pcdepth
+ i
] = pc
;
5354 /* Keep the switch value. */
5355 JS_ASSERT(ndefs
== 1);
5359 JS_ASSERT(ndefs
== 2);
5361 pcstack
[pcdepth
+ 1] = pcstack
[pcdepth
];
5365 JS_ASSERT(ndefs
== 4);
5367 pcstack
[pcdepth
+ 2] = pcstack
[pcdepth
];
5368 pcstack
[pcdepth
+ 3] = pcstack
[pcdepth
+ 1];
5373 JS_ASSERT(ndefs
== 2);
5375 jsbytecode
*tmp
= pcstack
[pcdepth
+ 1];
5376 pcstack
[pcdepth
+ 1] = pcstack
[pcdepth
];
5377 pcstack
[pcdepth
] = tmp
;
5388 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_CUSTOM(expr, goto failure);
5391 SimulateImacroCFG(JSContext
*cx
, JSScript
*script
,
5392 uintN pcdepth
, jsbytecode
*pc
, jsbytecode
*target
,
5393 jsbytecode
**pcstack
)
5396 jsbytecode
** tmp_pcstack
= NULL
;
5398 nbytes
= StackDepth(script
) * sizeof *pcstack
;
5399 tmp_pcstack
= (jsbytecode
**) cx
->malloc(nbytes
);
5402 memcpy(tmp_pcstack
, pcstack
, nbytes
);
5406 for (; pc
< target
; pc
+= oplen
) {
5407 JSOp op
= js_GetOpcode(cx
, script
, pc
);
5408 const JSCodeSpec
*cs
= &js_CodeSpec
[op
];
5411 oplen
= js_GetVariableBytecodeLength(pc
);
5413 if (SimulateOp(cx
, script
, op
, cs
, pc
, tmp_pcstack
, pcdepth
) < 0)
5416 uint32 type
= cs
->format
& JOF_TYPEMASK
;
5417 if (type
== JOF_JUMP
|| type
== JOF_JUMPX
) {
5418 ptrdiff_t jmpoff
= (type
== JOF_JUMP
) ? GET_JUMP_OFFSET(pc
)
5419 : GET_JUMPX_OFFSET(pc
);
5420 LOCAL_ASSERT(jmpoff
>= 0);
5421 intN tmp_pcdepth
= SimulateImacroCFG(cx
, script
, pcdepth
, pc
+ jmpoff
,
5422 target
, tmp_pcstack
);
5423 if (tmp_pcdepth
>= 0) {
5424 pcdepth
= uintN(tmp_pcdepth
);
5428 if (op
== JSOP_GOTO
|| op
== JSOP_GOTOX
)
5436 LOCAL_ASSERT(pc
== target
);
5440 memcpy(pcstack
, tmp_pcstack
, nbytes
);
5441 cx
->free(tmp_pcstack
);
5447 cx
->free(tmp_pcstack
);
5452 #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, -1);
5455 ReconstructImacroPCStack(JSContext
*cx
, JSScript
*script
,
5456 jsbytecode
*imacstart
, jsbytecode
*target
,
5457 jsbytecode
**pcstack
)
5460 * Begin with a recursive call back to ReconstructPCStack to pick up
5461 * the state-of-the-world at the *start* of the imacro.
5463 JSStackFrame
*fp
= js_GetScriptedCaller(cx
, NULL
);
5464 JS_ASSERT(fp
->hasImacropc());
5465 intN pcdepth
= ReconstructPCStack(cx
, script
, fp
->imacropc(), pcstack
);
5468 return SimulateImacroCFG(cx
, script
, pcdepth
, imacstart
, target
, pcstack
);
5471 extern jsbytecode
* js_GetImacroStart(jsbytecode
* pc
);
5475 ReconstructPCStack(JSContext
*cx
, JSScript
*script
, jsbytecode
*target
,
5476 jsbytecode
**pcstack
)
5479 * Walk forward from script->main and compute the stack depth and stack of
5480 * operand-generating opcode PCs in pcstack.
5482 * FIXME: Code to compute oplen copied from js_Disassemble1 and reduced.
5483 * FIXME: Optimize to use last empty-stack sequence point.
5486 jsbytecode
*imacstart
= js_GetImacroStart(target
);
5489 return ReconstructImacroPCStack(cx
, script
, imacstart
, target
, pcstack
);
5492 LOCAL_ASSERT(script
->main
<= target
&& target
< script
->code
+ script
->length
);
5493 jsbytecode
*pc
= script
->main
;
5496 for (; pc
< target
; pc
+= oplen
) {
5497 JSOp op
= js_GetOpcode(cx
, script
, pc
);
5498 const JSCodeSpec
*cs
= &js_CodeSpec
[op
];
5501 oplen
= js_GetVariableBytecodeLength(pc
);
5504 * A (C ? T : E) expression requires skipping either T (if target is in
5505 * E) or both T and E (if target is after the whole expression) before
5506 * adjusting pcdepth based on the JSOP_IFEQ or JSOP_IFEQX at pc that
5507 * tests condition C. We know that the stack depth can't change from
5508 * what it was with C on top of stack.
5510 jssrcnote
*sn
= js_GetSrcNote(script
, pc
);
5511 if (sn
&& SN_TYPE(sn
) == SRC_COND
) {
5512 ptrdiff_t jmpoff
= js_GetSrcNoteOffset(sn
, 0);
5513 if (pc
+ jmpoff
< target
) {
5515 op
= js_GetOpcode(cx
, script
, pc
);
5516 JS_ASSERT(op
== JSOP_GOTO
|| op
== JSOP_GOTOX
);
5517 cs
= &js_CodeSpec
[op
];
5519 JS_ASSERT(oplen
> 0);
5520 ptrdiff_t jmplen
= GetJumpOffset(pc
, pc
);
5521 if (pc
+ jmplen
< target
) {
5522 oplen
= (uintN
) jmplen
;
5527 * Ok, target lies in E. Manually pop C off the model stack,
5528 * since we have moved beyond the IFEQ now.
5530 LOCAL_ASSERT(pcdepth
!= 0);
5535 /* Ignore early-exit code, which is annotated SRC_HIDDEN. */
5536 if (sn
&& SN_TYPE(sn
) == SRC_HIDDEN
)
5539 if (SimulateOp(cx
, script
, op
, cs
, pc
, pcstack
, pcdepth
) < 0)
5543 LOCAL_ASSERT(pc
== target
);
5549 #undef LOCAL_ASSERT_RV