зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1478124: Part 6 - Add helpers for creating/inspecting static modules. r=froydnj
The static XPCOM manifest format makes it easy to define a component in a single place, without separate contract ID and CID macro definitions in headers, and variable constants in module files. Without any other changes, however, those macros are still required in order to create instances of or retrieve services for the component. This patch solves that problem by allowing component definitions to include an explicit component name, and adding helpers for each named component to Components.h: mozilla::components::<Name>::CID() to retrieve its class ID. mozilla::components::<Name>::Create() to create a new instance. mozilla::components::<Name>::Service() to retrieve its service instance. These getters have the benefit of doing full compile-time sanity checking, with no possibilty of using a mistyped contract ID string, or a macro constant which has gotten out of sync with the component entry. Moreover, when possible, these getters are optimized to operate on module entries directly, without going through expensive hash lookups or virtual calls. Differential Revision: https://phabricator.services.mozilla.com/D15037 --HG-- extra : rebase_source : db7fe00fe80677a6a42d8136fd4505a02e330495 extra : absorb_source : ec11e22825befcd6fa4e96ffa81cd1c1b23e3bef extra : histedit_source : 650e8e98235df5d757f3fa725bad390e9c094c34
This commit is contained in:
Родитель
a1fd8ba309
Коммит
73670788c7
|
@ -29,6 +29,8 @@
|
|||
namespace mozilla {
|
||||
namespace xpcom {
|
||||
|
||||
static constexpr uint32_t kNoContractID = 0xffffffff;
|
||||
|
||||
namespace {
|
||||
// Template helpers for constructor function sanity checks.
|
||||
template <typename T>
|
||||
|
@ -165,6 +167,15 @@ bool StaticModule::Active() const {
|
|||
return FastProcessSelectorMatches(mProcessSelector);
|
||||
}
|
||||
|
||||
bool StaticModule::Overridable() const {
|
||||
return mContractID.mOffset != kNoContractID;
|
||||
}
|
||||
|
||||
nsCString StaticModule::ContractID() const {
|
||||
MOZ_ASSERT(Overridable());
|
||||
return GetString(mContractID);
|
||||
}
|
||||
|
||||
nsresult StaticModule::CreateInstance(nsISupports* aOuter, const nsIID& aIID,
|
||||
void** aResult) const {
|
||||
return CreateInstanceImpl(ID(), aOuter, aIID, aResult);
|
||||
|
@ -224,5 +235,26 @@ nsCString StaticCategory::Name() const {
|
|||
CallUnloadFuncs();
|
||||
}
|
||||
|
||||
/* static */ const nsID& Components::GetCID(ModuleID aID) {
|
||||
return gStaticModules[size_t(aID)].CID();
|
||||
}
|
||||
|
||||
nsresult GetServiceHelper::operator()(const nsIID& aIID, void** aResult) const {
|
||||
nsresult rv =
|
||||
nsComponentManagerImpl::gComponentManager->GetService(mId, aIID, aResult);
|
||||
return SetResult(rv);
|
||||
}
|
||||
|
||||
nsresult CreateInstanceHelper::operator()(const nsIID& aIID,
|
||||
void** aResult) const {
|
||||
const auto& entry = gStaticModules[size_t(mId)];
|
||||
if (!entry.Active()) {
|
||||
return SetResult(NS_ERROR_FACTORY_NOT_REGISTERED);
|
||||
}
|
||||
|
||||
nsresult rv = entry.CreateInstance(nullptr, aIID, aResult);
|
||||
return SetResult(rv);
|
||||
}
|
||||
|
||||
} // namespace xpcom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -78,6 +78,7 @@ struct StringOffset final {
|
|||
*/
|
||||
struct StaticModule {
|
||||
nsID mCID;
|
||||
StringOffset mContractID;
|
||||
Module::ProcessSelector mProcessSelector;
|
||||
|
||||
const nsID& CID() const { return mCID; }
|
||||
|
@ -89,6 +90,22 @@ struct StaticModule {
|
|||
*/
|
||||
size_t Idx() const { return size_t(ID()); }
|
||||
|
||||
/**
|
||||
* Returns true if this component's corresponding contract ID is expected to
|
||||
* be overridden at runtime. If so, it should always be looked up by its
|
||||
* ContractID() when retrieving its service instance.
|
||||
*/
|
||||
bool Overridable() const;
|
||||
|
||||
/**
|
||||
* If this entry is overridable, returns its associated contract ID string.
|
||||
* The component should always be looked up by this contract ID when
|
||||
* retrieving its service instance.
|
||||
*
|
||||
* Note: This may *only* be called if Overridable() returns true.
|
||||
*/
|
||||
nsCString ContractID() const;
|
||||
|
||||
/**
|
||||
* Returns true if this entry is active. Typically this will only return false
|
||||
* if the entry's process selector does not match this process.
|
||||
|
|
|
@ -11,6 +11,8 @@ from perfecthash import PerfectHash
|
|||
import buildconfig
|
||||
|
||||
|
||||
NO_CONTRACT_ID = 0xffffffff
|
||||
|
||||
PHF_SIZE = 512
|
||||
|
||||
ENDIAN = '<' if buildconfig.substs['TARGET_ENDIANNESS'] == 'little' else '>'
|
||||
|
@ -219,6 +221,7 @@ class ModuleEntry(object):
|
|||
self.external = data.get('external', not (self.headers or
|
||||
self.legacy_constructor))
|
||||
self.singleton = data.get('singleton', False)
|
||||
self.overridable = data.get('overridable', False)
|
||||
|
||||
if 'name' in data:
|
||||
self.anonymous = False
|
||||
|
@ -248,6 +251,10 @@ class ModuleEntry(object):
|
|||
error("The 'constructor' and 'legacy_constructor' properties "
|
||||
"are mutually exclusive")
|
||||
|
||||
if self.overridable and not self.contract_ids:
|
||||
error("Overridable components must specify at least one contract "
|
||||
"ID")
|
||||
|
||||
@property
|
||||
def contract_id(self):
|
||||
return self.contract_ids[0]
|
||||
|
@ -255,13 +262,19 @@ class ModuleEntry(object):
|
|||
# Generates the C++ code for a StaticModule struct initializer
|
||||
# representing this component.
|
||||
def to_cxx(self):
|
||||
contract_id = (strings.entry_to_cxx(self.contract_id)
|
||||
if self.overridable
|
||||
else '{ 0x%x }' % NO_CONTRACT_ID)
|
||||
|
||||
return """
|
||||
/* {name} */ {{
|
||||
/* {{{cid_string}}} */
|
||||
{cid},
|
||||
{contract_id},
|
||||
{processes},
|
||||
}}""".format(name=self.name, cid=self.cid.to_cxx(),
|
||||
cid_string=str(self.cid),
|
||||
contract_id=contract_id,
|
||||
processes=lower_processes(self.processes))
|
||||
|
||||
# Generates the C++ code necessary to construct an instance of this
|
||||
|
@ -322,6 +335,42 @@ class ModuleEntry(object):
|
|||
|
||||
return res
|
||||
|
||||
# Generates the C++ code for the `mozilla::components::<name>` entry
|
||||
# corresponding to this component. This may not be called for modules
|
||||
# without an explicit `name` (in which cases, `self.anonymous` will be
|
||||
# true).
|
||||
def lower_getters(self):
|
||||
assert not self.anonymous
|
||||
|
||||
substs = {
|
||||
'name': self.name,
|
||||
'id': '::mozilla::xpcom::ModuleID::%s' % self.name,
|
||||
}
|
||||
|
||||
res = """
|
||||
namespace %(name)s {
|
||||
static inline const nsID& CID() {
|
||||
return ::mozilla::xpcom::Components::GetCID(%(id)s);
|
||||
}
|
||||
|
||||
static inline ::mozilla::xpcom::GetServiceHelper Service(nsresult* aRv = nullptr) {
|
||||
return {%(id)s, aRv};
|
||||
}
|
||||
""" % substs
|
||||
|
||||
if not self.singleton:
|
||||
res += """
|
||||
static inline ::mozilla::xpcom::CreateInstanceHelper Create(nsresult* aRv = nullptr) {
|
||||
return {%(id)s, aRv};
|
||||
}
|
||||
""" % substs
|
||||
|
||||
res += """\
|
||||
} // namespace %(name)s
|
||||
""" % substs
|
||||
|
||||
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
|
||||
|
@ -442,6 +491,17 @@ def gen_constructors(entries):
|
|||
return ''.join(constructors)
|
||||
|
||||
|
||||
# Generates the getter code for each named component entry in the
|
||||
# `mozilla::components::` namespace.
|
||||
def gen_getters(entries):
|
||||
entries = list(entries)
|
||||
entries.sort(key=lambda e: e.name)
|
||||
|
||||
return ''.join(entry.lower_getters()
|
||||
for entry in entries
|
||||
if not entry.anonymous)
|
||||
|
||||
|
||||
def gen_includes(substs, all_headers):
|
||||
headers = set()
|
||||
absolute_headers = set()
|
||||
|
@ -535,6 +595,8 @@ def gen_substs(manifests):
|
|||
|
||||
substs['constructors'] = gen_constructors(cid_phf.entries)
|
||||
|
||||
substs['component_getters'] = gen_getters(cid_phf.entries)
|
||||
|
||||
substs['module_cid_table'] = cid_phf.cxx_codegen(
|
||||
name='ModuleByCID',
|
||||
entry_type='StaticModule',
|
||||
|
@ -666,8 +728,52 @@ enum class ModuleID : uint16_t {
|
|||
%(module_ids)s
|
||||
};
|
||||
|
||||
class MOZ_STACK_CLASS StaticModuleHelper : public nsCOMPtr_helper {
|
||||
public:
|
||||
StaticModuleHelper(ModuleID aId, nsresult* aErrorPtr)
|
||||
: mId(aId), mErrorPtr(aErrorPtr) {}
|
||||
|
||||
protected:
|
||||
nsresult SetResult(nsresult aRv) const {
|
||||
if (mErrorPtr) {
|
||||
*mErrorPtr = aRv;
|
||||
}
|
||||
return aRv;
|
||||
}
|
||||
|
||||
ModuleID mId;
|
||||
nsresult* mErrorPtr;
|
||||
};
|
||||
|
||||
class MOZ_STACK_CLASS GetServiceHelper final : public StaticModuleHelper {
|
||||
public:
|
||||
using StaticModuleHelper::StaticModuleHelper;
|
||||
|
||||
nsresult NS_FASTCALL operator()(const nsIID& aIID,
|
||||
void** aResult) const override;
|
||||
};
|
||||
|
||||
class MOZ_STACK_CLASS CreateInstanceHelper final : public StaticModuleHelper {
|
||||
public:
|
||||
using StaticModuleHelper::StaticModuleHelper;
|
||||
|
||||
nsresult NS_FASTCALL operator()(const nsIID& aIID,
|
||||
void** aResult) const override;
|
||||
};
|
||||
|
||||
class Components final {
|
||||
public:
|
||||
static const nsID& GetCID(ModuleID aID);
|
||||
};
|
||||
|
||||
} // namespace xpcom
|
||||
|
||||
namespace components {
|
||||
%(component_getters)s
|
||||
} // namespace components
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
""" % substs)
|
||||
|
||||
|
|
|
@ -1444,6 +1444,46 @@ nsComponentManagerImpl::GetService(const nsCID& aClass, const nsIID& aIID,
|
|||
return GetServiceLocked(lock, *entry, aIID, aResult);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsComponentManagerImpl::GetService(ModuleID aId, const nsIID& aIID,
|
||||
void** aResult) {
|
||||
const auto& entry = gStaticModules[size_t(aId)];
|
||||
|
||||
// test this first, since there's no point in returning a service during
|
||||
// shutdown -- whether it's available or not would depend on the order it
|
||||
// occurs in the list
|
||||
if (gXPCOMShuttingDown) {
|
||||
// When processing shutdown, don't process new GetService() requests
|
||||
#ifdef SHOW_DENIED_ON_SHUTDOWN
|
||||
fprintf(stderr,
|
||||
"Getting service on shutdown. Denied.\n"
|
||||
" CID: %s\n IID: %s\n",
|
||||
AutoIDString(entry.CID()).get(),
|
||||
AutoIDString(aIID).get());
|
||||
#endif /* SHOW_DENIED_ON_SHUTDOWN */
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
MutexLock lock(mLock);
|
||||
|
||||
if (!entry.Active()) {
|
||||
return NS_ERROR_FACTORY_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
Maybe<EntryWrapper> wrapper;
|
||||
if (entry.Overridable()) {
|
||||
// If we expect this service to be overridden by test code, we need to look
|
||||
// it up by contract ID every time.
|
||||
wrapper = LookupByContractID(lock, entry.ContractID());
|
||||
if (!wrapper) {
|
||||
return NS_ERROR_FACTORY_NOT_REGISTERED;
|
||||
}
|
||||
} else {
|
||||
wrapper.emplace(&entry);
|
||||
}
|
||||
return GetServiceLocked(lock, *wrapper, aIID, aResult);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsComponentManagerImpl::IsServiceInstantiated(const nsCID& aClass,
|
||||
const nsIID& aIID,
|
||||
|
|
|
@ -163,6 +163,8 @@ class nsComponentManagerImpl final : public nsIComponentManager,
|
|||
mozilla::Maybe<EntryWrapper> LookupByContractID(
|
||||
const MutexLock&, const nsACString& aContractID);
|
||||
|
||||
nsresult GetService(mozilla::xpcom::ModuleID, const nsIID& aIID, void** aResult);
|
||||
|
||||
static void InitializeStaticModules();
|
||||
static void InitializeModuleLocations();
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче