diff --git a/uncompyle-code.py b/uncompyle-code.py index 529afc52..345895c8 100755 --- a/uncompyle-code.py +++ b/uncompyle-code.py @@ -5,7 +5,12 @@ from __future__ import print_function import uncompyle6 from uncompyle6 import uncompyle, walker, verify, magics from uncompyle6.spark import GenericASTTraversal, GenericASTTraversalPruningException -import sys, inspect, types, cStringIO +import sys, inspect, types + +if (sys.version_info > (3, 0)): + from io import StringIO +else: + from StringIO import StringIO from collections import namedtuple NodeInfo = namedtuple("NodeInfo", "node start finish") @@ -120,7 +125,7 @@ class FindWalker(walker.Walker, object): self.pending_newlines = 0 self.__params = { '_globals': {}, - 'f': cStringIO.StringIO(), + 'f': StringIO(), 'indent': indent, 'isLambda': isLambda, } diff --git a/uncompyle6/__init__.py b/uncompyle6/__init__.py index 6d1da838..35e81c7f 100644 --- a/uncompyle6/__init__.py +++ b/uncompyle6/__init__.py @@ -100,7 +100,7 @@ def uncompyle(version, co, out=None, showasm=0, showast=0): file=__real_out) # diff scanner if version == 2.7: - import scanner27 as scan + import uncompyle6.scanner27 as scan scanner = scan.Scanner27() elif version == 2.6: import scanner26 as scan diff --git a/uncompyle6/disas.py b/uncompyle6/disas.py index f89efcc5..0080517c 100755 --- a/uncompyle6/disas.py +++ b/uncompyle6/disas.py @@ -1,191 +1,18 @@ +from __future__ import print_function + """Disassembler of Python byte code into mnemonics.""" -import sys -import types -from struct import unpack -import marshal, pickle +import marshal, pickle, sys, types + +import dis as Mdis + +from struct import unpack -_have_code = (types.MethodType, types.FunctionType, types.CodeType, types.ClassType, type) internStrings = [] -def dis(x=None): - """Disassemble classes, methods, functions, or code. - With no argument, disassemble the last traceback. - """ - if x is None: - distb() - return - if isinstance(x, types.InstanceType): - x = x.__class__ - if hasattr(x, 'im_func'): - x = x.im_func - if hasattr(x, 'func_code'): - x = x.func_code - if hasattr(x, '__dict__'): - items = x.__dict__.items() - items.sort() - for name, x1 in items: - if isinstance(x1, _have_code): - print "Disassembly of %s:" % name - try: - dis(x1) - except TypeError, msg: - print "Sorry:", msg - print - elif hasattr(x, 'co_code'): - disassemble(x) - elif isinstance(x, str): - disassemble_string(x) - else: - raise (TypeError, - "don't know how to disassemble %s objects" % type(x).__name__) - -def distb(tb=None): - """Disassemble a traceback (default: last traceback).""" - if tb is None: - try: - tb = sys.last_traceback - except AttributeError: - raise(RuntimeError, "no last traceback to disassemble") - while tb.tb_next: tb = tb.tb_next - disassemble(tb.tb_frame.f_code, tb.tb_lasti) - -def disassemble(co, lasti=-1): - """Disassemble a code object.""" - code = co.co_code - labels = findlabels(code) - linestarts = dict(findlinestarts(co)) - n = len(code) - i = 0 - extended_arg = 0 - free = None - while i < n: - c = code[i] - op = ord(c) - if i in linestarts: - if i > 0: - print - print "%3d" % linestarts[i], - else: - print ' ', - - if i == lasti: print '-->', - else: print ' ', - if i in labels: print '>>', - else: print ' ', - print repr(i).rjust(4), - print opname[op].ljust(20), - i = i+1 - if op >= HAVE_ARGUMENT: - oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg - extended_arg = 0 - i = i+2 - if op == EXTENDED_ARG: - extended_arg = oparg*65536L - print repr(oparg).rjust(5), - if op in hasconst: - print '(' + repr(co.co_consts[oparg]) + ')', - elif op in hasname: - print '(' + co.co_names[oparg] + ')', - elif op in hasjrel: - print '(to ' + repr(i + oparg) + ')', - elif op in haslocal: - print '(' + co.co_varnames[oparg] + ')', - elif op in hascompare: - print '(' + cmp_op[oparg] + ')', - elif op in hasfree: - if free is None: - free = co.co_cellvars + co.co_freevars - print '(' + free[oparg] + ')', - print - -def disassemble_string(code, lasti=-1, varnames=None, names=None, - constants=None): - labels = findlabels(code) - n = len(code) - i = 0 - while i < n: - c = code[i] - op = ord(c) - if i == lasti: print '-->', - else: print ' ', - if i in labels: print '>>', - else: print ' ', - print repr(i).rjust(4), - print opname[op].ljust(15), - i = i+1 - if op >= HAVE_ARGUMENT: - oparg = ord(code[i]) + ord(code[i+1])*256 - i = i+2 - print repr(oparg).rjust(5), - if op in hasconst: - if constants: - print '(' + repr(constants[oparg]) + ')', - else: - print '(%d)' % oparg, - elif op in hasname: - if names is not None: - print '(' + names[oparg] + ')', - else: - print '(%d)'%oparg, - elif op in hasjrel: - print '(to ' + repr(i + oparg) + ')', - elif op in haslocal: - if varnames: - print '(' + varnames[oparg] + ')', - else: - print '(%d)' % oparg, - elif op in hascompare: - print '(' + cmp_op[oparg] + ')', - print - -disco = disassemble +disco = Mdis.disassemble # XXX For backwards compatibility -def findlabels(code): - """Detect all offsets in a byte code which are jump targets. - Return the list of offsets. - """ - labels = [] - n = len(code) - i = 0 - while i < n: - c = code[i] - op = ord(c) - i = i+1 - if op >= HAVE_ARGUMENT: - oparg = ord(code[i]) + ord(code[i+1])*256 - i = i+2 - label = -1 - if op in hasjrel: - label = i+oparg - elif op in hasjabs: - label = oparg - if label >= 0: - if label not in labels: - labels.append(label) - return labels - -def findlinestarts(code): - """Find the offsets in a byte code which are start of lines in the source. - Generate pairs (offset, lineno) as described in Python/compile.c. - """ - byte_increments = [ord(c) for c in code.co_lnotab[0::2]] - line_increments = [ord(c) for c in code.co_lnotab[1::2]] - - lastlineno = None - lineno = code.co_firstlineno - addr = 0 - for byte_incr, line_incr in zip(byte_increments, line_increments): - if byte_incr: - if lineno != lastlineno: - yield (addr, lineno) - lastlineno = lineno - addr += byte_incr - lineno += line_incr - if lineno != lastlineno: - yield (addr, lineno) - def marshalLoad(fp): global internStrings internStrings = [] @@ -223,7 +50,7 @@ def load(fp): elif marshalType == '.': return Ellipsis elif marshalType == '0': - raise KeyError, marshalType + raise KeyError(marshalType) return None elif marshalType == 'N': return None @@ -244,20 +71,20 @@ def load(fp): elif marshalType == 'I': return unpack('q', fp.read(8))[0] elif marshalType == 'x': - raise KeyError, marshalType + raise KeyError(marshalType) return None elif marshalType == 'y': - raise KeyError, marshalType + raise KeyError(marshalType) return None elif marshalType == 'l': n = unpack('i', fp.read(4))[0] if n == 0: return long(0) - size = abs(n); + size = abs(n) d = long(0) for j in range(0, size): md = int(unpack('h', fp.read(2))[0]) - d += md << j*15; + d += md << j*15 if n < 0: return long(d*-1) return d @@ -286,16 +113,16 @@ def load(fp): tuplesize -= 1 return ret elif marshalType == '[': - raise KeyError, marshalType + raise KeyError(marshalType) return None elif marshalType == '{': - raise KeyError, marshalType + raise KeyError(marshalType) return None elif marshalType in ['<', '>']: - raise KeyError, marshalType + raise KeyError(marshalType) return None else: - sys.stderr.write("Unkown type %i (hex %x)\n" % (ord(marshalType), ord(marshalType))) + sys.stderr.write("Unknown type %i (hex %x)\n" % (ord(marshalType), ord(marshalType))) def _test(): """Simple test program to disassemble a file.""" @@ -318,7 +145,7 @@ def _test(): else: fn = "" code = compile(source, fn, "exec") - dis(code) + Mdis.dis(code) if __name__ == "__main__": _test() diff --git a/uncompyle6/scanner.py b/uncompyle6/scanner.py index 656fb1ea..617e3e44 100755 --- a/uncompyle6/scanner.py +++ b/uncompyle6/scanner.py @@ -9,13 +9,18 @@ from __future__ import print_function __all__ = ['Token', 'Scanner', 'Code'] -import types +import sys, types from collections import namedtuple from array import array from operator import itemgetter +if (sys.version_info > (3, 0)): + intern = sys.intern + import uncompyle6 + from uncompyle6.opcodes import opcode_25, opcode_26, opcode_27 + class Token: ''' Class representing a byte-code token. @@ -84,7 +89,7 @@ class Scanner(object): self.out = out def setTokenClass(self, tokenClass): - assert isinstance(tokenClass, types.ClassType) + # assert isinstance(tokenClass, types.ClassType) self.Token = tokenClass return self.Token diff --git a/uncompyle6/scanner25.py b/uncompyle6/scanner25.py index e700908f..2c6c7a48 100755 --- a/uncompyle6/scanner25.py +++ b/uncompyle6/scanner25.py @@ -13,7 +13,7 @@ from operator import itemgetter from struct import * from uncompyle6.opcodes.opcode_25 import * -import disas as dis +import dis import scanner as scan class Scanner25(scan.Scanner): diff --git a/uncompyle6/scanner26.py b/uncompyle6/scanner26.py index 3cebe9cf..f068b8da 100755 --- a/uncompyle6/scanner26.py +++ b/uncompyle6/scanner26.py @@ -13,7 +13,7 @@ from operator import itemgetter from struct import * from uncompyle6.opcodes.opcode_26 import * -import disas as dis +import dis import scanner as scan class Scanner26(scan.Scanner): diff --git a/uncompyle6/scanner27.py b/uncompyle6/scanner27.py index bcc82b31..403cd8da 100755 --- a/uncompyle6/scanner27.py +++ b/uncompyle6/scanner27.py @@ -1,3 +1,5 @@ +from __future__ import print_function + ''' Copyright (c) 1999 John Aycock Copyright (c) 2000-2002 by hartmut Goebel @@ -6,14 +8,13 @@ See main module for license. ''' -import types +import dis, types from collections import namedtuple from array import array from operator import itemgetter from uncompyle6.opcodes.opcode_27 import * -import disas as dis -import scanner as scan +import uncompyle6.scanner as scan class Scanner27(scan.Scanner): def __init__(self): @@ -121,7 +122,7 @@ class Scanner27(scan.Scanner): oparg = self.get_argument(offset) + extended_arg extended_arg = 0 if op == EXTENDED_ARG: - extended_arg = oparg * 65536L + extended_arg = oparg * 65536 continue if op in hasconst: const = co.co_consts[oparg] @@ -197,8 +198,8 @@ class Scanner27(scan.Scanner): if self.showasm: out = self.out # shortcut for t in rv: - print >>out, t - print >>out + print(t, file=out) + print(file=out) return rv, customize def op_size(self, op): diff --git a/uncompyle6/verify.py b/uncompyle6/verify.py index 51caeeae..585b3848 100755 --- a/uncompyle6/verify.py +++ b/uncompyle6/verify.py @@ -1,18 +1,25 @@ +from __future__ import print_function + # # (C) Copyright 2000-2002 by hartmut Goebel # # byte-code verifier for uncompyle # -import types -import operator -import dis -import uncompyle2, scanner +import dis, operator, sys, types + +import uncompyle6 +import uncompyle6.scanner as scanner + +if (sys.version_info > (3, 0)): + truediv = operator.truediv +else: + truediv = operator.div BIN_OP_FUNCS = { 'BINARY_POWER': operator.pow, 'BINARY_MULTIPLY': operator.mul, -'BINARY_DIVIDE': operator.div, +'BINARY_DIVIDE': truediv, 'BINARY_FLOOR_DIVIDE': operator.floordiv, 'BINARY_TRUE_DIVIDE': operator.truediv, 'BINARY_MODULO' : operator.mod, @@ -41,7 +48,7 @@ class CmpErrorConsts(VerifyCmpError): def __str__(self): return 'Compare Error within Consts of %s at index %i' % \ (repr(self.name), self.index) - + class CmpErrorConstsType(VerifyCmpError): """Exception to be raised when consts differ.""" def __init__(self, name, index): @@ -61,9 +68,9 @@ class CmpErrorConstsLen(VerifyCmpError): 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]`) - + len(self.consts[0]), repr(self.consts[0]), + len(self.consts[1]), repr(self.consts[1])) + class CmpErrorCode(VerifyCmpError): """Exception to be raised when code differs.""" def __init__(self, name, index, token1, token2, tokens1, tokens2): @@ -72,12 +79,12 @@ class CmpErrorCode(VerifyCmpError): self.token1 = token1 self.token2 = token2 self.tokens = [tokens1, tokens2] - + def __str__(self): s = 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]), + list(map(lambda a,b: (a,b), + self.tokens[0], + self.tokens[1])), 'Code differs in %s\n' % str(self.name)) return ('Code differs in %s at offset %s [%s] != [%s]\n\n' % \ (repr(self.name), self.index, @@ -91,9 +98,9 @@ class CmpErrorCodeLen(VerifyCmpError): 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]), + list(map(lambda a,b: (a,b), + self.tokens[0], + self.tokens[1])), 'Code len differs in %s\n' % str(self.name)) class CmpErrorMember(VerifyCmpError): @@ -109,7 +116,7 @@ class CmpErrorMember(VerifyCmpError): repr(self.data[0]), repr(self.data[1])) #--- compare --- - + # these members are ignored __IGNORE_CODE_MEMBERS__ = ['co_filename', 'co_firstlineno', 'co_lnotab', 'co_stacksize', 'co_names'] @@ -132,13 +139,13 @@ def cmp_code_objects(version, code_obj1, code_obj2, name=''): 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__' - + if isinstance(code_obj1, object) and cmp(code_obj1, code_obj2): # use the new style code-classes' __cmp__ method, which # should be faster and more sophisticated @@ -149,7 +156,7 @@ def cmp_code_objects(version, code_obj1, code_obj2, name=''): pass if isinstance(code_obj1, object): - members = filter(lambda x: x.startswith('co_'), dir(code_obj1)) + members = [x for x in dir(code_obj1) if x.startswith('co_')] else: members = dir(code_obj1); members.sort(); #members.reverse() @@ -171,7 +178,7 @@ def cmp_code_objects(version, code_obj1, code_obj2, name=''): scanner.setShowAsm( showasm=0 ) global JUMP_OPs JUMP_OPs = scan.JUMP_OPs + ['JUMP_BACK'] - + # use changed Token class # we (re)set this here to save exception handling, # which would get 'unubersichtlich' @@ -201,14 +208,14 @@ def cmp_code_objects(version, code_obj1, code_obj2, name=''): break else: raise CmpErrorCodeLen(name, tokens1, tokens2) - + offset_map[tokens1[i1].offset] = tokens2[i2].offset - + for idx1, idx2, offset2 in check_jumps.get(tokens1[i1].offset, []): if offset2 != tokens2[i2].offset: raise CmpErrorCode(name, tokens1[idx1].offset, tokens1[idx1], tokens2[idx2], tokens1, tokens2) - + if tokens1[i1] != tokens2[i2]: if tokens1[i1].type == 'LOAD_CONST' == tokens2[i2].type: i = 1 @@ -228,7 +235,7 @@ def cmp_code_objects(version, code_obj1, code_obj2, name=''): i2 += 2 continue elif i == 2 and tokens1[i1+i].type in BIN_OP_FUNCS: - f = BIN_OP_FUNCS[tokens1[i1+i].type] + f = BIN_OP_FUNCS[tokens1[i1+i].type] if f(tokens1[i1].pattr, tokens1[i1+1].pattr) == tokens2[i2].pattr: i1 += 3 i2 += 1 @@ -257,7 +264,7 @@ def cmp_code_objects(version, code_obj1, code_obj2, name=''): i1 += 2 i2 += 2 continue - + raise CmpErrorCode(name, tokens1[i1].offset, tokens1[i1], tokens2[i2], tokens1, tokens2) elif tokens1[i1].type in JUMP_OPs and tokens1[i1].pattr != tokens2[i2].pattr: @@ -273,7 +280,7 @@ def cmp_code_objects(version, code_obj1, code_obj2, name=''): check_jumps[dest1].append((i1,i2,dest2)) else: check_jumps[dest1] = [(i1,i2,dest2)] - + i1 += 1 i2 += 1 del tokens1, tokens2 # save memory @@ -282,7 +289,7 @@ def cmp_code_objects(version, code_obj1, code_obj2, name=''): # so we'll just compare the code consts codes1 = ( c for c in code_obj1.co_consts if type(c) == types.CodeType ) codes2 = ( c for c in code_obj2.co_consts if type(c) == types.CodeType ) - + for c1, c2 in zip(codes1, codes2): cmp_code_objects(version, c1, c2, name=name) else: @@ -294,7 +301,7 @@ def cmp_code_objects(version, code_obj1, code_obj2, name=''): class Token(scanner.Token): """Token class with changed semantics for 'cmp()'.""" - + def __cmp__(self, o): t = self.type # shortcut loads = ('LOAD_NAME', 'LOAD_GLOBAL', 'LOAD_CONST') @@ -311,7 +318,7 @@ class Token(scanner.Token): return 0 if t == 'JUMP_IF_FALSE_OR_POP' and o.type == 'POP_JUMP_IF_FALSE': return 0 - if t in JUMP_OPs: + if JUMP_OPs and t in JUMP_OPs: # ignore offset return cmp(t, o.type) return cmp(t, o.type) or cmp(self.pattr, o.pattr) @@ -338,6 +345,8 @@ def compare_files(pyc_filename1, pyc_filename2): 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) + print(repr(t1)) + print(repr(t2)) + + if (sys.version_info < (3, 0)): + print(cmp(t1, t2), cmp(t1.type, t2.type), cmp(t1.attr, t2.attr)) diff --git a/uncompyle6/walker.py b/uncompyle6/walker.py index 4f5aad09..a92d6e83 100644 --- a/uncompyle6/walker.py +++ b/uncompyle6/walker.py @@ -43,23 +43,28 @@ from __future__ import print_function makes the engine walk down to N[C] before evaluating the escape code. ''' -try: +import sys, re + +if (sys.version_info > (3, 0)): + from io import StringIO + import uncompyle6 + from .spark import GenericASTTraversal + from .parser import AST + from .scanner import Token, Code + minint = -sys.maxsize-1 + maxint = sys.maxsize +else: from StringIO import StringIO from spark import GenericASTTraversal from parser import AST from scanner import Token, Code -except ImportError: - from io import StringIO - from .spark import GenericASTTraversal - from .parser import AST - from .scanner import Token, Code + minint = -sys.maxint-1 + maxint = sys.maxint -import sys, re from types import CodeType import parser -minint = -sys.maxint-1 # Some ASTs used for comparing code fragments (like 'return None' at # the end of functions). @@ -114,7 +119,7 @@ TABLE_R = { 'DELETE_SLICE+2': ( '%|del %c[:%c]\n', 0, 1 ), 'DELETE_SLICE+3': ( '%|del %c[%c:%c]\n', 0, 1, 2 ), 'DELETE_ATTR': ( '%|del %c.%[-1]{pattr}\n', 0 ), -# 'EXEC_STMT': ( '%|exec %c in %[1]C\n', 0, (0,sys.maxint,', ') ), +# 'EXEC_STMT': ( '%|exec %c in %[1]C\n', 0, (0,maxint,', ') ), } TABLE_R0 = { # 'BUILD_LIST': ( '[%C]', (0,-1,', ') ), @@ -183,9 +188,9 @@ TABLE_DIRECT = { 'STORE_NAME': ( '%{pattr}', ), 'STORE_GLOBAL': ( '%{pattr}', ), 'STORE_DEREF': ( '%{pattr}', ), - 'unpack': ( '%C%,', (1, sys.maxint, ', ') ), - 'unpack_w_parens': ( '(%C%,)', (1, sys.maxint, ', ') ), - 'unpack_list': ( '[%C]', (1, sys.maxint, ', ') ), + 'unpack': ( '%C%,', (1, maxint, ', ') ), + 'unpack_w_parens': ( '(%C%,)', (1, maxint, ', ') ), + 'unpack_list': ( '[%C]', (1, maxint, ', ') ), 'build_tuple2': ( '%P', (0,-1,', ', 100) ), #'list_compr': ( '[ %c ]', -2), # handled by n_list_compr @@ -232,7 +237,7 @@ TABLE_DIRECT = { 'classdefdeco': ( '%c', 0), 'classdefdeco1': ( '\n\n%|@%c%c', 0, 1), 'kwarg': ( '%[0]{pattr}=%c', 1), - 'importlist2': ( '%C', (0, sys.maxint, ', ') ), + 'importlist2': ( '%C', (0, maxint, ', ') ), 'assert': ( '%|assert %c\n' , 0 ), 'assert2': ( '%|assert %c, %c\n' , 0, 3 ), @@ -294,7 +299,7 @@ TABLE_DIRECT = { 'except': ( '%|except:\n%+%c%-', 3 ), 'except_cond1': ( '%|except %c:\n', 1 ), 'except_cond2': ( '%|except %c as %c:\n', 1, 5 ), - 'except_suite': ( '%+%c%-%C', 0, (1, sys.maxint, '') ), + 'except_suite': ( '%+%c%-%C', 0, (1, maxint, '') ), 'tryfinallystmt': ( '%|try:\n%+%c%-%|finally:\n%+%c%-\n\n', 1, 5 ), 'withstmt': ( '%|with %c:\n%+%c%-', 0, 3), 'withasstmt': ( '%|with %c as %c:\n%+%c%-', 0, 2, 3), @@ -302,7 +307,7 @@ TABLE_DIRECT = { 'STORE_FAST': ( '%{pattr}', ), 'kv': ( '%c: %c', 3, 1 ), 'kv2': ( '%c: %c', 1, 2 ), - 'mapexpr': ( '{%[1]C}', (0,sys.maxint,', ') ), + 'mapexpr': ( '{%[1]C}', (0,maxint,', ') ), ## ## Python 2.5 Additions @@ -583,7 +588,7 @@ class Walker(GenericASTTraversal, object): #Restore escaped backslashes docstring = docstring.replace('\t', '\\\\') lines = docstring.split('\n') - calculate_indent = sys.maxint + calculate_indent = maxint for line in lines[1:]: stripped = line.lstrip() if len(stripped) > 0: @@ -591,7 +596,7 @@ class Walker(GenericASTTraversal, object): calculate_indent = min(calculate_indent, len(lines[-1]) - len(lines[-1].lstrip())) # Remove indentation (first line is special): trimmed = [lines[0]] - if calculate_indent < sys.maxint: + if calculate_indent < maxint: trimmed += [line[calculate_indent:] for line in lines[1:]] self.write(quote)