Bug 1910362 - Create new Nimbus helper r=aaronmt,ohorvath
[gecko.git] / xpcom / components / gen_static_components.py
blob122b7b21ca5a60a2dbdd9a3f50ac7bab4420ef9f
1 import json
2 import os
3 import re
4 import struct
5 from collections import defaultdict
6 from uuid import UUID
8 import buildconfig
9 from mozbuild.util import FileAvoidWrite
10 from perfecthash import PerfectHash
12 NO_CONTRACT_ID = 0xFFFFFFFF
14 PHF_SIZE = 512
16 TINY_PHF_SIZE = 16
18 # In tests, we might not have a (complete) buildconfig.
19 ENDIAN = (
20 "<" if buildconfig.substs.get("TARGET_ENDIANNESS", "little") == "little" else ">"
24 # Represents a UUID in the format used internally by Gecko, and supports
25 # serializing it in that format to both C++ source and raw byte arrays.
26 class UUIDRepr(object):
27 def __init__(self, uuid):
28 self.uuid = uuid
30 fields = uuid.fields
32 self.a = fields[0]
33 self.b = fields[1]
34 self.c = fields[2]
36 d = list(fields[3:5])
37 for i in range(0, 6):
38 d.append(fields[5] >> (8 * (5 - i)) & 0xFF)
40 self.d = tuple(d)
42 def __str__(self):
43 return str(self.uuid)
45 @property
46 def bytes(self):
47 return struct.pack(ENDIAN + "IHHBBBBBBBB", self.a, self.b, self.c, *self.d)
49 def to_cxx(self):
50 rest = ", ".join("0x%02x" % b for b in self.d)
52 return "{ 0x%x, 0x%x, 0x%x, { %s } }" % (self.a, self.b, self.c, rest)
55 # Corresponds to the Module::ProcessSelector enum in Module.h. The actual
56 # values don't matter, since the code generator emits symbolic constants for
57 # these values, but we use the same values as the enum constants for clarity.
58 class ProcessSelector:
59 ANY_PROCESS = 0
60 MAIN_PROCESS_ONLY = 1 << 0
61 CONTENT_PROCESS_ONLY = 1 << 1
62 ALLOW_IN_GPU_PROCESS = 1 << 2
63 ALLOW_IN_VR_PROCESS = 1 << 3
64 ALLOW_IN_SOCKET_PROCESS = 1 << 4
65 ALLOW_IN_RDD_PROCESS = 1 << 5
66 ALLOW_IN_UTILITY_PROCESS = 1 << 6
67 ALLOW_IN_GMPLUGIN_PROCESS = 1 << 7
68 ALLOW_IN_GPU_AND_MAIN_PROCESS = ALLOW_IN_GPU_PROCESS | MAIN_PROCESS_ONLY
69 ALLOW_IN_GPU_AND_SOCKET_PROCESS = ALLOW_IN_GPU_PROCESS | ALLOW_IN_SOCKET_PROCESS
70 ALLOW_IN_GPU_AND_VR_PROCESS = ALLOW_IN_GPU_PROCESS | ALLOW_IN_VR_PROCESS
71 ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS = (
72 ALLOW_IN_GPU_PROCESS | ALLOW_IN_VR_PROCESS | ALLOW_IN_SOCKET_PROCESS
74 ALLOW_IN_RDD_AND_SOCKET_PROCESS = ALLOW_IN_RDD_PROCESS | ALLOW_IN_SOCKET_PROCESS
75 ALLOW_IN_GPU_RDD_AND_SOCKET_PROCESS = (
76 ALLOW_IN_GPU_PROCESS | ALLOW_IN_RDD_PROCESS | ALLOW_IN_SOCKET_PROCESS
78 ALLOW_IN_GPU_RDD_SOCKET_AND_UTILITY_PROCESS = (
79 ALLOW_IN_GPU_PROCESS
80 | ALLOW_IN_RDD_PROCESS
81 | ALLOW_IN_SOCKET_PROCESS
82 | ALLOW_IN_UTILITY_PROCESS
84 ALLOW_IN_GPU_RDD_VR_AND_SOCKET_PROCESS = (
85 ALLOW_IN_GPU_PROCESS
86 | ALLOW_IN_RDD_PROCESS
87 | ALLOW_IN_VR_PROCESS
88 | ALLOW_IN_SOCKET_PROCESS
90 ALLOW_IN_GPU_RDD_VR_SOCKET_AND_UTILITY_PROCESS = (
91 ALLOW_IN_GPU_PROCESS
92 | ALLOW_IN_RDD_PROCESS
93 | ALLOW_IN_VR_PROCESS
94 | ALLOW_IN_SOCKET_PROCESS
95 | ALLOW_IN_UTILITY_PROCESS
97 ALLOW_IN_GPU_RDD_VR_SOCKET_UTILITY_AND_GMPLUGIN_PROCESS = (
98 ALLOW_IN_GPU_PROCESS
99 | ALLOW_IN_RDD_PROCESS
100 | ALLOW_IN_VR_PROCESS
101 | ALLOW_IN_SOCKET_PROCESS
102 | ALLOW_IN_UTILITY_PROCESS
103 | ALLOW_IN_GMPLUGIN_PROCESS
107 # Maps ProcessSelector constants to the name of the corresponding
108 # Module::ProcessSelector enum value.
109 PROCESSES = {
110 ProcessSelector.ANY_PROCESS: "ANY_PROCESS",
111 ProcessSelector.MAIN_PROCESS_ONLY: "MAIN_PROCESS_ONLY",
112 ProcessSelector.CONTENT_PROCESS_ONLY: "CONTENT_PROCESS_ONLY",
113 ProcessSelector.ALLOW_IN_GPU_PROCESS: "ALLOW_IN_GPU_PROCESS",
114 ProcessSelector.ALLOW_IN_VR_PROCESS: "ALLOW_IN_VR_PROCESS",
115 ProcessSelector.ALLOW_IN_SOCKET_PROCESS: "ALLOW_IN_SOCKET_PROCESS",
116 ProcessSelector.ALLOW_IN_RDD_PROCESS: "ALLOW_IN_RDD_PROCESS",
117 ProcessSelector.ALLOW_IN_GPU_AND_MAIN_PROCESS: "ALLOW_IN_GPU_AND_MAIN_PROCESS",
118 ProcessSelector.ALLOW_IN_GPU_AND_SOCKET_PROCESS: "ALLOW_IN_GPU_AND_SOCKET_PROCESS",
119 ProcessSelector.ALLOW_IN_GPU_AND_VR_PROCESS: "ALLOW_IN_GPU_AND_VR_PROCESS",
120 ProcessSelector.ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS: "ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS",
121 ProcessSelector.ALLOW_IN_RDD_AND_SOCKET_PROCESS: "ALLOW_IN_RDD_AND_SOCKET_PROCESS",
122 ProcessSelector.ALLOW_IN_GPU_RDD_AND_SOCKET_PROCESS: "ALLOW_IN_GPU_RDD_AND_SOCKET_PROCESS",
123 ProcessSelector.ALLOW_IN_GPU_RDD_SOCKET_AND_UTILITY_PROCESS: "ALLOW_IN_GPU_RDD_SOCKET_AND_UTILITY_PROCESS", # NOQA: E501
124 ProcessSelector.ALLOW_IN_GPU_RDD_VR_AND_SOCKET_PROCESS: "ALLOW_IN_GPU_RDD_VR_AND_SOCKET_PROCESS", # NOQA: E501
125 ProcessSelector.ALLOW_IN_GPU_RDD_VR_SOCKET_AND_UTILITY_PROCESS: "ALLOW_IN_GPU_RDD_VR_SOCKET_AND_UTILITY_PROCESS", # NOQA: E501
126 ProcessSelector.ALLOW_IN_GPU_RDD_VR_SOCKET_UTILITY_AND_GMPLUGIN_PROCESS: "ALLOW_IN_GPU_RDD_VR_SOCKET_UTILITY_AND_GMPLUGIN_PROCESS", # NOQA: E501
130 # Emits the C++ symbolic constant corresponding to a ProcessSelector constant.
131 def lower_processes(processes):
132 return "Module::ProcessSelector::%s" % PROCESSES[processes]
135 # Emits the C++ symbolic constant for a ModuleEntry's ModuleID enum entry.
136 def lower_module_id(module):
137 return "ModuleID::%s" % module.name
140 # Corresponds to the Module::BackgroundTasksSelector enum in Module.h. The
141 # actual values don't matter, since the code generator emits symbolic constants
142 # for these values, but we use the same values as the enum constants for
143 # clarity.
144 class BackgroundTasksSelector:
145 NO_TASKS = 0x0
146 ALL_TASKS = 0xFFFF
149 # Maps BackgroundTasksSelector constants to the name of the corresponding
150 # Module::BackgroundTasksSelector enum value.
151 BACKGROUNDTASKS = {
152 BackgroundTasksSelector.ALL_TASKS: "ALL_TASKS",
153 BackgroundTasksSelector.NO_TASKS: "NO_TASKS",
157 # Emits the C++ symbolic constant corresponding to a BackgroundTasks constant.
158 def lower_backgroundtasks(backgroundtasks):
159 return "Module::BackgroundTasksSelector::%s" % BACKGROUNDTASKS[backgroundtasks]
162 # Represents a static string table, indexed by offset. This allows us to
163 # reference strings from static data structures without requiring runtime
164 # relocations.
165 class StringTable(object):
166 def __init__(self):
167 self.entries = {}
168 self.entry_list = []
169 self.size = 0
171 self._serialized = False
173 # Returns the index of the given string in the `entry_list` array. If
174 # no entry for the string exists, it first creates one.
175 def get_idx(self, string):
176 idx = self.entries.get(string, None)
177 if idx is not None:
178 return idx
180 assert not self._serialized
182 assert len(string) == len(string.encode("utf-8"))
184 idx = self.size
185 self.size += len(string) + 1
187 self.entries[string] = idx
188 self.entry_list.append(string)
189 return idx
191 # Returns the C++ code representing string data of this string table, as a
192 # single string literal. This must only be called after the last call to
193 # `get_idx()` or `entry_to_cxx()` for this instance.
194 def to_cxx(self):
195 self._serialized = True
197 lines = []
199 idx = 0
200 for entry in self.entry_list:
201 str_ = entry.replace("\\", "\\\\").replace('"', r"\"").replace("\n", r"\n")
203 lines.append(' /* 0x%x */ "%s\\0"\n' % (idx, str_))
205 idx += len(entry) + 1
207 return "".join(lines)
209 # Returns a `StringEntry` struct initializer for the string table entry
210 # corresponding to the given string. If no matching entry exists, it is
211 # first created.
212 def entry_to_cxx(self, string):
213 idx = self.get_idx(string)
214 return "{ 0x%x } /* %s */" % (idx, pretty_string(string))
217 strings = StringTable()
219 interfaces = []
222 # Represents a C++ namespace, containing a set of classes and potentially
223 # sub-namespaces. This is used to generate pre-declarations for incomplete
224 # types referenced in XPCOM manifests.
225 class Namespace(object):
226 def __init__(self, name=None):
227 self.name = name
228 self.classes = set()
229 self.namespaces = {}
231 # Returns a Namespace object for the sub-namespace with the given name.
232 def sub(self, name):
233 assert name not in self.classes
235 if name not in self.namespaces:
236 self.namespaces[name] = Namespace(name)
237 return self.namespaces[name]
239 # Generates C++ code to pre-declare all classes in this namespace and all
240 # of its sub-namespaces.
241 def to_cxx(self):
242 res = ""
243 if self.name:
244 res += "namespace %s {\n" % self.name
246 for clas in sorted(self.classes):
247 res += "class %s;\n" % clas
249 for ns in sorted(self.namespaces.keys()):
250 res += self.namespaces[ns].to_cxx()
252 if self.name:
253 res += "} // namespace %s\n" % self.name
255 return res
258 # Represents a component defined in an XPCOM manifest's `Classes` array.
259 class ModuleEntry(object):
260 next_anon_id = 0
262 def __init__(self, data, init_idx):
263 self.cid = UUIDRepr(UUID(data["cid"]))
264 self.contract_ids = data.get("contract_ids", [])
265 self.type = data.get("type", "nsISupports")
266 self.categories = data.get("categories", {})
267 self.processes = data.get("processes", 0)
268 self.headers = data.get("headers", [])
270 self.js_name = data.get("js_name", None)
271 self.interfaces = data.get("interfaces", [])
273 if len(self.interfaces) > 255:
274 raise Exception(
275 "JS service %s may not have more than 255 " "interfaces" % self.js_name
278 self.interfaces_offset = len(interfaces)
279 for iface in self.interfaces:
280 interfaces.append(iface)
282 # If the manifest declares Init or Unload functions, this contains its
283 # index, as understood by the `CallInitFunc()` function.
285 # If it contains any value other than `None`, a corresponding
286 # `CallInitFunc(init_idx)` call will be genrated before calling this
287 # module's constructor.
288 self.init_idx = init_idx
290 self.constructor = data.get("constructor", None)
291 self.legacy_constructor = data.get("legacy_constructor", None)
292 self.init_method = data.get("init_method", [])
294 self.jsm = data.get("jsm", None)
295 self.esModule = data.get("esModule", None)
297 self.external = data.get(
298 "external", not (self.headers or self.legacy_constructor)
300 self.singleton = data.get("singleton", False)
301 self.overridable = data.get("overridable", False)
303 self.protocol_config = data.get("protocol_config", None)
305 if "name" in data:
306 self.anonymous = False
307 self.name = data["name"]
308 else:
309 self.anonymous = True
310 self.name = "Anonymous%03d" % ModuleEntry.next_anon_id
311 ModuleEntry.next_anon_id += 1
313 def error(str_):
314 raise Exception(
315 "Error defining component %s (%s): %s"
316 % (str(self.cid), ", ".join(map(repr, self.contract_ids)), str_)
319 if self.jsm:
320 if not self.constructor:
321 error("JavaScript components must specify a constructor")
323 for prop in ("init_method", "legacy_constructor", "headers"):
324 if getattr(self, prop):
325 error(
326 "JavaScript components may not specify a '%s' "
327 "property" % prop
329 elif self.esModule:
330 if not self.constructor:
331 error("JavaScript components must specify a constructor")
333 for prop in ("init_method", "legacy_constructor", "headers"):
334 if getattr(self, prop):
335 error(
336 "JavaScript components may not specify a '%s' "
337 "property" % prop
339 elif self.external:
340 if self.constructor or self.legacy_constructor:
341 error(
342 "Externally-constructed components may not specify "
343 "'constructor' or 'legacy_constructor' properties"
345 if self.init_method:
346 error(
347 "Externally-constructed components may not specify "
348 "'init_method' properties"
350 if self.type == "nsISupports":
351 error(
352 "Externally-constructed components must specify a type "
353 "other than nsISupports"
356 if self.constructor and self.legacy_constructor:
357 error(
358 "The 'constructor' and 'legacy_constructor' properties "
359 "are mutually exclusive"
362 if self.overridable and not self.contract_ids:
363 error("Overridable components must specify at least one contract " "ID")
365 @property
366 def contract_id(self):
367 return self.contract_ids[0]
369 # Generates the C++ code for a StaticModule struct initializer
370 # representing this component.
371 def to_cxx(self):
372 contract_id = (
373 strings.entry_to_cxx(self.contract_id)
374 if self.overridable
375 else "{ 0x%x }" % NO_CONTRACT_ID
378 return """
379 /* {name} */ {{
380 /* {{{cid_string}}} */
381 {cid},
382 {contract_id},
383 {processes},
384 }}""".format(
385 name=self.name,
386 cid=self.cid.to_cxx(),
387 cid_string=str(self.cid),
388 contract_id=contract_id,
389 processes=lower_processes(self.processes),
392 # Generates the C++ code for a JSServiceEntry representing this module.
393 def lower_js_service(self):
394 return """
396 {js_name},
397 ModuleID::{name},
398 {{ {iface_offset} }},
399 {iface_count}
400 }}""".format(
401 js_name=strings.entry_to_cxx(self.js_name),
402 name=self.name,
403 iface_offset=self.interfaces_offset,
404 iface_count=len(self.interfaces),
407 # Generates the C++ code necessary to construct an instance of this
408 # component.
410 # This code lives in a function with the following arguments:
412 # - aIID: The `const nsIID&` interface ID that the resulting instance
413 # will be queried to.
415 # - aResult: The `void**` pointer in which to store the result.
417 # And which returns an `nsresult` indicating success or failure.
418 def lower_constructor(self):
419 res = ""
421 if self.init_idx is not None:
422 res += " MOZ_TRY(CallInitFunc(%d));\n" % self.init_idx
424 if self.legacy_constructor:
425 res += (
426 " return /* legacy */ %s(aIID, aResult);\n"
427 % self.legacy_constructor
429 return res
431 if self.jsm:
432 res += (
433 " nsCOMPtr<nsISupports> inst;\n"
434 " MOZ_TRY(ConstructJSMComponent(nsLiteralCString(%s),\n"
435 " %s,\n"
436 " getter_AddRefs(inst)));"
437 "\n" % (json.dumps(self.jsm), json.dumps(self.constructor))
439 elif self.esModule:
440 res += (
441 " nsCOMPtr<nsISupports> inst;\n"
442 " MOZ_TRY(ConstructESModuleComponent(nsLiteralCString(%s),\n"
443 " %s,\n"
444 " getter_AddRefs(inst)));"
445 "\n" % (json.dumps(self.esModule), json.dumps(self.constructor))
447 elif self.external:
448 res += (
449 " nsCOMPtr<nsISupports> inst = "
450 "mozCreateComponent<%s>();\n" % self.type
452 # The custom constructor may return null, so check before calling
453 # any methods.
454 res += " NS_ENSURE_TRUE(inst, NS_ERROR_FAILURE);\n"
455 else:
456 res += " RefPtr<%s> inst = " % self.type
458 if not self.constructor:
459 res += "new %s();\n" % self.type
460 else:
461 res += "%s();\n" % self.constructor
462 # The `new` operator is infallible, so we don't need to worry
463 # about it returning null, but custom constructors may, so
464 # check before calling any methods.
465 res += " NS_ENSURE_TRUE(inst, NS_ERROR_OUT_OF_MEMORY);\n"
467 # Check that the constructor function returns an appropriate
468 # `already_AddRefed` value for our declared type.
469 res += """
470 using T =
471 RemoveAlreadyAddRefed<decltype(%(constructor)s())>::Type;
472 static_assert(
473 std::is_same_v<already_AddRefed<T>, decltype(%(constructor)s())>,
474 "Singleton constructor must return already_AddRefed");
475 static_assert(
476 std::is_base_of<%(type)s, T>::value,
477 "Singleton constructor must return correct already_AddRefed");
479 """ % {
480 "type": self.type,
481 "constructor": self.constructor,
484 if self.init_method:
485 res += " MOZ_TRY(inst->%s());\n" % self.init_method
487 res += " return inst->QueryInterface(aIID, aResult);\n"
489 return res
491 # Generates the C++ code for the `mozilla::components::<name>` entry
492 # corresponding to this component. This may not be called for modules
493 # without an explicit `name` (in which cases, `self.anonymous` will be
494 # true).
495 def lower_getters(self):
496 assert not self.anonymous
498 substs = {
499 "name": self.name,
500 "id": "::mozilla::xpcom::ModuleID::%s" % self.name,
503 res = (
505 namespace %(name)s {
506 static inline const nsID& CID() {
507 return ::mozilla::xpcom::Components::GetCID(%(id)s);
510 static inline ::mozilla::xpcom::GetServiceHelper Service(nsresult* aRv = nullptr) {
511 return {%(id)s, aRv};
514 % substs
517 if not self.singleton:
518 res += (
520 static inline ::mozilla::xpcom::CreateInstanceHelper Create(nsresult* aRv = nullptr) {
521 return {%(id)s, aRv};
524 % substs
527 res += (
528 """\
529 } // namespace %(name)s
531 % substs
534 return res
536 # Generates the rust code for the `xpcom::components::<name>` entry
537 # corresponding to this component. This may not be called for modules
538 # without an explicit `name` (in which cases, `self.anonymous` will be
539 # true).
540 def lower_getters_rust(self):
541 assert not self.anonymous
543 substs = {
544 "name": self.name,
545 "id": "super::ModuleID::%s" % self.name,
548 res = (
550 #[allow(non_snake_case)]
551 pub mod %(name)s {
552 /// Get the singleton service instance for this component.
553 pub fn service<T: crate::XpCom>() -> Result<crate::RefPtr<T>, nserror::nsresult> {
554 let mut ga = crate::GetterAddrefs::<T>::new();
555 let rv = unsafe { super::Gecko_GetServiceByModuleID(%(id)s, &T::IID, ga.void_ptr()) };
556 if rv.failed() {
557 return Err(rv);
559 ga.refptr().ok_or(nserror::NS_ERROR_NO_INTERFACE)
562 % substs
565 if not self.singleton:
566 res += (
568 /// Create a new instance of this component.
569 pub fn create<T: crate::XpCom>() -> Result<crate::RefPtr<T>, nserror::nsresult> {
570 let mut ga = crate::GetterAddrefs::<T>::new();
571 let rv = unsafe { super::Gecko_CreateInstanceByModuleID(%(id)s, &T::IID, ga.void_ptr()) };
572 if rv.failed() {
573 return Err(rv);
575 ga.refptr().ok_or(nserror::NS_ERROR_NO_INTERFACE)
578 % substs
581 res += """\
585 return res
588 # Returns a quoted string literal representing the given raw string, with
589 # certain special characters replaced so that it can be used in a C++-style
590 # (/* ... */) comment.
591 def pretty_string(string):
592 return json.dumps(string).replace("*/", r"*\/").replace("/*", r"/\*")
595 # Represents a static contract ID entry, corresponding to a C++ ContractEntry
596 # struct, mapping a contract ID to a static module entry.
597 class ContractEntry(object):
598 def __init__(self, contract, module):
599 self.contract = contract
600 self.module = module
602 def to_cxx(self):
603 return """
605 {contract},
606 {module_id},
607 }}""".format(
608 contract=strings.entry_to_cxx(self.contract),
609 module_id=lower_module_id(self.module),
613 # Represents a static ProtocolHandler entry, corresponding to a C++
614 # ProtocolEntry struct, mapping a scheme to a static module entry and metadata.
615 class ProtocolHandler(object):
616 def __init__(self, config, module):
617 def error(str_):
618 raise Exception(
619 "Error defining protocol handler %s (%s): %s"
620 % (str(module.cid), ", ".join(map(repr, module.contract_ids)), str_)
623 self.module = module
624 self.scheme = config.get("scheme", None)
625 if self.scheme is None:
626 error("No scheme defined for protocol component")
627 self.flags = config.get("flags", None)
628 if self.flags is None:
629 error("No flags defined for protocol component")
630 self.default_port = config.get("default_port", -1)
631 self.has_dynamic_flags = config.get("has_dynamic_flags", False)
633 def to_cxx(self):
634 return """
636 .mScheme = {scheme},
637 .mProtocolFlags = {flags},
638 .mDefaultPort = {default_port},
639 .mModuleID = {module_id},
640 .mHasDynamicFlags = {has_dynamic_flags},
642 """.format(
643 scheme=strings.entry_to_cxx(self.scheme),
644 module_id=lower_module_id(self.module),
645 flags=" | ".join("nsIProtocolHandler::%s" % flag for flag in self.flags),
646 default_port=self.default_port,
647 has_dynamic_flags="true" if self.has_dynamic_flags else "false",
651 # Generates the C++ code for the StaticCategoryEntry and StaticCategory
652 # structs for all category entries declared in XPCOM manifests.
653 def gen_categories(substs, categories):
654 cats = []
655 ents = []
657 count = 0
658 for category, entries in sorted(categories.items()):
660 def k(entry):
661 return tuple(entry[0]["name"]) + entry[1:]
663 entries.sort(key=k)
665 cats.append(
666 " { %s,\n"
667 " %d, %d },\n" % (strings.entry_to_cxx(category), count, len(entries))
669 count += len(entries)
671 ents.append(" /* %s */\n" % pretty_string(category))
672 for entry, value, processes in entries:
673 name = entry["name"]
674 backgroundtasks = entry.get(
675 "backgroundtasks", BackgroundTasksSelector.NO_TASKS
678 ents.append(
679 " { %s,\n"
680 " %s,\n"
681 " %s,\n"
682 " %s },\n"
684 strings.entry_to_cxx(name),
685 strings.entry_to_cxx(value),
686 lower_backgroundtasks(backgroundtasks),
687 lower_processes(processes),
690 ents.append("\n")
691 ents.pop()
693 substs["category_count"] = len(cats)
694 substs["categories"] = "".join(cats)
695 substs["category_entries"] = "".join(ents)
698 # Generates the C++ code for all Init and Unload functions declared in XPCOM
699 # manifests. These form the bodies of the `CallInitFunc()` and `CallUnload`
700 # functions in StaticComponents.cpp.
701 def gen_module_funcs(substs, funcs):
702 inits = []
703 unloads = []
705 template = """\
706 case %d:
708 break;
711 for i, (init, unload) in enumerate(funcs):
712 init_code = "%s();" % init if init else "/* empty */"
713 inits.append(template % (i, init_code))
715 if unload:
716 unloads.append(
717 """\
718 if (CalledInit(%d)) {
719 %s();
722 % (i, unload)
725 substs["init_funcs"] = "".join(inits)
726 substs["unload_funcs"] = "".join(unloads)
727 substs["init_count"] = len(funcs)
730 def gen_interfaces(ifaces):
731 res = []
732 for iface in ifaces:
733 res.append(" nsXPTInterface::%s,\n" % iface)
734 return "".join(res)
737 # Generates class pre-declarations for any types referenced in `Classes` array
738 # entries which do not have corresponding `headers` entries to fully declare
739 # their types.
740 def gen_decls(types):
741 root_ns = Namespace()
743 for type_ in sorted(types):
744 parts = type_.split("::")
746 ns = root_ns
747 for part in parts[:-1]:
748 ns = ns.sub(part)
749 ns.classes.add(parts[-1])
751 return root_ns.to_cxx()
754 # Generates the `switch` body for the `CreateInstanceImpl()` function, with a
755 # `case` for each value in ModuleID to construct an instance of the
756 # corresponding component.
757 def gen_constructors(entries):
758 constructors = []
759 for entry in entries:
760 constructors.append(
761 """\
762 case {id}: {{
763 {constructor}\
765 """.format(
766 id=lower_module_id(entry), constructor=entry.lower_constructor()
770 return "".join(constructors)
773 # Generates the getter code for each named component entry in the
774 # `mozilla::components::` namespace.
775 def gen_getters(entries):
776 entries = list(entries)
777 entries.sort(key=lambda e: e.name)
779 return "".join(entry.lower_getters() for entry in entries if not entry.anonymous)
782 # Generates the rust getter code for each named component entry in the
783 # `xpcom::components::` module.
784 def gen_getters_rust(entries):
785 entries = list(entries)
786 entries.sort(key=lambda e: e.name)
788 return "".join(
789 entry.lower_getters_rust() for entry in entries if not entry.anonymous
793 def gen_includes(substs, all_headers):
794 headers = set()
795 absolute_headers = set()
797 for header in all_headers:
798 if header.startswith("/"):
799 absolute_headers.add(header)
800 else:
801 headers.add(header)
803 includes = ['#include "%s"' % header for header in sorted(headers)]
804 substs["includes"] = "\n".join(includes) + "\n"
806 relative_includes = [
807 '#include "../..%s"' % header for header in sorted(absolute_headers)
809 substs["relative_includes"] = "\n".join(relative_includes) + "\n"
812 def to_category_list(val):
813 # Entries can be bare strings (like `"m-browser"`), lists of bare strings,
814 # or dictionaries (like `{"name": "m-browser", "backgroundtasks":
815 # BackgroundTasksSelector.ALL_TASKS}`), somewhat recursively.
817 def ensure_dict(v):
818 # Turn `v` into `{"name": v}` if it's not already a dict.
819 if isinstance(v, dict):
820 return v
821 return {"name": v}
823 if isinstance(val, (list, tuple)):
824 return tuple(ensure_dict(v) for v in val)
826 if isinstance(val, dict):
827 # Explode `{"name": ["x", "y"], "backgroundtasks": ...}` into
828 # `[{"name": "x", "backgroundtasks": ...}, {"name": "y", "backgroundtasks": ...}]`.
829 names = val.pop("name")
831 vals = []
832 for entry in to_category_list(names):
833 d = dict(val)
834 d["name"] = entry["name"]
835 vals.append(d)
837 return tuple(vals)
839 return (ensure_dict(val),)
842 def gen_substs(manifests):
843 module_funcs = []
845 headers = set()
847 modules = []
848 categories = defaultdict(list)
850 for manifest in manifests:
851 headers |= set(manifest.get("Headers", []))
853 init_idx = None
854 init = manifest.get("InitFunc")
855 unload = manifest.get("UnloadFunc")
856 if init or unload:
857 init_idx = len(module_funcs)
858 module_funcs.append((init, unload))
860 for clas in manifest["Classes"]:
861 modules.append(ModuleEntry(clas, init_idx))
863 for category, entries in manifest.get("Categories", {}).items():
864 for key, entry in entries.items():
865 if isinstance(entry, tuple):
866 value, process = entry
867 else:
868 value, process = entry, 0
869 categories[category].append(({"name": key}, value, process))
871 cids = set()
872 contracts = []
873 contract_map = {}
874 js_services = {}
875 protocol_handlers = {}
877 jsms = set()
878 esModules = set()
880 types = set()
882 for mod in modules:
883 headers |= set(mod.headers)
885 for contract_id in mod.contract_ids:
886 if contract_id in contract_map:
887 raise Exception("Duplicate contract ID: %s" % contract_id)
889 entry = ContractEntry(contract_id, mod)
890 contracts.append(entry)
891 contract_map[contract_id] = entry
893 for category, entries in mod.categories.items():
894 for entry in to_category_list(entries):
895 categories[category].append((entry, mod.contract_id, mod.processes))
897 if mod.type and not mod.headers:
898 types.add(mod.type)
900 if mod.jsm:
901 jsms.add(mod.jsm)
903 if mod.esModule:
904 esModules.add(mod.esModule)
906 if mod.js_name:
907 if mod.js_name in js_services:
908 raise Exception("Duplicate JS service name: %s" % mod.js_name)
909 js_services[mod.js_name] = mod
911 if mod.protocol_config:
912 handler = ProtocolHandler(mod.protocol_config, mod)
913 if handler.scheme in protocol_handlers:
914 raise Exception("Duplicate protocol handler: %s" % handler.scheme)
915 protocol_handlers[handler.scheme] = handler
917 if str(mod.cid) in cids:
918 raise Exception("Duplicate cid: %s" % str(mod.cid))
919 cids.add(str(mod.cid))
921 cid_phf = PerfectHash(modules, PHF_SIZE, key=lambda module: module.cid.bytes)
923 contract_phf = PerfectHash(
924 contracts, PHF_SIZE, key=lambda entry: entry.contract.encode()
927 js_services_phf = PerfectHash(
928 list(js_services.values()), PHF_SIZE, key=lambda entry: entry.js_name.encode()
931 protocol_handlers_phf = PerfectHash(
932 list(protocol_handlers.values()),
933 TINY_PHF_SIZE,
934 key=lambda entry: entry.scheme.encode(),
937 js_services_json = {}
938 for entry in js_services.values():
939 for iface in entry.interfaces:
940 js_services_json[iface] = entry.js_name
942 substs = {}
944 gen_categories(substs, categories)
946 substs["module_ids"] = "".join(" %s,\n" % entry.name for entry in cid_phf.entries)
948 substs["module_count"] = len(modules)
949 substs["contract_count"] = len(contracts)
950 substs["protocol_handler_count"] = len(protocol_handlers)
952 substs["default_protocol_handler_idx"] = protocol_handlers_phf.get_index(b"default")
954 gen_module_funcs(substs, module_funcs)
956 gen_includes(substs, headers)
958 substs["component_jsms"] = (
959 "\n".join(" %s," % strings.entry_to_cxx(jsm) for jsm in sorted(jsms)) + "\n"
961 substs["define_has_component_jsms"] = (
962 "#define HAS_COMPONENT_JSMS" if len(jsms) > 0 else ""
964 substs["component_esmodules"] = (
965 "\n".join(
966 " %s," % strings.entry_to_cxx(esModule) for esModule in sorted(esModules)
968 + "\n"
971 substs["interfaces"] = gen_interfaces(interfaces)
973 substs["decls"] = gen_decls(types)
975 substs["constructors"] = gen_constructors(cid_phf.entries)
977 substs["component_getters"] = gen_getters(cid_phf.entries)
979 substs["component_getters_rust"] = gen_getters_rust(cid_phf.entries)
981 substs["module_cid_table"] = cid_phf.cxx_codegen(
982 name="ModuleByCID",
983 entry_type="StaticModule",
984 entries_name="gStaticModules",
985 lower_entry=lambda entry: entry.to_cxx(),
986 return_type="const StaticModule*",
987 return_entry=(
988 "return entry.CID().Equals(aKey) && entry.Active()" " ? &entry : nullptr;"
990 key_type="const nsID&",
991 key_bytes="reinterpret_cast<const char*>(&aKey)",
992 key_length="sizeof(nsID)",
995 substs["module_contract_id_table"] = contract_phf.cxx_codegen(
996 name="LookupContractID",
997 entry_type="ContractEntry",
998 entries_name="gContractEntries",
999 lower_entry=lambda entry: entry.to_cxx(),
1000 return_type="const ContractEntry*",
1001 return_entry="return entry.Matches(aKey) ? &entry : nullptr;",
1002 key_type="const nsACString&",
1003 key_bytes="aKey.BeginReading()",
1004 key_length="aKey.Length()",
1007 substs["js_services_table"] = js_services_phf.cxx_codegen(
1008 name="LookupJSService",
1009 entry_type="JSServiceEntry",
1010 entries_name="gJSServices",
1011 lower_entry=lambda entry: entry.lower_js_service(),
1012 return_type="const JSServiceEntry*",
1013 return_entry="return entry.Name() == aKey ? &entry : nullptr;",
1014 key_type="const nsACString&",
1015 key_bytes="aKey.BeginReading()",
1016 key_length="aKey.Length()",
1019 substs["protocol_handlers_table"] = protocol_handlers_phf.cxx_codegen(
1020 name="LookupProtocolHandler",
1021 entry_type="StaticProtocolHandler",
1022 entries_name="gStaticProtocolHandlers",
1023 lower_entry=lambda entry: entry.to_cxx(),
1024 return_type="const StaticProtocolHandler*",
1025 return_entry="return entry.Scheme() == aKey ? &entry : nullptr;",
1026 key_type="const nsACString&",
1027 key_bytes="aKey.BeginReading()",
1028 key_length="aKey.Length()",
1031 substs["js_services_json"] = (
1032 json.dumps(js_services_json, sort_keys=True, indent=2) + "\n"
1035 # Do this only after everything else has been emitted so we're sure the
1036 # string table is complete.
1037 substs["strings"] = strings.to_cxx()
1038 return substs
1041 # Returns true if the given build config substitution is defined and truthy.
1042 def defined(subst):
1043 return bool(buildconfig.substs.get(subst))
1046 def read_manifest(filename):
1047 glbl = {
1048 "buildconfig": buildconfig,
1049 "defined": defined,
1050 "ProcessSelector": ProcessSelector,
1051 "BackgroundTasksSelector": BackgroundTasksSelector,
1053 code = compile(open(filename).read(), filename, "exec")
1054 exec(code, glbl)
1055 return glbl
1058 def main(fd, conf_file, template_file):
1059 def open_output(filename):
1060 return FileAvoidWrite(os.path.join(os.path.dirname(fd.name), filename))
1062 conf = json.load(open(conf_file, "r"))
1064 deps = set()
1066 manifests = []
1067 for filename in conf["manifests"]:
1068 deps.add(filename)
1069 manifest = read_manifest(filename)
1070 manifests.append(manifest)
1071 manifest.setdefault("Priority", 50)
1072 manifest["__filename__"] = filename
1074 manifests.sort(key=lambda man: (man["Priority"], man["__filename__"]))
1076 substs = gen_substs(manifests)
1078 def replacer(match):
1079 return substs[match.group(1)]
1081 with open_output("StaticComponents.cpp") as fh:
1082 with open(template_file, "r") as tfh:
1083 template = tfh.read()
1085 fh.write(re.sub(r"//# @([a-zA-Z_]+)@\n", replacer, template))
1087 with open_output("StaticComponentData.h") as fh:
1088 fh.write(
1089 """\
1090 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
1091 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
1092 /* This Source Code Form is subject to the terms of the Mozilla Public
1093 * License, v. 2.0. If a copy of the MPL was not distributed with this
1094 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
1096 #ifndef StaticComponentData_h
1097 #define StaticComponentData_h
1099 #include <stddef.h>
1101 namespace mozilla {
1102 namespace xpcom {
1104 static constexpr size_t kStaticModuleCount = %(module_count)d;
1106 static constexpr size_t kContractCount = %(contract_count)d;
1108 static constexpr size_t kStaticCategoryCount = %(category_count)d;
1110 static constexpr size_t kModuleInitCount = %(init_count)d;
1112 static constexpr size_t kStaticProtocolHandlerCount = %(protocol_handler_count)d;
1114 static constexpr size_t kDefaultProtocolHandlerIndex = %(default_protocol_handler_idx)d;
1116 } // namespace xpcom
1117 } // namespace mozilla
1119 #endif
1121 % substs
1124 with open_output("components.rs") as fh:
1125 fh.write(
1126 """\
1127 /// Unique IDs for each statically-registered module.
1128 #[repr(u16)]
1129 pub enum ModuleID {
1130 %(module_ids)s
1133 %(component_getters_rust)s
1135 % substs
1138 fd.write(
1139 """\
1140 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
1141 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
1142 /* This Source Code Form is subject to the terms of the Mozilla Public
1143 * License, v. 2.0. If a copy of the MPL was not distributed with this
1144 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
1146 #ifndef mozilla_Components_h
1147 #define mozilla_Components_h
1149 #include "nsCOMPtr.h"
1151 struct nsID;
1153 #define NS_IMPL_COMPONENT_FACTORY(iface) \\
1154 template <> \\
1155 already_AddRefed<nsISupports> mozCreateComponent<iface>()
1157 template <typename T>
1158 already_AddRefed<nsISupports> mozCreateComponent();
1160 namespace mozilla {
1161 namespace xpcom {
1163 enum class ModuleID : uint16_t {
1164 %(module_ids)s
1167 // May be added as a friend function to allow constructing services via
1168 // private constructors and init methods.
1169 nsresult CreateInstanceImpl(ModuleID aID, const nsIID& aIID, void** aResult);
1171 class MOZ_STACK_CLASS StaticModuleHelper : public nsCOMPtr_helper {
1172 public:
1173 StaticModuleHelper(ModuleID aId, nsresult* aErrorPtr)
1174 : mId(aId), mErrorPtr(aErrorPtr) {}
1176 protected:
1177 nsresult SetResult(nsresult aRv) const {
1178 if (mErrorPtr) {
1179 *mErrorPtr = aRv;
1181 return aRv;
1184 ModuleID mId;
1185 nsresult* mErrorPtr;
1188 class MOZ_STACK_CLASS GetServiceHelper final : public StaticModuleHelper {
1189 public:
1190 using StaticModuleHelper::StaticModuleHelper;
1192 nsresult NS_FASTCALL operator()(const nsIID& aIID,
1193 void** aResult) const override;
1196 class MOZ_STACK_CLASS CreateInstanceHelper final : public StaticModuleHelper {
1197 public:
1198 using StaticModuleHelper::StaticModuleHelper;
1200 nsresult NS_FASTCALL operator()(const nsIID& aIID,
1201 void** aResult) const override;
1204 class Components final {
1205 public:
1206 static const nsID& GetCID(ModuleID aID);
1209 } // namespace xpcom
1211 namespace components {
1212 %(component_getters)s
1213 } // namespace components
1215 } // namespace mozilla
1217 #endif
1219 % substs
1222 with open_output("services.json") as fh:
1223 fh.write(substs["js_services_json"])
1225 return deps