Merge branch 'master' of github.com:rocky/python-uncompyle6

Conflicts:
	uncompyle6/parsers/parse26.py
This commit is contained in:
rocky
2016-07-03 12:08:37 -04:00
8 changed files with 33 additions and 21 deletions

View File

@@ -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.

View File

@@ -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.

View File

@@ -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 ::=

View File

@@ -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

View File

@@ -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
"""

View File

@@ -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):

View File

@@ -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) ):

View File

@@ -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'