Merge branch 'master' into line-mappings

This commit is contained in:
rocky
2016-11-18 09:04:03 -05:00
11 changed files with 81 additions and 40 deletions

Binary file not shown.

View 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

View File

@@ -146,12 +146,14 @@ def main(in_base, out_base, files, codes, outfile=None,
uncompyle_file(infile, outstream, showasm, showast, showgrammar)
tot_files += 1
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
except KeyboardInterrupt:
if outfile:
outstream.close()
os.remove(outfile)
sys.stdout.write("\n")
sys.stderr.write("\nLast file: %s " % (infile))
raise
# except:

View File

@@ -18,6 +18,9 @@ class Python23Parser(Python24Parser):
# of Python
_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
COME_FROM POP_TOP POP_BLOCK COME_FROM

View File

@@ -29,9 +29,9 @@ class Python26Parser(Python2Parser):
POP_TOP END_FINALLY
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
# 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
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.
come_froms ::= 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
"""
@@ -70,9 +76,9 @@ class Python26Parser(Python2Parser):
jmp_true ::= JUMP_IF_TRUE POP_TOP
jmp_false ::= JUMP_IF_FALSE POP_TOP
jf_pop ::= JUMP_FORWARD come_from_pop
jf_pop ::= JUMP_ABSOLUTE come_from_pop
jb_pop ::= JUMP_BACK come_from_pop
jf_pop ::= JUMP_FORWARD POP_TOP
jf_pop ::= JUMP_ABSOLUTE POP_TOP
jb_pop ::= JUMP_BACK POP_TOP
jb_cont ::= JUMP_BACK
jb_cont ::= CONTINUE
@@ -85,13 +91,12 @@ class Python26Parser(Python2Parser):
jb_bp_come_from ::= JUMP_BACK bp_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
# 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
"""
@@ -145,9 +150,9 @@ class Python26Parser(Python2Parser):
whileelsestmt ::= SETUP_LOOP testexpr l_stmts_opt jb_pop POP_BLOCK
else_suite COME_FROM
return_stmt ::= ret_expr RETURN_END_IF come_from_pop
return_stmt ::= ret_expr RETURN_VALUE come_from_pop
return_if_stmt ::= ret_expr RETURN_END_IF come_from_pop
return_stmt ::= ret_expr RETURN_END_IF POP_TOP
return_stmt ::= ret_expr RETURN_VALUE POP_TOP
return_if_stmt ::= ret_expr RETURN_END_IF POP_TOP
iflaststmtl ::= testexpr c_stmts_opt JUMP_BACK 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
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 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
'''
@@ -208,7 +214,7 @@ class Python26Parser(Python2Parser):
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):

View File

@@ -25,6 +25,7 @@ from __future__ import print_function
from collections import namedtuple
from array import array
from uncompyle6.scanner import op_has_argument
from xdis.code import iscode
import uncompyle6.scanner as scan
@@ -175,7 +176,7 @@ class Scanner2(scan.Scanner):
opname = self.opc.opname[op]
oparg = None; pattr = None
has_arg = (op >= self.opc.HAVE_ARGUMENT)
has_arg = op_has_argument(op, self.opc)
if has_arg:
oparg = self.get_argument(offset) + extended_arg
extended_arg = 0
@@ -814,20 +815,23 @@ class Scanner2(scan.Scanner):
self.fixed_jumps[pos] = self.restrict_to_parent(target, parent)
def find_jump_targets(self):
'''
"""
Detect all offsets in a byte code which are jump targets
where we might insert a COME_FROM instruction.
Return the list of offsets. An instruction can be jumped
to in from multiple instructions.
'''
n = len(self.code)
"""
code = self.code
n = len(code)
self.structs = [{'type': 'root',
'start': 0,
'end': n-1}]
self.loops = [] # All loop entry points
self.fixed_jumps = {} # Map fixed jumps to their real destination
# All loop entry points
self.loops = []
# Map fixed jumps to their real destination
self.fixed_jumps = {}
self.ignore_if = set()
self.build_stmt_indices()
@@ -837,13 +841,13 @@ class Scanner2(scan.Scanner):
targets = {}
for offset in self.op_range(0, n):
op = self.code[offset]
op = code[offset]
# Determine structures and fix jumps in Python versions
# since 2.3
self.detect_structure(offset, op)
if op >= self.opc.HAVE_ARGUMENT:
if op_has_argument(op, self.opc):
label = self.fixed_jumps.get(offset)
oparg = self.get_argument(offset)
@@ -867,21 +871,21 @@ class Scanner2(scan.Scanner):
# does now start a new statement
# Otherwise, we have want to add a "COME_FROM"
if not (self.version < 2.7 and
self.code[label] == self.opc.POP_TOP and
self.code[self.prev[label]] == self.opc.RETURN_VALUE):
code[label] == self.opc.POP_TOP and
code[self.prev[label]] == self.opc.RETURN_VALUE):
# In Python < 2.7, don't add a COME_FROM, for:
# JUMP_FORWARD, END_FINALLY
# or:
# JUMP_FORWARD, POP_TOP, END_FINALLY
if not (self.version < 2.7 and op == self.opc.JUMP_FORWARD
and ((self.code[offset+3] == self.opc.END_FINALLY)
or (self.code[offset+3] == self.opc.POP_TOP
and self.code[offset+4] == self.opc.END_FINALLY))):
and ((code[offset+3] == self.opc.END_FINALLY)
or (code[offset+3] == self.opc.POP_TOP
and code[offset+4] == self.opc.END_FINALLY))):
# FIXME: rocky: I think we need something like this...
# if offset not in set(self.ignore_if):
# targets[label] = targets.get(label, []) + [offset]
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]
pass
pass

View File

@@ -403,12 +403,13 @@ class Scanner3(Scanner):
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.
This procedure is modelled after dis.findlabels(), but here
for each target the number of jumps is counted.
Return the list of offsets. An instruction can be jumped
to in from multiple instructions.
"""
code = self.code
n = len(code)

View File

@@ -12,9 +12,9 @@ def checker(ast, in_loop, errors):
in_loop = in_loop or ast.type in ('while1stmt', 'whileTruestmt',
'whilestmt', 'whileelsestmt',
'for_block')
if ast.type == 'augassign1' and ast[0][0] == 'and':
text = str(ast[0])
error_text = '\n# improper augmented assigment:\n#\t' + '\n# '.join(text.split("\n"))
if ast.type in ('augassign1', 'augassign2') and ast[0][0] == 'and':
text = str(ast)
error_text = '\n# improper augmented assigment (e.g. +=, *=, ...):\n#\t' + '\n# '.join(text.split("\n")) + '\n'
errors.append(error_text)
for node in ast:

View File

@@ -2324,7 +2324,7 @@ def deparse_code(version, co, out=sys.stdout, showasm=None, showast=False,
deparsed.write('# global %s ## Warning: Unused global' % g)
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.")
for err in deparsed.ast_errors:
deparsed.write(err)

View File

@@ -316,7 +316,10 @@ def cmp_code_objects(version, is_pypy, code_obj1, code_obj2,
i1 += 2
i2 += 2
continue
elif tokens1[i1].type == 'LOAD_NAME' and tokens2[i2].type == 'LOAD_CONST' \
and tokens1[i1].pattr == 'None' and tokens2[i2].pattr == None:
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: