Back to index

enigmail  1.4.3
typelib.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 # typelib.py - Generate XPCOM typelib files from IDL.
00003 #
00004 # ***** BEGIN LICENSE BLOCK *****
00005 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006 #
00007 # The contents of this file are subject to the Mozilla Public License Version
00008 # 1.1 (the "License"); you may not use this file except in compliance with
00009 # the License. You may obtain a copy of the License at
00010 # http://www.mozilla.org/MPL/
00011 #
00012 # Software distributed under the License is distributed on an "AS IS" basis,
00013 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014 # for the specific language governing rights and limitations under the
00015 # License.
00016 #
00017 # The Original Code is pyxpidl.
00018 #
00019 # The Initial Developer of the Original Code is the Mozilla Foundation.
00020 # Portions created by the Initial Developer are Copyright (C) 2011
00021 # the Initial Developer. All Rights Reserved.
00022 #
00023 # Contributor(s):
00024 #   Kyle Huey <khuey@kylehuey.com>
00025 #
00026 # Alternatively, the contents of this file may be used under the terms of
00027 # either of the GNU General Public License Version 2 or later (the "GPL"),
00028 # or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029 # in which case the provisions of the GPL or the LGPL are applicable instead
00030 # of those above. If you wish to allow use of your version of this file only
00031 # under the terms of either the GPL or the LGPL, and not to allow others to
00032 # use your version of this file under the terms of the MPL, indicate your
00033 # decision by deleting the provisions above and replace them with the notice
00034 # and other provisions required by the GPL or the LGPL. If you do not delete
00035 # the provisions above, a recipient may use your version of this file under
00036 # the terms of any one of the MPL, the GPL or the LGPL.
00037 #
00038 # ***** END LICENSE BLOCK *****
00039 
00040 """Generate an XPIDL typelib for the IDL files specified on the command line"""
00041 
00042 import os
00043 import sys
00044 import xpidl, xpt
00045 
00046 # A map of xpidl.py types to xpt.py types
00047 TypeMap = {
00048     # nsresult is not strictly an xpidl.py type, but it's useful here
00049     'nsresult':           xpt.Type.Tags.uint32,
00050     # builtins
00051     'boolean':            xpt.Type.Tags.boolean,
00052     'void':               xpt.Type.Tags.void,
00053     'octet':              xpt.Type.Tags.uint8,
00054     'short':              xpt.Type.Tags.int16,
00055     'long':               xpt.Type.Tags.int32,
00056     'long long':          xpt.Type.Tags.int64,
00057     'unsigned short':     xpt.Type.Tags.uint16,
00058     'unsigned long':      xpt.Type.Tags.uint32,
00059     'unsigned long long': xpt.Type.Tags.uint64,
00060     'float':              xpt.Type.Tags.float,
00061     'double':             xpt.Type.Tags.double,
00062     'char':               xpt.Type.Tags.char,
00063     'string':             xpt.Type.Tags.char_ptr,
00064     'wchar':              xpt.Type.Tags.wchar_t,
00065     'wstring':            xpt.Type.Tags.wchar_t_ptr,
00066     # special types
00067     'nsid':               xpt.Type.Tags.nsIID,
00068     'domstring':          xpt.Type.Tags.DOMString,
00069     'astring':            xpt.Type.Tags.AString,
00070     'utf8string':         xpt.Type.Tags.UTF8String,
00071     'cstring':            xpt.Type.Tags.CString,
00072     'jsval':              xpt.Type.Tags.jsval
00073 }
00074 
00075 # XXXkhuey dipper types should go away (bug 677784)
00076 def isDipperType(type):
00077     return type == xpt.Type.Tags.DOMString or type == xpt.Type.Tags.AString or type == xpt.Type.Tags.CString or type == xpt.Type.Tags.UTF8String
00078 
00079 def build_interface(iface, ifaces):
00080     def get_type(type, calltype, iid_is=None, size_is=None):
00081         """ Return the appropriate xpt.Type object for this param """
00082 
00083         while isinstance(type, xpidl.Typedef):
00084             type = type.realtype
00085 
00086         if isinstance(type, xpidl.Builtin):
00087             if type.name == 'string' and size_is != None:
00088                   return xpt.StringWithSizeType(size_is, size_is)
00089             elif type.name == 'wstring' and size_is != None:
00090                   return xpt.WideStringWithSizeType(size_is, size_is)
00091             else:
00092                   tag = TypeMap[type.name]
00093                   isPtr = (tag == xpt.Type.Tags.char_ptr or tag == xpt.Type.Tags.wchar_t_ptr)
00094                   return xpt.SimpleType(tag,
00095                                         pointer=isPtr,
00096                                         reference=False)
00097 
00098         if isinstance(type, xpidl.Array):
00099             # NB: For an Array<T> we pass down the iid_is to get the type of T.
00100             #     This allows Arrays of InterfaceIs types to work.
00101             return xpt.ArrayType(get_type(type.type, calltype, iid_is), size_is,
00102                                  #XXXkhuey length_is duplicates size_is (bug 677788),
00103                                  size_is)
00104 
00105         if isinstance(type, xpidl.Interface) or isinstance(type, xpidl.Forward):
00106             xptiface = None
00107             for i in ifaces:
00108                 if i.name == type.name:
00109                     xptiface = i
00110 
00111             if not xptiface:
00112                 xptiface = xpt.Interface(name=type.name)
00113                 ifaces.append(xptiface)
00114 
00115             return xpt.InterfaceType(xptiface)
00116 
00117         if isinstance(type, xpidl.Native):
00118             if type.specialtype:
00119                 # XXXkhuey jsval is marked differently in the typelib and in the headers :-(
00120                 isPtr = (type.isPtr(calltype) or type.isRef(calltype)) and not type.specialtype == 'jsval'
00121                 isRef = type.isRef(calltype) and not type.specialtype == 'jsval'
00122                 return xpt.SimpleType(TypeMap[type.specialtype],
00123                                       pointer=isPtr,
00124                                       reference=isRef)
00125             elif iid_is != None:
00126                 return xpt.InterfaceIsType(iid_is)
00127             else:
00128                 # void ptr
00129                 return xpt.SimpleType(TypeMap['void'],
00130                                       pointer=True,
00131                                       reference=False)
00132 
00133         raise Exception("Unknown type!")
00134 
00135     def get_nsresult():
00136         return xpt.SimpleType(TypeMap['nsresult'])
00137 
00138     def build_nsresult_param():
00139         return xpt.Param(get_nsresult())
00140 
00141     def get_result_type(m):
00142         if not m.notxpcom:
00143             return get_nsresult()
00144 
00145         return get_type(m.realtype, '')
00146 
00147     def build_result_param(m):
00148         return xpt.Param(get_result_type(m))
00149 
00150     def build_retval_param(m):
00151         type = get_type(m.realtype, 'out')
00152         if isDipperType(type.tag):
00153             # NB: The retval bit needs to be set here, contrary to what the
00154             # xpt spec says.
00155             return xpt.Param(type, in_=True, retval=True, dipper=True)
00156         return xpt.Param(type, in_=False, out=True, retval=True)
00157 
00158     def build_attr_param(a, getter=False, setter=False):
00159         if not (getter or setter):
00160             raise Exception("Attribute param must be for a getter or a setter!")
00161 
00162         type = get_type(a.realtype, getter and 'out' or 'in')
00163         if setter:
00164             return xpt.Param(type)
00165         else:
00166             if isDipperType(type.tag):
00167                 # NB: The retval bit needs to be set here, contrary to what the
00168                 # xpt spec says.
00169                 return xpt.Param(type, in_=True, retval=True, dipper=True)
00170             return xpt.Param(type, in_=False, out=True, retval=True)
00171 
00172     if iface.namemap is None:
00173         raise Exception("Interface was not resolved.")
00174 
00175     consts = []
00176     methods = []
00177 
00178     def build_const(c):
00179         consts.append(xpt.Constant(c.name, get_type(c.basetype, ''), c.getValue()))
00180 
00181     def build_method(m):
00182         params = []
00183 
00184         def build_param(p):
00185             def findattr(p, attr):
00186                 if hasattr(p, attr) and getattr(p, attr):
00187                     for i, param in enumerate(m.params):
00188                         if param.name == getattr(p, attr):
00189                             return i
00190                     return None
00191 
00192             iid_is = findattr(p, 'iid_is')
00193             size_is = findattr(p, 'size_is')
00194 
00195             in_ = p.paramtype.count("in")
00196             out = p.paramtype.count("out")
00197             dipper = False
00198             type = get_type(p.realtype, p.paramtype, iid_is=iid_is, size_is=size_is)
00199             if out and isDipperType(type.tag):
00200                 out = False
00201                 dipper = True
00202 
00203             return xpt.Param(type, in_, out, p.retval, p.shared, dipper, p.optional)
00204 
00205         for p in m.params:
00206             params.append(build_param(p))
00207 
00208         if not m.notxpcom and m.realtype.name != 'void':
00209             params.append(build_retval_param(m))
00210 
00211         methods.append(xpt.Method(m.name, build_result_param(m), params,
00212                                   getter=False, setter=False, notxpcom=m.notxpcom,
00213                                   constructor=False, hidden=m.noscript,
00214                                   optargc=m.optional_argc,
00215                                   implicit_jscontext=m.implicit_jscontext))
00216 
00217     def build_attr(a):
00218         # Write the getter
00219         methods.append(xpt.Method(a.name, build_nsresult_param(),
00220                                   [build_attr_param(a, getter=True)],
00221                                   getter=True, setter=False, notxpcom=a.notxpcom,
00222                                   constructor=False, hidden=a.noscript,
00223                                   optargc=False,
00224                                   implicit_jscontext=a.implicit_jscontext))
00225 
00226         # And maybe the setter
00227         if not a.readonly:
00228             methods.append(xpt.Method(a.name, build_nsresult_param(),
00229                                       [build_attr_param(a, setter=True)],
00230                                       getter=False, setter=True, notxpcom=a.notxpcom,
00231                                       constructor=False, hidden=a.noscript,
00232                                       optargc=False,
00233                                       implicit_jscontext=a.implicit_jscontext))
00234 
00235     for member in iface.members:
00236         if isinstance(member, xpidl.ConstMember):
00237             build_const(member)
00238         elif isinstance(member, xpidl.Attribute):
00239             build_attr(member)
00240         elif isinstance(member, xpidl.Method):
00241             build_method(member)
00242         elif isinstance(member, xpidl.CDATA):
00243             pass
00244         else:
00245             raise Exception("Unexpected interface member: %s" % member)
00246 
00247     parent = None
00248     if iface.base:
00249         for i in ifaces:
00250             if i.name == iface.base:
00251                 parent = i
00252         if not parent:
00253             parent = xpt.Interface(name=iface.base)
00254             ifaces.append(parent)
00255 
00256     return xpt.Interface(iface.name, iface.attributes.uuid, methods=methods,
00257                          constants=consts, resolved=True, parent=parent,
00258                          scriptable=iface.attributes.scriptable,
00259                          function=iface.attributes.function,
00260                          builtinclass=iface.attributes.builtinclass)
00261 
00262 def write_typelib(idl, fd, filename):
00263     """ Generate the typelib. """
00264 
00265     # We only care about interfaces
00266     ifaces = []
00267     for p in idl.productions:
00268         if p.kind == 'interface':
00269             ifaces.append(build_interface(p, ifaces))
00270 
00271     typelib = xpt.Typelib(interfaces=ifaces)
00272     typelib.writefd(fd)
00273 
00274 if __name__ == '__main__':
00275     from optparse import OptionParser
00276     o = OptionParser()
00277     o.add_option('-I', action='append', dest='incdirs', default=['.'],
00278                  help="Directory to search for imported files")
00279     o.add_option('--cachedir', dest='cachedir', default=None,
00280                  help="Directory in which to cache lex/parse tables.")
00281     o.add_option('-o', dest='outfile', default=None,
00282                  help="Output file")
00283     o.add_option('-d', dest='depfile', default=None,
00284                  help="Generate a make dependency file")
00285     o.add_option('--regen', action='store_true', dest='regen', default=False,
00286                  help="Regenerate IDL Parser cache")
00287     options, args = o.parse_args()
00288     file = args[0] if args else None
00289 
00290     if options.cachedir is not None:
00291         if not os.path.isdir(options.cachedir):
00292             os.mkdir(options.cachedir)
00293         sys.path.append(options.cachedir)
00294 
00295     if options.regen:
00296         if options.cachedir is None:
00297             print >>sys.stderr, "--regen requires --cachedir"
00298             sys.exit(1)
00299 
00300         p = xpidl.IDLParser(outputdir=options.cachedir, regen=True)
00301         sys.exit(0)
00302 
00303     if options.depfile is not None and options.outfile is None:
00304         print >>sys.stderr, "-d requires -o"
00305         sys.exit(1)
00306 
00307     if options.outfile is not None:
00308         outfd = open(options.outfile, 'wb')
00309         closeoutfd = True
00310     else:
00311         raise "typelib generation requires an output file"
00312 
00313     p = xpidl.IDLParser(outputdir=options.cachedir)
00314     idl = p.parse(open(file).read(), filename=file)
00315     idl.resolve(options.incdirs, p)
00316     write_typelib(idl, outfd, file)
00317 
00318     if closeoutfd:
00319         outfd.close()
00320 
00321     if options.depfile is not None:
00322         depfd = open(options.depfile, 'w')
00323         deps = [dep.replace('\\', '/') for dep in idl.deps]
00324 
00325         print >>depfd, "%s: %s" % (options.outfile, " ".join(deps))