diff --git a/__pkginfo__.py b/__pkginfo__.py index b7fb7c14..7edfb3dc 100644 --- a/__pkginfo__.py +++ b/__pkginfo__.py @@ -37,7 +37,7 @@ entry_points={ ]} ftp_url = None install_requires = ['spark-parser >= 1.4.0', - 'xdis >= 1.1.4'] + 'xdis >= 1.1.5'] license = 'MIT' mailing_list = 'python-debugger@googlegroups.com' modname = 'uncompyle6' diff --git a/test/Makefile b/test/Makefile index 5f45e985..08c26c93 100644 --- a/test/Makefile +++ b/test/Makefile @@ -43,7 +43,7 @@ check-disasm: #: Check deparsing bytecode 2.x only check-bytecode-2: - $(PYTHON) test_pythonlib.py --bytecode-2.3 --bytecode-2.5 --bytecode-2.6 --bytecode-2.7 + $(PYTHON) test_pythonlib.py --bytecode-2.3 --bytecode-2.4 --bytecode-2.5 --bytecode-2.6 --bytecode-2.7 #: Check deparsing bytecode 3.x only check-bytecode-3: @@ -57,6 +57,10 @@ check-bytecode: check-bytecode-3 check-bytecode-2.3: $(PYTHON) test_pythonlib.py --bytecode-2.3 +#: Check deparsing Python 2.4 +check-bytecode-2.4: + $(PYTHON) test_pythonlib.py --bytecode-2.4 + #: Check deparsing Python 2.5 check-bytecode-2.5: $(PYTHON) test_pythonlib.py --bytecode-2.5 diff --git a/test/add-test.py b/test/add-test.py index 09ed8211..3ee7d996 100755 --- a/test/add-test.py +++ b/test/add-test.py @@ -2,12 +2,12 @@ """ Trivial helper program to bytecompile and run an uncompile """ import os, sys, py_compile -assert len(sys.argv) == 2 -path = sys.argv[1] -short = os.path.basename(path) +assert len(sys.argv) >= 2 version = sys.version[0:3] -cfile = "bytecode_%s/%s" % (version, short) + 'c' -print("byte-compiling %s to %s" % (path, cfile)) -py_compile.compile(path, cfile) -if sys.version >= (2, 6, 0): - os.system("../bin/uncompyle6 -a -t %s" % cfile) +for path in sys.argv[1:]: + short = os.path.basename(path) + cfile = "bytecode_%s/%s" % (version, short) + 'c' + print("byte-compiling %s to %s" % (path, cfile)) + py_compile.compile(path, cfile) + if sys.version >= (2, 6, 0): + os.system("../bin/uncompyle6 -a -t %s" % cfile) diff --git a/test/bytecode_2.4/03_if_elif.pyc b/test/bytecode_2.4/03_if_elif.pyc new file mode 100644 index 00000000..e5e5511c Binary files /dev/null and b/test/bytecode_2.4/03_if_elif.pyc differ diff --git a/test/bytecode_2.4/04_raise.pyc b/test/bytecode_2.4/04_raise.pyc new file mode 100644 index 00000000..70547e8d Binary files /dev/null and b/test/bytecode_2.4/04_raise.pyc differ diff --git a/test/bytecode_2.7/03_if_elif.pyc b/test/bytecode_2.7/03_if_elif.pyc new file mode 100644 index 00000000..b283cc02 Binary files /dev/null and b/test/bytecode_2.7/03_if_elif.pyc differ diff --git a/test/bytecode_2.7/04_raise.pyc b/test/bytecode_2.7/04_raise.pyc new file mode 100644 index 00000000..76d99f54 Binary files /dev/null and b/test/bytecode_2.7/04_raise.pyc differ diff --git a/test/bytecode_2.7/04_withas.pyc b/test/bytecode_2.7/04_withas.pyc new file mode 100644 index 00000000..4223a885 Binary files /dev/null and b/test/bytecode_2.7/04_withas.pyc differ diff --git a/test/test_pyenvlib.py b/test/test_pyenvlib.py index 030722bd..c9c247dd 100755 --- a/test/test_pyenvlib.py +++ b/test/test_pyenvlib.py @@ -27,7 +27,8 @@ from fnmatch import fnmatch #----- configure this for your needs -TEST_VERSIONS=('2.3.7', '2.5.6', '2.6.9', '2.7.10', '2.7.11', '3.2.6', '3.3.5', '3.4.2', '3.5.1') +TEST_VERSIONS=('2.3.7', '2.4.6', '2.5.6', '2.6.9', '2.7.10', '2.7.11', + '3.2.6', '3.3.5', '3.4.2', '3.5.1') target_base = '/tmp/py-dis/' lib_prefix = os.path.join(os.environ['HOME'], '.pyenv/versions') diff --git a/test/test_pythonlib.py b/test/test_pythonlib.py index a21232ed..4b537260 100755 --- a/test/test_pythonlib.py +++ b/test/test_pythonlib.py @@ -78,7 +78,7 @@ for vers in (2.7, 3.4, 3.5): test_options[key] = (os.path.join(src_dir, pythonlib), PYOC, key, vers) pass -for vers in (2.3, 2.5, 2.6, 2.7, 3.2, 3.3, 3.4, 3.5): +for vers in (2.3, 2.4, 2.5, 2.6, 2.7, 3.2, 3.3, 3.4, 3.5): bytecode = "bytecode_%s" % vers key = "bytecode-%s" % vers test_options[key] = (bytecode, PYC, bytecode, vers) diff --git a/uncompyle6/parser.py b/uncompyle6/parser.py index 583d192f..5f80fb19 100644 --- a/uncompyle6/parser.py +++ b/uncompyle6/parser.py @@ -591,6 +591,12 @@ def get_python_parser(version, debug_parser, compile_mode='exec'): p = parse23.Python23Parser(debug_parser) else: p = parse23.Python23ParserSingle(debug_parser) + elif version == 2.4: + import uncompyle6.parsers.parse24 as parse24 + if compile_mode == 'exec': + p = parse24.Python24Parser(debug_parser) + else: + p = parse24.Python24ParserSingle(debug_parser) elif version == 2.5: import uncompyle6.parsers.parse25 as parse25 if compile_mode == 'exec': diff --git a/uncompyle6/parsers/parse24.py b/uncompyle6/parsers/parse24.py new file mode 100644 index 00000000..a4acb1f5 --- /dev/null +++ b/uncompyle6/parsers/parse24.py @@ -0,0 +1,28 @@ +# Copyright (c) 2016 Rocky Bernstein +""" +spark grammar differences over Python2.5 for Python 2.4. +""" + +from uncompyle6.parser import PythonParserSingle +from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG +from uncompyle6.parsers.parse25 import Python25Parser + +class Python24Parser(Python25Parser): + def __init__(self, debug_parser=PARSER_DEFAULT_DEBUG): + super(Python24Parser, self).__init__(debug_parser) + self.customized = {} + + def p_misc24(self, args): + ''' + # 2.5+ has two LOAD_CONSTs, one for the number '.'s in a relative import + importstmt ::= LOAD_CONST filler import_as + importfrom ::= LOAD_CONST filler IMPORT_NAME importlist2 POP_TOP + ''' + +class Python24ParserSingle(Python25Parser, PythonParserSingle): + pass + +if __name__ == '__main__': + # Check grammar + p = Python24Parser() + p.checkGrammar() diff --git a/uncompyle6/parsers/parse25.py b/uncompyle6/parsers/parse25.py index 67e16bc9..0f8671ab 100644 --- a/uncompyle6/parsers/parse25.py +++ b/uncompyle6/parsers/parse25.py @@ -1,6 +1,6 @@ # Copyright (c) 2016 Rocky Bernstein """ -spark grammar differences over Python2.6 for Python 2.6. +spark grammar differences over Python2.6 for Python 2.5. """ from uncompyle6.parser import PythonParserSingle @@ -41,5 +41,5 @@ class Python25ParserSingle(Python26Parser, PythonParserSingle): if __name__ == '__main__': # Check grammar - p = Python26Parser() + p = Python25Parser() p.checkGrammar() diff --git a/uncompyle6/scanner.py b/uncompyle6/scanner.py index eb74e181..bc8aff57 100755 --- a/uncompyle6/scanner.py +++ b/uncompyle6/scanner.py @@ -26,7 +26,7 @@ if PYTHON3: # Need to work out Python 2.3. ord's in PYTHON3 PYTHON_VERSIONS = (2.5, 2.6, 2.7, 3.2, 3.3, 3.4, 3.5) else: - PYTHON_VERSIONS = (2.3, 2.5, 2.6, 2.7, 3.2, 3.3, 3.4, 3.5) + PYTHON_VERSIONS = (2.3, 2.4, 2.5, 2.6, 2.7, 3.2, 3.3, 3.4, 3.5) # FIXME: DRY if PYTHON3: diff --git a/uncompyle6/scanners/scanner24.py b/uncompyle6/scanners/scanner24.py new file mode 100755 index 00000000..a8613987 --- /dev/null +++ b/uncompyle6/scanners/scanner24.py @@ -0,0 +1,68 @@ +# Copyright (c) 2016 by Rocky Bernstein +""" +Python 2.4 bytecode scanner/deparser + +This overlaps Python's 2.4's dis module, but it can be run from +Python 3 and other versions of Python. Also, we save token +information for later use in deparsing. +""" + +import uncompyle6.scanners.scanner25 as scan +import uncompyle6.scanners.scanner2 as scan2 + +# bytecode verification, verify(), uses JUMP_OPs from here +from xdis.opcodes import opcode_24 +JUMP_OPs = opcode_24.JUMP_OPs + +# We base this off of 2.6 instead of the other way around +# because we cleaned things up this way. +# The history is that 2.7 support is the cleanest, +# then from that we got 2.6 and so on. +class Scanner24(scan.Scanner25): + def __init__(self, show_asm): + scan2.Scanner2.__init__(self, 2.4, show_asm) + self.stmt_opcodes = frozenset([ + self.opc.SETUP_LOOP, self.opc.BREAK_LOOP, + self.opc.SETUP_FINALLY, self.opc.END_FINALLY, + self.opc.SETUP_EXCEPT, self.opc.POP_BLOCK, + self.opc.STORE_FAST, self.opc.DELETE_FAST, + self.opc.STORE_DEREF, self.opc.STORE_GLOBAL, + self.opc.DELETE_GLOBAL, self.opc.STORE_NAME, + self.opc.DELETE_NAME, self.opc.STORE_ATTR, + self.opc.DELETE_ATTR, self.opc.STORE_SUBSCR, + self.opc.DELETE_SUBSCR, self.opc.RETURN_VALUE, + self.opc.RAISE_VARARGS, self.opc.POP_TOP, + self.opc.PRINT_EXPR, self.opc.PRINT_ITEM, + self.opc.PRINT_NEWLINE, self.opc.PRINT_ITEM_TO, + self.opc.PRINT_NEWLINE_TO, self.opc.CONTINUE_LOOP, + self.opc.JUMP_ABSOLUTE, self.opc.EXEC_STMT, + ]) + + # "setup" opcodes + self.setup_ops = frozenset([ + self.opc.SETUP_EXCEPT, self.opc.SETUP_FINALLY, + ]) + + # opcodes with expect a variable number pushed values whose + # count is in the opcode. For parsing we generally change the + # opcode name to include that number. + self.varargs_ops = frozenset([ + self.opc.BUILD_LIST, self.opc.BUILD_TUPLE, + self.opc.BUILD_SLICE, self.opc.UNPACK_SEQUENCE, + self.opc.MAKE_FUNCTION, self.opc.CALL_FUNCTION, + self.opc.MAKE_CLOSURE, self.opc.CALL_FUNCTION_VAR, + self.opc.CALL_FUNCTION_KW, self.opc.CALL_FUNCTION_VAR_KW, + self.opc.DUP_TOPX, self.opc.RAISE_VARARGS]) + + # opcodes that store values into a variable + self.designator_ops = frozenset([ + self.opc.STORE_FAST, self.opc.STORE_NAME, + self.opc.STORE_GLOBAL, self.opc.STORE_DEREF, self.opc.STORE_ATTR, + self.opc.STORE_SLICE_0, self.opc.STORE_SLICE_1, self.opc.STORE_SLICE_2, + self.opc.STORE_SLICE_3, self.opc.STORE_SUBSCR, self.opc.UNPACK_SEQUENCE, + self.opc.JA + ]) + # Python 2.7 has POP_JUMP_IF_{TRUE,FALSE}_OR_POP but < 2.7 doesn't + # Add an empty set make processing more uniform. + self.pop_jump_if_or_pop = frozenset([]) + return diff --git a/uncompyle6/scanners/scanner25.py b/uncompyle6/scanners/scanner25.py index 2de5882b..dbd3f180 100755 --- a/uncompyle6/scanners/scanner25.py +++ b/uncompyle6/scanners/scanner25.py @@ -1,7 +1,4 @@ # Copyright (c) 2015-2016 by Rocky Bernstein -# Copyright (c) 2005 by Dan Pascu -# Copyright (c) 2000-2002 by hartmut Goebel -# Copyright (c) 1999 John Aycock """ Python 2.5 bytecode scanner/deparser @@ -10,7 +7,6 @@ Python 3 and other versions of Python. Also, we save token information for later use in deparsing. """ -from xdis.opcodes.opcode_25 import * import uncompyle6.scanners.scanner26 as scan import uncompyle6.scanners.scanner2 as scan2 diff --git a/uncompyle6/semantics/pysource.py b/uncompyle6/semantics/pysource.py index 06b0d406..18d631f6 100644 --- a/uncompyle6/semantics/pysource.py +++ b/uncompyle6/semantics/pysource.py @@ -961,8 +961,9 @@ class SourceWalker(GenericASTTraversal, object): n_import_as_cont = n_import_as def n_importfrom(self, node): - if node[0].pattr > 0: - node[2].pattr = '.'*node[0].pattr+node[2].pattr + relative_path_index = 0 + if self.version >= 2.5 and node[relative_path_index].pattr > 0: + node[2].pattr = '.'*node[relative_path_index].pattr + node[2].pattr self.default(node) n_importstar = n_importfrom