You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 00:45:53 +08:00
Merge branch 'controlflow'
This commit is contained in:
14
HISTORY.md
14
HISTORY.md
@@ -29,7 +29,9 @@ augmented with pseudo instruction COME_FROM. This code introduced
|
||||
another clever idea: using table-driven semantics routines, using
|
||||
format specifiers.
|
||||
|
||||
The last mention of a release of SPARK from John is around 2002.
|
||||
The last mention of a release of SPARK from John is around 2002. As
|
||||
released, although the Early Algorithm parser was in good shape, this
|
||||
code was woefully lacking as serious Python deparser.
|
||||
|
||||
In the fall of 2000, Hartmut Goebel
|
||||
[took over maintaining the code](https://groups.google.com/forum/#!searchin/comp.lang.python/hartmut$20goebel/comp.lang.python/35s3mp4-nuY/UZALti6ujnQJ). The
|
||||
@@ -112,12 +114,18 @@ Fenx's uncompyle3 which I used for inspiration for Python3 support.
|
||||
I started working on this late 2015, mostly to add fragment support.
|
||||
In that, I decided to make this runnable on Python 3.2+ and Python 2.6+
|
||||
while, handling Python bytecodes from Python versions 2.5+ and
|
||||
3.2+.
|
||||
3.2+. In doing so, it has been expedient to separate this into three
|
||||
projects: load loading and disassembly (xdis), parsing and tree
|
||||
building (spark_parser), and grammar and semantic actions for
|
||||
decompiling (uncompyle6).
|
||||
|
||||
|
||||
Over the many years, code styles and Python features have
|
||||
changed. However brilliant the code was and still is, it hasn't really
|
||||
had a single public active maintainer. And there have been many forks
|
||||
of the code.
|
||||
of the code. I have spent a great deal of time trying to organize and
|
||||
modularize the code so that it can handle more Python versions more
|
||||
gracefully (with still only moderate success).
|
||||
|
||||
That it has been in need of an overhaul has been recognized by the
|
||||
Hartmut a decade an a half ago:
|
||||
|
@@ -31,9 +31,12 @@ def test_grammar():
|
||||
assert expect_right_recursive == right_recursive
|
||||
s = get_scanner(PYTHON_VERSION, IS_PYPY)
|
||||
ignore_set = set(
|
||||
"""JUMP_BACK CONTINUE RETURN_END_IF COME_FROM
|
||||
LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP
|
||||
LAMBDA_MARKER RETURN_LAST
|
||||
"""
|
||||
JUMP_BACK CONTINUE RETURN_END_IF
|
||||
COME_FROM COME_FROM_EXCEPT COME_FROM_LOOP COME_FROM_WITH
|
||||
COME_FROM_FINALLY
|
||||
LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP
|
||||
LAMBDA_MARKER RETURN_LAST
|
||||
""".split())
|
||||
if 2.6 <= PYTHON_VERSION <= 2.7:
|
||||
opcode_set = set(s.opc.opname).union(ignore_set)
|
||||
|
@@ -9,8 +9,6 @@ def test_single_mode():
|
||||
'i = j % 4',
|
||||
'i = {}',
|
||||
'i = []',
|
||||
'while i < 1 or stop:\n i\n',
|
||||
'while i < 1 or stop:\n print%s\n' % ('(i)' if PYTHON3 else ' i'),
|
||||
'for i in range(10):\n i\n',
|
||||
'for i in range(10):\n for j in range(10):\n i + j\n',
|
||||
'try:\n i\nexcept Exception:\n j\nelse:\n k\n'
|
||||
|
@@ -24,7 +24,7 @@ check-2.6 check-2.7: check-bytecode-2 check-bytecode-3 check-2.7-ok
|
||||
|
||||
#: Run working tests from Python 3.1
|
||||
check-3.1: check-bytecode
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.1 --verify $(COMPILE)
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.1 --weak-verify $(COMPILE)
|
||||
|
||||
#: Run working tests from Python 3.2
|
||||
check-3.2: check-bytecode
|
||||
@@ -32,11 +32,11 @@ check-3.2: check-bytecode
|
||||
|
||||
#: Run working tests from Python 3.3
|
||||
check-3.3: check-bytecode
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.3 --verify $(COMPILE)
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.3 --weak-verify $(COMPILE)
|
||||
|
||||
#: Run working tests from Python 3.4
|
||||
check-3.4: check-bytecode check-3.4-ok check-2.7-ok
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.4 --verify $(COMPILE)
|
||||
$(PYTHON) test_pythonlib.py --bytecode-3.4 --weak-verify $(COMPILE)
|
||||
|
||||
#: Run working tests from Python 3.5
|
||||
check-3.5: check-bytecode
|
||||
|
BIN
test/bytecode_3.4/04_while1_while1.pyc
Normal file
BIN
test/bytecode_3.4/04_while1_while1.pyc
Normal file
Binary file not shown.
@@ -1,92 +0,0 @@
|
||||
"""Bisection algorithms."""
|
||||
|
||||
def insort_right(a, x, lo=0, hi=None):
|
||||
"""Insert item x in list a, and keep it sorted assuming a is sorted.
|
||||
|
||||
If x is already in a, insert it to the right of the rightmost x.
|
||||
|
||||
Optional args lo (default 0) and hi (default len(a)) bound the
|
||||
slice of a to be searched.
|
||||
"""
|
||||
|
||||
if lo < 0:
|
||||
raise ValueError('lo must be non-negative')
|
||||
if hi is None:
|
||||
hi = len(a)
|
||||
while lo < hi:
|
||||
mid = (lo+hi)//2
|
||||
if x < a[mid]: hi = mid
|
||||
else: lo = mid+1
|
||||
a.insert(lo, x)
|
||||
|
||||
insort = insort_right # backward compatibility
|
||||
|
||||
def bisect_right(a, x, lo=0, hi=None):
|
||||
"""Return the index where to insert item x in list a, assuming a is sorted.
|
||||
|
||||
The return value i is such that all e in a[:i] have e <= x, and all e in
|
||||
a[i:] have e > x. So if x already appears in the list, a.insert(x) will
|
||||
insert just after the rightmost x already there.
|
||||
|
||||
Optional args lo (default 0) and hi (default len(a)) bound the
|
||||
slice of a to be searched.
|
||||
"""
|
||||
|
||||
if lo < 0:
|
||||
raise ValueError('lo must be non-negative')
|
||||
if hi is None:
|
||||
hi = len(a)
|
||||
while lo < hi:
|
||||
mid = (lo+hi)//2
|
||||
if x < a[mid]: hi = mid
|
||||
else: lo = mid+1
|
||||
return lo
|
||||
|
||||
bisect = bisect_right # backward compatibility
|
||||
|
||||
def insort_left(a, x, lo=0, hi=None):
|
||||
"""Insert item x in list a, and keep it sorted assuming a is sorted.
|
||||
|
||||
If x is already in a, insert it to the left of the leftmost x.
|
||||
|
||||
Optional args lo (default 0) and hi (default len(a)) bound the
|
||||
slice of a to be searched.
|
||||
"""
|
||||
|
||||
if lo < 0:
|
||||
raise ValueError('lo must be non-negative')
|
||||
if hi is None:
|
||||
hi = len(a)
|
||||
while lo < hi:
|
||||
mid = (lo+hi)//2
|
||||
if a[mid] < x: lo = mid+1
|
||||
else: hi = mid
|
||||
a.insert(lo, x)
|
||||
|
||||
|
||||
def bisect_left(a, x, lo=0, hi=None):
|
||||
"""Return the index where to insert item x in list a, assuming a is sorted.
|
||||
|
||||
The return value i is such that all e in a[:i] have e < x, and all e in
|
||||
a[i:] have e >= x. So if x already appears in the list, a.insert(x) will
|
||||
insert just before the leftmost x already there.
|
||||
|
||||
Optional args lo (default 0) and hi (default len(a)) bound the
|
||||
slice of a to be searched.
|
||||
"""
|
||||
|
||||
if lo < 0:
|
||||
raise ValueError('lo must be non-negative')
|
||||
if hi is None:
|
||||
hi = len(a)
|
||||
while lo < hi:
|
||||
mid = (lo+hi)//2
|
||||
if a[mid] < x: lo = mid+1
|
||||
else: hi = mid
|
||||
return lo
|
||||
|
||||
# Overwrite above definitions with a fast C implementation
|
||||
try:
|
||||
from _bisect import *
|
||||
except ImportError:
|
||||
pass
|
28
test/simple_source/looping/04_while1_while1.py
Normal file
28
test/simple_source/looping/04_while1_while1.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# From python 3.4 sre.pyc
|
||||
while 1:
|
||||
if __file__:
|
||||
while 1:
|
||||
if __file__:
|
||||
break
|
||||
raise RuntimeError
|
||||
else:
|
||||
raise RuntimeError
|
||||
|
||||
while 1:
|
||||
if __file__:
|
||||
if __name__:
|
||||
raise RuntimeError
|
||||
else:
|
||||
# flags
|
||||
while __name__:
|
||||
group = 5
|
||||
while 1:
|
||||
if __name__:
|
||||
while 1:
|
||||
if y:
|
||||
break
|
||||
raise RuntimeError
|
||||
elif __file__:
|
||||
x = 2
|
||||
else:
|
||||
raise RuntimeError
|
@@ -174,9 +174,33 @@ class PythonParser(GenericASTBuilder):
|
||||
else_suitec ::= c_stmts
|
||||
else_suitec ::= return_stmts
|
||||
|
||||
stmt ::= assert
|
||||
stmt ::= assert2
|
||||
|
||||
stmt ::= classdef
|
||||
stmt ::= call_stmt
|
||||
|
||||
stmt ::= ifstmt
|
||||
stmt ::= ifelsestmt
|
||||
|
||||
stmt ::= whilestmt
|
||||
stmt ::= while1stmt
|
||||
stmt ::= whileelsestmt
|
||||
stmt ::= while1elsestmt
|
||||
stmt ::= forstmt
|
||||
stmt ::= forelsestmt
|
||||
stmt ::= trystmt
|
||||
stmt ::= tryelsestmt
|
||||
stmt ::= tryfinallystmt
|
||||
stmt ::= withstmt
|
||||
stmt ::= withasstmt
|
||||
|
||||
stmt ::= del_stmt
|
||||
del_stmt ::= DELETE_FAST
|
||||
del_stmt ::= DELETE_NAME
|
||||
del_stmt ::= DELETE_GLOBAL
|
||||
|
||||
|
||||
stmt ::= return_stmt
|
||||
return_stmt ::= ret_expr RETURN_VALUE
|
||||
return_stmts ::= return_stmt
|
||||
@@ -287,20 +311,12 @@ class PythonParser(GenericASTBuilder):
|
||||
|
||||
def p_whilestmt(self, args):
|
||||
"""
|
||||
whilestmt ::= SETUP_LOOP
|
||||
testexpr
|
||||
l_stmts_opt JUMP_BACK
|
||||
POP_BLOCK _come_from
|
||||
whilestmt ::= SETUP_LOOP testexpr l_stmts_opt JUMP_BACK
|
||||
POP_BLOCK _come_from
|
||||
|
||||
whilestmt ::= SETUP_LOOP
|
||||
testexpr
|
||||
return_stmts
|
||||
POP_BLOCK COME_FROM
|
||||
whilestmt ::= SETUP_LOOP testexpr return_stmts
|
||||
POP_BLOCK COME_FROM
|
||||
|
||||
while1stmt ::= SETUP_LOOP l_stmts JUMP_BACK COME_FROM
|
||||
while1stmt ::= SETUP_LOOP l_stmts JUMP_BACK POP_BLOCK COME_FROM
|
||||
|
||||
while1elsestmt ::= SETUP_LOOP l_stmts JUMP_BACK else_suite COME_FROM
|
||||
whileelsestmt ::= SETUP_LOOP testexpr
|
||||
l_stmts_opt JUMP_BACK
|
||||
POP_BLOCK
|
||||
|
@@ -42,7 +42,11 @@ class Python2Parser(PythonParser):
|
||||
|
||||
def p_stmt2(self, args):
|
||||
"""
|
||||
while1stmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK POP_BLOCK COME_FROM
|
||||
while1stmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK POP_BLOCK COME_FROM
|
||||
while1stmt ::= SETUP_LOOP l_stmts JUMP_BACK COME_FROM
|
||||
while1stmt ::= SETUP_LOOP l_stmts JUMP_BACK POP_BLOCK COME_FROM
|
||||
|
||||
while1elsestmt ::= SETUP_LOOP l_stmts JUMP_BACK else_suite COME_FROM
|
||||
|
||||
exec_stmt ::= expr exprlist DUP_TOP EXEC_STMT
|
||||
exec_stmt ::= expr exprlist EXEC_STMT
|
||||
@@ -94,27 +98,6 @@ class Python2Parser(PythonParser):
|
||||
|
||||
stmt ::= exec_stmt
|
||||
|
||||
stmt ::= assert
|
||||
stmt ::= assert2
|
||||
stmt ::= ifstmt
|
||||
stmt ::= ifelsestmt
|
||||
|
||||
stmt ::= whilestmt
|
||||
stmt ::= while1stmt
|
||||
stmt ::= whileelsestmt
|
||||
stmt ::= while1elsestmt
|
||||
stmt ::= forstmt
|
||||
stmt ::= forelsestmt
|
||||
stmt ::= trystmt
|
||||
stmt ::= tryelsestmt
|
||||
stmt ::= tryfinallystmt
|
||||
stmt ::= withstmt
|
||||
stmt ::= withasstmt
|
||||
|
||||
stmt ::= del_stmt
|
||||
del_stmt ::= DELETE_FAST
|
||||
del_stmt ::= DELETE_NAME
|
||||
del_stmt ::= DELETE_GLOBAL
|
||||
del_stmt ::= expr DELETE_SLICE+0
|
||||
del_stmt ::= expr expr DELETE_SLICE+1
|
||||
del_stmt ::= expr expr DELETE_SLICE+2
|
||||
|
@@ -95,27 +95,6 @@ class Python3Parser(PythonParser):
|
||||
raise_stmt2 ::= expr expr RAISE_VARARGS_2
|
||||
raise_stmt3 ::= expr expr expr RAISE_VARARGS_3
|
||||
|
||||
stmt ::= assert
|
||||
stmt ::= assert2
|
||||
stmt ::= ifstmt
|
||||
stmt ::= ifelsestmt
|
||||
|
||||
stmt ::= whilestmt
|
||||
stmt ::= while1stmt
|
||||
stmt ::= whileelsestmt
|
||||
stmt ::= while1elsestmt
|
||||
stmt ::= forstmt
|
||||
stmt ::= forelsestmt
|
||||
stmt ::= trystmt
|
||||
stmt ::= tryelsestmt
|
||||
stmt ::= tryfinallystmt
|
||||
stmt ::= withstmt
|
||||
stmt ::= withasstmt
|
||||
|
||||
stmt ::= del_stmt
|
||||
del_stmt ::= DELETE_FAST
|
||||
del_stmt ::= DELETE_NAME
|
||||
del_stmt ::= DELETE_GLOBAL
|
||||
del_stmt ::= delete_subscr
|
||||
delete_subscr ::= expr expr DELETE_SUBSCR
|
||||
del_stmt ::= expr DELETE_ATTR
|
||||
@@ -158,6 +137,7 @@ class Python3Parser(PythonParser):
|
||||
iflaststmt ::= testexpr c_stmts_opt JUMP_ABSOLUTE
|
||||
|
||||
iflaststmtl ::= testexpr c_stmts_opt JUMP_BACK
|
||||
iflaststmtl ::= testexpr c_stmts_opt JUMP_BACK COME_FROM_LOOP
|
||||
|
||||
ifelsestmt ::= testexpr c_stmts_opt JUMP_FORWARD else_suite COME_FROM
|
||||
|
||||
@@ -166,6 +146,8 @@ class Python3Parser(PythonParser):
|
||||
ifelsestmtr ::= testexpr return_if_stmts return_stmts
|
||||
|
||||
ifelsestmtl ::= testexpr c_stmts_opt JUMP_BACK else_suitel
|
||||
ifelsestmtl ::= testexpr c_stmts_opt JUMP_BACK else_suitel JUMP_BACK COME_FROM_LOOP
|
||||
ifelsestmtl ::= testexpr c_stmts_opt JUMP_BACK else_suitel COME_FROM_LOOP
|
||||
|
||||
|
||||
# FIXME: this feels like a hack. Is it just 1 or two
|
||||
@@ -174,12 +156,12 @@ class Python3Parser(PythonParser):
|
||||
# COME_FROM targets from the wrong places
|
||||
|
||||
trystmt ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
|
||||
try_middle _come_from
|
||||
try_middle opt_come_from_except
|
||||
|
||||
# this is nested inside a trystmt
|
||||
tryfinallystmt ::= SETUP_FINALLY suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST
|
||||
COME_FROM suite_stmts_opt END_FINALLY
|
||||
come_from_or_finally suite_stmts_opt END_FINALLY
|
||||
|
||||
tryelsestmt ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
|
||||
try_middle else_suite come_froms
|
||||
@@ -192,8 +174,14 @@ class Python3Parser(PythonParser):
|
||||
|
||||
try_middle ::= jmp_abs COME_FROM except_stmts
|
||||
END_FINALLY
|
||||
try_middle ::= jmp_abs COME_FROM_EXCEPT except_stmts
|
||||
END_FINALLY
|
||||
|
||||
# FIXME: remove this
|
||||
try_middle ::= JUMP_FORWARD COME_FROM except_stmts
|
||||
END_FINALLY COME_FROM
|
||||
try_middle ::= JUMP_FORWARD COME_FROM except_stmts
|
||||
END_FINALLY COME_FROM_EXCEPT
|
||||
|
||||
except_stmts ::= except_stmts except_stmt
|
||||
except_stmts ::= except_stmt
|
||||
@@ -218,8 +206,8 @@ class Python3Parser(PythonParser):
|
||||
except_suite_finalize ::= SETUP_FINALLY c_stmts_opt except_var_finalize
|
||||
END_FINALLY _jump
|
||||
|
||||
except_var_finalize ::= POP_BLOCK POP_EXCEPT LOAD_CONST COME_FROM LOAD_CONST
|
||||
designator del_stmt
|
||||
except_var_finalize ::= POP_BLOCK POP_EXCEPT LOAD_CONST come_from_or_finally
|
||||
LOAD_CONST designator del_stmt
|
||||
|
||||
except_suite ::= return_stmts
|
||||
|
||||
@@ -236,11 +224,11 @@ class Python3Parser(PythonParser):
|
||||
jmp_abs ::= JUMP_BACK
|
||||
|
||||
withstmt ::= expr SETUP_WITH POP_TOP suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP END_FINALLY
|
||||
|
||||
withasstmt ::= expr SETUP_WITH designator suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP END_FINALLY
|
||||
|
||||
and ::= expr jmp_false expr COME_FROM
|
||||
@@ -249,17 +237,33 @@ class Python3Parser(PythonParser):
|
||||
|
||||
def p_misc3(self, args):
|
||||
"""
|
||||
try_middle ::= JUMP_FORWARD COME_FROM except_stmts END_FINALLY NOP COME_FROM
|
||||
try_middle ::= JUMP_FORWARD COME_FROM_EXCEPT except_stmts END_FINALLY COME_FROM
|
||||
|
||||
for_block ::= l_stmts_opt opt_come_from_loop JUMP_BACK
|
||||
for_block ::= l_stmts
|
||||
iflaststmtl ::= testexpr c_stmts_opt
|
||||
iflaststmt ::= testexpr c_stmts_opt34
|
||||
c_stmts_opt34 ::= JUMP_BACK JUMP_ABSOLUTE c_stmts_opt
|
||||
"""
|
||||
|
||||
def p_come_from3(self, args):
|
||||
"""
|
||||
opt_come_from_except ::= COME_FROM_EXCEPT
|
||||
opt_come_from_except ::= come_froms
|
||||
|
||||
come_froms ::= come_froms COME_FROM
|
||||
come_froms ::=
|
||||
|
||||
opt_come_from_loop ::= opt_come_from_loop COME_FROM_LOOP
|
||||
opt_come_from_loop ::=
|
||||
|
||||
come_from_or_finally ::= COME_FROM_FINALLY
|
||||
come_from_or_finally ::= COME_FROM
|
||||
|
||||
"""
|
||||
|
||||
def p_jump3(self, args):
|
||||
"""
|
||||
come_froms ::= come_froms COME_FROM
|
||||
come_froms ::= COME_FROM
|
||||
jmp_false ::= POP_JUMP_IF_FALSE
|
||||
jmp_true ::= POP_JUMP_IF_TRUE
|
||||
|
||||
@@ -285,16 +289,59 @@ class Python3Parser(PythonParser):
|
||||
stmt ::= LOAD_CLOSURE RETURN_VALUE RETURN_LAST
|
||||
stmt ::= whileTruestmt
|
||||
ifelsestmt ::= testexpr c_stmts_opt JUMP_FORWARD else_suite _come_from
|
||||
"""
|
||||
|
||||
forstmt ::= SETUP_LOOP expr _for designator for_block POP_BLOCK NOP _come_from
|
||||
whileTruestmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK POP_BLOCK _come_from
|
||||
whileTruestmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK POP_BLOCK NOP _come_from
|
||||
def p_loop_stmt3(self, args):
|
||||
"""
|
||||
forstmt ::= SETUP_LOOP expr _for designator for_block POP_BLOCK
|
||||
opt_come_from_loop
|
||||
forelsestmt ::= SETUP_LOOP expr _for designator for_block POP_BLOCK else_suite
|
||||
COME_FROM_LOOP
|
||||
|
||||
forelselaststmt ::= SETUP_LOOP expr _for designator for_block POP_BLOCK else_suitec
|
||||
COME_FROM_LOOP
|
||||
|
||||
forelselaststmtl ::= SETUP_LOOP expr _for designator for_block POP_BLOCK else_suitel
|
||||
COME_FROM_LOOP
|
||||
|
||||
whilestmt ::= SETUP_LOOP testexpr l_stmts_opt JUMP_BACK POP_BLOCK
|
||||
COME_FROM_LOOP
|
||||
|
||||
# The JUMP_ABSOLUTE below comes from escaping an "if" block which surrounds
|
||||
# the while. This is messy
|
||||
whilestmt ::= SETUP_LOOP testexpr l_stmts_opt JUMP_BACK POP_BLOCK
|
||||
JUMP_ABSOLUTE COME_FROM_LOOP
|
||||
|
||||
whilestmt ::= SETUP_LOOP testexpr return_stmts POP_BLOCK
|
||||
COME_FROM_LOOP
|
||||
|
||||
whileelsestmt ::= SETUP_LOOP testexpr l_stmts_opt JUMP_BACK POP_BLOCK
|
||||
else_suite COME_FROM_LOOP
|
||||
while1elsestmt ::= SETUP_LOOP l_stmts JUMP_BACK
|
||||
else_suite COME_FROM_LOOP
|
||||
|
||||
whileelselaststmt ::= SETUP_LOOP testexpr l_stmts_opt JUMP_BACK POP_BLOCK
|
||||
else_suitec COME_FROM_LOOP
|
||||
whileTruestmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK POP_BLOCK
|
||||
COME_FROM_LOOP
|
||||
|
||||
while1stmt ::= SETUP_LOOP l_stmts
|
||||
|
||||
# Python < 3.5 no POP BLOCK
|
||||
whileTruestmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK _come_from
|
||||
whileTruestmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK NOP _come_from
|
||||
whileTruestmt ::= SETUP_LOOP return_stmts _come_from
|
||||
while1stmt ::= SETUP_LOOP l_stmts _come_from JUMP_BACK _come_from
|
||||
whileTruestmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK
|
||||
COME_FROM_LOOP
|
||||
whileTruestmt ::= SETUP_LOOP return_stmts
|
||||
COME_FROM_LOOP
|
||||
|
||||
# FIXME: investigate - can code really produce a NOP?
|
||||
whileTruestmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK NOP
|
||||
COME_FROM_LOOP
|
||||
whileTruestmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK POP_BLOCK NOP
|
||||
COME_FROM_LOOP
|
||||
whileTruestmt ::= SETUP_LOOP l_stmts_opt JUMP_BACK POP_BLOCK NOP
|
||||
COME_FROM_LOOP
|
||||
forstmt ::= SETUP_LOOP expr _for designator for_block POP_BLOCK NOP
|
||||
COME_FROM_LOOP
|
||||
"""
|
||||
|
||||
def p_genexpr3(self, args):
|
||||
|
@@ -19,15 +19,15 @@ class Python35Parser(Python3Parser):
|
||||
# Python 3.5+ has WITH_CLEANUP_START/FINISH
|
||||
|
||||
withstmt ::= expr SETUP_WITH exprlist suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
|
||||
|
||||
withstmt ::= expr SETUP_WITH POP_TOP suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
|
||||
|
||||
withasstmt ::= expr SETUP_WITH designator suite_stmts_opt
|
||||
POP_BLOCK LOAD_CONST COME_FROM
|
||||
POP_BLOCK LOAD_CONST COME_FROM_WITH
|
||||
WITH_CLEANUP_START WITH_CLEANUP_FINISH END_FINALLY
|
||||
|
||||
inplace_op ::= INPLACE_MATRIX_MULTIPLY
|
||||
|
38
uncompyle6/scanners/controlflow.py
Normal file
38
uncompyle6/scanners/controlflow.py
Normal file
@@ -0,0 +1,38 @@
|
||||
"""
|
||||
Detect control flow as much as possible.
|
||||
The basic idea here is to put in explicit end instructions that make
|
||||
grammar parsing simpler and more precise.
|
||||
"""
|
||||
|
||||
from collections import namedtuple
|
||||
from xdis.bytecode import Bytecode
|
||||
|
||||
control_flow_start = namedtuple('control_flow_start', ['name', 'type', 'offset'])
|
||||
control_flow_end = namedtuple('control_flow_end', ['name', 'type', 'offset'])
|
||||
|
||||
class ControlFlow():
|
||||
def __init__(self, scanner):
|
||||
self.scanner = scanner
|
||||
self.opc = self.scanner.opc
|
||||
self.setup_ops = self.scanner.setup_ops
|
||||
self.op_range = self.scanner.op_range
|
||||
|
||||
# Control-flow nesting
|
||||
self.offset_action = {}
|
||||
self.cf_end = []
|
||||
|
||||
def detect_control_flow(self, co):
|
||||
self.bytecode = Bytecode(co, self.opc)
|
||||
for inst in self.bytecode:
|
||||
if inst.opcode in self.setup_ops:
|
||||
# Use part after SETUP_
|
||||
name = inst.opname[len('SETUP_'):]
|
||||
self.offset_action[inst.offset] = control_flow_start(name, 'start', inst.offset)
|
||||
self.offset_action[inst.argval] = control_flow_end(name, 'end', inst.offset)
|
||||
pass
|
||||
pass
|
||||
# import pprint
|
||||
# pp = pprint.PrettyPrinter(indent=4)
|
||||
# pp.pprint(self.offset_action)
|
||||
|
||||
return self.offset_action
|
@@ -29,6 +29,7 @@ from uncompyle6.scanner import Scanner, op_has_argument
|
||||
from xdis.code import iscode
|
||||
from xdis.bytecode import Bytecode
|
||||
from uncompyle6.scanner import Token, parse_fn_counts
|
||||
from uncompyle6.scanners.controlflow import ControlFlow
|
||||
|
||||
# Get all the opcodes into globals
|
||||
import xdis.opcodes.opcode_33 as op3
|
||||
@@ -119,6 +120,9 @@ class Scanner3(Scanner):
|
||||
(self.opc.POP_JUMP_IF_TRUE, self.opc.JUMP_ABSOLUTE)]
|
||||
|
||||
|
||||
def opName(self, offset):
|
||||
return self.opc.opname[self.code[offset]]
|
||||
|
||||
def ingest(self, co, classname=None, code_objects={}, show_asm=None):
|
||||
"""
|
||||
Pick out tokens from an uncompyle6 code object, and transform them,
|
||||
@@ -136,7 +140,7 @@ class Scanner3(Scanner):
|
||||
"""
|
||||
|
||||
show_asm = self.show_asm if not show_asm else show_asm
|
||||
# show_asm = 'both'
|
||||
# show_asm = 'after'
|
||||
if show_asm in ('both', 'before'):
|
||||
bytecode = Bytecode(co, self.opc)
|
||||
for instr in bytecode.get_instructions(co):
|
||||
@@ -188,13 +192,27 @@ class Scanner3(Scanner):
|
||||
# Format: {target offset: [jump offsets]}
|
||||
jump_targets = self.find_jump_targets()
|
||||
|
||||
offset_action = ControlFlow(self).detect_control_flow(co)
|
||||
for inst in bytecode:
|
||||
|
||||
argval = inst.argval
|
||||
if inst.offset in jump_targets:
|
||||
jump_idx = 0
|
||||
for jump_offset in jump_targets[inst.offset]:
|
||||
tokens.append(Token('COME_FROM', None, repr(jump_offset),
|
||||
# We want to process COME_FROMs to the same offset to be in *descending*
|
||||
# offset order so we have the larger range or biggest instruction interval
|
||||
# last. (I think they are sorted in increasing order, but for safety
|
||||
# we sort them). That way, specific COME_FROM tags will match up
|
||||
# properly. For example, a "loop" with an "if" nested in it should have the
|
||||
# "loop" tag last so the grammar rule matches that properly.
|
||||
for jump_offset in sorted(jump_targets[inst.offset], reverse=True):
|
||||
come_from_name = 'COME_FROM'
|
||||
opname = self.opName(jump_offset)
|
||||
if opname.startswith('SETUP_'):
|
||||
come_from_type = opname[len('SETUP_'):]
|
||||
come_from_name = 'COME_FROM_%s' % come_from_type
|
||||
pass
|
||||
tokens.append(Token(come_from_name,
|
||||
None, repr(jump_offset),
|
||||
offset='%s_%s' % (inst.offset, jump_idx),
|
||||
has_arg = True, opc=self.opc))
|
||||
jump_idx += 1
|
||||
|
@@ -400,9 +400,10 @@ def compare_code_with_srcfile(pyc_filename, src_filename, weak_verify=False):
|
||||
|
||||
def compare_files(pyc_filename1, pyc_filename2, weak_verify=False):
|
||||
"""Compare two .pyc files."""
|
||||
version, timestamp, magic_int1, code_obj1, is_pypy = uncompyle6.load_module(pyc_filename1)
|
||||
version, timestamp, magic_int2, code_obj2, is_pypy = uncompyle6.load_module(pyc_filename2)
|
||||
cmp_code_objects(version, is_pypy, code_obj1, code_obj2, ignore_code=weak_verify)
|
||||
version1, timestamp, magic_int1, code_obj1, is_pypy = uncompyle6.load_module(pyc_filename1)
|
||||
version2, timestamp, magic_int2, code_obj2, is_pypy = uncompyle6.load_module(pyc_filename2)
|
||||
weak_verify = weak_verify or (magic_int1 != magic_int2)
|
||||
cmp_code_objects(version1, is_pypy, code_obj1, code_obj2, ignore_code=weak_verify)
|
||||
|
||||
if __name__ == '__main__':
|
||||
t1 = Token('LOAD_CONST', None, 'code_object _expandLang', 52)
|
||||
|
Reference in New Issue
Block a user