Merge branch 'master' into python-3.3-to-3.5

This commit is contained in:
rocky
2023-06-30 01:57:41 -04:00
16 changed files with 206 additions and 143 deletions

View File

@@ -7,6 +7,11 @@ about: Tell us about uncompyle6 bugs
<!-- __Note:__ If you are using this program to do something illegal - don't. <!-- __Note:__ If you are using this program to do something illegal - don't.
The issue may be flagged to make it easier for those looking for illegal activity. The issue may be flagged to make it easier for those looking for illegal activity.
If you are reporting a bug in decompilation, it will probably not be acted upon
unless it is narrowed to a small example. You may have to do some work remove
extraneous code from the source example. Most bugs can be expressed in 30 lines of
code.
Bugs are not for asking questions about a problem you Bugs are not for asking questions about a problem you
are trying to solve that involve the use of uncompyle6 along the way, are trying to solve that involve the use of uncompyle6 along the way,
although I may be more tolerant of this if you sponsor the project. although I may be more tolerant of this if you sponsor the project.
@@ -50,6 +55,7 @@ Prerequisites/Caveats
* Make sure the bytecode you have can be disassembled with a * Make sure the bytecode you have can be disassembled with a
disassembler and produces valid results. disassembler and produces valid results.
* Try to make the bytecode that exhibits a bug as small as possible.
* Don't put bytecode and corresponding source code on any service that * Don't put bytecode and corresponding source code on any service that
requires registration to download. requires registration to download.
* When you open a bug report there is no privacy. If you need privacy, then * When you open a bug report there is no privacy. If you need privacy, then
@@ -118,7 +124,11 @@ Please modify for your setup
## Priority ## Priority
<!-- If this is blocking some important activity let us know what activity it blocks. --> <!-- If this is important for a particular public good state that here.
If this is blocking some important activity let us know what activity it blocks.
Otherwise, we'll assume this has the lowest priority in addressing.
-->
## Additional Context ## Additional Context

View File

@@ -5,4 +5,4 @@ if [[ $0 == ${BASH_SOURCE[0]} ]] ; then
echo "This script should be *sourced* rather than run directly through bash" echo "This script should be *sourced* rather than run directly through bash"
exit 1 exit 1
fi fi
export PYVERSIONS='3.6.15 pypy3.6-7.3.1 3.7.16 pypy3.7-7.3.9 pypy3.8-7.3.10 pyston-2.3.5 3.8.16' export PYVERSIONS='3.6.15 pypy3.6-7.3.1 3.7.16 pypy3.7-7.3.9 pypy3.8-7.3.10 pyston-2.3.5 3.8.17'

View File

@@ -726,9 +726,12 @@ values = {
assert sorted(values.values())[1:] == list(range(2, 34)) assert sorted(values.values())[1:] == list(range(2, 34))
def assert_equal(x, y):
assert x == y
# Check that we can distinguish names from strings in literal collections, e.g. lists. # Check that we can distinguish names from strings in literal collections, e.g. lists.
# The list has to have more than 4 items to get accumulated in a collection # The list has to have more than 4 items to get accumulated in a collection
a = ["y", 'Exception', "x", Exception, "z"] a = ["y", 'Exception', "x", Exception, "z"]
assert a[1] == "Exception"
assert a[3] == Exception assert_equal(a[1], "Exception")
assert_equal(a[3], Exception)

View File

@@ -25,7 +25,6 @@ SKIP_TESTS=(
[test_nis.py]=1 # it fails on its own [test_nis.py]=1 # it fails on its own
[test_normalization.py]=1 # it fails on its own [test_normalization.py]=1 # it fails on its own
[test_ossaudiodev.py]=1 # it fails on its own [test_ossaudiodev.py]=1 # it fails on its own
[test_pep277.py]=1 # it fails on its own
[test_plistlib.py]=1 # it fails on its own [test_plistlib.py]=1 # it fails on its own
[test_rgbimg.py]=1 # it fails on its own [test_rgbimg.py]=1 # it fails on its own
[test_scriptpackages.py]=1 # it fails on its own [test_scriptpackages.py]=1 # it fails on its own

View File

@@ -1,10 +1,13 @@
#!/usr/bin/env python #!/usr/bin/env python
# Mode: -*- python -*- # Mode: -*- python -*-
# #
# Copyright (c) 2015-2016, 2018, 2020, 2022 by Rocky Bernstein <rb@dustyfeet.com> # Copyright (c) 2015-2016, 2018, 2020, 2022-2023 by Rocky Bernstein <rb@dustyfeet.com>
# #
from __future__ import print_function from __future__ import print_function
import sys, os, getopt
import getopt
import os
import sys
from uncompyle6.code_fns import disassemble_file from uncompyle6.code_fns import disassemble_file
from uncompyle6.version import __version__ from uncompyle6.version import __version__
@@ -40,13 +43,19 @@ Options:
-V | --version show version and stop -V | --version show version and stop
-h | --help show this message -h | --help show this message
""".format(program) """.format(
program
)
PATTERNS = ("*.pyc", "*.pyo")
PATTERNS = ('*.pyc', '*.pyo')
def main(): def main():
Usage_short = """usage: %s FILE... Usage_short = (
Type -h for for full help.""" % program """usage: %s FILE...
Type -h for for full help."""
% program
)
if len(sys.argv) == 1: if len(sys.argv) == 1:
print("No file(s) given", file=sys.stderr) print("No file(s) given", file=sys.stderr)
@@ -54,17 +63,18 @@ Type -h for for full help.""" % program
sys.exit(1) sys.exit(1)
try: try:
opts, files = getopt.getopt(sys.argv[1:], 'hVU', opts, files = getopt.getopt(
['help', 'version', 'uncompyle6']) sys.argv[1:], "hVU", ["help", "version", "uncompyle6"]
)
except getopt.GetoptError as e: except getopt.GetoptError as e:
print('%s: %s' % (os.path.basename(sys.argv[0]), e), file=sys.stderr) print("%s: %s" % (os.path.basename(sys.argv[0]), e), file=sys.stderr)
sys.exit(-1) sys.exit(-1)
for opt, val in opts: for opt, val in opts:
if opt in ('-h', '--help'): if opt in ("-h", "--help"):
print(__doc__) print(__doc__)
sys.exit(1) sys.exit(1)
elif opt in ('-V', '--version'): elif opt in ("-V", "--version"):
print("%s %s" % (program, __version__)) print("%s %s" % (program, __version__))
sys.exit(0) sys.exit(0)
else: else:
@@ -76,11 +86,11 @@ Type -h for for full help.""" % program
if os.path.exists(files[0]): if os.path.exists(files[0]):
disassemble_file(file, sys.stdout) disassemble_file(file, sys.stdout)
else: else:
print("Can't read %s - skipping" % files[0], print("Can't read %s - skipping" % files[0], file=sys.stderr)
file=sys.stderr)
pass pass
pass pass
return return
if __name__ == '__main__':
if __name__ == "__main__":
main() main()

View File

@@ -1,14 +1,19 @@
#!/usr/bin/env python #!/usr/bin/env python
# Mode: -*- python -*- # Mode: -*- python -*-
# #
# Copyright (c) 2015-2017, 2019-2020 by Rocky Bernstein # Copyright (c) 2015-2017, 2019-2020, 2023 by Rocky Bernstein
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com> # Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
# #
from __future__ import print_function from __future__ import print_function
import sys, os, getopt, time
import getopt
import os
import sys
import time
from xdis.version_info import version_tuple_to_str from xdis.version_info import version_tuple_to_str
program = 'uncompyle6' program = "uncompyle6"
__doc__ = """ __doc__ = """
Usage: Usage:
@@ -58,14 +63,17 @@ Extensions of generated files:
'.pyc_dis' '.pyo_dis' successfully decompiled (and verified if --verify) '.pyc_dis' '.pyo_dis' successfully decompiled (and verified if --verify)
+ '_unverified' successfully decompile but --verify failed + '_unverified' successfully decompile but --verify failed
+ '_failed' decompile failed (contact author for enhancement) + '_failed' decompile failed (contact author for enhancement)
""" % ((program,) * 5) """ % (
(program,) * 5
)
program = 'uncompyle6' program = "uncompyle6"
from uncompyle6 import verify from uncompyle6 import verify
from uncompyle6.main import main, status_msg from uncompyle6.main import main, status_msg
from uncompyle6.version import __version__ from uncompyle6.version import __version__
def usage(): def usage():
print(__doc__) print(__doc__)
sys.exit(1) sys.exit(1)
@@ -83,74 +91,77 @@ def main_bin():
do_verify = recurse_dirs = False do_verify = recurse_dirs = False
numproc = 0 numproc = 0
outfile = '-' outfile = "-"
out_base = None out_base = None
source_paths = [] source_paths = []
timestamp = False timestamp = False
timestampfmt = "# %Y.%m.%d %H:%M:%S %Z" timestampfmt = "# %Y.%m.%d %H:%M:%S %Z"
try: try:
opts, pyc_paths = getopt.getopt(sys.argv[1:], 'hac:gtTdrVo:p:', opts, pyc_paths = getopt.getopt(
'help asm compile= grammar linemaps recurse ' sys.argv[1:],
'timestamp tree= tree+ ' "hac:gtTdrVo:p:",
'fragments verify verify-run version ' "help asm compile= grammar linemaps recurse "
'syntax-verify ' "timestamp tree= tree+ "
'showgrammar encoding='.split(' ')) "fragments verify verify-run version "
"syntax-verify "
"showgrammar encoding=".split(" "),
)
except getopt.GetoptError as e: except getopt.GetoptError as e:
print('%s: %s' % (os.path.basename(sys.argv[0]), e), file=sys.stderr) print("%s: %s" % (os.path.basename(sys.argv[0]), e), file=sys.stderr)
sys.exit(-1) sys.exit(-1)
options = {} options = {}
for opt, val in opts: for opt, val in opts:
if opt in ('-h', '--help'): if opt in ("-h", "--help"):
print(__doc__) print(__doc__)
sys.exit(0) sys.exit(0)
elif opt in ('-V', '--version'): elif opt in ("-V", "--version"):
print("%s %s" % (program, __version__)) print("%s %s" % (program, __version__))
sys.exit(0) sys.exit(0)
elif opt == '--verify': elif opt == "--verify":
options['do_verify'] = 'strong' options["do_verify"] = "strong"
elif opt == '--syntax-verify': elif opt == "--syntax-verify":
options['do_verify'] = 'weak' options["do_verify"] = "weak"
elif opt == '--fragments': elif opt == "--fragments":
options['do_fragments'] = True options["do_fragments"] = True
elif opt == '--verify-run': elif opt == "--verify-run":
options['do_verify'] = 'verify-run' options["do_verify"] = "verify-run"
elif opt == '--linemaps': elif opt == "--linemaps":
options['do_linemaps'] = True options["do_linemaps"] = True
elif opt in ('--asm', '-a'): elif opt in ("--asm", "-a"):
options['showasm'] = 'after' options["showasm"] = "after"
options['do_verify'] = None options["do_verify"] = None
elif opt in ('--tree', '-t'): elif opt in ("--tree", "-t"):
if 'showast' not in options: if "showast" not in options:
options['showast'] = {} options["showast"] = {}
if val == 'before': if val == "before":
options['showast'][val] = True options["showast"][val] = True
elif val == 'after': elif val == "after":
options['showast'][val] = True options["showast"][val] = True
else: else:
options['showast']['before'] = True options["showast"]["before"] = True
options['do_verify'] = None options["do_verify"] = None
elif opt in ('--tree+', '-T'): elif opt in ("--tree+", "-T"):
if 'showast' not in options: if "showast" not in options:
options['showast'] = {} options["showast"] = {}
options['showast']['after'] = True options["showast"]["after"] = True
options['showast']['before'] = True options["showast"]["before"] = True
options['do_verify'] = None options["do_verify"] = None
elif opt in ('--grammar', '-g'): elif opt in ("--grammar", "-g"):
options['showgrammar'] = True options["showgrammar"] = True
elif opt == '-o': elif opt == "-o":
outfile = val outfile = val
elif opt in ('--timestamp', '-d'): elif opt in ("--timestamp", "-d"):
timestamp = True timestamp = True
elif opt in ('--compile', '-c'): elif opt in ("--compile", "-c"):
source_paths.append(val) source_paths.append(val)
elif opt == '-p': elif opt == "-p":
numproc = int(val) numproc = int(val)
elif opt in ('--recurse', '-r'): elif opt in ("--recurse", "-r"):
recurse_dirs = True recurse_dirs = True
elif opt == '--encoding': elif opt == "--encoding":
options['source_encoding'] = val options["source_encoding"] = val
else: else:
print(opt, file=sys.stderr) print(opt, file=sys.stderr)
usage() usage()
@@ -162,7 +173,7 @@ def main_bin():
if os.path.isdir(f): if os.path.isdir(f):
for root, _, dir_files in os.walk(f): for root, _, dir_files in os.walk(f):
for df in dir_files: for df in dir_files:
if df.endswith('.pyc') or df.endswith('.pyo'): if df.endswith(".pyc") or df.endswith(".pyo"):
expanded_files.append(os.path.join(root, df)) expanded_files.append(os.path.join(root, df))
pyc_paths = expanded_files pyc_paths = expanded_files
@@ -173,36 +184,39 @@ def main_bin():
if src_base[-1:] != os.sep: if src_base[-1:] != os.sep:
src_base = os.path.dirname(src_base) src_base = os.path.dirname(src_base)
if src_base: if src_base:
sb_len = len( os.path.join(src_base, '') ) sb_len = len(os.path.join(src_base, ""))
pyc_paths = [f[sb_len:] for f in pyc_paths] pyc_paths = [f[sb_len:] for f in pyc_paths]
if not pyc_paths and not source_paths: if not pyc_paths and not source_paths:
print("No input files given to decompile", file=sys.stderr) print("No input files given to decompile", file=sys.stderr)
usage() usage()
if outfile == '-': if outfile == "-":
outfile = None # use stdout outfile = None # use stdout
elif outfile and os.path.isdir(outfile): elif outfile and os.path.isdir(outfile):
out_base = outfile; outfile = None out_base = outfile
outfile = None
elif outfile and len(pyc_paths) > 1: elif outfile and len(pyc_paths) > 1:
out_base = outfile; outfile = None out_base = outfile
outfile = None
if timestamp: if timestamp:
print(time.strftime(timestampfmt)) print(time.strftime(timestampfmt))
if numproc <= 1: if numproc <= 1:
try: try:
result = main(src_base, out_base, pyc_paths, source_paths, outfile, result = main(
**options) src_base, out_base, pyc_paths, source_paths, outfile, **options
result = [options.get('do_verify', None)] + list(result) )
result = [options.get("do_verify", None)] + list(result)
if len(pyc_paths) > 1: if len(pyc_paths) > 1:
mess = status_msg(*result) mess = status_msg(*result)
print('# ' + mess) print("# " + mess)
pass pass
except ImportError as e: except ImportError as e:
print(str(e)) print(str(e))
sys.exit(2) sys.exit(2)
except (KeyboardInterrupt): except KeyboardInterrupt:
pass pass
except verify.VerifyCmpError: except verify.VerifyCmpError:
raise raise
@@ -214,7 +228,7 @@ def main_bin():
except ImportError: except ImportError:
from queue import Empty from queue import Empty
fqueue = Queue(len(pyc_paths)+numproc) fqueue = Queue(len(pyc_paths) + numproc)
for f in pyc_paths: for f in pyc_paths:
fqueue.put(f) fqueue.put(f)
for i in range(numproc): for i in range(numproc):
@@ -224,13 +238,17 @@ def main_bin():
def process_func(): def process_func():
try: try:
(tot_files, okay_files, failed_files, verify_failed_files) = (0, 0, 0, 0) (tot_files, okay_files, failed_files, verify_failed_files) = (
0,
0,
0,
0,
)
while 1: while 1:
f = fqueue.get() f = fqueue.get()
if f is None: if f is None:
break break
(t, o, f, v) = \ (t, o, f, v) = main(src_base, out_base, [f], [], outfile, **options)
main(src_base, out_base, [f], [], outfile, **options)
tot_files += t tot_files += t
okay_files += o okay_files += o
failed_files += f failed_files += f
@@ -247,7 +265,12 @@ def main_bin():
for p in procs: for p in procs:
p.join() p.join()
try: try:
(tot_files, okay_files, failed_files, verify_failed_files) = (0, 0, 0, 0) (tot_files, okay_files, failed_files, verify_failed_files) = (
0,
0,
0,
0,
)
while True: while True:
(t, o, f, v) = rqueue.get(False) (t, o, f, v) = rqueue.get(False)
tot_files += t tot_files += t
@@ -256,8 +279,10 @@ def main_bin():
verify_failed_files += v verify_failed_files += v
except Empty: except Empty:
pass pass
print('# decompiled %i files: %i okay, %i failed, %i verify failed' % print(
(tot_files, okay_files, failed_files, verify_failed_files)) "# decompiled %i files: %i okay, %i failed, %i verify failed"
% (tot_files, okay_files, failed_files, verify_failed_files)
)
except (KeyboardInterrupt, OSError): except (KeyboardInterrupt, OSError):
pass pass
@@ -266,5 +291,6 @@ def main_bin():
return return
if __name__ == '__main__':
if __name__ == "__main__":
main_bin() main_bin()

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2018-2022 Rocky Bernstein <rocky@gnu.org> # Copyright (C) 2018-2023 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
@@ -13,23 +13,25 @@
# 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/>.
import datetime, py_compile, os, sys import datetime
import os
import py_compile
import sys
from xdis import iscode from xdis import iscode
from xdis.load import load_module
from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE, version_tuple_to_str from xdis.version_info import IS_PYPY, PYTHON_VERSION_TRIPLE, version_tuple_to_str
from uncompyle6.code_fns import check_object_path from uncompyle6.code_fns import check_object_path
from uncompyle6.semantics import pysource
from uncompyle6.semantics.pysource import PARSER_DEFAULT_DEBUG
from uncompyle6.parser import ParserError from uncompyle6.parser import ParserError
from uncompyle6.semantics import pysource
from uncompyle6.semantics.fragments import code_deparse as code_deparse_fragments
from uncompyle6.semantics.linemap import deparse_code_with_map
from uncompyle6.semantics.pysource import PARSER_DEFAULT_DEBUG, code_deparse
from uncompyle6.version import __version__ from uncompyle6.version import __version__
# from uncompyle6.linenumbers import line_number_mapping # from uncompyle6.linenumbers import line_number_mapping
from uncompyle6.semantics.pysource import code_deparse
from uncompyle6.semantics.fragments import code_deparse as code_deparse_fragments
from uncompyle6.semantics.linemap import deparse_code_with_map
from xdis.load import load_module
def _get_outstream(outfile): def _get_outstream(outfile):
dir = os.path.dirname(outfile) dir = os.path.dirname(outfile)
@@ -54,7 +56,7 @@ def decompile(
source_encoding=None, source_encoding=None,
code_objects={}, code_objects={},
source_size=None, source_size=None,
is_pypy=None, is_pypy=False,
magic_int=None, magic_int=None,
mapstream=None, mapstream=None,
do_fragments=False, do_fragments=False,
@@ -144,8 +146,8 @@ def decompile(
out, out,
bytecode_version, bytecode_version,
debug_opts=debug_opts, debug_opts=debug_opts,
is_pypy=is_pypy,
compile_mode=compile_mode, compile_mode=compile_mode,
is_pypy=is_pypy,
) )
pass pass
return deparsed return deparsed
@@ -187,7 +189,7 @@ def decompile_file(
filename = check_object_path(filename) filename = check_object_path(filename)
code_objects = {} code_objects = {}
(version, timestamp, magic_int, co, is_pypy, source_size, sip_hash) = load_module( version, timestamp, magic_int, co, is_pypy, source_size, _ = load_module(
filename, code_objects filename, code_objects
) )
@@ -393,14 +395,14 @@ def main(
try: try:
# FIXME: Something is weird with Pypy here # FIXME: Something is weird with Pypy here
sys.stdout.flush() sys.stdout.flush()
except: except Exception:
pass pass
if current_outfile: if current_outfile:
sys.stdout.write("\n") sys.stdout.write("\n")
try: try:
# FIXME: Something is weird with Pypy here # FIXME: Something is weird with Pypy here
sys.stdout.flush() sys.stdout.flush()
except: except Exception:
pass pass
pass pass
return (tot_files, okay_files, failed_files, verify_failed_files) return (tot_files, okay_files, failed_files, verify_failed_files)
@@ -416,7 +418,6 @@ if sys.platform.startswith("linux") and os.uname()[2][:2] in ["2.", "3.", "4."]:
mi.close() mi.close()
return int(mu) / 1000000 return int(mu) / 1000000
else: else:
def __memUsage(): def __memUsage():

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2015-2022 Rocky Bernstein # Copyright (c) 2015-2023 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
@@ -44,8 +44,8 @@ def nop_func(self, args):
class PythonParser(GenericASTBuilder): class PythonParser(GenericASTBuilder):
def __init__(self, SyntaxTree, start, debug): def __init__(self, syntax_tree_class, start, debug):
super(PythonParser, self).__init__(SyntaxTree, start, debug) super(PythonParser, self).__init__(syntax_tree_class, start, debug)
# FIXME: customize per python parser version # FIXME: customize per python parser version
# These are the non-terminals we should collect into a list. # These are the non-terminals we should collect into a list.
@@ -103,6 +103,7 @@ class PythonParser(GenericASTBuilder):
) )
# Instructions filled in from scanner # Instructions filled in from scanner
self.insts = [] self.insts = []
self.version = tuple()
def ast_first_offset(self, ast): def ast_first_offset(self, ast):
if hasattr(ast, "offset"): if hasattr(ast, "offset"):
@@ -151,9 +152,9 @@ class PythonParser(GenericASTBuilder):
Remove recursive references to allow garbage Remove recursive references to allow garbage
collector to collect this object. collector to collect this object.
""" """
for dict in (self.rule2func, self.rules, self.rule2name): for rule_dict in (self.rule2func, self.rules, self.rule2name):
for i in list(dict.keys()): for i in list(rule_dict.keys()):
dict[i] = None rule_dict[i] = None
for i in dir(self): for i in dir(self):
setattr(self, i, None) setattr(self, i, None)
@@ -164,11 +165,11 @@ class PythonParser(GenericASTBuilder):
def fix(c): def fix(c):
s = str(c) s = str(c)
last_token_pos = s.find("_") token_pos = s.find("_")
if last_token_pos == -1: if token_pos == -1:
return s return s
else: else:
return s[:last_token_pos] return s[:token_pos]
prefix = "" prefix = ""
if parent and tokens: if parent and tokens:
@@ -267,13 +268,13 @@ class PythonParser(GenericASTBuilder):
print(children) print(children)
return GenericASTBuilder.ambiguity(self, children) return GenericASTBuilder.ambiguity(self, children)
def resolve(self, list): def resolve(self, rule: list):
if len(list) == 2 and "function_def" in list and "assign" in list: if len(rule) == 2 and "function_def" in rule and "assign" in rule:
return "function_def" return "function_def"
if "grammar" in list and "expr" in list: if "grammar" in rule and "expr" in rule:
return "expr" return "expr"
# print >> sys.stderr, 'resolve', str(list) # print >> sys.stderr, 'resolve', str(rule)
return GenericASTBuilder.resolve(self, list) return GenericASTBuilder.resolve(self, rule)
############################################### ###############################################
# Common Python 2 and Python 3 grammar rules # # Common Python 2 and Python 3 grammar rules #
@@ -667,7 +668,7 @@ def get_python_parser(
if compile_mode == "exec": if compile_mode == "exec":
p = parse10.Python10Parser(debug_parser) p = parse10.Python10Parser(debug_parser)
else: else:
p = parse10.Python01ParserSingle(debug_parser) p = parse10.Python10ParserSingle(debug_parser)
elif version == (1, 1): elif version == (1, 1):
import uncompyle6.parsers.parse11 as parse11 import uncompyle6.parsers.parse11 as parse11
@@ -873,6 +874,7 @@ def python_parser(
:param showasm: Flag which determines whether the disassembled and :param showasm: Flag which determines whether the disassembled and
ingested code is written to sys.stdout or not. ingested code is written to sys.stdout or not.
:param parser_debug: dict containing debug flags for the spark parser. :param parser_debug: dict containing debug flags for the spark parser.
:param is_pypy: True if we are running PyPY
:return: Abstract syntax tree representation of the code object. :return: Abstract syntax tree representation of the code object.
""" """

View File

@@ -1,11 +1,12 @@
# Copyright (c) 2018 Rocky Bernstein # Copyright (c) 2018, 2023 Rocky Bernstein
from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG from spark_parser import DEFAULT_DEBUG as PARSER_DEFAULT_DEBUG
from uncompyle6.parser import PythonParserSingle from uncompyle6.parser import PythonParserSingle
from uncompyle6.parsers.parse14 import Python14Parser from uncompyle6.parsers.parse14 import Python14Parser
class Python13Parser(Python14Parser):
class Python13Parser(Python14Parser):
def p_misc13(self, args): def p_misc13(self, args):
""" """
# Nothing here yet, but will need to add LOAD_GLOBALS # Nothing here yet, but will need to add LOAD_GLOBALS
@@ -24,7 +25,6 @@ class Python13Parser(Python14Parser):
# """) # """)
# self.check_reduce['doc_junk'] = 'tokens' # self.check_reduce['doc_junk'] = 'tokens'
# def reduce_is_invalid(self, rule, ast, tokens, first, last): # def reduce_is_invalid(self, rule, ast, tokens, first, last):
# invalid = super(Python14Parser, # invalid = super(Python14Parser,
# self).reduce_is_invalid(rule, ast, # self).reduce_is_invalid(rule, ast,
@@ -35,11 +35,11 @@ class Python13Parser(Python14Parser):
# return not isinstance(tokens[first].pattr, str) # return not isinstance(tokens[first].pattr, str)
class Python13ParserSingle(Python13Parser, PythonParserSingle): class Python13ParserSingle(Python13Parser, PythonParserSingle):
pass pass
if __name__ == '__main__':
if __name__ == "__main__":
# Check grammar # Check grammar
p = Python13Parser() p = Python13Parser()
p.check_grammar() p.check_grammar()

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2016-2018, 2020, 2022 Rocky Bernstein # Copyright (c) 2016-2018, 2020, 2022-2023 Rocky Bernstein
""" """
spark grammar differences over Python2.5 for Python 2.4. spark grammar differences over Python2.5 for Python 2.4.
""" """
@@ -115,8 +115,8 @@ class Python24Parser(Python25Parser):
lhs = rule[0] lhs = rule[0]
if lhs == "nop_stmt": if lhs == "nop_stmt":
l = len(tokens) token_len = len(tokens)
if 0 <= l < len(tokens): if 0 <= token_len < len(tokens):
return not int(tokens[first].pattr) == tokens[last].offset return not int(tokens[first].pattr) == tokens[last].offset
elif lhs == "try_except": elif lhs == "try_except":
if last == len(tokens): if last == len(tokens):

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2015-2019, 2021-2022 by Rocky Bernstein # Copyright (c) 2015-2019, 2021-2023 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
@@ -1169,11 +1169,14 @@ 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
ast = python_parser.parse(self.p, tokens, customize, code) ast = python_parser.parse(self.p, tokens, customize, code)
self.customize(customize)
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)
transform_tree = self.treeTransform.transform(ast, code)
maybe_show_tree(self, ast) maybe_show_tree(self, ast)
return ast return transform_tree
# The bytecode for the end of the main routine has a # The bytecode for the end of the main routine has a
# "return None". However you can't issue a "return" statement in # "return None". However you can't issue a "return" statement in
@@ -1199,23 +1202,28 @@ class FragmentsWalker(pysource.SourceWalker, object):
if len(tokens) == 0: if len(tokens) == 0:
return PASS return PASS
# Build parse tree from tokenized and massaged disassembly. # Build a parse tree from tokenized and massaged disassembly.
try: try:
# 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
p_insts = self.p.insts p_insts = self.p.insts
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
ast = parser.parse(self.p, tokens, customize, code) ast = parser.parse(self.p, tokens, customize, code)
self.p.insts = p_insts self.p.insts = p_insts
except (parser.ParserError, AssertionError) as e: except (python_parser.ParserError, AssertionError) as e:
raise ParserError(e, tokens, {}) raise ParserError(e, tokens, {})
maybe_show_tree(self, ast)
checker(ast, False, self.ast_errors) checker(ast, False, self.ast_errors)
return ast self.customize(customize)
transform_tree = self.treeTransform.transform(ast, code)
maybe_show_tree(self, ast)
del ast # Save memory
return transform_tree
# FIXME: we could provide another customized routine # FIXME: we could provide another customized routine
# that fixes up parents along a particular path to a node that # that fixes up parents along a particular path to a node that

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2022 by Rocky Bernstein # Copyright (c) 2022-2023 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
@@ -159,7 +159,7 @@ class NonterminalActions:
# * class_name - the name of the class # * class_name - the name of the class
# * subclass_info - the parameters to the class e.g. # * subclass_info - the parameters to the class e.g.
# class Foo(bar, baz) # class Foo(bar, baz)
# ----------- # ------------
# * subclass_code - the code for the subclass body # * subclass_code - the code for the subclass body
if node == "classdefdeco2": if node == "classdefdeco2":
@@ -181,7 +181,7 @@ class NonterminalActions:
subclass_code = build_class[-3][1].attr subclass_code = build_class[-3][1].attr
class_name = node[0][0].pattr class_name = node[0][0].pattr
else: else:
raise "Internal Error n_classdef: cannot find class name" raise RuntimeError("Internal Error n_classdef: cannot find class name")
if node == "classdefdeco2": if node == "classdefdeco2":
self.write("\n") self.write("\n")
@@ -228,7 +228,8 @@ class NonterminalActions:
else: else:
# from trepan.api import debug; debug() # from trepan.api import debug; debug()
raise TypeError( raise TypeError(
"Internal Error: n_const_list expects dict, list set, or set; got %s" % lastnodetype ("Internal Error: n_const_list expects dict, list set, or set; got %s"
% lastnodetype)
) )
self.indent_more(INDENT_PER_LEVEL) self.indent_more(INDENT_PER_LEVEL)
@@ -267,7 +268,10 @@ class NonterminalActions:
if elem == "add_value": if elem == "add_value":
elem = elem[0] elem = elem[0]
if elem == "ADD_VALUE": if elem == "ADD_VALUE":
value = "%r" % elem.pattr if self.version < (3, 0, 0):
value = "%r" % elem.pattr
else:
value = "%s" % elem.pattr
else: else:
assert elem.kind == "ADD_VALUE_VAR" assert elem.kind == "ADD_VALUE_VAR"
value = "%s" % elem.pattr value = "%s" % elem.pattr

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2018, 2020 Rocky Bernstein <rocky@gnu.org> # Copyright (C) 2018, 2020, 2023 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

View File

@@ -1,5 +1,5 @@
# #
# (C) Copyright 2015-2018, 2020-2021 by Rocky Bernstein # (C) Copyright 2015-2018, 2020-2021, 2023 by Rocky Bernstein
# (C) Copyright 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com> # (C) Copyright 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@@ -411,7 +411,7 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2, verify, name=""):
check_jumps[dest1].append((i1, i2, dest2)) check_jumps[dest1].append((i1, i2, dest2))
else: else:
check_jumps[dest1] = [(i1, i2, dest2)] check_jumps[dest1] = [(i1, i2, dest2)]
except: except Exception:
pass pass
i1 += 1 i1 += 1