bug 540805 - using reference counting only for empty scopes. r=jorendorff

This commit is contained in:
Igor Bukanov 2010-01-27 09:50:17 +03:00
parent 01a8412479
commit 42951409df
18 changed files with 201 additions and 204 deletions

View File

@ -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;
}

View File

@ -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) {

View File

@ -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);

View File

@ -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

View File

@ -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 *);
}

View File

@ -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);
}

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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++) {

View File

@ -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) \

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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);

View File

@ -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)
{

View File

@ -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)
{