Fix up linemap option

This commit is contained in:
rocky
2024-02-03 12:37:48 -05:00
parent 675206911a
commit d249c522a7
4 changed files with 73 additions and 62 deletions

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2018-2023 Rocky Bernstein <rocky@gnu.org> # Copyright (C) 2018-2024 Rocky Bernstein <rocky@gnu.org>
# #
# 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
@@ -35,12 +35,15 @@ from uncompyle6.version import __version__
def _get_outstream(outfile: str) -> Any: def _get_outstream(outfile: str) -> Any:
dir = os.path.dirname(outfile) """
Return an opened output file descriptor for ``outfile``.
"""
dir_name = os.path.dirname(outfile)
failed_file = outfile + "_failed" failed_file = outfile + "_failed"
if os.path.exists(failed_file): if os.path.exists(failed_file):
os.remove(failed_file) os.remove(failed_file)
try: try:
os.makedirs(dir) os.makedirs(dir_name)
except OSError: except OSError:
pass pass
return open(outfile, mode="w", encoding="utf-8") return open(outfile, mode="w", encoding="utf-8")
@@ -50,7 +53,7 @@ def decompile(
co, co,
bytecode_version: Tuple[int] = PYTHON_VERSION_TRIPLE, bytecode_version: Tuple[int] = PYTHON_VERSION_TRIPLE,
out=sys.stdout, out=sys.stdout,
showasm: Optional[str]=None, showasm: Optional[str] = None,
showast={}, showast={},
timestamp=None, timestamp=None,
showgrammar=False, showgrammar=False,
@@ -118,13 +121,12 @@ def decompile(
if isinstance(mapstream, str): if isinstance(mapstream, str):
mapstream = _get_outstream(mapstream) mapstream = _get_outstream(mapstream)
debug_opts = {"asm": showasm, "tree": showast, "grammar": showgrammar}
deparsed = deparse_code_with_map( deparsed = deparse_code_with_map(
bytecode_version, co=co,
co, out=out,
out, version=bytecode_version,
showasm,
showast,
showgrammar,
code_objects=code_objects, code_objects=code_objects,
is_pypy=is_pypy, is_pypy=is_pypy,
) )

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2015-2019, 2021-2023 by Rocky Bernstein # Copyright (c) 2015-2019, 2021-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
@@ -75,7 +75,6 @@ from xdis import iscode
from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE
import uncompyle6.parser as python_parser import uncompyle6.parser as python_parser
from uncompyle6 import parser
from uncompyle6.parsers.treenode import SyntaxTree from uncompyle6.parsers.treenode import SyntaxTree
from uncompyle6.scanner import Code, Token, get_scanner from uncompyle6.scanner import Code, Token, get_scanner
from uncompyle6.semantics import pysource from uncompyle6.semantics import pysource
@@ -89,7 +88,7 @@ from uncompyle6.semantics.consts import (
TABLE_DIRECT, TABLE_DIRECT,
escape, escape,
) )
from uncompyle6.semantics.pysource import ParserError, StringIO from uncompyle6.semantics.pysource import DEFAULT_DEBUG_OPTS, ParserError, StringIO
from uncompyle6.show import maybe_show_asm, maybe_show_tree from uncompyle6.show import maybe_show_asm, maybe_show_tree
NodeInfo = namedtuple("NodeInfo", "node start finish") NodeInfo = namedtuple("NodeInfo", "node start finish")
@@ -1118,7 +1117,13 @@ class FragmentsWalker(pysource.SourceWalker, object):
n_classdefdeco2 = n_classdef n_classdefdeco2 = n_classdef
def gen_source( def gen_source(
self, ast, name, customize, is_lambda=False, returnNone=False, debug_opts=None self,
ast,
name,
customize,
is_lambda=False,
returnNone=False,
debug_opts=DEFAULT_DEBUG_OPTS,
): ):
"""convert parse tree to Python source code""" """convert parse tree to Python source code"""
@@ -1204,7 +1209,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
self.p.insts = self.scanner.insts self.p.insts = self.scanner.insts
self.p.offset2inst_index = self.scanner.offset2inst_index self.p.offset2inst_index = self.scanner.offset2inst_index
self.p.opc = self.scanner.opc self.p.opc = self.scanner.opc
ast = parser.parse(self.p, tokens, customize, code) ast = python_parser.parse(self.p, tokens, customize, code)
self.p.insts = p_insts self.p.insts = p_insts
except (python_parser.ParserError, AssertionError) as e: except (python_parser.ParserError, AssertionError) as e:
raise ParserError(e, tokens, {}) raise ParserError(e, tokens, {})
@@ -2065,11 +2070,11 @@ def code_deparse(
# Build Syntax Tree from tokenized and massaged disassembly. # Build Syntax Tree from tokenized and massaged disassembly.
# deparsed = pysource.FragmentsWalker(out, scanner, showast=showast) # deparsed = pysource.FragmentsWalker(out, scanner, showast=showast)
show_ast = debug_opts.get("ast", None) show_tree = debug_opts.get("tree", False)
deparsed = walker( deparsed = walker(
version, version,
scanner, scanner,
showast=show_ast, showast=show_tree,
debug_parser=debug_parser, debug_parser=debug_parser,
compile_mode=compile_mode, compile_mode=compile_mode,
is_pypy=is_pypy, is_pypy=is_pypy,

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2018 by Rocky Bernstein # Copyright (c) 2018, 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
@@ -12,96 +12,97 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# 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 uncompyle6.semantics.fragments import FragmentsWalker, code_deparse as fragments_code_deparse
from uncompyle6.semantics.pysource import SourceWalker, code_deparse from uncompyle6.semantics.pysource import SourceWalker, code_deparse
import uncompyle6.semantics.fragments as fragments
# FIXME: does this handle nested code, and lambda properly # FIXME: does this handle nested code, and lambda properly
class LineMapWalker(SourceWalker): class LineMapWalker(SourceWalker):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(LineMapWalker, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.source_linemap = {} self.source_linemap = {}
self.current_line_number = 1 self.current_line_number = 1
def write(self, *data): def write(self, *data):
"""Augment write routine to keep track of current line""" """Augment write routine to keep track of current line"""
for l in data: for line in data:
## print("XXX write: '%s'" % l) ## print("XXX write: '%s'" % l)
for i in str(l): for i in str(line):
if i == '\n': if i == "\n":
self.current_line_number += 1 self.current_line_number += 1
pass pass
pass pass
pass pass
return super(LineMapWalker, self).write(*data) return super().write(*data)
# Note n_expr needs treatment too # Note n_expr needs treatment too
def default(self, node): def default(self, node):
"""Augment write default routine to record line number changes""" """Augment write default routine to record line number changes"""
if hasattr(node, 'linestart'): if hasattr(node, "linestart"):
if node.linestart: if node.linestart:
self.source_linemap[self.current_line_number] = node.linestart self.source_linemap[self.current_line_number] = node.linestart
return super(LineMapWalker, self).default(node) return super().default(node)
def n_LOAD_CONST(self, node): def n_LOAD_CONST(self, node):
if hasattr(node, 'linestart'): if hasattr(node, "linestart"):
if node.linestart: if node.linestart:
self.source_linemap[self.current_line_number] = node.linestart self.source_linemap[self.current_line_number] = node.linestart
return super(LineMapWalker, self).n_LOAD_CONST(node) return super().n_LOAD_CONST(node)
class LineMapFragmentWalker(fragments.FragmentsWalker, LineMapWalker): class LineMapFragmentWalker(LineMapWalker, FragmentsWalker):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(LineMapFragmentWalker, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.source_linemap = {}
self.current_line_number = 0
def deparse_code_with_map(*args, **kwargs): def deparse_code_with_map(*args, **kwargs):
""" """
Like deparse_code but saves line number correspondences. Like deparse_code but saves line number correspondences.
Deprecated. Use code_deparse_with_map Deprecated. Use code_deparse_with_map
""" """
kwargs['walker'] = LineMapWalker kwargs["walker"] = LineMapWalker
return code_deparse(*args, **kwargs) return code_deparse(*args, **kwargs)
def code_deparse_with_map(*args, **kwargs): def code_deparse_with_map(*args, **kwargs):
""" """
Like code_deparse but saves line number correspondences. Like code_deparse but saves line number correspondences.
""" """
kwargs['walker'] = LineMapWalker kwargs["walker"] = LineMapWalker
return code_deparse(*args, **kwargs) return code_deparse(*args, **kwargs)
def deparse_code_with_fragments_and_map(*args, **kwargs):
"""
Like deparse_code_with_map but saves fragments.
Deprecated. Use code_deparse_with_fragments_and_map
"""
kwargs['walker'] = LineMapFragmentWalker
return fragments.deparse_code(*args, **kwargs)
def code_deparse_with_fragments_and_map(*args, **kwargs): def code_deparse_with_fragments_and_map(*args, **kwargs):
""" """
Like code_deparse_with_map but saves fragments. Like code_deparse_with_map but saves fragments.
""" """
kwargs['walker'] = LineMapFragmentWalker kwargs["walker"] = LineMapFragmentWalker
return fragments.code_deparse(*args, **kwargs) return fragments_code_deparse(*args, **kwargs)
if __name__ == "__main__":
if __name__ == '__main__':
def deparse_test(co): def deparse_test(co):
"This is a docstring" "This is a docstring"
deparsed = code_deparse_with_map(co) deparsed = code_deparse_with_map(co)
a = 1; b = 2 a = 1
b = 2
print("\n") print("\n")
linemap = [(line_no, deparsed.source_linemap[line_no]) linemap = [
for line_no in (line_no, deparsed.source_linemap[line_no])
sorted(deparsed.source_linemap.keys())] for line_no in sorted(deparsed.source_linemap.keys())
]
print(linemap) print(linemap)
deparsed = code_deparse_with_fragments_and_map(co) deparsed = code_deparse_with_fragments_and_map(co)
print("\n") print("\n")
linemap2 = [(line_no, deparsed.source_linemap[line_no]) linemap2 = [
for line_no in (line_no, deparsed.source_linemap[line_no])
sorted(deparsed.source_linemap.keys())] for line_no in sorted(deparsed.source_linemap.keys())
]
print(linemap2) print(linemap2)
# assert linemap == linemap2 # assert linemap == linemap2
return return
deparse_test(deparse_test.__code__) deparse_test(deparse_test.__code__)

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2015-2023 by Rocky Bernstein # Copyright (c) 2015-2024 by Rocky Bernstein
# Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org> # Copyright (c) 2005 by Dan Pascu <dan@windowmaker.org>
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com> # Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
# Copyright (c) 1999 John Aycock # Copyright (c) 1999 John Aycock
@@ -196,6 +196,10 @@ 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.
The Parse tree may be turned an Abstract Syntax tree as an intermediate step.
"""
stacked_params = ("f", "indent", "is_lambda", "_globals") stacked_params = ("f", "indent", "is_lambda", "_globals")
def __init__( def __init__(
@@ -245,24 +249,24 @@ class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin):
is_pypy=is_pypy, is_pypy=is_pypy,
) )
# Initialize p_lambda on demand self.ast_errors = []
self.p_lambda = None self.currentclass = None
self.classes = []
self.treeTransform = TreeTransform(version=self.version, show_ast=showast)
self.debug_parser = dict(debug_parser) self.debug_parser = dict(debug_parser)
self.showast = showast # Initialize p_lambda on demand
self.line_number = 1
self.linemap = {}
self.p_lambda = None
self.params = params self.params = params
self.param_stack = [] self.param_stack = []
self.ERROR = None self.ERROR = None
self.prec = 100 self.prec = 100
self.return_none = False self.return_none = False
self.mod_globs = set() self.mod_globs = set()
self.currentclass = None self.showast = showast
self.classes = []
self.pending_newlines = 0 self.pending_newlines = 0
self.linestarts = linestarts self.linestarts = linestarts
self.line_number = 1 self.treeTransform = TreeTransform(version=self.version, show_ast=showast)
self.ast_errors = []
# FIXME: have p.insts update in a better way # FIXME: have p.insts update in a better way
# modularity is broken here # modularity is broken here
self.insts = scanner.insts self.insts = scanner.insts
@@ -1257,7 +1261,6 @@ def code_deparse(
assert iscode(co) assert iscode(co)
if version is None: if version is None:
version = PYTHON_VERSION_TRIPLE version = PYTHON_VERSION_TRIPLE