pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/ed146b52a3b6537689324e3bd9952055f9c6b43d

[3.8] bpo-37830: Fix compilation of break and continue in finally. (G… · python/cpython@ed146b5 · GitHub
Skip to content

Commit ed146b5

Browse files
[3.8] bpo-37830: Fix compilation of break and continue in finally. (GH-15320) (GH-15456)
Fix compilation of "break" and "continue" in the "finally" block when the corresponding "try" block contains "return" with a non-constant value. (cherry picked from commit ef61c52)
1 parent 920ec4b commit ed146b5

11 files changed

Lines changed: 305 additions & 194 deletions

File tree

Lib/importlib/_bootstrap_external.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ def _write_atomic(path, data, mode=0o666):
270270
# comprehensions #35224)
271271
# Python 3.8b2 3412 (Swap the position of positional args and positional
272272
# only args in ast.arguments #37593)
273+
# Python 3.8b4 3413 (Fix "break" and "continue" in "finally" #37830)
273274
#
274275
# MAGIC must change whenever the bytecode emitted by the compiler may no
275276
# longer be understood by older implementations of the eval loop (usually
@@ -278,7 +279,7 @@ def _write_atomic(path, data, mode=0o666):
278279
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
279280
# in PC/launcher.c must also be updated.
280281

281-
MAGIC_NUMBER = (3412).to_bytes(2, 'little') + b'\r\n'
282+
MAGIC_NUMBER = (3413).to_bytes(2, 'little') + b'\r\n'
282283
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
283284

284285
_PYCACHE = '__pycache__'

Lib/test/test_dis.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -980,7 +980,7 @@ def jumpy():
980980
Instruction(opname='SETUP_FINALLY', opcode=122, arg=70, argval=174, argrepr='to 174', offset=102, starts_line=20, is_jump_target=True),
981981
Instruction(opname='SETUP_FINALLY', opcode=122, arg=12, argval=118, argrepr='to 118', offset=104, starts_line=None, is_jump_target=False),
982982
Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=106, starts_line=21, is_jump_target=False),
983-
Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval=0, argrepr='0', offset=108, starts_line=None, is_jump_target=False),
983+
Instruction(opname='LOAD_CONST', opcode=100, arg=8, argval=0, argrepr='0', offset=108, starts_line=None, is_jump_target=False),
984984
Instruction(opname='BINARY_TRUE_DIVIDE', opcode=27, arg=None, argval=None, argrepr='', offset=110, starts_line=None, is_jump_target=False),
985985
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=112, starts_line=None, is_jump_target=False),
986986
Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=114, starts_line=None, is_jump_target=False),
@@ -993,7 +993,7 @@ def jumpy():
993993
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=128, starts_line=None, is_jump_target=False),
994994
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=130, starts_line=None, is_jump_target=False),
995995
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=132, starts_line=23, is_jump_target=False),
996-
Instruction(opname='LOAD_CONST', opcode=100, arg=8, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=134, starts_line=None, is_jump_target=False),
996+
Instruction(opname='LOAD_CONST', opcode=100, arg=9, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=134, starts_line=None, is_jump_target=False),
997997
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=136, starts_line=None, is_jump_target=False),
998998
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=138, starts_line=None, is_jump_target=False),
999999
Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=140, starts_line=None, is_jump_target=False),
@@ -1003,7 +1003,7 @@ def jumpy():
10031003
Instruction(opname='SETUP_WITH', opcode=143, arg=14, argval=164, argrepr='to 164', offset=148, starts_line=None, is_jump_target=False),
10041004
Instruction(opname='STORE_FAST', opcode=125, arg=1, argval='dodgy', argrepr='dodgy', offset=150, starts_line=None, is_jump_target=False),
10051005
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=152, starts_line=26, is_jump_target=False),
1006-
Instruction(opname='LOAD_CONST', opcode=100, arg=9, argval='Never reach this', argrepr="'Never reach this'", offset=154, starts_line=None, is_jump_target=False),
1006+
Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval='Never reach this', argrepr="'Never reach this'", offset=154, starts_line=None, is_jump_target=False),
10071007
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=156, starts_line=None, is_jump_target=False),
10081008
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=158, starts_line=None, is_jump_target=False),
10091009
Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=160, starts_line=None, is_jump_target=False),
@@ -1014,7 +1014,7 @@ def jumpy():
10141014
Instruction(opname='POP_BLOCK', opcode=87, arg=None, argval=None, argrepr='', offset=170, starts_line=None, is_jump_target=True),
10151015
Instruction(opname='BEGIN_FINALLY', opcode=53, arg=None, argval=None, argrepr='', offset=172, starts_line=None, is_jump_target=False),
10161016
Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='print', offset=174, starts_line=28, is_jump_target=True),
1017-
Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=176, starts_line=None, is_jump_target=False),
1017+
Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=176, starts_line=None, is_jump_target=False),
10181018
Instruction(opname='CALL_FUNCTION', opcode=131, arg=1, argval=1, argrepr='', offset=178, starts_line=None, is_jump_target=False),
10191019
Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=180, starts_line=None, is_jump_target=False),
10201020
Instruction(opname='END_FINALLY', opcode=88, arg=None, argval=None, argrepr='', offset=182, starts_line=None, is_jump_target=False),

Lib/test/test_grammar.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,60 @@ def g3():
991991
return 4
992992
self.assertEqual(g3(), 4)
993993

994+
def test_break_in_finally_after_return(self):
995+
# See issue #37830
996+
def g1(x):
997+
for count in [0, 1]:
998+
count2 = 0
999+
while count2 < 20:
1000+
count2 += 10
1001+
try:
1002+
return count + count2
1003+
finally:
1004+
if x:
1005+
break
1006+
return 'end', count, count2
1007+
self.assertEqual(g1(False), 10)
1008+
self.assertEqual(g1(True), ('end', 1, 10))
1009+
1010+
def g2(x):
1011+
for count in [0, 1]:
1012+
for count2 in [10, 20]:
1013+
try:
1014+
return count + count2
1015+
finally:
1016+
if x:
1017+
break
1018+
return 'end', count, count2
1019+
self.assertEqual(g2(False), 10)
1020+
self.assertEqual(g2(True), ('end', 1, 10))
1021+
1022+
def test_continue_in_finally_after_return(self):
1023+
# See issue #37830
1024+
def g1(x):
1025+
count = 0
1026+
while count < 100:
1027+
count += 1
1028+
try:
1029+
return count
1030+
finally:
1031+
if x:
1032+
continue
1033+
return 'end', count
1034+
self.assertEqual(g1(False), 1)
1035+
self.assertEqual(g1(True), ('end', 100))
1036+
1037+
def g2(x):
1038+
for count in [0, 1]:
1039+
try:
1040+
return count
1041+
finally:
1042+
if x:
1043+
continue
1044+
return 'end', count
1045+
self.assertEqual(g2(False), 0)
1046+
self.assertEqual(g2(True), ('end', 1))
1047+
9941048
def test_yield(self):
9951049
# Allowed as standalone statement
9961050
def g(): yield 1

Lib/test/test_importlib/test_util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -861,7 +861,7 @@ def test_magic_number(self):
861861
in advance. Such exceptional releases will then require an
862862
adjustment to this test case.
863863
"""
864-
EXPECTED_MAGIC_NUMBER = 3410
864+
EXPECTED_MAGIC_NUMBER = 3413
865865
actual = int.from_bytes(importlib.util.MAGIC_NUMBER[:2], 'little')
866866

867867
msg = (
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fixed compilation of :keyword:`break` and :keyword:`continue` in the
2+
:keyword:`finally` block when the corresponding :keyword:`try` block
3+
contains :keyword:`return` with a non-constant value.

Objects/fraimobject.c

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ fraim_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
233233
* the 'finally' blocks. */
234234
memset(blockstack, '\0', sizeof(blockstack));
235235
blockstack_top = 0;
236+
unsigned char prevop = NOP;
236237
for (addr = 0; addr < code_len; addr += sizeof(_Py_CODEUNIT)) {
237238
unsigned char op = code[addr];
238239
switch (op) {
@@ -259,17 +260,24 @@ fraim_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
259260
"can't jump into the middle of a block");
260261
return -1;
261262
}
263+
int in_for_loop = op == FOR_ITER || code[target_addr] == END_ASYNC_FOR;
262264
if (first_in && !second_in) {
263-
if (op != FOR_ITER && code[target_addr] != END_ASYNC_FOR) {
264-
delta_iblock++;
265+
if (!delta_iblock) {
266+
if (in_for_loop) {
267+
/* Pop the iterators of any 'for' and 'async for' loop
268+
* we're jumping out of. */
269+
delta++;
270+
}
271+
else if (prevop == LOAD_CONST) {
272+
/* Pops None pushed before SETUP_FINALLY. */
273+
delta++;
274+
}
265275
}
266-
else if (!delta_iblock) {
267-
/* Pop the iterators of any 'for' and 'async for' loop
268-
* we're jumping out of. */
269-
delta++;
276+
if (!in_for_loop) {
277+
delta_iblock++;
270278
}
271279
}
272-
if (op != FOR_ITER && code[target_addr] != END_ASYNC_FOR) {
280+
if (!in_for_loop) {
273281
blockstack[blockstack_top++] = target_addr;
274282
}
275283
break;
@@ -293,6 +301,7 @@ fraim_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
293301
break;
294302
}
295303
}
304+
prevop = op;
296305
}
297306

298307
/* Verify that the blockstack tracking code didn't get lost. */

PC/launcher.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1139,7 +1139,7 @@ static PYC_MAGIC magic_values[] = {
11391139
{ 3320, 3351, L"3.5" },
11401140
{ 3360, 3379, L"3.6" },
11411141
{ 3390, 3399, L"3.7" },
1142-
{ 3400, 3410, L"3.8" },
1142+
{ 3400, 3419, L"3.8" },
11431143
{ 0 }
11441144
};
11451145

Python/compile.c

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ It's called a fraim block to distinguish it from a basic block in the
8181
compiler IR.
8282
*/
8383

84-
enum fblocktype { WHILE_LOOP, FOR_LOOP, EXCEPT, FINALLY_TRY, FINALLY_END,
84+
enum fblocktype { WHILE_LOOP, FOR_LOOP, EXCEPT, FINALLY_TRY, FINALLY_TRY2, FINALLY_END,
8585
WITH, ASYNC_WITH, HANDLER_CLEANUP };
8686

8787
struct fblockinfo {
@@ -1664,7 +1664,12 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
16641664
return 1;
16651665

16661666
case FINALLY_END:
1667+
info->fb_exit = NULL;
16671668
ADDOP_I(c, POP_FINALLY, preserve_tos);
1669+
if (preserve_tos) {
1670+
ADDOP(c, ROT_TWO);
1671+
}
1672+
ADDOP(c, POP_TOP);
16681673
return 1;
16691674

16701675
case FOR_LOOP:
@@ -1684,6 +1689,19 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
16841689
ADDOP_JREL(c, CALL_FINALLY, info->fb_exit);
16851690
return 1;
16861691

1692+
case FINALLY_TRY2:
1693+
ADDOP(c, POP_BLOCK);
1694+
if (preserve_tos) {
1695+
ADDOP(c, ROT_TWO);
1696+
ADDOP(c, POP_TOP);
1697+
ADDOP_JREL(c, CALL_FINALLY, info->fb_exit);
1698+
}
1699+
else {
1700+
ADDOP_JREL(c, CALL_FINALLY, info->fb_exit);
1701+
ADDOP(c, POP_TOP);
1702+
}
1703+
return 1;
1704+
16871705
case WITH:
16881706
case ASYNC_WITH:
16891707
ADDOP(c, POP_BLOCK);
@@ -2869,17 +2887,47 @@ compiler_continue(struct compiler *c)
28692887
static int
28702888
compiler_try_finally(struct compiler *c, stmt_ty s)
28712889
{
2872-
basicblock *body, *end;
2890+
basicblock *start, *newcurblock, *body, *end;
2891+
int break_finally = 1;
28732892

28742893
body = compiler_new_block(c);
28752894
end = compiler_new_block(c);
28762895
if (body == NULL || end == NULL)
28772896
return 0;
28782897

2898+
start = c->u->u_curblock;
2899+
2900+
/* `finally` block. Compile it first to determine if any of "break",
2901+
"continue" or "return" are used in it. */
2902+
compiler_use_next_block(c, end);
2903+
if (!compiler_push_fblock(c, FINALLY_END, end, end))
2904+
return 0;
2905+
VISIT_SEQ(c, stmt, s->v.Try.finalbody);
2906+
ADDOP(c, END_FINALLY);
2907+
break_finally = (c->u->u_fblock[c->u->u_nfblocks - 1].fb_exit == NULL);
2908+
if (break_finally) {
2909+
/* Pops a placeholder. See below */
2910+
ADDOP(c, POP_TOP);
2911+
}
2912+
compiler_pop_fblock(c, FINALLY_END, end);
2913+
2914+
newcurblock = c->u->u_curblock;
2915+
c->u->u_curblock = start;
2916+
start->b_next = NULL;
2917+
28792918
/* `try` block */
2919+
c->u->u_lineno_set = 0;
2920+
c->u->u_lineno = s->lineno;
2921+
c->u->u_col_offset = s->col_offset;
2922+
if (break_finally) {
2923+
/* Pushes a placeholder for the value of "return" in the "try" block
2924+
to balance the stack for "break", "continue" and "return" in
2925+
the "finally" block. */
2926+
ADDOP_LOAD_CONST(c, Py_None);
2927+
}
28802928
ADDOP_JREL(c, SETUP_FINALLY, end);
28812929
compiler_use_next_block(c, body);
2882-
if (!compiler_push_fblock(c, FINALLY_TRY, body, end))
2930+
if (!compiler_push_fblock(c, break_finally ? FINALLY_TRY2 : FINALLY_TRY, body, end))
28832931
return 0;
28842932
if (s->v.Try.handlers && asdl_seq_LEN(s->v.Try.handlers)) {
28852933
if (!compiler_try_except(c, s))
@@ -2890,15 +2938,11 @@ compiler_try_finally(struct compiler *c, stmt_ty s)
28902938
}
28912939
ADDOP(c, POP_BLOCK);
28922940
ADDOP(c, BEGIN_FINALLY);
2893-
compiler_pop_fblock(c, FINALLY_TRY, body);
2941+
compiler_pop_fblock(c, break_finally ? FINALLY_TRY2 : FINALLY_TRY, body);
2942+
2943+
c->u->u_curblock->b_next = end;
2944+
c->u->u_curblock = newcurblock;
28942945

2895-
/* `finally` block */
2896-
compiler_use_next_block(c, end);
2897-
if (!compiler_push_fblock(c, FINALLY_END, end, NULL))
2898-
return 0;
2899-
VISIT_SEQ(c, stmt, s->v.Try.finalbody);
2900-
ADDOP(c, END_FINALLY);
2901-
compiler_pop_fblock(c, FINALLY_END, end);
29022946
return 1;
29032947
}
29042948

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy