bug 505460 - preallocating reserved slots. r=brendan

This commit is contained in:
Igor Bukanov 2009-07-24 12:01:37 +02:00
parent a590e2793e
commit 1fcf4af3b3
5 changed files with 82 additions and 53 deletions

View File

@ -4354,13 +4354,10 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
*/ */
if (FUN_FLAT_CLOSURE(fun)) { if (FUN_FLAT_CLOSURE(fun)) {
JS_ASSERT(funobj->dslots); JS_ASSERT(funobj->dslots);
JS_ASSERT(JSSLOT_FREE(&js_FunctionClass) == JS_INITIAL_NSLOTS); if (!js_EnsureReservedSlots(cx, clone,
fun->countInterpretedReserveSlots())) {
uint32 nslots = JSSLOT_FREE(&js_FunctionClass);
JS_ASSERT(nslots == JS_INITIAL_NSLOTS);
nslots += js_FunctionClass.reserveSlots(cx, clone);
if (!js_AllocSlots(cx, clone, nslots))
return NULL; return NULL;
}
JSUpvarArray *uva = JS_SCRIPT_UPVARS(fun->u.i.script); JSUpvarArray *uva = JS_SCRIPT_UPVARS(fun->u.i.script);
JS_ASSERT(uva->length <= size_t(clone->dslots[-1])); JS_ASSERT(uva->length <= size_t(clone->dslots[-1]));

View File

@ -897,8 +897,10 @@ js_GetCallObject(JSContext *cx, JSStackFrame *fp)
} }
callobj = js_NewObjectWithGivenProto(cx, &js_CallClass, NULL, fp->scopeChain); callobj = js_NewObjectWithGivenProto(cx, &js_CallClass, NULL, fp->scopeChain);
if (!callobj) if (!callobj ||
!js_EnsureReservedSlots(cx, callobj, fp->fun->countArgsAndVars())) {
return NULL; return NULL;
}
JS_SetPrivate(cx, callobj, fp); JS_SetPrivate(cx, callobj, fp);
JS_ASSERT(fp->fun == GET_FUNCTION_PRIVATE(cx, fp->callee)); JS_ASSERT(fp->fun == GET_FUNCTION_PRIVATE(cx, fp->callee));
@ -936,7 +938,6 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
JSBool ok; JSBool ok;
JSFunction *fun; JSFunction *fun;
uintN n; uintN n;
JSScope *scope;
/* /*
* Since for a call object all fixed slots happen to be taken, we can copy * Since for a call object all fixed slots happen to be taken, we can copy
@ -965,26 +966,15 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
JS_ASSERT(fun == GetCallObjectFunction(callobj)); JS_ASSERT(fun == GetCallObjectFunction(callobj));
n = fun->countArgsAndVars(); n = fun->countArgsAndVars();
if (n != 0) { if (n != 0) {
JS_LOCK_OBJ(cx, callobj);
n += JS_INITIAL_NSLOTS; n += JS_INITIAL_NSLOTS;
if (n > STOBJ_NSLOTS(callobj)) JS_LOCK_OBJ(cx, callobj);
ok &= js_GrowSlots(cx, callobj, n);
scope = OBJ_SCOPE(callobj);
if (ok) {
memcpy(callobj->dslots, fp->argv, fun->nargs * sizeof(jsval)); memcpy(callobj->dslots, fp->argv, fun->nargs * sizeof(jsval));
memcpy(callobj->dslots + fun->nargs, fp->slots, memcpy(callobj->dslots + fun->nargs, fp->slots,
fun->u.i.nvars * sizeof(jsval)); fun->u.i.nvars * sizeof(jsval));
if (scope->owned() && n > scope->freeslot) JS_UNLOCK_OBJ(cx, callobj);
scope->freeslot = n;
}
JS_UNLOCK_SCOPE(cx, scope);
} }
/* /* Clear private pointers to fp, which is about to go away (js_Invoke). */
* Clear private pointers to fp, which is about to go away (js_Invoke).
* Do this last because js_GetProperty calls above need to follow the
* call object's private slot to find fp.
*/
if ((fun->flags & JSFUN_LAMBDA) && fun->atom) { if ((fun->flags & JSFUN_LAMBDA) && fun->atom) {
JSObject *env = STOBJ_GET_PARENT(callobj); JSObject *env = STOBJ_GET_PARENT(callobj);
@ -1847,26 +1837,31 @@ fun_finalize(JSContext *cx, JSObject *obj)
} }
} }
uint32
JSFunction::countInterpretedReserveSlots() const
{
JS_ASSERT(FUN_INTERPRETED(this));
uint32 nslots = (u.i.nupvars == 0)
? 0
: JS_SCRIPT_UPVARS(u.i.script)->length;
if (u.i.script->regexpsOffset != 0)
nslots += JS_SCRIPT_REGEXPS(u.i.script)->length;
return nslots;
}
static uint32 static uint32
fun_reserveSlots(JSContext *cx, JSObject *obj) fun_reserveSlots(JSContext *cx, JSObject *obj)
{ {
JSFunction *fun;
uint32 nslots;
/* /*
* We use JS_GetPrivate and not GET_FUNCTION_PRIVATE because during * We use JS_GetPrivate and not GET_FUNCTION_PRIVATE because during
* js_InitFunctionClass invocation the function is called before the * js_InitFunctionClass invocation the function is called before the
* private slot of the function object is set. * private slot of the function object is set.
*/ */
fun = (JSFunction *) JS_GetPrivate(cx, obj); JSFunction *fun = (JSFunction *) JS_GetPrivate(cx, obj);
nslots = 0; return (fun && FUN_INTERPRETED(fun))
if (fun && FUN_INTERPRETED(fun) && fun->u.i.script) { ? fun->countInterpretedReserveSlots()
if (fun->u.i.nupvars != 0) : 0;
nslots = JS_SCRIPT_UPVARS(fun->u.i.script)->length;
if (fun->u.i.script->regexpsOffset != 0)
nslots += JS_SCRIPT_REGEXPS(fun->u.i.script)->length;
}
return nslots;
} }
/* /*
@ -2509,16 +2504,20 @@ js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
? JS_SCRIPT_UPVARS(fun->u.i.script)->length ? JS_SCRIPT_UPVARS(fun->u.i.script)->length
: 0) == fun->u.i.nupvars); : 0) == fun->u.i.nupvars);
/*
* Assert that fun->countInterpretedReserveSlots returns 0 when
* fun->u.i.nupvars is zero.
*/
JS_ASSERT(fun->u.i.script->regexpsOffset == 0);
JSObject *closure = js_CloneFunctionObject(cx, fun, scopeChain); JSObject *closure = js_CloneFunctionObject(cx, fun, scopeChain);
if (!closure || fun->u.i.nupvars == 0) if (!closure || fun->u.i.nupvars == 0)
return closure; return closure;
if (!js_EnsureReservedSlots(cx, closure,
uint32 nslots = JSSLOT_FREE(&js_FunctionClass); fun->countInterpretedReserveSlots())) {
JS_ASSERT(nslots == JS_INITIAL_NSLOTS);
nslots += fun_reserveSlots(cx, closure);
if (!js_GrowSlots(cx, closure, nslots))
return NULL; return NULL;
}
return closure; return closure;
} }

View File

@ -162,7 +162,6 @@ struct JSFunction {
} u; } u;
JSAtom *atom; /* name for diagnostics and decompiling */ JSAtom *atom; /* name for diagnostics and decompiling */
#ifdef __cplusplus
bool optimizedClosure() { return FUN_KIND(this) > JSFUN_INTERPRETED; } bool optimizedClosure() { return FUN_KIND(this) > JSFUN_INTERPRETED; }
bool needsWrapper() { return FUN_NULL_CLOSURE(this) && u.i.skipmin != 0; } bool needsWrapper() { return FUN_NULL_CLOSURE(this) && u.i.skipmin != 0; }
@ -180,7 +179,8 @@ struct JSFunction {
JS_ASSERT(FUN_INTERPRETED(this)); JS_ASSERT(FUN_INTERPRETED(this));
return countLocalNames() != 0; return countLocalNames() != 0;
} }
#endif
uint32 countInterpretedReserveSlots() const;
}; };
/* /*

View File

@ -2011,6 +2011,9 @@ static JSFunctionSpec object_static_methods[] = {
JS_FS_END JS_FS_END
}; };
static bool
AllocSlots(JSContext *cx, JSObject *obj, size_t nslots);
static inline bool static inline bool
InitScopeForObject(JSContext* cx, JSObject* obj, JSObject* proto, JSObjectOps* ops) InitScopeForObject(JSContext* cx, JSObject* obj, JSObject* proto, JSObjectOps* ops)
{ {
@ -2032,7 +2035,7 @@ InitScopeForObject(JSContext* cx, JSObject* obj, JSObject* proto, JSObjectOps* o
/* Let JSScope::create set freeslot so as to reserve slots. */ /* Let JSScope::create set freeslot so as to reserve slots. */
JS_ASSERT(scope->freeslot >= JSSLOT_PRIVATE); JS_ASSERT(scope->freeslot >= JSSLOT_PRIVATE);
if (scope->freeslot > JS_INITIAL_NSLOTS && if (scope->freeslot > JS_INITIAL_NSLOTS &&
!js_AllocSlots(cx, obj, scope->freeslot)) { !AllocSlots(cx, obj, scope->freeslot)) {
JSScope::destroy(cx, scope); JSScope::destroy(cx, scope);
goto bad; goto bad;
} }
@ -2600,7 +2603,7 @@ js_PutBlockObject(JSContext *cx, JSBool normalUnwind)
if (normalUnwind && count > 1) { if (normalUnwind && count > 1) {
--count; --count;
JS_LOCK_OBJ(cx, obj); JS_LOCK_OBJ(cx, obj);
if (!js_AllocSlots(cx, obj, JS_INITIAL_NSLOTS + count)) if (!AllocSlots(cx, obj, JS_INITIAL_NSLOTS + count))
normalUnwind = JS_FALSE; normalUnwind = JS_FALSE;
else else
memcpy(obj->dslots, fp->slots + depth + 1, count * sizeof(jsval)); memcpy(obj->dslots, fp->slots + depth + 1, count * sizeof(jsval));
@ -2976,8 +2979,8 @@ bad:
(JS_ASSERT((words) > 1), (words) - 1 + JS_INITIAL_NSLOTS) (JS_ASSERT((words) > 1), (words) - 1 + JS_INITIAL_NSLOTS)
bool static bool
js_AllocSlots(JSContext *cx, JSObject *obj, size_t nslots) AllocSlots(JSContext *cx, JSObject *obj, size_t nslots)
{ {
JS_ASSERT(!obj->dslots); JS_ASSERT(!obj->dslots);
JS_ASSERT(nslots > JS_INITIAL_NSLOTS); JS_ASSERT(nslots > JS_INITIAL_NSLOTS);
@ -3037,7 +3040,7 @@ js_GrowSlots(JSContext *cx, JSObject *obj, size_t nslots)
*/ */
jsval* slots = obj->dslots; jsval* slots = obj->dslots;
if (!slots) if (!slots)
return js_AllocSlots(cx, obj, nslots); return AllocSlots(cx, obj, nslots);
size_t oslots = size_t(slots[-1]); size_t oslots = size_t(slots[-1]);
@ -3076,6 +3079,28 @@ js_ShrinkSlots(JSContext *cx, JSObject *obj, size_t nslots)
} }
} }
bool
js_EnsureReservedSlots(JSContext *cx, JSObject *obj, size_t nreserved)
{
JS_ASSERT(OBJ_IS_NATIVE(obj));
JS_ASSERT(!obj->dslots);
uintN nslots = JSSLOT_FREE(STOBJ_GET_CLASS(obj)) + nreserved;
if (nslots > STOBJ_NSLOTS(obj) && !AllocSlots(cx, obj, nslots))
return false;
JSScope *scope = OBJ_SCOPE(obj);
if (scope->owned()) {
#ifdef JS_THREADSAFE
JS_ASSERT(scope->title.ownercx->thread == cx->thread);
#endif
JS_ASSERT(scope->freeslot == JSSLOT_FREE(STOBJ_GET_CLASS(obj)));
if (scope->freeslot < nslots)
scope->freeslot = nslots;
}
return true;
}
extern JSBool extern JSBool
js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp) js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp)
{ {
@ -5863,7 +5888,7 @@ js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
if (clasp->reserveSlots) if (clasp->reserveSlots)
nslots += clasp->reserveSlots(cx, obj); nslots += clasp->reserveSlots(cx, obj);
JS_ASSERT(slot < nslots); JS_ASSERT(slot < nslots);
if (!js_AllocSlots(cx, obj, nslots)) { if (!AllocSlots(cx, obj, nslots)) {
JS_UNLOCK_SCOPE(cx, scope); JS_UNLOCK_SCOPE(cx, scope);
return JS_FALSE; return JS_FALSE;
} }

View File

@ -578,9 +578,6 @@ js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp);
extern void extern void
js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot); js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot);
extern bool
js_AllocSlots(JSContext *cx, JSObject *obj, size_t nslots);
extern bool extern bool
js_GrowSlots(JSContext *cx, JSObject *obj, size_t nslots); js_GrowSlots(JSContext *cx, JSObject *obj, size_t nslots);
@ -594,6 +591,17 @@ js_FreeSlots(JSContext *cx, JSObject *obj)
js_ShrinkSlots(cx, obj, 0); js_ShrinkSlots(cx, obj, 0);
} }
/*
* Ensure that the object has at least JSCLASS_RESERVED_SLOTS(clasp)+nreserved
* slots. The function can be called only for native objects just created with
* js_NewObject or its forms. In particular, the object should not be shared
* between threads and its dslots array must be null. nreserved must match the
* value that JSClass.reserveSlots (if any) would return after the object is
* fully initialized.
*/
bool
js_EnsureReservedSlots(JSContext *cx, JSObject *obj, size_t nreserved);
extern jsid extern jsid
js_CheckForStringIndex(jsid id); js_CheckForStringIndex(jsid id);