// static void XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC() { XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); // FIXME The lock may not be necessary since we are inside // JSGC_FINALIZE_END callback and at this point GC still serializes access // to JS runtime. See bug 380139. XPCAutoLock lock(rt->GetMapLock()); KillDyingScopes(); }
// static void XPCNativeSet::ClearCacheEntryForClassInfo(nsIClassInfo* classInfo) { XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); ClassInfo2NativeSetMap* map = rt->GetClassInfo2NativeSetMap(); if (map) { // scoped lock XPCAutoLock lock(rt->GetMapLock()); map->Remove(classInfo); } }
size_t XPCWrappedNativeScope::SizeOfAllScopesIncludingThis(nsMallocSizeOfFun mallocSizeOf) { XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance(); XPCAutoLock lock(rt->GetMapLock()); size_t n = 0; for (XPCWrappedNativeScope *cur = gScopes; cur; cur = cur->mNext) { n += cur->SizeOfIncludingThis(mallocSizeOf); } return n; }
void nsXPCWrappedJS::Unlink() { nsXPConnect::GetRuntimeInstance()->AssertInvalidWrappedJSNotInTable(this); if (IsValid()) { XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); if (rt) { if (IsRootWrapper()) rt->RemoveWrappedJS(this); if (mRefCnt > 1) RemoveFromRootSet(); } mJSObj = nullptr; } if (IsRootWrapper()) { ClearWeakReferences(); } else if (mRoot) { // unlink this wrapper nsXPCWrappedJS* cur = mRoot; while (1) { if (cur->mNext == this) { cur->mNext = mNext; break; } cur = cur->mNext; MOZ_ASSERT(cur, "failed to find wrapper in its own chain"); } // Note: unlinking this wrapper may have changed us from a multi- // compartment wrapper chain to a single-compartment wrapper chain. We // leave the wrapper in the multi-compartment table as it is likely to // need to be multi-compartment again in the future and, moreover, we // cannot get a JSContext here. // let the root go NS_RELEASE(mRoot); } mClass = nullptr; if (mOuter) { XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); if (rt->GCIsRunning()) { DeferredFinalize(mOuter.forget().take()); } else { mOuter = nullptr; } } }
nsXPCWrappedJS::~nsXPCWrappedJS() { NS_PRECONDITION(0 == mRefCnt, "refcounting error"); if (mRoot == this) { // Remove this root wrapper from the map XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); JSObject2WrappedJSMap* map = rt->GetWrappedJSMap(); if (map) map->Remove(this); } Unlink(); }
nsrefcnt nsXPCWrappedJS::AddRef(void) { nsrefcnt cnt = NS_AtomicIncrementRefcnt(mRefCnt); NS_LOG_ADDREF(this, cnt, "nsXPCWrappedJS", sizeof(*this)); if (2 == cnt && IsValid()) { XPCJSRuntime* rt = mClass->GetRuntime(); rt->AddWrappedJSRoot(this); } return cnt; }
void nsXPCWrappedJS::Destroy() { MOZ_ASSERT(1 == int32_t(mRefCnt), "should be stabilized for deletion"); if (IsRootWrapper()) { XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); JSObject2WrappedJSMap* map = rt->GetWrappedJSMap(); if (map) map->Remove(this); } Unlink(); }
// static nsresult nsXPCWrappedJS::GetNewOrUsed(JS::HandleObject jsObj, REFNSIID aIID, nsXPCWrappedJS** wrapperResult) { // Do a release-mode assert against accessing nsXPCWrappedJS off-main-thread. if (!MOZ_LIKELY(NS_IsMainThread())) MOZ_CRASH(); AutoJSContext cx; XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); JSObject2WrappedJSMap* map = rt->GetWrappedJSMap(); if (!map) { MOZ_ASSERT(map,"bad map"); return NS_ERROR_FAILURE; } bool allowNonScriptable = mozilla::jsipc::IsWrappedCPOW(jsObj); nsRefPtr<nsXPCWrappedJSClass> clasp = nsXPCWrappedJSClass::GetNewOrUsed(cx, aIID, allowNonScriptable); if (!clasp) return NS_ERROR_FAILURE; JS::RootedObject rootJSObj(cx, clasp->GetRootJSObject(cx, jsObj)); if (!rootJSObj) return NS_ERROR_FAILURE; nsRefPtr<nsXPCWrappedJS> root = map->Find(rootJSObj); if (root) { nsRefPtr<nsXPCWrappedJS> wrapper = root->FindOrFindInherited(aIID); if (wrapper) { wrapper.forget(wrapperResult); return NS_OK; } } else if (rootJSObj != jsObj) { // Make a new root wrapper, because there is no existing // root wrapper, and the wrapper we are trying to make isn't // a root. nsRefPtr<nsXPCWrappedJSClass> rootClasp = nsXPCWrappedJSClass::GetNewOrUsed(cx, NS_GET_IID(nsISupports)); if (!rootClasp) return NS_ERROR_FAILURE; root = new nsXPCWrappedJS(cx, rootJSObj, rootClasp, nullptr); } nsRefPtr<nsXPCWrappedJS> wrapper = new nsXPCWrappedJS(cx, jsObj, clasp, root); wrapper.forget(wrapperResult); return NS_OK; }
// static XPCNativeSet* XPCNativeSet::GetNewOrUsed(XPCCallContext& ccx, const nsIID* iid) { AutoMarkingNativeSetPtr set(ccx); AutoMarkingNativeInterfacePtr iface(ccx); iface = XPCNativeInterface::GetNewOrUsed(ccx, iid); if(!iface) return nsnull; XPCNativeSetKey key(nsnull, iface, 0); XPCJSRuntime* rt = ccx.GetRuntime(); NativeSetMap* map = rt->GetNativeSetMap(); if(!map) return nsnull; { // scoped lock XPCAutoLock lock(rt->GetMapLock()); set = map->Find(&key); } if(set) return set; // hacky way to get a XPCNativeInterface** using the AutoPtr XPCNativeInterface* temp[] = {iface}; set = NewInstance(ccx, temp, 1); if(!set) return nsnull; { // scoped lock XPCAutoLock lock(rt->GetMapLock()); XPCNativeSet* set2 = map->Add(&key, set); if(!set2) { NS_ERROR("failed to add our set!"); DestroyInstance(set); set = nsnull; } else if(set2 != set) { DestroyInstance(set); set = set2; } } return set; }
nsrefcnt nsXPCWrappedJS::Release(void) { if (!MOZ_LIKELY(NS_IsMainThread() || NS_IsCycleCollectorThread())) MOZ_CRASH(); NS_PRECONDITION(0 != mRefCnt, "dup release"); if (mMainThreadOnly && !NS_IsMainThread()) { // We'd like to abort here, but this can happen if someone uses a proxy // for the nsXPCWrappedJS. nsCOMPtr<nsIThread> mainThread = do_GetMainThread(); // If we can't get the main thread anymore we just leak, but this really // shouldn't happen. NS_ASSERTION(mainThread, "Can't get main thread, leaking nsXPCWrappedJS!"); if (mainThread) { NS_ProxyRelease(mainThread, static_cast<nsIXPConnectWrappedJS*>(this)); } return mRefCnt; } // need to take the map lock here to prevent GetNewOrUsed from trying // to reuse a wrapper on one thread while it's being destroyed on another XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); XPCAutoLock lock(rt->GetMapLock()); do_decrement: nsrefcnt cnt = --mRefCnt; NS_LOG_RELEASE(this, cnt, "nsXPCWrappedJS"); if (0 == cnt) { delete this; // also unlinks us from chain return 0; } if (1 == cnt) { if (IsValid()) RemoveFromRootSet(rt->GetMapLock()); // If we are not the root wrapper or if we are not being used from a // weak reference, then this extra ref is not needed and we can let // ourself be deleted. // Note: HasWeakReferences() could only return true for the root. if (!HasWeakReferences()) goto do_decrement; } return cnt; }
nsrefcnt nsXPCWrappedJS::AddRef(void) { if (!MOZ_LIKELY(NS_IsMainThread() || NS_IsCycleCollectorThread())) MOZ_CRASH(); nsrefcnt cnt = ++mRefCnt; NS_LOG_ADDREF(this, cnt, "nsXPCWrappedJS", sizeof(*this)); if (2 == cnt && IsValid()) { XPCJSRuntime* rt = mClass->GetRuntime(); rt->AddWrappedJSRoot(this); } return cnt; }
void nsXPCWrappedJS::Unlink() { if (IsValid()) { XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); if (rt) { if (mRoot == this) { // remove this root wrapper from the map JSObject2WrappedJSMap* map = rt->GetWrappedJSMap(); if (map) map->Remove(this); } if (mRefCnt > 1) RemoveFromRootSet(); } mJSObj = nullptr; } if (mRoot == this) { ClearWeakReferences(); } else if (mRoot) { // unlink this wrapper nsXPCWrappedJS* cur = mRoot; while (1) { if (cur->mNext == this) { cur->mNext = mNext; break; } cur = cur->mNext; MOZ_ASSERT(cur, "failed to find wrapper in its own chain"); } // let the root go NS_RELEASE(mRoot); } NS_IF_RELEASE(mClass); if (mOuter) { XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); if (rt->GCIsRunning()) { nsContentUtils::DeferredFinalize(mOuter); mOuter = nullptr; } else { NS_RELEASE(mOuter); } } }
// static XPCNativeSet* XPCNativeSet::GetNewOrUsed(XPCCallContext& ccx, XPCNativeSet* otherSet, XPCNativeInterface* newInterface, PRUint16 position) { AutoMarkingNativeSetPtr set(ccx); XPCJSRuntime* rt = ccx.GetRuntime(); NativeSetMap* map = rt->GetNativeSetMap(); if(!map) return nsnull; XPCNativeSetKey key(otherSet, newInterface, position); { // scoped lock XPCAutoLock lock(rt->GetMapLock()); set = map->Find(&key); } if(set) return set; if(otherSet) set = NewInstanceMutate(otherSet, newInterface, position); else set = NewInstance(ccx, &newInterface, 1); if(!set) return nsnull; { // scoped lock XPCAutoLock lock(rt->GetMapLock()); XPCNativeSet* set2 = map->Add(&key, set); if(!set2) { NS_ERROR("failed to add our set!"); DestroyInstance(set); set = nsnull; } else if(set2 != set) { DestroyInstance(set); set = set2; } } return set; }
inline JSFunction * xpcFunctionDefiner::Define(JSObject * globalObject, uintN aNameIndex, JSNative aCall) { return JS_DefineFunction(m_JSContext, globalObject, m_Runtime->GetStringName(aNameIndex), aCall, 1, JSPROP_PERMANENT | JSPROP_READONLY); }
// static XPCNativeInterface* XPCNativeInterface::GetNewOrUsed(XPCCallContext& ccx, const nsIID* iid) { AutoMarkingNativeInterfacePtr iface(ccx); XPCJSRuntime* rt = ccx.GetRuntime(); IID2NativeInterfaceMap* map = rt->GetIID2NativeInterfaceMap(); if(!map) return nsnull; { // scoped lock XPCAutoLock lock(rt->GetMapLock()); iface = map->Find(*iid); } if(iface) return iface; nsCOMPtr<nsIInterfaceInfo> info; ccx.GetXPConnect()->GetInfoForIID(iid, getter_AddRefs(info)); if(!info) return nsnull; iface = NewInstance(ccx, info); if(!iface) return nsnull; { // scoped lock XPCAutoLock lock(rt->GetMapLock()); XPCNativeInterface* iface2 = map->Add(iface); if(!iface2) { NS_ERROR("failed to add our interface!"); DestroyInstance(ccx, rt, iface); iface = nsnull; } else if(iface2 != iface) { DestroyInstance(ccx, rt, iface); iface = iface2; } } return iface; }
// static XPCNativeInterface* XPCNativeInterface::GetNewOrUsed(XPCCallContext& ccx, nsIInterfaceInfo* info) { AutoMarkingNativeInterfacePtr iface(ccx); const nsIID* iid; if(NS_FAILED(info->GetIIDShared(&iid)) || !iid) return nsnull; XPCJSRuntime* rt = ccx.GetRuntime(); IID2NativeInterfaceMap* map = rt->GetIID2NativeInterfaceMap(); if(!map) return nsnull; { // scoped lock XPCAutoLock lock(rt->GetMapLock()); iface = map->Find(*iid); } if(iface) return iface; iface = NewInstance(ccx, info); if(!iface) return nsnull; { // scoped lock XPCAutoLock lock(rt->GetMapLock()); XPCNativeInterface* iface2 = map->Add(iface); if(!iface2) { NS_ERROR("failed to add our interface!"); DestroyInstance(ccx, rt, iface); iface = nsnull; } else if(iface2 != iface) { DestroyInstance(ccx, rt, iface); iface = iface2; } } return iface; }
/* PRBool construct (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in PRUint32 argc, in JSValPtr argv, in JSValPtr vp); */ NS_IMETHODIMP nsJSCID::Construct(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj, PRUint32 argc, jsval * argv, jsval * vp, PRBool *_retval) { XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); if(!rt) return NS_ERROR_FAILURE; // 'push' a call context and call on it XPCCallContext ccx(JS_CALLER, cx, obj, nsnull, rt->GetStringID(XPCJSRuntime::IDX_CREATE_INSTANCE), argc, argv, vp); *_retval = XPCWrappedNative::CallMethod(ccx); return NS_OK; }
// GCCallback calls are chained static JSBool ContextCallback(JSContext *cx, uintN operation) { XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance(); if(self) { if(operation == JSCONTEXT_NEW) { if(!self->OnJSContextNew(cx)) return JS_FALSE; } else if(operation == JSCONTEXT_DESTROY) { delete XPCContext::GetXPCContext(cx); } } return JS_TRUE; }
NS_IMETHODIMP nsJSCID::Construct(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* objArg, const CallArgs& args, bool* _retval) { RootedObject obj(cx, objArg); XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); if (!rt) return NS_ERROR_FAILURE; // 'push' a call context and call on it RootedId name(cx, rt->GetStringID(XPCJSRuntime::IDX_CREATE_INSTANCE)); XPCCallContext ccx(JS_CALLER, cx, obj, nullptr, name, args.length(), args.array(), args.rval().address()); *_retval = XPCWrappedNative::CallMethod(ccx); return NS_OK; }
// static XPCNativeInterface* XPCNativeInterface::GetNewOrUsed(const nsIID* iid) { AutoJSContext cx; AutoMarkingNativeInterfacePtr iface(cx); XPCJSRuntime* rt = XPCJSRuntime::Get(); IID2NativeInterfaceMap* map = rt->GetIID2NativeInterfaceMap(); if (!map) return nullptr; { // scoped lock XPCAutoLock lock(rt->GetMapLock()); iface = map->Find(*iid); } if (iface) return iface; nsCOMPtr<nsIInterfaceInfo> info; XPTInterfaceInfoManager::GetSingleton()->GetInfoForIID(iid, getter_AddRefs(info)); if (!info) return nullptr; iface = NewInstance(info); if (!iface) return nullptr; { // scoped lock XPCAutoLock lock(rt->GetMapLock()); XPCNativeInterface* iface2 = map->Add(iface); if (!iface2) { NS_ERROR("failed to add our interface!"); DestroyInstance(iface); iface = nullptr; } else if (iface2 != iface) { DestroyInstance(iface); iface = iface2; } } return iface; }
// static void XPCJSRuntime::TraceJS(JSTracer* trc, void* data) { XPCJSRuntime* self = (XPCJSRuntime*)data; // Skip this part if XPConnect is shutting down. We get into // bad locking problems with the thread iteration otherwise. if(!self->GetXPConnect()->IsShuttingDown()) { PRLock* threadLock = XPCPerThreadData::GetLock(); if(threadLock) { // scoped lock nsAutoLock lock(threadLock); XPCPerThreadData* iterp = nsnull; XPCPerThreadData* thread; while(nsnull != (thread = XPCPerThreadData::IterateThreads(&iterp))) { // Trace those AutoMarkingPtr lists! thread->TraceJS(trc); } } } // XPCJSObjectHolders don't participate in cycle collection, so always trace // them here. for(XPCRootSetElem *e = self->mObjectHolderRoots; e ; e = e->GetNextRoot()) static_cast<XPCJSObjectHolder*>(e)->TraceJS(trc); // Mark these roots as gray so the CC can walk them later. js::GCMarker *gcmarker = NULL; if (IS_GC_MARKING_TRACER(trc)) { gcmarker = static_cast<js::GCMarker *>(trc); JS_ASSERT(gcmarker->getMarkColor() == XPC_GC_COLOR_BLACK); gcmarker->setMarkColor(XPC_GC_COLOR_GRAY); } self->TraceXPConnectRoots(trc); if (gcmarker) gcmarker->setMarkColor(XPC_GC_COLOR_BLACK); }
// static XPCNativeSet* XPCNativeSet::GetNewOrUsed(const nsIID* iid) { AutoJSContext cx; AutoMarkingNativeSetPtr set(cx); AutoMarkingNativeInterfacePtr iface(cx); iface = XPCNativeInterface::GetNewOrUsed(iid); if (!iface) return nullptr; XPCNativeSetKey key(nullptr, iface, 0); XPCJSRuntime* rt = XPCJSRuntime::Get(); NativeSetMap* map = rt->GetNativeSetMap(); if (!map) return nullptr; set = map->Find(&key); if (set) return set; // hacky way to get a XPCNativeInterface** using the AutoPtr XPCNativeInterface* temp[] = {iface}; set = NewInstance(temp, 1); if (!set) return nullptr; XPCNativeSet* set2 = map->Add(&key, set); if (!set2) { NS_ERROR("failed to add our set!"); DestroyInstance(set); set = nullptr; } else if (set2 != set) { DestroyInstance(set); set = set2; } return set; }
// static XPCNativeSet* XPCNativeSet::GetNewOrUsed(XPCNativeSet* otherSet, XPCNativeInterface* newInterface, uint16_t position) { AutoJSContext cx; AutoMarkingNativeSetPtr set(cx); XPCJSRuntime* rt = XPCJSRuntime::Get(); NativeSetMap* map = rt->GetNativeSetMap(); if (!map) return nullptr; XPCNativeSetKey key(otherSet, newInterface, position); set = map->Find(&key); if (set) return set; if (otherSet) set = NewInstanceMutate(otherSet, newInterface, position); else set = NewInstance(&newInterface, 1); if (!set) return nullptr; XPCNativeSet* set2 = map->Add(&key, set); if (!set2) { NS_ERROR("failed to add our set!"); DestroyInstance(set); set = nullptr; } else if (set2 != set) { DestroyInstance(set); set = set2; } return set; }
// static void XPCJSRuntime::TraceJS(JSTracer* trc, void* data) { XPCJSRuntime* self = (XPCJSRuntime*)data; // Skip this part if XPConnect is shutting down. We get into // bad locking problems with the thread iteration otherwise. if(!self->GetXPConnect()->IsShuttingDown()) { PRLock* threadLock = XPCPerThreadData::GetLock(); if(threadLock) { // scoped lock nsAutoLock lock(threadLock); XPCPerThreadData* iterp = nsnull; XPCPerThreadData* thread; while(nsnull != (thread = XPCPerThreadData::IterateThreads(&iterp))) { // Trace those AutoMarkingPtr lists! thread->TraceJS(trc); } } } // XPCJSObjectHolders don't participate in cycle collection, so always trace // them here. for(XPCRootSetElem *e = self->mObjectHolderRoots; e ; e = e->GetNextRoot()) static_cast<XPCJSObjectHolder*>(e)->TraceJS(trc); if(self->GetXPConnect()->ShouldTraceRoots()) { // Only trace these if we're not cycle-collecting, the cycle collector // will do that if we are. self->TraceXPConnectRoots(trc); } }
JSBool WrapperMoved(JSContext *cx, XPCWrappedNative *innerObj, XPCWrappedNativeScope *newScope) { typedef WrappedNative2WrapperMap::Link Link; XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance(); WrappedNative2WrapperMap *map = innerObj->GetScope()->GetWrapperMap(); Link *link; { // Scoped lock XPCAutoLock al(rt->GetMapLock()); link = map->FindLink(innerObj->GetFlatJSObject()); } if (!link) { // No link here means that there were no XOWs for this object. return JS_TRUE; } JSObject *xow = link->obj; { // Scoped lock. XPCAutoLock al(rt->GetMapLock()); if (!newScope->GetWrapperMap()->AddLink(innerObj->GetFlatJSObject(), link)) return JS_FALSE; map->Remove(innerObj->GetFlatJSObject()); } if (!xow) { // Nothing else to do. return JS_TRUE; } return JS_SetReservedSlot(cx, xow, XPC_XOW_ScopeSlot, PRIVATE_TO_JSVAL(newScope)) && JS_SetParent(cx, xow, newScope->GetGlobalJSObject()); }
// static XPCNativeInterface* XPCNativeInterface::GetNewOrUsed(nsIInterfaceInfo* info) { AutoJSContext cx; AutoMarkingNativeInterfacePtr iface(cx); const nsIID* iid; if (NS_FAILED(info->GetIIDShared(&iid)) || !iid) return nullptr; XPCJSRuntime* rt = XPCJSRuntime::Get(); IID2NativeInterfaceMap* map = rt->GetIID2NativeInterfaceMap(); if (!map) return nullptr; iface = map->Find(*iid); if (iface) return iface; iface = NewInstance(info); if (!iface) return nullptr; XPCNativeInterface* iface2 = map->Add(iface); if (!iface2) { NS_ERROR("failed to add our interface!"); DestroyInstance(iface); iface = nullptr; } else if (iface2 != iface) { DestroyInstance(iface); iface = iface2; } return iface; }
void nsXPCWrappedJS::Unlink() { if (IsValid()) { XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); if (rt) { if (mRoot == this) { // remove this root wrapper from the map JSObject2WrappedJSMap* map = rt->GetWrappedJSMap(); if (map) { XPCAutoLock lock(rt->GetMapLock()); map->Remove(this); } } if (mRefCnt > 1) RemoveFromRootSet(rt->GetMapLock()); } mJSObj = nsnull; } if (mRoot == this) { ClearWeakReferences(); } else if (mRoot) { // 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); } NS_IF_RELEASE(mClass); if (mOuter) { XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); if (rt->GetThreadRunningGC()) { rt->DeferredRelease(mOuter); mOuter = nsnull; } else { NS_RELEASE(mOuter); } } }
// static JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status) { nsVoidArray* dyingWrappedJSArray; XPCJSRuntime* self = nsXPConnect::GetRuntime(); if(self) { switch(status) { case JSGC_BEGIN: { if(!NS_IsMainThread()) { return JS_FALSE; } break; } case JSGC_MARK_END: { NS_ASSERTION(!self->mDoingFinalization, "bad state"); // mThreadRunningGC indicates that GC is running { // scoped lock XPCAutoLock lock(self->GetMapLock()); NS_ASSERTION(!self->mThreadRunningGC, "bad state"); self->mThreadRunningGC = PR_GetCurrentThread(); } dyingWrappedJSArray = &self->mWrappedJSToReleaseArray; { JSDyingJSObjectData data = {cx, dyingWrappedJSArray}; // 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); } // Do cleanup in NativeInterfaces. This part just finds // member cloned function objects that are about to be // collected. It does not deal with collection of interfaces or // sets at this point. CX_AND_XPCRT_Data data = {cx, self}; self->mIID2NativeInterfaceMap-> Enumerate(NativeInterfaceGC, &data); // Find dying scopes... XPCWrappedNativeScope::FinishedMarkPhaseOfGC(cx, self); self->mDoingFinalization = JS_TRUE; break; } case JSGC_FINALIZE_END: { NS_ASSERTION(self->mDoingFinalization, "bad state"); self->mDoingFinalization = JS_FALSE; // Release all the members whose JSObjects are now known // to be dead. dyingWrappedJSArray = &self->mWrappedJSToReleaseArray; while(1) { nsXPCWrappedJS* wrapper; PRInt32 count = dyingWrappedJSArray->Count(); if(!count) { dyingWrappedJSArray->Compact(); break; } wrapper = static_cast<nsXPCWrappedJS*> (dyingWrappedJSArray->ElementAt(count-1)); dyingWrappedJSArray->RemoveElementAt(count-1); NS_RELEASE(wrapper); } #ifdef XPC_REPORT_NATIVE_INTERFACE_AND_SET_FLUSHING printf("--------------------------------------------------------------\n"); int setsBefore = (int) self->mNativeSetMap->Count(); int ifacesBefore = (int) self->mIID2NativeInterfaceMap->Count(); #endif // We use this occasion to mark and sweep NativeInterfaces, // NativeSets, and the WrappedNativeJSClasses... // Do the marking... XPCWrappedNativeScope::MarkAllWrappedNativesAndProtos(); self->mDetachedWrappedNativeProtoMap-> Enumerate(DetachedWrappedNativeProtoMarker, nsnull); // Mark the sets used in the call contexts. There is a small // chance that a wrapper's set will change *while* a call is // happening which uses that wrapper's old interfface set. So, // we need to do this marking to avoid collecting those sets // that might no longer be otherwise reachable from the wrappers // or the wrapperprotos. // Skip this part if XPConnect is shutting down. We get into // bad locking problems with the thread iteration otherwise. if(!self->GetXPConnect()->IsShuttingDown()) { PRLock* threadLock = XPCPerThreadData::GetLock(); if(threadLock) { // scoped lock nsAutoLock lock(threadLock); XPCPerThreadData* iterp = nsnull; XPCPerThreadData* thread; while(nsnull != (thread = XPCPerThreadData::IterateThreads(&iterp))) { // Mark those AutoMarkingPtr lists! thread->MarkAutoRootsAfterJSFinalize(); XPCCallContext* ccxp = thread->GetCallContext(); while(ccxp) { // Deal with the strictness of callcontext that // complains if you ask for a set when // it is in a state where the set could not // possibly be valid. if(ccxp->CanGetSet()) { XPCNativeSet* set = ccxp->GetSet(); if(set) set->Mark(); } if(ccxp->CanGetInterface()) { XPCNativeInterface* iface = ccxp->GetInterface(); if(iface) iface->Mark(); } ccxp = ccxp->GetPrevCallContext(); } } } } // Do the sweeping... // We don't want to sweep the JSClasses at shutdown time. // At this point there may be JSObjects using them that have // been removed from the other maps. if(!self->GetXPConnect()->IsShuttingDown()) { self->mNativeScriptableSharedMap-> Enumerate(JSClassSweeper, nsnull); } self->mClassInfo2NativeSetMap-> Enumerate(NativeUnMarkedSetRemover, nsnull); self->mNativeSetMap-> Enumerate(NativeSetSweeper, nsnull); CX_AND_XPCRT_Data data = {cx, self}; self->mIID2NativeInterfaceMap-> Enumerate(NativeInterfaceSweeper, &data); #ifdef DEBUG XPCWrappedNativeScope::ASSERT_NoInterfaceSetsAreMarked(); #endif #ifdef XPC_REPORT_NATIVE_INTERFACE_AND_SET_FLUSHING int setsAfter = (int) self->mNativeSetMap->Count(); int ifacesAfter = (int) self->mIID2NativeInterfaceMap->Count(); printf("\n"); printf("XPCNativeSets: before: %d collected: %d remaining: %d\n", setsBefore, setsBefore - setsAfter, setsAfter); printf("XPCNativeInterfaces: before: %d collected: %d remaining: %d\n", ifacesBefore, ifacesBefore - ifacesAfter, ifacesAfter); printf("--------------------------------------------------------------\n"); #endif // Sweep scopes needing cleanup XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC(cx); // Now we are going to recycle any unused WrappedNativeTearoffs. // We do this by iterating all the live callcontexts (on all // threads!) and marking the tearoffs in use. And then we // iterate over all the WrappedNative wrappers and sweep their // tearoffs. // // This allows us to perhaps minimize the growth of the // tearoffs. And also makes us not hold references to interfaces // on our wrapped natives that we are not actually using. // // XXX We may decide to not do this on *every* gc cycle. // Skip this part if XPConnect is shutting down. We get into // bad locking problems with the thread iteration otherwise. if(!self->GetXPConnect()->IsShuttingDown()) { PRLock* threadLock = XPCPerThreadData::GetLock(); if(threadLock) { // Do the marking... { // scoped lock nsAutoLock lock(threadLock); XPCPerThreadData* iterp = nsnull; XPCPerThreadData* thread; while(nsnull != (thread = XPCPerThreadData::IterateThreads(&iterp))) { XPCCallContext* ccxp = thread->GetCallContext(); while(ccxp) { // Deal with the strictness of callcontext that // complains if you ask for a tearoff when // it is in a state where the tearoff could not // possibly be valid. if(ccxp->CanGetTearOff()) { XPCWrappedNativeTearOff* to = ccxp->GetTearOff(); if(to) to->Mark(); } ccxp = ccxp->GetPrevCallContext(); } } } // Do the sweeping... XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs(); } } // Now we need to kill the 'Dying' XPCWrappedNativeProtos. // We transfered these native objects to this table when their // JSObject's were finalized. We did not destroy them immediately // at that point because the ordering of JS finalization is not // deterministic and we did not yet know if any wrappers that // might still be referencing the protos where still yet to be // finalized and destroyed. We *do* know that the protos' // JSObjects would not have been finalized if there were any // wrappers that referenced the proto but where not themselves // slated for finalization in this gc cycle. So... at this point // we know that any and all wrappers that might have been // referencing the protos in the dying list are themselves dead. // So, we can safely delete all the protos in the list. self->mDyingWrappedNativeProtoMap-> Enumerate(DyingProtoKiller, nsnull); // mThreadRunningGC indicates that GC is running. // Clear it and notify waiters. { // scoped lock XPCAutoLock lock(self->GetMapLock()); NS_ASSERTION(self->mThreadRunningGC == PR_GetCurrentThread(), "bad state"); self->mThreadRunningGC = nsnull; xpc_NotifyAll(self->GetMapLock()); } break; } case JSGC_END: { // NOTE that this event happens outside of the gc lock in // the js engine. So this could be simultaneous with the // events above. // Do any deferred released of native objects. nsVoidArray* array = &self->mNativesToReleaseArray; #ifdef XPC_TRACK_DEFERRED_RELEASES printf("XPC - Begin deferred Release of %d nsISupports pointers\n", array->Count()); #endif while(1) { nsISupports* obj; { PRInt32 count = array->Count(); if(!count) { array->Compact(); break; } obj = reinterpret_cast<nsISupports*> (array->ElementAt(count-1)); array->RemoveElementAt(count-1); } NS_RELEASE(obj); } #ifdef XPC_TRACK_DEFERRED_RELEASES printf("XPC - End deferred Releases\n"); #endif break; } default: break; } } // always chain to old GCCallback if non-null. return gOldJSGCCallback ? gOldJSGCCallback(cx, status) : JS_TRUE; }
// static XPCJSRuntime* XPCJSRuntime::newXPCJSRuntime(nsXPConnect* aXPConnect, nsIJSRuntimeService* aJSRuntimeService) { NS_PRECONDITION(aXPConnect,"bad param"); NS_PRECONDITION(aJSRuntimeService,"bad param"); XPCJSRuntime* self; self = new XPCJSRuntime(aXPConnect, aJSRuntimeService); if(self && self->GetJSRuntime() && self->GetContextMap() && self->GetWrappedJSMap() && self->GetWrappedJSClassMap() && self->GetIID2NativeInterfaceMap() && self->GetClassInfo2NativeSetMap() && self->GetNativeSetMap() && self->GetThisTranslatorMap() && self->GetNativeScriptableSharedMap() && self->GetDyingWrappedNativeProtoMap() && self->GetExplicitNativeWrapperMap() && self->GetMapLock()) { return self; } delete self; return nsnull; }
bool XrayWrapper<Base>::resolveOwnProperty(JSContext *cx, JSObject *wrapper, jsid id, bool set, PropertyDescriptor *desc) { // Partially transparent wrappers (which used to be known as XOWs) don't // have a .wrappedJSObject property. XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); if (!WrapperFactory::IsPartiallyTransparent(wrapper) && (id == rt->GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT) || // Check for baseURIObject and nodePrincipal no nodes and // documentURIObject on documents, but only from privileged scripts. // Do the id checks before the QIs and IsPrivilegedScript() checks, // since they're cheaper and will tend to fail most of the time // anyway. (((id == rt->GetStringID(XPCJSRuntime::IDX_BASEURIOBJECT) || id == rt->GetStringID(XPCJSRuntime::IDX_NODEPRINCIPAL)) && Is<nsINode>(wrapper)) || (id == rt->GetStringID(XPCJSRuntime::IDX_DOCUMENTURIOBJECT) && Is<nsIDocument>(wrapper))) && IsPrivilegedScript())) { bool status; JSWrapper::Action action = set ? JSWrapper::SET : JSWrapper::GET; desc->obj = NULL; // default value if (!this->enter(cx, wrapper, id, action, &status)) return status; AutoLeaveHelper<Base> helper(*this, cx, wrapper); desc->obj = wrapper; desc->attrs = JSPROP_ENUMERATE|JSPROP_SHARED; if (id == rt->GetStringID(XPCJSRuntime::IDX_WRAPPED_JSOBJECT)) desc->getter = wrappedJSObject_getter; else if (id == rt->GetStringID(XPCJSRuntime::IDX_BASEURIOBJECT)) desc->getter = baseURIObject_getter; else if (id == rt->GetStringID(XPCJSRuntime::IDX_DOCUMENTURIOBJECT)) desc->getter = documentURIObject_getter; else desc->getter = nodePrincipal_getter; desc->setter = NULL; desc->shortid = 0; desc->value = JSVAL_VOID; return true; } desc->obj = NULL; uintN flags = (set ? JSRESOLVE_ASSIGNING : 0) | JSRESOLVE_QUALIFIED; JSObject *holder = GetHolder(wrapper); JSObject *expando = GetExpandoObject(holder); // Check for expando properties first. if (expando && !JS_GetPropertyDescriptorById(cx, expando, id, flags, desc)) { return false; } if (desc->obj) { // Pretend the property lives on the wrapper. desc->obj = wrapper; return true; } JSBool hasProp; if (!JS_HasPropertyById(cx, holder, id, &hasProp)) { return false; } if (!hasProp) { XPCWrappedNative *wn = GetWrappedNativeFromHolder(holder); // Run the resolve hook of the wrapped native. if (!NATIVE_HAS_FLAG(wn, WantNewResolve)) { desc->obj = nsnull; return true; } PRBool retval = true; JSObject *pobj = NULL; nsresult rv = wn->GetScriptableInfo()->GetCallback()->NewResolve(wn, cx, wrapper, id, flags, &pobj, &retval); if (NS_FAILED(rv)) { if (retval) XPCThrower::Throw(rv, cx); return false; } if (!pobj) { desc->obj = nsnull; return true; } #ifdef DEBUG NS_ASSERTION(JS_HasPropertyById(cx, holder, id, &hasProp) && hasProp, "id got defined somewhere else?"); #endif } if (!JS_GetPropertyDescriptorById(cx, holder, id, flags, desc)) return false; // Pretend we found the property on the wrapper, not the holder. desc->obj = wrapper; return true; }