Bug 628599. Traverse expando objects. r=jst@mozilla.com, a=blocker
authorPeter Van der Beken <peterv@propagandism.org>
Wed, 02 Feb 2011 14:08:13 -0800
changeset 61809 e039358204f572f20d0c4bded87db468ece08304
parent 61808 f83ed5f4e8857ef49025fb5187c760cbdd563236
child 61812 a8f321ab835c5773360a51b977b8df3283dce262
push id18500
push userjst@mozilla.com
push dateWed, 02 Feb 2011 22:08:45 +0000
treeherdermozilla-central@e039358204f5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjst, blocker
bugs628599
milestone2.0b12pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 628599. Traverse expando objects. r=jst@mozilla.com, a=blocker
js/src/xpconnect/src/xpcjsruntime.cpp
js/src/xpconnect/src/xpcprivate.h
js/src/xpconnect/src/xpcwrappednative.cpp
js/src/xpconnect/src/xpcwrappednativescope.cpp
--- a/js/src/xpconnect/src/xpcjsruntime.cpp
+++ b/js/src/xpconnect/src/xpcjsruntime.cpp
@@ -400,30 +400,31 @@ TraceJSHolder(JSDHashTable *table, JSDHa
 
 struct ClearedGlobalObject : public JSDHashEntryHdr
 {
     JSContext* mContext;
     JSObject* mGlobalObject;
 };
 
 static PLDHashOperator
-TraceExpandos(XPCWrappedNative *wn, JSObject *expando, void *aClosure)
+TraceExpandos(XPCWrappedNative *wn, JSObject *&expando, void *aClosure)
 {
+    if(wn->IsWrapperExpired())
+        return PL_DHASH_REMOVE;
     JS_CALL_OBJECT_TRACER(static_cast<JSTracer *>(aClosure), expando, "expando object");
     return PL_DHASH_NEXT;
 }
 
-
 static PLDHashOperator
-TraceCompartment(nsCStringHashKey& aKey, JSCompartment *compartment, void *aClosure)
+TraceCompartment(xpc::PtrAndPrincipalHashKey *aKey, JSCompartment *compartment, void *aClosure)
 {
     xpc::CompartmentPrivate *priv = (xpc::CompartmentPrivate *)
         JS_GetCompartmentPrivate(static_cast<JSTracer *>(aClosure)->context, compartment);
     if (priv->expandoMap)
-        priv->expandoMap->EnumerateRead(TraceExpandos, (JSContext *)aClosure);
+        priv->expandoMap->Enumerate(TraceExpandos, aClosure);
     return PL_DHASH_NEXT;
 }
 
 void XPCJSRuntime::TraceXPConnectRoots(JSTracer *trc)
 {
     JSContext *iter = nsnull, *acx;
     while ((acx = JS_ContextIterator(GetJSRuntime(), &iter))) {
         JS_ASSERT(acx->hasRunOption(JSOPTION_UNROOTED_GLOBAL));
@@ -440,18 +441,17 @@ void XPCJSRuntime::TraceXPConnectRoots(J
 
     for(XPCRootSetElem *e = mWrappedJSRoots; e ; e = e->GetNextRoot())
         static_cast<nsXPCWrappedJS*>(e)->TraceJS(trc);
 
     if(mJSHolders.ops)
         JS_DHashTableEnumerate(&mJSHolders, TraceJSHolder, trc);
 
     // Trace compartments.
-    GetCompartmentMap().EnumerateRead((XPCCompartmentMap::EnumReadFunction)
-                                      TraceCompartment, trc);
+    GetCompartmentMap().EnumerateRead(TraceCompartment, trc);
 }
 
 struct Closure
 {
     JSContext *cx;
     bool cycleCollectionEnabled;
     nsCycleCollectionTraversalCallback *cb;
 };
@@ -485,16 +485,62 @@ NoteJSHolder(JSDHashTable *table, JSDHas
         return JS_DHASH_NEXT;
 
     closure->cb->NoteRoot(nsIProgrammingLanguage::CPLUSPLUS, entry->holder,
                           entry->tracer);
 
     return JS_DHASH_NEXT;
 }
 
+// static
+void
+XPCJSRuntime::SuspectWrappedNative(JSContext *cx, XPCWrappedNative *wrapper,
+                                   nsCycleCollectionTraversalCallback &cb)
+{
+    if(!wrapper->IsValid() || wrapper->IsWrapperExpired())
+        return;
+
+    NS_ASSERTION(NS_IsMainThread() || NS_IsCycleCollectorThread(), 
+                 "Suspecting wrapped natives from non-CC thread");
+
+    // Only suspect wrappedJSObjects that are in a compartment that
+    // participates in cycle collection.
+    JSObject* obj = wrapper->GetFlatJSObjectAndMark();
+    if(!xpc::ParticipatesInCycleCollection(cx, obj))
+        return;
+
+    NS_ASSERTION(!JS_IsAboutToBeFinalized(cx, obj),
+                 "SuspectWrappedNative attempting to touch dead object");
+
+    // Only record objects that might be part of a cycle as roots, unless
+    // the callback wants all traces (a debug feature).
+    if(nsXPConnect::IsGray(obj) || cb.WantAllTraces())
+        cb.NoteRoot(nsIProgrammingLanguage::JAVASCRIPT, obj,
+                    nsXPConnect::GetXPConnect());
+}
+
+static PLDHashOperator
+SuspectExpandos(XPCWrappedNative *wrapper, JSObject *&expando, void *arg)
+{
+    Closure* closure = static_cast<Closure*>(arg);
+    XPCJSRuntime::SuspectWrappedNative(closure->cx, wrapper, *closure->cb);
+
+    return PL_DHASH_NEXT;
+}
+
+static PLDHashOperator
+SuspectCompartment(xpc::PtrAndPrincipalHashKey *key, JSCompartment *compartment, void *arg)
+{
+    Closure* closure = static_cast<Closure*>(arg);
+    xpc::CompartmentPrivate *priv = (xpc::CompartmentPrivate *)
+        JS_GetCompartmentPrivate(closure->cx, compartment);
+    if (priv->expandoMap)
+        priv->expandoMap->Enumerate(SuspectExpandos, arg);
+    return PL_DHASH_NEXT;
+}
 
 void
 XPCJSRuntime::AddXPConnectRoots(JSContext* cx,
                                 nsCycleCollectionTraversalCallback &cb)
 {
     // For all JS objects that are held by native objects but aren't held
     // through rooting or locking, we need to add all the native objects that
     // hold them so that the JS objects are colored correctly in the cycle
@@ -531,21 +577,24 @@ XPCJSRuntime::AddXPConnectRoots(JSContex
         // Only suspect wrappedJSObjects that are in a compartment that
         // participates in cycle collection.
         if(!xpc::ParticipatesInCycleCollection(cx, obj))
             continue;
 
         cb.NoteXPCOMRoot(static_cast<nsIXPConnectWrappedJS *>(wrappedJS));
     }
 
+    Closure closure = { cx, PR_TRUE, &cb };
     if(mJSHolders.ops)
     {
-        Closure closure = { cx, PR_TRUE, &cb };
         JS_DHashTableEnumerate(&mJSHolders, NoteJSHolder, &closure);
     }
+
+    // Suspect wrapped natives with expando objects.
+    GetCompartmentMap().EnumerateRead(SuspectCompartment, &closure);
 }
 
 void
 XPCJSRuntime::ClearWeakRoots()
 {
     JSContext *iter = nsnull, *acx;
 
     while((acx = JS_ContextIterator(GetJSRuntime(), &iter)))
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -595,16 +595,21 @@ public:
     virtual nsresult FinishTraverse();
     virtual nsresult FinishCycleCollection();
     virtual nsCycleCollectionParticipant *ToParticipant(void *p);
     virtual void Collect();
 #ifdef DEBUG_CC
     virtual void PrintAllReferencesTo(void *p);
 #endif
 
+    XPCCallContext *GetCycleCollectionContext()
+    {
+        return mCycleCollectionContext;
+    }
+
     unsigned GetOutstandingRequests(JSContext* cx);
 
     // This returns the singleton nsCycleCollectionParticipant for JSContexts.
     static nsCycleCollectionParticipant *JSContextParticipant();
 
 #ifndef XPCONNECT_STANDALONE
     virtual nsIPrincipal* GetPrincipal(JSObject* obj,
                                        PRBool allowShortCircuit) const;
@@ -799,16 +804,19 @@ public:
     inline void AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS);
     inline void AddObjectHolderRoot(XPCJSObjectHolder* holder);
 
     nsresult AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer);
     nsresult RemoveJSHolder(void* aHolder);
 
     void ClearWeakRoots();
 
+    static void SuspectWrappedNative(JSContext *cx, XPCWrappedNative *wrapper,
+                                     nsCycleCollectionTraversalCallback &cb);
+
     void DebugDump(PRInt16 depth);
 
     void SystemIsBeingShutDown(JSContext* cx);
 
     PRThread* GetThreadRunningGC() const {return mThreadRunningGC;}
 
     ~XPCJSRuntime();
 
--- a/js/src/xpconnect/src/xpcwrappednative.cpp
+++ b/js/src/xpconnect/src/xpcwrappednative.cpp
@@ -60,16 +60,36 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(XPCWrappe
 NS_IMETHODIMP
 NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::RootAndUnlinkJSObjects(void *p)
 {
     XPCWrappedNative *tmp = static_cast<XPCWrappedNative*>(p);
     tmp->ExpireWrapper();
     return NS_OK;
 }
 
+struct TraverseExpandoObjectClosure
+{
+    JSContext *cx;
+    XPCWrappedNative *wn;
+    nsCycleCollectionTraversalCallback &cb;
+};
+
+static PLDHashOperator
+TraverseExpandoObjects(xpc::PtrAndPrincipalHashKey *aKey, JSCompartment *compartment, void *aClosure)
+{
+    TraverseExpandoObjectClosure *closure = static_cast<TraverseExpandoObjectClosure*>(aClosure);
+    xpc::CompartmentPrivate *priv = 
+        (xpc::CompartmentPrivate *)JS_GetCompartmentPrivate(closure->cx, compartment);
+
+    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(closure->cb, "XPCWrappedNative expando object");
+    closure->cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
+                                priv->LookupExpandoObject(closure->wn));
+
+    return PL_DHASH_NEXT;
+}
 
 NS_IMETHODIMP
 NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Traverse(void *p,
                                                           nsCycleCollectionTraversalCallback &cb)
 {
     XPCWrappedNative *tmp = static_cast<XPCWrappedNative*>(p);
     if(!tmp->IsValid())
         return NS_OK;
@@ -103,16 +123,24 @@ NS_CYCLE_COLLECTION_CLASSNAME(XPCWrapped
         // on our wiki at http://wiki.mozilla.org/XPConnect_object_wrapping 
 
         JSObject *obj = nsnull;
         nsresult rv = tmp->GetJSObject(&obj);
         if(NS_SUCCEEDED(rv))
             cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, obj);
     }
 
+    XPCJSRuntime *rt = tmp->GetRuntime();
+    TraverseExpandoObjectClosure closure = {
+         rt->GetXPConnect()->GetCycleCollectionContext()->GetJSContext(),
+         tmp,
+         cb
+    };
+    rt->GetCompartmentMap().EnumerateRead(TraverseExpandoObjects, &closure);
+
     // XPCWrappedNative keeps its native object alive.
     cb.NoteXPCOMChild(tmp->GetIdentityObject());
 
     tmp->NoteTearoffs(cb);
 
     return NS_OK;
 }
 
--- a/js/src/xpconnect/src/xpcwrappednativescope.cpp
+++ b/js/src/xpconnect/src/xpcwrappednativescope.cpp
@@ -395,42 +395,22 @@ struct SuspectClosure
     JSContext* cx;
     nsCycleCollectionTraversalCallback& cb;
 };
 
 static JSDHashOperator
 WrappedNativeSuspecter(JSDHashTable *table, JSDHashEntryHdr *hdr,
                        uint32 number, void *arg)
 {
-    SuspectClosure* closure = static_cast<SuspectClosure*>(arg);
     XPCWrappedNative* wrapper = ((Native2WrappedNativeMap::Entry*)hdr)->value;
 
-    if(wrapper->IsValid() &&
-       wrapper->HasExternalReference() &&
-       !wrapper->IsWrapperExpired())
+    if(wrapper->HasExternalReference())
     {
-        NS_ASSERTION(NS_IsMainThread() || NS_IsCycleCollectorThread(), 
-                     "Suspecting wrapped natives from non-CC thread");
-
-        // Only suspect wrappedJSObjects that are in a compartment that
-        // participates in cycle collection.
-        JSObject* obj = wrapper->GetFlatJSObjectAndMark();
-        if(!xpc::ParticipatesInCycleCollection(closure->cx, obj))
-            return JS_DHASH_NEXT;
-
-        NS_ASSERTION(!JS_IsAboutToBeFinalized(closure->cx, obj),
-                     "WrappedNativeSuspecter attempting to touch dead object");
-
-        // Only record objects that might be part of a cycle as roots, unless
-        // the callback wants all traces (a debug feature).
-        if(!(closure->cb.WantAllTraces()) && !nsXPConnect::IsGray(obj))
-            return JS_DHASH_NEXT;
-
-        closure->cb.NoteRoot(nsIProgrammingLanguage::JAVASCRIPT, obj,
-                             nsXPConnect::GetXPConnect());
+        SuspectClosure* closure = static_cast<SuspectClosure*>(arg);
+        XPCJSRuntime::SuspectWrappedNative(closure->cx, wrapper, closure->cb);
     }
 
     return JS_DHASH_NEXT;
 }
 
 // static
 void
 XPCWrappedNativeScope::SuspectAllWrappers(XPCJSRuntime* rt, JSContext* cx,