You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-02 16:44:46 +08:00
Merging in recent 3.7 and 3.8 improvements from decompyle6
This rebases 3.7, 3.8 ...decompilation off of 3.7ish rather than a 3.4 base. Add more 3.7 and 3.8 tests
This commit is contained in:
@@ -20,8 +20,12 @@ def test_grammar():
|
||||
(lhs, rhs, tokens, right_recursive, dup_rhs) = p.check_sets()
|
||||
|
||||
# We have custom rules that create the below
|
||||
expect_lhs = set(["pos_arg", "attribute"])
|
||||
expect_lhs = set(["pos_arg"])
|
||||
|
||||
if PYTHON_VERSION < 3.8:
|
||||
if PYTHON_VERSION < 3.7:
|
||||
expect_lhs.add("attribute")
|
||||
|
||||
expect_lhs.add("get_iter")
|
||||
else:
|
||||
expect_lhs.add("async_with_as_stmt")
|
||||
@@ -31,7 +35,7 @@ def test_grammar():
|
||||
|
||||
expect_right_recursive = set([("designList", ("store", "DUP_TOP", "designList"))])
|
||||
|
||||
if PYTHON_VERSION < 3.7:
|
||||
if PYTHON_VERSION <= 3.7:
|
||||
unused_rhs.add("call")
|
||||
|
||||
if PYTHON_VERSION > 2.6:
|
||||
@@ -50,9 +54,11 @@ def test_grammar():
|
||||
)
|
||||
)
|
||||
if PYTHON_VERSION >= 3.0:
|
||||
expect_lhs.add("annotate_arg")
|
||||
expect_lhs.add("annotate_tuple")
|
||||
unused_rhs.add("mkfunc_annotate")
|
||||
if PYTHON_VERSION < 3.7:
|
||||
expect_lhs.add("annotate_arg")
|
||||
expect_lhs.add("annotate_tuple")
|
||||
unused_rhs.add("mkfunc_annotate")
|
||||
|
||||
unused_rhs.add("dict_comp")
|
||||
unused_rhs.add("classdefdeco1")
|
||||
unused_rhs.add("tryelsestmtl")
|
||||
|
BIN
test/bytecode_3.7_run/00_chained-compare.pyc
Normal file
BIN
test/bytecode_3.7_run/00_chained-compare.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.7_run/03_ifelse_chained_for.pyc
Normal file
BIN
test/bytecode_3.7_run/03_ifelse_chained_for.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.7_run/06_while_return.pyc
Normal file
BIN
test/bytecode_3.7_run/06_while_return.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.7_run/14_mixed_expressions.pyc
Normal file
BIN
test/bytecode_3.7_run/14_mixed_expressions.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
test/bytecode_3.8_run/00_docstring.pyc
Normal file
BIN
test/bytecode_3.8_run/00_docstring.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.8_run/01_chained_compare.pyc
Normal file
BIN
test/bytecode_3.8_run/01_chained_compare.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.8_run/01_extra_iter.pyc
Normal file
BIN
test/bytecode_3.8_run/01_extra_iter.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.8_run/01_fstring.pyc
Normal file
BIN
test/bytecode_3.8_run/01_fstring.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.8_run/04_def_annotate.pyc
Normal file
BIN
test/bytecode_3.8_run/04_def_annotate.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.8_run/04_for_no_jump_back.pyc
Normal file
BIN
test/bytecode_3.8_run/04_for_no_jump_back.pyc
Normal file
Binary file not shown.
BIN
test/bytecode_3.8_run/14_mixed_expressions.pyc
Normal file
BIN
test/bytecode_3.8_run/14_mixed_expressions.pyc
Normal file
Binary file not shown.
24
test/simple_source/bug37/03_ifelse_chained_for.py
Normal file
24
test/simple_source/bug37/03_ifelse_chained_for.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# From decompyle3/semantics/customize3.py
|
||||
# The bug is handling "for" loop inside the
|
||||
# chained compare ifelse
|
||||
def n_classdef3(a, b, c, l):
|
||||
r = 1
|
||||
if 3.0 <= a <= 3.2:
|
||||
for n in l:
|
||||
if b:
|
||||
break
|
||||
elif c:
|
||||
r = 2
|
||||
pass
|
||||
pass
|
||||
else:
|
||||
r = 3
|
||||
pass
|
||||
return r
|
||||
|
||||
assert n_classdef3(10, True, True, []) == 3
|
||||
assert n_classdef3(0, False, True, []) == 3
|
||||
assert n_classdef3(3.1, True, True, []) == 1
|
||||
assert n_classdef3(3.1, True, False, [1]) == 1
|
||||
assert n_classdef3(3.1, True, True, [2]) == 1
|
||||
assert n_classdef3(3.1, False, True, [3]) == 2
|
5
test/simple_source/bug38/01_named_expr.py
Normal file
5
test/simple_source/bug38/01_named_expr.py
Normal file
@@ -0,0 +1,5 @@
|
||||
(x, y) = "foo", 0
|
||||
if x := __name__:
|
||||
y = 1
|
||||
assert x == "__main__", "Walrus operator changes value"
|
||||
assert y == 1, "Walrus operator branch taken"
|
17
test/simple_source/bug38/04_for_no_jump_back.py
Normal file
17
test/simple_source/bug38/04_for_no_jump_back.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# from mult_by_const/instruction.py
|
||||
# Bug in 3.8 was handling no JUMP_BACK in "for" loop. It is
|
||||
# in the "if" instead
|
||||
def instruction_sequence_value(instrs, a, b):
|
||||
for instr in instrs:
|
||||
if a:
|
||||
a = 6
|
||||
elif b:
|
||||
return 0
|
||||
pass
|
||||
|
||||
return a
|
||||
|
||||
assert instruction_sequence_value([], True, True) == 1
|
||||
assert instruction_sequence_value([1], True, True) == 6
|
||||
assert instruction_sequence_value([1], False, True) == 0
|
||||
assert instruction_sequence_value([1], False, False) == False
|
53
test/simple_source/expression/14_mixed_expressions.py
Normal file
53
test/simple_source/expression/14_mixed_expressions.py
Normal file
@@ -0,0 +1,53 @@
|
||||
# Covers a large number of operators
|
||||
#
|
||||
# This code is RUNNABLE!
|
||||
|
||||
import sys
|
||||
PYTHON_VERSION = sys.version_info[0] + (sys.version_info[1] / 10.0)
|
||||
|
||||
assert PYTHON_VERSION >= 3.7
|
||||
|
||||
# some floats (from 01_float.py)
|
||||
|
||||
x = 1e300
|
||||
assert 0.0 == x * 0
|
||||
assert x * 1e300 == float("inf")
|
||||
assert str(float("inf") * 0.0) == "nan"
|
||||
assert str(float("-inf") * 0.0) == "nan"
|
||||
assert -1e300 * 1e300 == float("-inf")
|
||||
|
||||
# Complex (adapted from 02_complex.py)
|
||||
y = 5j
|
||||
assert y ** 2 == -25
|
||||
y **= 3
|
||||
assert y == (-0-125j)
|
||||
|
||||
|
||||
# Tests BINARY_TRUE_DIVIDE and INPLACE_TRUE_DIVIDE (from 02_try_divide.py)
|
||||
x = 2
|
||||
assert 4 / x == 2
|
||||
|
||||
x = 5
|
||||
assert x / 2 == 2.5
|
||||
x = 3
|
||||
x /= 2
|
||||
assert x == 1.5
|
||||
|
||||
x = 2
|
||||
assert 4 // x == 2
|
||||
x = 7
|
||||
x //= 2
|
||||
assert x == 3
|
||||
|
||||
x = 3
|
||||
assert x % 2 == 1
|
||||
x %= 2
|
||||
assert x == 1
|
||||
|
||||
assert x << 2 == 4
|
||||
x <<= 3
|
||||
assert x == 8
|
||||
|
||||
assert x >> 1 == 4
|
||||
x >>= 1
|
||||
assert x == 4
|
File diff suppressed because it is too large
Load Diff
1304
uncompyle6/parsers/parse37base.py
Normal file
1304
uncompyle6/parsers/parse37base.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,12 @@ from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
|
||||
from uncompyle6.parsers.parse37 import Python37Parser
|
||||
|
||||
class Python38Parser(Python37Parser):
|
||||
def p_38walrus(self, args):
|
||||
"""
|
||||
# named_expr is also known as the "walrus op" :=
|
||||
expr ::= named_expr
|
||||
named_expr ::= expr DUP_TOP store
|
||||
"""
|
||||
|
||||
def p_38misc(self, args):
|
||||
"""
|
||||
@@ -38,6 +44,12 @@ class Python38Parser(Python37Parser):
|
||||
stmt ::= whilestmt38
|
||||
stmt ::= whileTruestmt38
|
||||
stmt ::= call
|
||||
stmt ::= ifstmtl
|
||||
|
||||
break ::= POP_BLOCK BREAK_LOOP
|
||||
break ::= POP_BLOCK POP_TOP BREAK_LOOP
|
||||
break ::= POP_TOP BREAK_LOOP
|
||||
break ::= POP_EXCEPT BREAK_LOOP
|
||||
|
||||
# FIXME: this should be restricted to being inside a try block
|
||||
stmt ::= except_ret38
|
||||
@@ -89,27 +101,39 @@ class Python38Parser(Python37Parser):
|
||||
|
||||
return ::= ret_expr ROT_TWO POP_TOP RETURN_VALUE
|
||||
|
||||
# 3.8 can push a looping JUMP_BACK into into a JUMP_ from a statement that jumps to it
|
||||
lastl_stmt ::= ifpoplaststmtl
|
||||
ifpoplaststmtl ::= testexpr POP_TOP c_stmts_opt JUMP_BACK
|
||||
ifelsestmtl ::= testexpr c_stmts_opt jb_cfs else_suitel JUMP_BACK come_froms
|
||||
|
||||
_ifstmts_jumpl ::= c_stmts JUMP_BACK
|
||||
_ifstmts_jumpl ::= _ifstmts_jump
|
||||
ifstmtl ::= testexpr _ifstmts_jumpl
|
||||
|
||||
for38 ::= expr get_iter store for_block JUMP_BACK
|
||||
for38 ::= expr for_iter store for_block JUMP_BACK
|
||||
for38 ::= expr for_iter store for_block JUMP_BACK POP_BLOCK
|
||||
for38 ::= expr for_iter store for_block
|
||||
for38 ::= expr get_for_iter store for_block JUMP_BACK
|
||||
for38 ::= expr get_for_iter store for_block JUMP_BACK POP_BLOCK
|
||||
for38 ::= expr get_for_iter store for_block
|
||||
|
||||
forelsestmt38 ::= expr for_iter store for_block POP_BLOCK else_suite
|
||||
forelselaststmt38 ::= expr for_iter store for_block POP_BLOCK else_suitec
|
||||
forelselaststmtl38 ::= expr for_iter store for_block POP_BLOCK else_suitel
|
||||
forelsestmt38 ::= expr get_for_iter store for_block POP_BLOCK else_suite
|
||||
forelselaststmt38 ::= expr get_for_iter store for_block POP_BLOCK else_suitec
|
||||
forelselaststmtl38 ::= expr get_for_iter store for_block POP_BLOCK else_suitel
|
||||
|
||||
whilestmt38 ::= testexpr l_stmts_opt COME_FROM JUMP_BACK POP_BLOCK
|
||||
whilestmt38 ::= testexpr l_stmts_opt JUMP_BACK POP_BLOCK
|
||||
whilestmt38 ::= testexpr returns POP_BLOCK
|
||||
whilestmt38 ::= testexpr l_stmts JUMP_BACK
|
||||
whilestmt38 ::= _come_froms testexpr l_stmts_opt COME_FROM JUMP_BACK POP_BLOCK
|
||||
whilestmt38 ::= _come_froms testexpr l_stmts_opt JUMP_BACK POP_BLOCK
|
||||
whilestmt38 ::= _come_froms testexpr l_stmts_opt JUMP_BACK come_froms
|
||||
whilestmt38 ::= _come_froms testexpr returns POP_BLOCK
|
||||
whilestmt38 ::= _come_froms testexpr l_stmts JUMP_BACK
|
||||
whilestmt38 ::= _come_froms testexpr l_stmts come_froms
|
||||
|
||||
# while1elsestmt ::= l_stmts JUMP_BACK
|
||||
whileTruestmt ::= l_stmts JUMP_BACK POP_BLOCK
|
||||
while1stmt ::= l_stmts COME_FROM_LOOP
|
||||
while1stmt ::= l_stmts COME_FROM JUMP_BACK COME_FROM_LOOP
|
||||
whileTruestmt38 ::= l_stmts JUMP_BACK
|
||||
whileTruestmt ::= _come_froms l_stmts JUMP_BACK POP_BLOCK
|
||||
while1stmt ::= _come_froms l_stmts COME_FROM_LOOP
|
||||
while1stmt ::= _come_froms l_stmts COME_FROM JUMP_BACK COME_FROM_LOOP
|
||||
whileTruestmt38 ::= _come_froms l_stmts JUMP_BACK
|
||||
whileTruestmt38 ::= _come_froms l_stmts JUMP_BACK COME_FROM_EXCEPT_CLAUSE
|
||||
|
||||
for_block ::= l_stmts_opt _come_from_loops JUMP_BACK
|
||||
for_block ::= _come_froms l_stmts_opt _come_from_loops JUMP_BACK
|
||||
|
||||
except_cond1 ::= DUP_TOP expr COMPARE_OP jmp_false
|
||||
POP_TOP POP_TOP POP_TOP
|
||||
@@ -134,7 +158,8 @@ class Python38Parser(Python37Parser):
|
||||
except_ret38a ::= COME_FROM_FINALLY POP_TOP POP_TOP POP_TOP
|
||||
expr ROT_FOUR
|
||||
POP_EXCEPT RETURN_VALUE END_FINALLY
|
||||
except_handler38 ::= JUMP_FORWARD COME_FROM_FINALLY
|
||||
|
||||
except_handler38 ::= _jump COME_FROM_FINALLY
|
||||
except_stmts END_FINALLY opt_come_from_except
|
||||
except_handler38a ::= COME_FROM_FINALLY POP_TOP POP_TOP POP_TOP
|
||||
POP_EXCEPT POP_TOP stmts END_FINALLY
|
||||
@@ -160,13 +185,15 @@ class Python38Parser(Python37Parser):
|
||||
self.customized = {}
|
||||
|
||||
def remove_rules_38(self):
|
||||
self.remove_rules("""
|
||||
self.remove_rules(
|
||||
"""
|
||||
stmt ::= async_for_stmt37
|
||||
stmt ::= for
|
||||
stmt ::= forelsestmt
|
||||
stmt ::= try_except36
|
||||
stmt ::= async_forelse_stmt
|
||||
|
||||
async_for_stmt ::= SETUP_LOOP expr
|
||||
async_for_stmt ::= setup_loop expr
|
||||
GET_AITER
|
||||
SETUP_EXCEPT GET_ANEXT LOAD_CONST
|
||||
YIELD_FROM
|
||||
@@ -178,7 +205,7 @@ class Python38Parser(Python37Parser):
|
||||
COME_FROM
|
||||
POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_TOP POP_BLOCK
|
||||
COME_FROM_LOOP
|
||||
async_for_stmt37 ::= SETUP_LOOP expr
|
||||
async_for_stmt37 ::= setup_loop expr
|
||||
GET_AITER
|
||||
SETUP_EXCEPT GET_ANEXT
|
||||
LOAD_CONST YIELD_FROM
|
||||
@@ -190,7 +217,7 @@ class Python38Parser(Python37Parser):
|
||||
POP_TOP POP_BLOCK
|
||||
COME_FROM_LOOP
|
||||
|
||||
async_forelse_stmt ::= SETUP_LOOP expr
|
||||
async_forelse_stmt ::= setup_loop expr
|
||||
GET_AITER
|
||||
SETUP_EXCEPT GET_ANEXT LOAD_CONST
|
||||
YIELD_FROM
|
||||
@@ -203,13 +230,13 @@ class Python38Parser(Python37Parser):
|
||||
POP_TOP POP_TOP POP_TOP POP_EXCEPT POP_TOP POP_BLOCK
|
||||
else_suite COME_FROM_LOOP
|
||||
|
||||
for ::= SETUP_LOOP expr for_iter store for_block POP_BLOCK
|
||||
for ::= SETUP_LOOP expr for_iter store for_block POP_BLOCK NOP
|
||||
for ::= setup_loop expr get_for_iter store for_block POP_BLOCK
|
||||
for ::= setup_loop expr get_for_iter store for_block POP_BLOCK NOP
|
||||
|
||||
for_block ::= l_stmts_opt COME_FROM_LOOP JUMP_BACK
|
||||
forelsestmt ::= SETUP_LOOP expr for_iter store for_block POP_BLOCK else_suite
|
||||
forelselaststmt ::= SETUP_LOOP expr for_iter store for_block POP_BLOCK else_suitec
|
||||
forelselaststmtl ::= SETUP_LOOP expr for_iter store for_block POP_BLOCK else_suitel
|
||||
forelsestmt ::= setup_loop expr get_for_iter store for_block POP_BLOCK else_suite
|
||||
forelselaststmt ::= setup_loop expr get_for_iter store for_block POP_BLOCK else_suitec
|
||||
forelselaststmtl ::= setup_loop expr get_for_iter store for_block POP_BLOCK else_suitel
|
||||
|
||||
tryelsestmtl3 ::= SETUP_EXCEPT suite_stmts_opt POP_BLOCK
|
||||
except_handler COME_FROM else_suitel
|
||||
@@ -223,15 +250,16 @@ class Python38Parser(Python37Parser):
|
||||
COME_FROM_FINALLY suite_stmts_opt END_FINALLY
|
||||
tryfinally_return_stmt ::= SETUP_FINALLY suite_stmts_opt POP_BLOCK
|
||||
LOAD_CONST COME_FROM_FINALLY
|
||||
|
||||
|
||||
""")
|
||||
"""
|
||||
)
|
||||
|
||||
def customize_grammar_rules(self, tokens, customize):
|
||||
super(Python37Parser, self).customize_grammar_rules(tokens, customize)
|
||||
self.remove_rules_38()
|
||||
self.check_reduce['ifstmt'] = 'tokens'
|
||||
self.check_reduce['whileTruestmt38'] = 'tokens'
|
||||
self.check_reduce["ifstmt"] = "tokens"
|
||||
self.check_reduce["whileTruestmt38"] = "tokens"
|
||||
self.check_reduce["whilestmt38"] = "tokens"
|
||||
self.check_reduce["ifstmtl"] = "tokens"
|
||||
|
||||
def reduce_is_invalid(self, rule, ast, tokens, first, last):
|
||||
invalid = super(Python38Parser,
|
||||
@@ -240,34 +268,47 @@ class Python38Parser(Python37Parser):
|
||||
self.remove_rules_38()
|
||||
if invalid:
|
||||
return invalid
|
||||
if rule[0] == 'ifstmt':
|
||||
lhs = rule[0]
|
||||
if lhs == "ifstmt":
|
||||
# Make sure jumps don't extend beyond the end of the if statement.
|
||||
l = last
|
||||
if l == len(tokens):
|
||||
l -= 1
|
||||
if isinstance(tokens[l].offset, str):
|
||||
last_offset = int(tokens[l].offset.split('_')[0], 10)
|
||||
last_offset = int(tokens[l].offset.split("_")[0], 10)
|
||||
else:
|
||||
last_offset = tokens[l].offset
|
||||
for i in range(first, l):
|
||||
t = tokens[i]
|
||||
if t.kind == 'POP_JUMP_IF_FALSE':
|
||||
if t.kind == "POP_JUMP_IF_FALSE":
|
||||
if t.attr > last_offset:
|
||||
return True
|
||||
pass
|
||||
pass
|
||||
pass
|
||||
elif rule[0] == 'whileTruestmt38':
|
||||
t = tokens[last-1]
|
||||
if t.kind == 'JUMP_BACK':
|
||||
return t.attr != tokens[first].offset
|
||||
elif lhs == "ifstmtl":
|
||||
if last == len(tokens):
|
||||
last -= 1
|
||||
if (tokens[last].attr and isinstance(tokens[last].attr, int)):
|
||||
return tokens[first].offset < tokens[last].attr
|
||||
pass
|
||||
elif lhs in ("whileTruestmt38", "whilestmt38"):
|
||||
jb_index = last - 1
|
||||
while jb_index > 0 and tokens[jb_index].kind.startswith("COME_FROM"):
|
||||
jb_index -= 1
|
||||
t = tokens[jb_index]
|
||||
if t.kind != "JUMP_BACK":
|
||||
return True
|
||||
return t.attr != tokens[first].off2int()
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class Python38ParserSingle(Python38Parser, PythonParserSingle):
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Check grammar
|
||||
# FIXME: DRY this with other parseXX.py routines
|
||||
@@ -284,9 +325,11 @@ if __name__ == "__main__":
|
||||
opcode_set = set(s.opc.opname).union(
|
||||
set(
|
||||
"""JUMP_BACK CONTINUE RETURN_END_IF COME_FROM
|
||||
LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP LOAD_CLASSNAME
|
||||
LAMBDA_MARKER RETURN_LAST
|
||||
""".split()))
|
||||
LOAD_GENEXPR LOAD_ASSERT LOAD_SETCOMP LOAD_DICTCOMP LOAD_CLASSNAME
|
||||
LAMBDA_MARKER RETURN_LAST
|
||||
""".split()
|
||||
)
|
||||
)
|
||||
remain_tokens = set(tokens) - opcode_set
|
||||
import re
|
||||
|
||||
|
@@ -22,24 +22,50 @@ This sets up opcodes Python's 3.7 and calls a generalized
|
||||
scanner routine for Python 3.
|
||||
"""
|
||||
|
||||
from uncompyle6.scanners.scanner36 import Scanner36
|
||||
from uncompyle6.scanners.scanner3 import Scanner3
|
||||
from uncompyle6.scanners.scanner37base import Scanner37Base
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPs from here
|
||||
from xdis.opcodes import opcode_37 as opc
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPS from here
|
||||
JUMP_OPs = opc.JUMP_OPS
|
||||
|
||||
class Scanner37(Scanner36):
|
||||
|
||||
class Scanner37(Scanner37Base):
|
||||
def __init__(self, show_asm=None):
|
||||
Scanner3.__init__(self, 3.7, show_asm)
|
||||
Scanner37Base.__init__(self, 3.7, show_asm)
|
||||
return
|
||||
|
||||
pass
|
||||
|
||||
def ingest(self, co, classname=None, code_objects={}, show_asm=None):
|
||||
tokens, customize = Scanner37Base.ingest(self, co, classname, code_objects, show_asm)
|
||||
for t in tokens:
|
||||
# The lowest bit of flags indicates whether the
|
||||
# var-keyword argument is placed at the top of the stack
|
||||
if t.op == self.opc.CALL_FUNCTION_EX and t.attr & 1:
|
||||
t.kind = "CALL_FUNCTION_EX_KW"
|
||||
pass
|
||||
elif t.op == self.opc.BUILD_STRING:
|
||||
t.kind = "BUILD_STRING_%s" % t.attr
|
||||
elif t.op == self.opc.CALL_FUNCTION_KW:
|
||||
t.kind = "CALL_FUNCTION_KW_%s" % t.attr
|
||||
elif t.op == self.opc.FORMAT_VALUE:
|
||||
if t.attr & 0x4:
|
||||
t.kind = "FORMAT_VALUE_ATTR"
|
||||
pass
|
||||
elif t.op == self.opc.BUILD_MAP_UNPACK_WITH_CALL:
|
||||
t.kind = "BUILD_MAP_UNPACK_WITH_CALL_%d" % t.attr
|
||||
elif t.op == self.opc.BUILD_TUPLE_UNPACK_WITH_CALL:
|
||||
t.kind = "BUILD_TUPLE_UNPACK_WITH_CALL_%d" % t.attr
|
||||
pass
|
||||
return tokens, customize
|
||||
|
||||
if __name__ == "__main__":
|
||||
from uncompyle6 import PYTHON_VERSION
|
||||
if PYTHON_VERSION == 3.7:
|
||||
import inspect
|
||||
|
||||
co = inspect.currentframe().f_code
|
||||
tokens, customize = Scanner37().ingest(co)
|
||||
for t in tokens:
|
||||
|
1110
uncompyle6/scanners/scanner37base.py
Normal file
1110
uncompyle6/scanners/scanner37base.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -23,23 +23,63 @@ scanner routine for Python 3.
|
||||
"""
|
||||
|
||||
from uncompyle6.scanners.scanner37 import Scanner37
|
||||
from uncompyle6.scanners.scanner3 import Scanner3
|
||||
from uncompyle6.scanners.scanner37base import Scanner37Base
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPs from here
|
||||
from xdis.opcodes import opcode_38 as opc
|
||||
|
||||
# bytecode verification, verify(), uses JUMP_OPS from here
|
||||
JUMP_OPs = opc.JUMP_OPS
|
||||
|
||||
class Scanner38(Scanner37):
|
||||
|
||||
class Scanner38(Scanner37):
|
||||
def __init__(self, show_asm=None):
|
||||
Scanner3.__init__(self, 3.8, show_asm)
|
||||
Scanner37Base.__init__(self, 3.8, show_asm)
|
||||
return
|
||||
|
||||
pass
|
||||
|
||||
def ingest(self, co, classname=None, code_objects={}, show_asm=None):
|
||||
tokens, customize = super(Scanner38, self).ingest(
|
||||
co, classname, code_objects, show_asm
|
||||
)
|
||||
for i, token in enumerate(tokens):
|
||||
opname = token.kind
|
||||
if opname in ("JUMP_FORWARD", "JUMP_ABSOLUTE"):
|
||||
# Turn JUMPs into BREAK_LOOP
|
||||
jump_target = token.attr
|
||||
|
||||
if opname == "JUMP_ABSOLUTE" and token.offset >= jump_target:
|
||||
# Not a forward jump, so continue
|
||||
# FIXME: Do we need "continue" detection?
|
||||
continue
|
||||
if i + 1 < len(tokens) and tokens[i + 1] == "JUMP_BACK":
|
||||
# Sometimes the jump back is *after* the break...
|
||||
jump_back_index = i + 1
|
||||
else:
|
||||
# and sometimes it is *before* where we jumped to.
|
||||
jump_back_index = self.offset2tok_index[jump_target] - 1
|
||||
while tokens[jump_back_index].kind.startswith("COME_FROM_"):
|
||||
jump_back_index -= 1
|
||||
pass
|
||||
pass
|
||||
jump_back_token = tokens[jump_back_index]
|
||||
if (
|
||||
jump_back_token == "JUMP_BACK"
|
||||
and jump_back_token.attr < token.offset
|
||||
):
|
||||
token.kind = "BREAK_LOOP"
|
||||
pass
|
||||
pass
|
||||
return tokens, customize
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from uncompyle6 import PYTHON_VERSION
|
||||
from decompyle3 import PYTHON_VERSION
|
||||
|
||||
if PYTHON_VERSION == 3.8:
|
||||
import inspect
|
||||
|
||||
co = inspect.currentframe().f_code
|
||||
tokens, customize = Scanner38().ingest(co)
|
||||
for t in tokens:
|
||||
|
@@ -159,5 +159,12 @@ class Token:
|
||||
def __getitem__(self, i):
|
||||
raise IndexError
|
||||
|
||||
def off2int(self):
|
||||
if isinstance(self.offset, int):
|
||||
return self.offset
|
||||
else:
|
||||
assert isinstance(self.offset, str)
|
||||
return(int(self.offset.split("_")[0]))
|
||||
|
||||
|
||||
NoneToken = Token("LOAD_CONST", offset=-1, attr=None, pattr=None)
|
||||
|
@@ -344,8 +344,12 @@ TABLE_DIRECT = {
|
||||
# 'return': ( '%|return %c\n', 0),
|
||||
'return_if_stmt': ( 'return %c\n', 0),
|
||||
|
||||
'ifstmt': ( '%|if %c:\n%+%c%-', 0, 1 ),
|
||||
'iflaststmt': ( '%|if %c:\n%+%c%-', 0, 1 ),
|
||||
'ifstmt': ( '%|if %c:\n%+%c%-',
|
||||
0, # "testexpr" or "testexpr_then"
|
||||
1, # "_ifstmts_jump" or "return_stmts"
|
||||
),
|
||||
|
||||
'iflaststmt': ( '%|if %c:\n%+%c%-', 0, 1 ),
|
||||
'iflaststmtl': ( '%|if %c:\n%+%c%-', 0, 1 ),
|
||||
'testtrue': ( 'not %p',
|
||||
(0, PRECEDENCE['unary_not']) ),
|
||||
@@ -359,18 +363,18 @@ TABLE_DIRECT = {
|
||||
'ifelsestmt': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-', 0, 1, 3 ),
|
||||
'ifelsestmtc': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-', 0, 1, 3 ),
|
||||
'ifelsestmtl': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-', 0, 1, 3 ),
|
||||
'ifelsestmtr': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-', 0, 1, 2 ),
|
||||
'ifelsestmtr2': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-\n\n', 0, 1, 3 ), # has COME_FROM in position 2
|
||||
|
||||
|
||||
# "elif" forms are not generated by the parser but are created through tree
|
||||
# transformations. See "n_ifelsestmt".
|
||||
'ifelifstmt': ( '%|if %c:\n%+%c%-%c', 0, 1, 3 ),
|
||||
# These are created only via transformation
|
||||
'ifelifstmt': ( '%|if %c:\n%+%c%-%c',
|
||||
0, # "testexpr" or "testexpr_then"
|
||||
1, 3 ),
|
||||
'elifelifstmt': ( '%|elif %c:\n%+%c%-%c', 0, 1, 3 ),
|
||||
'elifstmt': ( '%|elif %c:\n%+%c%-', 0, 1 ),
|
||||
'elifelsestmt': ( '%|elif %c:\n%+%c%-%|else:\n%+%c%-', 0, 1, 3 ),
|
||||
'ifelsestmtr': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-', 0, 1, 2 ),
|
||||
'ifelsestmtr2': ( '%|if %c:\n%+%c%-%|else:\n%+%c%-\n\n', 0, 1, 3 ), # has COME_FROM
|
||||
'elifelsestmtr': ( '%|elif %c:\n%+%c%-%|else:\n%+%c%-\n\n', 0, 1, 2 ),
|
||||
'elifelsestmtr2': ( '%|elif %c:\n%+%c%-%|else:\n%+%c%-\n\n', 0, 1, 3 ), # has COME_FROM in position 2
|
||||
'elifelsestmtr2': ( '%|elif %c:\n%+%c%-%|else:\n%+%c%-\n\n', 0, 1, 3 ), # has COME_FROM
|
||||
|
||||
'whileTruestmt': ( '%|while True:\n%+%c%-\n\n', 1 ),
|
||||
'whilestmt': ( '%|while %c:\n%+%c%-\n\n', 1, 2 ),
|
||||
@@ -414,10 +418,11 @@ TABLE_DIRECT = {
|
||||
(1, 'expr'), (5, 'store') ),
|
||||
'except_suite': ( '%+%c%-%C', 0, (1, maxint, '') ),
|
||||
|
||||
# In Python 3.6, this is more complicated in the presence of "returns"
|
||||
# In Python 3.6+, this is more complicated in the presence of "returns"
|
||||
'except_suite_finalize': ( '%+%c%-%C', 1, (3, maxint, '') ),
|
||||
|
||||
'pass': ( '%|pass\n', ),
|
||||
'STORE_FAST': ( '%{pattr}', ),
|
||||
'kv': ( '%c: %c', 3, 1 ),
|
||||
'kv2': ( '%c: %c', 1, 2 ),
|
||||
'import': ( '%|import %c\n', 2),
|
||||
|
@@ -37,6 +37,7 @@ def customize_for_version3(self, version):
|
||||
(0, "expr"),
|
||||
(4, "expr"),
|
||||
),
|
||||
"except_cond2": ("%|except %c as %c:\n", (1, "expr"), (5, "store")),
|
||||
"function_def_annotate": ("\n\n%|def %c%c\n", -1, 0),
|
||||
# When a generator is a single parameter of a function,
|
||||
# it doesn't need the surrounding parenethesis.
|
||||
@@ -331,7 +332,8 @@ def customize_for_version3(self, version):
|
||||
(1, "suite_stmts_opt"),
|
||||
(3, "except_handler"),
|
||||
(5, "else_suitel"),
|
||||
)
|
||||
),
|
||||
"LOAD_CLASSDEREF": ("%{pattr}",),
|
||||
}
|
||||
)
|
||||
if version >= 3.4:
|
||||
|
@@ -12,7 +12,7 @@
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""Isolate Python 3.6 version-specific semantic actions here.
|
||||
"""Isolate Python 3.8 version-specific semantic actions here.
|
||||
"""
|
||||
|
||||
########################
|
||||
@@ -80,10 +80,18 @@ def customize_for_version38(self, version):
|
||||
(0, 'expr'),
|
||||
(3, 'for_block'), -2 ),
|
||||
|
||||
'ifpoplaststmtl': ( '%|if %c:\n%+%c%-',
|
||||
(0, "testexpr"),
|
||||
(2, "c_stmts" ) ),
|
||||
|
||||
'ifstmtl': ( '%|if %c:\n%+%c%-',
|
||||
(0, "testexpr"),
|
||||
(1, "_ifstmts_jumpl") ),
|
||||
|
||||
'whilestmt38': ( '%|while %c:\n%+%c%-\n\n',
|
||||
(0, 'testexpr'), (1, 'l_stmts') ),
|
||||
(1, 'testexpr'), (2, 'l_stmts') ),
|
||||
'whileTruestmt38': ( '%|while True:\n%+%c%-\n\n',
|
||||
(0, 'l_stmts') ),
|
||||
(1, 'l_stmts') ),
|
||||
'try_elsestmtl38': (
|
||||
'%|try:\n%+%c%-%c%|else:\n%+%c%-',
|
||||
(1, 'suite_stmts_opt'),
|
||||
@@ -98,4 +106,7 @@ def customize_for_version38(self, version):
|
||||
'tryfinally38': (
|
||||
'%|try:\n%+%c%-%|finally:\n%+%c%-\n\n',
|
||||
(3, 'returns'), 6 ),
|
||||
"named_expr": ( # AKA "walrus operatotr"
|
||||
"%c := %c", (2, "store"), (0, "expr")
|
||||
)
|
||||
})
|
||||
|
@@ -1130,11 +1130,15 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
|
||||
def n_generator_exp(self, node):
|
||||
self.write("(")
|
||||
iter_index = 3
|
||||
if self.version > 3.2:
|
||||
code_index = -6
|
||||
if self.version > 3.6:
|
||||
# Python 3.7+ adds optional "come_froms" at node[0]
|
||||
iter_index = 4
|
||||
else:
|
||||
code_index = -5
|
||||
self.comprehension_walk(node, iter_index=3, code_index=code_index)
|
||||
self.comprehension_walk(node, iter_index=iter_index, code_index=code_index)
|
||||
self.write(")")
|
||||
self.prune()
|
||||
|
||||
@@ -2015,8 +2019,13 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
self.default(node)
|
||||
|
||||
def n_except_cond2(self, node):
|
||||
if node[-2][0] == "unpack":
|
||||
node[-2][0].kind = "unpack_w_parens"
|
||||
if node[-1] == "come_from_opt":
|
||||
unpack_node = -3
|
||||
else:
|
||||
unpack_node = -2
|
||||
|
||||
if node[unpack_node][0] == "unpack":
|
||||
node[unpack_node][0].kind = "unpack_w_parens"
|
||||
self.default(node)
|
||||
|
||||
def template_engine(self, entry, startnode):
|
||||
|
@@ -181,6 +181,7 @@ class TreeTransform(GenericASTTraversal, object):
|
||||
|
||||
n = else_suite[0]
|
||||
old_stmts = None
|
||||
else_suite_index = 1
|
||||
|
||||
if len(n) == 1 == len(n[0]) and n[0] == "stmt":
|
||||
n = n[0][0]
|
||||
@@ -192,9 +193,12 @@ class TreeTransform(GenericASTTraversal, object):
|
||||
"iflaststmtl",
|
||||
"ifelsestmtl",
|
||||
"ifelsestmtc",
|
||||
"ifpoplaststmtl",
|
||||
):
|
||||
# This seems needed for Python 2.5-2.7
|
||||
n = n[0]
|
||||
if n.kind == "ifpoplaststmtl":
|
||||
old_stmts = n[2]
|
||||
else_suite_index = 2
|
||||
pass
|
||||
pass
|
||||
elif len(n) > 1 and 1 == len(n[0]) and n[0] == "stmt" and n[1].kind == "stmt":
|
||||
@@ -206,7 +210,7 @@ class TreeTransform(GenericASTTraversal, object):
|
||||
else:
|
||||
return node
|
||||
|
||||
if n.kind in ("ifstmt", "iflaststmt", "iflaststmtl"):
|
||||
if n.kind in ("ifstmt", "iflaststmt", "iflaststmtl", "ifpoplaststmtl"):
|
||||
node.kind = "ifelifstmt"
|
||||
n.kind = "elifstmt"
|
||||
elif n.kind in ("ifelsestmtr",):
|
||||
@@ -223,17 +227,24 @@ class TreeTransform(GenericASTTraversal, object):
|
||||
if old_stmts:
|
||||
if n.kind == "elifstmt":
|
||||
trailing_else = SyntaxTree("stmts", old_stmts[1:])
|
||||
# We use elifelsestmtr because it has 3 nodes
|
||||
elifelse_stmt = SyntaxTree(
|
||||
"elifelsestmtr", [n[0], n[1], trailing_else]
|
||||
)
|
||||
node[3] = elifelse_stmt
|
||||
if len(trailing_else):
|
||||
# We use elifelsestmtr because it has 3 nodes
|
||||
elifelse_stmt = SyntaxTree(
|
||||
"elifelsestmtr", [n[0], n[else_suite_index], trailing_else]
|
||||
)
|
||||
node[3] = elifelse_stmt
|
||||
else:
|
||||
elif_stmt = SyntaxTree(
|
||||
"elifstmt", [n[0], n[else_suite_index]]
|
||||
)
|
||||
node[3] = elif_stmt
|
||||
|
||||
node.transformed_by = "n_ifelsestmt"
|
||||
pass
|
||||
else:
|
||||
# Other cases for n.kind may happen here
|
||||
pass
|
||||
pass
|
||||
node.transformed_by = "n_ifelsestmt"
|
||||
return node
|
||||
|
||||
n_ifelsestmtc = n_ifelsestmtl = n_ifelsestmt
|
||||
|
Reference in New Issue
Block a user