Bug 888338 - 1 - Add TenuredHeap<T> class r=terrence r=bz

This commit is contained in:
Jon Coppeard 2013-07-23 10:58:26 +01:00
Родитель f0b09dd716
Коммит e526358777
11 изменённых файлов: 190 добавлений и 71 удалений

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

@ -1650,6 +1650,7 @@ nsContentUtils::TraceSafeJSContext(JSTracer* aTrc)
return;
}
if (JSObject* global = js::GetDefaultGlobalForContext(cx)) {
JS::AssertGCThingMustBeTenured(global);
JS_CallObjectTracer(aTrc, &global, "safe context");
MOZ_ASSERT(global == js::GetDefaultGlobalForContext(cx));
}

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

@ -1027,7 +1027,6 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
mTimeoutInsertionPoint(nullptr),
mTimeoutPublicIdCounter(1),
mTimeoutFiringDepth(0),
mJSObject(nullptr),
mTimeoutsSuspendDepth(0),
mFocusMethod(0),
mSerial(0),
@ -1865,7 +1864,7 @@ void
nsGlobalWindow::TraceGlobalJSObject(JSTracer* aTrc)
{
if (mJSObject) {
JS_CallObjectTracer(aTrc, &mJSObject, "active window global");
JS_CallTenuredObjectTracer(aTrc, &mJSObject, "active window global");
}
}
@ -2117,14 +2116,13 @@ CreateNativeGlobalForInner(JSContext* aCx,
nsGlobalWindow* aNewInner,
nsIURI* aURI,
nsIPrincipal* aPrincipal,
JSObject** aNativeGlobal,
JS::TenuredHeap<JSObject*>& aNativeGlobal,
nsIXPConnectJSObjectHolder** aHolder)
{
MOZ_ASSERT(aCx);
MOZ_ASSERT(aNewInner);
MOZ_ASSERT(aNewInner->IsInnerWindow());
MOZ_ASSERT(aPrincipal);
MOZ_ASSERT(aNativeGlobal);
MOZ_ASSERT(aHolder);
nsGlobalWindow *top = NULL;
@ -2154,13 +2152,13 @@ CreateNativeGlobalForInner(JSContext* aCx,
NS_ENSURE_SUCCESS(rv, rv);
MOZ_ASSERT(jsholder);
*aNativeGlobal = jsholder->GetJSObject();
aNativeGlobal = jsholder->GetJSObject();
jsholder.forget(aHolder);
// Set the location information for the new global, so that tools like
// about:memory may use that information
MOZ_ASSERT(*aNativeGlobal);
xpc::SetLocationForGlobal(*aNativeGlobal, aURI);
MOZ_ASSERT(aNativeGlobal.getPtr());
xpc::SetLocationForGlobal(aNativeGlobal, aURI);
return NS_OK;
}
@ -2353,7 +2351,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
rv = CreateNativeGlobalForInner(cx, newInnerWindow,
aDocument->GetDocumentURI(),
aDocument->NodePrincipal(),
&newInnerWindow->mJSObject,
newInnerWindow->mJSObject,
getter_AddRefs(mInnerWindowHolder));
NS_ASSERTION(NS_SUCCEEDED(rv) && newInnerWindow->mJSObject && mInnerWindowHolder,
"Failed to get script global and holder");
@ -3153,7 +3151,7 @@ nsGlobalWindow::PoisonOuterWindowProxy(JSObject *aObject)
{
MOZ_ASSERT(IsOuterWindow());
if (aObject == mJSObject) {
mJSObject = reinterpret_cast<JSObject*>(0x1);
mJSObject.setToCrashOnTouch();
}
}

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

@ -1225,7 +1225,9 @@ protected:
// These member variables are used on both inner and the outer windows.
nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
JSObject* mJSObject;
// The JS global object. Global objects are always allocated tenured.
JS::TenuredHeap<JSObject*> mJSObject;
typedef nsCOMArray<nsIDOMStorageEvent> nsDOMStorageEventArray;
nsDOMStorageEventArray mPendingStorageEvents;

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

@ -169,21 +169,19 @@ struct JS_PUBLIC_API(NullPtr)
};
/*
* An encapsulated pointer class for heap based GC thing pointers.
* The Heap<T> class is a C/C++ heap-stored reference to a JS GC thing. All
* members of heap classes that refer to GC thing should use Heap<T> (or
* possibly TenuredHeap<T>, described below).
*
* This implements post-barriers for GC thing pointers stored on the heap. It is
* designed to be used for all heap-based GC thing pointers outside the JS
* engine.
* Heap<T> wraps the complex mechanisms required to ensure GC safety for the
* contained reference into a C++ class that behaves similarly to a normal
* pointer.
*
* The template parameter T must be a JS GC thing pointer, masked pointer or
* possible pointer, such as a JS::Value or jsid.
* GC references stored on the C/C++ stack must use Rooted/Handle/MutableHandle
* instead.
*
* The class must be used to declare data members of heap classes only.
* Stack-based GC thing pointers should used Rooted<T>.
*
* Write barriers are implemented by overloading the assingment operator.
* Assiging to a Heap<T> triggers the appropriate calls into the GC to notify it
* of the change.
* Requirements for type T:
* - Must be one of: Value, jsid, JSObject*, JSString*, JSScript*
*/
template <typename T>
class Heap : public js::HeapBase<T>
@ -257,6 +255,117 @@ class Heap : public js::HeapBase<T>
T ptr;
};
#ifdef 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,
* it has two important differences:
*
* 1) Pointers which are statically known to only reference "tenured" objects
* can avoid the extra overhead of SpiderMonkey's write barriers.
*
* 2) Objects in the "tenured" heap have stronger alignment restrictions than
* those in the "nursery", so it is possible to store flags in the lower
* bits of pointers known to be tenured. TenuredHeap wraps a normal tagged
* pointer with a nice API for accessing the flag bits and adds various
* assertions to ensure that it is not mis-used.
*
* GC things are said to be "tenured" when they are located in the long-lived
* heap: e.g. they have gained tenure as an object by surviving past at least
* one GC. For performance, SpiderMonkey allocates some things which are known
* to normally be long lived directly into the tenured generation; for example,
* global objects. Additionally, SpiderMonkey does not visit individual objects
* when deleting non-tenured objects, so object with finalizers are also always
* tenured; for instance, this includes most DOM objects.
*
* The considerations to keep in mind when using a TenuredHeap<T> vs a normal
* Heap<T> are:
*
* - It is invalid for a TenuredHeap<T> to refer to a non-tenured thing.
* - It is however valid for a Heap<T> to refer to a tenured thing.
* - It is not possible to store flag bits in a Heap<T>.
*/
template <typename T>
class TenuredHeap : public js::HeapBase<T>
{
public:
TenuredHeap() : bits(0) {
MOZ_STATIC_ASSERT(sizeof(T) == sizeof(TenuredHeap<T>),
"TenuredHeap<T> must be binary compatible with T.");
}
explicit TenuredHeap(T p) : bits(0) { setPtr(p); }
explicit TenuredHeap(const TenuredHeap<T> &p) : bits(0) { setPtr(p.ptr); }
bool operator==(const TenuredHeap<T> &other) { return bits == other.bits; }
bool operator!=(const TenuredHeap<T> &other) { return bits != other.bits; }
void setPtr(T newPtr) {
JS_ASSERT((reinterpret_cast<uintptr_t>(newPtr) & flagsMask) == 0);
JS_ASSERT(!js::GCMethods<T>::poisoned(newPtr));
if (newPtr)
AssertGCThingMustBeTenured(newPtr);
bits = (bits & flagsMask) | reinterpret_cast<uintptr_t>(newPtr);
}
void setFlags(uintptr_t flagsToSet) {
JS_ASSERT((flagsToSet & ~flagsMask) == 0);
bits |= flagsToSet;
}
void unsetFlags(uintptr_t flagsToUnset) {
JS_ASSERT((flagsToUnset & ~flagsMask) == 0);
bits &= ~flagsToUnset;
}
bool hasFlag(uintptr_t flag) const {
JS_ASSERT((flag & ~flagsMask) == 0);
return (bits & flag) != 0;
}
T getPtr() const { return reinterpret_cast<T>(bits & ~flagsMask); }
uintptr_t getFlags() const { return bits & flagsMask; }
operator T() const { return getPtr(); }
T operator->() const { return getPtr(); }
TenuredHeap<T> &operator=(T p) {
setPtr(p);
return *this;
}
/*
* Set the pointer to a value which will cause a crash if it is
* dereferenced.
*/
void setToCrashOnTouch() {
bits = (bits & flagsMask) | crashOnTouchPointer;
}
bool isSetToCrashOnTouch() {
return (bits & ~flagsMask) == crashOnTouchPointer;
}
private:
enum {
maskBits = 3,
flagsMask = (1 << maskBits) - 1,
crashOnTouchPointer = 1 << maskBits
};
uintptr_t bits;
};
/*
* Reference to a T that has been rooted elsewhere. This is most useful
* as a parameter type, which guarantees that the T lvalue is properly

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

@ -2362,17 +2362,16 @@ JS_CallObjectTracer(JSTracer *trc, JSObject **objp, const char *name)
}
JS_PUBLIC_API(void)
JS_CallMaskedObjectTracer(JSTracer *trc, uintptr_t *objp, uintptr_t flagMask, const char *name)
JS_CallTenuredObjectTracer(JSTracer *trc, JS::TenuredHeap<JSObject *> *objp, const char *name)
{
uintptr_t flags = *objp & flagMask;
JSObject *obj = reinterpret_cast<JSObject *>(*objp & ~flagMask);
JSObject *obj = objp->getPtr();
if (!obj)
return;
JS_SET_TRACING_LOCATION(trc, (void*)objp);
MarkObjectUnbarriered(trc, &obj, name);
*objp = uintptr_t(obj) | flags;
objp->setPtr(obj);
}
JS_PUBLIC_API(void)

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

@ -2586,13 +2586,11 @@ JS_CallHashSetObjectTracer(JSTracer *trc, HashSetEnum &e, JSObject *const &key,
}
/*
* The JS_CallMaskedObjectTracer variant traces a JSObject* that is stored
* with flags embedded in the low bits of the word. The flagMask parameter
* expects |*objp & flagMask| to yield the flags with the pointer value
* stripped and |*objp & ~flagMask| to yield a valid GC pointer.
* Trace an object that is known to always be tenured. No post barriers are
* required in this case.
*/
extern JS_PUBLIC_API(void)
JS_CallMaskedObjectTracer(JSTracer *trc, uintptr_t *objp, uintptr_t flagMask, const char *name);
JS_CallTenuredObjectTracer(JSTracer *trc, JS::TenuredHeap<JSObject *> *objp, const char *name);
/*
* API for JSTraceCallback implementations.

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

@ -5107,4 +5107,11 @@ AutoDisableProxyCheck::AutoDisableProxyCheck(JSRuntime *rt
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
count++;
}
JS_FRIEND_API(void)
JS::AssertGCThingMustBeTenured(JSObject *obj)
{
JS_ASSERT((!IsNurseryAllocable(obj->tenuredGetAllocKind()) || obj->getClass()->finalize) &&
obj->isTenured());
}
#endif

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

@ -151,7 +151,7 @@ template <> struct MapTypeToTraceKind<JSLinearString> { const static JSGCTrace
template <> struct MapTypeToTraceKind<PropertyName> { const static JSGCTraceKind kind = JSTRACE_STRING; };
template <> struct MapTypeToTraceKind<ion::IonCode> { const static JSGCTraceKind kind = JSTRACE_IONCODE; };
#ifdef JSGC_GENERATIONAL
#if defined(JSGC_GENERATIONAL) || defined(DEBUG)
static inline bool
IsNurseryAllocable(AllocKind kind)
{

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

@ -367,6 +367,7 @@ XPCWrappedNative::WrapNewGlobal(xpcObjectHelper &nativeHelper,
// Set the JS object to the global we already created.
wrapper->mFlatJSObject = global;
wrapper->mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
// Set the private to the XPCWrappedNative.
JS_SetPrivate(global, wrapper);
@ -757,11 +758,10 @@ XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports> aIdentity,
XPCWrappedNativeProto* aProto)
: mMaybeProto(aProto),
mSet(aProto->GetSet()),
mFlatJSObject(INVALID_OBJECT), // non-null to pass IsValid() test
mScriptableInfo(nullptr),
mWrapperWord(0)
mScriptableInfo(nullptr)
{
mIdentity = aIdentity.get();
mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
NS_ASSERTION(mMaybeProto, "bad ctor param");
NS_ASSERTION(mSet, "bad ctor param");
@ -776,11 +776,10 @@ XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports> aIdentity,
: mMaybeScope(TagScope(aScope)),
mSet(aSet),
mFlatJSObject(INVALID_OBJECT), // non-null to pass IsValid() test
mScriptableInfo(nullptr),
mWrapperWord(0)
mScriptableInfo(nullptr)
{
mIdentity = aIdentity.get();
mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
NS_ASSERTION(aScope, "bad ctor param");
NS_ASSERTION(aSet, "bad ctor param");
@ -795,8 +794,6 @@ XPCWrappedNative::~XPCWrappedNative()
Destroy();
}
static const intptr_t WRAPPER_WORD_POISON = 0xa8a8a8a8;
void
XPCWrappedNative::Destroy()
{
@ -835,14 +832,14 @@ XPCWrappedNative::Destroy()
* The only time GetRuntime() will be NULL is if Destroy is called a second
* time on a wrapped native. Since we already unregistered the pointer the
* first time, there's no need to unregister again. Unregistration is safe
* the first time because mWrapperWord isn't used afterwards.
* the first time because mWrapper isn't used afterwards.
*/
if (XPCJSRuntime *rt = GetRuntime()) {
if (IsIncrementalBarrierNeeded(rt->Runtime()))
IncrementalObjectBarrier(GetWrapperPreserveColor());
mWrapperWord = WRAPPER_WORD_POISON;
mWrapper.setToCrashOnTouch();
} else {
MOZ_ASSERT(mWrapperWord == WRAPPER_WORD_POISON);
MOZ_ASSERT(mWrapper.isSetToCrashOnTouch());
}
mMaybeScope = nullptr;
@ -1033,9 +1030,12 @@ XPCWrappedNative::Init(HandleObject parent,
}
mFlatJSObject = JS_NewObject(cx, jsclazz, protoJSObject, parent);
if (!mFlatJSObject)
if (!mFlatJSObject) {
mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
return false;
}
mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
JS_SetPrivate(mFlatJSObject, this);
return FinishInit();
@ -1179,8 +1179,8 @@ XPCWrappedNative::FlatJSObjectFinalized()
if (cache)
cache->ClearWrapper();
// This makes IsValid return false from now on...
mFlatJSObject = nullptr;
mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
NS_ASSERTION(mIdentity, "bad pointer!");
#ifdef XP_WIN
@ -1227,7 +1227,8 @@ XPCWrappedNative::SystemIsBeingShutDown()
// short circuit future finalization
JS_SetPrivate(mFlatJSObject, nullptr);
mFlatJSObject = nullptr; // This makes 'IsValid()' return false.
mFlatJSObject = nullptr;
mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
XPCWrappedNativeProto* proto = GetProto();
@ -1472,7 +1473,10 @@ XPCWrappedNative::ReparentWrapperIfFound(XPCWrappedNativeScope* aOldScope,
MOZ_CRASH();
}
MOZ_ASSERT(flat);
wrapper->mFlatJSObject = flat;
wrapper->mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
if (cache) {
bool preserving = cache->PreservingWrapper();
cache->SetPreservingWrapper(false);
@ -2934,7 +2938,7 @@ NS_IMETHODIMP XPCWrappedNative::DebugDump(int16_t depth)
else
XPC_LOG_ALWAYS(("mSet @ %x", mSet));
XPC_LOG_ALWAYS(("mFlatJSObject of %x", mFlatJSObject));
XPC_LOG_ALWAYS(("mFlatJSObject of %x", mFlatJSObject.getPtr()));
XPC_LOG_ALWAYS(("mIdentity of %x", mIdentity));
XPC_LOG_ALWAYS(("mScriptableInfo @ %x", mScriptableInfo));

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

@ -1385,6 +1385,8 @@ public:
js::SystemAllocPolicy> DOMExpandoSet;
bool RegisterDOMExpandoObject(JSObject *expando) {
// Expandos are proxy objects, and proxies are always tenured.
JS::AssertGCThingMustBeTenured(expando);
if (!mDOMExpandoSet) {
mDOMExpandoSet = new DOMExpandoSet();
mDOMExpandoSet->init(8);
@ -2259,7 +2261,7 @@ public:
nsIPrincipal* GetObjectPrincipal() const;
JSBool
IsValid() const {return nullptr != mFlatJSObject;}
IsValid() const { return mFlatJSObject.hasFlag(FLAT_JS_OBJECT_VALID); }
#define XPC_SCOPE_WORD(s) (intptr_t(s))
#define XPC_SCOPE_MASK (intptr_t(0x3))
@ -2309,8 +2311,7 @@ public:
*/
JSObject*
GetFlatJSObject() const
{if (mFlatJSObject != INVALID_OBJECT)
xpc_UnmarkGrayObject(mFlatJSObject);
{xpc_UnmarkGrayObject(mFlatJSObject);
return mFlatJSObject;}
/**
@ -2443,8 +2444,7 @@ public:
else
GetScope()->TraceSelf(trc);
TraceWrapper(trc);
if (mFlatJSObject && mFlatJSObject != INVALID_OBJECT &&
JS_IsGlobalObject(mFlatJSObject))
if (mFlatJSObject && JS_IsGlobalObject(mFlatJSObject))
{
TraceXPCGlobal(trc, mFlatJSObject);
}
@ -2460,9 +2460,9 @@ public:
// This is the only time we should be tracing our mFlatJSObject,
// normally somebody else is doing that. Be careful not to trace the
// bogus INVALID_OBJECT value we can have during init, though.
if (mFlatJSObject && mFlatJSObject != INVALID_OBJECT) {
JS_CallObjectTracer(trc, &mFlatJSObject,
"XPCWrappedNative::mFlatJSObject");
if (mFlatJSObject) {
JS_CallTenuredObjectTracer(trc, &mFlatJSObject,
"XPCWrappedNative::mFlatJSObject");
}
}
@ -2490,13 +2490,12 @@ public:
JSBool HasExternalReference() const {return mRefCnt > 1;}
JSBool NeedsSOW() { return !!(mWrapperWord & NEEDS_SOW); }
void SetNeedsSOW() { mWrapperWord |= NEEDS_SOW; }
JSBool NeedsCOW() { return !!(mWrapperWord & NEEDS_COW); }
void SetNeedsCOW() { mWrapperWord |= NEEDS_COW; }
JSBool NeedsSOW() { return mWrapper.hasFlag(WRAPPER_NEEDS_SOW); }
void SetNeedsSOW() { mWrapper.setFlags(WRAPPER_NEEDS_SOW); }
JSBool NeedsCOW() { return mWrapper.hasFlag(WRAPPER_NEEDS_COW); }
void SetNeedsCOW() { mWrapper.setFlags(WRAPPER_NEEDS_COW); }
JSObject* GetWrapperPreserveColor() const
{return (JSObject*)(mWrapperWord & (size_t)~(size_t)FLAG_MASK);}
JSObject* GetWrapperPreserveColor() const { return mWrapper.getPtr(); }
JSObject* GetWrapper()
{
@ -2511,20 +2510,18 @@ public:
void SetWrapper(JSObject *obj)
{
JS::IncrementalObjectBarrier(GetWrapperPreserveColor());
intptr_t newval = intptr_t(obj) | (mWrapperWord & FLAG_MASK);
mWrapperWord = newval;
mWrapper.setPtr(obj);
}
void TraceWrapper(JSTracer *trc)
{
JS_CallMaskedObjectTracer(trc, reinterpret_cast<uintptr_t *>(&mWrapperWord),
(uintptr_t)FLAG_MASK, "XPCWrappedNative::mWrapper");
JS_CallTenuredObjectTracer(trc, &mWrapper, "XPCWrappedNative::mWrapper");
}
// Returns the relevant same-compartment security if applicable, or
// mFlatJSObject otherwise.
//
// This takes care of checking mWrapperWord to see if we already have such
// This takes care of checking mWrapper to see if we already have such
// a wrapper.
JSObject *GetSameCompartmentSecurityWrapper(JSContext *cx);
@ -2550,9 +2547,12 @@ protected:
private:
enum {
NEEDS_SOW = JS_BIT(0),
NEEDS_COW = JS_BIT(1),
FLAG_MASK = JS_BITMASK(3)
// Flags bits for mWrapper:
WRAPPER_NEEDS_SOW = JS_BIT(0),
WRAPPER_NEEDS_COW = JS_BIT(1),
// Flags bits for mFlatJSObject:
FLAT_JS_OBJECT_VALID = JS_BIT(0)
};
private:
@ -2581,10 +2581,10 @@ private:
XPCWrappedNativeProto* mMaybeProto;
};
XPCNativeSet* mSet;
JSObject* mFlatJSObject;
JS::TenuredHeap<JSObject*> mFlatJSObject;
XPCNativeScriptableInfo* mScriptableInfo;
XPCWrappedNativeTearOffChunk mFirstChunk;
intptr_t mWrapperWord;
JS::TenuredHeap<JSObject*> mWrapper;
};
/***************************************************************************

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

@ -563,6 +563,7 @@ CycleCollectedJSRuntime::MaybeTraceGlobals(JSTracer* aTracer) const
}
if (JSObject* global = js::GetDefaultGlobalForContext(acx)) {
JS::AssertGCThingMustBeTenured(global);
JS_CallObjectTracer(aTracer, &global, "Global Object");
}
}