зеркало из https://github.com/mozilla/gecko-dev.git
899 строки
28 KiB
Python
899 строки
28 KiB
Python
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
|
|
|
|
|
|
NO_CONTRACT_ID = 0xffffffff
|
|
|
|
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_RDD_PROCESS = 0x20
|
|
ALLOW_IN_GPU_AND_MAIN_PROCESS = (ALLOW_IN_GPU_PROCESS |
|
|
MAIN_PROCESS_ONLY)
|
|
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)
|
|
ALLOW_IN_RDD_AND_SOCKET_PROCESS = (ALLOW_IN_RDD_PROCESS |
|
|
ALLOW_IN_SOCKET_PROCESS)
|
|
ALLOW_IN_GPU_RDD_AND_SOCKET_PROCESS = (ALLOW_IN_GPU_PROCESS |
|
|
ALLOW_IN_RDD_PROCESS |
|
|
ALLOW_IN_SOCKET_PROCESS)
|
|
ALLOW_IN_GPU_RDD_VR_AND_SOCKET_PROCESS = (ALLOW_IN_GPU_PROCESS |
|
|
ALLOW_IN_RDD_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_RDD_PROCESS: 'ALLOW_IN_RDD_PROCESS',
|
|
ProcessSelector.ALLOW_IN_GPU_AND_MAIN_PROCESS: 'ALLOW_IN_GPU_AND_MAIN_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',
|
|
ProcessSelector.ALLOW_IN_RDD_AND_SOCKET_PROCESS:
|
|
'ALLOW_IN_RDD_AND_SOCKET_PROCESS',
|
|
ProcessSelector.ALLOW_IN_GPU_RDD_AND_SOCKET_PROCESS:
|
|
'ALLOW_IN_GPU_RDD_AND_SOCKET_PROCESS',
|
|
ProcessSelector.ALLOW_IN_GPU_RDD_VR_AND_SOCKET_PROCESS:
|
|
'ALLOW_IN_GPU_RDD_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()
|
|
|
|
interfaces = []
|
|
|
|
|
|
# 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', [])
|
|
|
|
self.js_name = data.get('js_name', None)
|
|
self.interfaces = data.get('interfaces', [])
|
|
|
|
if len(self.interfaces) > 255:
|
|
raise Exception('JS service %s may not have more than 255 '
|
|
'interfaces' % self.js_name)
|
|
|
|
self.interfaces_offset = len(interfaces)
|
|
for iface in self.interfaces:
|
|
interfaces.append(iface)
|
|
|
|
# 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.jsm = data.get('jsm', None)
|
|
|
|
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
|
|
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.jsm:
|
|
if not self.constructor:
|
|
error("JavaScript components must specify a constructor")
|
|
|
|
for prop in ('init_method', 'legacy_constructor', 'headers'):
|
|
if getattr(self, prop):
|
|
error("JavaScript components may not specify a '%s' "
|
|
"property" % prop)
|
|
elif 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")
|
|
|
|
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]
|
|
|
|
# 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 for a JSServiceEntry represengin this module.
|
|
def lower_js_service(self):
|
|
return """
|
|
{{
|
|
{js_name},
|
|
ModuleID::{name},
|
|
{{ {iface_offset} }},
|
|
{iface_count}
|
|
}}""".format(js_name=strings.entry_to_cxx(self.js_name),
|
|
name=self.name,
|
|
iface_offset=self.interfaces_offset,
|
|
iface_count=len(self.interfaces))
|
|
|
|
# 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.jsm:
|
|
res += (
|
|
' nsCOMPtr<nsISupports> inst;\n'
|
|
' MOZ_TRY(ConstructJSMComponent(nsLiteralCString(%s),\n'
|
|
' %s,\n'
|
|
' getter_AddRefs(inst)));'
|
|
'\n' % (json.dumps(self.jsm), json.dumps(self.constructor)))
|
|
elif self.external:
|
|
res += (' nsCOMPtr<nsISupports> inst = '
|
|
'mozCreateComponent<%s>();\n' % self.type)
|
|
# The custom constructor may return null, so check before calling
|
|
# any methods.
|
|
res += ' NS_ENSURE_TRUE(inst, NS_ERROR_FAILURE);\n'
|
|
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(
|
|
std::is_same_v<already_AddRefed<T>, decltype(%(constructor)s())>,
|
|
"Singleton constructor must return already_AddRefed");
|
|
static_assert(
|
|
std::is_base_of<%(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
|
|
|
|
# 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
|
|
# (/* ... */) 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)
|
|
|
|
|
|
def gen_interfaces(ifaces):
|
|
res = []
|
|
for iface in ifaces:
|
|
res.append(' nsXPTInterface::%s,\n' % iface)
|
|
return ''.join(res)
|
|
|
|
|
|
# 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)
|
|
|
|
|
|
# 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()
|
|
|
|
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 = []
|
|
categories = defaultdict(list)
|
|
|
|
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))
|
|
|
|
for category, entries in manifest.get('Categories', {}).items():
|
|
for key, entry in entries.items():
|
|
if isinstance(entry, tuple):
|
|
value, process = entry
|
|
else:
|
|
value, process = entry, 0
|
|
categories[category].append((key, value, process))
|
|
|
|
cids = set()
|
|
contracts = []
|
|
contract_map = {}
|
|
js_services = {}
|
|
|
|
jsms = set()
|
|
|
|
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)
|
|
|
|
if mod.jsm:
|
|
jsms.add(mod.jsm)
|
|
|
|
if mod.js_name:
|
|
if mod.js_name in js_services:
|
|
raise Exception('Duplicate JS service name: %s' % mod.js_name)
|
|
js_services[mod.js_name] = mod
|
|
|
|
if str(mod.cid) in cids:
|
|
raise Exception('Duplicate cid: %s' % str(mod.cid))
|
|
cids.add(str(mod.cid))
|
|
|
|
cid_phf = PerfectHash(modules, PHF_SIZE,
|
|
key=lambda module: module.cid.bytes)
|
|
|
|
contract_phf = PerfectHash(contracts, PHF_SIZE,
|
|
key=lambda entry: entry.contract)
|
|
|
|
js_services_phf = PerfectHash(list(js_services.values()), PHF_SIZE,
|
|
key=lambda entry: entry.js_name)
|
|
|
|
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['component_jsms'] = '\n'.join(' %s,' % strings.entry_to_cxx(jsm)
|
|
for jsm in sorted(jsms)) + '\n'
|
|
|
|
substs['interfaces'] = gen_interfaces(interfaces)
|
|
|
|
substs['decls'] = gen_decls(types)
|
|
|
|
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',
|
|
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()')
|
|
|
|
substs['js_services_table'] = js_services_phf.cxx_codegen(
|
|
name='LookupJSService',
|
|
entry_type='JSServiceEntry',
|
|
entries_name='gJSServices',
|
|
lower_entry=lambda entry: entry.lower_js_service(),
|
|
|
|
return_type='const JSServiceEntry*',
|
|
return_entry='return entry.Name() == 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}
|
|
exec(open(filename).read(), 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
|
|
};
|
|
|
|
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)
|
|
|
|
return deps
|