diff options
Diffstat (limited to 'xpcom/reflect/xptinfo/xptiInterfaceInfo.cpp')
-rw-r--r-- | xpcom/reflect/xptinfo/xptiInterfaceInfo.cpp | 742 |
1 files changed, 742 insertions, 0 deletions
diff --git a/xpcom/reflect/xptinfo/xptiInterfaceInfo.cpp b/xpcom/reflect/xptinfo/xptiInterfaceInfo.cpp new file mode 100644 index 000000000..8ea80fda1 --- /dev/null +++ b/xpcom/reflect/xptinfo/xptiInterfaceInfo.cpp @@ -0,0 +1,742 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +/* Implementation of xptiInterfaceEntry and xptiInterfaceInfo. */ + +#include "xptiprivate.h" +#include "mozilla/dom/ScriptSettings.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/XPTInterfaceInfoManager.h" +#include "mozilla/PodOperations.h" +#include "jsapi.h" + +using namespace mozilla; + +/* static */ xptiInterfaceEntry* +xptiInterfaceEntry::Create(const char* name, const nsID& iid, + XPTInterfaceDescriptor* aDescriptor, + xptiTypelibGuts* aTypelib) +{ + int namelen = strlen(name); + void* place = + XPT_CALLOC8(gXPTIStructArena, sizeof(xptiInterfaceEntry) + namelen); + if (!place) { + return nullptr; + } + return new (place) xptiInterfaceEntry(name, namelen, iid, aDescriptor, + aTypelib); +} + +xptiInterfaceEntry::xptiInterfaceEntry(const char* name, + size_t nameLength, + const nsID& iid, + XPTInterfaceDescriptor* aDescriptor, + xptiTypelibGuts* aTypelib) + : mIID(iid) + , mDescriptor(aDescriptor) + , mTypelib(aTypelib) + , mParent(nullptr) + , mInfo(nullptr) + , mMethodBaseIndex(0) + , mConstantBaseIndex(0) + , mFlags(0) +{ + memcpy(mName, name, nameLength); + SetResolvedState(PARTIALLY_RESOLVED); +} + +bool +xptiInterfaceEntry::Resolve() +{ + MutexAutoLock lock(XPTInterfaceInfoManager::GetResolveLock()); + return ResolveLocked(); +} + +bool +xptiInterfaceEntry::ResolveLocked() +{ + int resolvedState = GetResolveState(); + + if(resolvedState == FULLY_RESOLVED) + return true; + if(resolvedState == RESOLVE_FAILED) + return false; + + NS_ASSERTION(GetResolveState() == PARTIALLY_RESOLVED, "bad state!"); + + // Finish out resolution by finding parent and Resolving it so + // we can set the info we get from it. + + uint16_t parent_index = mDescriptor->parent_interface; + + if(parent_index) + { + xptiInterfaceEntry* parent = + mTypelib->GetEntryAt(parent_index - 1); + + if(!parent || !parent->EnsureResolvedLocked()) + { + SetResolvedState(RESOLVE_FAILED); + return false; + } + + mParent = parent; + if (parent->GetHasNotXPCOMFlag()) { + SetHasNotXPCOMFlag(); + } else { + for (uint16_t idx = 0; idx < mDescriptor->num_methods; ++idx) { + nsXPTMethodInfo* method = reinterpret_cast<nsXPTMethodInfo*>( + mDescriptor->method_descriptors + idx); + if (method->IsNotXPCOM()) { + SetHasNotXPCOMFlag(); + break; + } + } + } + + + mMethodBaseIndex = + parent->mMethodBaseIndex + + parent->mDescriptor->num_methods; + + mConstantBaseIndex = + parent->mConstantBaseIndex + + parent->mDescriptor->num_constants; + + } + LOG_RESOLVE(("+ complete resolve of %s\n", mName)); + + SetResolvedState(FULLY_RESOLVED); + return true; +} + +/**************************************************/ +// These non-virtual methods handle the delegated nsIInterfaceInfo methods. + +nsresult +xptiInterfaceEntry::GetName(char **name) +{ + // It is not necessary to Resolve because this info is read from manifest. + *name = (char*) nsMemory::Clone(mName, strlen(mName)+1); + return *name ? NS_OK : NS_ERROR_OUT_OF_MEMORY; +} + +nsresult +xptiInterfaceEntry::GetIID(nsIID **iid) +{ + // It is not necessary to Resolve because this info is read from manifest. + *iid = (nsIID*) nsMemory::Clone(&mIID, sizeof(nsIID)); + return *iid ? NS_OK : NS_ERROR_OUT_OF_MEMORY; +} + +nsresult +xptiInterfaceEntry::IsScriptable(bool* result) +{ + // It is not necessary to Resolve because this info is read from manifest. + *result = GetScriptableFlag(); + return NS_OK; +} + +nsresult +xptiInterfaceEntry::IsFunction(bool* result) +{ + if(!EnsureResolved()) + return NS_ERROR_UNEXPECTED; + + *result = XPT_ID_IS_FUNCTION(mDescriptor->flags); + return NS_OK; +} + +nsresult +xptiInterfaceEntry::GetMethodCount(uint16_t* count) +{ + if(!EnsureResolved()) + return NS_ERROR_UNEXPECTED; + + *count = mMethodBaseIndex + + mDescriptor->num_methods; + return NS_OK; +} + +nsresult +xptiInterfaceEntry::GetConstantCount(uint16_t* count) +{ + if(!EnsureResolved()) + return NS_ERROR_UNEXPECTED; + + if(!count) + return NS_ERROR_UNEXPECTED; + + *count = mConstantBaseIndex + + mDescriptor->num_constants; + return NS_OK; +} + +nsresult +xptiInterfaceEntry::GetMethodInfo(uint16_t index, const nsXPTMethodInfo** info) +{ + if(!EnsureResolved()) + return NS_ERROR_UNEXPECTED; + + if(index < mMethodBaseIndex) + return mParent->GetMethodInfo(index, info); + + if(index >= mMethodBaseIndex + + mDescriptor->num_methods) + { + NS_ERROR("bad param"); + *info = nullptr; + return NS_ERROR_INVALID_ARG; + } + + // else... + *info = reinterpret_cast<nsXPTMethodInfo*> + (&mDescriptor->method_descriptors[index - mMethodBaseIndex]); + return NS_OK; +} + +nsresult +xptiInterfaceEntry::GetMethodInfoForName(const char* methodName, uint16_t *index, + const nsXPTMethodInfo** result) +{ + if(!EnsureResolved()) + return NS_ERROR_UNEXPECTED; + + // This is a slow algorithm, but this is not expected to be called much. + for(uint16_t i = 0; i < mDescriptor->num_methods; ++i) + { + const nsXPTMethodInfo* info; + info = reinterpret_cast<nsXPTMethodInfo*> + (&mDescriptor-> + method_descriptors[i]); + if (PL_strcmp(methodName, info->GetName()) == 0) { + *index = i + mMethodBaseIndex; + *result = info; + return NS_OK; + } + } + + if(mParent) + return mParent->GetMethodInfoForName(methodName, index, result); + else + { + *index = 0; + *result = 0; + return NS_ERROR_INVALID_ARG; + } +} + +nsresult +xptiInterfaceEntry::GetConstant(uint16_t index, JS::MutableHandleValue constant, + char** name) +{ + if(!EnsureResolved()) + return NS_ERROR_UNEXPECTED; + + if(index < mConstantBaseIndex) + return mParent->GetConstant(index, constant, name); + + if(index >= mConstantBaseIndex + + mDescriptor->num_constants) + { + NS_PRECONDITION(0, "bad param"); + return NS_ERROR_INVALID_ARG; + } + + const auto& c = mDescriptor->const_descriptors[index - mConstantBaseIndex]; + AutoJSContext cx; + JS::Rooted<JS::Value> v(cx); + v.setUndefined(); + + switch (c.type.prefix.flags) { + case nsXPTType::T_I8: + { + v.setInt32(c.value.i8); + break; + } + case nsXPTType::T_U8: + { + v.setInt32(c.value.ui8); + break; + } + case nsXPTType::T_I16: + { + v.setInt32(c.value.i16); + break; + } + case nsXPTType::T_U16: + { + v.setInt32(c.value.ui16); + break; + } + case nsXPTType::T_I32: + { + v = JS_NumberValue(c.value.i32); + break; + } + case nsXPTType::T_U32: + { + v = JS_NumberValue(c.value.ui32); + break; + } + default: + { +#ifdef DEBUG + NS_ERROR("Non-numeric constant found in interface."); +#endif + } + } + + constant.set(v); + *name = ToNewCString(nsDependentCString(c.name)); + + return NS_OK; +} + +// this is a private helper + +nsresult +xptiInterfaceEntry::GetInterfaceIndexForParam(uint16_t methodIndex, + const nsXPTParamInfo* param, + uint16_t* interfaceIndex) +{ + if(!EnsureResolved()) + return NS_ERROR_UNEXPECTED; + + if(methodIndex < mMethodBaseIndex) + return mParent->GetInterfaceIndexForParam(methodIndex, param, + interfaceIndex); + + if(methodIndex >= mMethodBaseIndex + + mDescriptor->num_methods) + { + NS_ERROR("bad param"); + return NS_ERROR_INVALID_ARG; + } + + const XPTTypeDescriptor *td = ¶m->type; + + while (XPT_TDP_TAG(td->prefix) == TD_ARRAY) { + td = &mDescriptor->additional_types[td->u.array.additional_type]; + } + + if(XPT_TDP_TAG(td->prefix) != TD_INTERFACE_TYPE) { + NS_ERROR("not an interface"); + return NS_ERROR_INVALID_ARG; + } + + *interfaceIndex = (td->u.iface.iface_hi8 << 8) | td->u.iface.iface_lo8; + return NS_OK; +} + +nsresult +xptiInterfaceEntry::GetEntryForParam(uint16_t methodIndex, + const nsXPTParamInfo * param, + xptiInterfaceEntry** entry) +{ + if(!EnsureResolved()) + return NS_ERROR_UNEXPECTED; + + if(methodIndex < mMethodBaseIndex) + return mParent->GetEntryForParam(methodIndex, param, entry); + + uint16_t interfaceIndex = 0; + nsresult rv = GetInterfaceIndexForParam(methodIndex, param, + &interfaceIndex); + if (NS_FAILED(rv)) { + return rv; + } + + xptiInterfaceEntry* theEntry = mTypelib->GetEntryAt(interfaceIndex - 1); + + // This can happen if a declared interface is not available at runtime. + if(!theEntry) + { + *entry = nullptr; + return NS_ERROR_FAILURE; + } + + *entry = theEntry; + return NS_OK; +} + +already_AddRefed<ShimInterfaceInfo> +xptiInterfaceEntry::GetShimForParam(uint16_t methodIndex, + const nsXPTParamInfo* param) +{ + if(methodIndex < mMethodBaseIndex) { + return mParent->GetShimForParam(methodIndex, param); + } + + uint16_t interfaceIndex = 0; + nsresult rv = GetInterfaceIndexForParam(methodIndex, param, + &interfaceIndex); + if (NS_FAILED(rv)) { + return nullptr; + } + + const char* shimName = mTypelib->GetEntryNameAt(interfaceIndex - 1); + RefPtr<ShimInterfaceInfo> shim = + ShimInterfaceInfo::MaybeConstruct(shimName, nullptr); + return shim.forget(); +} + +nsresult +xptiInterfaceEntry::GetInfoForParam(uint16_t methodIndex, + const nsXPTParamInfo *param, + nsIInterfaceInfo** info) +{ + xptiInterfaceEntry* entry; + nsresult rv = GetEntryForParam(methodIndex, param, &entry); + if (NS_FAILED(rv)) { + RefPtr<ShimInterfaceInfo> shim = GetShimForParam(methodIndex, param); + if (!shim) { + return rv; + } + + shim.forget(info); + return NS_OK; + } + + *info = entry->InterfaceInfo().take(); + + return NS_OK; +} + +nsresult +xptiInterfaceEntry::GetIIDForParam(uint16_t methodIndex, + const nsXPTParamInfo* param, nsIID** iid) +{ + xptiInterfaceEntry* entry; + nsresult rv = GetEntryForParam(methodIndex, param, &entry); + if (NS_FAILED(rv)) { + RefPtr<ShimInterfaceInfo> shim = GetShimForParam(methodIndex, param); + if (!shim) { + return rv; + } + + return shim->GetInterfaceIID(iid); + } + return entry->GetIID(iid); +} + +nsresult +xptiInterfaceEntry::GetIIDForParamNoAlloc(uint16_t methodIndex, + const nsXPTParamInfo * param, + nsIID *iid) +{ + xptiInterfaceEntry* entry; + nsresult rv = GetEntryForParam(methodIndex, param, &entry); + if (NS_FAILED(rv)) { + RefPtr<ShimInterfaceInfo> shim = GetShimForParam(methodIndex, param); + if (!shim) { + return rv; + } + + const nsIID* shimIID; + DebugOnly<nsresult> rv2 = shim->GetIIDShared(&shimIID); + MOZ_ASSERT(NS_SUCCEEDED(rv2)); + *iid = *shimIID; + return NS_OK; + } + *iid = entry->mIID; + return NS_OK; +} + +// this is a private helper +nsresult +xptiInterfaceEntry::GetTypeInArray(const nsXPTParamInfo* param, + uint16_t dimension, + const XPTTypeDescriptor** type) +{ + NS_ASSERTION(IsFullyResolved(), "bad state"); + + const XPTTypeDescriptor *td = ¶m->type; + const XPTTypeDescriptor *additional_types = + mDescriptor->additional_types; + + for (uint16_t i = 0; i < dimension; i++) { + if(XPT_TDP_TAG(td->prefix) != TD_ARRAY) { + NS_ERROR("bad dimension"); + return NS_ERROR_INVALID_ARG; + } + td = &additional_types[td->u.array.additional_type]; + } + + *type = td; + return NS_OK; +} + +nsresult +xptiInterfaceEntry::GetTypeForParam(uint16_t methodIndex, + const nsXPTParamInfo* param, + uint16_t dimension, + nsXPTType* type) +{ + if(!EnsureResolved()) + return NS_ERROR_UNEXPECTED; + + if(methodIndex < mMethodBaseIndex) + return mParent-> + GetTypeForParam(methodIndex, param, dimension, type); + + if(methodIndex >= mMethodBaseIndex + + mDescriptor->num_methods) + { + NS_ERROR("bad index"); + return NS_ERROR_INVALID_ARG; + } + + const XPTTypeDescriptor *td; + + if(dimension) { + nsresult rv = GetTypeInArray(param, dimension, &td); + if(NS_FAILED(rv)) + return rv; + } + else + td = ¶m->type; + + *type = nsXPTType(td->prefix); + return NS_OK; +} + +nsresult +xptiInterfaceEntry::GetSizeIsArgNumberForParam(uint16_t methodIndex, + const nsXPTParamInfo* param, + uint16_t dimension, + uint8_t* argnum) +{ + if(!EnsureResolved()) + return NS_ERROR_UNEXPECTED; + + if(methodIndex < mMethodBaseIndex) + return mParent-> + GetSizeIsArgNumberForParam(methodIndex, param, dimension, argnum); + + if(methodIndex >= mMethodBaseIndex + + mDescriptor->num_methods) + { + NS_ERROR("bad index"); + return NS_ERROR_INVALID_ARG; + } + + const XPTTypeDescriptor *td; + + if(dimension) { + nsresult rv = GetTypeInArray(param, dimension, &td); + if(NS_FAILED(rv)) + return rv; + } + else + td = ¶m->type; + + // verify that this is a type that has size_is + switch (XPT_TDP_TAG(td->prefix)) { + case TD_ARRAY: + *argnum = td->u.array.argnum; + break; + case TD_PSTRING_SIZE_IS: + case TD_PWSTRING_SIZE_IS: + *argnum = td->u.pstring_is.argnum; + break; + default: + NS_ERROR("not a size_is"); + return NS_ERROR_INVALID_ARG; + } + + return NS_OK; +} + +nsresult +xptiInterfaceEntry::GetInterfaceIsArgNumberForParam(uint16_t methodIndex, + const nsXPTParamInfo* param, + uint8_t* argnum) +{ + if(!EnsureResolved()) + return NS_ERROR_UNEXPECTED; + + if(methodIndex < mMethodBaseIndex) + return mParent-> + GetInterfaceIsArgNumberForParam(methodIndex, param, argnum); + + if(methodIndex >= mMethodBaseIndex + + mDescriptor->num_methods) + { + NS_ERROR("bad index"); + return NS_ERROR_INVALID_ARG; + } + + const XPTTypeDescriptor *td = ¶m->type; + + while (XPT_TDP_TAG(td->prefix) == TD_ARRAY) { + td = &mDescriptor->additional_types[td->u.array.additional_type]; + } + + if(XPT_TDP_TAG(td->prefix) != TD_INTERFACE_IS_TYPE) { + NS_ERROR("not an iid_is"); + return NS_ERROR_INVALID_ARG; + } + + *argnum = td->u.interface_is.argnum; + return NS_OK; +} + +nsresult +xptiInterfaceEntry::IsIID(const nsIID * iid, bool *_retval) +{ + // It is not necessary to Resolve because this info is read from manifest. + *_retval = mIID.Equals(*iid); + return NS_OK; +} + +nsresult +xptiInterfaceEntry::GetNameShared(const char **name) +{ + // It is not necessary to Resolve because this info is read from manifest. + *name = mName; + return NS_OK; +} + +nsresult +xptiInterfaceEntry::GetIIDShared(const nsIID * *iid) +{ + // It is not necessary to Resolve because this info is read from manifest. + *iid = &mIID; + return NS_OK; +} + +nsresult +xptiInterfaceEntry::HasAncestor(const nsIID * iid, bool *_retval) +{ + *_retval = false; + + for(xptiInterfaceEntry* current = this; + current; + current = current->mParent) + { + if(current->mIID.Equals(*iid)) + { + *_retval = true; + break; + } + if(!current->EnsureResolved()) + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +/***************************************************/ + +already_AddRefed<xptiInterfaceInfo> +xptiInterfaceEntry::InterfaceInfo() +{ +#ifdef DEBUG + XPTInterfaceInfoManager::GetSingleton()->mWorkingSet.mTableReentrantMonitor. + AssertCurrentThreadIn(); +#endif + + if(!mInfo) + { + mInfo = new xptiInterfaceInfo(this); + } + + RefPtr<xptiInterfaceInfo> info = mInfo; + return info.forget(); +} + +void +xptiInterfaceEntry::LockedInvalidateInterfaceInfo() +{ + if(mInfo) + { + mInfo->Invalidate(); + mInfo = nullptr; + } +} + +bool +xptiInterfaceInfo::BuildParent() +{ + mozilla::ReentrantMonitorAutoEnter monitor(XPTInterfaceInfoManager::GetSingleton()-> + mWorkingSet.mTableReentrantMonitor); + NS_ASSERTION(mEntry && + mEntry->IsFullyResolved() && + !mParent && + mEntry->Parent(), + "bad BuildParent call"); + mParent = mEntry->Parent()->InterfaceInfo(); + return true; +} + +/***************************************************************************/ + +NS_IMPL_QUERY_INTERFACE(xptiInterfaceInfo, nsIInterfaceInfo) + +xptiInterfaceInfo::xptiInterfaceInfo(xptiInterfaceEntry* entry) + : mEntry(entry) +{ +} + +xptiInterfaceInfo::~xptiInterfaceInfo() +{ + NS_ASSERTION(!mEntry, "bad state in dtor"); +} + +void +xptiInterfaceInfo::Invalidate() +{ + mParent = nullptr; + mEntry = nullptr; +} + +MozExternalRefCountType +xptiInterfaceInfo::AddRef(void) +{ + nsrefcnt cnt = ++mRefCnt; + NS_LOG_ADDREF(this, cnt, "xptiInterfaceInfo", sizeof(*this)); + return cnt; +} + +MozExternalRefCountType +xptiInterfaceInfo::Release(void) +{ + xptiInterfaceEntry* entry = mEntry; + nsrefcnt cnt = --mRefCnt; + NS_LOG_RELEASE(this, cnt, "xptiInterfaceInfo"); + if(!cnt) + { + mozilla::ReentrantMonitorAutoEnter monitor(XPTInterfaceInfoManager:: + GetSingleton()->mWorkingSet. + mTableReentrantMonitor); + + // If InterfaceInfo added and *released* a reference before we + // acquired the monitor then 'this' might already be dead. In that + // case we would not want to try to access any instance data. We + // would want to bail immediately. If 'this' is already dead then the + // entry will no longer have a pointer to 'this'. So, we can protect + // ourselves from danger without more aggressive locking. + if(entry && !entry->InterfaceInfoEquals(this)) + return 0; + + // If InterfaceInfo added a reference before we acquired the monitor + // then we want to bail out of here without destorying the object. + if(mRefCnt) + return 1; + + if(mEntry) + { + mEntry->LockedInterfaceInfoDeathNotification(); + mEntry = nullptr; + } + + delete this; + return 0; + } + return cnt; +} + +/***************************************************************************/ |