~ubuntu-branches/ubuntu/precise/enigmail/precise-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: 2012-11-12 16:36:01 UTC
  • mfrom: (0.12.15)
  • Revision ID: package-import@ubuntu.com-20121112163601-t8e8skdfi3ni9iqp
Tags: 2:1.4.6-0ubuntu0.12.04.1
* New upstream release v1.4.6
  - see LP: #1080212 for USN information
* Drop unneeded patches
  - remove debian/patches/correct-version-number.diff
  - remove debian/patches/dont_register_cids_multiple_times.diff
  - update debian/patches/series
* Support building in an objdir
  - update debian/rules

Show diffs side-by-side

added added

removed removed

Lines of Context:
43
43
and writing them from files.
44
44
 
45
45
The usable public interfaces are currently:
46
 
Typelib.read(filename) - read a typelib from a file on disk, return
47
 
                         a Typelib object.
 
46
Typelib.read(input_file) - read a typelib from a file on disk or file-like
 
47
                           object, return a Typelib object.
48
48
 
49
49
xpt_dump(filename)     - read a typelib from a file on disk, dump
50
50
                         the contents to stdout in a human-readable
68
68
from __future__ import with_statement
69
69
import os, sys
70
70
import struct
 
71
import operator
71
72
 
72
73
# header magic
73
74
XPT_MAGIC = "XPCOM\nTypeLib\r\n\x1a"
878
879
    def __str__(self):
879
880
        return "Interface(name='%s', iid='%s')" % (self.name, self.iid)
880
881
 
 
882
    def __hash__(self):
 
883
        return hash((self.name, self.iid))
 
884
 
881
885
    def __cmp__(self, other):
882
886
        c = cmp(self.iid, other.iid)
883
887
        if c != 0:
1051
1055
        return map[data_pool + offset - 1:sz]
1052
1056
 
1053
1057
    @staticmethod
1054
 
    def read(filename):
1055
 
        """
1056
 
        Read a typelib from the file named |filename| and return
1057
 
        the constructed Typelib object.
1058
 
 
1059
 
        """
1060
 
        with open(filename, "r+b") as f:
1061
 
            st = os.fstat(f.fileno())
1062
 
            map = f.read(st.st_size)
1063
 
            data = Typelib._header.unpack(map[:Typelib._header.size])
1064
 
            if data[0] != XPT_MAGIC:
1065
 
                raise FileFormatError, "Bad magic: %s" % data[0]
1066
 
            xpt = Typelib((data[1], data[2]))
1067
 
            xpt.filename = filename
1068
 
            num_interfaces = data[3]
1069
 
            file_length = data[4]
1070
 
            if file_length != st.st_size:
1071
 
                raise FileFormatError, "File is of wrong length, got %d bytes, expected %d" % (st.st_size, file_length)
1072
 
            #XXX: by spec this is a zero-based file offset. however,
1073
 
            # the xpt_xdr code always subtracts 1 from data offsets
1074
 
            # (because that's what you do in the data pool) so it
1075
 
            # winds up accidentally treating this as 1-based.
1076
 
            # Filed as: https://bugzilla.mozilla.org/show_bug.cgi?id=575343
1077
 
            interface_directory_offset = data[5] - 1
1078
 
            data_pool_offset = data[6]
1079
 
            # make a half-hearted attempt to read Annotations,
1080
 
            # since XPIDL doesn't produce any anyway.
1081
 
            start = Typelib._header.size
1082
 
            (anno, ) = struct.unpack(">B", map[start:start + struct.calcsize(">B")])
1083
 
            islast = anno & 0x80
1084
 
            tag = anno & 0x7F
1085
 
            if tag == 0: # EmptyAnnotation
1086
 
                xpt.annotations.append(None)
1087
 
            # We don't bother handling PrivateAnnotations or anything
1088
 
            
1089
 
            for i in range(num_interfaces):
1090
 
                # iid, name, namespace, interface_descriptor
1091
 
                start = interface_directory_offset + i * Interface._direntry.size
1092
 
                end = interface_directory_offset + (i+1) * Interface._direntry.size
1093
 
                ide = Interface._direntry.unpack(map[start:end])
1094
 
                iid = Typelib.iid_to_string(ide[0])
1095
 
                name = Typelib.read_string(map, data_pool_offset, ide[1])
1096
 
                namespace = Typelib.read_string(map, data_pool_offset, ide[2])
1097
 
                iface = Interface(name, iid, namespace)
1098
 
                iface._descriptor_offset = ide[3]
1099
 
                iface.xpt_filename = xpt.filename
1100
 
                xpt.interfaces.append(iface)
1101
 
            for iface in xpt.interfaces:
1102
 
                iface.read_descriptor(xpt, map, data_pool_offset)
 
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)
1103
1120
        return xpt
1104
1121
 
1105
1122
    def __repr__(self):
1160
1177
        for i in self.interfaces:
1161
1178
            i.write_directory_entry(fd)
1162
1179
 
1163
 
    def write(self, filename):
 
1180
    def write(self, output_file):
1164
1181
        """
1165
 
        Write the contents of this typelib to the file named |filename|.
 
1182
        Write the contents of this typelib to |output_file|,
 
1183
        which can be either a filename or a file-like object.
1166
1184
 
1167
1185
        """
1168
1186
        self._sanityCheck()
1169
 
        with open(filename, "wb") as f:
1170
 
            self.writefd(f)
1171
 
 
1172
 
    def merge(self, other, sanitycheck=True):
1173
 
        """
1174
 
        Merge the contents of Typelib |other| into this typelib.
1175
 
        If |sanitycheck| is False, don't sort the interface table
1176
 
        after merging.
1177
 
 
1178
 
        """
1179
 
        # This will be a list of (replaced interface, replaced with)
1180
 
        # containing interfaces that were replaced with interfaces from
1181
 
        # another typelib, and the interface that replaced them.
1182
 
        merged_interfaces = []
1183
 
        for i in other.interfaces:
1184
 
            if i in self.interfaces:
1185
 
                continue
1186
 
            # See if there's a copy of this interface with different
1187
 
            # resolved status or IID value.
1188
 
            merged = False
1189
 
            for j in self.interfaces:
1190
 
                if i.name == j.name:
1191
 
                    if i.resolved != j.resolved:
1192
 
                        # prefer resolved interfaces over unresolved
1193
 
                        if j.resolved:
1194
 
                            # keep j
1195
 
                            merged_interfaces.append((i, j))
1196
 
                            merged = True
1197
 
                            # Fixup will happen after processing all interfaces.
1198
 
                        else:
1199
 
                            # replace j with i
1200
 
                            merged_interfaces.append((j, i))
1201
 
                            merged = True
1202
 
                            self.interfaces[self.interfaces.index(j)] = i
1203
 
                    elif i.iid != j.iid:
1204
 
                        # Prefer unresolved interfaces with valid IIDs
1205
 
                        if j.iid == Interface.UNRESOLVED_IID:
1206
 
                            # replace j with i
1207
 
                            merged_interfaces.append((j, i))
1208
 
                            merged = True
1209
 
                            self.interfaces[self.interfaces.index(j)] = i
1210
 
                        elif i.iid == Interface.UNRESOLVED_IID:
1211
 
                            # keep j
1212
 
                            merged_interfaces.append((i, j))
1213
 
                            merged = True
1214
 
                            # Fixup will happen after processing all interfaces.
1215
 
                        else:
1216
 
                            # Same name but different IIDs: raise an exception.
1217
 
                            # self.* is the (target) Typelib being merged into,
1218
 
                            #   not the one which j.iid was from.
1219
 
                            raise DataError, \
1220
 
                                  "Typelibs contain definitions of interface %s" \
1221
 
                                    " with different IIDs (%s (%s) vs %s (%s))!" % \
1222
 
                                    (i.name, i.iid, i.xpt_filename or other.filename, \
1223
 
                                             j.iid, j.xpt_filename or self.filename)
1224
 
                elif i.iid == j.iid and i.iid != Interface.UNRESOLVED_IID:
1225
 
                    # Same IID but different names: raise an exception.
1226
 
                    # self.* is the (target) Typelib being merged into,
1227
 
                    #   not the one which j.name was from.
1228
 
                    raise DataError, \
1229
 
                          "Typelibs contain definitions of interface %s" \
1230
 
                            " with different names (%s (%s) vs %s (%s))!" % \
1231
 
                            (i.iid, i.name, i.xpt_filename or other.filename, \
1232
 
                                    j.name, j.xpt_filename or self.filename)
1233
 
            if not merged:
1234
 
                # No partially matching interfaces, so just take this interface
1235
 
                self.interfaces.append(i)
1236
 
 
1237
 
        # Now fixup any merged interfaces
1238
 
        def checkType(t, replaced_from, replaced_to):
1239
 
            if isinstance(t, InterfaceType) and t.iface == replaced_from:
1240
 
                t.iface = replaced_to
1241
 
            elif isinstance(t, ArrayType) and \
1242
 
                 isinstance(t.element_type, InterfaceType) and \
1243
 
                 t.element_type.iface == replaced_from:
1244
 
                t.element_type.iface = replaced_to
1245
 
 
1246
 
        for replaced_from, replaced_to in merged_interfaces:
1247
 
            for i in self.interfaces:
1248
 
                # Replace parent references
1249
 
                if i.parent is not None and i.parent == replaced_from:
1250
 
                    i.parent = replaced_to
1251
 
                for m in i.methods:
1252
 
                    # Replace InterfaceType params and return values
1253
 
                    checkType(m.result.type, replaced_from, replaced_to)
1254
 
                    for p in m.params:
1255
 
                        checkType(p.type, replaced_from, replaced_to)
1256
 
        if sanitycheck:
1257
 
            self._sanityCheck()
1258
 
        #TODO: do we care about annotations? probably not
 
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)
1259
1192
 
1260
1193
    def dump(self, out):
1261
1194
        """
1318
1251
    t = Typelib.read(file)
1319
1252
    t.dump(sys.stdout)
1320
1253
 
1321
 
def xpt_link(dest, inputs):
1322
 
    """
1323
 
    Link all of the xpt files in |inputs| together and write the
1324
 
    result ot |dest|.
1325
 
 
1326
 
    """
 
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
 
1327
1266
    if not inputs:
1328
1267
        print >>sys.stderr, "Usage: xpt_link <destination file> <input files>"
1329
 
        return
1330
 
    t1 = Typelib.read(inputs[0])
1331
 
    for f in inputs[1:]:
1332
 
        t2 = Typelib.read(f)
1333
 
        # write will call sanitycheck, so skip it here.
1334
 
        t1.merge(t2, sanitycheck=False)
1335
 
    t1.write(dest)
 
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)
1336
1374
 
1337
1375
if __name__ == '__main__':
1338
1376
    if len(sys.argv) < 3:
1341
1379
    if sys.argv[1] == 'dump':
1342
1380
        xpt_dump(sys.argv[2])
1343
1381
    elif sys.argv[1] == 'link':
1344
 
        xpt_link(sys.argv[2], sys.argv[3:])
 
1382
        xpt_link(sys.argv[3:]).write(sys.argv[2])
 
1383