From 1fcf4af3b3546b2329be3b05d40218c6b8b2a601 Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Fri, 24 Jul 2009 12:01:37 +0200 Subject: [PATCH] bug 505460 - preallocating reserved slots. r=brendan --- js/src/jsapi.cpp | 9 ++---- js/src/jsfun.cpp | 71 ++++++++++++++++++++++++------------------------ js/src/jsfun.h | 4 +-- js/src/jsobj.cpp | 37 +++++++++++++++++++++---- js/src/jsobj.h | 14 ++++++++-- 5 files changed, 82 insertions(+), 53 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 85653753467..b812687f0b5 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4354,13 +4354,10 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent) */ if (FUN_FLAT_CLOSURE(fun)) { JS_ASSERT(funobj->dslots); - JS_ASSERT(JSSLOT_FREE(&js_FunctionClass) == JS_INITIAL_NSLOTS); - - uint32 nslots = JSSLOT_FREE(&js_FunctionClass); - JS_ASSERT(nslots == JS_INITIAL_NSLOTS); - nslots += js_FunctionClass.reserveSlots(cx, clone); - if (!js_AllocSlots(cx, clone, nslots)) + if (!js_EnsureReservedSlots(cx, clone, + fun->countInterpretedReserveSlots())) { return NULL; + } JSUpvarArray *uva = JS_SCRIPT_UPVARS(fun->u.i.script); JS_ASSERT(uva->length <= size_t(clone->dslots[-1])); diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index b8b8201ade4..b31006e29af 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -897,8 +897,10 @@ js_GetCallObject(JSContext *cx, JSStackFrame *fp) } callobj = js_NewObjectWithGivenProto(cx, &js_CallClass, NULL, fp->scopeChain); - if (!callobj) + if (!callobj || + !js_EnsureReservedSlots(cx, callobj, fp->fun->countArgsAndVars())) { return NULL; + } JS_SetPrivate(cx, callobj, fp); JS_ASSERT(fp->fun == GET_FUNCTION_PRIVATE(cx, fp->callee)); @@ -936,7 +938,6 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp) JSBool ok; JSFunction *fun; uintN n; - JSScope *scope; /* * 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)); n = fun->countArgsAndVars(); if (n != 0) { - JS_LOCK_OBJ(cx, callobj); n += JS_INITIAL_NSLOTS; - if (n > STOBJ_NSLOTS(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 + fun->nargs, fp->slots, - fun->u.i.nvars * sizeof(jsval)); - if (scope->owned() && n > scope->freeslot) - scope->freeslot = n; - } - JS_UNLOCK_SCOPE(cx, scope); + JS_LOCK_OBJ(cx, callobj); + memcpy(callobj->dslots, fp->argv, fun->nargs * sizeof(jsval)); + memcpy(callobj->dslots + fun->nargs, fp->slots, + fun->u.i.nvars * sizeof(jsval)); + JS_UNLOCK_OBJ(cx, callobj); } - /* - * 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. - */ + /* Clear private pointers to fp, which is about to go away (js_Invoke). */ if ((fun->flags & JSFUN_LAMBDA) && fun->atom) { 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 fun_reserveSlots(JSContext *cx, JSObject *obj) { - JSFunction *fun; - uint32 nslots; - /* * We use JS_GetPrivate and not GET_FUNCTION_PRIVATE because during * js_InitFunctionClass invocation the function is called before the * private slot of the function object is set. */ - fun = (JSFunction *) JS_GetPrivate(cx, obj); - nslots = 0; - if (fun && FUN_INTERPRETED(fun) && fun->u.i.script) { - if (fun->u.i.nupvars != 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; + JSFunction *fun = (JSFunction *) JS_GetPrivate(cx, obj); + return (fun && FUN_INTERPRETED(fun)) + ? fun->countInterpretedReserveSlots() + : 0; } /* @@ -2509,16 +2504,20 @@ js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain) ? JS_SCRIPT_UPVARS(fun->u.i.script)->length : 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); if (!closure || fun->u.i.nupvars == 0) return closure; - - uint32 nslots = JSSLOT_FREE(&js_FunctionClass); - JS_ASSERT(nslots == JS_INITIAL_NSLOTS); - nslots += fun_reserveSlots(cx, closure); - if (!js_GrowSlots(cx, closure, nslots)) + if (!js_EnsureReservedSlots(cx, closure, + fun->countInterpretedReserveSlots())) { return NULL; + } return closure; } diff --git a/js/src/jsfun.h b/js/src/jsfun.h index cf06ae37f18..55d4fe04bd5 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -162,7 +162,6 @@ struct JSFunction { } u; JSAtom *atom; /* name for diagnostics and decompiling */ -#ifdef __cplusplus bool optimizedClosure() { return FUN_KIND(this) > JSFUN_INTERPRETED; } bool needsWrapper() { return FUN_NULL_CLOSURE(this) && u.i.skipmin != 0; } @@ -180,7 +179,8 @@ struct JSFunction { JS_ASSERT(FUN_INTERPRETED(this)); return countLocalNames() != 0; } -#endif + + uint32 countInterpretedReserveSlots() const; }; /* diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 6b16b191d0e..80bf3a89e96 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2011,6 +2011,9 @@ static JSFunctionSpec object_static_methods[] = { JS_FS_END }; +static bool +AllocSlots(JSContext *cx, JSObject *obj, size_t nslots); + static inline bool 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. */ JS_ASSERT(scope->freeslot >= JSSLOT_PRIVATE); if (scope->freeslot > JS_INITIAL_NSLOTS && - !js_AllocSlots(cx, obj, scope->freeslot)) { + !AllocSlots(cx, obj, scope->freeslot)) { JSScope::destroy(cx, scope); goto bad; } @@ -2600,7 +2603,7 @@ js_PutBlockObject(JSContext *cx, JSBool normalUnwind) if (normalUnwind && count > 1) { --count; 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; else memcpy(obj->dslots, fp->slots + depth + 1, count * sizeof(jsval)); @@ -2976,8 +2979,8 @@ bad: (JS_ASSERT((words) > 1), (words) - 1 + JS_INITIAL_NSLOTS) -bool -js_AllocSlots(JSContext *cx, JSObject *obj, size_t nslots) +static bool +AllocSlots(JSContext *cx, JSObject *obj, size_t nslots) { JS_ASSERT(!obj->dslots); JS_ASSERT(nslots > JS_INITIAL_NSLOTS); @@ -3037,7 +3040,7 @@ js_GrowSlots(JSContext *cx, JSObject *obj, size_t nslots) */ jsval* slots = obj->dslots; if (!slots) - return js_AllocSlots(cx, obj, nslots); + return AllocSlots(cx, obj, nslots); 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 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) nslots += clasp->reserveSlots(cx, obj); JS_ASSERT(slot < nslots); - if (!js_AllocSlots(cx, obj, nslots)) { + if (!AllocSlots(cx, obj, nslots)) { JS_UNLOCK_SCOPE(cx, scope); return JS_FALSE; } diff --git a/js/src/jsobj.h b/js/src/jsobj.h index e0aac4b98ca..b29c7af7bde 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -578,9 +578,6 @@ js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp); extern void js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot); -extern bool -js_AllocSlots(JSContext *cx, JSObject *obj, size_t nslots); - extern bool js_GrowSlots(JSContext *cx, JSObject *obj, size_t nslots); @@ -594,6 +591,17 @@ js_FreeSlots(JSContext *cx, JSObject *obj) 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 js_CheckForStringIndex(jsid id);