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.
This commit is contained in:
Mark Shannon 2020-07-17 11:44:23 +01:00 committed by GitHub
parent 8e836bb21c
commit cb9879b948
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 155 additions and 95 deletions

View File

@ -4,6 +4,21 @@
# error "this header file must not be included directly" # error "this header file must not be included directly"
#endif #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 { typedef struct {
int b_type; /* what kind of block this is */ int b_type; /* what kind of block this is */
int b_handler; /* where to jump to find handler */ int b_handler; /* where to jump to find handler */
@ -18,11 +33,8 @@ struct _frame {
PyObject *f_globals; /* global symbol table (PyDictObject) */ PyObject *f_globals; /* global symbol table (PyDictObject) */
PyObject *f_locals; /* local symbol table (any mapping) */ PyObject *f_locals; /* local symbol table (any mapping) */
PyObject **f_valuestack; /* points after the last local */ 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 */ PyObject *f_trace; /* Trace function */
int f_stackdepth; /* Depth of value stack */
char f_trace_lines; /* Emit per-line trace events? */ char f_trace_lines; /* Emit per-line trace events? */
char f_trace_opcodes; /* Emit per-opcode trace events? */ char f_trace_opcodes; /* Emit per-opcode trace events? */
@ -37,11 +49,22 @@ struct _frame {
bytecode index. */ bytecode index. */
int f_lineno; /* Current line number */ int f_lineno; /* Current line number */
int f_iblock; /* index in f_blockstack */ 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 */ PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */
PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */ 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 */ /* Standard object interface */

View File

@ -16,8 +16,6 @@ extern "C" {
PyObject_HEAD \ PyObject_HEAD \
/* Note: gi_frame can be NULL if the generator is "finished" */ \ /* Note: gi_frame can be NULL if the generator is "finished" */ \
PyFrameObject *prefix##_frame; \ PyFrameObject *prefix##_frame; \
/* True if generator is being executed. */ \
char prefix##_running; \
/* The code object backing the generator */ \ /* The code object backing the generator */ \
PyObject *prefix##_code; \ PyObject *prefix##_code; \
/* List of weak reference. */ \ /* List of weak reference. */ \

View File

@ -881,7 +881,7 @@ And more, added later.
>>> i.gi_running = 42 >>> i.gi_running = 42
Traceback (most recent call last): Traceback (most recent call last):
... ...
AttributeError: readonly attribute AttributeError: attribute 'gi_running' of 'generator' objects is not writable
>>> def g(): >>> def g():
... yield me.gi_running ... yield me.gi_running
>>> me = g() >>> me = g()

View File

@ -1236,7 +1236,7 @@ class SizeofTest(unittest.TestCase):
nfrees = len(x.f_code.co_freevars) nfrees = len(x.f_code.co_freevars)
extras = x.f_code.co_stacksize + x.f_code.co_nlocals +\ extras = x.f_code.co_stacksize + x.f_code.co_nlocals +\
ncells + nfrees - 1 ncells + nfrees - 1
check(x, vsize('5P2c4P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P')) check(x, vsize('4Pi2c4P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P'))
# function # function
def func(): pass def func(): pass
check(func, size('13P')) check(func, size('13P'))
@ -1253,7 +1253,7 @@ class SizeofTest(unittest.TestCase):
check(bar, size('PP')) check(bar, size('PP'))
# generator # generator
def get_gen(): yield 1 def get_gen(): yield 1
check(get_gen(), size('Pb2PPP4P')) check(get_gen(), size('P2PPP4P'))
# iterator # iterator
check(iter('abc'), size('lP')) check(iter('abc'), size('lP'))
# callable-iterator # callable-iterator

View File

@ -938,6 +938,9 @@ class TestPEP380Operation(unittest.TestCase):
res.append(g1.throw(MyErr)) res.append(g1.throw(MyErr))
except StopIteration: except StopIteration:
pass pass
except:
self.assertEqual(res, [0, 1, 2, 3])
raise
# Check with close # Check with close
class MyIt(object): class MyIt(object):
def __iter__(self): def __iter__(self):

View File

@ -1847,7 +1847,7 @@ _is_running(PyInterpreterState *interp)
return 0; return 0;
} }
int executing = (int)(frame->f_executing); int executing = _PyFrame_IsExecuting(frame);
Py_DECREF(frame); Py_DECREF(frame);
return executing; return executing;

View File

@ -300,17 +300,20 @@ first_line_not_before(int *lines, int len, int line)
static void static void
frame_stack_pop(PyFrameObject *f) 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); Py_DECREF(v);
} }
static void static void
frame_block_unwind(PyFrameObject *f) frame_block_unwind(PyFrameObject *f)
{ {
assert(f->f_stackdepth >= 0);
assert(f->f_iblock > 0); assert(f->f_iblock > 0);
f->f_iblock--; f->f_iblock--;
PyTryBlock *b = &f->f_blockstack[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) { while (delta > 0) {
frame_stack_pop(f); frame_stack_pop(f);
delta--; delta--;
@ -352,33 +355,36 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
return -1; 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. * This code preserves the historical restrictions on
* Forbidding jumps from the 'call' event of a new frame is a side effect * setting the line number of a frame.
* of allowing to set f_lineno only from trace functions. */ * Jumps are forbidden on a 'return' trace event (except after a yield).
if (f->f_lasti == -1) { * Jumps from 'call' trace events are also forbidden.
PyErr_Format(PyExc_ValueError, * 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"); "can't jump from the 'call' trace event of a new frame");
return -1; return -1;
} case FRAME_RETURNED:
case FRAME_UNWINDING:
/* You can only do this from within a trace function, not via case FRAME_RAISED:
* _getframe or similar hackery. */ case FRAME_CLEARED:
if (!f->f_trace) { PyErr_SetString(PyExc_ValueError,
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,
"can only jump from a 'line' trace event"); "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; int new_lineno;
@ -585,11 +591,10 @@ frame_dealloc(PyFrameObject *f)
} }
/* Free stack */ /* Free stack */
if (f->f_stacktop != NULL) { for (int i = 0; i < f->f_stackdepth; i++) {
for (PyObject **p = valuestack; p < f->f_stacktop; p++) { Py_XDECREF(f->f_valuestack[i]);
Py_XDECREF(*p);
}
} }
f->f_stackdepth = 0;
Py_XDECREF(f->f_back); Py_XDECREF(f->f_back);
Py_DECREF(f->f_builtins); Py_DECREF(f->f_builtins);
@ -647,10 +652,8 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg)
} }
/* stack */ /* stack */
if (f->f_stacktop != NULL) { for (int i = 0; i < f->f_stackdepth; i++) {
for (PyObject **p = f->f_valuestack; p < f->f_stacktop; p++) { Py_VISIT(f->f_valuestack[i]);
Py_VISIT(*p);
}
} }
return 0; return 0;
} }
@ -663,9 +666,7 @@ frame_tp_clear(PyFrameObject *f)
* frame may also point to this frame, believe itself to still be * frame may also point to this frame, believe itself to still be
* active, and try cleaning up this frame again. * active, and try cleaning up this frame again.
*/ */
PyObject **oldtop = f->f_stacktop; f->f_state = FRAME_CLEARED;
f->f_stacktop = NULL;
f->f_executing = 0;
Py_CLEAR(f->f_trace); Py_CLEAR(f->f_trace);
@ -676,18 +677,17 @@ frame_tp_clear(PyFrameObject *f)
} }
/* stack */ /* stack */
if (oldtop != NULL) { for (int i = 0; i < f->f_stackdepth; i++) {
for (PyObject **p = f->f_valuestack; p < oldtop; p++) { Py_CLEAR(f->f_valuestack[i]);
Py_CLEAR(*p);
}
} }
f->f_stackdepth = 0;
return 0; return 0;
} }
static PyObject * static PyObject *
frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored))
{ {
if (f->f_executing) { if (_PyFrame_IsExecuting(f)) {
PyErr_SetString(PyExc_RuntimeError, PyErr_SetString(PyExc_RuntimeError,
"cannot clear an executing frame"); "cannot clear an executing frame");
return NULL; return NULL;
@ -898,7 +898,7 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
return NULL; return NULL;
} }
f->f_stacktop = f->f_valuestack; f->f_stackdepth = 0;
f->f_builtins = builtins; f->f_builtins = builtins;
Py_XINCREF(back); Py_XINCREF(back);
f->f_back = back; f->f_back = back;
@ -927,7 +927,7 @@ _PyFrame_New_NoTrack(PyThreadState *tstate, PyCodeObject *code,
f->f_lasti = -1; f->f_lasti = -1;
f->f_lineno = code->co_firstlineno; f->f_lineno = code->co_firstlineno;
f->f_iblock = 0; f->f_iblock = 0;
f->f_executing = 0; f->f_state = FRAME_CREATED;
f->f_gen = NULL; f->f_gen = NULL;
f->f_trace_opcodes = 0; f->f_trace_opcodes = 0;
f->f_trace_lines = 1; f->f_trace_lines = 1;

View File

@ -47,7 +47,7 @@ _PyGen_Finalize(PyObject *self)
PyObject *res = NULL; PyObject *res = NULL;
PyObject *error_type, *error_value, *error_traceback; 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 */ /* Generator isn't paused, so no need to close */
return; return;
} }
@ -143,7 +143,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
PyFrameObject *f = gen->gi_frame; PyFrameObject *f = gen->gi_frame;
PyObject *result; PyObject *result;
if (gen->gi_running) { if (f != NULL && _PyFrame_IsExecuting(f)) {
const char *msg = "generator already executing"; const char *msg = "generator already executing";
if (PyCoro_CheckExact(gen)) { if (PyCoro_CheckExact(gen)) {
msg = "coroutine already executing"; msg = "coroutine already executing";
@ -154,7 +154,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
PyErr_SetString(PyExc_ValueError, msg); PyErr_SetString(PyExc_ValueError, msg);
return NULL; return NULL;
} }
if (f == NULL || f->f_stacktop == NULL) { if (f == NULL || _PyFrameHasCompleted(f)) {
if (PyCoro_CheckExact(gen) && !closing) { if (PyCoro_CheckExact(gen) && !closing) {
/* `gen` is an exhausted coroutine: raise an error, /* `gen` is an exhausted coroutine: raise an error,
except when called from gen_close(), which should 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; return NULL;
} }
assert(_PyFrame_IsRunnable(f));
if (f->f_lasti == -1) { if (f->f_lasti == -1) {
if (arg && arg != Py_None) { if (arg && arg != Py_None) {
const char *msg = "can't send non-None value to a " 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 */ /* Push arg onto the frame's value stack */
result = arg ? arg : Py_None; result = arg ? arg : Py_None;
Py_INCREF(result); 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 /* 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); assert(f->f_back == NULL);
f->f_back = tstate->frame; f->f_back = tstate->frame;
gen->gi_running = 1;
gen->gi_exc_state.previous_item = tstate->exc_info; gen->gi_exc_state.previous_item = tstate->exc_info;
tstate->exc_info = &gen->gi_exc_state; 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); result = _PyEval_EvalFrame(tstate, f, exc);
tstate->exc_info = gen->gi_exc_state.previous_item; tstate->exc_info = gen->gi_exc_state.previous_item;
gen->gi_exc_state.previous_item = NULL; gen->gi_exc_state.previous_item = NULL;
gen->gi_running = 0;
/* Don't keep the reference to f_back any longer than necessary. It /* 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 * 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 /* If the generator just returned (as opposed to yielding), signal
* that the generator is exhausted. */ * that the generator is exhausted. */
if (result && f->f_stacktop == NULL) { if (result && _PyFrameHasCompleted(f)) {
if (result == Py_None) { if (result == Py_None) {
/* Delay exception instantiation if we can */ /* Delay exception instantiation if we can */
if (PyAsyncGen_CheckExact(gen)) { 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); _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 */ /* generator can't be rerun, so release the frame */
/* first clean reference cycle through stored exception traceback */ /* first clean reference cycle through stored exception traceback */
_PyErr_ClearExcState(&gen->gi_exc_state); _PyErr_ClearExcState(&gen->gi_exc_state);
@ -327,7 +327,7 @@ _PyGen_yf(PyGenObject *gen)
PyObject *yf = NULL; PyObject *yf = NULL;
PyFrameObject *f = gen->gi_frame; PyFrameObject *f = gen->gi_frame;
if (f && f->f_stacktop) { if (f) {
PyObject *bytecode = f->f_code->co_code; PyObject *bytecode = f->f_code->co_code;
unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode); 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) if (code[f->f_lasti + sizeof(_Py_CODEUNIT)] != YIELD_FROM)
return NULL; return NULL;
yf = f->f_stacktop[-1]; assert(f->f_stackdepth > 0);
yf = f->f_valuestack[f->f_stackdepth-1];
Py_INCREF(yf); Py_INCREF(yf);
} }
@ -356,9 +357,10 @@ gen_close(PyGenObject *gen, PyObject *args)
int err = 0; int err = 0;
if (yf) { 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); err = gen_close_iter(yf);
gen->gi_running = 0; gen->gi_frame->f_state = state;
Py_DECREF(yf); Py_DECREF(yf);
} }
if (err == 0) 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 We have to allow some awaits to work it through, hence the
`close_on_genexit` parameter here. `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); err = gen_close_iter(yf);
gen->gi_running = 0; gen->gi_frame->f_state = state;
Py_DECREF(yf); Py_DECREF(yf);
if (err < 0) if (err < 0)
return gen_send_ex(gen, Py_None, 1, 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(); PyThreadState *tstate = _PyThreadState_GET();
PyFrameObject *f = tstate->frame; PyFrameObject *f = tstate->frame;
gen->gi_running = 1;
/* Since we are fast-tracking things by skipping the eval loop, /* Since we are fast-tracking things by skipping the eval loop,
we need to update the current frame so the stack trace we need to update the current frame so the stack trace
will be reported correctly to the user. */ will be reported correctly to the user. */
@ -427,10 +429,12 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
tstate->frame = gen->gi_frame; tstate->frame = gen->gi_frame;
/* Close the generator that we are currently iterating with /* Close the generator that we are currently iterating with
'yield from' or awaiting on with 'await'. */ '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, ret = _gen_throw((PyGenObject *)yf, close_on_genexit,
typ, val, tb); typ, val, tb);
gen->gi_frame->f_state = state;
tstate->frame = f; tstate->frame = f;
gen->gi_running = 0;
} else { } else {
/* `yf` is an iterator or a coroutine-like object. */ /* `yf` is an iterator or a coroutine-like object. */
PyObject *meth; PyObject *meth;
@ -442,16 +446,19 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
Py_DECREF(yf); Py_DECREF(yf);
goto throw_here; 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); ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL);
gen->gi_running = 0; gen->gi_frame->f_state = state;
Py_DECREF(meth); Py_DECREF(meth);
} }
Py_DECREF(yf); Py_DECREF(yf);
if (!ret) { if (!ret) {
PyObject *val; PyObject *val;
/* Pop subiterator from stack */ /* 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); assert(ret == yf);
Py_DECREF(ret); Py_DECREF(ret);
/* Termination repetition of YIELD_FROM */ /* Termination repetition of YIELD_FROM */
@ -701,6 +708,16 @@ gen_getyieldfrom(PyGenObject *gen, void *Py_UNUSED(ignored))
return yf; 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[] = { static PyGetSetDef gen_getsetlist[] = {
{"__name__", (getter)gen_get_name, (setter)gen_set_name, {"__name__", (getter)gen_get_name, (setter)gen_set_name,
PyDoc_STR("name of the generator")}, PyDoc_STR("name of the generator")},
@ -708,12 +725,12 @@ static PyGetSetDef gen_getsetlist[] = {
PyDoc_STR("qualified name of the generator")}, PyDoc_STR("qualified name of the generator")},
{"gi_yieldfrom", (getter)gen_getyieldfrom, NULL, {"gi_yieldfrom", (getter)gen_getyieldfrom, NULL,
PyDoc_STR("object being iterated by yield from, or None")}, PyDoc_STR("object being iterated by yield from, or None")},
{"gi_running", (getter)gen_getrunning, NULL, NULL},
{NULL} /* Sentinel */ {NULL} /* Sentinel */
}; };
static PyMemberDef gen_memberlist[] = { static PyMemberDef gen_memberlist[] = {
{"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), READONLY}, {"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}, {"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY},
{NULL} /* Sentinel */ {NULL} /* Sentinel */
}; };
@ -791,7 +808,6 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f,
f->f_gen = (PyObject *) gen; f->f_gen = (PyObject *) gen;
Py_INCREF(f->f_code); Py_INCREF(f->f_code);
gen->gi_code = (PyObject *)(f->f_code); gen->gi_code = (PyObject *)(f->f_code);
gen->gi_running = 0;
gen->gi_weakreflist = NULL; gen->gi_weakreflist = NULL;
gen->gi_exc_state.exc_type = NULL; gen->gi_exc_state.exc_type = NULL;
gen->gi_exc_state.exc_value = NULL; gen->gi_exc_state.exc_value = NULL;
@ -921,6 +937,15 @@ coro_get_cr_await(PyCoroObject *coro, void *Py_UNUSED(ignored))
return yf; 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[] = { static PyGetSetDef coro_getsetlist[] = {
{"__name__", (getter)gen_get_name, (setter)gen_set_name, {"__name__", (getter)gen_get_name, (setter)gen_set_name,
PyDoc_STR("name of the coroutine")}, PyDoc_STR("name of the coroutine")},
@ -928,12 +953,12 @@ static PyGetSetDef coro_getsetlist[] = {
PyDoc_STR("qualified name of the coroutine")}, PyDoc_STR("qualified name of the coroutine")},
{"cr_await", (getter)coro_get_cr_await, NULL, {"cr_await", (getter)coro_get_cr_await, NULL,
PyDoc_STR("object being awaited on, or None")}, PyDoc_STR("object being awaited on, or None")},
{"cr_running", (getter)cr_getrunning, NULL, NULL},
{NULL} /* Sentinel */ {NULL} /* Sentinel */
}; };
static PyMemberDef coro_memberlist[] = { static PyMemberDef coro_memberlist[] = {
{"cr_frame", T_OBJECT, offsetof(PyCoroObject, cr_frame), READONLY}, {"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_code", T_OBJECT, offsetof(PyCoroObject, cr_code), READONLY},
{"cr_origin", T_OBJECT, offsetof(PyCoroObject, cr_origin), READONLY}, {"cr_origin", T_OBJECT, offsetof(PyCoroObject, cr_origin), READONLY},
{NULL} /* Sentinel */ {NULL} /* Sentinel */
@ -1828,7 +1853,7 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg)
return NULL; return NULL;
} }
if (f == NULL || f->f_stacktop == NULL) { if (f == NULL || _PyFrameHasCompleted(f)) {
o->agt_state = AWAITABLE_STATE_CLOSED; o->agt_state = AWAITABLE_STATE_CLOSED;
PyErr_SetNone(PyExc_StopIteration); PyErr_SetNone(PyExc_StopIteration);
return NULL; return NULL;

View File

@ -1349,10 +1349,15 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
assert(f->f_lasti % sizeof(_Py_CODEUNIT) == 0); assert(f->f_lasti % sizeof(_Py_CODEUNIT) == 0);
next_instr += f->f_lasti / sizeof(_Py_CODEUNIT) + 1; next_instr += f->f_lasti / sizeof(_Py_CODEUNIT) + 1;
} }
stack_pointer = f->f_stacktop; stack_pointer = f->f_valuestack + f->f_stackdepth;
assert(stack_pointer != NULL); /* Set f->f_stackdepth to -1.
f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */ * Update when returning or calling trace function.
f->f_executing = 1; 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) { if (co->co_opcache_flag < OPCACHE_MIN_RUNS) {
co->co_opcache_flag++; co->co_opcache_flag++;
@ -1440,7 +1445,7 @@ main_loop:
int err; int err;
/* see maybe_call_line_trace /* see maybe_call_line_trace
for expository comments */ for expository comments */
f->f_stacktop = stack_pointer; f->f_stackdepth = stack_pointer-f->f_valuestack;
err = maybe_call_line_trace(tstate->c_tracefunc, err = maybe_call_line_trace(tstate->c_tracefunc,
tstate->c_traceobj, tstate->c_traceobj,
@ -1448,10 +1453,8 @@ main_loop:
&instr_lb, &instr_ub, &instr_prev); &instr_lb, &instr_ub, &instr_prev);
/* Reload possibly changed frame fields */ /* Reload possibly changed frame fields */
JUMPTO(f->f_lasti); JUMPTO(f->f_lasti);
if (f->f_stacktop != NULL) { stack_pointer = f->f_valuestack+f->f_stackdepth;
stack_pointer = f->f_stacktop; f->f_stackdepth = -1;
f->f_stacktop = NULL;
}
if (err) if (err)
/* trace function raised an exception */ /* trace function raised an exception */
goto error; goto error;
@ -2076,6 +2079,8 @@ main_loop:
retval = POP(); retval = POP();
assert(f->f_iblock == 0); assert(f->f_iblock == 0);
assert(EMPTY()); assert(EMPTY());
f->f_state = FRAME_RETURNED;
f->f_stackdepth = 0;
goto exiting; goto exiting;
} }
@ -2242,10 +2247,11 @@ main_loop:
DISPATCH(); DISPATCH();
} }
/* receiver remains on stack, retval is value to be yielded */ /* receiver remains on stack, retval is value to be yielded */
f->f_stacktop = stack_pointer;
/* and repeat... */ /* and repeat... */
assert(f->f_lasti >= (int)sizeof(_Py_CODEUNIT)); assert(f->f_lasti >= (int)sizeof(_Py_CODEUNIT));
f->f_lasti -= sizeof(_Py_CODEUNIT); f->f_lasti -= sizeof(_Py_CODEUNIT);
f->f_state = FRAME_SUSPENDED;
f->f_stackdepth = stack_pointer-f->f_valuestack;
goto exiting; goto exiting;
} }
@ -2261,8 +2267,8 @@ main_loop:
} }
retval = w; retval = w;
} }
f->f_state = FRAME_SUSPENDED;
f->f_stacktop = stack_pointer; f->f_stackdepth = stack_pointer-f->f_valuestack;
goto exiting; goto exiting;
} }
@ -3762,11 +3768,15 @@ error:
/* Log traceback info. */ /* Log traceback info. */
PyTraceBack_Here(f); 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, call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj,
tstate, f); tstate, f);
}
exception_unwind: exception_unwind:
f->f_state = FRAME_UNWINDING;
/* Unwind stacks if an exception occurred */ /* Unwind stacks if an exception occurred */
while (f->f_iblock > 0) { while (f->f_iblock > 0) {
/* Pop the current block. */ /* Pop the current block. */
@ -3825,6 +3835,7 @@ exception_unwind:
} }
} }
/* Resume normal execution */ /* Resume normal execution */
f->f_state = FRAME_EXECUTING;
goto main_loop; goto main_loop;
} }
} /* unwind stack */ } /* unwind stack */
@ -3841,7 +3852,8 @@ exception_unwind:
PyObject *o = POP(); PyObject *o = POP();
Py_XDECREF(o); Py_XDECREF(o);
} }
f->f_stackdepth = 0;
f->f_state = FRAME_RAISED;
exiting: exiting:
if (tstate->use_tracing) { if (tstate->use_tracing) {
if (tstate->c_tracefunc) { if (tstate->c_tracefunc) {
@ -3863,7 +3875,6 @@ exit_eval_frame:
if (PyDTrace_FUNCTION_RETURN_ENABLED()) if (PyDTrace_FUNCTION_RETURN_ENABLED())
dtrace_function_return(f); dtrace_function_return(f);
_Py_LeaveRecursiveCall(tstate); _Py_LeaveRecursiveCall(tstate);
f->f_executing = 0;
tstate->frame = f->f_back; tstate->frame = f->f_back;
return _Py_CheckFunctionResult(tstate, NULL, retval, __func__); return _Py_CheckFunctionResult(tstate, NULL, retval, __func__);