You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 00:45:53 +08:00
Cleaning code & patch
This commit is contained in:
3
setup.py
3
setup.py
@@ -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'],
|
|
||||||
)
|
)
|
||||||
|
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@@ -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)
|
16
test_one
16
test_one
@@ -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
|
|
@@ -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)
|
|
||||||
#
|
|
@@ -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..."
|
@@ -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)
|
|
@@ -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)
|
||||||
|
@@ -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:]:
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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
274
uncompyle2/scanner.py
Executable 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
|
@@ -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
|
||||||
|
|
@@ -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',
|
@@ -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',
|
@@ -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'
|
||||||
|
|
||||||
|
@@ -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):
|
||||||
|
@@ -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:
|
Reference in New Issue
Block a user