mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-04 07:40:42 +00:00
the rest of the fix for bug 66950 to add nsISupportsWeakReference support of xpconnect wrapped JSObjects. r=rogerl sr=brendan
This commit is contained in:
parent
9dd0a41319
commit
bd58ec8cea
@ -55,6 +55,84 @@ const char* XPCJSRuntime::mStrings[] = {
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
// GCCallback calls are chained
|
||||
static JSGCCallback gOldJSGCCallback;
|
||||
|
||||
// data holder class for the enumerator callback below
|
||||
struct JSDyingJSObjectData
|
||||
{
|
||||
JSContext* cx;
|
||||
nsVoidArray* array;
|
||||
};
|
||||
|
||||
JS_STATIC_DLL_CALLBACK(intN)
|
||||
WrappedJSDyingJSObjectFinder(JSHashEntry *he, intN i, void *arg)
|
||||
{
|
||||
JSDyingJSObjectData* data = (JSDyingJSObjectData*) arg;
|
||||
nsXPCWrappedJS* wrapper = (nsXPCWrappedJS*)he->value;
|
||||
NS_ASSERTION(wrapper, "found a null JS wrapper!");
|
||||
|
||||
// walk the wrapper chain and find any whose JSObject is to be finalized
|
||||
while(wrapper)
|
||||
{
|
||||
if(wrapper->IsSubjectToFinalization())
|
||||
{
|
||||
if(JS_IsAboutToBeFinalized(data->cx, wrapper->GetJSObject()))
|
||||
data->array->AppendElement(wrapper);
|
||||
}
|
||||
wrapper = wrapper->GetNextWrapper();
|
||||
}
|
||||
return HT_ENUMERATE_NEXT;
|
||||
}
|
||||
|
||||
|
||||
// static
|
||||
JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status)
|
||||
{
|
||||
if(status == JSGC_MARK_END || status == JSGC_END)
|
||||
{
|
||||
XPCJSRuntime* self = nsXPConnect::GetRuntime();
|
||||
if(self)
|
||||
{
|
||||
nsVoidArray* array = &self->mWrappedJSToReleaseArray;
|
||||
|
||||
if(status == JSGC_MARK_END)
|
||||
{
|
||||
nsAutoLock lock(self->mMapLock); // lock the wrapper map
|
||||
JSDyingJSObjectData data = {cx, array};
|
||||
|
||||
// Add any wrappers whose JSObjects are to be finalized to
|
||||
// this array. Note that this is a nsVoidArray because
|
||||
// we do not want to be changing the refcount of these wrappers.
|
||||
// We add them to the array now and Release the array members
|
||||
// later to avoid the posibility of doing any JS GCThing
|
||||
// allocations during the gc cycle.
|
||||
self->mWrappedJSMap->Enumerate(WrappedJSDyingJSObjectFinder,
|
||||
&data);
|
||||
}
|
||||
else // status == JSGC_END
|
||||
{
|
||||
// Release all the members whose JSObjects are now known
|
||||
// to be dead.
|
||||
for(PRInt32 i = array->Count() - 1; i >= 0; i--)
|
||||
{
|
||||
nsXPCWrappedJS* wrapper =
|
||||
NS_REINTERPRET_CAST(nsXPCWrappedJS*,
|
||||
array->ElementAt(i));
|
||||
|
||||
NS_RELEASE(wrapper);
|
||||
}
|
||||
array->Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// always chain to old GCCallback if non-null.
|
||||
return gOldJSGCCallback ? gOldJSGCCallback(cx, status) : JS_TRUE;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
#ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN
|
||||
JS_STATIC_DLL_CALLBACK(JSHashNumber)
|
||||
hash_root(const void *key)
|
||||
@ -158,7 +236,8 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect,
|
||||
mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_SIZE)),
|
||||
mWrappedJSClassMap(IID2WrappedJSClassMap::newMap(XPC_JS_CLASS_MAP_SIZE)),
|
||||
mWrappedNativeClassMap(IID2WrappedNativeClassMap::newMap(XPC_NATIVE_CLASS_MAP_SIZE)),
|
||||
mMapLock(PR_NewLock())
|
||||
mMapLock(PR_NewLock()),
|
||||
mWrappedJSToReleaseArray()
|
||||
{
|
||||
|
||||
#ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN
|
||||
@ -177,6 +256,10 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect,
|
||||
mJSRuntimeService->GetRuntime(&mJSRuntime);
|
||||
}
|
||||
|
||||
NS_ASSERTION(!gOldJSGCCallback, "XPCJSRuntime created more than once");
|
||||
if(mJSRuntime)
|
||||
gOldJSGCCallback = JS_SetGCCallbackRT(mJSRuntime, GCCallback);
|
||||
|
||||
// Install a JavaScript 'debugger' keyword handler in debug builds only
|
||||
#ifdef DEBUG
|
||||
if(mJSRuntime)
|
||||
|
@ -54,6 +54,7 @@
|
||||
#include "nsIXPCScriptable.h"
|
||||
#include "nsIXPCSecurityManager.h"
|
||||
#include "nsIJSRuntimeService.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIModule.h"
|
||||
#include "nsAutoLock.h"
|
||||
@ -73,10 +74,12 @@
|
||||
#include "prlong.h"
|
||||
#include "prmem.h"
|
||||
#include "prenv.h"
|
||||
#include "nsReadableUtils.h"
|
||||
|
||||
#include "nsIJSContextStack.h"
|
||||
#include "prthread.h"
|
||||
#include "nsDeque.h"
|
||||
#include "nsVoidArray.h"
|
||||
|
||||
#ifdef XPCONNECT_STANDALONE
|
||||
#include <math.h>
|
||||
@ -252,6 +255,8 @@ public:
|
||||
return mStrings[index];
|
||||
}
|
||||
|
||||
static JSBool GCCallback(JSContext *cx, JSGCStatus status);
|
||||
|
||||
void DebugDump(PRInt16 depth);
|
||||
|
||||
~XPCJSRuntime();
|
||||
@ -291,6 +296,7 @@ private:
|
||||
IID2WrappedJSClassMap* mWrappedJSClassMap;
|
||||
IID2WrappedNativeClassMap* mWrappedNativeClassMap;
|
||||
PRLock* mMapLock;
|
||||
nsVoidArray mWrappedJSToReleaseArray;
|
||||
};
|
||||
|
||||
/***************************************************************************/
|
||||
@ -689,7 +695,7 @@ public:
|
||||
static nsXPCWrappedJSClass* GetNewOrUsedClass(XPCJSRuntime* rt,
|
||||
REFNSIID aIID);
|
||||
REFNSIID GetIID() const {return mIID;}
|
||||
XPCJSRuntime* GetJSRuntime() const {return mRuntime;}
|
||||
XPCJSRuntime* GetRuntime() const {return mRuntime;}
|
||||
nsIInterfaceInfo* GetInterfaceInfo() const {return mInfo;}
|
||||
const char* GetInterfaceName();
|
||||
|
||||
@ -758,7 +764,9 @@ private:
|
||||
|
||||
/*************************/
|
||||
|
||||
class nsXPCWrappedJS : public nsXPTCStubBase, public nsIXPConnectWrappedJS
|
||||
class nsXPCWrappedJS : public nsXPTCStubBase,
|
||||
public nsIXPConnectWrappedJS,
|
||||
public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
@ -787,12 +795,21 @@ public:
|
||||
nsXPCWrappedJSClass* GetClass() const {return mClass;}
|
||||
REFNSIID GetIID() const {return GetClass()->GetIID();}
|
||||
nsXPCWrappedJS* GetRootWrapper() const {return mRoot;}
|
||||
nsXPCWrappedJS* GetNextWrapper() const {return mNext;}
|
||||
|
||||
nsXPCWrappedJS* Find(REFNSIID aIID);
|
||||
nsXPCWrappedJS* FindInherited(REFNSIID aIID);
|
||||
|
||||
JSBool IsValid() const {return mJSObj != nsnull;}
|
||||
void SystemIsBeingShutDown(JSRuntime* rt);
|
||||
|
||||
// This is used by XPCJSRuntime::GCCallback to find wrappers that no
|
||||
// longer root their JSObject and are only still alive because they
|
||||
// were being used via nsSupportsWeakReference at the time when their
|
||||
// last (outside) reference was released. Wrappers that fit into that
|
||||
// category are only deleted when we see that their cooresponding JSObject
|
||||
// is to be finalized.
|
||||
JSBool IsSubjectToFinalization() const {return IsValid() && mRefCnt == 1;}
|
||||
|
||||
JSBool IsAggregatedToNative() const {return mRoot->mOuter != nsnull;}
|
||||
nsISupports* GetAggregatedNativeObject() const {return mRoot->mOuter;}
|
||||
@ -1350,7 +1367,7 @@ private:
|
||||
void FillCache(JSContext *cx, JSObject *obj,
|
||||
nsIXPConnectWrappedNative *wrapper,
|
||||
nsIXPCScriptable *arbitrary);
|
||||
|
||||
|
||||
JSBool NeedToFillCache(JSObject* obj) const {return obj != mObj;}
|
||||
|
||||
private:
|
||||
|
@ -94,7 +94,24 @@ nsXPCWrappedJS::QueryInterface(REFNSIID aIID, void** aInstancePtr)
|
||||
}
|
||||
|
||||
|
||||
// do chained ref counting
|
||||
// Refcounting is now similar to that used in the chained (pre-flattening)
|
||||
// wrappednative system.
|
||||
//
|
||||
// We are now holding an extra refcount for nsISupportsWeakReference support.
|
||||
//
|
||||
// Non-root wrappers remove themselves from the chain in their destructors.
|
||||
// We root the JSObject as the refcount transitions from 1->2. And we unroot
|
||||
// the JSObject when the refcount transitions from 2->1.
|
||||
//
|
||||
// When the transition from 2->1 is made and no one holds a weak ref to the
|
||||
// (aggregated) object then we decrement the refcount again to 0 (and
|
||||
// destruct) . However, if a weak ref is held at the 2->1 transition, then we
|
||||
// leave the refcount at 1 to indicate that state. This leaves the JSObject
|
||||
// no longer rooted by us and (as far as we know) subject to possible
|
||||
// collection. Code in XPCJSRuntime watches for JS gc to happen and will do
|
||||
// the final release on wrappers whose JSObjects get finalized. Note that
|
||||
// even after tranistioning to this refcount-of-one state callers might do
|
||||
// an addref and cause us to re-root the JSObject and continue on more normally.
|
||||
|
||||
nsrefcnt
|
||||
nsXPCWrappedJS::AddRef(void)
|
||||
@ -102,8 +119,15 @@ nsXPCWrappedJS::AddRef(void)
|
||||
NS_PRECONDITION(mRoot, "bad root");
|
||||
nsrefcnt cnt = (nsrefcnt) PR_AtomicIncrement((PRInt32*)&mRefCnt);
|
||||
NS_LOG_ADDREF(this, cnt, "nsXPCWrappedJS", sizeof(*this));
|
||||
if(1 == cnt && mRoot && mRoot != this)
|
||||
NS_ADDREF(mRoot);
|
||||
|
||||
if(2 == cnt && IsValid())
|
||||
{
|
||||
AutoPushCompatibleJSContext
|
||||
autoContext(mClass->GetRuntime()->GetJSRuntime());
|
||||
JSContext* cx = autoContext.GetJSContext();
|
||||
if(cx)
|
||||
JS_AddNamedRoot(cx, &mJSObj, "nsXPCWrappedJS::mJSObj");
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}
|
||||
@ -118,20 +142,30 @@ nsXPCWrappedJS::Release(void)
|
||||
NS_ASSERTION(IsValid(), "post xpconnect shutdown call of nsXPCWrappedJS::Release");
|
||||
#endif
|
||||
|
||||
do_decrement:
|
||||
|
||||
nsrefcnt cnt = (nsrefcnt) PR_AtomicDecrement((PRInt32*)&mRefCnt);
|
||||
NS_LOG_RELEASE(this, cnt, "nsXPCWrappedJS");
|
||||
|
||||
if(0 == cnt)
|
||||
{
|
||||
if(mRoot == this)
|
||||
{
|
||||
NS_DELETEXPCOM(this); // cascaded delete
|
||||
}
|
||||
else
|
||||
{
|
||||
mRoot->Release();
|
||||
}
|
||||
NS_DELETEXPCOM(this); // also unlinks us from chain
|
||||
return 0;
|
||||
}
|
||||
if(1 == cnt)
|
||||
{
|
||||
if(IsValid())
|
||||
{
|
||||
XPCJSRuntime* rt = mClass->GetRuntime();
|
||||
if(rt)
|
||||
JS_RemoveRootRT(rt->GetJSRuntime(), &mJSObj);
|
||||
}
|
||||
|
||||
// If we are not being used from a weak reference, then this extra
|
||||
// ref is not needed and we can let ourself be deleted.
|
||||
if(!mRoot->HasWeakReferences())
|
||||
goto do_decrement;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
@ -275,41 +309,60 @@ nsXPCWrappedJS::nsXPCWrappedJS(XPCContext* xpcc,
|
||||
#endif
|
||||
|
||||
NS_INIT_REFCNT();
|
||||
// intensionally do double addref - see Release().
|
||||
NS_ADDREF_THIS();
|
||||
NS_ADDREF_THIS();
|
||||
NS_ADDREF(aClass);
|
||||
NS_IF_ADDREF(mOuter);
|
||||
NS_ASSERTION(xpcc && xpcc->GetJSContext(), "bad context");
|
||||
JS_AddNamedRoot(xpcc->GetJSContext(), &mJSObj,
|
||||
"nsXPCWrappedJS::mJSObj");
|
||||
|
||||
if(mRoot != this)
|
||||
NS_ADDREF(mRoot);
|
||||
|
||||
}
|
||||
|
||||
nsXPCWrappedJS::~nsXPCWrappedJS()
|
||||
{
|
||||
NS_PRECONDITION(0 == mRefCnt, "refcounting error");
|
||||
// Any destructors called after shutdown are just going to leak stuff.
|
||||
if(IsValid())
|
||||
|
||||
if(mRoot != this)
|
||||
{
|
||||
// unlink this wrapper
|
||||
nsXPCWrappedJS* cur = mRoot;
|
||||
while(1)
|
||||
{
|
||||
if(cur->mNext == this)
|
||||
{
|
||||
cur->mNext = mNext;
|
||||
break;
|
||||
}
|
||||
cur = cur->mNext;
|
||||
NS_ASSERTION(cur, "failed to find wrapper in its own chain");
|
||||
}
|
||||
// let the root go
|
||||
NS_RELEASE(mRoot);
|
||||
}
|
||||
else
|
||||
{
|
||||
NS_ASSERTION(!mNext, "root wrapper with non-empty chain being deleted");
|
||||
|
||||
// Let the nsWeakReference object (if present) know of our demise.
|
||||
ClearWeakReferences();
|
||||
|
||||
// remove this root wrapper from the map
|
||||
XPCJSRuntime* rt = nsXPConnect::GetRuntime();
|
||||
if(rt)
|
||||
{
|
||||
if(mRoot == this)
|
||||
JSObject2WrappedJSMap* map = rt->GetWrappedJSMap();
|
||||
if(map)
|
||||
{
|
||||
JSObject2WrappedJSMap* map = rt->GetWrappedJSMap();
|
||||
if(map)
|
||||
{
|
||||
nsAutoLock lock(rt->GetMapLock());
|
||||
map->Remove(this);
|
||||
}
|
||||
nsAutoLock lock(rt->GetMapLock());
|
||||
map->Remove(this);
|
||||
}
|
||||
JS_RemoveRootRT(rt->GetJSRuntime(), &mJSObj);
|
||||
}
|
||||
NS_IF_RELEASE(mClass);
|
||||
// XXX Should this moved out of the 'if' block?
|
||||
// XXX OR... Should this called in SystemIsBeingShutDown?
|
||||
NS_IF_RELEASE(mOuter);
|
||||
}
|
||||
if(mNext)
|
||||
NS_DELETEXPCOM(mNext); // cascaded delete
|
||||
|
||||
NS_IF_RELEASE(mClass);
|
||||
NS_IF_RELEASE(mOuter);
|
||||
}
|
||||
|
||||
nsXPCWrappedJS*
|
||||
|
@ -291,6 +291,26 @@ nsXPCWrappedJSClass::DelegatedQueryInterface(nsXPCWrappedJS* self,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// We support nsISupportsWeakReference iff the root wrapped JSObject
|
||||
// claims to support it in its QueryInterface implementation.
|
||||
if(aIID.Equals(NS_GET_IID(nsISupportsWeakReference)))
|
||||
{
|
||||
// We only want to expose one implementation from our aggregate.
|
||||
nsXPCWrappedJS* root = self->GetRootWrapper();
|
||||
|
||||
// Fail if JSObject doesn't claim support for nsISupportsWeakReference
|
||||
if(!root->IsValid() ||
|
||||
!CallQueryInterfaceOnJSObject(root->GetJSObject(), aIID))
|
||||
{
|
||||
*aInstancePtr = nsnull;
|
||||
return NS_NOINTERFACE;
|
||||
}
|
||||
|
||||
NS_ADDREF(root);
|
||||
*aInstancePtr = (void*) NS_STATIC_CAST(nsISupportsWeakReference*,root);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsXPCWrappedJS* sibling;
|
||||
|
||||
// Checks for any existing wrapper explicitly constructed for this iid.
|
||||
|
Loading…
x
Reference in New Issue
Block a user