Fix try/finally and try/guarded-catches/finally hard cases (104077, r=jband, sr=shaver).
This commit is contained in:
Родитель
45defae20a
Коммит
30d6bf4ad3
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
213
js/src/jsemit.c
213
js/src/jsemit.c
|
@ -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)
|
||||
|
|
Загрузка…
Ссылка в новой задаче