Bug 487563 - Crash [@ js_Interpret] (r=mrbkap).

This commit is contained in:
Brendan Eich 2009-04-09 18:44:54 -07:00
parent c05dbc60ec
commit 26a5b7f3fc
7 changed files with 62 additions and 57 deletions

View File

@ -2205,26 +2205,9 @@ js_NewFlatClosure(JSContext *cx, JSFunction *fun)
JSUpvarArray *uva = JS_SCRIPT_UPVARS(fun->u.i.script); JSUpvarArray *uva = JS_SCRIPT_UPVARS(fun->u.i.script);
JS_ASSERT(uva->length <= size_t(closure->dslots[-1])); JS_ASSERT(uva->length <= size_t(closure->dslots[-1]));
for (uint32 i = 0, n = uva->length; i < n; i++) { uintN level = fun->u.i.script->staticLevel;
uint32 cookie = uva->vector[i]; for (uint32 i = 0, n = uva->length; i < n; i++)
closure->dslots[i] = js_GetUpvar(cx, level, uva->vector[i]);
uintN upvarLevel = fun->u.i.script->staticLevel - UPVAR_FRAME_SKIP(cookie);
JS_ASSERT(upvarLevel <= JS_DISPLAY_SIZE);
JSStackFrame *fp2 = cx->display[upvarLevel];
uintN slot = UPVAR_FRAME_SLOT(cookie);
jsval *vp;
if (fp2->fun && slot < fp2->fun->nargs) {
vp = fp2->argv;
} else {
if (fp2->fun)
slot -= fp2->fun->nargs;
JS_ASSERT(slot < fp2->script->nslots);
vp = fp2->slots;
}
closure->dslots[i] = vp[slot];
}
return closure; return closure;
} }

View File

@ -2054,6 +2054,34 @@ js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, jsval *vp, jsval *vp2)
return JS_TRUE; return JS_TRUE;
} }
jsval
js_GetUpvar(JSContext *cx, uintN level, uintN cookie)
{
level -= UPVAR_FRAME_SKIP(cookie);
JS_ASSERT(level < JS_DISPLAY_SIZE);
JSStackFrame *fp = cx->display[level];
JS_ASSERT(fp->script);
uintN slot = UPVAR_FRAME_SLOT(cookie);
jsval *vp;
if (!fp->fun) {
vp = fp->slots + fp->script->nfixed;
} else if (slot < fp->fun->nargs) {
vp = fp->argv;
} else if (slot == CALLEE_UPVAR_SLOT) {
vp = &fp->argv[-2];
slot = 0;
} else {
slot -= fp->fun->nargs;
JS_ASSERT(slot < fp->script->nslots);
vp = fp->slots;
}
return vp[slot];
}
#ifdef DEBUG #ifdef DEBUG
JS_STATIC_INTERPRET JS_REQUIRES_STACK void JS_STATIC_INTERPRET JS_REQUIRES_STACK void
@ -5682,29 +5710,14 @@ js_Interpret(JSContext *cx)
BEGIN_CASE(JSOP_GETUPVAR) BEGIN_CASE(JSOP_GETUPVAR)
BEGIN_CASE(JSOP_CALLUPVAR) BEGIN_CASE(JSOP_CALLUPVAR)
{ {
JSUpvarArray *uva; JSUpvarArray *uva = JS_SCRIPT_UPVARS(script);
uint32 skip;
JSStackFrame *fp2;
index = GET_UINT16(regs.pc); index = GET_UINT16(regs.pc);
uva = JS_SCRIPT_UPVARS(script);
JS_ASSERT(index < uva->length); JS_ASSERT(index < uva->length);
skip = UPVAR_FRAME_SKIP(uva->vector[index]);
fp2 = cx->display[script->staticLevel - skip];
JS_ASSERT(fp2->script);
slot = UPVAR_FRAME_SLOT(uva->vector[index]); rval = js_GetUpvar(cx, script->staticLevel, uva->vector[index]);
if (!fp2->fun) { PUSH_OPND(rval);
vp = fp2->slots + fp2->script->nfixed;
} else if (slot < fp2->fun->nargs) {
vp = fp2->argv;
} else {
slot -= fp2->fun->nargs;
JS_ASSERT(slot < fp2->script->nslots);
vp = fp2->slots;
}
PUSH_OPND(vp[slot]);
if (op == JSOP_CALLUPVAR) if (op == JSOP_CALLUPVAR)
PUSH_OPND(JSVAL_NULL); PUSH_OPND(JSVAL_NULL);
} }

View File

@ -606,6 +606,13 @@ js_OnUnknownMethod(JSContext *cx, jsval *vp);
extern JSBool extern JSBool
js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, jsval *vp, jsval *vp2); js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, jsval *vp, jsval *vp2);
/*
* Given an active context, a static scope level, and an upvar cookie, return
* the value of the upvar.
*/
extern jsval
js_GetUpvar(JSContext *cx, uintN level, uintN cookie);
/* /*
* Opcode tracing helper. When len is not 0, cx->fp->regs->pc[-len] gives the * Opcode tracing helper. When len is not 0, cx->fp->regs->pc[-len] gives the
* previous opcode. * previous opcode.

View File

@ -748,13 +748,15 @@ static inline bool
SetStaticLevel(JSTreeContext *tc, uintN staticLevel) SetStaticLevel(JSTreeContext *tc, uintN staticLevel)
{ {
/* /*
* Reserve staticLevel 0xffff in order to reserve FREE_UPVAR_COOKIE. This * Reserve FREE_STATIC_LEVEL (0xffff) in order to reserve FREE_UPVAR_COOKIE
* is simpler than error-checking every MAKE_UPVAR_COOKIE, and practically * (0xffffffff) and other cookies with that level.
* speaking it leaves more than enough room for upvars. In fact we might *
* want to split cookies with fewer bits for skip and more for slot, but * This is a lot simpler than error-checking every MAKE_UPVAR_COOKIE, and
* only based on evidence. * practically speaking it leaves more than enough room for upvars. In fact
* we might want to split cookie fields giving fewer bits for skip and more
* for slot, but only based on evidence.
*/ */
if (staticLevel >= JS_BITMASK(16)) { if (staticLevel >= FREE_STATIC_LEVEL) {
JS_ReportErrorNumber(tc->compiler->context, js_GetErrorMessage, NULL, JS_ReportErrorNumber(tc->compiler->context, js_GetErrorMessage, NULL,
JSMSG_TOO_DEEP, js_function_str); JSMSG_TOO_DEEP, js_function_str);
return false; return false;
@ -2198,7 +2200,7 @@ LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSTreeContext *tc,
if (atom == funAtom && lambda != 0) { if (atom == funAtom && lambda != 0) {
dn->pn_op = JSOP_CALLEE; dn->pn_op = JSOP_CALLEE;
dn->pn_cookie = MAKE_UPVAR_COOKIE(funtc->staticLevel, 0); dn->pn_cookie = MAKE_UPVAR_COOKIE(funtc->staticLevel, CALLEE_UPVAR_SLOT);
dn->pn_dflags |= PND_BOUND; dn->pn_dflags |= PND_BOUND;
/* /*
@ -2211,7 +2213,7 @@ LeaveFunction(JSParseNode *fn, JSTreeContext *funtc, JSTreeContext *tc,
* code unfairly (see JSCompiler::setFunctionKinds, where this * code unfairly (see JSCompiler::setFunctionKinds, where this
* flag is interpreted in its broader sense, not only to mean * flag is interpreted in its broader sense, not only to mean
* "this function might leak arguments.callee"), we can perhaps * "this function might leak arguments.callee"), we can perhaps
* try to work harder to add a TCF_FUN_CALLS_ITSELF flag and * try to work harder to add a TCF_FUN_LEAKS_ITSELF flag and
* use that more precisely, both here and for unnamed function * use that more precisely, both here and for unnamed function
* expressions. * expressions.
*/ */
@ -7875,7 +7877,6 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
} else if (!afterDot && !(ts->flags & TSF_DESTRUCTURING)) { } else if (!afterDot && !(ts->flags & TSF_DESTRUCTURING)) {
JSAtomListElement *ale = NULL; JSAtomListElement *ale = NULL;
JSTreeContext *tcx = tc; JSTreeContext *tcx = tc;
bool hit_named_lambda = false;
JSDefinition *dn; JSDefinition *dn;
do { do {
@ -7898,10 +7899,9 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
} }
/* If this id names the current lambda's name, we are done. */ /* If this id names the current lambda's name, we are done. */
if ((tc->flags & TCF_IN_FUNCTION) && if ((tcx->flags & TCF_IN_FUNCTION) &&
(tc->fun->flags & JSFUN_LAMBDA) && (tcx->fun->flags & JSFUN_LAMBDA) &&
tc->fun->atom == pn->pn_atom) { tcx->fun->atom == pn->pn_atom) {
hit_named_lambda = true;
break; break;
} }
} while ((tcx = tcx->parent) != NULL); } while ((tcx = tcx->parent) != NULL);
@ -7950,14 +7950,17 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
* a backward definition appears later; see NewBindingNode/Define). * a backward definition appears later; see NewBindingNode/Define).
* *
* (b) If pn names the named function expression whose body we are * (b) If pn names the named function expression whose body we are
* parsing, there's no way an upvar could be referenced here. * parsing, there's no way an upvar above tcx's static level could
* be referenced here. However, we add to upvars anyway, to treat
* the function's name as an upvar in case it is used in a nested
* function.
* *
* (a) is is an optimization to handle forward upvar refs. Without * (a) is is an optimization to handle forward upvar refs. Without
* it, if we add only a lexdep, then inner functions making forward * it, if we add only a lexdep, then inner functions making forward
* refs to upvars will lose track of those upvars as their lexdeps * refs to upvars will lose track of those upvars as their lexdeps
* entries are propagated upward to their parent functions. * entries are propagated upward to their parent functions.
*/ */
if (tcx != tc && !hit_named_lambda) { if (tcx != tc) {
ale = tc->upvars.add(tc->compiler, pn->pn_atom); ale = tc->upvars.add(tc->compiler, pn->pn_atom);
if (!ale) if (!ale)
return NULL; return NULL;

View File

@ -86,6 +86,8 @@ typedef struct JSUpvarArray {
uint32 length; /* count of indexed upvar cookies */ uint32 length; /* count of indexed upvar cookies */
} JSUpvarArray; } JSUpvarArray;
#define CALLEE_UPVAR_SLOT 0xffff
#define FREE_STATIC_LEVEL 0xffff
#define FREE_UPVAR_COOKIE 0xffffffff #define FREE_UPVAR_COOKIE 0xffffffff
#define MAKE_UPVAR_COOKIE(skip,slot) ((skip) << 16 | (slot)) #define MAKE_UPVAR_COOKIE(skip,slot) ((skip) << 16 | (slot))
#define UPVAR_FRAME_SKIP(cookie) ((uint32)(cookie) >> 16) #define UPVAR_FRAME_SKIP(cookie) ((uint32)(cookie) >> 16)

View File

@ -367,9 +367,6 @@ struct InterpState
#ifdef EXECUTE_TREE_TIMER #ifdef EXECUTE_TREE_TIMER
uint64 startTime; uint64 startTime;
#endif #endif
#ifdef DEBUG
bool jsframe_pop_blocks_set_on_entry;
#endif
/* /*
* Used by _FAIL builtins; see jsbuiltins.h. The builtin sets the * Used by _FAIL builtins; see jsbuiltins.h. The builtin sets the

View File

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