Python 2.6- bug: RETURN_ENDIF, POP_TOP ..

POP_TOP should be excluded as a potentional statement beginning
This commit is contained in:
rocky
2016-09-02 21:08:44 -04:00
parent 136f935e26
commit f1bb40f485
4 changed files with 56 additions and 11 deletions

Binary file not shown.

View File

@@ -0,0 +1,34 @@
# From python 2.6 StringIO.py
# Bug was turning emitting code like:
# if spos == slen:
# self.len = self.pos = spos + len(s)
# return None
# # wrong indent below
# if spos > slen:
# ...
# if self.buflist:
# # Invalid "and" below
# self.buflist and self.buf += ''.join(self.buflist)
#
# This was caused by POP_TOP in RETURN_VALUE, POP_TOP getting treated
# as the beginning of a statement
def write(self, s, spos):
if not s: return
# Force s to be a string or unicode
if not isinstance(s, basestring):
s = str(s)
slen = self.len
if spos == slen:
self.len = self.pos = spos + len(s)
return
if spos > slen:
slen = spos
newpos = spos + len(s)
if spos < slen:
if self.buflist:
self.buf += ''.join(self.buflist)
self.buflist = [self.buf[:spos], s, self.buf[newpos:]]
if newpos > slen:
slen = newpos
else:
self.buflist.append(s)

View File

@@ -199,9 +199,12 @@ class Python26Parser(Python2Parser):
'''
ret_and ::= expr jmp_false ret_expr_or_cond COME_FROM
ret_or ::= expr jmp_true ret_expr_or_cond COME_FROM
ret_cond ::= expr jmp_false expr RETURN_END_IF come_from_pop ret_expr_or_cond
ret_cond ::= expr jmp_false expr RETURN_END_IF POP_TOP ret_expr_or_cond
ret_cond ::= expr jmp_false expr ret_expr_or_cond
ret_cond_not ::= expr jmp_true expr RETURN_END_IF come_from_pop ret_expr_or_cond
ret_cond_not ::= expr jmp_true expr RETURN_END_IF POP_TOP ret_expr_or_cond
return_if_stmt ::= ret_expr RETURN_END_IF POP_TOP
return_stmt ::= ret_expr RETURN_VALUE POP_TOP
# FIXME: split into Python 2.5
ret_or ::= expr jmp_true ret_expr_or_cond come_froms

View File

@@ -393,14 +393,16 @@ class Scanner2(scan.Scanner):
continue
elif code[s] == self.opc.POP_TOP:
# The POP_TOP in:
# ROT_TWO, POP_TOP or
# JUMP_IF_{FALSE,TRUE}, POP_TOP
# ROT_TWO, POP_TOP,
# RETURN_xxx, POP_TOP (in 2.6-), or
# JUMP_IF_{FALSE,TRUE}, POP_TOP (in 2.6-)
# is part of the previous instruction and not the
# beginning of a new statement
prev = code[self.prev[s]]
if (prev == self.opc.ROT_TWO or
self.version <= 2.6 and prev in
(self.opc.JUMP_IF_FALSE, self.opc.JUMP_IF_TRUE)):
(self.opc.JUMP_IF_FALSE, self.opc.JUMP_IF_TRUE,
self.opc.RETURN_VALUE)):
stmts.remove(s)
continue
elif code[s] in self.designator_ops:
@@ -793,12 +795,11 @@ class Scanner2(scan.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 are counted.
Return the list of offsets. An instruction can be jumped
to in from multiple instructions.
'''
n = len(self.code)
@@ -836,7 +837,14 @@ class Scanner2(scan.Scanner):
label = oparg
if label is not None and label != -1:
targets[label] = targets.get(label, []) + [offset]
# In Python <= 2.6, the POP_TOP in:
# RETURN_VALUE, POP_TOP
# does now start a new statement
# Otherwise, we have want to add a "COME_FROM"
if not (self.version <= 2.6 and
self.code[label] == self.opc.POP_TOP and
self.code[self.prev[label]] == self.opc.RETURN_VALUE):
targets[label] = targets.get(label, []) + [offset]
elif op == self.opc.END_FINALLY and offset in self.fixed_jumps:
label = self.fixed_jumps[offset]
targets[label] = targets.get(label, []) + [offset]