1import json 2import os 3import re 4import struct 5from collections import defaultdict 6 7from uuid import UUID 8 9from mozbuild.util import FileAvoidWrite 10from perfecthash import PerfectHash 11import buildconfig 12 13 14NO_CONTRACT_ID = 0xFFFFFFFF 15 16PHF_SIZE = 512 17 18# In tests, we might not have a (complete) buildconfig. 19ENDIAN = ( 20 "<" if buildconfig.substs.get("TARGET_ENDIANNESS", "little") == "little" else ">" 21) 22 23 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. 26class UUIDRepr(object): 27 def __init__(self, uuid): 28 self.uuid = uuid 29 30 fields = uuid.fields 31 32 self.a = fields[0] 33 self.b = fields[1] 34 self.c = fields[2] 35 36 d = list(fields[3:5]) 37 for i in range(0, 6): 38 d.append(fields[5] >> (8 * (5 - i)) & 0xFF) 39 40 self.d = tuple(d) 41 42 def __str__(self): 43 return str(self.uuid) 44 45 @property 46 def bytes(self): 47 return struct.pack(ENDIAN + "IHHBBBBBBBB", self.a, self.b, self.c, *self.d) 48 49 def to_cxx(self): 50 rest = ", ".join("0x%02x" % b for b in self.d) 51 52 return "{ 0x%x, 0x%x, 0x%x, { %s } }" % (self.a, self.b, self.c, rest) 53 54 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. 58class ProcessSelector: 59 ANY_PROCESS = 0x0 60 MAIN_PROCESS_ONLY = 0x1 61 CONTENT_PROCESS_ONLY = 0x2 62 ALLOW_IN_GPU_PROCESS = 0x4 63 ALLOW_IN_VR_PROCESS = 0x8 64 ALLOW_IN_SOCKET_PROCESS = 0x10 65 ALLOW_IN_RDD_PROCESS = 0x20 66 ALLOW_IN_GPU_AND_MAIN_PROCESS = ALLOW_IN_GPU_PROCESS | MAIN_PROCESS_ONLY 67 ALLOW_IN_GPU_AND_SOCKET_PROCESS = ALLOW_IN_GPU_PROCESS | ALLOW_IN_SOCKET_PROCESS 68 ALLOW_IN_GPU_AND_VR_PROCESS = ALLOW_IN_GPU_PROCESS | ALLOW_IN_VR_PROCESS 69 ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS = ( 70 ALLOW_IN_GPU_PROCESS | ALLOW_IN_VR_PROCESS | ALLOW_IN_SOCKET_PROCESS 71 ) 72 ALLOW_IN_RDD_AND_SOCKET_PROCESS = ALLOW_IN_RDD_PROCESS | ALLOW_IN_SOCKET_PROCESS 73 ALLOW_IN_GPU_RDD_AND_SOCKET_PROCESS = ( 74 ALLOW_IN_GPU_PROCESS | ALLOW_IN_RDD_PROCESS | ALLOW_IN_SOCKET_PROCESS 75 ) 76 ALLOW_IN_GPU_RDD_VR_AND_SOCKET_PROCESS = ( 77 ALLOW_IN_GPU_PROCESS 78 | ALLOW_IN_RDD_PROCESS 79 | ALLOW_IN_VR_PROCESS 80 | ALLOW_IN_SOCKET_PROCESS 81 ) 82 83 84# Maps ProcessSelector constants to the name of the corresponding 85# Module::ProcessSelector enum value. 86PROCESSES = { 87 ProcessSelector.ANY_PROCESS: "ANY_PROCESS", 88 ProcessSelector.MAIN_PROCESS_ONLY: "MAIN_PROCESS_ONLY", 89 ProcessSelector.CONTENT_PROCESS_ONLY: "CONTENT_PROCESS_ONLY", 90 ProcessSelector.ALLOW_IN_GPU_PROCESS: "ALLOW_IN_GPU_PROCESS", 91 ProcessSelector.ALLOW_IN_VR_PROCESS: "ALLOW_IN_VR_PROCESS", 92 ProcessSelector.ALLOW_IN_SOCKET_PROCESS: "ALLOW_IN_SOCKET_PROCESS", 93 ProcessSelector.ALLOW_IN_RDD_PROCESS: "ALLOW_IN_RDD_PROCESS", 94 ProcessSelector.ALLOW_IN_GPU_AND_MAIN_PROCESS: "ALLOW_IN_GPU_AND_MAIN_PROCESS", 95 ProcessSelector.ALLOW_IN_GPU_AND_SOCKET_PROCESS: "ALLOW_IN_GPU_AND_SOCKET_PROCESS", 96 ProcessSelector.ALLOW_IN_GPU_AND_VR_PROCESS: "ALLOW_IN_GPU_AND_VR_PROCESS", 97 ProcessSelector.ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS: "ALLOW_IN_GPU_VR_AND_SOCKET_PROCESS", 98 ProcessSelector.ALLOW_IN_RDD_AND_SOCKET_PROCESS: "ALLOW_IN_RDD_AND_SOCKET_PROCESS", 99 ProcessSelector.ALLOW_IN_GPU_RDD_AND_SOCKET_PROCESS: "ALLOW_IN_GPU_RDD_AND_SOCKET_PROCESS", 100 ProcessSelector.ALLOW_IN_GPU_RDD_VR_AND_SOCKET_PROCESS: "ALLOW_IN_GPU_RDD_VR_AND_SOCKET_PROCESS", # NOQA: E501 101} 102 103 104# Emits the C++ symbolic constant corresponding to a ProcessSelector constant. 105def lower_processes(processes): 106 return "Module::ProcessSelector::%s" % PROCESSES[processes] 107 108 109# Emits the C++ symbolic constant for a ModuleEntry's ModuleID enum entry. 110def lower_module_id(module): 111 return "ModuleID::%s" % module.name 112 113 114# Corresponds to the Module::BackgroundTasksSelector enum in Module.h. The 115# actual values don't matter, since the code generator emits symbolic constants 116# for these values, but we use the same values as the enum constants for 117# clarity. 118class BackgroundTasksSelector: 119 NO_TASKS = 0x0 120 ALL_TASKS = 0xFFFF 121 122 123# Maps BackgroundTasksSelector constants to the name of the corresponding 124# Module::BackgroundTasksSelector enum value. 125BACKGROUNDTASKS = { 126 BackgroundTasksSelector.ALL_TASKS: "ALL_TASKS", 127 BackgroundTasksSelector.NO_TASKS: "NO_TASKS", 128} 129 130 131# Emits the C++ symbolic constant corresponding to a BackgroundTasks constant. 132def lower_backgroundtasks(backgroundtasks): 133 return "Module::BackgroundTasksSelector::%s" % BACKGROUNDTASKS[backgroundtasks] 134 135 136# Represents a static string table, indexed by offset. This allows us to 137# reference strings from static data structures without requiring runtime 138# relocations. 139class StringTable(object): 140 def __init__(self): 141 self.entries = {} 142 self.entry_list = [] 143 self.size = 0 144 145 self._serialized = False 146 147 # Returns the index of the given string in the `entry_list` array. If 148 # no entry for the string exists, it first creates one. 149 def get_idx(self, string): 150 idx = self.entries.get(string, None) 151 if idx is not None: 152 return idx 153 154 assert not self._serialized 155 156 assert len(string) == len(string.encode("utf-8")) 157 158 idx = self.size 159 self.size += len(string) + 1 160 161 self.entries[string] = idx 162 self.entry_list.append(string) 163 return idx 164 165 # Returns the C++ code representing string data of this string table, as a 166 # single string literal. This must only be called after the last call to 167 # `get_idx()` or `entry_to_cxx()` for this instance. 168 def to_cxx(self): 169 self._serialized = True 170 171 lines = [] 172 173 idx = 0 174 for entry in self.entry_list: 175 str_ = entry.replace("\\", "\\\\").replace('"', r"\"").replace("\n", r"\n") 176 177 lines.append(' /* 0x%x */ "%s\\0"\n' % (idx, str_)) 178 179 idx += len(entry) + 1 180 181 return "".join(lines) 182 183 # Returns a `StringEntry` struct initializer for the string table entry 184 # corresponding to the given string. If no matching entry exists, it is 185 # first created. 186 def entry_to_cxx(self, string): 187 idx = self.get_idx(string) 188 return "{ 0x%x } /* %s */" % (idx, pretty_string(string)) 189 190 191strings = StringTable() 192 193interfaces = [] 194 195 196# Represents a C++ namespace, containing a set of classes and potentially 197# sub-namespaces. This is used to generate pre-declarations for incomplete 198# types referenced in XPCOM manifests. 199class Namespace(object): 200 def __init__(self, name=None): 201 self.name = name 202 self.classes = set() 203 self.namespaces = {} 204 205 # Returns a Namespace object for the sub-namespace with the given name. 206 def sub(self, name): 207 assert name not in self.classes 208 209 if name not in self.namespaces: 210 self.namespaces[name] = Namespace(name) 211 return self.namespaces[name] 212 213 # Generates C++ code to pre-declare all classes in this namespace and all 214 # of its sub-namespaces. 215 def to_cxx(self): 216 res = "" 217 if self.name: 218 res += "namespace %s {\n" % self.name 219 220 for clas in sorted(self.classes): 221 res += "class %s;\n" % clas 222 223 for ns in sorted(self.namespaces.keys()): 224 res += self.namespaces[ns].to_cxx() 225 226 if self.name: 227 res += "} // namespace %s\n" % self.name 228 229 return res 230 231 232# Represents a component defined in an XPCOM manifest's `Classes` array. 233class ModuleEntry(object): 234 next_anon_id = 0 235 236 def __init__(self, data, init_idx): 237 self.cid = UUIDRepr(UUID(data["cid"])) 238 self.contract_ids = data.get("contract_ids", []) 239 self.type = data.get("type", "nsISupports") 240 self.categories = data.get("categories", {}) 241 self.processes = data.get("processes", 0) 242 self.headers = data.get("headers", []) 243 244 self.js_name = data.get("js_name", None) 245 self.interfaces = data.get("interfaces", []) 246 247 if len(self.interfaces) > 255: 248 raise Exception( 249 "JS service %s may not have more than 255 " "interfaces" % self.js_name 250 ) 251 252 self.interfaces_offset = len(interfaces) 253 for iface in self.interfaces: 254 interfaces.append(iface) 255 256 # If the manifest declares Init or Unload functions, this contains its 257 # index, as understood by the `CallInitFunc()` function. 258 # 259 # If it contains any value other than `None`, a corresponding 260 # `CallInitFunc(init_idx)` call will be genrated before calling this 261 # module's constructor. 262 self.init_idx = init_idx 263 264 self.constructor = data.get("constructor", None) 265 self.legacy_constructor = data.get("legacy_constructor", None) 266 self.init_method = data.get("init_method", []) 267 268 self.jsm = data.get("jsm", None) 269 270 self.external = data.get( 271 "external", not (self.headers or self.legacy_constructor) 272 ) 273 self.singleton = data.get("singleton", False) 274 self.overridable = data.get("overridable", False) 275 276 if "name" in data: 277 self.anonymous = False 278 self.name = data["name"] 279 else: 280 self.anonymous = True 281 self.name = "Anonymous%03d" % ModuleEntry.next_anon_id 282 ModuleEntry.next_anon_id += 1 283 284 def error(str_): 285 raise Exception( 286 "Error defining component %s (%s): %s" 287 % (str(self.cid), ", ".join(map(repr, self.contract_ids)), str_) 288 ) 289 290 if self.jsm: 291 if not self.constructor: 292 error("JavaScript components must specify a constructor") 293 294 for prop in ("init_method", "legacy_constructor", "headers"): 295 if getattr(self, prop): 296 error( 297 "JavaScript components may not specify a '%s' " 298 "property" % prop 299 ) 300 elif self.external: 301 if self.constructor or self.legacy_constructor: 302 error( 303 "Externally-constructed components may not specify " 304 "'constructor' or 'legacy_constructor' properties" 305 ) 306 if self.init_method: 307 error( 308 "Externally-constructed components may not specify " 309 "'init_method' properties" 310 ) 311 if self.type == "nsISupports": 312 error( 313 "Externally-constructed components must specify a type " 314 "other than nsISupports" 315 ) 316 317 if self.constructor and self.legacy_constructor: 318 error( 319 "The 'constructor' and 'legacy_constructor' properties " 320 "are mutually exclusive" 321 ) 322 323 if self.overridable and not self.contract_ids: 324 error("Overridable components must specify at least one contract " "ID") 325 326 @property 327 def contract_id(self): 328 return self.contract_ids[0] 329 330 # Generates the C++ code for a StaticModule struct initializer 331 # representing this component. 332 def to_cxx(self): 333 contract_id = ( 334 strings.entry_to_cxx(self.contract_id) 335 if self.overridable 336 else "{ 0x%x }" % NO_CONTRACT_ID 337 ) 338 339 return """ 340 /* {name} */ {{ 341 /* {{{cid_string}}} */ 342 {cid}, 343 {contract_id}, 344 {processes}, 345 }}""".format( 346 name=self.name, 347 cid=self.cid.to_cxx(), 348 cid_string=str(self.cid), 349 contract_id=contract_id, 350 processes=lower_processes(self.processes), 351 ) 352 353 # Generates the C++ code for a JSServiceEntry representing this module. 354 def lower_js_service(self): 355 return """ 356 {{ 357 {js_name}, 358 ModuleID::{name}, 359 {{ {iface_offset} }}, 360 {iface_count} 361 }}""".format( 362 js_name=strings.entry_to_cxx(self.js_name), 363 name=self.name, 364 iface_offset=self.interfaces_offset, 365 iface_count=len(self.interfaces), 366 ) 367 368 # Generates the C++ code necessary to construct an instance of this 369 # component. 370 # 371 # This code lives in a function with the following arguments: 372 # 373 # - aIID: The `const nsIID&` interface ID that the resulting instance 374 # will be queried to. 375 # 376 # - aResult: The `void**` pointer in which to store the result. 377 # 378 # And which returns an `nsresult` indicating success or failure. 379 def lower_constructor(self): 380 res = "" 381 382 if self.init_idx is not None: 383 res += " MOZ_TRY(CallInitFunc(%d));\n" % self.init_idx 384 385 if self.legacy_constructor: 386 res += ( 387 " return /* legacy */ %s(nullptr, aIID, aResult);\n" 388 % self.legacy_constructor 389 ) 390 return res 391 392 if self.jsm: 393 res += ( 394 " nsCOMPtr<nsISupports> inst;\n" 395 " MOZ_TRY(ConstructJSMComponent(nsLiteralCString(%s),\n" 396 " %s,\n" 397 " getter_AddRefs(inst)));" 398 "\n" % (json.dumps(self.jsm), json.dumps(self.constructor)) 399 ) 400 elif self.external: 401 res += ( 402 " nsCOMPtr<nsISupports> inst = " 403 "mozCreateComponent<%s>();\n" % self.type 404 ) 405 # The custom constructor may return null, so check before calling 406 # any methods. 407 res += " NS_ENSURE_TRUE(inst, NS_ERROR_FAILURE);\n" 408 else: 409 res += " RefPtr<%s> inst = " % self.type 410 411 if not self.constructor: 412 res += "new %s();\n" % self.type 413 else: 414 res += "%s();\n" % self.constructor 415 # The `new` operator is infallible, so we don't need to worry 416 # about it returning null, but custom constructors may, so 417 # check before calling any methods. 418 res += " NS_ENSURE_TRUE(inst, NS_ERROR_OUT_OF_MEMORY);\n" 419 420 # Check that the constructor function returns an appropriate 421 # `already_AddRefed` value for our declared type. 422 res += """ 423 using T = 424 RemoveAlreadyAddRefed<decltype(%(constructor)s())>::Type; 425 static_assert( 426 std::is_same_v<already_AddRefed<T>, decltype(%(constructor)s())>, 427 "Singleton constructor must return already_AddRefed"); 428 static_assert( 429 std::is_base_of<%(type)s, T>::value, 430 "Singleton constructor must return correct already_AddRefed"); 431 432""" % { 433 "type": self.type, 434 "constructor": self.constructor, 435 } 436 437 if self.init_method: 438 res += " MOZ_TRY(inst->%s());\n" % self.init_method 439 440 res += " return inst->QueryInterface(aIID, aResult);\n" 441 442 return res 443 444 # Generates the C++ code for the `mozilla::components::<name>` entry 445 # corresponding to this component. This may not be called for modules 446 # without an explicit `name` (in which cases, `self.anonymous` will be 447 # true). 448 def lower_getters(self): 449 assert not self.anonymous 450 451 substs = { 452 "name": self.name, 453 "id": "::mozilla::xpcom::ModuleID::%s" % self.name, 454 } 455 456 res = ( 457 """ 458namespace %(name)s { 459static inline const nsID& CID() { 460 return ::mozilla::xpcom::Components::GetCID(%(id)s); 461} 462 463static inline ::mozilla::xpcom::GetServiceHelper Service(nsresult* aRv = nullptr) { 464 return {%(id)s, aRv}; 465} 466""" 467 % substs 468 ) 469 470 if not self.singleton: 471 res += ( 472 """ 473static inline ::mozilla::xpcom::CreateInstanceHelper Create(nsresult* aRv = nullptr) { 474 return {%(id)s, aRv}; 475} 476""" 477 % substs 478 ) 479 480 res += ( 481 """\ 482} // namespace %(name)s 483""" 484 % substs 485 ) 486 487 return res 488 489 490# Returns a quoted string literal representing the given raw string, with 491# certain special characters replaced so that it can be used in a C++-style 492# (/* ... */) comment. 493def pretty_string(string): 494 return json.dumps(string).replace("*/", r"*\/").replace("/*", r"/\*") 495 496 497# Represents a static contract ID entry, corresponding to a C++ ContractEntry 498# struct, mapping a contract ID to a static module entry. 499class ContractEntry(object): 500 def __init__(self, contract, module): 501 self.contract = contract 502 self.module = module 503 504 def to_cxx(self): 505 return """ 506 {{ 507 {contract}, 508 {module_id}, 509 }}""".format( 510 contract=strings.entry_to_cxx(self.contract), 511 module_id=lower_module_id(self.module), 512 ) 513 514 515# Generates the C++ code for the StaticCategoryEntry and StaticCategory 516# structs for all category entries declared in XPCOM manifests. 517def gen_categories(substs, categories): 518 cats = [] 519 ents = [] 520 521 count = 0 522 for category, entries in sorted(categories.items()): 523 524 def k(entry): 525 return tuple(entry[0]["name"]) + entry[1:] 526 527 entries.sort(key=k) 528 529 cats.append( 530 " { %s,\n" 531 " %d, %d },\n" % (strings.entry_to_cxx(category), count, len(entries)) 532 ) 533 count += len(entries) 534 535 ents.append(" /* %s */\n" % pretty_string(category)) 536 for entry, value, processes in entries: 537 name = entry["name"] 538 backgroundtasks = entry.get( 539 "backgroundtasks", BackgroundTasksSelector.NO_TASKS 540 ) 541 542 ents.append( 543 " { %s,\n" 544 " %s,\n" 545 " %s,\n" 546 " %s },\n" 547 % ( 548 strings.entry_to_cxx(name), 549 strings.entry_to_cxx(value), 550 lower_backgroundtasks(backgroundtasks), 551 lower_processes(processes), 552 ) 553 ) 554 ents.append("\n") 555 ents.pop() 556 557 substs["category_count"] = len(cats) 558 substs["categories"] = "".join(cats) 559 substs["category_entries"] = "".join(ents) 560 561 562# Generates the C++ code for all Init and Unload functions declared in XPCOM 563# manifests. These form the bodies of the `CallInitFunc()` and `CallUnload` 564# functions in StaticComponents.cpp. 565def gen_module_funcs(substs, funcs): 566 inits = [] 567 unloads = [] 568 569 template = """\ 570 case %d: 571 %s 572 break; 573""" 574 575 for i, (init, unload) in enumerate(funcs): 576 init_code = "%s();" % init if init else "/* empty */" 577 inits.append(template % (i, init_code)) 578 579 if unload: 580 unloads.append( 581 """\ 582 if (CalledInit(%d)) { 583 %s(); 584 } 585""" 586 % (i, unload) 587 ) 588 589 substs["init_funcs"] = "".join(inits) 590 substs["unload_funcs"] = "".join(unloads) 591 substs["init_count"] = len(funcs) 592 593 594def gen_interfaces(ifaces): 595 res = [] 596 for iface in ifaces: 597 res.append(" nsXPTInterface::%s,\n" % iface) 598 return "".join(res) 599 600 601# Generates class pre-declarations for any types referenced in `Classes` array 602# entries which do not have corresponding `headers` entries to fully declare 603# their types. 604def gen_decls(types): 605 root_ns = Namespace() 606 607 for type_ in sorted(types): 608 parts = type_.split("::") 609 610 ns = root_ns 611 for part in parts[:-1]: 612 ns = ns.sub(part) 613 ns.classes.add(parts[-1]) 614 615 return root_ns.to_cxx() 616 617 618# Generates the `switch` body for the `CreateInstanceImpl()` function, with a 619# `case` for each value in ModuleID to construct an instance of the 620# corresponding component. 621def gen_constructors(entries): 622 constructors = [] 623 for entry in entries: 624 constructors.append( 625 """\ 626 case {id}: {{ 627{constructor}\ 628 }} 629""".format( 630 id=lower_module_id(entry), constructor=entry.lower_constructor() 631 ) 632 ) 633 634 return "".join(constructors) 635 636 637# Generates the getter code for each named component entry in the 638# `mozilla::components::` namespace. 639def gen_getters(entries): 640 entries = list(entries) 641 entries.sort(key=lambda e: e.name) 642 643 return "".join(entry.lower_getters() for entry in entries if not entry.anonymous) 644 645 646def gen_includes(substs, all_headers): 647 headers = set() 648 absolute_headers = set() 649 650 for header in all_headers: 651 if header.startswith("/"): 652 absolute_headers.add(header) 653 else: 654 headers.add(header) 655 656 includes = ['#include "%s"' % header for header in sorted(headers)] 657 substs["includes"] = "\n".join(includes) + "\n" 658 659 relative_includes = [ 660 '#include "../..%s"' % header for header in sorted(absolute_headers) 661 ] 662 substs["relative_includes"] = "\n".join(relative_includes) + "\n" 663 664 665def to_category_list(val): 666 # Entries can be bare strings (like `"m-browser"`), lists of bare strings, 667 # or dictionaries (like `{"name": "m-browser", "backgroundtasks": 668 # BackgroundTasksSelector.ALL_TASKS}`), somewhat recursively. 669 670 def ensure_dict(v): 671 # Turn `v` into `{"name": v}` if it's not already a dict. 672 if isinstance(v, dict): 673 return v 674 return {"name": v} 675 676 if isinstance(val, (list, tuple)): 677 return tuple(ensure_dict(v) for v in val) 678 679 if isinstance(val, dict): 680 # Explode `{"name": ["x", "y"], "backgroundtasks": ...}` into 681 # `[{"name": "x", "backgroundtasks": ...}, {"name": "y", "backgroundtasks": ...}]`. 682 names = val.pop("name") 683 684 vals = [] 685 for entry in to_category_list(names): 686 d = dict(val) 687 d["name"] = entry["name"] 688 vals.append(d) 689 690 return tuple(vals) 691 692 return (ensure_dict(val),) 693 694 695def gen_substs(manifests): 696 module_funcs = [] 697 698 headers = set() 699 700 modules = [] 701 categories = defaultdict(list) 702 703 for manifest in manifests: 704 headers |= set(manifest.get("Headers", [])) 705 706 init_idx = None 707 init = manifest.get("InitFunc") 708 unload = manifest.get("UnloadFunc") 709 if init or unload: 710 init_idx = len(module_funcs) 711 module_funcs.append((init, unload)) 712 713 for clas in manifest["Classes"]: 714 modules.append(ModuleEntry(clas, init_idx)) 715 716 for category, entries in manifest.get("Categories", {}).items(): 717 for key, entry in entries.items(): 718 if isinstance(entry, tuple): 719 value, process = entry 720 else: 721 value, process = entry, 0 722 categories[category].append((key, value, process)) 723 724 cids = set() 725 contracts = [] 726 contract_map = {} 727 js_services = {} 728 729 jsms = set() 730 731 types = set() 732 733 for mod in modules: 734 headers |= set(mod.headers) 735 736 for contract_id in mod.contract_ids: 737 if contract_id in contract_map: 738 raise Exception("Duplicate contract ID: %s" % contract_id) 739 740 entry = ContractEntry(contract_id, mod) 741 contracts.append(entry) 742 contract_map[contract_id] = entry 743 744 for category, entries in mod.categories.items(): 745 for entry in to_category_list(entries): 746 categories[category].append((entry, mod.contract_id, mod.processes)) 747 748 if mod.type and not mod.headers: 749 types.add(mod.type) 750 751 if mod.jsm: 752 jsms.add(mod.jsm) 753 754 if mod.js_name: 755 if mod.js_name in js_services: 756 raise Exception("Duplicate JS service name: %s" % mod.js_name) 757 js_services[mod.js_name] = mod 758 759 if str(mod.cid) in cids: 760 raise Exception("Duplicate cid: %s" % str(mod.cid)) 761 cids.add(str(mod.cid)) 762 763 cid_phf = PerfectHash(modules, PHF_SIZE, key=lambda module: module.cid.bytes) 764 765 contract_phf = PerfectHash(contracts, PHF_SIZE, key=lambda entry: entry.contract) 766 767 js_services_phf = PerfectHash( 768 list(js_services.values()), PHF_SIZE, key=lambda entry: entry.js_name 769 ) 770 771 js_services_json = {} 772 for entry in js_services.values(): 773 for iface in entry.interfaces: 774 js_services_json[iface] = entry.js_name 775 776 substs = {} 777 778 gen_categories(substs, categories) 779 780 substs["module_ids"] = "".join(" %s,\n" % entry.name for entry in cid_phf.entries) 781 782 substs["module_count"] = len(modules) 783 substs["contract_count"] = len(contracts) 784 785 gen_module_funcs(substs, module_funcs) 786 787 gen_includes(substs, headers) 788 789 substs["component_jsms"] = ( 790 "\n".join(" %s," % strings.entry_to_cxx(jsm) for jsm in sorted(jsms)) + "\n" 791 ) 792 793 substs["interfaces"] = gen_interfaces(interfaces) 794 795 substs["decls"] = gen_decls(types) 796 797 substs["constructors"] = gen_constructors(cid_phf.entries) 798 799 substs["component_getters"] = gen_getters(cid_phf.entries) 800 801 substs["module_cid_table"] = cid_phf.cxx_codegen( 802 name="ModuleByCID", 803 entry_type="StaticModule", 804 entries_name="gStaticModules", 805 lower_entry=lambda entry: entry.to_cxx(), 806 return_type="const StaticModule*", 807 return_entry=( 808 "return entry.CID().Equals(aKey) && entry.Active()" " ? &entry : nullptr;" 809 ), 810 key_type="const nsID&", 811 key_bytes="reinterpret_cast<const char*>(&aKey)", 812 key_length="sizeof(nsID)", 813 ) 814 815 substs["module_contract_id_table"] = contract_phf.cxx_codegen( 816 name="LookupContractID", 817 entry_type="ContractEntry", 818 entries_name="gContractEntries", 819 lower_entry=lambda entry: entry.to_cxx(), 820 return_type="const ContractEntry*", 821 return_entry="return entry.Matches(aKey) ? &entry : nullptr;", 822 key_type="const nsACString&", 823 key_bytes="aKey.BeginReading()", 824 key_length="aKey.Length()", 825 ) 826 827 substs["js_services_table"] = js_services_phf.cxx_codegen( 828 name="LookupJSService", 829 entry_type="JSServiceEntry", 830 entries_name="gJSServices", 831 lower_entry=lambda entry: entry.lower_js_service(), 832 return_type="const JSServiceEntry*", 833 return_entry="return entry.Name() == aKey ? &entry : nullptr;", 834 key_type="const nsACString&", 835 key_bytes="aKey.BeginReading()", 836 key_length="aKey.Length()", 837 ) 838 839 substs["js_services_json"] = json.dumps(js_services_json, sort_keys=True, indent=4) 840 841 # Do this only after everything else has been emitted so we're sure the 842 # string table is complete. 843 substs["strings"] = strings.to_cxx() 844 return substs 845 846 847# Returns true if the given build config substitution is defined and truthy. 848def defined(subst): 849 return bool(buildconfig.substs.get(subst)) 850 851 852def read_manifest(filename): 853 glbl = { 854 "buildconfig": buildconfig, 855 "defined": defined, 856 "ProcessSelector": ProcessSelector, 857 "BackgroundTasksSelector": BackgroundTasksSelector, 858 } 859 code = compile(open(filename).read(), filename, "exec") 860 exec(code, glbl) 861 return glbl 862 863 864def main(fd, conf_file, template_file): 865 def open_output(filename): 866 return FileAvoidWrite(os.path.join(os.path.dirname(fd.name), filename)) 867 868 conf = json.load(open(conf_file, "r")) 869 870 deps = set() 871 872 manifests = [] 873 for filename in conf["manifests"]: 874 deps.add(filename) 875 manifest = read_manifest(filename) 876 manifests.append(manifest) 877 manifest.setdefault("Priority", 50) 878 manifest["__filename__"] = filename 879 880 manifests.sort(key=lambda man: (man["Priority"], man["__filename__"])) 881 882 substs = gen_substs(manifests) 883 884 def replacer(match): 885 return substs[match.group(1)] 886 887 with open_output("StaticComponents.cpp") as fh: 888 with open(template_file, "r") as tfh: 889 template = tfh.read() 890 891 fh.write(re.sub(r"//# @([a-zA-Z_]+)@\n", replacer, template)) 892 893 with open_output("StaticComponentData.h") as fh: 894 fh.write( 895 """\ 896/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 897/* vim: set ts=8 sts=2 et sw=2 tw=80: */ 898/* This Source Code Form is subject to the terms of the Mozilla Public 899 * License, v. 2.0. If a copy of the MPL was not distributed with this 900 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 901 902#ifndef StaticComponentData_h 903#define StaticComponentData_h 904 905#include <stddef.h> 906 907namespace mozilla { 908namespace xpcom { 909 910static constexpr size_t kStaticModuleCount = %(module_count)d; 911 912static constexpr size_t kContractCount = %(contract_count)d; 913 914static constexpr size_t kStaticCategoryCount = %(category_count)d; 915 916static constexpr size_t kModuleInitCount = %(init_count)d; 917 918} // namespace xpcom 919} // namespace mozilla 920 921#endif 922""" 923 % substs 924 ) 925 926 fd.write( 927 """\ 928/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 929/* vim: set ts=8 sts=2 et sw=2 tw=80: */ 930/* This Source Code Form is subject to the terms of the Mozilla Public 931 * License, v. 2.0. If a copy of the MPL was not distributed with this 932 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 933 934#ifndef mozilla_Components_h 935#define mozilla_Components_h 936 937#include "nsCOMPtr.h" 938 939struct nsID; 940 941#define NS_IMPL_COMPONENT_FACTORY(iface) \\ 942 template <> \\ 943 already_AddRefed<nsISupports> mozCreateComponent<iface>() 944 945template <typename T> 946already_AddRefed<nsISupports> mozCreateComponent(); 947 948namespace mozilla { 949namespace xpcom { 950 951enum class ModuleID : uint16_t { 952%(module_ids)s 953}; 954 955class MOZ_STACK_CLASS StaticModuleHelper : public nsCOMPtr_helper { 956 public: 957 StaticModuleHelper(ModuleID aId, nsresult* aErrorPtr) 958 : mId(aId), mErrorPtr(aErrorPtr) {} 959 960 protected: 961 nsresult SetResult(nsresult aRv) const { 962 if (mErrorPtr) { 963 *mErrorPtr = aRv; 964 } 965 return aRv; 966 } 967 968 ModuleID mId; 969 nsresult* mErrorPtr; 970}; 971 972class MOZ_STACK_CLASS GetServiceHelper final : public StaticModuleHelper { 973 public: 974 using StaticModuleHelper::StaticModuleHelper; 975 976 nsresult NS_FASTCALL operator()(const nsIID& aIID, 977 void** aResult) const override; 978}; 979 980class MOZ_STACK_CLASS CreateInstanceHelper final : public StaticModuleHelper { 981 public: 982 using StaticModuleHelper::StaticModuleHelper; 983 984 nsresult NS_FASTCALL operator()(const nsIID& aIID, 985 void** aResult) const override; 986}; 987 988class Components final { 989 public: 990 static const nsID& GetCID(ModuleID aID); 991}; 992 993} // namespace xpcom 994 995namespace components { 996%(component_getters)s 997} // namespace components 998 999} // namespace mozilla 1000 1001#endif 1002""" 1003 % substs 1004 ) 1005 1006 with open_output("services.json") as fh: 1007 fh.write(substs["js_services_json"]) 1008 1009 return deps 1010