зеркало из https://github.com/mozilla/pjs.git
Fix try/catch/finally code generation (350312, r=igor/shaver).
This commit is contained in:
Родитель
9ddd848246
Коммит
d95a636109
|
@ -129,7 +129,7 @@ const char XUL_FASTLOAD_FILE_BASENAME[] = "XUL";
|
|||
// Increase the subtractor when changing version, say when changing the
|
||||
// (opaque to FastLoad code) format of JS script, function, regexp, etc.
|
||||
// XDR serializations.
|
||||
#define XUL_FASTLOAD_FILE_VERSION (0xfeedbeef - 16)
|
||||
#define XUL_FASTLOAD_FILE_VERSION (0xfeedbeef - 18)
|
||||
|
||||
#define XUL_SERIALIZATION_BUFFER_SIZE (64 * 1024)
|
||||
#define XUL_DESERIALIZATION_BUFFER_SIZE (8 * 1024)
|
||||
|
|
|
@ -45,6 +45,18 @@
|
|||
|
||||
JS_BEGIN_EXTERN_C
|
||||
|
||||
/*
|
||||
* Crypto-booleans, not visible to script but used internally by the engine.
|
||||
*
|
||||
* JSVAL_HOLE is a useful value for identifying a hole in an array. It's also
|
||||
* used in the interpreter to represent "no exception pending". In general it
|
||||
* can be used to represent "no value".
|
||||
*
|
||||
* JSVAL_ARETURN is used to throw asynchronous return for generator.close().
|
||||
*/
|
||||
#define JSVAL_HOLE BOOLEAN_TO_JSVAL(2)
|
||||
#define JSVAL_ARETURN BOOLEAN_TO_JSVAL(3)
|
||||
|
||||
extern JSClass js_BooleanClass;
|
||||
|
||||
extern JSObject *
|
||||
|
|
168
js/src/jsemit.c
168
js/src/jsemit.c
|
@ -329,11 +329,10 @@ ReportStatementTooLarge(JSContext *cx, JSCodeGenerator *cg)
|
|||
Note that backpatch chains would present a problem for BuildSpanDepTable,
|
||||
which inspects bytecode to build cg->spanDeps on demand, when the first
|
||||
short jump offset overflows. To solve this temporary problem, we emit a
|
||||
proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a
|
||||
result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP
|
||||
for branch ops) whose nuses/ndefs counts help keep the stack balanced, but
|
||||
whose opcode format distinguishes its backpatch delta immediate operand from
|
||||
a normal jump offset.
|
||||
proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_POP for branch ops) whose
|
||||
nuses/ndefs counts help keep the stack balanced, but whose opcode format
|
||||
distinguishes its backpatch delta immediate operand from a normal jump
|
||||
offset.
|
||||
*/
|
||||
static int
|
||||
BalanceJumpTargets(JSJumpTarget **jtp)
|
||||
|
@ -1352,7 +1351,7 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
|
|||
case STMT_FINALLY:
|
||||
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
|
||||
return JS_FALSE;
|
||||
jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH_PUSH, &GOSUBS(*stmt));
|
||||
jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(*stmt));
|
||||
if (jmp < 0)
|
||||
return JS_FALSE;
|
||||
break;
|
||||
|
@ -4379,8 +4378,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
if (pn->pn_kid3) {
|
||||
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
|
||||
return JS_FALSE;
|
||||
jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH_PUSH,
|
||||
&GOSUBS(stmtInfo));
|
||||
jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(stmtInfo));
|
||||
if (jmp < 0)
|
||||
return JS_FALSE;
|
||||
|
||||
|
@ -4420,7 +4418,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
*
|
||||
* If there's no catch block without a catchguard, the last
|
||||
* <offset to next catch block> points to rethrow code. This
|
||||
* code will GOSUB to the finally code if appropriate, and is
|
||||
* code will [gosub] to the finally code if appropriate, and is
|
||||
* also used for the catch-all trynote for capturing exceptions
|
||||
* thrown from catch{} blocks.
|
||||
*/
|
||||
|
@ -4438,6 +4436,10 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
/* Fix up and clean up previous catch block. */
|
||||
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, guardJump);
|
||||
|
||||
/* Set cx->throwing to protect cx->exception from the GC. */
|
||||
if (!js_Emit1(cx, cg, JSOP_THROWING) < 0)
|
||||
return JS_FALSE;
|
||||
|
||||
/*
|
||||
* Emit an unbalanced [leaveblock] for the previous catch,
|
||||
* whose block object count is saved below.
|
||||
|
@ -4472,12 +4474,11 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
|
||||
/* gosub <finally>, if required */
|
||||
if (pn->pn_kid3) {
|
||||
jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH_PUSH,
|
||||
jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH,
|
||||
&GOSUBS(stmtInfo));
|
||||
if (jmp < 0)
|
||||
return JS_FALSE;
|
||||
JS_ASSERT(cg->stackDepth == depth + 1);
|
||||
cg->stackDepth = depth;
|
||||
JS_ASSERT(cg->stackDepth == depth);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -4499,11 +4500,35 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
}
|
||||
|
||||
/*
|
||||
* We emit a [setsp, exception, gosub, throw] block for rethrowing
|
||||
* when there's no unguarded catch, and also for running finally code
|
||||
* while letting an uncaught exception pass through.
|
||||
* We emit a [setsp][gosub] sequence for running finally code while
|
||||
* letting an uncaught exception pass thrown from within the try in a
|
||||
* try-finally. The [gosub] and [retsub] opcodes will take care of
|
||||
* stacking and rethrowing any exception pending across the finally.
|
||||
*
|
||||
* For rethrowing after a try-catch(guard)-finally, we have a problem:
|
||||
* all the guards have mismatched, leaving cx->exception still set but
|
||||
* cx->throwing clear, so that no exception appears to be pending for
|
||||
* [gosub] to stack and [retsub] to rethrow. We must emit a special
|
||||
* [throwing] opcode in front of the [setsp][gosub] finally sequence.
|
||||
* This opcode will restore cx->throwing to true before running the
|
||||
* finally.
|
||||
*
|
||||
* For rethrowing after a try-catch(guard) without a finally, we emit
|
||||
* [throwing] before the [setsp][exception][throw] rethrow sequence.
|
||||
*/
|
||||
if (pn->pn_kid3 || (lastCatch && lastCatch->pn_kid2)) {
|
||||
/*
|
||||
* Last catch guard jumps to the rethrow code sequence if none
|
||||
* of the guards match. Target guardJump at the beginning of the
|
||||
* rethrow sequence, just in case a guard expression throws and
|
||||
* leaves the stack unbalanced.
|
||||
*/
|
||||
if (lastCatch && lastCatch->pn_kid2) {
|
||||
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, GUARDJUMP(stmtInfo));
|
||||
if (pn->pn_kid3 && !js_Emit1(cx, cg, JSOP_THROWING) < 0)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit another stack fixup, because the catch could itself
|
||||
* throw an exception in an unbalanced state, and the finally
|
||||
|
@ -4512,51 +4537,25 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
* stack fixup.
|
||||
*/
|
||||
finallyCatch = CG_OFFSET(cg);
|
||||
|
||||
/*
|
||||
* Last catch guard jumps to the rethrow code sequence if none
|
||||
* of the guards match. Target guardJump at the beginning of the
|
||||
* rethrow sequence, just in case a guard expression throws and
|
||||
* leaves the stack unbalanced.
|
||||
*
|
||||
* What's more, we must jump to the front of the rethrow sequence
|
||||
* for a second reason: to re-sample the pending exception via the
|
||||
* [exception] opcode, so that it can be saved across the [gosub].
|
||||
* See below for case 2 in the comment describing finally clause
|
||||
* stack budget.
|
||||
*/
|
||||
if (lastCatch && lastCatch->pn_kid2)
|
||||
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, GUARDJUMP(stmtInfo));
|
||||
|
||||
EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth);
|
||||
cg->stackDepth = depth;
|
||||
|
||||
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
|
||||
js_Emit1(cx, cg, JSOP_EXCEPTION) < 0) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (pn->pn_kid3) {
|
||||
jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH_PUSH,
|
||||
jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH,
|
||||
&GOSUBS(stmtInfo));
|
||||
if (jmp < 0)
|
||||
return JS_FALSE;
|
||||
|
||||
/*
|
||||
* Exception and retsub pc index are modeled as on the stack.
|
||||
* Decrease cg->stackDepth by one to account for JSOP_RETSUB
|
||||
* popping the pc index.
|
||||
*/
|
||||
JS_ASSERT(cg->stackDepth == depth + 2);
|
||||
JS_ASSERT((uintN)cg->stackDepth <= cg->maxStackDepth);
|
||||
cg->stackDepth = depth + 1;
|
||||
JS_ASSERT(cg->stackDepth == depth);
|
||||
JS_ASSERT((uintN)depth <= cg->maxStackDepth);
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0 ||
|
||||
js_Emit1(cx, cg, JSOP_THROW) < 0) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
JS_ASSERT(cg->stackDepth == depth);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -4568,30 +4567,10 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
return JS_FALSE;
|
||||
|
||||
/*
|
||||
* The stack budget must be balanced at this point. There are
|
||||
* three cases:
|
||||
*
|
||||
* 1. try-finally: we need two more slots for the saved exception
|
||||
* and the JSOP_RETSUB's return pc index that was pushed by
|
||||
* the JSOP_GOSUB opcode that called this finally clause.
|
||||
*
|
||||
* 2. try-catch(guard)...-catch(guard)-finally: we can't know at
|
||||
* compile time whether a guarded catch caught the exception,
|
||||
* so we may need to propagate it via re-throw bytecode when
|
||||
* the finally returns. In this case, if no guard expression
|
||||
* evaluates to true, we will jump to the rethrow sequence,
|
||||
* which re-samples cx->exception using JSOP_EXCEPTION, then
|
||||
* calls the finally subroutine. So in this case as well as
|
||||
* in case 1, two more slots will already be on the stack.
|
||||
*
|
||||
* 3. try-catch-finally or try-catch(guard)...-catch-finally:
|
||||
* we need one slot for the JSOP_RETSUB's return pc index.
|
||||
* The unguarded catch is guaranteed to pop the exception,
|
||||
* i.e., to "catch the exception" -- so we do not need to
|
||||
* stack it across the finally in order to propagate it --
|
||||
* unless the catch block explicitly re-throws it! We can't
|
||||
* know whether this will happen by static analysis, so we
|
||||
* must always budget for two slots.
|
||||
* The stack budget must be balanced at this point. All [gosub]
|
||||
* calls emitted before this point will push two stack slots, one
|
||||
* for the pending exception (or JSVAL_HOLE if there is no pending
|
||||
* exception) and one for the [retsub] pc-index.
|
||||
*/
|
||||
JS_ASSERT(cg->stackDepth == depth);
|
||||
cg->stackDepth += 2;
|
||||
|
@ -4609,7 +4588,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
|||
}
|
||||
|
||||
/* Restore stack depth budget to its balanced state. */
|
||||
JS_ASSERT(cg->stackDepth == depth + 1);
|
||||
JS_ASSERT(cg->stackDepth == depth + 2);
|
||||
cg->stackDepth = depth;
|
||||
}
|
||||
if (!js_PopStatementCG(cx, cg))
|
||||
|
@ -6390,40 +6369,3 @@ js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote *notes)
|
|||
notes[count].length = CG_OFFSET(cg);
|
||||
notes[count].catchStart = 0;
|
||||
}
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
|
||||
jsbytecode *
|
||||
js_FindFinallyHandler(JSScript *script, jsbytecode *pc)
|
||||
{
|
||||
JSTryNote *tn;
|
||||
ptrdiff_t off;
|
||||
|
||||
tn = script->trynotes;
|
||||
if (!tn)
|
||||
return NULL;
|
||||
|
||||
off = pc - script->main;
|
||||
if (off < 0)
|
||||
return NULL;
|
||||
|
||||
JS_ASSERT(tn->catchStart != 0);
|
||||
do {
|
||||
if ((jsuword)(off - tn->start) < (jsuword)tn->length) {
|
||||
/*
|
||||
* We have a handler, is it finally?
|
||||
*
|
||||
* Catch bytecode begins with: JSOP_SETSP JSOP_ENTERBLOCK
|
||||
* Finally bytecode begins with: JSOP_SETSP JSOP_EXCEPTION
|
||||
*/
|
||||
pc = script->main + tn->catchStart;
|
||||
JS_ASSERT(*pc == JSOP_SETSP);
|
||||
if (pc[js_CodeSpec[JSOP_SETSP].length] == JSOP_EXCEPTION)
|
||||
return pc;
|
||||
JS_ASSERT(pc[js_CodeSpec[JSOP_SETSP].length] == JSOP_ENTERBLOCK);
|
||||
}
|
||||
} while ((++tn)->catchStart != 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -5452,12 +5452,18 @@ interrupt:
|
|||
END_CASE(JSOP_SETSP)
|
||||
|
||||
BEGIN_CASE(JSOP_GOSUB)
|
||||
JS_ASSERT(cx->exception != JSVAL_HOLE);
|
||||
lval = cx->throwing ? cx->exception : JSVAL_HOLE;
|
||||
PUSH(lval);
|
||||
i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUB_LENGTH;
|
||||
len = GET_JUMP_OFFSET(pc);
|
||||
PUSH(INT_TO_JSVAL(i));
|
||||
END_VARLEN_CASE
|
||||
|
||||
BEGIN_CASE(JSOP_GOSUBX)
|
||||
JS_ASSERT(cx->exception != JSVAL_HOLE);
|
||||
lval = cx->throwing ? cx->exception : JSVAL_HOLE;
|
||||
PUSH(lval);
|
||||
i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUBX_LENGTH;
|
||||
len = GET_JUMPX_OFFSET(pc);
|
||||
PUSH(INT_TO_JSVAL(i));
|
||||
|
@ -5466,6 +5472,19 @@ interrupt:
|
|||
BEGIN_CASE(JSOP_RETSUB)
|
||||
rval = POP();
|
||||
JS_ASSERT(JSVAL_IS_INT(rval));
|
||||
lval = POP();
|
||||
if (lval != JSVAL_HOLE) {
|
||||
/*
|
||||
* Exception was pending during finally, throw it *before* we
|
||||
* adjust pc, because pc indexes into script->trynotes. This
|
||||
* turns out not to be necessary, but it seems clearer. And
|
||||
* it points out a FIXME: 350509, due to Igor Bukanov.
|
||||
*/
|
||||
cx->throwing = JS_TRUE;
|
||||
cx->exception = lval;
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
}
|
||||
len = JSVAL_TO_INT(rval);
|
||||
pc = script->main;
|
||||
END_VARLEN_CASE
|
||||
|
@ -5475,6 +5494,11 @@ interrupt:
|
|||
cx->throwing = JS_FALSE;
|
||||
END_CASE(JSOP_EXCEPTION)
|
||||
|
||||
BEGIN_CASE(JSOP_THROWING)
|
||||
JS_ASSERT(!cx->throwing);
|
||||
cx->throwing = JS_TRUE;
|
||||
END_CASE(JSOP_THROWING)
|
||||
|
||||
BEGIN_CASE(JSOP_THROW)
|
||||
cx->throwing = JS_TRUE;
|
||||
cx->exception = POP_OPND();
|
||||
|
@ -6108,7 +6132,6 @@ interrupt:
|
|||
|
||||
#ifdef JS_THREADED_INTERP
|
||||
L_JSOP_BACKPATCH:
|
||||
L_JSOP_BACKPATCH_PUSH:
|
||||
L_JSOP_BACKPATCH_POP:
|
||||
#else
|
||||
default:
|
||||
|
|
|
@ -123,14 +123,6 @@ js_NewGenerator(JSContext *cx, JSStackFrame *fp);
|
|||
extern JSBool
|
||||
js_CloseGeneratorObject(JSContext *cx, JSGenerator *gen);
|
||||
|
||||
|
||||
/*
|
||||
* Special unique value to implement asynchronous return for
|
||||
* generator.close(). Scripts never see it.
|
||||
*/
|
||||
|
||||
#define JSVAL_ARETURN BOOLEAN_TO_JSVAL(JS_TRUE + 1)
|
||||
|
||||
#endif
|
||||
|
||||
extern JSClass js_GeneratorClass;
|
||||
|
|
|
@ -937,7 +937,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
jsval val;
|
||||
int stackDummy;
|
||||
|
||||
static const char finally_cookie[] = "/*FINALLY*/";
|
||||
static const char exception_cookie[] = "/*EXCEPTION*/";
|
||||
static const char retsub_pc_cookie[] = "/*RETSUB_PC*/";
|
||||
static const char iter_cookie[] = "/*ITER*/";
|
||||
static const char with_cookie[] = "/*WITH*/";
|
||||
static const char dot_format[] = "%s.%s";
|
||||
|
@ -1238,12 +1239,17 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
* 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);
|
||||
todo = Sprint(&ss->sprinter, exception_cookie);
|
||||
if (todo < 0 || !PushOff(ss, todo, op))
|
||||
return JS_FALSE;
|
||||
todo = Sprint(&ss->sprinter, retsub_pc_cookie);
|
||||
break;
|
||||
|
||||
case JSOP_RETSUB:
|
||||
rval = POP_STR();
|
||||
LOCAL_ASSERT(strcmp(rval, finally_cookie) == 0);
|
||||
LOCAL_ASSERT(strcmp(rval, retsub_pc_cookie) == 0);
|
||||
lval = POP_STR();
|
||||
LOCAL_ASSERT(strcmp(lval, exception_cookie) == 0);
|
||||
todo = -2;
|
||||
break;
|
||||
|
||||
|
@ -1639,6 +1645,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
|||
}
|
||||
#endif
|
||||
|
||||
case JSOP_THROWING:
|
||||
todo = -2;
|
||||
break;
|
||||
|
||||
case JSOP_THROW:
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
todo = -2;
|
||||
|
|
|
@ -226,8 +226,8 @@ OPDEF(JSOP_INSTANCEOF,112,js_instanceof_str,js_instanceof_str,1,2,1,6,JOF_BYTE|J
|
|||
OPDEF(JSOP_DEBUGGER, 113,"debugger", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
|
||||
/* gosub/retsub for finally handling */
|
||||
OPDEF(JSOP_GOSUB, 114,"gosub", NULL, 3, 0, 1, 0, JOF_JUMP)
|
||||
OPDEF(JSOP_RETSUB, 115,"retsub", NULL, 1, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_GOSUB, 114,"gosub", NULL, 3, 0, 0, 0, JOF_JUMP)
|
||||
OPDEF(JSOP_RETSUB, 115,"retsub", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
|
||||
/* More exception handling ops. */
|
||||
OPDEF(JSOP_EXCEPTION, 116,"exception", NULL, 1, 0, 1, 0, JOF_BYTE)
|
||||
|
@ -319,7 +319,7 @@ OPDEF(JSOP_IFEQX, 140,"ifeqx", NULL, 5, 1, 0, 0, JOF_JUMPX|
|
|||
OPDEF(JSOP_IFNEX, 141,"ifnex", NULL, 5, 1, 0, 0, JOF_JUMPX)
|
||||
OPDEF(JSOP_ORX, 142,"orx", NULL, 5, 1, 0, 0, JOF_JUMPX|JOF_DETECTING)
|
||||
OPDEF(JSOP_ANDX, 143,"andx", NULL, 5, 1, 0, 0, JOF_JUMPX|JOF_DETECTING)
|
||||
OPDEF(JSOP_GOSUBX, 144,"gosubx", NULL, 5, 0, 1, 0, JOF_JUMPX)
|
||||
OPDEF(JSOP_GOSUBX, 144,"gosubx", NULL, 5, 0, 0, 0, JOF_JUMPX)
|
||||
OPDEF(JSOP_CASEX, 145,"casex", NULL, 5, 1, 0, 0, JOF_JUMPX)
|
||||
OPDEF(JSOP_DEFAULTX, 146,"defaultx", NULL, 5, 1, 0, 0, JOF_JUMPX)
|
||||
OPDEF(JSOP_TABLESWITCHX, 147,"tableswitchx",NULL, -1, 1, 0, 0, JOF_TABLESWITCHX|JOF_DETECTING)
|
||||
|
@ -328,7 +328,9 @@ OPDEF(JSOP_LOOKUPSWITCHX, 148,"lookupswitchx",NULL, -1, 1, 0, 0, JOF_LOOKUP
|
|||
/* Placeholders for a real jump opcode set during backpatch chain fixup. */
|
||||
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 cx->throwing where cx->exception was already set, to trigger rethrow. */
|
||||
OPDEF(JSOP_THROWING, 151,"throwing", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
|
||||
/* Set and get return value pseudo-register in stack frame. */
|
||||
OPDEF(JSOP_SETRVAL, 152,"setrval", NULL, 1, 1, 0, 0, JOF_BYTE)
|
||||
|
|
|
@ -1515,3 +1515,43 @@ js_GetScriptLineExtent(JSScript *script)
|
|||
}
|
||||
return 1 + lineno - script->lineno;
|
||||
}
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
|
||||
jsbytecode *
|
||||
js_FindFinallyHandler(JSScript *script, jsbytecode *pc)
|
||||
{
|
||||
JSTryNote *tn;
|
||||
ptrdiff_t off;
|
||||
JSOp op2;
|
||||
|
||||
tn = script->trynotes;
|
||||
if (!tn)
|
||||
return NULL;
|
||||
|
||||
off = pc - script->main;
|
||||
if (off < 0)
|
||||
return NULL;
|
||||
|
||||
JS_ASSERT(tn->catchStart != 0);
|
||||
do {
|
||||
if ((jsuword)(off - tn->start) < (jsuword)tn->length) {
|
||||
/*
|
||||
* We have a handler: is it the finally one, or a catch handler?
|
||||
*
|
||||
* Catch bytecode begins with: JSOP_SETSP JSOP_ENTERBLOCK
|
||||
* Finally bytecode begins with: JSOP_SETSP JSOP_(GOSUB|EXCEPTION)
|
||||
*/
|
||||
pc = script->main + tn->catchStart;
|
||||
JS_ASSERT(*pc == JSOP_SETSP);
|
||||
op2 = pc[JSOP_SETSP_LENGTH];
|
||||
if (op2 != JSOP_ENTERBLOCK) {
|
||||
JS_ASSERT(op2 == JSOP_GOSUB || op2 == JSOP_EXCEPTION);
|
||||
return pc;
|
||||
}
|
||||
}
|
||||
} while ((++tn)->catchStart != 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче