From 16c900ff1d727d9f6875b4b0bb48f0733ddb7f6b Mon Sep 17 00:00:00 2001 From: rocky Date: Fri, 17 Dec 2021 05:27:15 -0500 Subject: [PATCH] Propagate show_asm debug option down to functions --- README.rst | 2 +- uncompyle6/scanner.py | 4 ++-- uncompyle6/scanners/scanner37base.py | 3 ++- uncompyle6/semantics/customize3.py | 11 ++++++++-- uncompyle6/semantics/fragments.py | 4 +++- uncompyle6/semantics/make_function3.py | 7 +++++- uncompyle6/semantics/make_function36.py | 18 +++++++++------ uncompyle6/semantics/pysource.py | 29 ++++++++++++++++--------- 8 files changed, 53 insertions(+), 25 deletions(-) diff --git a/README.rst b/README.rst index fb7e7677..35c85243 100644 --- a/README.rst +++ b/README.rst @@ -264,7 +264,7 @@ See Also .. _Cython: https://en.wikipedia.org/wiki/Cython -.. _trepan: https://pypi.python.org/pypi/trepan2g +.. _trepan: https://pypi.python.org/pypi/trepan3k .. _compiler: https://pypi.python.org/pypi/spark_parser .. _HISTORY: https://github.com/rocky/python-uncompyle6/blob/master/HISTORY.md .. _debuggers: https://pypi.python.org/pypi/trepan3k diff --git a/uncompyle6/scanner.py b/uncompyle6/scanner.py index bcaea77d..7baf8fb5 100644 --- a/uncompyle6/scanner.py +++ b/uncompyle6/scanner.py @@ -92,11 +92,11 @@ class Code(object): the diassembled code is stored in the attribute '_tokens'. """ - def __init__(self, co, scanner, classname=None): + def __init__(self, co, scanner, classname=None, show_asm=None): for i in dir(co): if i.startswith("co_"): setattr(self, i, getattr(co, i)) - self._tokens, self._customize = scanner.ingest(co, classname) + self._tokens, self._customize = scanner.ingest(co, classname, show_asm=show_asm) class Scanner(object): diff --git a/uncompyle6/scanners/scanner37base.py b/uncompyle6/scanners/scanner37base.py index 4df09e3e..6a0a4a13 100644 --- a/uncompyle6/scanners/scanner37base.py +++ b/uncompyle6/scanners/scanner37base.py @@ -194,6 +194,7 @@ class Scanner37Base(Scanner): Also, when we encounter certain tokens, we add them to a set which will cause custom grammar rules. Specifically, variable arg tokens like MAKE_FUNCTION or BUILD_LIST cause specific rules for the specific number of arguments they take. + """ def tokens_append(j, token): @@ -455,7 +456,7 @@ class Scanner37Base(Scanner): # as CONTINUE, but that's okay since we add a grammar # rule for that. pattr = argval - target = self.get_target(inst.offset) + target = inst.argval if target <= inst.offset: next_opname = self.insts[i + 1].opname diff --git a/uncompyle6/semantics/customize3.py b/uncompyle6/semantics/customize3.py index cd4a2ef4..5c14b45f 100644 --- a/uncompyle6/semantics/customize3.py +++ b/uncompyle6/semantics/customize3.py @@ -84,12 +84,15 @@ def customize_for_version3(self, version): """List comprehensions in Python 3 when handled as a closure. See if we can combine code. """ + + # FIXME: DRY with comprehension_walk_newer p = self.prec self.prec = 27 code_obj = node[1].attr - assert iscode(code_obj) - code = Code(code_obj, self.scanner, self.currentclass) + assert iscode(code_obj), node[1] + code = Code(code_obj, self.scanner, self.currentclass, self.debug_opts["asm"]) + ast = self.build_ast(code._tokens, code._customize, code) self.customize(code._customize) @@ -103,6 +106,10 @@ def customize_for_version3(self, version): n = ast[1] + # Pick out important parts of the comprehension: + # * the variables we iterate over: "stores" + # * the results we accumulate: "n" + # collections is the name of the expression(s) we are iterating over collections = [node[-3]] list_ifs = [] diff --git a/uncompyle6/semantics/fragments.py b/uncompyle6/semantics/fragments.py index 867cd92e..64a9a72d 100644 --- a/uncompyle6/semantics/fragments.py +++ b/uncompyle6/semantics/fragments.py @@ -1149,13 +1149,15 @@ class FragmentsWalker(pysource.SourceWalker, object): n_classdefdeco2 = n_classdef - def gen_source(self, ast, name, customize, is_lambda=False, returnNone=False): + def gen_source(self, ast, name, customize, is_lambda=False, returnNone=False, + debug_opts=None): """convert parse tree to Python source code""" rn = self.return_none self.return_none = returnNone old_name = self.name self.name = name + self.debug_opts = debug_opts # if code would be empty, append 'pass' if len(ast) == 0: self.println(self.indent, "pass") diff --git a/uncompyle6/semantics/make_function3.py b/uncompyle6/semantics/make_function3.py index d749cf0b..ad4d80e4 100644 --- a/uncompyle6/semantics/make_function3.py +++ b/uncompyle6/semantics/make_function3.py @@ -659,7 +659,12 @@ def make_function3(self, node, is_lambda, nested=1, code_node=None): has_none = "None" in code.co_names rn = has_none and not find_none(ast) self.gen_source( - ast, code.co_name, scanner_code._customize, is_lambda=is_lambda, returnNone=rn + ast, + code.co_name, + scanner_code._customize, + is_lambda=is_lambda, + returnNone=rn, + debug_opts=self.debug_opts, ) # In obscure cases, a function may be a generator but the "yield" diff --git a/uncompyle6/semantics/make_function36.py b/uncompyle6/semantics/make_function36.py index 3ea8efbd..f6e80c88 100644 --- a/uncompyle6/semantics/make_function36.py +++ b/uncompyle6/semantics/make_function36.py @@ -38,7 +38,7 @@ from uncompyle6.show import maybe_show_tree_param_default def make_function36(self, node, is_lambda, nested=1, code_node=None): """Dump function definition, doc string, and function body in - Python version 3.6 and above. + Python version 3.6 and above. """ # MAKE_CLOSURE adds an additional closure slot @@ -51,8 +51,8 @@ def make_function36(self, node, is_lambda, nested=1, code_node=None): def build_param(ast, name, default, annotation=None): """build parameters: - - handle defaults - - handle format tuple parameters + - handle defaults + - handle format tuple parameters """ value = default maybe_show_tree_param_default(self.showast, name, value) @@ -124,8 +124,6 @@ def make_function36(self, node, is_lambda, nested=1, code_node=None): kw_node = node[pos_args] if kw_node == "expr": kw_node = kw_node[0] - if kw_node == "dict": - kw_pairs = kw_node[-1].attr defparams = [] # FIXME: DRY with code below @@ -150,7 +148,8 @@ def make_function36(self, node, is_lambda, nested=1, code_node=None): code = code_node.attr assert iscode(code) - scanner_code = Code(code, self.scanner, self.currentclass) + debug_opts = self.debug_opts["asm"] if self.debug_opts else None + scanner_code = Code(code, self.scanner, self.currentclass, debug_opts) # add defaults values to parameter names argc = code.co_argcount @@ -347,7 +346,12 @@ def make_function36(self, node, is_lambda, nested=1, code_node=None): has_none = "None" in code.co_names rn = has_none and not find_none(ast) self.gen_source( - ast, code.co_name, scanner_code._customize, is_lambda=is_lambda, returnNone=rn + ast, + code.co_name, + scanner_code._customize, + is_lambda=is_lambda, + returnNone=rn, + debug_opts=self.debug_opts, ) # In obscure cases, a function may be a generator but the "yield" diff --git a/uncompyle6/semantics/pysource.py b/uncompyle6/semantics/pysource.py index ec5e5bfd..a0c7c7d5 100644 --- a/uncompyle6/semantics/pysource.py +++ b/uncompyle6/semantics/pysource.py @@ -182,6 +182,7 @@ from uncompyle6.semantics.consts import ( from uncompyle6.show import maybe_show_tree from uncompyle6.util import better_repr +DEFAULT_DEBUG_OPTS = {"asm": False, "tree": False, "grammar": False} def unicode(x): return x from io import StringIO @@ -648,7 +649,7 @@ class SourceWalker(GenericASTTraversal, object): attr = node.attr data = node.pattr datatype = type(data) - if isinstance(data, float) : + if isinstance(data, float): self.write(better_repr(data, self.version)) elif isinstance(data, complex): self.write(better_repr(data, self.version)) @@ -1179,10 +1180,11 @@ class SourceWalker(GenericASTTraversal, object): """ p = self.prec self.prec = 27 - code = node[code_index].attr - assert iscode(code), node[code_index] - code = Code(code, self.scanner, self.currentclass) + code_obj = node[code_index].attr + assert iscode(code_obj), node[code_index] + + code = Code(code_obj, self.scanner, self.currentclass, self.debug_opts["asm"]) ast = self.build_ast(code._tokens, code._customize, code) self.customize(code._customize) @@ -2426,13 +2428,22 @@ class SourceWalker(GenericASTTraversal, object): self.classes.pop(-1) - def gen_source(self, ast, name, customize, is_lambda=False, returnNone=False): + def gen_source( + self, + ast, + name, + customize, + is_lambda=False, + returnNone=False, + debug_opts=DEFAULT_DEBUG_OPTS, + ): """convert SyntaxTree to Python source code""" rn = self.return_none self.return_none = returnNone old_name = self.name self.name = name + self.debug_opts = debug_opts # if code would be empty, append 'pass' if len(ast) == 0: self.println(self.indent, "pass") @@ -2525,10 +2536,6 @@ class SourceWalker(GenericASTTraversal, object): return MAP.get(node, MAP_DIRECT) -# -DEFAULT_DEBUG_OPTS = {"asm": False, "tree": False, "grammar": False} - - def code_deparse( co, out=sys.stdout, @@ -2623,7 +2630,9 @@ def code_deparse( ) # What we've been waiting for: Generate source from Syntax Tree! - deparsed.gen_source(deparsed.ast, co.co_name, customize) + deparsed.gen_source( + deparsed.ast, name=co.co_name, customize=customize, debug_opts=debug_opts + ) for g in sorted(deparsed.mod_globs): deparsed.write("# global %s ## Warning: Unused global\n" % g)