You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
5.5 KiB
182 lines
5.5 KiB
# |
|
# (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)
|
|
|