forked from mirrors/gecko-dev
		
	 760cc7e936
			
		
	
	
		760cc7e936
		
	
	
	
	
		
			
			- Add missing include directives and forward declarations. - Remove some extra include directives. - Add missing namespace qualifications. - Move include directives out of namespace in toolkit/xre/GlobalSemaphore.h Differential Revision: https://phabricator.services.mozilla.com/D98894
		
			
				
	
	
		
			1009 lines
		
	
	
	
		
			30 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			1009 lines
		
	
	
	
		
			30 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
 | |
| 
 | |
| # 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
 | |
| 
 | |
|         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",  # NOQA: E501
 | |
| }
 | |
| 
 | |
| 
 | |
| # 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 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 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 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()):
 | |
| 
 | |
|         def k(entry):
 | |
|             return tuple(entry[0]["name"]) + entry[1:]
 | |
| 
 | |
|         entries.sort(key=k)
 | |
| 
 | |
|         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:
 | |
|             name = entry["name"]
 | |
|             backgroundtasks = entry.get(
 | |
|                 "backgroundtasks", BackgroundTasksSelector.NO_TASKS
 | |
|             )
 | |
| 
 | |
|             ents.append(
 | |
|                 "  { %s,\n"
 | |
|                 "    %s,\n"
 | |
|                 "    %s,\n"
 | |
|                 "    %s },\n"
 | |
|                 % (
 | |
|                     strings.entry_to_cxx(name),
 | |
|                     strings.entry_to_cxx(value),
 | |
|                     lower_backgroundtasks(backgroundtasks),
 | |
|                     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_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)
 | |
| 
 | |
|     if isinstance(val, dict):
 | |
|         # Explode `{"name": ["x", "y"], "backgroundtasks": ...}` into
 | |
|         # `[{"name": "x", "backgroundtasks": ...}, {"name": "y", "backgroundtasks": ...}]`.
 | |
|         names = val.pop("name")
 | |
| 
 | |
|         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((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_category_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
 | |
|     )
 | |
| 
 | |
|     js_services_json = {}
 | |
|     for entry in js_services.values():
 | |
|         for iface in entry.interfaces:
 | |
|             js_services_json[iface] = 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()",
 | |
|     )
 | |
| 
 | |
|     substs["js_services_json"] = json.dumps(js_services_json, sort_keys=True, indent=4)
 | |
| 
 | |
|     # 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,
 | |
|         "BackgroundTasksSelector": BackgroundTasksSelector,
 | |
|     }
 | |
|     code = compile(open(filename).read(), filename, "exec")
 | |
|     exec(code, 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
 | |
| 
 | |
| #include <stddef.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
 | |
|     )
 | |
| 
 | |
|     with open_output("services.json") as fh:
 | |
|         fh.write(substs["js_services_json"])
 | |
| 
 | |
|     return deps
 |