mirror of
https://github.com/python/cpython.git
synced 2025-08-04 17:08:35 +00:00
bpo-44590: Lazily allocate frame objects (GH-27077)
* Convert "specials" array to InterpreterFrame struct, adding f_lasti, f_state and other non-debug FrameObject fields to it. * Refactor, calls pushing the call to the interpreter upward toward _PyEval_Vector. * Compute f_back when on thread stack, only filling in value when frame object outlives stack invocation. * Move ownership of InterpreterFrame in generator from frame object to generator object. * Do not create frame objects for Python calls. * Do not create frame objects for generators.
This commit is contained in:
parent
0363a4014d
commit
ae0a2b7562
27 changed files with 1037 additions and 619 deletions
|
@ -6,6 +6,7 @@
|
|||
#include "pycore_pyerrors.h" // _PyErr_ClearExcState()
|
||||
#include "pycore_pystate.h" // _PyThreadState_GET()
|
||||
#include "frameobject.h"
|
||||
#include "pycore_frame.h"
|
||||
#include "structmember.h" // PyMemberDef
|
||||
#include "opcode.h"
|
||||
|
||||
|
@ -31,10 +32,17 @@ exc_state_traverse(_PyErr_StackItem *exc_state, visitproc visit, void *arg)
|
|||
static int
|
||||
gen_traverse(PyGenObject *gen, visitproc visit, void *arg)
|
||||
{
|
||||
Py_VISIT((PyObject *)gen->gi_frame);
|
||||
Py_VISIT(gen->gi_code);
|
||||
Py_VISIT(gen->gi_name);
|
||||
Py_VISIT(gen->gi_qualname);
|
||||
InterpreterFrame *frame = gen->gi_xframe;
|
||||
if (frame != NULL) {
|
||||
assert(frame->frame_obj == NULL || frame->frame_obj->f_own_locals_memory == 0);
|
||||
int err = _PyFrame_Traverse(frame, visit, arg);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
/* No need to visit cr_origin, because it's just tuples/str/int, so can't
|
||||
participate in a reference cycle. */
|
||||
return exc_state_traverse(&gen->gi_exc_state, visit, arg);
|
||||
|
@ -47,7 +55,7 @@ _PyGen_Finalize(PyObject *self)
|
|||
PyObject *res = NULL;
|
||||
PyObject *error_type, *error_value, *error_traceback;
|
||||
|
||||
if (gen->gi_frame == NULL || _PyFrameHasCompleted(gen->gi_frame)) {
|
||||
if (gen->gi_xframe == NULL || _PyFrameHasCompleted(gen->gi_xframe)) {
|
||||
/* Generator isn't paused, so no need to close */
|
||||
return;
|
||||
}
|
||||
|
@ -79,7 +87,7 @@ _PyGen_Finalize(PyObject *self)
|
|||
issue a RuntimeWarning. */
|
||||
if (gen->gi_code != NULL &&
|
||||
((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE &&
|
||||
gen->gi_frame->f_lasti == -1)
|
||||
gen->gi_xframe->f_lasti == -1)
|
||||
{
|
||||
_PyErr_WarnUnawaitedCoroutine((PyObject *)gen);
|
||||
}
|
||||
|
@ -122,9 +130,11 @@ gen_dealloc(PyGenObject *gen)
|
|||
and GC_Del. */
|
||||
Py_CLEAR(((PyAsyncGenObject*)gen)->ag_finalizer);
|
||||
}
|
||||
if (gen->gi_frame != NULL) {
|
||||
gen->gi_frame->f_gen = NULL;
|
||||
Py_CLEAR(gen->gi_frame);
|
||||
InterpreterFrame *frame = gen->gi_xframe;
|
||||
if (frame != NULL) {
|
||||
gen->gi_xframe = NULL;
|
||||
frame->previous = NULL;
|
||||
_PyFrame_Clear(frame, 1);
|
||||
}
|
||||
if (((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE) {
|
||||
Py_CLEAR(((PyCoroObject *)gen)->cr_origin);
|
||||
|
@ -141,11 +151,11 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
|
|||
int exc, int closing)
|
||||
{
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
PyFrameObject *f = gen->gi_frame;
|
||||
InterpreterFrame *frame = gen->gi_xframe;
|
||||
PyObject *result;
|
||||
|
||||
*presult = NULL;
|
||||
if (f != NULL && _PyFrame_IsExecuting(f)) {
|
||||
if (frame != NULL && _PyFrame_IsExecuting(frame)) {
|
||||
const char *msg = "generator already executing";
|
||||
if (PyCoro_CheckExact(gen)) {
|
||||
msg = "coroutine already executing";
|
||||
|
@ -156,7 +166,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
|
|||
PyErr_SetString(PyExc_ValueError, msg);
|
||||
return PYGEN_ERROR;
|
||||
}
|
||||
if (f == NULL || _PyFrameHasCompleted(f)) {
|
||||
if (frame == NULL || _PyFrameHasCompleted(frame)) {
|
||||
if (PyCoro_CheckExact(gen) && !closing) {
|
||||
/* `gen` is an exhausted coroutine: raise an error,
|
||||
except when called from gen_close(), which should
|
||||
|
@ -175,19 +185,15 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
|
|||
return PYGEN_ERROR;
|
||||
}
|
||||
|
||||
assert(_PyFrame_IsRunnable(f));
|
||||
assert(f->f_lasti >= 0 || ((unsigned char *)PyBytes_AS_STRING(gen->gi_code->co_code))[0] == GEN_START);
|
||||
assert(_PyFrame_IsRunnable(frame));
|
||||
assert(frame->f_lasti >= 0 || ((unsigned char *)PyBytes_AS_STRING(gen->gi_code->co_code))[0] == GEN_START);
|
||||
/* Push arg onto the frame's value stack */
|
||||
result = arg ? arg : Py_None;
|
||||
Py_INCREF(result);
|
||||
gen->gi_frame->f_valuestack[gen->gi_frame->f_stackdepth] = result;
|
||||
gen->gi_frame->f_stackdepth++;
|
||||
frame->stack[frame->stackdepth] = result;
|
||||
frame->stackdepth++;
|
||||
|
||||
/* Generators always return to their most recent caller, not
|
||||
* necessarily their creator. */
|
||||
Py_XINCREF(tstate->frame);
|
||||
assert(f->f_back == NULL);
|
||||
f->f_back = tstate->frame;
|
||||
frame->previous = tstate->frame;
|
||||
|
||||
gen->gi_exc_state.previous_item = tstate->exc_info;
|
||||
tstate->exc_info = &gen->gi_exc_state;
|
||||
|
@ -197,20 +203,20 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
|
|||
_PyErr_ChainStackItem(NULL);
|
||||
}
|
||||
|
||||
result = _PyEval_EvalFrame(tstate, f, exc);
|
||||
result = _PyEval_EvalFrame(tstate, frame, exc);
|
||||
tstate->exc_info = gen->gi_exc_state.previous_item;
|
||||
gen->gi_exc_state.previous_item = NULL;
|
||||
|
||||
/* Don't keep the reference to f_back any longer than necessary. It
|
||||
assert(tstate->frame == frame->previous);
|
||||
/* Don't keep the reference to previous any longer than necessary. It
|
||||
* may keep a chain of frames alive or it could create a reference
|
||||
* cycle. */
|
||||
assert(f->f_back == tstate->frame);
|
||||
Py_CLEAR(f->f_back);
|
||||
frame->previous = NULL;
|
||||
|
||||
/* If the generator just returned (as opposed to yielding), signal
|
||||
* that the generator is exhausted. */
|
||||
if (result) {
|
||||
if (!_PyFrameHasCompleted(f)) {
|
||||
if (!_PyFrameHasCompleted(frame)) {
|
||||
*presult = result;
|
||||
return PYGEN_NEXT;
|
||||
}
|
||||
|
@ -245,10 +251,10 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
|
|||
/* generator can't be rerun, so release the frame */
|
||||
/* first clean reference cycle through stored exception traceback */
|
||||
_PyErr_ClearExcState(&gen->gi_exc_state);
|
||||
gen->gi_frame->f_gen = NULL;
|
||||
gen->gi_frame = NULL;
|
||||
Py_DECREF(f);
|
||||
|
||||
frame->generator = NULL;
|
||||
gen->gi_xframe = NULL;
|
||||
_PyFrame_Clear(frame, 1);
|
||||
*presult = result;
|
||||
return result ? PYGEN_RETURN : PYGEN_ERROR;
|
||||
}
|
||||
|
@ -328,13 +334,13 @@ PyObject *
|
|||
_PyGen_yf(PyGenObject *gen)
|
||||
{
|
||||
PyObject *yf = NULL;
|
||||
PyFrameObject *f = gen->gi_frame;
|
||||
|
||||
if (f) {
|
||||
if (gen->gi_xframe) {
|
||||
InterpreterFrame *frame = gen->gi_xframe;
|
||||
PyObject *bytecode = gen->gi_code->co_code;
|
||||
unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode);
|
||||
|
||||
if (f->f_lasti < 0) {
|
||||
if (frame->f_lasti < 0) {
|
||||
/* Return immediately if the frame didn't start yet. YIELD_FROM
|
||||
always come after LOAD_CONST: a code object should not start
|
||||
with YIELD_FROM */
|
||||
|
@ -342,10 +348,10 @@ _PyGen_yf(PyGenObject *gen)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (code[(f->f_lasti+1)*sizeof(_Py_CODEUNIT)] != YIELD_FROM)
|
||||
if (code[(frame->f_lasti+1)*sizeof(_Py_CODEUNIT)] != YIELD_FROM)
|
||||
return NULL;
|
||||
assert(f->f_stackdepth > 0);
|
||||
yf = f->f_valuestack[f->f_stackdepth-1];
|
||||
assert(frame->stackdepth > 0);
|
||||
yf = frame->stack[frame->stackdepth-1];
|
||||
Py_INCREF(yf);
|
||||
}
|
||||
|
||||
|
@ -360,10 +366,10 @@ gen_close(PyGenObject *gen, PyObject *args)
|
|||
int err = 0;
|
||||
|
||||
if (yf) {
|
||||
PyFrameState state = gen->gi_frame->f_state;
|
||||
gen->gi_frame->f_state = FRAME_EXECUTING;
|
||||
PyFrameState state = gen->gi_xframe->f_state;
|
||||
gen->gi_xframe->f_state = FRAME_EXECUTING;
|
||||
err = gen_close_iter(yf);
|
||||
gen->gi_frame->f_state = state;
|
||||
gen->gi_xframe->f_state = state;
|
||||
Py_DECREF(yf);
|
||||
}
|
||||
if (err == 0)
|
||||
|
@ -410,10 +416,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.
|
||||
*/
|
||||
PyFrameState state = gen->gi_frame->f_state;
|
||||
gen->gi_frame->f_state = FRAME_EXECUTING;
|
||||
PyFrameState state = gen->gi_xframe->f_state;
|
||||
gen->gi_xframe->f_state = FRAME_EXECUTING;
|
||||
err = gen_close_iter(yf);
|
||||
gen->gi_frame->f_state = state;
|
||||
gen->gi_xframe->f_state = state;
|
||||
Py_DECREF(yf);
|
||||
if (err < 0)
|
||||
return gen_send_ex(gen, Py_None, 1, 0);
|
||||
|
@ -422,22 +428,26 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
|
|||
if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) {
|
||||
/* `yf` is a generator or a coroutine. */
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
PyFrameObject *f = tstate->frame;
|
||||
InterpreterFrame *frame = gen->gi_xframe;
|
||||
|
||||
|
||||
/* 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. */
|
||||
/* XXX We should probably be updating the current frame
|
||||
somewhere in ceval.c. */
|
||||
tstate->frame = gen->gi_frame;
|
||||
InterpreterFrame *prev = tstate->frame;
|
||||
frame->previous = prev;
|
||||
tstate->frame = 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;
|
||||
PyFrameState state = gen->gi_xframe->f_state;
|
||||
gen->gi_xframe->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_xframe->f_state = state;
|
||||
tstate->frame = prev;
|
||||
frame->previous = NULL;
|
||||
} else {
|
||||
/* `yf` is an iterator or a coroutine-like object. */
|
||||
PyObject *meth;
|
||||
|
@ -449,24 +459,24 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
|
|||
Py_DECREF(yf);
|
||||
goto throw_here;
|
||||
}
|
||||
PyFrameState state = gen->gi_frame->f_state;
|
||||
gen->gi_frame->f_state = FRAME_EXECUTING;
|
||||
PyFrameState state = gen->gi_xframe->f_state;
|
||||
gen->gi_xframe->f_state = FRAME_EXECUTING;
|
||||
ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL);
|
||||
gen->gi_frame->f_state = state;
|
||||
gen->gi_xframe->f_state = state;
|
||||
Py_DECREF(meth);
|
||||
}
|
||||
Py_DECREF(yf);
|
||||
if (!ret) {
|
||||
PyObject *val;
|
||||
/* Pop subiterator from stack */
|
||||
assert(gen->gi_frame->f_stackdepth > 0);
|
||||
gen->gi_frame->f_stackdepth--;
|
||||
ret = gen->gi_frame->f_valuestack[gen->gi_frame->f_stackdepth];
|
||||
assert(gen->gi_xframe->stackdepth > 0);
|
||||
gen->gi_xframe->stackdepth--;
|
||||
ret = gen->gi_xframe->stack[gen->gi_xframe->stackdepth];
|
||||
assert(ret == yf);
|
||||
Py_DECREF(ret);
|
||||
/* Termination repetition of YIELD_FROM */
|
||||
assert(gen->gi_frame->f_lasti >= 0);
|
||||
gen->gi_frame->f_lasti += 1;
|
||||
assert(gen->gi_xframe->f_lasti >= 0);
|
||||
gen->gi_xframe->f_lasti += 1;
|
||||
if (_PyGen_FetchStopIterationValue(&val) == 0) {
|
||||
ret = gen_send(gen, val);
|
||||
Py_DECREF(val);
|
||||
|
@ -723,10 +733,28 @@ gen_getyieldfrom(PyGenObject *gen, void *Py_UNUSED(ignored))
|
|||
static PyObject *
|
||||
gen_getrunning(PyGenObject *gen, void *Py_UNUSED(ignored))
|
||||
{
|
||||
if (gen->gi_frame == NULL) {
|
||||
if (gen->gi_xframe == NULL) {
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
return PyBool_FromLong(_PyFrame_IsExecuting(gen->gi_frame));
|
||||
return PyBool_FromLong(_PyFrame_IsExecuting(gen->gi_xframe));
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
_gen_getframe(PyGenObject *gen, const char *const name)
|
||||
{
|
||||
if (PySys_Audit("object.__getattr__", "Os", gen, name) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (gen->gi_xframe == NULL) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
return _Py_XNewRef((PyObject *)_PyFrame_GetFrameObject(gen->gi_xframe));
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
gen_getframe(PyGenObject *gen, void *Py_UNUSED(ignored))
|
||||
{
|
||||
return _gen_getframe(gen, "gi_frame");
|
||||
}
|
||||
|
||||
static PyGetSetDef gen_getsetlist[] = {
|
||||
|
@ -737,11 +765,11 @@ static PyGetSetDef gen_getsetlist[] = {
|
|||
{"gi_yieldfrom", (getter)gen_getyieldfrom, NULL,
|
||||
PyDoc_STR("object being iterated by yield from, or None")},
|
||||
{"gi_running", (getter)gen_getrunning, NULL, NULL},
|
||||
{"gi_frame", (getter)gen_getframe, NULL, NULL},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
static PyMemberDef gen_memberlist[] = {
|
||||
{"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), READONLY|PY_AUDIT_READ},
|
||||
{"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY|PY_AUDIT_READ},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
@ -814,6 +842,84 @@ PyTypeObject PyGen_Type = {
|
|||
_PyGen_Finalize, /* tp_finalize */
|
||||
};
|
||||
|
||||
static PyObject *
|
||||
make_gen(PyTypeObject *type, PyFrameConstructor *con, InterpreterFrame *frame)
|
||||
{
|
||||
PyGenObject *gen = PyObject_GC_New(PyGenObject, type);
|
||||
if (gen == NULL) {
|
||||
assert(frame->frame_obj == NULL);
|
||||
_PyFrame_Clear(frame, 1);
|
||||
return NULL;
|
||||
}
|
||||
gen->gi_xframe = frame;
|
||||
frame->generator = (PyObject *)gen;
|
||||
gen->gi_code = frame->f_code;
|
||||
Py_INCREF(gen->gi_code);
|
||||
gen->gi_weakreflist = NULL;
|
||||
gen->gi_exc_state.exc_type = NULL;
|
||||
gen->gi_exc_state.exc_value = NULL;
|
||||
gen->gi_exc_state.exc_traceback = NULL;
|
||||
gen->gi_exc_state.previous_item = NULL;
|
||||
if (con->fc_name != NULL)
|
||||
gen->gi_name = con->fc_name;
|
||||
else
|
||||
gen->gi_name = gen->gi_code->co_name;
|
||||
Py_INCREF(gen->gi_name);
|
||||
if (con->fc_qualname != NULL)
|
||||
gen->gi_qualname = con->fc_qualname;
|
||||
else
|
||||
gen->gi_qualname = gen->gi_name;
|
||||
Py_INCREF(gen->gi_qualname);
|
||||
_PyObject_GC_TRACK(gen);
|
||||
return (PyObject *)gen;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
compute_cr_origin(int origin_depth);
|
||||
|
||||
PyObject *
|
||||
_Py_MakeCoro(PyFrameConstructor *con, InterpreterFrame *frame)
|
||||
{
|
||||
int coro_flags = ((PyCodeObject *)con->fc_code)->co_flags &
|
||||
(CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR);
|
||||
assert(coro_flags);
|
||||
if (coro_flags == CO_GENERATOR) {
|
||||
return make_gen(&PyGen_Type, con, frame);
|
||||
}
|
||||
if (coro_flags == CO_ASYNC_GENERATOR) {
|
||||
PyAsyncGenObject *o;
|
||||
o = (PyAsyncGenObject *)make_gen(&PyAsyncGen_Type, con, frame);
|
||||
if (o == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
o->ag_finalizer = NULL;
|
||||
o->ag_closed = 0;
|
||||
o->ag_hooks_inited = 0;
|
||||
o->ag_running_async = 0;
|
||||
return (PyObject*)o;
|
||||
}
|
||||
assert (coro_flags == CO_COROUTINE);
|
||||
PyObject *coro = make_gen(&PyCoro_Type, con, frame);
|
||||
if (!coro) {
|
||||
return NULL;
|
||||
}
|
||||
PyThreadState *tstate = _PyThreadState_GET();
|
||||
int origin_depth = tstate->coroutine_origin_tracking_depth;
|
||||
|
||||
if (origin_depth == 0) {
|
||||
((PyCoroObject *)coro)->cr_origin = NULL;
|
||||
} else {
|
||||
PyObject *cr_origin = compute_cr_origin(origin_depth);
|
||||
((PyCoroObject *)coro)->cr_origin = cr_origin;
|
||||
if (!cr_origin) {
|
||||
Py_DECREF(coro);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return coro;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f,
|
||||
PyObject *name, PyObject *qualname)
|
||||
|
@ -823,8 +929,16 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f,
|
|||
Py_DECREF(f);
|
||||
return NULL;
|
||||
}
|
||||
gen->gi_frame = f;
|
||||
f->f_gen = (PyObject *) gen;
|
||||
|
||||
/* Take ownership of the frame */
|
||||
assert(f->f_frame->frame_obj == NULL);
|
||||
assert(f->f_own_locals_memory);
|
||||
gen->gi_xframe = f->f_frame;
|
||||
gen->gi_xframe->frame_obj = f;
|
||||
f->f_own_locals_memory = 0;
|
||||
gen->gi_xframe->generator = (PyObject *) gen;
|
||||
assert(PyObject_GC_IsTracked((PyObject *)f));
|
||||
|
||||
gen->gi_code = PyFrame_GetCode(f);
|
||||
gen->gi_weakreflist = NULL;
|
||||
gen->gi_exc_state.exc_type = NULL;
|
||||
|
@ -958,12 +1072,19 @@ coro_get_cr_await(PyCoroObject *coro, void *Py_UNUSED(ignored))
|
|||
static PyObject *
|
||||
cr_getrunning(PyCoroObject *coro, void *Py_UNUSED(ignored))
|
||||
{
|
||||
if (coro->cr_frame == NULL) {
|
||||
if (coro->cr_xframe == NULL) {
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
return PyBool_FromLong(_PyFrame_IsExecuting(coro->cr_frame));
|
||||
return PyBool_FromLong(_PyFrame_IsExecuting(coro->cr_xframe));
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
cr_getframe(PyCoroObject *coro, void *Py_UNUSED(ignored))
|
||||
{
|
||||
return _gen_getframe((PyGenObject *)coro, "cr_frame");
|
||||
}
|
||||
|
||||
|
||||
static PyGetSetDef coro_getsetlist[] = {
|
||||
{"__name__", (getter)gen_get_name, (setter)gen_set_name,
|
||||
PyDoc_STR("name of the coroutine")},
|
||||
|
@ -972,11 +1093,11 @@ static PyGetSetDef coro_getsetlist[] = {
|
|||
{"cr_await", (getter)coro_get_cr_await, NULL,
|
||||
PyDoc_STR("object being awaited on, or None")},
|
||||
{"cr_running", (getter)cr_getrunning, NULL, NULL},
|
||||
{"cr_frame", (getter)cr_getframe, NULL, NULL},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
static PyMemberDef coro_memberlist[] = {
|
||||
{"cr_frame", T_OBJECT, offsetof(PyCoroObject, cr_frame), READONLY|PY_AUDIT_READ},
|
||||
{"cr_code", T_OBJECT, offsetof(PyCoroObject, cr_code), READONLY|PY_AUDIT_READ},
|
||||
{"cr_origin", T_OBJECT, offsetof(PyCoroObject, cr_origin), READONLY},
|
||||
{NULL} /* Sentinel */
|
||||
|
@ -1150,11 +1271,11 @@ PyTypeObject _PyCoroWrapper_Type = {
|
|||
static PyObject *
|
||||
compute_cr_origin(int origin_depth)
|
||||
{
|
||||
PyFrameObject *frame = PyEval_GetFrame();
|
||||
InterpreterFrame *frame = _PyEval_GetFrame();
|
||||
/* First count how many frames we have */
|
||||
int frame_count = 0;
|
||||
for (; frame && frame_count < origin_depth; ++frame_count) {
|
||||
frame = frame->f_back;
|
||||
frame = frame->previous;
|
||||
}
|
||||
|
||||
/* Now collect them */
|
||||
|
@ -1162,20 +1283,19 @@ compute_cr_origin(int origin_depth)
|
|||
if (cr_origin == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
frame = PyEval_GetFrame();
|
||||
frame = _PyEval_GetFrame();
|
||||
for (int i = 0; i < frame_count; ++i) {
|
||||
PyCodeObject *code = PyFrame_GetCode(frame);
|
||||
PyCodeObject *code = frame->f_code;
|
||||
PyObject *frameinfo = Py_BuildValue("OiO",
|
||||
code->co_filename,
|
||||
PyFrame_GetLineNumber(frame),
|
||||
PyCode_Addr2Line(frame->f_code, frame->f_lasti*2),
|
||||
code->co_name);
|
||||
Py_DECREF(code);
|
||||
if (!frameinfo) {
|
||||
Py_DECREF(cr_origin);
|
||||
return NULL;
|
||||
}
|
||||
PyTuple_SET_ITEM(cr_origin, i, frameinfo);
|
||||
frame = frame->f_back;
|
||||
frame = frame->previous;
|
||||
}
|
||||
|
||||
return cr_origin;
|
||||
|
@ -1346,6 +1466,11 @@ async_gen_athrow(PyAsyncGenObject *o, PyObject *args)
|
|||
return async_gen_athrow_new(o, args);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
ag_getframe(PyAsyncGenObject *ag, void *Py_UNUSED(ignored))
|
||||
{
|
||||
return _gen_getframe((PyGenObject *)ag, "ag_frame");
|
||||
}
|
||||
|
||||
static PyGetSetDef async_gen_getsetlist[] = {
|
||||
{"__name__", (getter)gen_get_name, (setter)gen_set_name,
|
||||
|
@ -1354,11 +1479,11 @@ static PyGetSetDef async_gen_getsetlist[] = {
|
|||
PyDoc_STR("qualified name of the async generator")},
|
||||
{"ag_await", (getter)coro_get_cr_await, NULL,
|
||||
PyDoc_STR("object being awaited on, or None")},
|
||||
{"ag_frame", (getter)ag_getframe, NULL, NULL},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
static PyMemberDef async_gen_memberlist[] = {
|
||||
{"ag_frame", T_OBJECT, offsetof(PyAsyncGenObject, ag_frame), READONLY|PY_AUDIT_READ},
|
||||
{"ag_running", T_BOOL, offsetof(PyAsyncGenObject, ag_running_async),
|
||||
READONLY},
|
||||
{"ag_code", T_OBJECT, offsetof(PyAsyncGenObject, ag_code), READONLY|PY_AUDIT_READ},
|
||||
|
@ -1865,7 +1990,7 @@ static PyObject *
|
|||
async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg)
|
||||
{
|
||||
PyGenObject *gen = (PyGenObject*)o->agt_gen;
|
||||
PyFrameObject *f = gen->gi_frame;
|
||||
InterpreterFrame *frame = gen->gi_xframe;
|
||||
PyObject *retval;
|
||||
|
||||
if (o->agt_state == AWAITABLE_STATE_CLOSED) {
|
||||
|
@ -1875,7 +2000,7 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (f == NULL || _PyFrameHasCompleted(f)) {
|
||||
if (frame == NULL || _PyFrameHasCompleted(frame)) {
|
||||
o->agt_state = AWAITABLE_STATE_CLOSED;
|
||||
PyErr_SetNone(PyExc_StopIteration);
|
||||
return NULL;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue