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