Address bug 699446 review comments, r=luke.

This commit is contained in:
Brian Hackett 2011-11-18 14:40:14 -08:00
parent 540d7ac81a
commit e4d3f5339b
7 changed files with 148 additions and 68 deletions

View File

@ -3876,11 +3876,13 @@ NewArray(JSContext *cx, jsuint length, JSObject *proto)
kind = GetBackgroundAllocKind(kind);
#endif
JSObject *parent = GetCurrentGlobal(cx);
GlobalObject *parent = GetCurrentGlobal(cx);
NewObjectCache::Entry *entry = NULL;
if (cx->compartment->newObjectCache.lookup(&ArrayClass, parent, kind, &entry)) {
JSObject *obj = NewObjectFromCacheHit(cx, entry);
NewObjectCache &cache = cx->compartment->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
if (cache.lookupGlobal(&ArrayClass, parent, kind, &entry)) {
JSObject *obj = cache.newObjectFromHit(cx, entry);
if (!obj)
return NULL;
/* Fixup the elements pointer and length, which may be incorrect. */
@ -3909,8 +3911,8 @@ NewArray(JSContext *cx, jsuint length, JSObject *proto)
obj->initializeDenseArray(shape, type, length);
if (entry)
entry->fill(&ArrayClass, parent, kind, obj);
if (entry != -1)
cache.fillGlobal(entry, &ArrayClass, parent, kind, obj);
if (allocateCapacity && !EnsureNewArrayElements(cx, obj, length))
return NULL;

View File

@ -367,9 +367,6 @@ class CellIter: public CellIterImpl
inline void EmptyArenaOp(Arena *arena) {}
inline void EmptyCellOp(Cell *t) {}
} /* namespace gc */
} /* namespace js */
/*
* Allocates a new GC thing. After a successful allocation the caller must
* fully initialize the thing before calling any function that can potentially
@ -423,55 +420,58 @@ TryNewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize)
return static_cast<T *>(t);
}
} /* namespace gc */
} /* namespace js */
inline JSObject *
js_NewGCObject(JSContext *cx, js::gc::AllocKind kind)
{
JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST);
return NewGCThing<JSObject>(cx, kind, js::gc::Arena::thingSize(kind));
return js::gc::NewGCThing<JSObject>(cx, kind, js::gc::Arena::thingSize(kind));
}
inline JSObject *
js_TryNewGCObject(JSContext *cx, js::gc::AllocKind kind)
{
JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST);
return TryNewGCThing<JSObject>(cx, kind, js::gc::Arena::thingSize(kind));
return js::gc::TryNewGCThing<JSObject>(cx, kind, js::gc::Arena::thingSize(kind));
}
inline JSString *
js_NewGCString(JSContext *cx)
{
return NewGCThing<JSString>(cx, js::gc::FINALIZE_STRING, sizeof(JSString));
return js::gc::NewGCThing<JSString>(cx, js::gc::FINALIZE_STRING, sizeof(JSString));
}
inline JSShortString *
js_NewGCShortString(JSContext *cx)
{
return NewGCThing<JSShortString>(cx, js::gc::FINALIZE_SHORT_STRING, sizeof(JSShortString));
return js::gc::NewGCThing<JSShortString>(cx, js::gc::FINALIZE_SHORT_STRING, sizeof(JSShortString));
}
inline JSExternalString *
js_NewGCExternalString(JSContext *cx)
{
return NewGCThing<JSExternalString>(cx, js::gc::FINALIZE_EXTERNAL_STRING,
sizeof(JSExternalString));
return js::gc::NewGCThing<JSExternalString>(cx, js::gc::FINALIZE_EXTERNAL_STRING,
sizeof(JSExternalString));
}
inline JSScript *
js_NewGCScript(JSContext *cx)
{
return NewGCThing<JSScript>(cx, js::gc::FINALIZE_SCRIPT, sizeof(JSScript));
return js::gc::NewGCThing<JSScript>(cx, js::gc::FINALIZE_SCRIPT, sizeof(JSScript));
}
inline js::Shape *
js_NewGCShape(JSContext *cx)
{
return NewGCThing<js::Shape>(cx, js::gc::FINALIZE_SHAPE, sizeof(js::Shape));
return js::gc::NewGCThing<js::Shape>(cx, js::gc::FINALIZE_SHAPE, sizeof(js::Shape));
}
inline js::BaseShape *
js_NewGCBaseShape(JSContext *cx)
{
return NewGCThing<js::BaseShape>(cx, js::gc::FINALIZE_BASE_SHAPE, sizeof(js::BaseShape));
return js::gc::NewGCThing<js::BaseShape>(cx, js::gc::FINALIZE_BASE_SHAPE, sizeof(js::BaseShape));
}
#if JS_HAS_XML_SUPPORT

View File

@ -1974,7 +1974,7 @@ TypeObject *
TypeCompartment::newTypeObject(JSContext *cx, JSScript *script,
JSProtoKey key, JSObject *proto, bool unknown)
{
TypeObject *object = NewGCThing<TypeObject>(cx, gc::FINALIZE_TYPE_OBJECT, sizeof(TypeObject));
TypeObject *object = gc::NewGCThing<TypeObject>(cx, gc::FINALIZE_TYPE_OBJECT, sizeof(TypeObject));
if (!object)
return NULL;
new(object) TypeObject(proto, key == JSProto_Function, unknown);

View File

@ -2942,10 +2942,12 @@ js::NewObjectWithGivenProto(JSContext *cx, js::Class *clasp, JSObject *proto, JS
if (CanBeFinalizedInBackground(kind, clasp))
kind = GetBackgroundAllocKind(kind);
NewObjectCache::Entry *entry = NULL;
NewObjectCache &cache = cx->compartment->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
if (proto && (!parent || parent == proto->getParent()) && !proto->isGlobal()) {
if (cx->compartment->newObjectCache.lookup(clasp, proto, kind, &entry))
return NewObjectFromCacheHit(cx, entry);
if (cache.lookupProto(clasp, proto, kind, &entry))
return cache.newObjectFromHit(cx, entry);
}
types::TypeObject *type = proto ? proto->getNewType(cx) : cx->compartment->getEmptyType(cx);
@ -2963,8 +2965,8 @@ js::NewObjectWithGivenProto(JSContext *cx, js::Class *clasp, JSObject *proto, JS
if (!obj)
return NULL;
if (entry && !obj->hasDynamicSlots())
entry->fill(clasp, proto, kind, obj);
if (entry != -1 && !obj->hasDynamicSlots())
cache.fillProto(entry, clasp, proto, kind, obj);
return obj;
}
@ -2993,10 +2995,12 @@ js::NewObjectWithClassProto(JSContext *cx, js::Class *clasp, JSObject *proto, JS
*/
JSProtoKey protoKey = GetClassProtoKey(clasp);
NewObjectCache::Entry *entry = NULL;
NewObjectCache &cache = cx->compartment->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
if (parent->isGlobal() && protoKey != JSProto_Null) {
if (cx->compartment->newObjectCache.lookup(clasp, parent, kind, &entry))
return NewObjectFromCacheHit(cx, entry);
if (cache.lookupGlobal(clasp, parent->asGlobal(), kind, &entry))
return cache.newObjectFromHit(cx, entry);
}
if (!FindProto(cx, clasp, parent, &proto))
@ -3010,8 +3014,8 @@ js::NewObjectWithClassProto(JSContext *cx, js::Class *clasp, JSObject *proto, JS
if (!obj)
return NULL;
if (entry && !obj->hasDynamicSlots())
entry->fill(clasp, parent, kind, obj);
if (entry != -1 && !obj->hasDynamicSlots())
cache.fillGlobal(entry, clasp, parent->asGlobal(), kind, obj);
return obj;
}
@ -3025,18 +3029,20 @@ js::NewObjectWithType(JSContext *cx, types::TypeObject *type, JSObject *parent,
if (CanBeFinalizedInBackground(kind, &ObjectClass))
kind = GetBackgroundAllocKind(kind);
NewObjectCache::Entry *entry = NULL;
NewObjectCache &cache = cx->compartment->newObjectCache;
NewObjectCache::EntryIndex entry = -1;
if (parent == type->proto->getParent()) {
if (cx->compartment->newObjectCache.lookup(&ObjectClass, type, kind, &entry))
return NewObjectFromCacheHit(cx, entry);
if (cache.lookupType(&ObjectClass, type, kind, &entry))
return cache.newObjectFromHit(cx, entry);
}
JSObject *obj = NewObject(cx, &ObjectClass, type, parent, kind);
if (!obj)
return NULL;
if (entry && !obj->hasDynamicSlots())
entry->fill(&ObjectClass, type, kind, obj);
if (entry != -1 && !obj->hasDynamicSlots())
cache.fillType(entry, &ObjectClass, type, kind, obj);
return obj;
}

View File

@ -1596,7 +1596,7 @@ MarkStandardClassInitializedNoProto(JSObject *obj, js::Class *clasp);
* When an object is created which matches the criteria in the 'key' section
* below, an entry is filled with the resulting object.
*/
struct NewObjectCache
class NewObjectCache
{
struct Entry
{
@ -1629,31 +1629,45 @@ struct NewObjectCache
* fixed slots (undefined) and private data (NULL).
*/
JSObject_Slots16 templateObject;
inline void fill(Class *clasp, gc::Cell *key, gc::AllocKind kind, JSObject *obj);
};
Entry entries[41];
void reset() { PodZero(this); }
bool lookup(Class *clasp, gc::Cell *key, gc::AllocKind kind, Entry **pentry)
{
jsuword hash = (jsuword(clasp) ^ jsuword(key)) + kind;
Entry *entry = *pentry = &entries[hash % JS_ARRAY_LENGTH(entries)];
/* N.B. Lookups with the same clasp/key but different kinds map to different entries. */
return (entry->clasp == clasp && entry->key == key);
}
void invalidateEntriesForShape(JSContext *cx, Shape *shape, JSObject *proto);
void staticAsserts() {
JS_STATIC_ASSERT(gc::FINALIZE_OBJECT_LAST == gc::FINALIZE_OBJECT16_BACKGROUND);
}
public:
typedef int EntryIndex;
void reset() { PodZero(this); }
/*
* Get the entry index for the given lookup, return whether there was a hit
* on an existing entry.
*/
inline bool lookupProto(Class *clasp, JSObject *proto, gc::AllocKind kind, EntryIndex *pentry);
inline bool lookupGlobal(Class *clasp, js::GlobalObject *global, gc::AllocKind kind, EntryIndex *pentry);
inline bool lookupType(Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, EntryIndex *pentry);
/* Return a new object from a cache hit produced by a lookup method. */
inline JSObject *newObjectFromHit(JSContext *cx, EntryIndex entry);
/* Fill an entry after a cache miss. */
inline void fillProto(EntryIndex entry, Class *clasp, JSObject *proto, gc::AllocKind kind, JSObject *obj);
inline void fillGlobal(EntryIndex entry, Class *clasp, js::GlobalObject *global, gc::AllocKind kind, JSObject *obj);
inline void fillType(EntryIndex entry, Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, JSObject *obj);
/* Invalidate any entries which might produce an object with shape/proto. */
void invalidateEntriesForShape(JSContext *cx, Shape *shape, JSObject *proto);
private:
inline bool lookup(Class *clasp, gc::Cell *key, gc::AllocKind kind, EntryIndex *pentry);
inline void fill(EntryIndex entry, Class *clasp, gc::Cell *key, gc::AllocKind kind, JSObject *obj);
};
}
} /* namespace js */
/*
* Select Object.prototype method names shared between jsapi.cpp and jsobj.cpp.

View File

@ -1544,22 +1544,81 @@ class AutoPropertyDescriptorRooter : private AutoGCRooter, public PropertyDescri
friend void AutoGCRooter::trace(JSTracer *trc);
};
inline void
NewObjectCache::Entry::fill(Class *clasp, gc::Cell *key, gc::AllocKind kind, JSObject *obj)
inline bool
NewObjectCache::lookup(Class *clasp, gc::Cell *key, gc::AllocKind kind, EntryIndex *pentry)
{
jsuword hash = (jsuword(clasp) ^ jsuword(key)) + kind;
*pentry = hash % js::ArrayLength(entries);
Entry *entry = &entries[*pentry];
/* N.B. Lookups with the same clasp/key but different kinds map to different entries. */
return (entry->clasp == clasp && entry->key == key);
}
inline bool
NewObjectCache::lookupProto(Class *clasp, JSObject *proto, gc::AllocKind kind, EntryIndex *pentry)
{
JS_ASSERT(!proto->isGlobal());
return lookup(clasp, proto, kind, pentry);
}
inline bool
NewObjectCache::lookupGlobal(Class *clasp, js::GlobalObject *global, gc::AllocKind kind, EntryIndex *pentry)
{
return lookup(clasp, global, kind, pentry);
}
inline bool
NewObjectCache::lookupType(Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, EntryIndex *pentry)
{
return lookup(clasp, type, kind, pentry);
}
inline void
NewObjectCache::fill(EntryIndex entry_, Class *clasp, gc::Cell *key, gc::AllocKind kind, JSObject *obj)
{
JS_ASSERT(unsigned(entry_) < ArrayLength(entries));
Entry *entry = &entries[entry_];
JS_ASSERT(!obj->hasDynamicSlots() && !obj->hasDynamicElements());
this->clasp = clasp;
this->key = key;
this->kind = kind;
entry->clasp = clasp;
entry->key = key;
entry->kind = kind;
nbytes = obj->structSize();
memcpy(&templateObject, obj, nbytes);
entry->nbytes = obj->structSize();
memcpy(&entry->templateObject, obj, entry->nbytes);
}
inline void
NewObjectCache::fillProto(EntryIndex entry, Class *clasp, JSObject *proto, gc::AllocKind kind, JSObject *obj)
{
JS_ASSERT(!proto->isGlobal());
JS_ASSERT(obj->getProto() == proto);
return fill(entry, clasp, proto, kind, obj);
}
inline void
NewObjectCache::fillGlobal(EntryIndex entry, Class *clasp, js::GlobalObject *global, gc::AllocKind kind, JSObject *obj)
{
JS_ASSERT(global == obj->getGlobal());
return fill(entry, clasp, global, kind, obj);
}
inline void
NewObjectCache::fillType(EntryIndex entry, Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, JSObject *obj)
{
JS_ASSERT(obj->type() == type);
return fill(entry, clasp, type, kind, obj);
}
inline JSObject *
NewObjectFromCacheHit(JSContext *cx, NewObjectCache::Entry *entry)
NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entry_)
{
JS_ASSERT(unsigned(entry_) < ArrayLength(entries));
Entry *entry = &entries[entry_];
JSObject *obj = js_TryNewGCObject(cx, entry->kind);
if (obj) {
memcpy(obj, &entry->templateObject, entry->nbytes);

View File

@ -1399,23 +1399,22 @@ EmptyShape::lookupInitialShape(JSContext *cx, Class *clasp, JSObject *proto, JSO
void
NewObjectCache::invalidateEntriesForShape(JSContext *cx, Shape *shape, JSObject *proto)
{
NewObjectCache::Entry *entry = NULL;
Class *clasp = shape->getObjectClass();
gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
if (CanBeFinalizedInBackground(kind, clasp))
kind = GetBackgroundAllocKind(kind);
JSObject *global = shape->getObjectParent()->getGlobal();
GlobalObject *global = shape->getObjectParent()->getGlobal();
types::TypeObject *type = proto->getNewType(cx);
if (lookup(clasp, global, kind, &entry))
PodZero(entry);
if (lookup(clasp, proto, kind, &entry))
PodZero(entry);
if (lookup(clasp, type, kind, &entry))
PodZero(entry);
EntryIndex entry;
if (lookupGlobal(clasp, global, kind, &entry))
PodZero(&entries[entry]);
if (!proto->isGlobal() && lookupProto(clasp, proto, kind, &entry))
PodZero(&entries[entry]);
if (lookupType(clasp, type, kind, &entry))
PodZero(&entries[entry]);
}
/* static */ void