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.
This commit is contained in:
Mark Shannon 2019-11-21 09:11:43 +00:00 committed by GitHub
parent 5dcc06f6e0
commit fee552669f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 4789 additions and 4754 deletions

View file

@ -79,7 +79,7 @@ static PyObject * unicode_concatenate(PyThreadState *, PyObject *, PyObject *,
static PyObject * special_lookup(PyThreadState *, PyObject *, _Py_Identifier *);
static int check_args_iterable(PyThreadState *, PyObject *func, PyObject *vararg);
static void format_kwargs_error(PyThreadState *, PyObject *func, PyObject *kwargs);
static void format_awaitable_error(PyThreadState *, PyTypeObject *, int);
static void format_awaitable_error(PyThreadState *, PyTypeObject *, int, int);
#define NAME_ERROR_MSG \
"name '%.200s' is not defined"
@ -2017,7 +2017,12 @@ main_loop:
PyObject *iter = _PyCoro_GetAwaitableIter(iterable);
if (iter == NULL) {
int opcode_at_minus_3 = 0;
if ((next_instr - first_instr) > 2) {
opcode_at_minus_3 = _Py_OPCODE(next_instr[-3]);
}
format_awaitable_error(tstate, Py_TYPE(iterable),
opcode_at_minus_3,
_Py_OPCODE(next_instr[-2]));
}
@ -2128,104 +2133,13 @@ main_loop:
DISPATCH();
}
case TARGET(POP_FINALLY): {
/* If oparg is 0 at the top of the stack are 1 or 6 values:
Either:
- TOP = NULL or an integer
or:
- (TOP, SECOND, THIRD) = exc_info()
- (FOURTH, FITH, SIXTH) = previous exception for EXCEPT_HANDLER
If oparg is 1 the value for 'return' was additionally pushed
at the top of the stack.
*/
PyObject *res = NULL;
if (oparg) {
res = POP();
}
case TARGET(RERAISE): {
PyObject *exc = POP();
if (exc == NULL || PyLong_CheckExact(exc)) {
Py_XDECREF(exc);
}
else {
Py_DECREF(exc);
Py_DECREF(POP());
Py_DECREF(POP());
PyObject *type, *value, *traceback;
_PyErr_StackItem *exc_info;
PyTryBlock *b = PyFrame_BlockPop(f);
if (b->b_type != EXCEPT_HANDLER) {
_PyErr_SetString(tstate, PyExc_SystemError,
"popped block is not an except handler");
Py_XDECREF(res);
goto error;
}
assert(STACK_LEVEL() == (b)->b_level + 3);
exc_info = tstate->exc_info;
type = exc_info->exc_type;
value = exc_info->exc_value;
traceback = exc_info->exc_traceback;
exc_info->exc_type = POP();
exc_info->exc_value = POP();
exc_info->exc_traceback = POP();
Py_XDECREF(type);
Py_XDECREF(value);
Py_XDECREF(traceback);
}
if (oparg) {
PUSH(res);
}
DISPATCH();
}
case TARGET(CALL_FINALLY): {
PyObject *ret = PyLong_FromLong(INSTR_OFFSET());
if (ret == NULL) {
goto error;
}
PUSH(ret);
JUMPBY(oparg);
FAST_DISPATCH();
}
case TARGET(BEGIN_FINALLY): {
/* Push NULL onto the stack for using it in END_FINALLY,
POP_FINALLY, WITH_CLEANUP_START and WITH_CLEANUP_FINISH.
*/
PUSH(NULL);
FAST_DISPATCH();
}
case TARGET(END_FINALLY): {
PREDICTED(END_FINALLY);
/* At the top of the stack are 1 or 6 values:
Either:
- TOP = NULL or an integer
or:
- (TOP, SECOND, THIRD) = exc_info()
- (FOURTH, FITH, SIXTH) = previous exception for EXCEPT_HANDLER
*/
PyObject *exc = POP();
if (exc == NULL) {
FAST_DISPATCH();
}
else if (PyLong_CheckExact(exc)) {
int ret = _PyLong_AsInt(exc);
Py_DECREF(exc);
if (ret == -1 && _PyErr_Occurred(tstate)) {
goto error;
}
JUMPTO(ret);
FAST_DISPATCH();
}
else {
assert(PyExceptionClass_Check(exc));
PyObject *val = POP();
PyObject *tb = POP();
_PyErr_Restore(tstate, exc, val, tb);
goto exception_unwind;
}
PyObject *val = POP();
PyObject *tb = POP();
assert(PyExceptionClass_Check(exc));
PyErr_Restore(exc, val, tb);
goto exception_unwind;
}
case TARGET(END_ASYNC_FOR): {
@ -3302,111 +3216,31 @@ main_loop:
DISPATCH();
}
case TARGET(WITH_CLEANUP_START): {
/* At the top of the stack are 1 or 6 values indicating
how/why we entered the finally clause:
- TOP = NULL
case TARGET(WITH_EXCEPT_START): {
/* At the top of the stack are 7 values:
- (TOP, SECOND, THIRD) = exc_info()
(FOURTH, FITH, SIXTH) = previous exception for EXCEPT_HANDLER
Below them is EXIT, the context.__exit__ or context.__aexit__
bound method.
In the first case, we must call
EXIT(None, None, None)
otherwise we must call
EXIT(TOP, SECOND, THIRD)
In the first case, we remove EXIT from the
stack, leaving TOP, and push TOP on the stack.
Otherwise we shift the bottom 3 values of the
stack down, replace the empty spot with NULL, and push
None on the stack.
Finally we push the result of the call.
- (FOURTH, FIFTH, SIXTH) = previous exception for EXCEPT_HANDLER
- SEVENTH: the context.__exit__ bound method
We call SEVENTH(TOP, SECOND, THIRD).
Then we push again the TOP exception and the __exit__
return value.
*/
PyObject *exit_func;
PyObject *exc, *val, *tb, *res;
val = tb = Py_None;
exc = TOP();
if (exc == NULL) {
STACK_SHRINK(1);
exit_func = TOP();
SET_TOP(exc);
exc = Py_None;
}
else {
assert(PyExceptionClass_Check(exc));
PyObject *tp2, *exc2, *tb2;
PyTryBlock *block;
val = SECOND();
tb = THIRD();
tp2 = FOURTH();
exc2 = PEEK(5);
tb2 = PEEK(6);
exit_func = PEEK(7);
SET_VALUE(7, tb2);
SET_VALUE(6, exc2);
SET_VALUE(5, tp2);
/* UNWIND_EXCEPT_HANDLER will pop this off. */
SET_FOURTH(NULL);
/* We just shifted the stack down, so we have
to tell the except handler block that the
values are lower than it expects. */
assert(f->f_iblock > 0);
block = &f->f_blockstack[f->f_iblock - 1];
assert(block->b_type == EXCEPT_HANDLER);
assert(block->b_level > 0);
block->b_level--;
}
val = SECOND();
tb = THIRD();
assert(exc != Py_None);
assert(!PyLong_Check(exc));
exit_func = PEEK(7);
PyObject *stack[4] = {NULL, exc, val, tb};
res = _PyObject_Vectorcall(exit_func, stack + 1,
3 | PY_VECTORCALL_ARGUMENTS_OFFSET, NULL);
Py_DECREF(exit_func);
if (res == NULL)
goto error;
Py_INCREF(exc); /* Duplicating the exception on the stack */
PUSH(exc);
PUSH(res);
PREDICT(WITH_CLEANUP_FINISH);
DISPATCH();
}
case TARGET(WITH_CLEANUP_FINISH): {
PREDICTED(WITH_CLEANUP_FINISH);
/* TOP = the result of calling the context.__exit__ bound method
SECOND = either None or exception type
If SECOND is None below is NULL or the return address,
otherwise below are 7 values representing an exception.
*/
PyObject *res = POP();
PyObject *exc = POP();
int err;
if (exc != Py_None)
err = PyObject_IsTrue(res);
else
err = 0;
Py_DECREF(res);
Py_DECREF(exc);
if (err < 0)
goto error;
else if (err > 0) {
/* There was an exception and a True return.
* We must manually unwind the EXCEPT_HANDLER block
* which was created when the exception was caught,
* otherwise the stack will be in an inconsistent state.
*/
PyTryBlock *b = PyFrame_BlockPop(f);
assert(b->b_type == EXCEPT_HANDLER);
UNWIND_EXCEPT_HANDLER(b);
PUSH(NULL);
}
PREDICT(END_FINALLY);
DISPATCH();
}
@ -3776,6 +3610,10 @@ exception_unwind:
PUSH(val);
PUSH(exc);
JUMPTO(handler);
if (_Py_TracingPossible(ceval)) {
/* Make sure that we trace line after exception */
instr_prev = INT_MAX;
}
/* Resume normal execution */
goto main_loop;
}
@ -5477,7 +5315,7 @@ format_exc_unbound(PyThreadState *tstate, PyCodeObject *co, int oparg)
}
static void
format_awaitable_error(PyThreadState *tstate, PyTypeObject *type, int prevopcode)
format_awaitable_error(PyThreadState *tstate, PyTypeObject *type, int prevprevopcode, int prevopcode)
{
if (type->tp_as_async == NULL || type->tp_as_async->am_await == NULL) {
if (prevopcode == BEFORE_ASYNC_WITH) {
@ -5486,7 +5324,7 @@ format_awaitable_error(PyThreadState *tstate, PyTypeObject *type, int prevopcode
"that does not implement __await__: %.100s",
type->tp_name);
}
else if (prevopcode == WITH_CLEANUP_START) {
else if (prevopcode == WITH_EXCEPT_START || (prevopcode == CALL_FUNCTION && prevprevopcode == DUP_TOP)) {
_PyErr_Format(tstate, PyExc_TypeError,
"'async with' received an object from __aexit__ "
"that does not implement __await__: %.100s",

View file

@ -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) {
@ -1687,22 +1679,30 @@ compiler_unwind_fblock(struct compiler *c, struct fblockinfo *info,
return 1;
case FINALLY_TRY:
ADDOP(c, POP_BLOCK);
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);
if (!compiler_push_fblock(c, POP_VALUE, NULL, NULL, NULL)) {
return 0;
}
}
else {
ADDOP_JREL(c, CALL_FINALLY, info->fb_exit);
ADDOP(c, POP_TOP);
VISIT_SEQ(c, stmt, info->fb_datum);
if (preserve_tos) {
compiler_pop_fblock(c, POP_VALUE, NULL);
}
return 1;
case FINALLY_END:
if (preserve_tos) {
ADDOP(c, ROT_FOUR);
}
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:
case ASYNC_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;
}
return compiler_error(c, "'break' outside loop");
if (loop == NULL) {
return compiler_error(c, "'break' outside loop");
}
if (!compiler_unwind_fblock(c, loop, 0)) {
return 0;
}
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;

2165
Python/importlib.h generated

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -47,12 +47,12 @@ static void *opcode_targets[256] = {
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&TARGET_RERAISE,
&&TARGET_WITH_EXCEPT_START,
&&TARGET_GET_AITER,
&&TARGET_GET_ANEXT,
&&TARGET_BEFORE_ASYNC_WITH,
&&TARGET_BEGIN_FINALLY,
&&_unknown_opcode,
&&TARGET_END_ASYNC_FOR,
&&TARGET_INPLACE_ADD,
&&TARGET_INPLACE_SUBTRACT,
@ -80,14 +80,14 @@ static void *opcode_targets[256] = {
&&TARGET_INPLACE_XOR,
&&TARGET_INPLACE_OR,
&&_unknown_opcode,
&&TARGET_WITH_CLEANUP_START,
&&TARGET_WITH_CLEANUP_FINISH,
&&_unknown_opcode,
&&_unknown_opcode,
&&TARGET_RETURN_VALUE,
&&TARGET_IMPORT_STAR,
&&TARGET_SETUP_ANNOTATIONS,
&&TARGET_YIELD_VALUE,
&&TARGET_POP_BLOCK,
&&TARGET_END_FINALLY,
&&_unknown_opcode,
&&TARGET_POP_EXCEPT,
&&TARGET_STORE_NAME,
&&TARGET_DELETE_NAME,
@ -161,8 +161,8 @@ static void *opcode_targets[256] = {
&&_unknown_opcode,
&&TARGET_LOAD_METHOD,
&&TARGET_CALL_METHOD,
&&TARGET_CALL_FINALLY,
&&TARGET_POP_FINALLY,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,
&&_unknown_opcode,

View file

@ -198,7 +198,6 @@ markblocks(_Py_CODEUNIT *code, Py_ssize_t len)
case SETUP_FINALLY:
case SETUP_WITH:
case SETUP_ASYNC_WITH:
case CALL_FINALLY:
j = GETJUMPTGT(code, i);
assert(j < len);
blocks[j] = 1;
@ -432,14 +431,10 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
/* Remove unreachable ops after RETURN */
case RETURN_VALUE:
h = i + 1;
/* END_FINALLY should be kept since it denotes the end of
the 'finally' block in frame_setlineno() in frameobject.c.
SETUP_FINALLY should be kept for balancing.
*/
while (h < codelen && ISBASICBLOCK(blocks, i, h) &&
_Py_OPCODE(codestr[h]) != END_FINALLY)
while (h < codelen && ISBASICBLOCK(blocks, i, h))
{
if (_Py_OPCODE(codestr[h]) == SETUP_FINALLY) {
/* Leave SETUP_FINALLY and RERAISE in place to help find block limits. */
if (_Py_OPCODE(codestr[h]) == SETUP_FINALLY || _Py_OPCODE(codestr[h]) == RERAISE) {
while (h > i + 1 &&
_Py_OPCODE(codestr[h - 1]) == EXTENDED_ARG)
{
@ -506,7 +501,6 @@ PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
case SETUP_FINALLY:
case SETUP_WITH:
case SETUP_ASYNC_WITH:
case CALL_FINALLY:
j = blocks[j / sizeof(_Py_CODEUNIT) + i + 1] - blocks[i] - 1;
j *= sizeof(_Py_CODEUNIT);
break;