xpcom/idl-parser/header.py

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 #!/usr/bin/env python
     2 # header.py - Generate C++ header files from IDL.
     3 #
     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 sys, os.path, re, xpidl, itertools, glob
    12 printdoccomments = False
    14 if printdoccomments:
    15     def printComments(fd, clist, indent):
    16         for c in clist:
    17             fd.write("%s%s\n" % (indent, c))
    18 else:
    19     def printComments(fd, clist, indent):
    20         pass
    22 def firstCap(str):
    23     return str[0].upper() + str[1:]
    25 def attributeParamName(a):
    26     return "a" + firstCap(a.name)
    28 def attributeParamNames(a):
    29     l = [attributeParamName(a)]
    30     if a.implicit_jscontext:
    31         l.insert(0, "cx")
    32     return ", ".join(l)
    34 def attributeNativeName(a, getter):
    35     binaryname = a.binaryname is not None and a.binaryname or firstCap(a.name)
    36     return "%s%s" % (getter and 'Get' or 'Set', binaryname)
    38 def attributeReturnType(a, macro):
    39     """macro should be NS_IMETHOD or NS_IMETHODIMP"""
    40     if (a.nostdcall):
    41         return macro == "NS_IMETHOD" and "virtual nsresult" or "nsresult"
    42     else:
    43         return macro
    45 def attributeParamlist(a, getter):
    46     l = ["%s%s" % (a.realtype.nativeType(getter and 'out' or 'in'),
    47                    attributeParamName(a))]
    48     if a.implicit_jscontext:
    49         l.insert(0, "JSContext* cx")
    51     return ", ".join(l)
    53 def attributeAsNative(a, getter):
    54         deprecated = a.deprecated and "NS_DEPRECATED " or ""
    55         params = {'deprecated': deprecated,
    56                   'returntype': attributeReturnType(a, 'NS_IMETHOD'),
    57                   'binaryname': attributeNativeName(a, getter),
    58                   'paramlist': attributeParamlist(a, getter)}
    59         return "%(deprecated)s%(returntype)s %(binaryname)s(%(paramlist)s)" % params
    61 def methodNativeName(m):
    62     return m.binaryname is not None and m.binaryname or firstCap(m.name)
    64 def methodReturnType(m, macro):
    65     """macro should be NS_IMETHOD or NS_IMETHODIMP"""
    66     if m.nostdcall and m.notxpcom:
    67         return "%s%s" % (macro == "NS_IMETHOD" and "virtual " or "",
    68                          m.realtype.nativeType('in').strip())
    69     elif m.nostdcall:
    70         return "%snsresult" % (macro == "NS_IMETHOD" and "virtual " or "")
    71     elif m.notxpcom:
    72         return "%s_(%s)" % (macro, m.realtype.nativeType('in').strip())
    73     else:
    74         return macro
    76 def methodAsNative(m):
    77     return "%s %s(%s)" % (methodReturnType(m, 'NS_IMETHOD'),
    78                           methodNativeName(m),
    79                           paramlistAsNative(m))
    81 def paramlistAsNative(m, empty='void'):
    82     l = [paramAsNative(p) for p in m.params]
    84     if m.implicit_jscontext:
    85         l.append("JSContext* cx")
    87     if m.optional_argc:
    88         l.append('uint8_t _argc')
    90     if not m.notxpcom and m.realtype.name != 'void':
    91         l.append(paramAsNative(xpidl.Param(paramtype='out',
    92                                            type=None,
    93                                            name='_retval',
    94                                            attlist=[],
    95                                            location=None,
    96                                            realtype=m.realtype)))
    98     if len(l) == 0:
    99         return empty
   101     return ", ".join(l)
   103 def paramAsNative(p):
   104     return "%s%s" % (p.nativeType(),
   105                      p.name)
   107 def paramlistNames(m):
   108     names = [p.name for p in m.params]
   110     if m.implicit_jscontext:
   111         names.append('cx')
   113     if m.optional_argc:
   114         names.append('_argc')
   116     if not m.notxpcom and m.realtype.name != 'void':
   117         names.append('_retval')
   119     if len(names) == 0:
   120         return ''
   121     return ', '.join(names)
   123 header = """/*
   124  * DO NOT EDIT.  THIS FILE IS GENERATED FROM %(filename)s
   125  */
   127 #ifndef __gen_%(basename)s_h__
   128 #define __gen_%(basename)s_h__
   129 """
   131 include = """
   132 #ifndef __gen_%(basename)s_h__
   133 #include "%(basename)s.h"
   134 #endif
   135 """
   137 jsvalue_include = """
   138 #include "js/Value.h"
   139 """
   141 infallible_includes = """
   142 #include "mozilla/Assertions.h"
   143 #include "mozilla/DebugOnly.h"
   144 """
   146 header_end = """/* For IDL files that don't want to include root IDL files. */
   147 #ifndef NS_NO_VTABLE
   148 #define NS_NO_VTABLE
   149 #endif
   150 """
   152 footer = """
   153 #endif /* __gen_%(basename)s_h__ */
   154 """
   156 forward_decl = """class %(name)s; /* forward declaration */
   158 """
   160 def idl_basename(f):
   161     """returns the base name of a file with the last extension stripped"""
   162     return os.path.basename(f).rpartition('.')[0]
   164 def print_header(idl, fd, filename):
   165     fd.write(header % {'filename': filename,
   166                        'basename': idl_basename(filename)})
   168     foundinc = False
   169     for inc in idl.includes():
   170         if not foundinc:
   171             foundinc = True
   172             fd.write('\n')
   173         fd.write(include % {'basename': idl_basename(inc.filename)})
   175     if idl.needsJSTypes():
   176         fd.write(jsvalue_include)
   178     # Include some extra files if any attributes are infallible.
   179     for iface in [p for p in idl.productions if p.kind == 'interface']:
   180         for attr in [m for m in iface.members if isinstance(m, xpidl.Attribute)]:
   181             if attr.infallible:
   182                 fd.write(infallible_includes)
   183                 break
   185     fd.write('\n')
   186     fd.write(header_end)
   188     for p in idl.productions:
   189         if p.kind == 'include': continue
   190         if p.kind == 'cdata':
   191             fd.write(p.data)
   192             continue
   194         if p.kind == 'forward':
   195             fd.write(forward_decl % {'name': p.name})
   196             continue
   197         if p.kind == 'interface':
   198             write_interface(p, fd)
   199             continue
   200         if p.kind == 'typedef':
   201             printComments(fd, p.doccomments, '')
   202             fd.write("typedef %s %s;\n\n" % (p.realtype.nativeType('in'),
   203                                              p.name))
   205     fd.write(footer % {'basename': idl_basename(filename)})
   207 iface_header = r"""
   208 /* starting interface:    %(name)s */
   209 #define %(defname)s_IID_STR "%(iid)s"
   211 #define %(defname)s_IID \
   212   {0x%(m0)s, 0x%(m1)s, 0x%(m2)s, \
   213     { %(m3joined)s }}
   215 """
   217 uuid_decoder = re.compile(r"""(?P<m0>[a-f0-9]{8})-
   218                               (?P<m1>[a-f0-9]{4})-
   219                               (?P<m2>[a-f0-9]{4})-
   220                               (?P<m3>[a-f0-9]{4})-
   221                               (?P<m4>[a-f0-9]{12})$""", re.X)
   223 iface_prolog = """ {
   224  public: 
   226   NS_DECLARE_STATIC_IID_ACCESSOR(%(defname)s_IID)
   228 """
   230 iface_epilog = """};
   232   NS_DEFINE_STATIC_IID_ACCESSOR(%(name)s, %(defname)s_IID)
   234 /* Use this macro when declaring classes that implement this interface. */
   235 #define NS_DECL_%(macroname)s """
   238 iface_forward = """
   240 /* Use this macro to declare functions that forward the behavior of this interface to another object. */
   241 #define NS_FORWARD_%(macroname)s(_to) """
   243 iface_forward_safe = """
   245 /* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */
   246 #define NS_FORWARD_SAFE_%(macroname)s(_to) """
   248 iface_template_prolog = """
   250 #if 0
   251 /* Use the code below as a template for the implementation class for this interface. */
   253 /* Header file */
   254 class %(implclass)s : public %(name)s
   255 {
   256 public:
   257   NS_DECL_ISUPPORTS
   258   NS_DECL_%(macroname)s
   260   %(implclass)s();
   262 private:
   263   ~%(implclass)s();
   265 protected:
   266   /* additional members */
   267 };
   269 /* Implementation file */
   270 NS_IMPL_ISUPPORTS(%(implclass)s, %(name)s)
   272 %(implclass)s::%(implclass)s()
   273 {
   274   /* member initializers and constructor code */
   275 }
   277 %(implclass)s::~%(implclass)s()
   278 {
   279   /* destructor code */
   280 }
   282 """
   284 example_tmpl = """%(returntype)s %(implclass)s::%(nativeName)s(%(paramList)s)
   285 {
   286     return NS_ERROR_NOT_IMPLEMENTED;
   287 }
   288 """
   290 iface_template_epilog = """/* End of implementation class template. */
   291 #endif
   293 """
   295 attr_infallible_tmpl = """\
   296   inline %(realtype)s%(nativename)s(%(args)s)
   297   {
   298     %(realtype)sresult;
   299     mozilla::DebugOnly<nsresult> rv = %(nativename)s(%(argnames)s&result);
   300     MOZ_ASSERT(NS_SUCCEEDED(rv));
   301     return result;
   302   }
   303 """
   305 def write_interface(iface, fd):
   306     if iface.namemap is None:
   307         raise Exception("Interface was not resolved.")
   309     def write_const_decls(g):
   310         fd.write("  enum {\n")
   311         enums = []
   312         for c in g:
   313             printComments(fd, c.doccomments, '  ')
   314             basetype = c.basetype
   315             value = c.getValue()
   316             enums.append("    %(name)s = %(value)s%(signed)s" % {
   317                          'name': c.name,
   318                          'value': value,
   319                          'signed': (not basetype.signed) and 'U' or ''})
   320         fd.write(",\n".join(enums))
   321         fd.write("\n  };\n\n")
   323     def write_method_decl(m):
   324         printComments(fd, m.doccomments, '  ')
   326         fd.write("  /* %s */\n" % m.toIDL())
   327         fd.write("  %s = 0;\n\n" % methodAsNative(m))
   329     def write_attr_decl(a):
   330         printComments(fd, a.doccomments, '  ')
   332         fd.write("  /* %s */\n" % a.toIDL());
   334         fd.write("  %s = 0;\n" % attributeAsNative(a, True))
   335         if a.infallible:
   336             fd.write(attr_infallible_tmpl %
   337                      {'realtype': a.realtype.nativeType('in'),
   338                       'nativename': attributeNativeName(a, getter=True),
   339                       'args': '' if not a.implicit_jscontext else 'JSContext* cx',
   340                       'argnames': '' if not a.implicit_jscontext else 'cx, '})
   342         if not a.readonly:
   343             fd.write("  %s = 0;\n" % attributeAsNative(a, False))
   344         fd.write("\n")
   346     defname = iface.name.upper()
   347     if iface.name[0:2] == 'ns':
   348         defname = 'NS_' + defname[2:]
   350     names = uuid_decoder.match(iface.attributes.uuid).groupdict()
   351     m3str = names['m3'] + names['m4']
   352     names['m3joined'] = ", ".join(["0x%s" % m3str[i:i+2] for i in xrange(0, 16, 2)])
   354     if iface.name[2] == 'I':
   355         implclass = iface.name[:2] + iface.name[3:]
   356     else:
   357         implclass = '_MYCLASS_'
   359     names.update({'defname': defname,
   360                   'macroname': iface.name.upper(),
   361                   'name': iface.name,
   362                   'iid': iface.attributes.uuid,
   363                   'implclass': implclass})
   365     fd.write(iface_header % names)
   367     printComments(fd, iface.doccomments, '')
   369     fd.write("class ")
   370     foundcdata = False
   371     for m in iface.members:
   372         if isinstance(m, xpidl.CDATA):
   373             foundcdata = True
   375     if not foundcdata:
   376         fd.write("NS_NO_VTABLE ")
   378     if iface.attributes.deprecated:
   379         fd.write("MOZ_DEPRECATED ")
   380     fd.write(iface.name)
   381     if iface.base:
   382         fd.write(" : public %s" % iface.base)
   383     fd.write(iface_prolog % names)
   385     for key, group in itertools.groupby(iface.members, key=type):
   386         if key == xpidl.ConstMember:
   387             write_const_decls(group) # iterator of all the consts
   388         else:
   389             for member in group:
   390                 if key == xpidl.Attribute:
   391                     write_attr_decl(member)
   392                 elif key == xpidl.Method:
   393                     write_method_decl(member)
   394                 elif key == xpidl.CDATA:
   395                     fd.write(" %s" % member.data)
   396                 else:
   397                     raise Exception("Unexpected interface member: %s" % member)
   399     fd.write(iface_epilog % names)
   401     for member in iface.members:
   402         if isinstance(member, xpidl.Attribute):
   403             fd.write("\\\n  %s; " % attributeAsNative(member, True))
   404             if not member.readonly:
   405                 fd.write("\\\n  %s; " % attributeAsNative(member, False))
   406         elif isinstance(member, xpidl.Method):
   407             fd.write("\\\n  %s; " % methodAsNative(member))
   408     if len(iface.members) == 0:
   409         fd.write('\\\n  /* no methods! */')
   410     elif not member.kind in ('attribute', 'method'):
   411        fd.write('\\')
   413     fd.write(iface_forward % names)
   415     def emitTemplate(tmpl, tmpl_notxpcom=None):
   416         if tmpl_notxpcom == None:
   417             tmpl_notxpcom = tmpl
   418         for member in iface.members:
   419             if isinstance(member, xpidl.Attribute):
   420                 fd.write(tmpl % {'asNative': attributeAsNative(member, True),
   421                                  'nativeName': attributeNativeName(member, True),
   422                                  'paramList': attributeParamNames(member)})
   423                 if not member.readonly:
   424                     fd.write(tmpl % {'asNative': attributeAsNative(member, False),
   425                                      'nativeName': attributeNativeName(member, False),
   426                                      'paramList': attributeParamNames(member)})
   427             elif isinstance(member, xpidl.Method):
   428                 if member.notxpcom:
   429                     fd.write(tmpl_notxpcom % {'asNative': methodAsNative(member),
   430                                               'nativeName': methodNativeName(member),
   431                                               'paramList': paramlistNames(member)})
   432                 else:
   433                     fd.write(tmpl % {'asNative': methodAsNative(member),
   434                                      'nativeName': methodNativeName(member),
   435                                      'paramList': paramlistNames(member)})
   436         if len(iface.members) == 0:
   437             fd.write('\\\n  /* no methods! */')
   438         elif not member.kind in ('attribute', 'method'):
   439             fd.write('\\')
   441     emitTemplate("\\\n  %(asNative)s { return _to %(nativeName)s(%(paramList)s); } ")
   443     fd.write(iface_forward_safe % names)
   445     # Don't try to safely forward notxpcom functions, because we have no
   446     # sensible default error return.  Instead, the caller will have to
   447     # implement them.
   448     emitTemplate("\\\n  %(asNative)s { return !_to ? NS_ERROR_NULL_POINTER : _to->%(nativeName)s(%(paramList)s); } ",
   449                  "\\\n  %(asNative)s; ")
   451     fd.write(iface_template_prolog % names)
   453     for member in iface.members:
   454         if isinstance(member, xpidl.ConstMember) or isinstance(member, xpidl.CDATA): continue
   455         fd.write("/* %s */\n" % member.toIDL())
   456         if isinstance(member, xpidl.Attribute):
   457             fd.write(example_tmpl % {'implclass': implclass,
   458                                      'returntype': attributeReturnType(member, 'NS_IMETHODIMP'),
   459                                      'nativeName': attributeNativeName(member, True),
   460                                      'paramList': attributeParamlist(member, True)})
   461             if not member.readonly:
   462                 fd.write(example_tmpl % {'implclass': implclass,
   463                                          'returntype': attributeReturnType(member, 'NS_IMETHODIMP'),
   464                                          'nativeName': attributeNativeName(member, False),
   465                                          'paramList': attributeParamlist(member, False)})
   466         elif isinstance(member, xpidl.Method):
   467             fd.write(example_tmpl % {'implclass': implclass,
   468                                      'returntype': methodReturnType(member, 'NS_IMETHODIMP'),
   469                                      'nativeName': methodNativeName(member),
   470                                      'paramList': paramlistAsNative(member, empty='')})
   471         fd.write('\n')
   473     fd.write(iface_template_epilog)
   475 if __name__ == '__main__':
   476     from optparse import OptionParser
   477     o = OptionParser()
   478     o.add_option('-I', action='append', dest='incdirs', default=['.'],
   479                  help="Directory to search for imported files")
   480     o.add_option('--cachedir', dest='cachedir', default=None,
   481                  help="Directory in which to cache lex/parse tables.")
   482     o.add_option('-o', dest='outfile', default=None,
   483                  help="Output file (default is stdout)")
   484     o.add_option('-d', dest='depfile', default=None,
   485                  help="Generate a make dependency file")
   486     o.add_option('--regen', action='store_true', dest='regen', default=False,
   487                  help="Regenerate IDL Parser cache")
   488     options, args = o.parse_args()
   489     file = args[0] if args else None
   491     if options.cachedir is not None:
   492         if not os.path.isdir(options.cachedir):
   493             os.mkdir(options.cachedir)
   494         sys.path.append(options.cachedir)
   496     # The only thing special about a regen is that there are no input files.
   497     if options.regen:
   498         if options.cachedir is None:
   499             print >>sys.stderr, "--regen useless without --cachedir"
   500         # Delete the lex/yacc files.  Ply is too stupid to regenerate them
   501         # properly
   502         for fileglobs in [os.path.join(options.cachedir, f) for f in ["xpidllex.py*", "xpidlyacc.py*"]]:
   503             for filename in glob.glob(fileglobs):
   504                 os.remove(filename)
   506     # Instantiate the parser.
   507     p = xpidl.IDLParser(outputdir=options.cachedir)
   509     if options.regen:
   510         sys.exit(0)
   512     if options.depfile is not None and options.outfile is None:
   513         print >>sys.stderr, "-d requires -o"
   514         sys.exit(1)
   516     if options.outfile is not None:
   517         outfd = open(options.outfile, 'w')
   518         closeoutfd = True
   519     else:
   520         outfd = sys.stdout
   521         closeoutfd = False
   523     idl = p.parse(open(file).read(), filename=file)
   524     idl.resolve(options.incdirs, p)
   525     print_header(idl, outfd, file)
   527     if closeoutfd:
   528         outfd.close()
   530     if options.depfile is not None:
   531         dirname = os.path.dirname(options.depfile)
   532         if dirname:
   533             try:
   534                 os.makedirs(dirname)
   535             except:
   536                 pass
   537         depfd = open(options.depfile, 'w')
   538         deps = [dep.replace('\\', '/') for dep in idl.deps]
   540         print >>depfd, "%s: %s" % (options.outfile, " ".join(deps))
   541         for dep in deps:
   542             print >>depfd, "%s:" % dep

mercurial