# # (C) Copyright 2000 by hartmut Goebel # # 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)