michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 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 xptiInterfaceInfoManager. */ michael@0: michael@0: #include "mozilla/XPTInterfaceInfoManager.h" michael@0: michael@0: #include "mozilla/FileUtils.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/StaticPtr.h" michael@0: michael@0: #include "xptiprivate.h" michael@0: #include "nsDependentString.h" michael@0: #include "nsString.h" michael@0: #include "nsISupportsArray.h" michael@0: #include "nsArrayEnumerator.h" michael@0: #include "nsDirectoryService.h" michael@0: #include "nsIMemoryReporter.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: NS_IMPL_ISUPPORTS( michael@0: XPTInterfaceInfoManager, michael@0: nsIInterfaceInfoManager, michael@0: nsIMemoryReporter) michael@0: michael@0: static StaticRefPtr gInterfaceInfoManager; michael@0: michael@0: size_t michael@0: XPTInterfaceInfoManager::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) michael@0: { michael@0: size_t n = aMallocSizeOf(this); michael@0: ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor); michael@0: // The entries themselves are allocated out of an arena accounted michael@0: // for elsewhere, so don't measure them michael@0: n += mWorkingSet.mIIDTable.SizeOfExcludingThis(nullptr, aMallocSizeOf); michael@0: n += mWorkingSet.mNameTable.SizeOfExcludingThis(nullptr, aMallocSizeOf); michael@0: return n; michael@0: } michael@0: michael@0: MOZ_DEFINE_MALLOC_SIZE_OF(XPTIMallocSizeOf) michael@0: michael@0: NS_IMETHODIMP michael@0: XPTInterfaceInfoManager::CollectReports(nsIHandleReportCallback* aHandleReport, michael@0: nsISupports* aData) michael@0: { michael@0: size_t amount = SizeOfIncludingThis(XPTIMallocSizeOf); michael@0: michael@0: // Measure gXPTIStructArena here, too. This is a bit grotty because it michael@0: // doesn't belong to the XPTIInterfaceInfoManager, but there's no michael@0: // obviously better place to measure it. michael@0: amount += XPT_SizeOfArena(gXPTIStructArena, XPTIMallocSizeOf); michael@0: michael@0: return MOZ_COLLECT_REPORT( michael@0: "explicit/xpti-working-set", KIND_HEAP, UNITS_BYTES, amount, michael@0: "Memory used by the XPCOM typelib system."); michael@0: } michael@0: michael@0: // static michael@0: XPTInterfaceInfoManager* michael@0: XPTInterfaceInfoManager::GetSingleton() michael@0: { michael@0: if (!gInterfaceInfoManager) { michael@0: gInterfaceInfoManager = new XPTInterfaceInfoManager(); michael@0: gInterfaceInfoManager->InitMemoryReporter(); michael@0: } michael@0: return gInterfaceInfoManager; michael@0: } michael@0: michael@0: void michael@0: XPTInterfaceInfoManager::FreeInterfaceInfoManager() michael@0: { michael@0: gInterfaceInfoManager = nullptr; michael@0: } michael@0: michael@0: XPTInterfaceInfoManager::XPTInterfaceInfoManager() michael@0: : mWorkingSet(), michael@0: mResolveLock("XPTInterfaceInfoManager.mResolveLock") michael@0: { michael@0: } michael@0: michael@0: XPTInterfaceInfoManager::~XPTInterfaceInfoManager() michael@0: { michael@0: // We only do this on shutdown of the service. michael@0: mWorkingSet.InvalidateInterfaceInfos(); michael@0: michael@0: UnregisterWeakMemoryReporter(this); michael@0: } michael@0: michael@0: void michael@0: XPTInterfaceInfoManager::InitMemoryReporter() michael@0: { michael@0: RegisterWeakMemoryReporter(this); michael@0: } michael@0: michael@0: void michael@0: XPTInterfaceInfoManager::RegisterBuffer(char *buf, uint32_t length) michael@0: { michael@0: XPTState *state = XPT_NewXDRState(XPT_DECODE, buf, length); michael@0: if (!state) michael@0: return; michael@0: michael@0: XPTCursor cursor; michael@0: if (!XPT_MakeCursor(state, XPT_HEADER, 0, &cursor)) { michael@0: XPT_DestroyXDRState(state); michael@0: return; michael@0: } michael@0: michael@0: XPTHeader *header = nullptr; michael@0: if (XPT_DoHeader(gXPTIStructArena, &cursor, &header)) { michael@0: RegisterXPTHeader(header); michael@0: } michael@0: michael@0: XPT_DestroyXDRState(state); michael@0: } michael@0: michael@0: void michael@0: XPTInterfaceInfoManager::RegisterXPTHeader(XPTHeader* aHeader) michael@0: { michael@0: if (aHeader->major_version >= XPT_MAJOR_INCOMPATIBLE_VERSION) { michael@0: NS_ASSERTION(!aHeader->num_interfaces,"bad libxpt"); michael@0: LOG_AUTOREG((" file is version %d.%d Type file of version %d.0 or higher can not be read.\n", (int)header->major_version, (int)header->minor_version, (int)XPT_MAJOR_INCOMPATIBLE_VERSION)); michael@0: } michael@0: michael@0: xptiTypelibGuts* typelib = xptiTypelibGuts::Create(aHeader); michael@0: michael@0: ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor); michael@0: for(uint16_t k = 0; k < aHeader->num_interfaces; k++) michael@0: VerifyAndAddEntryIfNew(aHeader->interface_directory + k, k, typelib); michael@0: } michael@0: michael@0: void michael@0: XPTInterfaceInfoManager::VerifyAndAddEntryIfNew(XPTInterfaceDirectoryEntry* iface, michael@0: uint16_t idx, michael@0: xptiTypelibGuts* typelib) michael@0: { michael@0: if (!iface->interface_descriptor) michael@0: return; michael@0: michael@0: // The number of maximum methods is not arbitrary. It is the same value as michael@0: // in xpcom/reflect/xptcall/public/genstubs.pl; do not change this value michael@0: // without changing that one or you WILL see problems. michael@0: if (iface->interface_descriptor->num_methods > 250 && michael@0: !(XPT_ID_IS_BUILTINCLASS(iface->interface_descriptor->flags))) { michael@0: NS_ASSERTION(0, "Too many methods to handle for the stub, cannot load"); michael@0: fprintf(stderr, "ignoring too large interface: %s\n", iface->name); michael@0: return; michael@0: } michael@0: michael@0: mWorkingSet.mTableReentrantMonitor.AssertCurrentThreadIn(); michael@0: xptiInterfaceEntry* entry = mWorkingSet.mIIDTable.Get(iface->iid); michael@0: if (entry) { michael@0: // XXX validate this info to find possible inconsistencies michael@0: LOG_AUTOREG((" ignoring repeated interface: %s\n", iface->name)); michael@0: return; michael@0: } michael@0: michael@0: // Build a new xptiInterfaceEntry object and hook it up. michael@0: michael@0: entry = xptiInterfaceEntry::Create(iface->name, michael@0: iface->iid, michael@0: iface->interface_descriptor, michael@0: typelib); michael@0: if (!entry) michael@0: return; michael@0: michael@0: //XXX We should SetHeader too as part of the validation, no? michael@0: entry->SetScriptableFlag(XPT_ID_IS_SCRIPTABLE(iface->interface_descriptor->flags)); michael@0: entry->SetBuiltinClassFlag(XPT_ID_IS_BUILTINCLASS(iface->interface_descriptor->flags)); michael@0: michael@0: mWorkingSet.mIIDTable.Put(entry->IID(), entry); michael@0: mWorkingSet.mNameTable.Put(entry->GetTheName(), entry); michael@0: michael@0: typelib->SetEntryAt(idx, entry); michael@0: michael@0: LOG_AUTOREG((" added interface: %s\n", iface->name)); michael@0: } michael@0: michael@0: // this is a private helper michael@0: static nsresult michael@0: EntryToInfo(xptiInterfaceEntry* entry, nsIInterfaceInfo **_retval) michael@0: { michael@0: if (!entry) { michael@0: *_retval = nullptr; michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsRefPtr info = entry->InterfaceInfo(); michael@0: info.forget(_retval); michael@0: return NS_OK; michael@0: } michael@0: michael@0: xptiInterfaceEntry* michael@0: XPTInterfaceInfoManager::GetInterfaceEntryForIID(const nsIID *iid) michael@0: { michael@0: ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor); michael@0: return mWorkingSet.mIIDTable.Get(*iid); michael@0: } michael@0: michael@0: /* nsIInterfaceInfo getInfoForIID (in nsIIDPtr iid); */ michael@0: NS_IMETHODIMP michael@0: XPTInterfaceInfoManager::GetInfoForIID(const nsIID * iid, nsIInterfaceInfo **_retval) michael@0: { michael@0: NS_ASSERTION(iid, "bad param"); michael@0: NS_ASSERTION(_retval, "bad param"); michael@0: michael@0: ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor); michael@0: xptiInterfaceEntry* entry = mWorkingSet.mIIDTable.Get(*iid); michael@0: return EntryToInfo(entry, _retval); michael@0: } michael@0: michael@0: /* nsIInterfaceInfo getInfoForName (in string name); */ michael@0: NS_IMETHODIMP michael@0: XPTInterfaceInfoManager::GetInfoForName(const char *name, nsIInterfaceInfo **_retval) michael@0: { michael@0: NS_ASSERTION(name, "bad param"); michael@0: NS_ASSERTION(_retval, "bad param"); michael@0: michael@0: ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor); michael@0: xptiInterfaceEntry* entry = mWorkingSet.mNameTable.Get(name); michael@0: return EntryToInfo(entry, _retval); michael@0: } michael@0: michael@0: /* nsIIDPtr getIIDForName (in string name); */ michael@0: NS_IMETHODIMP michael@0: XPTInterfaceInfoManager::GetIIDForName(const char *name, nsIID * *_retval) michael@0: { michael@0: NS_ASSERTION(name, "bad param"); michael@0: NS_ASSERTION(_retval, "bad param"); michael@0: michael@0: ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor); michael@0: xptiInterfaceEntry* entry = mWorkingSet.mNameTable.Get(name); michael@0: if (!entry) { michael@0: *_retval = nullptr; michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return entry->GetIID(_retval); michael@0: } michael@0: michael@0: /* string getNameForIID (in nsIIDPtr iid); */ michael@0: NS_IMETHODIMP michael@0: XPTInterfaceInfoManager::GetNameForIID(const nsIID * iid, char **_retval) michael@0: { michael@0: NS_ASSERTION(iid, "bad param"); michael@0: NS_ASSERTION(_retval, "bad param"); michael@0: michael@0: ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor); michael@0: xptiInterfaceEntry* entry = mWorkingSet.mIIDTable.Get(*iid); michael@0: if (!entry) { michael@0: *_retval = nullptr; michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return entry->GetName(_retval); michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: xpti_ArrayAppender(const char* name, xptiInterfaceEntry* entry, void* arg) michael@0: { michael@0: nsCOMArray* array = static_cast*>(arg); michael@0: michael@0: if (entry->GetScriptableFlag()) { michael@0: nsCOMPtr ii = entry->InterfaceInfo(); michael@0: array->AppendElement(ii); michael@0: } michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: /* nsIEnumerator enumerateInterfaces (); */ michael@0: void michael@0: XPTInterfaceInfoManager::GetScriptableInterfaces(nsCOMArray& aInterfaces) michael@0: { michael@0: // I didn't want to incur the size overhead of using nsHashtable just to michael@0: // make building an enumerator easier. So, this code makes a snapshot of michael@0: // the table using an nsISupportsArray and builds an enumerator for that. michael@0: // We can afford this transient cost. michael@0: michael@0: ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor); michael@0: aInterfaces.SetCapacity(mWorkingSet.mNameTable.Count()); michael@0: mWorkingSet.mNameTable.EnumerateRead(xpti_ArrayAppender, &aInterfaces); michael@0: } michael@0: michael@0: struct ArrayAndPrefix michael@0: { michael@0: nsISupportsArray* array; michael@0: const char* prefix; michael@0: uint32_t length; michael@0: }; michael@0: michael@0: static PLDHashOperator michael@0: xpti_ArrayPrefixAppender(const char* keyname, xptiInterfaceEntry* entry, void* arg) michael@0: { michael@0: ArrayAndPrefix* args = (ArrayAndPrefix*) arg; michael@0: michael@0: const char* name = entry->GetTheName(); michael@0: if (name != PL_strnstr(name, args->prefix, args->length)) michael@0: return PL_DHASH_NEXT; michael@0: michael@0: nsCOMPtr ii; michael@0: if (NS_SUCCEEDED(EntryToInfo(entry, getter_AddRefs(ii)))) michael@0: args->array->AppendElement(ii); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: /* nsIEnumerator enumerateInterfacesWhoseNamesStartWith (in string prefix); */ michael@0: NS_IMETHODIMP michael@0: XPTInterfaceInfoManager::EnumerateInterfacesWhoseNamesStartWith(const char *prefix, nsIEnumerator **_retval) michael@0: { michael@0: nsCOMPtr array; michael@0: NS_NewISupportsArray(getter_AddRefs(array)); michael@0: if (!array) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: ReentrantMonitorAutoEnter monitor(mWorkingSet.mTableReentrantMonitor); michael@0: ArrayAndPrefix args = {array, prefix, static_cast(strlen(prefix))}; michael@0: mWorkingSet.mNameTable.EnumerateRead(xpti_ArrayPrefixAppender, &args); michael@0: michael@0: return array->Enumerate(_retval); michael@0: } michael@0: michael@0: /* void autoRegisterInterfaces (); */ michael@0: NS_IMETHODIMP michael@0: XPTInterfaceInfoManager::AutoRegisterInterfaces() michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: }