Sync with decompyle3

This commit is contained in:
rocky
2024-02-10 21:06:05 -05:00
parent c8b92e2275
commit f1bf86088e
2 changed files with 38 additions and 20 deletions

View File

@@ -210,7 +210,8 @@ class SourceWalkerError(Exception):
class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin): class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin):
""" """
Class to traverses a Parse Tree of the bytecode instruction built from parsing to produce some sort of source text. Class to traverses a Parse Tree of the bytecode instruction built from parsing to
produce some sort of source text.
The Parse tree may be turned an Abstract Syntax tree as an intermediate step. The Parse tree may be turned an Abstract Syntax tree as an intermediate step.
""" """
@@ -318,7 +319,7 @@ class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin):
customize_for_version(self, is_pypy, version) customize_for_version(self, is_pypy, version)
return return
def maybe_show_tree(self, ast, phase): def maybe_show_tree(self, tree, phase):
if self.showast.get("before", False): if self.showast.get("before", False):
self.println( self.println(
""" """
@@ -334,7 +335,7 @@ class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin):
+ " " + " "
) )
if self.showast.get(phase, False): if self.showast.get(phase, False):
maybe_show_tree(self, ast) maybe_show_tree(self, tree)
def str_with_template(self, ast): def str_with_template(self, ast):
stream = sys.stdout stream = sys.stdout

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2019-2023 by Rocky Bernstein # Copyright (c) 2019-2024 by Rocky Bernstein
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@@ -14,6 +14,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from copy import copy from copy import copy
from typing import Optional
from spark_parser import GenericASTTraversal, GenericASTTraversalPruningException from spark_parser import GenericASTTraversal, GenericASTTraversalPruningException
@@ -56,29 +57,34 @@ def is_docstring(node, version, co_consts):
return node == ASSIGN_DOC_STRING(co_consts[0], doc_load) return node == ASSIGN_DOC_STRING(co_consts[0], doc_load)
def is_not_docstring(call_stmt_node): def is_not_docstring(call_stmt_node) -> bool:
try: try:
return ( return (
call_stmt_node == "call_stmt" call_stmt_node == "call_stmt"
and call_stmt_node[0][0] == "LOAD_STR" and call_stmt_node[0][0] == "LOAD_STR"
and call_stmt_node[1] == "POP_TOP" and call_stmt_node[1] == "POP_TOP"
) )
except: except Exception:
return False return False
class TreeTransform(GenericASTTraversal, object): class TreeTransform(GenericASTTraversal, object):
def __init__(self, version, show_ast=None, is_pypy=False): def __init__(
self,
version: tuple,
is_pypy=False,
show_ast: Optional[dict] = None,
):
self.version = version self.version = version
self.showast = show_ast self.showast = show_ast
self.is_pypy = is_pypy self.is_pypy = is_pypy
return return
def maybe_show_tree(self, ast): def maybe_show_tree(self, tree):
if isinstance(self.showast, dict) and ( if isinstance(self.showast, dict) and (
self.showast.get("before") or self.showast.get("after") self.showast.get("before") or self.showast.get("after")
): ):
maybe_show_tree(self, ast) maybe_show_tree(self, tree)
def preorder(self, node=None): def preorder(self, node=None):
"""Walk the tree in roughly 'preorder' (a bit of a lie explained below). """Walk the tree in roughly 'preorder' (a bit of a lie explained below).
@@ -122,6 +128,7 @@ class TreeTransform(GenericASTTraversal, object):
mkfunc_pattr = node[-1].pattr mkfunc_pattr = node[-1].pattr
if isinstance(mkfunc_pattr, tuple): if isinstance(mkfunc_pattr, tuple):
assert isinstance(mkfunc_pattr, tuple)
assert len(mkfunc_pattr) == 4 and isinstance(mkfunc_pattr, int) assert len(mkfunc_pattr) == 4 and isinstance(mkfunc_pattr, int)
if len(code.co_consts) > 0 and isinstance(code.co_consts[0], str): if len(code.co_consts) > 0 and isinstance(code.co_consts[0], str):
@@ -216,6 +223,7 @@ class TreeTransform(GenericASTTraversal, object):
return node return node
if isinstance(call[1], SyntaxTree): if isinstance(call[1], SyntaxTree):
expr = call[1][0] expr = call[1][0]
assert_expr.transformed_by = "n_ifstmt"
node = SyntaxTree( node = SyntaxTree(
kind, kind,
[ [
@@ -225,8 +233,8 @@ class TreeTransform(GenericASTTraversal, object):
expr, expr,
RAISE_VARARGS_1, RAISE_VARARGS_1,
], ],
transformed_by="n_ifstmt",
) )
node.transformed_by = "n_ifstmt"
pass pass
pass pass
else: else:
@@ -254,9 +262,10 @@ class TreeTransform(GenericASTTraversal, object):
LOAD_ASSERT = expr[0] LOAD_ASSERT = expr[0]
node = SyntaxTree( node = SyntaxTree(
kind, [assert_expr, jump_cond, LOAD_ASSERT, RAISE_VARARGS_1] kind,
[assert_expr, jump_cond, LOAD_ASSERT, RAISE_VARARGS_1],
transformed_by="n_ifstmt",
) )
node.transformed_by = ("n_ifstmt",)
pass pass
pass pass
return node return node
@@ -416,6 +425,12 @@ class TreeTransform(GenericASTTraversal, object):
list_for_node.transformed_by = ("n_list_for",) list_for_node.transformed_by = ("n_list_for",)
return list_for_node return list_for_node
def n_negated_testtrue(self, node):
assert node[0] == "testtrue"
test_node = node[0][0]
test_node.transformed_by = "n_negated_testtrue"
return test_node
def n_stmts(self, node): def n_stmts(self, node):
if node.first_child() == "SETUP_ANNOTATIONS": if node.first_child() == "SETUP_ANNOTATIONS":
prev = node[0][0] prev = node[0][0]
@@ -448,26 +463,28 @@ class TreeTransform(GenericASTTraversal, object):
node = self.preorder(node) node = self.preorder(node)
return node return node
def transform(self, ast, code): def transform(self, parse_tree: GenericASTTraversal, code) -> GenericASTTraversal:
self.maybe_show_tree(ast) self.maybe_show_tree(parse_tree)
self.ast = copy(ast) self.ast = copy(parse_tree)
del parse_tree
self.ast = self.traverse(self.ast, is_lambda=False) self.ast = self.traverse(self.ast, is_lambda=False)
n = len(self.ast)
try: try:
# Disambiguate a string (expression) which appears as a "call_stmt" at # Disambiguate a string (expression) which appears as a "call_stmt" at
# the beginning of a function versus a docstring. Seems pretty academic, # the beginning of a function versus a docstring. Seems pretty academic,
# but this is Python. # but this is Python.
call_stmt = ast[0][0] call_stmt = self.ast[0][0]
if is_not_docstring(call_stmt): if is_not_docstring(call_stmt):
call_stmt.kind = "string_at_beginning" call_stmt.kind = "string_at_beginning"
call_stmt.transformed_by = "transform" call_stmt.transformed_by = "transform"
pass pass
except: except Exception:
pass pass
try: try:
for i in range(len(self.ast)): for i in range(n):
sstmt = ast[i] sstmt = self.ast[i]
if len(sstmt) == 1 and sstmt == "sstmt": if len(sstmt) == 1 and sstmt == "sstmt":
self.ast[i] = self.ast[i][0] self.ast[i] = self.ast[i][0]
@@ -493,7 +510,7 @@ class TreeTransform(GenericASTTraversal, object):
if self.ast[-1] == RETURN_NONE: if self.ast[-1] == RETURN_NONE:
self.ast.pop() # remove last node self.ast.pop() # remove last node
# todo: if empty, add 'pass' # todo: if empty, add 'pass'
except: except Exception:
pass pass
return self.ast return self.ast