Fix try/finally and try/guarded-catches/finally hard cases (104077, r=jband, sr=shaver).

This commit is contained in:
brendan%mozilla.org 2001-12-12 05:00:29 +00:00
Родитель 45defae20a
Коммит 30d6bf4ad3
6 изменённых файлов: 244 добавлений и 100 удалений

Просмотреть файл

@ -997,7 +997,7 @@ Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
JSFunction *fun = JS_ValueToFunction(cx, argv[i]);
if (fun && (fun->flags & JSFUN_FLAGS_MASK)) {
uint8 flags = fun->flags;
fputs("flags:", stderr);
fputs("flags:", stdout);
#define SHOW_FLAG(flag) if (flags & JSFUN_##flag) fputs(" " #flag, stdout);
@ -1126,7 +1126,7 @@ Tracing(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
JS_GetStringBytes(str));
return JS_TRUE;
}
cx->tracefp = bval ? stdout : NULL;
cx->tracefp = bval ? stderr : NULL;
return JS_TRUE;
}

Просмотреть файл

@ -1189,15 +1189,55 @@ js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type,
/* Emit additional bytecode(s) for non-local jumps. */
static JSBool
EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
JSBool preserveTop)
JSOp *returnop)
{
intN depth;
JSStmtInfo *stmt;
ptrdiff_t jmp;
/*
* If we're here as part of processing a return, emit JSOP_SWAP to preserve
* the top element.
* Return from within a try block that has a finally clause must be split
* into two ops: JSOP_SETRVAL, to pop the r.v. and store it in fp->rval;
* and JSOP_RETRVAL, which makes control flow go back to the caller, who
* picks up fp->rval as usual. Otherwise, the stack will be unbalanced
* when executing the finally clause.
*
* We mutate *returnop once only if we find an enclosing try-block (viz,
* STMT_FINALLY) to ensure that we emit just one JSOP_SETRVAL before one
* or more JSOP_GOSUBs and other fixup opcodes emitted by this function.
* Our caller (the TOK_RETURN case of js_EmitTree) then emits *returnop.
* The fixup opcodes and gosubs must interleave in the proper order, from
* inner statement to outer, so that finally clauses run at the correct
* stack depth.
*/
if (returnop) {
JS_ASSERT(*returnop == JSOP_RETURN);
for (stmt = cg->treeContext.topStmt; stmt != toStmt;
stmt = stmt->down) {
if (stmt->type == STMT_FINALLY) {
if (js_Emit1(cx, cg, JSOP_SETRVAL) < 0)
return JS_FALSE;
*returnop = JSOP_RETRVAL;
break;
}
}
/*
* If there are no try-with-finally blocks open around this return
* statement, we can generate a return forthwith and skip generating
* any fixup code.
*/
if (*returnop == JSOP_RETURN)
return JS_TRUE;
}
/*
* The non-local jump fixup we emit will unbalance cg->stackDepth, because
* the fixup replicates balanced code such as JSOP_LEAVEWITH emitted at the
* end of a with statement, so we save cg->stackDepth here and restore it
* just before a successful return.
*/
depth = cg->stackDepth;
for (stmt = cg->treeContext.topStmt; stmt != toStmt; stmt = stmt->down) {
switch (stmt->type) {
case STMT_FINALLY:
@ -1207,39 +1247,38 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
if (jmp < 0)
return JS_FALSE;
break;
case STMT_WITH:
case STMT_CATCH:
if (preserveTop) {
if (js_Emit1(cx, cg, JSOP_SWAP) < 0)
return JS_FALSE;
}
/* There's a With object on the stack that we need to pop. */
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
return JS_FALSE;
cg->stackDepth++;
if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0)
return JS_FALSE;
break;
case STMT_FOR_IN_LOOP:
cg->stackDepth += 2;
if (preserveTop) {
if (js_Emit1(cx, cg, JSOP_SWAP) < 0 ||
js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
js_Emit1(cx, cg, JSOP_POP) < 0 ||
js_Emit1(cx, cg, JSOP_SWAP) < 0 ||
js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
js_Emit1(cx, cg, JSOP_POP) < 0) {
return JS_FALSE;
}
} else {
/* JSOP_POP2 isn't decompiled, and doesn't need a src note. */
if (js_Emit1(cx, cg, JSOP_POP2) < 0)
return JS_FALSE;
}
/*
* The iterator and the object being iterated need to be popped.
* JSOP_POP2 isn't decompiled, so it doesn't need to be HIDDEN.
*/
if (js_Emit1(cx, cg, JSOP_POP2) < 0)
return JS_FALSE;
break;
case STMT_SUBROUTINE:
/* There's a retsub pc-offset on the stack that we need to pop. */
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
return JS_FALSE;
if (js_Emit1(cx, cg, JSOP_POP) < 0)
return JS_FALSE;
break;
default:;
}
}
cg->stackDepth = depth;
return JS_TRUE;
}
@ -1250,7 +1289,7 @@ EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
intN index;
ptrdiff_t jmp;
if (!EmitNonLocalJumpFixup(cx, cg, toStmt, JS_FALSE))
if (!EmitNonLocalJumpFixup(cx, cg, toStmt, NULL))
return -1;
if (label) {
@ -1268,8 +1307,8 @@ EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
}
static JSBool
BackPatch(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *stmt,
ptrdiff_t last, jsbytecode *target, jsbytecode op)
BackPatch(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t last,
jsbytecode *target, jsbytecode op)
{
jsbytecode *pc;
ptrdiff_t delta, span;
@ -1317,8 +1356,8 @@ js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg)
JSStmtInfo *stmt;
stmt = cg->treeContext.topStmt;
if (!BackPatch(cx, cg, stmt, stmt->breaks, CG_NEXT(cg), JSOP_GOTO) ||
!BackPatch(cx, cg, stmt, stmt->continues, CG_CODE(cg, stmt->update),
if (!BackPatch(cx, cg, stmt->breaks, CG_NEXT(cg), JSOP_GOTO) ||
!BackPatch(cx, cg, stmt->continues, CG_CODE(cg, stmt->update),
JSOP_GOTO)) {
return JS_FALSE;
}
@ -2693,10 +2732,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
#if JS_HAS_EXCEPTIONS
case TOK_TRY: {
ptrdiff_t start, end;
ptrdiff_t catchStart = -1, finallyCatch = -1, catchjmp = -1;
ptrdiff_t start, end, catchStart, finallyCatch, catchJump;
JSParseNode *iter;
uint16 depth;
intN depth;
/* Quell GCC overwarnings. */
end = catchStart = finallyCatch = catchJump = -1;
/* Emit JSOP_GOTO that points to the first op after the catch/finally blocks */
#define EMIT_CATCH_GOTO(cx, cg, jmp) \
@ -2720,10 +2761,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
CG_OFFSET(cg));
/*
* About JSOP_SETSP:
* An exception can be thrown while the stack is in an unbalanced
* state, and this causes problems with things like function invocation
* later on.
* About JSOP_SETSP: an exception can be thrown while the stack is in
* an unbalanced state, and this imbalance causes problems with things
* like function invocation later on.
*
* To fix this, we compute the `balanced' stack depth upon try entry,
* and then restore the stack to this depth when we hit the first catch
@ -2740,7 +2780,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (!js_EmitTree(cx, cg, pn->pn_kid1))
return JS_FALSE;
/* Emit (hidden) jump over catch and/or finally. */
/* GOSUB to finally, if present. */
if (pn->pn_kid3) {
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
return JS_FALSE;
@ -2749,6 +2789,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
return JS_FALSE;
}
/* Emit (hidden) jump over catch and/or finally. */
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
return JS_FALSE;
EMIT_CATCH_GOTO(cx, cg, jmp);
@ -2792,11 +2833,14 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (!UpdateLinenoNotes(cx, cg, iter))
return JS_FALSE;
if (catchjmp != -1) {
if (catchJump != -1) {
/* Fix up and clean up previous catch block. */
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchjmp);
if ((uintN)++cg->stackDepth > cg->maxStackDepth)
cg->maxStackDepth = cg->stackDepth;
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchJump);
/* Compensate for the [leavewith]. */
cg->stackDepth++;
JS_ASSERT(cg->stackDepth <= cg->maxStackDepth);
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) {
return JS_FALSE;
@ -2804,6 +2848,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
} else {
/* Set stack to original depth (see SETSP comment above). */
EMIT_ATOM_INDEX_OP(JSOP_SETSP, (jsatomid)depth);
cg->stackDepth = depth;
}
/* Non-negative guardnote offset is length of catchguard. */
@ -2848,8 +2893,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
return JS_FALSE;
}
/* ifeq <next block> */
catchjmp = EmitJump(cx, cg, JSOP_IFEQ, 0);
if (catchjmp < 0)
catchJump = EmitJump(cx, cg, JSOP_IFEQ, 0);
if (catchJump < 0)
return JS_FALSE;
}
@ -2897,38 +2942,46 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
* code while letting an uncaught exception pass through.
*/
if (pn->pn_kid3 ||
(catchjmp != -1 && iter->pn_kid1->pn_expr)) {
(catchJump != -1 && iter->pn_kid1->pn_expr)) {
/*
* Emit another stack fix, because the catch could itself
* Emit another stack fixup, because the catch could itself
* throw an exception in an unbalanced state, and the finally
* may need to call functions etc.
* may need to call functions. If there is no finally, only
* guarded catches, the rethrow code below nevertheless needs
* stack fixup.
*/
finallyCatch = CG_OFFSET(cg);
EMIT_ATOM_INDEX_OP(JSOP_SETSP, (jsatomid)depth);
if (catchjmp != -1 && iter->pn_kid1->pn_expr)
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchjmp);
cg->stackDepth = depth;
/* Last discriminant jumps to rethrow if none match. */
if ((uintN)++cg->stackDepth > cg->maxStackDepth)
cg->maxStackDepth = cg->stackDepth;
if (pn->pn_kid2 &&
(js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0)) {
return JS_FALSE;
if (catchJump != -1 && iter->pn_kid1->pn_expr) {
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, catchJump);
/* Compensate for the [leavewith]. */
cg->stackDepth++;
JS_ASSERT(cg->stackDepth <= cg->maxStackDepth);
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0) {
return JS_FALSE;
}
}
if (pn->pn_kid3) {
finallyCatch = CG_OFFSET(cg);
EMIT_FINALLY_GOSUB(cx, cg, jmp);
if (jmp < 0)
return JS_FALSE;
cg->stackDepth = depth;
}
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
js_Emit1(cx, cg, JSOP_EXCEPTION) < 0 ||
js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
js_Emit1(cx, cg, JSOP_THROW) < 0) {
return JS_FALSE;
}
JS_ASSERT(cg->stackDepth == depth);
}
/*
@ -2936,11 +2989,20 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
* gosubs that might have been emitted before non-local jumps.
*/
if (pn->pn_kid3) {
if (!BackPatch(cx, cg, &stmtInfo, stmtInfo.gosub, CG_NEXT(cg),
JSOP_GOSUB)) {
if (!BackPatch(cx, cg, stmtInfo.gosub, CG_NEXT(cg), JSOP_GOSUB))
return JS_FALSE;
}
js_PopStatementCG(cx, cg);
/*
* The stack budget must be balanced at this point, and we need
* one more slot for the JSOP_RETSUB return address pushed by a
* JSOP_GOSUB opcode that calls this finally clause.
*/
JS_ASSERT(cg->stackDepth == depth);
if ((uintN)++cg->stackDepth > cg->maxStackDepth)
cg->maxStackDepth = cg->stackDepth;
/* Now indicate that we're emitting a subroutine body. */
stmtInfo.type = STMT_SUBROUTINE;
if (!UpdateLinenoNotes(cx, cg, pn->pn_kid3))
return JS_FALSE;
if (js_Emit1(cx, cg, JSOP_FINALLY) < 0 ||
@ -2948,9 +3010,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
js_Emit1(cx, cg, JSOP_RETSUB) < 0) {
return JS_FALSE;
}
} else {
js_PopStatementCG(cx, cg);
}
js_PopStatementCG(cx, cg);
if (js_NewSrcNote(cx, cg, SRC_ENDBRACE) < 0 ||
js_Emit1(cx, cg, JSOP_NOP) < 0) {
@ -2958,17 +3019,15 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
}
/* Fix up the end-of-try/catch jumps to come here. */
if (!BackPatch(cx, cg, &stmtInfo, stmtInfo.catchJump, CG_NEXT(cg),
JSOP_GOTO)) {
if (!BackPatch(cx, cg, stmtInfo.catchJump, CG_NEXT(cg), JSOP_GOTO))
return JS_FALSE;
}
/*
* Add the try note last, to let post-order give us the right ordering
* (first to last for a given nesting level, inner to outer by level).
*/
if (pn->pn_kid2) {
JS_ASSERT(catchStart != -1);
JS_ASSERT(end != -1 && catchStart != -1);
if (!js_NewTryNote(cx, cg, start, end, catchStart))
return JS_FALSE;
}
@ -3054,12 +3113,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
}
/*
* EmitNonLocalJumpFixup emits JSOP_SWAPs to maintain the return value
* at the top of the stack, so the return still executes OK.
* EmitNonLocalJumpFixup mutates op to JSOP_RETRVAL after emitting a
* JSOP_SETRVAL if there are open try blocks having finally clauses.
* We can't simply transfer control flow to our caller in that case,
* because we must gosub to those clauses from inner to outer, with
* the correct stack pointer (i.e., after popping any with, for/in,
* etc., slots nested inside the finally's try).
*/
if (!EmitNonLocalJumpFixup(cx, cg, NULL, JS_TRUE))
op = JSOP_RETURN;
if (!EmitNonLocalJumpFixup(cx, cg, NULL, &op))
return JS_FALSE;
if (js_Emit1(cx, cg, JSOP_RETURN) < 0)
if (js_Emit1(cx, cg, op) < 0)
return JS_FALSE;
break;
@ -3307,6 +3371,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
if (!js_SetSrcNoteOffset(cx, cg, noteIndex, 0, jmp - beq))
return JS_FALSE;
/*
* Because each branch pushes a single value, but our stack budgeting
* analysis ignores branches, we now have two values accounted for in
* cg->stackDepth. Execution will follow only one path, so we must
* decrement cg->stackDepth here. Failing to do this will foil code,
* such as the try/catch/finally exception handling code generator,
* that samples cg->stackDepth for use at runtime (JSOP_SETSP).
*/
JS_ASSERT(cg->stackDepth > 1);
cg->stackDepth--;
break;
case TOK_OR:

Просмотреть файл

@ -60,7 +60,8 @@ typedef enum JSStmtType {
STMT_DO_LOOP = 9, /* do/while loop statement */
STMT_FOR_LOOP = 10, /* for loop statement */
STMT_FOR_IN_LOOP = 11, /* for/in loop statement */
STMT_WHILE_LOOP = 12 /* while loop statement */
STMT_WHILE_LOOP = 12, /* while loop statement */
STMT_SUBROUTINE = 13 /* gosub-target subroutine body */
} JSStmtType;
#define STMT_IS_LOOP(stmt) ((stmt)->type >= STMT_DO_LOOP)
@ -69,7 +70,6 @@ typedef struct JSStmtInfo JSStmtInfo;
struct JSStmtInfo {
JSStmtType type; /* statement type */
ptrdiff_t top; /* offset of loop top from cg base */
ptrdiff_t update; /* loop update offset (top if none) */
ptrdiff_t breaks; /* offset of last break in loop */
ptrdiff_t continues; /* offset of last continue in loop */
@ -80,7 +80,7 @@ struct JSStmtInfo {
};
#define SET_STATEMENT_TOP(stmt, top) \
((stmt)->top = (stmt)->update = (top), (stmt)->breaks = \
((stmt)->update = (top), (stmt)->breaks = \
(stmt)->continues = (stmt)->catchJump = (stmt)->gosub = (-1))
struct JSTreeContext { /* tree context for semantic checks */

Просмотреть файл

@ -1334,7 +1334,7 @@ js_Interpret(JSContext *cx, jsval *result)
JS_GetStringBytes(str));
}
}
putc('\n', tracefp);
fprintf(tracefp, " @ %d\n", sp - fp->spbase);
}
}
#endif
@ -1422,9 +1422,16 @@ js_Interpret(JSContext *cx, jsval *result)
fp->scopeChain = JSVAL_TO_OBJECT(rval);
break;
case JSOP_SETRVAL:
fp->rval = POP_OPND();
break;
case JSOP_RETURN:
CHECK_BRANCH(-1);
fp->rval = POP_OPND();
/* FALL THROUGH */
case JSOP_RETRVAL: /* fp->rval already set */
if (inlineCallCount)
inline_return:
{
@ -3876,6 +3883,7 @@ js_Interpret(JSContext *cx, jsval *result)
#ifdef DEBUG
if (tracefp) {
intN ndefs, n;
jsval *siter;
ndefs = cs->ndefs;
if (ndefs) {
@ -3888,8 +3896,15 @@ js_Interpret(JSContext *cx, jsval *result)
JS_GetStringBytes(str));
}
}
putc('\n', tracefp);
fprintf(tracefp, " @ %d\n", sp - fp->spbase);
}
fprintf(tracefp, " stack: ");
for (siter = fp->spbase; siter < sp; siter++) {
str = js_ValueToSource(cx, *siter);
fprintf(tracefp, "%s ",
str ? JS_GetStringBytes(str) : "<null>");
}
fputc('\n', tracefp);
}
#endif
}

Просмотреть файл

@ -927,7 +927,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
switch (sn ? SN_TYPE(sn) : SRC_NULL) {
#if JS_HAS_DO_WHILE_LOOP
case SRC_WHILE:
js_printf(jp, "\tdo {\n"); /* balance} */
js_printf(jp, "\tdo {\n");
jp->indent += 4;
break;
#endif /* JS_HAS_DO_WHILE_LOOP */
@ -999,7 +999,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
jp->indent -= 4;
sn = js_GetSrcNote(jp->script, pc);
pc += oplen;
js_printf(jp, "\t} catch ("); /* balance) */
js_printf(jp, "\t} catch (");
LOCAL_ASSERT(*pc == JSOP_NAME);
pc += js_CodeSpec[JSOP_NAME].length;
@ -1026,7 +1026,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
pc += js_CodeSpec[*pc].length;
}
js_printf(jp, ") {\n"); /* balance} */
js_printf(jp, ") {\n");
jp->indent += 4;
len = 0;
break;
@ -1053,6 +1053,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
default:;
}
case JSOP_RETRVAL:
break;
case JSOP_GROUP:
@ -1078,25 +1079,68 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
case JSOP_TRY:
js_printf(jp, "\ttry {\n");
jp->indent += 4;
todo = -2;
break;
{
static const char finally_cookie[] = "finally-cookie";
case JSOP_FINALLY:
jp->indent -= 4;
js_printf(jp, "\t} finally {\n");
jp->indent += 4;
/*
* We must push an empty string placeholder for gosub's return
* address, popped by JSOP_RETSUB and counted by script->depth
* but not by ss->top (see JSOP_SETSP, below).
*/
todo = Sprint(&ss->sprinter, finally_cookie);
break;
case JSOP_RETSUB:
rval = POP_STR();
LOCAL_ASSERT(strcmp(rval, finally_cookie) == 0);
todo = -2;
break;
}
case JSOP_SWAP:
/*
* We don't generate this opcode currently, and previously we
* did not need to decompile it. If old, serialized bytecode
* uses it still, we should fall through and set todo = -2.
*/
/* FALL THROUGH */
case JSOP_GOSUB:
case JSOP_GOSUBX:
case JSOP_RETSUB:
case JSOP_SETSP:
/*
* JSOP_GOSUB and GOSUBX have no effect on the decompiler's
* string stack because the next op in bytecode order finds
* the stack balanced by a JSOP_RETSUB executed elsewhere.
*/
todo = -2;
break;
case JSOP_SETSP:
/*
* The compiler models operand stack depth and fixes the stack
* pointer on entry to a catch clause based on its depth model.
* The decompiler must match the code generator's model, which
* is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops.
*/
ss->top = (uintN) GET_ATOM_INDEX(pc);
break;
case JSOP_EXCEPTION:
sn = js_GetSrcNote(jp->script, pc);
if (sn && SN_TYPE(sn) == SRC_HIDDEN)
todo = -2;
/*
* The only other JSOP_EXCEPTION case occurs as part of a code
* sequence that follows a SRC_CATCH-annotated JSOP_NOP.
*/
sn = js_GetSrcNote(jp->script, pc);
LOCAL_ASSERT(sn && SN_TYPE(sn) == SRC_HIDDEN);
todo = -2;
break;
#endif /* JS_HAS_EXCEPTIONS */
@ -1154,32 +1198,41 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
todo = -2;
break;
{
static const char with_cookie[] = "with-cookie";
case JSOP_ENTERWITH:
sn = js_GetSrcNote(jp->script, pc);
todo = -2;
if (sn && SN_TYPE(sn) == SRC_HIDDEN)
break;
rval = POP_STR();
js_printf(jp, "\twith (%s) {\n", rval);
jp->indent += 4;
if (sn && SN_TYPE(sn) == SRC_HIDDEN) {
todo = -2;
break;
}
rval = POP_STR();
js_printf(jp, "\twith (%s) {\n", rval);
jp->indent += 4;
todo = Sprint(&ss->sprinter, with_cookie);
break;
case JSOP_LEAVEWITH:
sn = js_GetSrcNote(jp->script, pc);
todo = -2;
todo = -2;
if (sn && SN_TYPE(sn) == SRC_HIDDEN)
break;
jp->indent -= 4;
js_printf(jp, "\t}\n");
break;
rval = POP_STR();
LOCAL_ASSERT(strcmp(rval, with_cookie) == 0);
jp->indent -= 4;
js_printf(jp, "\t}\n");
break;
}
case JSOP_SETRVAL:
case JSOP_RETURN:
rval = POP_STR();
if (*rval != '\0')
js_printf(jp, "\t%s %s;\n", cs->name, rval);
else
js_printf(jp, "\t%s;\n", cs->name);
todo = -2;
todo = -2;
break;
#if JS_HAS_EXCEPTIONS
@ -1293,7 +1346,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
#if JS_HAS_DO_WHILE_LOOP
/* Currently, this must be a do-while loop's upward branch. */
jp->indent -= 4;
/* {balance: */
js_printf(jp, "\t} while (%s);\n", POP_STR());
todo = -2;
#else
@ -1433,12 +1485,13 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
case JSOP_DUP2:
rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-2]);
todo = SprintPut(&ss->sprinter, rval, strlen(rval));
if (todo < 0 || !PushOff(ss, todo, op))
if (todo < 0 || !PushOff(ss, todo, ss->opcodes[ss->top-2]))
return JS_FALSE;
/* FALL THROUGH */
case JSOP_DUP:
rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]);
op = ss->opcodes[ss->top-1];
todo = SprintPut(&ss->sprinter, rval, strlen(rval));
break;
@ -2073,12 +2126,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
todo = Sprint(&ss->sprinter, "#%u=%c",
(unsigned) i,
(*lval == 'O') ? '{' : '[');
/* balance}] */
} else
#endif /* JS_HAS_SHARP_VARS */
{
todo = Sprint(&ss->sprinter, (*lval == 'O') ? "{" : "[");
/* balance}] */
}
break;
@ -2088,7 +2139,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
todo = Sprint(&ss->sprinter, "%s%s%c",
rval,
(sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "",
/* [balance */
(*rval == '{') ? '}' : ']');
break;

Просмотреть файл

@ -322,3 +322,7 @@ OPDEF(JSOP_LOOKUPSWITCHX, 148,"lookupswitchx",NULL, -1, 1, 0, 0, JOF_LOOKUP
OPDEF(JSOP_BACKPATCH, 149,"backpatch",NULL, 3, 0, 0, 0, JOF_JUMP|JOF_BACKPATCH)
OPDEF(JSOP_BACKPATCH_POP, 150,"backpatch_pop",NULL, 3, 1, 0, 0, JOF_JUMP|JOF_BACKPATCH)
OPDEF(JSOP_BACKPATCH_PUSH,151,"backpatch_push",NULL, 3, 0, 1, 0, JOF_JUMP|JOF_BACKPATCH)
/* Set and get return value pseudo-register in stack frame. */
OPDEF(JSOP_SETRVAL, 152,"setrval", NULL, 1, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_RETRVAL, 153,"retrval", NULL, 1, 0, 0, 0, JOF_BYTE)