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_and ::= expr jmp_false ret_expr_or_cond COME_FROM
ret_or ::= expr jmp_true 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 ::= 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 # FIXME: split into Python 2.5
ret_or ::= expr jmp_true ret_expr_or_cond come_froms ret_or ::= expr jmp_true ret_expr_or_cond come_froms

View File

@@ -393,14 +393,16 @@ class Scanner2(scan.Scanner):
continue continue
elif code[s] == self.opc.POP_TOP: elif code[s] == self.opc.POP_TOP:
# The POP_TOP in: # The POP_TOP in:
# ROT_TWO, POP_TOP or # ROT_TWO, POP_TOP,
# JUMP_IF_{FALSE,TRUE}, 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 # is part of the previous instruction and not the
# beginning of a new statement # beginning of a new statement
prev = code[self.prev[s]] prev = code[self.prev[s]]
if (prev == self.opc.ROT_TWO or if (prev == self.opc.ROT_TWO or
self.version <= 2.6 and prev in 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) stmts.remove(s)
continue continue
elif code[s] in self.designator_ops: elif code[s] in self.designator_ops:
@@ -793,12 +795,11 @@ class Scanner2(scan.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. An instruction can be jumped
to in from multiple instructions.
This procedure is modelled after dis.findlabels(), but here
for each target the number of jumps are counted.
''' '''
n = len(self.code) n = len(self.code)
@@ -836,6 +837,13 @@ class Scanner2(scan.Scanner):
label = oparg label = oparg
if label is not None and label != -1: if label is not None and label != -1:
# 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] targets[label] = targets.get(label, []) + [offset]
elif op == self.opc.END_FINALLY and offset in self.fixed_jumps: elif op == self.opc.END_FINALLY and offset in self.fixed_jumps:
label = self.fixed_jumps[offset] label = self.fixed_jumps[offset]