xpcom/idl-parser/typelib.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/xpcom/idl-parser/typelib.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,302 @@
     1.4 +#!/usr/bin/env python
     1.5 +# typelib.py - Generate XPCOM typelib 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 +"""Generate an XPIDL typelib for the IDL files specified on the command line"""
    1.12 +
    1.13 +import os
    1.14 +import sys
    1.15 +import xpidl, xpt
    1.16 +
    1.17 +# A map of xpidl.py types to xpt.py types
    1.18 +TypeMap = {
    1.19 +    # nsresult is not strictly an xpidl.py type, but it's useful here
    1.20 +    'nsresult':           xpt.Type.Tags.uint32,
    1.21 +    # builtins
    1.22 +    'boolean':            xpt.Type.Tags.boolean,
    1.23 +    'void':               xpt.Type.Tags.void,
    1.24 +    'int16_t':            xpt.Type.Tags.int16,
    1.25 +    'int32_t':            xpt.Type.Tags.int32,
    1.26 +    'int64_t':            xpt.Type.Tags.int64,
    1.27 +    'uint8_t':            xpt.Type.Tags.uint8,
    1.28 +    'uint16_t':           xpt.Type.Tags.uint16,
    1.29 +    'uint32_t':           xpt.Type.Tags.uint32,
    1.30 +    'uint64_t':           xpt.Type.Tags.uint64,
    1.31 +    'octet':              xpt.Type.Tags.uint8,
    1.32 +    'short':              xpt.Type.Tags.int16,
    1.33 +    'long':               xpt.Type.Tags.int32,
    1.34 +    'long long':          xpt.Type.Tags.int64,
    1.35 +    'unsigned short':     xpt.Type.Tags.uint16,
    1.36 +    'unsigned long':      xpt.Type.Tags.uint32,
    1.37 +    'unsigned long long': xpt.Type.Tags.uint64,
    1.38 +    'float':              xpt.Type.Tags.float,
    1.39 +    'double':             xpt.Type.Tags.double,
    1.40 +    'char':               xpt.Type.Tags.char,
    1.41 +    'string':             xpt.Type.Tags.char_ptr,
    1.42 +    'wchar':              xpt.Type.Tags.wchar_t,
    1.43 +    'wstring':            xpt.Type.Tags.wchar_t_ptr,
    1.44 +    # special types
    1.45 +    'nsid':               xpt.Type.Tags.nsIID,
    1.46 +    'domstring':          xpt.Type.Tags.DOMString,
    1.47 +    'astring':            xpt.Type.Tags.AString,
    1.48 +    'utf8string':         xpt.Type.Tags.UTF8String,
    1.49 +    'cstring':            xpt.Type.Tags.CString,
    1.50 +    'jsval':              xpt.Type.Tags.jsval
    1.51 +}
    1.52 +
    1.53 +# XXXkhuey dipper types should go away (bug 677784)
    1.54 +def isDipperType(type):
    1.55 +    return type == xpt.Type.Tags.DOMString or type == xpt.Type.Tags.AString or type == xpt.Type.Tags.CString or type == xpt.Type.Tags.UTF8String
    1.56 +
    1.57 +def build_interface(iface, ifaces):
    1.58 +    def get_type(type, calltype, iid_is=None, size_is=None):
    1.59 +        """ Return the appropriate xpt.Type object for this param """
    1.60 +
    1.61 +        while isinstance(type, xpidl.Typedef):
    1.62 +            type = type.realtype
    1.63 +
    1.64 +        if isinstance(type, xpidl.Builtin):
    1.65 +            if type.name == 'string' and size_is != None:
    1.66 +                  return xpt.StringWithSizeType(size_is, size_is)
    1.67 +            elif type.name == 'wstring' and size_is != None:
    1.68 +                  return xpt.WideStringWithSizeType(size_is, size_is)
    1.69 +            else:
    1.70 +                  tag = TypeMap[type.name]
    1.71 +                  isPtr = (tag == xpt.Type.Tags.char_ptr or tag == xpt.Type.Tags.wchar_t_ptr)
    1.72 +                  return xpt.SimpleType(tag,
    1.73 +                                        pointer=isPtr,
    1.74 +                                        reference=False)
    1.75 +
    1.76 +        if isinstance(type, xpidl.Array):
    1.77 +            # NB: For an Array<T> we pass down the iid_is to get the type of T.
    1.78 +            #     This allows Arrays of InterfaceIs types to work.
    1.79 +            return xpt.ArrayType(get_type(type.type, calltype, iid_is), size_is,
    1.80 +                                 #XXXkhuey length_is duplicates size_is (bug 677788),
    1.81 +                                 size_is)
    1.82 +
    1.83 +        if isinstance(type, xpidl.Interface) or isinstance(type, xpidl.Forward):
    1.84 +            xptiface = None
    1.85 +            for i in ifaces:
    1.86 +                if i.name == type.name:
    1.87 +                    xptiface = i
    1.88 +
    1.89 +            if not xptiface:
    1.90 +                xptiface = xpt.Interface(name=type.name)
    1.91 +                ifaces.append(xptiface)
    1.92 +
    1.93 +            return xpt.InterfaceType(xptiface)
    1.94 +
    1.95 +        if isinstance(type, xpidl.Native):
    1.96 +            if type.specialtype:
    1.97 +                # XXXkhuey jsval is marked differently in the typelib and in the headers :-(
    1.98 +                isPtr = (type.isPtr(calltype) or type.isRef(calltype)) and not type.specialtype == 'jsval'
    1.99 +                isRef = type.isRef(calltype) and not type.specialtype == 'jsval'
   1.100 +                return xpt.SimpleType(TypeMap[type.specialtype],
   1.101 +                                      pointer=isPtr,
   1.102 +                                      reference=isRef)
   1.103 +            elif iid_is != None:
   1.104 +                return xpt.InterfaceIsType(iid_is)
   1.105 +            else:
   1.106 +                # void ptr
   1.107 +                return xpt.SimpleType(TypeMap['void'],
   1.108 +                                      pointer=True,
   1.109 +                                      reference=False)
   1.110 +
   1.111 +        raise Exception("Unknown type!")
   1.112 +
   1.113 +    def get_nsresult():
   1.114 +        return xpt.SimpleType(TypeMap['nsresult'])
   1.115 +
   1.116 +    def build_nsresult_param():
   1.117 +        return xpt.Param(get_nsresult())
   1.118 +
   1.119 +    def get_result_type(m):
   1.120 +        if not m.notxpcom:
   1.121 +            return get_nsresult()
   1.122 +
   1.123 +        return get_type(m.realtype, '')
   1.124 +
   1.125 +    def build_result_param(m):
   1.126 +        return xpt.Param(get_result_type(m))
   1.127 +
   1.128 +    def build_retval_param(m):
   1.129 +        type = get_type(m.realtype, 'out')
   1.130 +        if isDipperType(type.tag):
   1.131 +            # NB: The retval bit needs to be set here, contrary to what the
   1.132 +            # xpt spec says.
   1.133 +            return xpt.Param(type, in_=True, retval=True, dipper=True)
   1.134 +        return xpt.Param(type, in_=False, out=True, retval=True)
   1.135 +
   1.136 +    def build_attr_param(a, getter=False, setter=False):
   1.137 +        if not (getter or setter):
   1.138 +            raise Exception("Attribute param must be for a getter or a setter!")
   1.139 +
   1.140 +        type = get_type(a.realtype, getter and 'out' or 'in')
   1.141 +        if setter:
   1.142 +            return xpt.Param(type)
   1.143 +        else:
   1.144 +            if isDipperType(type.tag):
   1.145 +                # NB: The retval bit needs to be set here, contrary to what the
   1.146 +                # xpt spec says.
   1.147 +                return xpt.Param(type, in_=True, retval=True, dipper=True)
   1.148 +            return xpt.Param(type, in_=False, out=True, retval=True)
   1.149 +
   1.150 +    if iface.namemap is None:
   1.151 +        raise Exception("Interface was not resolved.")
   1.152 +
   1.153 +    consts = []
   1.154 +    methods = []
   1.155 +
   1.156 +    def build_const(c):
   1.157 +        consts.append(xpt.Constant(c.name, get_type(c.basetype, ''), c.getValue()))
   1.158 +
   1.159 +    def build_method(m):
   1.160 +        params = []
   1.161 +
   1.162 +        def build_param(p):
   1.163 +            def findattr(p, attr):
   1.164 +                if hasattr(p, attr) and getattr(p, attr):
   1.165 +                    for i, param in enumerate(m.params):
   1.166 +                        if param.name == getattr(p, attr):
   1.167 +                            return i
   1.168 +                    return None
   1.169 +
   1.170 +            iid_is = findattr(p, 'iid_is')
   1.171 +            size_is = findattr(p, 'size_is')
   1.172 +
   1.173 +            in_ = p.paramtype.count("in")
   1.174 +            out = p.paramtype.count("out")
   1.175 +            dipper = False
   1.176 +            type = get_type(p.realtype, p.paramtype, iid_is=iid_is, size_is=size_is)
   1.177 +            if out and isDipperType(type.tag):
   1.178 +                out = False
   1.179 +                dipper = True
   1.180 +
   1.181 +            return xpt.Param(type, in_, out, p.retval, p.shared, dipper, p.optional)
   1.182 +
   1.183 +        for p in m.params:
   1.184 +            params.append(build_param(p))
   1.185 +
   1.186 +        if not m.notxpcom and m.realtype.name != 'void':
   1.187 +            params.append(build_retval_param(m))
   1.188 +
   1.189 +        methods.append(xpt.Method(m.name, build_result_param(m), params,
   1.190 +                                  getter=False, setter=False, notxpcom=m.notxpcom,
   1.191 +                                  constructor=False, hidden=m.noscript,
   1.192 +                                  optargc=m.optional_argc,
   1.193 +                                  implicit_jscontext=m.implicit_jscontext))
   1.194 +
   1.195 +    def build_attr(a):
   1.196 +        # Write the getter
   1.197 +        methods.append(xpt.Method(a.name, build_nsresult_param(),
   1.198 +                                  [build_attr_param(a, getter=True)],
   1.199 +                                  getter=True, setter=False,
   1.200 +                                  constructor=False, hidden=a.noscript,
   1.201 +                                  optargc=False,
   1.202 +                                  implicit_jscontext=a.implicit_jscontext))
   1.203 +
   1.204 +        # And maybe the setter
   1.205 +        if not a.readonly:
   1.206 +            methods.append(xpt.Method(a.name, build_nsresult_param(),
   1.207 +                                      [build_attr_param(a, setter=True)],
   1.208 +                                      getter=False, setter=True,
   1.209 +                                      constructor=False, hidden=a.noscript,
   1.210 +                                      optargc=False,
   1.211 +                                      implicit_jscontext=a.implicit_jscontext))
   1.212 +
   1.213 +    for member in iface.members:
   1.214 +        if isinstance(member, xpidl.ConstMember):
   1.215 +            build_const(member)
   1.216 +        elif isinstance(member, xpidl.Attribute):
   1.217 +            build_attr(member)
   1.218 +        elif isinstance(member, xpidl.Method):
   1.219 +            build_method(member)
   1.220 +        elif isinstance(member, xpidl.CDATA):
   1.221 +            pass
   1.222 +        else:
   1.223 +            raise Exception("Unexpected interface member: %s" % member)
   1.224 +
   1.225 +    parent = None
   1.226 +    if iface.base:
   1.227 +        for i in ifaces:
   1.228 +            if i.name == iface.base:
   1.229 +                parent = i
   1.230 +        if not parent:
   1.231 +            parent = xpt.Interface(name=iface.base)
   1.232 +            ifaces.append(parent)
   1.233 +
   1.234 +    return xpt.Interface(iface.name, iface.attributes.uuid, methods=methods,
   1.235 +                         constants=consts, resolved=True, parent=parent,
   1.236 +                         scriptable=iface.attributes.scriptable,
   1.237 +                         function=iface.attributes.function,
   1.238 +                         builtinclass=iface.attributes.builtinclass)
   1.239 +
   1.240 +def write_typelib(idl, fd, filename):
   1.241 +    """ Generate the typelib. """
   1.242 +
   1.243 +    # We only care about interfaces
   1.244 +    ifaces = []
   1.245 +    for p in idl.productions:
   1.246 +        if p.kind == 'interface':
   1.247 +            ifaces.append(build_interface(p, ifaces))
   1.248 +
   1.249 +    typelib = xpt.Typelib(interfaces=ifaces)
   1.250 +    typelib.writefd(fd)
   1.251 +
   1.252 +if __name__ == '__main__':
   1.253 +    from optparse import OptionParser
   1.254 +    o = OptionParser()
   1.255 +    o.add_option('-I', action='append', dest='incdirs', default=['.'],
   1.256 +                 help="Directory to search for imported files")
   1.257 +    o.add_option('--cachedir', dest='cachedir', default=None,
   1.258 +                 help="Directory in which to cache lex/parse tables.")
   1.259 +    o.add_option('-o', dest='outfile', default=None,
   1.260 +                 help="Output file")
   1.261 +    o.add_option('-d', dest='depfile', default=None,
   1.262 +                 help="Generate a make dependency file")
   1.263 +    o.add_option('--regen', action='store_true', dest='regen', default=False,
   1.264 +                 help="Regenerate IDL Parser cache")
   1.265 +    options, args = o.parse_args()
   1.266 +    file = args[0] if args else None
   1.267 +
   1.268 +    if options.cachedir is not None:
   1.269 +        if not os.path.isdir(options.cachedir):
   1.270 +            os.mkdir(options.cachedir)
   1.271 +        sys.path.append(options.cachedir)
   1.272 +
   1.273 +    if options.regen:
   1.274 +        if options.cachedir is None:
   1.275 +            print >>sys.stderr, "--regen requires --cachedir"
   1.276 +            sys.exit(1)
   1.277 +
   1.278 +        p = xpidl.IDLParser(outputdir=options.cachedir, regen=True)
   1.279 +        sys.exit(0)
   1.280 +
   1.281 +    if options.depfile is not None and options.outfile is None:
   1.282 +        print >>sys.stderr, "-d requires -o"
   1.283 +        sys.exit(1)
   1.284 +
   1.285 +    if options.outfile is not None:
   1.286 +        outfd = open(options.outfile, 'wb')
   1.287 +        closeoutfd = True
   1.288 +    else:
   1.289 +        raise "typelib generation requires an output file"
   1.290 +
   1.291 +    p = xpidl.IDLParser(outputdir=options.cachedir)
   1.292 +    idl = p.parse(open(file).read(), filename=file)
   1.293 +    idl.resolve(options.incdirs, p)
   1.294 +    write_typelib(idl, outfd, file)
   1.295 +
   1.296 +    if closeoutfd:
   1.297 +        outfd.close()
   1.298 +
   1.299 +    if options.depfile is not None:
   1.300 +        depfd = open(options.depfile, 'w')
   1.301 +        deps = [dep.replace('\\', '/') for dep in idl.deps]
   1.302 +
   1.303 +        print >>depfd, "%s: %s" % (options.outfile, " ".join(deps))
   1.304 +        for dep in deps:
   1.305 +            print >>depfd, "%s:" % dep

mercurial