You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-04 01:09:52 +08:00
Go over disassembly routine...
* tok.py: - add arg value when it is an integer * pydisassemble.py: - add option -U --uncomplyle for which flavor of disassembly - remove -o option * scanner27.py: - allow for native (non-uncompyle6) output
This commit is contained in:
15
pytest/testdata/if-2.7.right
vendored
15
pytest/testdata/if-2.7.right
vendored
@@ -1,13 +1,12 @@
|
|||||||
# Python 2.7
|
# Python 2.7
|
||||||
# Embedded file name: simple_source/branching/05_if.py
|
# Embedded file name: simple_source/branching/05_if.py
|
||||||
|
|
||||||
6 0 LOAD_NAME 'True'
|
6 0 LOAD_NAME 0 'True'
|
||||||
3 POP_JUMP_IF_FALSE '15'
|
3 POP_JUMP_IF_FALSE 15 '15'
|
||||||
|
|
||||||
7 6 LOAD_NAME 'False'
|
7 6 LOAD_NAME 1 'False'
|
||||||
9 STORE_NAME 'b'
|
9 STORE_NAME 2 'b'
|
||||||
12 JUMP_FORWARD '15'
|
12 JUMP_FORWARD 0 '15'
|
||||||
15_0 COME_FROM '12'
|
15 LOAD_CONST 0 ''
|
||||||
15 LOAD_CONST ''
|
18 RETURN_VALUE ''
|
||||||
18 RETURN_VALUE ''
|
|
||||||
|
|
||||||
|
19
pytest/testdata/ifelse-2.7.right
vendored
19
pytest/testdata/ifelse-2.7.right
vendored
@@ -1,16 +1,15 @@
|
|||||||
# Python 2.7
|
# Python 2.7
|
||||||
# Embedded file name: simple_source/branching/05_ifelse.py
|
# Embedded file name: simple_source/branching/05_ifelse.py
|
||||||
|
|
||||||
3 0 LOAD_NAME 'True'
|
3 0 LOAD_NAME 0 'True'
|
||||||
3 POP_JUMP_IF_FALSE '15'
|
3 POP_JUMP_IF_FALSE 15 '15'
|
||||||
|
|
||||||
4 6 LOAD_CONST 1
|
4 6 LOAD_CONST 0 1
|
||||||
9 STORE_NAME 'b'
|
9 STORE_NAME 1 'b'
|
||||||
12 JUMP_FORWARD '21'
|
12 JUMP_FORWARD 6 '21'
|
||||||
|
|
||||||
6 15 LOAD_CONST 2
|
6 15 LOAD_CONST 1 2
|
||||||
18 STORE_NAME 'd'
|
18 STORE_NAME 2 'd'
|
||||||
21_0 COME_FROM '12'
|
21 LOAD_CONST 2 ''
|
||||||
21 LOAD_CONST ''
|
24 RETURN_VALUE ''
|
||||||
24 RETURN_VALUE ''
|
|
||||||
|
|
||||||
|
@@ -7,7 +7,7 @@ from __future__ import print_function
|
|||||||
import sys, os, getopt
|
import sys, os, getopt
|
||||||
|
|
||||||
from uncompyle6 import check_python_version
|
from uncompyle6 import check_python_version
|
||||||
from uncompyle6.disas import disassemble_files
|
from uncompyle6.disas import disassemble_file
|
||||||
from uncompyle6.version import VERSION
|
from uncompyle6.version import VERSION
|
||||||
|
|
||||||
program, ext = os.path.splitext(os.path.basename(__file__))
|
program, ext = os.path.splitext(os.path.basename(__file__))
|
||||||
@@ -18,36 +18,37 @@ Usage:
|
|||||||
%s [--help | -h | -V | --version]
|
%s [--help | -h | -V | --version]
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
%s foo.pyc
|
{0} foo.pyc
|
||||||
%s foo.py
|
{0} foo.py # same thing as above but find the file
|
||||||
%s -o foo.pydis foo.pyc
|
{0} foo.pyc bar.pyc # disassemble foo.pyc and bar.pyc
|
||||||
%s -o /tmp foo.pyc
|
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-o <path> output decompiled files to this path:
|
-U | --uncompyle6 show instructions with uncompyle6 mangling
|
||||||
if multiple input files are decompiled, the common prefix
|
-V | --version show version and stop
|
||||||
is stripped from these names and the remainder appended to
|
-h | --help show this message
|
||||||
<path>
|
|
||||||
--help show this message
|
|
||||||
|
|
||||||
""" % ((program,) * 6)
|
""".format(program)
|
||||||
|
|
||||||
|
PATTERNS = ('*.pyc', '*.pyo')
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
Usage_short = \
|
Usage_short = """usage: %s FILE...
|
||||||
"%s [--help] [--verify] [--showasm] [--showast] [-o <path>] FILE|DIR..." % program
|
Type -h for for full help.""" % program
|
||||||
|
|
||||||
check_python_version(program)
|
check_python_version(program)
|
||||||
|
|
||||||
outfile = '-'
|
outfile = '-'
|
||||||
out_base = None
|
out_base = None
|
||||||
|
use_uncompyle6_format = False
|
||||||
|
|
||||||
if len(sys.argv) == 1:
|
if len(sys.argv) == 1:
|
||||||
print("No file(s) or directory given", file=sys.stderr)
|
print("No file(s) given", file=sys.stderr)
|
||||||
print(Usage_short, file=sys.stderr)
|
print(Usage_short, file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
opts, files = getopt.getopt(sys.argv[1:], 'hVo:', ['help', 'version'])
|
opts, files = getopt.getopt(sys.argv[1:], 'hVU',
|
||||||
|
['help', 'version', 'uncompyle6'])
|
||||||
except getopt.GetoptError as e:
|
except getopt.GetoptError as e:
|
||||||
print('%s: %s' % (os.path.basename(sys.argv[0]), e), file=sys.stderr)
|
print('%s: %s' % (os.path.basename(sys.argv[0]), e), file=sys.stderr)
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
@@ -59,33 +60,22 @@ def main():
|
|||||||
elif opt in ('-V', '--version'):
|
elif opt in ('-V', '--version'):
|
||||||
print("%s %s" % (program, VERSION))
|
print("%s %s" % (program, VERSION))
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
elif opt == '-o':
|
elif opt in ('-U', '--uncompyle6'):
|
||||||
outfile = val
|
use_uncompyle6_format = True
|
||||||
else:
|
else:
|
||||||
print(opt)
|
print(opt)
|
||||||
print(Usage_short, file=sys.stderr)
|
print(Usage_short, file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
# argl, commonprefix works on strings, not on path parts,
|
for file in files:
|
||||||
# thus we must handle the case with files in 'some/classes'
|
if os.path.exists(files[0]):
|
||||||
# and 'some/cmds'
|
disassemble_file(file, sys.stdout, use_uncompyle6_format)
|
||||||
src_base = os.path.commonprefix(files)
|
else:
|
||||||
if src_base[-1:] != os.sep:
|
print("Can't read %s - skipping" % files[0],
|
||||||
src_base = os.path.dirname(src_base)
|
file=sys.stderr)
|
||||||
if src_base:
|
pass
|
||||||
sb_len = len( os.path.join(src_base, '') )
|
pass
|
||||||
files = [f[sb_len:] for f in files]
|
|
||||||
del sb_len
|
|
||||||
|
|
||||||
if outfile == '-':
|
|
||||||
outfile = None # use stdout
|
|
||||||
elif outfile and os.path.isdir(outfile):
|
|
||||||
out_base = outfile; outfile = None
|
|
||||||
elif outfile and len(files) > 1:
|
|
||||||
out_base = outfile; outfile = None
|
|
||||||
|
|
||||||
disassemble_files(src_base, out_base, files, outfile, True)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (c) 2015 by Rocky Bernstein
|
# Copyright (c) 2015-2016 by Rocky Bernstein
|
||||||
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
|
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
|
||||||
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||||
# Copyright (c) 1999 John Aycock
|
# Copyright (c) 1999 John Aycock
|
||||||
@@ -25,7 +25,7 @@ from uncompyle6.code import iscode
|
|||||||
from uncompyle6.load import check_object_path, load_module
|
from uncompyle6.load import check_object_path, load_module
|
||||||
from uncompyle6.scanner import get_scanner
|
from uncompyle6.scanner import get_scanner
|
||||||
|
|
||||||
def disco(version, co, out=None, native=False):
|
def disco(version, co, out=None, use_uncompyle6_format=False):
|
||||||
"""
|
"""
|
||||||
diassembles and deparses a given code block 'co'
|
diassembles and deparses a given code block 'co'
|
||||||
"""
|
"""
|
||||||
@@ -40,13 +40,15 @@ def disco(version, co, out=None, native=False):
|
|||||||
file=real_out)
|
file=real_out)
|
||||||
|
|
||||||
scanner = get_scanner(version)
|
scanner = get_scanner(version)
|
||||||
if native and hasattr(scanner, 'disassemble_native'):
|
if (not use_uncompyle6_format) and hasattr(scanner, 'disassemble_native'):
|
||||||
tokens, customize = scanner.disassemble_native(co, True)
|
tokens, customize = scanner.disassemble_native(co, True)
|
||||||
else:
|
else:
|
||||||
tokens, customize = scanner.disassemble(co)
|
tokens, customize = scanner.disassemble(co)
|
||||||
|
|
||||||
|
# FIXME: This should go in another routine and
|
||||||
|
# a queue should be kept of code to disassemble.
|
||||||
for t in tokens:
|
for t in tokens:
|
||||||
print(t, file=real_out)
|
print(t.format(), file=real_out)
|
||||||
print(file=out)
|
print(file=out)
|
||||||
|
|
||||||
|
|
||||||
@@ -112,7 +114,7 @@ def disassemble_files(in_base, out_base, files, outfile=None,
|
|||||||
# print(outfile, file=sys.stderr)
|
# print(outfile, file=sys.stderr)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# try to decomyple the input file
|
# try to disassemble the input file
|
||||||
try:
|
try:
|
||||||
disassemble_file(infile, outstream, native)
|
disassemble_file(infile, outstream, native)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
|
@@ -192,6 +192,73 @@ class Scanner27(scan.Scanner):
|
|||||||
tokens.append(Token(replace[offset], oparg, pattr, offset, linestart))
|
tokens.append(Token(replace[offset], oparg, pattr, offset, linestart))
|
||||||
return tokens, customize
|
return tokens, customize
|
||||||
|
|
||||||
|
def disassemble_native(self, co, opnames, classname=None, code_objects={}):
|
||||||
|
"""
|
||||||
|
Like disassemble3 but doesn't try to adjust any opcodes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Container for tokens
|
||||||
|
tokens = []
|
||||||
|
|
||||||
|
customize = {}
|
||||||
|
Token = self.Token # shortcut
|
||||||
|
|
||||||
|
n = self.setup_code(co)
|
||||||
|
self.build_lines_data(co, n)
|
||||||
|
|
||||||
|
# self.lines contains (block,addrLastInstr)
|
||||||
|
if classname:
|
||||||
|
classname = '_' + classname.lstrip('_') + '__'
|
||||||
|
|
||||||
|
def unmangle(name):
|
||||||
|
if name.startswith(classname) and name[-2:] != '__':
|
||||||
|
return name[len(classname) - 2:]
|
||||||
|
return name
|
||||||
|
|
||||||
|
free = [ unmangle(name) for name in (co.co_cellvars + co.co_freevars) ]
|
||||||
|
names = [ unmangle(name) for name in co.co_names ]
|
||||||
|
varnames = [ unmangle(name) for name in co.co_varnames ]
|
||||||
|
else:
|
||||||
|
free = co.co_cellvars + co.co_freevars
|
||||||
|
names = co.co_names
|
||||||
|
varnames = co.co_varnames
|
||||||
|
|
||||||
|
extended_arg = 0
|
||||||
|
for offset in self.op_range(0, n):
|
||||||
|
op = self.code[offset]
|
||||||
|
op_name = opname[op]
|
||||||
|
|
||||||
|
oparg = None; pattr = None
|
||||||
|
if op >= HAVE_ARGUMENT:
|
||||||
|
oparg = self.get_argument(offset) + extended_arg
|
||||||
|
extended_arg = 0
|
||||||
|
if op == EXTENDED_ARG:
|
||||||
|
extended_arg = oparg * scan.L65536
|
||||||
|
continue
|
||||||
|
if op in hasconst:
|
||||||
|
pattr = co.co_consts[oparg]
|
||||||
|
elif op in hasname:
|
||||||
|
pattr = names[oparg]
|
||||||
|
elif op in hasjrel:
|
||||||
|
pattr = repr(offset + 3 + oparg)
|
||||||
|
elif op in hasjabs:
|
||||||
|
pattr = repr(oparg)
|
||||||
|
elif op in haslocal:
|
||||||
|
pattr = varnames[oparg]
|
||||||
|
elif op in hascompare:
|
||||||
|
pattr = cmp_op[oparg]
|
||||||
|
elif op in hasfree:
|
||||||
|
pattr = free[oparg]
|
||||||
|
|
||||||
|
if offset in self.linestartoffsets:
|
||||||
|
linestart = self.linestartoffsets[offset]
|
||||||
|
else:
|
||||||
|
linestart = None
|
||||||
|
|
||||||
|
tokens.append(Token(op_name, oparg, pattr, offset, linestart))
|
||||||
|
pass
|
||||||
|
return tokens, customize
|
||||||
|
|
||||||
def setup_code(self, co):
|
def setup_code(self, co):
|
||||||
"""
|
"""
|
||||||
Creates Python-independent bytecode structure (byte array) in
|
Creates Python-independent bytecode structure (byte array) in
|
||||||
|
@@ -41,6 +41,13 @@ class Token:
|
|||||||
return (prefix +
|
return (prefix +
|
||||||
('%6s\t%-17s %r' % (self.offset, self.type, pattr)))
|
('%6s\t%-17s %r' % (self.offset, self.type, pattr)))
|
||||||
|
|
||||||
|
def format(self):
|
||||||
|
prefix = '\n%4d ' % self.linestart if self.linestart else (' ' * 5)
|
||||||
|
offset_opname = '%6s\t%-17s' % (self.offset, self.type)
|
||||||
|
argstr = "%6d " % self.attr if isinstance(self.attr, int) else (' '*7)
|
||||||
|
pattr = self.pattr if self.pattr is not None else ''
|
||||||
|
return "%s%s%s %r" % (prefix, offset_opname, argstr, pattr)
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash(self.type)
|
return hash(self.type)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user