~ubuntu-branches/ubuntu/quantal/enigmail/quantal-security

« back to all changes in this revision

Viewing changes to xpcom/typelib/xpt/tools/xpt.py

  • Committer: Package Import Robot
  • Author(s): Chris Coulson
  • Date: 2013-09-13 16:02:15 UTC
  • mfrom: (0.12.16)
  • Revision ID: package-import@ubuntu.com-20130913160215-u3g8nmwa0pdwagwc
Tags: 2:1.5.2-0ubuntu0.12.10.1
* New upstream release v1.5.2 for Thunderbird 24

* Build enigmail using a stripped down Thunderbird 17 build system, as it's
  now quite difficult to build the way we were doing previously, with the
  latest Firefox build system
* Add debian/patches/no_libxpcom.patch - Don't link against libxpcom, as it
  doesn't exist anymore (but exists in the build system)
* Add debian/patches/use_sdk.patch - Use the SDK version of xpt.py and
  friends
* Drop debian/patches/ipc-pipe_rename.diff (not needed anymore)
* Drop debian/patches/makefile_depth.diff (not needed anymore)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/env python
2
 
# Copyright 2010,2011 Mozilla Foundation. All rights reserved.
3
 
#
4
 
# Redistribution and use in source and binary forms, with or without
5
 
# modification, are permitted provided that the following conditions are
6
 
# met:
7
 
#
8
 
#   1. Redistributions of source code must retain the above copyright
9
 
#      notice, this list of conditions and the following disclaimer.
10
 
#
11
 
#   2. Redistributions in binary form must reproduce the above copyright
12
 
#      notice, this list of conditions and the following disclaimer in
13
 
#      the documentation and/or other materials provided with the
14
 
#      distribution.
15
 
#
16
 
# THIS SOFTWARE IS PROVIDED BY THE MOZILLA FOUNDATION ``AS IS'' AND ANY
17
 
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
 
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
 
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE MOZILLA FOUNDATION OR
20
 
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21
 
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22
 
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23
 
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24
 
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
 
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
 
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
 
#
28
 
# The views and conclusions contained in the software and documentation
29
 
# are those of the authors and should not be interpreted as representing
30
 
# official policies, either expressed or implied, of the Mozilla
31
 
# Foundation.
32
 
 
33
 
"""
34
 
A module for working with XPCOM Type Libraries.
35
 
 
36
 
The XPCOM Type Library File Format is described at:
37
 
http://www.mozilla.org/scriptable/typelib_file.html . It is used
38
 
to provide type information for calling methods on XPCOM objects
39
 
from scripting languages such as JavaScript.
40
 
 
41
 
This module provides a set of classes representing the parts of
42
 
a typelib in a high-level manner, as well as methods for reading
43
 
and writing them from files.
44
 
 
45
 
The usable public interfaces are currently:
46
 
Typelib.read(input_file) - read a typelib from a file on disk or file-like
47
 
                           object, return a Typelib object.
48
 
 
49
 
xpt_dump(filename)     - read a typelib from a file on disk, dump
50
 
                         the contents to stdout in a human-readable
51
 
                         format.
52
 
 
53
 
Typelib()              - construct a new Typelib object
54
 
Interface()            - construct a new Interface object
55
 
Method()               - construct a new object representing a method
56
 
                         defined on an Interface
57
 
Constant()             - construct a new object representing a constant
58
 
                         defined on an Interface
59
 
Param()                - construct a new object representing a parameter
60
 
                         to a method
61
 
SimpleType()           - construct a new object representing a simple
62
 
                         data type
63
 
InterfaceType()        - construct a new object representing a type that
64
 
                         is an IDL-defined interface
65
 
 
66
 
"""
67
 
 
68
 
from __future__ import with_statement
69
 
import os, sys
70
 
import struct
71
 
import operator
72
 
 
73
 
# header magic
74
 
XPT_MAGIC = "XPCOM\nTypeLib\r\n\x1a"
75
 
TYPELIB_VERSION = (1, 2)
76
 
 
77
 
class FileFormatError(Exception):
78
 
    pass
79
 
 
80
 
class DataError(Exception):
81
 
    pass
82
 
 
83
 
# Magic for creating enums
84
 
def M_add_class_attribs(attribs):
85
 
    def foo(name, bases, dict_):
86
 
        for v, k in attribs:
87
 
            dict_[k] = v
88
 
        return type(name, bases, dict_)
89
 
    return foo
90
 
 
91
 
def enum(*names):
92
 
    class Foo(object):
93
 
        __metaclass__ = M_add_class_attribs(enumerate(names))
94
 
        def __setattr__(self, name, value):  # this makes it read-only
95
 
            raise NotImplementedError
96
 
    return Foo()
97
 
 
98
 
# Descriptor types as described in the spec
99
 
class Type(object):
100
 
    """
101
 
    Data type of a method parameter or return value. Do not instantiate
102
 
    this class directly. Rather, use one of its subclasses.
103
 
    
104
 
    """
105
 
    _prefixdescriptor = struct.Struct(">B")
106
 
    Tags = enum(
107
 
        # The first 18 entries are SimpleTypeDescriptor
108
 
        'int8',
109
 
        'int16',
110
 
        'int32',
111
 
        'int64',
112
 
        'uint8',
113
 
        'uint16',
114
 
        'uint32',
115
 
        'uint64',
116
 
        'float',
117
 
        'double',
118
 
        'boolean',
119
 
        'char',
120
 
        'wchar_t',
121
 
        'void',
122
 
        # the following four values are only valid as pointers
123
 
        'nsIID',
124
 
        'DOMString',
125
 
        'char_ptr',
126
 
        'wchar_t_ptr',
127
 
        # InterfaceTypeDescriptor
128
 
        'Interface',
129
 
        # InterfaceIsTypeDescriptor
130
 
        'InterfaceIs',
131
 
        # ArrayTypeDescriptor
132
 
        'Array',
133
 
        # StringWithSizeTypeDescriptor
134
 
        'StringWithSize',
135
 
        # WideStringWithSizeTypeDescriptor
136
 
        'WideStringWithSize',
137
 
        #XXX: These are also SimpleTypes (but not in the spec)
138
 
        # http://hg.mozilla.org/mozilla-central/annotate/0e0e2516f04e/xpcom/typelib/xpt/tools/xpt_dump.c#l69
139
 
        'UTF8String',
140
 
        'CString',
141
 
        'AString',
142
 
        'jsval',
143
 
        )
144
 
 
145
 
    def __init__(self, pointer=False, reference=False):
146
 
        self.pointer = pointer
147
 
        self.reference = reference
148
 
        if reference and not pointer:
149
 
            raise Exception("If reference is True pointer must be True too")
150
 
 
151
 
    @staticmethod
152
 
    def decodeflags(byte):
153
 
        """
154
 
        Given |byte|, an unsigned uint8 containing flag bits,
155
 
        decode the flag bits as described in
156
 
        http://www.mozilla.org/scriptable/typelib_file.html#TypeDescriptor
157
 
        and return a dict of flagname: (True|False) suitable
158
 
        for passing to Type.__init__ as **kwargs.
159
 
        
160
 
        """
161
 
        return {'pointer': bool(byte & 0x80),
162
 
                'reference': bool(byte & 0x20),
163
 
                }
164
 
 
165
 
    def encodeflags(self):
166
 
        """
167
 
        Encode the flag bits of this Type object. Returns a byte.
168
 
 
169
 
        """
170
 
        flags = 0
171
 
        if self.pointer:
172
 
            flags |= 0x80
173
 
        if self.reference:
174
 
            flags |= 0x20
175
 
        return flags
176
 
 
177
 
    @staticmethod
178
 
    def read(typelib, map, data_pool, offset):
179
 
        """
180
 
        Read a TypeDescriptor at |offset| from the mmaped file |map| with
181
 
        data pool offset |data_pool|. Returns (Type, next offset),
182
 
        where |next offset| is an offset suitable for reading the data
183
 
        following this TypeDescriptor.
184
 
        
185
 
        """
186
 
        start = data_pool + offset - 1
187
 
        (data,) = Type._prefixdescriptor.unpack(map[start:start + Type._prefixdescriptor.size])
188
 
        # first three bits are the flags
189
 
        flags = data & 0xE0
190
 
        flags = Type.decodeflags(flags)
191
 
        # last five bits is the tag
192
 
        tag = data & 0x1F
193
 
        offset += Type._prefixdescriptor.size
194
 
        t = None
195
 
        if tag <= Type.Tags.wchar_t_ptr or tag >= Type.Tags.UTF8String:
196
 
            t = SimpleType.get(data, tag, flags)
197
 
        elif tag == Type.Tags.Interface:
198
 
            t, offset = InterfaceType.read(typelib, map, data_pool, offset, flags)
199
 
        elif tag == Type.Tags.InterfaceIs:
200
 
            t, offset = InterfaceIsType.read(typelib, map, data_pool, offset, flags)
201
 
        elif tag == Type.Tags.Array:
202
 
            t, offset = ArrayType.read(typelib, map, data_pool, offset, flags)
203
 
        elif tag == Type.Tags.StringWithSize:
204
 
            t, offset = StringWithSizeType.read(typelib, map, data_pool, offset, flags)
205
 
        elif tag == Type.Tags.WideStringWithSize:
206
 
            t, offset = WideStringWithSizeType.read(typelib, map, data_pool, offset, flags)
207
 
        return t, offset
208
 
 
209
 
    def write(self, typelib, file):
210
 
        """
211
 
        Write a TypeDescriptor to |file|, which is assumed
212
 
        to be seeked to the proper position. For types other than
213
 
        SimpleType, this is not sufficient for writing the TypeDescriptor,
214
 
        and the subclass method must be called.
215
 
 
216
 
        """
217
 
        file.write(Type._prefixdescriptor.pack(self.encodeflags() | self.tag))
218
 
 
219
 
class SimpleType(Type):
220
 
    """
221
 
    A simple data type. (SimpleTypeDescriptor from the typelib specification.)
222
 
 
223
 
    """
224
 
    _cache = {}
225
 
 
226
 
    def __init__(self, tag, **kwargs):
227
 
        Type.__init__(self, **kwargs)
228
 
        self.tag = tag
229
 
 
230
 
    @staticmethod
231
 
    def get(data, tag, flags):
232
 
        """
233
 
        Get a SimpleType object representing |data| (a TypeDescriptorPrefix).
234
 
        May return an already-created object. If no cached object is found,
235
 
        construct one with |tag| and |flags|.
236
 
        
237
 
        """
238
 
        if data not in SimpleType._cache:
239
 
            SimpleType._cache[data] = SimpleType(tag, **flags)
240
 
        return SimpleType._cache[data]
241
 
 
242
 
    def __str__(self):
243
 
        s = "unknown"
244
 
        if self.tag == Type.Tags.char_ptr and self.pointer:
245
 
            return "string"
246
 
        if self.tag == Type.Tags.wchar_t_ptr and self.pointer:
247
 
            return "wstring"
248
 
        for t in dir(Type.Tags):
249
 
            if self.tag == getattr(Type.Tags, t):
250
 
                s = t
251
 
                break
252
 
 
253
 
        if self.pointer:
254
 
            if self.reference:
255
 
                s += " &"
256
 
            else:
257
 
                s += " *"
258
 
        return s
259
 
 
260
 
class InterfaceType(Type):
261
 
    """
262
 
    A type representing a pointer to an IDL-defined interface.
263
 
    (InterfaceTypeDescriptor from the typelib specification.)
264
 
 
265
 
    """
266
 
    _descriptor = struct.Struct(">H")
267
 
 
268
 
    def __init__(self, iface, pointer=True, **kwargs):
269
 
        if not pointer:
270
 
            raise DataError, "InterfaceType is not valid with pointer=False"
271
 
        Type.__init__(self, pointer=pointer, **kwargs)
272
 
        self.iface = iface
273
 
        self.tag = Type.Tags.Interface
274
 
 
275
 
    @staticmethod
276
 
    def read(typelib, map, data_pool, offset, flags):
277
 
        """
278
 
        Read an InterfaceTypeDescriptor at |offset| from the mmaped
279
 
        file |map| with data pool offset |data_pool|.
280
 
        Returns (InterfaceType, next offset),
281
 
        where |next offset| is an offset suitable for reading the data
282
 
        following this InterfaceTypeDescriptor.
283
 
        
284
 
        """
285
 
        if not flags['pointer']:
286
 
            return None, offset
287
 
        start = data_pool + offset - 1
288
 
        (iface_index,) = InterfaceType._descriptor.unpack(map[start:start + InterfaceType._descriptor.size])
289
 
        offset += InterfaceType._descriptor.size
290
 
        iface = None
291
 
        # interface indices are 1-based
292
 
        if iface_index > 0 and iface_index <= len(typelib.interfaces):
293
 
            iface = typelib.interfaces[iface_index - 1]
294
 
        return InterfaceType(iface, **flags), offset
295
 
 
296
 
    def write(self, typelib, file):
297
 
        """
298
 
        Write an InterfaceTypeDescriptor to |file|, which is assumed
299
 
        to be seeked to the proper position.
300
 
 
301
 
        """
302
 
        Type.write(self, typelib, file)
303
 
        # write out the interface index (1-based)
304
 
        file.write(InterfaceType._descriptor.pack(typelib.interfaces.index(self.iface) + 1))
305
 
 
306
 
    def __str__(self):
307
 
        if self.iface:
308
 
            return self.iface.name
309
 
        return "unknown interface"
310
 
 
311
 
class InterfaceIsType(Type):
312
 
    """
313
 
    A type representing an interface described by one of the other
314
 
    arguments to the method. (InterfaceIsTypeDescriptor from the
315
 
    typelib specification.)
316
 
    
317
 
    """
318
 
    _descriptor = struct.Struct(">B")
319
 
    _cache = {}
320
 
 
321
 
    def __init__(self, param_index, pointer=True, **kwargs):
322
 
        if not pointer:
323
 
            raise DataError, "InterfaceIsType is not valid with pointer=False"
324
 
        Type.__init__(self, pointer=pointer, **kwargs)
325
 
        self.param_index = param_index
326
 
        self.tag = Type.Tags.InterfaceIs
327
 
 
328
 
    @staticmethod
329
 
    def read(typelib, map, data_pool, offset, flags):
330
 
        """
331
 
        Read an InterfaceIsTypeDescriptor at |offset| from the mmaped
332
 
        file |map| with data pool offset |data_pool|.
333
 
        Returns (InterfaceIsType, next offset),
334
 
        where |next offset| is an offset suitable for reading the data
335
 
        following this InterfaceIsTypeDescriptor.
336
 
        May return a cached value.
337
 
        
338
 
        """
339
 
        if not flags['pointer']:
340
 
            return None, offset
341
 
        start = data_pool + offset - 1
342
 
        (param_index,) = InterfaceIsType._descriptor.unpack(map[start:start + InterfaceIsType._descriptor.size])
343
 
        offset += InterfaceIsType._descriptor.size
344
 
        if param_index not in InterfaceIsType._cache:
345
 
            InterfaceIsType._cache[param_index] = InterfaceIsType(param_index, **flags)
346
 
        return InterfaceIsType._cache[param_index], offset
347
 
 
348
 
    def write(self, typelib, file):
349
 
        """
350
 
        Write an InterfaceIsTypeDescriptor to |file|, which is assumed
351
 
        to be seeked to the proper position.
352
 
 
353
 
        """
354
 
        Type.write(self, typelib, file)
355
 
        file.write(InterfaceIsType._descriptor.pack(self.param_index))
356
 
 
357
 
    def __str__(self):
358
 
        return "InterfaceIs *"
359
 
 
360
 
class ArrayType(Type):
361
 
    """
362
 
    A type representing an Array of elements of another type, whose
363
 
    size and length are passed as separate parameters to a method.
364
 
    (ArrayTypeDescriptor from the typelib specification.)
365
 
    
366
 
    """
367
 
    _descriptor = struct.Struct(">BB")
368
 
 
369
 
    def __init__(self, element_type, size_is_arg_num, length_is_arg_num,
370
 
                 pointer=True, **kwargs):
371
 
        if not pointer:
372
 
            raise DataError, "ArrayType is not valid with pointer=False"
373
 
        Type.__init__(self, pointer=pointer, **kwargs)
374
 
        self.element_type = element_type
375
 
        self.size_is_arg_num = size_is_arg_num
376
 
        self.length_is_arg_num = length_is_arg_num
377
 
        self.tag = Type.Tags.Array
378
 
 
379
 
    @staticmethod
380
 
    def read(typelib, map, data_pool, offset, flags):
381
 
        """
382
 
        Read an ArrayTypeDescriptor at |offset| from the mmaped
383
 
        file |map| with data pool offset |data_pool|.
384
 
        Returns (ArrayType, next offset),
385
 
        where |next offset| is an offset suitable for reading the data
386
 
        following this ArrayTypeDescriptor.
387
 
        """
388
 
        if not flags['pointer']:
389
 
            return None, offset
390
 
        start = data_pool + offset - 1
391
 
        (size_is_arg_num, length_is_arg_num) = ArrayType._descriptor.unpack(map[start:start + ArrayType._descriptor.size])
392
 
        offset += ArrayType._descriptor.size
393
 
        t, offset = Type.read(typelib, map, data_pool, offset)
394
 
        return ArrayType(t, size_is_arg_num, length_is_arg_num, **flags), offset
395
 
 
396
 
    def write(self, typelib, file):
397
 
        """
398
 
        Write an ArrayTypeDescriptor to |file|, which is assumed
399
 
        to be seeked to the proper position.
400
 
 
401
 
        """
402
 
        Type.write(self, typelib, file)
403
 
        file.write(ArrayType._descriptor.pack(self.size_is_arg_num,
404
 
                                              self.length_is_arg_num))
405
 
        self.element_type.write(typelib, file)
406
 
 
407
 
    def __str__(self):
408
 
        return "%s []" % str(self.element_type)
409
 
 
410
 
class StringWithSizeType(Type):
411
 
    """
412
 
    A type representing a UTF-8 encoded string whose size and length
413
 
    are passed as separate arguments to a method. (StringWithSizeTypeDescriptor
414
 
    from the typelib specification.)
415
 
 
416
 
    """
417
 
    _descriptor = struct.Struct(">BB")
418
 
 
419
 
    def __init__(self, size_is_arg_num, length_is_arg_num,
420
 
                 pointer=True, **kwargs):
421
 
        if not pointer:
422
 
            raise DataError, "StringWithSizeType is not valid with pointer=False"
423
 
        Type.__init__(self, pointer=pointer, **kwargs)
424
 
        self.size_is_arg_num = size_is_arg_num
425
 
        self.length_is_arg_num = length_is_arg_num
426
 
        self.tag = Type.Tags.StringWithSize
427
 
 
428
 
    @staticmethod
429
 
    def read(typelib, map, data_pool, offset, flags):
430
 
        """
431
 
        Read an StringWithSizeTypeDescriptor at |offset| from the mmaped
432
 
        file |map| with data pool offset |data_pool|.
433
 
        Returns (StringWithSizeType, next offset),
434
 
        where |next offset| is an offset suitable for reading the data
435
 
        following this StringWithSizeTypeDescriptor.
436
 
        """
437
 
        if not flags['pointer']:
438
 
            return None, offset
439
 
        start = data_pool + offset - 1
440
 
        (size_is_arg_num, length_is_arg_num) = StringWithSizeType._descriptor.unpack(map[start:start + StringWithSizeType._descriptor.size])
441
 
        offset += StringWithSizeType._descriptor.size
442
 
        return StringWithSizeType(size_is_arg_num, length_is_arg_num, **flags), offset
443
 
 
444
 
    def write(self, typelib, file):
445
 
        """
446
 
        Write a StringWithSizeTypeDescriptor to |file|, which is assumed
447
 
        to be seeked to the proper position.
448
 
 
449
 
        """
450
 
        Type.write(self, typelib, file)
451
 
        file.write(StringWithSizeType._descriptor.pack(self.size_is_arg_num,
452
 
                                                       self.length_is_arg_num))
453
 
 
454
 
    def __str__(self):
455
 
        return "string_s"
456
 
 
457
 
class WideStringWithSizeType(Type):
458
 
    """
459
 
    A type representing a UTF-16 encoded string whose size and length
460
 
    are passed as separate arguments to a method.
461
 
    (WideStringWithSizeTypeDescriptor from the typelib specification.)
462
 
 
463
 
    """
464
 
    _descriptor = struct.Struct(">BB")
465
 
 
466
 
    def __init__(self, size_is_arg_num, length_is_arg_num,
467
 
                 pointer=True, **kwargs):
468
 
        if not pointer:
469
 
            raise DataError, "WideStringWithSizeType is not valid with pointer=False"
470
 
        Type.__init__(self, pointer=pointer, **kwargs)
471
 
        self.size_is_arg_num = size_is_arg_num
472
 
        self.length_is_arg_num = length_is_arg_num
473
 
        self.tag = Type.Tags.WideStringWithSize
474
 
 
475
 
    @staticmethod
476
 
    def read(typelib, map, data_pool, offset, flags):
477
 
        """
478
 
        Read an WideStringWithSizeTypeDescriptor at |offset| from the mmaped
479
 
        file |map| with data pool offset |data_pool|.
480
 
        Returns (WideStringWithSizeType, next offset),
481
 
        where |next offset| is an offset suitable for reading the data
482
 
        following this WideStringWithSizeTypeDescriptor.
483
 
        """
484
 
        if not flags['pointer']:
485
 
            return None, offset
486
 
        start = data_pool + offset - 1
487
 
        (size_is_arg_num, length_is_arg_num) = WideStringWithSizeType._descriptor.unpack(map[start:start + WideStringWithSizeType._descriptor.size])
488
 
        offset += WideStringWithSizeType._descriptor.size
489
 
        return WideStringWithSizeType(size_is_arg_num, length_is_arg_num, **flags), offset
490
 
 
491
 
    def write(self, typelib, file):
492
 
        """
493
 
        Write a WideStringWithSizeTypeDescriptor to |file|, which is assumed
494
 
        to be seeked to the proper position.
495
 
 
496
 
        """
497
 
        Type.write(self, typelib, file)
498
 
        file.write(WideStringWithSizeType._descriptor.pack(self.size_is_arg_num,
499
 
                                                           self.length_is_arg_num))
500
 
 
501
 
    def __str__(self):
502
 
        return "wstring_s"
503
 
 
504
 
class Param(object):
505
 
    """
506
 
    A parameter to a method, or the return value of a method.
507
 
    (ParamDescriptor from the typelib specification.)
508
 
 
509
 
    """
510
 
    _descriptorstart = struct.Struct(">B")
511
 
 
512
 
    def __init__(self, type, in_=True, out=False, retval=False,
513
 
                 shared=False, dipper=False, optional=False):
514
 
        """
515
 
        Construct a Param object with the specified |type| and
516
 
        flags. Params default to "in".
517
 
 
518
 
        """
519
 
 
520
 
        self.type = type
521
 
        self.in_ = in_
522
 
        self.out = out
523
 
        self.retval = retval
524
 
        self.shared = shared
525
 
        self.dipper = dipper
526
 
        self.optional = optional
527
 
 
528
 
    @staticmethod
529
 
    def decodeflags(byte):
530
 
        """
531
 
        Given |byte|, an unsigned uint8 containing flag bits,
532
 
        decode the flag bits as described in
533
 
        http://www.mozilla.org/scriptable/typelib_file.html#ParamDescriptor
534
 
        and return a dict of flagname: (True|False) suitable
535
 
        for passing to Param.__init__ as **kwargs
536
 
        """
537
 
        return {'in_': bool(byte & 0x80),
538
 
                'out': bool(byte & 0x40),
539
 
                'retval': bool(byte & 0x20),
540
 
                'shared': bool(byte & 0x10),
541
 
                'dipper': bool(byte & 0x08),
542
 
                #XXX: Not in the spec, see:
543
 
                # http://hg.mozilla.org/mozilla-central/annotate/0e0e2516f04e/xpcom/typelib/xpt/public/xpt_struct.h#l456
544
 
                'optional': bool(byte & 0x04),
545
 
                }
546
 
 
547
 
    def encodeflags(self):
548
 
        """
549
 
        Encode the flags of this Param. Return a byte suitable for
550
 
        writing to a typelib file.
551
 
 
552
 
        """
553
 
        flags = 0
554
 
        if self.in_:
555
 
            flags |= 0x80
556
 
        if self.out:
557
 
            flags |= 0x40
558
 
        if self.retval:
559
 
            flags |= 0x20
560
 
        if self.shared:
561
 
            flags |= 0x10
562
 
        if self.dipper:
563
 
            flags |= 0x08
564
 
        if self.optional:
565
 
            flags |= 0x04
566
 
        return flags
567
 
 
568
 
    @staticmethod
569
 
    def read(typelib, map, data_pool, offset):
570
 
        """
571
 
        Read a ParamDescriptor at |offset| from the mmaped file |map| with
572
 
        data pool offset |data_pool|. Returns (Param, next offset),
573
 
        where |next offset| is an offset suitable for reading the data
574
 
        following this ParamDescriptor.
575
 
        """
576
 
        start = data_pool + offset - 1
577
 
        (flags,) = Param._descriptorstart.unpack(map[start:start + Param._descriptorstart.size])
578
 
        # only the first five bits are flags
579
 
        flags &= 0xFC
580
 
        flags = Param.decodeflags(flags)
581
 
        offset += Param._descriptorstart.size
582
 
        t, offset = Type.read(typelib, map, data_pool, offset)
583
 
        p = Param(t, **flags)
584
 
        return p, offset
585
 
 
586
 
    def write(self, typelib, file):
587
 
        """
588
 
        Write a ParamDescriptor to |file|, which is assumed to be seeked
589
 
        to the correct position.
590
 
 
591
 
        """
592
 
        file.write(Param._descriptorstart.pack(self.encodeflags()))
593
 
        self.type.write(typelib, file)
594
 
 
595
 
    def prefix(self):
596
 
        """
597
 
        Return a human-readable string representing the flags set
598
 
        on this Param.
599
 
 
600
 
        """
601
 
        s = ""
602
 
        if self.out:
603
 
            if self.in_:
604
 
                s = "inout "
605
 
            else:
606
 
                s = "out "
607
 
        else:
608
 
            s = "in "
609
 
        if self.dipper:
610
 
            s += "dipper "
611
 
        if self.retval:
612
 
            s += "retval "
613
 
        if self.shared:
614
 
            s += "shared "
615
 
        if self.optional:
616
 
            s += "optional "
617
 
        return s
618
 
 
619
 
    def __str__(self):
620
 
        return self.prefix() + str(self.type)
621
 
 
622
 
class Method(object):
623
 
    """
624
 
    A method of an interface, defining its associated parameters
625
 
    and return value.
626
 
    (MethodDescriptor from the typelib specification.)
627
 
    
628
 
    """
629
 
    _descriptorstart = struct.Struct(">BIB")
630
 
 
631
 
    def __init__(self, name, result,
632
 
                 params=[], getter=False, setter=False, notxpcom=False,
633
 
                 constructor=False, hidden=False, optargc=False,
634
 
                 implicit_jscontext=False):
635
 
        self.name = name
636
 
        self._name_offset = 0
637
 
        self.getter = getter
638
 
        self.setter = setter
639
 
        self.notxpcom = notxpcom
640
 
        self.constructor = constructor
641
 
        self.hidden = hidden
642
 
        self.optargc = optargc
643
 
        self.implicit_jscontext = implicit_jscontext
644
 
        self.params = list(params)
645
 
        if result and not isinstance(result, Param):
646
 
            raise Exception("result must be a Param!")
647
 
        self.result = result
648
 
 
649
 
    def read_params(self, typelib, map, data_pool, offset, num_args):
650
 
        """
651
 
        Read |num_args| ParamDescriptors representing this Method's arguments
652
 
        from the mmaped file |map| with data pool at the offset |data_pool|,
653
 
        starting at |offset| into self.params. Returns the offset
654
 
        suitable for reading the data following the ParamDescriptor array.
655
 
        
656
 
        """
657
 
        for i in range(num_args):
658
 
            p, offset = Param.read(typelib, map, data_pool, offset)
659
 
            self.params.append(p)
660
 
        return offset
661
 
 
662
 
    def read_result(self, typelib, map, data_pool, offset):
663
 
        """
664
 
        Read a ParamDescriptor representing this Method's return type
665
 
        from the mmaped file |map| with data pool at the offset |data_pool|,
666
 
        starting at |offset| into self.result. Returns the offset
667
 
        suitable for reading the data following the ParamDescriptor.
668
 
        
669
 
        """
670
 
        self.result, offset = Param.read(typelib, map, data_pool, offset)
671
 
        return offset
672
 
 
673
 
    @staticmethod
674
 
    def decodeflags(byte):
675
 
        """
676
 
        Given |byte|, an unsigned uint8 containing flag bits,
677
 
        decode the flag bits as described in
678
 
        http://www.mozilla.org/scriptable/typelib_file.html#MethodDescriptor
679
 
        and return a dict of flagname: (True|False) suitable
680
 
        for passing to Method.__init__ as **kwargs
681
 
        
682
 
        """
683
 
        return {'getter': bool(byte & 0x80),
684
 
                'setter': bool(byte & 0x40),
685
 
                'notxpcom': bool(byte & 0x20),
686
 
                'constructor': bool(byte & 0x10),
687
 
                'hidden': bool(byte & 0x08),
688
 
                # Not in the spec, see
689
 
                # http://hg.mozilla.org/mozilla-central/annotate/0e0e2516f04e/xpcom/typelib/xpt/public/xpt_struct.h#l489
690
 
                'optargc': bool(byte & 0x04),
691
 
                'implicit_jscontext': bool(byte & 0x02),
692
 
                }
693
 
 
694
 
    def encodeflags(self):
695
 
        """
696
 
        Encode the flags of this Method object, return a byte suitable
697
 
        for writing to a typelib file.
698
 
 
699
 
        """
700
 
        flags = 0
701
 
        if self.getter:
702
 
            flags |= 0x80
703
 
        if self.setter:
704
 
            flags |= 0x40
705
 
        if self.notxpcom:
706
 
            flags |= 0x20
707
 
        if self.constructor:
708
 
            flags |= 0x10
709
 
        if self.hidden:
710
 
            flags |= 0x08
711
 
        if self.optargc:
712
 
            flags |= 0x04
713
 
        if self.implicit_jscontext:
714
 
            flags |= 0x02
715
 
        return flags
716
 
 
717
 
    @staticmethod
718
 
    def read(typelib, map, data_pool, offset):
719
 
        """
720
 
        Read a MethodDescriptor at |offset| from the mmaped file |map| with
721
 
        data pool offset |data_pool|. Returns (Method, next offset),
722
 
        where |next offset| is an offset suitable for reading the data
723
 
        following this MethodDescriptor.
724
 
        
725
 
        """
726
 
        start = data_pool + offset - 1
727
 
        flags, name_offset, num_args = Method._descriptorstart.unpack(map[start:start + Method._descriptorstart.size])
728
 
        # only the first seven bits are flags
729
 
        flags &= 0xFE
730
 
        flags = Method.decodeflags(flags)
731
 
        name = Typelib.read_string(map, data_pool, name_offset)
732
 
        m = Method(name, None, **flags)
733
 
        offset += Method._descriptorstart.size
734
 
        offset = m.read_params(typelib, map, data_pool, offset, num_args)
735
 
        offset = m.read_result(typelib, map, data_pool, offset)
736
 
        return m, offset
737
 
 
738
 
    def write(self, typelib, file):
739
 
        """
740
 
        Write a MethodDescriptor to |file|, which is assumed to be
741
 
        seeked to the right position.
742
 
 
743
 
        """
744
 
        file.write(Method._descriptorstart.pack(self.encodeflags(),
745
 
                                                self._name_offset,
746
 
                                                len(self.params)))
747
 
        for p in self.params:
748
 
            p.write(typelib, file)
749
 
        self.result.write(typelib, file)
750
 
 
751
 
    def write_name(self, file, data_pool_offset):
752
 
        """
753
 
        Write this method's name to |file|.
754
 
        Assumes that |file| is currently seeked to an unused portion
755
 
        of the data pool.
756
 
 
757
 
        """
758
 
        if self.name:
759
 
            self._name_offset = file.tell() - data_pool_offset + 1
760
 
            file.write(self.name + "\x00")
761
 
        else:
762
 
            self._name_offset = 0
763
 
 
764
 
class Constant(object):
765
 
    """
766
 
    A constant value of a specific type defined on an interface.
767
 
    (ConstantDesciptor from the typelib specification.)
768
 
 
769
 
    """
770
 
    _descriptorstart = struct.Struct(">I")
771
 
    # Actual value is restricted to this set of types
772
 
    #XXX: the spec lies, the source allows a bunch more
773
 
    # http://hg.mozilla.org/mozilla-central/annotate/9c85f9aaec8c/xpcom/typelib/xpt/src/xpt_struct.c#l689
774
 
    typemap = {Type.Tags.int16: '>h',
775
 
               Type.Tags.uint16: '>H',
776
 
               Type.Tags.int32: '>i',
777
 
               Type.Tags.uint32: '>I'}
778
 
 
779
 
    def __init__(self, name, type, value):
780
 
        self.name = name
781
 
        self._name_offset = 0
782
 
        self.type = type
783
 
        self.value = value
784
 
 
785
 
    @staticmethod
786
 
    def read(typelib, map, data_pool, offset):
787
 
        """
788
 
        Read a ConstDescriptor at |offset| from the mmaped file |map| with
789
 
        data pool offset |data_pool|. Returns (Constant, next offset),
790
 
        where |next offset| is an offset suitable for reading the data
791
 
        following this ConstDescriptor.
792
 
        
793
 
        """
794
 
        start = data_pool + offset - 1
795
 
        (name_offset,) = Constant._descriptorstart.unpack(map[start:start + Constant._descriptorstart.size])
796
 
        name = Typelib.read_string(map, data_pool, name_offset)
797
 
        offset += Constant._descriptorstart.size
798
 
        # Read TypeDescriptor
799
 
        t, offset = Type.read(typelib, map, data_pool, offset)
800
 
        c = None
801
 
        if isinstance(t, SimpleType) and t.tag in Constant.typemap:
802
 
            tt = Constant.typemap[t.tag]
803
 
            start = data_pool + offset - 1
804
 
            (val,) = struct.unpack(tt, map[start:start + struct.calcsize(tt)])
805
 
            offset += struct.calcsize(tt)
806
 
            c = Constant(name, t, val)
807
 
        return c, offset
808
 
 
809
 
    def write(self, typelib, file):
810
 
        """
811
 
        Write a ConstDescriptor to |file|, which is assumed
812
 
        to be seeked to the proper position.
813
 
 
814
 
        """
815
 
        file.write(Constant._descriptorstart.pack(self._name_offset))
816
 
        self.type.write(typelib, file)
817
 
        tt = Constant.typemap[self.type.tag]
818
 
        file.write(struct.pack(tt, self.value))
819
 
 
820
 
    def write_name(self, file, data_pool_offset):
821
 
        """
822
 
        Write this constants's name to |file|.
823
 
        Assumes that |file| is currently seeked to an unused portion
824
 
        of the data pool.
825
 
 
826
 
        """
827
 
        if self.name:
828
 
            self._name_offset = file.tell() - data_pool_offset + 1
829
 
            file.write(self.name + "\x00")
830
 
        else:
831
 
            self._name_offset = 0
832
 
 
833
 
    def __repr__(self):
834
 
        return "Constant(%s, %s, %d)" % (self.name, str(self.type), self.value)
835
 
 
836
 
class Interface(object):
837
 
    """
838
 
    An Interface represents an object, with its associated methods
839
 
    and constant values.
840
 
    (InterfaceDescriptor from the typelib specification.)
841
 
    
842
 
    """
843
 
    _direntry = struct.Struct(">16sIII")
844
 
    _descriptorstart = struct.Struct(">HH")
845
 
 
846
 
    UNRESOLVED_IID = "00000000-0000-0000-0000-000000000000"
847
 
 
848
 
    def __init__(self, name, iid=UNRESOLVED_IID, namespace="",
849
 
                 resolved=False, parent=None, methods=[], constants=[],
850
 
                 scriptable=False, function=False, builtinclass=False):
851
 
        self.resolved = resolved
852
 
        #TODO: should validate IIDs!
853
 
        self.iid = iid
854
 
        self.name = name
855
 
        self.namespace = namespace
856
 
        # if unresolved, all the members following this are unusable
857
 
        self.parent = parent
858
 
        self.methods = list(methods)
859
 
        self.constants = list(constants)
860
 
        self.scriptable = scriptable
861
 
        self.function = function
862
 
        self.builtinclass = builtinclass
863
 
        # For sanity, if someone constructs an Interface and passes
864
 
        # in methods or constants, then it's resolved.
865
 
        if self.methods or self.constants:
866
 
            # make sure it has a valid IID
867
 
            if self.iid == Interface.UNRESOLVED_IID:
868
 
                raise DataError, "Cannot instantiate Interface %s containing methods or constants with an unresolved IID" % self.name
869
 
            self.resolved = True
870
 
        # These are only used for writing out the interface
871
 
        self._descriptor_offset = 0
872
 
        self._name_offset = 0
873
 
        self._namespace_offset = 0
874
 
        self.xpt_filename = None
875
 
 
876
 
    def __repr__(self):
877
 
        return "Interface('%s', '%s', '%s', methods=%s)" % (self.name, self.iid, self.namespace, self.methods)
878
 
 
879
 
    def __str__(self):
880
 
        return "Interface(name='%s', iid='%s')" % (self.name, self.iid)
881
 
 
882
 
    def __hash__(self):
883
 
        return hash((self.name, self.iid))
884
 
 
885
 
    def __cmp__(self, other):
886
 
        c = cmp(self.iid, other.iid)
887
 
        if c != 0:
888
 
            return c
889
 
        c = cmp(self.name, other.name)
890
 
        if c != 0:
891
 
            return c
892
 
        c = cmp(self.namespace, other.namespace)
893
 
        if c != 0:
894
 
            return c
895
 
        # names and IIDs are the same, check resolved
896
 
        if self.resolved != other.resolved:
897
 
            if self.resolved:
898
 
                return -1
899
 
            else:
900
 
                return 1
901
 
        else:
902
 
            # both unresolved, but names and IIDs are the same, so equal
903
 
            return 0
904
 
        #TODO: actually compare methods etc
905
 
        return 0
906
 
 
907
 
    def read_descriptor(self, typelib, map, data_pool):
908
 
        offset = self._descriptor_offset
909
 
        if offset == 0:
910
 
            return
911
 
        start = data_pool + offset - 1
912
 
        parent, num_methods = Interface._descriptorstart.unpack(map[start:start + Interface._descriptorstart.size])
913
 
        if parent > 0 and parent <= len(typelib.interfaces):
914
 
            self.parent = typelib.interfaces[parent - 1]
915
 
        # Read methods
916
 
        offset += Interface._descriptorstart.size
917
 
        for i in range(num_methods):
918
 
            m, offset = Method.read(typelib, map, data_pool, offset)
919
 
            self.methods.append(m)
920
 
        # Read constants
921
 
        start = data_pool + offset - 1
922
 
        (num_constants, ) = struct.unpack(">H", map[start:start + struct.calcsize(">H")])
923
 
        offset = offset + struct.calcsize(">H")
924
 
        for i in range(num_constants):
925
 
            c, offset = Constant.read(typelib, map, data_pool, offset)
926
 
            self.constants.append(c)
927
 
        # Read flags
928
 
        start = data_pool + offset - 1
929
 
        (flags, ) = struct.unpack(">B", map[start:start + struct.calcsize(">B")])
930
 
        offset = offset + struct.calcsize(">B")
931
 
        # only the first two bits are flags
932
 
        flags &= 0xE0
933
 
        if flags & 0x80:
934
 
            self.scriptable = True
935
 
        if flags & 0x40:
936
 
            self.function = True
937
 
        if flags & 0x20:
938
 
            self.builtinclass = True
939
 
        self.resolved = True
940
 
 
941
 
    def write_directory_entry(self, file):
942
 
        """
943
 
        Write an InterfaceDirectoryEntry for this interface
944
 
        to |file|, which is assumed to be seeked to the correct offset.
945
 
 
946
 
        """
947
 
        file.write(Interface._direntry.pack(Typelib.string_to_iid(self.iid),
948
 
                                            self._name_offset,
949
 
                                            self._namespace_offset,
950
 
                                            self._descriptor_offset))
951
 
 
952
 
    def write(self, typelib, file, data_pool_offset):
953
 
        """
954
 
        Write an InterfaceDescriptor to |file|, which is assumed
955
 
        to be seeked to the proper position. If this interface
956
 
        is not resolved, do not write any data.
957
 
 
958
 
        """
959
 
        if not self.resolved:
960
 
            self._descriptor_offset = 0
961
 
            return
962
 
        self._descriptor_offset = file.tell() - data_pool_offset + 1
963
 
        parent_idx = 0
964
 
        if self.parent:
965
 
            parent_idx = typelib.interfaces.index(self.parent) + 1
966
 
        file.write(Interface._descriptorstart.pack(parent_idx, len(self.methods)))
967
 
        for m in self.methods:
968
 
            m.write(typelib, file)
969
 
        file.write(struct.pack(">H", len(self.constants)))
970
 
        for c in self.constants:
971
 
            c.write(typelib, file)
972
 
        flags = 0
973
 
        if self.scriptable:
974
 
            flags |= 0x80
975
 
        if self.function:
976
 
            flags |= 0x40
977
 
        if self.builtinclass:
978
 
            flags |= 0x20
979
 
        file.write(struct.pack(">B", flags))
980
 
 
981
 
    def write_names(self, file, data_pool_offset):
982
 
        """
983
 
        Write this interface's name and namespace to |file|,
984
 
        as well as the names of all of its methods and constants.
985
 
        Assumes that |file| is currently seeked to an unused portion
986
 
        of the data pool.
987
 
 
988
 
        """
989
 
        if self.name:
990
 
            self._name_offset = file.tell() - data_pool_offset + 1
991
 
            file.write(self.name + "\x00")
992
 
        else:
993
 
            self._name_offset = 0
994
 
        if self.namespace:
995
 
            self._namespace_offset = file.tell() - data_pool_offset + 1
996
 
            file.write(self.namespace + "\x00")
997
 
        else:
998
 
            self._namespace_offset = 0
999
 
        for m in self.methods:
1000
 
            m.write_name(file, data_pool_offset)
1001
 
        for c in self.constants:
1002
 
            c.write_name(file, data_pool_offset)
1003
 
 
1004
 
class Typelib(object):
1005
 
    """
1006
 
    A typelib represents one entire typelib file and all the interfaces
1007
 
    referenced within, whether defined entirely within the typelib or
1008
 
    merely referenced by name or IID.
1009
 
 
1010
 
    Typelib objects may be instantiated directly and populated with data,
1011
 
    or the static Typelib.read method may be called to read one from a file.
1012
 
 
1013
 
    """
1014
 
    _header = struct.Struct(">16sBBHIII")
1015
 
 
1016
 
    def __init__(self, version=TYPELIB_VERSION, interfaces=[], annotations=[]):
1017
 
        """
1018
 
        Instantiate a new Typelib.
1019
 
 
1020
 
        """
1021
 
        self.version = version
1022
 
        self.interfaces = list(interfaces)
1023
 
        self.annotations = list(annotations)
1024
 
        self.filename = None
1025
 
 
1026
 
    @staticmethod
1027
 
    def iid_to_string(iid):
1028
 
        """
1029
 
        Convert a 16-byte IID into a UUID string.
1030
 
 
1031
 
        """
1032
 
        def hexify(s):
1033
 
            return ''.join(["%02x" % ord(x) for x in s])
1034
 
 
1035
 
        return "%s-%s-%s-%s-%s" % (hexify(iid[:4]), hexify(iid[4:6]),
1036
 
                                   hexify(iid[6:8]), hexify(iid[8:10]),
1037
 
                                   hexify(iid[10:]))
1038
 
 
1039
 
    @staticmethod
1040
 
    def string_to_iid(iid_str):
1041
 
        """
1042
 
        Convert a UUID string into a 16-byte IID.
1043
 
 
1044
 
        """
1045
 
        s = iid_str.replace('-','')
1046
 
        return ''.join([chr(int(s[i:i+2], 16)) for i in range(0, len(s), 2)])
1047
 
 
1048
 
    @staticmethod
1049
 
    def read_string(map, data_pool, offset):
1050
 
        if offset == 0:
1051
 
            return ""
1052
 
        sz = map.find('\x00', data_pool + offset - 1)
1053
 
        if sz == -1:
1054
 
            return ""
1055
 
        return map[data_pool + offset - 1:sz]
1056
 
 
1057
 
    @staticmethod
1058
 
    def read(input_file):
1059
 
        """
1060
 
        Read a typelib from |input_file| and return
1061
 
        the constructed Typelib object. |input_file| can be a filename
1062
 
        or a file-like object.
1063
 
 
1064
 
        """
1065
 
        filename = ""
1066
 
        data = None
1067
 
        expected_size = None
1068
 
        if isinstance(input_file, basestring):
1069
 
            filename = input_file
1070
 
            with open(input_file, "rb") as f:
1071
 
                st = os.fstat(f.fileno())
1072
 
                data = f.read(st.st_size)
1073
 
                expected_size = st.st_size
1074
 
        else:
1075
 
            data = input_file.read()
1076
 
 
1077
 
        (magic,
1078
 
         major_ver,
1079
 
         minor_ver,
1080
 
         num_interfaces,
1081
 
         file_length,
1082
 
         interface_directory_offset,
1083
 
         data_pool_offset) = Typelib._header.unpack(data[:Typelib._header.size])
1084
 
        if magic != XPT_MAGIC:
1085
 
            raise FileFormatError, "Bad magic: %s" % magic
1086
 
        xpt = Typelib((major_ver, minor_ver))
1087
 
        xpt.filename = filename
1088
 
        if expected_size and file_length != expected_size:
1089
 
            raise FileFormatError, "File is of wrong length, got %d bytes, expected %d" % (expected_size, file_length)
1090
 
        #XXX: by spec this is a zero-based file offset. however,
1091
 
        # the xpt_xdr code always subtracts 1 from data offsets
1092
 
        # (because that's what you do in the data pool) so it
1093
 
        # winds up accidentally treating this as 1-based.
1094
 
        # Filed as: https://bugzilla.mozilla.org/show_bug.cgi?id=575343
1095
 
        interface_directory_offset -= 1
1096
 
        # make a half-hearted attempt to read Annotations,
1097
 
        # since XPIDL doesn't produce any anyway.
1098
 
        start = Typelib._header.size
1099
 
        (anno, ) = struct.unpack(">B", data[start:start + struct.calcsize(">B")])
1100
 
        islast = anno & 0x80
1101
 
        tag = anno & 0x7F
1102
 
        if tag == 0: # EmptyAnnotation
1103
 
            xpt.annotations.append(None)
1104
 
        # We don't bother handling PrivateAnnotations or anything
1105
 
 
1106
 
        for i in range(num_interfaces):
1107
 
            # iid, name, namespace, interface_descriptor
1108
 
            start = interface_directory_offset + i * Interface._direntry.size
1109
 
            end = interface_directory_offset + (i+1) * Interface._direntry.size
1110
 
            ide = Interface._direntry.unpack(data[start:end])
1111
 
            iid = Typelib.iid_to_string(ide[0])
1112
 
            name = Typelib.read_string(data, data_pool_offset, ide[1])
1113
 
            namespace = Typelib.read_string(data, data_pool_offset, ide[2])
1114
 
            iface = Interface(name, iid, namespace)
1115
 
            iface._descriptor_offset = ide[3]
1116
 
            iface.xpt_filename = xpt.filename
1117
 
            xpt.interfaces.append(iface)
1118
 
        for iface in xpt.interfaces:
1119
 
            iface.read_descriptor(xpt, data, data_pool_offset)
1120
 
        return xpt
1121
 
 
1122
 
    def __repr__(self):
1123
 
        return "<Typelib with %d interfaces>" % len(self.interfaces)
1124
 
 
1125
 
    def _sanityCheck(self):
1126
 
        """
1127
 
        Check certain assumptions about data contained in this typelib.
1128
 
        Sort the interfaces array by IID, check that all interfaces
1129
 
        referenced by methods exist in the array.
1130
 
 
1131
 
        """
1132
 
        self.interfaces.sort()
1133
 
        for i in self.interfaces:
1134
 
            if i.parent and i.parent not in self.interfaces:
1135
 
                raise DataError, "Interface %s has parent %s not present in typelib!" % (i.name, i.parent.name)
1136
 
            for m in i.methods:
1137
 
                for n, p in enumerate(m.params):
1138
 
                    if isinstance(p, InterfaceType) and \
1139
 
                        p.iface not in self.interfaces:
1140
 
                        raise DataError, "Interface method %s::%s, parameter %d references interface %s not present in typelib!" % (i.name, m.name, n, p.iface.name)
1141
 
                if isinstance(m.result, InterfaceType) and m.result.iface not in self.interfaces:
1142
 
                    raise DataError, "Interface method %s::%s, result references interface %s not present in typelib!" % (i.name, m.name, m.result.iface.name)
1143
 
 
1144
 
    def writefd(self, fd):
1145
 
        # write out space for a header + one empty annotation,
1146
 
        # padded to 4-byte alignment.
1147
 
        headersize = (Typelib._header.size + 1)
1148
 
        if headersize % 4:
1149
 
            headersize += 4 - headersize % 4
1150
 
        fd.write("\x00" * headersize)
1151
 
        # save this offset, it's the interface directory offset.
1152
 
        interface_directory_offset = fd.tell()
1153
 
        # write out space for an interface directory
1154
 
        fd.write("\x00" * Interface._direntry.size * len(self.interfaces))
1155
 
        # save this offset, it's the data pool offset.
1156
 
        data_pool_offset = fd.tell()
1157
 
        # write out all the interface descriptors to the data pool
1158
 
        for i in self.interfaces:
1159
 
            i.write_names(fd, data_pool_offset)
1160
 
            i.write(self, fd, data_pool_offset)
1161
 
        # now, seek back and write the header
1162
 
        file_len = fd.tell()
1163
 
        fd.seek(0)
1164
 
        fd.write(Typelib._header.pack(XPT_MAGIC,
1165
 
                                      TYPELIB_VERSION[0],
1166
 
                                      TYPELIB_VERSION[1],
1167
 
                                      len(self.interfaces),
1168
 
                                      file_len,
1169
 
                                      interface_directory_offset,
1170
 
                                      data_pool_offset))
1171
 
        # write an empty annotation
1172
 
        fd.write(struct.pack(">B", 0x80))
1173
 
        # now write the interface directory
1174
 
        #XXX: bug-compatible with existing xpt lib, put it one byte
1175
 
        # ahead of where it's supposed to be.
1176
 
        fd.seek(interface_directory_offset - 1)
1177
 
        for i in self.interfaces:
1178
 
            i.write_directory_entry(fd)
1179
 
 
1180
 
    def write(self, output_file):
1181
 
        """
1182
 
        Write the contents of this typelib to |output_file|,
1183
 
        which can be either a filename or a file-like object.
1184
 
 
1185
 
        """
1186
 
        self._sanityCheck()
1187
 
        if isinstance(output_file, basestring):
1188
 
            with open(output_file, "wb") as f:
1189
 
                self.writefd(f)
1190
 
        else:
1191
 
            self.writefd(output_file)
1192
 
 
1193
 
    def dump(self, out):
1194
 
        """
1195
 
        Print a human-readable listing of the contents of this typelib
1196
 
        to |out|, in the format of xpt_dump.
1197
 
        
1198
 
        """
1199
 
        out.write("""Header:
1200
 
   Major version:         %d
1201
 
   Minor version:         %d
1202
 
   Number of interfaces:  %d
1203
 
   Annotations:\n""" % (self.version[0], self.version[1], len(self.interfaces)))
1204
 
        for i, a in enumerate(self.annotations):
1205
 
            if a is None:
1206
 
                out.write("      Annotation #%d is empty.\n" % i)
1207
 
        out.write("\nInterface Directory:\n")
1208
 
        for i in self.interfaces:
1209
 
            out.write("   - %s::%s (%s):\n" % (i.namespace, i.name, i.iid))
1210
 
            if not i.resolved:
1211
 
                out.write("      [Unresolved]\n")
1212
 
            else:
1213
 
                if i.parent:
1214
 
                    out.write("      Parent: %s::%s\n" % (i.parent.namespace,
1215
 
                                                    i.parent.name))
1216
 
                out.write("""      Flags:
1217
 
         Scriptable: %s
1218
 
         BuiltinClass: %s
1219
 
         Function: %s\n""" % (i.scriptable and "TRUE" or "FALSE",
1220
 
                              i.builtinclass and "TRUE" or "FALSE",
1221
 
                              i.function and "TRUE" or "FALSE"))
1222
 
                out.write("      Methods:\n")
1223
 
                if len(i.methods) == 0:
1224
 
                    out.write("         No Methods\n")
1225
 
                else:
1226
 
                    for m in i.methods:
1227
 
                        out.write("   %s%s%s%s%s%s%s %s %s(%s);\n" % (
1228
 
                            m.getter and "G" or " ",
1229
 
                            m.setter and "S" or " ",
1230
 
                            m.hidden and "H" or " ",
1231
 
                            m.notxpcom and "N" or " ",
1232
 
                            m.constructor and "C" or " ",
1233
 
                            m.optargc and "O" or " ",
1234
 
                            m.implicit_jscontext and "J" or " ",
1235
 
                            str(m.result.type),
1236
 
                            m.name,
1237
 
                            m.params and ", ".join(str(p) for p in m.params) or ""
1238
 
                            ))
1239
 
                out.write("      Constants:\n")
1240
 
                if len(i.constants) == 0:
1241
 
                    out.write("         No Constants\n")
1242
 
                else:
1243
 
                    for c in i.constants:
1244
 
                        out.write("         %s %s = %d;\n" % (c.type, c.name, c.value))
1245
 
 
1246
 
def xpt_dump(file):
1247
 
    """
1248
 
    Dump the contents of |file| to stdout in the format of xpt_dump.
1249
 
 
1250
 
    """
1251
 
    t = Typelib.read(file)
1252
 
    t.dump(sys.stdout)
1253
 
 
1254
 
def xpt_link(inputs):
1255
 
    """
1256
 
    Link all of the xpt files in |inputs| together and return the result
1257
 
    as a Typelib object. All entries in inputs may be filenames or
1258
 
    file-like objects.
1259
 
 
1260
 
    """
1261
 
    def read_input(i):
1262
 
        if isinstance(i, Typelib):
1263
 
            return i
1264
 
        return Typelib.read(i)
1265
 
 
1266
 
    if not inputs:
1267
 
        print >>sys.stderr, "Usage: xpt_link <destination file> <input files>"
1268
 
        return None
1269
 
    # This is the aggregate list of interfaces.
1270
 
    interfaces = []
1271
 
    # This will be a dict of replaced interface -> replaced with
1272
 
    # containing interfaces that were replaced with interfaces from
1273
 
    # another typelib, and the interface that replaced them.
1274
 
    merged_interfaces = {}
1275
 
    for f in inputs:
1276
 
        t = read_input(f)
1277
 
        interfaces.extend(t.interfaces)
1278
 
    # Sort interfaces by name so we can merge adjacent duplicates
1279
 
    interfaces.sort(key=operator.attrgetter('name'))
1280
 
 
1281
 
    Result = enum('Equal',     # Interfaces the same, doesn't matter
1282
 
                  'NotEqual',  # Interfaces differ, keep both
1283
 
                  'KeepFirst', # Replace second interface with first
1284
 
                  'KeepSecond')# Replace first interface with second
1285
 
        
1286
 
    def compare(i, j):
1287
 
        """
1288
 
        Compare two interfaces, determine if they're equal or
1289
 
        completely different, or should be merged (and indicate which
1290
 
        one to keep in that case).
1291
 
 
1292
 
        """
1293
 
        if i == j:
1294
 
            # Arbitrary, just pick one
1295
 
            return Result.Equal
1296
 
        if i.name != j.name:
1297
 
            if i.iid == j.iid and i.iid != Interface.UNRESOLVED_IID:
1298
 
                # Same IID but different names: raise an exception.
1299
 
                raise DataError, \
1300
 
                    "Typelibs contain definitions of interface %s" \
1301
 
                    " with different names (%s (%s) vs %s (%s))!" % \
1302
 
                    (i.iid, i.name, i.xpt_filename, j.name, j.xpt_filename)
1303
 
            # Otherwise just different interfaces.
1304
 
            return Result.NotEqual
1305
 
        # Interfaces have the same name, so either they need to be merged
1306
 
        # or there's a data error. Sort out which one to keep
1307
 
        if i.resolved != j.resolved:
1308
 
            # prefer resolved interfaces over unresolved
1309
 
            if j.resolved:
1310
 
                assert i.iid == j.iid or i.iid == Interface.UNRESOLVED_IID
1311
 
                # keep j
1312
 
                return Result.KeepSecond
1313
 
            else:
1314
 
                assert i.iid == j.iid or j.iid == Interface.UNRESOLVED_IID
1315
 
                # replace j with i
1316
 
                return Result.KeepFirst
1317
 
        elif i.iid != j.iid:
1318
 
            # Prefer unresolved interfaces with valid IIDs
1319
 
            if j.iid == Interface.UNRESOLVED_IID:
1320
 
                # replace j with i
1321
 
                assert not j.resolved
1322
 
                return Result.KeepFirst
1323
 
            elif i.iid == Interface.UNRESOLVED_IID:
1324
 
                # keep j
1325
 
                assert not i.resolved
1326
 
                return Result.KeepSecond
1327
 
            else:
1328
 
                # Same name but different IIDs: raise an exception.
1329
 
                raise DataError, \
1330
 
                    "Typelibs contain definitions of interface %s" \
1331
 
                                " with different IIDs (%s (%s) vs %s (%s))!" % \
1332
 
                                (i.name, i.iid, i.xpt_filename, \
1333
 
                                 j.iid, j.xpt_filename)
1334
 
        raise DataError, "No idea what happened here: %s:%s (%s), %s:%s (%s)" % \
1335
 
            (i.name, i.iid, i.xpt_filename, j.name, j.iid, j.xpt_filename)
1336
 
    
1337
 
    # Compare interfaces pairwise to find duplicates that should be merged.
1338
 
    i = 1
1339
 
    while i < len(interfaces):
1340
 
        res = compare(interfaces[i-1], interfaces[i])
1341
 
        if res == Result.NotEqual:
1342
 
            i += 1
1343
 
        elif res == Result.Equal:
1344
 
            # Need to drop one but it doesn't matter which
1345
 
            del interfaces[i]
1346
 
        elif res == Result.KeepFirst:
1347
 
            merged_interfaces[interfaces[i]] = interfaces[i-1]
1348
 
            del interfaces[i]
1349
 
        elif res == Result.KeepSecond:
1350
 
            merged_interfaces[interfaces[i-1]] = interfaces[i]
1351
 
            del interfaces[i-1]
1352
 
    
1353
 
    # Now fixup any merged interfaces
1354
 
    def checkType(t):
1355
 
        if isinstance(t, InterfaceType) and t.iface in merged_interfaces:
1356
 
            t.iface = merged_interfaces[t.iface]
1357
 
        elif isinstance(t, ArrayType) and \
1358
 
             isinstance(t.element_type, InterfaceType) and \
1359
 
             t.element_type.iface in merged_interfaces:
1360
 
            t.element_type.iface = merged_interfaces[t.element_type.iface]
1361
 
 
1362
 
    for i in interfaces:
1363
 
        # Replace parent references
1364
 
        if i.parent in merged_interfaces:
1365
 
            i.parent = merged_interfaces[i.parent]
1366
 
        for m in i.methods:
1367
 
            # Replace InterfaceType params and return values
1368
 
            checkType(m.result.type)
1369
 
            for p in m.params:
1370
 
                checkType(p.type)
1371
 
    # Re-sort interfaces (by IID)
1372
 
    interfaces.sort()
1373
 
    return Typelib(interfaces=interfaces)
1374
 
 
1375
 
if __name__ == '__main__':
1376
 
    if len(sys.argv) < 3:
1377
 
        print >>sys.stderr, "xpt <dump|link> <files>"
1378
 
        sys.exit(1)
1379
 
    if sys.argv[1] == 'dump':
1380
 
        xpt_dump(sys.argv[2])
1381
 
    elif sys.argv[1] == 'link':
1382
 
        xpt_link(sys.argv[3:]).write(sys.argv[2])
1383