summaryrefslogtreecommitdiff
path: root/xpcom/idl-parser/xpidl/header.py
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/idl-parser/xpidl/header.py')
-rw-r--r--xpcom/idl-parser/xpidl/header.py566
1 files changed, 566 insertions, 0 deletions
diff --git a/xpcom/idl-parser/xpidl/header.py b/xpcom/idl-parser/xpidl/header.py
new file mode 100644
index 0000000000..8e02a7d11b
--- /dev/null
+++ b/xpcom/idl-parser/xpidl/header.py
@@ -0,0 +1,566 @@
+#!/usr/bin/env python
+# header.py - Generate C++ header files from IDL.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+"""Print a C++ header file for the IDL files specified on the command line"""
+
+import sys
+import os.path
+import re
+import xpidl
+import itertools
+import glob
+
+printdoccomments = False
+
+if printdoccomments:
+ def printComments(fd, clist, indent):
+ for c in clist:
+ fd.write("%s%s\n" % (indent, c))
+else:
+ def printComments(fd, clist, indent):
+ pass
+
+
+def firstCap(str):
+ return str[0].upper() + str[1:]
+
+
+def attributeParamName(a):
+ return "a" + firstCap(a.name)
+
+
+def attributeParamNames(a):
+ l = [attributeParamName(a)]
+ if a.implicit_jscontext:
+ l.insert(0, "cx")
+ return ", ".join(l)
+
+
+def attributeNativeName(a, getter):
+ binaryname = a.binaryname is not None and a.binaryname or firstCap(a.name)
+ return "%s%s" % (getter and 'Get' or 'Set', binaryname)
+
+
+def attributeReturnType(a, macro):
+ """macro should be NS_IMETHOD or NS_IMETHODIMP"""
+ if a.nostdcall:
+ ret = macro == "NS_IMETHOD" and "virtual nsresult" or "nsresult"
+ else:
+ ret = macro
+ if a.must_use:
+ ret = "MOZ_MUST_USE " + ret
+ return ret
+
+
+def attributeParamlist(a, getter):
+ l = ["%s%s" % (a.realtype.nativeType(getter and 'out' or 'in'),
+ attributeParamName(a))]
+ if a.implicit_jscontext:
+ l.insert(0, "JSContext* cx")
+
+ return ", ".join(l)
+
+
+def attributeAsNative(a, getter, declType = 'NS_IMETHOD'):
+ deprecated = a.deprecated and "NS_DEPRECATED " or ""
+ params = {'deprecated': deprecated,
+ 'returntype': attributeReturnType(a, declType),
+ 'binaryname': attributeNativeName(a, getter),
+ 'paramlist': attributeParamlist(a, getter)}
+ return "%(deprecated)s%(returntype)s %(binaryname)s(%(paramlist)s)" % params
+
+
+def methodNativeName(m):
+ return m.binaryname is not None and m.binaryname or firstCap(m.name)
+
+
+def methodReturnType(m, macro):
+ """macro should be NS_IMETHOD or NS_IMETHODIMP"""
+ if m.nostdcall and m.notxpcom:
+ ret = "%s%s" % (macro == "NS_IMETHOD" and "virtual " or "",
+ m.realtype.nativeType('in').strip())
+ elif m.nostdcall:
+ ret = "%snsresult" % (macro == "NS_IMETHOD" and "virtual " or "")
+ elif m.notxpcom:
+ ret = "%s_(%s)" % (macro, m.realtype.nativeType('in').strip())
+ else:
+ ret = macro
+ if m.must_use:
+ ret = "MOZ_MUST_USE " + ret
+ return ret
+
+
+def methodAsNative(m, declType = 'NS_IMETHOD'):
+ return "%s %s(%s)" % (methodReturnType(m, declType),
+ methodNativeName(m),
+ paramlistAsNative(m))
+
+
+def paramlistAsNative(m, empty='void'):
+ l = [paramAsNative(p) for p in m.params]
+
+ if m.implicit_jscontext:
+ l.append("JSContext* cx")
+
+ if m.optional_argc:
+ l.append('uint8_t _argc')
+
+ if not m.notxpcom and m.realtype.name != 'void':
+ l.append(paramAsNative(xpidl.Param(paramtype='out',
+ type=None,
+ name='_retval',
+ attlist=[],
+ location=None,
+ realtype=m.realtype)))
+
+ # Set any optional out params to default to nullptr. Skip if we just added
+ # extra non-optional args to l.
+ if len(l) == len(m.params):
+ paramIter = len(m.params) - 1
+ while (paramIter >= 0 and m.params[paramIter].optional and
+ m.params[paramIter].paramtype == "out"):
+ t = m.params[paramIter].type
+ # Strings can't be optional, so this shouldn't happen, but let's make sure:
+ if t == "AString" or t == "ACString" or t == "DOMString" or t == "AUTF8String":
+ break
+ l[paramIter] += " = nullptr"
+ paramIter -= 1
+
+ if len(l) == 0:
+ return empty
+
+ return ", ".join(l)
+
+
+def paramAsNative(p):
+ return "%s%s" % (p.nativeType(),
+ p.name)
+
+
+def paramlistNames(m):
+ names = [p.name for p in m.params]
+
+ if m.implicit_jscontext:
+ names.append('cx')
+
+ if m.optional_argc:
+ names.append('_argc')
+
+ if not m.notxpcom and m.realtype.name != 'void':
+ names.append('_retval')
+
+ if len(names) == 0:
+ return ''
+ return ', '.join(names)
+
+header = """/*
+ * DO NOT EDIT. THIS FILE IS GENERATED FROM %(filename)s
+ */
+
+#ifndef __gen_%(basename)s_h__
+#define __gen_%(basename)s_h__
+"""
+
+include = """
+#ifndef __gen_%(basename)s_h__
+#include "%(basename)s.h"
+#endif
+"""
+
+jsvalue_include = """
+#include "js/Value.h"
+"""
+
+infallible_includes = """
+#include "mozilla/Assertions.h"
+#include "mozilla/DebugOnly.h"
+"""
+
+header_end = """/* For IDL files that don't want to include root IDL files. */
+#ifndef NS_NO_VTABLE
+#define NS_NO_VTABLE
+#endif
+"""
+
+footer = """
+#endif /* __gen_%(basename)s_h__ */
+"""
+
+forward_decl = """class %(name)s; /* forward declaration */
+
+"""
+
+
+def idl_basename(f):
+ """returns the base name of a file with the last extension stripped"""
+ return os.path.basename(f).rpartition('.')[0]
+
+
+def print_header(idl, fd, filename):
+ fd.write(header % {'filename': filename,
+ 'basename': idl_basename(filename)})
+
+ foundinc = False
+ for inc in idl.includes():
+ if not foundinc:
+ foundinc = True
+ fd.write('\n')
+ fd.write(include % {'basename': idl_basename(inc.filename)})
+
+ if idl.needsJSTypes():
+ fd.write(jsvalue_include)
+
+ # Include some extra files if any attributes are infallible.
+ for iface in [p for p in idl.productions if p.kind == 'interface']:
+ for attr in [m for m in iface.members if isinstance(m, xpidl.Attribute)]:
+ if attr.infallible:
+ fd.write(infallible_includes)
+ break
+
+ fd.write('\n')
+ fd.write(header_end)
+
+ for p in idl.productions:
+ if p.kind == 'include':
+ continue
+ if p.kind == 'cdata':
+ fd.write(p.data)
+ continue
+
+ if p.kind == 'forward':
+ fd.write(forward_decl % {'name': p.name})
+ continue
+ if p.kind == 'interface':
+ write_interface(p, fd)
+ continue
+ if p.kind == 'typedef':
+ printComments(fd, p.doccomments, '')
+ fd.write("typedef %s %s;\n\n" % (p.realtype.nativeType('in'),
+ p.name))
+
+ fd.write(footer % {'basename': idl_basename(filename)})
+
+iface_header = r"""
+/* starting interface: %(name)s */
+#define %(defname)s_IID_STR "%(iid)s"
+
+#define %(defname)s_IID \
+ {0x%(m0)s, 0x%(m1)s, 0x%(m2)s, \
+ { %(m3joined)s }}
+
+"""
+
+uuid_decoder = re.compile(r"""(?P<m0>[a-f0-9]{8})-
+ (?P<m1>[a-f0-9]{4})-
+ (?P<m2>[a-f0-9]{4})-
+ (?P<m3>[a-f0-9]{4})-
+ (?P<m4>[a-f0-9]{12})$""", re.X)
+
+iface_prolog = """ {
+ public:
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(%(defname)s_IID)
+
+"""
+
+iface_epilog = """};
+
+ NS_DEFINE_STATIC_IID_ACCESSOR(%(name)s, %(defname)s_IID)
+
+/* Use this macro when declaring classes that implement this interface. */
+#define NS_DECL_%(macroname)s """
+
+iface_nonvirtual = """
+
+/* Use this macro when declaring the members of this interface when the
+ class doesn't implement the interface. This is useful for forwarding. */
+#define NS_DECL_NON_VIRTUAL_%(macroname)s """
+
+iface_forward = """
+
+/* Use this macro to declare functions that forward the behavior of this interface to another object. */
+#define NS_FORWARD_%(macroname)s(_to) """
+
+iface_forward_safe = """
+
+/* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */
+#define NS_FORWARD_SAFE_%(macroname)s(_to) """
+
+iface_template_prolog = """
+
+#if 0
+/* Use the code below as a template for the implementation class for this interface. */
+
+/* Header file */
+class %(implclass)s : public %(name)s
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_%(macroname)s
+
+ %(implclass)s();
+
+private:
+ ~%(implclass)s();
+
+protected:
+ /* additional members */
+};
+
+/* Implementation file */
+NS_IMPL_ISUPPORTS(%(implclass)s, %(name)s)
+
+%(implclass)s::%(implclass)s()
+{
+ /* member initializers and constructor code */
+}
+
+%(implclass)s::~%(implclass)s()
+{
+ /* destructor code */
+}
+
+"""
+
+example_tmpl = """%(returntype)s %(implclass)s::%(nativeName)s(%(paramList)s)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+"""
+
+iface_template_epilog = """/* End of implementation class template. */
+#endif
+
+"""
+
+attr_infallible_tmpl = """\
+ inline %(realtype)s%(nativename)s(%(args)s)
+ {
+ %(realtype)sresult;
+ mozilla::DebugOnly<nsresult> rv = %(nativename)s(%(argnames)s&result);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ return result;
+ }
+"""
+
+
+def write_interface(iface, fd):
+ if iface.namemap is None:
+ raise Exception("Interface was not resolved.")
+
+ # Confirm that no names of methods will overload in this interface
+ names = set()
+ def record_name(name):
+ if name in names:
+ raise Exception("Unexpected overloaded virtual method %s in interface %s"
+ % (name, iface.name))
+ names.add(name)
+ for m in iface.members:
+ if type(m) == xpidl.Attribute:
+ record_name(attributeNativeName(m, getter=True))
+ if not m.readonly:
+ record_name(attributeNativeName(m, getter=False))
+ elif type(m) == xpidl.Method:
+ record_name(methodNativeName(m))
+
+ def write_const_decls(g):
+ fd.write(" enum {\n")
+ enums = []
+ for c in g:
+ printComments(fd, c.doccomments, ' ')
+ basetype = c.basetype
+ value = c.getValue()
+ enums.append(" %(name)s = %(value)s%(signed)s" % {
+ 'name': c.name,
+ 'value': value,
+ 'signed': (not basetype.signed) and 'U' or ''})
+ fd.write(",\n".join(enums))
+ fd.write("\n };\n\n")
+
+ def write_method_decl(m):
+ printComments(fd, m.doccomments, ' ')
+
+ fd.write(" /* %s */\n" % m.toIDL())
+ fd.write(" %s = 0;\n\n" % methodAsNative(m))
+
+ def write_attr_decl(a):
+ printComments(fd, a.doccomments, ' ')
+
+ fd.write(" /* %s */\n" % a.toIDL())
+
+ fd.write(" %s = 0;\n" % attributeAsNative(a, True))
+ if a.infallible:
+ fd.write(attr_infallible_tmpl %
+ {'realtype': a.realtype.nativeType('in'),
+ 'nativename': attributeNativeName(a, getter=True),
+ 'args': '' if not a.implicit_jscontext else 'JSContext* cx',
+ 'argnames': '' if not a.implicit_jscontext else 'cx, '})
+
+ if not a.readonly:
+ fd.write(" %s = 0;\n" % attributeAsNative(a, False))
+ fd.write("\n")
+
+ defname = iface.name.upper()
+ if iface.name[0:2] == 'ns':
+ defname = 'NS_' + defname[2:]
+
+ names = uuid_decoder.match(iface.attributes.uuid).groupdict()
+ m3str = names['m3'] + names['m4']
+ names['m3joined'] = ", ".join(["0x%s" % m3str[i:i+2] for i in xrange(0, 16, 2)])
+
+ if iface.name[2] == 'I':
+ implclass = iface.name[:2] + iface.name[3:]
+ else:
+ implclass = '_MYCLASS_'
+
+ names.update({'defname': defname,
+ 'macroname': iface.name.upper(),
+ 'name': iface.name,
+ 'iid': iface.attributes.uuid,
+ 'implclass': implclass})
+
+ fd.write(iface_header % names)
+
+ printComments(fd, iface.doccomments, '')
+
+ fd.write("class ")
+ foundcdata = False
+ for m in iface.members:
+ if isinstance(m, xpidl.CDATA):
+ foundcdata = True
+
+ if not foundcdata:
+ fd.write("NS_NO_VTABLE ")
+
+ if iface.attributes.deprecated:
+ fd.write("MOZ_DEPRECATED ")
+ fd.write(iface.name)
+ if iface.base:
+ fd.write(" : public %s" % iface.base)
+ fd.write(iface_prolog % names)
+
+ for key, group in itertools.groupby(iface.members, key=type):
+ if key == xpidl.ConstMember:
+ write_const_decls(group) # iterator of all the consts
+ else:
+ for member in group:
+ if key == xpidl.Attribute:
+ write_attr_decl(member)
+ elif key == xpidl.Method:
+ write_method_decl(member)
+ elif key == xpidl.CDATA:
+ fd.write(" %s" % member.data)
+ else:
+ raise Exception("Unexpected interface member: %s" % member)
+
+ fd.write(iface_epilog % names)
+
+ def writeDeclaration(fd, iface, virtual):
+ declType = "NS_IMETHOD" if virtual else "NS_METHOD"
+ suffix = " override" if virtual else ""
+ for member in iface.members:
+ if isinstance(member, xpidl.Attribute):
+ if member.infallible:
+ fd.write("\\\n using %s::%s; " % (iface.name, attributeNativeName(member, True)))
+ fd.write("\\\n %s%s; " % (attributeAsNative(member, True, declType), suffix))
+ if not member.readonly:
+ fd.write("\\\n %s%s; " % (attributeAsNative(member, False, declType), suffix))
+ elif isinstance(member, xpidl.Method):
+ fd.write("\\\n %s%s; " % (methodAsNative(member, declType), suffix))
+ if len(iface.members) == 0:
+ fd.write('\\\n /* no methods! */')
+ elif not member.kind in ('attribute', 'method'):
+ fd.write('\\')
+
+ writeDeclaration(fd, iface, True);
+ fd.write(iface_nonvirtual % names)
+ writeDeclaration(fd, iface, False);
+ fd.write(iface_forward % names)
+
+ def emitTemplate(forward_infallible, tmpl, tmpl_notxpcom=None):
+ if tmpl_notxpcom is None:
+ tmpl_notxpcom = tmpl
+ for member in iface.members:
+ if isinstance(member, xpidl.Attribute):
+ if forward_infallible and member.infallible:
+ fd.write("\\\n using %s::%s; " % (iface.name, attributeNativeName(member, True)))
+ fd.write(tmpl % {'asNative': attributeAsNative(member, True),
+ 'nativeName': attributeNativeName(member, True),
+ 'paramList': attributeParamNames(member)})
+ if not member.readonly:
+ fd.write(tmpl % {'asNative': attributeAsNative(member, False),
+ 'nativeName': attributeNativeName(member, False),
+ 'paramList': attributeParamNames(member)})
+ elif isinstance(member, xpidl.Method):
+ if member.notxpcom:
+ fd.write(tmpl_notxpcom % {'asNative': methodAsNative(member),
+ 'nativeName': methodNativeName(member),
+ 'paramList': paramlistNames(member)})
+ else:
+ fd.write(tmpl % {'asNative': methodAsNative(member),
+ 'nativeName': methodNativeName(member),
+ 'paramList': paramlistNames(member)})
+ if len(iface.members) == 0:
+ fd.write('\\\n /* no methods! */')
+ elif not member.kind in ('attribute', 'method'):
+ fd.write('\\')
+
+ emitTemplate(True,
+ "\\\n %(asNative)s override { return _to %(nativeName)s(%(paramList)s); } ")
+
+ fd.write(iface_forward_safe % names)
+
+ # Don't try to safely forward notxpcom functions, because we have no
+ # sensible default error return. Instead, the caller will have to
+ # implement them.
+ emitTemplate(False,
+ "\\\n %(asNative)s override { return !_to ? NS_ERROR_NULL_POINTER : _to->%(nativeName)s(%(paramList)s); } ",
+ "\\\n %(asNative)s override; ")
+
+ fd.write(iface_template_prolog % names)
+
+ for member in iface.members:
+ if isinstance(member, xpidl.ConstMember) or isinstance(member, xpidl.CDATA):
+ continue
+ fd.write("/* %s */\n" % member.toIDL())
+ if isinstance(member, xpidl.Attribute):
+ fd.write(example_tmpl % {'implclass': implclass,
+ 'returntype': attributeReturnType(member, 'NS_IMETHODIMP'),
+ 'nativeName': attributeNativeName(member, True),
+ 'paramList': attributeParamlist(member, True)})
+ if not member.readonly:
+ fd.write(example_tmpl % {'implclass': implclass,
+ 'returntype': attributeReturnType(member, 'NS_IMETHODIMP'),
+ 'nativeName': attributeNativeName(member, False),
+ 'paramList': attributeParamlist(member, False)})
+ elif isinstance(member, xpidl.Method):
+ fd.write(example_tmpl % {'implclass': implclass,
+ 'returntype': methodReturnType(member, 'NS_IMETHODIMP'),
+ 'nativeName': methodNativeName(member),
+ 'paramList': paramlistAsNative(member, empty='')})
+ fd.write('\n')
+
+ fd.write(iface_template_epilog)
+
+
+def main(outputfile):
+ cachedir = '.'
+ if not os.path.isdir(cachedir):
+ os.mkdir(cachedir)
+ sys.path.append(cachedir)
+
+ # Delete the lex/yacc files. Ply is too stupid to regenerate them
+ # properly
+ for fileglobs in [os.path.join(cachedir, f) for f in ["xpidllex.py*", "xpidlyacc.py*"]]:
+ for filename in glob.glob(fileglobs):
+ os.remove(filename)
+
+ # Instantiate the parser.
+ p = xpidl.IDLParser(outputdir=cachedir)
+
+if __name__ == '__main__':
+ main(None)