From 2e01f470e649a5d725c007317fcf1a03882ceab1 Mon Sep 17 00:00:00 2001 From: "peterv@propagandism.org" Date: Mon, 29 Oct 2007 06:45:07 -0700 Subject: [PATCH] Part 1 of fix for bug 379718 (using trace API for reference counts) and bug 386912 (cycle collector faults after tracing "JS object but unknown to the JS GC"). r=igor/jst, sr=jst, a=blocking1.9+/M9 (for bug 386912). --- content/base/public/nsContentUtils.h | 169 +++++++++--------- content/base/src/nsContentUtils.cpp | 74 +++++--- content/xbl/src/nsXBLBinding.cpp | 2 +- content/xbl/src/nsXBLDocumentInfo.cpp | 34 +++- content/xbl/src/nsXBLDocumentInfo.h | 4 +- content/xbl/src/nsXBLInsertionPoint.cpp | 2 +- content/xbl/src/nsXBLProtoImpl.cpp | 4 +- content/xbl/src/nsXBLProtoImpl.h | 2 +- content/xbl/src/nsXBLProtoImplMember.h | 4 +- content/xbl/src/nsXBLProtoImplMethod.cpp | 10 +- content/xbl/src/nsXBLProtoImplMethod.h | 2 +- content/xbl/src/nsXBLProtoImplProperty.cpp | 20 +-- content/xbl/src/nsXBLProtoImplProperty.h | 2 +- content/xbl/src/nsXBLPrototypeBinding.cpp | 11 +- content/xbl/src/nsXBLPrototypeBinding.h | 1 + content/xul/content/src/nsXULElement.cpp | 74 +++++--- content/xul/content/src/nsXULElement.h | 55 +++++- content/xul/document/src/nsXULDocument.cpp | 2 +- dom/src/base/nsJSEnvironment.cpp | 46 +++-- dom/src/base/nsJSTimeoutHandler.cpp | 61 ++----- dom/src/events/nsJSEventListener.cpp | 12 +- dom/src/events/nsJSEventListener.h | 4 +- js/src/xpconnect/idl/nsIXPCScriptable.idl | 2 + js/src/xpconnect/idl/nsIXPConnect.idl | 20 ++- js/src/xpconnect/src/nsXPConnect.cpp | 54 ++++-- js/src/xpconnect/src/xpcjsruntime.cpp | 88 ++++++++- js/src/xpconnect/src/xpcprivate.h | 20 ++- js/src/xpconnect/src/xpcwrappedjs.cpp | 4 - js/src/xpconnect/src/xpcwrappednative.cpp | 2 +- .../xpconnect/src/xpcwrappednativejsops.cpp | 6 +- .../xpconnect/src/xpcwrappednativescope.cpp | 10 -- xpcom/base/nsAgg.h | 3 +- xpcom/glue/nsCycleCollectionParticipant.cpp | 21 +++ xpcom/glue/nsCycleCollectionParticipant.h | 141 ++++++++++++--- 34 files changed, 640 insertions(+), 326 deletions(-) diff --git a/content/base/public/nsContentUtils.h b/content/base/public/nsContentUtils.h index bbf25c512c3a..aa6415e3e5db 100644 --- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -752,42 +752,6 @@ public: */ static nsIContentPolicy *GetContentPolicy(); - /** - * Make sure that whatever value *aPtr contains at any given moment is - * protected from JS GC until we remove the GC root. A call to this that - * succeeds MUST be matched by a call to RemoveJSGCRoot to avoid leaking. - */ - static nsresult AddJSGCRoot(jsval* aPtr, const char* aName) { - return AddJSGCRoot((void*)aPtr, aName); - } - - /** - * Make sure that whatever object *aPtr is pointing to at any given moment is - * protected from JS GC until we remove the GC root. A call to this that - * succeeds MUST be matched by a call to RemoveJSGCRoot to avoid leaking. - */ - static nsresult AddJSGCRoot(JSObject** aPtr, const char* aName) { - return AddJSGCRoot((void*)aPtr, aName); - } - - /** - * Make sure that whatever object *aPtr is pointing to at any given moment is - * protected from JS GC until we remove the GC root. A call to this that - * succeeds MUST be matched by a call to RemoveJSGCRoot to avoid leaking. - */ - static nsresult AddJSGCRoot(void* aPtr, const char* aName); - - /** - * Remove aPtr as a JS GC root - */ - static nsresult RemoveJSGCRoot(jsval* aPtr) { - return RemoveJSGCRoot((void*)aPtr); - } - static nsresult RemoveJSGCRoot(JSObject** aPtr) { - return RemoveJSGCRoot((void*)aPtr); - } - static nsresult RemoveJSGCRoot(void* aPtr); - /** * Quick helper to determine whether there are any mutation listeners * of a given type that apply to this content or any of its ancestors. @@ -1002,40 +966,73 @@ public: */ static void DestroyAnonymousContent(nsCOMPtr* aContent); - static nsresult HoldScriptObject(PRUint32 aLangID, void *aObject); - static nsresult DropScriptObject(PRUint32 aLangID, void *aObject); - - class ScriptObjectHolder + /** + * Keep script object aNewObject, held by aScriptObjectHolder, alive. + * + * NOTE: This currently only supports objects that hold script objects of one + * scripting language. + * + * @param aLangID script language ID of aNewObject + * @param aScriptObjectHolder the object that holds aNewObject + * @param aTracer the tracer for aScriptObject + * @param aNewObject the script object to hold + * @param aWasHoldingObjects whether aScriptObjectHolder was already holding + * script objects (ie. HoldScriptObject was called + * on it before, without a corresponding call to + * DropScriptObjects) + */ + static nsresult HoldScriptObject(PRUint32 aLangID, void* aScriptObjectHolder, + nsScriptObjectTracer* aTracer, + void* aNewObject, PRBool aWasHoldingObjects) { - public: - ScriptObjectHolder(PRUint32 aLangID) : mLangID(aLangID), - mObject(nsnull) - { - MOZ_COUNT_CTOR(ScriptObjectHolder); + if (aLangID == nsIProgrammingLanguage::JAVASCRIPT) { + return aWasHoldingObjects ? NS_OK : + HoldJSObjects(aScriptObjectHolder, aTracer); } - ~ScriptObjectHolder() - { - MOZ_COUNT_DTOR(ScriptObjectHolder); - if (mObject) - DropScriptObject(mLangID, mObject); + + return HoldScriptObject(aLangID, aNewObject); + } + + /** + * Drop any script objects that aScriptObjectHolder is holding. + * + * NOTE: This currently only supports objects that hold script objects of one + * scripting language. + * + * @param aLangID script language ID of the objects that + * @param aScriptObjectHolder the object that holds script object that we want + * to drop + * @param aTracer the tracer for aScriptObject + */ + static nsresult DropScriptObjects(PRUint32 aLangID, void* aScriptObjectHolder, + nsScriptObjectTracer* aTracer) + { + if (aLangID == nsIProgrammingLanguage::JAVASCRIPT) { + return DropJSObjects(aScriptObjectHolder); } - nsresult set(void *aObject) - { - NS_ASSERTION(aObject, "unexpected null object"); - NS_ASSERTION(!mObject, "already have an object"); - nsresult rv = HoldScriptObject(mLangID, aObject); - if (NS_SUCCEEDED(rv)) { - mObject = aObject; - } - return rv; - } - void traverse(nsCycleCollectionTraversalCallback &cb) - { - cb.NoteScriptChild(mLangID, mObject); - } - PRUint32 mLangID; - void *mObject; - }; + + aTracer->Trace(aScriptObjectHolder, DropScriptObject, nsnull); + + return NS_OK; + } + + /** + * Keep the JS objects held by aScriptObjectHolder alive. + * + * @param aScriptObjectHolder the object that holds JS objects that we want to + * keep alive + * @param aTracer the tracer for aScriptObject + */ + static nsresult HoldJSObjects(void* aScriptObjectHolder, + nsScriptObjectTracer* aTracer); + + /** + * Drop the JS objects held by aScriptObjectHolder. + * + * @param aScriptObjectHolder the object that holds JS objects that we want to + * drop + */ + static nsresult DropJSObjects(void* aScriptObjectHolder); /** * Convert nsIContent::IME_STATUS_* to nsIKBStateControll::IME_STATUS_* @@ -1122,6 +1119,10 @@ private: static nsIDOMScriptObjectFactory *GetDOMScriptObjectFactory(); + static nsresult HoldScriptObject(PRUint32 aLangID, void* aObject); + PR_STATIC_CALLBACK(void) DropScriptObject(PRUint32 aLangID, void *aObject, + void *aClosure); + static nsIDOMScriptObjectFactory *sDOMScriptObjectFactory; static nsIXPConnect *sXPConnect; @@ -1163,14 +1164,9 @@ private: // Holds pointers to nsISupports* that should be released at shutdown static nsVoidArray* sPtrsToPtrsToRelease; - // For now, we don't want to automatically clean this up in Shutdown(), since - // consumers might unfortunately end up wanting to use it after that - static nsIJSRuntimeService* sJSRuntimeService; - static JSRuntime* sJSScriptRuntime; - static PRInt32 sJSScriptRootCount; - static nsIScriptRuntime* sScriptRuntimes[NS_STID_ARRAY_UBOUND]; static PRInt32 sScriptRootCount[NS_STID_ARRAY_UBOUND]; + static PRUint32 sJSGCThingRootCount; #ifdef IBMBIDI static nsIBidiKeyboard* sBidiKeyboard; @@ -1180,6 +1176,14 @@ private: }; +#define NS_HOLD_JS_OBJECTS(obj, clazz) \ + nsContentUtils::HoldJSObjects(NS_CYCLE_COLLECTION_UPCAST(obj, clazz), \ + &NS_CYCLE_COLLECTION_NAME(clazz)) + +#define NS_DROP_JS_OBJECTS(obj, clazz) \ + nsContentUtils::DropJSObjects(NS_CYCLE_COLLECTION_UPCAST(obj, clazz)) + + class nsCxPusher { public: @@ -1202,33 +1206,38 @@ public: nsAutoGCRoot(jsval* aPtr, nsresult* aResult) : mPtr(aPtr) { - mResult = *aResult = - nsContentUtils::AddJSGCRoot(aPtr, "nsAutoGCRoot"); + mResult = *aResult = AddJSGCRoot(aPtr, "nsAutoGCRoot"); } // aPtr should be the pointer to the JSObject* we want to protect nsAutoGCRoot(JSObject** aPtr, nsresult* aResult) : mPtr(aPtr) { - mResult = *aResult = - nsContentUtils::AddJSGCRoot(aPtr, "nsAutoGCRoot"); + mResult = *aResult = AddJSGCRoot(aPtr, "nsAutoGCRoot"); } // aPtr should be the pointer to the thing we want to protect nsAutoGCRoot(void* aPtr, nsresult* aResult) : mPtr(aPtr) { - mResult = *aResult = - nsContentUtils::AddJSGCRoot(aPtr, "nsAutoGCRoot"); + mResult = *aResult = AddJSGCRoot(aPtr, "nsAutoGCRoot"); } ~nsAutoGCRoot() { if (NS_SUCCEEDED(mResult)) { - nsContentUtils::RemoveJSGCRoot(mPtr); + RemoveJSGCRoot(mPtr); } } + static void Shutdown(); + private: + static nsresult AddJSGCRoot(void *aPtr, const char* aName); + static nsresult RemoveJSGCRoot(void *aPtr); + + static nsIJSRuntimeService* sJSRuntimeService; + static JSRuntime* sJSScriptRuntime; + void* mPtr; nsresult mResult; }; diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 827e66bed169..6957e21c3d7d 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -180,15 +180,15 @@ nsILineBreaker *nsContentUtils::sLineBreaker; nsIWordBreaker *nsContentUtils::sWordBreaker; nsICaseConversion *nsContentUtils::sCaseConv; nsVoidArray *nsContentUtils::sPtrsToPtrsToRelease; -nsIJSRuntimeService *nsContentUtils::sJSRuntimeService; -JSRuntime *nsContentUtils::sJSScriptRuntime; -PRInt32 nsContentUtils::sJSScriptRootCount = 0; nsIScriptRuntime *nsContentUtils::sScriptRuntimes[NS_STID_ARRAY_UBOUND]; PRInt32 nsContentUtils::sScriptRootCount[NS_STID_ARRAY_UBOUND]; +PRUint32 nsContentUtils::sJSGCThingRootCount; #ifdef IBMBIDI nsIBidiKeyboard *nsContentUtils::sBidiKeyboard = nsnull; #endif +nsIJSRuntimeService *nsAutoGCRoot::sJSRuntimeService; +JSRuntime *nsAutoGCRoot::sJSScriptRuntime; PRBool nsContentUtils::sInitialized = PR_FALSE; @@ -671,7 +671,8 @@ nsContentUtils::Shutdown() NS_IF_RELEASE(sStringBundleService); NS_IF_RELEASE(sConsoleService); NS_IF_RELEASE(sDOMScriptObjectFactory); - NS_IF_RELEASE(sXPConnect); + if (sJSGCThingRootCount == 0 && sXPConnect) + NS_RELEASE(sXPConnect); NS_IF_RELEASE(sSecurityManager); NS_IF_RELEASE(sThreadJSContextStack); NS_IF_RELEASE(sNameSpaceManager); @@ -721,6 +722,8 @@ nsContentUtils::Shutdown() sEventListenerManagersHash.ops = nsnull; } } + + nsAutoGCRoot::Shutdown(); } static PRBool IsCallerTrustedForCapability(const char* aCapability) @@ -2672,7 +2675,7 @@ nsContentUtils::GetContentPolicy() // static nsresult -nsContentUtils::AddJSGCRoot(void* aPtr, const char* aName) +nsAutoGCRoot::AddJSGCRoot(void* aPtr, const char* aName) { if (!sJSScriptRuntime) { nsresult rv = CallGetService("@mozilla.org/js/xpc/RuntimeService;1", @@ -2690,25 +2693,16 @@ nsContentUtils::AddJSGCRoot(void* aPtr, const char* aName) PRBool ok; ok = ::JS_AddNamedRootRT(sJSScriptRuntime, aPtr, aName); if (!ok) { - if (sJSScriptRootCount == 0) { - // We just got the runtime... Just null things out, since no - // one's expecting us to have a runtime yet - NS_RELEASE(sJSRuntimeService); - sJSScriptRuntime = nsnull; - } NS_WARNING("JS_AddNamedRootRT failed"); return NS_ERROR_OUT_OF_MEMORY; } - // We now have one more root we added to the runtime - ++sJSScriptRootCount; - return NS_OK; } /* static */ nsresult -nsContentUtils::RemoveJSGCRoot(void* aPtr) +nsAutoGCRoot::RemoveJSGCRoot(void* aPtr) { if (!sJSScriptRuntime) { NS_NOTREACHED("Trying to remove a JS GC root when none were added"); @@ -2717,11 +2711,6 @@ nsContentUtils::RemoveJSGCRoot(void* aPtr) ::JS_RemoveRootRT(sJSScriptRuntime, aPtr); - if (--sJSScriptRootCount == 0) { - NS_RELEASE(sJSRuntimeService); - sJSScriptRuntime = nsnull; - } - return NS_OK; } @@ -3525,6 +3514,8 @@ nsresult nsContentUtils::HoldScriptObject(PRUint32 aLangID, void *aObject) { NS_ASSERTION(aObject, "unexpected null object"); + NS_ASSERTION(aLangID != nsIProgrammingLanguage::JAVASCRIPT, + "Should use HoldJSObjects."); nsresult rv; PRUint32 langIndex = NS_STID_INDEX(aLangID); @@ -3551,17 +3542,47 @@ nsContentUtils::HoldScriptObject(PRUint32 aLangID, void *aObject) } /* static */ -nsresult -nsContentUtils::DropScriptObject(PRUint32 aLangID, void *aObject) +void +nsContentUtils::DropScriptObject(PRUint32 aLangID, void *aObject, + void *aClosure) { NS_ASSERTION(aObject, "unexpected null object"); + NS_ASSERTION(aLangID != nsIProgrammingLanguage::JAVASCRIPT, + "Should use DropJSObjects."); PRUint32 langIndex = NS_STID_INDEX(aLangID); NS_LOG_RELEASE(sScriptRuntimes[langIndex], sScriptRootCount[langIndex] - 1, "HoldScriptObject"); - nsresult rv = sScriptRuntimes[langIndex]->DropScriptObject(aObject); + sScriptRuntimes[langIndex]->DropScriptObject(aObject); if (--sScriptRootCount[langIndex] == 0) { NS_RELEASE(sScriptRuntimes[langIndex]); } +} + +/* static */ +nsresult +nsContentUtils::HoldJSObjects(void* aScriptObjectHolder, + nsScriptObjectTracer* aTracer) +{ + PRBool newHolder; + nsresult rv = sXPConnect->AddJSHolder(aScriptObjectHolder, aTracer); + NS_ENSURE_SUCCESS(rv, rv); + + ++sJSGCThingRootCount; + NS_LOG_ADDREF(sXPConnect, sJSGCThingRootCount, "HoldJSObjects", + sizeof(void*)); + + return NS_OK; +} + +/* static */ +nsresult +nsContentUtils::DropJSObjects(void* aScriptObjectHolder) +{ + NS_LOG_RELEASE(sXPConnect, sJSGCThingRootCount - 1, "HoldJSObjects"); + nsresult rv = sXPConnect->RemoveJSHolder(aScriptObjectHolder); + if (--sJSGCThingRootCount == 0 && !sInitialized) { + NS_RELEASE(sXPConnect); + } return rv; } @@ -3717,3 +3738,10 @@ nsContentUtils::IsNativeAnonymous(nsIContent* aContent) return PR_FALSE; } + +/* static */ +void +nsAutoGCRoot::Shutdown() +{ + NS_IF_RELEASE(sJSRuntimeService); +} diff --git a/content/xbl/src/nsXBLBinding.cpp b/content/xbl/src/nsXBLBinding.cpp index 1b94a9f7c93b..611519fccf62 100644 --- a/content/xbl/src/nsXBLBinding.cpp +++ b/content/xbl/src/nsXBLBinding.cpp @@ -306,7 +306,7 @@ TraverseKey(nsISupports* aKey, nsInsertionPointList* aData, void* aClosure) return PL_DHASH_NEXT; } -NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(nsXBLBinding) +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLBinding) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(nsXBLBinding) // XXX Probably can't unlink mPrototypeBinding->XBLDocumentInfo(), because // mPrototypeBinding is weak. diff --git a/content/xbl/src/nsXBLDocumentInfo.cpp b/content/xbl/src/nsXBLDocumentInfo.cpp index 4da0e377fb1c..5b4007f0deba 100644 --- a/content/xbl/src/nsXBLDocumentInfo.cpp +++ b/content/xbl/src/nsXBLDocumentInfo.cpp @@ -452,6 +452,21 @@ UnlinkProtos(nsHashKey *aKey, void *aData, void* aClosure) return kHashEnumerateNext; } +struct ProtoTracer +{ + TraceCallback mCallback; + void *mClosure; +}; + +static PRIntn PR_CALLBACK +TraceProtos(nsHashKey *aKey, void *aData, void* aClosure) +{ + ProtoTracer* closure = static_cast(aClosure); + nsXBLPrototypeBinding *proto = static_cast(aData); + proto->Trace(closure->mCallback, closure->mClosure); + return kHashEnumerateNext; +} + NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLDocumentInfo) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLDocumentInfo) if (tmp->mBindingTable) { @@ -466,7 +481,14 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLDocumentInfo) tmp->mBindingTable->Enumerate(TraverseProtos, &cb); } cb.NoteXPCOMChild(static_cast(tmp->mGlobalObject)); + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXBLDocumentInfo) + if (tmp->mBindingTable) { + ProtoTracer closure = { aCallback, aClosure }; + tmp->mBindingTable->Enumerate(TraceProtos, &closure); + } +NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXBLDocumentInfo) NS_INTERFACE_MAP_ENTRY(nsIXBLDocumentInfo) @@ -507,7 +529,10 @@ nsXBLDocumentInfo::~nsXBLDocumentInfo() mGlobalObject->SetScriptContext(nsIProgrammingLanguage::JAVASCRIPT, nsnull); mGlobalObject->ClearGlobalObjectOwner(); // just in case } - delete mBindingTable; + if (mBindingTable) { + NS_DROP_JS_OBJECTS(this, nsXBLDocumentInfo); + delete mBindingTable; + } } NS_IMETHODIMP @@ -541,8 +566,13 @@ DeletePrototypeBinding(nsHashKey* aKey, void* aData, void* aClosure) NS_IMETHODIMP nsXBLDocumentInfo::SetPrototypeBinding(const nsACString& aRef, nsXBLPrototypeBinding* aBinding) { - if (!mBindingTable) + if (!mBindingTable) { mBindingTable = new nsObjectHashtable(nsnull, nsnull, DeletePrototypeBinding, nsnull); + if (!mBindingTable) + return NS_ERROR_OUT_OF_MEMORY; + + NS_HOLD_JS_OBJECTS(this, nsXBLDocumentInfo); + } const nsPromiseFlatCString& flat = PromiseFlatCString(aRef); nsCStringKey key(flat.get()); diff --git a/content/xbl/src/nsXBLDocumentInfo.h b/content/xbl/src/nsXBLDocumentInfo.h index 1f65e7168d6b..29054dd1e2bd 100644 --- a/content/xbl/src/nsXBLDocumentInfo.h +++ b/content/xbl/src/nsXBLDocumentInfo.h @@ -72,8 +72,8 @@ public: // nsIScriptGlobalObjectOwner methods virtual nsIScriptGlobalObject* GetScriptGlobalObject(); - NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXBLDocumentInfo, - nsIXBLDocumentInfo) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsXBLDocumentInfo, + nsIXBLDocumentInfo) private: nsCOMPtr mDocument; diff --git a/content/xbl/src/nsXBLInsertionPoint.cpp b/content/xbl/src/nsXBLInsertionPoint.cpp index c3c6dd6b500e..56d86fccabaf 100644 --- a/content/xbl/src/nsXBLInsertionPoint.cpp +++ b/content/xbl/src/nsXBLInsertionPoint.cpp @@ -64,7 +64,7 @@ nsXBLInsertionPoint::Release() return mRefCnt; } -NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(nsXBLInsertionPoint) +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLInsertionPoint) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(nsXBLInsertionPoint) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mElements) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDefaultContentTemplate) diff --git a/content/xbl/src/nsXBLProtoImpl.cpp b/content/xbl/src/nsXBLProtoImpl.cpp index 2cd341545373..a1860f98ded1 100644 --- a/content/xbl/src/nsXBLProtoImpl.cpp +++ b/content/xbl/src/nsXBLProtoImpl.cpp @@ -200,7 +200,7 @@ nsXBLProtoImpl::CompilePrototypeMembers(nsXBLPrototypeBinding* aBinding) } void -nsXBLProtoImpl::Traverse(nsCycleCollectionTraversalCallback &cb) const +nsXBLProtoImpl::Trace(TraceCallback aCallback, void *aClosure) const { // If we don't have a class object then we either didn't compile members // or we only have fields, in both cases there are no cycles through our @@ -211,7 +211,7 @@ nsXBLProtoImpl::Traverse(nsCycleCollectionTraversalCallback &cb) const nsXBLProtoImplMember *member; for (member = mMembers; member; member = member->GetNext()) { - member->Traverse(cb); + member->Trace(aCallback, aClosure); } } diff --git a/content/xbl/src/nsXBLProtoImpl.h b/content/xbl/src/nsXBLProtoImpl.h index e9989f1e4371..caa264360447 100644 --- a/content/xbl/src/nsXBLProtoImpl.h +++ b/content/xbl/src/nsXBLProtoImpl.h @@ -90,7 +90,7 @@ public: mFields = aFieldList; } - void Traverse(nsCycleCollectionTraversalCallback &cb) const; + void Trace(TraceCallback aCallback, void *aClosure) const; void Unlink(); nsXBLProtoImplField* FindField(const nsString& aFieldName) const; diff --git a/content/xbl/src/nsXBLProtoImplMember.h b/content/xbl/src/nsXBLProtoImplMember.h index 0ca62573a13e..d8e12cd3181f 100644 --- a/content/xbl/src/nsXBLProtoImplMember.h +++ b/content/xbl/src/nsXBLProtoImplMember.h @@ -47,11 +47,11 @@ #include "nsIJSRuntimeService.h" #include "nsIServiceManager.h" #include "nsReadableUtils.h" +#include "nsCycleCollectionParticipant.h" class nsIScriptContext; struct JSRuntime; class nsIJSRuntimeService; -class nsCycleCollectionTraversalCallback; struct nsXBLTextWithLineNumber { @@ -114,7 +114,7 @@ public: const nsCString& aClassStr, void* aClassObject)=0; - virtual void Traverse(nsCycleCollectionTraversalCallback &cb) const = 0; + virtual void Trace(TraceCallback aCallback, void *aClosure) const = 0; protected: friend class nsAutoGCRoot; diff --git a/content/xbl/src/nsXBLProtoImplMethod.cpp b/content/xbl/src/nsXBLProtoImplMethod.cpp index d18768118319..3da1692c64ce 100644 --- a/content/xbl/src/nsXBLProtoImplMethod.cpp +++ b/content/xbl/src/nsXBLProtoImplMethod.cpp @@ -72,8 +72,6 @@ nsXBLProtoImplMethod::Destroy(PRBool aIsCompiled) NS_PRECONDITION(aIsCompiled == mIsCompiled, "Incorrect aIsCompiled in nsXBLProtoImplMethod::Destroy"); if (aIsCompiled) { - if (mJSMethodObject) - nsContentUtils::RemoveJSGCRoot(&mJSMethodObject); mJSMethodObject = nsnull; } else { @@ -263,8 +261,6 @@ nsXBLProtoImplMethod::CompileMember(nsIScriptContext* aContext, const nsCString& if (methodObject) { // Root the compiled prototype script object. - rv = nsContentUtils::AddJSGCRoot(&mJSMethodObject, - "nsXBLProtoImplMethod::mJSMethodObject"); if (NS_FAILED(rv)) { mJSMethodObject = nsnull; } @@ -277,11 +273,13 @@ nsXBLProtoImplMethod::CompileMember(nsIScriptContext* aContext, const nsCString& } void -nsXBLProtoImplMethod::Traverse(nsCycleCollectionTraversalCallback &cb) const +nsXBLProtoImplMethod::Trace(TraceCallback aCallback, void *aClosure) const { NS_ASSERTION(mIsCompiled, "Shouldn't traverse uncompiled method"); - cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, mJSMethodObject); + if (mJSMethodObject) { + aCallback(nsIProgrammingLanguage::JAVASCRIPT, mJSMethodObject, aClosure); + } } nsresult diff --git a/content/xbl/src/nsXBLProtoImplMethod.h b/content/xbl/src/nsXBLProtoImplMethod.h index a1e665836397..bac2927b56f2 100644 --- a/content/xbl/src/nsXBLProtoImplMethod.h +++ b/content/xbl/src/nsXBLProtoImplMethod.h @@ -129,7 +129,7 @@ public: const nsCString& aClassStr, void* aClassObject); - virtual void Traverse(nsCycleCollectionTraversalCallback &cb) const; + virtual void Trace(TraceCallback aCallback, void *aClosure) const; protected: union { diff --git a/content/xbl/src/nsXBLProtoImplProperty.cpp b/content/xbl/src/nsXBLProtoImplProperty.cpp index 76a5b44bec06..407265f8acf7 100644 --- a/content/xbl/src/nsXBLProtoImplProperty.cpp +++ b/content/xbl/src/nsXBLProtoImplProperty.cpp @@ -87,14 +87,14 @@ nsXBLProtoImplProperty::Destroy(PRBool aIsCompiled) "Incorrect aIsCompiled in nsXBLProtoImplProperty::Destroy"); if ((mJSAttributes & JSPROP_GETTER) && mJSGetterObject) { - nsContentUtils::RemoveJSGCRoot(&mJSGetterObject); + mJSGetterObject = nsnull; } else { delete mGetterText; } if ((mJSAttributes & JSPROP_SETTER) && mJSSetterObject) { - nsContentUtils::RemoveJSGCRoot(&mJSSetterObject); + mJSSetterObject = nsnull; } else { delete mSetterText; @@ -268,9 +268,6 @@ nsXBLProtoImplProperty::CompileMember(nsIScriptContext* aContext, const nsCStrin if (mJSGetterObject && NS_SUCCEEDED(rv)) { mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED; - // Root the compiled prototype script object. - rv = nsContentUtils::AddJSGCRoot(&mJSGetterObject, - "nsXBLProtoImplProperty::mJSGetterObject"); } if (NS_FAILED(rv)) { mJSGetterObject = nsnull; @@ -320,9 +317,6 @@ nsXBLProtoImplProperty::CompileMember(nsIScriptContext* aContext, const nsCStrin if (mJSSetterObject && NS_SUCCEEDED(rv)) { mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED; - // Root the compiled prototype script object. - rv = nsContentUtils::AddJSGCRoot(&mJSSetterObject, - "nsXBLProtoImplProperty::mJSSetterObject"); } if (NS_FAILED(rv)) { mJSSetterObject = nsnull; @@ -345,15 +339,15 @@ nsXBLProtoImplProperty::CompileMember(nsIScriptContext* aContext, const nsCStrin } void -nsXBLProtoImplProperty::Traverse(nsCycleCollectionTraversalCallback &cb) const +nsXBLProtoImplProperty::Trace(TraceCallback aCallback, void *aClosure) const { NS_ASSERTION(mIsCompiled, "Shouldn't traverse uncompiled method"); - if (mJSAttributes & JSPROP_GETTER) { - cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, mJSGetterObject); + if ((mJSAttributes & JSPROP_GETTER) && mJSGetterObject) { + aCallback(nsIProgrammingLanguage::JAVASCRIPT, mJSGetterObject, aClosure); } - if (mJSAttributes & JSPROP_SETTER) { - cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, mJSSetterObject); + if ((mJSAttributes & JSPROP_SETTER) && mJSSetterObject) { + aCallback(nsIProgrammingLanguage::JAVASCRIPT, mJSSetterObject, aClosure); } } diff --git a/content/xbl/src/nsXBLProtoImplProperty.h b/content/xbl/src/nsXBLProtoImplProperty.h index f5ab70c79ace..9c10326c43de 100644 --- a/content/xbl/src/nsXBLProtoImplProperty.h +++ b/content/xbl/src/nsXBLProtoImplProperty.h @@ -72,7 +72,7 @@ public: const nsCString& aClassStr, void* aClassObject); - virtual void Traverse(nsCycleCollectionTraversalCallback &cb) const; + virtual void Trace(TraceCallback aCallback, void *aClosure) const; protected: union { diff --git a/content/xbl/src/nsXBLPrototypeBinding.cpp b/content/xbl/src/nsXBLPrototypeBinding.cpp index 568945789b41..725ec9a755f6 100644 --- a/content/xbl/src/nsXBLPrototypeBinding.cpp +++ b/content/xbl/src/nsXBLPrototypeBinding.cpp @@ -244,7 +244,7 @@ private: PRUint32 nsXBLInsertionPointEntry::gRefCnt = 0; nsFixedSizeAllocator* nsXBLInsertionPointEntry::kPool; -NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(nsXBLInsertionPointEntry) +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLInsertionPointEntry) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(nsXBLInsertionPointEntry) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mInsertionParent) if (tmp->mDefaultContent) { @@ -355,8 +355,6 @@ void nsXBLPrototypeBinding::Traverse(nsCycleCollectionTraversalCallback &cb) const { cb.NoteXPCOMChild(mBinding); - if (mImplementation) - mImplementation->Traverse(cb); if (mResources) cb.NoteXPCOMChild(mResources->mLoader); if (mInsertionPointTable) @@ -372,6 +370,13 @@ nsXBLPrototypeBinding::Unlink() mImplementation->Unlink(); } +void +nsXBLPrototypeBinding::Trace(TraceCallback aCallback, void *aClosure) const +{ + if (mImplementation) + mImplementation->Trace(aCallback, aClosure); +} + void nsXBLPrototypeBinding::Initialize() { diff --git a/content/xbl/src/nsXBLPrototypeBinding.h b/content/xbl/src/nsXBLPrototypeBinding.h index 83ced720db47..3243bd254900 100644 --- a/content/xbl/src/nsXBLPrototypeBinding.h +++ b/content/xbl/src/nsXBLPrototypeBinding.h @@ -198,6 +198,7 @@ public: void Traverse(nsCycleCollectionTraversalCallback &cb) const; void Unlink(); + void Trace(TraceCallback aCallback, void *aClosure) const; // Static members static PRUint32 gRefCnt; diff --git a/content/xul/content/src/nsXULElement.cpp b/content/xul/content/src/nsXULElement.cpp index cc28aeda5117..2cc482e868cf 100644 --- a/content/xul/content/src/nsXULElement.cpp +++ b/content/xul/content/src/nsXULElement.cpp @@ -55,7 +55,6 @@ * use in OS2 */ -#include "jsapi.h" // for JS_AddNamedRoot and JS_RemoveRootRT #include "nsCOMPtr.h" #include "nsDOMCID.h" #include "nsDOMError.h" @@ -702,7 +701,8 @@ nsScriptEventHandlerOwnerTearoff::CompileEventHandler( nsCOMPtr xuldoc = do_QueryInterface(mElement->GetOwnerDoc()); nsIScriptContext *context; - if (mElement->mPrototype && xuldoc) { + nsXULPrototypeElement *elem = mElement->mPrototype; + if (elem && xuldoc) { // It'll be shared among the instances of the prototype. // Use the prototype document's special context. Because @@ -755,9 +755,16 @@ nsScriptEventHandlerOwnerTearoff::CompileEventHandler( XUL_PROTOTYPE_ATTRIBUTE_METER(gNumCacheFills); // take a copy of the event handler, and tell the language about it. if (aHandler) { + NS_ASSERTION(!attr->mEventHandler, "Leaking handler."); + rv = nsContentUtils::HoldScriptObject(aContext->GetScriptTypeID(), - aHandler); + elem, + &NS_CYCLE_COLLECTION_NAME(nsXULPrototypeNode), + aHandler, + elem->mHoldsScriptObject); if (NS_FAILED(rv)) return rv; + + elem->mHoldsScriptObject = PR_TRUE; } attr->mEventHandler = (void *)aHandler; } @@ -2351,26 +2358,40 @@ nsXULElement::RecompileScriptEventListeners() } } -NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(nsXULPrototypeNode) +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULPrototypeNode) NS_IMPL_CYCLE_COLLECTION_UNLINK_NATIVE_0(nsXULPrototypeNode) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsXULPrototypeNode) if (tmp->mType == nsXULPrototypeNode::eType_Element) { nsXULPrototypeElement *elem = static_cast(tmp); PRUint32 i; - for (i = 0; i < elem->mNumAttributes; ++i) { - cb.NoteScriptChild(elem->mScriptTypeID, - elem->mAttributes[i].mEventHandler); - } for (i = 0; i < elem->mNumChildren; ++i) { NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(elem->mChildren[i], nsXULPrototypeNode) } } - else if (tmp->mType == nsXULPrototypeNode::eType_Script) { - static_cast(tmp)->mScriptObject.traverse(cb); - } + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_TRACE_NATIVE_BEGIN(nsXULPrototypeNode) + if (tmp->mType == nsXULPrototypeNode::eType_Element) { + nsXULPrototypeElement *elem = + static_cast(tmp); + if (elem->mHoldsScriptObject) { + PRUint32 i; + for (i = 0; i < elem->mNumAttributes; ++i) { + void *handler = elem->mAttributes[i].mEventHandler; + NS_IMPL_CYCLE_COLLECTION_TRACE_CALLBACK(elem->mScriptTypeID, + handler) + } + } + } + else if (tmp->mType == nsXULPrototypeNode::eType_Script) { + nsXULPrototypeScript *script = + static_cast(tmp); + NS_IMPL_CYCLE_COLLECTION_TRACE_CALLBACK(script->mScriptObject.mLangID, + script->mScriptObject.mObject) + } +NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXULPrototypeNode, AddRef) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXULPrototypeNode, Release) @@ -2382,17 +2403,6 @@ NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsXULPrototypeNode, Release) nsXULPrototypeAttribute::~nsXULPrototypeAttribute() { MOZ_COUNT_DTOR(nsXULPrototypeAttribute); - NS_ASSERTION(!mEventHandler, "Finalize not called - language object leak!"); -} - -void -nsXULPrototypeAttribute::Finalize(PRUint32 aLangID) -{ - if (mEventHandler) { - if (NS_FAILED(nsContentUtils::DropScriptObject(aLangID, mEventHandler))) - NS_ERROR("Failed to drop script object"); - mEventHandler = nsnull; - } } @@ -2679,6 +2689,19 @@ nsXULPrototypeElement::SetAttrAt(PRUint32 aPos, const nsAString& aValue, return NS_OK; } +void +nsXULPrototypeElement::Unlink() +{ + if (mHoldsScriptObject) { + nsContentUtils::DropScriptObjects(mScriptTypeID, this, + &NS_CYCLE_COLLECTION_NAME(nsXULPrototypeNode)); + mHoldsScriptObject = PR_FALSE; + } + mNumAttributes = 0; + delete[] mAttributes; + mAttributes = nsnull; +} + //---------------------------------------------------------------------- // // nsXULPrototypeScript @@ -2701,6 +2724,7 @@ nsXULPrototypeScript::nsXULPrototypeScript(PRUint32 aLangID, PRUint32 aLineNo, P nsXULPrototypeScript::~nsXULPrototypeScript() { + Unlink(); } nsresult @@ -2818,7 +2842,7 @@ nsXULPrototypeScript::Deserialize(nsIObjectInputStream* aStream, NS_WARNING("Language deseralization failed"); return rv; } - mScriptObject.set(newScriptObject); + Set(newScriptObject); return NS_OK; } @@ -2871,7 +2895,7 @@ nsXULPrototypeScript::DeserializeOutOfLine(nsIObjectInputStream* aInput, NS_ERROR("XUL cache gave different language?"); return NS_ERROR_UNEXPECTED; } - mScriptObject.set(newScriptObject); + Set(newScriptObject); } } } @@ -2997,7 +3021,7 @@ nsXULPrototypeScript::Compile(const PRUnichar* aText, if (NS_FAILED(rv)) return rv; - mScriptObject.set(newScriptObject); + Set(newScriptObject); return rv; } diff --git a/content/xul/content/src/nsXULElement.h b/content/xul/content/src/nsXULElement.h index aed4c073b3b8..aa3af24903ab 100644 --- a/content/xul/content/src/nsXULElement.h +++ b/content/xul/content/src/nsXULElement.h @@ -124,9 +124,6 @@ public: // nsScriptObjectHolder, but want to avoid the extra lang ID. void* mEventHandler; - // Containing element must tell us the langID so we can cleanup. - void Finalize(PRUint32 aLangID); - #ifdef XUL_PROTOTYPE_ATTRIBUTE_METERING /** If enough attributes, on average, are event handlers, it pays to keep @@ -230,7 +227,7 @@ public: */ virtual void ReleaseSubtree() { Release(); } - NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsXULPrototypeNode) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(nsXULPrototypeNode) protected: nsXULPrototypeNode(Type aType) @@ -249,6 +246,7 @@ public: mHasIdAttribute(PR_FALSE), mHasClassAttribute(PR_FALSE), mHasStyleAttribute(PR_FALSE), + mHoldsScriptObject(PR_FALSE), mScriptTypeID(nsIProgrammingLanguage::UNKNOWN) { NS_LOG_ADDREF(this, 1, ClassName(), ClassSize()); @@ -256,10 +254,7 @@ public: virtual ~nsXULPrototypeElement() { - PRUint32 i; - for (i = 0; i < mNumAttributes; i++) - mAttributes[i].Finalize(mScriptTypeID); - delete[] mAttributes; + Unlink(); NS_ASSERTION(!mChildren && mNumChildren == 0, "ReleaseSubtree not called"); } @@ -294,6 +289,8 @@ public: nsresult SetAttrAt(PRUint32 aPos, const nsAString& aValue, nsIURI* aDocumentURI); + void Unlink(); + PRUint32 mNumChildren; nsXULPrototypeNode** mChildren; // [OWNER] @@ -305,6 +302,7 @@ public: PRPackedBool mHasIdAttribute:1; PRPackedBool mHasClassAttribute:1; PRPackedBool mHasStyleAttribute:1; + PRPackedBool mHoldsScriptObject:1; // The language ID can not be set on a per-node basis, but is tracked // so that the language ID from the originating root can be used @@ -361,13 +359,52 @@ public: nsIDocument* aDocument, nsIScriptGlobalObjectOwner* aGlobalOwner); + void Unlink() + { + if (mScriptObject.mObject) { + nsContentUtils::DropScriptObjects(mScriptObject.mLangID, this, + &NS_CYCLE_COLLECTION_NAME(nsXULPrototypeNode)); + mScriptObject.mObject = nsnull; + } + } + + void Set(nsScriptObjectHolder &aHolder) + { + NS_ASSERTION(mScriptObject.mLangID == aHolder.getScriptTypeID(), + "Wrong language, this will leak the previous object."); + + mScriptObject.mLangID = aHolder.getScriptTypeID(); + Set((void*)aHolder); + } + void Set(void *aObject) + { + NS_ASSERTION(!mScriptObject.mObject, "Leaking script object."); + + nsresult rv = nsContentUtils::HoldScriptObject(mScriptObject.mLangID, + this, + &NS_CYCLE_COLLECTION_NAME(nsXULPrototypeNode), + aObject, PR_FALSE); + if (NS_SUCCEEDED(rv)) { + mScriptObject.mObject = aObject; + } + } + + struct ScriptObjectHolder + { + ScriptObjectHolder(PRUint32 aLangID) : mLangID(aLangID), + mObject(nsnull) + { + } + PRUint32 mLangID; + void* mObject; + }; nsCOMPtr mSrcURI; PRUint32 mLineNo; PRPackedBool mSrcLoading; PRPackedBool mOutOfLine; nsXULDocument* mSrcLoadWaiters; // [OWNER] but not COMPtr PRUint32 mLangVersion; - nsContentUtils::ScriptObjectHolder mScriptObject; + ScriptObjectHolder mScriptObject; }; class nsXULPrototypeText : public nsXULPrototypeNode diff --git a/content/xul/document/src/nsXULDocument.cpp b/content/xul/document/src/nsXULDocument.cpp index 8b5dded271a7..f5b2a1e9b09f 100644 --- a/content/xul/document/src/nsXULDocument.cpp +++ b/content/xul/document/src/nsXULDocument.cpp @@ -3209,7 +3209,7 @@ nsXULDocument::LoadScript(nsXULPrototypeScript* aScriptProto, PRBool* aBlock) NS_ERROR("XUL cache gave me an incorrect script language"); return NS_ERROR_UNEXPECTED; } - aScriptProto->mScriptObject.set(newScriptObject); + aScriptProto->Set(newScriptObject); } if (aScriptProto->mScriptObject.mObject) { diff --git a/dom/src/base/nsJSEnvironment.cpp b/dom/src/base/nsJSEnvironment.cpp index 442fe2e28e31..5a82ead17822 100644 --- a/dom/src/base/nsJSEnvironment.cpp +++ b/dom/src/base/nsJSEnvironment.cpp @@ -3729,7 +3729,8 @@ public: ~nsJSArgArray(); // nsISupports NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsJSArgArray, nsIJSArgArray) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSArgArray, + nsIJSArgArray) // nsIArray NS_DECL_NSIARRAY @@ -3759,16 +3760,14 @@ nsJSArgArray::nsJSArgArray(JSContext *aContext, PRUint32 argc, jsval *argv, return; } - JSAutoRequest ar(aContext); - for (PRUint32 i = 0; i < argc; ++i) { - if (argv) + // Callers are allowed to pass in a null argv even for argc > 0. They can + // then use GetArgs to initialize the values. + if (argv) { + for (PRUint32 i = 0; i < argc; ++i) mArgv[i] = argv[i]; - if (!::JS_AddNamedRoot(aContext, &mArgv[i], "nsJSArgArray.mArgv[i]")) { - *prv = NS_ERROR_UNEXPECTED; - return; - } } - *prv = NS_OK; + if (argc > 0) + *prv = NS_HOLD_JS_OBJECTS(this, nsJSArgArray); } nsJSArgArray::~nsJSArgArray() @@ -3779,13 +3778,9 @@ nsJSArgArray::~nsJSArgArray() void nsJSArgArray::ReleaseJSObjects() { + if (mArgc > 0) + NS_DROP_JS_OBJECTS(this, nsJSArgArray); if (mArgv) { - NS_ASSERTION(nsJSRuntime::sRuntime, "Where's the runtime gone?"); - if (nsJSRuntime::sRuntime) { - for (PRUint32 i = 0; i < mArgc; ++i) { - ::JS_RemoveRootRT(nsJSRuntime::sRuntime, &mArgv[i]); - } - } PR_DELETE(mArgv); } mArgc = 0; @@ -3797,17 +3792,20 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSArgArray) tmp->ReleaseJSObjects(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSArgArray) - { - jsval *argv = tmp->mArgv; - if (argv) { - jsval *end; - for (end = argv + tmp->mArgc; argv < end; ++argv) { - if (JSVAL_IS_OBJECT(*argv)) - cb.NoteScriptChild(JAVASCRIPT, JSVAL_TO_OBJECT(*argv)); - } + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSArgArray) + jsval *argv = tmp->mArgv; + if (argv) { + jsval *end; + for (end = argv + tmp->mArgc; argv < end; ++argv) { + if (JSVAL_IS_GCTHING(*argv)) + NS_IMPL_CYCLE_COLLECTION_TRACE_CALLBACK(JAVASCRIPT, + JSVAL_TO_GCTHING(*argv)) } } -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSArgArray) NS_INTERFACE_MAP_ENTRY(nsIArray) diff --git a/dom/src/base/nsJSTimeoutHandler.cpp b/dom/src/base/nsJSTimeoutHandler.cpp index b2dd3e89eb7f..989f4dd3c0cb 100644 --- a/dom/src/base/nsJSTimeoutHandler.cpp +++ b/dom/src/base/nsJSTimeoutHandler.cpp @@ -59,7 +59,7 @@ class nsJSScriptTimeoutHandler: public nsIScriptTimeoutHandler public: // nsISupports NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_CLASS(nsJSScriptTimeoutHandler) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsJSScriptTimeoutHandler) nsJSScriptTimeoutHandler(); ~nsJSScriptTimeoutHandler(); @@ -118,9 +118,14 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSScriptTimeoutHandler) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mArgv) - cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, tmp->mFunObj); + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSScriptTimeoutHandler) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mExpr) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mFunObj) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSScriptTimeoutHandler) NS_INTERFACE_MAP_ENTRY(nsIScriptTimeoutHandler) NS_INTERFACE_MAP_ENTRY(nsISupports) @@ -146,49 +151,11 @@ void nsJSScriptTimeoutHandler::ReleaseJSObjects() { if (mExpr || mFunObj) { - nsCOMPtr scx = mContext; - JSRuntime *rt = nsnull; - - if (scx) { - JSContext *cx; - cx = (JSContext *)scx->GetNativeContext(); - rt = ::JS_GetRuntime(cx); - mContext = nsnull; - } else { - // XXX The timeout *must* be unrooted, even if !scx. This can be - // done without a JS context using the JSRuntime. This is safe - // enough, but it would be better to drop all a window's - // timeouts before its context is cleared. Bug 50705 describes a - // situation where we're not. In that case, at the time the - // context is cleared, a timeout (actually an Interval) is still - // active, but temporarily removed from the window's list of - // timers (placed instead on the timer manager's list). This - // makes the nearly handy ClearAllTimeouts routine useless, so - // we settled on using the JSRuntime rather than relying on the - // window having a context. It would be good to remedy this - // workable but clumsy situation someday. - - nsCOMPtr rtsvc = - do_GetService("@mozilla.org/js/xpc/RuntimeService;1"); - - if (rtsvc) { - rtsvc->GetRuntime(&rt); - } - } - - if (!rt) { - // most unexpected. not much choice but to bail. - - NS_ERROR("nsTimeout::Release() with no JSRuntime. eek!"); - - return; - } - if (mExpr) { - ::JS_RemoveRootRT(rt, &mExpr); + NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler); mExpr = nsnull; } else if (mFunObj) { - ::JS_RemoveRootRT(rt, &mFunObj); + NS_DROP_JS_OBJECTS(this, nsJSScriptTimeoutHandler); mFunObj = nsnull; } else { NS_WARNING("No func and no expr - roots may not have been removed"); @@ -280,9 +247,8 @@ nsJSScriptTimeoutHandler::Init(nsIScriptContext *aContext, PRBool *aIsInterval, } if (expr) { - if (!::JS_AddNamedRoot(cx, &mExpr, "timeout.mExpr")) { - return NS_ERROR_OUT_OF_MEMORY; - } + rv = NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler); + NS_ENSURE_SUCCESS(rv, rv); mExpr = expr; @@ -292,9 +258,8 @@ nsJSScriptTimeoutHandler::Init(nsIScriptContext *aContext, PRBool *aIsInterval, mFileName.Assign(filename); } } else if (funobj) { - if (!::JS_AddNamedRoot(cx, &mFunObj, "timeout.mFunObj")) { - return NS_ERROR_OUT_OF_MEMORY; - } + rv = NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler); + NS_ENSURE_SUCCESS(rv, rv); mFunObj = funobj; diff --git a/dom/src/events/nsJSEventListener.cpp b/dom/src/events/nsJSEventListener.cpp index 414da25a809a..46ae7c366cec 100644 --- a/dom/src/events/nsJSEventListener.cpp +++ b/dom/src/events/nsJSEventListener.cpp @@ -81,12 +81,13 @@ nsJSEventListener::nsJSEventListener(nsIScriptContext *aContext, // until we are done with it. NS_ASSERTION(aScopeObject && aContext, "EventListener with no context or scope?"); - aContext->HoldScriptObject(aScopeObject); + NS_HOLD_JS_OBJECTS(this, nsJSEventListener); } nsJSEventListener::~nsJSEventListener() { - mContext->DropScriptObject(mScopeObject); + if (mContext) + NS_DROP_JS_OBJECTS(this, nsJSEventListener); } NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSEventListener) @@ -96,9 +97,14 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSEventListener) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mTarget) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext) - cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, tmp->mScopeObject); + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSEventListener) + NS_IMPL_CYCLE_COLLECTION_TRACE_MEMBER_CALLBACK(tmp->mContext->GetScriptTypeID(), + mScopeObject) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSEventListener) NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) NS_INTERFACE_MAP_ENTRY(nsIJSEventListener) diff --git a/dom/src/events/nsJSEventListener.h b/dom/src/events/nsJSEventListener.h index 542d113d8384..d7de952c1803 100644 --- a/dom/src/events/nsJSEventListener.h +++ b/dom/src/events/nsJSEventListener.h @@ -65,8 +65,8 @@ public: // nsIJSEventListener interface virtual void SetEventName(nsIAtom* aName); - NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsJSEventListener, - nsIDOMEventListener) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSEventListener, + nsIDOMEventListener) protected: nsCOMPtr mEventName; diff --git a/js/src/xpconnect/idl/nsIXPCScriptable.idl b/js/src/xpconnect/idl/nsIXPCScriptable.idl index 7c37283480ee..00c8edd905cf 100644 --- a/js/src/xpconnect/idl/nsIXPCScriptable.idl +++ b/js/src/xpconnect/idl/nsIXPCScriptable.idl @@ -41,6 +41,8 @@ #include "nsISupports.idl" #include "nsIXPConnect.idl" +[ptr] native JSTracerPtr(JSTracer); + %{ C++ #define NS_SUCCESS_I_DID_SOMETHING \ (NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_XPCONNECT,1)) diff --git a/js/src/xpconnect/idl/nsIXPConnect.idl b/js/src/xpconnect/idl/nsIXPConnect.idl index a523c7c7c104..beecc437481a 100644 --- a/js/src/xpconnect/idl/nsIXPConnect.idl +++ b/js/src/xpconnect/idl/nsIXPConnect.idl @@ -64,7 +64,7 @@ native JSVal(jsval); native JSID(jsid); [ptr] native voidPtrPtr(void*); -[ptr] native JSTracerPtr(JSTracer); +[ptr] native nsScriptObjectTracerPtr(nsScriptObjectTracer); /***************************************************************************/ @@ -150,7 +150,7 @@ interface nsIXPCSecurityManager; interface nsIPrincipal; %{C++ -class nsCycleCollectionTraversalCallback; +class nsScriptObjectTracer; %} /***************************************************************************/ @@ -443,7 +443,7 @@ interface nsIXPCFunctionThisTranslator : nsISupports { 0xbd, 0xd6, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74 } } %} -[uuid(52fc2ff3-c0ea-46c1-9105-655283c361ff)] +[uuid(3eb7f5fc-1325-43af-aead-6033162e04af)] interface nsIXPConnect : nsISupports { %{ C++ @@ -727,4 +727,18 @@ interface nsIXPConnect : nsISupports [noscript] JSVal getCrossOriginWrapperForObject(in JSContextPtr aJSContext, in JSObjectPtr aParent, in JSObjectPtr aWrappedObj); + + /** + * Root JS objects held by aHolder. + * @param aHolder The object that hold the JS objects that should be rooted. + * @param aTrace The tracer for aHolder. + */ + [noscript] void addJSHolder(in voidPtr aHolder, + in nsScriptObjectTracerPtr aTracer); + + /** + * Stop rooting the JS objects held by aHolder. + * @param aHolder The object that hold the rooted JS objects. + */ + [noscript] void removeJSHolder(in voidPtr aHolder); }; diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp index 91e9f94551dd..6a6edec25c6a 100644 --- a/js/src/xpconnect/src/nsXPConnect.cpp +++ b/js/src/xpconnect/src/nsXPConnect.cpp @@ -534,10 +534,7 @@ void XPCMarkNotification(void *thing, uint8 flags, void *closure) { // XXX This can't deal with JS atoms yet, but probably should. uint8 ty = flags & GCF_TYPEMASK; - if(ty != GCX_OBJECT && - ty != GCX_NAMESPACE && - ty != GCX_QNAME && - ty != GCX_XML) + if(ty == GCX_FUNCTION) return; JSObjectRefcounts* jsr = static_cast(closure); @@ -695,16 +692,31 @@ NoteJSChild(JSTracer *trc, void *thing, uint32 kind) static uint8 GCTypeToTraceKindMap[GCX_NTYPES] = { JSTRACE_OBJECT, /* GCX_OBJECT */ - JSTRACE_STRING, /* GCX_STRING (unused) */ - JSTRACE_DOUBLE, /* GCX_DOUBLE (unused) */ - JSTRACE_STRING, /* GCX_MUTABLE_STRING (unused) */ - JSTRACE_FUNCTION, /* GCX_FUNCTION (unused) */ + JSTRACE_STRING, /* GCX_STRING */ + JSTRACE_DOUBLE, /* GCX_DOUBLE */ + JSTRACE_FUNCTION, /* GCX_FUNCTION */ JSTRACE_NAMESPACE, /* GCX_NAMESPACE */ JSTRACE_QNAME, /* GCX_QNAME */ - JSTRACE_XML /* GCX_XML */ - // We don't care about JSTRACE_STRING, so stop here + JSTRACE_XML, /* GCX_XML */ + (uint8)-1, /* unused */ + JSTRACE_STRING, /* GCX_EXTERNAL_STRING + 0 */ + JSTRACE_STRING, /* GCX_EXTERNAL_STRING + 1 */ + JSTRACE_STRING, /* GCX_EXTERNAL_STRING + 2 */ + JSTRACE_STRING, /* GCX_EXTERNAL_STRING + 3 */ + JSTRACE_STRING, /* GCX_EXTERNAL_STRING + 4 */ + JSTRACE_STRING, /* GCX_EXTERNAL_STRING + 5 */ + JSTRACE_STRING, /* GCX_EXTERNAL_STRING + 6 */ + JSTRACE_STRING, /* GCX_EXTERNAL_STRING + 7 */ }; +// static +uint8 +nsXPConnect::GetTraceKind(void *thing) +{ + uint8 type = *js_GetGCThingFlags(thing) & GCF_TYPEMASK; + return GCTypeToTraceKindMap[type]; +} + NS_IMETHODIMP nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb) { @@ -716,11 +728,6 @@ nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb) PRUint32 refcount = mObjRefcounts->Get(p); NS_ASSERTION(refcount > 0, "JS object but unknown to the JS GC?"); - uint8 ty = *js_GetGCThingFlags(p) & GCF_TYPEMASK; - if(ty != GCX_OBJECT && ty != GCX_NAMESPACE && ty != GCX_QNAME && - ty != GCX_XML) - return NS_OK; - #ifdef DEBUG_CC if(ty == GCX_OBJECT) { @@ -826,6 +833,11 @@ nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb) cb.DescribeNode(refcount); #endif + uint8 ty = GetTraceKind(p); + if(ty != GCX_OBJECT && ty != GCX_NAMESPACE && ty != GCX_QNAME && + ty != GCX_XML) + return NS_OK; + ContextCallbackItem trc; trc.cb = &cb; @@ -2097,6 +2109,18 @@ nsXPConnect::OnDispatchedEvent(nsIThreadInternal* aThread) return NS_ERROR_UNEXPECTED; } +NS_IMETHODIMP +nsXPConnect::AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer) +{ + return mRuntime->AddJSHolder(aHolder, aTracer); +} + +NS_IMETHODIMP +nsXPConnect::RemoveJSHolder(void* aHolder) +{ + return mRuntime->RemoveJSHolder(aHolder); +} + #ifdef DEBUG /* These are here to be callable from a debugger */ JS_BEGIN_EXTERN_C diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp index b02612fc5ad7..ced529b9ae79 100644 --- a/js/src/xpconnect/src/xpcjsruntime.cpp +++ b/js/src/xpconnect/src/xpcjsruntime.cpp @@ -251,6 +251,42 @@ ContextCallback(JSContext *cx, uintN operation) : JS_TRUE; } +struct ObjectHolder : public JSDHashEntryHdr +{ + void *holder; + nsScriptObjectTracer* tracer; +}; + +nsresult +XPCJSRuntime::AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer) +{ + if(!mJSHolders.ops) + return NS_ERROR_OUT_OF_MEMORY; + + ObjectHolder *entry = + reinterpret_cast(JS_DHashTableOperate(&mJSHolders, + aHolder, + JS_DHASH_ADD)); + if(!entry) + return NS_ERROR_OUT_OF_MEMORY; + + entry->holder = aHolder; + entry->tracer = aTracer; + + return NS_OK; +} + +nsresult +XPCJSRuntime::RemoveJSHolder(void* aHolder) +{ + if(!mJSHolders.ops) + return NS_ERROR_OUT_OF_MEMORY; + + JS_DHashTableOperate(&mJSHolders, aHolder, JS_DHASH_REMOVE); + + return NS_OK; +} + // static void XPCJSRuntime::TraceJS(JSTracer* trc, void* data) { @@ -277,16 +313,48 @@ void XPCJSRuntime::TraceJS(JSTracer* trc, void* data) } } - XPCWrappedNativeScope::TraceJS(trc, self); + // XPCJSObjectHolders don't participate in cycle collection, so always trace + // them here. + for(XPCRootSetElem *e = self->mObjectHolderRoots; e ; e = e->GetNextRoot()) + static_cast(e)->TraceJS(trc); + + self->TraceXPConnectRoots(trc); +} - for (XPCRootSetElem *e = self->mVariantRoots; e ; e = e->GetNextRoot()) +PR_STATIC_CALLBACK(void) +TraceJSObject(PRUint32 aLangID, void *aScriptThing, void *aClosure) +{ + if(aLangID == nsIProgrammingLanguage::JAVASCRIPT) + { + JS_CALL_TRACER(static_cast(aClosure), aScriptThing, + nsXPConnect::GetXPConnect()->GetTraceKind(aScriptThing), + "JSObjectHolder"); + } +} + +JS_STATIC_DLL_CALLBACK(JSDHashOperator) +TraceJSHolder(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, + void *arg) +{ + ObjectHolder* entry = reinterpret_cast(hdr); + + entry->tracer->Trace(entry->holder, TraceJSObject, arg); + + return JS_DHASH_NEXT; +} + +void XPCJSRuntime::TraceXPConnectRoots(JSTracer *trc) +{ + XPCWrappedNativeScope::TraceJS(trc, this); + + for(XPCRootSetElem *e = mVariantRoots; e ; e = e->GetNextRoot()) static_cast(e)->TraceJS(trc); - for (XPCRootSetElem *e = self->mWrappedJSRoots; e ; e = e->GetNextRoot()) + for(XPCRootSetElem *e = mWrappedJSRoots; e ; e = e->GetNextRoot()) static_cast(e)->TraceJS(trc); - for (XPCRootSetElem *e = self->mObjectHolderRoots; e ; e = e->GetNextRoot()) - static_cast(e)->TraceJS(trc); + if(mJSHolders.ops) + JS_DHashTableEnumerate(&mJSHolders, TraceJSHolder, trc); } // static @@ -809,6 +877,12 @@ XPCJSRuntime::~XPCJSRuntime() gOldJSGCCallback = NULL; gOldJSContextCallback = NULL; + + if(mJSHolders.ops) + { + JS_DHashTableFinish(&mJSHolders); + mJSHolders.ops = nsnull; + } } XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect, @@ -862,6 +936,10 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect, JS_SetExtraGCRoots(mJSRuntime, TraceJS, this); } + if(!JS_DHashTableInit(&mJSHolders, JS_DHashGetStubOps(), nsnull, + sizeof(ObjectHolder), 512)) + mJSHolders.ops = nsnull; + // Install a JavaScript 'debugger' keyword handler in debug builds only #ifdef DEBUG if(mJSRuntime && !JS_GetGlobalDebugHooks(mJSRuntime)->debuggerHandler) diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index 0bcc64caaf14..31ed95b2e080 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -498,6 +498,9 @@ public: #endif JSObjectRefcounts* GetJSObjectRefcounts() {return mObjRefcounts;} + + static uint8 GetTraceKind(void *thing); + #ifndef XPCONNECT_STANDALONE void RecordTraversal(void *p, nsISupports *s); #endif @@ -676,6 +679,9 @@ public: } static void JS_DLL_CALLBACK TraceJS(JSTracer* trc, void* data); + void TraceXPConnectRoots(JSTracer *trc); + void AddXPConnectRoots(JSContext* cx, + nsCycleCollectionTraversalCallback& cb); static JSBool JS_DLL_CALLBACK GCCallback(JSContext *cx, JSGCStatus status); @@ -683,6 +689,9 @@ public: inline void AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS); inline void AddObjectHolderRoot(XPCJSObjectHolder* holder); + nsresult AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer); + nsresult RemoveJSHolder(void* aHolder); + void DebugDump(PRInt16 depth); void SystemIsBeingShutDown(JSContext* cx); @@ -746,6 +755,7 @@ private: XPCRootSetElem *mVariantRoots; XPCRootSetElem *mWrappedJSRoots; XPCRootSetElem *mObjectHolderRoots; + JSDHashTable mJSHolders; }; /***************************************************************************/ @@ -1191,8 +1201,6 @@ public: static void InitStatics() { gScopes = nsnull; gDyingScopes = nsnull; } - void Traverse(nsCycleCollectionTraversalCallback &cb); - #ifndef XPCONNECT_STANDALONE /** * Fills the hash mapping global object to principal. @@ -1931,10 +1939,11 @@ private: class XPCWrappedNative : public nsIXPConnectWrappedNative { public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_ISUPPORTS NS_DECL_NSIXPCONNECTJSOBJECTHOLDER NS_DECL_NSIXPCONNECTWRAPPEDNATIVE NS_DECL_CYCLE_COLLECTION_CLASS(XPCWrappedNative) + NS_DECL_CYCLE_COLLECTION_UNMARK_PURPLE_STUB(XPCWrappedNative) #ifndef XPCONNECT_STANDALONE virtual nsIPrincipal* GetObjectPrincipal() const; @@ -2044,7 +2053,7 @@ public: nsISupports* aCOMObj, XPCWrappedNative** aWrapper); - void FlatJSObjectFinalized(JSContext *cx, JSObject *obj); + void FlatJSObjectFinalized(JSContext *cx); void SystemIsBeingShutDown(JSContext* cx); @@ -2196,8 +2205,10 @@ private: XPCWrappedNativeTearOffChunk mFirstChunk; JSObject* mWrapper; +#ifdef XPC_CHECK_WRAPPER_THREADSAFETY public: nsCOMPtr mThread; // Don't want to overload _mOwningThread +#endif }; /*************************************************************************** @@ -3028,7 +3039,6 @@ private: JSUint32 mWrappedNativeThreadsafetyReportDepth; #endif PRThread* mThread; - nsVoidArray mNativesToReleaseArray; static PRLock* gLock; static XPCPerThreadData* gThreads; diff --git a/js/src/xpconnect/src/xpcwrappedjs.cpp b/js/src/xpconnect/src/xpcwrappedjs.cpp index 32cd5edcf712..8eed14c1d4fc 100644 --- a/js/src/xpconnect/src/xpcwrappedjs.cpp +++ b/js/src/xpconnect/src/xpcwrappedjs.cpp @@ -589,10 +589,6 @@ nsXPCWrappedJS::SystemIsBeingShutDown(JSRuntime* rt) // work (and avoid crashing some platforms). mJSObj = nsnull; - // There is no reason to keep this root any longer. Since we've cleared - // mJSObj our dtor will not remove the root later. So, we do it now. - JS_RemoveRootRT(rt, &mJSObj); - // Notify other wrappers in the chain. if(mNext) mNext->SystemIsBeingShutDown(rt); diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp index 2b1d3e88b820..ed78d2add53a 100644 --- a/js/src/xpconnect/src/xpcwrappednative.cpp +++ b/js/src/xpconnect/src/xpcwrappednative.cpp @@ -961,7 +961,7 @@ NS_IMPL_THREADSAFE_RELEASE(XPCWrappedNative) */ void -XPCWrappedNative::FlatJSObjectFinalized(JSContext *cx, JSObject *obj) +XPCWrappedNative::FlatJSObjectFinalized(JSContext *cx) { if(!IsValid()) return; diff --git a/js/src/xpconnect/src/xpcwrappednativejsops.cpp b/js/src/xpconnect/src/xpcwrappednativejsops.cpp index 32d084a7cb6f..674e457af3c8 100644 --- a/js/src/xpconnect/src/xpcwrappednativejsops.cpp +++ b/js/src/xpconnect/src/xpcwrappednativejsops.cpp @@ -647,7 +647,7 @@ XPC_WN_NoHelper_Finalize(JSContext *cx, JSObject *obj) XPCWrappedNative* p = (XPCWrappedNative*) JS_GetPrivate(cx, obj); if(!p) return; - p->FlatJSObjectFinalized(cx, obj); + p->FlatJSObjectFinalized(cx); } static void @@ -658,7 +658,7 @@ TraceScopeJSObjects(JSTracer *trc, XPCWrappedNativeScope* scope) JSObject* obj; obj = scope->GetGlobalJSObject(); - NS_ASSERTION(scope, "bad scope JSObject"); + NS_ASSERTION(obj, "bad scope JSObject"); JS_CALL_OBJECT_TRACER(trc, obj, "XPCWrappedNativeScope::mGlobalJSObject"); obj = scope->GetPrototypeJSObject(); @@ -1035,7 +1035,7 @@ XPC_WN_Helper_Finalize(JSContext *cx, JSObject *obj) if(!wrapper) return; wrapper->GetScriptableCallback()->Finalize(wrapper, cx, obj); - wrapper->FlatJSObjectFinalized(cx, obj); + wrapper->FlatJSObjectFinalized(cx); } JS_STATIC_DLL_CALLBACK(void) diff --git a/js/src/xpconnect/src/xpcwrappednativescope.cpp b/js/src/xpconnect/src/xpcwrappednativescope.cpp index ea9c5b6d588f..e167780f0167 100644 --- a/js/src/xpconnect/src/xpcwrappednativescope.cpp +++ b/js/src/xpconnect/src/xpcwrappednativescope.cpp @@ -851,16 +851,6 @@ XPCWrappedNativeScope::DebugDump(PRInt16 depth) #endif } -void -XPCWrappedNativeScope::Traverse(nsCycleCollectionTraversalCallback &cb) -{ - // See TraceScopeJSObjects. - cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, mGlobalJSObject); - cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, mPrototypeJSObject); - cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, - mPrototypeJSFunction); -} - #ifndef XPCONNECT_STANDALONE // static void diff --git a/xpcom/base/nsAgg.h b/xpcom/base/nsAgg.h index 2ba68b5477c8..294a50d97d77 100644 --- a/xpcom/base/nsAgg.h +++ b/xpcom/base/nsAgg.h @@ -118,7 +118,8 @@ public: \ { \ return p->InnerObject(); \ } \ -}; +}; \ +NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE // Put this in your class's constructor: #define NS_INIT_AGGREGATED(outer) \ diff --git a/xpcom/glue/nsCycleCollectionParticipant.cpp b/xpcom/glue/nsCycleCollectionParticipant.cpp index 5a90aaf536ce..4e0e3a2a3e7f 100644 --- a/xpcom/glue/nsCycleCollectionParticipant.cpp +++ b/xpcom/glue/nsCycleCollectionParticipant.cpp @@ -38,6 +38,21 @@ #include "nsCycleCollectionParticipant.h" #include "nsCOMPtr.h" +PR_STATIC_CALLBACK(void) +NoteChild(PRUint32 aLangID, void *aScriptThing, void *aClosure) +{ + nsCycleCollectionTraversalCallback *cb = + static_cast(aClosure); + cb->NoteScriptChild(aLangID, aScriptThing); +} + +void +nsScriptObjectTracer::TraverseScriptObjects(void *p, + nsCycleCollectionTraversalCallback &cb) +{ + Trace(p, NoteChild, &cb); +} + nsresult nsXPCOMCycleCollectionParticipant::Root(void *p) { @@ -72,6 +87,12 @@ nsXPCOMCycleCollectionParticipant::UnmarkPurple(nsISupports *n) { } +NS_IMETHODIMP_(void) +nsXPCOMCycleCollectionParticipant::Trace(void *p, TraceCallback cb, + void *closure) +{ +} + PRBool nsXPCOMCycleCollectionParticipant::CheckForRightISupports(nsISupports *s) { diff --git a/xpcom/glue/nsCycleCollectionParticipant.h b/xpcom/glue/nsCycleCollectionParticipant.h index fdb54f2292dc..9f5ed2a9d759 100644 --- a/xpcom/glue/nsCycleCollectionParticipant.h +++ b/xpcom/glue/nsCycleCollectionParticipant.h @@ -125,8 +125,19 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsCycleCollectionParticipant, #undef IMETHOD_VISIBILITY #define IMETHOD_VISIBILITY NS_COM_GLUE +typedef void +(* PR_CALLBACK TraceCallback)(PRUint32 langID, void *p, void *closure); + +class NS_NO_VTABLE nsScriptObjectTracer : public nsCycleCollectionParticipant +{ +public: + NS_IMETHOD_(void) Trace(void *p, TraceCallback cb, void *closure) = 0; + void NS_COM_GLUE TraverseScriptObjects(void *p, + nsCycleCollectionTraversalCallback &cb); +}; + class NS_COM_GLUE nsXPCOMCycleCollectionParticipant - : public nsCycleCollectionParticipant + : public nsScriptObjectTracer { public: NS_IMETHOD Traverse(void *p, nsCycleCollectionTraversalCallback &cb); @@ -135,6 +146,8 @@ public: NS_IMETHOD Unlink(void *p); NS_IMETHOD Unroot(void *p); + NS_IMETHOD_(void) Trace(void *p, TraceCallback cb, void *closure); + NS_IMETHOD_(void) UnmarkPurple(nsISupports *p); PRBool CheckForRightISupports(nsISupports *s); @@ -153,8 +166,11 @@ public: #define NS_CYCLE_COLLECTION_CLASSNAME(_class) \ _class::NS_CYCLE_COLLECTION_INNERCLASS +#define NS_CYCLE_COLLECTION_INNERNAME \ + _cycleCollectorGlobal + #define NS_CYCLE_COLLECTION_NAME(_class) \ - _class##_cycleCollectorGlobal + _class::NS_CYCLE_COLLECTION_INNERNAME #define NS_IMPL_QUERY_CYCLE_COLLECTION(_class) \ if ( aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ) { \ @@ -201,6 +217,8 @@ public: } \ nsresult rv; +#define NS_CYCLE_COLLECTION_UPCAST(obj, clazz) \ + NS_CYCLE_COLLECTION_CLASSNAME(clazz)::Upcast(obj) /////////////////////////////////////////////////////////////////////////////// // Helpers for implementing nsCycleCollectionParticipant::Unlink @@ -323,7 +341,7 @@ public: } #define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(_ptr, _ptr_class) \ - cb.NoteNativeChild(_ptr, &NS_CYCLE_COLLECTION_NATIVE_NAME(_ptr_class)); + cb.NoteNativeChild(_ptr, &NS_CYCLE_COLLECTION_NAME(_ptr_class)); #define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(_field, _field_class) \ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(tmp->_field, _field_class) @@ -340,18 +358,61 @@ public: _element_class) \ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY(tmp->_field, _element_class) +#define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS \ + TraverseScriptObjects(tmp, cb); + #define NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END \ return NS_OK; \ } +/////////////////////////////////////////////////////////////////////////////// +// Helpers for implementing nsScriptObjectTracer::Trace +/////////////////////////////////////////////////////////////////////////////// + +#define NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(_class) \ + void \ + NS_CYCLE_COLLECTION_CLASSNAME(_class)::Trace(void *p, \ + TraceCallback aCallback, \ + void *aClosure) \ + { \ + nsISupports *s = static_cast(p); \ + NS_ASSERTION(CheckForRightISupports(s), \ + "not the nsISupports pointer we expect"); \ + _class *tmp = Downcast(s); + +#define NS_IMPL_CYCLE_COLLECTION_TRACE_NATIVE_BEGIN(_class) \ + void \ + NS_CYCLE_COLLECTION_CLASSNAME(_class)::Trace(void *p, \ + TraceCallback aCallback, \ + void *aClosure) \ + { \ + _class *tmp = static_cast<_class*>(p); + +#define NS_IMPL_CYCLE_COLLECTION_TRACE_CALLBACK(_langID, _object) \ + if (_object) \ + aCallback(_langID, _object, aClosure); + +#define NS_IMPL_CYCLE_COLLECTION_TRACE_MEMBER_CALLBACK(_langID, _field) \ + NS_IMPL_CYCLE_COLLECTION_TRACE_CALLBACK(_langID, tmp->_field) + +#define NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(_object) \ + NS_IMPL_CYCLE_COLLECTION_TRACE_CALLBACK(nsIProgrammingLanguage::JAVASCRIPT, \ + _object) + +#define NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(_field) \ + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(tmp->_field) + +#define NS_IMPL_CYCLE_COLLECTION_TRACE_END \ + } + /////////////////////////////////////////////////////////////////////////////// // Helpers for implementing a concrete nsCycleCollectionParticipant /////////////////////////////////////////////////////////////////////////////// -#define NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(_class, _base) \ -class NS_CYCLE_COLLECTION_INNERCLASS \ - : public nsXPCOMCycleCollectionParticipant \ -{ \ +#define NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE \ + static NS_CYCLE_COLLECTION_INNERCLASS NS_CYCLE_COLLECTION_INNERNAME; + +#define NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base) \ public: \ NS_IMETHOD Unlink(void *p); \ NS_IMETHOD Traverse(void *p, \ @@ -367,12 +428,31 @@ public: \ static nsISupports* Upcast(_class *p) \ { \ return NS_ISUPPORTS_CAST(_base*, p); \ - } \ -}; + } + +#define NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(_class, _base) \ +class NS_CYCLE_COLLECTION_INNERCLASS \ + : public nsXPCOMCycleCollectionParticipant \ +{ \ + NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base) \ +}; \ +NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE #define NS_DECL_CYCLE_COLLECTION_CLASS(_class) \ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(_class, _class) +#define NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(_class, _base) \ +class NS_CYCLE_COLLECTION_INNERCLASS \ + : public nsXPCOMCycleCollectionParticipant \ +{ \ + NS_DECL_CYCLE_COLLECTION_CLASS_BODY(_class, _base) \ + NS_IMETHOD_(void) Trace(void *p, TraceCallback cb, void *closure); \ +}; \ +NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE + +#define NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(_class) \ + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(_class, _class) + #define NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(_class, _base_class) \ class NS_CYCLE_COLLECTION_INNERCLASS \ : public NS_CYCLE_COLLECTION_CLASSNAME(_base_class) \ @@ -386,7 +466,8 @@ public: \ return static_cast<_class*>(static_cast<_base_class*>( \ NS_CYCLE_COLLECTION_CLASSNAME(_base_class)::Downcast(s))); \ } \ -}; +}; \ +NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE #define NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(_class, \ _base_class) \ @@ -401,7 +482,8 @@ public: \ return static_cast<_class*>(static_cast<_base_class*>( \ NS_CYCLE_COLLECTION_CLASSNAME(_base_class)::Downcast(s))); \ } \ -}; +}; \ +NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE /** * This implements a stub UnmarkPurple function for classes that want to be @@ -415,31 +497,32 @@ public: \ } \ #define NS_IMPL_CYCLE_COLLECTION_CLASS(_class) \ - static NS_CYCLE_COLLECTION_CLASSNAME(_class) \ - NS_CYCLE_COLLECTION_NAME(_class); + NS_CYCLE_COLLECTION_CLASSNAME(_class) NS_CYCLE_COLLECTION_NAME(_class); -#define NS_CYCLE_COLLECTION_NATIVE_INNERNAME \ - _cycleCollectorGlobal - -#define NS_CYCLE_COLLECTION_NATIVE_NAME(_class) \ - _class::NS_CYCLE_COLLECTION_NATIVE_INNERNAME - -#define NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(_class) \ - class NS_CYCLE_COLLECTION_INNERCLASS \ - : public nsCycleCollectionParticipant \ - { \ +#define NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY \ public: \ NS_IMETHOD Root(void *n); \ NS_IMETHOD Unlink(void *n); \ NS_IMETHOD Unroot(void *n); \ NS_IMETHOD Traverse(void *n, \ - nsCycleCollectionTraversalCallback &cb); \ - }; \ - static NS_CYCLE_COLLECTION_INNERCLASS \ - NS_CYCLE_COLLECTION_NATIVE_INNERNAME; + nsCycleCollectionTraversalCallback &cb); -#define NS_IMPL_CYCLE_COLLECTION_NATIVE_CLASS(_class) \ - NS_CYCLE_COLLECTION_CLASSNAME(_class) NS_CYCLE_COLLECTION_NATIVE_NAME(_class); +#define NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(_class) \ + class NS_CYCLE_COLLECTION_INNERCLASS \ + : public nsCycleCollectionParticipant \ + { \ + NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY \ + }; \ + NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE + +#define NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(_class) \ + class NS_CYCLE_COLLECTION_INNERCLASS \ + : public nsScriptObjectTracer \ + { \ + NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS_BODY \ + NS_IMETHOD_(void) Trace(void *p, TraceCallback cb, void *closure); \ + }; \ + NS_CYCLE_COLLECTION_PARTICIPANT_INSTANCE #define NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(_class, _root_function) \ NS_IMETHODIMP \