You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 00:45:53 +08:00
Port over some recent decompyle3 3.8 fixes
This commit is contained in:
@@ -17,7 +17,7 @@ spark grammar differences over Python 3.7 for Python 3.8
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
from uncompyle6.parser import PythonParserSingle
|
||||
from uncompyle6.parser import PythonParserSingle, nop_func
|
||||
from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
|
||||
from uncompyle6.parsers.parse37 import Python37Parser
|
||||
|
||||
@@ -367,6 +367,195 @@ class Python38Parser(Python37Parser):
|
||||
self.check_reduce["whilestmt38"] = "tokens"
|
||||
self.check_reduce["try_elsestmtl38"] = "AST"
|
||||
|
||||
# For a rough break out on the first word. This may
|
||||
# include instructions that don't need customization,
|
||||
# but we'll do a finer check after the rough breakout.
|
||||
customize_instruction_basenames = frozenset(
|
||||
(
|
||||
"BEFORE",
|
||||
"BUILD",
|
||||
"CALL",
|
||||
"DICT",
|
||||
"GET",
|
||||
"FORMAT",
|
||||
"LIST",
|
||||
"LOAD",
|
||||
"MAKE",
|
||||
"SETUP",
|
||||
"UNPACK",
|
||||
)
|
||||
)
|
||||
|
||||
# Opcode names in the custom_ops_processed set have rules that get added
|
||||
# unconditionally and the rules are constant. So they need to be done
|
||||
# only once and if we see the opcode a second we don't have to consider
|
||||
# adding more rules.
|
||||
#
|
||||
custom_ops_processed = frozenset()
|
||||
|
||||
# A set of instruction operation names that exist in the token stream.
|
||||
# We use this customize the grammar that we create.
|
||||
# 2.6-compatible set comprehensions
|
||||
self.seen_ops = frozenset([t.kind for t in tokens])
|
||||
self.seen_op_basenames = frozenset(
|
||||
[opname[: opname.rfind("_")] for opname in self.seen_ops]
|
||||
)
|
||||
|
||||
custom_ops_processed = set(["DICT_MERGE"])
|
||||
|
||||
# Loop over instructions adding custom grammar rules based on
|
||||
# a specific instruction seen.
|
||||
|
||||
if "PyPy" in customize:
|
||||
self.addRule(
|
||||
"""
|
||||
stmt ::= assign3_pypy
|
||||
stmt ::= assign2_pypy
|
||||
assign3_pypy ::= expr expr expr store store store
|
||||
assign2_pypy ::= expr expr store store
|
||||
""",
|
||||
nop_func,
|
||||
)
|
||||
|
||||
n = len(tokens)
|
||||
# Determine if we have an iteration CALL_FUNCTION_1.
|
||||
has_get_iter_call_function1 = False
|
||||
for i, token in enumerate(tokens):
|
||||
if (
|
||||
token == "GET_ITER"
|
||||
and i < n - 2
|
||||
and tokens[i + 1] == "CALL_FUNCTION_1"
|
||||
):
|
||||
has_get_iter_call_function1 = True
|
||||
|
||||
for i, token in enumerate(tokens):
|
||||
opname = token.kind
|
||||
|
||||
# Do a quick breakout before testing potentially
|
||||
# each of the dozen or so instruction in if elif.
|
||||
if (
|
||||
opname[: opname.find("_")] not in customize_instruction_basenames
|
||||
or opname in custom_ops_processed
|
||||
):
|
||||
continue
|
||||
|
||||
opname_base = opname[: opname.rfind("_")]
|
||||
|
||||
# Do a quick breakout before testing potentially
|
||||
# each of the dozen or so instruction in if elif.
|
||||
if (
|
||||
opname[: opname.find("_")] not in customize_instruction_basenames
|
||||
or opname in custom_ops_processed
|
||||
):
|
||||
continue
|
||||
if opname_base in (
|
||||
"BUILD_LIST",
|
||||
"BUILD_SET",
|
||||
"BUILD_SET_UNPACK",
|
||||
"BUILD_TUPLE",
|
||||
"BUILD_TUPLE_UNPACK",
|
||||
):
|
||||
v = token.attr
|
||||
|
||||
is_LOAD_CLOSURE = False
|
||||
if opname_base == "BUILD_TUPLE":
|
||||
# If is part of a "load_closure", then it is not part of a
|
||||
# "list".
|
||||
is_LOAD_CLOSURE = True
|
||||
for j in range(v):
|
||||
if tokens[i - j - 1].kind != "LOAD_CLOSURE":
|
||||
is_LOAD_CLOSURE = False
|
||||
break
|
||||
if is_LOAD_CLOSURE:
|
||||
rule = "load_closure ::= %s%s" % (("LOAD_CLOSURE " * v), opname)
|
||||
self.add_unique_rule(rule, opname, token.attr, customize)
|
||||
|
||||
elif opname_base == "BUILD_LIST":
|
||||
v = token.attr
|
||||
if v == 0:
|
||||
rule_str = """
|
||||
list ::= BUILD_LIST_0
|
||||
list_unpack ::= BUILD_LIST_0 expr LIST_EXTEND
|
||||
list ::= list_unpack
|
||||
"""
|
||||
self.add_unique_doc_rules(rule_str, customize)
|
||||
|
||||
elif opname == "BUILD_TUPLE_UNPACK_WITH_CALL":
|
||||
# FIXME: should this be parameterized by EX value?
|
||||
self.addRule(
|
||||
"""expr ::= call_ex_kw3
|
||||
call_ex_kw3 ::= expr
|
||||
build_tuple_unpack_with_call
|
||||
expr
|
||||
CALL_FUNCTION_EX_KW
|
||||
""",
|
||||
nop_func,
|
||||
)
|
||||
|
||||
if not is_LOAD_CLOSURE or v == 0:
|
||||
# We do this complicated test to speed up parsing of
|
||||
# pathelogically long literals, especially those over 1024.
|
||||
build_count = token.attr
|
||||
thousands = build_count // 1024
|
||||
thirty32s = (build_count // 32) % 32
|
||||
if thirty32s > 0:
|
||||
rule = "expr32 ::=%s" % (" expr" * 32)
|
||||
self.add_unique_rule(rule, opname_base, build_count, customize)
|
||||
pass
|
||||
if thousands > 0:
|
||||
self.add_unique_rule(
|
||||
"expr1024 ::=%s" % (" expr32" * 32),
|
||||
opname_base,
|
||||
build_count,
|
||||
customize,
|
||||
)
|
||||
pass
|
||||
collection = opname_base[opname_base.find("_") + 1 :].lower()
|
||||
rule = (
|
||||
("%s ::= " % collection)
|
||||
+ "expr1024 " * thousands
|
||||
+ "expr32 " * thirty32s
|
||||
+ "expr " * (build_count % 32)
|
||||
+ opname
|
||||
)
|
||||
self.add_unique_rules(["expr ::= %s" % collection, rule], customize)
|
||||
continue
|
||||
continue
|
||||
elif opname == "LOAD_CLOSURE":
|
||||
self.addRule("""load_closure ::= LOAD_CLOSURE+""", nop_func)
|
||||
|
||||
elif opname == "LOOKUP_METHOD":
|
||||
# A PyPy speciality - DRY with parse3
|
||||
self.addRule(
|
||||
"""
|
||||
expr ::= attribute
|
||||
attribute ::= expr LOOKUP_METHOD
|
||||
""",
|
||||
nop_func,
|
||||
)
|
||||
custom_ops_processed.add(opname)
|
||||
|
||||
elif opname == "MAKE_FUNCTION_8":
|
||||
if "LOAD_DICTCOMP" in self.seen_ops:
|
||||
# Is there something general going on here?
|
||||
rule = """
|
||||
dict_comp ::= load_closure LOAD_DICTCOMP LOAD_STR
|
||||
MAKE_FUNCTION_8 expr
|
||||
GET_ITER CALL_FUNCTION_1
|
||||
"""
|
||||
self.addRule(rule, nop_func)
|
||||
elif "LOAD_SETCOMP" in self.seen_ops:
|
||||
rule = """
|
||||
set_comp ::= load_closure LOAD_SETCOMP LOAD_STR
|
||||
MAKE_FUNCTION_CLOSURE expr
|
||||
GET_ITER CALL_FUNCTION_1
|
||||
"""
|
||||
self.addRule(rule, nop_func)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def reduce_is_invalid(self, rule, ast, tokens, first, last):
|
||||
invalid = super(Python38Parser,
|
||||
self).reduce_is_invalid(rule, ast,
|
||||
|
Reference in New Issue
Block a user