Back to index

enigmail  1.4.3
xpt.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 # Copyright 2010,2011 Mozilla Foundation. All rights reserved.
00003 #
00004 # Redistribution and use in source and binary forms, with or without
00005 # modification, are permitted provided that the following conditions are
00006 # met:
00007 #
00008 #   1. Redistributions of source code must retain the above copyright
00009 #      notice, this list of conditions and the following disclaimer.
00010 #
00011 #   2. Redistributions in binary form must reproduce the above copyright
00012 #      notice, this list of conditions and the following disclaimer in
00013 #      the documentation and/or other materials provided with the
00014 #      distribution.
00015 #
00016 # THIS SOFTWARE IS PROVIDED BY THE MOZILLA FOUNDATION ``AS IS'' AND ANY
00017 # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00018 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00019 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE MOZILLA FOUNDATION OR
00020 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00021 # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00022 # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00023 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
00024 # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
00026 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027 #
00028 # The views and conclusions contained in the software and documentation
00029 # are those of the authors and should not be interpreted as representing
00030 # official policies, either expressed or implied, of the Mozilla
00031 # Foundation.
00032 
00033 """
00034 A module for working with XPCOM Type Libraries.
00035 
00036 The XPCOM Type Library File Format is described at:
00037 http://www.mozilla.org/scriptable/typelib_file.html . It is used
00038 to provide type information for calling methods on XPCOM objects
00039 from scripting languages such as JavaScript.
00040 
00041 This module provides a set of classes representing the parts of
00042 a typelib in a high-level manner, as well as methods for reading
00043 and writing them from files.
00044 
00045 The usable public interfaces are currently:
00046 Typelib.read(filename) - read a typelib from a file on disk, return
00047                          a Typelib object.
00048 
00049 xpt_dump(filename)     - read a typelib from a file on disk, dump
00050                          the contents to stdout in a human-readable
00051                          format.
00052 
00053 Typelib()              - construct a new Typelib object
00054 Interface()            - construct a new Interface object
00055 Method()               - construct a new object representing a method
00056                          defined on an Interface
00057 Constant()             - construct a new object representing a constant
00058                          defined on an Interface
00059 Param()                - construct a new object representing a parameter
00060                          to a method
00061 SimpleType()           - construct a new object representing a simple
00062                          data type
00063 InterfaceType()        - construct a new object representing a type that
00064                          is an IDL-defined interface
00065 
00066 """
00067 
00068 from __future__ import with_statement
00069 import os, sys
00070 import struct
00071 
00072 # header magic
00073 XPT_MAGIC = "XPCOM\nTypeLib\r\n\x1a"
00074 TYPELIB_VERSION = (1, 2)
00075 
00076 class FileFormatError(Exception):
00077     pass
00078 
00079 class DataError(Exception):
00080     pass
00081 
00082 # Magic for creating enums
00083 def M_add_class_attribs(attribs):
00084     def foo(name, bases, dict_):
00085         for v, k in attribs:
00086             dict_[k] = v
00087         return type(name, bases, dict_)
00088     return foo
00089 
00090 def enum(*names):
00091     class Foo(object):
00092         __metaclass__ = M_add_class_attribs(enumerate(names))
00093         def __setattr__(self, name, value):  # this makes it read-only
00094             raise NotImplementedError
00095     return Foo()
00096 
00097 # Descriptor types as described in the spec
00098 class Type(object):
00099     """
00100     Data type of a method parameter or return value. Do not instantiate
00101     this class directly. Rather, use one of its subclasses.
00102     
00103     """
00104     _prefixdescriptor = struct.Struct(">B")
00105     Tags = enum(
00106         # The first 18 entries are SimpleTypeDescriptor
00107         'int8',
00108         'int16',
00109         'int32',
00110         'int64',
00111         'uint8',
00112         'uint16',
00113         'uint32',
00114         'uint64',
00115         'float',
00116         'double',
00117         'boolean',
00118         'char',
00119         'wchar_t',
00120         'void',
00121         # the following four values are only valid as pointers
00122         'nsIID',
00123         'DOMString',
00124         'char_ptr',
00125         'wchar_t_ptr',
00126         # InterfaceTypeDescriptor
00127         'Interface',
00128         # InterfaceIsTypeDescriptor
00129         'InterfaceIs',
00130         # ArrayTypeDescriptor
00131         'Array',
00132         # StringWithSizeTypeDescriptor
00133         'StringWithSize',
00134         # WideStringWithSizeTypeDescriptor
00135         'WideStringWithSize',
00136         #XXX: These are also SimpleTypes (but not in the spec)
00137         # http://hg.mozilla.org/mozilla-central/annotate/0e0e2516f04e/xpcom/typelib/xpt/tools/xpt_dump.c#l69
00138         'UTF8String',
00139         'CString',
00140         'AString',
00141         'jsval',
00142         )
00143 
00144     def __init__(self, pointer=False, reference=False):
00145         self.pointer = pointer
00146         self.reference = reference
00147         if reference and not pointer:
00148             raise Exception("If reference is True pointer must be True too")
00149 
00150     @staticmethod
00151     def decodeflags(byte):
00152         """
00153         Given |byte|, an unsigned uint8 containing flag bits,
00154         decode the flag bits as described in
00155         http://www.mozilla.org/scriptable/typelib_file.html#TypeDescriptor
00156         and return a dict of flagname: (True|False) suitable
00157         for passing to Type.__init__ as **kwargs.
00158         
00159         """
00160         return {'pointer': bool(byte & 0x80),
00161                 'reference': bool(byte & 0x20),
00162                 }
00163 
00164     def encodeflags(self):
00165         """
00166         Encode the flag bits of this Type object. Returns a byte.
00167 
00168         """
00169         flags = 0
00170         if self.pointer:
00171             flags |= 0x80
00172         if self.reference:
00173             flags |= 0x20
00174         return flags
00175 
00176     @staticmethod
00177     def read(typelib, map, data_pool, offset):
00178         """
00179         Read a TypeDescriptor at |offset| from the mmaped file |map| with
00180         data pool offset |data_pool|. Returns (Type, next offset),
00181         where |next offset| is an offset suitable for reading the data
00182         following this TypeDescriptor.
00183         
00184         """
00185         start = data_pool + offset - 1
00186         (data,) = Type._prefixdescriptor.unpack(map[start:start + Type._prefixdescriptor.size])
00187         # first three bits are the flags
00188         flags = data & 0xE0
00189         flags = Type.decodeflags(flags)
00190         # last five bits is the tag
00191         tag = data & 0x1F
00192         offset += Type._prefixdescriptor.size
00193         t = None
00194         if tag <= Type.Tags.wchar_t_ptr or tag >= Type.Tags.UTF8String:
00195             t = SimpleType.get(data, tag, flags)
00196         elif tag == Type.Tags.Interface:
00197             t, offset = InterfaceType.read(typelib, map, data_pool, offset, flags)
00198         elif tag == Type.Tags.InterfaceIs:
00199             t, offset = InterfaceIsType.read(typelib, map, data_pool, offset, flags)
00200         elif tag == Type.Tags.Array:
00201             t, offset = ArrayType.read(typelib, map, data_pool, offset, flags)
00202         elif tag == Type.Tags.StringWithSize:
00203             t, offset = StringWithSizeType.read(typelib, map, data_pool, offset, flags)
00204         elif tag == Type.Tags.WideStringWithSize:
00205             t, offset = WideStringWithSizeType.read(typelib, map, data_pool, offset, flags)
00206         return t, offset
00207 
00208     def write(self, typelib, file):
00209         """
00210         Write a TypeDescriptor to |file|, which is assumed
00211         to be seeked to the proper position. For types other than
00212         SimpleType, this is not sufficient for writing the TypeDescriptor,
00213         and the subclass method must be called.
00214 
00215         """
00216         file.write(Type._prefixdescriptor.pack(self.encodeflags() | self.tag))
00217 
00218 class SimpleType(Type):
00219     """
00220     A simple data type. (SimpleTypeDescriptor from the typelib specification.)
00221 
00222     """
00223     _cache = {}
00224 
00225     def __init__(self, tag, **kwargs):
00226         Type.__init__(self, **kwargs)
00227         self.tag = tag
00228 
00229     @staticmethod
00230     def get(data, tag, flags):
00231         """
00232         Get a SimpleType object representing |data| (a TypeDescriptorPrefix).
00233         May return an already-created object. If no cached object is found,
00234         construct one with |tag| and |flags|.
00235         
00236         """
00237         if data not in SimpleType._cache:
00238             SimpleType._cache[data] = SimpleType(tag, **flags)
00239         return SimpleType._cache[data]
00240 
00241     def __str__(self):
00242         s = "unknown"
00243         if self.tag == Type.Tags.char_ptr and self.pointer:
00244             return "string"
00245         if self.tag == Type.Tags.wchar_t_ptr and self.pointer:
00246             return "wstring"
00247         for t in dir(Type.Tags):
00248             if self.tag == getattr(Type.Tags, t):
00249                 s = t
00250                 break
00251 
00252         if self.pointer:
00253             if self.reference:
00254                 s += " &"
00255             else:
00256                 s += " *"
00257         return s
00258 
00259 class InterfaceType(Type):
00260     """
00261     A type representing a pointer to an IDL-defined interface.
00262     (InterfaceTypeDescriptor from the typelib specification.)
00263 
00264     """
00265     _descriptor = struct.Struct(">H")
00266 
00267     def __init__(self, iface, pointer=True, **kwargs):
00268         if not pointer:
00269             raise DataError, "InterfaceType is not valid with pointer=False"
00270         Type.__init__(self, pointer=pointer, **kwargs)
00271         self.iface = iface
00272         self.tag = Type.Tags.Interface
00273 
00274     @staticmethod
00275     def read(typelib, map, data_pool, offset, flags):
00276         """
00277         Read an InterfaceTypeDescriptor at |offset| from the mmaped
00278         file |map| with data pool offset |data_pool|.
00279         Returns (InterfaceType, next offset),
00280         where |next offset| is an offset suitable for reading the data
00281         following this InterfaceTypeDescriptor.
00282         
00283         """
00284         if not flags['pointer']:
00285             return None, offset
00286         start = data_pool + offset - 1
00287         (iface_index,) = InterfaceType._descriptor.unpack(map[start:start + InterfaceType._descriptor.size])
00288         offset += InterfaceType._descriptor.size
00289         iface = None
00290         # interface indices are 1-based
00291         if iface_index > 0 and iface_index <= len(typelib.interfaces):
00292             iface = typelib.interfaces[iface_index - 1]
00293         return InterfaceType(iface, **flags), offset
00294 
00295     def write(self, typelib, file):
00296         """
00297         Write an InterfaceTypeDescriptor to |file|, which is assumed
00298         to be seeked to the proper position.
00299 
00300         """
00301         Type.write(self, typelib, file)
00302         # write out the interface index (1-based)
00303         file.write(InterfaceType._descriptor.pack(typelib.interfaces.index(self.iface) + 1))
00304 
00305     def __str__(self):
00306         if self.iface:
00307             return self.iface.name
00308         return "unknown interface"
00309 
00310 class InterfaceIsType(Type):
00311     """
00312     A type representing an interface described by one of the other
00313     arguments to the method. (InterfaceIsTypeDescriptor from the
00314     typelib specification.)
00315     
00316     """
00317     _descriptor = struct.Struct(">B")
00318     _cache = {}
00319 
00320     def __init__(self, param_index, pointer=True, **kwargs):
00321         if not pointer:
00322             raise DataError, "InterfaceIsType is not valid with pointer=False"
00323         Type.__init__(self, pointer=pointer, **kwargs)
00324         self.param_index = param_index
00325         self.tag = Type.Tags.InterfaceIs
00326 
00327     @staticmethod
00328     def read(typelib, map, data_pool, offset, flags):
00329         """
00330         Read an InterfaceIsTypeDescriptor at |offset| from the mmaped
00331         file |map| with data pool offset |data_pool|.
00332         Returns (InterfaceIsType, next offset),
00333         where |next offset| is an offset suitable for reading the data
00334         following this InterfaceIsTypeDescriptor.
00335         May return a cached value.
00336         
00337         """
00338         if not flags['pointer']:
00339             return None, offset
00340         start = data_pool + offset - 1
00341         (param_index,) = InterfaceIsType._descriptor.unpack(map[start:start + InterfaceIsType._descriptor.size])
00342         offset += InterfaceIsType._descriptor.size
00343         if param_index not in InterfaceIsType._cache:
00344             InterfaceIsType._cache[param_index] = InterfaceIsType(param_index, **flags)
00345         return InterfaceIsType._cache[param_index], offset
00346 
00347     def write(self, typelib, file):
00348         """
00349         Write an InterfaceIsTypeDescriptor to |file|, which is assumed
00350         to be seeked to the proper position.
00351 
00352         """
00353         Type.write(self, typelib, file)
00354         file.write(InterfaceIsType._descriptor.pack(self.param_index))
00355 
00356     def __str__(self):
00357         return "InterfaceIs *"
00358 
00359 class ArrayType(Type):
00360     """
00361     A type representing an Array of elements of another type, whose
00362     size and length are passed as separate parameters to a method.
00363     (ArrayTypeDescriptor from the typelib specification.)
00364     
00365     """
00366     _descriptor = struct.Struct(">BB")
00367 
00368     def __init__(self, element_type, size_is_arg_num, length_is_arg_num,
00369                  pointer=True, **kwargs):
00370         if not pointer:
00371             raise DataError, "ArrayType is not valid with pointer=False"
00372         Type.__init__(self, pointer=pointer, **kwargs)
00373         self.element_type = element_type
00374         self.size_is_arg_num = size_is_arg_num
00375         self.length_is_arg_num = length_is_arg_num
00376         self.tag = Type.Tags.Array
00377 
00378     @staticmethod
00379     def read(typelib, map, data_pool, offset, flags):
00380         """
00381         Read an ArrayTypeDescriptor at |offset| from the mmaped
00382         file |map| with data pool offset |data_pool|.
00383         Returns (ArrayType, next offset),
00384         where |next offset| is an offset suitable for reading the data
00385         following this ArrayTypeDescriptor.
00386         """
00387         if not flags['pointer']:
00388             return None, offset
00389         start = data_pool + offset - 1
00390         (size_is_arg_num, length_is_arg_num) = ArrayType._descriptor.unpack(map[start:start + ArrayType._descriptor.size])
00391         offset += ArrayType._descriptor.size
00392         t, offset = Type.read(typelib, map, data_pool, offset)
00393         return ArrayType(t, size_is_arg_num, length_is_arg_num, **flags), offset
00394 
00395     def write(self, typelib, file):
00396         """
00397         Write an ArrayTypeDescriptor to |file|, which is assumed
00398         to be seeked to the proper position.
00399 
00400         """
00401         Type.write(self, typelib, file)
00402         file.write(ArrayType._descriptor.pack(self.size_is_arg_num,
00403                                               self.length_is_arg_num))
00404         self.element_type.write(typelib, file)
00405 
00406     def __str__(self):
00407         return "%s []" % str(self.element_type)
00408 
00409 class StringWithSizeType(Type):
00410     """
00411     A type representing a UTF-8 encoded string whose size and length
00412     are passed as separate arguments to a method. (StringWithSizeTypeDescriptor
00413     from the typelib specification.)
00414 
00415     """
00416     _descriptor = struct.Struct(">BB")
00417 
00418     def __init__(self, size_is_arg_num, length_is_arg_num,
00419                  pointer=True, **kwargs):
00420         if not pointer:
00421             raise DataError, "StringWithSizeType is not valid with pointer=False"
00422         Type.__init__(self, pointer=pointer, **kwargs)
00423         self.size_is_arg_num = size_is_arg_num
00424         self.length_is_arg_num = length_is_arg_num
00425         self.tag = Type.Tags.StringWithSize
00426 
00427     @staticmethod
00428     def read(typelib, map, data_pool, offset, flags):
00429         """
00430         Read an StringWithSizeTypeDescriptor at |offset| from the mmaped
00431         file |map| with data pool offset |data_pool|.
00432         Returns (StringWithSizeType, next offset),
00433         where |next offset| is an offset suitable for reading the data
00434         following this StringWithSizeTypeDescriptor.
00435         """
00436         if not flags['pointer']:
00437             return None, offset
00438         start = data_pool + offset - 1
00439         (size_is_arg_num, length_is_arg_num) = StringWithSizeType._descriptor.unpack(map[start:start + StringWithSizeType._descriptor.size])
00440         offset += StringWithSizeType._descriptor.size
00441         return StringWithSizeType(size_is_arg_num, length_is_arg_num, **flags), offset
00442 
00443     def write(self, typelib, file):
00444         """
00445         Write a StringWithSizeTypeDescriptor to |file|, which is assumed
00446         to be seeked to the proper position.
00447 
00448         """
00449         Type.write(self, typelib, file)
00450         file.write(StringWithSizeType._descriptor.pack(self.size_is_arg_num,
00451                                                        self.length_is_arg_num))
00452 
00453     def __str__(self):
00454         return "string_s"
00455 
00456 class WideStringWithSizeType(Type):
00457     """
00458     A type representing a UTF-16 encoded string whose size and length
00459     are passed as separate arguments to a method.
00460     (WideStringWithSizeTypeDescriptor from the typelib specification.)
00461 
00462     """
00463     _descriptor = struct.Struct(">BB")
00464 
00465     def __init__(self, size_is_arg_num, length_is_arg_num,
00466                  pointer=True, **kwargs):
00467         if not pointer:
00468             raise DataError, "WideStringWithSizeType is not valid with pointer=False"
00469         Type.__init__(self, pointer=pointer, **kwargs)
00470         self.size_is_arg_num = size_is_arg_num
00471         self.length_is_arg_num = length_is_arg_num
00472         self.tag = Type.Tags.WideStringWithSize
00473 
00474     @staticmethod
00475     def read(typelib, map, data_pool, offset, flags):
00476         """
00477         Read an WideStringWithSizeTypeDescriptor at |offset| from the mmaped
00478         file |map| with data pool offset |data_pool|.
00479         Returns (WideStringWithSizeType, next offset),
00480         where |next offset| is an offset suitable for reading the data
00481         following this WideStringWithSizeTypeDescriptor.
00482         """
00483         if not flags['pointer']:
00484             return None, offset
00485         start = data_pool + offset - 1
00486         (size_is_arg_num, length_is_arg_num) = WideStringWithSizeType._descriptor.unpack(map[start:start + WideStringWithSizeType._descriptor.size])
00487         offset += WideStringWithSizeType._descriptor.size
00488         return WideStringWithSizeType(size_is_arg_num, length_is_arg_num, **flags), offset
00489 
00490     def write(self, typelib, file):
00491         """
00492         Write a WideStringWithSizeTypeDescriptor to |file|, which is assumed
00493         to be seeked to the proper position.
00494 
00495         """
00496         Type.write(self, typelib, file)
00497         file.write(WideStringWithSizeType._descriptor.pack(self.size_is_arg_num,
00498                                                            self.length_is_arg_num))
00499 
00500     def __str__(self):
00501         return "wstring_s"
00502 
00503 class Param(object):
00504     """
00505     A parameter to a method, or the return value of a method.
00506     (ParamDescriptor from the typelib specification.)
00507 
00508     """
00509     _descriptorstart = struct.Struct(">B")
00510 
00511     def __init__(self, type, in_=True, out=False, retval=False,
00512                  shared=False, dipper=False, optional=False):
00513         """
00514         Construct a Param object with the specified |type| and
00515         flags. Params default to "in".
00516 
00517         """
00518 
00519         self.type = type
00520         self.in_ = in_
00521         self.out = out
00522         self.retval = retval
00523         self.shared = shared
00524         self.dipper = dipper
00525         self.optional = optional
00526 
00527     @staticmethod
00528     def decodeflags(byte):
00529         """
00530         Given |byte|, an unsigned uint8 containing flag bits,
00531         decode the flag bits as described in
00532         http://www.mozilla.org/scriptable/typelib_file.html#ParamDescriptor
00533         and return a dict of flagname: (True|False) suitable
00534         for passing to Param.__init__ as **kwargs
00535         """
00536         return {'in_': bool(byte & 0x80),
00537                 'out': bool(byte & 0x40),
00538                 'retval': bool(byte & 0x20),
00539                 'shared': bool(byte & 0x10),
00540                 'dipper': bool(byte & 0x08),
00541                 #XXX: Not in the spec, see:
00542                 # http://hg.mozilla.org/mozilla-central/annotate/0e0e2516f04e/xpcom/typelib/xpt/public/xpt_struct.h#l456
00543                 'optional': bool(byte & 0x04),
00544                 }
00545 
00546     def encodeflags(self):
00547         """
00548         Encode the flags of this Param. Return a byte suitable for
00549         writing to a typelib file.
00550 
00551         """
00552         flags = 0
00553         if self.in_:
00554             flags |= 0x80
00555         if self.out:
00556             flags |= 0x40
00557         if self.retval:
00558             flags |= 0x20
00559         if self.shared:
00560             flags |= 0x10
00561         if self.dipper:
00562             flags |= 0x08
00563         if self.optional:
00564             flags |= 0x04
00565         return flags
00566 
00567     @staticmethod
00568     def read(typelib, map, data_pool, offset):
00569         """
00570         Read a ParamDescriptor at |offset| from the mmaped file |map| with
00571         data pool offset |data_pool|. Returns (Param, next offset),
00572         where |next offset| is an offset suitable for reading the data
00573         following this ParamDescriptor.
00574         """
00575         start = data_pool + offset - 1
00576         (flags,) = Param._descriptorstart.unpack(map[start:start + Param._descriptorstart.size])
00577         # only the first five bits are flags
00578         flags &= 0xFC
00579         flags = Param.decodeflags(flags)
00580         offset += Param._descriptorstart.size
00581         t, offset = Type.read(typelib, map, data_pool, offset)
00582         p = Param(t, **flags)
00583         return p, offset
00584 
00585     def write(self, typelib, file):
00586         """
00587         Write a ParamDescriptor to |file|, which is assumed to be seeked
00588         to the correct position.
00589 
00590         """
00591         file.write(Param._descriptorstart.pack(self.encodeflags()))
00592         self.type.write(typelib, file)
00593 
00594     def prefix(self):
00595         """
00596         Return a human-readable string representing the flags set
00597         on this Param.
00598 
00599         """
00600         s = ""
00601         if self.out:
00602             if self.in_:
00603                 s = "inout "
00604             else:
00605                 s = "out "
00606         else:
00607             s = "in "
00608         if self.dipper:
00609             s += "dipper "
00610         if self.retval:
00611             s += "retval "
00612         if self.shared:
00613             s += "shared "
00614         if self.optional:
00615             s += "optional "
00616         return s
00617 
00618     def __str__(self):
00619         return self.prefix() + str(self.type)
00620 
00621 class Method(object):
00622     """
00623     A method of an interface, defining its associated parameters
00624     and return value.
00625     (MethodDescriptor from the typelib specification.)
00626     
00627     """
00628     _descriptorstart = struct.Struct(">BIB")
00629 
00630     def __init__(self, name, result,
00631                  params=[], getter=False, setter=False, notxpcom=False,
00632                  constructor=False, hidden=False, optargc=False,
00633                  implicit_jscontext=False):
00634         self.name = name
00635         self._name_offset = 0
00636         self.getter = getter
00637         self.setter = setter
00638         self.notxpcom = notxpcom
00639         self.constructor = constructor
00640         self.hidden = hidden
00641         self.optargc = optargc
00642         self.implicit_jscontext = implicit_jscontext
00643         self.params = list(params)
00644         if result and not isinstance(result, Param):
00645             raise Exception("result must be a Param!")
00646         self.result = result
00647 
00648     def read_params(self, typelib, map, data_pool, offset, num_args):
00649         """
00650         Read |num_args| ParamDescriptors representing this Method's arguments
00651         from the mmaped file |map| with data pool at the offset |data_pool|,
00652         starting at |offset| into self.params. Returns the offset
00653         suitable for reading the data following the ParamDescriptor array.
00654         
00655         """
00656         for i in range(num_args):
00657             p, offset = Param.read(typelib, map, data_pool, offset)
00658             self.params.append(p)
00659         return offset
00660 
00661     def read_result(self, typelib, map, data_pool, offset):
00662         """
00663         Read a ParamDescriptor representing this Method's return type
00664         from the mmaped file |map| with data pool at the offset |data_pool|,
00665         starting at |offset| into self.result. Returns the offset
00666         suitable for reading the data following the ParamDescriptor.
00667         
00668         """
00669         self.result, offset = Param.read(typelib, map, data_pool, offset)
00670         return offset
00671 
00672     @staticmethod
00673     def decodeflags(byte):
00674         """
00675         Given |byte|, an unsigned uint8 containing flag bits,
00676         decode the flag bits as described in
00677         http://www.mozilla.org/scriptable/typelib_file.html#MethodDescriptor
00678         and return a dict of flagname: (True|False) suitable
00679         for passing to Method.__init__ as **kwargs
00680         
00681         """
00682         return {'getter': bool(byte & 0x80),
00683                 'setter': bool(byte & 0x40),
00684                 'notxpcom': bool(byte & 0x20),
00685                 'constructor': bool(byte & 0x10),
00686                 'hidden': bool(byte & 0x08),
00687                 # Not in the spec, see
00688                 # http://hg.mozilla.org/mozilla-central/annotate/0e0e2516f04e/xpcom/typelib/xpt/public/xpt_struct.h#l489
00689                 'optargc': bool(byte & 0x04),
00690                 'implicit_jscontext': bool(byte & 0x02),
00691                 }
00692 
00693     def encodeflags(self):
00694         """
00695         Encode the flags of this Method object, return a byte suitable
00696         for writing to a typelib file.
00697 
00698         """
00699         flags = 0
00700         if self.getter:
00701             flags |= 0x80
00702         if self.setter:
00703             flags |= 0x40
00704         if self.notxpcom:
00705             flags |= 0x20
00706         if self.constructor:
00707             flags |= 0x10
00708         if self.hidden:
00709             flags |= 0x08
00710         if self.optargc:
00711             flags |= 0x04
00712         if self.implicit_jscontext:
00713             flags |= 0x02
00714         return flags
00715 
00716     @staticmethod
00717     def read(typelib, map, data_pool, offset):
00718         """
00719         Read a MethodDescriptor at |offset| from the mmaped file |map| with
00720         data pool offset |data_pool|. Returns (Method, next offset),
00721         where |next offset| is an offset suitable for reading the data
00722         following this MethodDescriptor.
00723         
00724         """
00725         start = data_pool + offset - 1
00726         flags, name_offset, num_args = Method._descriptorstart.unpack(map[start:start + Method._descriptorstart.size])
00727         # only the first seven bits are flags
00728         flags &= 0xFE
00729         flags = Method.decodeflags(flags)
00730         name = Typelib.read_string(map, data_pool, name_offset)
00731         m = Method(name, None, **flags)
00732         offset += Method._descriptorstart.size
00733         offset = m.read_params(typelib, map, data_pool, offset, num_args)
00734         offset = m.read_result(typelib, map, data_pool, offset)
00735         return m, offset
00736 
00737     def write(self, typelib, file):
00738         """
00739         Write a MethodDescriptor to |file|, which is assumed to be
00740         seeked to the right position.
00741 
00742         """
00743         file.write(Method._descriptorstart.pack(self.encodeflags(),
00744                                                 self._name_offset,
00745                                                 len(self.params)))
00746         for p in self.params:
00747             p.write(typelib, file)
00748         self.result.write(typelib, file)
00749 
00750     def write_name(self, file, data_pool_offset):
00751         """
00752         Write this method's name to |file|.
00753         Assumes that |file| is currently seeked to an unused portion
00754         of the data pool.
00755 
00756         """
00757         if self.name:
00758             self._name_offset = file.tell() - data_pool_offset + 1
00759             file.write(self.name + "\x00")
00760         else:
00761             self._name_offset = 0
00762 
00763 class Constant(object):
00764     """
00765     A constant value of a specific type defined on an interface.
00766     (ConstantDesciptor from the typelib specification.)
00767 
00768     """
00769     _descriptorstart = struct.Struct(">I")
00770     # Actual value is restricted to this set of types
00771     #XXX: the spec lies, the source allows a bunch more
00772     # http://hg.mozilla.org/mozilla-central/annotate/9c85f9aaec8c/xpcom/typelib/xpt/src/xpt_struct.c#l689
00773     typemap = {Type.Tags.int16: '>h',
00774                Type.Tags.uint16: '>H',
00775                Type.Tags.int32: '>i',
00776                Type.Tags.uint32: '>I'}
00777 
00778     def __init__(self, name, type, value):
00779         self.name = name
00780         self._name_offset = 0
00781         self.type = type
00782         self.value = value
00783 
00784     @staticmethod
00785     def read(typelib, map, data_pool, offset):
00786         """
00787         Read a ConstDescriptor at |offset| from the mmaped file |map| with
00788         data pool offset |data_pool|. Returns (Constant, next offset),
00789         where |next offset| is an offset suitable for reading the data
00790         following this ConstDescriptor.
00791         
00792         """
00793         start = data_pool + offset - 1
00794         (name_offset,) = Constant._descriptorstart.unpack(map[start:start + Constant._descriptorstart.size])
00795         name = Typelib.read_string(map, data_pool, name_offset)
00796         offset += Constant._descriptorstart.size
00797         # Read TypeDescriptor
00798         t, offset = Type.read(typelib, map, data_pool, offset)
00799         c = None
00800         if isinstance(t, SimpleType) and t.tag in Constant.typemap:
00801             tt = Constant.typemap[t.tag]
00802             start = data_pool + offset - 1
00803             (val,) = struct.unpack(tt, map[start:start + struct.calcsize(tt)])
00804             offset += struct.calcsize(tt)
00805             c = Constant(name, t, val)
00806         return c, offset
00807 
00808     def write(self, typelib, file):
00809         """
00810         Write a ConstDescriptor to |file|, which is assumed
00811         to be seeked to the proper position.
00812 
00813         """
00814         file.write(Constant._descriptorstart.pack(self._name_offset))
00815         self.type.write(typelib, file)
00816         tt = Constant.typemap[self.type.tag]
00817         file.write(struct.pack(tt, self.value))
00818 
00819     def write_name(self, file, data_pool_offset):
00820         """
00821         Write this constants's name to |file|.
00822         Assumes that |file| is currently seeked to an unused portion
00823         of the data pool.
00824 
00825         """
00826         if self.name:
00827             self._name_offset = file.tell() - data_pool_offset + 1
00828             file.write(self.name + "\x00")
00829         else:
00830             self._name_offset = 0
00831 
00832     def __repr__(self):
00833         return "Constant(%s, %s, %d)" % (self.name, str(self.type), self.value)
00834 
00835 class Interface(object):
00836     """
00837     An Interface represents an object, with its associated methods
00838     and constant values.
00839     (InterfaceDescriptor from the typelib specification.)
00840     
00841     """
00842     _direntry = struct.Struct(">16sIII")
00843     _descriptorstart = struct.Struct(">HH")
00844 
00845     UNRESOLVED_IID = "00000000-0000-0000-0000-000000000000"
00846 
00847     def __init__(self, name, iid=UNRESOLVED_IID, namespace="",
00848                  resolved=False, parent=None, methods=[], constants=[],
00849                  scriptable=False, function=False, builtinclass=False):
00850         self.resolved = resolved
00851         #TODO: should validate IIDs!
00852         self.iid = iid
00853         self.name = name
00854         self.namespace = namespace
00855         # if unresolved, all the members following this are unusable
00856         self.parent = parent
00857         self.methods = list(methods)
00858         self.constants = list(constants)
00859         self.scriptable = scriptable
00860         self.function = function
00861         self.builtinclass = builtinclass
00862         # For sanity, if someone constructs an Interface and passes
00863         # in methods or constants, then it's resolved.
00864         if self.methods or self.constants:
00865             # make sure it has a valid IID
00866             if self.iid == Interface.UNRESOLVED_IID:
00867                 raise DataError, "Cannot instantiate Interface %s containing methods or constants with an unresolved IID" % self.name
00868             self.resolved = True
00869         # These are only used for writing out the interface
00870         self._descriptor_offset = 0
00871         self._name_offset = 0
00872         self._namespace_offset = 0
00873         self.xpt_filename = None
00874 
00875     def __repr__(self):
00876         return "Interface('%s', '%s', '%s', methods=%s)" % (self.name, self.iid, self.namespace, self.methods)
00877 
00878     def __str__(self):
00879         return "Interface(name='%s', iid='%s')" % (self.name, self.iid)
00880 
00881     def __cmp__(self, other):
00882         c = cmp(self.iid, other.iid)
00883         if c != 0:
00884             return c
00885         c = cmp(self.name, other.name)
00886         if c != 0:
00887             return c
00888         c = cmp(self.namespace, other.namespace)
00889         if c != 0:
00890             return c
00891         # names and IIDs are the same, check resolved
00892         if self.resolved != other.resolved:
00893             if self.resolved:
00894                 return -1
00895             else:
00896                 return 1
00897         else:
00898             # both unresolved, but names and IIDs are the same, so equal
00899             return 0
00900         #TODO: actually compare methods etc
00901         return 0
00902 
00903     def read_descriptor(self, typelib, map, data_pool):
00904         offset = self._descriptor_offset
00905         if offset == 0:
00906             return
00907         start = data_pool + offset - 1
00908         parent, num_methods = Interface._descriptorstart.unpack(map[start:start + Interface._descriptorstart.size])
00909         if parent > 0 and parent <= len(typelib.interfaces):
00910             self.parent = typelib.interfaces[parent - 1]
00911         # Read methods
00912         offset += Interface._descriptorstart.size
00913         for i in range(num_methods):
00914             m, offset = Method.read(typelib, map, data_pool, offset)
00915             self.methods.append(m)
00916         # Read constants
00917         start = data_pool + offset - 1
00918         (num_constants, ) = struct.unpack(">H", map[start:start + struct.calcsize(">H")])
00919         offset = offset + struct.calcsize(">H")
00920         for i in range(num_constants):
00921             c, offset = Constant.read(typelib, map, data_pool, offset)
00922             self.constants.append(c)
00923         # Read flags
00924         start = data_pool + offset - 1
00925         (flags, ) = struct.unpack(">B", map[start:start + struct.calcsize(">B")])
00926         offset = offset + struct.calcsize(">B")
00927         # only the first two bits are flags
00928         flags &= 0xE0
00929         if flags & 0x80:
00930             self.scriptable = True
00931         if flags & 0x40:
00932             self.function = True
00933         if flags & 0x20:
00934             self.builtinclass = True
00935         self.resolved = True
00936 
00937     def write_directory_entry(self, file):
00938         """
00939         Write an InterfaceDirectoryEntry for this interface
00940         to |file|, which is assumed to be seeked to the correct offset.
00941 
00942         """
00943         file.write(Interface._direntry.pack(Typelib.string_to_iid(self.iid),
00944                                             self._name_offset,
00945                                             self._namespace_offset,
00946                                             self._descriptor_offset))
00947 
00948     def write(self, typelib, file, data_pool_offset):
00949         """
00950         Write an InterfaceDescriptor to |file|, which is assumed
00951         to be seeked to the proper position. If this interface
00952         is not resolved, do not write any data.
00953 
00954         """
00955         if not self.resolved:
00956             self._descriptor_offset = 0
00957             return
00958         self._descriptor_offset = file.tell() - data_pool_offset + 1
00959         parent_idx = 0
00960         if self.parent:
00961             parent_idx = typelib.interfaces.index(self.parent) + 1
00962         file.write(Interface._descriptorstart.pack(parent_idx, len(self.methods)))
00963         for m in self.methods:
00964             m.write(typelib, file)
00965         file.write(struct.pack(">H", len(self.constants)))
00966         for c in self.constants:
00967             c.write(typelib, file)
00968         flags = 0
00969         if self.scriptable:
00970             flags |= 0x80
00971         if self.function:
00972             flags |= 0x40
00973         if self.builtinclass:
00974             flags |= 0x20
00975         file.write(struct.pack(">B", flags))
00976 
00977     def write_names(self, file, data_pool_offset):
00978         """
00979         Write this interface's name and namespace to |file|,
00980         as well as the names of all of its methods and constants.
00981         Assumes that |file| is currently seeked to an unused portion
00982         of the data pool.
00983 
00984         """
00985         if self.name:
00986             self._name_offset = file.tell() - data_pool_offset + 1
00987             file.write(self.name + "\x00")
00988         else:
00989             self._name_offset = 0
00990         if self.namespace:
00991             self._namespace_offset = file.tell() - data_pool_offset + 1
00992             file.write(self.namespace + "\x00")
00993         else:
00994             self._namespace_offset = 0
00995         for m in self.methods:
00996             m.write_name(file, data_pool_offset)
00997         for c in self.constants:
00998             c.write_name(file, data_pool_offset)
00999 
01000 class Typelib(object):
01001     """
01002     A typelib represents one entire typelib file and all the interfaces
01003     referenced within, whether defined entirely within the typelib or
01004     merely referenced by name or IID.
01005 
01006     Typelib objects may be instantiated directly and populated with data,
01007     or the static Typelib.read method may be called to read one from a file.
01008 
01009     """
01010     _header = struct.Struct(">16sBBHIII")
01011 
01012     def __init__(self, version=TYPELIB_VERSION, interfaces=[], annotations=[]):
01013         """
01014         Instantiate a new Typelib.
01015 
01016         """
01017         self.version = version
01018         self.interfaces = list(interfaces)
01019         self.annotations = list(annotations)
01020         self.filename = None
01021 
01022     @staticmethod
01023     def iid_to_string(iid):
01024         """
01025         Convert a 16-byte IID into a UUID string.
01026 
01027         """
01028         def hexify(s):
01029             return ''.join(["%02x" % ord(x) for x in s])
01030 
01031         return "%s-%s-%s-%s-%s" % (hexify(iid[:4]), hexify(iid[4:6]),
01032                                    hexify(iid[6:8]), hexify(iid[8:10]),
01033                                    hexify(iid[10:]))
01034 
01035     @staticmethod
01036     def string_to_iid(iid_str):
01037         """
01038         Convert a UUID string into a 16-byte IID.
01039 
01040         """
01041         s = iid_str.replace('-','')
01042         return ''.join([chr(int(s[i:i+2], 16)) for i in range(0, len(s), 2)])
01043 
01044     @staticmethod
01045     def read_string(map, data_pool, offset):
01046         if offset == 0:
01047             return ""
01048         sz = map.find('\x00', data_pool + offset - 1)
01049         if sz == -1:
01050             return ""
01051         return map[data_pool + offset - 1:sz]
01052 
01053     @staticmethod
01054     def read(filename):
01055         """
01056         Read a typelib from the file named |filename| and return
01057         the constructed Typelib object.
01058 
01059         """
01060         with open(filename, "r+b") as f:
01061             st = os.fstat(f.fileno())
01062             map = f.read(st.st_size)
01063             data = Typelib._header.unpack(map[:Typelib._header.size])
01064             if data[0] != XPT_MAGIC:
01065                 raise FileFormatError, "Bad magic: %s" % data[0]
01066             xpt = Typelib((data[1], data[2]))
01067             xpt.filename = filename
01068             num_interfaces = data[3]
01069             file_length = data[4]
01070             if file_length != st.st_size:
01071                 raise FileFormatError, "File is of wrong length, got %d bytes, expected %d" % (st.st_size, file_length)
01072             #XXX: by spec this is a zero-based file offset. however,
01073             # the xpt_xdr code always subtracts 1 from data offsets
01074             # (because that's what you do in the data pool) so it
01075             # winds up accidentally treating this as 1-based.
01076             # Filed as: https://bugzilla.mozilla.org/show_bug.cgi?id=575343
01077             interface_directory_offset = data[5] - 1
01078             data_pool_offset = data[6]
01079             # make a half-hearted attempt to read Annotations,
01080             # since XPIDL doesn't produce any anyway.
01081             start = Typelib._header.size
01082             (anno, ) = struct.unpack(">B", map[start:start + struct.calcsize(">B")])
01083             islast = anno & 0x80
01084             tag = anno & 0x7F
01085             if tag == 0: # EmptyAnnotation
01086                 xpt.annotations.append(None)
01087             # We don't bother handling PrivateAnnotations or anything
01088             
01089             for i in range(num_interfaces):
01090                 # iid, name, namespace, interface_descriptor
01091                 start = interface_directory_offset + i * Interface._direntry.size
01092                 end = interface_directory_offset + (i+1) * Interface._direntry.size
01093                 ide = Interface._direntry.unpack(map[start:end])
01094                 iid = Typelib.iid_to_string(ide[0])
01095                 name = Typelib.read_string(map, data_pool_offset, ide[1])
01096                 namespace = Typelib.read_string(map, data_pool_offset, ide[2])
01097                 iface = Interface(name, iid, namespace)
01098                 iface._descriptor_offset = ide[3]
01099                 iface.xpt_filename = xpt.filename
01100                 xpt.interfaces.append(iface)
01101             for iface in xpt.interfaces:
01102                 iface.read_descriptor(xpt, map, data_pool_offset)
01103         return xpt
01104 
01105     def __repr__(self):
01106         return "<Typelib with %d interfaces>" % len(self.interfaces)
01107 
01108     def _sanityCheck(self):
01109         """
01110         Check certain assumptions about data contained in this typelib.
01111         Sort the interfaces array by IID, check that all interfaces
01112         referenced by methods exist in the array.
01113 
01114         """
01115         self.interfaces.sort()
01116         for i in self.interfaces:
01117             if i.parent and i.parent not in self.interfaces:
01118                 raise DataError, "Interface %s has parent %s not present in typelib!" % (i.name, i.parent.name)
01119             for m in i.methods:
01120                 for n, p in enumerate(m.params):
01121                     if isinstance(p, InterfaceType) and \
01122                         p.iface not in self.interfaces:
01123                         raise DataError, "Interface method %s::%s, parameter %d references interface %s not present in typelib!" % (i.name, m.name, n, p.iface.name)
01124                 if isinstance(m.result, InterfaceType) and m.result.iface not in self.interfaces:
01125                     raise DataError, "Interface method %s::%s, result references interface %s not present in typelib!" % (i.name, m.name, m.result.iface.name)
01126 
01127     def writefd(self, fd):
01128         # write out space for a header + one empty annotation,
01129         # padded to 4-byte alignment.
01130         headersize = (Typelib._header.size + 1)
01131         if headersize % 4:
01132             headersize += 4 - headersize % 4
01133         fd.write("\x00" * headersize)
01134         # save this offset, it's the interface directory offset.
01135         interface_directory_offset = fd.tell()
01136         # write out space for an interface directory
01137         fd.write("\x00" * Interface._direntry.size * len(self.interfaces))
01138         # save this offset, it's the data pool offset.
01139         data_pool_offset = fd.tell()
01140         # write out all the interface descriptors to the data pool
01141         for i in self.interfaces:
01142             i.write_names(fd, data_pool_offset)
01143             i.write(self, fd, data_pool_offset)
01144         # now, seek back and write the header
01145         file_len = fd.tell()
01146         fd.seek(0)
01147         fd.write(Typelib._header.pack(XPT_MAGIC,
01148                                       TYPELIB_VERSION[0],
01149                                       TYPELIB_VERSION[1],
01150                                       len(self.interfaces),
01151                                       file_len,
01152                                       interface_directory_offset,
01153                                       data_pool_offset))
01154         # write an empty annotation
01155         fd.write(struct.pack(">B", 0x80))
01156         # now write the interface directory
01157         #XXX: bug-compatible with existing xpt lib, put it one byte
01158         # ahead of where it's supposed to be.
01159         fd.seek(interface_directory_offset - 1)
01160         for i in self.interfaces:
01161             i.write_directory_entry(fd)
01162 
01163     def write(self, filename):
01164         """
01165         Write the contents of this typelib to the file named |filename|.
01166 
01167         """
01168         self._sanityCheck()
01169         with open(filename, "wb") as f:
01170             self.writefd(f)
01171 
01172     def merge(self, other, sanitycheck=True):
01173         """
01174         Merge the contents of Typelib |other| into this typelib.
01175         If |sanitycheck| is False, don't sort the interface table
01176         after merging.
01177 
01178         """
01179         # This will be a list of (replaced interface, replaced with)
01180         # containing interfaces that were replaced with interfaces from
01181         # another typelib, and the interface that replaced them.
01182         merged_interfaces = []
01183         for i in other.interfaces:
01184             if i in self.interfaces:
01185                 continue
01186             # See if there's a copy of this interface with different
01187             # resolved status or IID value.
01188             merged = False
01189             for j in self.interfaces:
01190                 if i.name == j.name:
01191                     if i.resolved != j.resolved:
01192                         # prefer resolved interfaces over unresolved
01193                         if j.resolved:
01194                             # keep j
01195                             merged_interfaces.append((i, j))
01196                             merged = True
01197                             # Fixup will happen after processing all interfaces.
01198                         else:
01199                             # replace j with i
01200                             merged_interfaces.append((j, i))
01201                             merged = True
01202                             self.interfaces[self.interfaces.index(j)] = i
01203                     elif i.iid != j.iid:
01204                         # Prefer unresolved interfaces with valid IIDs
01205                         if j.iid == Interface.UNRESOLVED_IID:
01206                             # replace j with i
01207                             merged_interfaces.append((j, i))
01208                             merged = True
01209                             self.interfaces[self.interfaces.index(j)] = i
01210                         elif i.iid == Interface.UNRESOLVED_IID:
01211                             # keep j
01212                             merged_interfaces.append((i, j))
01213                             merged = True
01214                             # Fixup will happen after processing all interfaces.
01215                         else:
01216                             # Same name but different IIDs: raise an exception.
01217                             # self.* is the (target) Typelib being merged into,
01218                             #   not the one which j.iid was from.
01219                             raise DataError, \
01220                                   "Typelibs contain definitions of interface %s" \
01221                                     " with different IIDs (%s (%s) vs %s (%s))!" % \
01222                                     (i.name, i.iid, i.xpt_filename or other.filename, \
01223                                              j.iid, j.xpt_filename or self.filename)
01224                 elif i.iid == j.iid and i.iid != Interface.UNRESOLVED_IID:
01225                     # Same IID but different names: raise an exception.
01226                     # self.* is the (target) Typelib being merged into,
01227                     #   not the one which j.name was from.
01228                     raise DataError, \
01229                           "Typelibs contain definitions of interface %s" \
01230                             " with different names (%s (%s) vs %s (%s))!" % \
01231                             (i.iid, i.name, i.xpt_filename or other.filename, \
01232                                     j.name, j.xpt_filename or self.filename)
01233             if not merged:
01234                 # No partially matching interfaces, so just take this interface
01235                 self.interfaces.append(i)
01236 
01237         # Now fixup any merged interfaces
01238         def checkType(t, replaced_from, replaced_to):
01239             if isinstance(t, InterfaceType) and t.iface == replaced_from:
01240                 t.iface = replaced_to
01241             elif isinstance(t, ArrayType) and \
01242                  isinstance(t.element_type, InterfaceType) and \
01243                  t.element_type.iface == replaced_from:
01244                 t.element_type.iface = replaced_to
01245 
01246         for replaced_from, replaced_to in merged_interfaces:
01247             for i in self.interfaces:
01248                 # Replace parent references
01249                 if i.parent is not None and i.parent == replaced_from:
01250                     i.parent = replaced_to
01251                 for m in i.methods:
01252                     # Replace InterfaceType params and return values
01253                     checkType(m.result.type, replaced_from, replaced_to)
01254                     for p in m.params:
01255                         checkType(p.type, replaced_from, replaced_to)
01256         if sanitycheck:
01257             self._sanityCheck()
01258         #TODO: do we care about annotations? probably not
01259 
01260     def dump(self, out):
01261         """
01262         Print a human-readable listing of the contents of this typelib
01263         to |out|, in the format of xpt_dump.
01264         
01265         """
01266         out.write("""Header:
01267    Major version:         %d
01268    Minor version:         %d
01269    Number of interfaces:  %d
01270    Annotations:\n""" % (self.version[0], self.version[1], len(self.interfaces)))
01271         for i, a in enumerate(self.annotations):
01272             if a is None:
01273                 out.write("      Annotation #%d is empty.\n" % i)
01274         out.write("\nInterface Directory:\n")
01275         for i in self.interfaces:
01276             out.write("   - %s::%s (%s):\n" % (i.namespace, i.name, i.iid))
01277             if not i.resolved:
01278                 out.write("      [Unresolved]\n")
01279             else:
01280                 if i.parent:
01281                     out.write("      Parent: %s::%s\n" % (i.parent.namespace,
01282                                                     i.parent.name))
01283                 out.write("""      Flags:
01284          Scriptable: %s
01285          BuiltinClass: %s
01286          Function: %s\n""" % (i.scriptable and "TRUE" or "FALSE",
01287                               i.builtinclass and "TRUE" or "FALSE",
01288                               i.function and "TRUE" or "FALSE"))
01289                 out.write("      Methods:\n")
01290                 if len(i.methods) == 0:
01291                     out.write("         No Methods\n")
01292                 else:
01293                     for m in i.methods:
01294                         out.write("   %s%s%s%s%s%s%s %s %s(%s);\n" % (
01295                             m.getter and "G" or " ",
01296                             m.setter and "S" or " ",
01297                             m.hidden and "H" or " ",
01298                             m.notxpcom and "N" or " ",
01299                             m.constructor and "C" or " ",
01300                             m.optargc and "O" or " ",
01301                             m.implicit_jscontext and "J" or " ",
01302                             str(m.result.type),
01303                             m.name,
01304                             m.params and ", ".join(str(p) for p in m.params) or ""
01305                             ))
01306                 out.write("      Constants:\n")
01307                 if len(i.constants) == 0:
01308                     out.write("         No Constants\n")
01309                 else:
01310                     for c in i.constants:
01311                         out.write("         %s %s = %d;\n" % (c.type, c.name, c.value))
01312 
01313 def xpt_dump(file):
01314     """
01315     Dump the contents of |file| to stdout in the format of xpt_dump.
01316 
01317     """
01318     t = Typelib.read(file)
01319     t.dump(sys.stdout)
01320 
01321 def xpt_link(dest, inputs):
01322     """
01323     Link all of the xpt files in |inputs| together and write the
01324     result ot |dest|.
01325 
01326     """
01327     if not inputs:
01328         print >>sys.stderr, "Usage: xpt_link <destination file> <input files>"
01329         return
01330     t1 = Typelib.read(inputs[0])
01331     for f in inputs[1:]:
01332         t2 = Typelib.read(f)
01333         # write will call sanitycheck, so skip it here.
01334         t1.merge(t2, sanitycheck=False)
01335     t1.write(dest)
01336 
01337 if __name__ == '__main__':
01338     if len(sys.argv) < 3:
01339         print >>sys.stderr, "xpt <dump|link> <files>"
01340         sys.exit(1)
01341     if sys.argv[1] == 'dump':
01342         xpt_dump(sys.argv[2])
01343     elif sys.argv[1] == 'link':
01344         xpt_link(sys.argv[2], sys.argv[3:])