|
|
|
#
|
|
|
|
# (C) Copyright 2000 by hartmut Goebel <hartmut@goebel.noris.de>
|
|
|
|
#
|
|
|
|
# byte-code verifier for decompyle
|
|
|
|
#
|
|
|
|
|
|
|
|
import types
|
|
|
|
import decompyle
|
|
|
|
|
|
|
|
#--- exceptions ---
|
|
|
|
|
|
|
|
class VerifyCmpError(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class CmpErrorConsts(VerifyCmpError):
|
|
|
|
"""Exception to be raised when consts differ."""
|
|
|
|
def __init__(self, name, index):
|
|
|
|
self.name = name
|
|
|
|
self.index = index
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return 'Compare Error within Consts of %s at index %i' % \
|
|
|
|
(repr(self.name), self.index)
|
|
|
|
|
|
|
|
class CmpErrorConstsLen(VerifyCmpError):
|
|
|
|
"""Exception to be raised when length of co_consts differs."""
|
|
|
|
def __init__(self, name, consts1, consts2):
|
|
|
|
self.name = name
|
|
|
|
self.consts = (consts1, consts2)
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return 'Consts length differs in %s:\n\n%i:\t%s\n\n%i:\t%s\n\n' % \
|
|
|
|
(repr(self.name),
|
|
|
|
len(self.consts[0]), `self.consts[0]`,
|
|
|
|
len(self.consts[1]), `self.consts[1]`)
|
|
|
|
|
|
|
|
class CmpErrorCode(VerifyCmpError):
|
|
|
|
"""Exception to be raised when code differs."""
|
|
|
|
def __init__(self, name, index, token1, token2):
|
|
|
|
self.name = name
|
|
|
|
self.index = index
|
|
|
|
self.token1 = token1
|
|
|
|
self.token2 = token2
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return 'Code differs in %s at offset %i [%s] != [%s]' % \
|
|
|
|
(repr(self.name), self.index,
|
|
|
|
repr(self.token1), repr(self.token2)) #\
|
|
|
|
# + ('%s %s') % (self.token1.pattr, self.token2.pattr)
|
|
|
|
|
|
|
|
class CmpErrorCodeLen(VerifyCmpError):
|
|
|
|
"""Exception to be raised when code length differs."""
|
|
|
|
def __init__(self, name, tokens1, tokens2):
|
|
|
|
self.name = name
|
|
|
|
self.tokens = [tokens1, tokens2]
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return reduce(lambda s,t: "%s%-37s\t%-37s\n" % (s, t[0], t[1]),
|
|
|
|
map(lambda a,b: (a,b),
|
|
|
|
self.tokens[0],
|
|
|
|
self.tokens[1]),
|
|
|
|
'Code len differs in %s\n' % str(self.name))
|
|
|
|
|
|
|
|
class CmpErrorMember(VerifyCmpError):
|
|
|
|
"""Exception to be raised when other members differ."""
|
|
|
|
def __init__(self, name, member, data1, data2):
|
|
|
|
self.name = name
|
|
|
|
self.member = member
|
|
|
|
self.data = (data1, data2)
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return 'Member %s differs in %s:\n\t%s\n\t%s\n' % \
|
|
|
|
(repr(self.member), repr(self.name),
|
|
|
|
repr(self.data[0]), repr(self.data[1]))
|
|
|
|
|
|
|
|
#--- compare ---
|
|
|
|
|
|
|
|
# these members are ignored
|
|
|
|
__IGNORE_CODE_MEMBERS__ = ['co_filename', 'co_firstlineno', 'co_lnotab']
|
|
|
|
|
|
|
|
def cmp_code_objects(code_obj1, code_obj2, name=''):
|
|
|
|
"""
|
|
|
|
Compare two code-objects.
|
|
|
|
|
|
|
|
This is the main part of this module.
|
|
|
|
"""
|
|
|
|
assert type(code_obj1) == types.CodeType
|
|
|
|
assert type(code_obj2) == types.CodeType
|
|
|
|
assert dir(code_obj1) == code_obj1.__members__
|
|
|
|
assert dir(code_obj2) == code_obj2.__members__
|
|
|
|
assert code_obj1.__members__ == code_obj2.__members__
|
|
|
|
if name == '__main__':
|
|
|
|
name = code_obj1.co_name
|
|
|
|
else:
|
|
|
|
name = '%s.%s' % (name, code_obj1.co_name)
|
|
|
|
if name == '.?': name = '__main__'
|
|
|
|
|
|
|
|
members = code_obj1.__members__; members.sort(); #members.reverse()
|
|
|
|
tokens1 = None
|
|
|
|
for member in members:
|
|
|
|
if member in __IGNORE_CODE_MEMBERS__:
|
|
|
|
pass
|
|
|
|
elif member == 'co_code':
|
|
|
|
# use changed Token class
|
|
|
|
__Token = decompyle.Token
|
|
|
|
decompyle.Token = Token
|
|
|
|
# tokenize both code-objects
|
|
|
|
tokens1, customize = decompyle._tokenize(None, code_obj1)
|
|
|
|
tokens2, customize = decompyle._tokenize(None, code_obj2)
|
|
|
|
del customize
|
|
|
|
decompyle.Token = __Token # restore Token class
|
|
|
|
|
|
|
|
# compare length
|
|
|
|
if len(tokens1) != len(tokens2):
|
|
|
|
raise CmpErrorCodeLen(name, tokens1, tokens2)
|
|
|
|
# compare contents
|
|
|
|
#print len(tokens1), type(tokens1), type(tokens2)
|
|
|
|
for i in xrange(len(tokens1)):
|
|
|
|
if tokens1[i] != tokens2[i]:
|
|
|
|
#print '-->', i, type(tokens1[i]), type(tokens2[i])
|
|
|
|
raise CmpErrorCode(name, i, tokens1[i],
|
|
|
|
tokens2[i])
|
|
|
|
elif member == 'co_consts':
|
|
|
|
# compare length
|
|
|
|
if len(code_obj1.co_consts) != len(code_obj2.co_consts):
|
|
|
|
raise CmpErrorConstsLen(name, code_obj1.co_consts ,code_obj2.co_consts)
|
|
|
|
# compare contents
|
|
|
|
for idx in xrange(len(code_obj1.co_consts)):
|
|
|
|
const1 = code_obj1.co_consts[idx]
|
|
|
|
const2 = code_obj2.co_consts[idx]
|
|
|
|
# same type?
|
|
|
|
if type(const1) != type(const2):
|
|
|
|
raise CmpErrorContType(name, idx)
|
|
|
|
if type(const1) == types.CodeType:
|
|
|
|
# code object -> recursive compare
|
|
|
|
cmp_code_objects(const1, const2,
|
|
|
|
name)
|
|
|
|
elif cmp(const1, const2) != 0:
|
|
|
|
# content differs
|
|
|
|
raise CmpErrorConsts(name, idx)
|
|
|
|
else:
|
|
|
|
# all other members must be equal
|
|
|
|
if eval('code_obj1.%s != code_obj2.%s' % (member, member)):
|
|
|
|
data1 = eval('code_obj1.%s' % member)
|
|
|
|
data2 = eval('code_obj2.%s' % member)
|
|
|
|
raise CmpErrorMember(name, member, data1,data2)
|
|
|
|
|
|
|
|
|
|
|
|
class Token(decompyle.Token):
|
|
|
|
"""Token class with changed semantics for 'cmp()'."""
|
|
|
|
|
|
|
|
def __cmp__(self, o):
|
|
|
|
if self.type in decompyle._JUMP_OPS_:
|
|
|
|
# ignore offset
|
|
|
|
return cmp(self.type, o.type)
|
|
|
|
else:
|
|
|
|
return cmp(self.type, o.type) \
|
|
|
|
or cmp(self.pattr, o.pattr)
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return '%s %s (%s)' % (str(self.type), str(self.attr),
|
|
|
|
str(self.pattr))
|
|
|
|
|
|
|
|
|
|
|
|
def compare_code_with_srcfile(pyc_filename, src_filename):
|
|
|
|
"""Compare a .pyc with a source code file."""
|
|
|
|
code_obj1 = decompyle._load_module(pyc_filename)
|
|
|
|
code_obj2 = decompyle._load_file(src_filename)
|
|
|
|
cmp_code_objects(code_obj1, code_obj2)
|
|
|
|
|
|
|
|
def compare_files(pyc_filename1, pyc_filename2):
|
|
|
|
"""Compare two .pyc files."""
|
|
|
|
code_obj1 = decompyle._load_module(pyc_filename1)
|
|
|
|
code_obj2 = decompyle._load_module(pyc_filename2)
|
|
|
|
cmp_code_objects(code_obj1, code_obj2)
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
t1 = Token('LOAD_CONST', None, 'code_object _expandLang', 52)
|
|
|
|
t2 = Token('LOAD_CONST', -421, 'code_object _expandLang', 55)
|
|
|
|
print `t1`
|
|
|
|
print `t2`
|
|
|
|
print cmp(t1, t2), cmp(t1.type, t2.type), cmp(t1.attr, t2.attr)
|