Bug 416601: property cache is properly disabled under with statements with generators. r=brendan a1.9=blocking1.9

This commit is contained in:
igor@mir2.org 2008-02-15 03:38:40 -08:00
parent 08cc7ed6d6
commit f86e5a86d2
8 changed files with 256 additions and 280 deletions

View File

@ -898,9 +898,12 @@ js_ReportOutOfMemory(JSContext *cx)
}
/*
* If debugErrorHook is present then we give it a chance to veto
* sending the error on to the regular ErrorReporter.
* If debugErrorHook is present then we give it a chance to veto sending
* the error on to the regular ErrorReporter. We also clear a pending
* exception if any now so the hooks can replace the out-of-memory error
* by a script-catchable exception.
*/
cx->throwing = JS_FALSE;
if (onError) {
JSDebugErrorHook hook = cx->debugHooks->debugErrorHook;
if (hook &&

View File

@ -1325,57 +1325,12 @@ FlushPops(JSContext *cx, JSCodeGenerator *cg, intN *npops)
/*
* Emit additional bytecode(s) for non-local jumps.
*
* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=379758
*/
static JSBool
EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
JSOp *returnop)
EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt)
{
intN depth, npops;
JSStmtInfo *stmt;
ptrdiff_t jmp;
/*
* Return from a try block that has a finally clause or from a for-in loop
* 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) or a for-in loop to ensure that we emit just one
* JSOP_SETRVAL before one or more JSOP_GOSUBs/JSOP_ENDITERs 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/enditers 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 ||
((cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT) &&
STMT_MAYBE_SCOPE(stmt)) ||
stmt->type == STMT_FOR_IN_LOOP) {
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
@ -1394,8 +1349,7 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
FLUSH_POPS();
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
return JS_FALSE;
jmp = EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(*stmt));
if (jmp < 0)
if (EmitBackPatchOp(cx, cg, JSOP_BACKPATCH, &GOSUBS(*stmt)) < 0)
return JS_FALSE;
break;
@ -1455,7 +1409,7 @@ EmitGoto(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
{
intN index;
if (!EmitNonLocalJumpFixup(cx, cg, toStmt, NULL))
if (!EmitNonLocalJumpFixup(cx, cg, toStmt))
return -1;
if (label)
@ -5112,18 +5066,26 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
}
/*
* EmitNonLocalJumpFixup mutates op to JSOP_RETRVAL after emitting a
* JSOP_SETRVAL if there are open try blocks having finally clauses.
* EmitNonLocalJumpFixup may add fixup bytecode to close open try
* blocks having finally clauses and to exit intermingled let blocks.
* 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).
* because we must gosub to those finally 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).
*
* In this case we mutate JSOP_RETURN into JSOP_SETRVAL and add an
* extra JSOP_RETRVAL after the fixups.
*/
op = JSOP_RETURN;
if (!EmitNonLocalJumpFixup(cx, cg, NULL, &op))
top = CG_OFFSET(cg);
if (js_Emit1(cx, cg, JSOP_RETURN) < 0)
return JS_FALSE;
if (js_Emit1(cx, cg, op) < 0)
if (!EmitNonLocalJumpFixup(cx, cg, NULL))
return JS_FALSE;
if (top + JSOP_RETURN_LENGTH != CG_OFFSET(cg)) {
CG_BASE(cg)[top] = JSOP_SETRVAL;
if (js_Emit1(cx, cg, JSOP_RETRVAL) < 0)
return JS_FALSE;
}
break;
#if JS_HAS_GENERATORS

View File

@ -678,8 +678,8 @@ AllocateAfterSP(JSContext *cx, jsval *sp, uintN nslots)
return JS_TRUE;
}
JS_FRIEND_API(jsval *)
js_AllocRawStack(JSContext *cx, uintN nslots, void **markp)
static jsval *
AllocRawStack(JSContext *cx, uintN nslots, void **markp)
{
jsval *sp;
@ -702,8 +702,8 @@ js_AllocRawStack(JSContext *cx, uintN nslots, void **markp)
return sp;
}
JS_FRIEND_API(void)
js_FreeRawStack(JSContext *cx, void *mark)
static void
FreeRawStack(JSContext *cx, void *mark)
{
JS_ARENA_RELEASE(&cx->stackPool, mark);
}
@ -722,7 +722,7 @@ js_AllocStack(JSContext *cx, uintN nslots, void **markp)
}
/* Allocate 2 extra slots for the stack segment header we'll likely need. */
sp = js_AllocRawStack(cx, 2 + nslots, markp);
sp = AllocRawStack(cx, 2 + nslots, markp);
if (!sp)
return NULL;
@ -864,27 +864,6 @@ js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
return obj;
}
/*
* Walk the scope chain looking for block scopes whose locals need to be
* copied from stack slots into object slots before fp goes away.
*/
static JSBool
PutBlockObjects(JSContext *cx, JSStackFrame *fp)
{
JSBool ok;
JSObject *obj;
ok = JS_TRUE;
for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) {
if (OBJ_GET_PRIVATE(cx, obj) != fp)
break;
ok &= js_PutBlockObject(cx, obj);
}
}
return ok;
}
JSBool
js_GetPrimitiveThis(JSContext *cx, jsval *vp, JSClass *clasp, jsval *thisvp)
{
@ -1265,7 +1244,7 @@ have_fun:
*/
if (!AllocateAfterSP(cx, sp, nslots)) {
rootedArgsFlag = 0;
newvp = js_AllocRawStack(cx, 2 + argc + nslots, NULL);
newvp = AllocRawStack(cx, 2 + argc + nslots, NULL);
if (!newvp) {
ok = JS_FALSE;
goto out2;
@ -1315,7 +1294,7 @@ have_fun:
if (nvars) {
if (!AllocateAfterSP(cx, sp, nvars)) {
/* NB: Discontinuity between argv and vars. */
sp = js_AllocRawStack(cx, nvars, NULL);
sp = AllocRawStack(cx, nvars, NULL);
if (!sp) {
ok = JS_FALSE;
goto out2;
@ -1360,9 +1339,6 @@ have_fun:
frame.dormantNext = NULL;
frame.xmlNamespace = NULL;
frame.blockChain = NULL;
#ifdef DEBUG
frame.pcDisabledSave = JS_PROPERTY_CACHE(cx).disabled;
#endif
/* From here on, control must flow through label out: to return. */
cx->fp = &frame;
@ -1584,7 +1560,7 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
if (script->regexpsOffset != 0)
frame.nvars += JS_SCRIPT_REGEXPS(script)->length;
if (frame.nvars != 0) {
frame.vars = js_AllocRawStack(cx, frame.nvars, &mark);
frame.vars = AllocRawStack(cx, frame.nvars, &mark);
if (!frame.vars) {
ok = JS_FALSE;
goto out;
@ -1607,9 +1583,6 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
frame.dormantNext = NULL;
frame.xmlNamespace = NULL;
frame.blockChain = NULL;
#ifdef DEBUG
frame.pcDisabledSave = JS_PROPERTY_CACHE(cx).disabled;
#endif
/*
* Here we wrap the call to js_Interpret with code to (conditionally)
@ -1649,7 +1622,7 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
hook(cx, &frame, JS_FALSE, &ok, hookData);
}
if (mark)
js_FreeRawStack(cx, mark);
FreeRawStack(cx, mark);
cx->fp = oldfp;
if (oldfp && oldfp != down) {
@ -2078,11 +2051,83 @@ LeaveWith(JSContext *cx)
withobj = cx->fp->scopeChain;
JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass);
JS_ASSERT(OBJ_GET_PRIVATE(cx, withobj) == cx->fp);
JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0);
cx->fp->scopeChain = OBJ_GET_PARENT(cx, withobj);
JS_SetPrivate(cx, withobj, NULL);
js_EnablePropertyCache(cx);
}
static JSClass *
IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth)
{
JSClass *clasp;
clasp = OBJ_GET_CLASS(cx, obj);
if ((clasp == &js_WithClass || clasp == &js_BlockClass) &&
OBJ_GET_PRIVATE(cx, obj) == cx->fp &&
OBJ_BLOCK_DEPTH(cx, obj) >= stackDepth) {
return clasp;
}
return NULL;
}
static jsint
CountWithBlocks(JSContext *cx, JSStackFrame *fp)
{
jsint n;
JSObject *obj;
JSClass *clasp;
n = 0;
for (obj = fp->scopeChain;
(clasp = IsActiveWithOrBlock(cx, obj, 0)) != NULL;
obj = OBJ_GET_PARENT(cx, obj)) {
if (clasp == &js_WithClass)
++n;
}
return n;
}
/*
* Unwind block and scope chains to match the given depth. The function sets
* fp->sp on return to stackDepth.
*/
static JSBool
UnwindScope(JSContext *cx, JSStackFrame *fp, jsint stackDepth,
JSBool normalUnwind)
{
JSObject *obj;
JSClass *clasp;
JS_ASSERT(stackDepth >= 0);
JS_ASSERT(fp->spbase + stackDepth <= fp->sp);
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) < stackDepth)
break;
}
fp->blockChain = obj;
for (;;) {
obj = fp->scopeChain;
clasp = IsActiveWithOrBlock(cx, obj, stackDepth);
if (!clasp)
break;
if (clasp == &js_BlockClass) {
/* Don't fail until after we've updated all stacks. */
normalUnwind &= js_PutBlockObject(cx, obj, normalUnwind);
fp->scopeChain = OBJ_GET_PARENT(cx, obj);
} else {
LeaveWith(cx);
}
}
fp->sp = fp->spbase + stackDepth;
return normalUnwind;
}
/*
* Threaded interpretation via computed goto appears to be well-supported by
* GCC 3 and higher. IBM's C compiler when run with the right options (e.g.,
@ -2306,7 +2351,6 @@ js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result)
#if JS_HAS_GETTER_SETTER
JSPropertyOp getter, setter;
#endif
int stackDummy;
#ifdef __GNUC__
# define JS_EXTENSION __extension__
@ -2352,6 +2396,9 @@ js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result)
# define EMPTY_CASE(OP) BEGIN_CASE(OP) END_CASE(OP)
#endif
/* Check for too deep a C stack. */
JS_CHECK_RECURSION(cx, return JS_FALSE);
*result = JSVAL_VOID;
rt = cx->runtime;
@ -2391,26 +2438,6 @@ js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result)
JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_FunctionClass); \
JS_END_MACRO
/*
* Optimized Get and SetVersion for proper script language versioning.
*
* If any native method or JSClass/JSObjectOps hook calls js_SetVersion
* and changes cx->version, the effect will "stick" and we will stop
* maintaining currentVersion. This is relied upon by testsuites, for
* the most part -- web browsers select version before compiling and not
* at run-time.
*/
currentVersion = (JSVersion) script->version;
originalVersion = (JSVersion) cx->version;
if (currentVersion != originalVersion)
js_SetVersion(cx, currentVersion);
#ifdef __GNUC__
flags = 0; /* suppress gcc warnings */
id = 0;
atom = NULL;
#endif
/*
* Prepare to call a user-supplied branch handler, and abort the script
* if it returns false.
@ -2446,13 +2473,27 @@ js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result)
LOAD_INTERRUPT_HANDLER(cx);
/* Check for too much js_Interpret nesting, or too deep a C stack. */
/*
* Optimized Get and SetVersion for proper script language versioning.
*
* If any native method or JSClass/JSObjectOps hook calls js_SetVersion
* and changes cx->version, the effect will "stick" and we will stop
* maintaining currentVersion. This is relied upon by testsuites, for
* the most part -- web browsers select version before compiling and not
* at run-time.
*/
currentVersion = (JSVersion) script->version;
originalVersion = (JSVersion) cx->version;
if (currentVersion != originalVersion)
js_SetVersion(cx, currentVersion);
++cx->interpLevel;
if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
js_ReportOverRecursed(cx);
ok = JS_FALSE;
goto out2;
}
#ifdef DEBUG
fp->pcDisabledSave = JS_PROPERTY_CACHE(cx).disabled;
#endif
/* From this point the control must flow through the label exit. */
ok = JS_TRUE;
/*
* Allocate operand and pc stack slots for the script's worst-case depth,
@ -2461,36 +2502,40 @@ js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result)
*/
depth = (jsint) script->depth;
if (JS_LIKELY(!fp->spbase)) {
newsp = js_AllocRawStack(cx, (uintN)(2 * depth), &mark);
ASSERT_NOT_THROWING(cx);
newsp = AllocRawStack(cx, (uintN)(2 * depth), &mark);
if (!newsp) {
mark = NULL;
ok = JS_FALSE;
goto out2;
goto exit;
}
JS_ASSERT(mark);
sp = newsp + depth;
fp->spbase = sp;
SAVE_SP(fp);
} else {
JS_ASSERT(fp->flags & JSFRAME_GENERATOR);
sp = fp->sp;
JS_ASSERT(JS_UPTRDIFF(sp, fp->spbase) <= depth * sizeof(jsval));
newsp = fp->spbase - depth;
mark = NULL;
}
JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);
JS_PROPERTY_CACHE(cx).disabled += CountWithBlocks(cx, fp);
/*
* To support generator_throw and to catch ignored exceptions, fail right
* away if cx->throwing is set. If no exception is pending, null obj in
* case a callable object is being sent into a yield expression, and the
* yield's result is invoked.
*/
ok = !cx->throwing;
if (!ok) {
/*
* To support generator_throw and to catch ignored exceptions,
* fail if cx->throwing is set.
*/
if (cx->throwing) {
#ifdef DEBUG_NOT_THROWING
if (cx->exception != JSVAL_ARETURN) {
printf("JS INTERPRETER CALLED WITH PENDING EXCEPTION %lx\n",
(unsigned long) cx->exception);
}
if (cx->exception != JSVAL_ARETURN) {
printf("JS INTERPRETER CALLED WITH PENDING EXCEPTION %lx\n",
(unsigned long) cx->exception);
}
#endif
goto out;
ok = JS_FALSE;
goto out;
}
}
#if JS_THREADED_INTERP
@ -2520,7 +2565,7 @@ interrupt:
break;
case JSTRAP_RETURN:
fp->rval = rval;
goto out;
goto forced_return;
case JSTRAP_THROW:
cx->throwing = JS_TRUE;
cx->exception = rval;
@ -2579,7 +2624,7 @@ interrupt:
break;
case JSTRAP_RETURN:
fp->rval = rval;
goto out;
goto forced_return;
case JSTRAP_THROW:
cx->throwing = JS_TRUE;
cx->exception = rval;
@ -2594,9 +2639,6 @@ interrupt:
#endif /* !JS_THREADED_INTERP */
BEGIN_CASE(JSOP_STOP)
goto out;
EMPTY_CASE(JSOP_NOP)
EMPTY_CASE(JSOP_GROUP)
@ -2683,6 +2725,7 @@ interrupt:
/* FALL THROUGH */
BEGIN_CASE(JSOP_RETRVAL) /* fp->rval already set */
BEGIN_CASE(JSOP_STOP)
ASSERT_NOT_THROWING(cx);
if (inlineCallCount)
inline_return:
@ -2690,17 +2733,9 @@ interrupt:
JSInlineFrame *ifp = (JSInlineFrame *) fp;
void *hookData = ifp->hookData;
/*
* If fp has blocks on its scope chain, home their locals now,
* before calling any debugger hook, and before freeing stack.
* This matches the order of block putting and hook calling in
* the "out-of-line" return code at the bottom of js_Interpret
* and in js_Invoke.
*/
if (fp->flags & JSFRAME_POP_BLOCKS) {
SAVE_SP_AND_PC(fp);
ok &= PutBlockObjects(cx, fp);
}
JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled == fp->pcDisabledSave);
JS_ASSERT(!fp->blockChain);
JS_ASSERT(!IsActiveWithOrBlock(cx, fp->scopeChain, 0));
if (hookData) {
JSInterpreterHook hook = cx->debugHooks->callHook;
@ -2770,8 +2805,9 @@ interrupt:
len = JSOP_CALL_LENGTH;
DO_NEXT_OP(len);
}
goto out;
}
goto out;
goto exit;
BEGIN_CASE(JSOP_DEFAULT)
(void) POP();
@ -4456,7 +4492,8 @@ interrupt:
newifp->frame.xmlNamespace = NULL;
newifp->frame.blockChain = NULL;
#ifdef DEBUG
newifp->frame.pcDisabledSave = JS_PROPERTY_CACHE(cx).disabled;
newifp->frame.pcDisabledSave =
JS_PROPERTY_CACHE(cx).disabled;
#endif
newifp->rvp = rvp;
newifp->mark = newmark;
@ -4525,7 +4562,7 @@ interrupt:
script = fp->script;
depth = (jsint) script->depth;
atoms = script->atomMap.vector;
js_FreeRawStack(cx, newmark);
FreeRawStack(cx, newmark);
ok = JS_FALSE;
goto out;
}
@ -5153,7 +5190,7 @@ interrupt:
DO_OP();
case JSTRAP_RETURN:
fp->rval = rval;
goto out;
goto forced_return;
case JSTRAP_THROW:
cx->throwing = JS_TRUE;
cx->exception = rval;
@ -5968,7 +6005,7 @@ interrupt:
break;
case JSTRAP_RETURN:
fp->rval = rval;
goto out;
goto forced_return;
case JSTRAP_THROW:
cx->throwing = JS_TRUE;
cx->exception = rval;
@ -6334,7 +6371,7 @@ interrupt:
* its locals to their property slots.
*/
SAVE_SP_AND_PC(fp);
ok = js_PutBlockObject(cx, obj);
ok = js_PutBlockObject(cx, obj, JS_TRUE);
if (!ok)
goto out;
}
@ -6352,9 +6389,8 @@ interrupt:
: fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp);
*chainp = OBJ_GET_PARENT(cx, obj);
JS_ASSERT(chainp != &fp->blockChain ||
!*chainp ||
OBJ_GET_CLASS(cx, *chainp) == &js_BlockClass);
JS_ASSERT_IF(fp->blockChain,
OBJ_GET_CLASS(cx, fp->blockChain) == &js_BlockClass);
}
END_CASE(JSOP_LEAVEBLOCK)
@ -6425,11 +6461,13 @@ interrupt:
obj = js_NewGenerator(cx, fp);
if (!obj) {
ok = JS_FALSE;
} else {
JS_ASSERT(!fp->callobj && !fp->argsobj);
fp->rval = OBJECT_TO_JSVAL(obj);
goto out;
}
goto out;
JS_ASSERT(!fp->callobj && !fp->argsobj);
fp->rval = OBJECT_TO_JSVAL(obj);
if (inlineCallCount != 0)
goto inline_return;
goto exit;
BEGIN_CASE(JSOP_YIELD)
ASSERT_NOT_THROWING(cx);
@ -6444,7 +6482,9 @@ interrupt:
fp->flags |= JSFRAME_YIELDING;
pc += JSOP_YIELD_LENGTH;
SAVE_SP_AND_PC(fp);
goto out;
JS_PROPERTY_CACHE(cx).disabled -= CountWithBlocks(cx, fp);
JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);
goto exit;
BEGIN_CASE(JSOP_ARRAYPUSH)
slot = GET_UINT16(pc);
@ -6580,7 +6620,7 @@ interrupt:
}
#endif /* !JS_THREADED_INTERP */
out:
out:
JS_ASSERT((size_t)(pc - script->code) < script->length);
if (!ok && cx->throwing) {
/* An exception has been raised. */
@ -6588,9 +6628,7 @@ out:
JSTryNote *tn, *tnlimit;
uint32 offset;
/*
* Call debugger throw hook if set (XXX thread safety?).
*/
/* Call debugger throw hook if set. */
handler = cx->debugHooks->throwHook;
if (handler) {
SAVE_SP_AND_PC(fp);
@ -6598,12 +6636,12 @@ out:
cx->debugHooks->throwHookData)) {
case JSTRAP_ERROR:
cx->throwing = JS_FALSE;
goto no_catch;
goto forced_return;
case JSTRAP_RETURN:
ok = JS_TRUE;
cx->throwing = JS_FALSE;
fp->rval = rval;
goto no_catch;
ok = JS_TRUE;
goto forced_return;
case JSTRAP_THROW:
cx->exception = rval;
case JSTRAP_CONTINUE:
@ -6647,55 +6685,24 @@ out:
if (tn->stackDepth > sp - fp->spbase)
continue;
/*
* Prepare to execute the try note handler and unwind the block
* and scope chains until we match the stack depth of the try
* note. Note that we set sp after we call js_PutBlockObject to
* avoid potential GC hazards.
*/
ok = JS_TRUE;
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);
if (clasp != &js_WithClass && clasp != &js_BlockClass)
break;
if (OBJ_GET_PRIVATE(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);
fp->scopeChain = OBJ_GET_PARENT(cx, obj);
} else {
LeaveWith(cx);
}
}
sp = fp->spbase + i;
/*
* Set pc to the first bytecode after the the try note to point
* to the beginning of catch or finally or to [enditer] closing
* the for-in loop.
*
* We do it before checking for ok so, when failing during the
* scope recovery, we restart the exception search with the
* updated stack and pc avoiding calling the handler again.
*/
offset = tn->start + tn->length;
pc = (script)->main + offset;
if (!ok)
pc = (script)->main + tn->start + tn->length;
SAVE_SP_AND_PC(fp);
ok = UnwindScope(cx, fp, tn->stackDepth, JS_TRUE);
JS_ASSERT(fp->sp == fp->spbase + tn->stackDepth);
sp = fp->sp;
if (!ok) {
/*
* Restart the handler search with updated pc and stack depth
* to properly notify the debugger.
*/
goto out;
}
switch (tn->kind) {
case JSTN_CATCH:
@ -6769,34 +6776,35 @@ out:
#endif
}
forced_return:
/*
* We either have an error or uncaught exception or were forced to return
* from a trap handler.
*/
SAVE_SP_AND_PC(fp);
/* ok must stay false even when UnwindScope returns true. */
ok &= UnwindScope(cx, fp, 0, ok || cx->throwing);
JS_ASSERT(fp->sp == fp->spbase);
sp = fp->sp;
if (inlineCallCount)
goto inline_return;
exit:
/*
* At this point we are inevitably leaving an interpreted function or a
* top-level script, and returning to one of:
* (a) an inline call in the same js_Interpret;
* (b) an "out of line" call made through js_Invoke;
* (c) a js_Execute activation;
* (d) a generator (SendToGenerator, jsiter.c).
* (a) an "out of line" call made through js_Invoke;
* (b) a js_Execute activation;
* (c) a generator (SendToGenerator, jsiter.c).
*
* We must not be in an inline frame since the check above ensures that
* for the error case and for a normal return the code jumps directly to
* parent's frame pc.
*/
if (!ok) {
for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
clasp = OBJ_GET_CLASS(cx, obj);
if (clasp != &js_WithClass && clasp != &js_BlockClass)
break;
if (OBJ_GET_PRIVATE(cx, obj) != fp || OBJ_BLOCK_DEPTH(cx, obj) < 0)
break;
if (clasp == &js_WithClass)
js_EnablePropertyCache(cx);
}
}
JS_ASSERT_IF(mark, JS_PROPERTY_CACHE(cx).disabled == fp->pcDisabledSave);
/*
* Check whether control fell off the end of a lightweight function, or an
* exception thrown under such a function was not caught by it. If so, go
* to the inline code under JSOP_RETURN.
*/
if (inlineCallCount)
goto inline_return;
JS_ASSERT(inlineCallCount == 0);
JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled == fp->pcDisabledSave);
/*
* Reset sp before freeing stack slots, because our caller may GC soon.
@ -6804,26 +6812,20 @@ out:
* Restore the previous frame's execution state.
*/
if (JS_LIKELY(mark != NULL)) {
/* If fp has blocks on its scope chain, home their locals now. */
if (fp->flags & JSFRAME_POP_BLOCKS) {
SAVE_SP_AND_PC(fp);
ok &= PutBlockObjects(cx, fp);
}
JS_ASSERT(!fp->blockChain);
JS_ASSERT(!IsActiveWithOrBlock(cx, fp->scopeChain, 0));
JS_ASSERT(!(fp->flags & JSFRAME_GENERATOR));
fp->sp = fp->spbase;
fp->spbase = NULL;
js_FreeRawStack(cx, mark);
} else {
SAVE_SP(fp);
FreeRawStack(cx, mark);
}
out2:
if (cx->version == currentVersion && currentVersion != originalVersion)
js_SetVersion(cx, originalVersion);
cx->interpLevel--;
return ok;
atom_not_defined:
atom_not_defined:
{
const char *printable;

View File

@ -360,10 +360,10 @@ js_ComputeThis(JSContext *cx, jsval *argv);
* NB: js_Invoke requires that cx is currently running JS (i.e., that cx->fp
* is non-null), and that vp points to the callee, |this| parameter, and
* actual arguments of the call. [vp .. vp + 2 + argc) must belong to the last
* JS stack segment that js_AllocStack or js_AllocRawStack allocated. The
* function may use the space available after vp + 2 + argc in the stack
* segment for temporaries so the caller should not use that space for values
* that must be preserved across the call.
* JS stack segment that js_AllocStack allocated. The function may use the
* space available after vp + 2 + argc in the stack segment for temporaries,
* so the caller should not use that space for values that must be preserved
* across the call.
*/
extern JS_FRIEND_API(JSBool)
js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags);

View File

@ -1922,33 +1922,44 @@ js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent,
* the prototype's scope harder!
*/
JSBool
js_PutBlockObject(JSContext *cx, JSObject *obj)
js_PutBlockObject(JSContext *cx, JSObject *obj, JSBool normalUnwind)
{
JSStackFrame *fp;
uintN depth, slot;
JSScopeProperty *sprop;
fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
JS_ASSERT(fp);
depth = OBJ_BLOCK_DEPTH(cx, obj);
for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) {
if (sprop->getter != js_BlockClass.getProperty)
continue;
if (!(sprop->flags & SPROP_HAS_SHORTID))
continue;
slot = depth + (uintN)sprop->shortid;
JS_ASSERT(slot < fp->script->depth);
if (!js_DefineNativeProperty(cx, obj, sprop->id,
fp->spbase[slot], NULL, NULL,
JSPROP_ENUMERATE | JSPROP_PERMANENT,
SPROP_HAS_SHORTID, sprop->shortid,
NULL)) {
JS_SetPrivate(cx, obj, NULL);
return JS_FALSE;
if (normalUnwind) {
fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
JS_ASSERT(fp);
depth = OBJ_BLOCK_DEPTH(cx, obj);
for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) {
if (sprop->getter != js_BlockClass.getProperty)
continue;
if (!(sprop->flags & SPROP_HAS_SHORTID))
continue;
slot = depth + (uintN)sprop->shortid;
JS_ASSERT(slot < fp->script->depth);
if (!js_DefineNativeProperty(cx, obj, sprop->id,
fp->spbase[slot], NULL, NULL,
JSPROP_ENUMERATE | JSPROP_PERMANENT,
SPROP_HAS_SHORTID, sprop->shortid,
NULL)) {
/*
* Stop adding properties if we failed due to out-of-memory or
* other quit-asap errors.
*/
if (!cx->throwing) {
normalUnwind = JS_FALSE;
goto out;
}
}
}
}
return JS_SetPrivate(cx, obj, NULL);
out:
/* We must clear the private slot even with errors. */
JS_SetPrivate(cx, obj, NULL);
return normalUnwind;
}
static JSBool

View File

@ -350,7 +350,7 @@ js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent,
JSStackFrame *fp);
extern JSBool
js_PutBlockObject(JSContext *cx, JSObject *obj);
js_PutBlockObject(JSContext *cx, JSObject *obj, JSBool normalUnwind);
struct JSSharpObjectMap {
jsrefcount depth;

View File

@ -202,7 +202,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 - 19)
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 20)
/*
* Library-private functions.

View File

@ -66,7 +66,6 @@ js_Emit1 ; 63012
JS_ContextIterator ; 57847
JS_GetInstancePrivate ; 57817
JS_HashTableRawRemove ; 57057
js_AllocRawStack ; 54181
js_Invoke ; 53568
js_FindProperty ; 53150
JS_GetFrameScript ; 51395
@ -123,7 +122,6 @@ js_DestroyScope ; 17198
JS_GetStringLength ; 16306
js_PopStatementCG ; 15418
JS_GetFrameAnnotation ; 14949
js_FreeRawStack ; 14032
js_Interpret ; 14032
js_TransferScopeLock ; 13899
JS_ResolveStandardClass ; 13645