You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-03 08:49:51 +08:00
Merge branch 'master' into python-2.4
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
Copyright (c) 1999 John Aycock
|
||||
Copyright (c) 2015, 2018 by Rocky Bernstein
|
||||
Copyright (c) 2000 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||
Copyright (c) 2015 by Rocky Bernstein
|
||||
Copyright (c) 1999 John Aycock
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@@ -55,6 +55,7 @@ from uncompyle6.main import decompile_file
|
||||
uncompyle_file = decompile_file
|
||||
|
||||
# Conventience functions so you can say:
|
||||
# from uncompyle6 import deparse_code
|
||||
# from uncompyle6 import (deparse_code, deparse_code2str)
|
||||
|
||||
deparse_code = uncompyle6.semantics.pysource.deparse_code
|
||||
deparse_code2str = uncompyle6.semantics.pysource.deparse_code2str
|
||||
|
@@ -136,7 +136,7 @@ class Python3Parser(PythonParser):
|
||||
iflaststmtl ::= testexpr c_stmts_opt JUMP_BACK COME_FROM_LOOP
|
||||
iflaststmtl ::= testexpr c_stmts_opt JUMP_BACK POP_BLOCK
|
||||
|
||||
# These are used to keep AST indices the same
|
||||
# These are used to keep parse tree indices the same
|
||||
jump_forward_else ::= JUMP_FORWARD ELSE
|
||||
jump_absolute_else ::= JUMP_ABSOLUTE ELSE
|
||||
|
||||
|
@@ -100,7 +100,7 @@ def align_deparse_code(version, co, out=sys.stderr, showasm=False, showast=False
|
||||
debug_parser['reduce'] = showgrammar
|
||||
debug_parser['errorstack'] = True
|
||||
|
||||
# Build AST from disassembly.
|
||||
# Build a parse tree from tokenized and massaged disassembly.
|
||||
deparsed = AligningWalker(version, scanner, out, showast=showast,
|
||||
debug_parser=debug_parser, compile_mode=compile_mode,
|
||||
is_pypy = is_pypy)
|
||||
@@ -125,7 +125,7 @@ def align_deparse_code(version, co, out=sys.stderr, showasm=False, showast=False
|
||||
except:
|
||||
pass
|
||||
|
||||
# What we've been waiting for: Generate source from AST!
|
||||
# What we've been waiting for: Generate Python source from the parse tree!
|
||||
deparsed.gen_source(deparsed.ast, co.co_name, customize)
|
||||
|
||||
for g in deparsed.mod_globs:
|
||||
|
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Python AST grammar checker.
|
||||
Python parse tree checker.
|
||||
|
||||
Our rules sometimes give erroneous results. Until we have perfect rules,
|
||||
This checker will catch mistakes in decompilation we've made.
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Copyright (c) 2017 by Rocky Bernstein
|
||||
# Copyright (c) 2017, 2018 by Rocky Bernstein
|
||||
"""Constants and initial table values used in pysource.py and fragments.py"""
|
||||
|
||||
import re, sys
|
||||
@@ -16,8 +16,8 @@ else:
|
||||
|
||||
LINE_LENGTH = 80
|
||||
|
||||
# Some ASTs used for comparing code fragments (like 'return None' at
|
||||
# the end of functions).
|
||||
# Some parse trees created below are used for comparing code
|
||||
# fragments (like 'return None' at the end of functions).
|
||||
|
||||
RETURN_LOCALS = AST('return',
|
||||
[ AST('ret_expr', [AST('expr', [ Token('LOAD_LOCALS') ])]),
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# Copyright (c) 2015-2018 by Rocky Bernstein
|
||||
|
||||
"""
|
||||
Creates Python source code from an uncompyle6 abstract syntax tree,
|
||||
Creates Python source code from an uncompyle6 parse tree,
|
||||
and indexes fragments which can be accessed by instruction offset
|
||||
address.
|
||||
|
||||
@@ -61,7 +61,7 @@ from uncompyle6.semantics.check_ast import checker
|
||||
|
||||
from uncompyle6.show import (
|
||||
maybe_show_asm,
|
||||
maybe_show_ast,
|
||||
maybe_show_tree,
|
||||
)
|
||||
|
||||
from uncompyle6.parsers.astnode import AST
|
||||
@@ -1062,7 +1062,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
n_classdefdeco2 = n_classdef
|
||||
|
||||
def gen_source(self, ast, name, customize, is_lambda=False, returnNone=False):
|
||||
"""convert AST to Python source code"""
|
||||
"""convert parse tree to Python source code"""
|
||||
|
||||
rn = self.return_none
|
||||
self.return_none = returnNone
|
||||
@@ -1100,7 +1100,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
self.p.insts = p_insts
|
||||
except (parser.ParserError(e), AssertionError(e)):
|
||||
raise ParserError(e, tokens)
|
||||
maybe_show_ast(self.showast, ast)
|
||||
maybe_show_tree(self.showast, ast)
|
||||
return ast
|
||||
|
||||
# The bytecode for the end of the main routine has a
|
||||
@@ -1128,7 +1128,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
if len(tokens) == 0:
|
||||
return PASS
|
||||
|
||||
# Build AST from disassembly.
|
||||
# Build parse tree from tokenized and massaged disassembly.
|
||||
try:
|
||||
# FIXME: have p.insts update in a better way
|
||||
# modularity is broken here
|
||||
@@ -1139,7 +1139,7 @@ class FragmentsWalker(pysource.SourceWalker, object):
|
||||
except (parser.ParserError(e), AssertionError(e)):
|
||||
raise ParserError(e, tokens)
|
||||
|
||||
maybe_show_ast(self.showast, ast)
|
||||
maybe_show_tree(self.showast, ast)
|
||||
|
||||
checker(ast, False, self.ast_errors)
|
||||
|
||||
@@ -1731,12 +1731,12 @@ def deparse_code(version, co, out=StringIO(), showasm=False, showast=False,
|
||||
pass a file like object, into which the asm will be
|
||||
written).
|
||||
:param showast: Flag which determines whether the constructed
|
||||
abstract syntax tree is written to sys.stdout or
|
||||
parse tree is written to sys.stdout or
|
||||
not. (It is also to pass a file like object, into
|
||||
which the ast will be written).
|
||||
:param showgrammar: Flag which determines whether the grammar
|
||||
:param showgrammar: Flag which determines whether the grammar reduction rules
|
||||
is written to sys.stdout or not. (It is also to
|
||||
pass a file like object, into which the grammer
|
||||
pass a file like object, into which the grammar
|
||||
will be written).
|
||||
|
||||
:return: The deparsed source fragment.
|
||||
@@ -1757,7 +1757,7 @@ def deparse_code(version, co, out=StringIO(), showasm=False, showast=False,
|
||||
debug_parser['reduce'] = showgrammar
|
||||
debug_parser['errorstack'] = True
|
||||
|
||||
# Build AST from disassembly.
|
||||
# Build Syntax Tree from tokenized and massaged disassembly.
|
||||
# deparsed = pysource.FragmentsWalker(out, scanner, showast=showast)
|
||||
deparsed = walker(version, scanner, showast=showast,
|
||||
debug_parser=debug_parser, compile_mode=compile_mode,
|
||||
|
@@ -16,7 +16,7 @@ read_global_ops = frozenset(('STORE_GLOBAL', 'DELETE_GLOBAL'))
|
||||
# FIXME: this and find_globals could be paramaterized with one of the
|
||||
# above global ops
|
||||
def find_all_globals(node, globs):
|
||||
"""Search AST node to find variable names that are global."""
|
||||
"""Search Syntax Tree node to find variable names that are global."""
|
||||
for n in node:
|
||||
if isinstance(n, AST):
|
||||
globs = find_all_globals(n, globs)
|
||||
@@ -25,7 +25,8 @@ def find_all_globals(node, globs):
|
||||
return globs
|
||||
|
||||
def find_globals(node, globs):
|
||||
"""search AST node to find variable names that need a 'global' added."""
|
||||
"""search a node of parse tree to find variable names that need a
|
||||
'global' added."""
|
||||
for n in node:
|
||||
if isinstance(n, AST):
|
||||
globs = find_globals(n, globs)
|
||||
|
@@ -12,7 +12,7 @@ from uncompyle6.semantics.helper import (
|
||||
)
|
||||
|
||||
|
||||
from uncompyle6.show import maybe_show_ast_param_default
|
||||
from uncompyle6.show import maybe_show_tree_param_default
|
||||
|
||||
# FIXME: DRY the below code...
|
||||
|
||||
@@ -29,7 +29,7 @@ def make_function3_annotate(self, node, is_lambda, nested=1,
|
||||
"""
|
||||
if default:
|
||||
value = self.traverse(default, indent='')
|
||||
maybe_show_ast_param_default(self.showast, name, value)
|
||||
maybe_show_tree_param_default(self.showast, name, value)
|
||||
result = '%s=%s' % (name, value)
|
||||
if result[-2:] == '= ': # default was 'LOAD_CONST None'
|
||||
result += 'None'
|
||||
@@ -284,7 +284,7 @@ def make_function2(self, node, is_lambda, nested=1, codeNode=None):
|
||||
|
||||
if default:
|
||||
value = self.traverse(default, indent='')
|
||||
maybe_show_ast_param_default(self.showast, name, value)
|
||||
maybe_show_tree_param_default(self.showast, name, value)
|
||||
result = '%s=%s' % (name, value)
|
||||
if result[-2:] == '= ': # default was 'LOAD_CONST None'
|
||||
result += 'None'
|
||||
@@ -456,7 +456,7 @@ def make_function3(self, node, is_lambda, nested=1, codeNode=None):
|
||||
value = default
|
||||
else:
|
||||
value = self.traverse(default, indent='')
|
||||
maybe_show_ast_param_default(self.showast, name, value)
|
||||
maybe_show_tree_param_default(self.showast, name, value)
|
||||
result = '%s=%s' % (name, value)
|
||||
if result[-2:] == '= ': # default was 'LOAD_CONST None'
|
||||
result += 'None'
|
||||
|
@@ -3,7 +3,7 @@
|
||||
# Copyright (c) 2000-2002 by hartmut Goebel <h.goebel@crazy-compilers.com>
|
||||
# Copyright (c) 1999 John Aycock
|
||||
|
||||
"""Creates Python source code from an uncompyle6 abstract syntax tree.
|
||||
"""Creates Python source code from an uncompyle6 parse tree.
|
||||
|
||||
The terminal symbols are CPython bytecode instructions. (See the
|
||||
python documentation under module "dis" for a list of instructions
|
||||
@@ -47,7 +47,7 @@ Python.
|
||||
# In the diagram below, N is a nonterminal name, and K also a nonterminal
|
||||
# name but the one used as a key in the table.
|
||||
# we show where those are with respect to each other in the
|
||||
# AST tree for N.
|
||||
# parse tree for N.
|
||||
#
|
||||
#
|
||||
# N&K N N
|
||||
@@ -139,7 +139,7 @@ from uncompyle6.semantics.consts import (
|
||||
|
||||
|
||||
from uncompyle6.show import (
|
||||
maybe_show_ast,
|
||||
maybe_show_tree,
|
||||
)
|
||||
|
||||
if PYTHON3:
|
||||
@@ -2567,7 +2567,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
raise ParserError(e, tokens)
|
||||
except AssertionError, e:
|
||||
raise ParserError(e, tokens)
|
||||
maybe_show_ast(self.showast, ast)
|
||||
maybe_show_tree(self.showast, ast)
|
||||
return ast
|
||||
|
||||
# The bytecode for the end of the main routine has a
|
||||
@@ -2589,7 +2589,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
if len(tokens) == 0:
|
||||
return PASS
|
||||
|
||||
# Build AST from disassembly.
|
||||
# Build a parse tree from a tokenized and massaged disassembly.
|
||||
try:
|
||||
# FIXME: have p.insts update in a better way
|
||||
# modularity is broken here
|
||||
@@ -2600,7 +2600,7 @@ class SourceWalker(GenericASTTraversal, object):
|
||||
except python_parser.ParserError, e:
|
||||
raise ParserError(e, tokens)
|
||||
|
||||
maybe_show_ast(self.showast, ast)
|
||||
maybe_show_tree(self.showast, ast)
|
||||
|
||||
checker(ast, False, self.ast_errors)
|
||||
|
||||
@@ -2635,7 +2635,7 @@ def deparse_code(version, co, out=sys.stdout, showasm=None, showast=False,
|
||||
debug_parser['reduce'] = showgrammar
|
||||
debug_parser['errorstack'] = 'full'
|
||||
|
||||
# Build AST from disassembly.
|
||||
# Build Syntax Tree from disassembly.
|
||||
linestarts = dict(scanner.opc.findlinestarts(co))
|
||||
deparsed = walker(version, out, scanner, showast=showast,
|
||||
debug_parser=debug_parser, compile_mode=compile_mode,
|
||||
@@ -2669,7 +2669,7 @@ def deparse_code(version, co, out=sys.stdout, showasm=None, showast=False,
|
||||
deparsed.FUTURE_UNICODE_LITERALS = (
|
||||
COMPILER_FLAG_BIT['FUTURE_UNICODE_LITERALS'] & co.co_flags != 0)
|
||||
|
||||
# What we've been waiting for: Generate source from AST!
|
||||
# What we've been waiting for: Generate source from Syntax Tree!
|
||||
deparsed.gen_source(deparsed.ast, co.co_name, customize)
|
||||
|
||||
for g in deparsed.mod_globs:
|
||||
@@ -2686,13 +2686,30 @@ def deparse_code(version, co, out=sys.stdout, showasm=None, showast=False,
|
||||
raise SourceWalkerError("Deparsing stopped due to parse error")
|
||||
return deparsed
|
||||
|
||||
#
|
||||
DEFAULT_DEBUG_OPTS = {
|
||||
'asm': False,
|
||||
'tree': False,
|
||||
'grammar': False
|
||||
}
|
||||
def deparse_code2str(code, out=sys.stdout, version=None,
|
||||
debug_opts=DEFAULT_DEBUG_OPTS,
|
||||
code_objects={}, compile_mode='exec',
|
||||
is_pypy=False, walker=SourceWalker):
|
||||
"""Return the deparsed text for a Python code object. `out` is where any intermediate
|
||||
output for assembly or tree output will be sent.
|
||||
"""
|
||||
return deparse_code(version, code, out, showasm=debug_opts.get('asm', None),
|
||||
showast=debug_opts.get('tree', None),
|
||||
showgrammar=debug_opts.get('grammar', None), code_objects=code_objects,
|
||||
compile_mode=compile_mode, is_pypy=is_pypy, walker=walker).text
|
||||
|
||||
if __name__ == '__main__':
|
||||
def deparse_test(co):
|
||||
"This is a docstring"
|
||||
sys_version = float(sys.version[0:3])
|
||||
deparsed = deparse_code(sys_version, co, showasm='after', showast=True)
|
||||
# deparsed = deparse_code(sys_version, co, showasm=None, showast=False,
|
||||
# showgrammar=True)
|
||||
print(deparsed.text)
|
||||
s = deparse_code2str(co, debug_opts={'asm':'after', 'tree':True})
|
||||
# s = deparse_code2str(co, showasm=None, showast=False,
|
||||
# showgrammar=True)
|
||||
print(s)
|
||||
return
|
||||
deparse_test(deparse_test.func_code)
|
||||
|
@@ -21,41 +21,41 @@ def maybe_show_asm(showasm, tokens):
|
||||
stream.write('\n')
|
||||
|
||||
|
||||
def maybe_show_ast(showast, ast):
|
||||
def maybe_show_tree(show_tree, ast):
|
||||
"""
|
||||
Show the ast based on the showast flag (or file object), writing to the
|
||||
appropriate stream depending on the type of the flag.
|
||||
|
||||
:param showasm: Flag which determines whether the abstract syntax tree is
|
||||
written to sys.stdout or not. (It is also to pass a file
|
||||
like object, into which the ast will be written).
|
||||
:param show_tree: Flag which determines whether the parse tree is
|
||||
written to sys.stdout or not. (It is also to pass a file
|
||||
like object, into which the ast will be written).
|
||||
:param ast: The ast to show.
|
||||
"""
|
||||
if showast:
|
||||
if hasattr(showast, 'write'):
|
||||
stream = showast
|
||||
if show_tree:
|
||||
if hasattr(show_tree, 'write'):
|
||||
stream = show_tree
|
||||
else:
|
||||
stream = sys.stdout
|
||||
stream.write(str(ast))
|
||||
stream.write('\n')
|
||||
|
||||
|
||||
def maybe_show_ast_param_default(showast, name, default):
|
||||
def maybe_show_tree_param_default(show_tree, name, default):
|
||||
"""
|
||||
Show a function parameter with default for an ast based on the showast flag
|
||||
Show a function parameter with default for an grammar-tree based on the show_tree flag
|
||||
(or file object), writing to the appropriate stream depending on the type
|
||||
of the flag.
|
||||
|
||||
:param showasm: Flag which determines whether the function parameter with
|
||||
default is written to sys.stdout or not. (It is also to
|
||||
pass a file like object, into which the ast will be
|
||||
written).
|
||||
:param show_tree: Flag which determines whether the function parameter with
|
||||
default is written to sys.stdout or not. (It is also to
|
||||
pass a file like object, into which the ast will be
|
||||
written).
|
||||
:param name: The function parameter name.
|
||||
:param default: The function parameter default.
|
||||
"""
|
||||
if showast:
|
||||
if hasattr(showast, 'write'):
|
||||
stream = showast
|
||||
if show_tree:
|
||||
if hasattr(show_tree, 'write'):
|
||||
stream = show_tree
|
||||
else:
|
||||
stream = sys.stdout
|
||||
stream.write('\n')
|
||||
|
Reference in New Issue
Block a user