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) 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:

View File

@@ -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

View File

@@ -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):

View File

@@ -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

View File

@@ -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)

View File

@@ -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:

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) 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)

View File

@@ -316,7 +316,10 @@ 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' \
and tokens1[i1].pattr == 'None' and tokens2[i2].pattr == None:
pass
else:
raise CmpErrorCode(name, tokens1[i1].offset, tokens1[i1], raise CmpErrorCode(name, tokens1[i1].offset, tokens1[i1],
tokens2[i2], tokens1, tokens2) 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: