You've already forked python-uncompyle6
mirror of
https://github.com/rocky/python-uncompyle6.git
synced 2025-08-04 01:09:52 +08:00
Merge branch 'master' into line-mappings
This commit is contained in:
BIN
test/bytecode_2.6/03_if_vs_and.pyc
Normal file
BIN
test/bytecode_2.6/03_if_vs_and.pyc
Normal file
Binary file not shown.
22
test/simple_source/bug26/03_if_vs_and.py
Normal file
22
test/simple_source/bug26/03_if_vs_and.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# From 2.6 decimal
|
||||||
|
# Bug was not recognizing scope of if and
|
||||||
|
# turning it into xc == 1 and xe *= yc
|
||||||
|
def _power_exact(y, xc, yc, xe):
|
||||||
|
yc, ye = y.int, y.exp
|
||||||
|
while yc % 10 == 0:
|
||||||
|
yc //= 10
|
||||||
|
ye += 1
|
||||||
|
|
||||||
|
if xc == 1:
|
||||||
|
xe *= yc
|
||||||
|
while xe % 10 == 0:
|
||||||
|
xe //= 10
|
||||||
|
ye += 1
|
||||||
|
if ye < 0:
|
||||||
|
return None
|
||||||
|
exponent = xe * 10**ye
|
||||||
|
if y and xe:
|
||||||
|
xc = exponent
|
||||||
|
else:
|
||||||
|
xc = 0
|
||||||
|
return 5
|
@@ -146,12 +146,14 @@ def main(in_base, out_base, files, codes, outfile=None,
|
|||||||
uncompyle_file(infile, outstream, showasm, showast, showgrammar)
|
uncompyle_file(infile, outstream, showasm, showast, showgrammar)
|
||||||
tot_files += 1
|
tot_files += 1
|
||||||
except (ValueError, SyntaxError, ParserError, pysource.SourceWalkerError) as e:
|
except (ValueError, SyntaxError, ParserError, pysource.SourceWalkerError) as e:
|
||||||
sys.stderr.write("\n# file %s\n# %s" % (infile, e))
|
sys.stdout.write("\n")
|
||||||
|
sys.stderr.write("\n# file %s\n# %s\n" % (infile, e))
|
||||||
failed_files += 1
|
failed_files += 1
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
if outfile:
|
if outfile:
|
||||||
outstream.close()
|
outstream.close()
|
||||||
os.remove(outfile)
|
os.remove(outfile)
|
||||||
|
sys.stdout.write("\n")
|
||||||
sys.stderr.write("\nLast file: %s " % (infile))
|
sys.stderr.write("\nLast file: %s " % (infile))
|
||||||
raise
|
raise
|
||||||
# except:
|
# except:
|
||||||
|
@@ -18,6 +18,9 @@ class Python23Parser(Python24Parser):
|
|||||||
# of Python
|
# of Python
|
||||||
_while1test ::= SETUP_LOOP JUMP_FORWARD JUMP_IF_FALSE POP_TOP COME_FROM
|
_while1test ::= SETUP_LOOP JUMP_FORWARD JUMP_IF_FALSE POP_TOP COME_FROM
|
||||||
|
|
||||||
|
while1stmt ::= _while1test l_stmts_opt JUMP_BACK
|
||||||
|
POP_TOP POP_BLOCK COME_FROM
|
||||||
|
|
||||||
while1stmt ::= _while1test l_stmts_opt JUMP_BACK
|
while1stmt ::= _while1test l_stmts_opt JUMP_BACK
|
||||||
COME_FROM POP_TOP POP_BLOCK COME_FROM
|
COME_FROM POP_TOP POP_BLOCK COME_FROM
|
||||||
|
|
||||||
|
@@ -29,9 +29,9 @@ class Python26Parser(Python2Parser):
|
|||||||
POP_TOP END_FINALLY
|
POP_TOP END_FINALLY
|
||||||
|
|
||||||
try_middle ::= jmp_abs COME_FROM except_stmts
|
try_middle ::= jmp_abs COME_FROM except_stmts
|
||||||
come_from_pop END_FINALLY
|
POP_TOP END_FINALLY
|
||||||
|
|
||||||
trystmt ::= SETUP_EXCEPT suite_stmts_opt come_from_pop
|
trystmt ::= SETUP_EXCEPT suite_stmts_opt POP_TOP
|
||||||
try_middle
|
try_middle
|
||||||
|
|
||||||
# Sometimes we don't put in COME_FROM to the next statement
|
# Sometimes we don't put in COME_FROM to the next statement
|
||||||
@@ -48,11 +48,17 @@ class Python26Parser(Python2Parser):
|
|||||||
_ifstmts_jump ::= c_stmts_opt JUMP_FORWARD COME_FROM POP_TOP
|
_ifstmts_jump ::= c_stmts_opt JUMP_FORWARD COME_FROM POP_TOP
|
||||||
|
|
||||||
except_suite ::= c_stmts_opt JUMP_FORWARD come_from_pop
|
except_suite ::= c_stmts_opt JUMP_FORWARD come_from_pop
|
||||||
|
except_suite ::= c_stmts_opt JUMP_FORWARD POP_TOP
|
||||||
|
except_suite ::= c_stmts_opt jmp_abs come_from_pop
|
||||||
|
|
||||||
# Python 3 also has this.
|
# Python 3 also has this.
|
||||||
come_froms ::= come_froms COME_FROM
|
come_froms ::= come_froms COME_FROM
|
||||||
come_froms ::= COME_FROM
|
come_froms ::= COME_FROM
|
||||||
|
|
||||||
|
# This is what happens after a jump where
|
||||||
|
# we start a new block. For reasons I don't fully
|
||||||
|
# understand, there is also a value on the top of the stack
|
||||||
|
come_from_pop ::= COME_FROM POP_TOP
|
||||||
come_froms_pop ::= come_froms POP_TOP
|
come_froms_pop ::= come_froms POP_TOP
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -70,9 +76,9 @@ class Python26Parser(Python2Parser):
|
|||||||
jmp_true ::= JUMP_IF_TRUE POP_TOP
|
jmp_true ::= JUMP_IF_TRUE POP_TOP
|
||||||
jmp_false ::= JUMP_IF_FALSE POP_TOP
|
jmp_false ::= JUMP_IF_FALSE POP_TOP
|
||||||
|
|
||||||
jf_pop ::= JUMP_FORWARD come_from_pop
|
jf_pop ::= JUMP_FORWARD POP_TOP
|
||||||
jf_pop ::= JUMP_ABSOLUTE come_from_pop
|
jf_pop ::= JUMP_ABSOLUTE POP_TOP
|
||||||
jb_pop ::= JUMP_BACK come_from_pop
|
jb_pop ::= JUMP_BACK POP_TOP
|
||||||
|
|
||||||
jb_cont ::= JUMP_BACK
|
jb_cont ::= JUMP_BACK
|
||||||
jb_cont ::= CONTINUE
|
jb_cont ::= CONTINUE
|
||||||
@@ -85,13 +91,12 @@ class Python26Parser(Python2Parser):
|
|||||||
jb_bp_come_from ::= JUMP_BACK bp_come_from
|
jb_bp_come_from ::= JUMP_BACK bp_come_from
|
||||||
|
|
||||||
_ifstmts_jump ::= c_stmts_opt jf_pop COME_FROM
|
_ifstmts_jump ::= c_stmts_opt jf_pop COME_FROM
|
||||||
_ifstmts_jump ::= c_stmts_opt JUMP_FORWARD COME_FROM come_from_pop
|
_ifstmts_jump ::= c_stmts_opt JUMP_FORWARD COME_FROM POP_TOP
|
||||||
_ifstmts_jump ::= c_stmts_opt JUMP_FORWARD come_froms POP_TOP COME_FROM
|
_ifstmts_jump ::= c_stmts_opt JUMP_FORWARD come_froms POP_TOP COME_FROM
|
||||||
|
|
||||||
# This is what happens after a jump where
|
# This is what happens after a jump where
|
||||||
# we start a new block. For reasons I don't fully
|
# we start a new block. For reasons I don't fully
|
||||||
# understand, there is also a value on the top of the stack
|
# understand, there is also a value on the top of the stack
|
||||||
come_from_pop ::= COME_FROM POP_TOP
|
|
||||||
come_froms_pop ::= come_froms POP_TOP
|
come_froms_pop ::= come_froms POP_TOP
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -145,9 +150,9 @@ class Python26Parser(Python2Parser):
|
|||||||
whileelsestmt ::= SETUP_LOOP testexpr l_stmts_opt jb_pop POP_BLOCK
|
whileelsestmt ::= SETUP_LOOP testexpr l_stmts_opt jb_pop POP_BLOCK
|
||||||
else_suite COME_FROM
|
else_suite COME_FROM
|
||||||
|
|
||||||
return_stmt ::= ret_expr RETURN_END_IF come_from_pop
|
return_stmt ::= ret_expr RETURN_END_IF POP_TOP
|
||||||
return_stmt ::= ret_expr RETURN_VALUE come_from_pop
|
return_stmt ::= ret_expr RETURN_VALUE POP_TOP
|
||||||
return_if_stmt ::= ret_expr RETURN_END_IF come_from_pop
|
return_if_stmt ::= ret_expr RETURN_END_IF POP_TOP
|
||||||
|
|
||||||
iflaststmtl ::= testexpr c_stmts_opt JUMP_BACK come_from_pop
|
iflaststmtl ::= testexpr c_stmts_opt JUMP_BACK come_from_pop
|
||||||
iflaststmt ::= testexpr c_stmts_opt JUMP_ABSOLUTE come_from_pop
|
iflaststmt ::= testexpr c_stmts_opt JUMP_ABSOLUTE come_from_pop
|
||||||
@@ -186,7 +191,8 @@ class Python26Parser(Python2Parser):
|
|||||||
# Make sure we keep indices the same as 2.7
|
# Make sure we keep indices the same as 2.7
|
||||||
setup_loop_lf ::= SETUP_LOOP LOAD_FAST
|
setup_loop_lf ::= SETUP_LOOP LOAD_FAST
|
||||||
genexpr_func ::= setup_loop_lf FOR_ITER designator comp_iter jb_bp_come_from
|
genexpr_func ::= setup_loop_lf FOR_ITER designator comp_iter jb_bp_come_from
|
||||||
genexpr_func ::= setup_loop_lf FOR_ITER designator comp_iter JUMP_BACK come_from_pop jb_bp_come_from
|
genexpr_func ::= setup_loop_lf FOR_ITER designator comp_iter JUMP_BACK come_from_pop
|
||||||
|
jb_bp_come_from
|
||||||
genexpr ::= LOAD_GENEXPR MAKE_FUNCTION_0 expr GET_ITER CALL_FUNCTION_1 COME_FROM
|
genexpr ::= LOAD_GENEXPR MAKE_FUNCTION_0 expr GET_ITER CALL_FUNCTION_1 COME_FROM
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@@ -208,7 +214,7 @@ class Python26Parser(Python2Parser):
|
|||||||
|
|
||||||
def p_except26(self, args):
|
def p_except26(self, args):
|
||||||
'''
|
'''
|
||||||
except_suite ::= c_stmts_opt jmp_abs come_from_pop
|
except_suite ::= c_stmts_opt jmp_abs POP_TOP
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def p_misc26(self, args):
|
def p_misc26(self, args):
|
||||||
|
@@ -25,6 +25,7 @@ from __future__ import print_function
|
|||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from array import array
|
from array import array
|
||||||
|
|
||||||
|
from uncompyle6.scanner import op_has_argument
|
||||||
from xdis.code import iscode
|
from xdis.code import iscode
|
||||||
|
|
||||||
import uncompyle6.scanner as scan
|
import uncompyle6.scanner as scan
|
||||||
@@ -175,7 +176,7 @@ class Scanner2(scan.Scanner):
|
|||||||
opname = self.opc.opname[op]
|
opname = self.opc.opname[op]
|
||||||
|
|
||||||
oparg = None; pattr = None
|
oparg = None; pattr = None
|
||||||
has_arg = (op >= self.opc.HAVE_ARGUMENT)
|
has_arg = op_has_argument(op, self.opc)
|
||||||
if has_arg:
|
if has_arg:
|
||||||
oparg = self.get_argument(offset) + extended_arg
|
oparg = self.get_argument(offset) + extended_arg
|
||||||
extended_arg = 0
|
extended_arg = 0
|
||||||
@@ -814,20 +815,23 @@ class Scanner2(scan.Scanner):
|
|||||||
self.fixed_jumps[pos] = self.restrict_to_parent(target, parent)
|
self.fixed_jumps[pos] = self.restrict_to_parent(target, parent)
|
||||||
|
|
||||||
def find_jump_targets(self):
|
def find_jump_targets(self):
|
||||||
'''
|
"""
|
||||||
Detect all offsets in a byte code which are jump targets
|
Detect all offsets in a byte code which are jump targets
|
||||||
where we might insert a COME_FROM instruction.
|
where we might insert a COME_FROM instruction.
|
||||||
|
|
||||||
Return the list of offsets. An instruction can be jumped
|
Return the list of offsets. An instruction can be jumped
|
||||||
to in from multiple instructions.
|
to in from multiple instructions.
|
||||||
'''
|
"""
|
||||||
|
code = self.code
|
||||||
n = len(self.code)
|
n = len(code)
|
||||||
self.structs = [{'type': 'root',
|
self.structs = [{'type': 'root',
|
||||||
'start': 0,
|
'start': 0,
|
||||||
'end': n-1}]
|
'end': n-1}]
|
||||||
self.loops = [] # All loop entry points
|
# All loop entry points
|
||||||
self.fixed_jumps = {} # Map fixed jumps to their real destination
|
self.loops = []
|
||||||
|
|
||||||
|
# Map fixed jumps to their real destination
|
||||||
|
self.fixed_jumps = {}
|
||||||
self.ignore_if = set()
|
self.ignore_if = set()
|
||||||
self.build_stmt_indices()
|
self.build_stmt_indices()
|
||||||
|
|
||||||
@@ -837,13 +841,13 @@ class Scanner2(scan.Scanner):
|
|||||||
|
|
||||||
targets = {}
|
targets = {}
|
||||||
for offset in self.op_range(0, n):
|
for offset in self.op_range(0, n):
|
||||||
op = self.code[offset]
|
op = code[offset]
|
||||||
|
|
||||||
# Determine structures and fix jumps in Python versions
|
# Determine structures and fix jumps in Python versions
|
||||||
# since 2.3
|
# since 2.3
|
||||||
self.detect_structure(offset, op)
|
self.detect_structure(offset, op)
|
||||||
|
|
||||||
if op >= self.opc.HAVE_ARGUMENT:
|
if op_has_argument(op, self.opc):
|
||||||
label = self.fixed_jumps.get(offset)
|
label = self.fixed_jumps.get(offset)
|
||||||
oparg = self.get_argument(offset)
|
oparg = self.get_argument(offset)
|
||||||
|
|
||||||
@@ -867,21 +871,21 @@ class Scanner2(scan.Scanner):
|
|||||||
# does now start a new statement
|
# does now start a new statement
|
||||||
# Otherwise, we have want to add a "COME_FROM"
|
# Otherwise, we have want to add a "COME_FROM"
|
||||||
if not (self.version < 2.7 and
|
if not (self.version < 2.7 and
|
||||||
self.code[label] == self.opc.POP_TOP and
|
code[label] == self.opc.POP_TOP and
|
||||||
self.code[self.prev[label]] == self.opc.RETURN_VALUE):
|
code[self.prev[label]] == self.opc.RETURN_VALUE):
|
||||||
# In Python < 2.7, don't add a COME_FROM, for:
|
# In Python < 2.7, don't add a COME_FROM, for:
|
||||||
# JUMP_FORWARD, END_FINALLY
|
# JUMP_FORWARD, END_FINALLY
|
||||||
# or:
|
# or:
|
||||||
# JUMP_FORWARD, POP_TOP, END_FINALLY
|
# JUMP_FORWARD, POP_TOP, END_FINALLY
|
||||||
if not (self.version < 2.7 and op == self.opc.JUMP_FORWARD
|
if not (self.version < 2.7 and op == self.opc.JUMP_FORWARD
|
||||||
and ((self.code[offset+3] == self.opc.END_FINALLY)
|
and ((code[offset+3] == self.opc.END_FINALLY)
|
||||||
or (self.code[offset+3] == self.opc.POP_TOP
|
or (code[offset+3] == self.opc.POP_TOP
|
||||||
and self.code[offset+4] == self.opc.END_FINALLY))):
|
and code[offset+4] == self.opc.END_FINALLY))):
|
||||||
|
|
||||||
# FIXME: rocky: I think we need something like this...
|
# FIXME: rocky: I think we need something like this...
|
||||||
# if offset not in set(self.ignore_if):
|
if offset not in set(self.ignore_if) or self.version == 2.7:
|
||||||
# targets[label] = targets.get(label, []) + [offset]
|
targets[label] = targets.get(label, []) + [offset]
|
||||||
targets[label] = targets.get(label, []) + [offset]
|
# targets[label] = targets.get(label, []) + [offset]
|
||||||
pass
|
pass
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
@@ -403,12 +403,13 @@ class Scanner3(Scanner):
|
|||||||
|
|
||||||
def find_jump_targets(self):
|
def find_jump_targets(self):
|
||||||
"""
|
"""
|
||||||
Detect all offsets in a byte code which are jump targets.
|
Detect all offsets in a byte code which are jump targets
|
||||||
|
where we might insert a COME_FROM instruction.
|
||||||
|
|
||||||
Return the list of offsets.
|
Return the list of offsets.
|
||||||
|
|
||||||
This procedure is modelled after dis.findlabels(), but here
|
Return the list of offsets. An instruction can be jumped
|
||||||
for each target the number of jumps is counted.
|
to in from multiple instructions.
|
||||||
"""
|
"""
|
||||||
code = self.code
|
code = self.code
|
||||||
n = len(code)
|
n = len(code)
|
||||||
|
@@ -12,9 +12,9 @@ def checker(ast, in_loop, errors):
|
|||||||
in_loop = in_loop or ast.type in ('while1stmt', 'whileTruestmt',
|
in_loop = in_loop or ast.type in ('while1stmt', 'whileTruestmt',
|
||||||
'whilestmt', 'whileelsestmt',
|
'whilestmt', 'whileelsestmt',
|
||||||
'for_block')
|
'for_block')
|
||||||
if ast.type == 'augassign1' and ast[0][0] == 'and':
|
if ast.type in ('augassign1', 'augassign2') and ast[0][0] == 'and':
|
||||||
text = str(ast[0])
|
text = str(ast)
|
||||||
error_text = '\n# improper augmented assigment:\n#\t' + '\n# '.join(text.split("\n"))
|
error_text = '\n# improper augmented assigment (e.g. +=, *=, ...):\n#\t' + '\n# '.join(text.split("\n")) + '\n'
|
||||||
errors.append(error_text)
|
errors.append(error_text)
|
||||||
|
|
||||||
for node in ast:
|
for node in ast:
|
||||||
|
@@ -2324,7 +2324,7 @@ def deparse_code(version, co, out=sys.stdout, showasm=None, showast=False,
|
|||||||
deparsed.write('# global %s ## Warning: Unused global' % g)
|
deparsed.write('# global %s ## Warning: Unused global' % g)
|
||||||
|
|
||||||
if deparsed.ast_errors:
|
if deparsed.ast_errors:
|
||||||
deparsed.write("# NOTE: have decompilation errors.\n")
|
deparsed.write("# NOTE: have internal decompilation grammar errors.\n")
|
||||||
deparsed.write("# Use -t option to show full context.")
|
deparsed.write("# Use -t option to show full context.")
|
||||||
for err in deparsed.ast_errors:
|
for err in deparsed.ast_errors:
|
||||||
deparsed.write(err)
|
deparsed.write(err)
|
||||||
|
@@ -316,9 +316,12 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2,
|
|||||||
i1 += 2
|
i1 += 2
|
||||||
i2 += 2
|
i2 += 2
|
||||||
continue
|
continue
|
||||||
|
elif tokens1[i1].type == 'LOAD_NAME' and tokens2[i2].type == 'LOAD_CONST' \
|
||||||
raise CmpErrorCode(name, tokens1[i1].offset, tokens1[i1],
|
and tokens1[i1].pattr == 'None' and tokens2[i2].pattr == None:
|
||||||
tokens2[i2], tokens1, tokens2)
|
pass
|
||||||
|
else:
|
||||||
|
raise CmpErrorCode(name, tokens1[i1].offset, tokens1[i1],
|
||||||
|
tokens2[i2], tokens1, tokens2)
|
||||||
elif tokens1[i1].type in JUMP_OPs and tokens1[i1].pattr != tokens2[i2].pattr:
|
elif tokens1[i1].type in JUMP_OPs and tokens1[i1].pattr != tokens2[i2].pattr:
|
||||||
dest1 = int(tokens1[i1].pattr)
|
dest1 = int(tokens1[i1].pattr)
|
||||||
dest2 = int(tokens2[i2].pattr)
|
dest2 = int(tokens2[i2].pattr)
|
||||||
|
Reference in New Issue
Block a user