Bug 1478124: Part 4b - Support loading components from static lookup tables. r=froydnj

This patch essentially creates a separate, static component database for
statically-defined CID and contract ID entries, and gives it precedence over
the runtime DB. It combines the two separate databases by updating existing
code to use lookup functions which understand both databases, and then access
all entries through wrappers which defer to the appropriate underlying type.

Static component entries require no runtime relocations, and require no
writable data allocation aside from one pointer-sized BSS entry per CID, and
one bit of BSS per contract ID.

To achieve this, all strings in the static lookup tables are stored as indexes
into a static string table, all constructor functions live in a switch
statement which compiles to a relative jump table, and all writable data for
static entries is accessed by indexed lookups into BSS arrays.

We also avoid creating nsIFactory entries for static components when possible
by adding a CreateInstance method to nsFactoryEntry and the corresponding
entry wrapper to directly call the appropriate constructor method, and only
create a factory object when required by external code.

Differential Revision: https://phabricator.services.mozilla.com/D15035

--HG--
extra : rebase_source : 8d02ff3b67b8078d1ac837d8c12f54786155c6b6
extra : absorb_source : 0fe36ca220c9270e634abf5b1f320a01878e0ce7
extra : histedit_source : 51521ceae2c1b3e4e8bf63d4ed1e2e67e9468780
This commit is contained in:
Kris Maglione 2018-12-18 20:30:13 -08:00
Родитель f6ce24cfbc
Коммит 43481fed07
8 изменённых файлов: 1391 добавлений и 106 удалений

Просмотреть файл

@ -127,13 +127,13 @@ class TestPACMan : public ::testing::Test {
virtual void SetUp() {
ASSERT_EQ(NS_OK, GetNetworkProxyType(&originalNetworkProxyTypePref));
nsFactoryEntry* factoryEntry =
nsComponentManagerImpl::gComponentManager->GetFactoryEntry(
kNS_TESTDHCPCLIENTSERVICE_CID);
if (factoryEntry) {
nsresult rv =
nsComponentManagerImpl::gComponentManager->UnregisterFactory(
kNS_TESTDHCPCLIENTSERVICE_CID, factoryEntry->mFactory);
nsCOMPtr<nsIFactory> factory;
nsresult rv = nsComponentManagerImpl::gComponentManager->GetClassObject(
kNS_TESTDHCPCLIENTSERVICE_CID, NS_GET_IID(nsIFactory),
getter_AddRefs(factory));
if (NS_SUCCEEDED(rv) && factory) {
rv = nsComponentManagerImpl::gComponentManager->UnregisterFactory(
kNS_TESTDHCPCLIENTSERVICE_CID, factory);
ASSERT_EQ(NS_OK, rv);
}
nsComponentManagerImpl::gComponentManager->RegisterModule(

Просмотреть файл

@ -36,4 +36,4 @@ FINAL_LIBRARY = 'xul-gtest'
LOCAL_INCLUDES += [
'!/xpcom',
'/xpcom/components'
]
]

Просмотреть файл

@ -0,0 +1,228 @@
/* -*- 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/. */
#include "StaticComponents.h"
#include "mozilla/PerfectHash.h"
#include "mozilla/ResultExtensions.h"
#include "mozilla/StaticPtr.h"
#include "nsCOMPtr.h"
#include "nsComponentManager.h"
#include "nsIFactory.h"
#include "nsISupports.h"
#include "nsString.h"
// Cleanup pollution from zipstruct.h
#undef UNSUPPORTED
// Public includes
//# @includes@
// Relative includes
//# @relative_includes@
//# @decls@
namespace mozilla {
namespace xpcom {
namespace {
// Template helpers for constructor function sanity checks.
template <typename T>
struct RemoveAlreadyAddRefed {
using Type = T;
};
template <typename T>
struct RemoveAlreadyAddRefed<already_AddRefed<T>> {
using Type = T;
};
} // anonymous namespace
uint8_t gInvalidContracts[kContractCount / 8 + 1];
static StaticRefPtr<nsISupports> gServiceInstances[kStaticModuleCount];
uint8_t gInitCalled[kModuleInitCount / 8 + 1];
static const char gStrings[] =
//# @strings@
"";
const StaticCategory gStaticCategories[kStaticCategoryCount] = {
//# @categories@
};
const StaticCategoryEntry gStaticCategoryEntries[] = {
//# @category_entries@
};
/**
* Returns a nsCString corresponding to the given entry in the `gStrings` string
* table. The resulting nsCString points directly to static storage, and does
* not incur any memory allocation overhead.
*/
static inline nsCString GetString(const StringOffset& aOffset) {
const char* str = &gStrings[aOffset.mOffset];
nsCString result;
result.AssignLiteral(str, strlen(str));
return result;
}
nsCString ContractEntry::ContractID() const {
return GetString(mContractID);
}
bool ContractEntry::Matches(const nsACString& aContractID) const {
return aContractID == ContractID() && Module().Active();
}
//# @module_cid_table@
//# @module_contract_id_table@
static inline bool CalledInit(size_t aIdx) {
return GetBit(gInitCalled, aIdx);
}
static nsresult CallInitFunc(size_t aIdx) {
if (CalledInit(aIdx)) {
return NS_OK;
}
nsresult rv = NS_OK;
switch (aIdx) {
//# @init_funcs@
}
SetBit(gInitCalled, aIdx);
MOZ_ASSERT(NS_SUCCEEDED(rv));
return rv;
}
static void CallUnloadFuncs() {
//# @unload_funcs@
}
static nsresult CreateInstanceImpl(ModuleID aID, nsISupports* aOuter,
const nsIID& aIID, void** aResult) {
if (aOuter) {
return NS_ERROR_NO_AGGREGATION;
}
// The full set of constructors for all static modules.
// This switch statement will be compiled to a relative address jump table
// with no runtime relocations and a single indirect jump.
switch (aID) {
//# @constructors@
}
MOZ_ASSERT_UNREACHABLE("Constructor didn't return");
return NS_ERROR_FAILURE;
}
namespace {
class StaticModuleFactory final : public nsIFactory {
NS_DECL_ISUPPORTS
NS_DECL_NSIFACTORY
explicit StaticModuleFactory(ModuleID aID) : mID(aID) {}
private:
~StaticModuleFactory() = default;
const ModuleID mID;
};
NS_IMPL_ISUPPORTS(StaticModuleFactory, nsIFactory)
NS_IMETHODIMP StaticModuleFactory::CreateInstance(nsISupports* aOuter,
const nsIID& aIID,
void** aResult) {
return CreateInstanceImpl(mID, aOuter, aIID, aResult);
}
NS_IMETHODIMP StaticModuleFactory::LockFactory(bool aLock) {
MOZ_CRASH("LockFactory is no longer a thing");
return NS_ERROR_NOT_IMPLEMENTED;
}
} // anonymous namespace
already_AddRefed<nsIFactory> StaticModule::GetFactory() const {
return do_AddRef(new StaticModuleFactory(ID()));
}
bool StaticModule::Active() const {
return FastProcessSelectorMatches(mProcessSelector);
}
nsresult StaticModule::CreateInstance(nsISupports* aOuter, const nsIID& aIID,
void** aResult) const {
return CreateInstanceImpl(ID(), aOuter, aIID, aResult);
}
nsISupports* StaticModule::ServiceInstance() const {
return gServiceInstances[Idx()];
}
void StaticModule::SetServiceInstance(
already_AddRefed<nsISupports> aInst) const {
gServiceInstances[Idx()] = aInst;
}
nsCString StaticCategoryEntry::Entry() const {
return GetString(mEntry);
}
nsCString StaticCategoryEntry::Value() const {
return GetString(mValue);
}
bool StaticCategoryEntry::Active() const {
return FastProcessSelectorMatches(mProcessSelector);
}
nsCString StaticCategory::Name() const {
return GetString(mName);
}
/* static */ const StaticModule* StaticComponents::LookupByCID(
const nsID& aCID) {
return ModuleByCID(aCID);
}
/* static */ const StaticModule* StaticComponents::LookupByContractID(
const nsACString& aContractID) {
if (const ContractEntry* entry = LookupContractID(aContractID)) {
if (!entry->Invalid()) {
return &entry->Module();
}
}
return nullptr;
}
/* static */ bool StaticComponents::InvalidateContractID(
const nsACString& aContractID, bool aInvalid) {
if (const ContractEntry* entry = LookupContractID(aContractID)) {
entry->SetInvalid(aInvalid);
return true;
}
return false;
}
/* static */ void StaticComponents::Shutdown() {
CallUnloadFuncs();
}
} // namespace xpcom
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,203 @@
/* -*- 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/. */
#ifndef StaticComponents_h
#define StaticComponents_h
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/Module.h"
#include "nsID.h"
#include "nsStringFwd.h"
#include "nscore.h"
#include "mozilla/Components.h"
#include "StaticComponentData.h"
class nsIFactory;
class nsISupports;
namespace mozilla {
namespace xpcom {
struct ContractEntry;
struct StaticModule;
struct StaticCategoryEntry;
struct StaticCategory;
extern const StaticModule gStaticModules[kStaticModuleCount];
extern const ContractEntry gContractEntries[kContractCount];
extern uint8_t gInvalidContracts[kContractCount / 8 + 1];
extern const StaticCategory gStaticCategories[kStaticCategoryCount];
extern const StaticCategoryEntry gStaticCategoryEntries[];
template <size_t N>
static inline bool GetBit(const uint8_t (&aBits)[N], size_t aBit) {
static constexpr size_t width = sizeof(aBits[0]) * 8;
size_t idx = aBit / width;
MOZ_ASSERT(idx < N);
return aBits[idx] & (1 << (aBit % width));
}
template <size_t N>
static inline void SetBit(uint8_t (&aBits)[N], size_t aBit,
bool aValue = true) {
static constexpr size_t width = sizeof(aBits[0]) * 8;
size_t idx = aBit / width;
MOZ_ASSERT(idx < N);
if (aValue) {
aBits[idx] |= 1 << (aBit % width);
} else {
aBits[idx] &= ~(1 << (aBit % width));
}
}
/**
* Represents a string entry in the static string table. Can be converted to a
* nsCString using GetString() in StaticComponents.cpp.
*
* This is a struct rather than a pure offset primarily for the purposes of type
* safety, but also so that it can easily be extended to include a static length
* in the future, if efficiency concerns warrant it.
*/
struct StringOffset final {
uint32_t mOffset;
};
/**
* Represents a static component entry defined in a `Classes` list in an XPCOM
* manifest. Handles creating instances of and caching service instances for
* that class.
*/
struct StaticModule {
nsID mCID;
Module::ProcessSelector mProcessSelector;
const nsID& CID() const { return mCID; }
ModuleID ID() const { return ModuleID(this - gStaticModules); }
/**
* Returns this entry's index in the gStaticModules array.
*/
size_t Idx() const { return size_t(ID()); }
/**
* Returns true if this entry is active. Typically this will only return false
* if the entry's process selector does not match this process.
*/
bool Active() const;
already_AddRefed<nsIFactory> GetFactory() const;
nsresult CreateInstance(nsISupports* aOuter, const nsIID& aIID,
void** aResult) const;
nsISupports* ServiceInstance() const;
void SetServiceInstance(already_AddRefed<nsISupports> aInst) const;
};
/**
* Represents a static mapping between a contract ID string and a StaticModule
* entry.
*/
struct ContractEntry final {
StringOffset mContractID;
ModuleID mModuleID;
size_t Idx() const { return this - gContractEntries; }
nsCString ContractID() const;
const StaticModule& Module() const {
return gStaticModules[size_t(mModuleID)];
}
/**
* Returns true if this entry's underlying module is active, and its contract
* ID matches the given contract ID string. This is used by the PerfectHash
* function to determine whether to return a result for this entry.
*/
bool Matches(const nsACString& aContractID) const;
/**
* Returns true if this entry has been invalidated, and should be ignored.
*
* Contract IDs may be overwritten at runtime. When that happens for a static
* contract ID, we mark its entry invalid, and ignore it thereafter.
*/
bool Invalid() const { return GetBit(gInvalidContracts, Idx()); }
/**
* Marks this entry invalid (or unsets the invalid bit if aInvalid is false),
* after which it will be ignored in contract ID lookup attempts. See
* `Invalid()` above.
*/
void SetInvalid(bool aInvalid = true) const {
return SetBit(gInvalidContracts, Idx(), aInvalid);
}
};
/**
* Represents a declared category manager entry declared in an XPCOM manifest.
*
* The entire set of static category entries is read at startup and loaded into
* the category manager's dynamic hash tables, so there is memory and
* initialization overhead for each entry in these tables. This may be further
* optimized in the future to reduce some of that overhead.
*/
struct StaticCategoryEntry final {
StringOffset mEntry;
StringOffset mValue;
Module::ProcessSelector mProcessSelector;
nsCString Entry() const;
nsCString Value() const;
bool Active() const;
};
struct StaticCategory final {
StringOffset mName;
uint16_t mStart;
uint16_t mCount;
nsCString Name() const;
const StaticCategoryEntry* begin() const {
return &gStaticCategoryEntries[mStart];
}
const StaticCategoryEntry* end() const {
return &gStaticCategoryEntries[mStart + mCount];
}
};
class StaticComponents final {
public:
static const StaticModule* LookupByCID(const nsID& aCID);
static const StaticModule* LookupByContractID(const nsACString& aContractID);
/**
* Marks a static contract ID entry invalid (or unsets the invalid bit if
* aInvalid is false). See `CategoryEntry::Invalid()`.
*/
static bool InvalidateContractID(const nsACString& aContractID,
bool aInvalid = true);
/**
* Calls any module unload from manifests whose components have been loaded.
*/
static void Shutdown();
};
} // namespace xpcom
} // namespace mozilla
#endif // defined StaticComponents_h

Просмотреть файл

@ -0,0 +1,674 @@
import json
import os
import re
import struct
from collections import defaultdict
from uuid import UUID
from mozbuild.util import FileAvoidWrite
from perfecthash import PerfectHash
import buildconfig
PHF_SIZE = 512
ENDIAN = '<' if buildconfig.substs['TARGET_ENDIANNESS'] == 'little' else '>'
# Represents a UUID in the format used internally by Gecko, and supports
# serializing it in that format to both C++ source and raw byte arrays.
class UUIDRepr(object):
def __init__(self, uuid):
self.uuid = uuid
fields = uuid.fields
self.a = fields[0]
self.b = fields[1]
self.c = fields[2]
d = list(fields[3:5])
for i in range(0, 6):
d.append(fields[5] >> (8 * (5 - i)) & 0xff)
self.d = tuple(d)
def __str__(self):
return str(self.uuid)
@property
def bytes(self):
return struct.pack(ENDIAN + 'IHHBBBBBBBB',
self.a, self.b, self.c, *self.d)
def to_cxx(self):
rest = ', '.join('0x%02x' % b for b in self.d)
return '{ 0x%x, 0x%x, 0x%x, { %s } }' % (self.a, self.b, self.c,
rest)
# Corresponds to the Module::ProcessSelector enum in Module.h. The actual
# values don't matter, since the code generator emits symbolic constants for
# these values, but we use the same values as the enum constants for clarity.
class ProcessSelector:
ANY_PROCESS = 0x0
MAIN_PROCESS_ONLY = 0x1
CONTENT_PROCESS_ONLY = 0x2
ALLOW_IN_GPU_PROCESS = 0x4
ALLOW_IN_VR_PROCESS = 0x8
ALLOW_IN_SOCKET_PROCESS = 0x10
ALLOW_IN_GPU_AND_SOCKET_PROCESS = (ALLOW_IN_GPU_PROCESS |
ALLOW_IN_SOCKET_PROCESS)
ALLOW_IN_GPU_AND_VR_PROCESS = ALLOW_IN_GPU_PROCESS | ALLOW_IN_VR_PROCESS
ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS = (ALLOW_IN_GPU_PROCESS |
ALLOW_IN_VR_PROCESS |
ALLOW_IN_SOCKET_PROCESS)
# Maps ProcessSelector constants to the name of the corresponding
# Module::ProcessSelector enum value.
PROCESSES = {
ProcessSelector.ANY_PROCESS: 'ANY_PROCESS',
ProcessSelector.MAIN_PROCESS_ONLY: 'MAIN_PROCESS_ONLY',
ProcessSelector.CONTENT_PROCESS_ONLY: 'CONTENT_PROCESS_ONLY',
ProcessSelector.ALLOW_IN_GPU_PROCESS: 'ALLOW_IN_GPU_PROCESS',
ProcessSelector.ALLOW_IN_VR_PROCESS: 'ALLOW_IN_VR_PROCESS',
ProcessSelector.ALLOW_IN_SOCKET_PROCESS: 'ALLOW_IN_SOCKET_PROCESS',
ProcessSelector.ALLOW_IN_GPU_AND_SOCKET_PROCESS: 'ALLOW_IN_GPU_AND_SOCKET_PROCESS',
ProcessSelector.ALLOW_IN_GPU_AND_VR_PROCESS: 'ALLOW_IN_GPU_AND_VR_PROCESS',
ProcessSelector.ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS: 'ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS',
}
# Emits the C++ symbolic constant corresponding to a ProcessSelector constant.
def lower_processes(processes):
return 'Module::ProcessSelector::%s' % PROCESSES[processes]
# Emits the C++ symbolic constant for a ModuleEntry's ModuleID enum entry.
def lower_module_id(module):
return 'ModuleID::%s' % module.name
# Represents a static string table, indexed by offset. This allows us to
# reference strings from static data structures without requiring runtime
# relocations.
class StringTable(object):
def __init__(self):
self.entries = {}
self.entry_list = []
self.size = 0
self._serialized = False
# Returns the index of the given string in the `entry_list` array. If
# no entry for the string exists, it first creates one.
def get_idx(self, string):
idx = self.entries.get(string, None)
if idx is not None:
return idx
assert not self._serialized
assert len(string) == len(string.encode('utf-8'))
idx = self.size
self.size += len(string) + 1
self.entries[string] = idx
self.entry_list.append(string)
return idx
# Returns the C++ code representing string data of this string table, as a
# single string literal. This must only be called after the last call to
# `get_idx()` or `entry_to_cxx()` for this instance.
def to_cxx(self):
self._serialized = True
lines = []
idx = 0
for entry in self.entry_list:
str_ = entry.replace('\\', '\\\\').replace('"', r'\"') \
.replace('\n', r'\n')
lines.append(' /* 0x%x */ "%s\\0"\n' % (idx, str_))
idx += len(entry) + 1
return ''.join(lines)
# Returns a `StringEntry` struct initializer for the string table entry
# corresponding to the given string. If no matching entry exists, it is
# first created.
def entry_to_cxx(self, string):
idx = self.get_idx(string)
return '{ 0x%x } /* %s */' % (
idx,
pretty_string(string))
strings = StringTable()
# Represents a C++ namespace, containing a set of classes and potentially
# sub-namespaces. This is used to generate pre-declarations for incomplete
# types referenced in XPCOM manifests.
class Namespace(object):
def __init__(self, name=None):
self.name = name
self.classes = set()
self.namespaces = {}
# Returns a Namespace object for the sub-namespace with the given name.
def sub(self, name):
assert name not in self.classes
if name not in self.namespaces:
self.namespaces[name] = Namespace(name)
return self.namespaces[name]
# Generates C++ code to pre-declare all classes in this namespace and all
# of its sub-namespaces.
def to_cxx(self):
res = ""
if self.name:
res += 'namespace %s {\n' % self.name
for clas in sorted(self.classes):
res += 'class %s;\n' % clas
for ns in sorted(self.namespaces.keys()):
res += self.namespaces[ns].to_cxx()
if self.name:
res += '} // namespace %s\n' % self.name
return res
# Represents a component defined in an XPCOM manifest's `Classes` array.
class ModuleEntry(object):
next_anon_id = 0
def __init__(self, data, init_idx):
self.cid = UUIDRepr(UUID(data['cid']))
self.contract_ids = data.get('contract_ids', [])
self.type = data.get('type', 'nsISupports')
self.categories = data.get('categories', {})
self.processes = data.get('processes', 0)
self.headers = data.get('headers', [])
# If the manifest declares Init or Unload functions, this contains its
# index, as understood by the `CallInitFunc()` function.
#
# If it contains any value other than `None`, a corresponding
# `CallInitFunc(init_idx)` call will be genrated before calling this
# module's constructor.
self.init_idx = init_idx
self.constructor = data.get('constructor', None)
self.legacy_constructor = data.get('legacy_constructor', None)
self.init_method = data.get('init_method', [])
self.external = data.get('external', not (self.headers or
self.legacy_constructor))
self.singleton = data.get('singleton', False)
if 'name' in data:
self.anonymous = False
self.name = data['name']
else:
self.anonymous = True
self.name = 'Anonymous%03d' % ModuleEntry.next_anon_id
ModuleEntry.next_anon_id += 1
def error(str_):
raise Exception("Error defining component %s (%s): %s" % (
str(self.cid), ', '.join(map(repr, self.contract_ids)),
str_))
if self.external:
if self.constructor or self.legacy_constructor:
error("Externally-constructed components may not specify "
"'constructor' or 'legacy_constructor' properties")
if self.init_method:
error("Externally-constructed components may not specify "
"'init_method' properties")
if self.type == 'nsISupports':
error("Externally-constructed components must specify a type "
"other than nsISupports")
if self.constructor and self.legacy_constructor:
error("The 'constructor' and 'legacy_constructor' properties "
"are mutually exclusive")
@property
def contract_id(self):
return self.contract_ids[0]
# Generates the C++ code for a StaticModule struct initializer
# representing this component.
def to_cxx(self):
return """
/* {name} */ {{
/* {{{cid_string}}} */
{cid},
{processes},
}}""".format(name=self.name, cid=self.cid.to_cxx(),
cid_string=str(self.cid),
processes=lower_processes(self.processes))
# Generates the C++ code necessary to construct an instance of this
# component.
#
# This code lives in a function with the following arguments:
#
# - aIID: The `const nsIID&` interface ID that the resulting instance
# will be queried to.
#
# - aResult: The `void**` pointer in which to store the result.
#
# And which returns an `nsresult` indicating success or failure.
def lower_constructor(self):
res = ''
if self.init_idx is not None:
res += ' MOZ_TRY(CallInitFunc(%d));\n' % self.init_idx
if self.legacy_constructor:
res += (' return /* legacy */ %s(nullptr, aIID, aResult);\n'
% self.legacy_constructor)
return res
if self.external:
res += (' nsCOMPtr<nsISupports> inst = '
'mozCreateComponent<%s>();\n' % self.type)
else:
res += ' RefPtr<%s> inst = ' % self.type
if not self.constructor:
res += 'new %s();\n' % self.type
else:
res += '%s();\n' % self.constructor
# The `new` operator is infallible, so we don't need to worry
# about it returning null, but custom constructors may, so
# check before calling any methods.
res += ' NS_ENSURE_TRUE(inst, NS_ERROR_OUT_OF_MEMORY);\n'
# Check that the constructor function returns an appropriate
# `already_AddRefed` value for our declared type.
res += """
using T =
RemoveAlreadyAddRefed<decltype(%(constructor)s())>::Type;
static_assert(
mozilla::IsSame<already_AddRefed<T>, decltype(%(constructor)s())>::value,
"Singleton constructor must return already_AddRefed");
static_assert(
mozilla::IsBaseOf<%(type)s, T>::value,
"Singleton constructor must return correct already_AddRefed");
""" % {'type': self.type, 'constructor': self.constructor}
if self.init_method:
res += ' MOZ_TRY(inst->%s());\n' % self.init_method
res += ' return inst->QueryInterface(aIID, aResult);\n'
return res
# Returns a quoted string literal representing the given raw string, with
# certain special characters replaced so that it can be used in a C++-style
# (/* ... */) comment.
def pretty_string(string):
return (json.dumps(string).replace('*/', r'*\/')
.replace('/*', r'/\*'))
# Represents a static contract ID entry, corresponding to a C++ ContractEntry
# struct, mapping a contract ID to a static module entry.
class ContractEntry(object):
def __init__(self, contract, module):
self.contract = contract
self.module = module
def to_cxx(self):
return """
{{
{contract},
{module_id},
}}""".format(contract=strings.entry_to_cxx(self.contract),
module_id=lower_module_id(self.module))
# Generates the C++ code for the StaticCategoryEntry and StaticCategory
# structs for all category entries declared in XPCOM manifests.
def gen_categories(substs, categories):
cats = []
ents = []
count = 0
for category, entries in sorted(categories.items()):
entries.sort()
cats.append(' { %s,\n'
' %d, %d },\n'
% (strings.entry_to_cxx(category),
count, len(entries)))
count += len(entries)
ents.append(' /* %s */\n' % pretty_string(category))
for entry, value, processes in entries:
ents.append(' { %s,\n'
' %s,\n'
' %s },\n'
% (strings.entry_to_cxx(entry),
strings.entry_to_cxx(value),
lower_processes(processes)))
ents.append('\n')
ents.pop()
substs['category_count'] = len(cats)
substs['categories'] = ''.join(cats)
substs['category_entries'] = ''.join(ents)
# Generates the C++ code for all Init and Unload functions declared in XPCOM
# manifests. These form the bodies of the `CallInitFunc()` and `CallUnload`
# functions in StaticComponents.cpp.
def gen_module_funcs(substs, funcs):
inits = []
unloads = []
template = """\
case %d:
%s
break;
"""
for i, (init, unload) in enumerate(funcs):
init_code = '%s();' % init if init else '/* empty */'
inits.append(template % (i, init_code))
if unload:
unloads.append("""\
if (CalledInit(%d)) {
%s();
}
""" % (i, unload))
substs['init_funcs'] = ''.join(inits)
substs['unload_funcs'] = ''.join(unloads)
substs['init_count'] = len(funcs)
# Generates class pre-declarations for any types referenced in `Classes` array
# entries which do not have corresponding `headers` entries to fully declare
# their types.
def gen_decls(types):
root_ns = Namespace()
for type_ in sorted(types):
parts = type_.split('::')
ns = root_ns
for part in parts[:-1]:
ns = ns.sub(part)
ns.classes.add(parts[-1])
return root_ns.to_cxx()
# Generates the `switch` body for the `CreateInstanceImpl()` function, with a
# `case` for each value in ModuleID to construct an instance of the
# corresponding component.
def gen_constructors(entries):
constructors = []
for entry in entries:
constructors.append("""\
case {id}: {{
{constructor}\
}}
""".format(id=lower_module_id(entry),
constructor=entry.lower_constructor()))
return ''.join(constructors)
def gen_includes(substs, all_headers):
headers = set()
absolute_headers = set()
for header in all_headers:
if header.startswith('/'):
absolute_headers.add(header)
else:
headers.add(header)
includes = ['#include "%s"' % header for header in sorted(headers)]
substs['includes'] = '\n'.join(includes) + '\n'
relative_includes = ['#include "../..%s"' % header
for header in sorted(absolute_headers)]
substs['relative_includes'] = '\n'.join(relative_includes) + '\n'
def to_list(val):
if isinstance(val, (list, tuple)):
return val
return val,
def gen_substs(manifests):
module_funcs = []
headers = set()
modules = []
for manifest in manifests:
headers |= set(manifest.get('Headers', []))
init_idx = None
init = manifest.get('InitFunc')
unload = manifest.get('UnloadFunc')
if init or unload:
init_idx = len(module_funcs)
module_funcs.append((init, unload))
for clas in manifest['Classes']:
modules.append(ModuleEntry(clas, init_idx))
contracts = []
contract_map = {}
categories = defaultdict(list)
types = set()
for mod in modules:
headers |= set(mod.headers)
for contract_id in mod.contract_ids:
if contract_id in contract_map:
raise Exception('Duplicate contract ID: %s' % contract_id)
entry = ContractEntry(contract_id, mod)
contracts.append(entry)
contract_map[contract_id] = entry
for category, entries in mod.categories.items():
for entry in to_list(entries):
categories[category].append((entry, mod.contract_id,
mod.processes))
if mod.type and not mod.headers:
types.add(mod.type)
cid_phf = PerfectHash(modules, PHF_SIZE,
key=lambda module: module.cid.bytes)
contract_phf = PerfectHash(contracts, PHF_SIZE,
key=lambda entry: entry.contract)
substs = {}
gen_categories(substs, categories)
substs['module_ids'] = ''.join(' %s,\n' % entry.name
for entry in cid_phf.entries)
substs['module_count'] = len(modules)
substs['contract_count'] = len(contracts)
gen_module_funcs(substs, module_funcs)
gen_includes(substs, headers)
substs['decls'] = gen_decls(types)
substs['constructors'] = gen_constructors(cid_phf.entries)
substs['module_cid_table'] = cid_phf.cxx_codegen(
name='ModuleByCID',
entry_type='StaticModule',
entries_name='gStaticModules',
lower_entry=lambda entry: entry.to_cxx(),
return_type='const StaticModule*',
return_entry=('return entry.CID().Equals(aKey) && entry.Active()'
' ? &entry : nullptr;'),
key_type='const nsID&',
key_bytes='reinterpret_cast<const char*>(&aKey)',
key_length='sizeof(nsID)')
substs['module_contract_id_table'] = contract_phf.cxx_codegen(
name='LookupContractID',
entry_type='ContractEntry',
entries_name='gContractEntries',
lower_entry=lambda entry: entry.to_cxx(),
return_type='const ContractEntry*',
return_entry='return entry.Matches(aKey) ? &entry : nullptr;',
key_type='const nsACString&',
key_bytes='aKey.BeginReading()',
key_length='aKey.Length()')
# Do this only after everything else has been emitted so we're sure the
# string table is complete.
substs['strings'] = strings.to_cxx()
return substs
# Returns true if the given build config substitution is defined and truthy.
def defined(subst):
return bool(buildconfig.substs.get(subst))
def read_manifest(filename):
glbl = {'buildconfig': buildconfig,
'defined': defined,
'ProcessSelector': ProcessSelector}
execfile(filename, glbl)
return glbl
def main(fd, conf_file, template_file):
def open_output(filename):
return FileAvoidWrite(os.path.join(os.path.dirname(fd.name), filename))
conf = json.load(open(conf_file, 'r'))
deps = set()
manifests = []
for filename in conf['manifests']:
deps.add(filename)
manifest = read_manifest(filename)
manifests.append(manifest)
manifest.setdefault('Priority', 50)
manifest['__filename__'] = filename
manifests.sort(key=lambda man: (man['Priority'], man['__filename__']))
substs = gen_substs(manifests)
def replacer(match):
return substs[match.group(1)]
with open_output('StaticComponents.cpp') as fh:
with open(template_file, 'r') as tfh:
template = tfh.read()
fh.write(re.sub(r'//# @([a-zA-Z_]+)@\n', replacer, template))
with open_output('StaticComponentData.h') as fh:
fh.write("""\
/* -*- 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/. */
#ifndef StaticComponentData_h
#define StaticComponentData_h
namespace mozilla {
namespace xpcom {
static constexpr size_t kStaticModuleCount = %(module_count)d;
static constexpr size_t kContractCount = %(contract_count)d;
static constexpr size_t kStaticCategoryCount = %(category_count)d;
static constexpr size_t kModuleInitCount = %(init_count)d;
} // namespace xpcom
} // namespace mozilla
#endif
""" % substs)
fd.write("""\
/* -*- 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/. */
#ifndef mozilla_Components_h
#define mozilla_Components_h
#include "nsCOMPtr.h"
struct nsID;
#define NS_IMPL_COMPONENT_FACTORY(iface) \\
template <> \\
already_AddRefed<nsISupports> mozCreateComponent<iface>()
template <typename T>
already_AddRefed<nsISupports> mozCreateComponent();
namespace mozilla {
namespace xpcom {
enum class ModuleID : uint16_t {
%(module_ids)s
};
} // namespace xpcom
#endif
""" % substs)
return deps

Просмотреть файл

@ -29,6 +29,20 @@ EXPORTS.mozilla += [
'ModuleUtils.h',
]
if CONFIG['COMPILE_ENVIRONMENT']:
EXPORTS.mozilla += [
'!Components.h',
]
generated = ('Components.h', 'StaticComponentData.h',
'StaticComponents.cpp')
GENERATED_FILES += [generated]
gen = GENERATED_FILES[generated]
gen.script = 'gen_static_components.py'
gen.inputs += ['!manifest-lists.json', 'StaticComponents.cpp.in']
UNIFIED_SOURCES += [
'GenericFactory.cpp',
'ManifestParser.cpp',
@ -38,6 +52,10 @@ UNIFIED_SOURCES += [
'nsComponentManagerUtils.cpp',
]
SOURCES += [
'!StaticComponents.cpp',
]
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
@ -53,3 +71,5 @@ LOCAL_INCLUDES += [
if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
CXXFLAGS += CONFIG['TK_CFLAGS']
include('/ipc/chromium/chromium-config.mozbuild')

Просмотреть файл

@ -10,6 +10,8 @@
#include "nspr.h"
#include "nsCRT.h" // for atoll
#include "StaticComponents.h"
#include "nsCategoryManager.h"
#include "nsCOMPtr.h"
#include "nsComponentManager.h"
@ -53,6 +55,7 @@
#include "mozilla/ScopeExit.h"
#include "mozilla/URLPreloader.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Variant.h"
#include "nsDataHashtable.h"
#include <new> // for placement new
@ -157,12 +160,90 @@ bool FastProcessSelectorMatches(ProcessSelector aSelector) {
} // namespace xpcom
} // namespace mozilla
namespace {
/**
* A wrapper simple wrapper class, which can hold either a dynamic
* nsFactoryEntry instance, or a static StaticModule entry, and transparently
* forwards method calls to the wrapped object.
*
* This allows the same code to work with either static or dynamic modules
* without caring about the difference.
*/
class MOZ_STACK_CLASS EntryWrapper final {
public:
explicit EntryWrapper(nsFactoryEntry* aEntry) : mEntry(aEntry) {}
explicit EntryWrapper(const StaticModule* aEntry) : mEntry(aEntry) {}
#define MATCH(type, ifFactory, ifStatic) \
struct Matcher { \
type match(nsFactoryEntry* entry) { ifFactory; } \
type match(const StaticModule* entry) { ifStatic; } \
}; \
return mEntry.match((Matcher()))
const nsID& CID() {
MATCH(const nsID&, return *entry->mCIDEntry->cid, return entry->CID());
}
already_AddRefed<nsIFactory> GetFactory() {
MATCH(already_AddRefed<nsIFactory>, return entry->GetFactory(),
return entry->GetFactory());
}
/**
* Creates an instance of the underlying component. This should be used in
* preference to GetFactory()->CreateInstance() where appropriate, since it
* side-steps the necessity of creating a nsIFactory instance for static
* modules.
*/
nsresult CreateInstance(nsISupports* aOuter, const nsIID& aIID,
void** aResult) {
if (mEntry.is<nsFactoryEntry*>()) {
return mEntry.as<nsFactoryEntry*>()->CreateInstance(aOuter, aIID,
aResult);
}
return mEntry.as<const StaticModule*>()->CreateInstance(aOuter, aIID,
aResult);
}
/**
* Returns the cached service instance for this entry, if any. This should
* only be accessed while mLock is held.
*/
nsISupports* ServiceInstance() {
MATCH(nsISupports*, return entry->mServiceObject,
return entry->ServiceInstance());
}
void SetServiceInstance(already_AddRefed<nsISupports> aInst) {
if (mEntry.is<nsFactoryEntry*>()) {
mEntry.as<nsFactoryEntry*>()->mServiceObject = aInst;
} else {
return mEntry.as<const StaticModule*>()->SetServiceInstance(
std::move(aInst));
}
}
/**
* Returns the description string for the module this entry belongs to. For
* static entries, always returns "<unknown module>".
*/
nsCString ModuleDescription() {
MATCH(nsCString,
return entry->mModule ? entry->mModule->Description()
: NS_LITERAL_CSTRING("<unknown module>"),
return NS_LITERAL_CSTRING("<unknown module>"));
}
private:
Variant<nsFactoryEntry*, const StaticModule*> mEntry;
};
// GetService and a few other functions need to exit their mutex mid-function
// without reentering it later in the block. This class supports that
// style of early-exit that MutexAutoUnlock doesn't.
namespace {
class MOZ_STACK_CLASS MutexLock {
public:
explicit MutexLock(SafeMutex& aMutex) : mMutex(aMutex), mLocked(false) {
@ -244,7 +325,7 @@ nsresult nsComponentManagerImpl::Create(nsISupports* aOuter, REFNSIID aIID,
return gComponentManager->QueryInterface(aIID, aResult);
}
static const int CONTRACTID_HASHTABLE_INITIAL_LENGTH = 1024;
static const int CONTRACTID_HASHTABLE_INITIAL_LENGTH = 512;
nsComponentManagerImpl::nsComponentManagerImpl()
: mFactories(CONTRACTID_HASHTABLE_INITIAL_LENGTH),
@ -398,6 +479,15 @@ nsresult nsComponentManagerImpl::Init() {
RegisterModule((*sExtraStaticModules)[i]);
}
auto* catMan = nsCategoryManager::GetSingleton();
for (const auto& cat : gStaticCategories) {
for (const auto& entry : cat) {
if (entry.Active()) {
catMan->AddCategoryEntry(cat.Name(), entry.Entry(), entry.Value());
}
}
}
bool loadChromeManifests;
switch (XRE_GetProcessType()) {
// We are going to assume that only a select few (see below) process types
@ -495,6 +585,10 @@ nsresult nsComponentManagerImpl::Init() {
mStatus = NORMAL;
MOZ_ASSERT(!XRE_IsContentProcess() ||
mFactories.Count() > CONTRACTID_HASHTABLE_INITIAL_LENGTH / 3,
"Initial component hashtable size is too large");
return NS_OK;
}
@ -705,17 +799,11 @@ void nsComponentManagerImpl::ManifestComponent(ManifestProcessingContext& aCx,
fl.GetURIString(hash);
MutexLock lock(mLock);
nsFactoryEntry* f = mFactories.Get(&cid);
if (f) {
if (Maybe<EntryWrapper> f = LookupByCID(lock, cid)) {
char idstr[NSID_LENGTH];
cid.ToProvidedString(idstr);
nsCString existing;
if (f->mModule) {
existing = f->mModule->Description();
} else {
existing = "<unknown module>";
}
nsCString existing(f->ModuleDescription());
lock.Unlock();
@ -855,6 +943,8 @@ nsresult nsComponentManagerImpl::Shutdown(void) {
mKnownModules.Clear();
mKnownStaticModules.Clear();
StaticComponents::Shutdown();
delete sExtraStaticModules;
delete sModuleLocations;
@ -891,20 +981,46 @@ nsresult nsComponentManagerImpl::GetInterface(const nsIID& aUuid,
return QueryInterface(aUuid, aResult);
}
nsFactoryEntry* nsComponentManagerImpl::GetFactoryEntry(
const char* aContractID, uint32_t aContractIDLen) {
SafeMutexAutoLock lock(mLock);
return mContractIDs.Get(nsDependentCString(aContractID, aContractIDLen));
Maybe<EntryWrapper> nsComponentManagerImpl::LookupByCID(const nsID& aCID) {
return LookupByCID(MutexLock(mLock), aCID);
}
nsFactoryEntry* nsComponentManagerImpl::GetFactoryEntry(const nsCID& aClass) {
SafeMutexAutoLock lock(mLock);
return mFactories.Get(&aClass);
Maybe<EntryWrapper> nsComponentManagerImpl::LookupByCID(const MutexLock&,
const nsID& aCID) {
if (const StaticModule* module = StaticComponents::LookupByCID(aCID)) {
return Some(EntryWrapper(module));
}
if (nsFactoryEntry* entry = mFactories.Get(&aCID)) {
return Some(EntryWrapper(entry));
}
return Nothing();
}
Maybe<EntryWrapper> nsComponentManagerImpl::LookupByContractID(
const nsACString& aContractID) {
return LookupByContractID(MutexLock(mLock), aContractID);
}
Maybe<EntryWrapper> nsComponentManagerImpl::LookupByContractID(
const MutexLock&, const nsACString& aContractID) {
if (const StaticModule* module =
StaticComponents::LookupByContractID(aContractID)) {
return Some(EntryWrapper(module));
}
if (nsFactoryEntry* entry = mContractIDs.Get(aContractID)) {
// UnregisterFactory might have left a stale nsFactoryEntry in
// mContractIDs, so we should check to see whether this entry has
// anything useful.
if (entry->mModule || entry->mFactory || entry->mServiceObject) {
return Some(EntryWrapper(entry));
}
}
return Nothing();
}
already_AddRefed<nsIFactory> nsComponentManagerImpl::FindFactory(
const nsCID& aClass) {
nsFactoryEntry* e = GetFactoryEntry(aClass);
Maybe<EntryWrapper> e = LookupByCID(aClass);
if (!e) {
return nullptr;
}
@ -914,7 +1030,8 @@ already_AddRefed<nsIFactory> nsComponentManagerImpl::FindFactory(
already_AddRefed<nsIFactory> nsComponentManagerImpl::FindFactory(
const char* aContractID, uint32_t aContractIDLen) {
nsFactoryEntry* entry = GetFactoryEntry(aContractID, aContractIDLen);
Maybe<EntryWrapper> entry =
LookupByContractID(nsDependentCString(aContractID, aContractIDLen));
if (!entry) {
return nullptr;
}
@ -1017,16 +1134,16 @@ nsComponentManagerImpl::CreateInstance(const nsCID& aClass,
}
*aResult = nullptr;
nsFactoryEntry* entry = GetFactoryEntry(aClass);
Maybe<EntryWrapper> entry = LookupByCID(aClass);
if (!entry) {
return NS_ERROR_FACTORY_NOT_REGISTERED;
}
#ifdef SHOW_CI_ON_EXISTING_SERVICE
if (entry->mServiceObject) {
char cid[NSID_LENGTH];
aClass.ToProvidedString(cid);
char cid[NSID_LENGTH];
aClass.ToProvidedString(cid);
if (entry->ServiceInstance()) {
nsAutoCString message;
message =
NS_LITERAL_CSTRING("You are calling CreateInstance \"") +
@ -1102,14 +1219,15 @@ nsComponentManagerImpl::CreateInstanceByContractID(const char* aContractID,
}
*aResult = nullptr;
nsFactoryEntry* entry = GetFactoryEntry(aContractID, strlen(aContractID));
Maybe<EntryWrapper> entry =
LookupByContractID(nsDependentCString(aContractID));
if (!entry) {
return NS_ERROR_FACTORY_NOT_REGISTERED;
}
#ifdef SHOW_CI_ON_EXISTING_SERVICE
if (entry->mServiceObject) {
if (entry->ServiceInstance()) {
nsAutoCString message;
message =
NS_LITERAL_CSTRING("You are calling CreateInstance \"") +
@ -1155,6 +1273,10 @@ nsresult nsComponentManagerImpl::FreeServices() {
entry->mServiceObject = nullptr;
}
for (const auto& module : gStaticModules) {
module.SetServiceInstance(nullptr);
}
return NS_OK;
}
@ -1196,12 +1318,12 @@ PRThread* nsComponentManagerImpl::GetPendingServiceThread(
}
nsresult nsComponentManagerImpl::GetServiceLocked(MutexLock& aLock,
nsFactoryEntry& aEntry,
EntryWrapper& aEntry,
const nsIID& aIID,
void** aResult) {
if (aEntry.mServiceObject) {
if (auto* service = aEntry.ServiceInstance()) {
aLock.Unlock();
return aEntry.mServiceObject->QueryInterface(aIID, aResult);
return service->QueryInterface(aIID, aResult);
}
PRThread* currentPRThread = PR_GetCurrentThread();
@ -1211,7 +1333,7 @@ nsresult nsComponentManagerImpl::GetServiceLocked(MutexLock& aLock,
nsIThread* currentThread = nullptr;
PRThread* pendingPRThread;
while ((pendingPRThread = GetPendingServiceThread(*aEntry.mCIDEntry->cid))) {
while ((pendingPRThread = GetPendingServiceThread(aEntry.CID()))) {
if (pendingPRThread == currentPRThread) {
NS_ERROR("Recursive GetService!");
return NS_ERROR_NOT_AVAILABLE;
@ -1241,13 +1363,13 @@ nsresult nsComponentManagerImpl::GetServiceLocked(MutexLock& aLock,
// It's still possible that the other thread failed to create the
// service so we're not guaranteed to have an entry or service yet.
if (aEntry.mServiceObject) {
if (auto* service = aEntry.ServiceInstance()) {
aLock.Unlock();
return aEntry.mServiceObject->QueryInterface(aIID, aResult);
return service->QueryInterface(aIID, aResult);
}
DebugOnly<PendingServiceInfo*> newInfo =
AddPendingService(*aEntry.mCIDEntry->cid, currentPRThread);
AddPendingService(aEntry.CID(), currentPRThread);
NS_ASSERTION(newInfo, "Failed to add info to the array!");
// We need to not be holding the service manager's lock while calling
@ -1267,8 +1389,7 @@ nsresult nsComponentManagerImpl::GetServiceLocked(MutexLock& aLock,
nsresult rv;
{
SafeMutexAutoUnlock unlock(mLock);
rv = CreateInstance(*aEntry.mCIDEntry->cid, nullptr, aIID,
getter_AddRefs(service));
rv = aEntry.CreateInstance(nullptr, aIID, getter_AddRefs(service));
}
if (NS_SUCCEEDED(rv) && !service) {
NS_ERROR("Factory did not return an object but returned success");
@ -1276,22 +1397,23 @@ nsresult nsComponentManagerImpl::GetServiceLocked(MutexLock& aLock,
}
#ifdef DEBUG
pendingPRThread = GetPendingServiceThread(*aEntry.mCIDEntry->cid);
pendingPRThread = GetPendingServiceThread(aEntry.CID());
MOZ_ASSERT(pendingPRThread == currentPRThread,
"Pending service array has been changed!");
#endif
RemovePendingService(*aEntry.mCIDEntry->cid);
RemovePendingService(aEntry.CID());
if (NS_FAILED(rv)) {
return rv;
}
NS_ASSERTION(!aEntry.mServiceObject, "Created two instances of a service!");
NS_ASSERTION(!aEntry.ServiceInstance(),
"Created two instances of a service!");
aEntry.mServiceObject = service.forget();
aEntry.SetServiceInstance(service.forget());
aLock.Unlock();
*aResult = do_AddRef(aEntry.mServiceObject).take();
*aResult = do_AddRef(aEntry.ServiceInstance()).take();
return NS_OK;
}
@ -1317,7 +1439,7 @@ nsComponentManagerImpl::GetService(const nsCID& aClass, const nsIID& aIID,
MutexLock lock(mLock);
nsFactoryEntry* entry = mFactories.Get(&aClass);
Maybe<EntryWrapper> entry = LookupByCID(lock, aClass);
if (!entry) {
return NS_ERROR_FACTORY_NOT_REGISTERED;
}
@ -1349,23 +1471,18 @@ nsComponentManagerImpl::IsServiceInstantiated(const nsCID& aClass,
return NS_ERROR_UNEXPECTED;
}
nsresult rv = NS_OK;
nsFactoryEntry* entry;
{
SafeMutexAutoLock lock(mLock);
entry = mFactories.Get(&aClass);
if (Maybe<EntryWrapper> entry = LookupByCID(aClass)) {
if (auto* service = entry->ServiceInstance()) {
nsCOMPtr<nsISupports> instance;
nsresult rv = service->QueryInterface(
aIID, getter_AddRefs(instance));
*aResult = (instance != nullptr);
return rv;
}
}
if (entry && entry->mServiceObject) {
nsCOMPtr<nsISupports> service;
rv = entry->mServiceObject->QueryInterface(aIID, getter_AddRefs(service));
*aResult = (service != nullptr);
} else {
*aResult = false;
}
return rv;
*aResult = false;
return NS_OK;
}
NS_IMETHODIMP
@ -1390,21 +1507,19 @@ nsComponentManagerImpl::IsServiceInstantiatedByContractID(
return NS_ERROR_UNEXPECTED;
}
nsresult rv = NS_OK;
nsFactoryEntry* entry;
{
SafeMutexAutoLock lock(mLock);
entry = mContractIDs.Get(nsDependentCString(aContractID));
if (Maybe<EntryWrapper> entry =
LookupByContractID(nsDependentCString(aContractID))) {
if (auto* service = entry->ServiceInstance()) {
nsCOMPtr<nsISupports> instance;
nsresult rv = service->QueryInterface(
aIID, getter_AddRefs(instance));
*aResult = (instance != nullptr);
return rv;
}
}
if (entry && entry->mServiceObject) {
nsCOMPtr<nsISupports> service;
rv = entry->mServiceObject->QueryInterface(aIID, getter_AddRefs(service));
*aResult = (service != nullptr);
} else {
*aResult = false;
}
return rv;
*aResult = false;
return NS_OK;
}
NS_IMETHODIMP
@ -1429,7 +1544,8 @@ nsComponentManagerImpl::GetServiceByContractID(const char* aContractID,
MutexLock lock(mLock);
nsFactoryEntry* entry = mContractIDs.Get(nsDependentCString(aContractID));
Maybe<EntryWrapper> entry =
LookupByContractID(lock, nsDependentCString(aContractID));
if (!entry) {
return NS_ERROR_FACTORY_NOT_REGISTERED;
}
@ -1450,12 +1566,22 @@ nsComponentManagerImpl::RegisterFactory(const nsCID& aClass, const char* aName,
SafeMutexAutoLock lock(mLock);
nsFactoryEntry* oldf = mFactories.Get(&aClass);
if (!oldf) {
return NS_ERROR_FACTORY_NOT_REGISTERED;
if (oldf) {
mContractIDs.Put(nsDependentCString(aContractID), oldf);
return NS_OK;
}
mContractIDs.Put(nsDependentCString(aContractID), oldf);
return NS_OK;
if (StaticComponents::LookupByCID(aClass)) {
// If this is the CID of a static module, just reset the invalid bit of
// the static entry for this contract ID, and assume it points to the
// correct class.
nsDependentCString contractID(aContractID);
if (StaticComponents::InvalidateContractID(contractID, false)) {
mContractIDs.Remove(contractID);
return NS_OK;
}
}
return NS_ERROR_FACTORY_NOT_REGISTERED;
}
nsAutoPtr<nsFactoryEntry> f(new nsFactoryEntry(aClass, aFactory));
@ -1464,8 +1590,16 @@ nsComponentManagerImpl::RegisterFactory(const nsCID& aClass, const char* aName,
if (auto entry = mFactories.LookupForAdd(f->mCIDEntry->cid)) {
return NS_ERROR_FACTORY_EXISTS;
} else {
if (StaticComponents::LookupByCID(*f->mCIDEntry->cid)) {
entry.OrRemove();
return NS_ERROR_FACTORY_EXISTS;
}
if (aContractID) {
mContractIDs.Put(nsDependentCString(aContractID), f);
nsDependentCString contractID(aContractID);
mContractIDs.Put(contractID, f);
// We allow dynamically-registered contract IDs to override static
// entries, so invalidate any static entry for this contract ID.
StaticComponents::InvalidateContractID(contractID);
}
entry.OrInsert([&f]() { return f.forget(); });
}
@ -1486,6 +1620,7 @@ nsComponentManagerImpl::UnregisterFactory(const nsCID& aClass,
auto entry = mFactories.Lookup(&aClass);
nsFactoryEntry* f = entry ? entry.Data() : nullptr;
if (!f || f->mFactory != aFactory) {
// Note: We do not support unregistering static factories.
return NS_ERROR_FACTORY_NOT_REGISTERED;
}
@ -1530,7 +1665,7 @@ nsComponentManagerImpl::UnregisterFactoryLocation(const nsCID& aCID,
NS_IMETHODIMP
nsComponentManagerImpl::IsCIDRegistered(const nsCID& aClass, bool* aResult) {
*aResult = (nullptr != GetFactoryEntry(aClass));
*aResult = LookupByCID(aClass).isSome();
return NS_OK;
}
@ -1541,29 +1676,30 @@ nsComponentManagerImpl::IsContractIDRegistered(const char* aClass,
return NS_ERROR_INVALID_ARG;
}
nsFactoryEntry* entry = GetFactoryEntry(aClass, strlen(aClass));
Maybe<EntryWrapper> entry = LookupByContractID(nsDependentCString(aClass));
if (entry) {
// UnregisterFactory might have left a stale nsFactoryEntry in
// mContractIDs, so we should check to see whether this entry has
// anything useful.
*aResult = (bool(entry->mModule) || bool(entry->mFactory) ||
bool(entry->mServiceObject));
} else {
*aResult = false;
}
*aResult = entry.isSome();
return NS_OK;
}
NS_IMETHODIMP
nsComponentManagerImpl::EnumerateCIDs(nsISimpleEnumerator** aEnumerator) {
nsCOMArray<nsISupports> array;
for (auto iter = mFactories.Iter(); !iter.Done(); iter.Next()) {
const nsID* id = iter.Key();
auto appendEntry = [&](const nsID& aCID) {
nsCOMPtr<nsISupportsID> wrapper = new nsSupportsID();
wrapper->SetData(id);
wrapper->SetData(&aCID);
array.AppendObject(wrapper);
};
for (auto iter = mFactories.Iter(); !iter.Done(); iter.Next()) {
appendEntry(*iter.Key());
}
for (const auto& module : gStaticModules) {
if (module.Active()) {
appendEntry(module.CID());
}
}
return NS_NewArrayEnumerator(aEnumerator, array);
}
@ -1576,6 +1712,12 @@ nsComponentManagerImpl::EnumerateContractIDs(
array->AppendElement(contract);
}
for (const auto& entry : gContractEntries) {
if (!entry.Invalid()) {
array->AppendElement(entry.ContractID());
}
}
nsCOMPtr<nsIUTF8StringEnumerator> e;
nsresult rv = NS_NewAdoptingUTF8StringEnumerator(getter_AddRefs(e), array);
if (NS_FAILED(rv)) {
@ -1595,11 +1737,12 @@ NS_IMETHODIMP
nsComponentManagerImpl::ContractIDToCID(const char* aContractID,
nsCID** aResult) {
{
SafeMutexAutoLock lock(mLock);
nsFactoryEntry* entry = mContractIDs.Get(nsDependentCString(aContractID));
MutexLock lock(mLock);
Maybe<EntryWrapper> entry =
LookupByContractID(lock, nsDependentCString(aContractID));
if (entry) {
*aResult = (nsCID*)moz_xmalloc(sizeof(nsCID));
**aResult = *entry->mCIDEntry->cid;
**aResult = entry->CID();
return NS_OK;
}
}
@ -1722,6 +1865,13 @@ already_AddRefed<nsIFactory> nsFactoryEntry::GetFactory() {
return factory.forget();
}
nsresult nsFactoryEntry::CreateInstance(nsISupports* aOuter, const nsIID& aIID,
void** aResult) {
nsCOMPtr<nsIFactory> factory = GetFactory();
NS_ENSURE_TRUE(factory, NS_ERROR_FAILURE);
return factory->CreateInstance(aOuter, aIID, aResult);
}
size_t nsFactoryEntry::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
size_t n = aMallocSizeOf(this);

Просмотреть файл

@ -35,6 +35,8 @@
#include "nsClassHashtable.h"
#include "nsTArray.h"
#include "mozilla/Components.h"
#include "mozilla/Maybe.h"
#include "mozilla/Omnijar.h"
#include "mozilla/Attributes.h"
@ -53,8 +55,9 @@ struct PRThread;
extern const mozilla::Module kXPCOMModule;
namespace {
class EntryWrapper;
class MutexLock;
}
} // namespace
namespace mozilla {
namespace xpcom {
@ -147,15 +150,19 @@ class nsComponentManagerImpl final : public nsIComponentManager,
already_AddRefed<nsIFactory> LoadFactory(nsFactoryEntry* aEntry);
nsFactoryEntry* GetFactoryEntry(const char* aContractID,
uint32_t aContractIDLen);
nsFactoryEntry* GetFactoryEntry(const nsCID& aClass);
nsDataHashtable<nsIDPointerHashKey, nsFactoryEntry*> mFactories;
nsDataHashtable<nsCStringHashKey, nsFactoryEntry*> mContractIDs;
SafeMutex mLock;
mozilla::Maybe<EntryWrapper> LookupByCID(const nsID& aCID);
mozilla::Maybe<EntryWrapper> LookupByCID(const MutexLock&, const nsID& aCID);
mozilla::Maybe<EntryWrapper> LookupByContractID(
const nsACString& aContractID);
mozilla::Maybe<EntryWrapper> LookupByContractID(
const MutexLock&, const nsACString& aContractID);
static void InitializeStaticModules();
static void InitializeModuleLocations();
@ -276,7 +283,7 @@ class nsComponentManagerImpl final : public nsIComponentManager,
private:
~nsComponentManagerImpl();
nsresult GetServiceLocked(MutexLock& aLock, nsFactoryEntry& aEntry,
nsresult GetServiceLocked(MutexLock& aLock, EntryWrapper& aEntry,
const nsIID& aIID, void** aResult);
};
@ -295,6 +302,9 @@ struct nsFactoryEntry {
already_AddRefed<nsIFactory> GetFactory();
nsresult CreateInstance(nsISupports* aOuter, const nsIID& aIID,
void** aResult);
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
const mozilla::Module::CIDEntry* mCIDEntry;