Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / xpcom / idl-parser / xpidl / header.py
blobed179b1bad2a0c316b8a27101baadd3659849ba1
1 #!/usr/bin/env python
2 # header.py - Generate C++ header files from IDL.
4 # This Source Code Form is subject to the terms of the Mozilla Public
5 # License, v. 2.0. If a copy of the MPL was not distributed with this
6 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 """Print a C++ header file for the IDL files specified on the command line"""
10 import itertools
11 import os.path
12 import re
14 from xpidl import xpidl
16 printdoccomments = False
18 if printdoccomments:
20 def printComments(fd, clist, indent):
21 for c in clist:
22 fd.write("%s%s\n" % (indent, c))
24 else:
26 def printComments(fd, clist, indent):
27 pass
30 def firstCap(str):
31 return str[0].upper() + str[1:]
34 def attributeParamName(a):
35 return "a" + firstCap(a.name)
38 def attributeParamNames(a, getter, return_param=True):
39 if getter and (a.notxpcom or not return_param):
40 l = []
41 else:
42 l = [attributeParamName(a)]
43 if a.implicit_jscontext:
44 l.insert(0, "cx")
45 return ", ".join(l)
48 def attributeNativeName(a, getter):
49 binaryname = a.binaryname is not None and a.binaryname or firstCap(a.name)
50 return "%s%s" % (getter and "Get" or "Set", binaryname)
53 def attributeAttributes(a, getter):
54 ret = ""
56 if a.must_use:
57 ret = "[[nodiscard]] " + ret
59 # Ideally, we'd set MOZ_CAN_RUN_SCRIPT in the "scriptable and not
60 # builtinclass" case too, so we'd just have memberCanRunScript() check
61 # explicit_setter_can_run_script/explicit_setter_can_run_script and call it
62 # here. But that would likely require a fair amount of Gecko-side
63 # annotation work. See bug 1534292.
64 if (a.explicit_getter_can_run_script and getter) or (
65 a.explicit_setter_can_run_script and not getter
67 ret = "MOZ_CAN_RUN_SCRIPT " + ret
69 return ret
72 def attributeReturnType(a, getter, macro):
73 """macro should be NS_IMETHOD or NS_IMETHODIMP"""
74 # Pick the type to be returned from the getter/setter.
75 if a.notxpcom:
76 ret = a.realtype.nativeType("in").strip() if getter else "void"
77 else:
78 ret = "nsresult"
80 # Set calling convention and virtual-ness
81 if a.nostdcall:
82 if macro == "NS_IMETHOD":
83 # This is the declaration.
84 ret = "virtual %s" % ret
85 else:
86 if ret == "nsresult":
87 ret = macro
88 else:
89 ret = "%s_(%s)" % (macro, ret)
91 return attributeAttributes(a, getter) + ret
94 def attributeParamlist(a, getter, return_param=True):
95 if getter and (a.notxpcom or not return_param):
96 l = []
97 else:
98 l = [
99 "%s%s"
100 % (a.realtype.nativeType(getter and "out" or "in"), attributeParamName(a))
102 if a.implicit_jscontext:
103 l.insert(0, "JSContext* cx")
105 return ", ".join(l)
108 def attributeAsNative(a, getter, declType="NS_IMETHOD"):
109 params = {
110 "returntype": attributeReturnType(a, getter, declType),
111 "binaryname": attributeNativeName(a, getter),
112 "paramlist": attributeParamlist(a, getter),
114 return "%(returntype)s %(binaryname)s(%(paramlist)s)" % params
117 def methodNativeName(m):
118 return m.binaryname is not None and m.binaryname or firstCap(m.name)
121 def methodAttributes(m):
122 ret = ""
124 if m.must_use:
125 ret = "[[nodiscard]] " + ret
127 # Ideally, we'd set MOZ_CAN_RUN_SCRIPT in the "scriptable and not
128 # builtinclass" case too, so we'd just have memberCanRunScript() check
129 # explicit_can_run_script and call it here. But that would likely require
130 # a fair amount of Gecko-side annotation work. See bug 1534292.
131 if m.explicit_can_run_script:
132 ret = "MOZ_CAN_RUN_SCRIPT " + ret
134 return ret
137 def methodReturnType(m, macro):
138 """macro should be NS_IMETHOD or NS_IMETHODIMP"""
139 if m.notxpcom:
140 ret = m.realtype.nativeType("in").strip()
141 else:
142 ret = "nsresult"
144 # Set calling convention and virtual-ness
145 if m.nostdcall:
146 if macro == "NS_IMETHOD":
147 # This is the declaration
148 ret = "virtual %s" % ret
149 else:
150 if ret == "nsresult":
151 ret = macro
152 else:
153 ret = "%s_(%s)" % (macro, ret)
155 return methodAttributes(m) + ret
158 def methodAsNative(m, declType="NS_IMETHOD"):
159 return "%s %s(%s)" % (
160 methodReturnType(m, declType),
161 methodNativeName(m),
162 paramlistAsNative(m),
166 def paramlistAsNative(m, empty="void", return_param=True):
167 l = [paramAsNative(p) for p in m.params]
169 if m.implicit_jscontext:
170 l.append("JSContext* cx")
172 if m.optional_argc:
173 l.append("uint8_t _argc")
175 if not m.notxpcom and m.realtype.name != "void" and return_param:
176 l.append(
177 paramAsNative(
178 xpidl.Param(
179 paramtype="out",
180 type=None,
181 name="_retval",
182 attlist=[],
183 location=None,
184 realtype=m.realtype,
189 # Set any optional out params to default to nullptr. Skip if we just added
190 # extra non-optional args to l.
191 if len(l) == len(m.params):
192 paramIter = len(m.params) - 1
193 while (
194 paramIter >= 0
195 and m.params[paramIter].optional
196 and "out" in m.params[paramIter].paramtype
198 t = m.params[paramIter].type
199 # Strings can't be optional, so this shouldn't happen, but let's make sure:
200 if t == "AString" or t == "ACString" or t == "AUTF8String":
201 break
202 l[paramIter] += " = nullptr"
203 paramIter -= 1
205 if len(l) == 0:
206 return empty
208 return ", ".join(l)
211 def memberCanRunScript(member):
212 # This can only happen if the member is scriptable and its interface is not builtinclass.
213 return member.isScriptable() and not member.iface.attributes.builtinclass
216 def runScriptAnnotation(member):
217 return "JS_HAZ_CAN_RUN_SCRIPT " if memberCanRunScript(member) else ""
220 def paramAsNative(p):
221 default_spec = ""
222 if p.default_value:
223 default_spec = " = " + p.default_value
224 return "%s%s%s" % (p.nativeType(), p.name, default_spec)
227 def paramlistNames(m, return_param=True):
228 names = [p.name for p in m.params]
230 if m.implicit_jscontext:
231 names.append("cx")
233 if m.optional_argc:
234 names.append("_argc")
236 if not m.notxpcom and m.realtype.name != "void" and return_param:
237 names.append("_retval")
239 if len(names) == 0:
240 return ""
241 return ", ".join(names)
244 header = """/*
245 * DO NOT EDIT. THIS FILE IS GENERATED FROM $SRCDIR/%(relpath)s
248 #ifndef __gen_%(basename)s_h__
249 #define __gen_%(basename)s_h__
252 include = """
253 #include "%(basename)s.h"
256 jsvalue_include = """
257 #include "js/Value.h"
260 infallible_includes = """
261 #include "mozilla/AlreadyAddRefed.h"
262 #include "mozilla/Assertions.h"
263 #include "mozilla/DebugOnly.h"
266 can_run_script_includes = """
267 #include "js/GCAnnotations.h"
270 header_end = """/* For IDL files that don't want to include root IDL files. */
271 #ifndef NS_NO_VTABLE
272 #define NS_NO_VTABLE
273 #endif
276 footer = """
277 #endif /* __gen_%(basename)s_h__ */
280 forward_decl = """class %(name)s; /* forward declaration */
285 def idl_basename(f):
286 """returns the base name of a file with the last extension stripped"""
287 return os.path.basename(f).rpartition(".")[0]
290 def print_header(idl, fd, filename, relpath):
291 fd.write(header % {"relpath": relpath, "basename": idl_basename(filename)})
293 foundinc = False
294 for inc in idl.includes():
295 if not foundinc:
296 foundinc = True
297 fd.write("\n")
298 fd.write(include % {"basename": idl_basename(inc.filename)})
300 if idl.needsJSTypes():
301 fd.write(jsvalue_include)
303 # Include some extra files if any attributes are infallible.
304 interfaces = [p for p in idl.productions if p.kind == "interface"]
305 wroteRunScriptIncludes = False
306 wroteInfallibleIncludes = False
307 for iface in interfaces:
308 for member in iface.members:
309 if not isinstance(member, xpidl.Attribute) and not isinstance(
310 member, xpidl.Method
312 continue
313 if not wroteInfallibleIncludes and member.infallible:
314 fd.write(infallible_includes)
315 wroteInfallibleIncludes = True
316 if not wroteRunScriptIncludes and memberCanRunScript(member):
317 fd.write(can_run_script_includes)
318 wroteRunScriptIncludes = True
319 if wroteRunScriptIncludes and wroteInfallibleIncludes:
320 break
322 fd.write("\n")
323 fd.write(header_end)
325 for p in idl.productions:
326 if p.kind == "include":
327 continue
328 if p.kind == "cdata":
329 fd.write(p.data)
330 continue
332 if p.kind == "webidl":
333 write_webidl(p, fd)
334 continue
335 if p.kind == "forward":
336 fd.write(forward_decl % {"name": p.name})
337 continue
338 if p.kind == "interface":
339 write_interface(p, fd)
340 continue
341 if p.kind == "typedef":
342 printComments(fd, p.doccomments, "")
343 fd.write("typedef %s %s;\n\n" % (p.realtype.nativeType("in"), p.name))
345 fd.write(footer % {"basename": idl_basename(filename)})
348 def write_webidl(p, fd):
349 path = p.native.split("::")
350 for seg in path[:-1]:
351 fd.write("namespace %s {\n" % seg)
352 fd.write("class %s; /* webidl %s */\n" % (path[-1], p.name))
353 for seg in reversed(path[:-1]):
354 fd.write("} // namespace %s\n" % seg)
355 fd.write("\n")
358 iface_header = r"""
359 /* starting interface: %(name)s */
360 #define %(defname)s_IID_STR "%(iid)s"
362 #define %(defname)s_IID \
363 {0x%(m0)s, 0x%(m1)s, 0x%(m2)s, \
364 { %(m3joined)s }}
368 uuid_decoder = re.compile(
369 r"""(?P<m0>[a-f0-9]{8})-
370 (?P<m1>[a-f0-9]{4})-
371 (?P<m2>[a-f0-9]{4})-
372 (?P<m3>[a-f0-9]{4})-
373 (?P<m4>[a-f0-9]{12})$""",
374 re.X,
377 iface_prolog = """ {
378 public:
380 NS_DECLARE_STATIC_IID_ACCESSOR(%(defname)s_IID)
384 iface_scriptable = """\
385 /* Used by ToJSValue to check which scriptable interface is implemented. */
386 using ScriptableInterfaceType = %(name)s;
390 iface_epilog = """};
392 NS_DEFINE_STATIC_IID_ACCESSOR(%(name)s, %(defname)s_IID)"""
394 iface_decl = """
396 /* Use this macro when declaring classes that implement this interface. */
397 #define NS_DECL_%(macroname)s """
399 iface_nonvirtual = """
401 /* Use this macro when declaring the members of this interface when the
402 class doesn't implement the interface. This is useful for forwarding. */
403 #define NS_DECL_NON_VIRTUAL_%(macroname)s """
405 iface_forward = """
407 /* Use this macro to declare functions that forward the behavior of this interface to another object. */
408 #define NS_FORWARD_%(macroname)s(_to) """ # NOQA: E501
410 iface_forward_safe = """
412 /* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */
413 #define NS_FORWARD_SAFE_%(macroname)s(_to) """ # NOQA: E501
415 builtin_infallible_tmpl = """\
416 %(attributes)sinline %(realtype)s %(nativename)s(%(args)s)
418 %(realtype)sresult;
419 mozilla::DebugOnly<nsresult> rv = %(nativename)s(%(argnames)s&result);
420 MOZ_ASSERT(NS_SUCCEEDED(rv));
421 return result;
425 # NOTE: We don't use RefPtr::forget here because we don't want to need the
426 # definition of %(realtype)s in scope, which we would need for the
427 # AddRef/Release calls.
428 refcnt_infallible_tmpl = """\
429 %(attributes)s inline already_AddRefed<%(realtype)s> %(nativename)s(%(args)s)
431 %(realtype)s* result = nullptr;
432 mozilla::DebugOnly<nsresult> rv = %(nativename)s(%(argnames)s&result);
433 MOZ_ASSERT(NS_SUCCEEDED(rv));
434 return already_AddRefed<%(realtype)s>(result);
438 iface_threadsafe_tmpl = """\
439 namespace mozilla::detail {
440 template <>
441 class InterfaceNeedsThreadSafeRefCnt<%(name)s> : public std::true_type {};
446 def infallibleDecl(member):
447 isattr = isinstance(member, xpidl.Attribute)
448 ismethod = isinstance(member, xpidl.Method)
449 assert isattr or ismethod
451 realtype = member.realtype.nativeType("in")
452 tmpl = builtin_infallible_tmpl
454 if member.realtype.kind != "builtin" and member.realtype.kind != "cenum":
455 assert realtype.endswith(" *"), "bad infallible type"
456 tmpl = refcnt_infallible_tmpl
457 realtype = realtype[:-2] # strip trailing pointer
459 if isattr:
460 nativename = attributeNativeName(member, getter=True)
461 args = attributeParamlist(member, getter=True, return_param=False)
462 argnames = attributeParamNames(member, getter=True, return_param=False)
463 attributes = attributeAttributes(member, getter=True)
464 else:
465 nativename = methodNativeName(member)
466 args = paramlistAsNative(member, return_param=False)
467 argnames = paramlistNames(member, return_param=False)
468 attributes = methodAttributes(member)
470 return tmpl % {
471 "attributes": attributes,
472 "realtype": realtype,
473 "nativename": nativename,
474 "args": args,
475 "argnames": argnames + ", " if argnames else "",
479 def write_interface(iface, fd):
480 if iface.namemap is None:
481 raise Exception("Interface was not resolved.")
483 # Confirm that no names of methods will overload in this interface
484 names = set()
486 def record_name(name):
487 if name in names:
488 raise Exception(
489 "Unexpected overloaded virtual method %s in interface %s"
490 % (name, iface.name)
492 names.add(name)
494 for m in iface.members:
495 if type(m) == xpidl.Attribute:
496 record_name(attributeNativeName(m, getter=True))
497 if not m.readonly:
498 record_name(attributeNativeName(m, getter=False))
499 elif type(m) == xpidl.Method:
500 record_name(methodNativeName(m))
502 def write_const_decls(g):
503 fd.write(" enum {\n")
504 enums = []
505 for c in g:
506 printComments(fd, c.doccomments, " ")
507 basetype = c.basetype
508 value = c.getValue()
509 enums.append(
510 " %(name)s = %(value)s%(signed)s"
512 "name": c.name,
513 "value": value,
514 "signed": (not basetype.signed) and "U" or "",
517 fd.write(",\n".join(enums))
518 fd.write("\n };\n\n")
520 def write_cenum_decl(b):
521 fd.write(" enum %s : uint%d_t {\n" % (b.basename, b.width))
522 for var in b.variants:
523 fd.write(" %s = %s,\n" % (var.name, var.value))
524 fd.write(" };\n\n")
526 def write_method_decl(m):
527 printComments(fd, m.doccomments, " ")
529 fd.write(" /* %s */\n" % m.toIDL())
530 fd.write(" %s%s = 0;\n\n" % (runScriptAnnotation(m), methodAsNative(m)))
532 if m.infallible:
533 fd.write(infallibleDecl(m))
535 def write_attr_decl(a):
536 printComments(fd, a.doccomments, " ")
538 fd.write(" /* %s */\n" % a.toIDL())
540 fd.write(" %s%s = 0;\n" % (runScriptAnnotation(a), attributeAsNative(a, True)))
541 if a.infallible:
542 fd.write(infallibleDecl(a))
544 if not a.readonly:
545 fd.write(
546 " %s%s = 0;\n" % (runScriptAnnotation(a), attributeAsNative(a, False))
548 fd.write("\n")
550 defname = iface.name.upper()
551 if iface.name[0:2] == "ns":
552 defname = "NS_" + defname[2:]
554 names = uuid_decoder.match(iface.attributes.uuid).groupdict()
555 m3str = names["m3"] + names["m4"]
556 names["m3joined"] = ", ".join(["0x%s" % m3str[i : i + 2] for i in range(0, 16, 2)])
558 if iface.name[2] == "I":
559 implclass = iface.name[:2] + iface.name[3:]
560 else:
561 implclass = "_MYCLASS_"
563 names.update(
565 "defname": defname,
566 "macroname": iface.name.upper(),
567 "name": iface.name,
568 "iid": iface.attributes.uuid,
569 "implclass": implclass,
573 fd.write(iface_header % names)
575 printComments(fd, iface.doccomments, "")
577 fd.write("class ")
578 foundcdata = False
579 for m in iface.members:
580 if isinstance(m, xpidl.CDATA):
581 foundcdata = True
583 if not foundcdata:
584 fd.write("NS_NO_VTABLE ")
586 fd.write(iface.name)
587 if iface.base:
588 fd.write(" : public %s" % iface.base)
589 fd.write(iface_prolog % names)
591 if iface.attributes.scriptable:
592 fd.write(iface_scriptable % names)
594 for key, group in itertools.groupby(iface.members, key=type):
595 if key == xpidl.ConstMember:
596 write_const_decls(group) # iterator of all the consts
597 else:
598 for member in group:
599 if key == xpidl.Attribute:
600 write_attr_decl(member)
601 elif key == xpidl.Method:
602 write_method_decl(member)
603 elif key == xpidl.CDATA:
604 fd.write(" %s" % member.data)
605 elif key == xpidl.CEnum:
606 write_cenum_decl(member)
607 else:
608 raise Exception("Unexpected interface member: %s" % member)
610 fd.write(iface_epilog % names)
612 if iface.attributes.rust_sync:
613 fd.write(iface_threadsafe_tmpl % names)
615 fd.write(iface_decl % names)
617 def writeDeclaration(fd, iface, virtual):
618 declType = "NS_IMETHOD" if virtual else "nsresult"
619 suffix = " override" if virtual else ""
620 for member in iface.members:
621 if isinstance(member, xpidl.Attribute):
622 if member.infallible:
623 fd.write(
624 "\\\n using %s::%s; "
625 % (iface.name, attributeNativeName(member, True))
627 fd.write(
628 "\\\n %s%s; " % (attributeAsNative(member, True, declType), suffix)
630 if not member.readonly:
631 fd.write(
632 "\\\n %s%s; "
633 % (attributeAsNative(member, False, declType), suffix)
635 elif isinstance(member, xpidl.Method):
636 fd.write("\\\n %s%s; " % (methodAsNative(member, declType), suffix))
637 if len(iface.members) == 0:
638 fd.write("\\\n /* no methods! */")
639 elif member.kind not in ("attribute", "method"):
640 fd.write("\\")
642 writeDeclaration(fd, iface, True)
643 fd.write(iface_nonvirtual % names)
644 writeDeclaration(fd, iface, False)
645 fd.write(iface_forward % names)
647 def emitTemplate(forward_infallible, tmpl, tmpl_notxpcom=None):
648 if tmpl_notxpcom is None:
649 tmpl_notxpcom = tmpl
650 for member in iface.members:
651 if isinstance(member, xpidl.Attribute):
652 if forward_infallible and member.infallible:
653 fd.write(
654 "\\\n using %s::%s; "
655 % (iface.name, attributeNativeName(member, True))
657 attr_tmpl = tmpl_notxpcom if member.notxpcom else tmpl
658 fd.write(
659 attr_tmpl
661 "asNative": attributeAsNative(member, True),
662 "nativeName": attributeNativeName(member, True),
663 "paramList": attributeParamNames(member, True),
666 if not member.readonly:
667 fd.write(
668 attr_tmpl
670 "asNative": attributeAsNative(member, False),
671 "nativeName": attributeNativeName(member, False),
672 "paramList": attributeParamNames(member, False),
675 elif isinstance(member, xpidl.Method):
676 if member.notxpcom:
677 fd.write(
678 tmpl_notxpcom
680 "asNative": methodAsNative(member),
681 "nativeName": methodNativeName(member),
682 "paramList": paramlistNames(member),
685 else:
686 fd.write(
687 tmpl
689 "asNative": methodAsNative(member),
690 "nativeName": methodNativeName(member),
691 "paramList": paramlistNames(member),
694 if len(iface.members) == 0:
695 fd.write("\\\n /* no methods! */")
696 elif member.kind not in ("attribute", "method"):
697 fd.write("\\")
699 emitTemplate(
700 True,
701 "\\\n %(asNative)s override { return _to %(nativeName)s(%(paramList)s); } ",
704 fd.write(iface_forward_safe % names)
706 # Don't try to safely forward notxpcom functions, because we have no
707 # sensible default error return. Instead, the caller will have to
708 # implement them.
709 emitTemplate(
710 False,
711 "\\\n %(asNative)s override { return !_to ? NS_ERROR_NULL_POINTER : _to->%(nativeName)s(%(paramList)s); } ", # NOQA: E501
712 "\\\n %(asNative)s override; ",
715 fd.write("\n\n")
718 def main(outputfile):
719 xpidl.IDLParser()
722 if __name__ == "__main__":
723 main(None)