Bug 798678 - Add weakmap key preservation support to cycle collector (r=mccr8)
authorBill McCloskey <wmccloskey@mozilla.com>
Wed, 17 Oct 2012 18:22:46 -0700
changeset 110759 1e5bf398fc887b518462e789526ee4159fdaf632
parent 110758 4ca5990b24d5654294d83ae25a428e58282cef5d
child 110760 2f193f44cb99ff2275a14e04f50f0b22f5018c53
push id93
push usernmatsakis@mozilla.com
push dateWed, 31 Oct 2012 21:26:57 +0000
reviewersmccr8
bugs798678
milestone19.0a1
Bug 798678 - Add weakmap key preservation support to cycle collector (r=mccr8)
content/base/src/nsContentUtils.cpp
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/xpconnect/src/nsXPConnect.cpp
xpcom/base/nsCycleCollector.cpp
xpcom/glue/nsCycleCollectionParticipant.h
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -6353,17 +6353,17 @@ public:
                                     nsCycleCollectionParticipant* helper)
   {
   }
 
   NS_IMETHOD_(void) NoteNextEdgeName(const char* name)
   {
   }
 
-  NS_IMETHOD_(void) NoteWeakMapping(void* map, void* key, void* val)
+  NS_IMETHOD_(void) NoteWeakMapping(void* map, void* key, void* kdelegate, void* val)
   {
   }
 
   bool mFound;
 
 private:
   void* mWrapper;
 };
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -527,16 +527,24 @@ js::VisitGrayWrapperTargets(JSCompartmen
 {
     for (WrapperMap::Enum e(comp->crossCompartmentWrappers); !e.empty(); e.popFront()) {
         gc::Cell *thing = e.front().key.wrapped;
         if (thing->isMarked(gc::GRAY))
             callback(closure, thing);
     }
 }
 
+JS_FRIEND_API(JSObject *)
+js::GetWeakmapKeyDelegate(JSObject *key)
+{
+    if (JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp)
+        return op(key);
+    return NULL;
+}
+
 JS_FRIEND_API(void)
 JS_SetAccumulateTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallback callback)
 {
     rt->telemetryCallback = callback;
 }
 
 JS_FRIEND_API(JSObject *)
 JS_CloneObject(JSContext *cx, JSObject *obj_, JSObject *proto_, JSObject *parent_)
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -260,16 +260,19 @@ extern JS_FRIEND_API(bool)
 GCThingIsMarkedGray(void *thing);
 
 typedef void
 (GCThingCallback)(void *closure, void *gcthing);
 
 extern JS_FRIEND_API(void)
 VisitGrayWrapperTargets(JSCompartment *comp, GCThingCallback *callback, void *closure);
 
+extern JS_FRIEND_API(JSObject *)
+GetWeakmapKeyDelegate(JSObject *key);
+
 /*
  * Shadow declarations of JS internal structures, for access by inline access
  * functions below. Do not use these structures in any other way. When adding
  * new fields for access by inline methods, make sure to add static asserts to
  * the original header file to ensure that offsets are consistent.
  */
 namespace shadow {
 
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -393,31 +393,32 @@ struct NoteWeakMapChildrenTracer : publi
 {
     NoteWeakMapChildrenTracer(nsCycleCollectionTraversalCallback &cb)
         : mCb(cb)
     {
     }
     nsCycleCollectionTraversalCallback &mCb;
     JSObject *mMap;
     void *mKey;
+    void *mKeyDelegate;
 };
 
 static void
 TraceWeakMappingChild(JSTracer *trc, void **thingp, JSGCTraceKind kind)
 {
     MOZ_ASSERT(trc->callback == TraceWeakMappingChild);
     void *thing = *thingp;
     NoteWeakMapChildrenTracer *tracer =
         static_cast<NoteWeakMapChildrenTracer *>(trc);
     if (kind == JSTRACE_STRING)
         return;
     if (!xpc_IsGrayGCThing(thing) && !tracer->mCb.WantAllTraces())
         return;
     if (AddToCCKind(kind)) {
-        tracer->mCb.NoteWeakMapping(tracer->mMap, tracer->mKey, thing);
+        tracer->mCb.NoteWeakMapping(tracer->mMap, tracer->mKey, tracer->mKeyDelegate, thing);
     } else {
         JS_TraceChildren(trc, thing, kind);
     }
 }
 
 struct NoteWeakMapsTracer : public js::WeakMapTracer
 {
     NoteWeakMapsTracer(JSRuntime *rt, js::WeakMapTraceCallback cb,
@@ -450,21 +451,26 @@ TraceWeakMapping(js::WeakMapTracer *trc,
 
     // As an emergency fallback for non-debug builds, if the key is not
     // representable in the cycle collector graph, we treat it as marked.  This
     // can cause leaks, but is preferable to ignoring the binding, which could
     // cause the cycle collector to free live objects.
     if (!AddToCCKind(kkind))
         k = nullptr;
 
+    JSObject *kdelegate = NULL;
+    if (kkind == JSTRACE_OBJECT)
+        kdelegate = js::GetWeakmapKeyDelegate((JSObject *)k);
+
     if (AddToCCKind(vkind)) {
-        tracer->mCb.NoteWeakMapping(m, k, v);
+        tracer->mCb.NoteWeakMapping(m, k, kdelegate, v);
     } else {
         tracer->mChildTracer.mMap = m;
         tracer->mChildTracer.mKey = k;
+        tracer->mChildTracer.mKeyDelegate = kdelegate;
         JS_TraceChildren(&tracer->mChildTracer, v, vkind);
     }
 }
 
 nsresult
 nsXPConnect::BeginCycleCollection(nsCycleCollectionTraversalCallback &cb)
 {
     // It is important not to call GetSafeJSContext while on the
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -664,16 +664,17 @@ private:
 };
 
 
 struct WeakMapping
 {
     // map and key will be null if the corresponding objects are GC marked
     PtrInfo *mMap;
     PtrInfo *mKey;
+    PtrInfo *mKeyDelegate;
     PtrInfo *mVal;
 };
 
 class GCGraphBuilder;
 
 struct GCGraph
 {
     NodePool mNodes;
@@ -1673,17 +1674,17 @@ public:
     NS_IMETHOD_(void) NoteNativeRoot(void *root, nsCycleCollectionParticipant *participant);
 
     NS_IMETHOD_(void) NoteXPCOMChild(nsISupports *child);
     NS_IMETHOD_(void) NoteJSChild(void *child);
     NS_IMETHOD_(void) NoteNativeChild(void *child,
                                       nsCycleCollectionParticipant *participant);
 
     NS_IMETHOD_(void) NoteNextEdgeName(const char* name);
-    NS_IMETHOD_(void) NoteWeakMapping(void *map, void *key, void *val);
+    NS_IMETHOD_(void) NoteWeakMapping(void *map, void *key, void *kdelegate, void *val);
 
 private:
     NS_IMETHOD_(void) NoteRoot(void *root,
                                nsCycleCollectionParticipant *participant)
     {
         MOZ_ASSERT(root);
         MOZ_ASSERT(participant);
 
@@ -1963,26 +1964,27 @@ GCGraphBuilder::AddWeakMapNode(void *nod
     if (JSCompartment *comp = MergeCompartment(node)) {
         return AddNode(comp, mJSCompParticipant);
     } else {
         return AddNode(node, mJSParticipant);
     }
 }
 
 NS_IMETHODIMP_(void)
-GCGraphBuilder::NoteWeakMapping(void *map, void *key, void *val)
+GCGraphBuilder::NoteWeakMapping(void *map, void *key, void *kdelegate, void *val)
 {
     PtrInfo *valNode = AddWeakMapNode(val);
 
     if (!valNode)
         return;
 
     WeakMapping *mapping = mWeakMaps.AppendElement();
     mapping->mMap = map ? AddWeakMapNode(map) : nullptr;
     mapping->mKey = key ? AddWeakMapNode(key) : nullptr;
+    mapping->mKeyDelegate = kdelegate ? AddWeakMapNode(kdelegate) : mapping->mKey;
     mapping->mVal = valNode;
 }
 
 static bool
 AddPurpleRoot(GCGraphBuilder &builder, void *root, nsCycleCollectionParticipant *cp)
 {
     CanonicalizeParticipant(&root, &cp);
 
@@ -2016,17 +2018,17 @@ public:
                                              const char *objname) {}
     NS_IMETHOD_(void) DescribeGCedNode(bool ismarked,
                                        const char *objname) {}
     NS_IMETHOD_(void) NoteXPCOMRoot(nsISupports *root) {}
     NS_IMETHOD_(void) NoteJSRoot(void *root) {}
     NS_IMETHOD_(void) NoteNativeRoot(void *root,
                                      nsCycleCollectionParticipant *helper) {}
     NS_IMETHOD_(void) NoteNextEdgeName(const char* name) {}
-    NS_IMETHOD_(void) NoteWeakMapping(void *map, void *key, void *val) {}
+    NS_IMETHOD_(void) NoteWeakMapping(void *map, void *key, void *kdelegate, void *val) {}
     bool MayHaveChild() {
         return mMayHaveChild;
     }
 private:
     bool mMayHaveChild;
 };
 
 NS_IMETHODIMP_(void)
@@ -2201,25 +2203,32 @@ nsCycleCollector::ScanWeakMaps()
     do {
         anyChanged = false;
         for (uint32_t i = 0; i < mGraph.mWeakMaps.Length(); i++) {
             WeakMapping *wm = &mGraph.mWeakMaps[i];
 
             // If mMap or mKey are null, the original object was marked black.
             uint32_t mColor = wm->mMap ? wm->mMap->mColor : black;
             uint32_t kColor = wm->mKey ? wm->mKey->mColor : black;
+            uint32_t kdColor = wm->mKeyDelegate ? wm->mKeyDelegate->mColor : black;
             PtrInfo *v = wm->mVal;
 
             // All non-null weak mapping maps, keys and values are
             // roots (in the sense of WalkFromRoots) in the cycle
             // collector graph, and thus should have been colored
             // either black or white in ScanRoots().
-            NS_ASSERTION(mColor != grey, "Uncolored weak map");
-            NS_ASSERTION(kColor != grey, "Uncolored weak map key");
-            NS_ASSERTION(v->mColor != grey, "Uncolored weak map value");
+            MOZ_ASSERT(mColor != grey, "Uncolored weak map");
+            MOZ_ASSERT(kColor != grey, "Uncolored weak map key");
+            MOZ_ASSERT(kdColor != grey, "Uncolored weak map key delegate");
+            MOZ_ASSERT(v->mColor != grey, "Uncolored weak map value");
+
+            if (mColor == black && kColor != black && kdColor == black) {
+                GraphWalker<ScanBlackVisitor>(ScanBlackVisitor(mWhiteNodeCount)).Walk(wm->mKey);
+                anyChanged = true;
+            }
 
             if (mColor == black && kColor == black && v->mColor != black) {
                 GraphWalker<ScanBlackVisitor>(ScanBlackVisitor(mWhiteNodeCount)).Walk(v);
                 anyChanged = true;
             }
         }
     } while (anyChanged);
 }
@@ -2466,17 +2475,17 @@ public:
     NS_IMETHOD_(void) NoteJSRoot(void *root) {}
     NS_IMETHOD_(void) NoteNativeRoot(void *root,
                                      nsCycleCollectionParticipant *participant) {}
     NS_IMETHOD_(void) NoteXPCOMChild(nsISupports *child) {}
     NS_IMETHOD_(void) NoteJSChild(void *child) {}
     NS_IMETHOD_(void) NoteNativeChild(void *child,
                                      nsCycleCollectionParticipant *participant) {}
     NS_IMETHOD_(void) NoteNextEdgeName(const char* name) {}
-    NS_IMETHOD_(void) NoteWeakMapping(void *map, void *key, void *val) {}
+    NS_IMETHOD_(void) NoteWeakMapping(void *map, void *key, void *kdelegate, void *val) {}
 };
 
 char *Suppressor::sSuppressionList = nullptr;
 bool Suppressor::sInitialized = false;
 
 static bool
 nsCycleCollector_shouldSuppress(nsISupports *s)
 {
--- a/xpcom/glue/nsCycleCollectionParticipant.h
+++ b/xpcom/glue/nsCycleCollectionParticipant.h
@@ -79,17 +79,17 @@ public:
                                       nsCycleCollectionParticipant *helper) = 0;
 
     // Give a name to the edge associated with the next call to
     // NoteXPCOMChild, NoteJSChild, or NoteNativeChild.
     // Callbacks who care about this should set WANT_DEBUG_INFO in the
     // flags.
     NS_IMETHOD_(void) NoteNextEdgeName(const char* name) = 0;
 
-    NS_IMETHOD_(void) NoteWeakMapping(void *map, void *key, void *val) = 0;
+    NS_IMETHOD_(void) NoteWeakMapping(void *map, void *key, void *kdelegate, void *val) = 0;
 
     enum {
         // Values for flags:
 
         // Caller should call NoteNextEdgeName and pass useful objName
         // to DescribeRefCountedNode and DescribeGCedNode.
         WANT_DEBUG_INFO = (1<<0),