mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2024-11-27 15:10:55 +00: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:
parent
a3dd61c981
commit
d42f84a59c
15
pytest/testdata/if-2.7.right
vendored
15
pytest/testdata/if-2.7.right
vendored
@ -1,13 +1,12 @@
|
||||
# Python 2.7
|
||||
# Embedded file name: simple_source/branching/05_if.py
|
||||
|
||||
6 0 LOAD_NAME 'True'
|
||||
3 POP_JUMP_IF_FALSE '15'
|
||||
6 0 LOAD_NAME 0 'True'
|
||||
3 POP_JUMP_IF_FALSE 15 '15'
|
||||
|
||||
7 6 LOAD_NAME 'False'
|
||||
9 STORE_NAME 'b'
|
||||
12 JUMP_FORWARD '15'
|
||||
15_0 COME_FROM '12'
|
||||
15 LOAD_CONST ''
|
||||
18 RETURN_VALUE ''
|
||||
7 6 LOAD_NAME 1 'False'
|
||||
9 STORE_NAME 2 'b'
|
||||
12 JUMP_FORWARD 0 '15'
|
||||
15 LOAD_CONST 0 ''
|
||||
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
|
||||
# Embedded file name: simple_source/branching/05_ifelse.py
|
||||
|
||||
3 0 LOAD_NAME 'True'
|
||||
3 POP_JUMP_IF_FALSE '15'
|
||||
3 0 LOAD_NAME 0 'True'
|
||||
3 POP_JUMP_IF_FALSE 15 '15'
|
||||
|
||||
4 6 LOAD_CONST 1
|
||||
9 STORE_NAME 'b'
|
||||
12 JUMP_FORWARD '21'
|
||||
4 6 LOAD_CONST 0 1
|
||||
9 STORE_NAME 1 'b'
|
||||
12 JUMP_FORWARD 6 '21'
|
||||
|
||||
6 15 LOAD_CONST 2
|
||||
18 STORE_NAME 'd'
|
||||
21_0 COME_FROM '12'
|
||||
21 LOAD_CONST ''
|
||||
24 RETURN_VALUE ''
|
||||
6 15 LOAD_CONST 1 2
|
||||
18 STORE_NAME 2 'd'
|
||||
21 LOAD_CONST 2 ''
|
||||
24 RETURN_VALUE ''
|
||||
|
||||
|
@ -7,7 +7,7 @@ from __future__ import print_function
|
||||
import sys, os, getopt
|
||||
|
||||
from uncompyle6 import check_python_version
|
||||
from uncompyle6.disas import disassemble_files
|
||||
from uncompyle6.disas import disassemble_file
|
||||
from uncompyle6.version import VERSION
|
||||
|
||||
program, ext = os.path.splitext(os.path.basename(__file__))
|
||||
@ -18,36 +18,37 @@ Usage:
|
||||
%s [--help | -h | -V | --version]
|
||||
|
||||
Examples:
|
||||
%s foo.pyc
|
||||
%s foo.py
|
||||
%s -o foo.pydis foo.pyc
|
||||
%s -o /tmp foo.pyc
|
||||
{0} foo.pyc
|
||||
{0} foo.py # same thing as above but find the file
|
||||
{0} foo.pyc bar.pyc # disassemble foo.pyc and bar.pyc
|
||||
|
||||
Options:
|
||||
-o <path> output decompiled files to this path:
|
||||
if multiple input files are decompiled, the common prefix
|
||||
is stripped from these names and the remainder appended to
|
||||
<path>
|
||||
--help show this message
|
||||
-U | --uncompyle6 show instructions with uncompyle6 mangling
|
||||
-V | --version show version and stop
|
||||
-h | --help show this message
|
||||
|
||||
""" % ((program,) * 6)
|
||||
""".format(program)
|
||||
|
||||
PATTERNS = ('*.pyc', '*.pyo')
|
||||
|
||||
def main():
|
||||
Usage_short = \
|
||||
"%s [--help] [--verify] [--showasm] [--showast] [-o <path>] FILE|DIR..." % program
|
||||
Usage_short = """usage: %s FILE...
|
||||
Type -h for for full help.""" % program
|
||||
|
||||
check_python_version(program)
|
||||
|
||||
outfile = '-'
|
||||
out_base = None
|
||||
use_uncompyle6_format = False
|
||||
|
||||
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)
|
||||
sys.exit(1)
|
||||
|
||||
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:
|
||||
print('%s: %s' % (os.path.basename(sys.argv[0]), e), file=sys.stderr)
|
||||
sys.exit(-1)
|
||||
@ -59,33 +60,22 @@ def main():
|
||||
elif opt in ('-V', '--version'):
|
||||
print("%s %s" % (program, VERSION))
|
||||
sys.exit(0)
|
||||
elif opt == '-o':
|
||||
outfile = val
|
||||
elif opt in ('-U', '--uncompyle6'):
|
||||
use_uncompyle6_format = True
|
||||
else:
|
||||
print(opt)
|
||||
print(Usage_short, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# argl, commonprefix works on strings, not on path parts,
|
||||
# thus we must handle the case with files in 'some/classes'
|
||||
# and 'some/cmds'
|
||||
src_base = os.path.commonprefix(files)
|
||||
if src_base[-1:] != os.sep:
|
||||
src_base = os.path.dirname(src_base)
|
||||
if src_base:
|
||||
sb_len = len( os.path.join(src_base, '') )
|
||||
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)
|
||||
for file in files:
|
||||
if os.path.exists(files[0]):
|
||||
disassemble_file(file, sys.stdout, use_uncompyle6_format)
|
||||
else:
|
||||
print("Can't read %s - skipping" % files[0],
|
||||
file=sys.stderr)
|
||||
pass
|
||||
pass
|
||||
return
|
||||
|
||||
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) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||
# 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.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'
|
||||
"""
|
||||
@ -40,13 +40,15 @@ def disco(version, co, out=None, native=False):
|
||||
file=real_out)
|
||||
|
||||
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)
|
||||
else:
|
||||
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:
|
||||
print(t, file=real_out)
|
||||
print(t.format(), file=real_out)
|
||||
print(file=out)
|
||||
|
||||
|
||||
@ -112,7 +114,7 @@ def disassemble_files(in_base, out_base, files, outfile=None,
|
||||
# print(outfile, file=sys.stderr)
|
||||
pass
|
||||
|
||||
# try to decomyple the input file
|
||||
# try to disassemble the input file
|
||||
try:
|
||||
disassemble_file(infile, outstream, native)
|
||||
except KeyboardInterrupt:
|
||||
|
@ -192,6 +192,73 @@ class Scanner27(scan.Scanner):
|
||||
tokens.append(Token(replace[offset], oparg, pattr, offset, linestart))
|
||||
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):
|
||||
"""
|
||||
Creates Python-independent bytecode structure (byte array) in
|
||||
|
@ -41,6 +41,13 @@ class Token:
|
||||
return (prefix +
|
||||
('%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):
|
||||
return hash(self.type)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user