// static XPCNativeSet* XPCNativeSet::NewInstance(XPCCallContext& ccx, XPCNativeInterface** array, uint16_t count) { XPCNativeSet* obj = nullptr; if (!array || !count) return nullptr; // We impose the invariant: // "All sets have exactly one nsISupports interface and it comes first." // This is the place where we impose that rule - even if given inputs // that don't exactly follow the rule. XPCNativeInterface* isup = XPCNativeInterface::GetISupports(ccx); uint16_t slots = count+1; uint16_t i; XPCNativeInterface** pcur; for (i = 0, pcur = array; i < count; i++, pcur++) { if (*pcur == isup) slots--; } // Use placement new to create an object with the right amount of space // to hold the members array int size = sizeof(XPCNativeSet); if (slots > 1) size += (slots - 1) * sizeof(XPCNativeInterface*); void* place = new char[size]; if (place) obj = new(place) XPCNativeSet(); if (obj) { // Stick the nsISupports in front and skip additional nsISupport(s) XPCNativeInterface** inp = array; XPCNativeInterface** outp = (XPCNativeInterface**) &obj->mInterfaces; uint16_t memberCount = 1; // for the one member in nsISupports *(outp++) = isup; for (i = 0; i < count; i++) { XPCNativeInterface* cur; if (isup == (cur = *(inp++))) continue; *(outp++) = cur; memberCount += cur->GetMemberCount(); } obj->mMemberCount = memberCount; obj->mInterfaceCount = slots; } return obj; }
XPCWrappedNative* XPCCallContext::UnwrapThisIfAllowed(HandleObject obj, HandleObject fun, unsigned argc) { // We should only get here for objects that aren't safe to unwrap. MOZ_ASSERT(!js::CheckedUnwrap(obj)); MOZ_ASSERT(js::IsObjectInContextCompartment(obj, mJSContext)); // We can't do anything here without a function. if (!fun) return nullptr; // Determine if we're allowed to unwrap the security wrapper to invoke the // method. // // We have the Interface and Member that this corresponds to, but // unfortunately our access checks are based on the object class name and // property name. So we cheat a little bit here - we verify that the object // does indeed implement the method's Interface, and then just check that we // can successfully access property with method's name from the object. // First, get the XPCWN out of the underlying object. We should have a wrapper // here, potentially an outer window proxy, and then an XPCWN. MOZ_ASSERT(js::IsWrapper(obj)); RootedObject unwrapped(mJSContext, js::UncheckedUnwrap(obj, /* stopAtOuter = */ false)); MOZ_ASSERT(unwrapped == JS_ObjectToInnerObject(mJSContext, js::Wrapper::wrappedObject(obj))); // Make sure we have an XPCWN, and grab it. if (!IS_WN_REFLECTOR(unwrapped)) return nullptr; XPCWrappedNative *wn = (XPCWrappedNative*)js::GetObjectPrivate(unwrapped); // Next, get the call info off the function object. XPCNativeInterface *interface; XPCNativeMember *member; XPCNativeMember::GetCallInfo(fun, &interface, &member); // To be extra safe, make sure that the underlying native implements the // interface before unwrapping. Even if we didn't check this, we'd still // theoretically fail during tearoff lookup for mismatched methods. if (!wn->HasInterfaceNoQI(*interface->GetIID())) return nullptr; // See if the access is permitted. // // NB: This calculation of SET vs GET is a bit wonky, but that's what // XPC_WN_GetterSetter does. bool set = argc && argc != NO_ARGS && member->IsWritableAttribute(); js::Wrapper::Action act = set ? js::Wrapper::SET : js::Wrapper::GET; js::Wrapper *handler = js::Wrapper::wrapperHandler(obj); bool ignored; JS::Rooted<jsid> id(mJSContext, member->GetName()); if (!handler->enter(mJSContext, obj, id, act, &ignored)) return nullptr; // Ok, this call is safe. return wn; }
// static void XPCThrower::Verbosify(XPCCallContext& ccx, char** psz, PRBool own) { char* sz = nsnull; if(ccx.HasInterfaceAndMember()) { XPCNativeInterface* iface = ccx.GetInterface(); #ifdef XPC_IDISPATCH_SUPPORT NS_ASSERTION(ccx.GetIDispatchMember() == nsnull || ccx.GetMember() == nsnull, "Both IDispatch member and regular XPCOM member " "were set in XPCCallContext"); char const * name; if(ccx.GetIDispatchMember()) { XPCDispInterface::Member * member = reinterpret_cast<XPCDispInterface::Member*>(ccx.GetIDispatchMember()); if(member && JSVAL_IS_STRING(member->GetName())) { name = JS_GetStringBytes(JSVAL_TO_STRING(member->GetName())); } else name = "Unknown"; } else name = iface->GetMemberName(ccx, ccx.GetMember()); sz = JS_smprintf("%s [%s.%s]", *psz, iface->GetNameString(), name); #else sz = JS_smprintf("%s [%s.%s]", *psz, iface->GetNameString(), iface->GetMemberName(ccx, ccx.GetMember())); #endif } if(sz) { if(own) JS_smprintf_free(*psz); *psz = sz; } }
// static void XPCThrower::Verbosify(XPCCallContext& ccx, char** psz, PRBool own) { char* sz = nsnull; if(ccx.HasInterfaceAndMember()) { XPCNativeInterface* iface = ccx.GetInterface(); jsid id = JSID_VOID; #ifdef XPC_IDISPATCH_SUPPORT NS_ASSERTION(ccx.GetIDispatchMember() == nsnull || ccx.GetMember() == nsnull, "Both IDispatch member and regular XPCOM member " "were set in XPCCallContext"); if(ccx.GetIDispatchMember()) { XPCDispInterface::Member * member = reinterpret_cast<XPCDispInterface::Member*>(ccx.GetIDispatchMember()); if(member && JSID_IS_STRING(member->GetName())) { id = member->GetName(); } } else #endif { id = ccx.GetMember()->GetName(); } JSAutoByteString bytes; const char *name = JSID_IS_VOID(id) ? "Unknown" : bytes.encode(ccx, JSID_TO_STRING(id)); if(!name) { name = ""; } sz = JS_smprintf("%s [%s.%s]", *psz, iface->GetNameString(), name); } if(sz) { if(own) JS_smprintf_free(*psz); *psz = sz; } }
/** * 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"); }
NativeInterfaceSweeper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, void *arg) { CX_AND_XPCRT_Data* data = (CX_AND_XPCRT_Data*) arg; XPCNativeInterface* iface = ((IID2NativeInterfaceMap::Entry*)hdr)->value; if(iface->IsMarked()) { iface->Unmark(); return JS_DHASH_NEXT; } #ifdef XPC_REPORT_NATIVE_INTERFACE_AND_SET_FLUSHING printf("- Destroying XPCNativeInterface for %s\n", JS_GetStringBytes(JSVAL_TO_STRING(iface->GetName()))); #endif XPCNativeInterface::DestroyInstance(data->cx, data->rt, iface); return JS_DHASH_REMOVE; }
static bool XPC_WN_TearOff_Enumerate(JSContext* cx, HandleObject obj) { XPCCallContext ccx(cx, obj); XPCWrappedNative* wrapper = ccx.GetWrapper(); THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); XPCWrappedNativeTearOff* to = ccx.GetTearOff(); XPCNativeInterface* iface; if (!to || nullptr == (iface = to->GetInterface())) return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); 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; }
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; }
// static void XPCThrower::Verbosify(XPCCallContext& ccx, char** psz, bool own) { char* sz = nullptr; if (ccx.HasInterfaceAndMember()) { XPCNativeInterface* iface = ccx.GetInterface(); jsid id = ccx.GetMember()->GetName(); JSAutoByteString bytes; const char* name = JSID_IS_VOID(id) ? "Unknown" : bytes.encodeLatin1(ccx, JSID_TO_STRING(id)); if (!name) { name = ""; } sz = JS_smprintf("%s [%s.%s]", *psz, iface->GetNameString(), name); } if (sz) { if (own) JS_smprintf_free(*psz); *psz = sz; } }
/** * 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; }