Bug 750570, part 2 - Add purple buffer support for non-nsISupports classes. r=smaug
authorAndrew McCreight <amccreight@mozilla.com>
Fri, 24 Aug 2012 09:50:06 -0700
changeset 105348 9077df78db40c7cf71f564831e66673be1e49e05
parent 105347 786f414ea72681508af57225d8b2082332cdc758
child 105349 24368b894189f68944f2acceded49f6a3f0859a4
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewerssmaug
bugs750570
milestone17.0a1
Bug 750570, part 2 - Add purple buffer support for non-nsISupports classes. r=smaug We add a new field to purple buffer entries, to store the participant for non-nsISupports classes. For nsISupports, we store NULL instead. The participant has to be passed into Suspect2. In the cycle collector itself, we generalize canonicalization to handle the possibility that we can just grab the participant directly from a field, rather than needing to QI something. Most of the patch is just routing around this extra pointer.
xpcom/base/nsCycleCollector.cpp
xpcom/build/nsXPCOM.h
xpcom/build/nsXPCOMPrivate.h
xpcom/glue/nsISupportsImpl.h
xpcom/glue/standalone/nsXPCOMGlue.cpp
xpcom/stub/nsXPComStub.cpp
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -691,19 +691,46 @@ struct GCGraph
     }
 
 };
 
 // XXX Would be nice to have an nsHashSet<KeyType> API that has
 // Add/Remove/Has rather than PutEntry/RemoveEntry/GetEntry.
 typedef nsTHashtable<nsPtrHashKey<const void> > PointerSet;
 
+static nsISupports *
+CanonicalizeXPCOMParticipant(nsISupports *in)
+{
+    nsISupports* out;
+    in->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
+                       reinterpret_cast<void**>(&out));
+    return out;
+}
+
 static inline void
 ToParticipant(nsISupports *s, nsXPCOMCycleCollectionParticipant **cp);
 
+static void
+CanonicalizeParticipant(void **parti, nsCycleCollectionParticipant **cp)
+{
+    // If the participant is null, this is an nsISupports participant,
+    // so we must QI to get the real participant.
+
+    if (!*cp) {
+        nsISupports *nsparti = static_cast<nsISupports*>(*parti);
+        nsparti = CanonicalizeXPCOMParticipant(nsparti);
+        NS_ASSERTION(nsparti,
+                     "Don't add objects that don't participate in collection!");
+        nsXPCOMCycleCollectionParticipant *xcp;
+        ToParticipant(nsparti, &xcp);
+        *parti = nsparti;
+        *cp = xcp;
+    }
+}
+
 struct nsPurpleBuffer
 {
 private:
     struct Block {
         Block *mNext;
         nsPurpleBufferEntry mEntries[255];
 
         Block() : mNext(nullptr) {}
@@ -794,20 +821,20 @@ public:
     {
         for (nsPurpleBufferEntry *e = b->mEntries,
                               *eEnd = ArrayEnd(b->mEntries);
              e != eEnd; ++e) {
             if (!(uintptr_t(e->mObject) & uintptr_t(1))) {
                 // This is a real entry (rather than something on the
                 // free list).
                 if (e->mObject) {
-                    nsXPCOMCycleCollectionParticipant *cp;
-                    ToParticipant(e->mObject, &cp);
-
-                    cp->UnmarkIfPurple(e->mObject);
+                    void *obj = e->mObject;
+                    nsCycleCollectionParticipant *cp = e->mParticipant;
+                    CanonicalizeParticipant(&obj, &cp);
+                    cp->UnmarkIfPurple(obj);
                 }
 
                 if (--mCount == 0)
                     break;
             }
         }
     }
 
@@ -844,26 +871,27 @@ public:
         }
 
         nsPurpleBufferEntry *e = mFreeList;
         mFreeList = (nsPurpleBufferEntry*)
             (uintptr_t(mFreeList->mNextInFreeList) & ~uintptr_t(1));
         return e;
     }
 
-    nsPurpleBufferEntry* Put(nsISupports *p)
+    nsPurpleBufferEntry* Put(void *p, nsCycleCollectionParticipant *cp)
     {
         nsPurpleBufferEntry *e = NewEntry();
         if (!e) {
             return nullptr;
         }
 
         ++mCount;
 
         e->mObject = p;
+        e->mParticipant = cp;
 
 #ifdef DEBUG_CC
         mNormalObjects.PutEntry(p);
 #endif
 
         // Caller is responsible for filling in result's mRefCnt.
         return e;
     }
@@ -902,37 +930,38 @@ public:
 
     size_t BlocksSize() const
     {
         return sizeof(Block) * mNumBlocksAlloced;
     }
 
 };
 
+static bool
+AddPurpleRoot(GCGraphBuilder &builder, void *root, nsCycleCollectionParticipant *cp);
+
 struct CallbackClosure
 {
     CallbackClosure(nsPurpleBuffer *aPurpleBuffer, GCGraphBuilder &aBuilder)
         : mPurpleBuffer(aPurpleBuffer),
           mBuilder(aBuilder)
     {
     }
     nsPurpleBuffer *mPurpleBuffer;
     GCGraphBuilder &mBuilder;
 };
 
-static bool
-AddPurpleRoot(GCGraphBuilder &builder, nsISupports *root);
-
 static PLDHashOperator
 selectionCallback(nsPtrHashKey<const void>* key, void* userArg)
 {
     CallbackClosure *closure = static_cast<CallbackClosure*>(userArg);
     if (AddPurpleRoot(closure->mBuilder,
                       static_cast<nsISupports *>(
-                        const_cast<void*>(key->GetKey()))))
+                        const_cast<void*>(key->GetKey())),
+                      nullptr))
         return PL_DHASH_REMOVE;
 
     return PL_DHASH_NEXT;
 }
 
 void
 nsPurpleBuffer::SelectPointers(GCGraphBuilder &aBuilder)
 {
@@ -966,17 +995,18 @@ nsPurpleBuffer::SelectPointers(GCGraphBu
     // Walk through all the blocks.
     for (Block *b = &mFirstBlock; b; b = b->mNext) {
         for (nsPurpleBufferEntry *e = b->mEntries,
                               *eEnd = ArrayEnd(b->mEntries);
             e != eEnd; ++e) {
             if (!(uintptr_t(e->mObject) & uintptr_t(1))) {
                 // This is a real entry (rather than something on the
                 // free list).
-                if (!e->mObject || AddPurpleRoot(aBuilder, e->mObject)) {
+                if (!e->mObject || AddPurpleRoot(aBuilder, e->mObject,
+                                                 e->mParticipant)) {
                     Remove(e);
                 }
             }
         }
     }
 
     NS_WARN_IF_FALSE(mCount == 0, "AddPurpleRoot failed");
     if (mCount == 0) {
@@ -1032,17 +1062,17 @@ struct nsCycleCollector
 
     nsCycleCollector();
     ~nsCycleCollector();
 
     // The first pair of Suspect and Forget functions are only used by
     // old XPCOM binary components.
     bool Suspect(nsISupports *n);
     bool Forget(nsISupports *n);
-    nsPurpleBufferEntry* Suspect2(nsISupports *n);
+    nsPurpleBufferEntry* Suspect2(void *n, nsCycleCollectionParticipant *cp);
     bool Forget2(nsPurpleBufferEntry *e);
 
     void Collect(bool aMergeCompartments,
                  nsCycleCollectorResults *aResults,
                  uint32_t aTryCollections,
                  nsICycleCollectorListener *aListener);
 
     // Prepare for and cleanup after one or more collection(s).
@@ -1163,25 +1193,16 @@ AbortIfOffMainThreadIfCheckFast()
 {
 #if defined(XP_WIN) || defined(NS_TLS)
     if (!NS_IsMainThread() && !NS_IsCycleCollectorThread()) {
         NS_RUNTIMEABORT("Main-thread-only object used off the main thread");
     }
 #endif
 }
 
-static nsISupports *
-canonicalize(nsISupports *in)
-{
-    nsISupports* child;
-    in->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
-                       reinterpret_cast<void**>(&child));
-    return child;
-}
-
 static inline void
 ToParticipant(nsISupports *s, nsXPCOMCycleCollectionParticipant **cp)
 {
     // We use QI to move from an nsISupports to an
     // nsXPCOMCycleCollectionParticipant, which is a per-class singleton helper
     // object that implements traversal and unlinking logic for the nsISupports
     // in question.
     CallQueryInterface(s, cp);
@@ -1766,25 +1787,25 @@ void
 GCGraphBuilder::SetLastChild()
 {
     mCurrPi->SetLastChild(mEdgeBuilder.Mark());
 }
 
 NS_IMETHODIMP_(void)
 GCGraphBuilder::NoteXPCOMRoot(nsISupports *root)
 {
-    root = canonicalize(root);
+    root = CanonicalizeXPCOMParticipant(root);
     NS_ASSERTION(root,
                  "Don't add objects that don't participate in collection!");
 
 #ifdef DEBUG_CC
     if (nsCycleCollector_shouldSuppress(root))
         return;
 #endif
-    
+
     nsXPCOMCycleCollectionParticipant *cp;
     ToParticipant(root, &cp);
 
     NoteRoot(root, cp);
 }
 
 NS_IMETHODIMP_(void)
 GCGraphBuilder::NoteJSRoot(void *root)
@@ -1838,24 +1859,24 @@ GCGraphBuilder::DescribeGCedNode(bool is
 NS_IMETHODIMP_(void)
 GCGraphBuilder::NoteXPCOMChild(nsISupports *child) 
 {
     nsCString edgeName;
     if (WantDebugInfo()) {
         edgeName.Assign(mNextEdgeName);
         mNextEdgeName.Truncate();
     }
-    if (!child || !(child = canonicalize(child)))
-        return; 
+    if (!child || !(child = CanonicalizeXPCOMParticipant(child)))
+        return;
 
 #ifdef DEBUG_CC
     if (nsCycleCollector_shouldSuppress(child))
         return;
 #endif
-    
+
     nsXPCOMCycleCollectionParticipant *cp;
     ToParticipant(child, &cp);
     if (cp && (!cp->CanSkipThis(child) || WantAllTraces())) {
         NoteChild(child, cp, edgeName);
     }
 }
 
 NS_IMETHODIMP_(void)
@@ -1928,16 +1949,33 @@ GCGraphBuilder::NoteWeakMapping(void *ma
         return;
 
     WeakMapping *mapping = mWeakMaps.AppendElement();
     mapping->mMap = map ? AddWeakMapNode(map) : nullptr;
     mapping->mKey = key ? AddWeakMapNode(key) : nullptr;
     mapping->mVal = valNode;
 }
 
+static bool
+AddPurpleRoot(GCGraphBuilder &builder, void *root, nsCycleCollectionParticipant *cp)
+{
+    CanonicalizeParticipant(&root, &cp);
+
+    if (builder.WantAllTraces() || !cp->CanSkipInCC(root)) {
+        PtrInfo *pinfo = builder.AddNode(root, cp);
+        if (!pinfo) {
+            return false;
+        }
+    }
+
+    cp->UnmarkIfPurple(root);
+
+    return true;
+}
+
 // MayHaveChild() will be false after a Traverse if the object does
 // not have any children the CC will visit.
 class ChildFinder : public nsCycleCollectionTraversalCallback
 {
 public:
     ChildFinder() : mMayHaveChild(false) {}
 
     // The logic of the Note*Child functions must mirror that of their
@@ -1964,18 +2002,18 @@ public:
     }
 private:
     bool mMayHaveChild;
 };
 
 NS_IMETHODIMP_(void)
 ChildFinder::NoteXPCOMChild(nsISupports *child)
 {
-    if (!child || !(child = canonicalize(child)))
-        return; 
+    if (!child || !(child = CanonicalizeXPCOMParticipant(child)))
+        return;
     nsXPCOMCycleCollectionParticipant *cp;
     ToParticipant(child, &cp);
     if (cp && !cp->CanSkip(child, true))
         mMayHaveChild = true;
 }
 
 NS_IMETHODIMP_(void)
 ChildFinder::NoteNativeChild(void *child,
@@ -1989,39 +2027,17 @@ NS_IMETHODIMP_(void)
 ChildFinder::NoteJSChild(void *child)
 {
     if (child && xpc_GCThingIsGrayCCThing(child)) {
         mMayHaveChild = true;
     }
 }
 
 static bool
-AddPurpleRoot(GCGraphBuilder &builder, nsISupports *root)
-{
-    root = canonicalize(root);
-    NS_ASSERTION(root,
-                 "Don't add objects that don't participate in collection!");
-
-    nsXPCOMCycleCollectionParticipant *cp;
-    ToParticipant(root, &cp);
-
-    if (builder.WantAllTraces() || !cp->CanSkipInCC(root)) {
-        PtrInfo *pinfo = builder.AddNode(root, cp);
-        if (!pinfo) {
-            return false;
-        }
-    }
-
-    cp->UnmarkIfPurple(root);
-
-    return true;
-}
-
-static bool
-MayHaveChild(nsISupports *o, nsXPCOMCycleCollectionParticipant* cp)
+MayHaveChild(void *o, nsCycleCollectionParticipant* cp)
 {
     ChildFinder cf;
     cp->Traverse(o, cf);
     return cf.MayHaveChild();
 }
 
 void
 nsPurpleBuffer::RemoveSkippable(bool removeChildlessNodes)
@@ -2030,19 +2046,19 @@ nsPurpleBuffer::RemoveSkippable(bool rem
     for (Block *b = &mFirstBlock; b; b = b->mNext) {
         for (nsPurpleBufferEntry *e = b->mEntries,
                               *eEnd = ArrayEnd(b->mEntries);
             e != eEnd; ++e) {
             if (!(uintptr_t(e->mObject) & uintptr_t(1))) {
                 // This is a real entry (rather than something on the
                 // free list).
                 if (e->mObject) {
-                    nsISupports* o = canonicalize(e->mObject);
-                    nsXPCOMCycleCollectionParticipant* cp;
-                    ToParticipant(o, &cp);
+                    void *o = e->mObject;
+                    nsCycleCollectionParticipant *cp = e->mParticipant;
+                    CanonicalizeParticipant(&o, &cp);
                     if (!cp->CanSkip(o, false) &&
                         (!removeChildlessNodes || MayHaveChild(o, cp))) {
                         continue;
                     }
                     cp->UnmarkIfPurple(o);
                 }
                 Remove(e);
             }
@@ -2442,41 +2458,44 @@ nsCycleCollector_shouldSuppress(nsISuppo
 {
     Suppressor supp;
     return supp.shouldSuppress(s);
 }
 #endif
 
 #ifdef DEBUG
 static bool
-nsCycleCollector_isScanSafe(nsISupports *s)
+nsCycleCollector_isScanSafe(void *s, nsCycleCollectionParticipant *cp)
 {
     if (!s)
         return false;
 
-    nsXPCOMCycleCollectionParticipant *cp;
-    ToParticipant(s, &cp);
-
-    return cp != nullptr;
+    if (cp)
+        return true;
+
+    nsXPCOMCycleCollectionParticipant *xcp;
+    ToParticipant(static_cast<nsISupports*>(s), &xcp);
+
+    return xcp != nullptr;
 }
 #endif
 
 bool
 nsCycleCollector::Suspect(nsISupports *n)
 {
     AbortIfOffMainThreadIfCheckFast();
 
     // Re-entering ::Suspect during collection used to be a fault, but
     // we are canonicalizing nsISupports pointers using QI, so we will
     // see some spurious refcount traffic here. 
 
     if (mScanInProgress)
         return false;
 
-    NS_ASSERTION(nsCycleCollector_isScanSafe(n),
+    NS_ASSERTION(nsCycleCollector_isScanSafe(n, nullptr),
                  "suspected a non-scansafe pointer");
 
     if (mParams.mDoNothing)
         return false;
 
 #ifdef DEBUG_CC
     mStats.mSuspectNode++;
 
@@ -2519,48 +2538,48 @@ nsCycleCollector::Forget(nsISupports *n)
     }
 #endif
 
     mPurpleBuf.RemoveCompatObject(n);
     return true;
 }
 
 nsPurpleBufferEntry*
-nsCycleCollector::Suspect2(nsISupports *n)
+nsCycleCollector::Suspect2(void *n, nsCycleCollectionParticipant *cp)
 {
     AbortIfOffMainThreadIfCheckFast();
 
     // Re-entering ::Suspect during collection used to be a fault, but
     // we are canonicalizing nsISupports pointers using QI, so we will
     // see some spurious refcount traffic here. 
 
     if (mScanInProgress)
         return nullptr;
 
-    NS_ASSERTION(nsCycleCollector_isScanSafe(n),
+    NS_ASSERTION(nsCycleCollector_isScanSafe(n, cp),
                  "suspected a non-scansafe pointer");
 
     if (mParams.mDoNothing)
         return nullptr;
 
 #ifdef DEBUG_CC
     mStats.mSuspectNode++;
 
-    if (nsCycleCollector_shouldSuppress(n))
+    if (!cp && nsCycleCollector_shouldSuppress(static_cast<nsISupports *>(n)))
         return nullptr;
 
     if (mParams.mLogPointers) {
         if (!mPtrLog)
             mPtrLog = fopen("pointer_log", "w");
         fprintf(mPtrLog, "S %p\n", static_cast<void*>(n));
     }
 #endif
 
     // Caller is responsible for filling in result's mRefCnt.
-    return mPurpleBuf.Put(n);
+    return mPurpleBuf.Put(n, cp);
 }
 
 
 bool
 nsCycleCollector::Forget2(nsPurpleBufferEntry *e)
 {
     AbortIfOffMainThreadIfCheckFast();
 
@@ -2991,20 +3010,20 @@ NS_CycleCollectorSuspect(nsISupports *n)
 
 bool
 NS_CycleCollectorForget(nsISupports *n)
 {
     return sCollector ? sCollector->Forget(n) : true;
 }
 
 nsPurpleBufferEntry*
-NS_CycleCollectorSuspect2(nsISupports *n)
+NS_CycleCollectorSuspect2(void *n, nsCycleCollectionParticipant *cp)
 {
     if (sCollector)
-        return sCollector->Suspect2(n);
+        return sCollector->Suspect2(n, cp);
     return nullptr;
 }
 
 bool
 NS_CycleCollectorForget2(nsPurpleBufferEntry *e)
 {
     return sCollector ? sCollector->Forget2(e) : true;
 }
--- a/xpcom/build/nsXPCOM.h
+++ b/xpcom/build/nsXPCOM.h
@@ -353,24 +353,26 @@ NS_LogCOMPtrRelease(void *aCOMPtr, nsISu
  * nsCycleCollectionParticipant to break cycles correctly.
  *
  * The first two functions below exist only to support binary components
  * that were compiled for older XPCOM versions.
  */
 
 #ifdef __cplusplus
 
+class nsCycleCollectionParticipant;
+
 XPCOM_API(bool)
 NS_CycleCollectorSuspect(nsISupports *n);
 
 XPCOM_API(bool)
 NS_CycleCollectorForget(nsISupports *n);
 
 XPCOM_API(nsPurpleBufferEntry*)
-NS_CycleCollectorSuspect2(nsISupports *n);
+NS_CycleCollectorSuspect2(void *n, nsCycleCollectionParticipant *p);
 
 XPCOM_API(bool)
 NS_CycleCollectorForget2(nsPurpleBufferEntry *e);
 
 #endif
 
 /**
  * Categories (in the category manager service) used by XPCOM:
--- a/xpcom/build/nsXPCOMPrivate.h
+++ b/xpcom/build/nsXPCOMPrivate.h
@@ -82,17 +82,17 @@ typedef void       (* LogReleaseFunc)(vo
 typedef void       (* LogCtorFunc)(void*, const char*, uint32_t);
 typedef void       (* LogCOMPtrFunc)(void*, nsISupports*);
 
 typedef nsresult   (* GetXPTCallStubFunc)(REFNSIID, nsIXPTCProxy*, nsISomeInterface**);
 typedef void       (* DestroyXPTCallStubFunc)(nsISomeInterface*);
 typedef nsresult   (* InvokeByIndexFunc)(nsISupports*, uint32_t, uint32_t, nsXPTCVariant*);
 typedef bool       (* CycleCollectorFunc)(nsISupports*);
 typedef nsPurpleBufferEntry*
-                   (* CycleCollectorSuspect2Func)(nsISupports*);
+                   (* CycleCollectorSuspect2Func)(void*, nsCycleCollectionParticipant*);
 typedef bool       (* CycleCollectorForget2Func)(nsPurpleBufferEntry*);
 
 // PRIVATE AND DEPRECATED
 typedef NS_CALLBACK(XPCOMExitRoutine)(void);
 
 typedef nsresult   (* RegisterXPCOMExitRoutineFunc)(XPCOMExitRoutine exitRoutine, uint32_t priority);
 typedef nsresult   (* UnregisterXPCOMExitRoutineFunc)(XPCOMExitRoutine exitRoutine);
 
--- a/xpcom/glue/nsISupportsImpl.h
+++ b/xpcom/glue/nsISupportsImpl.h
@@ -74,46 +74,41 @@ private:
 #define NS_CCAR_TAGGED_TO_REFCNT(tagged_) \
   nsrefcnt(NS_PTR_TO_INT32(tagged_) >> 1)
 #define NS_CCAR_TAGGED_TO_PURPLE_ENTRY(tagged_) \
   static_cast<nsPurpleBufferEntry*>(tagged_)
 #define NS_CCAR_TAGGED_STABILIZED_REFCNT NS_CCAR_PURPLE_ENTRY_TO_TAGGED(0)
 
 // Support for ISupports classes which interact with cycle collector.
 
-/**
- * This struct (once shipped) will be FROZEN with respect to the
- * NS_CycleCollectorSuspect2 and NS_CycleCollectorForget2 functions.  If
- * we need to change the struct, we'll need Suspect3 and Forget3 for the
- * new versions.
- */
 struct nsPurpleBufferEntry {
   union {
-    nsISupports *mObject;                 // when low bit unset
+    void *mObject;                        // when low bit unset
     nsPurpleBufferEntry *mNextInFreeList; // when low bit set
   };
   // When an object is in the purple buffer, it replaces its reference
   // count with a (tagged) pointer to this entry, so we store the
   // reference count for it.
   nsrefcnt mRefCnt;
+  nsCycleCollectionParticipant *mParticipant; // NULL for nsISupports
 };
 
 class nsCycleCollectingAutoRefCnt {
 
 public:
   nsCycleCollectingAutoRefCnt()
     : mTagged(NS_CCAR_REFCNT_TO_TAGGED(0))
   {}
 
   nsCycleCollectingAutoRefCnt(nsrefcnt aValue)
     : mTagged(NS_CCAR_REFCNT_TO_TAGGED(aValue))
   {
   }
 
-  nsrefcnt incr(nsISupports *owner)
+  nsrefcnt incr(void *owner)
   {
     if (NS_UNLIKELY(mTagged == NS_CCAR_TAGGED_STABILIZED_REFCNT)) {
       // The sentinel value "purple bit alone, refcount 0" means
       // that we're stabilized, during finalization. In this
       // state we lie about our actual refcount if anyone asks
       // and say it's 2, which is basically true: the caller who
       // is incrementing has a reference, as does the decr() frame
       // that stabilized-and-is-deleting us.
@@ -146,16 +141,21 @@ public:
 
   void stabilizeForDeletion(nsISupports*)
   {
     mTagged = NS_CCAR_TAGGED_STABILIZED_REFCNT;
   }
 
   nsrefcnt decr(nsISupports *owner)
   {
+    return decr(owner, nullptr);
+  }
+
+  nsrefcnt decr(void *owner, nsCycleCollectionParticipant *p)
+  {
     if (NS_UNLIKELY(mTagged == NS_CCAR_TAGGED_STABILIZED_REFCNT))
       return 1;
 
     nsrefcnt refcount;
     if (IsPurple()) {
       nsPurpleBufferEntry *e = NS_CCAR_TAGGED_TO_PURPLE_ENTRY(mTagged);
       NS_ASSERTION(e->mObject == owner, "wrong entry");
       refcount = e->mRefCnt;
@@ -172,17 +172,17 @@ public:
         e->mRefCnt = refcount;
       }
     } else {
       refcount = NS_CCAR_TAGGED_TO_REFCNT(mTagged);
       --refcount;
 
       nsPurpleBufferEntry *e;
       if (NS_LIKELY(refcount > 0) &&
-          ((e = NS_CycleCollectorSuspect2(owner)))) {
+          ((e = NS_CycleCollectorSuspect2(owner, p)))) {
         e->mRefCnt = refcount;
         mTagged = NS_CCAR_PURPLE_ENTRY_TO_TAGGED(e);
       } else {
         mTagged = NS_CCAR_REFCNT_TO_TAGGED(refcount);
       }
     }
 
     return refcount;
--- a/xpcom/glue/standalone/nsXPCOMGlue.cpp
+++ b/xpcom/glue/standalone/nsXPCOMGlue.cpp
@@ -538,22 +538,22 @@ NS_CycleCollectorForget(nsISupports* obj
 {
     if (!xpcomFunctions.cycleForgetFunc)
         return false;
 
     return xpcomFunctions.cycleForgetFunc(obj);
 }
 
 XPCOM_API(nsPurpleBufferEntry*)
-NS_CycleCollectorSuspect2(nsISupports* obj)
+NS_CycleCollectorSuspect2(void* obj, nsCycleCollectionParticipant *p)
 {
     if (!xpcomFunctions.cycleSuspect2Func)
         return nullptr;
 
-    return xpcomFunctions.cycleSuspect2Func(obj);
+    return xpcomFunctions.cycleSuspect2Func(obj, p);
 }
 
 XPCOM_API(bool)
 NS_CycleCollectorForget2(nsPurpleBufferEntry* e)
 {
     if (!xpcomFunctions.cycleForget2Func)
         return false;
 
--- a/xpcom/stub/nsXPComStub.cpp
+++ b/xpcom/stub/nsXPComStub.cpp
@@ -510,19 +510,19 @@ NS_CycleCollectorSuspect(nsISupports* ob
 EXPORT_XPCOM_API(bool)
 NS_CycleCollectorForget(nsISupports* obj)
 {
   return NS_CycleCollectorForget_P(obj);
 }
 
 #undef NS_CycleCollectorSuspect2
 EXPORT_XPCOM_API(nsPurpleBufferEntry*)
-NS_CycleCollectorSuspect2(nsISupports* obj)
+NS_CycleCollectorSuspect2(void *obj, nsCycleCollectionParticipant *p)
 {
-  return NS_CycleCollectorSuspect2_P(obj);
+  return NS_CycleCollectorSuspect2_P(obj, p);
 }
 
 #undef NS_CycleCollectorForget2
 EXPORT_XPCOM_API(bool)
 NS_CycleCollectorForget2(nsPurpleBufferEntry* e)
 {
   return NS_CycleCollectorForget2_P(e);
 }