зеркало из https://github.com/mozilla/gecko-dev.git
Bug 919544 - Allow cached object allocation to GC; r=jandem
This commit is contained in:
Родитель
afe17e2494
Коммит
42bee7f6b4
|
@ -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. */
|
||||
|
|
Загрузка…
Ссылка в новой задаче