Bug 919544 - Allow cached object allocation to GC; r=jandem

This commit is contained in:
Terrence Cole 2014-02-01 12:04:03 -08:00
Родитель afe17e2494
Коммит 42bee7f6b4
8 изменённых файлов: 105 добавлений и 32 удалений

Просмотреть файл

@ -1080,7 +1080,7 @@ class FakeMutableHandle : public js::MutableHandleBase<T>
/*
* Types for a variable that either should or shouldn't be rooted, depending on
* the template parameter Rooted. Used for implementing functions that can
* the template parameter allowGC. Used for implementing functions that can
* operate on either rooted or unrooted data.
*
* The toHandle() and toMutableHandle() functions are for calling functions

Просмотреть файл

@ -682,7 +682,7 @@ js::ArraySetLength(typename ExecutionModeTraits<mode>::ContextType cxArg,
arr->setLengthInt32(newLen);
} else {
JSContext *cx = cxArg->asJSContext();
ArrayObject::setLength(cx, arr, newLen);
arr->setLength(cx, newLen);
}
@ -787,7 +787,7 @@ array_addProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleVal
if (index >= length) {
MOZ_ASSERT(arr->lengthIsWritable(),
"how'd this element get added if length is non-writable?");
ArrayObject::setLength(cx, arr, index + 1);
arr->setLength(cx, index + 1);
}
return true;
}
@ -2610,7 +2610,7 @@ js::array_concat(JSContext *cx, unsigned argc, Value *vp)
if (!narr)
return false;
TryReuseArrayType(aobj, narr);
ArrayObject::setLength(cx, narr, length);
narr->setLength(cx, length);
args.rval().setObject(*narr);
if (argc == 0)
return true;
@ -3048,7 +3048,7 @@ js_Array(JSContext *cx, unsigned argc, Value *vp)
/* If the length calculation overflowed, make sure that is marked for the new type. */
if (arr->length() > INT32_MAX)
ArrayObject::setLength(cx, arr, arr->length());
arr->setLength(cx, arr->length());
args.rval().setObject(*arr);
return true;
@ -3147,16 +3147,21 @@ NewArray(ExclusiveContext *cxArg, uint32_t length,
!cx->compartment()->hasObjectMetadataCallback() &&
cache.lookupGlobal(&ArrayObject::class_, cx->global(), allocKind, &entry))
{
RootedObject obj(cx, cache.newObjectFromHit(cx, entry,
GetInitialHeap(newKind, &ArrayObject::class_)));
gc::InitialHeap heap = GetInitialHeap(newKind, &ArrayObject::class_);
JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, heap);
if (obj) {
/* Fixup the elements pointer and length, which may be incorrect. */
Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
ArrayObject *arr = &obj->as<ArrayObject>();
arr->setFixedElements();
ArrayObject::setLength(cx, arr, length);
arr->setLength(cx, length);
if (allocateCapacity && !EnsureNewArrayElements(cx, arr, length))
return nullptr;
return arr;
} else {
RootedObject proto(cxArg, protoArg);
obj = cache.newObjectFromHit<CanGC>(cx, entry, heap);
JS_ASSERT(!obj);
protoArg = proto;
}
}
}

Просмотреть файл

@ -525,6 +525,43 @@ AllocateNonObject(ThreadSafeContext *cx)
return t;
}
/*
* When allocating for initialization from a cached object copy, we will
* potentially destroy the cache entry we want to copy if we allow GC. On the
* other hand, since these allocations are extremely common, we don't want to
* delay GC from these allocation sites. Instead we allow the GC, but still
* fail the allocation, forcing the non-cached path.
*/
template <AllowGC allowGC>
inline JSObject *
AllocateObjectForCacheHit(JSContext *cx, AllocKind kind, InitialHeap heap)
{
#ifdef JSGC_GENERATIONAL
if (ShouldNurseryAllocate(cx->nursery(), kind, heap)) {
size_t thingSize = Arena::thingSize(kind);
JS_ASSERT(thingSize == Arena::thingSize(kind));
if (!CheckAllocatorState<NoGC>(cx, kind))
return nullptr;
JSObject *obj = TryNewNurseryObject<NoGC>(cx, thingSize, 0);
if (!obj && allowGC) {
MinorGC(cx, JS::gcreason::OUT_OF_NURSERY);
return nullptr;
}
return obj;
}
#endif
JSObject *obj = AllocateObject<NoGC>(cx, kind, 0, heap);
if (!obj && allowGC) {
MaybeGC(cx);
return nullptr;
}
return obj;
}
} /* namespace gc */
template <js::AllowGC allowGC>

Просмотреть файл

@ -1310,32 +1310,40 @@ NewObjectCache::fillProto(EntryIndex entry, const Class *clasp, js::TaggedProto
JSObject *
js::NewObjectWithGivenProto(ExclusiveContext *cxArg, const js::Class *clasp,
js::TaggedProto proto_, JSObject *parent_,
js::TaggedProto protoArg, JSObject *parentArg,
gc::AllocKind allocKind, NewObjectKind newKind)
{
Rooted<TaggedProto> proto(cxArg, proto_);
RootedObject parent(cxArg, parent_);
if (CanBeFinalizedInBackground(allocKind, clasp))
allocKind = GetBackgroundAllocKind(allocKind);
NewObjectCache::EntryIndex entry = -1;
if (JSContext *cx = cxArg->maybeJSContext()) {
NewObjectCache &cache = cx->runtime()->newObjectCache;
if (proto.isObject() &&
if (protoArg.isObject() &&
newKind == GenericObject &&
!cx->compartment()->hasObjectMetadataCallback() &&
(!parent || parent == proto.toObject()->getParent()) &&
!proto.toObject()->is<GlobalObject>())
(!parentArg || parentArg == protoArg.toObject()->getParent()) &&
!protoArg.toObject()->is<GlobalObject>())
{
if (cache.lookupProto(clasp, proto.toObject(), allocKind, &entry)) {
JSObject *obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp));
if (obj)
if (cache.lookupProto(clasp, protoArg.toObject(), allocKind, &entry)) {
JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, GetInitialHeap(newKind, clasp));
if (obj) {
return obj;
} else {
Rooted<TaggedProto> proto(cxArg, protoArg);
RootedObject parent(cxArg, parentArg);
obj = cache.newObjectFromHit<CanGC>(cx, entry, GetInitialHeap(newKind, clasp));
JS_ASSERT(!obj);
parentArg = parent;
protoArg = proto;
}
}
}
}
Rooted<TaggedProto> proto(cxArg, protoArg);
RootedObject parent(cxArg, parentArg);
types::TypeObject *type = cxArg->getNewType(clasp, proto, nullptr);
if (!type)
return nullptr;
@ -1393,9 +1401,17 @@ js::NewObjectWithClassProtoCommon(ExclusiveContext *cxArg,
!cx->compartment()->hasObjectMetadataCallback())
{
if (cache.lookupGlobal(clasp, &parentArg->as<GlobalObject>(), allocKind, &entry)) {
JSObject *obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, clasp));
if (obj)
JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, GetInitialHeap(newKind, clasp));
if (obj) {
return obj;
} else {
RootedObject parent(cxArg, parentArg);
RootedObject proto(cxArg, protoArg);
obj = cache.newObjectFromHit<CanGC>(cx, entry, GetInitialHeap(newKind, clasp));
JS_ASSERT(!obj);
protoArg = proto;
parentArg = parent;
}
}
}
}
@ -1445,9 +1461,13 @@ js::NewObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent, gc
!cx->compartment()->hasObjectMetadataCallback())
{
if (cache.lookupType(type, allocKind, &entry)) {
JSObject *obj = cache.newObjectFromHit(cx, entry, GetInitialHeap(newKind, type->clasp()));
if (obj)
JSObject *obj = cache.newObjectFromHit<NoGC>(cx, entry, GetInitialHeap(newKind, type->clasp()));
if (obj) {
return obj;
} else {
obj = cache.newObjectFromHit<CanGC>(cx, entry, GetInitialHeap(newKind, type->clasp()));
parent = type->proto().toObject()->getParent();
}
}
}
@ -3601,7 +3621,7 @@ CallAddPropertyHookDense(typename ExecutionModeTraits<mode>::ExclusiveContextTyp
{
/* Inline addProperty for array objects. */
if (obj->is<ArrayObject>()) {
Rooted<ArrayObject*> arr(cxArg, &obj->as<ArrayObject>());
ArrayObject *arr = &obj->as<ArrayObject>();
uint32_t length = arr->length();
if (index >= length) {
if (mode == ParallelExecution) {
@ -3610,7 +3630,7 @@ CallAddPropertyHookDense(typename ExecutionModeTraits<mode>::ExclusiveContextTyp
return false;
arr->setLengthInt32(index + 1);
} else {
ArrayObject::setLength(cxArg->asExclusiveContext(), arr, index + 1);
arr->setLength(cxArg->asExclusiveContext(), index + 1);
}
}
return true;

Просмотреть файл

@ -15,17 +15,17 @@
namespace js {
/* static */ inline void
ArrayObject::setLength(ExclusiveContext *cx, Handle<ArrayObject*> arr, uint32_t length)
inline void
ArrayObject::setLength(ExclusiveContext *cx, uint32_t length)
{
JS_ASSERT(arr->lengthIsWritable());
JS_ASSERT(lengthIsWritable());
if (length > INT32_MAX) {
/* Track objects with overflowing lengths in type information. */
types::MarkTypeObjectFlags(cx, arr, types::OBJECT_FLAG_LENGTH_OVERFLOW);
types::MarkTypeObjectFlags(cx, this, types::OBJECT_FLAG_LENGTH_OVERFLOW);
}
arr->getElementsHeader()->length = length;
getElementsHeader()->length = length;
}
} // namespace js

Просмотреть файл

@ -27,7 +27,7 @@ class ArrayObject : public JSObject
return getElementsHeader()->length;
}
static inline void setLength(ExclusiveContext *cx, Handle<ArrayObject*> arr, uint32_t length);
inline void setLength(ExclusiveContext *cx, uint32_t length);
// Variant of setLength for use on arrays where the length cannot overflow int32_t.
void setLengthInt32(uint32_t length) {

Просмотреть файл

@ -37,6 +37,7 @@ NewObjectCache::fillGlobal(EntryIndex entry, const Class *clasp, js::GlobalObjec
return fill(entry, clasp, global, kind, obj);
}
template <AllowGC allowGC>
inline JSObject *
NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entry_, js::gc::InitialHeap heap)
{
@ -58,7 +59,16 @@ NewObjectCache::newObjectFromHit(JSContext *cx, EntryIndex entry_, js::gc::Initi
if (cx->runtime()->upcomingZealousGC())
return nullptr;
JSObject *obj = js::NewGCObject<NoGC>(cx, entry->kind, 0, heap);
// Trigger an identical allocation to the one that notified us of OOM
// so that we trigger the right kind of GC automatically.
if (allowGC) {
JSObject *obj = js::gc::AllocateObjectForCacheHit<allowGC>(cx, entry->kind, heap);
JS_ASSERT(!obj);
return nullptr;
}
JS_ASSERT(allowGC == NoGC);
JSObject *obj = js::gc::AllocateObjectForCacheHit<NoGC>(cx, entry->kind, heap);
if (obj) {
copyCachedToObject(obj, templateObj, entry->kind);
probes::CreateObject(cx, obj);

Просмотреть файл

@ -352,6 +352,7 @@ class NewObjectCache
* nullptr if returning the object could possibly trigger GC (does not
* indicate failure).
*/
template <AllowGC allowGC>
inline JSObject *newObjectFromHit(JSContext *cx, EntryIndex entry, js::gc::InitialHeap heap);
/* Fill an entry after a cache miss. */