SSL gen_static_components.py
Interaktion und PortierbarkeitPython
import json import os import re import struct from collections import defaultdict from uuid import UUID
import buildconfig from mozbuild.util import FileAvoidWrite from perfecthash import PerfectHash
NO_CONTRACT_ID = 0xFFFFFFFF
PHF_SIZE = 512
TINY_PHF_SIZE = 16
# In tests, we might not have a (complete) buildconfig.
ENDIAN = ( "<"if buildconfig.substs.get("TARGET_ENDIANNESS", "little") == "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
# 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
# Corresponds to the Module::BackgroundTasksSelector 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 BackgroundTasksSelector:
NO_TASKS = 0x0
ALL_TASKS = 0xFFFF
# Maps BackgroundTasksSelector constants to the name of the corresponding # Module::BackgroundTasksSelector enum value.
BACKGROUNDTASKS = {
BackgroundTasksSelector.ALL_TASKS: "ALL_TASKS",
BackgroundTasksSelector.NO_TASKS: "NO_TASKS",
}
# Emits the C++ symbolic constant corresponding to a BackgroundTasks constant. def lower_backgroundtasks(backgroundtasks): return"Module::BackgroundTasksSelector::%s" % BACKGROUNDTASKS[backgroundtasks]
# 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 isnotNone: 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")
# 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 notin self.classes
if name notin 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
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
if self.esModule: ifnot 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 andnot self.contract_ids:
error("Overridable components must specify at least one contract ""ID")
# 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
)
# Generates the C++ code for a JSServiceEntry representing 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 isnotNone:
res += " MOZ_TRY(CallInitFunc(%d));\n" % self.init_idx
if self.legacy_constructor:
res += ( " return /* legacy */ %s(aIID, aResult);\n"
% self.legacy_constructor
) return res
if self.esModule:
res += ( " nsCOMPtr inst;\n" " MOZ_TRY(ConstructESModuleComponent(nsLiteralCString(%s),\n" " %s,\n" " getter_AddRefs(inst)));" "\n" % (json.dumps(self.esModule), json.dumps(self.constructor))
) elif self.external:
res += ( " nsCOMPtr 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
ifnot 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");
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): assertnot self.anonymous
# Generates the rust code for the `xpcom::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_rust(self): assertnot self.anonymous
res = ( """ #[allow(non_snake_case)]
pub mod %(name)s {
/// Get the singleton service instance for this component.
pub fn service<T: crate::XpCom>() -> Result<crate::RefPtr<T>, nserror::nsresult> {
let mut ga = crate::GetterAddrefs::<T>::new();
let rv = unsafe { super::Gecko_GetServiceByModuleID(%(id)s, &T::IID, ga.void_ptr()) }; if rv.failed() { return Err(rv);
}
ga.refptr().ok_or(nserror::NS_ERROR_NO_INTERFACE)
} """
% substs
)
ifnot self.singleton:
res += ( """
/// Create a new instance of this component.
pub fn create<T: crate::XpCom>() -> Result<crate::RefPtr<T>, nserror::nsresult> {
let mut ga = crate::GetterAddrefs::<T>::new();
let rv = unsafe { super::Gecko_CreateInstanceByModuleID(%(id)s, &T::IID, ga.void_ptr()) }; if rv.failed() { return Err(rv);
}
ga.refptr().ok_or(nserror::NS_ERROR_NO_INTERFACE)
} """
% substs
)
res += """\
} """
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
# Represents a static ProtocolHandler entry, corresponding to a C++ # ProtocolEntry struct, mapping a scheme to a static module entry and metadata. class ProtocolHandler(object): def __init__(self, config, module): def error(str_): raise Exception( "Error defining protocol handler %s (%s): %s"
% (str(module.cid), ", ".join(map(repr, module.contract_ids)), str_)
)
self.module = module
self.scheme = config.get("scheme", None) if self.scheme isNone:
error("No scheme defined for protocol component")
self.flags = config.get("flags", None) if self.flags isNone:
error("No flags defined for protocol component")
self.default_port = config.get("default_port", -1)
self.has_dynamic_flags = config.get("has_dynamic_flags", False)
def to_cxx(self): return"""
{{
.mScheme = {scheme},
.mProtocolFlags = {flags},
.mDefaultPort = {default_port},
.mModuleID = {module_id},
.mHasDynamicFlags = {has_dynamic_flags},
}} """.format(
scheme=strings.entry_to_cxx(self.scheme),
module_id=lower_module_id(self.module),
flags=" | ".join("nsIProtocolHandler::%s" % flag for flag in self.flags),
default_port=self.default_port,
has_dynamic_flags="true"if self.has_dynamic_flags else"false",
)
# 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()):
# 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)
)
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 ifnot entry.anonymous)
# Generates the rust getter code for each named component entry in the # `xpcom::components::` module. def gen_getters_rust(entries):
entries = list(entries)
entries.sort(key=lambda e: e.name)
return"".join(
entry.lower_getters_rust() for entry in entries ifnot entry.anonymous
)
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_category_list(val): # Entries can be bare strings (like `"m-browser"`), lists of bare strings, # or dictionaries (like `{"name": "m-browser", "backgroundtasks": # BackgroundTasksSelector.ALL_TASKS}`), somewhat recursively.
def ensure_dict(v): # Turn `v` into `{"name": v}` if it's not already a dict. if isinstance(v, dict): return v return {"name": v}
if isinstance(val, (list, tuple)): return tuple(ensure_dict(v) for v in val)
vals = [] for entry in to_category_list(names):
d = dict(val)
d["name"] = entry["name"]
vals.append(d)
return tuple(vals)
return (ensure_dict(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(({"name": key}, value, process))
for category, entries in mod.categories.items(): for entry in to_category_list(entries):
categories[category].append((entry, mod.contract_id, mod.processes))
if mod.type andnot mod.headers:
types.add(mod.type)
if mod.esModule:
esModules.add(mod.esModule)
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 mod.protocol_config:
handler = ProtocolHandler(mod.protocol_config, mod) if handler.scheme in protocol_handlers: raise Exception("Duplicate protocol handler: %s" % handler.scheme)
protocol_handlers[handler.scheme] = handler
if str(mod.cid) in cids: raise Exception("Duplicate cid: %s" % str(mod.cid))
cids.add(str(mod.cid))
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/. */
with open_output("components.rs") as fh:
fh.write( """\
/// Unique IDs for each statically-registered module. #[repr(u16)]
pub enum ModuleID {
%(module_ids)s
}
%(component_getters_rust)s """
% 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/. */
enum class ModuleID : uint16_t {
%(module_ids)s
};
// May be added as a friend function to allow constructing services via
// private constructors and init methods.
nsresult CreateInstanceImpl(ModuleID aID, const nsIID& aIID, void** aResult);
class MOZ_STACK_CLASS StaticModuleHelper : public nsCOMPtr_helper {
public:
StaticModuleHelper(ModuleID aId, nsresult* aErrorPtr)
: mId(aId), mErrorPtr(aErrorPtr) {}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.