diff --git a/HISTORY.md b/HISTORY.md index ef68b947..49f168f3 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -93,8 +93,18 @@ so. Then hamled made a few commits earler on, while Eike Siewertsen made a few commits later on. But mostly wibiti, and Guenther Starnberger got the code to where uncompyle2 was around 2012. -This project, uncompyle6, however owes its existence to the fork of -uncompyle2 by Myst herie (Mysterie) whose first commit picks up at +In uncompyle2 decompilation of python bytecode 2.5 & 2.6 is done by +transforming the byte code into a a pseudo 2.7 python bytecode and is +based on code from Eloi Vanderbeken. + +This project, uncompyle6, abandons that approach for various +reasons. However the main reason is that we need offsets in fragment +deparsing to be exactly the same, and the transformation process can +remove instructions. Adding instructions with psuedo_offsets is +however okay. + +Uncompyle6, however owes its existence to the fork of uncompyle2 by +Myst herie (Mysterie) whose first commit picks up at 2012. I chose this since it seemed to have been at that time the most actively, if briefly, worked on. Also starting around 2012 is Dark Fenx's uncompyle3 which I used for inspiration for Python3 support. diff --git a/README.rst b/README.rst index 3cd3bcd2..9290782d 100644 --- a/README.rst +++ b/README.rst @@ -86,11 +86,12 @@ Known Bugs/Restrictions Python 2 deparsing decompiles each and all the Python 2.7.10 and 2.7.11 installed packages I have on my system, more than 90% verify -ok. Some of these failures may be bugs in the verification process. So -as such, it is probably a little better than uncompyle2. Other Python -2 versions do worse. +ok. Some of these failures may be bugs in the verification process. +So as such, it is probably a little better than uncompyle2. Python 2.6 +is catching up the versions of uncompyle2 that support 2.6 but is +currently worse. Even less good is Python 2.5 bytecodes. -All of the Python 3.2-3.5.4 Python standard lib packages that I have +All of the Python 3.2-3.5 Python standard lib packages that I have installed on my system deparse. Each Python version has about 200 bytecode files. I'm not sure how well these verify though. diff --git a/uncompyle6/parser.py b/uncompyle6/parser.py index fea1afa8..59671e2f 100644 --- a/uncompyle6/parser.py +++ b/uncompyle6/parser.py @@ -199,13 +199,7 @@ class PythonParser(GenericASTBuilder): _jump ::= JUMP_FORWARD _jump ::= JUMP_BACK - # Note: Python < 2.7 doesn't have POP_JUMP_IF ... - # FIXME: segregate 2.7+ - - jmp_false ::= POP_JUMP_IF_FALSE - jmp_true ::= POP_JUMP_IF_TRUE - - # Zero or more COME_FROM + # Zero or more COME_FROMs # loops can have this _come_from ::= _come_from COME_FROM _come_from ::= diff --git a/uncompyle6/parsers/parse26.py b/uncompyle6/parsers/parse26.py index 45ca5be1..ab17a6dd 100644 --- a/uncompyle6/parsers/parse26.py +++ b/uncompyle6/parsers/parse26.py @@ -69,8 +69,6 @@ class Python26Parser(Python2Parser): # after one of these jumps def p_jumps26(self, args): """ - jmp_false ::= JUMP_IF_FALSE - jmp_true ::= JUMP_IF_TRUE jmp_true ::= JUMP_IF_TRUE POP_TOP jmp_false ::= JUMP_IF_FALSE POP_TOP jf_pop ::= JUMP_FORWARD come_from_pop @@ -84,7 +82,8 @@ class Python26Parser(Python2Parser): ja_cf_pop ::= JUMP_ABSOLUTE come_from_pop jf_cf_pop ::= JUMP_FORWARD come_froms POP_TOP - jb_bp_come_from ::= JUMP_BACK POP_BLOCK COME_FROM + bp_come_from ::= POP_BLOCK COME_FROM + jb_bp_come_from ::= JUMP_BACK bp_come_from _ifstmts_jump ::= c_stmts_opt jf_pop COME_FROM _ifstmts_jump ::= c_stmts_opt JUMP_FORWARD COME_FROM come_from_pop @@ -94,6 +93,7 @@ class Python26Parser(Python2Parser): # we start a new block. For reasons I don't fully # understand, there is also a value on the top of the stack come_from_pop ::= COME_FROM POP_TOP + """ def p_stmt26(self, args): @@ -134,11 +134,10 @@ class Python26Parser(Python2Parser): SETUP_FINALLY LOAD_FAST DELETE_FAST whilestmt ::= SETUP_LOOP testexpr l_stmts_opt jb_pop POP_BLOCK _come_from + whilestmt ::= SETUP_LOOP testexpr l_stmts_opt jb_cf_pop bp_come_from + whilestmt ::= SETUP_LOOP testexpr return_stmts come_froms POP_TOP bp_come_from - whilestmt ::= SETUP_LOOP testexpr l_stmts_opt jb_cf_pop POP_BLOCK COME_FROM - whilestmt ::= SETUP_LOOP testexpr return_stmts come_froms POP_TOP POP_BLOCK COME_FROM - - while1stmt ::= SETUP_LOOP return_stmts POP_BLOCK COME_FROM + while1stmt ::= SETUP_LOOP return_stmts bp_come_from return_stmt ::= ret_expr RETURN_END_IF come_from_pop return_stmt ::= ret_expr RETURN_VALUE come_from_pop diff --git a/uncompyle6/parsers/parse27.py b/uncompyle6/parsers/parse27.py index 4ba283b7..cacf206b 100644 --- a/uncompyle6/parsers/parse27.py +++ b/uncompyle6/parsers/parse27.py @@ -30,9 +30,11 @@ class Python27Parser(Python2Parser): jmp_false POP_TOP designator POP_TOP """ - def p_misc27(self, args): + def p_jump27(self, args): """ _ifstmts_jump ::= c_stmts_opt JUMP_FORWARD COME_FROM + jmp_false ::= POP_JUMP_IF_FALSE + jmp_true ::= POP_JUMP_IF_TRUE """ diff --git a/uncompyle6/parsers/parse3.py b/uncompyle6/parsers/parse3.py index 510e02a7..43e17dce 100644 --- a/uncompyle6/parsers/parse3.py +++ b/uncompyle6/parsers/parse3.py @@ -247,6 +247,8 @@ class Python3Parser(PythonParser): """ come_froms ::= come_froms COME_FROM come_froms ::= COME_FROM + jmp_false ::= POP_JUMP_IF_FALSE + jmp_true ::= POP_JUMP_IF_TRUE """ def p_stmt3(self, args): diff --git a/uncompyle6/scanners/scanner2.py b/uncompyle6/scanners/scanner2.py index 868dc4bc..f67e14b8 100755 --- a/uncompyle6/scanners/scanner2.py +++ b/uncompyle6/scanners/scanner2.py @@ -557,6 +557,8 @@ class Scanner2(scan.Scanner): # not myself? If so, it's part of a larger conditional. # rocky: if we have a conditional jump to the next instruction, then # possibly I am "skipping over" a "pass" or null statement. + + ## FIXME: need to handle <2.7 which has this as two instructions. if ( code[pre[target]] in (self.pop_jump_if_or_pop | self.pop_jump_if) and (target > pos) ): diff --git a/uncompyle6/scanners/scanner26.py b/uncompyle6/scanners/scanner26.py index 574f9c50..59c64c80 100755 --- a/uncompyle6/scanners/scanner26.py +++ b/uncompyle6/scanners/scanner26.py @@ -272,6 +272,8 @@ class Scanner26(scan.Scanner2): if offset in self.load_asserts: op_name = 'LOAD_ASSERT' elif op == self.opc.RETURN_VALUE: + # FIXME: return_end_ifs calculation is off. + # not taking into account "and" expressions if offset in self.return_end_ifs: op_name = 'RETURN_END_IF'