diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2019-08-24 10:11:52 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-08-24 10:11:52 (GMT) |
commit | ef61c524ddeeb56da3858b86e349e7288d68178e (patch) | |
tree | e8a9defe85faff05464db6335140eb0990499f15 /Python/compile.c | |
parent | e9c90aa43144b0be1e4e393e8cb549573437a5da (diff) | |
download | cpython-ef61c524ddeeb56da3858b86e349e7288d68178e.zip cpython-ef61c524ddeeb56da3858b86e349e7288d68178e.tar.gz cpython-ef61c524ddeeb56da3858b86e349e7288d68178e.tar.bz2 |
bpo-37830: Fix compilation of break and continue in finally. (GH-15320)
Fix compilation of "break" and "continue" in the
"finally" block when the corresponding "try" block
contains "return" with a non-constant value.
Diffstat (limited to 'Python/compile.c')
-rw-r--r-- | Python/compile.c | 66 |
1 files changed, 55 insertions, 11 deletions
diff --git a/Python/compile.c b/Python/compile.c index d2de7a7..1bf05e2 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -81,7 +81,7 @@ It's called a frame block to distinguish it from a basic block in the compiler IR. */ -enum fblocktype { WHILE_LOOP, FOR_LOOP, EXCEPT, FINALLY_TRY, FINALLY_END, +enum fblocktype { WHILE_LOOP, FOR_LOOP, EXCEPT, FINALLY_TRY, FINALLY_TRY2, FINALLY_END, WITH, ASYNC_WITH, HANDLER_CLEANUP }; struct fblockinfo { @@ -1664,7 +1664,12 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info, return 1; case FINALLY_END: + info->fb_exit = NULL; ADDOP_I(c, POP_FINALLY, preserve_tos); + if (preserve_tos) { + ADDOP(c, ROT_TWO); + } + ADDOP(c, POP_TOP); return 1; case FOR_LOOP: @@ -1684,6 +1689,19 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info, ADDOP_JREL(c, CALL_FINALLY, info->fb_exit); return 1; + case FINALLY_TRY2: + ADDOP(c, POP_BLOCK); + if (preserve_tos) { + ADDOP(c, ROT_TWO); + ADDOP(c, POP_TOP); + ADDOP_JREL(c, CALL_FINALLY, info->fb_exit); + } + else { + ADDOP_JREL(c, CALL_FINALLY, info->fb_exit); + ADDOP(c, POP_TOP); + } + return 1; + case WITH: case ASYNC_WITH: ADDOP(c, POP_BLOCK); @@ -2869,17 +2887,47 @@ compiler_continue(struct compiler *c) static int compiler_try_finally(struct compiler *c, stmt_ty s) { - basicblock *body, *end; + basicblock *start, *newcurblock, *body, *end; + int break_finally = 1; body = compiler_new_block(c); end = compiler_new_block(c); if (body == NULL || end == NULL) return 0; + start = c->u->u_curblock; + + /* `finally` block. Compile it first to determine if any of "break", + "continue" or "return" are used in it. */ + compiler_use_next_block(c, end); + if (!compiler_push_fblock(c, FINALLY_END, end, end)) + return 0; + VISIT_SEQ(c, stmt, s->v.Try.finalbody); + ADDOP(c, END_FINALLY); + break_finally = (c->u->u_fblock[c->u->u_nfblocks - 1].fb_exit == NULL); + if (break_finally) { + /* Pops a placeholder. See below */ + ADDOP(c, POP_TOP); + } + compiler_pop_fblock(c, FINALLY_END, end); + + newcurblock = c->u->u_curblock; + c->u->u_curblock = start; + start->b_next = NULL; + /* `try` block */ + c->u->u_lineno_set = 0; + c->u->u_lineno = s->lineno; + c->u->u_col_offset = s->col_offset; + if (break_finally) { + /* Pushes a placeholder for the value of "return" in the "try" block + to balance the stack for "break", "continue" and "return" in + the "finally" block. */ + ADDOP_LOAD_CONST(c, Py_None); + } ADDOP_JREL(c, SETUP_FINALLY, end); compiler_use_next_block(c, body); - if (!compiler_push_fblock(c, FINALLY_TRY, body, end)) + if (!compiler_push_fblock(c, break_finally ? FINALLY_TRY2 : FINALLY_TRY, body, end)) return 0; if (s->v.Try.handlers && asdl_seq_LEN(s->v.Try.handlers)) { if (!compiler_try_except(c, s)) @@ -2890,15 +2938,11 @@ compiler_try_finally(struct compiler *c, stmt_ty s) } ADDOP(c, POP_BLOCK); ADDOP(c, BEGIN_FINALLY); - compiler_pop_fblock(c, FINALLY_TRY, body); + compiler_pop_fblock(c, break_finally ? FINALLY_TRY2 : FINALLY_TRY, body); + + c->u->u_curblock->b_next = end; + c->u->u_curblock = newcurblock; - /* `finally` block */ - compiler_use_next_block(c, end); - if (!compiler_push_fblock(c, FINALLY_END, end, NULL)) - return 0; - VISIT_SEQ(c, stmt, s->v.Try.finalbody); - ADDOP(c, END_FINALLY); - compiler_pop_fblock(c, FINALLY_END, end); return 1; } |