Cleaning code & patch

This commit is contained in:
Mysterie
2012-10-11 16:32:59 +02:00
parent f91e514f44
commit 16174505a4
20 changed files with 713 additions and 1103 deletions

View File

@@ -10,6 +10,5 @@ setup (name = "uncompyle2",
author = "Mysterie", author = "Mysterie",
author_email = "kajusska@gmail.com", author_email = "kajusska@gmail.com",
url = "http://github.com/Mysterie/uncompyle2", url = "http://github.com/Mysterie/uncompyle2",
packages=['uncompyle2', 'uncompyle2.opcode'], packages=['uncompyle2', 'uncompyle2.opcode']
scripts=['scripts/uncompyle2'],
) )

View File

@@ -15,7 +15,6 @@ for i in range(10):
else: else:
print 'Else' print 'Else'
i = 0 i = 0
while i < 10: while i < 10:
i = i+1 i = i+1
@@ -45,3 +44,15 @@ for x, y in [(1,2),(3,4)]:
for x in (1, 2, 3): for x in (1, 2, 3):
if x == 1: if x == 1:
print x print x
i = 0
while i < 10:
i+=1
for x in (1,2,3):
for y in (1,2,3):
if x == y and x == 1:
while i < 10:
print x
break

View File

@@ -17,4 +17,4 @@ del x[1,2,3]
x=[1,2,3] x=[1,2,3]
b=(1 for i in x if i) b=(1 for i in x if i)
b=(e for i in range(4) if i == 2 for j in range(7) if i + i % 2 == 0) b=(e for i in range(4) if i == 2)

View File

@@ -1,16 +0,0 @@
#!/bin/sh
file=$1
shift
options=$@
BASEDIR=test/bytecode_2.6
#BASEDIR=test/bytecode_2.0
#BASEDIR=test/bytecode_2.1
#BASEDIR=test/bytecode_2.2
if [ `dirname $file` == '.' ] ; then
file=$BASEDIR/test_$file.pyc
fi
python2.7 -u ./scripts/uncompyle $options $file 2>&1 |less

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python #!/usr/bin/env python2.7
# emacs-mode: -*-python-*-
""" '''
test_pythonlib -- uncompyle and verify Python libraries test_pythonlib -- uncompyle and verify Python libraries
Usage-Examples: Usage-Examples:
@@ -17,16 +17,17 @@ Step 1) Edit this file and add a new entry to 'test_options', eg.
Step 2: Run the test: Step 2: Run the test:
test_pythonlib --mylib # decompile 'mylib' test_pythonlib --mylib # decompile 'mylib'
test_pythonlib --mylib --verify # decompile verify 'mylib' test_pythonlib --mylib --verify # decompile verify 'mylib'
""" '''
from uncompyle import main, verify from uncompyle2 import main, verify
import getopt, sys
import os, time, shutil import os, time, shutil
from fnmatch import fnmatch from fnmatch import fnmatch
#----- configure this for your needs #----- configure this for your needs
lib_prefix = ['.', '/usr/lib/', '/usr/local/lib/']
target_base = '/tmp/py-dis/' target_base = '/tmp/py-dis/'
lib_prefix = '/usr/lib'
PYC = ('*.pyc', ) PYC = ('*.pyc', )
PYO = ('*.pyo', ) PYO = ('*.pyo', )
@@ -34,21 +35,22 @@ PYOC = ('*.pyc', '*.pyo')
test_options = { test_options = {
# name: (src_basedir, pattern, output_base_suffix) # name: (src_basedir, pattern, output_base_suffix)
'test': ('./test', PYOC, 'test'), 'test': ['test', PYC, 'test'],
'1.5': (os.path.join(lib_prefix, 'python1.5'), PYC, 'python-lib1.5'), '2.5': ['python2.5', PYC, 'python-lib2.5'],
'1.6': (os.path.join(lib_prefix, 'python1.6'), PYC, 'python-lib1.6'), '2.6': ['python2.6', PYC, 'python-lib2.6'],
'2.0': (os.path.join(lib_prefix, 'python2.0'), PYC, 'python-lib2.0'), '2.7': ['python2.7', PYC, 'python-lib2.7']
'2.1': (os.path.join(lib_prefix, 'python2.1'), PYC, 'python-lib2.1'), }
'2.2': (os.path.join(lib_prefix, 'python2.2'), PYC, 'python-lib2.2'),
'2.5': (os.path.join(lib_prefix, 'python2.5'), PYC, 'python-lib2.5'),
'2.6': (os.path.join(lib_prefix, 'python2.6'), PYC, 'python-lib2.6'),
'2.7': (os.path.join(lib_prefix, 'python2.7'), PYC, 'python-lib2.7')
}
#----- #-----
def do_tests(src_dir, patterns, target_dir, start_with=None, do_verify=0): def help():
print 'Usage-Examples:'
print 'test_pythonlib --all # decompile all tests (suite + libs)'
print 'test_pythonlib --all --verify # decomyile all tests and verify results'
print 'test_pythonlib --test # decompile only the testsuite'
print 'test_pythonlib --2.2 --verify # decompile and verify python lib 2.2'
def do_tests(src_dir, patterns, target_dir, start_with=None, do_verify=0):
def visitor(files, dirname, names): def visitor(files, dirname, names):
files.extend( files.extend(
[os.path.normpath(os.path.join(dirname, n)) [os.path.normpath(os.path.join(dirname, n))
@@ -72,20 +74,25 @@ def do_tests(src_dir, patterns, target_dir, start_with=None, do_verify=0):
pass pass
print time.ctime() print time.ctime()
print 'Working directory: ', src_dir
try:
main(src_dir, target_dir, files, [], do_verify=do_verify) main(src_dir, target_dir, files, [], do_verify=do_verify)
print time.ctime() except (KeyboardInterrupt, OSError):
print
exit(1)
if __name__ == '__main__': if __name__ == '__main__':
import getopt, sys
do_verify = 0 do_verify = 0
test_dirs = [] test_dirs = []
checked_dirs = []
start_with = None start_with = None
test_options_keys = test_options.keys(); test_options_keys.sort() test_options_keys = test_options.keys(); test_options_keys.sort()
opts, args = getopt.getopt(sys.argv[1:], '', opts, args = getopt.getopt(sys.argv[1:], '',
['start-with=', 'verify', 'all', ] \ ['start-with=', 'verify', 'all', ] \
+ test_options_keys ) + test_options_keys )
if not opts:
help()
for opt, val in opts: for opt, val in opts:
if opt == '--verify': if opt == '--verify':
do_verify = 1 do_verify = 1
@@ -96,23 +103,20 @@ if __name__ == '__main__':
elif opt == '--all': elif opt == '--all':
for val in test_options_keys: for val in test_options_keys:
test_dirs.append(test_options[val]) test_dirs.append(test_options[val])
else:
help()
for src_dir, pattern, target_dir in test_dirs: for src_dir, pattern, target_dir in test_dirs:
if os.path.exists(src_dir): for libpath in lib_prefix:
testpath = os.path.join(libpath, src_dir)
testlibfile = "%s/%s" % (testpath, 'os.py')
testfile = "%s/%s" % (testpath, 'test_empty.py')
if os.path.exists(testlibfile) or os.path.exists(testfile):
src_dir = testpath
checked_dirs.append([src_dir, pattern, target_dir])
for src_dir, pattern, target_dir in checked_dirs:
target_dir = os.path.join(target_base, target_dir) target_dir = os.path.join(target_base, target_dir)
if os.path.exists(target_dir): if os.path.exists(target_dir):
shutil.rmtree(target_dir, ignore_errors=1) shutil.rmtree(target_dir, ignore_errors=1)
do_tests(src_dir, pattern, target_dir, start_with, do_verify) do_tests(src_dir, pattern, target_dir, start_with, do_verify)
else:
print '### skipping', src_dir
# python 1.5:
# test/re_tests memory error
# test/test_b1 memory error
# Verification notes:
# - xdrlib fails verification due the same lambda used twice
# (verification is successfull when using original .pyo as
# input)
#

View File

@@ -3,7 +3,7 @@
# #
# Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de> # Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de>
# #
""" '''
Usage: uncompyle [OPTIONS]... [ FILE | DIR]... Usage: uncompyle [OPTIONS]... [ FILE | DIR]...
Examples: Examples:
@@ -38,7 +38,7 @@ Extensions of generated files:
'.dis' successfully decompiled (and verified if --verify) '.dis' successfully decompiled (and verified if --verify)
'.dis_unverified' successfully decompile but --verify failed '.dis_unverified' successfully decompile but --verify failed
'.nodis' uncompyle failed (contact author for enhancement) '.nodis' uncompyle failed (contact author for enhancement)
""" '''
Usage_short = \ Usage_short = \
"decomyple [--help] [--verify] [--showasm] [--showast] [-o <path>] FILE|DIR..." "decomyple [--help] [--verify] [--showasm] [--showast] [-o <path>] FILE|DIR..."

View File

@@ -1,60 +0,0 @@
# Copyright (c) 1999 John Aycock
# Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de>
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
#
# See main module for license.
#
__all__ = ['Token', 'Scanner', 'getscanner']
import types
import disas as dis
from collections import namedtuple
from array import array
from operator import itemgetter
class Token:
"""
Class representing a byte-code token.
A byte-code token is equivalent to the contents of one line
as output by dis.dis().
"""
def __init__(self, type_, attr=None, pattr=None, offset=-1, linestart=False):
self.type = intern(type_)
self.attr = attr
self.pattr = pattr
self.offset = offset
self.linestart = linestart
def __cmp__(self, o):
if isinstance(o, Token):
# both are tokens: compare type and pattr
return cmp(self.type, o.type) or cmp(self.pattr, o.pattr)
else:
return cmp(self.type, o)
def __repr__(self): return str(self.type)
def __str__(self):
pattr = self.pattr
if self.linestart:
return '\n%s\t%-17s %r' % (self.offset, self.type, pattr)
else:
return '%s\t%-17s %r' % (self.offset, self.type, pattr)
def __hash__(self): return hash(self.type)
def __getitem__(self, i): raise IndexError
class Code:
"""
Class for representing code-objects.
This is similar to the original code object, but additionally
the diassembled code is stored in the attribute '_tokens'.
"""
def __init__(self, co, scanner, classname=None):
for i in dir(co):
if i.startswith('co_'):
setattr(self, i, getattr(co, i))
self._tokens, self._customize = scanner.disassemble(co, classname)

View File

@@ -1,39 +1,40 @@
# Copyright (c) 1999 John Aycock '''
# Copyright (c) 2000 by hartmut Goebel <hartmut@goebel.noris.de> Copyright (c) 1999 John Aycock
# Copyright (c) 2000 by hartmut Goebel <hartmut@goebel.noris.de>
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the Permission is hereby granted, free of charge, to any person obtaining
# "Software"), to deal in the Software without restriction, including a copy of this software and associated documentation files (the
# without limitation the rights to use, copy, modify, merge, publish, "Software"), to deal in the Software without restriction, including
# distribute, sublicense, and/or sell copies of the Software, and to without limitation the rights to use, copy, modify, merge, publish,
# permit persons to whom the Software is furnished to do so, subject to distribute, sublicense, and/or sell copies of the Software, and to
# the following conditions: permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software. The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# See the file 'CHANGES' for a list of changes
# See the file 'CHANGES' for a list of changes
# NB. This is not a masterpiece of software, but became more like a hack.
# Probably a complete rewrite would be sensefull. hG/2000-12-27 NB. This is not a masterpiece of software, but became more like a hack.
# Probably a complete rewrite would be sensefull. hG/2000-12-27
'''
import sys, types, os import sys, types, os
import Walker, verify, magics import walker, verify, magics
sys.setrecursionlimit(5000) sys.setrecursionlimit(5000)
__all__ = ['uncompyle_file', 'uncompyle_file', 'main'] __all__ = ['uncompyle_file', 'main']
def _load_file(filename): def _load_file(filename):
""" '''
load a Python source file and compile it to byte-code load a Python source file and compile it to byte-code
_load_module(filename: string): code_object _load_module(filename: string): code_object
@@ -43,26 +44,27 @@ def _load_file(filename):
code_object: code_object compiled from this source code code_object: code_object compiled from this source code
This function does NOT write any file! This function does NOT write any file!
""" '''
fp = open(filename, 'rb') fp = open(filename, 'rb')
source = fp.read()+'\n' source = fp.read()+'\n'
try: try:
co = compile(source, filename, 'exec') co = compile(source, filename, 'exec')
except SyntaxError: except SyntaxError:
print >> sys.stderr, '>>Syntax error in', filename print >> sys.stderr, '>>Syntax error in', filename, '\n'
raise raise
fp.close() fp.close()
return co return co
def _load_module(filename): def _load_module(filename):
""" '''
load a module without importing it load a module without importing it
_load_module(filename: string): code_object _load_module(filename: string): code_object
filename: name of file containing Python byte-code object filename: name of file containing Python byte-code object
(normally a .pyc) (normally a .pyc)
code_object: code_object from this file code_object: code_object from this file
""" '''
import magics, marshal import magics, marshal
fp = open(filename, 'rb') fp = open(filename, 'rb')
magic = fp.read(4) magic = fp.read(4)
@@ -79,9 +81,9 @@ def _load_module(filename):
return version, co return version, co
def uncompyle(version, co, out=None, showasm=0, showast=0): def uncompyle(version, co, out=None, showasm=0, showast=0):
""" '''
diassembles a given code block 'co' diassembles a given code block 'co'
""" '''
assert type(co) == types.CodeType assert type(co) == types.CodeType
# store final output stream for case of error # store final output stream for case of error
@@ -90,22 +92,24 @@ def uncompyle(version, co, out=None, showasm=0, showast=0):
print >>__real_out, '#Embedded file name: %s' % co.co_filename print >>__real_out, '#Embedded file name: %s' % co.co_filename
# diff scanner # diff scanner
if version == 2.7: if version == 2.7:
import Scanner27 as scan import scanner27 as scan
scanner = scan.Scanner27()
elif version == 2.6: elif version == 2.6:
import Scanner26 as scan import scanner26 as scan
scanner = scan.Scanner26()
elif version == 2.5: elif version == 2.5:
import Scanner25 as scan import scanner25 as scan
scanner = scan.Scanner25()
scanner = scan.Scanner(version)
scanner.setShowAsm(showasm, out) scanner.setShowAsm(showasm, out)
tokens, customize = scanner.disassemble(co) tokens, customize = scanner.disassemble(co)
#sys.exit(0) #sys.exit(0)
# Build AST from disassembly. # Build AST from disassembly.
walker = Walker.Walker(out, scanner, showast=showast) walk = walker.Walker(out, scanner, showast=showast)
try: try:
ast = walker.build_ast(tokens, customize) ast = walk.build_ast(tokens, customize)
except Walker.ParserError, e : # parser failed, dump disassembly except walker.ParserError, e : # parser failed, dump disassembly
print >>__real_out, e print >>__real_out, e
raise raise
@@ -114,20 +118,20 @@ def uncompyle(version, co, out=None, showasm=0, showast=0):
# convert leading '__doc__ = "..." into doc string # convert leading '__doc__ = "..." into doc string
assert ast == 'stmts' assert ast == 'stmts'
try: try:
if ast[0][0] == Walker.ASSIGN_DOC_STRING(co.co_consts[0]): if ast[0][0] == walker.ASSIGN_DOC_STRING(co.co_consts[0]):
walker.print_docstring('', co.co_consts[0]) walk.print_docstring('', co.co_consts[0])
del ast[0] del ast[0]
if ast[-1] == Walker.RETURN_NONE: if ast[-1] == walker.RETURN_NONE:
ast.pop() # remove last node ast.pop() # remove last node
#todo: if empty, add 'pass' #todo: if empty, add 'pass'
except: except:
pass pass
walker.mod_globs = Walker.find_globals(ast, set()) walk.mod_globs = walker.find_globals(ast, set())
walker.gen_source(ast, customize) walk.gen_source(ast, customize)
for g in walker.mod_globs: for g in walk.mod_globs:
walker.write('global %s ## Warning: Unused global\n' % g) walk.write('global %s ## Warning: Unused global' % g)
if walker.ERROR: if walk.ERROR:
raise walker.ERROR raise walk.ERROR
def uncompyle_file(filename, outstream=None, showasm=0, showast=0): def uncompyle_file(filename, outstream=None, showasm=0, showast=0):
""" """
@@ -137,7 +141,7 @@ def uncompyle_file(filename, outstream=None, showasm=0, showast=0):
uncompyle(version, co, outstream, showasm, showast) uncompyle(version, co, outstream, showasm, showast)
co = None co = None
#---- main ------- # ---- main ----
if sys.platform.startswith('linux') and os.uname()[2][:2] == '2.': if sys.platform.startswith('linux') and os.uname()[2][:2] == '2.':
def __memUsage(): def __memUsage():
@@ -151,7 +155,7 @@ else:
def main(in_base, out_base, files, codes, outfile=None, def main(in_base, out_base, files, codes, outfile=None,
showasm=0, showast=0, do_verify=0): showasm=0, showast=0, do_verify=0):
""" '''
in_base base directory for input files in_base base directory for input files
out_base base directory for output files (ignored when out_base base directory for output files (ignored when
files list of filenames to be uncompyled (relative to src_base) files list of filenames to be uncompyled (relative to src_base)
@@ -161,11 +165,12 @@ def main(in_base, out_base, files, codes, outfile=None,
- <filename> outfile=<filename> (out_base is ignored) - <filename> outfile=<filename> (out_base is ignored)
- files below out_base out_base=... - files below out_base out_base=...
- stdout out_base=None, outfile=None - stdout out_base=None, outfile=None
""" '''
def _get_outstream(outfile): def _get_outstream(outfile):
dir = os.path.dirname(outfile) dir = os.path.dirname(outfile)
failed_file = outfile + '_failed' failed_file = outfile + '_failed'
if os.path.exists(failed_file): os.remove(failed_file) if os.path.exists(failed_file):
os.remove(failed_file)
try: try:
os.makedirs(dir) os.makedirs(dir)
except OSError: except OSError:
@@ -173,7 +178,6 @@ def main(in_base, out_base, files, codes, outfile=None,
return open(outfile, 'w') return open(outfile, 'w')
of = outfile of = outfile
tot_files = okay_files = failed_files = verify_failed_files = 0 tot_files = okay_files = failed_files = verify_failed_files = 0
for code in codes: for code in codes:
@@ -203,13 +207,15 @@ def main(in_base, out_base, files, codes, outfile=None,
if outfile: if outfile:
outstream.close() outstream.close()
os.remove(outfile) os.remove(outfile)
sys.stderr.write("\nLast file: %s " % (infile))
raise raise
except: except:
failed_files += 1 failed_files += 1
sys.stderr.write("\n# Can't uncompyle %s\n" % infile)
if outfile: if outfile:
outstream.close() outstream.close()
os.rename(outfile, outfile + '_failed') os.rename(outfile, outfile + '_failed')
else:
sys.stderr.write("\n# Can't uncompyle %s\n" % infile)
import traceback import traceback
traceback.print_exc() traceback.print_exc()
#raise #raise
@@ -219,14 +225,21 @@ def main(in_base, out_base, files, codes, outfile=None,
if do_verify: if do_verify:
try: try:
verify.compare_code_with_srcfile(infile, outfile) verify.compare_code_with_srcfile(infile, outfile)
print '\n# okay decompyling', infile, __memUsage() if not outfile: print '\n# okay decompyling', infile, __memUsage()
okay_files += 1 okay_files += 1
except verify.VerifyCmpError, e: except verify.VerifyCmpError, e:
verify_failed_files += 1 verify_failed_files += 1
os.rename(outfile, outfile + '_unverified') os.rename(outfile, outfile + '_unverified')
if not outfile:
print >>sys.stderr, "### Error Verifiying", file print >>sys.stderr, "### Error Verifiying", file
print >>sys.stderr, e print >>sys.stderr, e
else: else:
okay_files += 1 okay_files += 1
print '\n# okay decompyling', infile, __memUsage() if not outfile: print '\n# okay decompyling', infile, __memUsage()
if outfile:
sys.stdout.write("decompiled %i files: %i okay, %i failed, %i verify failed\r" % (tot_files, okay_files, failed_files, verify_failed_files))
sys.stdout.flush()
if outfile:
sys.stdout.write("\n")
sys.stdout.flush()
return (tot_files, okay_files, failed_files, verify_failed_files) return (tot_files, okay_files, failed_files, verify_failed_files)

View File

@@ -189,28 +189,6 @@ def findlinestarts(code):
if lineno != lastlineno: if lineno != lastlineno:
yield (addr, lineno) yield (addr, lineno)
def setVersion(version):
if version == 2.7:
import uncompyle2.opcode.opcode_27 as opcodyn
elif version == 2.6:
import uncompyle2.opcode.opcode_26 as opcodyn
elif version == 2.5:
import uncompyle2.opcode.opcode_25 as opcodyn
globals().update({'cmp_op': opcodyn.cmp_op})
globals().update({'hasconst': opcodyn.hasconst})
globals().update({'hasname': opcodyn.hasname})
globals().update({'hasjrel': opcodyn.hasjrel})
globals().update({'hasjabs': opcodyn.hasjabs})
globals().update({'haslocal': opcodyn.haslocal})
globals().update({'hascompare': opcodyn.hascompare})
globals().update({'hasfree': opcodyn.hasfree})
globals().update({'opname': opcodyn.opname})
globals().update({'opmap': opcodyn.opmap})
globals().update({'HAVE_ARGUMENT': opcodyn.HAVE_ARGUMENT})
globals().update({'EXTENDED_ARG': opcodyn.EXTENDED_ARG})
def _test(): def _test():
"""Simple test program to disassemble a file.""" """Simple test program to disassemble a file."""
if sys.argv[1:]: if sys.argv[1:]:

View File

@@ -1,12 +1,7 @@
'''
"""
opcode module - potentially shared between dis and other modules which opcode module - potentially shared between dis and other modules which
operate on bytecodes (e.g. peephole optimizers). operate on bytecodes (e.g. peephole optimizers).
""" '''
__all__ = ["cmp_op", "hasconst", "hasname", "hasjrel", "hasjabs",
"haslocal", "hascompare", "hasfree", "opname", "opmap",
"HAVE_ARGUMENT", "EXTENDED_ARG"]
cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is', cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is',
'is not', 'exception match', 'BAD') 'is not', 'exception match', 'BAD')
@@ -18,6 +13,7 @@ hasjabs = []
haslocal = [] haslocal = []
hascompare = [] hascompare = []
hasfree = [] hasfree = []
PJIF = PJIT = JA = JF = 0
opmap = {} opmap = {}
opname = [''] * 256 opname = [''] * 256
@@ -27,6 +23,7 @@ del op
def def_op(name, op): def def_op(name, op):
opname[op] = name opname[op] = name
opmap[name] = op opmap[name] = op
globals().update({name: op})
def name_op(name, op): def name_op(name, op):
def_op(name, op) def_op(name, op)
@@ -40,6 +37,14 @@ def jabs_op(name, op):
def_op(name, op) def_op(name, op)
hasjabs.append(op) hasjabs.append(op)
def updateGlobal():
globals().update({'PJIF': opmap['JUMP_IF_FALSE']})
globals().update({'PJIT': opmap['JUMP_IF_TRUE']})
globals().update({'JA': opmap['JUMP_ABSOLUTE']})
globals().update({'JF': opmap['JUMP_FORWARD']})
globals().update({k.replace('+','_'):v for (k,v) in opmap.items()})
globals().update({'JUMP_OPs': map(lambda op: opname[op], hasjrel + hasjabs)})
# Instruction opcodes for compiled code # Instruction opcodes for compiled code
# Blank lines correspond to available opcodes # Blank lines correspond to available opcodes
@@ -182,4 +187,5 @@ def_op('CALL_FUNCTION_VAR_KW', 142) # 113 # #args + (#kwargs << 8)
def_op('EXTENDED_ARG', 143) # 114 def_op('EXTENDED_ARG', 143) # 114
EXTENDED_ARG = 143 # 114 EXTENDED_ARG = 143 # 114
updateGlobal()
del def_op, name_op, jrel_op, jabs_op del def_op, name_op, jrel_op, jabs_op

View File

@@ -1,12 +1,7 @@
'''
"""
opcode module - potentially shared between dis and other modules which opcode module - potentially shared between dis and other modules which
operate on bytecodes (e.g. peephole optimizers). operate on bytecodes (e.g. peephole optimizers).
""" '''
__all__ = ["cmp_op", "hasconst", "hasname", "hasjrel", "hasjabs",
"haslocal", "hascompare", "hasfree", "opname", "opmap",
"HAVE_ARGUMENT", "EXTENDED_ARG"]
cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is', cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is',
'is not', 'exception match', 'BAD') 'is not', 'exception match', 'BAD')
@@ -18,6 +13,7 @@ hasjabs = []
haslocal = [] haslocal = []
hascompare = [] hascompare = []
hasfree = [] hasfree = []
PJIF = PJIT = JA = JF = 0
opmap = {} opmap = {}
opname = [''] * 256 opname = [''] * 256
@@ -27,6 +23,7 @@ del op
def def_op(name, op): def def_op(name, op):
opname[op] = name opname[op] = name
opmap[name] = op opmap[name] = op
globals().update({name: op})
def name_op(name, op): def name_op(name, op):
def_op(name, op) def_op(name, op)
@@ -40,6 +37,14 @@ def jabs_op(name, op):
def_op(name, op) def_op(name, op)
hasjabs.append(op) hasjabs.append(op)
def updateGlobal():
globals().update({'PJIF': opmap['JUMP_IF_FALSE']})
globals().update({'PJIT': opmap['JUMP_IF_TRUE']})
globals().update({'JA': opmap['JUMP_ABSOLUTE']})
globals().update({'JF': opmap['JUMP_FORWARD']})
globals().update({k.replace('+','_'):v for (k,v) in opmap.items()})
globals().update({'JUMP_OPs': map(lambda op: opname[op], hasjrel + hasjabs)})
# Instruction opcodes for compiled code # Instruction opcodes for compiled code
# Blank lines correspond to available opcodes # Blank lines correspond to available opcodes
@@ -183,4 +188,5 @@ def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8)
def_op('EXTENDED_ARG', 143) def_op('EXTENDED_ARG', 143)
EXTENDED_ARG = 143 EXTENDED_ARG = 143
updateGlobal()
del def_op, name_op, jrel_op, jabs_op del def_op, name_op, jrel_op, jabs_op

View File

@@ -1,12 +1,7 @@
'''
"""
opcode module - potentially shared between dis and other modules which opcode module - potentially shared between dis and other modules which
operate on bytecodes (e.g. peephole optimizers). operate on bytecodes (e.g. peephole optimizers).
""" '''
__all__ = ["cmp_op", "hasconst", "hasname", "hasjrel", "hasjabs",
"haslocal", "hascompare", "hasfree", "opname", "opmap",
"HAVE_ARGUMENT", "EXTENDED_ARG"]
cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is', cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is',
'is not', 'exception match', 'BAD') 'is not', 'exception match', 'BAD')
@@ -18,6 +13,7 @@ hasjabs = []
haslocal = [] haslocal = []
hascompare = [] hascompare = []
hasfree = [] hasfree = []
PJIF = PJIT = JA = JF = 0
opmap = {} opmap = {}
opname = [''] * 256 opname = [''] * 256
@@ -27,6 +23,7 @@ del op
def def_op(name, op): def def_op(name, op):
opname[op] = name opname[op] = name
opmap[name] = op opmap[name] = op
globals().update({name: op})
def name_op(name, op): def name_op(name, op):
def_op(name, op) def_op(name, op)
@@ -40,6 +37,14 @@ def jabs_op(name, op):
def_op(name, op) def_op(name, op)
hasjabs.append(op) hasjabs.append(op)
def updateGlobal():
globals().update({'PJIF': opmap['POP_JUMP_IF_FALSE']})
globals().update({'PJIT': opmap['POP_JUMP_IF_TRUE']})
globals().update({'JA': opmap['JUMP_ABSOLUTE']})
globals().update({'JF': opmap['JUMP_FORWARD']})
globals().update({k.replace('+','_'):v for (k,v) in opmap.items()})
globals().update({'JUMP_OPs': map(lambda op: opname[op], hasjrel + hasjabs)})
# Instruction opcodes for compiled code # Instruction opcodes for compiled code
# Blank lines correspond to available opcodes # Blank lines correspond to available opcodes
@@ -189,4 +194,5 @@ EXTENDED_ARG = 145
def_op('SET_ADD', 146) def_op('SET_ADD', 146)
def_op('MAP_ADD', 147) def_op('MAP_ADD', 147)
updateGlobal()
del def_op, name_op, jrel_op, jabs_op del def_op, name_op, jrel_op, jabs_op

View File

@@ -1,9 +1,10 @@
# Copyright (c) 1999 John Aycock '''
# Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de> Copyright (c) 1999 John Aycock
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org> Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de>
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# See main module for license.
# See main module for license.
'''
__all__ = ['parse', 'AST', 'ParserError', 'Parser'] __all__ = ['parse', 'AST', 'ParserError', 'Parser']
@@ -11,7 +12,7 @@ from spark import GenericASTBuilder
import string, exceptions, sys import string, exceptions, sys
from UserList import UserList from UserList import UserList
from Scanner import Token from scanner import Token
class AST(UserList): class AST(UserList):
def __init__(self, type, kids=[]): def __init__(self, type, kids=[]):
@@ -41,7 +42,7 @@ class ParserError(Exception):
self.offset = offset self.offset = offset
def __str__(self): def __str__(self):
return "Syntax error at or near `%r' token at offset %s" % \ return "Syntax error at or near `%r' token at offset %s\n" % \
(self.token, self.offset) (self.token, self.offset)

274
uncompyle2/scanner.py Executable file
View File

@@ -0,0 +1,274 @@
# Copyright (c) 1999 John Aycock
# Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de>
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
#
# See main module for license.
#
__all__ = ['Token', 'Scanner', 'Code']
import types
from collections import namedtuple
from array import array
from operator import itemgetter
from uncompyle2.opcode import opcode_25, opcode_26, opcode_27
class Token:
'''
Class representing a byte-code token.
A byte-code token is equivalent to the contents of one line
as output by dis.dis().
'''
def __init__(self, type_, attr=None, pattr=None, offset=-1, linestart=False):
self.type = intern(type_)
self.attr = attr
self.pattr = pattr
self.offset = offset
self.linestart = linestart
def __cmp__(self, o):
if isinstance(o, Token):
# both are tokens: compare type and pattr
return cmp(self.type, o.type) or cmp(self.pattr, o.pattr)
else:
return cmp(self.type, o)
def __repr__(self): return str(self.type)
def __str__(self):
pattr = self.pattr
if self.linestart:
return '\n%s\t%-17s %r' % (self.offset, self.type, pattr)
else:
return '%s\t%-17s %r' % (self.offset, self.type, pattr)
def __hash__(self): return hash(self.type)
def __getitem__(self, i): raise IndexError
class Code:
'''
Class for representing code-objects.
This is similar to the original code object, but additionally
the diassembled code is stored in the attribute '_tokens'.
'''
def __init__(self, co, scanner, classname=None):
for i in dir(co):
if i.startswith('co_'):
setattr(self, i, getattr(co, i))
self._tokens, self._customize = scanner.disassemble(co, classname)
class Scanner(object):
opc = None # opcode module
def __init__(self, version):
if version == 2.7:
self.opc = opcode_27
elif version == 2.6:
self.opc = opcode_26
elif version == 2.5:
self.opc = opcode_25
return self.resetTokenClass()
def setShowAsm(self, showasm, out=None):
self.showasm = showasm
self.out = out
def setTokenClass(self, tokenClass):
assert type(tokenClass) == types.ClassType
self.Token = tokenClass
return self.Token
def resetTokenClass(self):
return self.setTokenClass(Token)
def get_target(self, pos, op=None):
if op is None:
op = self.code[pos]
target = self.get_argument(pos)
if op in self.opc.hasjrel:
target += pos + 3
return target
def get_argument(self, pos):
target = self.code[pos+1] + self.code[pos+2] * 256
return target
def print_bytecode(self):
for i in self.op_range(0, len(self.code)):
op = self.code[i]
if op in self.opc.hasjabs+self.opc.hasjrel:
dest = self.get_target(i, op)
print '%i\t%s\t%i' % (i, self.opc.opname[op], dest)
else:
print '%i\t%s\t' % (i, self.opc.opname[op])
def first_instr(self, start, end, instr, target=None, exact=True):
'''
Find the first <instr> in the block from start to end.
<instr> is any python bytecode instruction or a list of opcodes
If <instr> is an opcode with a target (like a jump), a target
destination can be specified which must match precisely if exact
is True, or if exact is False, the instruction which has a target
closest to <target> will be returned.
Return index to it or None if not found.
'''
code = self.code
assert(start>=0 and end<=len(code))
try: None in instr
except: instr = [instr]
pos = None
distance = len(code)
for i in self.op_range(start, end):
op = code[i]
if op in instr:
if target is None:
return i
dest = self.get_target(i, op)
if dest == target:
return i
elif not exact:
_distance = abs(target - dest)
if _distance < distance:
distance = _distance
pos = i
return pos
def last_instr(self, start, end, instr, target=None, exact=True):
'''
Find the last <instr> in the block from start to end.
<instr> is any python bytecode instruction or a list of opcodes
If <instr> is an opcode with a target (like a jump), a target
destination can be specified which must match precisely if exact
is True, or if exact is False, the instruction which has a target
closest to <target> will be returned.
Return index to it or None if not found.
'''
code = self.code
if not (start>=0 and end<=len(code)):
return None
try: None in instr
except: instr = [instr]
pos = None
distance = len(code)
for i in self.op_range(start, end):
op = code[i]
if op in instr:
if target is None:
pos = i
else:
dest = self.get_target(i, op)
if dest == target:
distance = 0
pos = i
elif not exact:
_distance = abs(target - dest)
if _distance <= distance:
distance = _distance
pos = i
return pos
def all_instr(self, start, end, instr, target=None, include_beyond_target=False):
'''
Find all <instr> in the block from start to end.
<instr> is any python bytecode instruction or a list of opcodes
If <instr> is an opcode with a target (like a jump), a target
destination can be specified which must match precisely.
Return a list with indexes to them or [] if none found.
'''
code = self.code
assert(start>=0 and end<=len(code))
try: None in instr
except: instr = [instr]
result = []
for i in self.op_range(start, end):
op = code[i]
if op in instr:
if target is None:
result.append(i)
else:
t = self.get_target(i, op)
if include_beyond_target and t >= target:
result.append(i)
elif t == target:
result.append(i)
return result
def op_size(self, op):
if op < self.opc.HAVE_ARGUMENT:
return 1
else:
return 3
def op_range(self, start, end):
while start < end:
yield start
start += self.op_size(self.code[start])
def remove_mid_line_ifs(self, ifs):
filtered = []
for i in ifs:
if self.lines[i].l_no == self.lines[i+3].l_no:
if self.code[self.prev[self.lines[i].next]] in (self.opc.PJIT, self.opc.PJIF):
continue
filtered.append(i)
return filtered
def rem_or(self, start, end, instr, target=None, include_beyond_target=False):
'''
Find all <instr> in the block from start to end.
<instr> is any python bytecode instruction or a list of opcodes
If <instr> is an opcode with a target (like a jump), a target
destination can be specified which must match precisely.
Return a list with indexes to them or [] if none found.
'''
code = self.code
assert(start>=0 and end<=len(code))
try: None in instr
except: instr = [instr]
result = []
for i in self.op_range(start, end):
op = code[i]
if op in instr:
if target is None:
result.append(i)
else:
t = self.get_target(i, op)
if include_beyond_target and t >= target:
result.append(i)
elif t == target:
result.append(i)
pjits = self.all_instr(start, end, self.opc.PJIT)
filtered = []
for pjit in pjits:
tgt = self.get_target(pjit)-3
for i in result:
if i <= pjit or i >= tgt:
filtered.append(i)
result = filtered
filtered = []
return result
def restrict_to_parent(self, target, parent):
'''Restrict pos to parent boundaries.'''
if not (parent['start'] < target < parent['end']):
target = parent['end']
return target

View File

@@ -1,54 +1,32 @@
# Copyright (c) 1999 John Aycock '''
# Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de> Copyright (c) 1999 John Aycock
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org> Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de>
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# See main module for license.
#
__all__ = ['Token', 'Scanner', 'getscanner'] See main module for license.
'''
import types import types
import disas as dis
from collections import namedtuple from collections import namedtuple
from array import array from array import array
from operator import itemgetter from operator import itemgetter
from struct import * from struct import *
from Scanner import Token, Code
class Scanner: from uncompyle2.opcode.opcode_25 import *
def __init__(self, version): import disas as dis
self.version = version import scanner as scan
self.resetTokenClass()
dis.setVersion(version) class Scanner25(scan.Scanner):
globals().update({'HAVE_ARGUMENT': dis.HAVE_ARGUMENT}) def __init__(self):
globals().update({k.replace('+','_'):v for (k,v) in dis.opmap.items()}) self.Token = scan.Scanner.__init__(self, 2.5)
globals().update({'PJIF': dis.opmap['JUMP_IF_FALSE']})
globals().update({'PJIT': dis.opmap['JUMP_IF_TRUE']})
globals().update({'JA': dis.opmap['JUMP_ABSOLUTE']})
globals().update({'JF': dis.opmap['JUMP_FORWARD']})
self.JUMP_OPs = map(lambda op: dis.opname[op],
dis.hasjrel + dis.hasjabs)
def setShowAsm(self, showasm, out=None):
self.showasm = showasm
self.out = out
def setTokenClass(self, tokenClass):
assert type(tokenClass) == types.ClassType
self.Token = tokenClass
def resetTokenClass(self):
self.setTokenClass(Token)
def disassemble(self, co, classname=None): def disassemble(self, co, classname=None):
""" '''
Disassemble a code object, returning a list of 'Token'. Disassemble a code object, returning a list of 'Token'.
The main part of this procedure is modelled after The main part of this procedure is modelled after
dis.disassemble(). dis.disassemble().
""" '''
rv = [] rv = []
customize = {} customize = {}
Token = self.Token # shortcut Token = self.Token # shortcut
@@ -93,7 +71,7 @@ class Scanner:
delta = 0 delta = 0
self.restructCode(toDel) self.restructCode(toDel)
for x in toDel: for x in toDel:
if self.code[x-delta] >= dis.HAVE_ARGUMENT: if self.code[x-delta] >= HAVE_ARGUMENT:
self.code.pop(x-delta) self.code.pop(x-delta)
self.code.pop(x-delta) self.code.pop(x-delta)
self.code.pop(x-delta) self.code.pop(x-delta)
@@ -154,7 +132,7 @@ class Scanner:
extended_arg = 0 extended_arg = 0
for offset in self.op_range(0, n): for offset in self.op_range(0, n):
op = self.code[offset] op = self.code[offset]
opname = dis.opname[op] op_name = opname[op]
oparg = None; pattr = None oparg = None; pattr = None
if offset in cf: if offset in cf:
@@ -166,23 +144,23 @@ class Scanner:
if op >= HAVE_ARGUMENT: if op >= HAVE_ARGUMENT:
oparg = self.get_argument(offset) + extended_arg oparg = self.get_argument(offset) + extended_arg
extended_arg = 0 extended_arg = 0
if op == dis.EXTENDED_ARG: if op == EXTENDED_ARG:
raise 'TODO' raise 'TODO'
extended_arg = oparg * 65536L extended_arg = oparg * 65536L
continue continue
if op in dis.hasconst: if op in hasconst:
const = co.co_consts[oparg] const = co.co_consts[oparg]
if type(const) == types.CodeType: if type(const) == types.CodeType:
oparg = const oparg = const
if const.co_name == '<lambda>': if const.co_name == '<lambda>':
assert opname == 'LOAD_CONST' assert op_name == 'LOAD_CONST'
opname = 'LOAD_LAMBDA' op_name = 'LOAD_LAMBDA'
elif const.co_name == '<genexpr>': elif const.co_name == '<genexpr>':
opname = 'LOAD_GENEXPR' op_name = 'LOAD_GENEXPR'
elif const.co_name == '<dictcomp>': elif const.co_name == '<dictcomp>':
opname = 'LOAD_DICTCOMP' op_name = 'LOAD_DICTCOMP'
elif const.co_name == '<setcomp>': elif const.co_name == '<setcomp>':
opname = 'LOAD_SETCOMP' op_name = 'LOAD_SETCOMP'
# verify uses 'pattr' for comparism, since 'attr' # verify uses 'pattr' for comparism, since 'attr'
# now holds Code(const) and thus can not be used # now holds Code(const) and thus can not be used
# for comparism (todo: think about changing this) # for comparism (todo: think about changing this)
@@ -191,21 +169,21 @@ class Scanner:
pattr = '<code_object ' + const.co_name + '>' pattr = '<code_object ' + const.co_name + '>'
else: else:
pattr = const pattr = const
elif op in dis.hasname: elif op in hasname:
pattr = names[oparg] pattr = names[oparg]
elif op in dis.hasjrel: elif op in hasjrel:
pattr = repr(offset + 3 + oparg) pattr = repr(offset + 3 + oparg)
elif op in dis.hasjabs: elif op in hasjabs:
pattr = repr(oparg) pattr = repr(oparg)
elif op in dis.haslocal: elif op in haslocal:
pattr = varnames[oparg] pattr = varnames[oparg]
elif op in dis.hascompare: elif op in hascompare:
pattr = dis.cmp_op[oparg] pattr = cmp_op[oparg]
elif op in dis.hasfree: elif op in hasfree:
pattr = free[oparg] pattr = free[oparg]
if offset in self.toChange: if offset in self.toChange:
if self.code[offset] == JA and self.code[oparg] == WITH_CLEANUP: if self.code[offset] == JA and self.code[oparg] == WITH_CLEANUP:
opname = 'SETUP_WITH' op_name = 'SETUP_WITH'
cf[oparg] = cf.get(oparg, []) + [offset] cf[oparg] = cf.get(oparg, []) + [offset]
if op in (BUILD_LIST, BUILD_TUPLE, BUILD_SLICE, if op in (BUILD_LIST, BUILD_TUPLE, BUILD_SLICE,
UNPACK_SEQUENCE, UNPACK_SEQUENCE,
@@ -220,30 +198,30 @@ class Scanner:
self.code[offset-3] == LOAD_CLOSURE: self.code[offset-3] == LOAD_CLOSURE:
continue continue
else: else:
opname = '%s_%d' % (opname, oparg) op_name = '%s_%d' % (op_name, oparg)
if op != BUILD_SLICE: if op != BUILD_SLICE:
customize[opname] = oparg customize[op_name] = oparg
elif op == JA: elif op == JA:
target = self.get_target(offset) target = self.get_target(offset)
if target < offset: if target < offset:
if offset in self.stmts and self.code[offset+3] not in (END_FINALLY, POP_BLOCK) \ if offset in self.stmts and self.code[offset+3] not in (END_FINALLY, POP_BLOCK) \
and offset not in self.not_continue: and offset not in self.not_continue:
opname = 'CONTINUE' op_name = 'CONTINUE'
else: else:
opname = 'JUMP_BACK' op_name = 'JUMP_BACK'
elif op == LOAD_GLOBAL: elif op == LOAD_GLOBAL:
try: try:
if pattr == 'AssertionError' and rv and rv[-1] == 'JUMP_IF_TRUE': if pattr == 'AssertionError' and rv and rv[-1] == 'JUMP_IF_TRUE':
opname = 'LOAD_ASSERT' op_name = 'LOAD_ASSERT'
except AttributeError: except AttributeError:
pass pass
elif op == RETURN_VALUE: elif op == RETURN_VALUE:
if offset in self.return_end_ifs: if offset in self.return_end_ifs:
opname = 'RETURN_END_IF' op_name = 'RETURN_END_IF'
if offset not in replace: if offset not in replace:
rv.append(Token(opname, oparg, pattr, offset, linestart = offset in linestartoffsets)) rv.append(Token(op_name, oparg, pattr, offset, linestart = offset in linestartoffsets))
else: else:
rv.append(Token(replace[offset], oparg, pattr, offset, linestart = offset in linestartoffsets)) rv.append(Token(replace[offset], oparg, pattr, offset, linestart = offset in linestartoffsets))
@@ -255,9 +233,9 @@ class Scanner:
return rv, customize return rv, customize
def getOpcodeToDel(self, i): def getOpcodeToDel(self, i):
""" '''
check validity of the opcode at position I and return a list of opcode to delete check validity of the opcode at position I and return a list of opcode to delete
""" '''
opcode = self.code[i] opcode = self.code[i]
opsize = self.op_size(opcode) opsize = self.op_size(opcode)
if opcode == EXTENDED_ARG: if opcode == EXTENDED_ARG:
@@ -380,10 +358,11 @@ class Scanner:
return None return None
def restructRelativeJump(self): def restructRelativeJump(self):
""" '''
change relative JUMP_IF_FALSE/TRUE to absolut jump change relative JUMP_IF_FALSE/TRUE to absolut jump
and remap the target of PJIF/PJIT and remap the target of PJIF/PJIT
""" '''
for i in self.op_range(0, len(self.code)): for i in self.op_range(0, len(self.code)):
if(self.code[i] in (PJIF,PJIT)): if(self.code[i] in (PJIF,PJIT)):
target = self.get_argument(i) target = self.get_argument(i)
@@ -398,9 +377,9 @@ class Scanner:
self.restructJump(i, target) self.restructJump(i, target)
def restructCode(self, listDel): def restructCode(self, listDel):
""" '''
restruct linestarts and jump destination after removing bad opcode restruct linestarts and jump destination after removing bad opcode
""" '''
result = list() result = list()
for block in self.linestarts: for block in self.linestarts:
@@ -413,21 +392,24 @@ class Scanner:
result.append((block[0]+startBlock, block[1])) result.append((block[0]+startBlock, block[1]))
self.linestarts = result self.linestarts = result
for change in self.toChange: for index in xrange(len(self.toChange)):
change = self.toChange[index]
delta = 0
for toDel in listDel: for toDel in listDel:
if change > toDel: if change > toDel:
self.toChange[self.toChange.index(change)] -= self.op_size(self.code[toDel]) delta += self.op_size(self.code[toDel])
else: else:
break break
self.toChange[index] -= delta
for jmp in self.op_range(0, len(self.code)): for jmp in self.op_range(0, len(self.code)):
op = self.code[jmp] op = self.code[jmp]
if op in dis.hasjrel+dis.hasjabs: # jmp if op in hasjrel+hasjabs: # jmp
offset = 0 offset = 0
jmpTarget = self.get_target(jmp) jmpTarget = self.get_target(jmp)
for toDel in listDel: for toDel in listDel:
if toDel < jmpTarget: if toDel < jmpTarget:
if op in dis.hasjabs: if op in hasjabs:
offset-=self.op_size(self.code[toDel]) offset-=self.op_size(self.code[toDel])
elif jmp < toDel: elif jmp < toDel:
offset-=self.op_size(self.code[toDel]) offset-=self.op_size(self.code[toDel])
@@ -436,7 +418,7 @@ class Scanner:
self.restructJump(jmp, self.get_target(jmp)+offset) self.restructJump(jmp, self.get_target(jmp)+offset)
def restructJump(self, pos, newTarget): def restructJump(self, pos, newTarget):
if not (self.code[pos] in dis.hasjabs+dis.hasjrel): if not (self.code[pos] in hasjabs+hasjrel):
raise 'Can t change this argument. Opcode is not a jump' raise 'Can t change this argument. Opcode is not a jump'
if newTarget > 0xFFFF: if newTarget > 0xFFFF:
raise 'TODO' raise 'TODO'
@@ -447,140 +429,6 @@ class Scanner:
self.code[pos+2] = (target >> 8) & 0xFF self.code[pos+2] = (target >> 8) & 0xFF
self.code[pos+1] = target & 0xFF self.code[pos+1] = target & 0xFF
def get_target(self, pos, op=None):
if op is None:
op = self.code[pos]
target = self.get_argument(pos)
if op in dis.hasjrel:
target += pos + 3
return target
def get_argument(self, pos):
target = self.code[pos+1] + self.code[pos+2] * 256
return target
def print_bytecode(self):
for i in self.op_range(0, len(self.code)):
op = self.code[i]
if op in dis.hasjabs+dis.hasjrel:
dest = self.get_target(i, op)
print '%i\t%s\t%i' % (i, dis.opname[op], dest)
else:
print '%i\t%s\t' % (i, dis.opname[op])
def first_instr(self, start, end, instr, target=None, exact=True):
"""
Find the first <instr> in the block from start to end.
<instr> is any python bytecode instruction or a list of opcodes
If <instr> is an opcode with a target (like a jump), a target
destination can be specified which must match precisely if exact
is True, or if exact is False, the instruction which has a target
closest to <target> will be returned.
Return index to it or None if not found.
"""
code = self.code
assert(start>=0 and end<=len(code))
try: None in instr
except: instr = [instr]
pos = None
distance = len(code)
for i in self.op_range(start, end):
op = code[i]
if op in instr:
if target is None:
return i
dest = self.get_target(i, op)
if dest == target:
return i
elif not exact:
_distance = abs(target - dest)
if _distance < distance:
distance = _distance
pos = i
return pos
def last_instr(self, start, end, instr, target=None, exact=True):
"""
Find the last <instr> in the block from start to end.
<instr> is any python bytecode instruction or a list of opcodes
If <instr> is an opcode with a target (like a jump), a target
destination can be specified which must match precisely if exact
is True, or if exact is False, the instruction which has a target
closest to <target> will be returned.
Return index to it or None if not found.
"""
code = self.code
if not (start>=0 and end<=len(code)):
return None
try: None in instr
except: instr = [instr]
pos = None
distance = len(code)
for i in self.op_range(start, end):
op = code[i]
if op in instr:
if target is None:
pos = i
else:
dest = self.get_target(i, op)
if dest == target:
distance = 0
pos = i
elif not exact:
_distance = abs(target - dest)
if _distance <= distance:
distance = _distance
pos = i
return pos
def all_instr(self, start, end, instr, target=None, include_beyond_target=False):
"""
Find all <instr> in the block from start to end.
<instr> is any python bytecode instruction or a list of opcodes
If <instr> is an opcode with a target (like a jump), a target
destination can be specified which must match precisely.
Return a list with indexes to them or [] if none found.
"""
code = self.code
assert(start>=0 and end<=len(code))
try: None in instr
except: instr = [instr]
result = []
for i in self.op_range(start, end):
op = code[i]
if op in instr:
if target is None:
result.append(i)
else:
t = self.get_target(i, op)
if include_beyond_target and t >= target:
result.append(i)
elif t == target:
result.append(i)
return result
def op_size(self, op):
if op < HAVE_ARGUMENT:
return 1
else:
return 3
def op_range(self, start, end):
while start < end:
yield start
start += self.op_size(self.code[start])
def build_stmt_indices(self): def build_stmt_indices(self):
code = self.code code = self.code
start = 0; start = 0;
@@ -659,61 +507,11 @@ class Scanner:
i = s i = s
slist += [len(code)] * (len(code)-len(slist)) slist += [len(code)] * (len(code)-len(slist))
def remove_mid_line_ifs(self, ifs):
filtered = []
for i in ifs:
if self.lines[i].l_no == self.lines[i+3].l_no:
if self.code[self.prev[self.lines[i].next]] in (PJIT, PJIF):
continue
filtered.append(i)
return filtered
def rem_or(self, start, end, instr, target=None, include_beyond_target=False):
"""
Find all <instr> in the block from start to end.
<instr> is any python bytecode instruction or a list of opcodes
If <instr> is an opcode with a target (like a jump), a target
destination can be specified which must match precisely.
Return a list with indexes to them or [] if none found.
"""
code = self.code
assert(start>=0 and end<=len(code))
try: None in instr
except: instr = [instr]
result = []
for i in self.op_range(start, end):
op = code[i]
if op in instr:
if target is None:
result.append(i)
else:
t = self.get_target(i, op)
if include_beyond_target and t >= target:
result.append(i)
elif t == target:
result.append(i)
pjits = self.all_instr(start, end, PJIT)
filtered = []
for pjit in pjits:
tgt = self.get_target(pjit)-3
for i in result:
if i <= pjit or i >= tgt:
filtered.append(i)
result = filtered
filtered = []
return result
def next_except_jump(self, start): def next_except_jump(self, start):
""" '''
Return the next jump that was generated by an except SomeException: Return the next jump that was generated by an except SomeException:
construct in a try...except...else clause or None if not found. construct in a try...except...else clause or None if not found.
""" '''
except_match = self.first_instr(start, self.lines[start].next, (PJIF)) except_match = self.first_instr(start, self.lines[start].next, (PJIF))
if except_match: if except_match:
jmp = self.prev[self.get_target(except_match)] jmp = self.prev[self.get_target(except_match)]
@@ -735,17 +533,11 @@ class Scanner:
count_SETUP_ += 1 count_SETUP_ += 1
#return self.lines[start].next #return self.lines[start].next
def restrict_to_parent(self, target, parent):
"""Restrict pos to parent boundaries."""
if not (parent['start'] < target < parent['end']):
target = parent['end']
return target
def detect_structure(self, pos, op=None): def detect_structure(self, pos, op=None):
""" '''
Detect type of block structures and their boundaries to fix optimizied jumps Detect type of block structures and their boundaries to fix optimizied jumps
in python2.3+ in python2.3+
""" '''
# TODO: check the struct boundaries more precisely -Dan # TODO: check the struct boundaries more precisely -Dan
@@ -848,7 +640,7 @@ class Scanner:
## Add the except blocks ## Add the except blocks
i = end i = end
while self.code[i] != END_FINALLY: while i < len(self.code) and self.code[i] != END_FINALLY:
jmp = self.next_except_jump(i) jmp = self.next_except_jump(i)
if jmp == None: # check if jmp == None: # check
i = self.next_stmt[i] i = self.next_stmt[i]
@@ -989,17 +781,14 @@ class Scanner:
# self.fixed_jumps[pos] = self.restrict_to_parent(target, parent) # self.fixed_jumps[pos] = self.restrict_to_parent(target, parent)
def find_jump_targets(self, code): def find_jump_targets(self, code):
""" '''
Detect all offsets in a byte code which are jump targets. Detect all offsets in a byte code which are jump targets.
Return the list of offsets. Return the list of offsets.
This procedure is modelled after dis.findlables(), but here This procedure is modelled after dis.findlables(), but here
for each target the number of jumps are counted. for each target the number of jumps are counted.
""" '''
hasjrel = dis.hasjrel
hasjabs = dis.hasjabs
n = len(code) n = len(code)
self.structs = [{'type': 'root', self.structs = [{'type': 'root',
@@ -1035,4 +824,3 @@ class Scanner:
label = self.fixed_jumps[i] label = self.fixed_jumps[i]
targets[label] = targets.get(label, []) + [i] targets[label] = targets.get(label, []) + [i]
return targets return targets

View File

@@ -1,54 +1,33 @@
# Copyright (c) 1999 John Aycock '''
# Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de> Copyright (c) 1999 John Aycock
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org> Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de>
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# See main module for license.
#
__all__ = ['Token', 'Scanner', 'getscanner'] See main module for license.
'''
import types import types
import disas as dis
from collections import namedtuple from collections import namedtuple
from array import array from array import array
from operator import itemgetter from operator import itemgetter
from struct import * from struct import *
from Scanner import Token, Code
class Scanner: from uncompyle2.opcode.opcode_26 import *
def __init__(self, version): import disas as dis
self.version = version import scanner as scan
self.resetTokenClass()
dis.setVersion(version) class Scanner26(scan.Scanner):
globals().update({'HAVE_ARGUMENT': dis.HAVE_ARGUMENT}) def __init__(self):
globals().update({k.replace('+','_'):v for (k,v) in dis.opmap.items()}) self.Token = scan.Scanner.__init__(self, 2.6)
globals().update({'PJIF': dis.opmap['JUMP_IF_FALSE']})
globals().update({'PJIT': dis.opmap['JUMP_IF_TRUE']})
globals().update({'JA': dis.opmap['JUMP_ABSOLUTE']})
globals().update({'JF': dis.opmap['JUMP_FORWARD']})
self.JUMP_OPs = map(lambda op: dis.opname[op],
dis.hasjrel + dis.hasjabs)
def setShowAsm(self, showasm, out=None):
self.showasm = showasm
self.out = out
def setTokenClass(self, tokenClass):
assert type(tokenClass) == types.ClassType
self.Token = tokenClass
def resetTokenClass(self):
self.setTokenClass(Token)
def disassemble(self, co, classname=None): def disassemble(self, co, classname=None):
""" '''
Disassemble a code object, returning a list of 'Token'. Disassemble a code object, returning a list of 'Token'.
The main part of this procedure is modelled after The main part of this procedure is modelled after
dis.disassemble(). dis.disassemble().
""" '''
rv = [] rv = []
customize = {} customize = {}
Token = self.Token # shortcut Token = self.Token # shortcut
@@ -59,7 +38,6 @@ class Scanner:
self.prev = [0] self.prev = [0]
# change jump struct # change jump struct
self.restructRelativeJump() self.restructRelativeJump()
# class and names # class and names
if classname: if classname:
classname = '_' + classname.lstrip('_') + '__' classname = '_' + classname.lstrip('_') + '__'
@@ -91,7 +69,7 @@ class Scanner:
delta = 0 delta = 0
self.restructCode(toDel) self.restructCode(toDel)
for x in toDel: for x in toDel:
if self.code[x-delta] >= dis.HAVE_ARGUMENT: if self.code[x-delta] >= HAVE_ARGUMENT:
self.code.pop(x-delta) self.code.pop(x-delta)
self.code.pop(x-delta) self.code.pop(x-delta)
self.code.pop(x-delta) self.code.pop(x-delta)
@@ -154,7 +132,7 @@ class Scanner:
extended_arg = 0 extended_arg = 0
for offset in self.op_range(0, n): for offset in self.op_range(0, n):
op = self.code[offset] op = self.code[offset]
opname = dis.opname[op] op_name = opname[op]
oparg = None; pattr = None oparg = None; pattr = None
if offset in cf: if offset in cf:
@@ -166,23 +144,23 @@ class Scanner:
if op >= HAVE_ARGUMENT: if op >= HAVE_ARGUMENT:
oparg = self.get_argument(offset) + extended_arg oparg = self.get_argument(offset) + extended_arg
extended_arg = 0 extended_arg = 0
if op == dis.EXTENDED_ARG: if op == EXTENDED_ARG:
raise 'TODO' raise 'TODO'
extended_arg = oparg * 65536L extended_arg = oparg * 65536L
continue continue
if op in dis.hasconst: if op in hasconst:
const = co.co_consts[oparg] const = co.co_consts[oparg]
if type(const) == types.CodeType: if type(const) == types.CodeType:
oparg = const oparg = const
if const.co_name == '<lambda>': if const.co_name == '<lambda>':
assert opname == 'LOAD_CONST' assert op_name == 'LOAD_CONST'
opname = 'LOAD_LAMBDA' op_name = 'LOAD_LAMBDA'
elif const.co_name == '<genexpr>': elif const.co_name == '<genexpr>':
opname = 'LOAD_GENEXPR' op_name = 'LOAD_GENEXPR'
elif const.co_name == '<dictcomp>': elif const.co_name == '<dictcomp>':
opname = 'LOAD_DICTCOMP' op_name = 'LOAD_DICTCOMP'
elif const.co_name == '<setcomp>': elif const.co_name == '<setcomp>':
opname = 'LOAD_SETCOMP' op_name = 'LOAD_SETCOMP'
# verify uses 'pattr' for comparism, since 'attr' # verify uses 'pattr' for comparism, since 'attr'
# now holds Code(const) and thus can not be used # now holds Code(const) and thus can not be used
# for comparism (todo: think about changing this) # for comparism (todo: think about changing this)
@@ -191,21 +169,21 @@ class Scanner:
pattr = '<code_object ' + const.co_name + '>' pattr = '<code_object ' + const.co_name + '>'
else: else:
pattr = const pattr = const
elif op in dis.hasname: elif op in hasname:
pattr = names[oparg] pattr = names[oparg]
elif op in dis.hasjrel: elif op in hasjrel:
pattr = repr(offset + 3 + oparg) pattr = repr(offset + 3 + oparg)
elif op in dis.hasjabs: elif op in hasjabs:
pattr = repr(oparg) pattr = repr(oparg)
elif op in dis.haslocal: elif op in haslocal:
pattr = varnames[oparg] pattr = varnames[oparg]
elif op in dis.hascompare: elif op in hascompare:
pattr = dis.cmp_op[oparg] pattr = cmp_op[oparg]
elif op in dis.hasfree: elif op in hasfree:
pattr = free[oparg] pattr = free[oparg]
if offset in self.toChange: if offset in self.toChange:
if self.code[offset] == JA and self.code[oparg] == WITH_CLEANUP: if self.code[offset] == JA and self.code[oparg] == WITH_CLEANUP:
opname = 'SETUP_WITH' op_name = 'SETUP_WITH'
cf[oparg] = cf.get(oparg, []) + [offset] cf[oparg] = cf.get(oparg, []) + [offset]
if op in (BUILD_LIST, BUILD_TUPLE, BUILD_SLICE, if op in (BUILD_LIST, BUILD_TUPLE, BUILD_SLICE,
UNPACK_SEQUENCE, UNPACK_SEQUENCE,
@@ -220,30 +198,30 @@ class Scanner:
self.code[offset-3] == LOAD_CLOSURE: self.code[offset-3] == LOAD_CLOSURE:
continue continue
else: else:
opname = '%s_%d' % (opname, oparg) op_name = '%s_%d' % (op_name, oparg)
if op != BUILD_SLICE: if op != BUILD_SLICE:
customize[opname] = oparg customize[op_name] = oparg
elif op == JA: elif op == JA:
target = self.get_target(offset) target = self.get_target(offset)
if target < offset: if target < offset:
if offset in self.stmts and self.code[offset+3] not in (END_FINALLY, POP_BLOCK) \ if offset in self.stmts and self.code[offset+3] not in (END_FINALLY, POP_BLOCK) \
and offset not in self.not_continue: and offset not in self.not_continue:
opname = 'CONTINUE' op_name = 'CONTINUE'
else: else:
opname = 'JUMP_BACK' op_name = 'JUMP_BACK'
elif op == LOAD_GLOBAL: elif op == LOAD_GLOBAL:
try: try:
if pattr == 'AssertionError' and rv and rv[-1] == 'JUMP_IF_TRUE': if pattr == 'AssertionError' and rv and rv[-1] == 'JUMP_IF_TRUE':
opname = 'LOAD_ASSERT' op_name = 'LOAD_ASSERT'
except AttributeError: except AttributeError:
pass pass
elif op == RETURN_VALUE: elif op == RETURN_VALUE:
if offset in self.return_end_ifs: if offset in self.return_end_ifs:
opname = 'RETURN_END_IF' op_name = 'RETURN_END_IF'
if offset not in replace: if offset not in replace:
rv.append(Token(opname, oparg, pattr, offset, linestart = offset in linestartoffsets)) rv.append(Token(op_name, oparg, pattr, offset, linestart = offset in linestartoffsets))
else: else:
rv.append(Token(replace[offset], oparg, pattr, offset, linestart = offset in linestartoffsets)) rv.append(Token(replace[offset], oparg, pattr, offset, linestart = offset in linestartoffsets))
@@ -255,9 +233,9 @@ class Scanner:
return rv, customize return rv, customize
def getOpcodeToDel(self, i): def getOpcodeToDel(self, i):
""" '''
check validity of the opcode at position I and return a list of opcode to delete check validity of the opcode at position I and return a list of opcode to delete
""" '''
opcode = self.code[i] opcode = self.code[i]
opsize = self.op_size(opcode) opsize = self.op_size(opcode)
@@ -276,12 +254,13 @@ class Scanner:
pass pass
else: else:
toDel += [i+opsize] toDel += [i+opsize]
# conditional tuple # conditional tuple (not optimal at all, no good solution...)
if self.code[i] == JA and self.code[i+opsize] == POP_TOP \ if self.code[i] == JA and self.code[i+opsize] == POP_TOP \
and self.code[i+opsize+1] == JA and self.code[i+opsize+4] == POP_BLOCK: and self.code[i+opsize+1] == JA and self.code[i+opsize+4] == POP_BLOCK:
jmpabs1target = self.get_target(i) jmpabs1target = self.get_target(i)
jmpabs2target = self.get_target(i+opsize+1) jmpabs2target = self.get_target(i+opsize+1)
if jmpabs1target == jmpabs2target and self.code[jmpabs1target] == FOR_ITER: if jmpabs1target == jmpabs2target and self.code[jmpabs1target] == FOR_ITER \
and self.code[jmpabs1target-1] != GET_ITER:
destFor = self.get_target(jmpabs1target) destFor = self.get_target(jmpabs1target)
if destFor == i+opsize+4: if destFor == i+opsize+4:
setupLoop = self.last_instr(0, jmpabs1target, SETUP_LOOP) setupLoop = self.last_instr(0, jmpabs1target, SETUP_LOOP)
@@ -289,6 +268,7 @@ class Scanner:
if standarFor == None: if standarFor == None:
self.restructJump(jmpabs1target, destFor+self.op_size(POP_BLOCK)) self.restructJump(jmpabs1target, destFor+self.op_size(POP_BLOCK))
toDel += [setupLoop, i+opsize+1, i+opsize+4] toDel += [setupLoop, i+opsize+1, i+opsize+4]
if len(toDel) > 0: if len(toDel) > 0:
return toDel return toDel
return None return None
@@ -377,10 +357,10 @@ class Scanner:
return None return None
def restructRelativeJump(self): def restructRelativeJump(self):
""" '''
change relative JUMP_IF_FALSE/TRUE to absolut jump change relative JUMP_IF_FALSE/TRUE to absolut jump
and remap the target of PJIF/PJIT and remap the target of PJIF/PJIT
""" '''
for i in self.op_range(0, len(self.code)): for i in self.op_range(0, len(self.code)):
if(self.code[i] in (PJIF,PJIT)): if(self.code[i] in (PJIF,PJIT)):
target = self.get_argument(i) target = self.get_argument(i)
@@ -395,9 +375,9 @@ class Scanner:
self.restructJump(i, target) self.restructJump(i, target)
def restructCode(self, listDel): def restructCode(self, listDel):
""" '''
restruct linestarts and jump destination after removing a POP_TOP restruct linestarts and jump destination after removing a POP_TOP
""" '''
result = list() result = list()
for block in self.linestarts: for block in self.linestarts:
startBlock = 0 startBlock = 0
@@ -409,21 +389,24 @@ class Scanner:
result.append((block[0]+startBlock, block[1])) result.append((block[0]+startBlock, block[1]))
self.linestarts = result self.linestarts = result
for change in self.toChange: for index in xrange(len(self.toChange)):
change = self.toChange[index]
delta = 0
for toDel in listDel: for toDel in listDel:
if change > toDel: if change > toDel:
self.toChange[self.toChange.index(change)] -= self.op_size(self.code[toDel]) delta += self.op_size(self.code[toDel])
else: else:
break break
self.toChange[index] -= delta
for jmp in self.op_range(0, len(self.code)): for jmp in self.op_range(0, len(self.code)):
op = self.code[jmp] op = self.code[jmp]
if op in dis.hasjrel+dis.hasjabs: # jmp if op in hasjrel+hasjabs: # jmp
offset = 0 offset = 0
jmpTarget = self.get_target(jmp) jmpTarget = self.get_target(jmp)
for toDel in listDel: for toDel in listDel:
if toDel < jmpTarget: if toDel < jmpTarget:
if op in dis.hasjabs: if op in hasjabs:
offset-=self.op_size(self.code[toDel]) offset-=self.op_size(self.code[toDel])
elif jmp < toDel: elif jmp < toDel:
offset-=self.op_size(self.code[toDel]) offset-=self.op_size(self.code[toDel])
@@ -432,7 +415,7 @@ class Scanner:
self.restructJump(jmp, self.get_target(jmp)+offset) self.restructJump(jmp, self.get_target(jmp)+offset)
def restructJump(self, pos, newTarget): def restructJump(self, pos, newTarget):
if not (self.code[pos] in dis.hasjabs+dis.hasjrel): if not (self.code[pos] in hasjabs+hasjrel):
raise 'Can t change this argument. Opcode is not a jump' raise 'Can t change this argument. Opcode is not a jump'
if newTarget > 0xFFFF: if newTarget > 0xFFFF:
raise 'TODO' raise 'TODO'
@@ -443,140 +426,6 @@ class Scanner:
self.code[pos+2] = (target >> 8) & 0xFF self.code[pos+2] = (target >> 8) & 0xFF
self.code[pos+1] = target & 0xFF self.code[pos+1] = target & 0xFF
def get_target(self, pos, op=None):
if op is None:
op = self.code[pos]
target = self.get_argument(pos)
if op in dis.hasjrel:
target += pos + 3
return target
def get_argument(self, pos):
target = self.code[pos+1] + self.code[pos+2] * 256
return target
def print_bytecode(self):
for i in self.op_range(0, len(self.code)):
op = self.code[i]
if op in dis.hasjabs+dis.hasjrel:
dest = self.get_target(i, op)
print '%i\t%s\t%i' % (i, dis.opname[op], dest)
else:
print '%i\t%s\t' % (i, dis.opname[op])
def first_instr(self, start, end, instr, target=None, exact=True):
"""
Find the first <instr> in the block from start to end.
<instr> is any python bytecode instruction or a list of opcodes
If <instr> is an opcode with a target (like a jump), a target
destination can be specified which must match precisely if exact
is True, or if exact is False, the instruction which has a target
closest to <target> will be returned.
Return index to it or None if not found.
"""
code = self.code
assert(start>=0 and end<=len(code))
try: None in instr
except: instr = [instr]
pos = None
distance = len(code)
for i in self.op_range(start, end):
op = code[i]
if op in instr:
if target is None:
return i
dest = self.get_target(i, op)
if dest == target:
return i
elif not exact:
_distance = abs(target - dest)
if _distance < distance:
distance = _distance
pos = i
return pos
def last_instr(self, start, end, instr, target=None, exact=True):
"""
Find the last <instr> in the block from start to end.
<instr> is any python bytecode instruction or a list of opcodes
If <instr> is an opcode with a target (like a jump), a target
destination can be specified which must match precisely if exact
is True, or if exact is False, the instruction which has a target
closest to <target> will be returned.
Return index to it or None if not found.
"""
code = self.code
if not (start>=0 and end<=len(code)):
return None
try: None in instr
except: instr = [instr]
pos = None
distance = len(code)
for i in self.op_range(start, end):
op = code[i]
if op in instr:
if target is None:
pos = i
else:
dest = self.get_target(i, op)
if dest == target:
distance = 0
pos = i
elif not exact:
_distance = abs(target - dest)
if _distance <= distance:
distance = _distance
pos = i
return pos
def all_instr(self, start, end, instr, target=None, include_beyond_target=False):
"""
Find all <instr> in the block from start to end.
<instr> is any python bytecode instruction or a list of opcodes
If <instr> is an opcode with a target (like a jump), a target
destination can be specified which must match precisely.
Return a list with indexes to them or [] if none found.
"""
code = self.code
assert(start>=0 and end<=len(code))
try: None in instr
except: instr = [instr]
result = []
for i in self.op_range(start, end):
op = code[i]
if op in instr:
if target is None:
result.append(i)
else:
t = self.get_target(i, op)
if include_beyond_target and t >= target:
result.append(i)
elif t == target:
result.append(i)
return result
def op_size(self, op):
if op < HAVE_ARGUMENT:
return 1
else:
return 3
def op_range(self, start, end):
while start < end:
yield start
start += self.op_size(self.code[start])
def build_stmt_indices(self): def build_stmt_indices(self):
code = self.code code = self.code
start = 0; start = 0;
@@ -655,61 +504,11 @@ class Scanner:
i = s i = s
slist += [len(code)] * (len(code)-len(slist)) slist += [len(code)] * (len(code)-len(slist))
def remove_mid_line_ifs(self, ifs):
filtered = []
for i in ifs:
if self.lines[i].l_no == self.lines[i+3].l_no:
if self.code[self.prev[self.lines[i].next]] in (PJIT, PJIF):
continue
filtered.append(i)
return filtered
def rem_or(self, start, end, instr, target=None, include_beyond_target=False):
"""
Find all <instr> in the block from start to end.
<instr> is any python bytecode instruction or a list of opcodes
If <instr> is an opcode with a target (like a jump), a target
destination can be specified which must match precisely.
Return a list with indexes to them or [] if none found.
"""
code = self.code
assert(start>=0 and end<=len(code))
try: None in instr
except: instr = [instr]
result = []
for i in self.op_range(start, end):
op = code[i]
if op in instr:
if target is None:
result.append(i)
else:
t = self.get_target(i, op)
if include_beyond_target and t >= target:
result.append(i)
elif t == target:
result.append(i)
pjits = self.all_instr(start, end, PJIT)
filtered = []
for pjit in pjits:
tgt = self.get_target(pjit)-3
for i in result:
if i <= pjit or i >= tgt:
filtered.append(i)
result = filtered
filtered = []
return result
def next_except_jump(self, start): def next_except_jump(self, start):
""" '''
Return the next jump that was generated by an except SomeException: Return the next jump that was generated by an except SomeException:
construct in a try...except...else clause or None if not found. construct in a try...except...else clause or None if not found.
""" '''
except_match = self.first_instr(start, self.lines[start].next, (PJIF)) except_match = self.first_instr(start, self.lines[start].next, (PJIF))
if except_match: if except_match:
jmp = self.prev[self.get_target(except_match)] jmp = self.prev[self.get_target(except_match)]
@@ -731,17 +530,11 @@ class Scanner:
count_SETUP_ += 1 count_SETUP_ += 1
#return self.lines[start].next #return self.lines[start].next
def restrict_to_parent(self, target, parent):
"""Restrict pos to parent boundaries."""
if not (parent['start'] < target < parent['end']):
target = parent['end']
return target
def detect_structure(self, pos, op=None): def detect_structure(self, pos, op=None):
""" '''
Detect type of block structures and their boundaries to fix optimizied jumps Detect type of block structures and their boundaries to fix optimizied jumps
in python2.3+ in python2.3+
""" '''
# TODO: check the struct boundaries more precisely -Dan # TODO: check the struct boundaries more precisely -Dan
@@ -844,7 +637,7 @@ class Scanner:
## Add the except blocks ## Add the except blocks
i = end i = end
while self.code[i] != END_FINALLY: while i < len(self.code) and self.code[i] != END_FINALLY:
jmp = self.next_except_jump(i) jmp = self.next_except_jump(i)
if jmp == None: # check if jmp == None: # check
i = self.next_stmt[i] i = self.next_stmt[i]
@@ -985,17 +778,14 @@ class Scanner:
# self.fixed_jumps[pos] = self.restrict_to_parent(target, parent) # self.fixed_jumps[pos] = self.restrict_to_parent(target, parent)
def find_jump_targets(self, code): def find_jump_targets(self, code):
""" '''
Detect all offsets in a byte code which are jump targets. Detect all offsets in a byte code which are jump targets.
Return the list of offsets. Return the list of offsets.
This procedure is modelled after dis.findlables(), but here This procedure is modelled after dis.findlables(), but here
for each target the number of jumps are counted. for each target the number of jumps are counted.
""" '''
hasjrel = dis.hasjrel
hasjabs = dis.hasjabs
n = len(code) n = len(code)
self.structs = [{'type': 'root', self.structs = [{'type': 'root',

View File

@@ -1,53 +1,31 @@
# Copyright (c) 1999 John Aycock '''
# Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de> Copyright (c) 1999 John Aycock
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org> Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de>
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# See main module for license.
#
__all__ = ['Token', 'Scanner', 'getscanner'] See main module for license.
'''
import types import types
import disas as dis
from collections import namedtuple from collections import namedtuple
from array import array from array import array
from operator import itemgetter from operator import itemgetter
from Scanner import Token, Code
class Scanner: from uncompyle2.opcode.opcode_27 import *
def __init__(self, version): import disas as dis
self.version = version import scanner as scan
self.resetTokenClass()
dis.setVersion(version) class Scanner27(scan.Scanner):
globals().update({'HAVE_ARGUMENT': dis.HAVE_ARGUMENT}) def __init__(self):
globals().update({k.replace('+','_'):v for (k,v) in dis.opmap.items()}) self.Token = scan.Scanner.__init__(self, 2.6)
globals().update({'PJIF': dis.opmap['POP_JUMP_IF_FALSE']})
globals().update({'PJIT': dis.opmap['POP_JUMP_IF_TRUE']})
globals().update({'JA': dis.opmap['JUMP_ABSOLUTE']})
globals().update({'JF': dis.opmap['JUMP_FORWARD']})
self.JUMP_OPs = map(lambda op: dis.opname[op],
dis.hasjrel + dis.hasjabs)
def setShowAsm(self, showasm, out=None):
self.showasm = showasm
self.out = out
def setTokenClass(self, tokenClass):
assert type(tokenClass) == types.ClassType
self.Token = tokenClass
def resetTokenClass(self):
self.setTokenClass(Token)
def disassemble(self, co, classname=None): def disassemble(self, co, classname=None):
""" '''
Disassemble a code object, returning a list of 'Token'. Disassemble a code object, returning a list of 'Token'.
The main part of this procedure is modelled after The main part of this procedure is modelled after
dis.disassemble(). dis.disassemble().
""" '''
rv = [] rv = []
customize = {} customize = {}
Token = self.Token # shortcut Token = self.Token # shortcut
@@ -129,27 +107,27 @@ class Scanner:
k += 1 k += 1
op = code[offset] op = code[offset]
opname = dis.opname[op] op_name = opname[op]
oparg = None; pattr = None oparg = None; pattr = None
if op >= HAVE_ARGUMENT: if op >= HAVE_ARGUMENT:
oparg = code[offset+1] + code[offset+2] * 256 + extended_arg oparg = code[offset+1] + code[offset+2] * 256 + extended_arg
extended_arg = 0 extended_arg = 0
if op == dis.EXTENDED_ARG: if op == EXTENDED_ARG:
extended_arg = oparg * 65536L extended_arg = oparg * 65536L
continue continue
if op in dis.hasconst: if op in hasconst:
const = co.co_consts[oparg] const = co.co_consts[oparg]
if type(const) == types.CodeType: if type(const) == types.CodeType:
oparg = const oparg = const
if const.co_name == '<lambda>': if const.co_name == '<lambda>':
assert opname == 'LOAD_CONST' assert op_name == 'LOAD_CONST'
opname = 'LOAD_LAMBDA' op_name = 'LOAD_LAMBDA'
elif const.co_name == '<genexpr>': elif const.co_name == '<genexpr>':
opname = 'LOAD_GENEXPR' op_name = 'LOAD_GENEXPR'
elif const.co_name == '<dictcomp>': elif const.co_name == '<dictcomp>':
opname = 'LOAD_DICTCOMP' op_name = 'LOAD_DICTCOMP'
elif const.co_name == '<setcomp>': elif const.co_name == '<setcomp>':
opname = 'LOAD_SETCOMP' op_name = 'LOAD_SETCOMP'
# verify uses 'pattr' for comparism, since 'attr' # verify uses 'pattr' for comparism, since 'attr'
# now holds Code(const) and thus can not be used # now holds Code(const) and thus can not be used
# for comparism (todo: think about changing this) # for comparism (todo: think about changing this)
@@ -158,17 +136,17 @@ class Scanner:
pattr = '<code_object ' + const.co_name + '>' pattr = '<code_object ' + const.co_name + '>'
else: else:
pattr = const pattr = const
elif op in dis.hasname: elif op in hasname:
pattr = names[oparg] pattr = names[oparg]
elif op in dis.hasjrel: elif op in hasjrel:
pattr = repr(offset + 3 + oparg) pattr = repr(offset + 3 + oparg)
elif op in dis.hasjabs: elif op in hasjabs:
pattr = repr(oparg) pattr = repr(oparg)
elif op in dis.haslocal: elif op in haslocal:
pattr = varnames[oparg] pattr = varnames[oparg]
elif op in dis.hascompare: elif op in hascompare:
pattr = dis.cmp_op[oparg] pattr = cmp_op[oparg]
elif op in dis.hasfree: elif op in hasfree:
pattr = free[oparg] pattr = free[oparg]
if op in (BUILD_LIST, BUILD_TUPLE, BUILD_SET, BUILD_SLICE, if op in (BUILD_LIST, BUILD_TUPLE, BUILD_SET, BUILD_SLICE,
@@ -184,30 +162,30 @@ class Scanner:
code[offset-3] == LOAD_CLOSURE: code[offset-3] == LOAD_CLOSURE:
continue continue
else: else:
opname = '%s_%d' % (opname, oparg) op_name = '%s_%d' % (op_name, oparg)
if op != BUILD_SLICE: if op != BUILD_SLICE:
customize[opname] = oparg customize[op_name] = oparg
elif op == JA: elif op == JA:
target = self.get_target(offset) target = self.get_target(offset)
if target < offset: if target < offset:
if offset in self.stmts and code[offset+3] not in (END_FINALLY, POP_BLOCK) \ if offset in self.stmts and code[offset+3] not in (END_FINALLY, POP_BLOCK) \
and offset not in self.not_continue: and offset not in self.not_continue:
opname = 'CONTINUE' op_name = 'CONTINUE'
else: else:
opname = 'JUMP_BACK' op_name = 'JUMP_BACK'
elif op == LOAD_GLOBAL: elif op == LOAD_GLOBAL:
try: try:
if pattr == 'AssertionError' and rv and rv[-1] == 'POP_JUMP_IF_TRUE': if pattr == 'AssertionError' and rv and rv[-1] == 'POP_JUMP_IF_TRUE':
opname = 'LOAD_ASSERT' op_name = 'LOAD_ASSERT'
except AttributeError: except AttributeError:
pass pass
elif op == RETURN_VALUE: elif op == RETURN_VALUE:
if offset in self.return_end_ifs: if offset in self.return_end_ifs:
opname = 'RETURN_END_IF' op_name = 'RETURN_END_IF'
if offset not in replace: if offset not in replace:
rv.append(Token(opname, oparg, pattr, offset, linestart = offset in linestartoffsets)) rv.append(Token(op_name, oparg, pattr, offset, linestart = offset in linestartoffsets))
else: else:
rv.append(Token(replace[offset], oparg, pattr, offset, linestart = offset in linestartoffsets)) rv.append(Token(replace[offset], oparg, pattr, offset, linestart = offset in linestartoffsets))
@@ -218,127 +196,6 @@ class Scanner:
print >>out print >>out
return rv, customize return rv, customize
def get_target(self, pos, op=None):
if op is None:
op = self.code[pos]
target = self.code[pos+1] + self.code[pos+2] * 256
if op in dis.hasjrel:
target += pos + 3
return target
def first_instr(self, start, end, instr, target=None, exact=True):
"""
Find the first <instr> in the block from start to end.
<instr> is any python bytecode instruction or a list of opcodes
If <instr> is an opcode with a target (like a jump), a target
destination can be specified which must match precisely if exact
is True, or if exact is False, the instruction which has a target
closest to <target> will be returned.
Return index to it or None if not found.
"""
code = self.code
assert(start>=0 and end<=len(code))
try: None in instr
except: instr = [instr]
pos = None
distance = len(code)
for i in self.op_range(start, end):
op = code[i]
if op in instr:
if target is None:
return i
dest = self.get_target(i, op)
if dest == target:
return i
elif not exact:
_distance = abs(target - dest)
if _distance < distance:
distance = _distance
pos = i
return pos
def last_instr(self, start, end, instr, target=None, exact=True):
"""
Find the last <instr> in the block from start to end.
<instr> is any python bytecode instruction or a list of opcodes
If <instr> is an opcode with a target (like a jump), a target
destination can be specified which must match precisely if exact
is True, or if exact is False, the instruction which has a target
closest to <target> will be returned.
Return index to it or None if not found.
"""
code = self.code
if not (start>=0 and end<=len(code)):
return None
try: None in instr
except: instr = [instr]
pos = None
distance = len(code)
for i in self.op_range(start, end):
op = code[i]
if op in instr:
if target is None:
pos = i
else:
dest = self.get_target(i, op)
if dest == target:
distance = 0
pos = i
elif not exact:
_distance = abs(target - dest)
if _distance <= distance:
distance = _distance
pos = i
return pos
def all_instr(self, start, end, instr, target=None, include_beyond_target=False):
"""
Find all <instr> in the block from start to end.
<instr> is any python bytecode instruction or a list of opcodes
If <instr> is an opcode with a target (like a jump), a target
destination can be specified which must match precisely.
Return a list with indexes to them or [] if none found.
"""
code = self.code
assert(start>=0 and end<=len(code))
try: None in instr
except: instr = [instr]
result = []
for i in self.op_range(start, end):
op = code[i]
if op in instr:
if target is None:
result.append(i)
else:
t = self.get_target(i, op)
if include_beyond_target and t >= target:
result.append(i)
elif t == target:
result.append(i)
return result
def op_size(self, op):
if op < HAVE_ARGUMENT:
return 1
else:
return 3
def op_range(self, start, end):
while start < end:
yield start
start += self.op_size(self.code[start])
def build_stmt_indices(self): def build_stmt_indices(self):
code = self.code code = self.code
start = 0; start = 0;
@@ -428,52 +285,11 @@ class Scanner:
filtered.append(i) filtered.append(i)
return filtered return filtered
def rem_or(self, start, end, instr, target=None, include_beyond_target=False):
"""
Find all <instr> in the block from start to end.
<instr> is any python bytecode instruction or a list of opcodes
If <instr> is an opcode with a target (like a jump), a target
destination can be specified which must match precisely.
Return a list with indexes to them or [] if none found.
"""
code = self.code
assert(start>=0 and end<=len(code))
try: None in instr
except: instr = [instr]
result = []
for i in self.op_range(start, end):
op = code[i]
if op in instr:
if target is None:
result.append(i)
else:
t = self.get_target(i, op)
if include_beyond_target and t >= target:
result.append(i)
elif t == target:
result.append(i)
pjits = self.all_instr(start, end, PJIT)
filtered = []
for pjit in pjits:
tgt = self.get_target(pjit)-3
for i in result:
if i <= pjit or i >= tgt:
filtered.append(i)
result = filtered
filtered = []
return result
def next_except_jump(self, start): def next_except_jump(self, start):
""" '''
Return the next jump that was generated by an except SomeException: Return the next jump that was generated by an except SomeException:
construct in a try...except...else clause or None if not found. construct in a try...except...else clause or None if not found.
""" '''
except_match = self.first_instr(start, self.lines[start].next, POP_JUMP_IF_FALSE) except_match = self.first_instr(start, self.lines[start].next, POP_JUMP_IF_FALSE)
if except_match: if except_match:
@@ -493,17 +309,11 @@ class Scanner:
elif op in (SETUP_EXCEPT, SETUP_WITH, SETUP_FINALLY): elif op in (SETUP_EXCEPT, SETUP_WITH, SETUP_FINALLY):
count_SETUP_ += 1 count_SETUP_ += 1
def restrict_to_parent(self, target, parent):
"""Restrict pos to parent boundaries."""
if not (parent['start'] < target < parent['end']):
target = parent['end']
return target
def detect_structure(self, pos, op=None): def detect_structure(self, pos, op=None):
""" '''
Detect type of block structures and their boundaries to fix optimizied jumps Detect type of block structures and their boundaries to fix optimizied jumps
in python2.3+ in python2.3+
""" '''
# TODO: check the struct boundaries more precisely -Dan # TODO: check the struct boundaries more precisely -Dan
@@ -751,17 +561,14 @@ class Scanner:
def find_jump_targets(self, code): def find_jump_targets(self, code):
""" '''
Detect all offsets in a byte code which are jump targets. Detect all offsets in a byte code which are jump targets.
Return the list of offsets. Return the list of offsets.
This procedure is modelled after dis.findlables(), but here This procedure is modelled after dis.findlables(), but here
for each target the number of jumps are counted. for each target the number of jumps are counted.
""" '''
hasjrel = dis.hasjrel
hasjabs = dis.hasjabs
n = len(code) n = len(code)
self.structs = [{'type': 'root', self.structs = [{'type': 'root',

View File

@@ -1,23 +1,25 @@
# Copyright (c) 1998-2002 John Aycock '''
# Copyright (c) 1998-2002 John Aycock
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the Permission is hereby granted, free of charge, to any person obtaining
# "Software"), to deal in the Software without restriction, including a copy of this software and associated documentation files (the
# without limitation the rights to use, copy, modify, merge, publish, "Software"), to deal in the Software without restriction, including
# distribute, sublicense, and/or sell copies of the Software, and to without limitation the rights to use, copy, modify, merge, publish,
# permit persons to whom the Software is furnished to do so, subject to distribute, sublicense, and/or sell copies of the Software, and to
# the following conditions: permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software. The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
'''
__version__ = 'SPARK-0.7 (pre-alpha-7) uncompyle trim' __version__ = 'SPARK-0.7 (pre-alpha-7) uncompyle trim'

View File

@@ -7,7 +7,7 @@
import types import types
import operator import operator
import dis import dis
import uncompyle2, Scanner import uncompyle2, scanner
BIN_OP_FUNCS = { BIN_OP_FUNCS = {
'BINARY_POWER': operator.pow, 'BINARY_POWER': operator.pow,
@@ -284,7 +284,7 @@ def cmp_code_objects(version, code_obj1, code_obj2, name=''):
getattr(code_obj1,member), getattr(code_obj1,member),
getattr(code_obj2,member)) getattr(code_obj2,member))
class Token(Scanner.Token): class Token(scanner.Token):
"""Token class with changed semantics for 'cmp()'.""" """Token class with changed semantics for 'cmp()'."""
def __cmp__(self, o): def __cmp__(self, o):

View File

@@ -1,51 +1,52 @@
# Copyright (c) 1999 John Aycock '''
# Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de> Copyright (c) 1999 John Aycock
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org> Copyright (c) 2000-2002 by hartmut Goebel <hartmut@goebel.noris.de>
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# See main module for license.
# See main module for license.
#
# Decompilation (walking AST)
# Decompilation (walking AST)
# All table-driven. Step 1 determines a table (T) and a path to a
# table key (K) from the node type (N) (other nodes are shown as O): All table-driven. Step 1 determines a table (T) and a path to a
# table key (K) from the node type (N) (other nodes are shown as O):
# N N N&K
# / | ... \ / | ... \ / | ... \ N N N&K
# O O O O O K O O O / | ... \ / | ... \ / | ... \
# | O O O O O K O O O
# K |
# K
# MAP_R0 (TABLE_R0) MAP_R (TABLE_R) MAP_DIRECT (TABLE_DIRECT)
# MAP_R0 (TABLE_R0) MAP_R (TABLE_R) MAP_DIRECT (TABLE_DIRECT)
# The default is a direct mapping. The key K is then extracted from the
# subtree and used to find a table entry T[K], if any. The result is a The default is a direct mapping. The key K is then extracted from the
# format string and arguments (a la printf()) for the formatting engine. subtree and used to find a table entry T[K], if any. The result is a
# Escapes in the format string are: format string and arguments (a la printf()) for the formatting engine.
# Escapes in the format string are:
# %c evaluate N[A] recursively*
# %C evaluate N[A[0]]..N[A[1]-1] recursively, separate by A[2]* %c evaluate N[A] recursively*
# %, print ',' if last %C only printed one item (for tuples--unused) %C evaluate N[A[0]]..N[A[1]-1] recursively, separate by A[2]*
# %| tab to current indentation level %, print ',' if last %C only printed one item (for tuples--unused)
# %+ increase current indentation level %| tab to current indentation level
# %- decrease current indentation level %+ increase current indentation level
# %{...} evaluate ... in context of N %- decrease current indentation level
# %% literal '%' %{...} evaluate ... in context of N
# %% literal '%'
# * indicates an argument (A) required.
# * indicates an argument (A) required.
# The '%' may optionally be followed by a number (C) in square brackets, which
# makes the engine walk down to N[C] before evaluating the escape code. The '%' may optionally be followed by a number (C) in square brackets, which
# makes the engine walk down to N[C] before evaluating the escape code.
'''
import sys, re, cStringIO import sys, re, cStringIO
from types import ListType, TupleType, DictType, \ from types import ListType, TupleType, DictType, \
EllipsisType, IntType, CodeType EllipsisType, IntType, CodeType
from spark import GenericASTTraversal from spark import GenericASTTraversal
import Parser import parser
from Parser import AST from parser import AST
from Scanner import Token, Code from scanner import Token, Code
minint = -sys.maxint-1 minint = -sys.maxint-1
@@ -385,7 +386,7 @@ escape = re.compile(r'''
( [{] (?P<expr> [^}]* ) [}] )) ( [{] (?P<expr> [^}]* ) [}] ))
''', re.VERBOSE) ''', re.VERBOSE)
class ParserError(Parser.ParserError): class ParserError(parser.ParserError):
def __init__(self, error, tokens): def __init__(self, error, tokens):
self.error = error # previous exception self.error = error # previous exception
self.tokens = tokens self.tokens = tokens
@@ -1391,8 +1392,8 @@ class Walker(GenericASTTraversal, object):
if isLambda: if isLambda:
tokens.append(Token('LAMBDA_MARKER')) tokens.append(Token('LAMBDA_MARKER'))
try: try:
ast = Parser.parse(tokens, customize) ast = parser.parse(tokens, customize)
except Parser.ParserError, e: except parser.ParserError, e:
raise ParserError(e, tokens) raise ParserError(e, tokens)
if self.showast: if self.showast:
self.print_(repr(ast)) self.print_(repr(ast))
@@ -1409,8 +1410,8 @@ class Walker(GenericASTTraversal, object):
# Build AST from disassembly. # Build AST from disassembly.
try: try:
ast = Parser.parse(tokens, customize) ast = parser.parse(tokens, customize)
except Parser.ParserError, e: except parser.ParserError, e:
raise ParserError(e, tokens) raise ParserError(e, tokens)
if self.showast: if self.showast: