From b7942bc5f2879ea9d89b79c1fd538ef1b3acc805 Mon Sep 17 00:00:00 2001 From: rocky Date: Wed, 13 Jun 2018 12:26:21 -0400 Subject: [PATCH] Add Python 1.3 decompilation .. Reduced checking via "make check-short" --- README.rst | 5 +-- test/Makefile | 21 +++++++++-- test/bytecode_1.3/test_builtin.pyc | Bin 0 -> 266 bytes test/bytecode_1.3/test_exceptions.pyc | Bin 0 -> 1984 bytes test/bytecode_1.3/test_grammar.pyc | Bin 0 -> 13010 bytes test/bytecode_1.3/test_operations.pyc | Bin 0 -> 145 bytes test/bytecode_1.3/testall.pyc | Bin 0 -> 552 bytes test/test_pythonlib.py | 2 +- uncompyle6/parser.py | 8 ++++- uncompyle6/parsers/parse13.py | 49 ++++++++++++++++++++++++++ uncompyle6/parsers/parse14.py | 5 ++- uncompyle6/scanner.py | 2 +- uncompyle6/scanners/scanner13.py | 35 ++++++++++++++++++ 13 files changed, 116 insertions(+), 11 deletions(-) create mode 100644 test/bytecode_1.3/test_builtin.pyc create mode 100644 test/bytecode_1.3/test_exceptions.pyc create mode 100644 test/bytecode_1.3/test_grammar.pyc create mode 100644 test/bytecode_1.3/test_operations.pyc create mode 100644 test/bytecode_1.3/testall.pyc create mode 100644 uncompyle6/parsers/parse13.py create mode 100644 uncompyle6/scanners/scanner13.py diff --git a/README.rst b/README.rst index 30840a9e..51d0b544 100644 --- a/README.rst +++ b/README.rst @@ -11,8 +11,9 @@ Introduction ------------ *uncompyle6* translates Python bytecode back into equivalent Python -source code. It accepts bytecodes from Python version 1.4, and 2.1 to -3.7 or so, including PyPy bytecode and Dropbox's Python 2.5 bytecode. +source code. It accepts bytecodes from Python version 1.3 to version +3.7, spanning over 22 years of Python releases. We include Dropbox's +Python 2.5 bytecode and some PyPy bytecode. Why this? --------- diff --git a/test/Makefile b/test/Makefile index 4fd7e497..85eaa431 100644 --- a/test/Makefile +++ b/test/Makefile @@ -22,7 +22,7 @@ COVER_DIR=../tmp/grammar-cover # Run short tests check-short: @$(PYTHON) -V && PYTHON_VERSION=`$(PYTHON) -V 2>&1 | cut -d ' ' -f 2 | cut -d'.' -f1,2`; \ - $(MAKE) check-bytecode + $(MAKE) check-bytecode-short # Run all tests check: @@ -91,16 +91,31 @@ check-bytecode-3: --bytecode-3.1 --bytecode-3.2 --bytecode-3.3 \ --bytecode-3.4 --bytecode-3.5 --bytecode-3.6 --bytecode-pypy3.2 -#: Check deparsing bytecode that works running Python 2 and Python 3 +#: Check deparsing on selected bytecode 3.x +check-bytecode-3-short: + $(PYTHON) test_pythonlib.py \ + --bytecode-3.4 --bytecode-3.5 --bytecode-3.6 + +#: Check deparsing bytecode on all Python 2 and Python 3 versions check-bytecode: check-bytecode-3 $(PYTHON) test_pythonlib.py \ - --bytecode-1.4 --bytecode-1.5 \ + --bytecode-1.3 --bytecode-1.4 --bytecode-1.5 \ --bytecode-2.2 --bytecode-2.3 --bytecode-2.4 \ --bytecode-2.1 --bytecode-2.2 --bytecode-2.3 --bytecode-2.4 \ --bytecode-2.5 --bytecode-2.6 --bytecode-2.7 \ --bytecode-pypy2.7 +#: Check deparsing bytecode on selected Python 2 and Python 3 versions +check-bytecode-short: check-bytecode-3-short + $(PYTHON) test_pythonlib.py \ + --bytecode-2.6 --bytecode-2.7 --bytecode-pypy2.7 + + +#: Check deparsing bytecode 1.3 only +check-bytecode-1.3: + $(PYTHON) test_pythonlib.py --bytecode-1.3 + #: Check deparsing bytecode 1.4 only check-bytecode-1.4: $(PYTHON) test_pythonlib.py --bytecode-1.4 diff --git a/test/bytecode_1.3/test_builtin.pyc b/test/bytecode_1.3/test_builtin.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9b0d4f0e19ab47a4da94cdd0502e5b73457ba503 GIT binary patch literal 266 zcmeCwym>IGe7;+dH80%RXQWzNAJ?hyRQW!xr2SX|oh}jI1 zUU4xyP^KicxFkNw5Q%N%r@;!+#{-isE-fg?FDfZ!1PW*svjNG{yqx^R n6eK+;`Za(m7>cEUgr2@nW|BV03NR6$1ad=Wo?byE*f4tlO6)rA literal 0 HcmV?d00001 diff --git a/test/bytecode_1.3/test_exceptions.pyc b/test/bytecode_1.3/test_exceptions.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2a808316630c3a8d04fb47c1edd5577a3439e58e GIT binary patch literal 1984 zcma)6ZByGu5PodTo58{6{k4dcG(a1>?KIP5+QK9gXqW`k@E%WR7-5~u6(gy-GoVgq z^nrfq59$Bx-kwy{Z}n)-cf0#M`|RFIf7L7H#^vWfb z1Mmhg*P5^_8^ki6XW2+uzF?L~%km7DmZi=tb!HiSU}+G`WS*swvV6rXQeNsamE^7aJeraX8*m2my}{ApWX5Z$_A4BfcP^co4BL< z-89CaMl-Q#gwr!aA;0}yES&NcY!UTbo_Z^#e#O-Dmijfk5hL^s8yBo`8}S&DZFr-g zfhZj_-wyFD`h`{%Ck39 z_Mez&*%EzVVIR!xcYqlSJs{|E9(s^Mj~MD$=rQ66B*zdd@OXT)cj1KKP9A=e!p|7~ z*up>Zh z17EeN5$J;&qX_!c(DR4+>d>d>aIHYLCaUA}qxs}@yjt)3%U7D0?GV}G6@Eq5Vo$DYHTkEPwjz(Ec82_akc?K7WFbce0TdYem zLsp-XbCq^#lopfxT=l{*h&0!*kyiDHoUgQwGm$kyt6T;b1tiUCt_==k%rz&LLzY$TV8{FDJ)7^ufuVscbyoa%`-Bgf0 zD=n@q!00`U`FtlePbH0w=B6CQVVd7Wow(gz?oQg5kus&UL=?P{H&W5;TT0XQp5K-J M)f(;aKSz4&ALOc~>i_@% literal 0 HcmV?d00001 diff --git a/test/bytecode_1.3/test_grammar.pyc b/test/bytecode_1.3/test_grammar.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bb1e6eae99868323e1f7ff2bafa8cb54f7ce210f GIT binary patch literal 13010 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 literal 0 HcmV?d00001 diff --git a/test/bytecode_1.3/test_operations.pyc b/test/bytecode_1.3/test_operations.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2d7c63a531ba93a17283a8bd46bca5371cb9892c GIT binary patch literal 145 zcmeCwySQt_m7~DPTSs79ofpiKJL$C%jP_CF4NEquW_!p!W zC6;97=M{ssL_|a=_~n-iW@6DcNWWZ1t8B s8{cKK=DJfHd^)z<*0Nw7`)A7wI|4MdAzMCGtEIE-gY}=Oy*GLP2V~obwg3PC literal 0 HcmV?d00001 diff --git a/test/test_pythonlib.py b/test/test_pythonlib.py index 97ddccf5..e74d9d46 100755 --- a/test/test_pythonlib.py +++ b/test/test_pythonlib.py @@ -78,7 +78,7 @@ for vers in (2.7, 3.4, 3.5, 3.6): test_options[key] = (os.path.join(src_dir, pythonlib), PYOC, key, vers) pass -for vers in (1.4, 1.5, +for vers in (1.3, 1.4, 1.5, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 'pypy3.2', 'pypy2.7'): diff --git a/uncompyle6/parser.py b/uncompyle6/parser.py index 114231c5..1514acdd 100644 --- a/uncompyle6/parser.py +++ b/uncompyle6/parser.py @@ -621,7 +621,13 @@ def get_python_parser( if version < 3.0: if version < 2.2: - if version == 1.4: + if version == 1.3: + import uncompyle6.parsers.parse13 as parse13 + if compile_mode == 'exec': + p = parse13.Python14Parser(debug_parser) + else: + p = parse13.Python14ParserSingle(debug_parser) + elif version == 1.4: import uncompyle6.parsers.parse14 as parse14 if compile_mode == 'exec': p = parse14.Python14Parser(debug_parser) diff --git a/uncompyle6/parsers/parse13.py b/uncompyle6/parsers/parse13.py new file mode 100644 index 00000000..593805ee --- /dev/null +++ b/uncompyle6/parsers/parse13.py @@ -0,0 +1,49 @@ +# Copyright (c) 2018 Rocky Bernstein + +from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG +from uncompyle6.parser import PythonParserSingle +from uncompyle6.parsers.parse14 import Python14Parser + +class Python13Parser(Python14Parser): + + def p_misc13(self, args): + """ + # Nothing here yet, but will need to add LOAD_GLOBALS + """ + + def __init__(self, debug_parser=PARSER_DEFAULT_DEBUG): + super(Python13Parser, self).__init__(debug_parser) + self.customized = {} + + # def customize_grammar_rules(self, tokens, customize): + # super(Python13Parser, self).customize_grammar_rules(tokens, customize) + # self.remove_rules(""" + # whileelsestmt ::= SETUP_LOOP testexpr l_stmts_opt + # jb_pop + # POP_BLOCK else_suitel COME_FROM + # """) + # self.check_reduce['doc_junk'] = 'tokens' + + + # def reduce_is_invalid(self, rule, ast, tokens, first, last): + # invalid = super(Python14Parser, + # self).reduce_is_invalid(rule, ast, + # tokens, first, last) + # if invalid or tokens is None: + # return invalid + # if rule[0] == 'doc_junk': + # return not isinstance(tokens[first].pattr, str) + + + +class Python13ParserSingle(Python13Parser, PythonParserSingle): + pass + +if __name__ == '__main__': + # Check grammar + p = Python13Parser() + p.check_grammar() + p.dump_grammar() + +# local variables: +# tab-width: 4 diff --git a/uncompyle6/parsers/parse14.py b/uncompyle6/parsers/parse14.py index 55c856f3..250f37f4 100644 --- a/uncompyle6/parsers/parse14.py +++ b/uncompyle6/parsers/parse14.py @@ -8,12 +8,11 @@ class Python14Parser(Python15Parser): def p_misc14(self, args): """ - # Nothing here yet, but will need to add UNARY_CALL, BINARY_CALL, + # Not much here yet, but will probably need to add UNARY_CALL, BINARY_CALL, # RAISE_EXCEPTION, BUILD_FUNCTION, UNPACK_ARG, UNPACK_VARARG, LOAD_LOCAL, # SET_FUNC_ARGS, and RESERVE_FAST - # FIXME: should check that this indeed around __doc__ - # Possibly not strictly needed + # Not strictly needed, but tidies up output stmt ::= doc_junk doc_junk ::= LOAD_CONST POP_TOP diff --git a/uncompyle6/scanner.py b/uncompyle6/scanner.py index e55cd380..9df4c08e 100755 --- a/uncompyle6/scanner.py +++ b/uncompyle6/scanner.py @@ -37,7 +37,7 @@ from xdis.util import code2num # The byte code versions we support. # Note: these all have to be floats -PYTHON_VERSIONS = frozenset((1.4, 1.5, +PYTHON_VERSIONS = frozenset((1.3, 1.4, 1.5, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7)) diff --git a/uncompyle6/scanners/scanner13.py b/uncompyle6/scanners/scanner13.py new file mode 100644 index 00000000..611199dd --- /dev/null +++ b/uncompyle6/scanners/scanner13.py @@ -0,0 +1,35 @@ +# Copyright (c) 2018 by Rocky Bernstein +""" +Python 1.3 bytecode decompiler massaging. + +This massages tokenized 1.3 bytecode to make it more amenable for +grammar parsing. +""" + +import uncompyle6.scanners.scanner14 as scan +# from uncompyle6.scanners.scanner26 import ingest as ingest26 + +# bytecode verification, verify(), uses JUMP_OPs from here +from xdis.opcodes import opcode_13 +JUMP_OPS = opcode_13.JUMP_OPS + +# We base this off of 1.4 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 Scanner13(scan.Scanner14): + def __init__(self, show_asm=False): + scan.Scanner14.__init__(self, show_asm) + self.opc = opcode_13 + self.opname = opcode_13.opname + self.version = 1.3 + return + + # def ingest22(self, co, classname=None, code_objects={}, show_asm=None): + # tokens, customize = self.parent_ingest(co, classname, code_objects, show_asm) + # tokens = [t for t in tokens if t.kind != 'SET_LINENO'] + + # # for t in tokens: + # # print(t) + # + # return tokens, customize