summaryrefslogtreecommitdiffstats
path: root/Python/compile.c
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2019-11-21 09:11:43 (GMT)
committerGitHub <noreply@github.com>2019-11-21 09:11:43 (GMT)
commitfee552669f21ca294f57fe0df826945edc779090 (patch)
tree13b461df5a1231220b8f72c197d2731e9cb88d85 /Python/compile.c
parent5dcc06f6e0d7b5d6589085692b86c63e35e2325e (diff)
downloadcpython-fee552669f21ca294f57fe0df826945edc779090.zip
cpython-fee552669f21ca294f57fe0df826945edc779090.tar.gz
cpython-fee552669f21ca294f57fe0df826945edc779090.tar.bz2
Produce cleaner bytecode for 'with' and 'async with' by generating separate code for normal and exceptional paths. (#6641)
Remove BEGIN_FINALLY, END_FINALLY, CALL_FINALLY and POP_FINALLY bytecodes. Implement finally blocks by code duplication. Reimplement frame.lineno setter using line numbers rather than bytecode offsets.
Diffstat (limited to 'Python/compile.c')
-rw-r--r--Python/compile.c446
1 files changed, 233 insertions, 213 deletions
diff --git a/Python/compile.c b/Python/compile.c
index f26ad3c..f56c015 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -81,14 +81,16 @@ 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_TRY2, FINALLY_END,
- WITH, ASYNC_WITH, HANDLER_CLEANUP };
+enum fblocktype { WHILE_LOOP, FOR_LOOP, EXCEPT, FINALLY_TRY, FINALLY_END,
+ WITH, ASYNC_WITH, HANDLER_CLEANUP, POP_VALUE };
struct fblockinfo {
enum fblocktype fb_type;
basicblock *fb_block;
/* (optional) type-specific exit or cleanup block */
basicblock *fb_exit;
+ /* (optional) additional information required for unwinding */
+ void *fb_datum;
};
enum {
@@ -960,12 +962,6 @@ stack_effect(int opcode, int oparg, int jump)
* Restore the stack position and push 6 values before jumping to
* the handler if an exception be raised. */
return jump ? 6 : 1;
- case WITH_CLEANUP_START:
- return 2; /* or 1, depending on TOS */
- case WITH_CLEANUP_FINISH:
- /* Pop a variable number of values pushed by WITH_CLEANUP_START
- * + __exit__ or __aexit__. */
- return -3;
case RETURN_VALUE:
return -1;
case IMPORT_STAR:
@@ -980,10 +976,6 @@ stack_effect(int opcode, int oparg, int jump)
return 0;
case POP_EXCEPT:
return -3;
- case END_FINALLY:
- case POP_FINALLY:
- /* Pop 6 values when an exception was raised. */
- return -6;
case STORE_NAME:
return -1;
@@ -1056,14 +1048,11 @@ stack_effect(int opcode, int oparg, int jump)
* Restore the stack position and push 6 values before jumping to
* the handler if an exception be raised. */
return jump ? 6 : 0;
- case BEGIN_FINALLY:
- /* Actually pushes 1 value, but count 6 for balancing with
- * END_FINALLY and POP_FINALLY.
- * This is the main reason of using this opcode instead of
- * "LOAD_CONST None". */
- return 6;
- case CALL_FINALLY:
- return jump ? 1 : 0;
+ case RERAISE:
+ return -3;
+
+ case WITH_EXCEPT_START:
+ return 1;
case LOAD_FAST:
return 1;
@@ -1629,7 +1618,7 @@ find_ann(asdl_seq *stmts)
static int
compiler_push_fblock(struct compiler *c, enum fblocktype t, basicblock *b,
- basicblock *exit)
+ basicblock *exit, void *datum)
{
struct fblockinfo *f;
if (c->u->u_nfblocks >= CO_MAXBLOCKS) {
@@ -1641,6 +1630,7 @@ compiler_push_fblock(struct compiler *c, enum fblocktype t, basicblock *b,
f->fb_type = t;
f->fb_block = b;
f->fb_exit = exit;
+ f->fb_datum = datum;
return 1;
}
@@ -1654,8 +1644,19 @@ compiler_pop_fblock(struct compiler *c, enum fblocktype t, basicblock *b)
assert(u->u_fblock[u->u_nfblocks].fb_block == b);
}
+static int
+compiler_call_exit_with_nones(struct compiler *c) {
+ ADDOP_O(c, LOAD_CONST, Py_None, consts);
+ ADDOP(c, DUP_TOP);
+ ADDOP(c, DUP_TOP);
+ ADDOP_I(c, CALL_FUNCTION, 3);
+ return 1;
+}
+
/* Unwind a frame block. If preserve_tos is true, the TOS before
- * popping the blocks will be restored afterwards.
+ * popping the blocks will be restored afterwards, unless another
+ * return, break or continue is found. In which case, the TOS will
+ * be popped.
*/
static int
compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
@@ -1665,15 +1666,6 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
case WHILE_LOOP:
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:
/* Pop the iterator */
if (preserve_tos) {
@@ -1688,20 +1680,28 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
case FINALLY_TRY:
ADDOP(c, POP_BLOCK);
- ADDOP_JREL(c, CALL_FINALLY, info->fb_exit);
+ if (preserve_tos) {
+ if (!compiler_push_fblock(c, POP_VALUE, NULL, NULL, NULL)) {
+ return 0;
+ }
+ }
+ VISIT_SEQ(c, stmt, info->fb_datum);
+ if (preserve_tos) {
+ compiler_pop_fblock(c, POP_VALUE, NULL);
+ }
return 1;
-
- case FINALLY_TRY2:
- ADDOP(c, POP_BLOCK);
+
+ case FINALLY_END:
if (preserve_tos) {
- ADDOP(c, ROT_TWO);
- ADDOP(c, POP_TOP);
- ADDOP_JREL(c, CALL_FINALLY, info->fb_exit);
+ ADDOP(c, ROT_FOUR);
}
- else {
- ADDOP_JREL(c, CALL_FINALLY, info->fb_exit);
- ADDOP(c, POP_TOP);
+ ADDOP(c, POP_TOP);
+ ADDOP(c, POP_TOP);
+ ADDOP(c, POP_TOP);
+ if (preserve_tos) {
+ ADDOP(c, ROT_FOUR);
}
+ ADDOP(c, POP_EXCEPT);
return 1;
case WITH:
@@ -1710,34 +1710,66 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
if (preserve_tos) {
ADDOP(c, ROT_TWO);
}
- ADDOP(c, BEGIN_FINALLY);
- ADDOP(c, WITH_CLEANUP_START);
+ if(!compiler_call_exit_with_nones(c)) {
+ return 0;
+ }
if (info->fb_type == ASYNC_WITH) {
ADDOP(c, GET_AWAITABLE);
ADDOP_LOAD_CONST(c, Py_None);
ADDOP(c, YIELD_FROM);
}
- ADDOP(c, WITH_CLEANUP_FINISH);
- ADDOP_I(c, POP_FINALLY, 0);
+ ADDOP(c, POP_TOP);
return 1;
case HANDLER_CLEANUP:
+ if (info->fb_datum) {
+ ADDOP(c, POP_BLOCK);
+ }
if (preserve_tos) {
ADDOP(c, ROT_FOUR);
}
- if (info->fb_exit) {
- ADDOP(c, POP_BLOCK);
- ADDOP(c, POP_EXCEPT);
- ADDOP_JREL(c, CALL_FINALLY, info->fb_exit);
+ ADDOP(c, POP_EXCEPT);
+ if (info->fb_datum) {
+ ADDOP_LOAD_CONST(c, Py_None);
+ compiler_nameop(c, info->fb_datum, Store);
+ compiler_nameop(c, info->fb_datum, Del);
}
- else {
- ADDOP(c, POP_EXCEPT);
+ return 1;
+
+ case POP_VALUE:
+ if (preserve_tos) {
+ ADDOP(c, ROT_TWO);
}
+ ADDOP(c, POP_TOP);
return 1;
}
Py_UNREACHABLE();
}
+/** Unwind block stack. If loop is not NULL, then stop when the first loop is encountered. */
+static int
+compiler_unwind_fblock_stack(struct compiler *c, int preserve_tos, struct fblockinfo **loop) {
+ if (c->u->u_nfblocks == 0) {
+ return 1;
+ }
+ struct fblockinfo *top = &c->u->u_fblock[c->u->u_nfblocks-1];
+ if (loop != NULL && (top->fb_type == WHILE_LOOP || top->fb_type == FOR_LOOP)) {
+ *loop = top;
+ return 1;
+ }
+ struct fblockinfo copy = *top;
+ c->u->u_nfblocks--;
+ if (!compiler_unwind_fblock(c, &copy, preserve_tos)) {
+ return 0;
+ }
+ if (!compiler_unwind_fblock_stack(c, preserve_tos, loop)) {
+ return 0;
+ }
+ c->u->u_fblock[c->u->u_nfblocks] = copy;
+ c->u->u_nfblocks++;
+ return 1;
+}
+
/* Compile a sequence of statements, checking for a docstring
and for annotations. */
@@ -2634,10 +2666,12 @@ compiler_if(struct compiler *c, stmt_ty s)
if (next == NULL)
return 0;
}
- else
+ else {
next = end;
- if (!compiler_jump_if(c, s->v.If.test, next, 0))
+ }
+ if (!compiler_jump_if(c, s->v.If.test, next, 0)) {
return 0;
+ }
VISIT_SEQ(c, stmt, s->v.If.body);
if (asdl_seq_LEN(s->v.If.orelse)) {
ADDOP_JREL(c, JUMP_FORWARD, end);
@@ -2657,12 +2691,12 @@ compiler_for(struct compiler *c, stmt_ty s)
start = compiler_new_block(c);
cleanup = compiler_new_block(c);
end = compiler_new_block(c);
- if (start == NULL || end == NULL || cleanup == NULL)
+ if (start == NULL || end == NULL || cleanup == NULL) {
return 0;
-
- if (!compiler_push_fblock(c, FOR_LOOP, start, end))
+ }
+ if (!compiler_push_fblock(c, FOR_LOOP, start, end, NULL)) {
return 0;
-
+ }
VISIT(c, expr, s->v.For.iter);
ADDOP(c, GET_ITER);
compiler_use_next_block(c, start);
@@ -2694,16 +2728,16 @@ compiler_async_for(struct compiler *c, stmt_ty s)
except = compiler_new_block(c);
end = compiler_new_block(c);
- if (start == NULL || except == NULL || end == NULL)
+ if (start == NULL || except == NULL || end == NULL) {
return 0;
-
+ }
VISIT(c, expr, s->v.AsyncFor.iter);
ADDOP(c, GET_AITER);
compiler_use_next_block(c, start);
- if (!compiler_push_fblock(c, FOR_LOOP, start, end))
+ if (!compiler_push_fblock(c, FOR_LOOP, start, end, NULL)) {
return 0;
-
+ }
/* SETUP_FINALLY to guard the __anext__ call */
ADDOP_JREL(c, SETUP_FINALLY, except);
ADDOP(c, GET_ANEXT);
@@ -2741,7 +2775,7 @@ compiler_while(struct compiler *c, stmt_ty s)
// Push a dummy block so the VISIT_SEQ knows that we are
// inside a while loop so it can correctly evaluate syntax
// errors.
- if (!compiler_push_fblock(c, WHILE_LOOP, NULL, NULL)) {
+ if (!compiler_push_fblock(c, WHILE_LOOP, NULL, NULL, NULL)) {
return 0;
}
VISIT_SEQ(c, stmt, s->v.While.body);
@@ -2771,7 +2805,7 @@ compiler_while(struct compiler *c, stmt_ty s)
orelse = NULL;
compiler_use_next_block(c, loop);
- if (!compiler_push_fblock(c, WHILE_LOOP, loop, end))
+ if (!compiler_push_fblock(c, WHILE_LOOP, loop, end, NULL))
return 0;
if (constant == -1) {
if (!compiler_jump_if(c, s->v.While.test, anchor, 0))
@@ -2811,12 +2845,8 @@ compiler_return(struct compiler *c, stmt_ty s)
if (preserve_tos) {
VISIT(c, expr, s->v.Return.value);
}
- for (int depth = c->u->u_nfblocks; depth--;) {
- struct fblockinfo *info = &c->u->u_fblock[depth];
-
- if (!compiler_unwind_fblock(c, info, preserve_tos))
- return 0;
- }
+ if (!compiler_unwind_fblock_stack(c, preserve_tos, NULL))
+ return 0;
if (s->v.Return.value == NULL) {
ADDOP_LOAD_CONST(c, Py_None);
}
@@ -2831,33 +2861,32 @@ compiler_return(struct compiler *c, stmt_ty s)
static int
compiler_break(struct compiler *c)
{
- for (int depth = c->u->u_nfblocks; depth--;) {
- struct fblockinfo *info = &c->u->u_fblock[depth];
-
- if (!compiler_unwind_fblock(c, info, 0))
- return 0;
- if (info->fb_type == WHILE_LOOP || info->fb_type == FOR_LOOP) {
- ADDOP_JABS(c, JUMP_ABSOLUTE, info->fb_exit);
- return 1;
- }
+ struct fblockinfo *loop = NULL;
+ if (!compiler_unwind_fblock_stack(c, 0, &loop)) {
+ return 0;
+ }
+ if (loop == NULL) {
+ return compiler_error(c, "'break' outside loop");
+ }
+ if (!compiler_unwind_fblock(c, loop, 0)) {
+ return 0;
}
- return compiler_error(c, "'break' outside loop");
+ ADDOP_JABS(c, JUMP_ABSOLUTE, loop->fb_exit);
+ return 1;
}
static int
compiler_continue(struct compiler *c)
{
- for (int depth = c->u->u_nfblocks; depth--;) {
- struct fblockinfo *info = &c->u->u_fblock[depth];
-
- if (info->fb_type == WHILE_LOOP || info->fb_type == FOR_LOOP) {
- ADDOP_JABS(c, JUMP_ABSOLUTE, info->fb_block);
- return 1;
- }
- if (!compiler_unwind_fblock(c, info, 0))
- return 0;
+ struct fblockinfo *loop = NULL;
+ if (!compiler_unwind_fblock_stack(c, 0, &loop)) {
+ return 0;
}
- return compiler_error(c, "'continue' not properly in loop");
+ if (loop == NULL) {
+ return compiler_error(c, "'continue' not properly in loop");
+ }
+ ADDOP_JABS(c, JUMP_ABSOLUTE, loop->fb_block);
+ return 1;
}
@@ -2866,10 +2895,11 @@ compiler_continue(struct compiler *c)
SETUP_FINALLY L
<code for body>
POP_BLOCK
- BEGIN_FINALLY
+ <code for finalbody>
+ JUMP E
L:
<code for finalbody>
- END_FINALLY
+ E:
The special instructions use the block stack. Each block
stack entry contains the instruction that created it (here
@@ -2881,11 +2911,6 @@ compiler_continue(struct compiler *c)
onto the block stack.
POP_BLOCK:
Pops en entry from the block stack.
- BEGIN_FINALLY
- Pushes NULL onto the value stack.
- END_FINALLY:
- Pops 1 (NULL or int) or 6 entries from the *value* stack and restore
- the raised and the caught exceptions they specify.
The block stack is unwound when an exception is raised:
when a SETUP_FINALLY entry is found, the raised and the caught
@@ -2897,47 +2922,18 @@ compiler_continue(struct compiler *c)
static int
compiler_try_finally(struct compiler *c, stmt_ty s)
{
- basicblock *start, *newcurblock, *body, *end;
- int break_finally = 1;
+ basicblock *body, *end, *exit;
body = compiler_new_block(c);
end = compiler_new_block(c);
- if (body == NULL || end == NULL)
+ exit = compiler_new_block(c);
+ if (body == NULL || end == NULL || exit == 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, break_finally ? FINALLY_TRY2 : FINALLY_TRY, body, end))
+ if (!compiler_push_fblock(c, FINALLY_TRY, body, end, s->v.Try.finalbody))
return 0;
if (s->v.Try.handlers && asdl_seq_LEN(s->v.Try.handlers)) {
if (!compiler_try_except(c, s))
@@ -2947,12 +2943,17 @@ compiler_try_finally(struct compiler *c, stmt_ty s)
VISIT_SEQ(c, stmt, s->v.Try.body);
}
ADDOP(c, POP_BLOCK);
- ADDOP(c, BEGIN_FINALLY);
- compiler_pop_fblock(c, break_finally ? FINALLY_TRY2 : FINALLY_TRY, body);
-
- c->u->u_curblock->b_next = end;
- c->u->u_curblock = newcurblock;
-
+ compiler_pop_fblock(c, FINALLY_TRY, body);
+ VISIT_SEQ(c, stmt, s->v.Try.finalbody);
+ ADDOP_JREL(c, JUMP_FORWARD, exit);
+ /* `finally` block */
+ compiler_use_next_block(c, end);
+ if (!compiler_push_fblock(c, FINALLY_END, end, NULL, NULL))
+ return 0;
+ VISIT_SEQ(c, stmt, s->v.Try.finalbody);
+ compiler_pop_fblock(c, FINALLY_END, end);
+ ADDOP(c, RERAISE);
+ compiler_use_next_block(c, exit);
return 1;
}
@@ -2981,7 +2982,7 @@ compiler_try_finally(struct compiler *c, stmt_ty s)
[tb, val, exc] L2: DUP
.............................etc.......................
- [tb, val, exc] Ln+1: END_FINALLY # re-raise exception
+ [tb, val, exc] Ln+1: RERAISE # re-raise exception
[] L0: <next statement>
@@ -3001,7 +3002,7 @@ compiler_try_except(struct compiler *c, stmt_ty s)
return 0;
ADDOP_JREL(c, SETUP_FINALLY, except);
compiler_use_next_block(c, body);
- if (!compiler_push_fblock(c, EXCEPT, body, NULL))
+ if (!compiler_push_fblock(c, EXCEPT, body, NULL, NULL))
return 0;
VISIT_SEQ(c, stmt, s->v.Try.body);
ADDOP(c, POP_BLOCK);
@@ -3053,28 +3054,29 @@ compiler_try_except(struct compiler *c, stmt_ty s)
/* second try: */
ADDOP_JREL(c, SETUP_FINALLY, cleanup_end);
compiler_use_next_block(c, cleanup_body);
- if (!compiler_push_fblock(c, HANDLER_CLEANUP, cleanup_body, cleanup_end))
+ if (!compiler_push_fblock(c, HANDLER_CLEANUP, cleanup_body, NULL, handler->v.ExceptHandler.name))
return 0;
/* second # body */
VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body);
- ADDOP(c, POP_BLOCK);
- ADDOP(c, BEGIN_FINALLY);
compiler_pop_fblock(c, HANDLER_CLEANUP, cleanup_body);
+ ADDOP(c, POP_BLOCK);
+ ADDOP(c, POP_EXCEPT);
+ /* name = None; del name */
+ ADDOP_LOAD_CONST(c, Py_None);
+ compiler_nameop(c, handler->v.ExceptHandler.name, Store);
+ compiler_nameop(c, handler->v.ExceptHandler.name, Del);
+ ADDOP_JREL(c, JUMP_FORWARD, end);
- /* finally: */
+ /* except: */
compiler_use_next_block(c, cleanup_end);
- if (!compiler_push_fblock(c, FINALLY_END, cleanup_end, NULL))
- return 0;
/* name = None; del name */
ADDOP_LOAD_CONST(c, Py_None);
compiler_nameop(c, handler->v.ExceptHandler.name, Store);
compiler_nameop(c, handler->v.ExceptHandler.name, Del);
- ADDOP(c, END_FINALLY);
- ADDOP(c, POP_EXCEPT);
- compiler_pop_fblock(c, FINALLY_END, cleanup_end);
+ ADDOP(c, RERAISE);
}
else {
basicblock *cleanup_body;
@@ -3086,16 +3088,16 @@ compiler_try_except(struct compiler *c, stmt_ty s)
ADDOP(c, POP_TOP);
ADDOP(c, POP_TOP);
compiler_use_next_block(c, cleanup_body);
- if (!compiler_push_fblock(c, HANDLER_CLEANUP, cleanup_body, NULL))
+ if (!compiler_push_fblock(c, HANDLER_CLEANUP, cleanup_body, NULL, NULL))
return 0;
VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body);
- ADDOP(c, POP_EXCEPT);
compiler_pop_fblock(c, HANDLER_CLEANUP, cleanup_body);
+ ADDOP(c, POP_EXCEPT);
+ ADDOP_JREL(c, JUMP_FORWARD, end);
}
- ADDOP_JREL(c, JUMP_FORWARD, end);
compiler_use_next_block(c, except);
}
- ADDOP(c, END_FINALLY);
+ ADDOP(c, RERAISE);
compiler_use_next_block(c, orelse);
VISIT_SEQ(c, stmt, s->v.Try.orelse);
compiler_use_next_block(c, end);
@@ -4630,6 +4632,22 @@ expr_constant(expr_ty e)
return -1;
}
+static int
+compiler_with_except_finish(struct compiler *c) {
+ basicblock *exit;
+ exit = compiler_new_block(c);
+ if (exit == NULL)
+ return 0;
+ ADDOP_JABS(c, POP_JUMP_IF_TRUE, exit);
+ ADDOP(c, RERAISE);
+ compiler_use_next_block(c, exit);
+ ADDOP(c, POP_TOP);
+ ADDOP(c, POP_TOP);
+ ADDOP(c, POP_TOP);
+ ADDOP(c, POP_EXCEPT);
+ ADDOP(c, POP_TOP);
+ return 1;
+}
/*
Implements the async with statement.
@@ -4658,7 +4676,7 @@ expr_constant(expr_ty e)
static int
compiler_async_with(struct compiler *c, stmt_ty s, int pos)
{
- basicblock *block, *finally;
+ basicblock *block, *final, *exit;
withitem_ty item = asdl_seq_GET(s->v.AsyncWith.items, pos);
assert(s->kind == AsyncWith_kind);
@@ -4669,8 +4687,9 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos)
}
block = compiler_new_block(c);
- finally = compiler_new_block(c);
- if (!block || !finally)
+ final = compiler_new_block(c);
+ exit = compiler_new_block(c);
+ if (!block || !final || !exit)
return 0;
/* Evaluate EXPR */
@@ -4681,11 +4700,11 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos)
ADDOP_LOAD_CONST(c, Py_None);
ADDOP(c, YIELD_FROM);
- ADDOP_JREL(c, SETUP_ASYNC_WITH, finally);
+ ADDOP_JREL(c, SETUP_ASYNC_WITH, final);
/* SETUP_ASYNC_WITH pushes a finally block. */
compiler_use_next_block(c, block);
- if (!compiler_push_fblock(c, ASYNC_WITH, block, finally)) {
+ if (!compiler_push_fblock(c, ASYNC_WITH, block, final, NULL)) {
return 0;
}
@@ -4704,76 +4723,80 @@ compiler_async_with(struct compiler *c, stmt_ty s, int pos)
else if (!compiler_async_with(c, s, pos))
return 0;
- /* End of try block; start the finally block */
- ADDOP(c, POP_BLOCK);
- ADDOP(c, BEGIN_FINALLY);
compiler_pop_fblock(c, ASYNC_WITH, block);
+ ADDOP(c, POP_BLOCK);
+ /* End of body; start the cleanup */
- compiler_use_next_block(c, finally);
- if (!compiler_push_fblock(c, FINALLY_END, finally, NULL))
+ /* For successful outcome:
+ * call __exit__(None, None, None)
+ */
+ if(!compiler_call_exit_with_nones(c))
return 0;
+ ADDOP(c, GET_AWAITABLE);
+ ADDOP_O(c, LOAD_CONST, Py_None, consts);
+ ADDOP(c, YIELD_FROM);
- /* Finally block starts; context.__exit__ is on the stack under
- the exception or return information. Just issue our magic
- opcode. */
- ADDOP(c, WITH_CLEANUP_START);
+ ADDOP(c, POP_TOP);
+
+ ADDOP_JABS(c, JUMP_ABSOLUTE, exit);
+
+ /* For exceptional outcome: */
+ compiler_use_next_block(c, final);
+ ADDOP(c, WITH_EXCEPT_START);
ADDOP(c, GET_AWAITABLE);
ADDOP_LOAD_CONST(c, Py_None);
ADDOP(c, YIELD_FROM);
+ compiler_with_except_finish(c);
- ADDOP(c, WITH_CLEANUP_FINISH);
-
- /* Finally block ends. */
- ADDOP(c, END_FINALLY);
- compiler_pop_fblock(c, FINALLY_END, finally);
+compiler_use_next_block(c, exit);
return 1;
}
/*
Implements the with statement from PEP 343.
-
- The semantics outlined in that PEP are as follows:
-
with EXPR as VAR:
BLOCK
-
- It is implemented roughly as:
-
- context = EXPR
- exit = context.__exit__ # not calling it
- value = context.__enter__()
- try:
- VAR = value # if VAR present in the syntax
- BLOCK
- finally:
- if an exception was raised:
- exc = copy of (exception, instance, traceback)
- else:
- exc = (None, None, None)
- exit(*exc)
+ is implemented as:
+ <code for EXPR>
+ SETUP_WITH E
+ <code to store to VAR> or POP_TOP
+ <code for BLOCK>
+ LOAD_CONST (None, None, None)
+ CALL_FUNCTION_EX 0
+ JUMP_FORWARD EXIT
+ E: WITH_EXCEPT_START (calls EXPR.__exit__)
+ POP_JUMP_IF_TRUE T:
+ RERAISE
+ T: POP_TOP * 3 (remove exception from stack)
+ POP_EXCEPT
+ POP_TOP
+ EXIT:
*/
+
static int
compiler_with(struct compiler *c, stmt_ty s, int pos)
{
- basicblock *block, *finally;
+ basicblock *block, *final, *exit;
withitem_ty item = asdl_seq_GET(s->v.With.items, pos);
assert(s->kind == With_kind);
block = compiler_new_block(c);
- finally = compiler_new_block(c);
- if (!block || !finally)
+ final = compiler_new_block(c);
+ exit = compiler_new_block(c);
+ if (!block || !final || !exit)
return 0;
/* Evaluate EXPR */
VISIT(c, expr, item->context_expr);
- ADDOP_JREL(c, SETUP_WITH, finally);
+ /* Will push bound __exit__ */
+ ADDOP_JREL(c, SETUP_WITH, final);
/* SETUP_WITH pushes a finally block. */
compiler_use_next_block(c, block);
- if (!compiler_push_fblock(c, WITH, block, finally)) {
+ if (!compiler_push_fblock(c, WITH, block, final, NULL)) {
return 0;
}
@@ -4792,24 +4815,26 @@ compiler_with(struct compiler *c, stmt_ty s, int pos)
else if (!compiler_with(c, s, pos))
return 0;
- /* End of try block; start the finally block */
ADDOP(c, POP_BLOCK);
- ADDOP(c, BEGIN_FINALLY);
compiler_pop_fblock(c, WITH, block);
-
- compiler_use_next_block(c, finally);
- if (!compiler_push_fblock(c, FINALLY_END, finally, NULL))
+
+ /* End of body; start the cleanup. */
+
+ /* For successful outcome:
+ * call __exit__(None, None, None)
+ */
+ if (!compiler_call_exit_with_nones(c))
return 0;
+ ADDOP(c, POP_TOP);
+ ADDOP_JREL(c, JUMP_FORWARD, exit);
- /* Finally block starts; context.__exit__ is on the stack under
- the exception or return information. Just issue our magic
- opcode. */
- ADDOP(c, WITH_CLEANUP_START);
- ADDOP(c, WITH_CLEANUP_FINISH);
+ /* For exceptional outcome: */
+ compiler_use_next_block(c, final);
- /* Finally block ends. */
- ADDOP(c, END_FINALLY);
- compiler_pop_fblock(c, FINALLY_END, finally);
+ ADDOP(c, WITH_EXCEPT_START);
+ compiler_with_except_finish(c);
+
+ compiler_use_next_block(c, exit);
return 1;
}
@@ -5427,7 +5452,7 @@ Py_LOCAL_INLINE(void)
stackdepth_push(basicblock ***sp, basicblock *b, int depth)
{
assert(b->b_startdepth < 0 || b->b_startdepth == depth);
- if (b->b_startdepth < depth) {
+ if (b->b_startdepth < depth && b->b_startdepth < 100) {
assert(b->b_startdepth < 0);
b->b_startdepth = depth;
*(*sp)++ = b;
@@ -5483,19 +5508,14 @@ stackdepth(struct compiler *c)
maxdepth = target_depth;
}
assert(target_depth >= 0); /* invalid code or bug in stackdepth() */
- if (instr->i_opcode == CALL_FINALLY) {
- assert(instr->i_target->b_startdepth >= 0);
- assert(instr->i_target->b_startdepth >= target_depth);
- depth = new_depth;
- continue;
- }
stackdepth_push(&sp, instr->i_target, target_depth);
}
depth = new_depth;
if (instr->i_opcode == JUMP_ABSOLUTE ||
instr->i_opcode == JUMP_FORWARD ||
instr->i_opcode == RETURN_VALUE ||
- instr->i_opcode == RAISE_VARARGS)
+ instr->i_opcode == RAISE_VARARGS ||
+ instr->i_opcode == RERAISE)
{
/* remaining code is dead */
next = NULL;