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_email = "kajusska@gmail.com",
url = "http://github.com/Mysterie/uncompyle2",
packages=['uncompyle2', 'uncompyle2.opcode'],
scripts=['scripts/uncompyle2'],
packages=['uncompyle2', 'uncompyle2.opcode']
)

View File

@@ -15,7 +15,6 @@ for i in range(10):
else:
print 'Else'
i = 0
while i < 10:
i = i+1
@@ -45,3 +44,15 @@ for x, y in [(1,2),(3,4)]:
for x in (1, 2, 3):
if x == 1:
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]
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
# emacs-mode: -*-python-*-
"""
#!/usr/bin/env python2.7
'''
test_pythonlib -- uncompyle and verify Python libraries
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:
test_pythonlib --mylib # decompile '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
from fnmatch import fnmatch
#----- configure this for your needs
lib_prefix = ['.', '/usr/lib/', '/usr/local/lib/']
target_base = '/tmp/py-dis/'
lib_prefix = '/usr/lib'
PYC = ('*.pyc', )
PYO = ('*.pyo', )
@@ -34,21 +35,22 @@ PYOC = ('*.pyc', '*.pyo')
test_options = {
# name: (src_basedir, pattern, output_base_suffix)
'test': ('./test', PYOC, 'test'),
'1.5': (os.path.join(lib_prefix, 'python1.5'), PYC, 'python-lib1.5'),
'1.6': (os.path.join(lib_prefix, 'python1.6'), PYC, 'python-lib1.6'),
'2.0': (os.path.join(lib_prefix, 'python2.0'), PYC, 'python-lib2.0'),
'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')
'test': ['test', PYC, 'test'],
'2.5': ['python2.5', PYC, 'python-lib2.5'],
'2.6': ['python2.6', PYC, 'python-lib2.6'],
'2.7': ['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):
files.extend(
[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
print time.ctime()
print 'Working directory: ', src_dir
try:
main(src_dir, target_dir, files, [], do_verify=do_verify)
print time.ctime()
except (KeyboardInterrupt, OSError):
print
exit(1)
if __name__ == '__main__':
import getopt, sys
do_verify = 0
test_dirs = []
checked_dirs = []
start_with = None
test_options_keys = test_options.keys(); test_options_keys.sort()
opts, args = getopt.getopt(sys.argv[1:], '',
['start-with=', 'verify', 'all', ] \
+ test_options_keys )
if not opts:
help()
for opt, val in opts:
if opt == '--verify':
do_verify = 1
@@ -96,23 +103,20 @@ if __name__ == '__main__':
elif opt == '--all':
for val in test_options_keys:
test_dirs.append(test_options[val])
else:
help()
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)
if os.path.exists(target_dir):
shutil.rmtree(target_dir, ignore_errors=1)
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>
#
"""
'''
Usage: uncompyle [OPTIONS]... [ FILE | DIR]...
Examples:
@@ -38,7 +38,7 @@ Extensions of generated files:
'.dis' successfully decompiled (and verified if --verify)
'.dis_unverified' successfully decompile but --verify failed
'.nodis' uncompyle failed (contact author for enhancement)
"""
'''
Usage_short = \
"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>
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# 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 SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# 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
#
# 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
#
'''
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
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
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 SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
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
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 Walker, verify, magics
import walker, verify, magics
sys.setrecursionlimit(5000)
__all__ = ['uncompyle_file', 'uncompyle_file', 'main']
__all__ = ['uncompyle_file', 'main']
def _load_file(filename):
"""
'''
load a Python source file and compile it to byte-code
_load_module(filename: string): code_object
@@ -43,26 +44,27 @@ def _load_file(filename):
code_object: code_object compiled from this source code
This function does NOT write any file!
"""
'''
fp = open(filename, 'rb')
source = fp.read()+'\n'
try:
co = compile(source, filename, 'exec')
except SyntaxError:
print >> sys.stderr, '>>Syntax error in', filename
print >> sys.stderr, '>>Syntax error in', filename, '\n'
raise
fp.close()
return co
def _load_module(filename):
"""
'''
load a module without importing it
_load_module(filename: string): code_object
filename: name of file containing Python byte-code object
(normally a .pyc)
code_object: code_object from this file
"""
'''
import magics, marshal
fp = open(filename, 'rb')
magic = fp.read(4)
@@ -79,9 +81,9 @@ def _load_module(filename):
return version, co
def uncompyle(version, co, out=None, showasm=0, showast=0):
"""
'''
diassembles a given code block 'co'
"""
'''
assert type(co) == types.CodeType
# 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
# diff scanner
if version == 2.7:
import Scanner27 as scan
import scanner27 as scan
scanner = scan.Scanner27()
elif version == 2.6:
import Scanner26 as scan
import scanner26 as scan
scanner = scan.Scanner26()
elif version == 2.5:
import Scanner25 as scan
import scanner25 as scan
scanner = scan.Scanner25()
scanner = scan.Scanner(version)
scanner.setShowAsm(showasm, out)
tokens, customize = scanner.disassemble(co)
#sys.exit(0)
# Build AST from disassembly.
walker = Walker.Walker(out, scanner, showast=showast)
walk = walker.Walker(out, scanner, showast=showast)
try:
ast = walker.build_ast(tokens, customize)
except Walker.ParserError, e : # parser failed, dump disassembly
ast = walk.build_ast(tokens, customize)
except walker.ParserError, e : # parser failed, dump disassembly
print >>__real_out, e
raise
@@ -114,20 +118,20 @@ def uncompyle(version, co, out=None, showasm=0, showast=0):
# convert leading '__doc__ = "..." into doc string
assert ast == 'stmts'
try:
if ast[0][0] == Walker.ASSIGN_DOC_STRING(co.co_consts[0]):
walker.print_docstring('', co.co_consts[0])
if ast[0][0] == walker.ASSIGN_DOC_STRING(co.co_consts[0]):
walk.print_docstring('', co.co_consts[0])
del ast[0]
if ast[-1] == Walker.RETURN_NONE:
if ast[-1] == walker.RETURN_NONE:
ast.pop() # remove last node
#todo: if empty, add 'pass'
except:
pass
walker.mod_globs = Walker.find_globals(ast, set())
walker.gen_source(ast, customize)
for g in walker.mod_globs:
walker.write('global %s ## Warning: Unused global\n' % g)
if walker.ERROR:
raise walker.ERROR
walk.mod_globs = walker.find_globals(ast, set())
walk.gen_source(ast, customize)
for g in walk.mod_globs:
walk.write('global %s ## Warning: Unused global' % g)
if walk.ERROR:
raise walk.ERROR
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)
co = None
#---- main -------
# ---- main ----
if sys.platform.startswith('linux') and os.uname()[2][:2] == '2.':
def __memUsage():
@@ -151,7 +155,7 @@ else:
def main(in_base, out_base, files, codes, outfile=None,
showasm=0, showast=0, do_verify=0):
"""
'''
in_base base directory for input files
out_base base directory for output files (ignored when
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)
- files below out_base out_base=...
- stdout out_base=None, outfile=None
"""
'''
def _get_outstream(outfile):
dir = os.path.dirname(outfile)
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:
os.makedirs(dir)
except OSError:
@@ -173,7 +178,6 @@ def main(in_base, out_base, files, codes, outfile=None,
return open(outfile, 'w')
of = outfile
tot_files = okay_files = failed_files = verify_failed_files = 0
for code in codes:
@@ -203,13 +207,15 @@ def main(in_base, out_base, files, codes, outfile=None,
if outfile:
outstream.close()
os.remove(outfile)
sys.stderr.write("\nLast file: %s " % (infile))
raise
except:
failed_files += 1
sys.stderr.write("\n# Can't uncompyle %s\n" % infile)
if outfile:
outstream.close()
os.rename(outfile, outfile + '_failed')
else:
sys.stderr.write("\n# Can't uncompyle %s\n" % infile)
import traceback
traceback.print_exc()
#raise
@@ -219,14 +225,21 @@ def main(in_base, out_base, files, codes, outfile=None,
if do_verify:
try:
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
except verify.VerifyCmpError, e:
verify_failed_files += 1
os.rename(outfile, outfile + '_unverified')
if not outfile:
print >>sys.stderr, "### Error Verifiying", file
print >>sys.stderr, e
else:
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)

View File

@@ -189,28 +189,6 @@ def findlinestarts(code):
if lineno != lastlineno:
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():
"""Simple test program to disassemble a file."""
if sys.argv[1:]:

View File

@@ -1,12 +1,7 @@
"""
'''
opcode module - potentially shared between dis and other modules which
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',
'is not', 'exception match', 'BAD')
@@ -18,6 +13,7 @@ hasjabs = []
haslocal = []
hascompare = []
hasfree = []
PJIF = PJIT = JA = JF = 0
opmap = {}
opname = [''] * 256
@@ -27,6 +23,7 @@ del op
def def_op(name, op):
opname[op] = name
opmap[name] = op
globals().update({name: op})
def name_op(name, op):
def_op(name, op)
@@ -40,6 +37,14 @@ def jabs_op(name, op):
def_op(name, 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
# 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
EXTENDED_ARG = 143 # 114
updateGlobal()
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
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',
'is not', 'exception match', 'BAD')
@@ -18,6 +13,7 @@ hasjabs = []
haslocal = []
hascompare = []
hasfree = []
PJIF = PJIT = JA = JF = 0
opmap = {}
opname = [''] * 256
@@ -27,6 +23,7 @@ del op
def def_op(name, op):
opname[op] = name
opmap[name] = op
globals().update({name: op})
def name_op(name, op):
def_op(name, op)
@@ -40,6 +37,14 @@ def jabs_op(name, op):
def_op(name, 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
# 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)
EXTENDED_ARG = 143
updateGlobal()
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
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',
'is not', 'exception match', 'BAD')
@@ -18,6 +13,7 @@ hasjabs = []
haslocal = []
hascompare = []
hasfree = []
PJIF = PJIT = JA = JF = 0
opmap = {}
opname = [''] * 256
@@ -27,6 +23,7 @@ del op
def def_op(name, op):
opname[op] = name
opmap[name] = op
globals().update({name: op})
def name_op(name, op):
def_op(name, op)
@@ -40,6 +37,14 @@ def jabs_op(name, op):
def_op(name, 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
# Blank lines correspond to available opcodes
@@ -189,4 +194,5 @@ EXTENDED_ARG = 145
def_op('SET_ADD', 146)
def_op('MAP_ADD', 147)
updateGlobal()
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) 2005 by Dan Pascu <dan@windowmaker.org>
#
# See main module for license.
#
'''
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__ = ['parse', 'AST', 'ParserError', 'Parser']
@@ -11,7 +12,7 @@ from spark import GenericASTBuilder
import string, exceptions, sys
from UserList import UserList
from Scanner import Token
from scanner import Token
class AST(UserList):
def __init__(self, type, kids=[]):
@@ -41,7 +42,7 @@ class ParserError(Exception):
self.offset = offset
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)

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) 2005 by Dan Pascu <dan@windowmaker.org>
#
# See main module for license.
#
'''
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>
__all__ = ['Token', 'Scanner', 'getscanner']
See main module for license.
'''
import types
import disas as dis
from collections import namedtuple
from array import array
from operator import itemgetter
from struct import *
from Scanner import Token, Code
class Scanner:
def __init__(self, version):
self.version = version
self.resetTokenClass()
from uncompyle2.opcode.opcode_25 import *
import disas as dis
import scanner as scan
dis.setVersion(version)
globals().update({'HAVE_ARGUMENT': dis.HAVE_ARGUMENT})
globals().update({k.replace('+','_'):v for (k,v) in dis.opmap.items()})
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)
class Scanner25(scan.Scanner):
def __init__(self):
self.Token = scan.Scanner.__init__(self, 2.5)
def disassemble(self, co, classname=None):
"""
'''
Disassemble a code object, returning a list of 'Token'.
The main part of this procedure is modelled after
dis.disassemble().
"""
'''
rv = []
customize = {}
Token = self.Token # shortcut
@@ -93,7 +71,7 @@ class Scanner:
delta = 0
self.restructCode(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)
@@ -154,7 +132,7 @@ class Scanner:
extended_arg = 0
for offset in self.op_range(0, n):
op = self.code[offset]
opname = dis.opname[op]
op_name = opname[op]
oparg = None; pattr = None
if offset in cf:
@@ -166,23 +144,23 @@ class Scanner:
if op >= HAVE_ARGUMENT:
oparg = self.get_argument(offset) + extended_arg
extended_arg = 0
if op == dis.EXTENDED_ARG:
if op == EXTENDED_ARG:
raise 'TODO'
extended_arg = oparg * 65536L
continue
if op in dis.hasconst:
if op in hasconst:
const = co.co_consts[oparg]
if type(const) == types.CodeType:
oparg = const
if const.co_name == '<lambda>':
assert opname == 'LOAD_CONST'
opname = 'LOAD_LAMBDA'
assert op_name == 'LOAD_CONST'
op_name = 'LOAD_LAMBDA'
elif const.co_name == '<genexpr>':
opname = 'LOAD_GENEXPR'
op_name = 'LOAD_GENEXPR'
elif const.co_name == '<dictcomp>':
opname = 'LOAD_DICTCOMP'
op_name = 'LOAD_DICTCOMP'
elif const.co_name == '<setcomp>':
opname = 'LOAD_SETCOMP'
op_name = 'LOAD_SETCOMP'
# verify uses 'pattr' for comparism, since 'attr'
# now holds Code(const) and thus can not be used
# for comparism (todo: think about changing this)
@@ -191,21 +169,21 @@ class Scanner:
pattr = '<code_object ' + const.co_name + '>'
else:
pattr = const
elif op in dis.hasname:
elif op in hasname:
pattr = names[oparg]
elif op in dis.hasjrel:
elif op in hasjrel:
pattr = repr(offset + 3 + oparg)
elif op in dis.hasjabs:
elif op in hasjabs:
pattr = repr(oparg)
elif op in dis.haslocal:
elif op in haslocal:
pattr = varnames[oparg]
elif op in dis.hascompare:
pattr = dis.cmp_op[oparg]
elif op in dis.hasfree:
elif op in hascompare:
pattr = cmp_op[oparg]
elif op in hasfree:
pattr = free[oparg]
if offset in self.toChange:
if self.code[offset] == JA and self.code[oparg] == WITH_CLEANUP:
opname = 'SETUP_WITH'
op_name = 'SETUP_WITH'
cf[oparg] = cf.get(oparg, []) + [offset]
if op in (BUILD_LIST, BUILD_TUPLE, BUILD_SLICE,
UNPACK_SEQUENCE,
@@ -220,30 +198,30 @@ class Scanner:
self.code[offset-3] == LOAD_CLOSURE:
continue
else:
opname = '%s_%d' % (opname, oparg)
op_name = '%s_%d' % (op_name, oparg)
if op != BUILD_SLICE:
customize[opname] = oparg
customize[op_name] = oparg
elif op == JA:
target = self.get_target(offset)
if target < offset:
if offset in self.stmts and self.code[offset+3] not in (END_FINALLY, POP_BLOCK) \
and offset not in self.not_continue:
opname = 'CONTINUE'
op_name = 'CONTINUE'
else:
opname = 'JUMP_BACK'
op_name = 'JUMP_BACK'
elif op == LOAD_GLOBAL:
try:
if pattr == 'AssertionError' and rv and rv[-1] == 'JUMP_IF_TRUE':
opname = 'LOAD_ASSERT'
op_name = 'LOAD_ASSERT'
except AttributeError:
pass
elif op == RETURN_VALUE:
if offset in self.return_end_ifs:
opname = 'RETURN_END_IF'
op_name = 'RETURN_END_IF'
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:
rv.append(Token(replace[offset], oparg, pattr, offset, linestart = offset in linestartoffsets))
@@ -255,9 +233,9 @@ class Scanner:
return rv, customize
def getOpcodeToDel(self, i):
"""
'''
check validity of the opcode at position I and return a list of opcode to delete
"""
'''
opcode = self.code[i]
opsize = self.op_size(opcode)
if opcode == EXTENDED_ARG:
@@ -380,10 +358,11 @@ class Scanner:
return None
def restructRelativeJump(self):
"""
'''
change relative JUMP_IF_FALSE/TRUE to absolut jump
and remap the target of PJIF/PJIT
"""
'''
for i in self.op_range(0, len(self.code)):
if(self.code[i] in (PJIF,PJIT)):
target = self.get_argument(i)
@@ -398,9 +377,9 @@ class Scanner:
self.restructJump(i, target)
def restructCode(self, listDel):
"""
'''
restruct linestarts and jump destination after removing bad opcode
"""
'''
result = list()
for block in self.linestarts:
@@ -413,21 +392,24 @@ class Scanner:
result.append((block[0]+startBlock, block[1]))
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:
if change > toDel:
self.toChange[self.toChange.index(change)] -= self.op_size(self.code[toDel])
delta += self.op_size(self.code[toDel])
else:
break
self.toChange[index] -= delta
for jmp in self.op_range(0, len(self.code)):
op = self.code[jmp]
if op in dis.hasjrel+dis.hasjabs: # jmp
if op in hasjrel+hasjabs: # jmp
offset = 0
jmpTarget = self.get_target(jmp)
for toDel in listDel:
if toDel < jmpTarget:
if op in dis.hasjabs:
if op in hasjabs:
offset-=self.op_size(self.code[toDel])
elif jmp < toDel:
offset-=self.op_size(self.code[toDel])
@@ -436,7 +418,7 @@ class Scanner:
self.restructJump(jmp, self.get_target(jmp)+offset)
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'
if newTarget > 0xFFFF:
raise 'TODO'
@@ -447,140 +429,6 @@ class Scanner:
self.code[pos+2] = (target >> 8) & 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):
code = self.code
start = 0;
@@ -659,61 +507,11 @@ class Scanner:
i = s
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):
"""
'''
Return the next jump that was generated by an except SomeException:
construct in a try...except...else clause or None if not found.
"""
'''
except_match = self.first_instr(start, self.lines[start].next, (PJIF))
if except_match:
jmp = self.prev[self.get_target(except_match)]
@@ -735,17 +533,11 @@ class Scanner:
count_SETUP_ += 1
#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):
"""
'''
Detect type of block structures and their boundaries to fix optimizied jumps
in python2.3+
"""
'''
# TODO: check the struct boundaries more precisely -Dan
@@ -848,7 +640,7 @@ class Scanner:
## Add the except blocks
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)
if jmp == None: # check
i = self.next_stmt[i]
@@ -989,17 +781,14 @@ class Scanner:
# self.fixed_jumps[pos] = self.restrict_to_parent(target, parent)
def find_jump_targets(self, code):
"""
'''
Detect all offsets in a byte code which are jump targets.
Return the list of offsets.
This procedure is modelled after dis.findlables(), but here
for each target the number of jumps are counted.
"""
hasjrel = dis.hasjrel
hasjabs = dis.hasjabs
'''
n = len(code)
self.structs = [{'type': 'root',
@@ -1035,4 +824,3 @@ class Scanner:
label = self.fixed_jumps[i]
targets[label] = targets.get(label, []) + [i]
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) 2005 by Dan Pascu <dan@windowmaker.org>
#
# See main module for license.
#
'''
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>
__all__ = ['Token', 'Scanner', 'getscanner']
See main module for license.
'''
import types
import disas as dis
from collections import namedtuple
from array import array
from operator import itemgetter
from struct import *
from Scanner import Token, Code
class Scanner:
def __init__(self, version):
self.version = version
self.resetTokenClass()
from uncompyle2.opcode.opcode_26 import *
import disas as dis
import scanner as scan
dis.setVersion(version)
globals().update({'HAVE_ARGUMENT': dis.HAVE_ARGUMENT})
globals().update({k.replace('+','_'):v for (k,v) in dis.opmap.items()})
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)
class Scanner26(scan.Scanner):
def __init__(self):
self.Token = scan.Scanner.__init__(self, 2.6)
def disassemble(self, co, classname=None):
"""
'''
Disassemble a code object, returning a list of 'Token'.
The main part of this procedure is modelled after
dis.disassemble().
"""
'''
rv = []
customize = {}
Token = self.Token # shortcut
@@ -59,7 +38,6 @@ class Scanner:
self.prev = [0]
# change jump struct
self.restructRelativeJump()
# class and names
if classname:
classname = '_' + classname.lstrip('_') + '__'
@@ -91,7 +69,7 @@ class Scanner:
delta = 0
self.restructCode(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)
@@ -154,7 +132,7 @@ class Scanner:
extended_arg = 0
for offset in self.op_range(0, n):
op = self.code[offset]
opname = dis.opname[op]
op_name = opname[op]
oparg = None; pattr = None
if offset in cf:
@@ -166,23 +144,23 @@ class Scanner:
if op >= HAVE_ARGUMENT:
oparg = self.get_argument(offset) + extended_arg
extended_arg = 0
if op == dis.EXTENDED_ARG:
if op == EXTENDED_ARG:
raise 'TODO'
extended_arg = oparg * 65536L
continue
if op in dis.hasconst:
if op in hasconst:
const = co.co_consts[oparg]
if type(const) == types.CodeType:
oparg = const
if const.co_name == '<lambda>':
assert opname == 'LOAD_CONST'
opname = 'LOAD_LAMBDA'
assert op_name == 'LOAD_CONST'
op_name = 'LOAD_LAMBDA'
elif const.co_name == '<genexpr>':
opname = 'LOAD_GENEXPR'
op_name = 'LOAD_GENEXPR'
elif const.co_name == '<dictcomp>':
opname = 'LOAD_DICTCOMP'
op_name = 'LOAD_DICTCOMP'
elif const.co_name == '<setcomp>':
opname = 'LOAD_SETCOMP'
op_name = 'LOAD_SETCOMP'
# verify uses 'pattr' for comparism, since 'attr'
# now holds Code(const) and thus can not be used
# for comparism (todo: think about changing this)
@@ -191,21 +169,21 @@ class Scanner:
pattr = '<code_object ' + const.co_name + '>'
else:
pattr = const
elif op in dis.hasname:
elif op in hasname:
pattr = names[oparg]
elif op in dis.hasjrel:
elif op in hasjrel:
pattr = repr(offset + 3 + oparg)
elif op in dis.hasjabs:
elif op in hasjabs:
pattr = repr(oparg)
elif op in dis.haslocal:
elif op in haslocal:
pattr = varnames[oparg]
elif op in dis.hascompare:
pattr = dis.cmp_op[oparg]
elif op in dis.hasfree:
elif op in hascompare:
pattr = cmp_op[oparg]
elif op in hasfree:
pattr = free[oparg]
if offset in self.toChange:
if self.code[offset] == JA and self.code[oparg] == WITH_CLEANUP:
opname = 'SETUP_WITH'
op_name = 'SETUP_WITH'
cf[oparg] = cf.get(oparg, []) + [offset]
if op in (BUILD_LIST, BUILD_TUPLE, BUILD_SLICE,
UNPACK_SEQUENCE,
@@ -220,30 +198,30 @@ class Scanner:
self.code[offset-3] == LOAD_CLOSURE:
continue
else:
opname = '%s_%d' % (opname, oparg)
op_name = '%s_%d' % (op_name, oparg)
if op != BUILD_SLICE:
customize[opname] = oparg
customize[op_name] = oparg
elif op == JA:
target = self.get_target(offset)
if target < offset:
if offset in self.stmts and self.code[offset+3] not in (END_FINALLY, POP_BLOCK) \
and offset not in self.not_continue:
opname = 'CONTINUE'
op_name = 'CONTINUE'
else:
opname = 'JUMP_BACK'
op_name = 'JUMP_BACK'
elif op == LOAD_GLOBAL:
try:
if pattr == 'AssertionError' and rv and rv[-1] == 'JUMP_IF_TRUE':
opname = 'LOAD_ASSERT'
op_name = 'LOAD_ASSERT'
except AttributeError:
pass
elif op == RETURN_VALUE:
if offset in self.return_end_ifs:
opname = 'RETURN_END_IF'
op_name = 'RETURN_END_IF'
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:
rv.append(Token(replace[offset], oparg, pattr, offset, linestart = offset in linestartoffsets))
@@ -255,9 +233,9 @@ class Scanner:
return rv, customize
def getOpcodeToDel(self, i):
"""
'''
check validity of the opcode at position I and return a list of opcode to delete
"""
'''
opcode = self.code[i]
opsize = self.op_size(opcode)
@@ -276,12 +254,13 @@ class Scanner:
pass
else:
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 \
and self.code[i+opsize+1] == JA and self.code[i+opsize+4] == POP_BLOCK:
jmpabs1target = self.get_target(i)
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)
if destFor == i+opsize+4:
setupLoop = self.last_instr(0, jmpabs1target, SETUP_LOOP)
@@ -289,6 +268,7 @@ class Scanner:
if standarFor == None:
self.restructJump(jmpabs1target, destFor+self.op_size(POP_BLOCK))
toDel += [setupLoop, i+opsize+1, i+opsize+4]
if len(toDel) > 0:
return toDel
return None
@@ -377,10 +357,10 @@ class Scanner:
return None
def restructRelativeJump(self):
"""
'''
change relative JUMP_IF_FALSE/TRUE to absolut jump
and remap the target of PJIF/PJIT
"""
'''
for i in self.op_range(0, len(self.code)):
if(self.code[i] in (PJIF,PJIT)):
target = self.get_argument(i)
@@ -395,9 +375,9 @@ class Scanner:
self.restructJump(i, target)
def restructCode(self, listDel):
"""
'''
restruct linestarts and jump destination after removing a POP_TOP
"""
'''
result = list()
for block in self.linestarts:
startBlock = 0
@@ -409,21 +389,24 @@ class Scanner:
result.append((block[0]+startBlock, block[1]))
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:
if change > toDel:
self.toChange[self.toChange.index(change)] -= self.op_size(self.code[toDel])
delta += self.op_size(self.code[toDel])
else:
break
self.toChange[index] -= delta
for jmp in self.op_range(0, len(self.code)):
op = self.code[jmp]
if op in dis.hasjrel+dis.hasjabs: # jmp
if op in hasjrel+hasjabs: # jmp
offset = 0
jmpTarget = self.get_target(jmp)
for toDel in listDel:
if toDel < jmpTarget:
if op in dis.hasjabs:
if op in hasjabs:
offset-=self.op_size(self.code[toDel])
elif jmp < toDel:
offset-=self.op_size(self.code[toDel])
@@ -432,7 +415,7 @@ class Scanner:
self.restructJump(jmp, self.get_target(jmp)+offset)
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'
if newTarget > 0xFFFF:
raise 'TODO'
@@ -443,140 +426,6 @@ class Scanner:
self.code[pos+2] = (target >> 8) & 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):
code = self.code
start = 0;
@@ -655,61 +504,11 @@ class Scanner:
i = s
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):
"""
'''
Return the next jump that was generated by an except SomeException:
construct in a try...except...else clause or None if not found.
"""
'''
except_match = self.first_instr(start, self.lines[start].next, (PJIF))
if except_match:
jmp = self.prev[self.get_target(except_match)]
@@ -731,17 +530,11 @@ class Scanner:
count_SETUP_ += 1
#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):
"""
'''
Detect type of block structures and their boundaries to fix optimizied jumps
in python2.3+
"""
'''
# TODO: check the struct boundaries more precisely -Dan
@@ -844,7 +637,7 @@ class Scanner:
## Add the except blocks
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)
if jmp == None: # check
i = self.next_stmt[i]
@@ -985,17 +778,14 @@ class Scanner:
# self.fixed_jumps[pos] = self.restrict_to_parent(target, parent)
def find_jump_targets(self, code):
"""
'''
Detect all offsets in a byte code which are jump targets.
Return the list of offsets.
This procedure is modelled after dis.findlables(), but here
for each target the number of jumps are counted.
"""
hasjrel = dis.hasjrel
hasjabs = dis.hasjabs
'''
n = len(code)
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) 2005 by Dan Pascu <dan@windowmaker.org>
#
# See main module for license.
#
'''
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>
__all__ = ['Token', 'Scanner', 'getscanner']
See main module for license.
'''
import types
import disas as dis
from collections import namedtuple
from array import array
from operator import itemgetter
from Scanner import Token, Code
class Scanner:
def __init__(self, version):
self.version = version
self.resetTokenClass()
from uncompyle2.opcode.opcode_27 import *
import disas as dis
import scanner as scan
dis.setVersion(version)
globals().update({'HAVE_ARGUMENT': dis.HAVE_ARGUMENT})
globals().update({k.replace('+','_'):v for (k,v) in dis.opmap.items()})
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)
class Scanner27(scan.Scanner):
def __init__(self):
self.Token = scan.Scanner.__init__(self, 2.6)
def disassemble(self, co, classname=None):
"""
'''
Disassemble a code object, returning a list of 'Token'.
The main part of this procedure is modelled after
dis.disassemble().
"""
'''
rv = []
customize = {}
Token = self.Token # shortcut
@@ -129,27 +107,27 @@ class Scanner:
k += 1
op = code[offset]
opname = dis.opname[op]
op_name = opname[op]
oparg = None; pattr = None
if op >= HAVE_ARGUMENT:
oparg = code[offset+1] + code[offset+2] * 256 + extended_arg
extended_arg = 0
if op == dis.EXTENDED_ARG:
if op == EXTENDED_ARG:
extended_arg = oparg * 65536L
continue
if op in dis.hasconst:
if op in hasconst:
const = co.co_consts[oparg]
if type(const) == types.CodeType:
oparg = const
if const.co_name == '<lambda>':
assert opname == 'LOAD_CONST'
opname = 'LOAD_LAMBDA'
assert op_name == 'LOAD_CONST'
op_name = 'LOAD_LAMBDA'
elif const.co_name == '<genexpr>':
opname = 'LOAD_GENEXPR'
op_name = 'LOAD_GENEXPR'
elif const.co_name == '<dictcomp>':
opname = 'LOAD_DICTCOMP'
op_name = 'LOAD_DICTCOMP'
elif const.co_name == '<setcomp>':
opname = 'LOAD_SETCOMP'
op_name = 'LOAD_SETCOMP'
# verify uses 'pattr' for comparism, since 'attr'
# now holds Code(const) and thus can not be used
# for comparism (todo: think about changing this)
@@ -158,17 +136,17 @@ class Scanner:
pattr = '<code_object ' + const.co_name + '>'
else:
pattr = const
elif op in dis.hasname:
elif op in hasname:
pattr = names[oparg]
elif op in dis.hasjrel:
elif op in hasjrel:
pattr = repr(offset + 3 + oparg)
elif op in dis.hasjabs:
elif op in hasjabs:
pattr = repr(oparg)
elif op in dis.haslocal:
elif op in haslocal:
pattr = varnames[oparg]
elif op in dis.hascompare:
pattr = dis.cmp_op[oparg]
elif op in dis.hasfree:
elif op in hascompare:
pattr = cmp_op[oparg]
elif op in hasfree:
pattr = free[oparg]
if op in (BUILD_LIST, BUILD_TUPLE, BUILD_SET, BUILD_SLICE,
@@ -184,30 +162,30 @@ class Scanner:
code[offset-3] == LOAD_CLOSURE:
continue
else:
opname = '%s_%d' % (opname, oparg)
op_name = '%s_%d' % (op_name, oparg)
if op != BUILD_SLICE:
customize[opname] = oparg
customize[op_name] = oparg
elif op == JA:
target = self.get_target(offset)
if target < offset:
if offset in self.stmts and code[offset+3] not in (END_FINALLY, POP_BLOCK) \
and offset not in self.not_continue:
opname = 'CONTINUE'
op_name = 'CONTINUE'
else:
opname = 'JUMP_BACK'
op_name = 'JUMP_BACK'
elif op == LOAD_GLOBAL:
try:
if pattr == 'AssertionError' and rv and rv[-1] == 'POP_JUMP_IF_TRUE':
opname = 'LOAD_ASSERT'
op_name = 'LOAD_ASSERT'
except AttributeError:
pass
elif op == RETURN_VALUE:
if offset in self.return_end_ifs:
opname = 'RETURN_END_IF'
op_name = 'RETURN_END_IF'
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:
rv.append(Token(replace[offset], oparg, pattr, offset, linestart = offset in linestartoffsets))
@@ -218,127 +196,6 @@ class Scanner:
print >>out
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):
code = self.code
start = 0;
@@ -428,52 +285,11 @@ class Scanner:
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):
"""
'''
Return the next jump that was generated by an except SomeException:
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)
if except_match:
@@ -493,17 +309,11 @@ class Scanner:
elif op in (SETUP_EXCEPT, SETUP_WITH, SETUP_FINALLY):
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):
"""
'''
Detect type of block structures and their boundaries to fix optimizied jumps
in python2.3+
"""
'''
# TODO: check the struct boundaries more precisely -Dan
@@ -751,17 +561,14 @@ class Scanner:
def find_jump_targets(self, code):
"""
'''
Detect all offsets in a byte code which are jump targets.
Return the list of offsets.
This procedure is modelled after dis.findlables(), but here
for each target the number of jumps are counted.
"""
hasjrel = dis.hasjrel
hasjabs = dis.hasjabs
'''
n = len(code)
self.structs = [{'type': 'root',

View File

@@ -1,23 +1,25 @@
# 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
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# 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 SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
'''
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
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
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 SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
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'

View File

@@ -7,7 +7,7 @@
import types
import operator
import dis
import uncompyle2, Scanner
import uncompyle2, scanner
BIN_OP_FUNCS = {
'BINARY_POWER': operator.pow,
@@ -284,7 +284,7 @@ def cmp_code_objects(version, code_obj1, code_obj2, name=''):
getattr(code_obj1,member),
getattr(code_obj2,member))
class Token(Scanner.Token):
class Token(scanner.Token):
"""Token class with changed semantics for 'cmp()'."""
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) 2005 by Dan Pascu <dan@windowmaker.org>
#
# See main module for license.
#
#
# 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):
#
# N N N&K
# / | ... \ / | ... \ / | ... \
# O O O O O K O O O
# |
# K
#
# 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
# 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]*
# %, print ',' if last %C only printed one item (for tuples--unused)
# %| tab to current indentation level
# %+ increase current indentation level
# %- decrease current indentation level
# %{...} evaluate ... in context of N
# %% literal '%'
#
# * 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.
#
'''
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.
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):
N N N&K
/ | ... \ / | ... \ / | ... \
O O O O O K O O O
|
K
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
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]*
%, print ',' if last %C only printed one item (for tuples--unused)
%| tab to current indentation level
%+ increase current indentation level
%- decrease current indentation level
%{...} evaluate ... in context of N
%% literal '%'
* 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.
'''
import sys, re, cStringIO
from types import ListType, TupleType, DictType, \
EllipsisType, IntType, CodeType
from spark import GenericASTTraversal
import Parser
from Parser import AST
from Scanner import Token, Code
import parser
from parser import AST
from scanner import Token, Code
minint = -sys.maxint-1
@@ -385,7 +386,7 @@ escape = re.compile(r'''
( [{] (?P<expr> [^}]* ) [}] ))
''', re.VERBOSE)
class ParserError(Parser.ParserError):
class ParserError(parser.ParserError):
def __init__(self, error, tokens):
self.error = error # previous exception
self.tokens = tokens
@@ -1391,8 +1392,8 @@ class Walker(GenericASTTraversal, object):
if isLambda:
tokens.append(Token('LAMBDA_MARKER'))
try:
ast = Parser.parse(tokens, customize)
except Parser.ParserError, e:
ast = parser.parse(tokens, customize)
except parser.ParserError, e:
raise ParserError(e, tokens)
if self.showast:
self.print_(repr(ast))
@@ -1409,8 +1410,8 @@ class Walker(GenericASTTraversal, object):
# Build AST from disassembly.
try:
ast = Parser.parse(tokens, customize)
except Parser.ParserError, e:
ast = parser.parse(tokens, customize)
except parser.ParserError, e:
raise ParserError(e, tokens)
if self.showast: