]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-45963: Make space for the InterpreterFrame of a generator in that generator....
authorMark Shannon <mark@hotpy.org>
Mon, 6 Dec 2021 10:13:49 +0000 (10:13 +0000)
committerGitHub <noreply@github.com>
Mon, 6 Dec 2021 10:13:49 +0000 (10:13 +0000)
* Make generator, coroutine and async gen structs all the same size.

* Store interpreter frame in generator (and coroutine). Reduces the number of allocations neeeded for a generator from two to one.

Include/cpython/genobject.h
Include/internal/pycore_ceval.h
Include/internal/pycore_frame.h
Lib/test/test_sys.py
Objects/genobject.c
Python/ceval.c
Python/frame.c

index 8f87cf5fff7579b39a9d758e59c767a50867589a..ad2818e8816670eefe6d664df7f0deeddcb63419 100644 (file)
@@ -14,7 +14,6 @@ extern "C" {
 #define _PyGenObject_HEAD(prefix)                                           \
     PyObject_HEAD                                                           \
     /* Note: gi_frame can be NULL if the generator is "finished" */         \
-    struct _interpreter_frame *prefix##_xframe;                             \
     /* The code object backing the generator */                             \
     PyCodeObject *prefix##_code;                                            \
     /* List of weak reference. */                                           \
@@ -23,7 +22,14 @@ extern "C" {
     PyObject *prefix##_name;                                                \
     /* Qualified name of the generator. */                                  \
     PyObject *prefix##_qualname;                                            \
-    _PyErr_StackItem prefix##_exc_state;
+    _PyErr_StackItem prefix##_exc_state;                                    \
+    PyObject *prefix##_origin_or_finalizer;                                 \
+    char prefix##_hooks_inited;                                             \
+    char prefix##_closed;                                                   \
+    char prefix##_running_async;                                            \
+    /* The frame */                                                         \
+    char prefix##_frame_valid;                                              \
+    PyObject *prefix##_iframe[1];
 
 typedef struct {
     /* The gi_ prefix is intended to remind of generator-iterator. */
@@ -48,7 +54,6 @@ PyAPI_FUNC(void) _PyGen_Finalize(PyObject *self);
 
 typedef struct {
     _PyGenObject_HEAD(cr)
-    PyObject *cr_origin;
 } PyCoroObject;
 
 PyAPI_DATA(PyTypeObject) PyCoro_Type;
@@ -64,18 +69,6 @@ PyAPI_FUNC(PyObject *) PyCoro_New(PyFrameObject *,
 
 typedef struct {
     _PyGenObject_HEAD(ag)
-    PyObject *ag_finalizer;
-
-    /* Flag is set to 1 when hooks set up by sys.set_asyncgen_hooks
-       were called on the generator, to avoid calling them more
-       than once. */
-    int ag_hooks_inited;
-
-    /* Flag is set to 1 when aclose() is called for the first time, or
-       when a StopAsyncIteration exception is raised. */
-    int ag_closed;
-
-    int ag_running_async;
 } PyAsyncGenObject;
 
 PyAPI_DATA(PyTypeObject) PyAsyncGen_Type;
index 9987f2076cd595c17731b3488870d056dc5d4f16..26d5677b1283772e257c9c871b00265ed001261f 100644 (file)
@@ -113,7 +113,7 @@ static inline void _Py_LeaveRecursiveCall_inline(void)  {
 
 struct _interpreter_frame *_PyEval_GetFrame(void);
 
-PyObject *_Py_MakeCoro(PyFunctionObject *func, struct _interpreter_frame *);
+PyObject *_Py_MakeCoro(PyFunctionObject *func);
 
 #ifdef __cplusplus
 }
index b0e51a6ddc46116cd65965c9883a20232bac3499..f4f7ab942c1ac7b5ce522a4aece34364834cc992 100644 (file)
@@ -74,7 +74,7 @@ static inline void _PyFrame_StackPush(InterpreterFrame *f, PyObject *value) {
 
 #define FRAME_SPECIALS_SIZE ((sizeof(InterpreterFrame)-1)/sizeof(PyObject *))
 
-InterpreterFrame *_PyFrame_Copy(InterpreterFrame *frame);
+void _PyFrame_Copy(InterpreterFrame *src, InterpreterFrame *dest);
 
 static inline void
 _PyFrame_InitializeSpecials(
index 2b1ba2457f50d2b7cd9d3b5efb6d681fd9e8d263..81802893a8ee5abc145cea767918b25aa28b4bc6 100644 (file)
@@ -1340,7 +1340,7 @@ class SizeofTest(unittest.TestCase):
             check(bar, size('PP'))
         # generator
         def get_gen(): yield 1
-        check(get_gen(), size('P2PPP4P'))
+        check(get_gen(), size('P2PPP4P4c8P2iciP'))
         # iterator
         check(iter('abc'), size('lP'))
         # callable-iterator
index 04d98a2b4edf1b6024017fed820a670e23e54507..c4ba660530cae67352bf3874fc74423f2f2238ce 100644 (file)
@@ -36,8 +36,8 @@ gen_traverse(PyGenObject *gen, visitproc visit, void *arg)
     Py_VISIT(gen->gi_code);
     Py_VISIT(gen->gi_name);
     Py_VISIT(gen->gi_qualname);
-    InterpreterFrame *frame = gen->gi_xframe;
-    if (frame != NULL) {
+    if (gen->gi_frame_valid) {
+        InterpreterFrame *frame = (InterpreterFrame *)(gen->gi_iframe);
         assert(frame->frame_obj == NULL || frame->frame_obj->f_owns_frame == 0);
         int err = _PyFrame_Traverse(frame, visit, arg);
         if (err) {
@@ -56,14 +56,14 @@ _PyGen_Finalize(PyObject *self)
     PyObject *res = NULL;
     PyObject *error_type, *error_value, *error_traceback;
 
-    if (gen->gi_xframe == NULL ||  _PyFrameHasCompleted(gen->gi_xframe)) {
+    if (gen->gi_frame_valid == 0 || _PyFrameHasCompleted((InterpreterFrame *)gen->gi_iframe)) {
         /* Generator isn't paused, so no need to close */
         return;
     }
 
     if (PyAsyncGen_CheckExact(self)) {
         PyAsyncGenObject *agen = (PyAsyncGenObject*)self;
-        PyObject *finalizer = agen->ag_finalizer;
+        PyObject *finalizer = agen->ag_origin_or_finalizer;
         if (finalizer && !agen->ag_closed) {
             /* Save the current exception, if any. */
             PyErr_Fetch(&error_type, &error_value, &error_traceback);
@@ -88,7 +88,7 @@ _PyGen_Finalize(PyObject *self)
        issue a RuntimeWarning. */
     if (gen->gi_code != NULL &&
         ((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE &&
-        gen->gi_xframe->f_lasti == -1)
+        ((InterpreterFrame *)gen->gi_iframe)->f_lasti == -1)
     {
         _PyErr_WarnUnawaitedCoroutine((PyObject *)gen);
     }
@@ -129,18 +129,17 @@ gen_dealloc(PyGenObject *gen)
         /* We have to handle this case for asynchronous generators
            right here, because this code has to be between UNTRACK
            and GC_Del. */
-        Py_CLEAR(((PyAsyncGenObject*)gen)->ag_finalizer);
+        Py_CLEAR(((PyAsyncGenObject*)gen)->ag_origin_or_finalizer);
     }
-    InterpreterFrame *frame = gen->gi_xframe;
-    if (frame != NULL) {
-        gen->gi_xframe = NULL;
+    if (gen->gi_frame_valid) {
+        InterpreterFrame *frame = (InterpreterFrame *)gen->gi_iframe;
+        gen->gi_frame_valid = 0;
         frame->generator = NULL;
         frame->previous = NULL;
         _PyFrame_Clear(frame);
-        PyMem_Free(frame);
     }
     if (((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE) {
-        Py_CLEAR(((PyCoroObject *)gen)->cr_origin);
+        Py_CLEAR(((PyCoroObject *)gen)->cr_origin_or_finalizer);
     }
     Py_CLEAR(gen->gi_code);
     Py_CLEAR(gen->gi_name);
@@ -154,11 +153,11 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
              int exc, int closing)
 {
     PyThreadState *tstate = _PyThreadState_GET();
-    InterpreterFrame *frame = gen->gi_xframe;
+    InterpreterFrame *frame = (InterpreterFrame *)gen->gi_iframe;
     PyObject *result;
 
     *presult = NULL;
-    if (frame != NULL && _PyFrame_IsExecuting(frame)) {
+    if (gen->gi_frame_valid && _PyFrame_IsExecuting(frame)) {
         const char *msg = "generator already executing";
         if (PyCoro_CheckExact(gen)) {
             msg = "coroutine already executing";
@@ -169,7 +168,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
         PyErr_SetString(PyExc_ValueError, msg);
         return PYGEN_ERROR;
     }
-    if (frame == NULL || _PyFrameHasCompleted(frame)) {
+    if (gen->gi_frame_valid == 0 || _PyFrameHasCompleted(frame)) {
         if (PyCoro_CheckExact(gen) && !closing) {
             /* `gen` is an exhausted coroutine: raise an error,
                except when called from gen_close(), which should
@@ -188,6 +187,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
         return PYGEN_ERROR;
     }
 
+    assert(gen->gi_frame_valid);
     assert(_PyFrame_IsRunnable(frame));
     /* Push arg onto the frame's value stack */
     result = arg ? arg : Py_None;
@@ -254,9 +254,8 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
     _PyErr_ClearExcState(&gen->gi_exc_state);
 
     frame->generator = NULL;
-    gen->gi_xframe = NULL;
+    gen->gi_frame_valid = 0;
     _PyFrame_Clear(frame);
-    PyMem_Free(frame);
     *presult = result;
     return result ? PYGEN_RETURN : PYGEN_ERROR;
 }
@@ -337,8 +336,8 @@ _PyGen_yf(PyGenObject *gen)
 {
     PyObject *yf = NULL;
 
-    if (gen->gi_xframe) {
-        InterpreterFrame *frame = gen->gi_xframe;
+    if (gen->gi_frame_valid) {
+        InterpreterFrame *frame = (InterpreterFrame *)gen->gi_iframe;
         PyObject *bytecode = gen->gi_code->co_code;
         unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode);
 
@@ -367,10 +366,11 @@ gen_close(PyGenObject *gen, PyObject *args)
     int err = 0;
 
     if (yf) {
-        PyFrameState state = gen->gi_xframe->f_state;
-        gen->gi_xframe->f_state = FRAME_EXECUTING;
+        InterpreterFrame *frame = (InterpreterFrame *)gen->gi_iframe;
+        PyFrameState state = frame->f_state;
+        frame->f_state = FRAME_EXECUTING;
         err = gen_close_iter(yf);
-        gen->gi_xframe->f_state = state;
+        frame->f_state = state;
         Py_DECREF(yf);
     }
     if (err == 0)
@@ -408,6 +408,7 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
     _Py_IDENTIFIER(throw);
 
     if (yf) {
+        InterpreterFrame *frame = (InterpreterFrame *)gen->gi_iframe;
         PyObject *ret;
         int err;
         if (PyErr_GivenExceptionMatches(typ, PyExc_GeneratorExit) &&
@@ -417,10 +418,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_xframe->f_state;
-            gen->gi_xframe->f_state = FRAME_EXECUTING;
+            PyFrameState state = frame->f_state;
+            frame->f_state = FRAME_EXECUTING;
             err = gen_close_iter(yf);
-            gen->gi_xframe->f_state = state;
+            frame->f_state = state;
             Py_DECREF(yf);
             if (err < 0)
                 return gen_send_ex(gen, Py_None, 1, 0);
@@ -429,9 +430,6 @@ _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();
-            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. */
@@ -442,11 +440,11 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
             tstate->cframe->current_frame = frame;
             /* Close the generator that we are currently iterating with
                'yield from' or awaiting on with 'await'. */
-            PyFrameState state = gen->gi_xframe->f_state;
-            gen->gi_xframe->f_state = FRAME_EXECUTING;
+            PyFrameState state = frame->f_state;
+            frame->f_state = FRAME_EXECUTING;
             ret = _gen_throw((PyGenObject *)yf, close_on_genexit,
                              typ, val, tb);
-            gen->gi_xframe->f_state = state;
+            frame->f_state = state;
             tstate->cframe->current_frame = prev;
             frame->previous = NULL;
         } else {
@@ -460,22 +458,23 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
                 Py_DECREF(yf);
                 goto throw_here;
             }
-            PyFrameState state = gen->gi_xframe->f_state;
-            gen->gi_xframe->f_state = FRAME_EXECUTING;
+            PyFrameState state = frame->f_state;
+            frame->f_state = FRAME_EXECUTING;
             ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL);
-            gen->gi_xframe->f_state = state;
+            frame->f_state = state;
             Py_DECREF(meth);
         }
         Py_DECREF(yf);
         if (!ret) {
             PyObject *val;
             /* Pop subiterator from stack */
-            ret = _PyFrame_StackPop(gen->gi_xframe);
+            assert(gen->gi_frame_valid);
+            ret = _PyFrame_StackPop((InterpreterFrame *)gen->gi_iframe);
             assert(ret == yf);
             Py_DECREF(ret);
             /* Termination repetition of YIELD_FROM */
-            assert(gen->gi_xframe->f_lasti >= 0);
-            gen->gi_xframe->f_lasti += 1;
+            assert(frame->f_lasti >= 0);
+            frame->f_lasti += 1;
             if (_PyGen_FetchStopIterationValue(&val) == 0) {
                 ret = gen_send(gen, val);
                 Py_DECREF(val);
@@ -732,10 +731,10 @@ gen_getyieldfrom(PyGenObject *gen, void *Py_UNUSED(ignored))
 static PyObject *
 gen_getrunning(PyGenObject *gen, void *Py_UNUSED(ignored))
 {
-    if (gen->gi_xframe == NULL) {
+    if (gen->gi_frame_valid == 0) {
         Py_RETURN_FALSE;
     }
-    return PyBool_FromLong(_PyFrame_IsExecuting(gen->gi_xframe));
+    return PyBool_FromLong(_PyFrame_IsExecuting((InterpreterFrame *)gen->gi_iframe));
 }
 
 static PyObject *
@@ -744,10 +743,10 @@ _gen_getframe(PyGenObject *gen, const char *const name)
     if (PySys_Audit("object.__getattr__", "Os", gen, name) < 0) {
         return NULL;
     }
-    if (gen->gi_xframe == NULL) {
+    if (gen->gi_frame_valid == 0) {
         Py_RETURN_NONE;
     }
-    return _Py_XNewRef((PyObject *)_PyFrame_GetFrameObject(gen->gi_xframe));
+    return _Py_XNewRef((PyObject *)_PyFrame_GetFrameObject((InterpreterFrame *)gen->gi_iframe));
 }
 
 static PyObject *
@@ -773,10 +772,24 @@ static PyMemberDef gen_memberlist[] = {
     {NULL}      /* Sentinel */
 };
 
+static PyObject *
+gen_sizeof(PyGenObject *gen, PyObject *Py_UNUSED(ignored))
+{
+    Py_ssize_t res;
+    res = offsetof(PyGenObject, gi_iframe) + offsetof(InterpreterFrame, localsplus);
+    PyCodeObject *code = gen->gi_code;
+    res += (code->co_nlocalsplus+code->co_stacksize) * sizeof(PyObject *);
+    return PyLong_FromSsize_t(res);
+}
+
+PyDoc_STRVAR(sizeof__doc__,
+"gen.__sizeof__() -> size of gen in memory, in bytes");
+
 static PyMethodDef gen_methods[] = {
     {"send",(PyCFunction)gen_send, METH_O, send_doc},
     {"throw",(PyCFunction)gen_throw, METH_VARARGS, throw_doc},
     {"close",(PyCFunction)gen_close, METH_NOARGS, close_doc},
+    {"__sizeof__", (PyCFunction)gen_sizeof, METH_NOARGS, sizeof__doc__},
     {NULL, NULL}        /* Sentinel */
 };
 
@@ -791,8 +804,9 @@ static PyAsyncMethods gen_as_async = {
 PyTypeObject PyGen_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0)
     "generator",                                /* tp_name */
-    sizeof(PyGenObject),                        /* tp_basicsize */
-    0,                                          /* tp_itemsize */
+    offsetof(PyGenObject, gi_iframe) +
+    offsetof(InterpreterFrame, localsplus),       /* tp_basicsize */
+    sizeof(PyObject *),                         /* tp_itemsize */
     /* methods */
     (destructor)gen_dealloc,                    /* tp_dealloc */
     0,                                          /* tp_vectorcall_offset */
@@ -842,18 +856,16 @@ PyTypeObject PyGen_Type = {
 };
 
 static PyObject *
-make_gen(PyTypeObject *type, PyFunctionObject *func, InterpreterFrame *frame)
+make_gen(PyTypeObject *type, PyFunctionObject *func)
 {
-    PyGenObject *gen = PyObject_GC_New(PyGenObject, type);
+    PyCodeObject *code = (PyCodeObject *)func->func_code;
+    int slots = code->co_nlocalsplus + code->co_stacksize;
+    PyGenObject *gen = PyObject_GC_NewVar(PyGenObject, type, slots);
     if (gen == NULL) {
-        assert(frame->frame_obj == NULL);
-        _PyFrame_Clear(frame);
-        PyMem_Free(frame);
         return NULL;
     }
-    gen->gi_xframe = frame;
-    frame->generator = (PyObject *)gen;
-    gen->gi_code = frame->f_code;
+    gen->gi_frame_valid = 0;
+    gen->gi_code = (PyCodeObject *)func->func_code;
     Py_INCREF(gen->gi_code);
     gen->gi_weakreflist = NULL;
     gen->gi_exc_state.exc_type = NULL;
@@ -878,28 +890,28 @@ static PyObject *
 compute_cr_origin(int origin_depth);
 
 PyObject *
-_Py_MakeCoro(PyFunctionObject *func, InterpreterFrame *frame)
+_Py_MakeCoro(PyFunctionObject *func)
 {
     int coro_flags = ((PyCodeObject *)func->func_code)->co_flags &
         (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR);
     assert(coro_flags);
     if (coro_flags == CO_GENERATOR) {
-        return make_gen(&PyGen_Type, func, frame);
+        return make_gen(&PyGen_Type, func);
     }
     if (coro_flags == CO_ASYNC_GENERATOR) {
         PyAsyncGenObject *o;
-        o = (PyAsyncGenObject *)make_gen(&PyAsyncGen_Type, func, frame);
+        o = (PyAsyncGenObject *)make_gen(&PyAsyncGen_Type, func);
         if (o == NULL) {
             return NULL;
         }
-        o->ag_finalizer = NULL;
+        o->ag_origin_or_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, func, frame);
+    PyObject *coro = make_gen(&PyCoro_Type, func);
     if (!coro) {
         return NULL;
     }
@@ -907,16 +919,15 @@ _Py_MakeCoro(PyFunctionObject *func, InterpreterFrame *frame)
     int origin_depth = tstate->coroutine_origin_tracking_depth;
 
     if (origin_depth == 0) {
-        ((PyCoroObject *)coro)->cr_origin = NULL;
+        ((PyCoroObject *)coro)->cr_origin_or_finalizer = NULL;
     } else {
         PyObject *cr_origin = compute_cr_origin(origin_depth);
-        ((PyCoroObject *)coro)->cr_origin = cr_origin;
+        ((PyCoroObject *)coro)->cr_origin_or_finalizer = cr_origin;
         if (!cr_origin) {
             Py_DECREF(coro);
             return NULL;
         }
     }
-
     return coro;
 }
 
@@ -924,27 +935,27 @@ static PyObject *
 gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f,
                       PyObject *name, PyObject *qualname)
 {
-    PyGenObject *gen = PyObject_GC_New(PyGenObject, type);
+    PyCodeObject *code = f->f_frame->f_code;
+    int size = code->co_nlocalsplus + code->co_stacksize;
+    PyGenObject *gen = PyObject_GC_NewVar(PyGenObject, type, size);
     if (gen == NULL) {
         Py_DECREF(f);
         return NULL;
     }
-
-    /* Take ownership of the frame */
+    /* Copy the frame */
     assert(f->f_frame->frame_obj == NULL);
     assert(f->f_owns_frame);
-    gen->gi_xframe = _PyFrame_Copy((InterpreterFrame *)f->_f_frame_data);
-    if (gen->gi_xframe == NULL) {
-        Py_DECREF(f);
-        Py_DECREF(gen);
-        return NULL;
-    }
-    gen->gi_xframe->frame_obj = f;
+    InterpreterFrame *frame = (InterpreterFrame *)gen->gi_iframe;
+    _PyFrame_Copy((InterpreterFrame *)f->_f_frame_data, frame);
+    gen->gi_frame_valid = 1;
+    assert(frame->frame_obj == f);
     f->f_owns_frame = 0;
-    gen->gi_xframe->generator = (PyObject *) gen;
+    f->f_frame = frame;
+    frame->generator = (PyObject *) gen;
     assert(PyObject_GC_IsTracked((PyObject *)f));
-
     gen->gi_code = PyFrame_GetCode(f);
+    Py_INCREF(gen->gi_code);
+    Py_DECREF(f);
     gen->gi_weakreflist = NULL;
     gen->gi_exc_state.exc_type = NULL;
     gen->gi_exc_state.exc_value = NULL;
@@ -1077,10 +1088,10 @@ coro_get_cr_await(PyCoroObject *coro, void *Py_UNUSED(ignored))
 static PyObject *
 cr_getrunning(PyCoroObject *coro, void *Py_UNUSED(ignored))
 {
-    if (coro->cr_xframe == NULL) {
+    if (coro->cr_frame_valid == 0) {
         Py_RETURN_FALSE;
     }
-    return PyBool_FromLong(_PyFrame_IsExecuting(coro->cr_xframe));
+    return PyBool_FromLong(_PyFrame_IsExecuting((InterpreterFrame *)coro->cr_iframe));
 }
 
 static PyObject *
@@ -1104,7 +1115,7 @@ static PyGetSetDef coro_getsetlist[] = {
 
 static PyMemberDef coro_memberlist[] = {
     {"cr_code",      T_OBJECT, offsetof(PyCoroObject, cr_code),     READONLY|PY_AUDIT_READ},
-    {"cr_origin",    T_OBJECT, offsetof(PyCoroObject, cr_origin),   READONLY},
+    {"cr_origin",    T_OBJECT, offsetof(PyCoroObject, cr_origin_or_finalizer),   READONLY},
     {NULL}      /* Sentinel */
 };
 
@@ -1123,6 +1134,7 @@ static PyMethodDef coro_methods[] = {
     {"send",(PyCFunction)gen_send, METH_O, coro_send_doc},
     {"throw",(PyCFunction)gen_throw, METH_VARARGS, coro_throw_doc},
     {"close",(PyCFunction)gen_close, METH_NOARGS, coro_close_doc},
+    {"__sizeof__", (PyCFunction)gen_sizeof, METH_NOARGS, sizeof__doc__},
     {NULL, NULL}        /* Sentinel */
 };
 
@@ -1136,8 +1148,9 @@ static PyAsyncMethods coro_as_async = {
 PyTypeObject PyCoro_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0)
     "coroutine",                                /* tp_name */
-    sizeof(PyCoroObject),                       /* tp_basicsize */
-    0,                                          /* tp_itemsize */
+    offsetof(PyCoroObject, cr_iframe) +
+    offsetof(InterpreterFrame, localsplus),       /* tp_basicsize */
+    sizeof(PyObject *),                         /* tp_itemsize */
     /* methods */
     (destructor)gen_dealloc,                    /* tp_dealloc */
     0,                                          /* tp_vectorcall_offset */
@@ -1318,10 +1331,10 @@ PyCoro_New(PyFrameObject *f, PyObject *name, PyObject *qualname)
     int origin_depth = tstate->coroutine_origin_tracking_depth;
 
     if (origin_depth == 0) {
-        ((PyCoroObject *)coro)->cr_origin = NULL;
+        ((PyCoroObject *)coro)->cr_origin_or_finalizer = NULL;
     } else {
         PyObject *cr_origin = compute_cr_origin(origin_depth);
-        ((PyCoroObject *)coro)->cr_origin = cr_origin;
+        ((PyCoroObject *)coro)->cr_origin_or_finalizer = cr_origin;
         if (!cr_origin) {
             Py_DECREF(coro);
             return NULL;
@@ -1382,7 +1395,7 @@ typedef struct _PyAsyncGenWrappedValue {
 static int
 async_gen_traverse(PyAsyncGenObject *gen, visitproc visit, void *arg)
 {
-    Py_VISIT(gen->ag_finalizer);
+    Py_VISIT(gen->ag_origin_or_finalizer);
     return gen_traverse((PyGenObject*)gen, visit, arg);
 }
 
@@ -1413,7 +1426,7 @@ async_gen_init_hooks(PyAsyncGenObject *o)
     finalizer = tstate->async_gen_finalizer;
     if (finalizer) {
         Py_INCREF(finalizer);
-        o->ag_finalizer = finalizer;
+        o->ag_origin_or_finalizer = finalizer;
     }
 
     firstiter = tstate->async_gen_firstiter;
@@ -1508,6 +1521,7 @@ static PyMethodDef async_gen_methods[] = {
     {"asend", (PyCFunction)async_gen_asend, METH_O, async_asend_doc},
     {"athrow",(PyCFunction)async_gen_athrow, METH_VARARGS, async_athrow_doc},
     {"aclose", (PyCFunction)async_gen_aclose, METH_NOARGS, async_aclose_doc},
+    {"__sizeof__", (PyCFunction)gen_sizeof, METH_NOARGS, sizeof__doc__},
     {"__class_getitem__",    Py_GenericAlias,
     METH_O|METH_CLASS,       PyDoc_STR("See PEP 585")},
     {NULL, NULL}        /* Sentinel */
@@ -1525,8 +1539,9 @@ static PyAsyncMethods async_gen_as_async = {
 PyTypeObject PyAsyncGen_Type = {
     PyVarObject_HEAD_INIT(&PyType_Type, 0)
     "async_generator",                          /* tp_name */
-    sizeof(PyAsyncGenObject),                   /* tp_basicsize */
-    0,                                          /* tp_itemsize */
+    offsetof(PyAsyncGenObject, ag_iframe) +
+    offsetof(InterpreterFrame, localsplus),       /* tp_basicsize */
+    sizeof(PyObject *),                         /* tp_itemsize */
     /* methods */
     (destructor)gen_dealloc,                    /* tp_dealloc */
     0,                                          /* tp_vectorcall_offset */
@@ -1594,7 +1609,7 @@ PyAsyncGen_New(PyFrameObject *f, PyObject *name, PyObject *qualname)
     if (o == NULL) {
         return NULL;
     }
-    o->ag_finalizer = NULL;
+    o->ag_origin_or_finalizer = NULL;
     o->ag_closed = 0;
     o->ag_hooks_inited = 0;
     o->ag_running_async = 0;
@@ -2011,7 +2026,7 @@ static PyObject *
 async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg)
 {
     PyGenObject *gen = (PyGenObject*)o->agt_gen;
-    InterpreterFrame *frame = gen->gi_xframe;
+    InterpreterFrame *frame = (InterpreterFrame *)gen->gi_iframe;
     PyObject *retval;
 
     if (o->agt_state == AWAITABLE_STATE_CLOSED) {
@@ -2021,7 +2036,7 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg)
         return NULL;
     }
 
-    if (frame == NULL || _PyFrameHasCompleted(frame)) {
+    if (gen->gi_frame_valid == 0 || _PyFrameHasCompleted(frame)) {
         o->agt_state = AWAITABLE_STATE_CLOSED;
         PyErr_SetNone(PyExc_StopIteration);
         return NULL;
index 05897c561a16ecd9d50c5e4cd740dc2e064a9232..a8bbad33552e46ba15706bd5cbb6d8c652abe88f 100644 (file)
@@ -5854,8 +5854,8 @@ fail_post_args:
     return -1;
 }
 
-static InterpreterFrame *
-make_coro_frame(PyThreadState *tstate,
+static int
+initialize_coro_frame(InterpreterFrame *frame, PyThreadState *tstate,
            PyFunctionObject *func, PyObject *locals,
            PyObject *const *args, Py_ssize_t argcount,
            PyObject *kwnames)
@@ -5863,37 +5863,15 @@ make_coro_frame(PyThreadState *tstate,
     assert(is_tstate_valid(tstate));
     assert(func->func_defaults == NULL || PyTuple_CheckExact(func->func_defaults));
     PyCodeObject *code = (PyCodeObject *)func->func_code;
-    int size = code->co_nlocalsplus+code->co_stacksize + FRAME_SPECIALS_SIZE;
-    InterpreterFrame *frame = (InterpreterFrame *)PyMem_Malloc(sizeof(PyObject *)*size);
-    if (frame == NULL) {
-        goto fail_no_memory;
-    }
     _PyFrame_InitializeSpecials(frame, func, locals, code->co_nlocalsplus);
     for (int i = 0; i < code->co_nlocalsplus; i++) {
         frame->localsplus[i] = NULL;
     }
     assert(frame->frame_obj == NULL);
-    if (initialize_locals(tstate, func, frame->localsplus, args, argcount, kwnames)) {
-        _PyFrame_Clear(frame);
-        PyMem_Free(frame);
-        return NULL;
-    }
-    return frame;
-fail_no_memory:
-    /* Consume the references */
-    for (Py_ssize_t i = 0; i < argcount; i++) {
-        Py_DECREF(args[i]);
-    }
-    if (kwnames) {
-        Py_ssize_t kwcount = PyTuple_GET_SIZE(kwnames);
-        for (Py_ssize_t i = 0; i < kwcount; i++) {
-            Py_DECREF(args[i+argcount]);
-        }
-    }
-    PyErr_NoMemory();
-    return NULL;
+    return initialize_locals(tstate, func, frame->localsplus, args, argcount, kwnames);
 }
 
+
 /* Consumes all the references to the args */
 static PyObject *
 make_coro(PyThreadState *tstate, PyFunctionObject *func,
@@ -5902,14 +5880,17 @@ make_coro(PyThreadState *tstate, PyFunctionObject *func,
           PyObject *kwnames)
 {
     assert (((PyCodeObject *)func->func_code)->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR));
-    InterpreterFrame *frame = make_coro_frame(tstate, func, locals, args, argcount, kwnames);
-    if (frame == NULL) {
+    PyObject *gen = _Py_MakeCoro(func);
+    if (gen == NULL) {
         return NULL;
     }
-    PyObject *gen = _Py_MakeCoro(func, frame);
-    if (gen == NULL) {
+    InterpreterFrame *frame = (InterpreterFrame *)((PyGenObject *)gen)->gi_iframe;
+    if (initialize_coro_frame(frame, tstate, func, locals, args, argcount, kwnames)) {
+        Py_DECREF(gen);
         return NULL;
     }
+    frame->generator = gen;
+    ((PyGenObject *)gen)->gi_frame_valid = 1;
     return gen;
 }
 
index 21f2ced959d807c0e7c2dd1339966769ddb1efe2..da2c1c4aec075ed6dbe0b3df8d21fa34a18904cf 100644 (file)
@@ -43,18 +43,12 @@ _PyFrame_MakeAndSetFrameObject(InterpreterFrame *frame)
     return f;
 }
 
-InterpreterFrame *
-_PyFrame_Copy(InterpreterFrame *frame)
+void
+_PyFrame_Copy(InterpreterFrame *src, InterpreterFrame *dest)
 {
-    assert(frame->stacktop >= frame->f_code->co_nlocalsplus);
-    Py_ssize_t size = ((char*)&frame->localsplus[frame->stacktop]) - (char *)frame;
-    InterpreterFrame *copy = PyMem_Malloc(size);
-    if (copy == NULL) {
-        PyErr_NoMemory();
-        return NULL;
-    }
-    memcpy(copy, frame, size);
-    return copy;
+    assert(src->stacktop >= src->f_code->co_nlocalsplus);
+    Py_ssize_t size = ((char*)&src->localsplus[src->stacktop]) - (char *)src;
+    memcpy(dest, src, size);
 }
 
 static inline void
@@ -112,7 +106,7 @@ _PyFrame_Clear(InterpreterFrame * frame)
         }
         Py_DECREF(f);
     }
-    assert(_PyFrame_GetStackPointer(frame) >= _PyFrame_Stackbase(frame));
+    assert(frame->stacktop >= 0);
     for (int i = 0; i < frame->stacktop; i++) {
         Py_XDECREF(frame->localsplus[i]);
     }