summaryrefslogtreecommitdiffstats
path: root/Python/ceval.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/ceval.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/ceval.c')
-rw-r--r--Python/ceval.c222
1 files changed, 30 insertions, 192 deletions
diff --git a/Python/ceval.c b/Python/ceval.c
index ee13fd1..96ed329 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -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();
- }
- 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
- */
+ case TARGET(RERAISE): {
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",