From cb9879b948a19c9434316f8ab6aba9c4601a8173 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 17 Jul 2020 11:44:23 +0100 Subject: [PATCH] bpo-40941: Unify implicit and explicit state in the frame and generator objects into a single value. (GH-20803) * Merge gen and frame state variables into one. * Replace stack pointer with depth in PyFrameObject. Makes code easier to read and saves a word of memory. --- Include/cpython/frameobject.h | 33 +++++++++-- Include/genobject.h | 2 - Lib/test/test_generators.py | 2 +- Lib/test/test_sys.py | 4 +- Lib/test/test_yield_from.py | 3 + Modules/_xxsubinterpretersmodule.c | 2 +- Objects/frameobject.c | 90 +++++++++++++++--------------- Objects/genobject.c | 71 +++++++++++++++-------- Python/ceval.c | 43 ++++++++------ 9 files changed, 155 insertions(+), 95 deletions(-) diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h index c76fbe0616c..63240b5b6d5 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -4,6 +4,21 @@ # error "this header file must not be included directly" #endif +/* These values are chosen so that the inline functions below all + * compare f_state to zero. + */ +enum _framestate { + FRAME_CREATED = -2, + FRAME_SUSPENDED = -1, + FRAME_EXECUTING = 0, + FRAME_RETURNED = 1, + FRAME_UNWINDING = 2, + FRAME_RAISED = 3, + FRAME_CLEARED = 4 +}; + +typedef signed char PyFrameState; + typedef struct { int b_type; /* what kind of block this is */ int b_handler; /* where to jump to find handler */ @@ -18,11 +33,8 @@ struct _frame { PyObject *f_globals; /* global symbol table (PyDictObject) */ PyObject *f_locals; /* local symbol table (any mapping) */ PyObject **f_valuestack; /* points after the last local */ - /* Next free slot in f_valuestack. Frame creation sets to f_valuestack. - Frame evaluation usually NULLs it, but a frame that yields sets it - to the current stack top. */ - PyObject **f_stacktop; PyObject *f_trace; /* Trace function */ + int f_stackdepth; /* Depth of value stack */ char f_trace_lines; /* Emit per-line trace events? */ char f_trace_opcodes; /* Emit per-opcode trace events? */ @@ -37,11 +49,22 @@ struct _frame { bytecode index. */ int f_lineno; /* Current line number */ int f_iblock; /* index in f_blockstack */ - char f_executing; /* whether the frame is still executing */ + PyFrameState f_state; /* What state the frame is in */ PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */ PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */ }; +static inline int _PyFrame_IsRunnable(struct _frame *f) { + return f->f_state < FRAME_EXECUTING; +} + +static inline int _PyFrame_IsExecuting(struct _frame *f) { + return f->f_state == FRAME_EXECUTING; +} + +static inline int _PyFrameHasCompleted(struct _frame *f) { + return f->f_state > FRAME_EXECUTING; +} /* Standard object interface */ diff --git a/Include/genobject.h b/Include/genobject.h index 8ffd15646f0..a76dc92e811 100644 --- a/Include/genobject.h +++ b/Include/genobject.h @@ -16,8 +16,6 @@ extern "C" { PyObject_HEAD \ /* Note: gi_frame can be NULL if the generator is "finished" */ \ PyFrameObject *prefix##_frame; \ - /* True if generator is being executed. */ \ - char prefix##_running; \ /* The code object backing the generator */ \ PyObject *prefix##_code; \ /* List of weak reference. */ \ diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index 3bf15228086..a634ccd5173 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -881,7 +881,7 @@ And more, added later. >>> i.gi_running = 42 Traceback (most recent call last): ... -AttributeError: readonly attribute +AttributeError: attribute 'gi_running' of 'generator' objects is not writable >>> def g(): ... yield me.gi_running >>> me = g() diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index aaba6630ff4..7a5df46b3e3 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1236,7 +1236,7 @@ class SizeofTest(unittest.TestCase): nfrees = len(x.f_code.co_freevars) extras = x.f_code.co_stacksize + x.f_code.co_nlocals +\ ncells + nfrees - 1 - check(x, vsize('5P2c4P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P')) + check(x, vsize('4Pi2c4P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P')) # function def func(): pass check(func, size('13P')) @@ -1253,7 +1253,7 @@ class SizeofTest(unittest.TestCase): check(bar, size('PP')) # generator def get_gen(): yield 1 - check(get_gen(), size('Pb2PPP4P')) + check(get_gen(), size('P2PPP4P')) # iterator check(iter('abc'), size('lP')) # callable-iterator diff --git a/Lib/test/test_yield_from.py b/Lib/test/test_yield_from.py index 4735ef4bee3..d105d8c6eb5 100644 --- a/Lib/test/test_yield_from.py +++ b/Lib/test/test_yield_from.py @@ -938,6 +938,9 @@ class TestPEP380Operation(unittest.TestCase): res.append(g1.throw(MyErr)) except StopIteration: pass + except: + self.assertEqual(res, [0, 1, 2, 3]) + raise # Check with close class MyIt(object): def __iter__(self): diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 8a6fce9e0b4..8943ebf455d 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -1847,7 +1847,7 @@ _is_running(PyInterpreterState *interp) return 0; } - int executing = (int)(frame->f_executing); + int executing = _PyFrame_IsExecuting(frame); Py_DECREF(frame); return executing; diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 7c2bce36158..8838b807462 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -300,17 +300,20 @@ first_line_not_before(int *lines, int len, int line) static void frame_stack_pop(PyFrameObject *f) { - PyObject *v = (*--f->f_stacktop); + assert(f->f_stackdepth >= 0); + f->f_stackdepth--; + PyObject *v = f->f_valuestack[f->f_stackdepth]; Py_DECREF(v); } static void frame_block_unwind(PyFrameObject *f) { + assert(f->f_stackdepth >= 0); assert(f->f_iblock > 0); f->f_iblock--; PyTryBlock *b = &f->f_blockstack[f->f_iblock]; - intptr_t delta = (f->f_stacktop - f->f_valuestack) - b->b_level; + intptr_t delta = f->f_stackdepth - b->b_level; while (delta > 0) { frame_stack_pop(f); delta--; @@ -352,33 +355,36 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore return -1; } - /* Upon the 'call' trace event of a new frame, f->f_lasti is -1 and - * f->f_trace is NULL, check first on the first condition. - * Forbidding jumps from the 'call' event of a new frame is a side effect - * of allowing to set f_lineno only from trace functions. */ - if (f->f_lasti == -1) { - PyErr_Format(PyExc_ValueError, + /* + * This code preserves the historical restrictions on + * setting the line number of a frame. + * Jumps are forbidden on a 'return' trace event (except after a yield). + * Jumps from 'call' trace events are also forbidden. + * In addition, jumps are forbidden when not tracing, + * as this is a debugging feature. + */ + switch(f->f_state) { + case FRAME_CREATED: + PyErr_Format(PyExc_ValueError, "can't jump from the 'call' trace event of a new frame"); - return -1; - } - - /* You can only do this from within a trace function, not via - * _getframe or similar hackery. */ - if (!f->f_trace) { - PyErr_Format(PyExc_ValueError, - "f_lineno can only be set by a trace function"); - return -1; - } - - /* Forbid jumps upon a 'return' trace event (except after executing a - * YIELD_VALUE or YIELD_FROM opcode, f_stacktop is not NULL in that case) - * and upon an 'exception' trace event. - * Jumps from 'call' trace events have already been forbidden above for new - * frames, so this check does not change anything for 'call' events. */ - if (f->f_stacktop == NULL) { - PyErr_SetString(PyExc_ValueError, + return -1; + case FRAME_RETURNED: + case FRAME_UNWINDING: + case FRAME_RAISED: + case FRAME_CLEARED: + PyErr_SetString(PyExc_ValueError, "can only jump from a 'line' trace event"); - return -1; + return -1; + case FRAME_EXECUTING: + case FRAME_SUSPENDED: + /* You can only do this from within a trace function, not via + * _getframe or similar hackery. */ + if (!f->f_trace) { + PyErr_Format(PyExc_ValueError, + "f_lineno can only be set by a trace function"); + return -1; + } + break; } int new_lineno; @@ -585,11 +591,10 @@ frame_dealloc(PyFrameObject *f) } /* Free stack */ - if (f->f_stacktop != NULL) { - for (PyObject **p = valuestack; p < f->f_stacktop; p++) { - Py_XDECREF(*p); - } + for (int i = 0; i < f->f_stackdepth; i++) { + Py_XDECREF(f->f_valuestack[i]); } + f->f_stackdepth = 0; Py_XDECREF(f->f_back); Py_DECREF(f->f_builtins); @@ -647,10 +652,8 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg) } /* stack */ - if (f->f_stacktop != NULL) { - for (PyObject **p = f->f_valuestack; p < f->f_stacktop; p++) { - Py_VISIT(*p); - } + for (int i = 0; i < f->f_stackdepth; i++) { + Py_VISIT(f->f_valuestack[i]); } return 0; } @@ -663,9 +666,7 @@ frame_tp_clear(PyFrameObject *f) * frame may also point to this frame, believe itself to still be * active, and try cleaning up this frame again. */ - PyObject **oldtop = f->f_stacktop; - f->f_stacktop = NULL; - f->f_executing = 0; + f->f_state = FRAME_CLEARED; Py_CLEAR(f->f_trace); @@ -676,18 +677,17 @@ frame_tp_clear(PyFrameObject *f) } /* stack */ - if (oldtop != NULL) { - for (PyObject **p = f->f_valuestack; p < oldtop; p++) { - Py_CLEAR(*p); - } + for (int i = 0; i < f->f_stackdepth; i++) { + Py_CLEAR(f->f_valuestack[i]); } + f->f_stackdepth = 0; return 0; } static PyObject * frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) { - if (f->f_executing) { + if (_PyFrame_IsExecuting(f)) { PyErr_SetString(PyExc_RuntimeError, "cannot clear an executing frame"); return NULL; @@ -898,7 +898,7 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code, return NULL; } - f->f_stacktop = f->f_valuestack; + f->f_stackdepth = 0; f->f_builtins = builtins; Py_XINCREF(back); f->f_back = back; @@ -927,7 +927,7 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code, f->f_lasti = -1; f->f_lineno = code->co_firstlineno; f->f_iblock = 0; - f->f_executing = 0; + f->f_state = FRAME_CREATED; f->f_gen = NULL; f->f_trace_opcodes = 0; f->f_trace_lines = 1; diff --git a/Objects/genobject.c b/Objects/genobject.c index a379fa6088e..809838a4cd2 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -47,7 +47,7 @@ _PyGen_Finalize(PyObject *self) PyObject *res = NULL; PyObject *error_type, *error_value, *error_traceback; - if (gen->gi_frame == NULL || gen->gi_frame->f_stacktop == NULL) { + if (gen->gi_frame == NULL || _PyFrameHasCompleted(gen->gi_frame)) { /* Generator isn't paused, so no need to close */ return; } @@ -143,7 +143,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing) PyFrameObject *f = gen->gi_frame; PyObject *result; - if (gen->gi_running) { + if (f != NULL && _PyFrame_IsExecuting(f)) { const char *msg = "generator already executing"; if (PyCoro_CheckExact(gen)) { msg = "coroutine already executing"; @@ -154,7 +154,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing) PyErr_SetString(PyExc_ValueError, msg); return NULL; } - if (f == NULL || f->f_stacktop == NULL) { + if (f == NULL || _PyFrameHasCompleted(f)) { if (PyCoro_CheckExact(gen) && !closing) { /* `gen` is an exhausted coroutine: raise an error, except when called from gen_close(), which should @@ -176,6 +176,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing) return NULL; } + assert(_PyFrame_IsRunnable(f)); if (f->f_lasti == -1) { if (arg && arg != Py_None) { const char *msg = "can't send non-None value to a " @@ -194,7 +195,8 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing) /* Push arg onto the frame's value stack */ result = arg ? arg : Py_None; Py_INCREF(result); - *(f->f_stacktop++) = result; + gen->gi_frame->f_valuestack[gen->gi_frame->f_stackdepth] = result; + gen->gi_frame->f_stackdepth++; } /* Generators always return to their most recent caller, not @@ -203,7 +205,6 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing) assert(f->f_back == NULL); f->f_back = tstate->frame; - gen->gi_running = 1; gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; @@ -215,7 +216,6 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing) result = _PyEval_EvalFrame(tstate, f, exc); tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; - gen->gi_running = 0; /* Don't keep the reference to f_back any longer than necessary. It * may keep a chain of frames alive or it could create a reference @@ -225,7 +225,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing) /* If the generator just returned (as opposed to yielding), signal * that the generator is exhausted. */ - if (result && f->f_stacktop == NULL) { + if (result && _PyFrameHasCompleted(f)) { if (result == Py_None) { /* Delay exception instantiation if we can */ if (PyAsyncGen_CheckExact(gen)) { @@ -264,7 +264,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing) _PyErr_FormatFromCause(PyExc_RuntimeError, "%s", msg); } - if (!result || f->f_stacktop == NULL) { + if (!result || _PyFrameHasCompleted(f)) { /* generator can't be rerun, so release the frame */ /* first clean reference cycle through stored exception traceback */ _PyErr_ClearExcState(&gen->gi_exc_state); @@ -327,7 +327,7 @@ _PyGen_yf(PyGenObject *gen) PyObject *yf = NULL; PyFrameObject *f = gen->gi_frame; - if (f && f->f_stacktop) { + if (f) { PyObject *bytecode = f->f_code->co_code; unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode); @@ -341,7 +341,8 @@ _PyGen_yf(PyGenObject *gen) if (code[f->f_lasti + sizeof(_Py_CODEUNIT)] != YIELD_FROM) return NULL; - yf = f->f_stacktop[-1]; + assert(f->f_stackdepth > 0); + yf = f->f_valuestack[f->f_stackdepth-1]; Py_INCREF(yf); } @@ -356,9 +357,10 @@ gen_close(PyGenObject *gen, PyObject *args) int err = 0; if (yf) { - gen->gi_running = 1; + PyFrameState state = gen->gi_frame->f_state; + gen->gi_frame->f_state = FRAME_EXECUTING; err = gen_close_iter(yf); - gen->gi_running = 0; + gen->gi_frame->f_state = state; Py_DECREF(yf); } if (err == 0) @@ -405,9 +407,10 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, We have to allow some awaits to work it through, hence the `close_on_genexit` parameter here. */ - gen->gi_running = 1; + PyFrameState state = gen->gi_frame->f_state; + gen->gi_frame->f_state = FRAME_EXECUTING; err = gen_close_iter(yf); - gen->gi_running = 0; + gen->gi_frame->f_state = state; Py_DECREF(yf); if (err < 0) return gen_send_ex(gen, Py_None, 1, 0); @@ -418,7 +421,6 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, PyThreadState *tstate = _PyThreadState_GET(); PyFrameObject *f = tstate->frame; - gen->gi_running = 1; /* Since we are fast-tracking things by skipping the eval loop, we need to update the current frame so the stack trace will be reported correctly to the user. */ @@ -427,10 +429,12 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, tstate->frame = gen->gi_frame; /* Close the generator that we are currently iterating with 'yield from' or awaiting on with 'await'. */ + PyFrameState state = gen->gi_frame->f_state; + gen->gi_frame->f_state = FRAME_EXECUTING; ret = _gen_throw((PyGenObject *)yf, close_on_genexit, typ, val, tb); + gen->gi_frame->f_state = state; tstate->frame = f; - gen->gi_running = 0; } else { /* `yf` is an iterator or a coroutine-like object. */ PyObject *meth; @@ -442,16 +446,19 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, Py_DECREF(yf); goto throw_here; } - gen->gi_running = 1; + PyFrameState state = gen->gi_frame->f_state; + gen->gi_frame->f_state = FRAME_EXECUTING; ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL); - gen->gi_running = 0; + gen->gi_frame->f_state = state; Py_DECREF(meth); } Py_DECREF(yf); if (!ret) { PyObject *val; /* Pop subiterator from stack */ - ret = *(--gen->gi_frame->f_stacktop); + assert(gen->gi_frame->f_stackdepth > 0); + gen->gi_frame->f_stackdepth--; + ret = gen->gi_frame->f_valuestack[gen->gi_frame->f_stackdepth]; assert(ret == yf); Py_DECREF(ret); /* Termination repetition of YIELD_FROM */ @@ -701,6 +708,16 @@ gen_getyieldfrom(PyGenObject *gen, void *Py_UNUSED(ignored)) return yf; } + +static PyObject * +gen_getrunning(PyGenObject *gen, void *Py_UNUSED(ignored)) +{ + if (gen->gi_frame == NULL) { + Py_RETURN_FALSE; + } + return PyBool_FromLong(_PyFrame_IsExecuting(gen->gi_frame)); +} + static PyGetSetDef gen_getsetlist[] = { {"__name__", (getter)gen_get_name, (setter)gen_set_name, PyDoc_STR("name of the generator")}, @@ -708,12 +725,12 @@ static PyGetSetDef gen_getsetlist[] = { PyDoc_STR("qualified name of the generator")}, {"gi_yieldfrom", (getter)gen_getyieldfrom, NULL, PyDoc_STR("object being iterated by yield from, or None")}, + {"gi_running", (getter)gen_getrunning, NULL, NULL}, {NULL} /* Sentinel */ }; static PyMemberDef gen_memberlist[] = { {"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), READONLY}, - {"gi_running", T_BOOL, offsetof(PyGenObject, gi_running), READONLY}, {"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY}, {NULL} /* Sentinel */ }; @@ -791,7 +808,6 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f, f->f_gen = (PyObject *) gen; Py_INCREF(f->f_code); gen->gi_code = (PyObject *)(f->f_code); - gen->gi_running = 0; gen->gi_weakreflist = NULL; gen->gi_exc_state.exc_type = NULL; gen->gi_exc_state.exc_value = NULL; @@ -921,6 +937,15 @@ coro_get_cr_await(PyCoroObject *coro, void *Py_UNUSED(ignored)) return yf; } +static PyObject * +cr_getrunning(PyCoroObject *coro, void *Py_UNUSED(ignored)) +{ + if (coro->cr_frame == NULL) { + Py_RETURN_FALSE; + } + return PyBool_FromLong(_PyFrame_IsExecuting(coro->cr_frame)); +} + static PyGetSetDef coro_getsetlist[] = { {"__name__", (getter)gen_get_name, (setter)gen_set_name, PyDoc_STR("name of the coroutine")}, @@ -928,12 +953,12 @@ static PyGetSetDef coro_getsetlist[] = { PyDoc_STR("qualified name of the coroutine")}, {"cr_await", (getter)coro_get_cr_await, NULL, PyDoc_STR("object being awaited on, or None")}, + {"cr_running", (getter)cr_getrunning, NULL, NULL}, {NULL} /* Sentinel */ }; static PyMemberDef coro_memberlist[] = { {"cr_frame", T_OBJECT, offsetof(PyCoroObject, cr_frame), READONLY}, - {"cr_running", T_BOOL, offsetof(PyCoroObject, cr_running), READONLY}, {"cr_code", T_OBJECT, offsetof(PyCoroObject, cr_code), READONLY}, {"cr_origin", T_OBJECT, offsetof(PyCoroObject, cr_origin), READONLY}, {NULL} /* Sentinel */ @@ -1828,7 +1853,7 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg) return NULL; } - if (f == NULL || f->f_stacktop == NULL) { + if (f == NULL || _PyFrameHasCompleted(f)) { o->agt_state = AWAITABLE_STATE_CLOSED; PyErr_SetNone(PyExc_StopIteration); return NULL; diff --git a/Python/ceval.c b/Python/ceval.c index 0386929a5b2..f747faaebf0 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1349,10 +1349,15 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag) assert(f->f_lasti % sizeof(_Py_CODEUNIT) == 0); next_instr += f->f_lasti / sizeof(_Py_CODEUNIT) + 1; } - stack_pointer = f->f_stacktop; - assert(stack_pointer != NULL); - f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */ - f->f_executing = 1; + stack_pointer = f->f_valuestack + f->f_stackdepth; + /* Set f->f_stackdepth to -1. + * Update when returning or calling trace function. + Having f_stackdepth <= 0 ensures that invalid + values are not visible to the cycle GC. + We choose -1 rather than 0 to assist debugging. + */ + f->f_stackdepth = -1; + f->f_state = FRAME_EXECUTING; if (co->co_opcache_flag < OPCACHE_MIN_RUNS) { co->co_opcache_flag++; @@ -1440,7 +1445,7 @@ main_loop: int err; /* see maybe_call_line_trace for expository comments */ - f->f_stacktop = stack_pointer; + f->f_stackdepth = stack_pointer-f->f_valuestack; err = maybe_call_line_trace(tstate->c_tracefunc, tstate->c_traceobj, @@ -1448,10 +1453,8 @@ main_loop: &instr_lb, &instr_ub, &instr_prev); /* Reload possibly changed frame fields */ JUMPTO(f->f_lasti); - if (f->f_stacktop != NULL) { - stack_pointer = f->f_stacktop; - f->f_stacktop = NULL; - } + stack_pointer = f->f_valuestack+f->f_stackdepth; + f->f_stackdepth = -1; if (err) /* trace function raised an exception */ goto error; @@ -2076,6 +2079,8 @@ main_loop: retval = POP(); assert(f->f_iblock == 0); assert(EMPTY()); + f->f_state = FRAME_RETURNED; + f->f_stackdepth = 0; goto exiting; } @@ -2242,10 +2247,11 @@ main_loop: DISPATCH(); } /* receiver remains on stack, retval is value to be yielded */ - f->f_stacktop = stack_pointer; /* and repeat... */ assert(f->f_lasti >= (int)sizeof(_Py_CODEUNIT)); f->f_lasti -= sizeof(_Py_CODEUNIT); + f->f_state = FRAME_SUSPENDED; + f->f_stackdepth = stack_pointer-f->f_valuestack; goto exiting; } @@ -2261,8 +2267,8 @@ main_loop: } retval = w; } - - f->f_stacktop = stack_pointer; + f->f_state = FRAME_SUSPENDED; + f->f_stackdepth = stack_pointer-f->f_valuestack; goto exiting; } @@ -3762,11 +3768,15 @@ error: /* Log traceback info. */ PyTraceBack_Here(f); - if (tstate->c_tracefunc != NULL) + if (tstate->c_tracefunc != NULL) { + /* Make sure state is set to FRAME_EXECUTING for tracing */ + assert(f->f_state == FRAME_EXECUTING); + f->f_state = FRAME_UNWINDING; call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, f); - + } exception_unwind: + f->f_state = FRAME_UNWINDING; /* Unwind stacks if an exception occurred */ while (f->f_iblock > 0) { /* Pop the current block. */ @@ -3825,6 +3835,7 @@ exception_unwind: } } /* Resume normal execution */ + f->f_state = FRAME_EXECUTING; goto main_loop; } } /* unwind stack */ @@ -3841,7 +3852,8 @@ exception_unwind: PyObject *o = POP(); Py_XDECREF(o); } - + f->f_stackdepth = 0; + f->f_state = FRAME_RAISED; exiting: if (tstate->use_tracing) { if (tstate->c_tracefunc) { @@ -3863,7 +3875,6 @@ exit_eval_frame: if (PyDTrace_FUNCTION_RETURN_ENABLED()) dtrace_function_return(f); _Py_LeaveRecursiveCall(tstate); - f->f_executing = 0; tstate->frame = f->f_back; return _Py_CheckFunctionResult(tstate, NULL, retval, __func__);