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
# 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:
dir = os.path.dirname(outfile)
"""
Return an opened output file descriptor for ``outfile``.
"""
dir_name = os.path.dirname(outfile)
failed_file = outfile + "_failed"
if os.path.exists(failed_file):
os.remove(failed_file)
try:
os.makedirs(dir)
os.makedirs(dir_name)
except OSError:
pass
return open(outfile, mode="w", encoding="utf-8")
@@ -50,7 +53,7 @@ def decompile(
co,
bytecode_version: Tuple[int] = PYTHON_VERSION_TRIPLE,
out=sys.stdout,
showasm: Optional[str]=None,
showasm: Optional[str] = None,
showast={},
timestamp=None,
showgrammar=False,
@@ -118,13 +121,12 @@ def decompile(
if isinstance(mapstream, str):
mapstream = _get_outstream(mapstream)
debug_opts = {"asm": showasm, "tree": showast, "grammar": showgrammar}
deparsed = deparse_code_with_map(
bytecode_version,
co,
out,
showasm,
showast,
showgrammar,
co=co,
out=out,
version=bytecode_version,
code_objects=code_objects,
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
# 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
import uncompyle6.parser as python_parser
from uncompyle6 import parser
from uncompyle6.parsers.treenode import SyntaxTree
from uncompyle6.scanner import Code, Token, get_scanner
from uncompyle6.semantics import pysource
@@ -89,7 +88,7 @@ from uncompyle6.semantics.consts import (
TABLE_DIRECT,
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
NodeInfo = namedtuple("NodeInfo", "node start finish")
@@ -1118,7 +1117,13 @@ class FragmentsWalker(pysource.SourceWalker, object):
n_classdefdeco2 = n_classdef
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"""
@@ -1204,7 +1209,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
self.p.insts = self.scanner.insts
self.p.offset2inst_index = self.scanner.offset2inst_index
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
except (python_parser.ParserError, AssertionError) as e:
raise ParserError(e, tokens, {})
@@ -2065,11 +2070,11 @@ def code_deparse(
# Build Syntax Tree from tokenized and massaged disassembly.
# deparsed = pysource.FragmentsWalker(out, scanner, showast=showast)
show_ast = debug_opts.get("ast", None)
show_tree = debug_opts.get("tree", False)
deparsed = walker(
version,
scanner,
showast=show_ast,
showast=show_tree,
debug_parser=debug_parser,
compile_mode=compile_mode,
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
# 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
# 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
import uncompyle6.semantics.fragments as fragments
# FIXME: does this handle nested code, and lambda properly
class LineMapWalker(SourceWalker):
def __init__(self, *args, **kwargs):
super(LineMapWalker, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.source_linemap = {}
self.current_line_number = 1
def write(self, *data):
"""Augment write routine to keep track of current line"""
for l in data:
for line in data:
## print("XXX write: '%s'" % l)
for i in str(l):
if i == '\n':
for i in str(line):
if i == "\n":
self.current_line_number += 1
pass
pass
pass
return super(LineMapWalker, self).write(*data)
return super().write(*data)
# Note n_expr needs treatment too
def default(self, node):
"""Augment write default routine to record line number changes"""
if hasattr(node, 'linestart'):
if hasattr(node, "linestart"):
if 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):
if hasattr(node, 'linestart'):
if hasattr(node, "linestart"):
if 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):
super(LineMapFragmentWalker, self).__init__(*args, **kwargs)
self.source_linemap = {}
self.current_line_number = 0
super().__init__(*args, **kwargs)
def deparse_code_with_map(*args, **kwargs):
"""
Like deparse_code but saves line number correspondences.
Deprecated. Use code_deparse_with_map
"""
kwargs['walker'] = LineMapWalker
kwargs["walker"] = LineMapWalker
return code_deparse(*args, **kwargs)
def code_deparse_with_map(*args, **kwargs):
"""
Like code_deparse but saves line number correspondences.
"""
kwargs['walker'] = LineMapWalker
kwargs["walker"] = LineMapWalker
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):
"""
Like code_deparse_with_map but saves fragments.
"""
kwargs['walker'] = LineMapFragmentWalker
return fragments.code_deparse(*args, **kwargs)
kwargs["walker"] = LineMapFragmentWalker
return fragments_code_deparse(*args, **kwargs)
if __name__ == "__main__":
if __name__ == '__main__':
def deparse_test(co):
"This is a docstring"
deparsed = code_deparse_with_map(co)
a = 1; b = 2
a = 1
b = 2
print("\n")
linemap = [(line_no, deparsed.source_linemap[line_no])
for line_no in
sorted(deparsed.source_linemap.keys())]
linemap = [
(line_no, deparsed.source_linemap[line_no])
for line_no in sorted(deparsed.source_linemap.keys())
]
print(linemap)
deparsed = code_deparse_with_fragments_and_map(co)
print("\n")
linemap2 = [(line_no, deparsed.source_linemap[line_no])
for line_no in
sorted(deparsed.source_linemap.keys())]
linemap2 = [
(line_no, deparsed.source_linemap[line_no])
for line_no in sorted(deparsed.source_linemap.keys())
]
print(linemap2)
# assert linemap == linemap2
return
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) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
# Copyright (c) 1999 John Aycock
@@ -196,6 +196,10 @@ class SourceWalkerError(Exception):
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")
def __init__(
@@ -245,24 +249,24 @@ class SourceWalker(GenericASTTraversal, NonterminalActions, ComprehensionMixin):
is_pypy=is_pypy,
)
# Initialize p_lambda on demand
self.p_lambda = None
self.treeTransform = TreeTransform(version=self.version, show_ast=showast)
self.ast_errors = []
self.currentclass = None
self.classes = []
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.param_stack = []
self.ERROR = None
self.prec = 100
self.return_none = False
self.mod_globs = set()
self.currentclass = None
self.classes = []
self.showast = showast
self.pending_newlines = 0
self.linestarts = linestarts
self.line_number = 1
self.ast_errors = []
self.treeTransform = TreeTransform(version=self.version, show_ast=showast)
# FIXME: have p.insts update in a better way
# modularity is broken here
self.insts = scanner.insts
@@ -1257,7 +1261,6 @@ def code_deparse(
assert iscode(co)
if version is None:
version = PYTHON_VERSION_TRIPLE