/** * Get the interface name and member name (for error messages). * * We could instead have each quick stub pass its name to the error-handling * functions, as that name is statically known. But that would be redundant; * the information is handy at runtime anyway. Also, this code often produces * a more specific error message, e.g. "[nsIDOMHTMLDocument.appendChild]" * rather than "[nsIDOMNode.appendChild]". */ static void GetMemberInfo(JSObject *obj, jsid memberId, const char **ifaceName) { *ifaceName = "Unknown"; // Don't try to generate a useful name if there are security wrappers, // because it isn't worth the risk of something going wrong just to generate // an error message. Instead, only handle the simple case where we have the // reflector in hand. if (IS_WRAPPER_CLASS(js::GetObjectClass(obj))) { XPCWrappedNativeProto *proto; if (IS_SLIM_WRAPPER_OBJECT(obj)) { proto = GetSlimWrapperProto(obj); } else { MOZ_ASSERT(IS_WN_WRAPPER_OBJECT(obj)); XPCWrappedNative *wrapper = static_cast<XPCWrappedNative *>(js::GetObjectPrivate(obj)); proto = wrapper->GetProto(); } if (proto) { XPCNativeSet *set = proto->GetSet(); if (set) { XPCNativeMember *member; XPCNativeInterface *iface; if (set->FindMember(memberId, &member, &iface)) *ifaceName = iface->GetNameString(); } } } }
static bool XPC_WN_Shared_Proto_Enumerate(JSContext* cx, HandleObject obj) { MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_ModsAllowed_Proto_JSClass || js::GetObjectClass(obj) == &XPC_WN_NoMods_Proto_JSClass, "bad proto"); XPCWrappedNativeProto* self = (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj); if (!self) return false; XPCNativeSet* set = self->GetSet(); if (!set) return false; XPCCallContext ccx(cx); if (!ccx.IsValid()) return false; uint16_t interface_count = set->GetInterfaceCount(); XPCNativeInterface** interfaceArray = set->GetInterfaceArray(); for (uint16_t i = 0; i < interface_count; i++) { XPCNativeInterface* iface = interfaceArray[i]; uint16_t member_count = iface->GetMemberCount(); for (uint16_t k = 0; k < member_count; k++) { if (!xpc_ForcePropertyResolve(cx, obj, iface->GetMemberAt(k)->GetName())) return false; } } return true; }
NativeUnMarkedSetRemover(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, void *arg) { XPCNativeSet* set = ((ClassInfo2NativeSetMap::Entry*)hdr)->value; if(set->IsMarked()) return JS_DHASH_NEXT; return JS_DHASH_REMOVE; }
// static XPCNativeSet* XPCNativeSet::GetNewOrUsed(XPCCallContext& ccx, XPCNativeSet* firstSet, XPCNativeSet* secondSet, bool preserveFirstSetOrder) { // Figure out how many interfaces we'll need in the new set. PRUint32 uniqueCount = firstSet->mInterfaceCount; for (PRUint32 i = 0; i < secondSet->mInterfaceCount; ++i) { if (!firstSet->HasInterface(secondSet->mInterfaces[i])) uniqueCount++; } // If everything in secondSet was a duplicate, we can just use the first // set. if (uniqueCount == firstSet->mInterfaceCount) return firstSet; // If the secondSet is just a superset of the first, we can use it provided // that the caller doesn't care about ordering. if (!preserveFirstSetOrder && uniqueCount == secondSet->mInterfaceCount) return secondSet; // Ok, darn. Now we have to make a new set. // // It would be faster to just create the new set all at once, but that // would involve wrangling with some pretty hairy code - especially since // a lot of stuff assumes that sets are created by adding one interface to an // existing set. So let's just do the slow and easy thing and hope that the // above optimizations handle the common cases. XPCNativeSet* currentSet = firstSet; for (PRUint32 i = 0; i < secondSet->mInterfaceCount; ++i) { XPCNativeInterface* iface = secondSet->mInterfaces[i]; if (!currentSet->HasInterface(iface)) { // Create a new augmented set, inserting this interface at the end. PRUint32 pos = currentSet->mInterfaceCount; currentSet = XPCNativeSet::GetNewOrUsed(ccx, currentSet, iface, pos); if (!currentSet) return nsnull; } } // We've got the union set. Hand it back to the caller. MOZ_ASSERT(currentSet->mInterfaceCount == uniqueCount); return currentSet; }
/** * Get the interface name and member name (for error messages). * * We could instead have each quick stub pass its name to the error-handling * functions, as that name is statically known. But that would be redundant; * the information is handy at runtime anyway. Also, this code often produces * a more specific error message, e.g. "[nsIDOMHTMLDocument.appendChild]" * rather than "[nsIDOMNode.appendChild]". */ static void GetMemberInfo(JSObject *obj, jsval memberId, const char **ifaceName, const char **memberName) { // Get the interface name. From DefinePropertyIfFound (in // xpcwrappednativejsops.cpp) and XPCThrower::Verbosify. // // We could instead make the quick stub could pass in its interface name, // but this code often produces a more specific error message, e.g. *ifaceName = "Unknown"; NS_ASSERTION(IS_WRAPPER_CLASS(STOBJ_GET_CLASS(obj)) || STOBJ_GET_CLASS(obj) == &XPC_WN_Tearoff_JSClass || IS_SLIM_WRAPPER(obj), "obj must be a wrapper"); XPCWrappedNativeProto *proto; if(IS_SLIM_WRAPPER(obj)) { proto = GetSlimWrapperProto(obj); } else { XPCWrappedNative *wrapper = (XPCWrappedNative *) obj->getPrivate(); proto = wrapper->GetProto(); } if(proto) { XPCNativeSet *set = proto->GetSet(); if(set) { XPCNativeMember *member; XPCNativeInterface *iface; if(set->FindMember(memberId, &member, &iface)) *ifaceName = iface->GetNameString(); } } *memberName = (JSVAL_IS_STRING(memberId) ? JS_GetStringBytes(JSVAL_TO_STRING(memberId)) : "unknown"); }
bool NativeSetMap::Entry::Match(const PLDHashEntryHdr* entry, const void* key) { auto Key = static_cast<const XPCNativeSetKey*>(key); XPCNativeSet* SetInTable = ((Entry*)entry)->key_value; XPCNativeSet* Set = Key->GetBaseSet(); XPCNativeInterface* Addition = Key->GetAddition(); if (!Set) { // This is a special case to deal with the invariant that says: // "All sets have exactly one nsISupports interface and it comes first." // See XPCNativeSet::NewInstance for details. // // Though we might have a key that represents only one interface, we // know that if that one interface were contructed into a set then // it would end up really being a set with two interfaces (except for // the case where the one interface happened to be nsISupports). return (SetInTable->GetInterfaceCount() == 1 && SetInTable->GetInterfaceAt(0) == Addition) || (SetInTable->GetInterfaceCount() == 2 && SetInTable->GetInterfaceAt(1) == Addition); } if (!Addition && Set == SetInTable) return true; uint16_t count = Set->GetInterfaceCount(); if (count + (Addition ? 1 : 0) != SetInTable->GetInterfaceCount()) return false; XPCNativeInterface** CurrentInTable = SetInTable->GetInterfaceArray(); XPCNativeInterface** Current = Set->GetInterfaceArray(); for (uint16_t i = 0; i < count; i++) { if (*(Current++) != *(CurrentInTable++)) return false; } return !Addition || Addition == *(CurrentInTable++); }
NativeSetSweeper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, void *arg) { XPCNativeSet* set = ((NativeSetMap::Entry*)hdr)->key_value; if(set->IsMarked()) { set->Unmark(); return JS_DHASH_NEXT; } #ifdef XPC_REPORT_NATIVE_INTERFACE_AND_SET_FLUSHING printf("- Destroying XPCNativeSet for:\n"); PRUint16 count = set->GetInterfaceCount(); for(PRUint16 k = 0; k < count; k++) { XPCNativeInterface* iface = set->GetInterfaceAt(k); printf(" %s\n",JS_GetStringBytes(JSVAL_TO_STRING(iface->GetName()))); } #endif XPCNativeSet::DestroyInstance(set); return JS_DHASH_REMOVE; }
/** * Get the interface name and member name (for error messages). * * We could instead have each quick stub pass its name to the error-handling * functions, as that name is statically known. But that would be redundant; * the information is handy at runtime anyway. Also, this code often produces * a more specific error message, e.g. "[nsIDOMHTMLDocument.appendChild]" * rather than "[nsIDOMNode.appendChild]". */ static void GetMemberInfo(JSObject *obj, jsid memberId, const char **ifaceName) { *ifaceName = "Unknown"; // Don't try to generate a useful name if there are security wrappers, // because it isn't worth the risk of something going wrong just to generate // an error message. Instead, only handle the simple case where we have the // reflector in hand. if (IS_WN_REFLECTOR(obj)) { XPCWrappedNative *wrapper = XPCWrappedNative::Get(obj); XPCWrappedNativeProto *proto = wrapper->GetProto(); if (proto) { XPCNativeSet *set = proto->GetSet(); if (set) { XPCNativeMember *member; XPCNativeInterface *iface; if (set->FindMember(memberId, &member, &iface)) *ifaceName = iface->GetNameString(); } } } }
bool XPC_WN_Shared_Enumerate(JSContext* cx, HandleObject obj) { XPCCallContext ccx(cx, obj); XPCWrappedNative* wrapper = ccx.GetWrapper(); THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); // Since we aren't going to enumerate tearoff names and the prototype // handles non-mutated members, we can do this potential short-circuit. if (!wrapper->HasMutatedSet()) return true; XPCNativeSet* set = wrapper->GetSet(); XPCNativeSet* protoSet = wrapper->HasProto() ? wrapper->GetProto()->GetSet() : nullptr; uint16_t interface_count = set->GetInterfaceCount(); XPCNativeInterface** interfaceArray = set->GetInterfaceArray(); for (uint16_t i = 0; i < interface_count; i++) { XPCNativeInterface* iface = interfaceArray[i]; uint16_t member_count = iface->GetMemberCount(); for (uint16_t k = 0; k < member_count; k++) { XPCNativeMember* member = iface->GetMemberAt(k); jsid name = member->GetName(); // Skip if this member is going to come from the proto. uint16_t index; if (protoSet && protoSet->FindMember(name, nullptr, &index) && index == i) continue; if (!xpc_ForcePropertyResolve(cx, obj, name)) return false; } } return true; }
// 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; }
bool XPC_WN_Helper_Resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp) { nsresult rv = NS_OK; bool retval = true; bool resolved = false; XPCCallContext ccx(cx, obj); XPCWrappedNative* wrapper = ccx.GetWrapper(); THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); RootedId old(cx, ccx.SetResolveName(id)); XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo(); if (si && si->GetFlags().WantResolve()) { XPCWrappedNative* oldResolvingWrapper; bool allowPropMods = si->GetFlags().AllowPropModsDuringResolve(); if (allowPropMods) oldResolvingWrapper = ccx.SetResolvingWrapper(wrapper); rv = si->GetCallback()->Resolve(wrapper, cx, obj, id, &resolved, &retval); if (allowPropMods) (void)ccx.SetResolvingWrapper(oldResolvingWrapper); } old = ccx.SetResolveName(old); MOZ_ASSERT(old == id, "bad nest"); if (NS_FAILED(rv)) { return Throw(rv, cx); } if (resolved) { *resolvedp = true; } else if (wrapper->HasMutatedSet()) { // We are here if scriptable did not resolve this property and // it *might* be in the instance set but not the proto set. XPCNativeSet* set = wrapper->GetSet(); XPCNativeSet* protoSet = wrapper->HasProto() ? wrapper->GetProto()->GetSet() : nullptr; XPCNativeMember* member = nullptr; RefPtr<XPCNativeInterface> iface; bool IsLocal = false; if (set->FindMember(id, &member, &iface, protoSet, &IsLocal) && IsLocal) { XPCWrappedNative* oldResolvingWrapper; XPCNativeScriptableFlags siFlags(0); if (si) siFlags = si->GetFlags(); XPCWrappedNative* wrapperForInterfaceNames = siFlags.DontReflectInterfaceNames() ? nullptr : wrapper; oldResolvingWrapper = ccx.SetResolvingWrapper(wrapper); retval = DefinePropertyIfFound(ccx, obj, id, set, iface, member, wrapper->GetScope(), false, wrapperForInterfaceNames, nullptr, si, JSPROP_ENUMERATE, resolvedp); (void)ccx.SetResolvingWrapper(oldResolvingWrapper); } } return retval; }