More Python3 compatability. Remove duplicate disassembly code and

get it from Python's standard library instead.
This commit is contained in:
rocky 2015-12-12 08:37:20 -05:00
parent 00d17461fc
commit 003d485814
9 changed files with 106 additions and 254 deletions

View File

@ -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,
}

View File

@ -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

View File

@ -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 = "<stdin>"
code = compile(source, fn, "exec")
dis(code)
Mdis.dis(code)
if __name__ == "__main__":
_test()

View File

@ -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

View File

@ -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):

View File

@ -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):

View File

@ -1,3 +1,5 @@
from __future__ import print_function
'''
Copyright (c) 1999 John Aycock
Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
@ -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):

View File

@ -1,18 +1,25 @@
from __future__ import print_function
#
# (C) Copyright 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
#
# 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))

View File

@ -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)