1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/idl-parser/header.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,542 @@ 1.4 +#!/usr/bin/env python 1.5 +# header.py - Generate C++ header files from IDL. 1.6 +# 1.7 +# This Source Code Form is subject to the terms of the Mozilla Public 1.8 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.9 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.10 + 1.11 +"""Print a C++ header file for the IDL files specified on the command line""" 1.12 + 1.13 +import sys, os.path, re, xpidl, itertools, glob 1.14 + 1.15 +printdoccomments = False 1.16 + 1.17 +if printdoccomments: 1.18 + def printComments(fd, clist, indent): 1.19 + for c in clist: 1.20 + fd.write("%s%s\n" % (indent, c)) 1.21 +else: 1.22 + def printComments(fd, clist, indent): 1.23 + pass 1.24 + 1.25 +def firstCap(str): 1.26 + return str[0].upper() + str[1:] 1.27 + 1.28 +def attributeParamName(a): 1.29 + return "a" + firstCap(a.name) 1.30 + 1.31 +def attributeParamNames(a): 1.32 + l = [attributeParamName(a)] 1.33 + if a.implicit_jscontext: 1.34 + l.insert(0, "cx") 1.35 + return ", ".join(l) 1.36 + 1.37 +def attributeNativeName(a, getter): 1.38 + binaryname = a.binaryname is not None and a.binaryname or firstCap(a.name) 1.39 + return "%s%s" % (getter and 'Get' or 'Set', binaryname) 1.40 + 1.41 +def attributeReturnType(a, macro): 1.42 + """macro should be NS_IMETHOD or NS_IMETHODIMP""" 1.43 + if (a.nostdcall): 1.44 + return macro == "NS_IMETHOD" and "virtual nsresult" or "nsresult" 1.45 + else: 1.46 + return macro 1.47 + 1.48 +def attributeParamlist(a, getter): 1.49 + l = ["%s%s" % (a.realtype.nativeType(getter and 'out' or 'in'), 1.50 + attributeParamName(a))] 1.51 + if a.implicit_jscontext: 1.52 + l.insert(0, "JSContext* cx") 1.53 + 1.54 + return ", ".join(l) 1.55 + 1.56 +def attributeAsNative(a, getter): 1.57 + deprecated = a.deprecated and "NS_DEPRECATED " or "" 1.58 + params = {'deprecated': deprecated, 1.59 + 'returntype': attributeReturnType(a, 'NS_IMETHOD'), 1.60 + 'binaryname': attributeNativeName(a, getter), 1.61 + 'paramlist': attributeParamlist(a, getter)} 1.62 + return "%(deprecated)s%(returntype)s %(binaryname)s(%(paramlist)s)" % params 1.63 + 1.64 +def methodNativeName(m): 1.65 + return m.binaryname is not None and m.binaryname or firstCap(m.name) 1.66 + 1.67 +def methodReturnType(m, macro): 1.68 + """macro should be NS_IMETHOD or NS_IMETHODIMP""" 1.69 + if m.nostdcall and m.notxpcom: 1.70 + return "%s%s" % (macro == "NS_IMETHOD" and "virtual " or "", 1.71 + m.realtype.nativeType('in').strip()) 1.72 + elif m.nostdcall: 1.73 + return "%snsresult" % (macro == "NS_IMETHOD" and "virtual " or "") 1.74 + elif m.notxpcom: 1.75 + return "%s_(%s)" % (macro, m.realtype.nativeType('in').strip()) 1.76 + else: 1.77 + return macro 1.78 + 1.79 +def methodAsNative(m): 1.80 + return "%s %s(%s)" % (methodReturnType(m, 'NS_IMETHOD'), 1.81 + methodNativeName(m), 1.82 + paramlistAsNative(m)) 1.83 + 1.84 +def paramlistAsNative(m, empty='void'): 1.85 + l = [paramAsNative(p) for p in m.params] 1.86 + 1.87 + if m.implicit_jscontext: 1.88 + l.append("JSContext* cx") 1.89 + 1.90 + if m.optional_argc: 1.91 + l.append('uint8_t _argc') 1.92 + 1.93 + if not m.notxpcom and m.realtype.name != 'void': 1.94 + l.append(paramAsNative(xpidl.Param(paramtype='out', 1.95 + type=None, 1.96 + name='_retval', 1.97 + attlist=[], 1.98 + location=None, 1.99 + realtype=m.realtype))) 1.100 + 1.101 + if len(l) == 0: 1.102 + return empty 1.103 + 1.104 + return ", ".join(l) 1.105 + 1.106 +def paramAsNative(p): 1.107 + return "%s%s" % (p.nativeType(), 1.108 + p.name) 1.109 + 1.110 +def paramlistNames(m): 1.111 + names = [p.name for p in m.params] 1.112 + 1.113 + if m.implicit_jscontext: 1.114 + names.append('cx') 1.115 + 1.116 + if m.optional_argc: 1.117 + names.append('_argc') 1.118 + 1.119 + if not m.notxpcom and m.realtype.name != 'void': 1.120 + names.append('_retval') 1.121 + 1.122 + if len(names) == 0: 1.123 + return '' 1.124 + return ', '.join(names) 1.125 + 1.126 +header = """/* 1.127 + * DO NOT EDIT. THIS FILE IS GENERATED FROM %(filename)s 1.128 + */ 1.129 + 1.130 +#ifndef __gen_%(basename)s_h__ 1.131 +#define __gen_%(basename)s_h__ 1.132 +""" 1.133 + 1.134 +include = """ 1.135 +#ifndef __gen_%(basename)s_h__ 1.136 +#include "%(basename)s.h" 1.137 +#endif 1.138 +""" 1.139 + 1.140 +jsvalue_include = """ 1.141 +#include "js/Value.h" 1.142 +""" 1.143 + 1.144 +infallible_includes = """ 1.145 +#include "mozilla/Assertions.h" 1.146 +#include "mozilla/DebugOnly.h" 1.147 +""" 1.148 + 1.149 +header_end = """/* For IDL files that don't want to include root IDL files. */ 1.150 +#ifndef NS_NO_VTABLE 1.151 +#define NS_NO_VTABLE 1.152 +#endif 1.153 +""" 1.154 + 1.155 +footer = """ 1.156 +#endif /* __gen_%(basename)s_h__ */ 1.157 +""" 1.158 + 1.159 +forward_decl = """class %(name)s; /* forward declaration */ 1.160 + 1.161 +""" 1.162 + 1.163 +def idl_basename(f): 1.164 + """returns the base name of a file with the last extension stripped""" 1.165 + return os.path.basename(f).rpartition('.')[0] 1.166 + 1.167 +def print_header(idl, fd, filename): 1.168 + fd.write(header % {'filename': filename, 1.169 + 'basename': idl_basename(filename)}) 1.170 + 1.171 + foundinc = False 1.172 + for inc in idl.includes(): 1.173 + if not foundinc: 1.174 + foundinc = True 1.175 + fd.write('\n') 1.176 + fd.write(include % {'basename': idl_basename(inc.filename)}) 1.177 + 1.178 + if idl.needsJSTypes(): 1.179 + fd.write(jsvalue_include) 1.180 + 1.181 + # Include some extra files if any attributes are infallible. 1.182 + for iface in [p for p in idl.productions if p.kind == 'interface']: 1.183 + for attr in [m for m in iface.members if isinstance(m, xpidl.Attribute)]: 1.184 + if attr.infallible: 1.185 + fd.write(infallible_includes) 1.186 + break 1.187 + 1.188 + fd.write('\n') 1.189 + fd.write(header_end) 1.190 + 1.191 + for p in idl.productions: 1.192 + if p.kind == 'include': continue 1.193 + if p.kind == 'cdata': 1.194 + fd.write(p.data) 1.195 + continue 1.196 + 1.197 + if p.kind == 'forward': 1.198 + fd.write(forward_decl % {'name': p.name}) 1.199 + continue 1.200 + if p.kind == 'interface': 1.201 + write_interface(p, fd) 1.202 + continue 1.203 + if p.kind == 'typedef': 1.204 + printComments(fd, p.doccomments, '') 1.205 + fd.write("typedef %s %s;\n\n" % (p.realtype.nativeType('in'), 1.206 + p.name)) 1.207 + 1.208 + fd.write(footer % {'basename': idl_basename(filename)}) 1.209 + 1.210 +iface_header = r""" 1.211 +/* starting interface: %(name)s */ 1.212 +#define %(defname)s_IID_STR "%(iid)s" 1.213 + 1.214 +#define %(defname)s_IID \ 1.215 + {0x%(m0)s, 0x%(m1)s, 0x%(m2)s, \ 1.216 + { %(m3joined)s }} 1.217 + 1.218 +""" 1.219 + 1.220 +uuid_decoder = re.compile(r"""(?P<m0>[a-f0-9]{8})- 1.221 + (?P<m1>[a-f0-9]{4})- 1.222 + (?P<m2>[a-f0-9]{4})- 1.223 + (?P<m3>[a-f0-9]{4})- 1.224 + (?P<m4>[a-f0-9]{12})$""", re.X) 1.225 + 1.226 +iface_prolog = """ { 1.227 + public: 1.228 + 1.229 + NS_DECLARE_STATIC_IID_ACCESSOR(%(defname)s_IID) 1.230 + 1.231 +""" 1.232 + 1.233 +iface_epilog = """}; 1.234 + 1.235 + NS_DEFINE_STATIC_IID_ACCESSOR(%(name)s, %(defname)s_IID) 1.236 + 1.237 +/* Use this macro when declaring classes that implement this interface. */ 1.238 +#define NS_DECL_%(macroname)s """ 1.239 + 1.240 + 1.241 +iface_forward = """ 1.242 + 1.243 +/* Use this macro to declare functions that forward the behavior of this interface to another object. */ 1.244 +#define NS_FORWARD_%(macroname)s(_to) """ 1.245 + 1.246 +iface_forward_safe = """ 1.247 + 1.248 +/* Use this macro to declare functions that forward the behavior of this interface to another object in a safe way. */ 1.249 +#define NS_FORWARD_SAFE_%(macroname)s(_to) """ 1.250 + 1.251 +iface_template_prolog = """ 1.252 + 1.253 +#if 0 1.254 +/* Use the code below as a template for the implementation class for this interface. */ 1.255 + 1.256 +/* Header file */ 1.257 +class %(implclass)s : public %(name)s 1.258 +{ 1.259 +public: 1.260 + NS_DECL_ISUPPORTS 1.261 + NS_DECL_%(macroname)s 1.262 + 1.263 + %(implclass)s(); 1.264 + 1.265 +private: 1.266 + ~%(implclass)s(); 1.267 + 1.268 +protected: 1.269 + /* additional members */ 1.270 +}; 1.271 + 1.272 +/* Implementation file */ 1.273 +NS_IMPL_ISUPPORTS(%(implclass)s, %(name)s) 1.274 + 1.275 +%(implclass)s::%(implclass)s() 1.276 +{ 1.277 + /* member initializers and constructor code */ 1.278 +} 1.279 + 1.280 +%(implclass)s::~%(implclass)s() 1.281 +{ 1.282 + /* destructor code */ 1.283 +} 1.284 + 1.285 +""" 1.286 + 1.287 +example_tmpl = """%(returntype)s %(implclass)s::%(nativeName)s(%(paramList)s) 1.288 +{ 1.289 + return NS_ERROR_NOT_IMPLEMENTED; 1.290 +} 1.291 +""" 1.292 + 1.293 +iface_template_epilog = """/* End of implementation class template. */ 1.294 +#endif 1.295 + 1.296 +""" 1.297 + 1.298 +attr_infallible_tmpl = """\ 1.299 + inline %(realtype)s%(nativename)s(%(args)s) 1.300 + { 1.301 + %(realtype)sresult; 1.302 + mozilla::DebugOnly<nsresult> rv = %(nativename)s(%(argnames)s&result); 1.303 + MOZ_ASSERT(NS_SUCCEEDED(rv)); 1.304 + return result; 1.305 + } 1.306 +""" 1.307 + 1.308 +def write_interface(iface, fd): 1.309 + if iface.namemap is None: 1.310 + raise Exception("Interface was not resolved.") 1.311 + 1.312 + def write_const_decls(g): 1.313 + fd.write(" enum {\n") 1.314 + enums = [] 1.315 + for c in g: 1.316 + printComments(fd, c.doccomments, ' ') 1.317 + basetype = c.basetype 1.318 + value = c.getValue() 1.319 + enums.append(" %(name)s = %(value)s%(signed)s" % { 1.320 + 'name': c.name, 1.321 + 'value': value, 1.322 + 'signed': (not basetype.signed) and 'U' or ''}) 1.323 + fd.write(",\n".join(enums)) 1.324 + fd.write("\n };\n\n") 1.325 + 1.326 + def write_method_decl(m): 1.327 + printComments(fd, m.doccomments, ' ') 1.328 + 1.329 + fd.write(" /* %s */\n" % m.toIDL()) 1.330 + fd.write(" %s = 0;\n\n" % methodAsNative(m)) 1.331 + 1.332 + def write_attr_decl(a): 1.333 + printComments(fd, a.doccomments, ' ') 1.334 + 1.335 + fd.write(" /* %s */\n" % a.toIDL()); 1.336 + 1.337 + fd.write(" %s = 0;\n" % attributeAsNative(a, True)) 1.338 + if a.infallible: 1.339 + fd.write(attr_infallible_tmpl % 1.340 + {'realtype': a.realtype.nativeType('in'), 1.341 + 'nativename': attributeNativeName(a, getter=True), 1.342 + 'args': '' if not a.implicit_jscontext else 'JSContext* cx', 1.343 + 'argnames': '' if not a.implicit_jscontext else 'cx, '}) 1.344 + 1.345 + if not a.readonly: 1.346 + fd.write(" %s = 0;\n" % attributeAsNative(a, False)) 1.347 + fd.write("\n") 1.348 + 1.349 + defname = iface.name.upper() 1.350 + if iface.name[0:2] == 'ns': 1.351 + defname = 'NS_' + defname[2:] 1.352 + 1.353 + names = uuid_decoder.match(iface.attributes.uuid).groupdict() 1.354 + m3str = names['m3'] + names['m4'] 1.355 + names['m3joined'] = ", ".join(["0x%s" % m3str[i:i+2] for i in xrange(0, 16, 2)]) 1.356 + 1.357 + if iface.name[2] == 'I': 1.358 + implclass = iface.name[:2] + iface.name[3:] 1.359 + else: 1.360 + implclass = '_MYCLASS_' 1.361 + 1.362 + names.update({'defname': defname, 1.363 + 'macroname': iface.name.upper(), 1.364 + 'name': iface.name, 1.365 + 'iid': iface.attributes.uuid, 1.366 + 'implclass': implclass}) 1.367 + 1.368 + fd.write(iface_header % names) 1.369 + 1.370 + printComments(fd, iface.doccomments, '') 1.371 + 1.372 + fd.write("class ") 1.373 + foundcdata = False 1.374 + for m in iface.members: 1.375 + if isinstance(m, xpidl.CDATA): 1.376 + foundcdata = True 1.377 + 1.378 + if not foundcdata: 1.379 + fd.write("NS_NO_VTABLE ") 1.380 + 1.381 + if iface.attributes.deprecated: 1.382 + fd.write("MOZ_DEPRECATED ") 1.383 + fd.write(iface.name) 1.384 + if iface.base: 1.385 + fd.write(" : public %s" % iface.base) 1.386 + fd.write(iface_prolog % names) 1.387 + 1.388 + for key, group in itertools.groupby(iface.members, key=type): 1.389 + if key == xpidl.ConstMember: 1.390 + write_const_decls(group) # iterator of all the consts 1.391 + else: 1.392 + for member in group: 1.393 + if key == xpidl.Attribute: 1.394 + write_attr_decl(member) 1.395 + elif key == xpidl.Method: 1.396 + write_method_decl(member) 1.397 + elif key == xpidl.CDATA: 1.398 + fd.write(" %s" % member.data) 1.399 + else: 1.400 + raise Exception("Unexpected interface member: %s" % member) 1.401 + 1.402 + fd.write(iface_epilog % names) 1.403 + 1.404 + for member in iface.members: 1.405 + if isinstance(member, xpidl.Attribute): 1.406 + fd.write("\\\n %s; " % attributeAsNative(member, True)) 1.407 + if not member.readonly: 1.408 + fd.write("\\\n %s; " % attributeAsNative(member, False)) 1.409 + elif isinstance(member, xpidl.Method): 1.410 + fd.write("\\\n %s; " % methodAsNative(member)) 1.411 + if len(iface.members) == 0: 1.412 + fd.write('\\\n /* no methods! */') 1.413 + elif not member.kind in ('attribute', 'method'): 1.414 + fd.write('\\') 1.415 + 1.416 + fd.write(iface_forward % names) 1.417 + 1.418 + def emitTemplate(tmpl, tmpl_notxpcom=None): 1.419 + if tmpl_notxpcom == None: 1.420 + tmpl_notxpcom = tmpl 1.421 + for member in iface.members: 1.422 + if isinstance(member, xpidl.Attribute): 1.423 + fd.write(tmpl % {'asNative': attributeAsNative(member, True), 1.424 + 'nativeName': attributeNativeName(member, True), 1.425 + 'paramList': attributeParamNames(member)}) 1.426 + if not member.readonly: 1.427 + fd.write(tmpl % {'asNative': attributeAsNative(member, False), 1.428 + 'nativeName': attributeNativeName(member, False), 1.429 + 'paramList': attributeParamNames(member)}) 1.430 + elif isinstance(member, xpidl.Method): 1.431 + if member.notxpcom: 1.432 + fd.write(tmpl_notxpcom % {'asNative': methodAsNative(member), 1.433 + 'nativeName': methodNativeName(member), 1.434 + 'paramList': paramlistNames(member)}) 1.435 + else: 1.436 + fd.write(tmpl % {'asNative': methodAsNative(member), 1.437 + 'nativeName': methodNativeName(member), 1.438 + 'paramList': paramlistNames(member)}) 1.439 + if len(iface.members) == 0: 1.440 + fd.write('\\\n /* no methods! */') 1.441 + elif not member.kind in ('attribute', 'method'): 1.442 + fd.write('\\') 1.443 + 1.444 + emitTemplate("\\\n %(asNative)s { return _to %(nativeName)s(%(paramList)s); } ") 1.445 + 1.446 + fd.write(iface_forward_safe % names) 1.447 + 1.448 + # Don't try to safely forward notxpcom functions, because we have no 1.449 + # sensible default error return. Instead, the caller will have to 1.450 + # implement them. 1.451 + emitTemplate("\\\n %(asNative)s { return !_to ? NS_ERROR_NULL_POINTER : _to->%(nativeName)s(%(paramList)s); } ", 1.452 + "\\\n %(asNative)s; ") 1.453 + 1.454 + fd.write(iface_template_prolog % names) 1.455 + 1.456 + for member in iface.members: 1.457 + if isinstance(member, xpidl.ConstMember) or isinstance(member, xpidl.CDATA): continue 1.458 + fd.write("/* %s */\n" % member.toIDL()) 1.459 + if isinstance(member, xpidl.Attribute): 1.460 + fd.write(example_tmpl % {'implclass': implclass, 1.461 + 'returntype': attributeReturnType(member, 'NS_IMETHODIMP'), 1.462 + 'nativeName': attributeNativeName(member, True), 1.463 + 'paramList': attributeParamlist(member, True)}) 1.464 + if not member.readonly: 1.465 + fd.write(example_tmpl % {'implclass': implclass, 1.466 + 'returntype': attributeReturnType(member, 'NS_IMETHODIMP'), 1.467 + 'nativeName': attributeNativeName(member, False), 1.468 + 'paramList': attributeParamlist(member, False)}) 1.469 + elif isinstance(member, xpidl.Method): 1.470 + fd.write(example_tmpl % {'implclass': implclass, 1.471 + 'returntype': methodReturnType(member, 'NS_IMETHODIMP'), 1.472 + 'nativeName': methodNativeName(member), 1.473 + 'paramList': paramlistAsNative(member, empty='')}) 1.474 + fd.write('\n') 1.475 + 1.476 + fd.write(iface_template_epilog) 1.477 + 1.478 +if __name__ == '__main__': 1.479 + from optparse import OptionParser 1.480 + o = OptionParser() 1.481 + o.add_option('-I', action='append', dest='incdirs', default=['.'], 1.482 + help="Directory to search for imported files") 1.483 + o.add_option('--cachedir', dest='cachedir', default=None, 1.484 + help="Directory in which to cache lex/parse tables.") 1.485 + o.add_option('-o', dest='outfile', default=None, 1.486 + help="Output file (default is stdout)") 1.487 + o.add_option('-d', dest='depfile', default=None, 1.488 + help="Generate a make dependency file") 1.489 + o.add_option('--regen', action='store_true', dest='regen', default=False, 1.490 + help="Regenerate IDL Parser cache") 1.491 + options, args = o.parse_args() 1.492 + file = args[0] if args else None 1.493 + 1.494 + if options.cachedir is not None: 1.495 + if not os.path.isdir(options.cachedir): 1.496 + os.mkdir(options.cachedir) 1.497 + sys.path.append(options.cachedir) 1.498 + 1.499 + # The only thing special about a regen is that there are no input files. 1.500 + if options.regen: 1.501 + if options.cachedir is None: 1.502 + print >>sys.stderr, "--regen useless without --cachedir" 1.503 + # Delete the lex/yacc files. Ply is too stupid to regenerate them 1.504 + # properly 1.505 + for fileglobs in [os.path.join(options.cachedir, f) for f in ["xpidllex.py*", "xpidlyacc.py*"]]: 1.506 + for filename in glob.glob(fileglobs): 1.507 + os.remove(filename) 1.508 + 1.509 + # Instantiate the parser. 1.510 + p = xpidl.IDLParser(outputdir=options.cachedir) 1.511 + 1.512 + if options.regen: 1.513 + sys.exit(0) 1.514 + 1.515 + if options.depfile is not None and options.outfile is None: 1.516 + print >>sys.stderr, "-d requires -o" 1.517 + sys.exit(1) 1.518 + 1.519 + if options.outfile is not None: 1.520 + outfd = open(options.outfile, 'w') 1.521 + closeoutfd = True 1.522 + else: 1.523 + outfd = sys.stdout 1.524 + closeoutfd = False 1.525 + 1.526 + idl = p.parse(open(file).read(), filename=file) 1.527 + idl.resolve(options.incdirs, p) 1.528 + print_header(idl, outfd, file) 1.529 + 1.530 + if closeoutfd: 1.531 + outfd.close() 1.532 + 1.533 + if options.depfile is not None: 1.534 + dirname = os.path.dirname(options.depfile) 1.535 + if dirname: 1.536 + try: 1.537 + os.makedirs(dirname) 1.538 + except: 1.539 + pass 1.540 + depfd = open(options.depfile, 'w') 1.541 + deps = [dep.replace('\\', '/') for dep in idl.deps] 1.542 + 1.543 + print >>depfd, "%s: %s" % (options.outfile, " ".join(deps)) 1.544 + for dep in deps: 1.545 + print >>depfd, "%s:" % dep