Bug 1047120 - PurpleBuffer doesn't actually need to use Heap<T>; r=mccr8,jonco

This commit is contained in:
Terrence Cole 2014-07-31 14:43:45 -07:00
parent 412332301d
commit b282e9647a
3 changed files with 83 additions and 45 deletions

View File

@ -8,6 +8,7 @@
#define js_RootingAPI_h
#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/GuardObjects.h"
#include "mozilla/LinkedList.h"
#include "mozilla/NullPtr.h"
@ -166,6 +167,23 @@ struct JS_PUBLIC_API(NullPtr)
static void * const constNullValue;
};
#ifdef JSGC_GENERATIONAL
JS_FRIEND_API(void) HeapCellPostBarrier(js::gc::Cell **cellp);
JS_FRIEND_API(void) HeapCellRelocate(js::gc::Cell **cellp);
#endif
#ifdef JS_DEBUG
/*
* For generational GC, assert that an object is in the tenured generation as
* opposed to being in the nursery.
*/
extern JS_FRIEND_API(void)
AssertGCThingMustBeTenured(JSObject* obj);
#else
inline void
AssertGCThingMustBeTenured(JSObject *obj) {}
#endif
/*
* The Heap<T> class is a heap-stored reference to a JS GC thing. All members of
* heap classes that refer to GC things should use Heap<T> (or possibly
@ -285,18 +303,6 @@ class Heap : public js::HeapBase<T>
T ptr;
};
#ifdef JS_DEBUG
/*
* For generational GC, assert that an object is in the tenured generation as
* opposed to being in the nursery.
*/
extern JS_FRIEND_API(void)
AssertGCThingMustBeTenured(JSObject* obj);
#else
inline void
AssertGCThingMustBeTenured(JSObject *obj) {}
#endif
/*
* The TenuredHeap<T> class is similar to the Heap<T> class above in that it
* encapsulates the GC concerns of an on-heap reference to a JS object. However,
@ -560,11 +566,6 @@ class MOZ_STACK_CLASS MutableHandle : public js::MutableHandleBase<T>
void operator=(MutableHandle other) MOZ_DELETE;
};
#ifdef JSGC_GENERATIONAL
JS_FRIEND_API(void) HeapCellPostBarrier(js::gc::Cell **cellp);
JS_FRIEND_API(void) HeapCellRelocate(js::gc::Cell **cellp);
#endif
} /* namespace JS */
namespace js {
@ -666,6 +667,12 @@ struct GCMethods<JSObject *>
{
static JSObject *initial() { return nullptr; }
static bool poisoned(JSObject *v) { return JS::IsPoisonedPtr(v); }
static gc::Cell *asGCThingOrNull(JSObject *v) {
if (!v)
return nullptr;
JS_ASSERT(uintptr_t(v) > 32);
return reinterpret_cast<gc::Cell *>(v);
}
static bool needsPostBarrier(JSObject *v) {
return v != nullptr && gc::IsInsideNursery(reinterpret_cast<gc::Cell *>(v));
}
@ -1246,6 +1253,24 @@ class CompilerRootNode
js::gc::Cell *ptr_;
};
} /* namespace js */
namespace gc {
template <typename T, typename TraceCallbacks>
void
CallTraceCallbackOnNonHeap(T *v, const TraceCallbacks &aCallbacks, const char *aName, void *aClosure)
{
static_assert(sizeof(T) == sizeof(JS::Heap<T>), "T and Heap<T> must be compatible.");
MOZ_ASSERT(v);
mozilla::DebugOnly<Cell *> cell = GCMethods<T>::asGCThingOrNull(*v);
MOZ_ASSERT(cell);
MOZ_ASSERT(!IsInsideNursery(cell));
JS::Heap<T> *asHeapT = reinterpret_cast<JS::Heap<T>*>(v);
aCallbacks.Trace(asHeapT, aName, aClosure);
MOZ_ASSERT(GCMethods<T>::asGCThingOrNull(*v) == cell);
}
} /* namespace gc */
} /* namespace js */
#endif /* js_RootingAPI_h */

View File

@ -1637,6 +1637,9 @@ template <> struct GCMethods<JS::Value>
static bool poisoned(const JS::Value &v) {
return v.isMarkable() && JS::IsPoisonedPtr(v.toGCThing());
}
static gc::Cell *asGCThingOrNull(const JS::Value &v) {
return v.isMarkable() ? v.toGCThing() : nullptr;
}
static bool needsPostBarrier(const JS::Value &v) {
return v.isObject() && gc::IsInsideNursery(reinterpret_cast<gc::Cell*>(&v.toObject()));
}

View File

@ -2521,7 +2521,6 @@ class JSPurpleBuffer
{
MOZ_ASSERT(mValues.IsEmpty());
MOZ_ASSERT(mObjects.IsEmpty());
MOZ_ASSERT(mTenuredObjects.IsEmpty());
}
public:
@ -2538,7 +2537,6 @@ public:
mReferenceToThis = nullptr;
mValues.Clear();
mObjects.Clear();
mTenuredObjects.Clear();
mozilla::DropJSObjects(this);
NS_RELEASE_THIS();
}
@ -2547,9 +2545,13 @@ public:
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(JSPurpleBuffer)
JSPurpleBuffer*& mReferenceToThis;
SegmentedArray<JS::Heap<JS::Value>> mValues;
SegmentedArray<JS::Heap<JSObject*>> mObjects;
SegmentedArray<JS::TenuredHeap<JSObject*>> mTenuredObjects;
// These are raw pointers instead of Heap<T> because we only need Heap<T> for
// pointers which may point into the nursery. The purple buffer never contains
// pointers to the nursery because nursery gcthings can never be gray and only
// gray things can be inserted into the purple buffer.
SegmentedArray<JS::Value> mValues;
SegmentedArray<JSObject*> mObjects;
};
NS_IMPL_CYCLE_COLLECTION_CLASS(JSPurpleBuffer)
@ -2563,21 +2565,21 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(JSPurpleBuffer)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
#define NS_TRACE_SEGMENTED_ARRAY(_field) \
{ \
auto segment = tmp->_field.GetFirstSegment(); \
while (segment) { \
for (uint32_t i = segment->Length(); i > 0;) { \
aCallbacks.Trace(&segment->ElementAt(--i), #_field, aClosure); \
} \
segment = segment->getNext(); \
} \
}
#define NS_TRACE_SEGMENTED_ARRAY(_field, _type) \
{ \
auto segment = tmp->_field.GetFirstSegment(); \
while (segment) { \
for (uint32_t i = segment->Length(); i > 0;) { \
js::gc::CallTraceCallbackOnNonHeap<_type, TraceCallbacks>( \
&segment->ElementAt(--i), aCallbacks, #_field, aClosure); \
} \
segment = segment->getNext(); \
} \
}
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(JSPurpleBuffer)
NS_TRACE_SEGMENTED_ARRAY(mValues)
NS_TRACE_SEGMENTED_ARRAY(mObjects)
NS_TRACE_SEGMENTED_ARRAY(mTenuredObjects)
NS_TRACE_SEGMENTED_ARRAY(mValues, JS::Value)
NS_TRACE_SEGMENTED_ARRAY(mObjects, JSObject*)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(JSPurpleBuffer, AddRef)
@ -2644,10 +2646,12 @@ public:
virtual void Trace(JS::Heap<JS::Value>* aValue, const char* aName,
void* aClosure) const
{
if (aValue->isMarkable()) {
void* thing = aValue->toGCThing();
JS::Value val = *aValue;
if (val.isMarkable()) {
void* thing = val.toGCThing();
if (thing && xpc_GCThingIsGrayCCThing(thing)) {
mCollector->GetJSPurpleBuffer()->mValues.AppendElement(*aValue);
MOZ_ASSERT(!js::gc::IsInsideNursery((js::gc::Cell *)thing));
mCollector->GetJSPurpleBuffer()->mValues.AppendElement(val);
}
}
}
@ -2657,20 +2661,24 @@ public:
{
}
void AppendJSObjectToPurpleBuffer(JSObject *obj) const
{
if (obj && xpc_GCThingIsGrayCCThing(obj)) {
MOZ_ASSERT(!js::gc::IsInsideNursery(JS::AsCell(obj)));
mCollector->GetJSPurpleBuffer()->mObjects.AppendElement(obj);
}
}
virtual void Trace(JS::Heap<JSObject*>* aObject, const char* aName,
void* aClosure) const
{
if (*aObject && xpc_GCThingIsGrayCCThing(*aObject)) {
mCollector->GetJSPurpleBuffer()->mObjects.AppendElement(*aObject);
}
AppendJSObjectToPurpleBuffer(*aObject);
}
virtual void Trace(JS::TenuredHeap<JSObject*>* aObject, const char* aName,
void* aClosure) const
{
if (*aObject && xpc_GCThingIsGrayCCThing(*aObject)) {
mCollector->GetJSPurpleBuffer()->mTenuredObjects.AppendElement(*aObject);
}
AppendJSObjectToPurpleBuffer(*aObject);
}
virtual void Trace(JS::Heap<JSString*>* aString, const char* aName,
@ -3817,6 +3825,8 @@ JSPurpleBuffer*
nsCycleCollector::GetJSPurpleBuffer()
{
if (!mJSPurpleBuffer) {
// The Release call here confuses the GC analysis.
JS::AutoSuppressGCAnalysis nogc;
// JSPurpleBuffer keeps itself alive, but we need to create it in such way
// that it ends up in the normal purple buffer. That happens when
// nsRefPtr goes out of the scope and calls Release.