xpconnect-finalizers
author Benjamin Smedberg <benjamin@smedbergs.us>
Sat, 26 Jul 2008 22:49:39 -0400
changeset 167 a4da40849f5436e629c5732f4368c6c48189637f
parent 164 fa6388d0a0da8b02a6de7c99bb09ca58d2dd2013
permissions -rw-r--r--
State as of now

* * *
* * *
* * *

diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -51,10 +51,12 @@
 #include "jsobj.h"
 #include "jsscript.h"
 #include "nsThreadUtilsInternal.h"
+#include "nsIObserverService.h"
 
-NS_IMPL_THREADSAFE_ISUPPORTS3(nsXPConnect,
+NS_IMPL_THREADSAFE_ISUPPORTS4(nsXPConnect,
                               nsIXPConnect,
                               nsISupportsWeakReference,
+                              nsIObserver,
                               nsIThreadObserver)
 
 nsXPConnect* nsXPConnect::gSelf = nsnull;
@@ -87,7 +89,14 @@ nsXPConnect::nsXPConnect()
     // then we'll set this up later as needed.
     CreateRuntime();
 
+    gSelf = this;
+    NS_SetGlobalThreadObserver(this);
+    NS_RootUntilShutdown(this);
+
     CallGetService(XPC_CONTEXT_STACK_CONTRACTID, &mContextStack);
+
+    nsIObserverService* obs = do_GetService("@mozilla.org/observer-service;1");
+    obs->AddObserver(this, "xpcom-shutdown-loaders", PR_FALSE);
 
 #ifdef XPC_TOOLS_SUPPORT
   {
@@ -118,31 +127,22 @@ nsXPConnect::nsXPConnect()
         gReportAllJSExceptions = 1;
 }
 
+NS_IMETHODIMP
+nsXPConnect::Observe(nsISupports* subject, const char *topic,
+                     const PRUnichar *data)
+{
+    NS_ASSERTION(!strcmp(topic, "xpcom-shutdown-loaders"),
+                 "Unexpected topic");
+
+    mShuttingDown = JS_TRUE;
+    XPCWrappedNativeScope::SystemIsBeingShutDown();
+    XPCPerThreadData::CleanupAllThreads();
+    return NS_OK;
+}
+
 nsXPConnect::~nsXPConnect()
 {
-    JSContext *cx = nsnull;
-    if (mRuntime) {
-        // Create our own JSContext rather than an XPCCallContext, since
-        // otherwise we will create a new safe JS context and attach a
-        // components object that won't get GCed.
-        // And do this before calling CleanupAllThreads, so that we
-        // don't create an extra xpcPerThreadData.
-        cx = JS_NewContext(mRuntime->GetJSRuntime(), 8192);
-    }
-
-    XPCPerThreadData::CleanupAllThreads();
-    mShuttingDown = JS_TRUE;
-    if (cx) {
-        JS_BeginRequest(cx);
-
-        // XXX Call even if |mRuntime| null?
-        XPCWrappedNativeScope::SystemIsBeingShutDown(cx);
-
-        mRuntime->SystemIsBeingShutDown(cx);
-
-        JS_EndRequest(cx);
-        JS_DestroyContext(cx);
-    }
+    NS_ASSERTION(mShuttingDown, "Shutdown not called!");
 
     NS_IF_RELEASE(mContextStack);
     NS_IF_RELEASE(mDefaultSecurityManager);
@@ -166,26 +166,8 @@ nsXPConnect::GetXPConnect()
     {
         if(gOnceAliveNowDead)
             return nsnull;
-        gSelf = new nsXPConnect();
-        if(!gSelf)
-            return nsnull;
 
-        if(!gSelf->mInterfaceInfoManager ||
-           !gSelf->mContextStack)
-        {
-            // ctor failed to create an acceptable instance
-            delete gSelf;
-            gSelf = nsnull;
-        }
-        else
-        {
-            // Initial extra ref to keep the singleton alive
-            // balanced by explicit call to ReleaseXPConnectSingleton()
-            if (NS_FAILED(NS_SetGlobalThreadObserver(gSelf))) {
-                NS_RELEASE(gSelf);
-                // Fall through to returning null
-            }
-        }
+        (void) new nsXPConnect(); // sets gSelf
     }
     return gSelf;
 }
diff --git a/js/src/xpconnect/src/xpccallcontext.cpp b/js/src/xpconnect/src/xpccallcontext.cpp
--- a/js/src/xpconnect/src/xpccallcontext.cpp
+++ b/js/src/xpconnect/src/xpccallcontext.cpp
@@ -289,20 +289,6 @@ XPCCallContext::CanCallNow()
     return NS_OK;
 }
 
-void
-XPCCallContext::SystemIsBeingShutDown()
-{
-    // XXX This is pretty questionable since the per thread cleanup stuff
-    // can be making this call on one thread for call contexts on another
-    // thread.
-    NS_WARNING("Shutting Down XPConnect even through there is a live XPCCallContext");
-    mThreadData = nsnull;
-    mXPCContext = nsnull;
-    mState = SYSTEM_SHUTDOWN;
-    if(mPrevCallContext)
-        mPrevCallContext->SystemIsBeingShutDown();
-}
-
 XPCCallContext::~XPCCallContext()
 {
     // do cleanup...
diff --git a/js/src/xpconnect/src/xpccomponents.cpp b/js/src/xpconnect/src/xpccomponents.cpp
--- a/js/src/xpconnect/src/xpccomponents.cpp
+++ b/js/src/xpconnect/src/xpccomponents.cpp
@@ -3818,21 +3818,6 @@ nsXPCComponents::nsXPCComponents()
 
 nsXPCComponents::~nsXPCComponents()
 {
-    ClearMembers();
-}
-
-void
-nsXPCComponents::ClearMembers()
-{
-    NS_IF_RELEASE(mInterfaces);
-    NS_IF_RELEASE(mInterfacesByID);
-    NS_IF_RELEASE(mClasses);
-    NS_IF_RELEASE(mClassesByID);
-    NS_IF_RELEASE(mResults);
-    NS_IF_RELEASE(mID);
-    NS_IF_RELEASE(mException);
-    NS_IF_RELEASE(mConstructor);
-    NS_IF_RELEASE(mUtils);
 }
 
 /*******************************************/
diff --git a/js/src/xpconnect/src/xpcjsid.cpp b/js/src/xpconnect/src/xpcjsid.cpp
--- a/js/src/xpconnect/src/xpcjsid.cpp
+++ b/js/src/xpconnect/src/xpcjsid.cpp
@@ -339,6 +339,8 @@ JSBool xpc_InitJSxIDClassObjects()
                                      (void**)&NS_CLASSINFO_NAME(nsJSIID));
         if(NS_FAILED(rv))
             goto return_failure;
+
+        NS_RootUntilShutdown(factory);
     }
 
     if(!NS_CLASSINFO_NAME(nsJSCID))
@@ -351,11 +353,15 @@ JSBool xpc_InitJSxIDClassObjects()
                                      (void**)&NS_CLASSINFO_NAME(nsJSCID));
         if(NS_FAILED(rv))
             goto return_failure;
+
+        NS_RootUntilShutdown(factory);
     }
 
     gSharedScriptableHelperForJSIID = new SharedScriptableHelperForJSIID();
     if(!gSharedScriptableHelperForJSIID)
         goto return_failure;
+
+    NS_RootUntilShutdown(gSharedScriptableHelperForJSIID);
 
     gClassObjectsWereInited = JS_TRUE;
     return JS_TRUE;
diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp
--- a/js/src/xpconnect/src/xpcjsruntime.cpp
+++ b/js/src/xpconnect/src/xpcjsruntime.cpp
@@ -98,26 +98,11 @@ NativeInterfaceGC(JSDHashTable *table, J
     XPCNativeInterface* iface = ((IID2NativeInterfaceMap::Entry*)hdr)->value;
     iface->DealWithDyingGCThings(data->cx, data->rt);
 
-    if(iface->IsMarked())
+    if(NS_GetGC()->GetMark(iface))
     {
         return JS_DHASH_NEXT;
     }
 
-    return JS_DHASH_REMOVE;
-}
-
-// *Some* NativeSets are referenced from mClassInfo2NativeSetMap.
-// *All* NativeSets are referenced from mNativeSetMap.
-// So, in mClassInfo2NativeSetMap we just clear references to the unmarked.
-// In mNativeSetMap we clear the references to the unmarked *and* delete them.
-
-JS_STATIC_DLL_CALLBACK(JSDHashOperator)
-NativeUnMarkedSetRemover(JSDHashTable *table, JSDHashEntryHdr *hdr,
-                         uint32 number, void *arg)
-{
-    XPCNativeSet* set = ((ClassInfo2NativeSetMap::Entry*)hdr)->value;
-    if(set->IsMarked())
-        return JS_DHASH_NEXT;
     return JS_DHASH_REMOVE;
 }
 
@@ -267,8 +252,7 @@ JSBool XPCJSRuntime::GCCallback(JSContex
 
                 // Do the sweeping...
 
-                self->mClassInfo2NativeSetMap->
-                    Enumerate(NativeUnMarkedSetRemover, nsnull);
+                self->mClassInfo2NativeSetMap->UnmapDyingSets();
 
 #ifdef XPC_REPORT_NATIVE_INTERFACE_AND_SET_FLUSHING
                 int setsAfter = (int) self->mNativeSetMap->Count();
@@ -320,36 +304,6 @@ DEBUG_WrapperChecker(JSDHashTable *table
 }
 #endif
 
-JS_STATIC_DLL_CALLBACK(JSDHashOperator)
-WrappedJSShutdownMarker(JSDHashTable *table, JSDHashEntryHdr *hdr,
-                        uint32 number, void *arg)
-{
-    JSRuntime* rt = (JSRuntime*) arg;
-    nsXPCWrappedJS* wrapper = ((JSObject2WrappedJSMap::Entry*)hdr)->value;
-    NS_ASSERTION(wrapper, "found a null JS wrapper!");
-    NS_ASSERTION(wrapper->IsValid(), "found an invalid JS wrapper!");
-    wrapper->SystemIsBeingShutDown(rt);
-    return JS_DHASH_NEXT;
-}
-
-JS_STATIC_DLL_CALLBACK(JSDHashOperator)
-DetachedWrappedNativeProtoShutdownMarker(JSDHashTable *table, JSDHashEntryHdr *hdr,
-                                         uint32 number, void *arg)
-{
-    XPCWrappedNativeProto* proto = 
-        (XPCWrappedNativeProto*)((JSDHashEntryStub*)hdr)->key;
-
-    proto->SystemIsBeingShutDown((JSContext*)arg);
-    return JS_DHASH_NEXT;
-}
-
-void XPCJSRuntime::SystemIsBeingShutDown(JSContext* cx)
-{
-    if(mDetachedWrappedNativeProtoMap)
-        mDetachedWrappedNativeProtoMap->
-            Enumerate(DetachedWrappedNativeProtoShutdownMarker, cx);
-}
-
 XPCJSRuntime::~XPCJSRuntime()
 {
 #ifdef XPC_DUMP_AT_SHUTDOWN
@@ -379,7 +333,6 @@ XPCJSRuntime::~XPCJSRuntime()
         if(count)
             printf("deleting XPCJSRuntime with %d live wrapped JSObject\n", (int)count);
 #endif
-        mWrappedJSMap->Enumerate(WrappedJSShutdownMarker, gJSRuntime);
         delete mWrappedJSMap;
     }
 
@@ -459,16 +412,6 @@ XPCJSRuntime::~XPCJSRuntime()
         delete mNativeScriptableSharedMap;
     }
 
-    if(mDyingWrappedNativeProtoMap)
-    {
-#ifdef XPC_DUMP_AT_SHUTDOWN
-        uint32 count = mDyingWrappedNativeProtoMap->Count();
-        if(count)
-            printf("deleting XPCJSRuntime with %d live but dying XPCWrappedNativeProto\n", (int)count);
-#endif
-        delete mDyingWrappedNativeProtoMap;
-    }
-
     if(mDetachedWrappedNativeProtoMap)
     {
 #ifdef XPC_DUMP_AT_SHUTDOWN
@@ -510,7 +453,6 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
    mNativeSetMap(NativeSetMap::newMap(XPC_NATIVE_SET_MAP_SIZE)),
    mThisTranslatorMap(IID2ThisTranslatorMap::newMap(XPC_THIS_TRANSLATOR_MAP_SIZE)),
    mNativeScriptableSharedMap(XPCNativeScriptableSharedMap::newMap(XPC_NATIVE_JSCLASS_MAP_SIZE)),
-   mDyingWrappedNativeProtoMap(XPCWrappedNativeProtoMap::newMap(XPC_DYING_NATIVE_PROTO_MAP_SIZE)),
    mDetachedWrappedNativeProtoMap(XPCWrappedNativeProtoMap::newMap(XPC_DETACHED_NATIVE_PROTO_MAP_SIZE)),
    mExplicitNativeWrapperMap(XPCNativeWrapperMap::newMap(XPC_NATIVE_WRAPPER_MAP_SIZE)),
    mMapLock(XPCAutoLock::NewLock("XPCJSRuntime::mMapLock")),
@@ -566,7 +508,6 @@ XPCJSRuntime::newXPCJSRuntime(nsXPConnec
        self->GetNativeSetMap()               &&
        self->GetThisTranslatorMap()          &&
        self->GetNativeScriptableSharedMap()  &&
-       self->GetDyingWrappedNativeProtoMap() &&
        self->GetExplicitNativeWrapperMap()   &&
        self->GetMapLock())
     {
diff --git a/js/src/xpconnect/src/xpcmaps.cpp b/js/src/xpconnect/src/xpcmaps.cpp
--- a/js/src/xpconnect/src/xpcmaps.cpp
+++ b/js/src/xpconnect/src/xpcmaps.cpp
@@ -316,6 +316,24 @@ ClassInfo2NativeSetMap::~ClassInfo2Nativ
 {
     if(mTable)
         JS_DHashTableDestroy(mTable);
+}
+
+JSDHashOperator
+ClassInfo2NativeSetMap::DyingUnmapper(JSDHashTable *table,
+                                      JSDHashEntryHdr *hdr,
+                                      uint32 number,
+                                      void *arg)
+{
+    Entry *entry = static_cast<Entry*>(hdr);
+    if (NS_GetGC()->GetMark(entry->value))
+    {
+        NS_ASSERTION(NS_GetGC()->GetMark(entry->key),
+                     "XPCNativeSet should keep classinfo alive!");
+
+        return JS_DHASH_NEXT;
+    }
+
+    return JS_DHASH_REMOVE;
 }
 
 /***************************************************************************/
@@ -645,6 +663,13 @@ XPCNativeScriptableSharedMap::DyingUnmap
 {
     Entry *entry = static_cast<Entry*>(hdr);
 
+    if (!entry->key) {
+        // If we GC while allocating a new nativescriptableshared above,
+        // in GetNewOrUsed, we can end up with an entry that is not yet fully
+        // populated
+        return JS_DHASH_NEXT;
+    }
+
     if(NS_GetGC()->GetMark(entry->key))
     {
         return JS_DHASH_NEXT;
diff --git a/js/src/xpconnect/src/xpcmaps.h b/js/src/xpconnect/src/xpcmaps.h
--- a/js/src/xpconnect/src/xpcmaps.h
+++ b/js/src/xpconnect/src/xpcmaps.h
@@ -386,20 +386,22 @@ public:
         return set;
     }
 
-    inline void Remove(nsIClassInfo* info)
+    inline uint32 Count() {return mTable->entryCount;}
+
+    inline void UnmapDyingSets()
     {
-        NS_PRECONDITION(info,"bad param");
-        JS_DHashTableOperate(mTable, info, JS_DHASH_REMOVE);
+        JS_DHashTableEnumerate(mTable, DyingUnmapper, nsnull);
     }
-
-    inline uint32 Count() {return mTable->entryCount;}
-    inline uint32 Enumerate(JSDHashEnumerator f, void *arg)
-        {return JS_DHashTableEnumerate(mTable, f, arg);}
 
     ~ClassInfo2NativeSetMap();
 private:
     ClassInfo2NativeSetMap();    // no implementation
     ClassInfo2NativeSetMap(int size);
+
+    static JSDHashOperator DyingUnmapper(JSDHashTable *table,
+                                         JSDHashEntryHdr *hdr,
+                                         uint32 number,
+                                         void *arg);
 private:
     JSDHashTable *mTable;
 };
diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -62,6 +62,7 @@
 #include "nsIComponentRegistrar.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIGenericFactory.h"
+#include "nsIObserver.h"
 #include "nsMemory.h"
 #include "nsIXPConnect.h"
 #include "nsIInterfaceInfo.h"
@@ -445,6 +446,7 @@ class nsXPConnect : public XPCOMGCFinali
 class nsXPConnect : public XPCOMGCFinalizedObject,
                     public nsIXPConnect,
                     public nsIThreadObserver,
+                    public nsIObserver,
                     public nsSupportsWeakReference
 {
 public:
@@ -452,6 +454,7 @@ public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIXPCONNECT
     NS_DECL_NSITHREADOBSERVER
+    NS_DECL_NSIOBSERVER
 
     // non-interface implementation
 public:
@@ -604,9 +607,6 @@ public:
     XPCNativeScriptableSharedMap* GetNativeScriptableSharedMap() const
         {return mNativeScriptableSharedMap;}
 
-    XPCWrappedNativeProtoMap* GetDyingWrappedNativeProtoMap() const
-        {return mDyingWrappedNativeProtoMap;}
-
     XPCWrappedNativeProtoMap* GetDetachedWrappedNativeProtoMap() const
         {return mDetachedWrappedNativeProtoMap;}
 
@@ -673,8 +673,6 @@ public:
 
     void DebugDump(PRInt16 depth);
 
-    void SystemIsBeingShutDown(JSContext* cx);
-
     PRThread* GetThreadRunningGC() const {return mThreadRunningGC;}
 
     ~XPCJSRuntime();
@@ -722,7 +720,6 @@ private:
     NativeSetMap*            mNativeSetMap;
     IID2ThisTranslatorMap*   mThisTranslatorMap;
     XPCNativeScriptableSharedMap* mNativeScriptableSharedMap;
-    XPCWrappedNativeProtoMap* mDyingWrappedNativeProtoMap;
     XPCWrappedNativeProtoMap* mDetachedWrappedNativeProtoMap;
     XPCNativeWrapperMap*     mExplicitNativeWrapperMap;
     XPCLock* mMapLock;
@@ -975,8 +972,6 @@ public:
 
     nsresult  CanCallNow();
 
-    void SystemIsBeingShutDown();
-
     operator JSContext*() const {return GetJSContext();}
 
     XPCReadableJSStringWrapper *NewStringWrapper(PRUnichar *str, PRUint32 len);
@@ -1203,7 +1198,7 @@ public:
                         JSBool OKIfNotInitialized = JS_FALSE);
 
     static void
-    SystemIsBeingShutDown(JSContext* cx);
+    SystemIsBeingShutDown();
 
     static void
     FinishedMarkPhaseOfGC(JSContext* cx, XPCJSRuntime* rt);
@@ -1403,12 +1398,12 @@ public:
 
     void DebugDump(PRInt16 depth);
 
-#define XPC_NATIVE_IFACE_MARK_FLAG ((PRUint16)JS_BIT(15)) // only high bit of 16 is set
-
+#if 0
     JSBool IsMarked() const
     {
         return 0 != NS_GetGC()->GetMark(this);
     }
+#endif
 
 protected:
     static XPCNativeInterface* NewInstance(XPCCallContext& ccx,
@@ -1505,7 +1500,7 @@ public:
                                       XPCNativeInterface* newInterface,
                                       PRUint16 position);
 
-    static void ClearCacheEntryForClassInfo(nsIClassInfo* classInfo);
+    // static void ClearCacheEntryForClassInfo(nsIClassInfo* classInfo);
 
     inline JSBool FindMember(jsval name, XPCNativeMember** pMember,
                              PRUint16* pInterfaceIndex) const;
@@ -1528,7 +1523,7 @@ public:
 
     PRUint16 GetMemberCount() const {return mMemberCount;}
     PRUint16 GetInterfaceCount() const
-        {NS_ASSERTION(!IsMarked(), "bad"); return mInterfaceCount;}
+        { return mInterfaceCount;}
     XPCNativeInterface** GetInterfaceArray() {return mInterfaces;}
 
     XPCNativeInterface* GetInterfaceAt(PRUint16 i)
@@ -1537,15 +1532,7 @@ public:
     inline JSBool MatchesSetUpToInterface(const XPCNativeSet* other,
                                           XPCNativeInterface* iface) const;
 
-#define XPC_NATIVE_SET_MARK_FLAG ((PRUint16)JS_BIT(15)) // only high bit of 16 is set
-
-private:
-    void MarkSelfOnly() {mInterfaceCount |= XPC_NATIVE_SET_MARK_FLAG;}
 public:
-    void Unmark()       {mInterfaceCount &= ~XPC_NATIVE_SET_MARK_FLAG;}
-    JSBool IsMarked() const
-                  {return 0 != (mInterfaceCount & XPC_NATIVE_SET_MARK_FLAG);}
-
     void DebugDump(PRInt16 depth);
 
 protected:
@@ -1821,13 +1808,7 @@ public:
     void SetScriptableInfo(XPCNativeScriptableInfo* si)
         {NS_ASSERTION(!mScriptableInfo, "leak here!"); mScriptableInfo = si;}
 
-    void JSProtoObjectFinalized(JSContext *cx, JSObject *obj);
-
-    void SystemIsBeingShutDown(JSContext* cx);
-
     void DebugDump(PRInt16 depth);
-
-    ~XPCWrappedNativeProto();
 
 protected:
     // disable copy ctor and assignment
@@ -1880,8 +1861,6 @@ public:
     void SetInterface(XPCNativeInterface*  Interface) {mInterface = Interface;}
     void SetNative(nsISupports*  Native)              {mNative = Native;}
     void SetJSObject(JSObject*  JSObj);
-
-    void JSObjectFinalized() {SetJSObject(nsnull);}
 
 #ifdef XPC_IDISPATCH_SUPPORT
     enum JSObject_flags
@@ -2086,10 +2065,6 @@ public:
                            JSObject* aNewParent,
                            nsISupports* aCOMObj,
                            XPCWrappedNative** aWrapper);
-
-    void FlatJSObjectFinalized(JSContext *cx);
-
-    void SystemIsBeingShutDown(JSContext* cx);
 
 #ifdef XPC_DETECT_LEADING_UPPERCASE_ACCESS_ERRORS
     // This will try to find a member that is of the form "camelCased"
@@ -2376,7 +2351,6 @@ public:
     nsXPCWrappedJS* FindInherited(REFNSIID aIID);
 
     JSBool IsValid() const {return mJSObj != nsnull;}
-    void SystemIsBeingShutDown(JSRuntime* rt);
 
     JSBool IsAggregatedToNative() const {return mRoot->mOuter != nsnull;}
     nsISupports* GetAggregatedNativeObject() const {return mRoot->mOuter;}
@@ -3182,13 +3156,10 @@ public:
                               XPCWrappedNativeScope* aScope,
                               JSObject* aGlobal);
 
-    void SystemIsBeingShutDown() {ClearMembers();}
-
     virtual ~nsXPCComponents();
 
 private:
     nsXPCComponents();
-    void ClearMembers();
 
 private:
     nsXPCComponents_Interfaces*     mInterfaces;
diff --git a/js/src/xpconnect/src/xpcthreadcontext.cpp b/js/src/xpconnect/src/xpcthreadcontext.cpp
--- a/js/src/xpconnect/src/xpcthreadcontext.cpp
+++ b/js/src/xpconnect/src/xpcthreadcontext.cpp
@@ -483,9 +483,6 @@ XPCPerThreadData::Cleanup()
     NS_IF_RELEASE(mException);
     delete mJSContextStack;
     mJSContextStack = nsnull;
-
-    if(mCallContext)
-        mCallContext->SystemIsBeingShutDown();
 }
 
 XPCPerThreadData::~XPCPerThreadData()
@@ -617,6 +614,9 @@ void
 void
 XPCPerThreadData::CleanupAllThreads()
 {
+    NS_ASSERTION(gThreads && !gThreads->mNextThread,
+                 "Only one thread should be alive at shutdown!");
+
     // I've questioned the sense of cleaning up other threads' data from the
     // start. But I got talked into it. Now I see that we *can't* do all the
     // cleaup while holding this lock. So, we are going to go to the trouble
diff --git a/js/src/xpconnect/src/xpcwrappedjs.cpp b/js/src/xpconnect/src/xpcwrappedjs.cpp
--- a/js/src/xpconnect/src/xpcwrappedjs.cpp
+++ b/js/src/xpconnect/src/xpcwrappedjs.cpp
@@ -368,26 +368,6 @@ nsXPCWrappedJS::GetInterfaceIID(nsIID** 
     return *iid ? NS_OK : NS_ERROR_UNEXPECTED;
 }
 
-void
-nsXPCWrappedJS::SystemIsBeingShutDown(JSRuntime* rt)
-{
-    // XXX It turns out that it is better to leak here then to do any Releases
-    // and have them propagate into all sorts of mischief as the system is being
-    // shutdown. This was learned the hard way :(
-
-    // mJSObj == nsnull is used to indicate that the wrapper is no longer valid
-    // and that calls should fail without trying to use any of the
-    // xpconnect mechanisms. 'IsValid' is implemented by checking this pointer.
-
-    // NOTE: that mClass is retained so that GetInterfaceInfo can continue to
-    // work (and avoid crashing some platforms).
-    mJSObj = nsnull;
-
-    // Notify other wrappers in the chain.
-    if(mNext)
-        mNext->SystemIsBeingShutDown(rt);
-}
-
 /***************************************************************************/
 
 /* readonly attribute nsISimpleEnumerator enumerator; */
diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp
--- a/js/src/xpconnect/src/xpcwrappednative.cpp
+++ b/js/src/xpconnect/src/xpcwrappednative.cpp
@@ -822,191 +822,6 @@ NS_INTERFACE_MAP_END_THREADSAFE
 
 NS_IMPL_THREADSAFE_ADDREF(XPCWrappedNative)
 NS_IMPL_THREADSAFE_RELEASE(XPCWrappedNative)
-
-/*
- *  Wrapped Native lifetime management is messy!
- *
- *  - At creation we push the refcount to 2 (only one of which is owned by
- *    the native caller that caused the wrapper creation).
- *  - During the JS GC Mark phase we mark any wrapper with a refcount > 1.
- *  - The *only* thing that can make the wrapper get destroyed is the
- *    finalization of mFlatJSObject. And *that* should only happen if the only
- *    reference is the single extra (internal) reference we hold.
- *
- *  - The wrapper has a pointer to the nsISupports 'view' of the wrapped native
- *    object i.e... mIdentity. This is held until the wrapper's refcount goes
- *    to zero and the wrapper is released.
- *
- *  - The wrapper also has 'tearoffs'. It has one tearoff for each interface
- *    that is actually used on the native object. 'Used' means we have either
- *    needed to QueryInterface to verify the availability of that interface
- *    of that we've had to QueryInterface in order to actually make a call
- *    into the wrapped object via the pointer for the given interface.
- *
- *  - Each tearoff's 'mNative' member (if non-null) indicates one reference
- *    held by our wrapper on the wrapped native for the given interface
- *    associated with the tearoff. If we release that reference then we set
- *    the tearoff's 'mNative' to null.
- *
- *  - We use the occasion of the JavaScript GCCallback for the JSGC_MARK_END
- *    event to scan the tearoffs of all wrappers for non-null mNative members
- *    that represent unused references. We can tell that a given tearoff's
- *    mNative is unused by noting that no live XPCCallContexts hold a pointer
- *    to the tearoff.
- *
- *  - As a time/space tradeoff we may decide to not do this scanning on
- *    *every* JavaScript GC. We *do* want to do this *sometimes* because
- *    we want to allow for wrapped native's to do their own tearoff patterns.
- *    So, we want to avoid holding references to interfaces that we don't need.
- *    At the same time, we don't want to be bracketing every call into a
- *    wrapped native object with a QueryInterface/Release pair. And we *never*
- *    make a call into the object except via the correct interface for which
- *    we've QI'd.
- *
- *  - Each tearoff *can* have a mJSObject whose lazily resolved properties
- *    represent the methods/attributes/constants of that specific interface.
- *    This is optionally reflected into JavaScript as "foo.nsIFoo" when "foo"
- *    is the name of mFlatJSObject and "nsIFoo" is the name of the given
- *    interface associated with the tearoff. When we create the tearoff's
- *    mJSObject we set it's parent to be mFlatJSObject. This way we know that
- *    when mFlatJSObject get's collected there are no outstanding reachable
- *    tearoff mJSObjects. Note that we must clear the private of any lingering
- *    mJSObjects at this point because we have no guarentee of the *order* of
- *    finalization within a given gc cycle.
- */
-
-void
-XPCWrappedNative::FlatJSObjectFinalized(JSContext *cx)
-{
-    if(!IsValid())
-        return;
-
-    // Iterate the tearoffs and null out each of their JSObject's privates.
-    // This will keep them from trying to access their pointers to the
-    // dying tearoff object. We can safely assume that those remaining
-    // JSObjects are about to be finalized too.
-
-    XPCWrappedNativeTearOffChunk* chunk;
-    for(chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk)
-    {
-        XPCWrappedNativeTearOff* to = chunk->mTearOffs;
-        for(int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK-1; i >= 0; i--, to++)
-        {
-            JSObject* jso = to->GetJSObject();
-            if(jso)
-            {
-                NS_ASSERTION(JS_IsAboutToBeFinalized(cx, jso), "bad!");
-                JS_SetPrivate(cx, jso, nsnull);
-                to->JSObjectFinalized();
-            }
-
-            // We also need to release any native pointers held...
-            to->SetNative(nsnull);
-            to->SetInterface(nsnull);
-        }
-    }
-
-    GetScope()->GetWrapperMap()->Remove(mFlatJSObject);
-
-    if(IsWrapperExpired())
-    {
-        GetScope()->GetWrappedNativeMap()->Remove(this);
-
-        XPCWrappedNativeProto* proto = GetProto();
-
-        if(mScriptableInfo &&
-           (!HasProto() ||
-            (proto && proto->GetScriptableInfo() != mScriptableInfo)))
-        {
-            delete mScriptableInfo;
-            mScriptableInfo = nsnull;
-        }
-
-        mMaybeScope = nsnull;
-    }
-
-    // This makes IsValid return false from now on...
-    mFlatJSObject = nsnull;
-
-    // Note that it's not safe to touch mNativeWrapper here since it's
-    // likely that it has already been finalized.
-}
-
-void
-XPCWrappedNative::SystemIsBeingShutDown(JSContext* cx)
-{
-#ifdef DEBUG_xpc_hacker
-    {
-        printf("Removing root for still-live XPCWrappedNative %p wrapping:\n",
-               static_cast<void*>(this));
-        for(PRUint16 i = 0, i_end = mSet->GetInterfaceCount(); i < i_end; ++i)
-        {
-            nsXPIDLCString name;
-            mSet->GetInterfaceAt(i)->GetInterfaceInfo()
-                ->GetName(getter_Copies(name));
-            printf("  %s\n", name.get());
-        }
-    }
-#endif
-    DEBUG_TrackShutdownWrapper(this);
-
-    if(!IsValid())
-        return;
-
-    // The long standing strategy is to leak some objects still held at shutdown.
-    // The general problem is that propagating release out of xpconnect at
-    // shutdown time causes a world of problems.
-
-    // We leak mIdentity (see above).
-
-    // short circuit future finalization
-    JS_SetPrivate(cx, mFlatJSObject, nsnull);
-    mFlatJSObject = nsnull; // This makes 'IsValid()' return false.
-
-    XPCWrappedNativeProto* proto = GetProto();
-
-    if(HasProto())
-        proto->SystemIsBeingShutDown(cx);
-
-    if(mScriptableInfo &&
-       (!HasProto() ||
-        (proto && proto->GetScriptableInfo() != mScriptableInfo)))
-    {
-        delete mScriptableInfo;
-    }
-
-    // cleanup the tearoffs...
-
-    XPCWrappedNativeTearOffChunk* chunk;
-    for(chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk)
-    {
-        XPCWrappedNativeTearOff* to = chunk->mTearOffs;
-        for(int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK-1; i >= 0; i--, to++)
-        {
-            if(to->GetJSObject())
-            {
-                JS_SetPrivate(cx, to->GetJSObject(), nsnull);
-#ifdef XPC_IDISPATCH_SUPPORT
-                if(to->IsIDispatch())
-                    delete to->GetIDispatchInfo();
-#endif
-                to->SetJSObject(nsnull);
-            }
-            // We leak the tearoff mNative
-            // (for the same reason we leak mIdentity - see above).
-            to->SetNative(nsnull);
-            to->SetInterface(nsnull);
-        }
-    }
-
-    if(mFirstChunk.mNextChunk)
-    {
-        delete mFirstChunk.mNextChunk;
-        mFirstChunk.mNextChunk = nsnull;
-    }
-}
-
-/***************************************************************************/
 
 // static
 nsresult
diff --git a/js/src/xpconnect/src/xpcwrappednativeinfo.cpp b/js/src/xpconnect/src/xpcwrappednativeinfo.cpp
--- a/js/src/xpconnect/src/xpcwrappednativeinfo.cpp
+++ b/js/src/xpconnect/src/xpcwrappednativeinfo.cpp
@@ -705,21 +705,6 @@ out:
     return set;
 }
 
-// static 
-void 
-XPCNativeSet::ClearCacheEntryForClassInfo(nsIClassInfo* classInfo)
-{
-    XPCJSRuntime* rt;
-    ClassInfo2NativeSetMap* map;
-    
-    if(nsnull != (rt = nsXPConnect::GetRuntime()) && 
-       nsnull != (map = rt->GetClassInfo2NativeSetMap()))
-    {   // scoped lock
-        XPCAutoLock lock(rt->GetMapLock());
-        map->Remove(classInfo);
-    }
-}
-
 // static
 XPCNativeSet*
 XPCNativeSet::GetNewOrUsed(XPCCallContext& ccx,
diff --git a/js/src/xpconnect/src/xpcwrappednativejsops.cpp b/js/src/xpconnect/src/xpcwrappednativejsops.cpp
--- a/js/src/xpconnect/src/xpcwrappednativejsops.cpp
+++ b/js/src/xpconnect/src/xpcwrappednativejsops.cpp
@@ -631,15 +631,6 @@ XPC_WN_Shared_Enumerate(JSContext *cx, J
 
 /***************************************************************************/
 
-JS_STATIC_DLL_CALLBACK(void)
-XPC_WN_NoHelper_Finalize(JSContext *cx, JSObject *obj)
-{
-    XPCWrappedNative* p = (XPCWrappedNative*) xpc_GetJSPrivate(obj);
-    if(!p)
-        return;
-    p->FlatJSObjectFinalized(cx);
-}
-
 static void
 TraceScopeJSObjects(JSTracer *trc, XPCWrappedNativeScope* scope)
 {
@@ -845,7 +836,7 @@ JSExtendedClass XPC_WN_NoHelper_JSClass 
         XPC_WN_Shared_Enumerate,        // enumerate;
         XPC_WN_NoHelper_Resolve,        // resolve;
         XPC_WN_Shared_Convert,          // convert;
-        XPC_WN_NoHelper_Finalize,       // finalize;
+        JS_FinalizeStub,                // finalize;
 
         /* Optionally non-null members start here. */
         XPC_WN_GetObjectOpsNoCall,      // getObjectOps;
@@ -976,16 +967,6 @@ XPC_WN_Helper_HasInstance(JSContext *cx,
     PRE_HELPER_STUB
     HasInstance(wrapper, cx, obj, v, bp, &retval);
     POST_HELPER_STUB
-}
-
-JS_STATIC_DLL_CALLBACK(void)
-XPC_WN_Helper_Finalize(JSContext *cx, JSObject *obj)
-{
-    XPCWrappedNative* wrapper = (XPCWrappedNative*) xpc_GetJSPrivate(obj);
-    if(!wrapper)
-        return;
-    wrapper->GetScriptableCallback()->Finalize(wrapper, cx, obj);
-    wrapper->FlatJSObjectFinalized(cx);
 }
 
 JS_STATIC_DLL_CALLBACK(JSBool)
@@ -1355,10 +1336,7 @@ XPCNativeScriptableShared::PopulateJSCla
     else
         mJSClass.base.convert = XPC_WN_Shared_Convert;
 
-    if(mFlags.WantFinalize())
-        mJSClass.base.finalize = XPC_WN_Helper_Finalize;
-    else
-        mJSClass.base.finalize = XPC_WN_NoHelper_Finalize;
+    mJSClass.base.finalize = JS_FinalizeStub;
 
     // We let the rest default to nsnull unless the helper wants them...
     if(mFlags.WantCheckAccess())
@@ -1504,15 +1482,6 @@ XPC_WN_Shared_Proto_Convert(JSContext *c
 }
 
 JS_STATIC_DLL_CALLBACK(void)
-XPC_WN_Shared_Proto_Finalize(JSContext *cx, JSObject *obj)
-{
-    // This can be null if xpc shutdown has already happened
-    XPCWrappedNativeProto* p = (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
-    if(p)
-        p->JSProtoObjectFinalized(cx, obj);
-}
-
-JS_STATIC_DLL_CALLBACK(void)
 XPC_WN_Shared_Proto_Trace(JSTracer *trc, JSObject *obj)
 {
     // This can be null if xpc shutdown has already happened
@@ -1591,7 +1560,7 @@ JSClass XPC_WN_ModsAllowed_WithCall_Prot
     XPC_WN_Shared_Proto_Enumerate,         // enumerate;
     XPC_WN_ModsAllowed_Proto_Resolve,      // resolve;
     XPC_WN_Shared_Proto_Convert,           // convert;
-    XPC_WN_Shared_Proto_Finalize,          // finalize;
+    JS_FinalizeStub,                       // finalize;
 
     /* Optionally non-null members start here. */
     XPC_WN_Proto_GetObjectOps,      // getObjectOps;
@@ -1622,7 +1591,7 @@ JSClass XPC_WN_ModsAllowed_NoCall_Proto_
     XPC_WN_Shared_Proto_Enumerate,         // enumerate;
     XPC_WN_ModsAllowed_Proto_Resolve,      // resolve;
     XPC_WN_Shared_Proto_Convert,           // convert;
-    XPC_WN_Shared_Proto_Finalize,          // finalize;
+    JS_FinalizeStub,                      // finalize;
 
     /* Optionally non-null members start here. */
     XPC_WN_Proto_GetObjectOps,      // getObjectOps;
@@ -1707,7 +1676,7 @@ JSClass XPC_WN_NoMods_WithCall_Proto_JSC
     XPC_WN_Shared_Proto_Enumerate,         // enumerate;
     XPC_WN_NoMods_Proto_Resolve,           // resolve;
     XPC_WN_Shared_Proto_Convert,           // convert;
-    XPC_WN_Shared_Proto_Finalize,          // finalize;
+    JS_FinalizeStub,                       // finalize;
 
     /* Optionally non-null members start here. */
     XPC_WN_Proto_GetObjectOps,      // getObjectOps;
@@ -1732,7 +1701,7 @@ JSClass XPC_WN_NoMods_NoCall_Proto_JSCla
     XPC_WN_Shared_Proto_Enumerate,         // enumerate;
     XPC_WN_NoMods_Proto_Resolve,           // resolve;
     XPC_WN_Shared_Proto_Convert,           // convert;
-    XPC_WN_Shared_Proto_Finalize,          // finalize;
+    JS_FinalizeStub,                       // finalize;
 
     /* Optionally non-null members start here. */
     XPC_WN_Proto_GetObjectOps,      // getObjectOps;
@@ -1793,16 +1762,6 @@ XPC_WN_TearOff_Resolve(JSContext *cx, JS
                                  JSPROP_ENUMERATE, nsnull);
 }
 
-JS_STATIC_DLL_CALLBACK(void)
-XPC_WN_TearOff_Finalize(JSContext *cx, JSObject *obj)
-{
-    XPCWrappedNativeTearOff* p = (XPCWrappedNativeTearOff*)
-        xpc_GetJSPrivate(obj);
-    if(!p)
-        return;
-    p->JSObjectFinalized();
-}
-
 JSClass XPC_WN_Tearoff_JSClass = {
     "WrappedNative_TearOff",            // name;
     JSCLASS_HAS_PRIVATE | JSCLASS_MARK_IS_TRACE, // flags;
@@ -1815,7 +1774,7 @@ JSClass XPC_WN_Tearoff_JSClass = {
     XPC_WN_TearOff_Enumerate,           // enumerate;
     XPC_WN_TearOff_Resolve,             // resolve;
     XPC_WN_Shared_Convert,              // convert;
-    XPC_WN_TearOff_Finalize,            // finalize;
+    JS_FinalizeStub,                    // finalize;
 
     /* Optionally non-null members start here. */
     nsnull,                         // getObjectOps;
diff --git a/js/src/xpconnect/src/xpcwrappednativeproto.cpp b/js/src/xpconnect/src/xpcwrappednativeproto.cpp
--- a/js/src/xpconnect/src/xpcwrappednativeproto.cpp
+++ b/js/src/xpconnect/src/xpcwrappednativeproto.cpp
@@ -68,23 +68,6 @@ XPCWrappedNativeProto::XPCWrappedNativeP
 #endif
 }
 
-XPCWrappedNativeProto::~XPCWrappedNativeProto()
-{
-    NS_ASSERTION(!mJSProtoObject, "JSProtoObject still alive");
-
-    MOZ_COUNT_DTOR(XPCWrappedNativeProto);
-
-#ifdef DEBUG
-    PR_AtomicDecrement(&gDEBUG_LiveProtoCount);
-#endif
-    
-    // Note that our weak ref to mScope is not to be trusted at this point.
-
-    XPCNativeSet::ClearCacheEntryForClassInfo(mClassInfo);
-
-    delete mScriptableInfo;
-}
-
 JSBool
 XPCWrappedNativeProto::Init(
                 XPCCallContext& ccx,
@@ -136,52 +119,6 @@ XPCWrappedNativeProto::Init(
     DEBUG_ReportShadowedMembers(mSet, nsnull, this);
 
     return ok;
-}
-
-void
-XPCWrappedNativeProto::JSProtoObjectFinalized(JSContext *cx, JSObject *obj)
-{
-    NS_ASSERTION(obj == mJSProtoObject, "huh?");
-
-    // Map locking is not necessary since we are running gc.
-
-    if(IsShared())
-    {
-        // Only remove this proto from the map if it is the one in the map.
-        ClassInfo2WrappedNativeProtoMap* map = 
-            GetScope()->GetWrappedNativeProtoMap();
-        if(map->Find(mClassInfo) == this)
-            map->Remove(mClassInfo);
-    }
-
-    GetRuntime()->GetDetachedWrappedNativeProtoMap()->Remove(this);
-    GetRuntime()->GetDyingWrappedNativeProtoMap()->Add(this);
-
-    mJSProtoObject = nsnull;
-}
-
-void
-XPCWrappedNativeProto::SystemIsBeingShutDown(JSContext* cx)
-{
-    // Note that the instance might receive this call multiple times
-    // as we walk to here from various places.
-
-#ifdef XPC_TRACK_PROTO_STATS
-    static PRBool DEBUG_DumpedStats = PR_FALSE;
-    if(!DEBUG_DumpedStats)
-    {
-        printf("%d XPCWrappedNativeProto(s) alive at shutdown\n",
-               gDEBUG_LiveProtoCount);
-        DEBUG_DumpedStats = PR_TRUE;
-    }
-#endif
-
-    if(mJSProtoObject)
-    {
-        // short circuit future finalization
-        JS_SetPrivate(cx, mJSProtoObject, nsnull);
-        mJSProtoObject = nsnull;
-    }
 }
 
 // static
diff --git a/js/src/xpconnect/src/xpcwrappednativescope.cpp b/js/src/xpconnect/src/xpcwrappednativescope.cpp
--- a/js/src/xpconnect/src/xpcwrappednativescope.cpp
+++ b/js/src/xpconnect/src/xpcwrappednativescope.cpp
@@ -383,74 +383,32 @@ struct ShutdownData
     int nonSharedProtoCount;
 };
 
-JS_STATIC_DLL_CALLBACK(JSDHashOperator)
-WrappedNativeShutdownEnumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
-                                uint32 number, void *arg)
+// static
+void
+XPCWrappedNativeScope::SystemIsBeingShutDown()
 {
-    ShutdownData* data = (ShutdownData*) arg;
-    XPCWrappedNative* wrapper = ((Native2WrappedNativeMap::Entry*)hdr)->value;
-
-    if(wrapper->IsValid())
-    {
-        if(wrapper->HasProto() && !wrapper->HasSharedProto())
-            data->nonSharedProtoCount++;
-        wrapper->SystemIsBeingShutDown(data->cx);
-        data->wrapperCount++;
-    }
-    return JS_DHASH_REMOVE;
-}
-
-JS_STATIC_DLL_CALLBACK(JSDHashOperator)
-WrappedNativeProtoShutdownEnumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
-                                     uint32 number, void *arg)
-{
-    ShutdownData* data = (ShutdownData*) arg;
-    ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value->
-        SystemIsBeingShutDown(data->cx);
-    data->sharedProtoCount++;
-    return JS_DHASH_REMOVE;
-}
-
-//static
-void
-XPCWrappedNativeScope::SystemIsBeingShutDown(JSContext* cx)
-{
-    DEBUG_TrackScopeTraversal();
-    DEBUG_TrackScopeShutdown();
-
-    ShutdownData data(cx);
-
+    // At shutdown, we no longer have scopes... any xpconnect code running
+    // after this point is bad. We clear gScopes to kill the assertions in
+    // ~XPCWrappedNativeScope
+    
     XPCWrappedNativeScope* cur = gScopes;
 
     gScopes = nsnull;
 
-    // Walk the list of scopes and call shutdown on all wrappers and protos
-
+    // Walk the list of scopes and clear the maps, because we're uninstalling
+    // the sweep hook and won't get a chance to later.
     for(; cur; cur = cur->mNext)
     {
-        // Give the Components object a chance to try to clean up.
-        if(cur->mComponents)
-            cur->mComponents->SystemIsBeingShutDown();
-
-        // Walk the protos first. Wrapper shutdown can leave dangling
-        // proto pointers in the proto map.
-        cur->mWrappedNativeProtoMap->
-                Enumerate(WrappedNativeProtoShutdownEnumerator,  &data);
-        cur->mWrappedNativeMap->
-                Enumerate(WrappedNativeShutdownEnumerator,  &data);
+        if (cur->mWrappedNativeProtoMap) {
+            delete cur->mWrappedNativeProtoMap;
+            cur->mWrappedNativeProtoMap = nsnull;
+        }
+        if (cur->mWrappedNativeMap) {
+            delete cur->mWrappedNativeMap;
+            cur->mWrappedNativeMap = nsnull;
+        }
     }
-
-#ifdef XPC_DUMP_AT_SHUTDOWN
-    if(data.wrapperCount)
-        printf("deleting nsXPConnect  with %d live XPCWrappedNatives\n",
-               data.wrapperCount);
-    if(data.sharedProtoCount + data.nonSharedProtoCount)
-        printf("deleting nsXPConnect  with %d live XPCWrappedNativeProtos (%d shared)\n",
-               data.sharedProtoCount + data.nonSharedProtoCount,
-               data.sharedProtoCount);
-#endif
 }
-
 
 /***************************************************************************/