8000 GH-100719: Remove redundant `gi_code` field from generator object. (G… · python/cpython@22b8d77 · GitHub
[go: up one dir, main page]

Skip to content

Commit 22b8d77

Browse files
authored
GH-100719: Remove redundant gi_code field from generator object. (GH-100749)
1 parent 572223f commit 22b8d77

File tree

9 files changed

+116
-61
lines changed
  • 9 files changed

    +116
    -61
    lines changed

    Include/cpython/genobject.h

    Lines changed: 2 additions & 3 deletions
    Original file line numberDiff line numberDiff line change
    @@ -13,8 +13,6 @@ extern "C" {
    1313
    and coroutine objects. */
    1414
    #define _PyGenObject_HEAD(prefix) \
    1515
    PyObject_HEAD \
    16-
    /* The code object backing the generator */ \
    17-
    PyCodeObject *prefix##_code; \
    1816
    /* List of weak reference. */ \
    1917
    PyObject *prefix##_weakreflist; \
    2018
    /* Name of the generator. */ \
    @@ -28,7 +26,7 @@ extern "C" {
    2826
    char prefix##_running_async; \
    2927
    /* The frame */ \
    3028
    int8_t prefix##_frame_state; \
    31-
    PyObject *prefix##_iframe[1];
    29+
    PyObject *prefix##_iframe[1]; \
    3230

    3331
    typedef struct {
    3432
    /* The gi_ prefix is intended to remind of generator-iterator. */
    @@ -46,6 +44,7 @@ PyAPI_FUNC(PyObject *) PyGen_NewWithQualName(PyFrameObject *,
    4644
    PyAPI_FUNC(int) _PyGen_SetStopIterationValue(PyObject *);
    4745
    PyAPI_FUNC(int) _PyGen_FetchStopIterationValue(PyObject **);
    4846
    PyAPI_FUNC(void) _PyGen_Finalize(PyObject *self);
    47+
    PyAPI_FUNC(PyCodeObject *) PyGen_GetCode(PyGenObject *gen);
    4948

    5049

    5150
    /* --- PyCoroObject ------------------------------------------------------- */

    Include/internal/pycore_frame.h

    Lines changed: 1 addition & 1 deletion
    Original file line numberDiff line numberDiff line change
    @@ -209,7 +209,7 @@ _PyFrame_GetFrameObject(_PyInterpreterFrame *frame)
    209209
    * frames like the ones in generators and coroutines.
    210210
    */
    211211
    void
    212-
    _PyFrame_Clear(_PyInterpreterFrame * frame);
    212+
    _PyFrame_ClearExceptCode(_PyInterpreterFrame * frame);
    213213

    214214
    int
    215215
    _PyFrame_Traverse(_PyInterpreterFrame *frame, visitproc visit, void *arg);

    Lib/test/test_capi/test_misc.py

    Lines changed: 5 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -1213,6 +1213,11 @@ def test_pendingcalls_non_threaded(self):
    12131213
    self.pendingcalls_submit(l, n)
    12141214
    self.pendingcalls_wait(l, n)
    12151215

    1216+
    def test_gen_get_code(self):
    1217+
    def genf(): yield
    1218+
    gen = genf()
    1219+
    self.assertEqual(_testcapi.gen_get_code(gen), gen.gi_code)
    1220+
    12161221

    12171222
    class SubinterpreterTest(unittest.TestCase):
    12181223

    Lib/test/test_sys.py

    Lines changed: 1 addition & 1 deletion
    Original file line numberDiff line numberDiff line change
    @@ -1460,7 +1460,7 @@ def bar(cls):
    14601460
    check(bar, size('PP'))
    14611461
    # generator
    14621462
    def get_gen(): yield 1
    1463-
    check(get_gen(), size('P2P4P4c7P2ic??2P'))
    1463+
    check(get_gen(), size('PP4P4c7P2ic??2P'))
    14641464
    # iterator
    14651465
    check(iter('abc'), size('lP'))
    14661466
    # callable-iterator
    Lines changed: 3 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -0,0 +1,3 @@
    1+
    Remove gi_code field from generator (and coroutine and async generator)
    2+
    objects as it is redundant. The frame already includes a reference to the
    3+
    code object.

    Modules/_testcapimodule.c

    Lines changed: 11 additions & 0 deletions
    Original file line numberDiff line numberDiff line change
    @@ -3076,6 +3076,16 @@ eval_get_func_desc(PyObject *self, PyObject *func)
    30763076
    return PyUnicode_FromString(PyEval_GetFuncDesc(func));
    30773077
    }
    30783078

    3079+
    static PyObject *
    3080+
    gen_get_code(PyObject *self, PyObject *gen)
    3081+
    {
    3082+
    if (!PyGen_Check(gen)) {
    3083+
    PyErr_SetString(PyExc_TypeError, "argument must be a generator object");
    3084+
    return NULL;
    3085+
    }
    3086+
    return (PyObject *)PyGen_GetCode((PyGenObject *)gen);
    3087+
    }
    3088+
    30793089
    static PyObject *
    30803090
    eval_eval_code_ex(PyObject *mod, PyObject *pos_args)
    30813091
    {
    @@ -3657,6 +3667,7 @@ static PyMethodDef TestMethods[] = {
    36573667
    {"frame_getvarstring", test_frame_getvarstring, METH_VARARGS, NULL},
    36583668
    {"eval_get_func_name", eval_get_func_name, METH_O, NULL},
    36593669
    {"eval_get_func_desc", eval_get_func_desc, METH_O, NULL},
    3670+
    {"gen_get_code", gen_get_code, METH_O, NULL},
    36603671
    {"get_feature_macros", get_feature_macros, METH_NOARGS, NULL},
    36613672
    {"test_code_api", test_code_api, METH_NOARGS, NULL},
    36623673
    {"settrace_to_record", settrace_to_record, METH_O, NULL},

    Objects/genobject.c

    Lines changed: 55 additions & 17 deletions
    Original file line numberDiff line numberDiff line change
    @@ -24,6 +24,21 @@ static const char *NON_INIT_CORO_MSG = "can't send non-None value to a "
    2424
    static const char *ASYNC_GEN_IGNORED_EXIT_MSG =
    2525
    "async generator ignored GeneratorExit";
    2626

    27+
    /* Returns a borrowed reference */
    28+
    static inline PyCodeObject *
    29+
    _PyGen_GetCode(PyGenObject *gen) {
    30+
    _PyInterpreterFrame *frame = (_PyInterpreterFrame *)(gen->gi_iframe);
    31+
    return frame->f_code;
    32+
    }
    33+
    34+
    PyCodeObject *
    35+
    PyGen_GetCode(PyGenObject *gen) {
    36+
    assert(PyGen_Check(gen));
    37+
    PyCodeObject *res = _PyGen_GetCode(gen);
    38+
    Py_INCREF(res);
    39+
    return res;
    40+
    }
    41+
    2742
    static inline int
    2843
    exc_state_traverse(_PyErr_StackItem *exc_state, visitproc visit, void *arg)
    2944
    {
    @@ -34,7 +49,6 @@ exc_state_traverse(_PyErr_StackItem *exc_state, visitproc visit, void *arg)
    3449
    static int
    3550
    gen_traverse(PyGenObject *gen, visitproc visit, void *arg)
    3651
    {
    37-
    Py_VISIT(gen->gi_code);
    3852
    Py_VISIT(gen->gi_name);
    3953
    Py_VISIT(gen->gi_qualname);
    4054
    if (gen->gi_frame_state < FRAME_CLEARED) {
    @@ -88,8 +102,8 @@ _PyGen_Finalize(PyObject *self)
    88102

    89103
    /* If `gen` is a coroutine, and if it was never awaited on,
    90104
    issue a RuntimeWarning. */
    91-
    if (gen->gi_code != NULL &&
    92-
    ((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE &&
    105+
    assert(_PyGen_GetCode(gen) != NULL);
    106+
    if (_PyGen_GetCode(gen)->co_flags & CO_COROUTINE &&
    93107
    gen->gi_frame_state == FRAME_CREATED)
    94108
    {
    95109
    _PyErr_WarnUnawaitedCoroutine((PyObject *)gen);
    @@ -137,12 +151,12 @@ gen_dealloc(PyGenObject *gen)
    137151
    _PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe;
    138152
    gen->gi_frame_state = FRAME_CLEARED;
    139153
    frame->previous = NULL;
    140-
    _PyFrame_Clear(frame);
    154+
    _PyFrame_ClearExceptCode(frame);
    141155
    }
    142-
    if (((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE) {
    156+
    if (_PyGen_GetCode(gen)->co_flags & CO_COROUTINE) {
    143157
    Py_CLEAR(((PyCoroObject *)gen)->cr_origin_or_finalizer);
    144158
    }
    145-
    Py_CLEAR(gen->gi_code);
    159+
    Py_DECREF(_PyGen_GetCode(gen));
    146160
    Py_CLEAR(gen->gi_name);
    147161
    Py_CLEAR(gen->gi_qualname);
    148162
    _PyErr_ClearExcState(&gen->gi_exc_state);
    @@ -332,7 +346,7 @@ _PyGen_yf(PyGenObject *gen)
    332346
    /* Return immediately if the frame didn't start yet. SEND
    333347
    always come after LOAD_CONST: a code object should not start
    334348
    with SEND */
    335-
    assert(_PyCode_CODE(gen->gi_code)[0].op.code != SEND);
    349+
    assert(_PyCode_CODE(_PyGen_GetCode(gen))[0].op.code != SEND);
    336350
    return NULL;
    337351
    }
    338352
    _Py_CODEUNIT next = frame->prev_instr[1];
    @@ -767,6 +781,21 @@ gen_getframe(PyGenObject *gen, void *Py_UNUSED(ignored))
    767781
    return _gen_getframe(gen, "gi_frame");
    768782
    }
    769783

    784+
    static PyObject *
    785+
    _gen_getcode(PyGenObject *gen, const char *const name)
    786+
    {
    787+
    if (PySys_Audit("object.__getattr__", "Os", gen, name) < 0) {
    788+
    return NULL;
    789+
    }
    790+
    return Py_NewRef(_PyGen_GetCode(gen));
    791+
    }
    792+
    793+
    static PyObject *
    794+
    gen_getcode(PyGenObject *gen, void *Py_UNUSED(ignored))
    795+
    {
    796+
    return _gen_getcode(gen, "gi_code");
    797+
    }
    798+
    770799
    static PyGetSetDef gen_getsetlist[] = {
    771800
    {"__name__", (getter)gen_get_name, (setter)gen_set_name,
    772801
    PyDoc_STR("name of the generator")},
    @@ -777,11 +806,11 @@ static PyGetSetDef gen_getsetlist[] = {
    777806
    {"gi_running", (getter)gen_getrunning, NULL, NULL},
    778807
    {"gi_frame", (getter)gen_getframe, NULL, NULL},
    779808
    {"gi_suspended", (getter)gen_getsuspended, NULL, NULL},
    809+
    {"gi_code", (getter)gen_getcode, NULL, NULL},
    780810
    {NULL} /* Sentinel */
    781811
    };
    782812

    783813
    static PyMemberDef gen_memberlist[] = {
    784-
    {"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY|PY_AUDIT_READ},
    785814
    {NULL} /* Sentinel */
    786815
    };
    787816

    @@ -790,7 +819,7 @@ gen_sizeof(PyGenObject *gen, PyObject *Py_UNUSED(ignored))
    790819
    {
    791820
    Py_ssize_t res;
    792821
    res = offsetof(PyGenObject, gi_iframe) + offsetof(_PyInterpreterFrame, localsplus);
    793-
    PyCodeObject *code = gen->gi_code;
    822+
    PyCodeObject *code = _PyGen_GetCode(gen);
    794823
    res += _PyFrame_NumSlotsForCodeObject(code) * sizeof(PyObject *);
    795824
    return PyLong_FromSsize_t(res);
    796825
    }
    @@ -878,7 +907,6 @@ make_gen(PyTypeObject *type, PyFunctionObject *func)
    878907
    return NULL;
    879908
    }
    880909
    gen->gi_frame_state = FRAME_CLEARED;
    881-
    gen->gi_code = (PyCodeObject *)Py_NewRef(func->func_code);
    882910
    gen->gi_weakreflist = NULL;
    883911
    gen->gi_exc_state.exc_value = NULL;
    884912
    gen->gi_exc_state.previous_item = NULL;
    @@ -960,20 +988,18 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f,
    960988
    f->f_frame = frame;
    961989
    frame->owner = FRAME_OWNED_BY_GENERATOR;
    962990
    assert(PyObject_GC_IsTracked((PyObject *)f));
    963-
    gen->gi_code = PyFrame_GetCode(f);
    964-
    Py_INCREF(gen->gi_code);
    965991
    Py_DECREF(f);
    966992
    gen->gi_weakreflist = NULL;
    967993
    gen->gi_exc_state.exc_value = NULL;
    968994
    gen->gi_exc_state.previous_item = NULL;
    969995
    if (name != NULL)
    970996
    gen->gi_name = Py_NewRef(name);
    971997
    else
    972-
    gen->gi_name = Py_NewRef(gen->gi_code->co_name);
    998+
    gen->gi_name = Py_NewRef(_PyGen_GetCode(gen)->co_name);
    973999
    if (qualname != NULL)
    9741000
    gen->gi_qualname = Py_NewRef(qualname);
    9751001
    else
    976-
    gen->gi_qualname = Py_NewRef(gen->gi_code->co_qualname);
    1002+
    gen->gi_qualname = Py_NewRef(_PyGen_GetCode(gen)->co_qualname);
    9771003
    _PyObject_GC_TRACK(gen);
    9781004
    return (PyObject *)gen;
    9791005
    }
    @@ -1001,7 +1027,7 @@ static int
    10011027
    gen_is_coroutine(PyObject *o)
    10021028
    {
    10031029
    if (PyGen_CheckExact(o)) {
    1004-
    PyCodeObject *code = (PyCodeObject *)((PyGenObject*)o)->gi_code;
    1030+
    PyCodeObject *code = _PyGen_GetCode((PyGenObject*)o);
    10051031
    if (code->co_flags & CO_ITERABLE_COROUTINE) {
    10061032
    return 1;
    10071033
    }
    @@ -1110,6 +1136,12 @@ cr_getframe(PyCoroObject *coro, void *Py_UNUSED(ignored))
    11101136
    return _gen_getframe((PyGenObject *)coro, "cr_frame");
    11111137
    }
    11121138

    1139+
    static PyObject *
    1140+
    cr_getcode(PyCoroObject *coro, void *Py_UNUSED(ignored))
    1141+
    {
    1142+
    return _gen_getcode((PyGenObject *)coro, "cr_code");
    1143+
    }
    1144+
    11131145

    11141146
    static PyGetSetDef coro_getsetlist[] = {
    11151147
    {"__name__", (getter)gen_get_name, (setter)gen_set_name,
    @@ -1120,12 +1152,12 @@ static PyGetSetDef coro_getsetlist[] = {
    11201152
    PyDoc_STR("object being awaited on, or None")},
    11211153
    {"cr_running", (getter)cr_getrunning, NULL, NULL},
    11221154
    {"cr_frame", (getter)cr_getframe, NULL, NULL},
    1155+
    {"cr_code", (getter)cr_getcode, NULL, NULL},
    11231156
    {"cr_suspended", (getter)cr_getsuspended, NULL, NULL},
    11241157
    {NULL} /* Sentinel */
    11251158
    };
    11261159

    11271160
    static PyMemberDef coro_memberlist[] = {
    1128-
    {"cr_code", T_OBJECT, offsetof(PyCoroObject, cr_code), READONLY|PY_AUDIT_READ},
    11291161
    {"cr_origin", T_OBJECT, offsetof(PyCoroObject, cr_origin_or_finalizer), READONLY},
    11301162
    {NULL} /* Sentinel */
    11311163
    };
    @@ -1514,6 +1546,12 @@ ag_getframe(PyAsyncGenObject *ag, void *Py_UNUSED(ignored))
    15141546
    return _gen_getframe((PyGenObject *)ag, "ag_frame");
    15151547
    }
    15161548

    1549+
    static PyObject *
    1550+
    ag_getcode(PyGenObject *gen, void *Py_UNUSED(ignored))
    1551+
    {
    1552+
    return _gen_getcode(gen, "ag__code");
    1553+
    }
    1554+
    15171555
    static PyGetSetDef async_gen_getsetlist[] = {
    15181556
    {"__name__", (getter)gen_get_name, (setter)gen_set_name,
    15191557
    PyDoc_STR("name of the async generator")},
    @@ -1522,13 +1560,13 @@ static PyGetSetDef async_gen_getsetlist[] = {
    15221560
    {"ag_await", (getter)coro_get_cr_await, NULL,
    15231561
    PyDoc_STR("object being awaited on, or None")},
    15241562
    {"ag_frame", (getter)ag_getframe, NULL, NULL},
    1563+
    {"ag_code", (getter)ag_getcode, NULL, NULL},
    15251564
    {NULL} /* Sentinel */
    15261565
    };
    15271566

    15281567
    static PyMemberDef async_gen_memberlist[] = {
    15291568
    {"ag_running", T_BOOL, offsetof(PyAsyncGenObject, ag_running_async),
    15301569
    READONLY},
    1531-
    {"ag_code", T_OBJECT, offsetof(PyAsyncGenObject, ag_code), READONLY|PY_AUDIT_READ},
    15321570
    {NULL} /* Sentinel */
    15331571
    };
    15341572

    Python/ceval.c

    Lines changed: 36 additions & 37 deletions
    < 84DA td data-grid-cell-id="diff-c22186367cbe20233e843261998dc027ae5f1f8c0d2e778abfa454ae74cc59de-1683-1672-2" data-line-anchor="diff-c22186367cbe20233e843261998dc027ae5f1f8c0d2e778abfa454ae74cc59deR1672" data-selected="false" role="gridcell" style="background-color:var(--diffBlob-additionLine-bgColor, var(--diffBlob-addition-bgColor-line));padding-right:24px" tabindex="-1" valign="top" class="focusable-grid-cell diff-text-cell right-side-diff-cell left-side">+
    Py_DECREF(args[i]);
    Original file line numberDiff line numberDiff line change
    @@ -1604,41 +1604,6 @@ initialize_locals(PyThreadState *tstate, PyFunctionObject *func,
    16041604
    return -1;
    16051605
    }
    16061606

    1607-
    /* Consumes references to func, locals and all the args */
    1608-
    static _PyInterpreterFrame *
    1609-
    _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func,
    1610-
    PyObject *locals, PyObject* const* args,
    1611-
    size_t argcount, PyObject *kwnames)
    1612-
    {
    1613-
    PyCodeObject * code = (PyCodeObject *)func->func_code;
    1614-
    CALL_STAT_INC(frames_pushed);
    1615-
    _PyInterpreterFrame *frame = _PyThreadState_PushFrame(tstate, code->co_framesize);
    1616-
    if (frame == NULL) {
    1617-
    goto fail;
    1618-
    }
    1619-
    _PyFrame_Initialize(frame, func, locals, code, 0);
    1620-
    PyObject **localsarray = &frame->localsplus[0];
    1621-
    if (initialize_locals(tstate, func, localsarray, args, argcount, kwnames)) {
    1622-
    assert(frame->owner != FRAME_OWNED_BY_GENERATOR);
    1623-
    _PyEvalFrameClearAndPop(tstate, frame);
    1624-
    return NULL;
    1625-
    }
    1626-
    return frame;
    1627-
    fail:
    1628-
    /* Consume the references */
    1629-
    for (size_t i = 0; i < argcount; i++) {
    1630-
    Py_DECREF(args[i]);
    1631-
    }
    1632-
    if (kwnames) {
    1633-
    Py_ssize_t kwcount = PyTuple_GET_SIZE(kwnames);
    1634-
    for (Py_ssize_t i = 0; i < kwcount; i++) {
    1635-
    Py_DECREF(args[i+argcount]);
    1636-
    }
    1637-
    }
    1638-
    PyErr_NoMemory();
    1639-
    return NULL;
    1640-
    }
    1641-
    16421607
    static void
    16431608
    clear_thread_frame(PyThreadState *tstate, _PyInterpreterFrame * frame)
    16441609
    {
    @@ -1649,7 +1614,8 @@ clear_thread_frame(PyThreadState *tstate, _PyInterpreterFrame * frame)
    16491614
    tstate->datastack_top);
    16501615
    tstate->c_recursion_remaining--;
    16511616
    assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame);
    1652-
    _PyFrame_Clear(frame);
    1617+
    _PyFrame_ClearExceptCode(frame);
    1618+
    Py_DECREF(frame->f_code);
    16531619
    tstate->c_recursion_remaining++;
    16541620
    _PyThreadState_PopFrame(tstate, frame);
    16551621
    }
    @@ -1665,7 +1631,7 @@ clear_gen_frame(PyThreadState *tstate, _PyInterpreterFrame * frame)
    16651631
    gen->gi_exc_state.previous_item = NULL;
    16661632
    tstate->c_recursion_remaining--;
    16671633
    assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame);
    1668-
    _PyFrame_Clear(frame);
    1634+
    _PyFrame_ClearExceptCode(frame);
    16691635
    tstate->c_recursion_remaining++;
    16701636
    frame->previous = NULL;
    16711637
    }
    @@ -1681,6 +1647,39 @@ _PyEvalFrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame * frame)
    16811647
    }
    16821648
    }
    16831649

    1650+
    /* Consumes references to func, locals and all the args */
    1651+
    static _PyInterpreterFrame *
    1652+
    _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func,
    1653+
    PyObject *locals, PyObject* const* args,
    1654+
    size_t argcount, PyObject *kwnames)
    1655+
    {
    1656+
    PyCodeObject * code = (PyCodeObject *)func->func_code;
    1657+
    CALL_STAT_INC(frames_pushed);
    1658+
    _PyInterpreterFrame *frame = _PyThreadState_PushFrame(tstate, code->co_framesize);
    1659+
    if (frame == NULL) {
    1660+
    goto fail;
    1661+
    }
    1662+
    _PyFrame_Initialize(frame, func, locals, code, 0);
    1663+
    if (initialize_locals(tstate, func, frame->localsplus, args, argcount, kwnames)) {
    1664+
    assert(frame->owner == FRAME_OWNED_BY_THREAD);
    1665+
    clear_thread_frame(tstate, frame);
    1666+
    return NULL;
    1667+
    }
    1668+
    return frame;
    1669+
    fail:
    1670+
    /* Consume the references */
    1671+
    for (size_t i = 0; i < argcount; i++) {
    1672
    1673+
    }
    1674+
    if (kwnames) {
    1675+
    Py_ssize_t kwcount = PyTuple_GET_SIZE(kwnames);
    1676+
    for (Py_ssize_t i = 0; i < kwcount; i++) {
    1677+
    Py_DECREF(args[i+argcount]);
    1678+
    }
    1679+
    }
    1680+
    PyErr_NoMemory();
    1681+
    return NULL;
    1682+
    }
    16841683

    16851684
    PyObject *
    16861685
    _PyEval_Vector(PyThreadState *tstate, PyFunctionObject *func,

    0 commit comments

    Comments
     (0)
    0