diff -r 000000000000 -r 6474c204b198 js/xpconnect/src/XPCWrappedNativeInfo.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/js/xpconnect/src/XPCWrappedNativeInfo.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,784 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=4 et sw=4 tw=99: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Manage the shared info about interfaces for use by wrappedNatives. */ + +#include "xpcprivate.h" +#include "jswrapper.h" +#include "nsCxPusher.h" + +#include "mozilla/MemoryReporting.h" +#include "mozilla/XPTInterfaceInfoManager.h" + +using namespace JS; +using namespace mozilla; + +/***************************************************************************/ + +// XPCNativeMember + +// static +bool +XPCNativeMember::GetCallInfo(JSObject* funobj, + XPCNativeInterface** pInterface, + XPCNativeMember** pMember) +{ + funobj = js::UncheckedUnwrap(funobj); + jsval ifaceVal = js::GetFunctionNativeReserved(funobj, 0); + jsval memberVal = js::GetFunctionNativeReserved(funobj, 1); + + *pInterface = (XPCNativeInterface*) JSVAL_TO_PRIVATE(ifaceVal); + *pMember = (XPCNativeMember*) JSVAL_TO_PRIVATE(memberVal); + + return true; +} + +bool +XPCNativeMember::NewFunctionObject(XPCCallContext& ccx, + XPCNativeInterface* iface, HandleObject parent, + jsval* pval) +{ + MOZ_ASSERT(!IsConstant(), "Only call this if you're sure this is not a constant!"); + + return Resolve(ccx, iface, parent, pval); +} + +bool +XPCNativeMember::Resolve(XPCCallContext& ccx, XPCNativeInterface* iface, + HandleObject parent, jsval *vp) +{ + if (IsConstant()) { + const nsXPTConstant* constant; + if (NS_FAILED(iface->GetInterfaceInfo()->GetConstant(mIndex, &constant))) + return false; + + const nsXPTCMiniVariant& mv = *constant->GetValue(); + + // XXX Big Hack! + nsXPTCVariant v; + v.flags = 0; + v.type = constant->GetType(); + memcpy(&v.val, &mv.val, sizeof(mv.val)); + + RootedValue resultVal(ccx); + + if (!XPCConvert::NativeData2JS(&resultVal, &v.val, v.type, nullptr, nullptr)) + return false; + + *vp = resultVal; + + return true; + } + // else... + + // This is a method or attribute - we'll be needing a function object + + int argc; + JSNative callback; + + if (IsMethod()) { + const nsXPTMethodInfo* info; + if (NS_FAILED(iface->GetInterfaceInfo()->GetMethodInfo(mIndex, &info))) + return false; + + // Note: ASSUMES that retval is last arg. + argc = (int) info->GetParamCount(); + if (argc && info->GetParam((uint8_t)(argc-1)).IsRetval()) + argc-- ; + + callback = XPC_WN_CallMethod; + } else { + argc = 0; + callback = XPC_WN_GetterSetter; + } + + JSFunction *fun = js::NewFunctionByIdWithReserved(ccx, callback, argc, 0, parent, GetName()); + if (!fun) + return false; + + JSObject* funobj = JS_GetFunctionObject(fun); + if (!funobj) + return false; + + js::SetFunctionNativeReserved(funobj, 0, PRIVATE_TO_JSVAL(iface)); + js::SetFunctionNativeReserved(funobj, 1, PRIVATE_TO_JSVAL(this)); + + *vp = OBJECT_TO_JSVAL(funobj); + + return true; +} + +/***************************************************************************/ +// XPCNativeInterface + +// static +XPCNativeInterface* +XPCNativeInterface::GetNewOrUsed(const nsIID* iid) +{ + AutoJSContext cx; + AutoMarkingNativeInterfacePtr iface(cx); + XPCJSRuntime* rt = XPCJSRuntime::Get(); + + IID2NativeInterfaceMap* map = rt->GetIID2NativeInterfaceMap(); + if (!map) + return nullptr; + + iface = map->Find(*iid); + + if (iface) + return iface; + + nsCOMPtr info; + XPTInterfaceInfoManager::GetSingleton()->GetInfoForIID(iid, getter_AddRefs(info)); + if (!info) + return nullptr; + + 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; +} + +// 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; +} + +// static +XPCNativeInterface* +XPCNativeInterface::GetNewOrUsed(const char* name) +{ + nsCOMPtr info; + XPTInterfaceInfoManager::GetSingleton()->GetInfoForName(name, getter_AddRefs(info)); + return info ? GetNewOrUsed(info) : nullptr; +} + +// static +XPCNativeInterface* +XPCNativeInterface::GetISupports() +{ + // XXX We should optimize this to cache this common XPCNativeInterface. + return GetNewOrUsed(&NS_GET_IID(nsISupports)); +} + +// static +XPCNativeInterface* +XPCNativeInterface::NewInstance(nsIInterfaceInfo* aInfo) +{ + AutoJSContext cx; + static const uint16_t MAX_LOCAL_MEMBER_COUNT = 16; + XPCNativeMember local_members[MAX_LOCAL_MEMBER_COUNT]; + XPCNativeInterface* obj = nullptr; + XPCNativeMember* members = nullptr; + + int i; + bool failed = false; + uint16_t constCount; + uint16_t methodCount; + uint16_t totalCount; + uint16_t realTotalCount = 0; + XPCNativeMember* cur; + RootedString str(cx); + RootedId interfaceName(cx); + + // XXX Investigate lazy init? This is a problem given the + // 'placement new' scheme - we need to at least know how big to make + // the object. We might do a scan of methods to determine needed size, + // then make our object, but avoid init'ing *any* members until asked? + // Find out how often we create these objects w/o really looking at + // (or using) the members. + + bool canScript; + if (NS_FAILED(aInfo->IsScriptable(&canScript)) || !canScript) + return nullptr; + + if (NS_FAILED(aInfo->GetMethodCount(&methodCount)) || + NS_FAILED(aInfo->GetConstantCount(&constCount))) + return nullptr; + + // If the interface does not have nsISupports in its inheritance chain + // then we know we can't reflect its methods. However, some interfaces that + // are used just to reflect constants are declared this way. We need to + // go ahead and build the thing. But, we'll ignore whatever methods it may + // have. + if (!nsXPConnect::IsISupportsDescendant(aInfo)) + methodCount = 0; + + totalCount = methodCount + constCount; + + if (totalCount > MAX_LOCAL_MEMBER_COUNT) { + members = new XPCNativeMember[totalCount]; + if (!members) + return nullptr; + } else { + members = local_members; + } + + // NOTE: since getters and setters share a member, we might not use all + // of the member objects. + + for (i = 0; i < methodCount; i++) { + const nsXPTMethodInfo* info; + if (NS_FAILED(aInfo->GetMethodInfo(i, &info))) { + failed = true; + break; + } + + // don't reflect Addref or Release + if (i == 1 || i == 2) + continue; + + if (!XPCConvert::IsMethodReflectable(*info)) + continue; + + str = JS_InternString(cx, info->GetName()); + if (!str) { + NS_ERROR("bad method name"); + failed = true; + break; + } + jsid name = INTERNED_STRING_TO_JSID(cx, str); + + if (info->IsSetter()) { + MOZ_ASSERT(realTotalCount,"bad setter"); + // Note: ASSUMES Getter/Setter pairs are next to each other + // This is a rule of the typelib spec. + cur = &members[realTotalCount-1]; + MOZ_ASSERT(cur->GetName() == name,"bad setter"); + MOZ_ASSERT(cur->IsReadOnlyAttribute(),"bad setter"); + MOZ_ASSERT(cur->GetIndex() == i-1,"bad setter"); + cur->SetWritableAttribute(); + } else { + // XXX need better way to find dups + // MOZ_ASSERT(!LookupMemberByID(name),"duplicate method name"); + cur = &members[realTotalCount++]; + cur->SetName(name); + if (info->IsGetter()) + cur->SetReadOnlyAttribute(i); + else + cur->SetMethod(i); + } + } + + if (!failed) { + for (i = 0; i < constCount; i++) { + const nsXPTConstant* constant; + if (NS_FAILED(aInfo->GetConstant(i, &constant))) { + failed = true; + break; + } + + str = JS_InternString(cx, constant->GetName()); + if (!str) { + NS_ERROR("bad constant name"); + failed = true; + break; + } + jsid name = INTERNED_STRING_TO_JSID(cx, str); + + // XXX need better way to find dups + //MOZ_ASSERT(!LookupMemberByID(name),"duplicate method/constant name"); + + cur = &members[realTotalCount++]; + cur->SetName(name); + cur->SetConstant(i); + } + } + + if (!failed) { + const char* bytes; + if (NS_FAILED(aInfo->GetNameShared(&bytes)) || !bytes || + nullptr == (str = JS_InternString(cx, bytes))) { + failed = true; + } + interfaceName = INTERNED_STRING_TO_JSID(cx, str); + } + + if (!failed) { + // Use placement new to create an object with the right amount of space + // to hold the members array + int size = sizeof(XPCNativeInterface); + if (realTotalCount > 1) + size += (realTotalCount - 1) * sizeof(XPCNativeMember); + void* place = new char[size]; + if (place) + obj = new(place) XPCNativeInterface(aInfo, interfaceName); + + if (obj) { + obj->mMemberCount = realTotalCount; + // copy valid members + if (realTotalCount) + memcpy(obj->mMembers, members, + realTotalCount * sizeof(XPCNativeMember)); + } + } + + if (members && members != local_members) + delete [] members; + + return obj; +} + +// static +void +XPCNativeInterface::DestroyInstance(XPCNativeInterface* inst) +{ + inst->~XPCNativeInterface(); + delete [] (char*) inst; +} + +size_t +XPCNativeInterface::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) +{ + return mallocSizeOf(this); +} + +void +XPCNativeInterface::DebugDump(int16_t depth) +{ +#ifdef DEBUG + depth--; + XPC_LOG_ALWAYS(("XPCNativeInterface @ %x", this)); + XPC_LOG_INDENT(); + XPC_LOG_ALWAYS(("name is %s", GetNameString())); + XPC_LOG_ALWAYS(("mMemberCount is %d", mMemberCount)); + XPC_LOG_ALWAYS(("mInfo @ %x", mInfo.get())); + XPC_LOG_OUTDENT(); +#endif +} + +/***************************************************************************/ +// XPCNativeSet + +// 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(nsIClassInfo* classInfo) +{ + AutoJSContext cx; + AutoMarkingNativeSetPtr set(cx); + XPCJSRuntime* rt = XPCJSRuntime::Get(); + + ClassInfo2NativeSetMap* map = rt->GetClassInfo2NativeSetMap(); + if (!map) + return nullptr; + + set = map->Find(classInfo); + + if (set) + return set; + + nsIID** iidArray = nullptr; + AutoMarkingNativeInterfacePtrArrayPtr interfaceArray(cx); + uint32_t iidCount = 0; + + if (NS_FAILED(classInfo->GetInterfaces(&iidCount, &iidArray))) { + // Note: I'm making it OK for this call to fail so that one can add + // nsIClassInfo to classes implemented in script without requiring this + // method to be implemented. + + // Make sure these are set correctly... + iidArray = nullptr; + iidCount = 0; + } + + MOZ_ASSERT((iidCount && iidArray) || !(iidCount || iidArray), "GetInterfaces returned bad array"); + + // !!! from here on we only exit through the 'out' label !!! + + if (iidCount) { + AutoMarkingNativeInterfacePtrArrayPtr + arr(cx, new XPCNativeInterface*[iidCount], iidCount, true); + + interfaceArray = arr; + + XPCNativeInterface** currentInterface = interfaceArray; + nsIID** currentIID = iidArray; + uint16_t interfaceCount = 0; + + for (uint32_t i = 0; i < iidCount; i++) { + nsIID* iid = *(currentIID++); + if (!iid) { + NS_ERROR("Null found in classinfo interface list"); + continue; + } + + XPCNativeInterface* iface = + XPCNativeInterface::GetNewOrUsed(iid); + + if (!iface) { + // XXX warn here + continue; + } + + *(currentInterface++) = iface; + interfaceCount++; + } + + if (interfaceCount) { + set = NewInstance(interfaceArray, interfaceCount); + if (set) { + NativeSetMap* map2 = rt->GetNativeSetMap(); + if (!map2) + goto out; + + XPCNativeSetKey key(set, nullptr, 0); + + XPCNativeSet* set2 = map2->Add(&key, set); + if (!set2) { + NS_ERROR("failed to add our set!"); + DestroyInstance(set); + set = nullptr; + goto out; + } + if (set2 != set) { + DestroyInstance(set); + set = set2; + } + } + } else + set = GetNewOrUsed(&NS_GET_IID(nsISupports)); + } else + set = GetNewOrUsed(&NS_GET_IID(nsISupports)); + + if (set) { +#ifdef DEBUG + XPCNativeSet* set2 = +#endif + map->Add(classInfo, set); + MOZ_ASSERT(set2, "failed to add our set!"); + MOZ_ASSERT(set2 == set, "hashtables inconsistent!"); + } + +out: + if (iidArray) + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(iidCount, iidArray); + if (interfaceArray) + delete [] interfaceArray.get(); + + return set; +} + +// static +void +XPCNativeSet::ClearCacheEntryForClassInfo(nsIClassInfo* classInfo) +{ + XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance(); + ClassInfo2NativeSetMap* map = rt->GetClassInfo2NativeSetMap(); + if (map) + map->Remove(classInfo); +} + +// 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 +XPCNativeSet* +XPCNativeSet::GetNewOrUsed(XPCNativeSet* firstSet, + XPCNativeSet* secondSet, + bool preserveFirstSetOrder) +{ + // Figure out how many interfaces we'll need in the new set. + uint32_t uniqueCount = firstSet->mInterfaceCount; + for (uint32_t 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 (uint32_t 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. + uint32_t pos = currentSet->mInterfaceCount; + currentSet = XPCNativeSet::GetNewOrUsed(currentSet, iface, pos); + if (!currentSet) + return nullptr; + } + } + + // We've got the union set. Hand it back to the caller. + MOZ_ASSERT(currentSet->mInterfaceCount == uniqueCount); + return currentSet; +} + +// static +XPCNativeSet* +XPCNativeSet::NewInstance(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(); + 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; +} + +// static +XPCNativeSet* +XPCNativeSet::NewInstanceMutate(XPCNativeSet* otherSet, + XPCNativeInterface* newInterface, + uint16_t position) +{ + XPCNativeSet* obj = nullptr; + + if (!newInterface) + return nullptr; + if (otherSet && position > otherSet->mInterfaceCount) + return nullptr; + + // Use placement new to create an object with the right amount of space + // to hold the members array + int size = sizeof(XPCNativeSet); + if (otherSet) + size += otherSet->mInterfaceCount * sizeof(XPCNativeInterface*); + void* place = new char[size]; + if (place) + obj = new(place) XPCNativeSet(); + + if (obj) { + if (otherSet) { + obj->mMemberCount = otherSet->GetMemberCount() + + newInterface->GetMemberCount(); + obj->mInterfaceCount = otherSet->mInterfaceCount + 1; + + XPCNativeInterface** src = otherSet->mInterfaces; + XPCNativeInterface** dest = obj->mInterfaces; + for (uint16_t i = 0; i < obj->mInterfaceCount; i++) { + if (i == position) + *dest++ = newInterface; + else + *dest++ = *src++; + } + } else { + obj->mMemberCount = newInterface->GetMemberCount(); + obj->mInterfaceCount = 1; + obj->mInterfaces[0] = newInterface; + } + } + + return obj; +} + +// static +void +XPCNativeSet::DestroyInstance(XPCNativeSet* inst) +{ + inst->~XPCNativeSet(); + delete [] (char*) inst; +} + +size_t +XPCNativeSet::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) +{ + return mallocSizeOf(this); +} + +void +XPCNativeSet::DebugDump(int16_t depth) +{ +#ifdef DEBUG + depth--; + XPC_LOG_ALWAYS(("XPCNativeSet @ %x", this)); + XPC_LOG_INDENT(); + + XPC_LOG_ALWAYS(("mInterfaceCount of %d", mInterfaceCount)); + if (depth) { + for (uint16_t i = 0; i < mInterfaceCount; i++) + mInterfaces[i]->DebugDump(depth); + } + XPC_LOG_ALWAYS(("mMemberCount of %d", mMemberCount)); + XPC_LOG_OUTDENT(); +#endif +}