1051
1055
return map[data_pool + offset - 1:sz]
1056
Read a typelib from the file named |filename| and return
1057
the constructed Typelib object.
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
1085
if tag == 0: # EmptyAnnotation
1086
xpt.annotations.append(None)
1087
# We don't bother handling PrivateAnnotations or anything
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):
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.
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
1075
data = input_file.read()
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
1102
if tag == 0: # EmptyAnnotation
1103
xpt.annotations.append(None)
1104
# We don't bother handling PrivateAnnotations or anything
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)
1105
1122
def __repr__(self):
1160
1177
for i in self.interfaces:
1161
1178
i.write_directory_entry(fd)
1163
def write(self, filename):
1180
def write(self, output_file):
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.
1168
1186
self._sanityCheck()
1169
with open(filename, "wb") as f:
1172
def merge(self, other, sanitycheck=True):
1174
Merge the contents of Typelib |other| into this typelib.
1175
If |sanitycheck| is False, don't sort the interface table
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:
1186
# See if there's a copy of this interface with different
1187
# resolved status or IID value.
1189
for j in self.interfaces:
1190
if i.name == j.name:
1191
if i.resolved != j.resolved:
1192
# prefer resolved interfaces over unresolved
1195
merged_interfaces.append((i, j))
1197
# Fixup will happen after processing all interfaces.
1200
merged_interfaces.append((j, i))
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:
1207
merged_interfaces.append((j, i))
1209
self.interfaces[self.interfaces.index(j)] = i
1210
elif i.iid == Interface.UNRESOLVED_IID:
1212
merged_interfaces.append((i, j))
1214
# Fixup will happen after processing all interfaces.
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.
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.
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)
1234
# No partially matching interfaces, so just take this interface
1235
self.interfaces.append(i)
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
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
1252
# Replace InterfaceType params and return values
1253
checkType(m.result.type, replaced_from, replaced_to)
1255
checkType(p.type, replaced_from, replaced_to)
1258
#TODO: do we care about annotations? probably not
1187
if isinstance(output_file, basestring):
1188
with open(output_file, "wb") as f:
1191
self.writefd(output_file)
1260
1193
def dump(self, out):
1318
1251
t = Typelib.read(file)
1319
1252
t.dump(sys.stdout)
1321
def xpt_link(dest, inputs):
1323
Link all of the xpt files in |inputs| together and write the
1254
def xpt_link(inputs):
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
1262
if isinstance(i, Typelib):
1264
return Typelib.read(i)
1328
1267
print >>sys.stderr, "Usage: xpt_link <destination file> <input files>"
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)
1269
# This is the aggregate list of 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 = {}
1277
interfaces.extend(t.interfaces)
1278
# Sort interfaces by name so we can merge adjacent duplicates
1279
interfaces.sort(key=operator.attrgetter('name'))
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
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).
1294
# Arbitrary, just pick one
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.
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
1310
assert i.iid == j.iid or i.iid == Interface.UNRESOLVED_IID
1312
return Result.KeepSecond
1314
assert i.iid == j.iid or j.iid == Interface.UNRESOLVED_IID
1316
return Result.KeepFirst
1317
elif i.iid != j.iid:
1318
# Prefer unresolved interfaces with valid IIDs
1319
if j.iid == Interface.UNRESOLVED_IID:
1321
assert not j.resolved
1322
return Result.KeepFirst
1323
elif i.iid == Interface.UNRESOLVED_IID:
1325
assert not i.resolved
1326
return Result.KeepSecond
1328
# Same name but different IIDs: raise an exception.
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)
1337
# Compare interfaces pairwise to find duplicates that should be merged.
1339
while i < len(interfaces):
1340
res = compare(interfaces[i-1], interfaces[i])
1341
if res == Result.NotEqual:
1343
elif res == Result.Equal:
1344
# Need to drop one but it doesn't matter which
1346
elif res == Result.KeepFirst:
1347
merged_interfaces[interfaces[i]] = interfaces[i-1]
1349
elif res == Result.KeepSecond:
1350
merged_interfaces[interfaces[i-1]] = interfaces[i]
1353
# Now fixup any merged interfaces
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]
1362
for i in interfaces:
1363
# Replace parent references
1364
if i.parent in merged_interfaces:
1365
i.parent = merged_interfaces[i.parent]
1367
# Replace InterfaceType params and return values
1368
checkType(m.result.type)
1371
# Re-sort interfaces (by IID)
1373
return Typelib(interfaces=interfaces)
1337
1375
if __name__ == '__main__':
1338
1376
if len(sys.argv) < 3: