LCOV - code coverage report
Current view: top level - libreoffice/workdir/unxlngi6.pro/UnpackedTarball/python3/Objects - genobject.c (source / functions) Hit Total Coverage
Test: libreoffice_filtered.info Lines: 92 246 37.4 %
Date: 2012-12-17 Functions: 7 15 46.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Generator object implementation */
       2             : 
       3             : #include "Python.h"
       4             : #include "frameobject.h"
       5             : #include "structmember.h"
       6             : #include "opcode.h"
       7             : 
       8             : static PyObject *gen_close(PyGenObject *gen, PyObject *args);
       9             : 
      10             : static int
      11           0 : gen_traverse(PyGenObject *gen, visitproc visit, void *arg)
      12             : {
      13           0 :     Py_VISIT((PyObject *)gen->gi_frame);
      14           0 :     Py_VISIT(gen->gi_code);
      15           0 :     return 0;
      16             : }
      17             : 
      18             : static void
      19         254 : gen_dealloc(PyGenObject *gen)
      20             : {
      21         254 :     PyObject *self = (PyObject *) gen;
      22             : 
      23         254 :     _PyObject_GC_UNTRACK(gen);
      24             : 
      25         254 :     if (gen->gi_weakreflist != NULL)
      26           0 :         PyObject_ClearWeakRefs(self);
      27             : 
      28         254 :     _PyObject_GC_TRACK(self);
      29             : 
      30         254 :     if (gen->gi_frame != NULL && gen->gi_frame->f_stacktop != NULL) {
      31             :         /* Generator is paused, so we need to close */
      32          24 :         Py_TYPE(gen)->tp_del(self);
      33          24 :         if (self->ob_refcnt > 0)
      34         254 :             return;                     /* resurrected.  :( */
      35             :     }
      36             : 
      37         254 :     _PyObject_GC_UNTRACK(self);
      38         254 :     Py_CLEAR(gen->gi_frame);
      39         254 :     Py_CLEAR(gen->gi_code);
      40         254 :     PyObject_GC_Del(gen);
      41             : }
      42             : 
      43             : 
      44             : static PyObject *
      45         757 : gen_send_ex(PyGenObject *gen, PyObject *arg, int exc)
      46             : {
      47         757 :     PyThreadState *tstate = PyThreadState_GET();
      48         757 :     PyFrameObject *f = gen->gi_frame;
      49             :     PyObject *result;
      50             : 
      51         757 :     if (gen->gi_running) {
      52           0 :         PyErr_SetString(PyExc_ValueError,
      53             :                         "generator already executing");
      54           0 :         return NULL;
      55             :     }
      56         757 :     if (f == NULL || f->f_stacktop == NULL) {
      57             :         /* Only set exception if called from send() */
      58           0 :         if (arg && !exc)
      59           0 :             PyErr_SetNone(PyExc_StopIteration);
      60           0 :         return NULL;
      61             :     }
      62             : 
      63         757 :     if (f->f_lasti == -1) {
      64         254 :         if (arg && arg != Py_None) {
      65           0 :             PyErr_SetString(PyExc_TypeError,
      66             :                             "can't send non-None value to a "
      67             :                             "just-started generator");
      68           0 :             return NULL;
      69             :         }
      70             :     } else {
      71             :         /* Push arg onto the frame's value stack */
      72         503 :         result = arg ? arg : Py_None;
      73         503 :         Py_INCREF(result);
      74         503 :         *(f->f_stacktop++) = result;
      75             :     }
      76             : 
      77             :     /* Generators always return to their most recent caller, not
      78             :      * necessarily their creator. */
      79         757 :     Py_XINCREF(tstate->frame);
      80             :     assert(f->f_back == NULL);
      81         757 :     f->f_back = tstate->frame;
      82             : 
      83         757 :     gen->gi_running = 1;
      84         757 :     result = PyEval_EvalFrameEx(f, exc);
      85         757 :     gen->gi_running = 0;
      86             : 
      87             :     /* Don't keep the reference to f_back any longer than necessary.  It
      88             :      * may keep a chain of frames alive or it could create a reference
      89             :      * cycle. */
      90             :     assert(f->f_back == tstate->frame);
      91         757 :     Py_CLEAR(f->f_back);
      92             : 
      93             :     /* If the generator just returned (as opposed to yielding), signal
      94             :      * that the generator is exhausted. */
      95         757 :     if (result && f->f_stacktop == NULL) {
      96         230 :         if (result == Py_None) {
      97             :             /* Delay exception instantiation if we can */
      98         230 :             PyErr_SetNone(PyExc_StopIteration);
      99             :         } else {
     100           0 :             PyObject *e = PyObject_CallFunctionObjArgs(
     101             :                                PyExc_StopIteration, result, NULL);
     102           0 :             if (e != NULL) {
     103           0 :                 PyErr_SetObject(PyExc_StopIteration, e);
     104           0 :                 Py_DECREF(e);
     105             :             }
     106             :         }
     107         230 :         Py_CLEAR(result);
     108             :     }
     109             : 
     110         757 :     if (!result || f->f_stacktop == NULL) {
     111             :         /* generator can't be rerun, so release the frame */
     112             :         /* first clean reference cycle through stored exception traceback */
     113             :         PyObject *t, *v, *tb;
     114         254 :         t = f->f_exc_type;
     115         254 :         v = f->f_exc_value;
     116         254 :         tb = f->f_exc_traceback;
     117         254 :         f->f_exc_type = NULL;
     118         254 :         f->f_exc_value = NULL;
     119         254 :         f->f_exc_traceback = NULL;
     120         254 :         Py_XDECREF(t);
     121         254 :         Py_XDECREF(v);
     122         254 :         Py_XDECREF(tb);
     123         254 :         gen->gi_frame = NULL;
     124         254 :         Py_DECREF(f);
     125             :     }
     126             : 
     127         757 :     return result;
     128             : }
     129             : 
     130             : PyDoc_STRVAR(send_doc,
     131             : "send(arg) -> send 'arg' into generator,\n\
     132             : return next yielded value or raise StopIteration.");
     133             : 
     134             : PyObject *
     135           0 : _PyGen_Send(PyGenObject *gen, PyObject *arg)
     136             : {
     137           0 :     return gen_send_ex(gen, arg, 0);
     138             : }
     139             : 
     140             : PyDoc_STRVAR(close_doc,
     141             : "close() -> raise GeneratorExit inside generator.");
     142             : 
     143             : /*
     144             :  *   This helper function is used by gen_close and gen_throw to
     145             :  *   close a subiterator being delegated to by yield-from.
     146             :  */
     147             : 
     148             : static int
     149           0 : gen_close_iter(PyObject *yf)
     150             : {
     151           0 :     PyObject *retval = NULL;
     152             :     _Py_IDENTIFIER(close);
     153             : 
     154           0 :     if (PyGen_CheckExact(yf)) {
     155           0 :         retval = gen_close((PyGenObject *)yf, NULL);
     156           0 :         if (retval == NULL)
     157           0 :             return -1;
     158             :     } else {
     159           0 :         PyObject *meth = _PyObject_GetAttrId(yf, &PyId_close);
     160           0 :         if (meth == NULL) {
     161           0 :             if (!PyErr_ExceptionMatches(PyExc_AttributeError))
     162           0 :                 PyErr_WriteUnraisable(yf);
     163           0 :             PyErr_Clear();
     164             :         } else {
     165           0 :             retval = PyObject_CallFunction(meth, "");
     166           0 :             Py_DECREF(meth);
     167           0 :             if (retval == NULL)
     168           0 :                 return -1;
     169             :         }
     170             :     }
     171           0 :     Py_XDECREF(retval);
     172           0 :     return 0;
     173             : }
     174             : 
     175             : static PyObject *
     176          24 : gen_yf(PyGenObject *gen)
     177             : {
     178          24 :     PyObject *yf = NULL;
     179          24 :     PyFrameObject *f = gen->gi_frame;
     180             : 
     181          24 :     if (f) {
     182          24 :         PyObject *bytecode = f->f_code->co_code;
     183          24 :         unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode);
     184             : 
     185          24 :         if (code[f->f_lasti + 1] != YIELD_FROM)
     186          24 :             return NULL;
     187           0 :         yf = f->f_stacktop[-1];
     188           0 :         Py_INCREF(yf);
     189             :     }
     190             : 
     191           0 :     return yf;
     192             : }
     193             : 
     194             : static PyObject *
     195          24 : gen_close(PyGenObject *gen, PyObject *args)
     196             : {
     197             :     PyObject *retval;
     198          24 :     PyObject *yf = gen_yf(gen);
     199          24 :     int err = 0;
     200             : 
     201          24 :     if (yf) {
     202           0 :         gen->gi_running = 1;
     203           0 :         err = gen_close_iter(yf);
     204           0 :         gen->gi_running = 0;
     205           0 :         Py_DECREF(yf);
     206             :     }
     207          24 :     if (err == 0)
     208          24 :         PyErr_SetNone(PyExc_GeneratorExit);
     209          24 :     retval = gen_send_ex(gen, Py_None, 1);
     210          24 :     if (retval) {
     211           0 :         Py_DECREF(retval);
     212           0 :         PyErr_SetString(PyExc_RuntimeError,
     213             :                         "generator ignored GeneratorExit");
     214           0 :         return NULL;
     215             :     }
     216          24 :     if (PyErr_ExceptionMatches(PyExc_StopIteration)
     217          24 :         || PyErr_ExceptionMatches(PyExc_GeneratorExit)) {
     218          24 :         PyErr_Clear();          /* ignore these errors */
     219          24 :         Py_INCREF(Py_None);
     220          24 :         return Py_None;
     221             :     }
     222           0 :     return NULL;
     223             : }
     224             : 
     225             : static void
     226          24 : gen_del(PyObject *self)
     227             : {
     228             :     PyObject *res;
     229             :     PyObject *error_type, *error_value, *error_traceback;
     230          24 :     PyGenObject *gen = (PyGenObject *)self;
     231             : 
     232          24 :     if (gen->gi_frame == NULL || gen->gi_frame->f_stacktop == NULL)
     233             :         /* Generator isn't paused, so no need to close */
     234             :         return;
     235             : 
     236             :     /* Temporarily resurrect the object. */
     237             :     assert(self->ob_refcnt == 0);
     238          24 :     self->ob_refcnt = 1;
     239             : 
     240             :     /* Save the current exception, if any. */
     241          24 :     PyErr_Fetch(&error_type, &error_value, &error_traceback);
     242             : 
     243          24 :     res = gen_close(gen, NULL);
     244             : 
     245          24 :     if (res == NULL)
     246           0 :         PyErr_WriteUnraisable(self);
     247             :     else
     248          24 :         Py_DECREF(res);
     249             : 
     250             :     /* Restore the saved exception. */
     251          24 :     PyErr_Restore(error_type, error_value, error_traceback);
     252             : 
     253             :     /* Undo the temporary resurrection; can't use DECREF here, it would
     254             :      * cause a recursive call.
     255             :      */
     256             :     assert(self->ob_refcnt > 0);
     257          24 :     if (--self->ob_refcnt == 0)
     258             :         return; /* this is the normal path out */
     259             : 
     260             :     /* close() resurrected it!  Make it look like the original Py_DECREF
     261             :      * never happened.
     262             :      */
     263             :     {
     264           0 :         Py_ssize_t refcnt = self->ob_refcnt;
     265           0 :         _Py_NewReference(self);
     266           0 :         self->ob_refcnt = refcnt;
     267             :     }
     268             :     assert(PyType_IS_GC(Py_TYPE(self)) &&
     269             :            _Py_AS_GC(self)->gc.gc_refs != _PyGC_REFS_UNTRACKED);
     270             : 
     271             :     /* If Py_REF_DEBUG, _Py_NewReference bumped _Py_RefTotal, so
     272             :      * we need to undo that. */
     273             :     _Py_DEC_REFTOTAL;
     274             :     /* If Py_TRACE_REFS, _Py_NewReference re-added self to the object
     275             :      * chain, so no more to do there.
     276             :      * If COUNT_ALLOCS, the original decref bumped tp_frees, and
     277             :      * _Py_NewReference bumped tp_allocs:  both of those need to be
     278             :      * undone.
     279             :      */
     280             : #ifdef COUNT_ALLOCS
     281             :     --(Py_TYPE(self)->tp_frees);
     282             :     --(Py_TYPE(self)->tp_allocs);
     283             : #endif
     284             : }
     285             : 
     286             : 
     287             : 
     288             : PyDoc_STRVAR(throw_doc,
     289             : "throw(typ[,val[,tb]]) -> raise exception in generator,\n\
     290             : return next yielded value or raise StopIteration.");
     291             : 
     292             : static PyObject *
     293           0 : gen_throw(PyGenObject *gen, PyObject *args)
     294             : {
     295             :     PyObject *typ;
     296           0 :     PyObject *tb = NULL;
     297           0 :     PyObject *val = NULL;
     298           0 :     PyObject *yf = gen_yf(gen);
     299             :     _Py_IDENTIFIER(throw);
     300             : 
     301           0 :     if (!PyArg_UnpackTuple(args, "throw", 1, 3, &typ, &val, &tb))
     302           0 :         return NULL;
     303             : 
     304           0 :     if (yf) {
     305             :         PyObject *ret;
     306             :         int err;
     307           0 :         if (PyErr_GivenExceptionMatches(typ, PyExc_GeneratorExit)) {
     308           0 :             gen->gi_running = 1;
     309           0 :             err = gen_close_iter(yf);
     310           0 :             gen->gi_running = 0;
     311           0 :             Py_DECREF(yf);
     312           0 :             if (err < 0)
     313           0 :                 return gen_send_ex(gen, Py_None, 1);
     314           0 :             goto throw_here;
     315             :         }
     316           0 :         if (PyGen_CheckExact(yf)) {
     317           0 :             gen->gi_running = 1;
     318           0 :             ret = gen_throw((PyGenObject *)yf, args);
     319           0 :             gen->gi_running = 0;
     320             :         } else {
     321           0 :             PyObject *meth = _PyObject_GetAttrId(yf, &PyId_throw);
     322           0 :             if (meth == NULL) {
     323           0 :                 if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
     324           0 :                     Py_DECREF(yf);
     325           0 :                     return NULL;
     326             :                 }
     327           0 :                 PyErr_Clear();
     328           0 :                 Py_DECREF(yf);
     329           0 :                 goto throw_here;
     330             :             }
     331           0 :             gen->gi_running = 1;
     332           0 :             ret = PyObject_CallObject(meth, args);
     333           0 :             gen->gi_running = 0;
     334           0 :             Py_DECREF(meth);
     335             :         }
     336           0 :         Py_DECREF(yf);
     337           0 :         if (!ret) {
     338             :             PyObject *val;
     339             :             /* Pop subiterator from stack */
     340           0 :             ret = *(--gen->gi_frame->f_stacktop);
     341             :             assert(ret == yf);
     342           0 :             Py_DECREF(ret);
     343             :             /* Termination repetition of YIELD_FROM */
     344           0 :             gen->gi_frame->f_lasti++;
     345           0 :             if (_PyGen_FetchStopIterationValue(&val) == 0) {
     346           0 :                 ret = gen_send_ex(gen, val, 0);
     347           0 :                 Py_DECREF(val);
     348             :             } else {
     349           0 :                 ret = gen_send_ex(gen, Py_None, 1);
     350             :             }
     351             :         }
     352           0 :         return ret;
     353             :     }
     354             : 
     355             : throw_here:
     356             :     /* First, check the traceback argument, replacing None with
     357             :        NULL. */
     358           0 :     if (tb == Py_None) {
     359           0 :         tb = NULL;
     360             :     }
     361           0 :     else if (tb != NULL && !PyTraceBack_Check(tb)) {
     362           0 :         PyErr_SetString(PyExc_TypeError,
     363             :             "throw() third argument must be a traceback object");
     364           0 :         return NULL;
     365             :     }
     366             : 
     367           0 :     Py_INCREF(typ);
     368           0 :     Py_XINCREF(val);
     369           0 :     Py_XINCREF(tb);
     370             : 
     371           0 :     if (PyExceptionClass_Check(typ))
     372           0 :         PyErr_NormalizeException(&typ, &val, &tb);
     373             : 
     374           0 :     else if (PyExceptionInstance_Check(typ)) {
     375             :         /* Raising an instance.  The value should be a dummy. */
     376           0 :         if (val && val != Py_None) {
     377           0 :             PyErr_SetString(PyExc_TypeError,
     378             :               "instance exception may not have a separate value");
     379           0 :             goto failed_throw;
     380             :         }
     381             :         else {
     382             :             /* Normalize to raise <class>, <instance> */
     383           0 :             Py_XDECREF(val);
     384           0 :             val = typ;
     385           0 :             typ = PyExceptionInstance_Class(typ);
     386           0 :             Py_INCREF(typ);
     387             : 
     388           0 :             if (tb == NULL)
     389             :                 /* Returns NULL if there's no traceback */
     390           0 :                 tb = PyException_GetTraceback(val);
     391             :         }
     392             :     }
     393             :     else {
     394             :         /* Not something you can raise.  throw() fails. */
     395           0 :         PyErr_Format(PyExc_TypeError,
     396             :                      "exceptions must be classes or instances "
     397             :                      "deriving from BaseException, not %s",
     398           0 :                      Py_TYPE(typ)->tp_name);
     399           0 :             goto failed_throw;
     400             :     }
     401             : 
     402           0 :     PyErr_Restore(typ, val, tb);
     403           0 :     return gen_send_ex(gen, Py_None, 1);
     404             : 
     405             : failed_throw:
     406             :     /* Didn't use our arguments, so restore their original refcounts */
     407           0 :     Py_DECREF(typ);
     408           0 :     Py_XDECREF(val);
     409           0 :     Py_XDECREF(tb);
     410           0 :     return NULL;
     411             : }
     412             : 
     413             : 
     414             : static PyObject *
     415         733 : gen_iternext(PyGenObject *gen)
     416             : {
     417         733 :     PyObject *val = NULL;
     418             :     PyObject *ret;
     419         733 :     ret = gen_send_ex(gen, val, 0);
     420         733 :     Py_XDECREF(val);
     421         733 :     return ret;
     422             : }
     423             : 
     424             : /*
     425             :  *   If StopIteration exception is set, fetches its 'value'
     426             :  *   attribute if any, otherwise sets pvalue to None.
     427             :  *
     428             :  *   Returns 0 if no exception or StopIteration is set.
     429             :  *   If any other exception is set, returns -1 and leaves
     430             :  *   pvalue unchanged.
     431             :  */
     432             : 
     433             : int
     434           0 : _PyGen_FetchStopIterationValue(PyObject **pvalue) {
     435             :     PyObject *et, *ev, *tb;
     436           0 :     PyObject *value = NULL;
     437             : 
     438           0 :     if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
     439           0 :         PyErr_Fetch(&et, &ev, &tb);
     440           0 :         Py_XDECREF(et);
     441           0 :         Py_XDECREF(tb);
     442           0 :         if (ev) {
     443           0 :             value = ((PyStopIterationObject *)ev)->value;
     444           0 :             Py_INCREF(value);
     445           0 :             Py_DECREF(ev);
     446             :         }
     447           0 :     } else if (PyErr_Occurred()) {
     448           0 :         return -1;
     449             :     }
     450           0 :     if (value == NULL) {
     451           0 :         value = Py_None;
     452           0 :         Py_INCREF(value);
     453             :     }
     454           0 :     *pvalue = value;
     455           0 :     return 0;
     456             : }
     457             : 
     458             : static PyObject *
     459           0 : gen_repr(PyGenObject *gen)
     460             : {
     461           0 :     return PyUnicode_FromFormat("<generator object %S at %p>",
     462           0 :                                 ((PyCodeObject *)gen->gi_code)->co_name,
     463             :                                 gen);
     464             : }
     465             : 
     466             : 
     467             : static PyObject *
     468           0 : gen_get_name(PyGenObject *gen)
     469             : {
     470           0 :     PyObject *name = ((PyCodeObject *)gen->gi_code)->co_name;
     471           0 :     Py_INCREF(name);
     472           0 :     return name;
     473             : }
     474             : 
     475             : 
     476             : PyDoc_STRVAR(gen__name__doc__,
     477             : "Return the name of the generator's associated code object.");
     478             : 
     479             : static PyGetSetDef gen_getsetlist[] = {
     480             :     {"__name__", (getter)gen_get_name, NULL, gen__name__doc__},
     481             :     {NULL}
     482             : };
     483             : 
     484             : 
     485             : static PyMemberDef gen_memberlist[] = {
     486             :     {"gi_frame",        T_OBJECT, offsetof(PyGenObject, gi_frame),      READONLY},
     487             :     {"gi_running",      T_BOOL,    offsetof(PyGenObject, gi_running),    READONLY},
     488             :     {"gi_code",     T_OBJECT, offsetof(PyGenObject, gi_code),  READONLY},
     489             :     {NULL}      /* Sentinel */
     490             : };
     491             : 
     492             : static PyMethodDef gen_methods[] = {
     493             :     {"send",(PyCFunction)_PyGen_Send, METH_O, send_doc},
     494             :     {"throw",(PyCFunction)gen_throw, METH_VARARGS, throw_doc},
     495             :     {"close",(PyCFunction)gen_close, METH_NOARGS, close_doc},
     496             :     {NULL, NULL}        /* Sentinel */
     497             : };
     498             : 
     499             : PyTypeObject PyGen_Type = {
     500             :     PyVarObject_HEAD_INIT(&PyType_Type, 0)
     501             :     "generator",                                /* tp_name */
     502             :     sizeof(PyGenObject),                        /* tp_basicsize */
     503             :     0,                                          /* tp_itemsize */
     504             :     /* methods */
     505             :     (destructor)gen_dealloc,                    /* tp_dealloc */
     506             :     0,                                          /* tp_print */
     507             :     0,                                          /* tp_getattr */
     508             :     0,                                          /* tp_setattr */
     509             :     0,                                          /* tp_reserved */
     510             :     (reprfunc)gen_repr,                         /* tp_repr */
     511             :     0,                                          /* tp_as_number */
     512             :     0,                                          /* tp_as_sequence */
     513             :     0,                                          /* tp_as_mapping */
     514             :     0,                                          /* tp_hash */
     515             :     0,                                          /* tp_call */
     516             :     0,                                          /* tp_str */
     517             :     PyObject_GenericGetAttr,                    /* tp_getattro */
     518             :     0,                                          /* tp_setattro */
     519             :     0,                                          /* tp_as_buffer */
     520             :     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
     521             :     0,                                          /* tp_doc */
     522             :     (traverseproc)gen_traverse,                 /* tp_traverse */
     523             :     0,                                          /* tp_clear */
     524             :     0,                                          /* tp_richcompare */
     525             :     offsetof(PyGenObject, gi_weakreflist),      /* tp_weaklistoffset */
     526             :     PyObject_SelfIter,                          /* tp_iter */
     527             :     (iternextfunc)gen_iternext,                 /* tp_iternext */
     528             :     gen_methods,                                /* tp_methods */
     529             :     gen_memberlist,                             /* tp_members */
     530             :     gen_getsetlist,                             /* tp_getset */
     531             :     0,                                          /* tp_base */
     532             :     0,                                          /* tp_dict */
     533             : 
     534             :     0,                                          /* tp_descr_get */
     535             :     0,                                          /* tp_descr_set */
     536             :     0,                                          /* tp_dictoffset */
     537             :     0,                                          /* tp_init */
     538             :     0,                                          /* tp_alloc */
     539             :     0,                                          /* tp_new */
     540             :     0,                                          /* tp_free */
     541             :     0,                                          /* tp_is_gc */
     542             :     0,                                          /* tp_bases */
     543             :     0,                                          /* tp_mro */
     544             :     0,                                          /* tp_cache */
     545             :     0,                                          /* tp_subclasses */
     546             :     0,                                          /* tp_weaklist */
     547             :     gen_del,                                    /* tp_del */
     548             : };
     549             : 
     550             : PyObject *
     551         254 : PyGen_New(PyFrameObject *f)
     552             : {
     553         254 :     PyGenObject *gen = PyObject_GC_New(PyGenObject, &PyGen_Type);
     554         254 :     if (gen == NULL) {
     555           0 :         Py_DECREF(f);
     556           0 :         return NULL;
     557             :     }
     558         254 :     gen->gi_frame = f;
     559         254 :     Py_INCREF(f->f_code);
     560         254 :     gen->gi_code = (PyObject *)(f->f_code);
     561         254 :     gen->gi_running = 0;
     562         254 :     gen->gi_weakreflist = NULL;
     563         254 :     _PyObject_GC_TRACK(gen);
     564         254 :     return (PyObject *)gen;
     565             : }
     566             : 
     567             : int
     568           0 : PyGen_NeedsFinalizing(PyGenObject *gen)
     569             : {
     570             :     int i;
     571           0 :     PyFrameObject *f = gen->gi_frame;
     572             : 
     573           0 :     if (f == NULL || f->f_stacktop == NULL)
     574           0 :         return 0; /* no frame or empty blockstack == no finalization */
     575             : 
     576             :     /* Any block type besides a loop requires cleanup. */
     577           0 :     for (i = 0; i < f->f_iblock; i++)
     578           0 :         if (f->f_blockstack[i].b_type != SETUP_LOOP)
     579           0 :             return 1;
     580             : 
     581             :     /* No blocks except loops, it's safe to skip finalization. */
     582           0 :     return 0;
     583             : }

Generated by: LCOV version 1.10