Bug 379758: SETSP is removed

This commit is contained in:
igor@mir2.org 2007-05-24 00:51:46 -07:00
parent 815166396c
commit 1972c7985b
11 changed files with 302 additions and 302 deletions

View File

@ -1068,19 +1068,29 @@ Notes(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
return JS_TRUE;
}
JS_STATIC_ASSERT(JSTN_CATCH == 0);
JS_STATIC_ASSERT(JSTN_FINALLY == 1);
static const char* const TryNoteNames[] = { "catch", "finally" };
static JSBool
TryNotes(JSContext *cx, JSScript *script)
{
JSTryNote *tn = script->trynotes;
JSTryNote *tn, *tnlimit;
if (!tn)
if (!script->trynotes)
return JS_TRUE;
fprintf(gOutFile, "\nException table:\nstart\tend\tcatch\n");
while (tn->start && tn->catchStart) {
fprintf(gOutFile, " %d\t%d\t%d\n",
tn->start, tn->start + tn->length, tn->catchStart);
tn++;
}
tn = script->trynotes->notes;
tnlimit = tn + script->trynotes->length;
fprintf(gOutFile, "\nException table:\n"
"kind stack start end\n");
do {
JS_ASSERT(tn->kind == JSTN_CATCH || tn->kind == JSTN_FINALLY);
fprintf(gOutFile, " %-7s %6u %8u %8u\n",
TryNoteNames[tn->kind], tn->stackDepth,
tn->start, tn->start + tn->length);
} while (++tn != tnlimit);
return JS_TRUE;
}

View File

@ -1492,7 +1492,6 @@ JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
JSObject *obj;
jsatomid i;
jssrcnote *sn, *notes;
JSTryNote *tn, *tnotes;
JSPrincipals *principals;
nbytes = sizeof *script;
@ -1513,11 +1512,9 @@ JS_GetScriptTotalSize(JSContext *cx, JSScript *script)
continue;
nbytes += (sn - notes + 1) * sizeof *sn;
tnotes = script->trynotes;
if (tnotes) {
for (tn = tnotes; tn->catchStart; tn++)
continue;
nbytes += (tn - tnotes + 1) * sizeof *tn;
if (script->trynotes) {
nbytes += offsetof(JSTryNoteArray, notes) +
script->trynotes->length * sizeof script->trynotes->notes[0];
}
principals = script->principals;

View File

@ -1079,16 +1079,6 @@ OptimizeSpanDeps(JSContext *cx, JSCodeGenerator *cg)
sd2 = FindNearestSpanDep(cg, offset + length, sd - sdbase, &guard);
if (sd2 != sd)
tn->length = length + sd2->offset - sd2->before - delta;
/*
* Finally, adjust tn->catchStart upward only if it is non-zero,
* and provided there are spandeps below it that grew.
*/
offset = tn->catchStart;
if (offset != 0) {
sd = FindNearestSpanDep(cg, offset, sd2 - sdbase, &guard);
tn->catchStart = offset + sd->offset - sd->before;
}
}
}
@ -4812,11 +4802,11 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
case TOK_TRY:
{
ptrdiff_t start, end, catchJump, catchStart, finallyCatch;
ptrdiff_t tryStart, tryEnd, catchJump, finallyStart;
intN depth;
JSParseNode *lastCatch;
catchJump = catchStart = finallyCatch = -1;
catchJump = -1;
/*
* Push stmtInfo to track jumps-over-catches and gosubs-to-finally
@ -4832,22 +4822,20 @@ 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 imbalance causes problems with things
* like function invocation later on.
* Since an exception can be thrown at any place inside the try block,
* we need to restore the stack and the scope chain before we transfer
* the control to the exception handler.
*
* 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
* or finally block. We can't just zero the stack, because things like
* for/in and with that are active upon entry to the block keep state
* variables on the stack.
* For that we store in a try note associated with the catch or
* finally block the stack depth upon the try entry. The interpreter
* uses this depth to properly unwind the stack and the scope chain.
*/
depth = cg->stackDepth;
/* Mark try location for decompilation, then emit try block. */
if (js_Emit1(cx, cg, JSOP_TRY) < 0)
return JS_FALSE;
start = CG_OFFSET(cg);
tryStart = CG_OFFSET(cg);
if (!js_EmitTree(cx, cg, pn->pn_kid1))
return JS_FALSE;
@ -4870,7 +4858,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (jmp < 0)
return JS_FALSE;
end = CG_OFFSET(cg);
tryEnd = CG_OFFSET(cg);
/* If this try has a catch block, emit it. */
pn2 = pn->pn_kid2;
@ -4878,8 +4866,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (pn2) {
jsint count = 0; /* previous catch block's population */
catchStart = end;
/*
* The emitted code for a catch block looks like:
*
@ -4905,12 +4891,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
ptrdiff_t guardJump, catchNote;
JS_ASSERT(cg->stackDepth == depth);
guardJump = GUARDJUMP(stmtInfo);
if (guardJump == -1) {
/* Set stack to original depth (see SETSP comment above). */
EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth);
cg->stackDepth = depth;
} else {
if (guardJump != -1) {
/* Fix up and clean up previous catch block. */
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, guardJump);
@ -4918,7 +4901,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
* Account for the pushed exception object that we still
* have after the jumping from the previous guard.
*/
JS_ASSERT(cg->stackDepth == depth);
cg->stackDepth = depth + 1;
/*
@ -5017,24 +4999,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
JS_ASSERT(cg->stackDepth == depth);
/* Emit finally handler if any. */
finallyStart = 0; /* to quell GCC uninitialized warnings */
if (pn->pn_kid3) {
/*
* We emit [setsp][gosub] to call try-finally when an exception is
* thrown from try or try-catch blocks. The [gosub] and [retsub]
* opcodes will take care of stacking and rethrowing any exception
* pending across the finally.
*/
finallyCatch = CG_OFFSET(cg);
EMIT_UINT16_IMM_OP(JSOP_SETSP, (jsatomid)depth);
jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH,
&GOSUBS(stmtInfo));
if (jmp < 0)
return JS_FALSE;
JS_ASSERT(cg->stackDepth == depth);
JS_ASSERT((uintN)depth <= cg->maxStackDepth);
/*
* Fix up the gosubs that might have been emitted before non-local
* jumps to the finally code.
@ -5042,13 +5008,20 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (!BackPatch(cx, cg, GOSUBS(stmtInfo), CG_NEXT(cg), JSOP_GOSUB))
return JS_FALSE;
finallyStart = CG_OFFSET(cg);
/*
* 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.
* The stack depth at the begining of finally must match the try
* depth plus 2 slots. The interpreter uses these two slots to
* either push (true, exception) pair when it transfers control
* flow to the finally after capturing an exception, or to push
* (false, pc-index) when it calls finally from [gosub]. The first
* element of the pair indicates for [retsub] that it should
* either rethrow the pending exception or transfer the control
* back to the caller of finally.
*/
JS_ASSERT(cg->stackDepth == depth);
JS_ASSERT((uintN)depth <= cg->maxStackDepth);
cg->stackDepth += 2;
if ((uintN)cg->stackDepth > cg->maxStackDepth)
cg->maxStackDepth = cg->stackDepth;
@ -5083,10 +5056,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
* 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(end != -1 && catchStart != -1);
if (!js_NewTryNote(cx, cg, start, end, catchStart))
return JS_FALSE;
if (pn->pn_kid2 &&
!js_NewTryNote(cx, cg, JSTN_CATCH, depth, tryStart, tryEnd)) {
return JS_FALSE;
}
/*
@ -5094,10 +5066,10 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
* trynote to catch exceptions (re)thrown from a catch block or
* for the try{}finally{} case.
*/
if (pn->pn_kid3) {
JS_ASSERT(finallyCatch != -1);
if (!js_NewTryNote(cx, cg, start, finallyCatch, finallyCatch))
return JS_FALSE;
if (pn->pn_kid3 &&
!js_NewTryNote(cx, cg, JSTN_FINALLY, depth, tryStart,
finallyStart)) {
return JS_FALSE;
}
break;
}
@ -6910,31 +6882,29 @@ js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg)
}
JSTryNote *
js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start,
ptrdiff_t end, ptrdiff_t catchStart)
js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, JSTryNoteKind kind,
uintN stackDepth, size_t start, size_t end)
{
JSTryNote *tn;
JS_ASSERT(cg->tryBase <= cg->tryNext);
JS_ASSERT(catchStart >= 0);
JS_ASSERT(kind == JSTN_FINALLY || kind == JSTN_CATCH);
JS_ASSERT((uintN)(uint16)stackDepth == stackDepth);
JS_ASSERT(start <= end);
JS_ASSERT((size_t)(uint32)start == start);
JS_ASSERT((size_t)(uint32)end == end);
tn = cg->tryNext++;
tn->start = start;
tn->length = end - start;
tn->catchStart = catchStart;
tn->kind = kind;
tn->stackDepth = (uint16)stackDepth;
tn->start = (uint32)start;
tn->length = (uint32)(end - start);
return tn;
}
void
js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote *notes)
js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg,
JSTryNoteArray *array)
{
uintN count;
count = PTRDIFF(cg->tryNext, cg->tryBase, JSTryNote);
if (!count)
return;
memcpy(notes, cg->tryBase, TRYNOTE_SIZE(count));
notes[count].start = 0;
notes[count].length = CG_OFFSET(cg);
notes[count].catchStart = 0;
JS_ASSERT(cg->tryNext - cg->tryBase == array->length);
memcpy(array->notes, cg->tryBase, TRYNOTE_SIZE(array->length));
}

View File

@ -48,6 +48,7 @@
#include "jstypes.h"
#include "jsatom.h"
#include "jsopcode.h"
#include "jsscript.h"
#include "jsprvtd.h"
#include "jspubtd.h"
@ -718,24 +719,12 @@ js_AllocTryNotes(JSContext *cx, JSCodeGenerator *cg);
* Grab the next trynote slot in cg, filling it in appropriately.
*/
extern JSTryNote *
js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t start,
ptrdiff_t end, ptrdiff_t catchStart);
/*
* Finish generating exception information into the space at notes. As with
* js_FinishTakingSrcNotes, the caller must use CG_COUNT_FINAL_TRYNOTES(cg) to
* preallocate enough space in a JSTryNote[] to pass as the notes parameter of
* js_FinishTakingTryNotes.
*/
#define CG_COUNT_FINAL_TRYNOTES(cg, cnt) \
JS_BEGIN_MACRO \
cnt = ((cg)->tryNext > (cg)->tryBase) \
? PTRDIFF(cg->tryNext, cg->tryBase, JSTryNote) + 1 \
: 0; \
JS_END_MACRO
js_NewTryNote(JSContext *cx, JSCodeGenerator *cg, JSTryNoteKind kind,
uintN stackDepth, size_t start, size_t end);
extern void
js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote *notes);
js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg,
JSTryNoteArray *array);
JS_END_EXTERN_C

View File

@ -1144,7 +1144,7 @@ FindAndMarkObjectsToClose(JSTracer *trc, JSGCInvocationKind gckind)
*genp = gen->next;
if (gen->state == JSGEN_OPEN &&
js_FindFinallyHandler(gen->frame.script, gen->frame.pc) &&
js_IsInsideTryWithFinally(gen->frame.script, gen->frame.pc) &&
CanScheduleCloseHook(gen)) {
/*
* Generator yielded inside a try with a finally block.

View File

@ -5291,65 +5291,15 @@ interrupt:
EMPTY_CASE(JSOP_TRY)
EMPTY_CASE(JSOP_FINALLY)
/* Reset the stack to the given depth. */
BEGIN_CASE(JSOP_SETSP)
i = (jsint) GET_UINT16(pc);
for (obj = fp->blockChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
if (OBJ_BLOCK_DEPTH(cx, obj) < i)
break;
}
fp->blockChain = obj;
JS_ASSERT(ok);
for (obj = fp->scopeChain;
(clasp = OBJ_GET_CLASS(cx, obj)) == &js_WithClass ||
clasp == &js_BlockClass;
obj = OBJ_GET_PARENT(cx, obj)) {
if (JS_GetPrivate(cx, obj) != fp ||
OBJ_BLOCK_DEPTH(cx, obj) < i) {
break;
}
if (clasp == &js_BlockClass)
ok &= js_PutBlockObject(cx, obj);
else
JS_SetPrivate(cx, obj, NULL);
}
fp->scopeChain = obj;
/* Set sp after js_PutBlockObject to avoid potential GC hazards. */
sp = fp->spbase + i;
/* Don't fail until after we've updated all stacks. */
if (!ok)
goto out;
END_CASE(JSOP_SETSP)
BEGIN_CASE(JSOP_GOSUB)
JS_ASSERT(cx->exception != JSVAL_HOLE);
if (!cx->throwing) {
lval = JSVAL_HOLE;
} else {
lval = cx->exception;
cx->throwing = JS_FALSE;
}
PUSH(lval);
PUSH(JSVAL_FALSE);
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);
if (!cx->throwing) {
lval = JSVAL_HOLE;
} else {
lval = cx->exception;
cx->throwing = JS_FALSE;
}
PUSH(lval);
PUSH(JSVAL_FALSE);
i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUBX_LENGTH;
len = GET_JUMPX_OFFSET(pc);
PUSH(INT_TO_JSVAL(i));
@ -5357,9 +5307,9 @@ interrupt:
BEGIN_CASE(JSOP_RETSUB)
rval = POP();
JS_ASSERT(JSVAL_IS_INT(rval));
lval = POP();
if (lval != JSVAL_HOLE) {
JS_ASSERT(JSVAL_IS_BOOLEAN(lval));
if (JSVAL_TO_BOOLEAN(lval)) {
/*
* Exception was pending during finally, throw it *before* we
* adjust pc, because pc indexes into script->trynotes. This
@ -5367,10 +5317,11 @@ interrupt:
* it points out a FIXME: 350509, due to Igor Bukanov.
*/
cx->throwing = JS_TRUE;
cx->exception = lval;
cx->exception = rval;
ok = JS_FALSE;
goto out;
}
JS_ASSERT(JSVAL_IS_INT(rval));
len = JSVAL_TO_INT(rval);
pc = script->main;
END_VARLEN_CASE
@ -5993,6 +5944,7 @@ interrupt:
#if JS_THREADED_INTERP
L_JSOP_BACKPATCH:
L_JSOP_BACKPATCH_POP:
L_JSOP_UNUSED117:
#else
default:
#endif
@ -6050,6 +6002,7 @@ interrupt:
#endif /* !JS_THREADED_INTERP */
out:
JS_ASSERT((size_t)(pc - script->code) < script->length);
if (!ok) {
/*
* Has an exception been raised? Also insist that we are not in an
@ -6076,10 +6029,14 @@ out:
* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=309894
*/
if (cx->throwing && !(fp->flags & JSFRAME_FILTERING)) {
JSTrapHandler handler;
JSTryNote *tn, *tnlimit;
uint32 offset;
/*
* Call debugger throw hook if set (XXX thread safety?).
*/
JSTrapHandler handler = rt->throwHook;
handler = rt->throwHook;
if (handler) {
SAVE_SP_AND_PC(fp);
switch (handler(cx, script, pc, &rval, rt->throwHookData)) {
@ -6102,27 +6059,100 @@ out:
/*
* Look for a try block in script that can catch this exception.
*/
if (!script->trynotes)
goto no_catch;
offset = (uint32)(pc - script->main);
tn = script->trynotes->notes;
tnlimit = tn + script->trynotes->length;
for (;;) {
if (offset - tn->start < tn->length) {
if (tn->kind == JSTN_FINALLY)
break;
JS_ASSERT(tn->kind == JSTN_CATCH);
#if JS_HAS_GENERATORS
if (JS_LIKELY(cx->exception != JSVAL_ARETURN)) {
SCRIPT_FIND_CATCH_START(script, pc, pc);
if (!pc)
goto no_catch;
} else {
pc = js_FindFinallyHandler(script, pc);
if (!pc) {
cx->throwing = JS_FALSE;
ok = JS_TRUE;
fp->rval = JSVAL_VOID;
/* Catch can not intercept closing of a generator. */
if (JS_LIKELY(cx->exception != JSVAL_ARETURN))
break;
#else
break;
#endif
}
if (++tn == tnlimit) {
#if JS_HAS_GENERATORS
if (JS_UNLIKELY(cx->exception == JSVAL_ARETURN)) {
cx->throwing = JS_FALSE;
ok = JS_TRUE;
fp->rval = JSVAL_VOID;
}
#endif
goto no_catch;
}
}
#else
SCRIPT_FIND_CATCH_START(script, pc, pc);
if (!pc)
goto no_catch;
#endif
/* Don't clear cx->throwing to save cx->exception from GC. */
ok = JS_TRUE;
/*
* Unwind the block and scope chains until we match the stack
* depth of the try note.
*/
i = tn->stackDepth;
for (obj = fp->blockChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
if (OBJ_BLOCK_DEPTH(cx, obj) < i)
break;
}
fp->blockChain = obj;
JS_ASSERT(ok);
for (obj = fp->scopeChain;
(clasp = OBJ_GET_CLASS(cx, obj)) == &js_WithClass ||
clasp == &js_BlockClass;
obj = OBJ_GET_PARENT(cx, obj)) {
if (JS_GetPrivate(cx, obj) != fp ||
OBJ_BLOCK_DEPTH(cx, obj) < i) {
break;
}
if (clasp == &js_BlockClass) {
/* Don't fail until after we've updated all stacks. */
ok &= js_PutBlockObject(cx, obj);
} else {
JS_SetPrivate(cx, obj, NULL);
}
}
fp->scopeChain = obj;
/* Set sp after js_PutBlockObject to avoid potential GC hazards. */
sp = fp->spbase + i;
/* The catch or finally begins right after the code they protect. */
pc = (script)->main + tn->start + tn->length;
/*
* When failing during the scope recovery, restart the exception
* search with the updated stack and pc.
*/
if (!ok)
goto out;
JS_ASSERT(cx->exception != JSVAL_HOLE);
if (tn->kind == JSTN_FINALLY) {
/*
* Push (false, exception) pair for finally to indicate that
* [retsub] should rethrow the exception.
*/
PUSH(JSVAL_TRUE);
PUSH(cx->exception);
cx->throwing = JS_FALSE;
} else {
/*
* Don't clear cx->throwing to save cx->exception from GC
* until it is pushed to the stack via [exception] in the
* catch block.
*/
}
len = 0;
ok = JS_TRUE;
DO_NEXT_OP(len);

View File

@ -2103,9 +2103,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
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).
* We push push the pair of exception/restsub cookies to
* simulate the effects [gosub] or control transfer during
* exception capturing on the stack.
*/
todo = Sprint(&ss->sprinter, exception_cookie);
if (todo < 0 || !PushOff(ss, todo, op))
@ -2139,7 +2139,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
todo = -2;
break;
case JSOP_SETSP:
case JSOP_POPN:
{
uintN newtop, oldtop, i;
@ -2150,10 +2149,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
* The decompiler must match the code generator's model, which
* is why JSOP_FINALLY pushes a cookie that JSOP_RETSUB pops.
*/
newtop = GET_UINT16(pc);
oldtop = ss->top;
if (op == JSOP_POPN)
newtop = oldtop - newtop;
newtop = oldtop - GET_UINT16(pc);
LOCAL_ASSERT(newtop <= oldtop);
todo = -2;
@ -2182,7 +2179,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
* Kill newtop before the end_groupassignment: label by
* retracting/popping early. Control will either jump to
* do_forloop: or do_letheadbody: or else break from our
* case JSOP_SETSP: after the switch (*pc2) below.
* case JSOP_POPN: after the switch (*pc2) below.
*/
if (newtop < oldtop) {
ss->sprinter.offset = GetOff(ss, newtop);
@ -2225,7 +2222,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCK);
js_printf(jp, "\tlet (%s) {\n", rval);
js_printf(jp, "\t}\n");
goto end_setsp;
goto end_popn;
}
todo = SprintCString(&ss->sprinter, rval);
if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
@ -2255,7 +2252,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
*/
if (todo == -2)
js_printf(jp, "\t%s;\n", rval);
end_setsp:
end_popn:
break;
}
#endif
@ -4954,10 +4951,6 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
cs = &js_CodeSpec[op];
oplen = cs->length;
if (op == JSOP_SETSP) {
pcdepth = GET_UINT16(pc);
continue;
}
if (op == JSOP_POPN) {
pcdepth -= GET_UINT16(pc);
continue;

View File

@ -262,7 +262,7 @@ 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)
OPDEF(JSOP_SETSP, 117,"setsp", NULL, 3, 0, 0, 0, JOF_UINT16)
OPDEF(JSOP_UNUSED117, 117,"", NULL, 0, 0, 0, 0, 0)
/*
* ECMA-compliant switch statement ops.

View File

@ -390,6 +390,54 @@ out:
#endif /* JS_HAS_SCRIPT_OBJECT */
/*
* JSTryNoteArray is allocated after script notes and an extra gap to ensure
* that JSTryNoteArray is alligned on sizeof(uint32) boundary, the maximum
* size of JSTryNoteArray.length and JSTryNote fields.
*/
JS_STATIC_ASSERT(sizeof(JSTryNote) == 3 * sizeof(uint32));
JS_STATIC_ASSERT(sizeof(JSTryNoteArray) == 4 * sizeof(uint32));
#define JSTRYNOTE_ALIGNMASK (sizeof(uint32) - 1)
/*
* Calculate the amount of memory required for a script.
*/
static size_t
GetScriptSize(uint32 bytecodeLength, uint32 nsrcnotes, uint32 ntrynotes)
{
size_t size;
size = sizeof(JSScript) +
bytecodeLength * sizeof(jsbytecode) +
nsrcnotes * sizeof(jssrcnote);
if (ntrynotes != 0) {
size += JSTRYNOTE_ALIGNMASK +
offsetof(JSTryNoteArray, notes) +
ntrynotes * sizeof(JSTryNote);
}
return size;
}
static void
InitScriptTryNotes(JSScript *script, uint32 bytecodeLength, uint32 nsrcnotes,
uint32 ntrynotes)
{
size_t offset;
JS_ASSERT(ntrynotes != 0);
offset = sizeof(JSScript) +
bytecodeLength * sizeof(jsbytecode) +
nsrcnotes * sizeof(jssrcnote) +
JSTRYNOTE_ALIGNMASK;
script->trynotes = (JSTryNoteArray *)(((jsword)script + offset) &
~(jsword)JSTRYNOTE_ALIGNMASK);
script->trynotes->length = ntrynotes;
memset(script->trynotes->notes, 0,
ntrynotes * sizeof script->trynotes->notes[0]);
}
#if JS_HAS_XDR
static JSBool
@ -517,12 +565,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
nsrcnotes = PTRDIFF(sn, notes, jssrcnote);
nsrcnotes++; /* room for the terminator */
/* Count the trynotes. */
if (script->trynotes) {
while (script->trynotes[ntrynotes].catchStart)
ntrynotes++;
ntrynotes++; /* room for the end marker */
}
ntrynotes = script->trynotes ? script->trynotes->length : 0;
}
if (!JS_XDRUint32(xdr, &length))
@ -649,53 +692,47 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
script->depth = (uintN)depth;
if (magic < JSXDR_MAGIC_SCRIPT_4) {
size_t scriptSize;
/*
* Argh, we have to reallocate script, copy notes into the extra
* space after the bytecodes, and free the temporary notes vector.
* First, add enough slop to nsrcnotes so we can align the address
* after the srcnotes of the first trynote.
*/
uint32 osrcnotes = nsrcnotes;
if (ntrynotes)
nsrcnotes += JSTRYNOTE_ALIGNMASK;
newscript = (JSScript *) JS_realloc(cx, script,
sizeof(JSScript) +
length * sizeof(jsbytecode) +
nsrcnotes * sizeof(jssrcnote) +
ntrynotes * sizeof(JSTryNote));
scriptSize = GetScriptSize(length, nsrcnotes, ntrynotes);
newscript = (JSScript *) JS_realloc(cx, script, scriptSize);
if (!newscript)
goto error;
*scriptp = script = newscript;
script->code = (jsbytecode *)(script + 1);
script->main = script->code + prologLength;
memcpy(script->code + length, notes, osrcnotes * sizeof(jssrcnote));
memcpy(script->code + length, notes, nsrcnotes * sizeof(jssrcnote));
JS_free(cx, (void *) notes);
notes = NULL;
if (ntrynotes) {
script->trynotes = (JSTryNote *)
((jsword)(SCRIPT_NOTES(script) + nsrcnotes) &
~(jsword)JSTRYNOTE_ALIGNMASK);
memset(script->trynotes, 0, ntrynotes * sizeof(JSTryNote));
}
if (ntrynotes)
InitScriptTryNotes(script, length, nsrcnotes, ntrynotes);
}
}
while (ntrynotes) {
JSTryNote *tn = &script->trynotes[--ntrynotes];
uint32 start = (uint32) tn->start,
catchLength = (uint32) tn->length,
catchStart = (uint32) tn->catchStart;
/*
* We combine kind and stackDepth when serializing as XDR is not
* efficient when serializing small integer types.
*/
JSTryNote *tn = &script->trynotes->notes[--ntrynotes];
uint32 kindAndDepth = ((uint32)tn->kind << 16) | (uint32)tn->stackDepth;
JS_STATIC_ASSERT(sizeof(tn->kind) == sizeof(uint8));
JS_STATIC_ASSERT(sizeof(tn->stackDepth) == sizeof(uint16));
if (!JS_XDRUint32(xdr, &start) ||
!JS_XDRUint32(xdr, &catchLength) ||
!JS_XDRUint32(xdr, &catchStart)) {
if (!JS_XDRUint32(xdr, &kindAndDepth) ||
!JS_XDRUint32(xdr, &tn->start) ||
!JS_XDRUint32(xdr, &tn->length)) {
goto error;
}
tn->start = (ptrdiff_t) start;
tn->length = (ptrdiff_t) catchLength;
tn->catchStart = (ptrdiff_t) catchStart;
tn->kind = (uint8)(kindAndDepth >> 16);
tn->stackDepth = (uint16)kindAndDepth;
}
xdr->script = oldscript;
@ -1345,26 +1382,16 @@ js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 ntrynotes)
{
JSScript *script;
/* Round up source note count to align script->trynotes for its type. */
if (ntrynotes)
nsrcnotes += JSTRYNOTE_ALIGNMASK;
script = (JSScript *) JS_malloc(cx,
sizeof(JSScript) +
length * sizeof(jsbytecode) +
nsrcnotes * sizeof(jssrcnote) +
ntrynotes * sizeof(JSTryNote));
script = (JSScript *) JS_malloc(cx, GetScriptSize(length, nsrcnotes,
ntrynotes));
if (!script)
return NULL;
memset(script, 0, sizeof(JSScript));
script->code = script->main = (jsbytecode *)(script + 1);
script->length = length;
script->version = cx->version;
if (ntrynotes) {
script->trynotes = (JSTryNote *)
((jsword)(SCRIPT_NOTES(script) + nsrcnotes) &
~(jsword)JSTRYNOTE_ALIGNMASK);
memset(script->trynotes, 0, ntrynotes * sizeof(JSTryNote));
}
if (ntrynotes != 0)
InitScriptTryNotes(script, length, nsrcnotes, ntrynotes);
return script;
}
@ -1378,7 +1405,7 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun)
mainLength = CG_OFFSET(cg);
prologLength = CG_PROLOG_OFFSET(cg);
CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes);
CG_COUNT_FINAL_TRYNOTES(cg, ntrynotes);
ntrynotes = (uint32)(cg->tryNext - cg->tryBase);
script = js_NewScript(cx, prologLength + mainLength, nsrcnotes, ntrynotes);
if (!script)
return NULL;
@ -1680,40 +1707,30 @@ js_GetScriptLineExtent(JSScript *script)
#if JS_HAS_GENERATORS
jsbytecode *
js_FindFinallyHandler(JSScript *script, jsbytecode *pc)
JSBool
js_IsInsideTryWithFinally(JSScript *script, jsbytecode *pc)
{
JSTryNote *tn;
ptrdiff_t off;
JSOp op2;
JSTryNote *tn, *tnlimit;
uint32 off;
tn = script->trynotes;
if (!tn)
return NULL;
JS_ASSERT(script->code <= pc);
JS_ASSERT(pc < script->code + script->length);
off = pc - script->main;
if (off < 0)
return NULL;
if (!script->trynotes)
return JS_FALSE;
JS_ASSERT(script->trynotes->length != 0);
JS_ASSERT(tn->catchStart != 0);
tn = script->trynotes->notes;
tnlimit = tn + script->trynotes->length;
off = (uint32)(pc - script->main);
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;
}
if (off - tn->start < tn->length) {
if (tn->kind == JSTN_FINALLY)
return JS_TRUE;
JS_ASSERT(tn->kind == JSTN_CATCH);
}
} while ((++tn)->catchStart != 0);
return NULL;
} while (++tn != tnlimit);
return JS_FALSE;
}
#endif

View File

@ -1,3 +1,4 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=78:
*
@ -49,20 +50,29 @@
JS_BEGIN_EXTERN_C
/*
* Exception handling runtime information.
*
* All fields except length are code offsets relative to the main entry point
* of the script. If script->trynotes is not null, it points to a vector of
* these structs terminated by one with catchStart == 0.
* Type of try note associated with each catch block or finally block.
*/
typedef enum JSTryNoteKind {
JSTN_CATCH,
JSTN_FINALLY
} JSTryNoteKind;
/*
* Exception handling record.
*/
struct JSTryNote {
ptrdiff_t start; /* start of try statement */
ptrdiff_t length; /* count of try statement bytecodes */
ptrdiff_t catchStart; /* start of catch block (0 if end) */
uint8 kind; /* one of JSTryNoteKind */
uint8 padding; /* explicit padding on uint16 boundary */
uint16 stackDepth; /* stack depth upon exception handler entry */
uint32 start; /* start of the try statement relative to
script->main */
uint32 length; /* length of the try statement */
};
#define JSTRYNOTE_GRAIN sizeof(ptrdiff_t)
#define JSTRYNOTE_ALIGNMASK (JSTRYNOTE_GRAIN - 1)
typedef struct JSTryNoteArray {
uint32 length; /* number of notes in the array */
JSTryNote notes[1]; /* the first eleemnt of notes array */
} JSTryNoteArray;
struct JSScript {
jsbytecode *code; /* bytecodes and their immediate operands */
@ -74,7 +84,7 @@ struct JSScript {
const char *filename; /* source filename or null */
uintN lineno; /* base line number of script */
uintN depth; /* maximum stack depth in slots */
JSTryNote *trynotes; /* exception table for this script */
JSTryNoteArray *trynotes; /* exception table for this script */
JSPrincipals *principals; /* principals for this script */
JSObject *object; /* optional Script-class object wrapper */
};
@ -82,29 +92,13 @@ struct JSScript {
/* No need to store script->notes now that it is allocated right after code. */
#define SCRIPT_NOTES(script) ((jssrcnote*)((script)->code+(script)->length))
#define SCRIPT_FIND_CATCH_START(script, pc, catchpc) \
JS_BEGIN_MACRO \
JSTryNote *tn_ = (script)->trynotes; \
jsbytecode *catchpc_ = NULL; \
if (tn_) { \
ptrdiff_t off_ = PTRDIFF(pc, (script)->main, jsbytecode); \
if (off_ >= 0) { \
while ((jsuword)(off_ - tn_->start) >= (jsuword)tn_->length) \
++tn_; \
if (tn_->catchStart) \
catchpc_ = (script)->main + tn_->catchStart; \
} \
} \
catchpc = catchpc_; \
JS_END_MACRO
/*
* Find the innermost finally block that handles the given pc. This is a
* version of SCRIPT_FIND_CATCH_START that ignore catch blocks and is used
* to implement generator.close().
* Check if pc is inside a try block that has finally code. GC calls this to
* check if it is necessary to schedule generator.close() invocation for an
* unreachable generator.
*/
jsbytecode *
js_FindFinallyHandler(JSScript *script, jsbytecode *pc);
JSBool
js_IsInsideTryWithFinally(JSScript *script, jsbytecode *pc);
extern JS_FRIEND_DATA(JSClass) js_ScriptClass;

View File

@ -200,7 +200,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
* before deserialization of bytecode. If the saved version does not match
* the current version, abort deserialization and invalidate the file.
*/
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 11)
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 12)
/*
* Library-private functions.