зеркало из https://github.com/mozilla/gecko-dev.git
Fix for bug 367779 (Make cycle collection through JS objects more reliable). r=jst, sr=brendan.
This commit is contained in:
Родитель
e991cbf0ba
Коммит
3e4c261c01
|
@ -108,18 +108,42 @@ nsXPConnect::nsXPConnect()
|
|||
}
|
||||
|
||||
typedef nsBaseHashtable<nsClearingVoidPtrHashKey, PRUint32, PRUint32> PointerSet;
|
||||
#ifndef XPCONNECT_STANDALONE
|
||||
typedef nsBaseHashtable<nsClearingVoidPtrHashKey, nsISupports*, nsISupports*> ScopeSet;
|
||||
#endif
|
||||
|
||||
struct JSObjectRefcounts
|
||||
{
|
||||
PointerSet mRefCounts;
|
||||
JSObjectRefcounts()
|
||||
#ifndef XPCONNECT_STANDALONE
|
||||
ScopeSet mScopes;
|
||||
#endif
|
||||
PRBool mMarkEnded;
|
||||
JSObjectRefcounts() : mMarkEnded(PR_FALSE)
|
||||
{
|
||||
mRefCounts.Init();
|
||||
#ifndef XPCONNECT_STANDALONE
|
||||
mScopes.Init();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
mRefCounts.Clear();
|
||||
#ifndef XPCONNECT_STANDALONE
|
||||
mScopes.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
void MarkStart()
|
||||
{
|
||||
Clear();
|
||||
mMarkEnded = PR_FALSE;
|
||||
}
|
||||
|
||||
void MarkEnd()
|
||||
{
|
||||
mMarkEnded = PR_TRUE;
|
||||
}
|
||||
|
||||
void Ref(void *obj, uint8 flags)
|
||||
|
@ -430,6 +454,23 @@ nsXPConnect::GetInfoForName(const char * name, nsIInterfaceInfo** info)
|
|||
return FindInfo(NameTester, name, mInterfaceInfoManager, info);
|
||||
}
|
||||
|
||||
static JSGCCallback gOldJSGCCallback;
|
||||
|
||||
JS_STATIC_DLL_CALLBACK(JSBool)
|
||||
XPCCycleGCCallback(JSContext *cx, JSGCStatus status)
|
||||
{
|
||||
// Chain to old GCCallback first, we want to get all the mark notifications
|
||||
// before recording the end of the mark phase.
|
||||
JSBool ok = gOldJSGCCallback ? gOldJSGCCallback(cx, status) : JS_TRUE;
|
||||
|
||||
// Record the end of a mark phase. If we get more mark notifications then
|
||||
// the GC has restarted and we'll need to clear the refcounts first.
|
||||
if(status == JSGC_MARK_END)
|
||||
nsXPConnect::GetXPConnect()->GetJSObjectRefcounts()->MarkEnd();
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void XPCMarkNotification(void *thing, uint8 flags, void *closure)
|
||||
{
|
||||
uint8 ty = flags & GCF_TYPEMASK;
|
||||
|
@ -439,6 +480,12 @@ void XPCMarkNotification(void *thing, uint8 flags, void *closure)
|
|||
return;
|
||||
|
||||
JSObjectRefcounts* jsr = NS_STATIC_CAST(JSObjectRefcounts*, closure);
|
||||
// We're marking after a mark phase ended, so the GC restarted itself and
|
||||
// we want to clear the refcounts first.
|
||||
// XXX With GC_MARK_DEBUG defined we end up too many times in
|
||||
// XPCMarkNotification, even while taking JSGC_MARK_END into account.
|
||||
if(jsr->mMarkEnded)
|
||||
jsr->MarkStart();
|
||||
jsr->Ref(thing, flags);
|
||||
}
|
||||
|
||||
|
@ -448,17 +495,32 @@ nsXPConnect::BeginCycleCollection()
|
|||
if (!mObjRefcounts)
|
||||
mObjRefcounts = new JSObjectRefcounts;
|
||||
|
||||
mObjRefcounts->Clear();
|
||||
mObjRefcounts->MarkStart();
|
||||
XPCCallContext cx(NATIVE_CALLER);
|
||||
if (cx.IsValid()) {
|
||||
gOldJSGCCallback = JS_SetGCCallback(cx, XPCCycleGCCallback);
|
||||
JS_SetGCThingCallback(cx, XPCMarkNotification, mObjRefcounts);
|
||||
JS_GC(cx);
|
||||
JS_SetGCThingCallback(cx, nsnull, nsnull);
|
||||
JS_SetGCCallback(cx, gOldJSGCCallback);
|
||||
gOldJSGCCallback = nsnull;
|
||||
|
||||
#ifndef XPCONNECT_STANDALONE
|
||||
XPCWrappedNativeScope::TraverseScopes(cx);
|
||||
#endif
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifndef XPCONNECT_STANDALONE
|
||||
void
|
||||
nsXPConnect::RecordTraversal(void *p, nsISupports *s)
|
||||
{
|
||||
mObjRefcounts->mScopes.Put(p, s);
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult
|
||||
nsXPConnect::FinishCycleCollection()
|
||||
{
|
||||
|
@ -524,23 +586,90 @@ nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb)
|
|||
if (!mObjRefcounts->Get(p, refcount))
|
||||
return NS_OK;
|
||||
|
||||
cb.DescribeNode(refcount, sizeof(JSObject), "JS Object");
|
||||
JSObject *obj = NS_STATIC_CAST(JSObject*, p);
|
||||
JSClass* clazz = OBJ_GET_CLASS(cx, obj);
|
||||
|
||||
#ifdef DEBUG
|
||||
char name[72];
|
||||
if(XPCNativeWrapper::IsNativeWrapperClass(clazz))
|
||||
{
|
||||
XPCWrappedNative* wn = XPCNativeWrapper::GetWrappedNative(cx, obj);
|
||||
XPCNativeScriptableInfo* si = wn ? wn->GetScriptableInfo() : nsnull;
|
||||
if(si)
|
||||
snprintf(name, sizeof(name), "XPCNativeWrapper (%s)",
|
||||
si->GetJSClass()->name);
|
||||
else
|
||||
snprintf(name, sizeof(name), "XPCNativeWrapper");
|
||||
}
|
||||
else if(obj)
|
||||
{
|
||||
XPCNativeScriptableInfo* si = nsnull;
|
||||
if(IS_PROTO_CLASS(clazz))
|
||||
{
|
||||
XPCWrappedNativeProto* p =
|
||||
(XPCWrappedNativeProto*) JS_GetPrivate(cx, obj);
|
||||
si = p->GetScriptableInfo();
|
||||
}
|
||||
if(si)
|
||||
snprintf(name, sizeof(name), "JS Object (%s - %s)", clazz->name,
|
||||
si->GetJSClass()->name);
|
||||
else
|
||||
snprintf(name, sizeof(name), "JS Object (%s)", clazz->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(name, sizeof(name), "JS Object");
|
||||
}
|
||||
cb.DescribeNode(refcount, sizeof(JSObject), name);
|
||||
#else
|
||||
cb.DescribeNode(refcount, sizeof(JSObject), "JS Object");
|
||||
#endif
|
||||
if (!p)
|
||||
return NS_OK;
|
||||
|
||||
JSObject *obj = NS_STATIC_CAST(JSObject*, p);
|
||||
|
||||
if (OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_HAS_PRIVATE
|
||||
&& OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS)
|
||||
if(XPCNativeWrapper::IsNativeWrapperClass(clazz))
|
||||
{
|
||||
// XPCNativeWrapper keeps its wrapped native alive (see XPC_NW_Mark).
|
||||
XPCWrappedNative* wn = XPCNativeWrapper::GetWrappedNative(cx, obj);
|
||||
if(wn)
|
||||
cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
|
||||
wn->GetFlatJSObject());
|
||||
}
|
||||
else if(clazz == &XPC_WN_Tearoff_JSClass)
|
||||
{
|
||||
// A tearoff holds a strong reference to its native object
|
||||
// (see XPCWrappedNative::FlatJSObjectFinalized). Its XPCWrappedNative
|
||||
// will be held alive through the parent of the JSObject of the tearoff.
|
||||
XPCWrappedNativeTearOff *to =
|
||||
(XPCWrappedNativeTearOff*) JS_GetPrivate(cx, obj);
|
||||
cb.NoteXPCOMChild(to->GetNative());
|
||||
}
|
||||
else if(IS_PROTO_CLASS(clazz))
|
||||
{
|
||||
// See XPC_WN_Shared_Proto_Mark.
|
||||
XPCWrappedNativeProto* p =
|
||||
(XPCWrappedNativeProto*) JS_GetPrivate(cx, obj);
|
||||
if(p)
|
||||
// Mark scope.
|
||||
p->GetScope()->Traverse(cb);
|
||||
}
|
||||
else if(clazz->flags & JSCLASS_HAS_PRIVATE &&
|
||||
clazz->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS)
|
||||
{
|
||||
void *v = JS_GetPrivate(cx, obj);
|
||||
if (v)
|
||||
cb.NoteXPCOMChild(NS_STATIC_CAST(nsISupports*, v));
|
||||
}
|
||||
|
||||
for (uint32 i = JSSLOT_START(OBJ_GET_CLASS(cx, obj));
|
||||
i < STOBJ_NSLOTS(obj); ++i)
|
||||
JSObject *parent = OBJ_GET_PARENT(cx, obj);
|
||||
if(parent)
|
||||
cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, parent);
|
||||
|
||||
JSObject *proto = OBJ_GET_PROTO(cx, obj);
|
||||
if(proto)
|
||||
cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, proto);
|
||||
|
||||
for(uint32 i = JSSLOT_START(clazz); i < STOBJ_NSLOTS(obj); ++i)
|
||||
{
|
||||
jsval val = STOBJ_GET_SLOT(obj, i);
|
||||
if (!JSVAL_IS_NULL(val)
|
||||
|
@ -552,6 +681,16 @@ nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb)
|
|||
}
|
||||
}
|
||||
|
||||
#ifndef XPCONNECT_STANDALONE
|
||||
if(clazz->flags & JSCLASS_IS_GLOBAL)
|
||||
{
|
||||
nsISupports *principal = nsnull;
|
||||
mObjRefcounts->mScopes.Get(obj, &principal);
|
||||
if(principal)
|
||||
cb.NoteXPCOMChild(principal);
|
||||
}
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -481,6 +481,10 @@ public:
|
|||
nsresult Unroot(const nsDeque &nodes);
|
||||
nsresult Traverse(void *p, nsCycleCollectionTraversalCallback &cb);
|
||||
nsresult FinishCycleCollection();
|
||||
JSObjectRefcounts* GetJSObjectRefcounts() {return mObjRefcounts;}
|
||||
#ifndef XPCONNECT_STANDALONE
|
||||
void RecordTraversal(void *p, nsISupports *s);
|
||||
#endif
|
||||
|
||||
#ifdef XPC_IDISPATCH_SUPPORT
|
||||
public:
|
||||
|
@ -1119,6 +1123,15 @@ public:
|
|||
|
||||
static void InitStatics() { gScopes = nsnull; gDyingScopes = nsnull; }
|
||||
|
||||
void Traverse(nsCycleCollectionTraversalCallback &cb);
|
||||
|
||||
#ifndef XPCONNECT_STANDALONE
|
||||
/**
|
||||
* Fills the hash mapping global object to principal.
|
||||
*/
|
||||
static void TraverseScopes(XPCCallContext& ccx);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
XPCWrappedNativeScope(XPCCallContext& ccx, JSObject* aGlobal);
|
||||
virtual ~XPCWrappedNativeScope();
|
||||
|
@ -3551,8 +3564,10 @@ extern char * xpc_CheckAccessList(const PRUnichar* wideName, const char* list[])
|
|||
class XPCVariant : public nsIVariant
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_NSIVARIANT
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(XPCVariant)
|
||||
|
||||
// If this class ever implements nsIWritableVariant, take special care with
|
||||
// the case when mJSVal is JSVAL_STRING, since we don't own the data in
|
||||
// that case.
|
||||
|
@ -3593,6 +3608,10 @@ protected:
|
|||
|
||||
// For faster GC-thing locking and unlocking
|
||||
JSRuntime* mJSRuntime;
|
||||
|
||||
#ifdef GC_MARK_DEBUG
|
||||
char *mGCRootName;
|
||||
#endif
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(XPCVariant, XPCVARIANT_IID)
|
||||
|
|
|
@ -42,7 +42,19 @@
|
|||
|
||||
#include "xpcprivate.h"
|
||||
|
||||
NS_IMPL_ISUPPORTS2_CI(XPCVariant, XPCVariant, nsIVariant)
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(XPCVariant)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(XPCVariant)
|
||||
NS_INTERFACE_MAP_ENTRY(XPCVariant)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIVariant)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_IMPL_QUERY_CLASSINFO(XPCVariant)
|
||||
NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(XPCVariant)
|
||||
NS_INTERFACE_MAP_END
|
||||
NS_IMPL_CI_INTERFACE_GETTER2(XPCVariant, XPCVariant, nsIVariant)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(XPCVariant)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(XPCVariant)
|
||||
|
||||
XPCVariant::XPCVariant(JSRuntime* aJSRuntime)
|
||||
: mJSVal(JSVAL_VOID),
|
||||
|
@ -61,10 +73,30 @@ XPCVariant::~XPCVariant()
|
|||
if(JSVAL_IS_GCTHING(mJSVal))
|
||||
{
|
||||
NS_ASSERTION(mJSRuntime, "Must have a runtime!");
|
||||
#ifdef GC_MARK_DEBUG
|
||||
JS_RemoveRootRT(mJSRuntime, &mJSVal);
|
||||
JS_smprintf_free(mGCRootName);
|
||||
mGCRootName = nsnull;
|
||||
#else
|
||||
JS_UnlockGCThingRT(mJSRuntime, JSVAL_TO_GCTHING(mJSVal));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XPCVariant)
|
||||
if(JSVAL_IS_OBJECT(tmp->mJSVal))
|
||||
cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
|
||||
JSVAL_TO_OBJECT(tmp->mJSVal));
|
||||
|
||||
nsVariant::Traverse(tmp->mData, cb);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
// NB: We might unlink our outgoing references in the future; for now we do
|
||||
// nothing. This is a harmless conservative behavior; it just means that we rely
|
||||
// on the cycle being broken by some of the external XPCOM objects' unlink()
|
||||
// methods, not our own. Typically *any* unlinking will break the cycle.
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_0(XPCVariant)
|
||||
|
||||
// static
|
||||
XPCVariant* XPCVariant::newVariant(XPCCallContext& ccx, jsval aJSVal)
|
||||
{
|
||||
|
@ -78,9 +110,16 @@ XPCVariant* XPCVariant::newVariant(XPCCallContext& ccx, jsval aJSVal)
|
|||
|
||||
if(JSVAL_IS_GCTHING(variant->mJSVal))
|
||||
{
|
||||
PRBool ok;
|
||||
#ifdef GC_MARK_DEBUG
|
||||
variant->mGCRootName = JS_smprintf("XPCVariant::mJSVal[0x%p]", variant);
|
||||
ok = JS_AddNamedRoot(ccx, &variant->mJSVal, variant->mGCRootName);
|
||||
#else
|
||||
// use JS_LockGCThingRT, because we get better performance in a lot of
|
||||
// cases than with adding a named GC root.
|
||||
if(!JS_LockGCThing(ccx, JSVAL_TO_GCTHING(variant->mJSVal)))
|
||||
ok = JS_LockGCThing(ccx, JSVAL_TO_GCTHING(variant->mJSVal));
|
||||
#endif
|
||||
if(!ok)
|
||||
{
|
||||
NS_RELEASE(variant); // Also sets variant to nsnull.
|
||||
}
|
||||
|
|
|
@ -63,24 +63,57 @@ NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Traverse
|
|||
// knows for how long.
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIXPConnectWrappedJS> owner = do_QueryInterface(s, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
nsIXPConnectWrappedJS *base;
|
||||
nsXPCWrappedJS *tmp;
|
||||
{
|
||||
// Put the nsCOMPtr in a local scope, to avoid messing up the refcount
|
||||
// below.
|
||||
nsCOMPtr<nsIXPConnectWrappedJS> owner = do_QueryInterface(s, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsIXPConnectWrappedJS *base = owner.get();
|
||||
nsXPCWrappedJS *tmp = NS_STATIC_CAST(nsXPCWrappedJS*, base);
|
||||
base = owner.get();
|
||||
tmp = NS_STATIC_CAST(nsXPCWrappedJS*, base);
|
||||
NS_ASSERTION(tmp->mRefCnt.get() > 2,
|
||||
"How can this be, no one else holds a strong ref?");
|
||||
}
|
||||
|
||||
// REVIEW ME PLEASE:
|
||||
//
|
||||
// I am not sure when this represents the true refcount.
|
||||
NS_ASSERTION(tmp->IsValid(), "How did we get here?");
|
||||
|
||||
cb.DescribeNode(tmp->mRefCnt.get(), sizeof(nsXPCWrappedJS),
|
||||
"nsXPCWrappedJS");
|
||||
nsrefcnt refcnt = tmp->mRefCnt.get();
|
||||
#ifdef DEBUG
|
||||
char name[72];
|
||||
snprintf(name, sizeof(name), "nsXPCWrappedJS (%s)",
|
||||
tmp->GetClass()->GetInterfaceName());
|
||||
cb.DescribeNode(refcnt, sizeof(nsXPCWrappedJS), name);
|
||||
#else
|
||||
cb.DescribeNode(refcnt, sizeof(nsXPCWrappedJS), "nsXPCWrappedJS");
|
||||
#endif
|
||||
|
||||
if (tmp->IsValid())
|
||||
// nsXPCWrappedJS keeps its own refcount artificially at or above 1, see the
|
||||
// comment above nsXPCWrappedJS::AddRef.
|
||||
cb.NoteXPCOMChild(base);
|
||||
|
||||
if(refcnt > 1)
|
||||
// nsXPCWrappedJS roots its mJSObj when its refcount is > 1, see
|
||||
// the comment above nsXPCWrappedJS::AddRef.
|
||||
cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
|
||||
tmp->GetJSObject());
|
||||
|
||||
nsXPCWrappedJS* root = tmp->GetRootWrapper();
|
||||
if(root == tmp)
|
||||
{
|
||||
// The root wrapper keeps the aggregated native object alive.
|
||||
nsISupports* outer = tmp->GetAggregatedNativeObject();
|
||||
if (outer)
|
||||
cb.NoteXPCOMChild(outer);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-root wrappers keep their root alive.
|
||||
cb.NoteXPCOMChild(NS_STATIC_CAST(nsIXPConnectWrappedJS*, root));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -134,6 +167,14 @@ nsXPCWrappedJS::QueryInterface(REFNSIID aIID, void** aInstancePtr)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
if(aIID.Equals(NS_GET_IID(nsCycleCollectionISupports)))
|
||||
{
|
||||
NS_ADDREF(this);
|
||||
*aInstancePtr =
|
||||
NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Always check for this first so that our 'outer' can get this interface
|
||||
// from us without recurring into a call to the outer's QI!
|
||||
if(aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS)))
|
||||
|
|
|
@ -54,7 +54,22 @@ NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Traverse(nsISupports *s,
|
|||
nsCycleCollectionTraversalCallback &cb)
|
||||
{
|
||||
XPCWrappedNative *tmp = NS_STATIC_CAST(XPCWrappedNative*, s);
|
||||
if(!tmp->IsValid())
|
||||
return NS_OK;
|
||||
|
||||
#ifdef DEBUG
|
||||
char name[72];
|
||||
XPCNativeScriptableInfo* si = tmp->GetScriptableInfo();
|
||||
if(si)
|
||||
snprintf(name, sizeof(name), "XPCWrappedNative (%s)",
|
||||
si->GetJSClass()->name);
|
||||
else
|
||||
snprintf(name, sizeof(name), "XPCWrappedNative");
|
||||
|
||||
cb.DescribeNode(tmp->mRefCnt.get(), sizeof(XPCWrappedNative), name);
|
||||
#else
|
||||
cb.DescribeNode(tmp->mRefCnt.get(), sizeof(XPCWrappedNative), "XPCWrappedNative");
|
||||
#endif
|
||||
|
||||
if (tmp->mRefCnt.get() > 1) {
|
||||
|
||||
|
@ -75,6 +90,27 @@ NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Traverse(nsISupports *s,
|
|||
}
|
||||
}
|
||||
|
||||
// XXX If there is a scriptable helper we will not be able to find out what
|
||||
// it marked.
|
||||
|
||||
|
||||
// xpc_MarkForValidWrapper calls MarkBeforeJSFinalize and
|
||||
// MarkScopeJSObjects.
|
||||
|
||||
// XPCWrappedNative marks its proto (see MarkBeforeJSFinalize).
|
||||
if(tmp->HasProto())
|
||||
cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
|
||||
tmp->GetProto()->GetJSProtoObject());
|
||||
|
||||
// XPCWrappedNative marks its mNativeWrapper (see MarkBeforeJSFinalize).
|
||||
if(tmp->mNativeWrapper)
|
||||
cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
|
||||
tmp->mNativeWrapper);
|
||||
|
||||
// XPCWrappedNative marks its scope.
|
||||
tmp->GetScope()->Traverse(cb);
|
||||
|
||||
// XPCWrappedNative keeps its native object alive.
|
||||
if (tmp->GetIdentityObject()) {
|
||||
cb.NoteXPCOMChild(tmp->GetIdentityObject());
|
||||
}
|
||||
|
|
|
@ -698,12 +698,6 @@ xpc_MarkForValidWrapper(JSContext *cx, XPCWrappedNative* wrapper, void *arg)
|
|||
|
||||
wrapper->MarkBeforeJSFinalize(cx);
|
||||
|
||||
if(wrapper->HasProto())
|
||||
{
|
||||
JSObject* obj = wrapper->GetProto()->GetJSProtoObject();
|
||||
NS_ASSERTION(obj, "bad proto");
|
||||
JS_MarkGCThing(cx, obj, "XPCWrappedNativeProto::mJSProtoObject", arg);
|
||||
}
|
||||
MarkScopeJSObjects(cx, wrapper->GetScope(), arg);
|
||||
}
|
||||
|
||||
|
|
|
@ -793,3 +793,32 @@ XPCWrappedNativeScope::DebugDump(PRInt16 depth)
|
|||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
XPCWrappedNativeScope::Traverse(nsCycleCollectionTraversalCallback &cb)
|
||||
{
|
||||
// See MarkScopeJSObjects.
|
||||
cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, mGlobalJSObject);
|
||||
JSObject *obj = mPrototypeJSObject;
|
||||
if(obj)
|
||||
cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, obj);
|
||||
obj = mPrototypeJSFunction;
|
||||
if(obj)
|
||||
cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, obj);
|
||||
}
|
||||
|
||||
#ifndef XPCONNECT_STANDALONE
|
||||
// static
|
||||
void
|
||||
XPCWrappedNativeScope::TraverseScopes(XPCCallContext& ccx)
|
||||
{
|
||||
// Hold the lock throughout.
|
||||
XPCAutoLock lock(ccx.GetRuntime()->GetMapLock());
|
||||
|
||||
for(XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext)
|
||||
if(cur->mGlobalJSObject && cur->mScriptObjectPrincipal)
|
||||
{
|
||||
ccx.GetXPConnect()->RecordTraversal(cur->mGlobalJSObject,
|
||||
cur->mScriptObjectPrincipal);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -169,6 +169,15 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsCycleCollectionParticipant,
|
|||
return NS_OK; \
|
||||
}
|
||||
|
||||
#define NS_IMPL_CYCLE_COLLECTION_UNLINK_0(_class) \
|
||||
NS_IMETHODIMP \
|
||||
NS_CYCLE_COLLECTION_CLASSNAME(_class)::Unlink(nsISupports *s) \
|
||||
{ \
|
||||
NS_ASSERTION(CheckForRightISupports(s), \
|
||||
"not the nsISupports pointer we expect"); \
|
||||
return NS_OK; \
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Helpers for implementing nsCycleCollectionParticipant::Traverse
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "prdtoa.h"
|
||||
#include <math.h>
|
||||
#include "nsCRT.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
/***************************************************************************/
|
||||
// Helpers for static convert functions...
|
||||
|
@ -1657,6 +1658,36 @@ nsVariant::Cleanup(nsDiscriminatedUnion* data)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsVariant::Traverse(const nsDiscriminatedUnion& data,
|
||||
nsCycleCollectionTraversalCallback &cb)
|
||||
{
|
||||
switch(data.mType)
|
||||
{
|
||||
case nsIDataType::VTYPE_INTERFACE:
|
||||
case nsIDataType::VTYPE_INTERFACE_IS:
|
||||
if (data.u.iface.mInterfaceValue) {
|
||||
cb.NoteXPCOMChild(data.u.iface.mInterfaceValue);
|
||||
}
|
||||
break;
|
||||
case nsIDataType::VTYPE_ARRAY:
|
||||
switch(data.u.array.mArrayType) {
|
||||
case nsIDataType::VTYPE_INTERFACE:
|
||||
case nsIDataType::VTYPE_INTERFACE_IS:
|
||||
{
|
||||
nsISupports** p = (nsISupports**) data.u.array.mArrayValue;
|
||||
for(PRUint32 i = data.u.array.mArrayCount; i > 0; p++, i--)
|
||||
if(*p)
|
||||
cb.NoteXPCOMChild(*p);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/***************************************************************************/
|
||||
// members...
|
||||
|
|
|
@ -179,6 +179,9 @@ public:
|
|||
static nsresult SetToEmpty(nsDiscriminatedUnion* data);
|
||||
static nsresult SetToEmptyArray(nsDiscriminatedUnion* data);
|
||||
|
||||
static void Traverse(const nsDiscriminatedUnion& data,
|
||||
nsCycleCollectionTraversalCallback &cb);
|
||||
|
||||
private:
|
||||
~nsVariant();
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче