From 3e4c261c010241285771525e851fd41c9dbc2102 Mon Sep 17 00:00:00 2001 From: "peterv%propagandism.org" Date: Sun, 18 Feb 2007 20:05:32 +0000 Subject: [PATCH] Fix for bug 367779 (Make cycle collection through JS objects more reliable). r=jst, sr=brendan. --- js/src/xpconnect/src/nsXPConnect.cpp | 159 ++++++++++++++++-- js/src/xpconnect/src/xpcprivate.h | 21 ++- js/src/xpconnect/src/xpcvariant.cpp | 43 ++++- js/src/xpconnect/src/xpcwrappedjs.cpp | 65 +++++-- js/src/xpconnect/src/xpcwrappednative.cpp | 36 ++++ .../xpconnect/src/xpcwrappednativejsops.cpp | 6 - .../xpconnect/src/xpcwrappednativescope.cpp | 29 ++++ xpcom/base/nsCycleCollectionParticipant.h | 9 + xpcom/ds/nsVariant.cpp | 31 ++++ xpcom/ds/nsVariant.h | 3 + 10 files changed, 371 insertions(+), 31 deletions(-) diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index 24fd876ae820..70c518d67c75 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -108,18 +108,42 @@ nsXPConnect::nsXPConnect() } typedef nsBaseHashtable PointerSet; +#ifndef XPCONNECT_STANDALONE +typedef nsBaseHashtable ScopeSet; +#endif struct JSObjectRefcounts { PointerSet mRefCounts; - JSObjectRefcounts() +#ifndef XPCONNECT_STANDALONE + ScopeSet mScopes; +#endif + PRBool mMarkEnded; + JSObjectRefcounts() : mMarkEnded(PR_FALSE) { mRefCounts.Init(); +#ifndef XPCONNECT_STANDALONE + mScopes.Init(); +#endif } void Clear() { mRefCounts.Clear(); +#ifndef XPCONNECT_STANDALONE + mScopes.Clear(); +#endif + } + + void MarkStart() + { + Clear(); + mMarkEnded = PR_FALSE; + } + + void MarkEnd() + { + mMarkEnded = PR_TRUE; } void Ref(void *obj, uint8 flags) @@ -430,6 +454,23 @@ nsXPConnect::GetInfoForName(const char * name, nsIInterfaceInfo** info) return FindInfo(NameTester, name, mInterfaceInfoManager, info); } +static JSGCCallback gOldJSGCCallback; + +JS_STATIC_DLL_CALLBACK(JSBool) +XPCCycleGCCallback(JSContext *cx, JSGCStatus status) +{ + // Chain to old GCCallback first, we want to get all the mark notifications + // before recording the end of the mark phase. + JSBool ok = gOldJSGCCallback ? gOldJSGCCallback(cx, status) : JS_TRUE; + + // Record the end of a mark phase. If we get more mark notifications then + // the GC has restarted and we'll need to clear the refcounts first. + if(status == JSGC_MARK_END) + nsXPConnect::GetXPConnect()->GetJSObjectRefcounts()->MarkEnd(); + + return ok; +} + void XPCMarkNotification(void *thing, uint8 flags, void *closure) { uint8 ty = flags & GCF_TYPEMASK; @@ -439,6 +480,12 @@ void XPCMarkNotification(void *thing, uint8 flags, void *closure) return; JSObjectRefcounts* jsr = NS_STATIC_CAST(JSObjectRefcounts*, closure); + // We're marking after a mark phase ended, so the GC restarted itself and + // we want to clear the refcounts first. + // XXX With GC_MARK_DEBUG defined we end up too many times in + // XPCMarkNotification, even while taking JSGC_MARK_END into account. + if(jsr->mMarkEnded) + jsr->MarkStart(); jsr->Ref(thing, flags); } @@ -448,17 +495,32 @@ nsXPConnect::BeginCycleCollection() if (!mObjRefcounts) mObjRefcounts = new JSObjectRefcounts; - mObjRefcounts->Clear(); + mObjRefcounts->MarkStart(); XPCCallContext cx(NATIVE_CALLER); if (cx.IsValid()) { + gOldJSGCCallback = JS_SetGCCallback(cx, XPCCycleGCCallback); JS_SetGCThingCallback(cx, XPCMarkNotification, mObjRefcounts); JS_GC(cx); JS_SetGCThingCallback(cx, nsnull, nsnull); + JS_SetGCCallback(cx, gOldJSGCCallback); + gOldJSGCCallback = nsnull; + +#ifndef XPCONNECT_STANDALONE + XPCWrappedNativeScope::TraverseScopes(cx); +#endif } return NS_OK; } +#ifndef XPCONNECT_STANDALONE +void +nsXPConnect::RecordTraversal(void *p, nsISupports *s) +{ + mObjRefcounts->mScopes.Put(p, s); +} +#endif + nsresult nsXPConnect::FinishCycleCollection() { @@ -524,23 +586,90 @@ nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb) if (!mObjRefcounts->Get(p, refcount)) return NS_OK; - cb.DescribeNode(refcount, sizeof(JSObject), "JS Object"); + JSObject *obj = NS_STATIC_CAST(JSObject*, p); + JSClass* clazz = OBJ_GET_CLASS(cx, obj); +#ifdef DEBUG + char name[72]; + if(XPCNativeWrapper::IsNativeWrapperClass(clazz)) + { + XPCWrappedNative* wn = XPCNativeWrapper::GetWrappedNative(cx, obj); + XPCNativeScriptableInfo* si = wn ? wn->GetScriptableInfo() : nsnull; + if(si) + snprintf(name, sizeof(name), "XPCNativeWrapper (%s)", + si->GetJSClass()->name); + else + snprintf(name, sizeof(name), "XPCNativeWrapper"); + } + else if(obj) + { + XPCNativeScriptableInfo* si = nsnull; + if(IS_PROTO_CLASS(clazz)) + { + XPCWrappedNativeProto* p = + (XPCWrappedNativeProto*) JS_GetPrivate(cx, obj); + si = p->GetScriptableInfo(); + } + if(si) + snprintf(name, sizeof(name), "JS Object (%s - %s)", clazz->name, + si->GetJSClass()->name); + else + snprintf(name, sizeof(name), "JS Object (%s)", clazz->name); + } + else + { + snprintf(name, sizeof(name), "JS Object"); + } + cb.DescribeNode(refcount, sizeof(JSObject), name); +#else + cb.DescribeNode(refcount, sizeof(JSObject), "JS Object"); +#endif if (!p) return NS_OK; - - JSObject *obj = NS_STATIC_CAST(JSObject*, p); - if (OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE - && OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) + if(XPCNativeWrapper::IsNativeWrapperClass(clazz)) + { + // XPCNativeWrapper keeps its wrapped native alive (see XPC_NW_Mark). + XPCWrappedNative* wn = XPCNativeWrapper::GetWrappedNative(cx, obj); + if(wn) + cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, + wn->GetFlatJSObject()); + } + else if(clazz == &XPC_WN_Tearoff_JSClass) + { + // A tearoff holds a strong reference to its native object + // (see XPCWrappedNative::FlatJSObjectFinalized). Its XPCWrappedNative + // will be held alive through the parent of the JSObject of the tearoff. + XPCWrappedNativeTearOff *to = + (XPCWrappedNativeTearOff*) JS_GetPrivate(cx, obj); + cb.NoteXPCOMChild(to->GetNative()); + } + else if(IS_PROTO_CLASS(clazz)) + { + // See XPC_WN_Shared_Proto_Mark. + XPCWrappedNativeProto* p = + (XPCWrappedNativeProto*) JS_GetPrivate(cx, obj); + if(p) + // Mark scope. + p->GetScope()->Traverse(cb); + } + else if(clazz->flags & JSCLASS_HAS_PRIVATE && + clazz->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) { void *v = JS_GetPrivate(cx, obj); if (v) cb.NoteXPCOMChild(NS_STATIC_CAST(nsISupports*, v)); } + + JSObject *parent = OBJ_GET_PARENT(cx, obj); + if(parent) + cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, parent); - for (uint32 i = JSSLOT_START(OBJ_GET_CLASS(cx, obj)); - i < STOBJ_NSLOTS(obj); ++i) + JSObject *proto = OBJ_GET_PROTO(cx, obj); + if(proto) + cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, proto); + + for(uint32 i = JSSLOT_START(clazz); i < STOBJ_NSLOTS(obj); ++i) { jsval val = STOBJ_GET_SLOT(obj, i); if (!JSVAL_IS_NULL(val) @@ -551,7 +680,17 @@ nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb) cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, child); } } - + +#ifndef XPCONNECT_STANDALONE + if(clazz->flags & JSCLASS_IS_GLOBAL) + { + nsISupports *principal = nsnull; + mObjRefcounts->mScopes.Get(obj, &principal); + if(principal) + cb.NoteXPCOMChild(principal); + } +#endif + return NS_OK; } diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index 38540310bb4d..f799c063c37c 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -481,6 +481,10 @@ public: nsresult Unroot(const nsDeque &nodes); nsresult Traverse(void *p, nsCycleCollectionTraversalCallback &cb); nsresult FinishCycleCollection(); + JSObjectRefcounts* GetJSObjectRefcounts() {return mObjRefcounts;} +#ifndef XPCONNECT_STANDALONE + void RecordTraversal(void *p, nsISupports *s); +#endif #ifdef XPC_IDISPATCH_SUPPORT public: @@ -1119,6 +1123,15 @@ public: static void InitStatics() { gScopes = nsnull; gDyingScopes = nsnull; } + void Traverse(nsCycleCollectionTraversalCallback &cb); + +#ifndef XPCONNECT_STANDALONE + /** + * Fills the hash mapping global object to principal. + */ + static void TraverseScopes(XPCCallContext& ccx); +#endif + protected: XPCWrappedNativeScope(XPCCallContext& ccx, JSObject* aGlobal); virtual ~XPCWrappedNativeScope(); @@ -3551,8 +3564,10 @@ extern char * xpc_CheckAccessList(const PRUnichar* wideName, const char* list[]) class XPCVariant : public nsIVariant { public: - NS_DECL_ISUPPORTS + NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_NSIVARIANT + NS_DECL_CYCLE_COLLECTION_CLASS(XPCVariant) + // If this class ever implements nsIWritableVariant, take special care with // the case when mJSVal is JSVAL_STRING, since we don't own the data in // that case. @@ -3593,6 +3608,10 @@ protected: // For faster GC-thing locking and unlocking JSRuntime* mJSRuntime; + +#ifdef GC_MARK_DEBUG + char *mGCRootName; +#endif }; NS_DEFINE_STATIC_IID_ACCESSOR(XPCVariant, XPCVARIANT_IID) diff --git a/js/src/xpconnect/src/xpcvariant.cpp b/js/src/xpconnect/src/xpcvariant.cpp index f9fa2644c307..3b437dc02f0e 100644 --- a/js/src/xpconnect/src/xpcvariant.cpp +++ b/js/src/xpconnect/src/xpcvariant.cpp @@ -42,7 +42,19 @@ #include "xpcprivate.h" -NS_IMPL_ISUPPORTS2_CI(XPCVariant, XPCVariant, nsIVariant) +NS_IMPL_CYCLE_COLLECTION_CLASS(XPCVariant) + +NS_INTERFACE_MAP_BEGIN(XPCVariant) + NS_INTERFACE_MAP_ENTRY(XPCVariant) + NS_INTERFACE_MAP_ENTRY(nsIVariant) + NS_INTERFACE_MAP_ENTRY(nsISupports) + NS_IMPL_QUERY_CLASSINFO(XPCVariant) + NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(XPCVariant) +NS_INTERFACE_MAP_END +NS_IMPL_CI_INTERFACE_GETTER2(XPCVariant, XPCVariant, nsIVariant) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(XPCVariant) +NS_IMPL_CYCLE_COLLECTING_RELEASE(XPCVariant) XPCVariant::XPCVariant(JSRuntime* aJSRuntime) : mJSVal(JSVAL_VOID), @@ -61,10 +73,30 @@ XPCVariant::~XPCVariant() if(JSVAL_IS_GCTHING(mJSVal)) { NS_ASSERTION(mJSRuntime, "Must have a runtime!"); +#ifdef GC_MARK_DEBUG + JS_RemoveRootRT(mJSRuntime, &mJSVal); + JS_smprintf_free(mGCRootName); + mGCRootName = nsnull; +#else JS_UnlockGCThingRT(mJSRuntime, JSVAL_TO_GCTHING(mJSVal)); +#endif } } +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XPCVariant) + if(JSVAL_IS_OBJECT(tmp->mJSVal)) + cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, + JSVAL_TO_OBJECT(tmp->mJSVal)); + + nsVariant::Traverse(tmp->mData, cb); +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +// NB: We might unlink our outgoing references in the future; for now we do +// nothing. This is a harmless conservative behavior; it just means that we rely +// on the cycle being broken by some of the external XPCOM objects' unlink() +// methods, not our own. Typically *any* unlinking will break the cycle. +NS_IMPL_CYCLE_COLLECTION_UNLINK_0(XPCVariant) + // static XPCVariant* XPCVariant::newVariant(XPCCallContext& ccx, jsval aJSVal) { @@ -78,9 +110,16 @@ XPCVariant* XPCVariant::newVariant(XPCCallContext& ccx, jsval aJSVal) if(JSVAL_IS_GCTHING(variant->mJSVal)) { + PRBool ok; +#ifdef GC_MARK_DEBUG + variant->mGCRootName = JS_smprintf("XPCVariant::mJSVal[0x%p]", variant); + ok = JS_AddNamedRoot(ccx, &variant->mJSVal, variant->mGCRootName); +#else // use JS_LockGCThingRT, because we get better performance in a lot of // cases than with adding a named GC root. - if(!JS_LockGCThing(ccx, JSVAL_TO_GCTHING(variant->mJSVal))) + ok = JS_LockGCThing(ccx, JSVAL_TO_GCTHING(variant->mJSVal)); +#endif + if(!ok) { NS_RELEASE(variant); // Also sets variant to nsnull. } diff --git a/js/src/xpconnect/src/xpcwrappedjs.cpp b/js/src/xpconnect/src/xpcwrappedjs.cpp index b63de5410fd8..b98d506a6a1f 100644 --- a/js/src/xpconnect/src/xpcwrappedjs.cpp +++ b/js/src/xpconnect/src/xpcwrappedjs.cpp @@ -63,24 +63,57 @@ NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Traverse // knows for how long. nsresult rv; - nsCOMPtr owner = do_QueryInterface(s, &rv); - if (NS_FAILED(rv)) - return rv; + nsIXPConnectWrappedJS *base; + nsXPCWrappedJS *tmp; + { + // Put the nsCOMPtr in a local scope, to avoid messing up the refcount + // below. + nsCOMPtr owner = do_QueryInterface(s, &rv); + if (NS_FAILED(rv)) + return rv; - nsIXPConnectWrappedJS *base = owner.get(); - nsXPCWrappedJS *tmp = NS_STATIC_CAST(nsXPCWrappedJS*, base); + base = owner.get(); + tmp = NS_STATIC_CAST(nsXPCWrappedJS*, base); + NS_ASSERTION(tmp->mRefCnt.get() > 2, + "How can this be, no one else holds a strong ref?"); + } - // REVIEW ME PLEASE: - // - // I am not sure when this represents the true refcount. + NS_ASSERTION(tmp->IsValid(), "How did we get here?"); - cb.DescribeNode(tmp->mRefCnt.get(), sizeof(nsXPCWrappedJS), - "nsXPCWrappedJS"); + nsrefcnt refcnt = tmp->mRefCnt.get(); +#ifdef DEBUG + char name[72]; + snprintf(name, sizeof(name), "nsXPCWrappedJS (%s)", + tmp->GetClass()->GetInterfaceName()); + cb.DescribeNode(refcnt, sizeof(nsXPCWrappedJS), name); +#else + cb.DescribeNode(refcnt, sizeof(nsXPCWrappedJS), "nsXPCWrappedJS"); +#endif - if (tmp->IsValid()) - cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, + // nsXPCWrappedJS keeps its own refcount artificially at or above 1, see the + // comment above nsXPCWrappedJS::AddRef. + cb.NoteXPCOMChild(base); + + if(refcnt > 1) + // nsXPCWrappedJS roots its mJSObj when its refcount is > 1, see + // the comment above nsXPCWrappedJS::AddRef. + cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, tmp->GetJSObject()); + nsXPCWrappedJS* root = tmp->GetRootWrapper(); + if(root == tmp) + { + // The root wrapper keeps the aggregated native object alive. + nsISupports* outer = tmp->GetAggregatedNativeObject(); + if (outer) + cb.NoteXPCOMChild(outer); + } + else + { + // Non-root wrappers keep their root alive. + cb.NoteXPCOMChild(NS_STATIC_CAST(nsIXPConnectWrappedJS*, root)); + } + return NS_OK; } @@ -134,6 +167,14 @@ nsXPCWrappedJS::QueryInterface(REFNSIID aIID, void** aInstancePtr) return NS_OK; } + if(aIID.Equals(NS_GET_IID(nsCycleCollectionISupports))) + { + NS_ADDREF(this); + *aInstancePtr = + NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this); + return NS_OK; + } + // Always check for this first so that our 'outer' can get this interface // from us without recurring into a call to the outer's QI! if(aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS))) diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp index 066d57f8fd7d..c76685756602 100644 --- a/js/src/xpconnect/src/xpcwrappednative.cpp +++ b/js/src/xpconnect/src/xpcwrappednative.cpp @@ -54,7 +54,22 @@ NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Traverse(nsISupports *s, nsCycleCollectionTraversalCallback &cb) { XPCWrappedNative *tmp = NS_STATIC_CAST(XPCWrappedNative*, s); + if(!tmp->IsValid()) + return NS_OK; + +#ifdef DEBUG + char name[72]; + XPCNativeScriptableInfo* si = tmp->GetScriptableInfo(); + if(si) + snprintf(name, sizeof(name), "XPCWrappedNative (%s)", + si->GetJSClass()->name); + else + snprintf(name, sizeof(name), "XPCWrappedNative"); + + cb.DescribeNode(tmp->mRefCnt.get(), sizeof(XPCWrappedNative), name); +#else cb.DescribeNode(tmp->mRefCnt.get(), sizeof(XPCWrappedNative), "XPCWrappedNative"); +#endif if (tmp->mRefCnt.get() > 1) { @@ -75,6 +90,27 @@ NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Traverse(nsISupports *s, } } + // XXX If there is a scriptable helper we will not be able to find out what + // it marked. + + + // xpc_MarkForValidWrapper calls MarkBeforeJSFinalize and + // MarkScopeJSObjects. + + // XPCWrappedNative marks its proto (see MarkBeforeJSFinalize). + if(tmp->HasProto()) + cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, + tmp->GetProto()->GetJSProtoObject()); + + // XPCWrappedNative marks its mNativeWrapper (see MarkBeforeJSFinalize). + if(tmp->mNativeWrapper) + cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, + tmp->mNativeWrapper); + + // XPCWrappedNative marks its scope. + tmp->GetScope()->Traverse(cb); + + // XPCWrappedNative keeps its native object alive. if (tmp->GetIdentityObject()) { cb.NoteXPCOMChild(tmp->GetIdentityObject()); } diff --git a/js/src/xpconnect/src/xpcwrappednativejsops.cpp b/js/src/xpconnect/src/xpcwrappednativejsops.cpp index aa60ee76c6b6..f3682c2cbaac 100644 --- a/js/src/xpconnect/src/xpcwrappednativejsops.cpp +++ b/js/src/xpconnect/src/xpcwrappednativejsops.cpp @@ -698,12 +698,6 @@ xpc_MarkForValidWrapper(JSContext *cx, XPCWrappedNative* wrapper, void *arg) wrapper->MarkBeforeJSFinalize(cx); - if(wrapper->HasProto()) - { - JSObject* obj = wrapper->GetProto()->GetJSProtoObject(); - NS_ASSERTION(obj, "bad proto"); - JS_MarkGCThing(cx, obj, "XPCWrappedNativeProto::mJSProtoObject", arg); - } MarkScopeJSObjects(cx, wrapper->GetScope(), arg); } diff --git a/js/src/xpconnect/src/xpcwrappednativescope.cpp b/js/src/xpconnect/src/xpcwrappednativescope.cpp index 098cc195f0f5..425cd6d5b453 100644 --- a/js/src/xpconnect/src/xpcwrappednativescope.cpp +++ b/js/src/xpconnect/src/xpcwrappednativescope.cpp @@ -793,3 +793,32 @@ XPCWrappedNativeScope::DebugDump(PRInt16 depth) #endif } +void +XPCWrappedNativeScope::Traverse(nsCycleCollectionTraversalCallback &cb) +{ + // See MarkScopeJSObjects. + cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, mGlobalJSObject); + JSObject *obj = mPrototypeJSObject; + if(obj) + cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, obj); + obj = mPrototypeJSFunction; + if(obj) + cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, obj); +} + +#ifndef XPCONNECT_STANDALONE +// static +void +XPCWrappedNativeScope::TraverseScopes(XPCCallContext& ccx) +{ + // Hold the lock throughout. + XPCAutoLock lock(ccx.GetRuntime()->GetMapLock()); + + for(XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) + if(cur->mGlobalJSObject && cur->mScriptObjectPrincipal) + { + ccx.GetXPConnect()->RecordTraversal(cur->mGlobalJSObject, + cur->mScriptObjectPrincipal); + } +} +#endif diff --git a/xpcom/base/nsCycleCollectionParticipant.h b/xpcom/base/nsCycleCollectionParticipant.h index c107aff7e58c..83adb49987f1 100644 --- a/xpcom/base/nsCycleCollectionParticipant.h +++ b/xpcom/base/nsCycleCollectionParticipant.h @@ -169,6 +169,15 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsCycleCollectionParticipant, return NS_OK; \ } +#define NS_IMPL_CYCLE_COLLECTION_UNLINK_0(_class) \ + NS_IMETHODIMP \ + NS_CYCLE_COLLECTION_CLASSNAME(_class)::Unlink(nsISupports *s) \ + { \ + NS_ASSERTION(CheckForRightISupports(s), \ + "not the nsISupports pointer we expect"); \ + return NS_OK; \ + } + /////////////////////////////////////////////////////////////////////////////// // Helpers for implementing nsCycleCollectionParticipant::Traverse diff --git a/xpcom/ds/nsVariant.cpp b/xpcom/ds/nsVariant.cpp index 38df847db89c..c1e240395fe7 100644 --- a/xpcom/ds/nsVariant.cpp +++ b/xpcom/ds/nsVariant.cpp @@ -45,6 +45,7 @@ #include "prdtoa.h" #include #include "nsCRT.h" +#include "nsCycleCollectionParticipant.h" /***************************************************************************/ // Helpers for static convert functions... @@ -1657,6 +1658,36 @@ nsVariant::Cleanup(nsDiscriminatedUnion* data) return NS_OK; } +/* static */ void +nsVariant::Traverse(const nsDiscriminatedUnion& data, + nsCycleCollectionTraversalCallback &cb) +{ + switch(data.mType) + { + case nsIDataType::VTYPE_INTERFACE: + case nsIDataType::VTYPE_INTERFACE_IS: + if (data.u.iface.mInterfaceValue) { + cb.NoteXPCOMChild(data.u.iface.mInterfaceValue); + } + break; + case nsIDataType::VTYPE_ARRAY: + switch(data.u.array.mArrayType) { + case nsIDataType::VTYPE_INTERFACE: + case nsIDataType::VTYPE_INTERFACE_IS: + { + nsISupports** p = (nsISupports**) data.u.array.mArrayValue; + for(PRUint32 i = data.u.array.mArrayCount; i > 0; p++, i--) + if(*p) + cb.NoteXPCOMChild(*p); + } + default: + break; + } + default: + break; + } +} + /***************************************************************************/ /***************************************************************************/ // members... diff --git a/xpcom/ds/nsVariant.h b/xpcom/ds/nsVariant.h index 393e745f59bb..3969230b6137 100644 --- a/xpcom/ds/nsVariant.h +++ b/xpcom/ds/nsVariant.h @@ -179,6 +179,9 @@ public: static nsresult SetToEmpty(nsDiscriminatedUnion* data); static nsresult SetToEmptyArray(nsDiscriminatedUnion* data); + static void Traverse(const nsDiscriminatedUnion& data, + nsCycleCollectionTraversalCallback &cb); + private: ~nsVariant();