summaryrefslogtreecommitdiffstats
path: root/xpcom/idl-parser/xpidl/typelib.py
blob: 911e3873da045d902275ddbf1d791d5d61989de0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
#!/usr/bin/env python
# typelib.py - Generate XPCOM typelib 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/.

"""Generate an XPIDL typelib for the IDL files specified on the command line"""

import os
import sys
import xpidl
import xpt

# A map of xpidl.py types to xpt.py types
TypeMap = {
    # nsresult is not strictly an xpidl.py type, but it's useful here
    'nsresult':           xpt.Type.Tags.uint32,
    # builtins
    'boolean':            xpt.Type.Tags.boolean,
    'void':               xpt.Type.Tags.void,
    'int16_t':            xpt.Type.Tags.int16,
    'int32_t':            xpt.Type.Tags.int32,
    'int64_t':            xpt.Type.Tags.int64,
    'uint8_t':            xpt.Type.Tags.uint8,
    'uint16_t':           xpt.Type.Tags.uint16,
    'uint32_t':           xpt.Type.Tags.uint32,
    'uint64_t':           xpt.Type.Tags.uint64,
    'octet':              xpt.Type.Tags.uint8,
    'short':              xpt.Type.Tags.int16,
    'long':               xpt.Type.Tags.int32,
    'long long':          xpt.Type.Tags.int64,
    'unsigned short':     xpt.Type.Tags.uint16,
    'unsigned long':      xpt.Type.Tags.uint32,
    'unsigned long long': xpt.Type.Tags.uint64,
    'float':              xpt.Type.Tags.float,
    'double':             xpt.Type.Tags.double,
    'char':               xpt.Type.Tags.char,
    'string':             xpt.Type.Tags.char_ptr,
    'wchar':              xpt.Type.Tags.wchar_t,
    'wstring':            xpt.Type.Tags.wchar_t_ptr,
    # special types
    'nsid':               xpt.Type.Tags.nsIID,
    'domstring':          xpt.Type.Tags.DOMString,
    'astring':            xpt.Type.Tags.AString,
    'utf8string':         xpt.Type.Tags.UTF8String,
    'cstring':            xpt.Type.Tags.CString,
    'jsval':              xpt.Type.Tags.jsval
}


# XXXkhuey dipper types should go away (bug 677784)
def isDipperType(type):
    return type == xpt.Type.Tags.DOMString or type == xpt.Type.Tags.AString or type == xpt.Type.Tags.CString or type == xpt.Type.Tags.UTF8String


def build_interface(iface, ifaces):
    def get_type(type, calltype, iid_is=None, size_is=None):
        """ Return the appropriate xpt.Type object for this param """

        while isinstance(type, xpidl.Typedef):
            type = type.realtype

        if isinstance(type, xpidl.Builtin):
            if type.name == 'string' and size_is is not None:
                return xpt.StringWithSizeType(size_is, size_is)
            elif type.name == 'wstring' and size_is is not None:
                return xpt.WideStringWithSizeType(size_is, size_is)
            else:
                tag = TypeMap[type.name]
                isPtr = (tag == xpt.Type.Tags.char_ptr or tag == xpt.Type.Tags.wchar_t_ptr)
                return xpt.SimpleType(tag,
                                      pointer=isPtr,
                                      reference=False)

        if isinstance(type, xpidl.Array):
            # NB: For an Array<T> we pass down the iid_is to get the type of T.
            #     This allows Arrays of InterfaceIs types to work.
            return xpt.ArrayType(get_type(type.type, calltype, iid_is), size_is,
                                 #XXXkhuey length_is duplicates size_is (bug 677788),
                                 size_is)

        if isinstance(type, xpidl.Interface) or isinstance(type, xpidl.Forward):
            xptiface = None
            for i in ifaces:
                if i.name == type.name:
                    xptiface = i

            if not xptiface:
                xptiface = xpt.Interface(name=type.name)
                ifaces.append(xptiface)

            return xpt.InterfaceType(xptiface)

        if isinstance(type, xpidl.Native):
            if type.specialtype:
                # XXXkhuey jsval is marked differently in the typelib and in the headers :-(
                isPtr = (type.isPtr(calltype) or type.isRef(calltype)) and not type.specialtype == 'jsval'
                isRef = type.isRef(calltype) and not type.specialtype == 'jsval'
                return xpt.SimpleType(TypeMap[type.specialtype],
                                      pointer=isPtr,
                                      reference=isRef)
            elif iid_is is not None:
                return xpt.InterfaceIsType(iid_is)
            else:
                # void ptr
                return xpt.SimpleType(TypeMap['void'],
                                      pointer=True,
                                      reference=False)

        raise Exception("Unknown type!")

    def get_nsresult():
        return xpt.SimpleType(TypeMap['nsresult'])

    def build_nsresult_param():
        return xpt.Param(get_nsresult())

    def get_result_type(m):
        if not m.notxpcom:
            return get_nsresult()

        return get_type(m.realtype, '')

    def build_result_param(m):
        return xpt.Param(get_result_type(m))

    def build_retval_param(m):
        type = get_type(m.realtype, 'out')
        if isDipperType(type.tag):
            # NB: The retval bit needs to be set here, contrary to what the
            # xpt spec says.
            return xpt.Param(type, in_=True, retval=True, dipper=True)
        return xpt.Param(type, in_=False, out=True, retval=True)

    def build_attr_param(a, getter=False, setter=False):
        if not (getter or setter):
            raise Exception("Attribute param must be for a getter or a setter!")

        type = get_type(a.realtype, getter and 'out' or 'in')
        if setter:
            return xpt.Param(type)
        else:
            if isDipperType(type.tag):
                # NB: The retval bit needs to be set here, contrary to what the
                # xpt spec says.
                return xpt.Param(type, in_=True, retval=True, dipper=True)
            return xpt.Param(type, in_=False, out=True, retval=True)

    if iface.namemap is None:
        raise Exception("Interface was not resolved.")

    consts = []
    methods = []

    def build_const(c):
        consts.append(xpt.Constant(c.name, get_type(c.basetype, ''), c.getValue()))

    def build_method(m):
        params = []

        def build_param(p):
            def findattr(p, attr):
                if hasattr(p, attr) and getattr(p, attr):
                    for i, param in enumerate(m.params):
                        if param.name == getattr(p, attr):
                            return i
                    return None

            iid_is = findattr(p, 'iid_is')
            size_is = findattr(p, 'size_is')

            in_ = p.paramtype.count("in")
            out = p.paramtype.count("out")
            dipper = False
            type = get_type(p.realtype, p.paramtype, iid_is=iid_is, size_is=size_is)
            if out and isDipperType(type.tag):
                out = False
                dipper = True

            return xpt.Param(type, in_, out, p.retval, p.shared, dipper, p.optional)

        for p in m.params:
            params.append(build_param(p))

        if not m.notxpcom and m.realtype.name != 'void':
            params.append(build_retval_param(m))

        methods.append(xpt.Method(m.name, build_result_param(m), params,
                                  getter=False, setter=False, notxpcom=m.notxpcom,
                                  constructor=False, hidden=m.noscript,
                                  optargc=m.optional_argc,
                                  implicit_jscontext=m.implicit_jscontext))

    def build_attr(a):
        # Write the getter
        methods.append(xpt.Method(a.name, build_nsresult_param(),
                                  [build_attr_param(a, getter=True)],
                                  getter=True, setter=False,
                                  constructor=False, hidden=a.noscript,
                                  optargc=False,
                                  implicit_jscontext=a.implicit_jscontext))

        # And maybe the setter
        if not a.readonly:
            methods.append(xpt.Method(a.name, build_nsresult_param(),
                                      [build_attr_param(a, setter=True)],
                                      getter=False, setter=True,
                                      constructor=False, hidden=a.noscript,
                                      optargc=False,
                                      implicit_jscontext=a.implicit_jscontext))

    for member in iface.members:
        if isinstance(member, xpidl.ConstMember):
            build_const(member)
        elif isinstance(member, xpidl.Attribute):
            build_attr(member)
        elif isinstance(member, xpidl.Method):
            build_method(member)
        elif isinstance(member, xpidl.CDATA):
            pass
        else:
            raise Exception("Unexpected interface member: %s" % member)

    parent = None
    if iface.base:
        for i in ifaces:
            if i.name == iface.base:
                parent = i
        if not parent:
            parent = xpt.Interface(name=iface.base)
            ifaces.append(parent)

    return xpt.Interface(iface.name, iface.attributes.uuid, methods=methods,
                         constants=consts, resolved=True, parent=parent,
                         scriptable=iface.attributes.scriptable,
                         function=iface.attributes.function,
                         builtinclass=iface.attributes.builtinclass,
                         main_process_scriptable_only=iface.attributes.main_process_scriptable_only)


def write_typelib(idl, fd, filename):
    """ Generate the typelib. """

    # We only care about interfaces that are scriptable.
    ifaces = []
    for p in idl.productions:
        if p.kind == 'interface' and p.attributes.scriptable:
            ifaces.append(build_interface(p, ifaces))

    typelib = xpt.Typelib(interfaces=ifaces)
    typelib.writefd(fd)

if __name__ == '__main__':
    from optparse import OptionParser
    o = OptionParser()
    o.add_option('-I', action='append', dest='incdirs', default=['.'],
                 help="Directory to search for imported files")
    o.add_option('--cachedir', dest='cachedir', default=None,
                 help="Directory in which to cache lex/parse tables.")
    o.add_option('-o', dest='outfile', default=None,
                 help="Output file")
    o.add_option('-d', dest='depfile', default=None,
                 help="Generate a make dependency file")
    o.add_option('--regen', action='store_true', dest='regen', default=False,
                 help="Regenerate IDL Parser cache")
    options, args = o.parse_args()
    file = args[0] if args else None

    if options.cachedir is not None:
        if not os.path.isdir(options.cachedir):
            os.mkdir(options.cachedir)
        sys.path.append(options.cachedir)

    if options.regen:
        if options.cachedir is None:
            print >>sys.stderr, "--regen requires --cachedir"
            sys.exit(1)

        p = xpidl.IDLParser(outputdir=options.cachedir, regen=True)
        sys.exit(0)

    if options.depfile is not None and options.outfile is None:
        print >>sys.stderr, "-d requires -o"
        sys.exit(1)

    if options.outfile is not None:
        outfd = open(options.outfile, 'wb')
        closeoutfd = True
    else:
        raise "typelib generation requires an output file"

    p = xpidl.IDLParser(outputdir=options.cachedir)
    idl = p.parse(open(file).read(), filename=file)
    idl.resolve(options.incdirs, p)
    write_typelib(idl, outfd, file)

    if closeoutfd:
        outfd.close()

    if options.depfile is not None:
        depfd = open(options.depfile, 'w')
        deps = [dep.replace('\\', '/') for dep in idl.deps]

        print >>depfd, "%s: %s" % (options.outfile, " ".join(deps))
        for dep in deps:
            print >>depfd, "%s:" % dep