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"""
14 from xpidl
import xpidl
16 printdoccomments
= False
20 def printComments(fd
, clist
, indent
):
22 fd
.write("%s%s\n" % (indent
, c
))
26 def printComments(fd
, clist
, indent
):
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
):
42 l
= [attributeParamName(a
)]
43 if a
.implicit_jscontext
:
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
):
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
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.
76 ret
= a
.realtype
.nativeType("in").strip() if getter
else "void"
80 # Set calling convention and virtual-ness
82 if macro
== "NS_IMETHOD":
83 # This is the declaration.
84 ret
= "virtual %s" % ret
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
):
100 % (a
.realtype
.nativeType(getter
and "out" or "in"), attributeParamName(a
))
102 if a
.implicit_jscontext
:
103 l
.insert(0, "JSContext* cx")
108 def attributeAsNative(a
, getter
, declType
="NS_IMETHOD"):
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
):
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
137 def methodReturnType(m
, macro
):
138 """macro should be NS_IMETHOD or NS_IMETHODIMP"""
140 ret
= m
.realtype
.nativeType("in").strip()
144 # Set calling convention and virtual-ness
146 if macro
== "NS_IMETHOD":
147 # This is the declaration
148 ret
= "virtual %s" % ret
150 if ret
== "nsresult":
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
),
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")
173 l
.append("uint8_t _argc")
175 if not m
.notxpcom
and m
.realtype
.name
!= "void" and return_param
:
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
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":
202 l
[paramIter
] += " = nullptr"
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
):
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
:
234 names
.append("_argc")
236 if not m
.notxpcom
and m
.realtype
.name
!= "void" and return_param
:
237 names
.append("_retval")
241 return ", ".join(names
)
245 * DO NOT EDIT. THIS FILE IS GENERATED FROM $SRCDIR/%(relpath)s
248 #ifndef __gen_%(basename)s_h__
249 #define __gen_%(basename)s_h__
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. */
277 #endif /* __gen_%(basename)s_h__ */
280 forward_decl
= """class %(name)s; /* forward declaration */
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
)})
294 for inc
in idl
.includes():
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(
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
:
325 for p
in idl
.productions
:
326 if p
.kind
== "include":
328 if p
.kind
== "cdata":
332 if p
.kind
== "webidl":
335 if p
.kind
== "forward":
336 fd
.write(forward_decl
% {"name": p
.name
})
338 if p
.kind
== "interface":
339 write_interface(p
, fd
)
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
)
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, \
368 uuid_decoder
= re
.compile(
369 r
"""(?P<m0>[a-f0-9]{8})-
373 (?P<m4>[a-f0-9]{12})$""",
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;
392 NS_DEFINE_STATIC_IID_ACCESSOR(%(name)s, %(defname)s_IID)"""
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 """
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)
419 mozilla::DebugOnly<nsresult> rv = %(nativename)s(%(argnames)s&result);
420 MOZ_ASSERT(NS_SUCCEEDED(rv));
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 {
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
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)
465 nativename
= methodNativeName(member
)
466 args
= paramlistAsNative(member
, return_param
=False)
467 argnames
= paramlistNames(member
, return_param
=False)
468 attributes
= methodAttributes(member
)
471 "attributes": attributes
,
472 "realtype": realtype
,
473 "nativename": nativename
,
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
486 def record_name(name
):
489 "Unexpected overloaded virtual method %s in interface %s"
494 for m
in iface
.members
:
495 if type(m
) == xpidl
.Attribute
:
496 record_name(attributeNativeName(m
, getter
=True))
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")
506 printComments(fd
, c
.doccomments
, " ")
507 basetype
= c
.basetype
510 " %(name)s = %(value)s%(signed)s"
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
))
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
)))
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)))
542 fd
.write(infallibleDecl(a
))
546 " %s%s = 0;\n" % (runScriptAnnotation(a
), attributeAsNative(a
, False))
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:]
561 implclass
= "_MYCLASS_"
566 "macroname": iface
.name
.upper(),
568 "iid": iface
.attributes
.uuid
,
569 "implclass": implclass
,
573 fd
.write(iface_header
% names
)
575 printComments(fd
, iface
.doccomments
, "")
579 for m
in iface
.members
:
580 if isinstance(m
, xpidl
.CDATA
):
584 fd
.write("NS_NO_VTABLE ")
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
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
)
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
:
624 "\\\n using %s::%s; "
625 % (iface
.name
, attributeNativeName(member
, True))
628 "\\\n %s%s; " % (attributeAsNative(member
, True, declType
), suffix
)
630 if not member
.readonly
:
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"):
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:
650 for member
in iface
.members
:
651 if isinstance(member
, xpidl
.Attribute
):
652 if forward_infallible
and member
.infallible
:
654 "\\\n using %s::%s; "
655 % (iface
.name
, attributeNativeName(member
, True))
657 attr_tmpl
= tmpl_notxpcom
if member
.notxpcom
else tmpl
661 "asNative": attributeAsNative(member
, True),
662 "nativeName": attributeNativeName(member
, True),
663 "paramList": attributeParamNames(member
, True),
666 if not member
.readonly
:
670 "asNative": attributeAsNative(member
, False),
671 "nativeName": attributeNativeName(member
, False),
672 "paramList": attributeParamNames(member
, False),
675 elif isinstance(member
, xpidl
.Method
):
680 "asNative": methodAsNative(member
),
681 "nativeName": methodNativeName(member
),
682 "paramList": paramlistNames(member
),
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"):
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
711 "\\\n %(asNative)s override { return !_to ? NS_ERROR_NULL_POINTER : _to->%(nativeName)s(%(paramList)s); } ", # NOQA: E501
712 "\\\n %(asNative)s override; ",
718 def main(outputfile
):
722 if __name__
== "__main__":