michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* Implementation of xptiInterfaceEntry and xptiInterfaceInfo. */ michael@0: michael@0: #include "xptiprivate.h" michael@0: #include "mozilla/XPTInterfaceInfoManager.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: /***************************************************************************/ michael@0: // Debug Instrumentation... michael@0: michael@0: #ifdef SHOW_INFO_COUNT_STATS michael@0: static int DEBUG_TotalInfos = 0; michael@0: static int DEBUG_CurrentInfos = 0; michael@0: static int DEBUG_MaxInfos = 0; michael@0: static int DEBUG_ReentrantMonitorEntryCount = 0; michael@0: michael@0: #define LOG_INFO_CREATE(t) \ michael@0: DEBUG_TotalInfos++; \ michael@0: DEBUG_CurrentInfos++; \ michael@0: if(DEBUG_MaxInfos < DEBUG_CurrentInfos) \ michael@0: DEBUG_MaxInfos = DEBUG_CurrentInfos /* no ';' */ michael@0: michael@0: #define LOG_INFO_DESTROY(t) \ michael@0: DEBUG_CurrentInfos-- /* no ';' */ michael@0: michael@0: #define LOG_INFO_MONITOR_ENTRY \ michael@0: DEBUG_ReentrantMonitorEntryCount++ /* no ';' */ michael@0: michael@0: #else /* SHOW_INFO_COUNT_STATS */ michael@0: michael@0: #define LOG_INFO_CREATE(t) ((void)0) michael@0: #define LOG_INFO_DESTROY(t) ((void)0) michael@0: #define LOG_INFO_MONITOR_ENTRY ((void)0) michael@0: #endif /* SHOW_INFO_COUNT_STATS */ michael@0: michael@0: /* static */ xptiInterfaceEntry* michael@0: xptiInterfaceEntry::Create(const char* name, const nsID& iid, michael@0: XPTInterfaceDescriptor* aDescriptor, michael@0: xptiTypelibGuts* aTypelib) michael@0: { michael@0: int namelen = strlen(name); michael@0: return new (XPT_MALLOC(gXPTIStructArena, michael@0: sizeof(xptiInterfaceEntry) + namelen)) michael@0: xptiInterfaceEntry(name, namelen, iid, aDescriptor, aTypelib); michael@0: } michael@0: michael@0: xptiInterfaceEntry::xptiInterfaceEntry(const char* name, michael@0: size_t nameLength, michael@0: const nsID& iid, michael@0: XPTInterfaceDescriptor* aDescriptor, michael@0: xptiTypelibGuts* aTypelib) michael@0: : mIID(iid) michael@0: , mDescriptor(aDescriptor) michael@0: , mMethodBaseIndex(0) michael@0: , mConstantBaseIndex(0) michael@0: , mTypelib(aTypelib) michael@0: , mParent(nullptr) michael@0: , mInfo(nullptr) michael@0: , mFlags(0) michael@0: { michael@0: memcpy(mName, name, nameLength); michael@0: SetResolvedState(PARTIALLY_RESOLVED); michael@0: } michael@0: michael@0: bool michael@0: xptiInterfaceEntry::Resolve() michael@0: { michael@0: MutexAutoLock lock(XPTInterfaceInfoManager::GetResolveLock()); michael@0: return ResolveLocked(); michael@0: } michael@0: michael@0: bool michael@0: xptiInterfaceEntry::ResolveLocked() michael@0: { michael@0: int resolvedState = GetResolveState(); michael@0: michael@0: if(resolvedState == FULLY_RESOLVED) michael@0: return true; michael@0: if(resolvedState == RESOLVE_FAILED) michael@0: return false; michael@0: michael@0: NS_ASSERTION(GetResolveState() == PARTIALLY_RESOLVED, "bad state!"); michael@0: michael@0: // Finish out resolution by finding parent and Resolving it so michael@0: // we can set the info we get from it. michael@0: michael@0: uint16_t parent_index = mDescriptor->parent_interface; michael@0: michael@0: if(parent_index) michael@0: { michael@0: xptiInterfaceEntry* parent = michael@0: mTypelib->GetEntryAt(parent_index - 1); michael@0: michael@0: if(!parent || !parent->EnsureResolvedLocked()) michael@0: { michael@0: SetResolvedState(RESOLVE_FAILED); michael@0: return false; michael@0: } michael@0: michael@0: mParent = parent; michael@0: michael@0: mMethodBaseIndex = michael@0: parent->mMethodBaseIndex + michael@0: parent->mDescriptor->num_methods; michael@0: michael@0: mConstantBaseIndex = michael@0: parent->mConstantBaseIndex + michael@0: parent->mDescriptor->num_constants; michael@0: michael@0: } michael@0: LOG_RESOLVE(("+ complete resolve of %s\n", mName)); michael@0: michael@0: SetResolvedState(FULLY_RESOLVED); michael@0: return true; michael@0: } michael@0: michael@0: /**************************************************/ michael@0: // These non-virtual methods handle the delegated nsIInterfaceInfo methods. michael@0: michael@0: nsresult michael@0: xptiInterfaceEntry::GetName(char **name) michael@0: { michael@0: // It is not necessary to Resolve because this info is read from manifest. michael@0: *name = (char*) nsMemory::Clone(mName, strlen(mName)+1); michael@0: return *name ? NS_OK : NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: nsresult michael@0: xptiInterfaceEntry::GetIID(nsIID **iid) michael@0: { michael@0: // It is not necessary to Resolve because this info is read from manifest. michael@0: *iid = (nsIID*) nsMemory::Clone(&mIID, sizeof(nsIID)); michael@0: return *iid ? NS_OK : NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: nsresult michael@0: xptiInterfaceEntry::IsScriptable(bool* result) michael@0: { michael@0: // It is not necessary to Resolve because this info is read from manifest. michael@0: *result = GetScriptableFlag(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: xptiInterfaceEntry::IsFunction(bool* result) michael@0: { michael@0: if(!EnsureResolved()) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: *result = XPT_ID_IS_FUNCTION(mDescriptor->flags); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: xptiInterfaceEntry::GetMethodCount(uint16_t* count) michael@0: { michael@0: if(!EnsureResolved()) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: *count = mMethodBaseIndex + michael@0: mDescriptor->num_methods; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: xptiInterfaceEntry::GetConstantCount(uint16_t* count) michael@0: { michael@0: if(!EnsureResolved()) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: *count = mConstantBaseIndex + michael@0: mDescriptor->num_constants; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: xptiInterfaceEntry::GetMethodInfo(uint16_t index, const nsXPTMethodInfo** info) michael@0: { michael@0: if(!EnsureResolved()) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: if(index < mMethodBaseIndex) michael@0: return mParent->GetMethodInfo(index, info); michael@0: michael@0: if(index >= mMethodBaseIndex + michael@0: mDescriptor->num_methods) michael@0: { michael@0: NS_ERROR("bad param"); michael@0: *info = nullptr; michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: // else... michael@0: *info = reinterpret_cast michael@0: (&mDescriptor->method_descriptors[index - mMethodBaseIndex]); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: xptiInterfaceEntry::GetMethodInfoForName(const char* methodName, uint16_t *index, michael@0: const nsXPTMethodInfo** result) michael@0: { michael@0: if(!EnsureResolved()) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: // This is a slow algorithm, but this is not expected to be called much. michael@0: for(uint16_t i = 0; i < mDescriptor->num_methods; ++i) michael@0: { michael@0: const nsXPTMethodInfo* info; michael@0: info = reinterpret_cast michael@0: (&mDescriptor-> michael@0: method_descriptors[i]); michael@0: if (PL_strcmp(methodName, info->GetName()) == 0) { michael@0: *index = i + mMethodBaseIndex; michael@0: *result = info; michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: if(mParent) michael@0: return mParent->GetMethodInfoForName(methodName, index, result); michael@0: else michael@0: { michael@0: *index = 0; michael@0: *result = 0; michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: xptiInterfaceEntry::GetConstant(uint16_t index, const nsXPTConstant** constant) michael@0: { michael@0: if(!EnsureResolved()) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: if(index < mConstantBaseIndex) michael@0: return mParent->GetConstant(index, constant); michael@0: michael@0: if(index >= mConstantBaseIndex + michael@0: mDescriptor->num_constants) michael@0: { michael@0: NS_PRECONDITION(0, "bad param"); michael@0: *constant = nullptr; michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: // else... michael@0: *constant = michael@0: reinterpret_cast michael@0: (&mDescriptor-> michael@0: const_descriptors[index - michael@0: mConstantBaseIndex]); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // this is a private helper michael@0: michael@0: nsresult michael@0: xptiInterfaceEntry::GetEntryForParam(uint16_t methodIndex, michael@0: const nsXPTParamInfo * param, michael@0: xptiInterfaceEntry** entry) michael@0: { michael@0: if(!EnsureResolved()) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: if(methodIndex < mMethodBaseIndex) michael@0: return mParent->GetEntryForParam(methodIndex, param, entry); michael@0: michael@0: if(methodIndex >= mMethodBaseIndex + michael@0: mDescriptor->num_methods) michael@0: { michael@0: NS_ERROR("bad param"); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: const XPTTypeDescriptor *td = ¶m->type; michael@0: michael@0: while (XPT_TDP_TAG(td->prefix) == TD_ARRAY) { michael@0: td = &mDescriptor->additional_types[td->type.additional_type]; michael@0: } michael@0: michael@0: if(XPT_TDP_TAG(td->prefix) != TD_INTERFACE_TYPE) { michael@0: NS_ERROR("not an interface"); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: xptiInterfaceEntry* theEntry = mTypelib-> michael@0: GetEntryAt(td->type.iface - 1); michael@0: michael@0: // This can happen if a declared interface is not available at runtime. michael@0: if(!theEntry) michael@0: { michael@0: NS_WARNING("Declared InterfaceInfo not found"); michael@0: *entry = nullptr; michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: *entry = theEntry; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: xptiInterfaceEntry::GetInfoForParam(uint16_t methodIndex, michael@0: const nsXPTParamInfo *param, michael@0: nsIInterfaceInfo** info) michael@0: { michael@0: xptiInterfaceEntry* entry; michael@0: nsresult rv = GetEntryForParam(methodIndex, param, &entry); michael@0: if(NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: *info = entry->InterfaceInfo().take(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: xptiInterfaceEntry::GetIIDForParam(uint16_t methodIndex, michael@0: const nsXPTParamInfo* param, nsIID** iid) michael@0: { michael@0: xptiInterfaceEntry* entry; michael@0: nsresult rv = GetEntryForParam(methodIndex, param, &entry); michael@0: if(NS_FAILED(rv)) michael@0: return rv; michael@0: return entry->GetIID(iid); michael@0: } michael@0: michael@0: nsresult michael@0: xptiInterfaceEntry::GetIIDForParamNoAlloc(uint16_t methodIndex, michael@0: const nsXPTParamInfo * param, michael@0: nsIID *iid) michael@0: { michael@0: xptiInterfaceEntry* entry; michael@0: nsresult rv = GetEntryForParam(methodIndex, param, &entry); michael@0: if(NS_FAILED(rv)) michael@0: return rv; michael@0: *iid = entry->mIID; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // this is a private helper michael@0: nsresult michael@0: xptiInterfaceEntry::GetTypeInArray(const nsXPTParamInfo* param, michael@0: uint16_t dimension, michael@0: const XPTTypeDescriptor** type) michael@0: { michael@0: NS_ASSERTION(IsFullyResolved(), "bad state"); michael@0: michael@0: const XPTTypeDescriptor *td = ¶m->type; michael@0: const XPTTypeDescriptor *additional_types = michael@0: mDescriptor->additional_types; michael@0: michael@0: for (uint16_t i = 0; i < dimension; i++) { michael@0: if(XPT_TDP_TAG(td->prefix) != TD_ARRAY) { michael@0: NS_ERROR("bad dimension"); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: td = &additional_types[td->type.additional_type]; michael@0: } michael@0: michael@0: *type = td; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: xptiInterfaceEntry::GetTypeForParam(uint16_t methodIndex, michael@0: const nsXPTParamInfo* param, michael@0: uint16_t dimension, michael@0: nsXPTType* type) michael@0: { michael@0: if(!EnsureResolved()) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: if(methodIndex < mMethodBaseIndex) michael@0: return mParent-> michael@0: GetTypeForParam(methodIndex, param, dimension, type); michael@0: michael@0: if(methodIndex >= mMethodBaseIndex + michael@0: mDescriptor->num_methods) michael@0: { michael@0: NS_ERROR("bad index"); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: const XPTTypeDescriptor *td; michael@0: michael@0: if(dimension) { michael@0: nsresult rv = GetTypeInArray(param, dimension, &td); michael@0: if(NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: else michael@0: td = ¶m->type; michael@0: michael@0: *type = nsXPTType(td->prefix); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: xptiInterfaceEntry::GetSizeIsArgNumberForParam(uint16_t methodIndex, michael@0: const nsXPTParamInfo* param, michael@0: uint16_t dimension, michael@0: uint8_t* argnum) michael@0: { michael@0: if(!EnsureResolved()) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: if(methodIndex < mMethodBaseIndex) michael@0: return mParent-> michael@0: GetSizeIsArgNumberForParam(methodIndex, param, dimension, argnum); michael@0: michael@0: if(methodIndex >= mMethodBaseIndex + michael@0: mDescriptor->num_methods) michael@0: { michael@0: NS_ERROR("bad index"); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: const XPTTypeDescriptor *td; michael@0: michael@0: if(dimension) { michael@0: nsresult rv = GetTypeInArray(param, dimension, &td); michael@0: if(NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: else michael@0: td = ¶m->type; michael@0: michael@0: // verify that this is a type that has size_is michael@0: switch (XPT_TDP_TAG(td->prefix)) { michael@0: case TD_ARRAY: michael@0: case TD_PSTRING_SIZE_IS: michael@0: case TD_PWSTRING_SIZE_IS: michael@0: break; michael@0: default: michael@0: NS_ERROR("not a size_is"); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: *argnum = td->argnum; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: xptiInterfaceEntry::GetInterfaceIsArgNumberForParam(uint16_t methodIndex, michael@0: const nsXPTParamInfo* param, michael@0: uint8_t* argnum) michael@0: { michael@0: if(!EnsureResolved()) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: if(methodIndex < mMethodBaseIndex) michael@0: return mParent-> michael@0: GetInterfaceIsArgNumberForParam(methodIndex, param, argnum); michael@0: michael@0: if(methodIndex >= mMethodBaseIndex + michael@0: mDescriptor->num_methods) michael@0: { michael@0: NS_ERROR("bad index"); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: const XPTTypeDescriptor *td = ¶m->type; michael@0: michael@0: while (XPT_TDP_TAG(td->prefix) == TD_ARRAY) { michael@0: td = &mDescriptor-> michael@0: additional_types[td->type.additional_type]; michael@0: } michael@0: michael@0: if(XPT_TDP_TAG(td->prefix) != TD_INTERFACE_IS_TYPE) { michael@0: NS_ERROR("not an iid_is"); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: *argnum = td->argnum; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* bool isIID (in nsIIDPtr IID); */ michael@0: nsresult michael@0: xptiInterfaceEntry::IsIID(const nsIID * IID, bool *_retval) michael@0: { michael@0: // It is not necessary to Resolve because this info is read from manifest. michael@0: *_retval = mIID.Equals(*IID); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void getNameShared ([shared, retval] out string name); */ michael@0: nsresult michael@0: xptiInterfaceEntry::GetNameShared(const char **name) michael@0: { michael@0: // It is not necessary to Resolve because this info is read from manifest. michael@0: *name = mName; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void getIIDShared ([shared, retval] out nsIIDPtrShared iid); */ michael@0: nsresult michael@0: xptiInterfaceEntry::GetIIDShared(const nsIID * *iid) michael@0: { michael@0: // It is not necessary to Resolve because this info is read from manifest. michael@0: *iid = &mIID; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* bool hasAncestor (in nsIIDPtr iid); */ michael@0: nsresult michael@0: xptiInterfaceEntry::HasAncestor(const nsIID * iid, bool *_retval) michael@0: { michael@0: *_retval = false; michael@0: michael@0: for(xptiInterfaceEntry* current = this; michael@0: current; michael@0: current = current->mParent) michael@0: { michael@0: if(current->mIID.Equals(*iid)) michael@0: { michael@0: *_retval = true; michael@0: break; michael@0: } michael@0: if(!current->EnsureResolved()) michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /***************************************************/ michael@0: michael@0: already_AddRefed michael@0: xptiInterfaceEntry::InterfaceInfo() michael@0: { michael@0: #ifdef DEBUG michael@0: XPTInterfaceInfoManager::GetSingleton()->mWorkingSet.mTableReentrantMonitor. michael@0: AssertCurrentThreadIn(); michael@0: #endif michael@0: LOG_INFO_MONITOR_ENTRY; michael@0: michael@0: if(!mInfo) michael@0: { michael@0: mInfo = new xptiInterfaceInfo(this); michael@0: } michael@0: michael@0: nsRefPtr info = mInfo; michael@0: return info.forget(); michael@0: } michael@0: michael@0: void michael@0: xptiInterfaceEntry::LockedInvalidateInterfaceInfo() michael@0: { michael@0: if(mInfo) michael@0: { michael@0: mInfo->Invalidate(); michael@0: mInfo = nullptr; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: xptiInterfaceInfo::BuildParent() michael@0: { michael@0: mozilla::ReentrantMonitorAutoEnter monitor(XPTInterfaceInfoManager::GetSingleton()-> michael@0: mWorkingSet.mTableReentrantMonitor); michael@0: NS_ASSERTION(mEntry && michael@0: mEntry->IsFullyResolved() && michael@0: !mParent && michael@0: mEntry->Parent(), michael@0: "bad BuildParent call"); michael@0: mParent = mEntry->Parent()->InterfaceInfo().take(); michael@0: return true; michael@0: } michael@0: michael@0: /***************************************************************************/ michael@0: michael@0: NS_IMPL_QUERY_INTERFACE(xptiInterfaceInfo, nsIInterfaceInfo) michael@0: michael@0: xptiInterfaceInfo::xptiInterfaceInfo(xptiInterfaceEntry* entry) michael@0: : mEntry(entry), mParent(nullptr) michael@0: { michael@0: LOG_INFO_CREATE(this); michael@0: } michael@0: michael@0: xptiInterfaceInfo::~xptiInterfaceInfo() michael@0: { michael@0: LOG_INFO_DESTROY(this); michael@0: NS_IF_RELEASE(mParent); michael@0: NS_ASSERTION(!mEntry, "bad state in dtor"); michael@0: } michael@0: michael@0: MozExternalRefCountType michael@0: xptiInterfaceInfo::AddRef(void) michael@0: { michael@0: nsrefcnt cnt = ++mRefCnt; michael@0: NS_LOG_ADDREF(this, cnt, "xptiInterfaceInfo", sizeof(*this)); michael@0: return cnt; michael@0: } michael@0: michael@0: MozExternalRefCountType michael@0: xptiInterfaceInfo::Release(void) michael@0: { michael@0: xptiInterfaceEntry* entry = mEntry; michael@0: nsrefcnt cnt = --mRefCnt; michael@0: NS_LOG_RELEASE(this, cnt, "xptiInterfaceInfo"); michael@0: if(!cnt) michael@0: { michael@0: mozilla::ReentrantMonitorAutoEnter monitor(XPTInterfaceInfoManager:: michael@0: GetSingleton()->mWorkingSet. michael@0: mTableReentrantMonitor); michael@0: LOG_INFO_MONITOR_ENTRY; michael@0: michael@0: // If InterfaceInfo added and *released* a reference before we michael@0: // acquired the monitor then 'this' might already be dead. In that michael@0: // case we would not want to try to access any instance data. We michael@0: // would want to bail immediately. If 'this' is already dead then the michael@0: // entry will no longer have a pointer to 'this'. So, we can protect michael@0: // ourselves from danger without more aggressive locking. michael@0: if(entry && !entry->InterfaceInfoEquals(this)) michael@0: return 0; michael@0: michael@0: // If InterfaceInfo added a reference before we acquired the monitor michael@0: // then we want to bail out of here without destorying the object. michael@0: if(mRefCnt) michael@0: return 1; michael@0: michael@0: if(mEntry) michael@0: { michael@0: mEntry->LockedInterfaceInfoDeathNotification(); michael@0: mEntry = nullptr; michael@0: } michael@0: michael@0: delete this; michael@0: return 0; michael@0: } michael@0: return cnt; michael@0: } michael@0: michael@0: /***************************************************************************/