From f1bb40f48592cf1381feae4a7fe6bee60041fe68 Mon Sep 17 00:00:00 2001 From: rocky Date: Fri, 2 Sep 2016 21:08:44 -0400 Subject: [PATCH] Python 2.6- bug: RETURN_ENDIF, POP_TOP .. POP_TOP should be excluded as a potentional statement beginning --- test/bytecode_2.6/06_return_pop.pyc | Bin 0 -> 693 bytes test/simple_source/bug26/06_return_pop.py | 34 ++++++++++++++++++++++ uncompyle6/parsers/parse26.py | 7 +++-- uncompyle6/scanners/scanner2.py | 26 +++++++++++------ 4 files changed, 56 insertions(+), 11 deletions(-) create mode 100644 test/bytecode_2.6/06_return_pop.pyc create mode 100644 test/simple_source/bug26/06_return_pop.py diff --git a/test/bytecode_2.6/06_return_pop.pyc b/test/bytecode_2.6/06_return_pop.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f69c4b90cec62f7e9c363e8f342c6bc27192cc4f GIT binary patch literal 693 zcmb7ByKdV+5S*n*$r2z(t^&A`ixjNlAcfr+L4Z_lTttNdH-sK%;SBnC;T>TFkShD@ z(&bn31)e?0k2VB2`<$KS^6$U#=XutCfW5Nn}0&eICW@X0AvPUTP?+@BA zCSxP;1%&N0i7AYN#|(Y~iJ6uk1zUj=$Of=IY9&jq^DW$S=;<&B+#sOLd9-4klVg~gdONqTj)OLS=E2tsIS};>T8)nj_G>0 zL7~Ar@mzQRH5742FBzj?Yqy3-ZU0F*;Q91(1=F2uQ8niMYP!Gzdax8_&#h_Ow! zHZED;gg9V+(cRWI#FV2SbLQ?E>tdhI+#%s1s#$covH+99+)fX zAwl@0ZT8;RrV35hF3stpTYo(}{c={dCU&i>eB(d+-%&LlS{uy=Whf0YK#@&kB10L= mu@uA+BgLtl&>m)2rEc?6Id7u>seC~4#eK8tYV$3LGsZKd`Fjlj literal 0 HcmV?d00001 diff --git a/test/simple_source/bug26/06_return_pop.py b/test/simple_source/bug26/06_return_pop.py new file mode 100644 index 00000000..80921f6a --- /dev/null +++ b/test/simple_source/bug26/06_return_pop.py @@ -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) diff --git a/uncompyle6/parsers/parse26.py b/uncompyle6/parsers/parse26.py index b270122d..6a49a8d2 100644 --- a/uncompyle6/parsers/parse26.py +++ b/uncompyle6/parsers/parse26.py @@ -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 diff --git a/uncompyle6/scanners/scanner2.py b/uncompyle6/scanners/scanner2.py index feb097db..f1496d6c 100644 --- a/uncompyle6/scanners/scanner2.py +++ b/uncompyle6/scanners/scanner2.py @@ -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]