mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-23 18:26:15 +00:00
bug 540805 - using reference counting only for empty scopes. r=jorendorff
This commit is contained in:
parent
01a8412479
commit
42951409df
@ -1345,7 +1345,7 @@ js_MakeArraySlow(JSContext *cx, JSObject *obj)
|
||||
return JS_TRUE;
|
||||
|
||||
out_bad:
|
||||
JSScope::destroy(cx, scope);
|
||||
scope->destroy(cx);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
|
@ -240,12 +240,12 @@ js_AddProperty(JSContext* cx, JSObject* obj, JSScopeProperty* sprop)
|
||||
goto exit_trace;
|
||||
JS_ASSERT(sprop->parent == scope->lastProperty());
|
||||
|
||||
if (scope->owned()) {
|
||||
JS_ASSERT(!scope->hasProperty(sprop));
|
||||
} else {
|
||||
if (scope->isSharedEmpty()) {
|
||||
scope = js_GetMutableScope(cx, obj);
|
||||
if (!scope)
|
||||
goto exit_trace;
|
||||
} else {
|
||||
JS_ASSERT(!scope->hasProperty(sprop));
|
||||
}
|
||||
|
||||
if (!scope->table) {
|
||||
|
@ -531,6 +531,9 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
|
||||
ok = js_InitRuntimeNumberState(cx);
|
||||
if (ok)
|
||||
ok = js_InitRuntimeStringState(cx);
|
||||
if (ok)
|
||||
ok = JSScope::initRuntimeState(cx);
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_EndRequest(cx);
|
||||
#endif
|
||||
@ -733,7 +736,7 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
|
||||
JS_BeginRequest(cx);
|
||||
#endif
|
||||
|
||||
/* Unlock and clear GC things held by runtime pointers. */
|
||||
JSScope::finishRuntimeState(cx);
|
||||
js_FinishRuntimeNumberState(cx);
|
||||
js_FinishRuntimeStringState(cx);
|
||||
|
||||
|
@ -813,6 +813,8 @@ struct JSRuntime {
|
||||
JSBackgroundThread *deallocatorThread;
|
||||
#endif
|
||||
|
||||
JSEmptyScope *emptyBlockScope;
|
||||
|
||||
/*
|
||||
* Various metering fields are defined at the end of JSRuntime. In this
|
||||
* way there is no need to recompile all the code that refers to other
|
||||
|
@ -1674,7 +1674,7 @@ JS_GetObjectTotalSize(JSContext *cx, JSObject *obj)
|
||||
}
|
||||
if (OBJ_IS_NATIVE(obj)) {
|
||||
scope = OBJ_SCOPE(obj);
|
||||
if (scope->owned()) {
|
||||
if (!scope->isSharedEmpty()) {
|
||||
nbytes += sizeof *scope;
|
||||
nbytes += SCOPE_CAPACITY(scope) * sizeof(JSScopeProperty *);
|
||||
}
|
||||
|
@ -2600,8 +2600,13 @@ FinalizeObject(JSContext *cx, JSObject *obj, unsigned thingKind)
|
||||
jsdtrace_object_finalize(obj);
|
||||
#endif
|
||||
|
||||
if (JS_LIKELY(OBJ_IS_NATIVE(obj)))
|
||||
OBJ_SCOPE(obj)->drop(cx, obj);
|
||||
if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {
|
||||
JSScope *scope = OBJ_SCOPE(obj);
|
||||
if (scope->isSharedEmpty())
|
||||
static_cast<JSEmptyScope *>(scope)->dropFromGC(cx);
|
||||
else
|
||||
scope->destroy(cx);
|
||||
}
|
||||
if (obj->hasSlotsArray())
|
||||
obj->freeSlotsArray(cx);
|
||||
}
|
||||
|
@ -284,7 +284,7 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
|
||||
* that on the third and subsequent iterations the cache will
|
||||
* be hit because the shape is no longer updated.
|
||||
*/
|
||||
JS_ASSERT(scope->owned());
|
||||
JS_ASSERT(!scope->isSharedEmpty());
|
||||
if (sprop->parent) {
|
||||
kshape = sprop->parent->shape;
|
||||
} else {
|
||||
|
@ -1268,17 +1268,18 @@ js_UnlockTitle(JSContext *cx, JSTitle *title)
|
||||
* dropped the last reference to oldtitle.
|
||||
*/
|
||||
void
|
||||
js_TransferTitle(JSContext *cx, JSTitle *oldtitle, JSTitle *newtitle)
|
||||
js_DropAllEmptyScopeLocks(JSContext *cx, JSScope *scope)
|
||||
{
|
||||
JS_ASSERT(JS_IS_TITLE_LOCKED(cx, newtitle));
|
||||
JS_ASSERT(!CX_OWNS_SCOPE_TITLE(cx,scope));
|
||||
JS_ASSERT(scope->isSharedEmpty());
|
||||
JS_ASSERT(JS_IS_TITLE_LOCKED(cx, &scope->title));
|
||||
|
||||
/*
|
||||
* If the last reference to oldtitle went away, newtitle needs no lock
|
||||
* state update.
|
||||
* Shared empty scope cannot be sealed so we do not need to deal with
|
||||
* cx->lockedSealedTitle.
|
||||
*/
|
||||
if (!oldtitle)
|
||||
return;
|
||||
JS_ASSERT(JS_IS_TITLE_LOCKED(cx, oldtitle));
|
||||
JS_ASSERT(!scope->sealed());
|
||||
JS_ASSERT(cx->lockedSealedTitle != &scope->title);
|
||||
|
||||
/*
|
||||
* Special case in js_LockTitle and js_UnlockTitle for the GC calling
|
||||
@ -1289,46 +1290,9 @@ js_TransferTitle(JSContext *cx, JSTitle *oldtitle, JSTitle *newtitle)
|
||||
if (CX_THREAD_IS_RUNNING_GC(cx))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Special case in js_LockObj and js_UnlockTitle for locking the sealed
|
||||
* scope of an object that owns that scope (the prototype or mutated obj
|
||||
* for which OBJ_SCOPE(obj)->object == obj), and unlocking it.
|
||||
*/
|
||||
JS_ASSERT(cx->lockedSealedTitle != newtitle);
|
||||
if (cx->lockedSealedTitle == oldtitle) {
|
||||
JS_ASSERT(newtitle->ownercx == cx ||
|
||||
(!newtitle->ownercx && newtitle->u.count == 1));
|
||||
cx->lockedSealedTitle = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If oldtitle is single-threaded, there's nothing to do.
|
||||
*/
|
||||
if (oldtitle->ownercx) {
|
||||
JS_ASSERT(oldtitle->ownercx == cx);
|
||||
JS_ASSERT(newtitle->ownercx == cx ||
|
||||
(!newtitle->ownercx && newtitle->u.count == 1));
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We transfer oldtitle->u.count only if newtitle is not single-threaded.
|
||||
* Flow unwinds from here through some number of JS_UNLOCK_TITLE and/or
|
||||
* JS_UNLOCK_OBJ macro calls, which will decrement newtitle->u.count only
|
||||
* if they find newtitle->ownercx != cx.
|
||||
*/
|
||||
if (newtitle->ownercx != cx) {
|
||||
JS_ASSERT(!newtitle->ownercx);
|
||||
newtitle->u.count = oldtitle->u.count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset oldtitle's lock state so that it is completely unlocked.
|
||||
*/
|
||||
LOGIT(oldtitle, '0');
|
||||
oldtitle->u.count = 0;
|
||||
ThinUnlock(&oldtitle->lock, CX_THINLOCK_ID(cx));
|
||||
LOGIT(&scope->title, '0');
|
||||
scope->title.u.count = 0;
|
||||
ThinUnlock(&scope->title.lock, CX_THINLOCK_ID(cx));
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -154,11 +154,13 @@ struct JSTitle {
|
||||
* are for optimizations above the JSObjectOps layer, under which object locks
|
||||
* normally hide.
|
||||
*/
|
||||
#define JS_LOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->title.ownercx == (cx)) \
|
||||
#define CX_OWNS_SCOPE_TITLE(cx,scope) ((scope)->title.ownercx == (cx))
|
||||
|
||||
#define JS_LOCK_OBJ(cx,obj) (CX_OWNS_SCOPE_TITLE(cx, OBJ_SCOPE(obj)) \
|
||||
? (void)0 \
|
||||
: (js_LockObj(cx, obj), \
|
||||
JS_SET_OBJ_INFO(obj,__FILE__,__LINE__)))
|
||||
#define JS_UNLOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->title.ownercx == (cx)) \
|
||||
#define JS_UNLOCK_OBJ(cx,obj) (CX_OWNS_SCOPE_TITLE(cx, OBJ_SCOPE(obj)) \
|
||||
? (void)0 : js_UnlockObj(cx, obj))
|
||||
|
||||
/*
|
||||
@ -182,9 +184,12 @@ struct JSTitle {
|
||||
#define JS_LOCK_SCOPE(cx,scope) JS_LOCK_TITLE(cx,&(scope)->title)
|
||||
#define JS_UNLOCK_SCOPE(cx,scope) JS_UNLOCK_TITLE(cx,&(scope)->title)
|
||||
|
||||
#define JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope) \
|
||||
js_TransferTitle(cx, &scope->title, &newscope->title)
|
||||
|
||||
#define JS_DROP_ALL_EMPTY_SCOPE_LOCKS(cx,scope) \
|
||||
JS_BEGIN_MACRO \
|
||||
JS_ASSERT((scope)->isSharedEmpty()); \
|
||||
if (!CX_OWNS_SCOPE_TITLE(cx, scope)) \
|
||||
js_DropAllEmptyScopeLocks(cx, scope); \
|
||||
JS_END_MACRO
|
||||
|
||||
extern void js_Lock(JSContext *cx, JSThinLock *tl);
|
||||
extern void js_Unlock(JSContext *cx, JSThinLock *tl);
|
||||
@ -199,7 +204,7 @@ extern void js_LockTitle(JSContext *cx, JSTitle *title);
|
||||
extern void js_UnlockTitle(JSContext *cx, JSTitle *title);
|
||||
extern int js_SetupLocks(int,int);
|
||||
extern void js_CleanupLocks();
|
||||
extern void js_TransferTitle(JSContext *, JSTitle *, JSTitle *);
|
||||
extern void js_DropAllEmptyScopeLocks(JSContext *cx, JSScope *scope);
|
||||
extern JS_FRIEND_API(jsval)
|
||||
js_GetSlotThreadSafe(JSContext *, JSObject *, uint32);
|
||||
extern void js_SetSlotThreadSafe(JSContext *, JSObject *, uint32, jsval);
|
||||
@ -259,6 +264,7 @@ extern void js_SetScopeInfo(JSScope *scope, const char *file, int line);
|
||||
|
||||
#define JS_LOCK_RUNTIME(rt) ((void)0)
|
||||
#define JS_UNLOCK_RUNTIME(rt) ((void)0)
|
||||
#define CX_OWNS_SCOPE_TITLE(cx,obj) true
|
||||
#define JS_LOCK_OBJ(cx,obj) ((void)0)
|
||||
#define JS_UNLOCK_OBJ(cx,obj) ((void)0)
|
||||
#define JS_LOCK_OBJ_IF_SHAPE(cx,obj,shape) (OBJ_SHAPE(obj) == (shape))
|
||||
@ -266,7 +272,7 @@ extern void js_SetScopeInfo(JSScope *scope, const char *file, int line);
|
||||
#define JS_LOCK_OBJ_VOID(cx,obj,e) (e)
|
||||
#define JS_LOCK_SCOPE(cx,scope) ((void)0)
|
||||
#define JS_UNLOCK_SCOPE(cx,scope) ((void)0)
|
||||
#define JS_TRANSFER_SCOPE_LOCK(c,o,n) ((void)0)
|
||||
#define JS_DROP_ALL_EMPTY_SCOPE_LOCKS(cx,scope) ((void)0)
|
||||
|
||||
#define JS_IS_RUNTIME_LOCKED(rt) 1
|
||||
#define JS_IS_OBJ_LOCKED(cx,obj) 1
|
||||
|
@ -2914,7 +2914,7 @@ InitScopeForObject(JSContext* cx, JSObject* obj, JSObject* proto, JSObjectOps* o
|
||||
JS_ASSERT(scope->freeslot >= JSSLOT_PRIVATE);
|
||||
if (scope->freeslot > JS_INITIAL_NSLOTS &&
|
||||
!AllocSlots(cx, obj, scope->freeslot)) {
|
||||
JSScope::destroy(cx, scope);
|
||||
scope->destroy(cx);
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
@ -3116,7 +3116,7 @@ js_NewInstance(JSContext *cx, JSClass *clasp, JSObject *ctor)
|
||||
if (scope->title.ownercx != cx)
|
||||
return NULL;
|
||||
#endif
|
||||
if (!scope->owned()) {
|
||||
if (scope->isSharedEmpty()) {
|
||||
scope = js_GetMutableScope(cx, ctor);
|
||||
if (!scope)
|
||||
return NULL;
|
||||
@ -3428,20 +3428,13 @@ js_CloneBlockObject(JSContext *cx, JSObject *proto, JSStackFrame *fp)
|
||||
if (!clone)
|
||||
return NULL;
|
||||
|
||||
JSScope *scope = OBJ_SCOPE(proto);
|
||||
scope->hold();
|
||||
JS_ASSERT(!scope->owned());
|
||||
clone->map = scope;
|
||||
|
||||
clone->classword = jsuword(&js_BlockClass);
|
||||
clone->setProto(proto);
|
||||
clone->setParent(NULL); // caller's responsibility
|
||||
clone->setPrivate(fp);
|
||||
/* The caller sets parent on its own. */
|
||||
clone->init(&js_BlockClass, proto, NULL, reinterpret_cast<jsval>(fp));
|
||||
clone->fslots[JSSLOT_BLOCK_DEPTH] = proto->fslots[JSSLOT_BLOCK_DEPTH];
|
||||
JS_ASSERT(scope->freeslot == JSSLOT_BLOCK_DEPTH + 1);
|
||||
for (uint32 i = JSSLOT_BLOCK_DEPTH + 1; i < JS_INITIAL_NSLOTS; ++i)
|
||||
clone->fslots[i] = JSVAL_VOID;
|
||||
clone->dslots = NULL;
|
||||
|
||||
JS_ASSERT(cx->runtime->emptyBlockScope->freeslot == JSSLOT_BLOCK_DEPTH + 1);
|
||||
clone->map = cx->runtime->emptyBlockScope;
|
||||
cx->runtime->emptyBlockScope->hold();
|
||||
JS_ASSERT(OBJ_IS_CLONED_BLOCK(clone));
|
||||
return clone;
|
||||
}
|
||||
@ -3689,11 +3682,6 @@ js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (xdr->mode == JSXDR_DECODE) {
|
||||
/* Do as the parser does and make this block scope shareable. */
|
||||
OBJ_SCOPE(obj)->object = NULL;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3996,7 +3984,7 @@ js_EnsureReservedSlots(JSContext *cx, JSObject *obj, size_t nreserved)
|
||||
return false;
|
||||
|
||||
JSScope *scope = OBJ_SCOPE(obj);
|
||||
if (scope->owned()) {
|
||||
if (!scope->isSharedEmpty()) {
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_ASSERT(scope->title.ownercx->thread == cx->thread);
|
||||
#endif
|
||||
@ -4748,12 +4736,12 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
||||
* "too bad!" case.
|
||||
*/
|
||||
scope = OBJ_SCOPE(obj2);
|
||||
if (scope->owned())
|
||||
if (!scope->isSharedEmpty())
|
||||
sprop = scope->lookup(id);
|
||||
}
|
||||
if (sprop) {
|
||||
JS_ASSERT(scope == OBJ_SCOPE(obj2));
|
||||
JS_ASSERT(scope->owned());
|
||||
JS_ASSERT(!scope->isSharedEmpty());
|
||||
obj = obj2;
|
||||
} else if (obj2 != obj) {
|
||||
if (OBJ_IS_NATIVE(obj2))
|
||||
@ -4773,7 +4761,7 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
|
||||
JS_LOCK_OBJ(cx, obj);
|
||||
JS_ASSERT(OBJ_IS_NATIVE(obj));
|
||||
scope = OBJ_SCOPE(obj);
|
||||
if (scope->owned())
|
||||
if (!scope->isSharedEmpty())
|
||||
sprop = scope->lookup(id);
|
||||
}
|
||||
|
||||
@ -4861,11 +4849,11 @@ js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult,
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, pobj) == clasp);
|
||||
if (clasp == &js_BlockClass) {
|
||||
/*
|
||||
* Block instances on the scope chain are immutable and
|
||||
* always share their scope with compile-time prototypes.
|
||||
* A block instance on the scope chain is immutable and
|
||||
* the compile-time prototype provides all its properties.
|
||||
*/
|
||||
JS_ASSERT(pobj == obj);
|
||||
JS_ASSERT(protoIndex == 0);
|
||||
JS_ASSERT(pobj == obj->getProto());
|
||||
JS_ASSERT(protoIndex == 1);
|
||||
} else {
|
||||
/* Call and DeclEnvClass objects have no prototypes. */
|
||||
JS_ASSERT(!OBJ_GET_PROTO(cx, obj));
|
||||
@ -6612,7 +6600,7 @@ js_TraceObject(JSTracer *trc, JSObject *obj)
|
||||
|
||||
JSContext *cx = trc->context;
|
||||
JSScope *scope = OBJ_SCOPE(obj);
|
||||
if (scope->owned() && IS_GC_MARKING_TRACER(trc)) {
|
||||
if (!scope->isSharedEmpty() && IS_GC_MARKING_TRACER(trc)) {
|
||||
/*
|
||||
* Check whether we should shrink the object's slots. Skip this check
|
||||
* if the scope is shared, since for Block objects and flat closures
|
||||
@ -6653,7 +6641,7 @@ js_TraceObject(JSTracer *trc, JSObject *obj)
|
||||
* above.
|
||||
*/
|
||||
uint32 nslots = STOBJ_NSLOTS(obj);
|
||||
if (scope->owned() && scope->freeslot < nslots)
|
||||
if (!scope->isSharedEmpty() && scope->freeslot < nslots)
|
||||
nslots = scope->freeslot;
|
||||
JS_ASSERT(nslots >= JSSLOT_START(clasp));
|
||||
|
||||
@ -6679,7 +6667,7 @@ js_Clear(JSContext *cx, JSObject *obj)
|
||||
*/
|
||||
JS_LOCK_OBJ(cx, obj);
|
||||
scope = OBJ_SCOPE(obj);
|
||||
if (scope->owned()) {
|
||||
if (!scope->isSharedEmpty()) {
|
||||
/* Now that we're done using scope->lastProp/table, clear scope. */
|
||||
scope->clear(cx);
|
||||
|
||||
@ -6772,7 +6760,7 @@ js_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v)
|
||||
* use STOBJ_NSLOTS(obj) rather than rely on freeslot.
|
||||
*/
|
||||
JSScope *scope = OBJ_SCOPE(obj);
|
||||
if (scope->owned() && slot >= scope->freeslot)
|
||||
if (!scope->isSharedEmpty() && slot >= scope->freeslot)
|
||||
scope->freeslot = slot + 1;
|
||||
|
||||
STOBJ_SET_SLOT(obj, slot, v);
|
||||
@ -7028,7 +7016,7 @@ js_DumpObject(JSObject *obj)
|
||||
|
||||
fprintf(stderr, "slots:\n");
|
||||
reservedEnd = i + JSCLASS_RESERVED_SLOTS(clasp);
|
||||
slots = (OBJ_IS_NATIVE(obj) && OBJ_SCOPE(obj)->owned())
|
||||
slots = (OBJ_IS_NATIVE(obj) && !OBJ_SCOPE(obj)->isSharedEmpty())
|
||||
? OBJ_SCOPE(obj)->freeslot
|
||||
: STOBJ_NSLOTS(obj);
|
||||
for (; i < slots; i++) {
|
||||
|
@ -608,7 +608,7 @@ extern JSBool
|
||||
js_DefineBlockVariable(JSContext *cx, JSObject *obj, jsid id, intN index);
|
||||
|
||||
#define OBJ_BLOCK_COUNT(cx,obj) \
|
||||
(OBJ_SCOPE(obj)->entryCount)
|
||||
(OBJ_SCOPE(OBJ_IS_CLONED_BLOCK(obj) ? obj->getProto() : obj)->entryCount)
|
||||
#define OBJ_BLOCK_DEPTH(cx,obj) \
|
||||
JSVAL_TO_INT(STOBJ_GET_SLOT(obj, JSSLOT_BLOCK_DEPTH))
|
||||
#define OBJ_SET_BLOCK_DEPTH(cx,obj,depth) \
|
||||
|
@ -70,7 +70,7 @@ JSObject::unbrand(JSContext *cx)
|
||||
if (OBJ_IS_NATIVE(this)) {
|
||||
JS_LOCK_OBJ(cx, this);
|
||||
JSScope *scope = OBJ_SCOPE(this);
|
||||
if (!scope->owned()) {
|
||||
if (scope->isSharedEmpty()) {
|
||||
scope = js_GetMutableScope(cx, this);
|
||||
if (!scope) {
|
||||
JS_UNLOCK_OBJ(cx, this);
|
||||
|
@ -1763,7 +1763,7 @@ BEGIN_CASE(JSOP_SETMETHOD)
|
||||
|
||||
/* The cache entry doesn't apply. vshape mismatch. */
|
||||
checkForAdd = false;
|
||||
} else if (scope->owned()) {
|
||||
} else if (!scope->isSharedEmpty()) {
|
||||
if (sprop == scope->lastProperty() || scope->hasProperty(sprop)) {
|
||||
fast_set_propcache_hit:
|
||||
PCMETER(cache->pchits++);
|
||||
@ -3475,7 +3475,7 @@ BEGIN_CASE(JSOP_INITMETHOD)
|
||||
}
|
||||
JS_ASSERT(sprop2 == sprop);
|
||||
} else {
|
||||
JS_ASSERT(scope->owned());
|
||||
JS_ASSERT(!scope->isSharedEmpty());
|
||||
scope->extend(cx, sprop);
|
||||
}
|
||||
|
||||
|
@ -3313,12 +3313,6 @@ PopStatement(JSTreeContext *tc)
|
||||
continue;
|
||||
tc->decls.remove(tc->compiler, atom);
|
||||
}
|
||||
|
||||
/*
|
||||
* The block scope will not be modified again. It may be shared. Clear
|
||||
* scope->object to make scope->owned() false.
|
||||
*/
|
||||
scope->object = NULL;
|
||||
}
|
||||
js_PopStatement(tc);
|
||||
}
|
||||
|
@ -101,6 +101,7 @@ typedef struct JSParseNode JSParseNode;
|
||||
typedef struct JSPropCacheEntry JSPropCacheEntry;
|
||||
typedef struct JSProperty JSProperty;
|
||||
typedef struct JSSharpObjectMap JSSharpObjectMap;
|
||||
typedef struct JSEmptyScope JSEmptyScope;
|
||||
typedef struct JSTempValueRooter JSTempValueRooter;
|
||||
typedef struct JSThread JSThread;
|
||||
typedef struct JSThreadData JSThreadData;
|
||||
|
@ -97,7 +97,7 @@ js_GetMutableScope(JSContext *cx, JSObject *obj)
|
||||
|
||||
scope = OBJ_SCOPE(obj);
|
||||
JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope));
|
||||
if (scope->owned())
|
||||
if (!scope->isSharedEmpty())
|
||||
return scope;
|
||||
|
||||
/*
|
||||
@ -108,7 +108,10 @@ js_GetMutableScope(JSContext *cx, JSObject *obj)
|
||||
newscope = JSScope::create(cx, scope->ops, obj->getClass(), obj, scope->shape);
|
||||
if (!newscope)
|
||||
return NULL;
|
||||
JS_LOCK_SCOPE(cx, newscope);
|
||||
|
||||
/* The newly allocated scope is single-threaded and, as such, is locked. */
|
||||
JS_ASSERT(CX_OWNS_SCOPE_TITLE(cx, newscope));
|
||||
JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, newscope));
|
||||
obj->map = newscope;
|
||||
|
||||
JS_ASSERT(newscope->freeslot == JSSLOT_FREE(STOBJ_GET_CLASS(obj)));
|
||||
@ -126,10 +129,8 @@ js_GetMutableScope(JSContext *cx, JSObject *obj)
|
||||
if (newscope->freeslot < freeslot)
|
||||
newscope->freeslot = freeslot;
|
||||
}
|
||||
JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);
|
||||
JS_ATOMIC_DECREMENT(&scope->nrefs);
|
||||
if (scope->nrefs == 0)
|
||||
JSScope::destroy(cx, scope);
|
||||
JS_DROP_ALL_EMPTY_SCOPE_LOCKS(cx, scope);
|
||||
static_cast<JSEmptyScope *>(scope)->drop(cx);
|
||||
return newscope;
|
||||
}
|
||||
|
||||
@ -214,7 +215,6 @@ JSScope::create(JSContext *cx, const JSObjectOps *ops, JSClass *clasp,
|
||||
if (!scope)
|
||||
return NULL;
|
||||
|
||||
scope->nrefs = 1;
|
||||
scope->freeslot = JSSLOT_FREE(clasp);
|
||||
scope->flags = cx->runtime->gcRegenShapesScopeFlag;
|
||||
scope->initMinimal(cx, shape);
|
||||
@ -227,31 +227,24 @@ JSScope::create(JSContext *cx, const JSObjectOps *ops, JSClass *clasp,
|
||||
return scope;
|
||||
}
|
||||
|
||||
JSEmptyScope *
|
||||
JSScope::createEmptyScope(JSContext *cx, JSClass *clasp)
|
||||
JSEmptyScope::JSEmptyScope(JSContext *cx, const JSObjectOps *ops,
|
||||
JSClass *clasp)
|
||||
: JSScope(ops, NULL), clasp(clasp)
|
||||
{
|
||||
JS_ASSERT(!emptyScope);
|
||||
|
||||
JSEmptyScope *scope = cx->create<JSEmptyScope>(ops, clasp);
|
||||
if (!scope)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* This scope holds a reference to the new empty scope. Our only caller,
|
||||
* getEmptyScope, also promises to incref on behalf of its caller.
|
||||
*/
|
||||
scope->nrefs = 2;
|
||||
scope->freeslot = JSSLOT_FREE(clasp);
|
||||
scope->flags = OWN_SHAPE | cx->runtime->gcRegenShapesScopeFlag;
|
||||
scope->initMinimal(cx, js_GenerateShape(cx, false));
|
||||
nrefs = 2;
|
||||
freeslot = JSSLOT_FREE(clasp);
|
||||
flags = OWN_SHAPE | cx->runtime->gcRegenShapesScopeFlag;
|
||||
initMinimal(cx, js_GenerateShape(cx, false));
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
js_InitTitle(cx, &scope->title);
|
||||
js_InitTitle(cx, &title);
|
||||
#endif
|
||||
JS_RUNTIME_METER(cx->runtime, liveScopes);
|
||||
JS_RUNTIME_METER(cx->runtime, totalScopes);
|
||||
emptyScope = scope;
|
||||
return scope;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -262,19 +255,46 @@ JSScope::createEmptyScope(JSContext *cx, JSClass *clasp)
|
||||
#endif
|
||||
|
||||
void
|
||||
JSScope::destroy(JSContext *cx, JSScope *scope)
|
||||
JSScope::destroy(JSContext *cx)
|
||||
{
|
||||
#ifdef JS_THREADSAFE
|
||||
js_FinishTitle(cx, &scope->title);
|
||||
js_FinishTitle(cx, &title);
|
||||
#endif
|
||||
if (scope->table)
|
||||
cx->free(scope->table);
|
||||
if (scope->emptyScope)
|
||||
scope->emptyScope->drop(cx, NULL);
|
||||
if (table)
|
||||
cx->free(table);
|
||||
|
||||
LIVE_SCOPE_METER(cx, cx->runtime->liveScopeProps -= scope->entryCount);
|
||||
/*
|
||||
* The scopes containing empty scopes are only destroyed from the GC
|
||||
* thread.
|
||||
*/
|
||||
if (emptyScope)
|
||||
emptyScope->dropFromGC(cx);
|
||||
|
||||
LIVE_SCOPE_METER(cx, cx->runtime->liveScopeProps -= entryCount);
|
||||
JS_RUNTIME_UNMETER(cx->runtime, liveScopes);
|
||||
cx->free(scope);
|
||||
cx->free(this);
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
JSScope::initRuntimeState(JSContext *cx)
|
||||
{
|
||||
cx->runtime->emptyBlockScope = cx->create<JSEmptyScope>(cx, &js_ObjectOps,
|
||||
&js_BlockClass);
|
||||
JS_ASSERT(cx->runtime->emptyBlockScope->nrefs == 2);
|
||||
cx->runtime->emptyBlockScope->nrefs = 1;
|
||||
return !!cx->runtime->emptyBlockScope;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
JSScope::finishRuntimeState(JSContext *cx)
|
||||
{
|
||||
JSRuntime *rt = cx->runtime;
|
||||
if (rt->emptyBlockScope) {
|
||||
rt->emptyBlockScope->drop(cx);
|
||||
rt->emptyBlockScope = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
JS_STATIC_ASSERT(sizeof(JSHashNumber) == 4);
|
||||
|
113
js/src/jsscope.h
113
js/src/jsscope.h
@ -210,9 +210,7 @@ struct JSScope : public JSObjectMap
|
||||
JSTitle title; /* lock state */
|
||||
#endif
|
||||
JSObject *object; /* object that owns this scope */
|
||||
jsrefcount nrefs; /* count of all referencing objects */
|
||||
uint32 freeslot; /* index of next free slot in object */
|
||||
JSEmptyScope *emptyScope; /* cache for getEmptyScope below */
|
||||
uint8 flags; /* flags, see below */
|
||||
int8 hashShift; /* multiplicative hash shift */
|
||||
|
||||
@ -220,6 +218,7 @@ struct JSScope : public JSObjectMap
|
||||
uint32 entryCount; /* number of entries in table */
|
||||
uint32 removedCount; /* removed entry sentinels in table */
|
||||
JSScopeProperty **table; /* table of ptrs to shared tree nodes */
|
||||
JSEmptyScope *emptyScope; /* cache for getEmptyScope below */
|
||||
|
||||
/*
|
||||
* A little information hiding for scope->lastProp, in case it ever becomes
|
||||
@ -259,14 +258,17 @@ struct JSScope : public JSObjectMap
|
||||
inline void updateShape(JSContext *cx);
|
||||
inline void updateFlags(const JSScopeProperty *sprop);
|
||||
|
||||
protected:
|
||||
void initMinimal(JSContext *cx, uint32 newShape);
|
||||
|
||||
private:
|
||||
bool createTable(JSContext *cx, bool report);
|
||||
bool changeTable(JSContext *cx, int change);
|
||||
void reportReadOnlyScope(JSContext *cx);
|
||||
void generateOwnShape(JSContext *cx);
|
||||
JSScopeProperty **searchTable(jsid id, bool adding);
|
||||
inline JSScopeProperty **search(jsid id, bool adding);
|
||||
JSEmptyScope *createEmptyScope(JSContext *cx, JSClass *clasp);
|
||||
inline JSEmptyScope *createEmptyScope(JSContext *cx, JSClass *clasp);
|
||||
|
||||
JSScopeProperty *addPropertyHelper(JSContext *cx, jsid id,
|
||||
JSPropertyOp getter, JSPropertyOp setter,
|
||||
@ -275,17 +277,14 @@ struct JSScope : public JSObjectMap
|
||||
JSScopeProperty **spp);
|
||||
|
||||
public:
|
||||
explicit JSScope(const JSObjectOps *ops, JSObject *obj = NULL)
|
||||
JSScope(const JSObjectOps *ops, JSObject *obj)
|
||||
: JSObjectMap(ops, 0), object(obj) {}
|
||||
|
||||
/* Create a mutable, owned, empty scope. */
|
||||
static JSScope *create(JSContext *cx, const JSObjectOps *ops, JSClass *clasp,
|
||||
JSObject *obj, uint32 shape);
|
||||
static JSScope *create(JSContext *cx, const JSObjectOps *ops,
|
||||
JSClass *clasp, JSObject *obj, uint32 shape);
|
||||
|
||||
static void destroy(JSContext *cx, JSScope *scope);
|
||||
|
||||
inline void hold();
|
||||
inline void drop(JSContext *cx, JSObject *obj);
|
||||
void destroy(JSContext *cx);
|
||||
|
||||
/*
|
||||
* Return an immutable, shareable, empty scope with the same ops as this
|
||||
@ -395,7 +394,10 @@ struct JSScope : public JSObjectMap
|
||||
* sealed.
|
||||
*/
|
||||
bool sealed() { return flags & SEALED; }
|
||||
void setSealed() { flags |= SEALED; }
|
||||
void setSealed() {
|
||||
JS_ASSERT(!isSharedEmpty());
|
||||
flags |= SEALED;
|
||||
}
|
||||
|
||||
/*
|
||||
* A branded scope's object contains plain old methods (function-valued
|
||||
@ -469,15 +471,45 @@ struct JSScope : public JSObjectMap
|
||||
bool
|
||||
brandedOrHasMethodBarrier() { return flags & (BRANDED | METHOD_BARRIER); }
|
||||
|
||||
bool owned() { return object != NULL; }
|
||||
bool isSharedEmpty() const { return !object; }
|
||||
|
||||
static bool initRuntimeState(JSContext *cx);
|
||||
static void finishRuntimeState(JSContext *cx);
|
||||
};
|
||||
|
||||
struct JSEmptyScope : public JSScope
|
||||
{
|
||||
JSClass * const clasp;
|
||||
jsrefcount nrefs; /* count of all referencing objects */
|
||||
|
||||
explicit JSEmptyScope(const JSObjectOps *ops, JSClass *clasp)
|
||||
: JSScope(ops), clasp(clasp) {}
|
||||
JSEmptyScope(JSContext *cx, const JSObjectOps *ops, JSClass *clasp);
|
||||
|
||||
void hold() {
|
||||
/* The method is only called for already held objects. */
|
||||
JS_ASSERT(nrefs >= 1);
|
||||
JS_ATOMIC_INCREMENT(&nrefs);
|
||||
}
|
||||
|
||||
void drop(JSContext *cx) {
|
||||
JS_ASSERT(nrefs >= 1);
|
||||
JS_ATOMIC_DECREMENT(&nrefs);
|
||||
if (nrefs == 0)
|
||||
destroy(cx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Optimized version of the drop method to use from the object finalizer
|
||||
* to skip expensive JS_ATOMIC_DECREMENT.
|
||||
*/
|
||||
void dropFromGC(JSContext *cx) {
|
||||
#ifdef JS_THREADSAFE
|
||||
JS_ASSERT(CX_THREAD_IS_RUNNING_GC(cx));
|
||||
#endif
|
||||
JS_ASSERT(nrefs >= 1);
|
||||
--nrefs;
|
||||
if (nrefs == 0)
|
||||
destroy(cx);
|
||||
}
|
||||
};
|
||||
|
||||
inline bool
|
||||
@ -787,59 +819,6 @@ JSScope::canProvideEmptyScope(JSObjectOps *ops, JSClass *clasp)
|
||||
return this->ops == ops && (!emptyScope || emptyScope->clasp == clasp);
|
||||
}
|
||||
|
||||
inline JSEmptyScope *
|
||||
JSScope::getEmptyScope(JSContext *cx, JSClass *clasp)
|
||||
{
|
||||
if (emptyScope) {
|
||||
JS_ASSERT(clasp == emptyScope->clasp);
|
||||
emptyScope->hold();
|
||||
return emptyScope;
|
||||
}
|
||||
return createEmptyScope(cx, clasp);
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSScope::ensureEmptyScope(JSContext *cx, JSClass *clasp)
|
||||
{
|
||||
if (emptyScope) {
|
||||
JS_ASSERT(clasp == emptyScope->clasp);
|
||||
return true;
|
||||
}
|
||||
if (!createEmptyScope(cx, clasp))
|
||||
return false;
|
||||
|
||||
/* We are going to have only single ref to the scope. */
|
||||
JS_ASSERT(emptyScope->nrefs == 2);
|
||||
emptyScope->nrefs = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSScope::hold()
|
||||
{
|
||||
JS_ASSERT(nrefs >= 0);
|
||||
JS_ATOMIC_INCREMENT(&nrefs);
|
||||
}
|
||||
|
||||
inline void
|
||||
JSScope::drop(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
#ifdef JS_THREADSAFE
|
||||
/*
|
||||
* We are called only from js_FinalizeObject and can avoid the overhead of
|
||||
* JS_ATOMIC_DECREMENT.
|
||||
*/
|
||||
JS_ASSERT(CX_THREAD_IS_RUNNING_GC(cx));
|
||||
#endif
|
||||
JS_ASSERT(nrefs > 0);
|
||||
--nrefs;
|
||||
|
||||
if (nrefs == 0)
|
||||
destroy(cx, this);
|
||||
else if (object == obj)
|
||||
object = NULL;
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSScopeProperty::get(JSContext* cx, JSObject* obj, JSObject *pobj, jsval* vp)
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
@ -46,6 +46,41 @@
|
||||
#include "jsobj.h"
|
||||
#include "jsscope.h"
|
||||
|
||||
inline JSEmptyScope *
|
||||
JSScope::createEmptyScope(JSContext *cx, JSClass *clasp)
|
||||
{
|
||||
JS_ASSERT(!emptyScope);
|
||||
emptyScope = cx->create<JSEmptyScope>(cx, ops, clasp);
|
||||
return emptyScope;
|
||||
}
|
||||
|
||||
inline JSEmptyScope *
|
||||
JSScope::getEmptyScope(JSContext *cx, JSClass *clasp)
|
||||
{
|
||||
if (emptyScope) {
|
||||
JS_ASSERT(clasp == emptyScope->clasp);
|
||||
emptyScope->hold();
|
||||
return emptyScope;
|
||||
}
|
||||
return createEmptyScope(cx, clasp);
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSScope::ensureEmptyScope(JSContext *cx, JSClass *clasp)
|
||||
{
|
||||
if (emptyScope) {
|
||||
JS_ASSERT(clasp == emptyScope->clasp);
|
||||
return true;
|
||||
}
|
||||
if (!createEmptyScope(cx, clasp))
|
||||
return false;
|
||||
|
||||
/* We are going to have only single ref to the scope. */
|
||||
JS_ASSERT(emptyScope->nrefs == 2);
|
||||
emptyScope->nrefs = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSScope::updateShape(JSContext *cx)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user