From 9fecb48744530c118396c64af1c11458b1c6a3e8 Mon Sep 17 00:00:00 2001 From: rocky Date: Wed, 16 Dec 2015 01:52:11 -0500 Subject: [PATCH] Tidy a little bit --- Makefile | 6 +++- pytest/testdata/.gitignore | 1 + test/Makefile | 4 +++ test/bytecode_2.7/if.pyc | Bin 0 -> 150 bytes test/bytecode_2.7/ifelse.pyc | Bin 0 -> 174 bytes test/compile_tests | 6 ++-- uncompyle6/deparser.py | 6 ++-- uncompyle6/disas.py | 48 ++++++++++++++++++++++--------- uncompyle6/marsh.py | 10 +++---- uncompyle6/scanner.py | 2 ++ uncompyle6/scanners/scanner25.py | 4 +-- uncompyle6/scanners/scanner26.py | 4 +-- uncompyle6/scanners/scanner27.py | 4 +-- uncompyle6/verify.py | 15 ++++++---- 14 files changed, 74 insertions(+), 36 deletions(-) create mode 100644 pytest/testdata/.gitignore create mode 100644 test/bytecode_2.7/if.pyc create mode 100644 test/bytecode_2.7/ifelse.pyc diff --git a/Makefile b/Makefile index 85148528..7379af4c 100644 --- a/Makefile +++ b/Makefile @@ -20,9 +20,13 @@ all: check test check: pytest check-short #: Run tests -check-short: pytest +check-short: pytest $(MAKE) -C test $@ +#: check that disassembly exactly matches Python lib's dis +check-disasm: + $(MAKE) -C test check-disasm + #: Run tests pytest: $(MAKE) -C pytest check diff --git a/pytest/testdata/.gitignore b/pytest/testdata/.gitignore new file mode 100644 index 00000000..323815dd --- /dev/null +++ b/pytest/testdata/.gitignore @@ -0,0 +1 @@ +/*.got diff --git a/test/Makefile b/test/Makefile index 17eb0c3c..3538081f 100644 --- a/test/Makefile +++ b/test/Makefile @@ -16,6 +16,10 @@ check: check-short check-2.7-ok ## This leads me to believe the problem is an ## initialization bug? +#: Check deparsing only, but from a different Python version +check-disasm: + $(PYTHON) dis-compare.py + #: Check deparsing only, but from a different Python version check-bytecode: $(PYTHON) test_pythonlib.py --bytecode-2.5 diff --git a/test/bytecode_2.7/if.pyc b/test/bytecode_2.7/if.pyc new file mode 100644 index 0000000000000000000000000000000000000000..97c3ef13e8a2ac1b10b1eb182f5176988aaf22d5 GIT binary patch literal 150 zcmZSn%**xPCnGGG0SXv_v;zK%28JSjhEzs|C?TZl QX-=vg$h2Z6Ai>560Fyo$2LJ#7 literal 0 HcmV?d00001 diff --git a/test/bytecode_2.7/ifelse.pyc b/test/bytecode_2.7/ifelse.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b2038018942a8b2e786c5624bf144a74eaa2a678 GIT binary patch literal 174 zcmZSn%**xPCnGGG0SXv_v;z6b6Q14UjTF z4OSpo0%9hD$z(8@1SV6#WQqn*k^w;!D*(BA`o;OBMail0#(F0DNkxfy$r+h>>H3*z hsX4`|dIgmw96&`jx%nxjIjMFa(~DVv1REnK69DBX9Y_EG literal 0 HcmV?d00001 diff --git a/test/compile_tests b/test/compile_tests index d87fdd6f..8d5d6258 100755 --- a/test/compile_tests +++ b/test/compile_tests @@ -55,8 +55,10 @@ tests['2.5'] = tests['2.3'] tests['2.6'] = tests['2.5'] # tests['2.7'] = ['mine'] + tests['2.6'] tests['2.7'] = [ - 'source_3.4/call_arguments/keyword', - 'source_3.4/call_arguments/positional' + 'source_3.4/branching/ifelse', + 'source_3.4/branching/if' + # 'source_3.4/call_arguments/keyword', + # 'source_3.4/call_arguments/positional' ] tests['3.4'] = [ diff --git a/uncompyle6/deparser.py b/uncompyle6/deparser.py index d21f944b..a3a67b3c 100644 --- a/uncompyle6/deparser.py +++ b/uncompyle6/deparser.py @@ -61,7 +61,7 @@ try: except ImportError: from io import StringIO -import sys, inspect, types, re +import sys, inspect, re # FIXME: remove uncompyle dups @@ -464,7 +464,7 @@ class Traverser(walker.Walker, object): self.prec = 27 code = node[-5].attr - assert isinstance(code, types.CodeType) + assert inspect.iscode(code) code = Code(code, self.scanner, self.currentclass) # assert isinstance(code, Code) @@ -1115,7 +1115,7 @@ class Traverser(walker.Walker, object): pass def deparse(version, co, out=StringIO(), showasm=0, showast=0): - assert isinstance(co, types.CodeType) + assert inspect.iscode(co) # store final output stream for case of error __real_out = out or sys.stdout try: diff --git a/uncompyle6/disas.py b/uncompyle6/disas.py index b033d25c..77f905c8 100644 --- a/uncompyle6/disas.py +++ b/uncompyle6/disas.py @@ -1,29 +1,41 @@ +# Copyright (c) 1999 John Aycock +# Copyright (c) 2000-2002 by hartmut Goebel +# Copyright (c) 2005 by Dan Pascu +# Copyright (c) 2015 by Rocky Bernstein + """ CPython magic- and version- independent disassembly routines -Copyright (c) 1999 John Aycock -Copyright (c) 2000-2002 by hartmut Goebel -Copyright (c) 2005 by Dan Pascu -Copyright (c) 2015 by Rocky Bernstein +There are two reasons we can't use Python's built-in routines +from dis. First, the bytecode we are extracting may be from a different +version of Python (different magic number) than the version of Python +that is doing the extraction. -This is needed when the bytecode extracted is from -a different version than the currently-running Python. - -When the two are the same, you can simply use Python's built-in disassemble +Second, we need structured instruction information for the +(de)-parsing step. Python 3.4 and up provides this, but we still do +want to run on Python 2.7. """ from __future__ import print_function -import os, sys, types - +import importlib, inspect, os, sys import uncompyle6 +def check_object_path(path): + if path.endswith(".py"): + if uncompyle6.PYTHON3: + path = importlib.util.cache_from_source(path) + return path + if not path.endswith(".pyc") and not path.endswith(".pyo"): + raise ValueError("path must point to a .py or .pyc file") + return path + def disco(version, co, out=None): """ diassembles and deparses a given code block 'co' """ - assert isinstance(co, types.CodeType) + assert inspect.iscode(co) # store final output stream for case of error real_out = out or sys.stdout @@ -59,7 +71,11 @@ def disco(version, co, out=None): def disassemble_file(filename, outstream=None): """ disassemble Python byte-code file (.pyc) + + If given a Python source file (".py") file, we'll + try to find the corresponding compiled object. """ + filename = check_object_path(filename) version, co = uncompyle6.load_module(filename) if type(co) == list: for con in co: @@ -144,9 +160,13 @@ def _test(): """Simple test program to disassemble a file.""" argc = len(sys.argv) if argc != 2: - sys.stderr.write("usage: %s [-|CPython compiled file]\n" % __file__) - sys.exit(2) - fn = sys.argv[1] + if argc == 1 and uncompyle6.PYTHON3: + fn = __file__ + else: + sys.stderr.write("usage: %s [-|CPython compiled file]\n" % __file__) + sys.exit(2) + else: + fn = sys.argv[1] disassemble_file(fn) if __name__ == "__main__": diff --git a/uncompyle6/marsh.py b/uncompyle6/marsh.py index 6720b4da..629432d9 100644 --- a/uncompyle6/marsh.py +++ b/uncompyle6/marsh.py @@ -1,11 +1,6 @@ """ CPython magic- and version- independent marshal routines - Copyright (c) 1999 John Aycock - Copyright (c) 2000-2002 by hartmut Goebel - Copyright (c) 2005 by Dan Pascu - Copyright (c) 2015 by Rocky Bernstein - This is needed when the bytecode extracted is from a different version than the currently-running Python. @@ -13,6 +8,11 @@ When the two are the same, you can simply use Python's built-in marshal.loads() to produce a code object """ +# Copyright (c) 1999 John Aycock +# Copyright (c) 2000-2002 by hartmut Goebel +# Copyright (c) 2005 by Dan Pascu +# Copyright (c) 2015 by Rocky Bernstein + from __future__ import print_function import imp, sys, types diff --git a/uncompyle6/scanner.py b/uncompyle6/scanner.py index 69ef3984..1b82bcae 100755 --- a/uncompyle6/scanner.py +++ b/uncompyle6/scanner.py @@ -20,9 +20,11 @@ __all__ = ['Token', 'Scanner', 'Code'] import sys +# FIXME: DRY if (sys.version_info > (3, 0)): intern = sys.intern L65536 = 65536 + def cmp(a, b): return (a > b) - (a < b) else: diff --git a/uncompyle6/scanners/scanner25.py b/uncompyle6/scanners/scanner25.py index 55be01d1..0d632912 100644 --- a/uncompyle6/scanners/scanner25.py +++ b/uncompyle6/scanners/scanner25.py @@ -13,7 +13,7 @@ Python 3 and other versions of Python. Also, we save token information for later use in deparsing. """ -import types +import inspect from collections import namedtuple from array import array @@ -152,7 +152,7 @@ class Scanner25(scan.Scanner): continue if op in hasconst: const = co.co_consts[oparg] - if isinstance(const, types.CodeType): + if inspect.iscode(const): oparg = const if const.co_name == '': assert op_name == 'LOAD_CONST' diff --git a/uncompyle6/scanners/scanner26.py b/uncompyle6/scanners/scanner26.py index c0b86ddf..5ad44951 100644 --- a/uncompyle6/scanners/scanner26.py +++ b/uncompyle6/scanners/scanner26.py @@ -13,7 +13,7 @@ other versions of Python. Also, we save token information for later use in deparsing. """ -import types +import inspect from collections import namedtuple from array import array @@ -147,7 +147,7 @@ class Scanner26(scan.Scanner): continue if op in hasconst: const = co.co_consts[oparg] - if isinstance(const, types.CodeType): + if inspect.iscode(const): oparg = const if const.co_name == '': assert op_name == 'LOAD_CONST' diff --git a/uncompyle6/scanners/scanner27.py b/uncompyle6/scanners/scanner27.py index 22fb224a..63199e98 100644 --- a/uncompyle6/scanners/scanner27.py +++ b/uncompyle6/scanners/scanner27.py @@ -13,7 +13,7 @@ for later use in deparsing. from __future__ import print_function -import dis, types +import dis, inspect from collections import namedtuple from array import array @@ -136,7 +136,7 @@ class Scanner27(scan.Scanner): continue if op in hasconst: const = co.co_consts[oparg] - if isinstance(const, types.CodeType): + if inspect.iscode(const): oparg = const if const.co_name == '': assert op_name == 'LOAD_CONST' diff --git a/uncompyle6/verify.py b/uncompyle6/verify.py index 2b22ccf1..df8cea35 100755 --- a/uncompyle6/verify.py +++ b/uncompyle6/verify.py @@ -1,18 +1,23 @@ -from __future__ import print_function - # # (C) Copyright 2000-2002 by hartmut Goebel # # byte-code verifier for uncompyle # -import dis, operator, sys, types +from __future__ import print_function + +import dis, inspect, operator, sys, types import uncompyle6 import uncompyle6.scanner as scanner +# FIXME: DRY if (sys.version_info >= (3, 0)): truediv = operator.truediv + + def cmp(a, b): + return (a > b) - (a < b) + from functools import reduce else: truediv = operator.div @@ -127,8 +132,8 @@ def cmp_code_objects(version, code_obj1, code_obj2, name=''): This is the main part of this module. """ # print code_obj1, type(code_obj2) - assert isinstance(code_obj1, types.CodeType) - assert isinstance(code_obj2, types.CodeType) + assert inspect.iscode(code_obj1) + assert inspect.iscode(code_obj2) # print dir(code_obj1) if isinstance(code_obj1, object): # new style classes (Python 2.2)