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