LCOV - code coverage report
Current view: top level - Objects - weakrefobject.c (source / functions) Hit Total Coverage
Test: CPython lcov report Lines: 348 411 84.7 %
Date: 2022-07-07 18:19:46 Functions: 45 74 60.8 %

          Line data    Source code
       1             : #include "Python.h"
       2             : #include "pycore_object.h"        // _PyObject_GET_WEAKREFS_LISTPTR()
       3             : #include "structmember.h"         // PyMemberDef
       4             : 
       5             : 
       6             : #define GET_WEAKREFS_LISTPTR(o) \
       7             :         ((PyWeakReference **) _PyObject_GET_WEAKREFS_LISTPTR(o))
       8             : 
       9             : 
      10             : Py_ssize_t
      11      627612 : _PyWeakref_GetWeakrefCount(PyWeakReference *head)
      12             : {
      13      627612 :     Py_ssize_t count = 0;
      14             : 
      15     1315520 :     while (head != NULL) {
      16      687910 :         ++count;
      17      687910 :         head = head->wr_next;
      18             :     }
      19      627612 :     return count;
      20             : }
      21             : 
      22             : static PyObject *weakref_vectorcall(PyWeakReference *self, PyObject *const *args, size_t nargsf, PyObject *kwnames);
      23             : 
      24             : static void
      25     3913850 : init_weakref(PyWeakReference *self, PyObject *ob, PyObject *callback)
      26             : {
      27     3913850 :     self->hash = -1;
      28     3913850 :     self->wr_object = ob;
      29     3913850 :     self->wr_prev = NULL;
      30     3913850 :     self->wr_next = NULL;
      31     3913850 :     self->wr_callback = Py_XNewRef(callback);
      32     3913850 :     self->vectorcall = (vectorcallfunc)weakref_vectorcall;
      33     3913850 : }
      34             : 
      35             : static PyWeakReference *
      36     2719910 : new_weakref(PyObject *ob, PyObject *callback)
      37             : {
      38             :     PyWeakReference *result;
      39             : 
      40     2719910 :     result = PyObject_GC_New(PyWeakReference, &_PyWeakref_RefType);
      41     2719910 :     if (result) {
      42     2719910 :         init_weakref(result, ob, callback);
      43     2719910 :         PyObject_GC_Track(result);
      44             :     }
      45     2719910 :     return result;
      46             : }
      47             : 
      48             : 
      49             : /* This function clears the passed-in reference and removes it from the
      50             :  * list of weak references for the referent.  This is the only code that
      51             :  * removes an item from the doubly-linked list of weak references for an
      52             :  * object; it is also responsible for clearing the callback slot.
      53             :  */
      54             : static void
      55     6455540 : clear_weakref(PyWeakReference *self)
      56             : {
      57     6455540 :     PyObject *callback = self->wr_callback;
      58             : 
      59     6455540 :     if (self->wr_object != Py_None) {
      60     3787620 :         PyWeakReference **list = GET_WEAKREFS_LISTPTR(self->wr_object);
      61             : 
      62     3787620 :         if (*list == self)
      63             :             /* If 'self' is the end of the list (and thus self->wr_next == NULL)
      64             :                then the weakref list itself (and thus the value of *list) will
      65             :                end up being set to NULL. */
      66     3574630 :             *list = self->wr_next;
      67     3787620 :         self->wr_object = Py_None;
      68     3787620 :         if (self->wr_prev != NULL)
      69      212994 :             self->wr_prev->wr_next = self->wr_next;
      70     3787620 :         if (self->wr_next != NULL)
      71      257253 :             self->wr_next->wr_prev = self->wr_prev;
      72     3787620 :         self->wr_prev = NULL;
      73     3787620 :         self->wr_next = NULL;
      74             :     }
      75     6455540 :     if (callback != NULL) {
      76      703769 :         Py_DECREF(callback);
      77      703769 :         self->wr_callback = NULL;
      78             :     }
      79     6455540 : }
      80             : 
      81             : /* Cyclic gc uses this to *just* clear the passed-in reference, leaving
      82             :  * the callback intact and uncalled.  It must be possible to call self's
      83             :  * tp_dealloc() after calling this, so self has to be left in a sane enough
      84             :  * state for that to work.  We expect tp_dealloc to decref the callback
      85             :  * then.  The reason for not letting clear_weakref() decref the callback
      86             :  * right now is that if the callback goes away, that may in turn trigger
      87             :  * another callback (if a weak reference to the callback exists) -- running
      88             :  * arbitrary Python code in the middle of gc is a disaster.  The convolution
      89             :  * here allows gc to delay triggering such callbacks until the world is in
      90             :  * a sane state again.
      91             :  */
      92             : void
      93     1872750 : _PyWeakref_ClearRef(PyWeakReference *self)
      94             : {
      95             :     PyObject *callback;
      96             : 
      97     1872750 :     assert(self != NULL);
      98     1872750 :     assert(PyWeakref_Check(self));
      99             :     /* Preserve and restore the callback around clear_weakref. */
     100     1872750 :     callback = self->wr_callback;
     101     1872750 :     self->wr_callback = NULL;
     102     1872750 :     clear_weakref(self);
     103     1872750 :     self->wr_callback = callback;
     104     1872750 : }
     105             : 
     106             : static void
     107     3780240 : weakref_dealloc(PyObject *self)
     108             : {
     109     3780240 :     PyObject_GC_UnTrack(self);
     110     3780240 :     clear_weakref((PyWeakReference *) self);
     111     3780240 :     Py_TYPE(self)->tp_free(self);
     112     3780240 : }
     113             : 
     114             : 
     115             : static int
     116    68166200 : gc_traverse(PyWeakReference *self, visitproc visit, void *arg)
     117             : {
     118    68166200 :     Py_VISIT(self->wr_callback);
     119    68166200 :     return 0;
     120             : }
     121             : 
     122             : 
     123             : static int
     124          25 : gc_clear(PyWeakReference *self)
     125             : {
     126          25 :     clear_weakref(self);
     127          25 :     return 0;
     128             : }
     129             : 
     130             : 
     131             : static PyObject *
     132      975194 : weakref_vectorcall(PyWeakReference *self, PyObject *const *args,
     133             :                    size_t nargsf, PyObject *kwnames)
     134             : {
     135      975194 :     if (!_PyArg_NoKwnames("weakref", kwnames)) {
     136           0 :         return NULL;
     137             :     }
     138      975194 :     Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
     139      975194 :     if (!_PyArg_CheckPositional("weakref", nargs, 0, 0)) {
     140           0 :         return NULL;
     141             :     }
     142      975194 :     return Py_NewRef(PyWeakref_GET_OBJECT(self));
     143             : }
     144             : 
     145             : static Py_hash_t
     146     1585490 : weakref_hash(PyWeakReference *self)
     147             : {
     148     1585490 :     if (self->hash != -1)
     149     1020420 :         return self->hash;
     150      565067 :     PyObject* obj = PyWeakref_GET_OBJECT(self);
     151      565067 :     if (obj == Py_None) {
     152           2 :         PyErr_SetString(PyExc_TypeError, "weak object has gone away");
     153           2 :         return -1;
     154             :     }
     155      565065 :     Py_INCREF(obj);
     156      565065 :     self->hash = PyObject_Hash(obj);
     157      565065 :     Py_DECREF(obj);
     158      565065 :     return self->hash;
     159             : }
     160             : 
     161             : 
     162             : static PyObject *
     163           9 : weakref_repr(PyWeakReference *self)
     164             : {
     165             :     PyObject *name, *repr;
     166           9 :     PyObject* obj = PyWeakref_GET_OBJECT(self);
     167             : 
     168           9 :     if (obj == Py_None) {
     169           2 :         return PyUnicode_FromFormat("<weakref at %p; dead>", self);
     170             :     }
     171             : 
     172           7 :     Py_INCREF(obj);
     173           7 :     if (_PyObject_LookupAttr(obj, &_Py_ID(__name__), &name) < 0) {
     174           0 :         Py_DECREF(obj);
     175           0 :         return NULL;
     176             :     }
     177           7 :     if (name == NULL || !PyUnicode_Check(name)) {
     178           7 :         repr = PyUnicode_FromFormat(
     179             :             "<weakref at %p; to '%s' at %p>",
     180             :             self,
     181           7 :             Py_TYPE(PyWeakref_GET_OBJECT(self))->tp_name,
     182             :             obj);
     183             :     }
     184             :     else {
     185           0 :         repr = PyUnicode_FromFormat(
     186             :             "<weakref at %p; to '%s' at %p (%U)>",
     187             :             self,
     188           0 :             Py_TYPE(PyWeakref_GET_OBJECT(self))->tp_name,
     189             :             obj,
     190             :             name);
     191             :     }
     192           7 :     Py_DECREF(obj);
     193           7 :     Py_XDECREF(name);
     194           7 :     return repr;
     195             : }
     196             : 
     197             : /* Weak references only support equality, not ordering. Two weak references
     198             :    are equal if the underlying objects are equal. If the underlying object has
     199             :    gone away, they are equal if they are identical. */
     200             : 
     201             : static PyObject *
     202      549567 : weakref_richcompare(PyWeakReference* self, PyWeakReference* other, int op)
     203             : {
     204     1099120 :     if ((op != Py_EQ && op != Py_NE) ||
     205     1099100 :         !PyWeakref_Check(self) ||
     206      549555 :         !PyWeakref_Check(other)) {
     207          20 :         Py_RETURN_NOTIMPLEMENTED;
     208             :     }
     209      549547 :     if (PyWeakref_GET_OBJECT(self) == Py_None
     210      549536 :         || PyWeakref_GET_OBJECT(other) == Py_None) {
     211         345 :         int res = (self == other);
     212         345 :         if (op == Py_NE)
     213           3 :             res = !res;
     214         345 :         if (res)
     215           3 :             Py_RETURN_TRUE;
     216             :         else
     217         342 :             Py_RETURN_FALSE;
     218             :     }
     219      549202 :     PyObject* obj = PyWeakref_GET_OBJECT(self);
     220      549202 :     PyObject* other_obj = PyWeakref_GET_OBJECT(other);
     221      549202 :     Py_INCREF(obj);
     222      549202 :     Py_INCREF(other_obj);
     223      549202 :     PyObject* res = PyObject_RichCompare(obj, other_obj, op);
     224      549202 :     Py_DECREF(obj);
     225      549202 :     Py_DECREF(other_obj);
     226      549202 :     return res;
     227             : }
     228             : 
     229             : /* Given the head of an object's list of weak references, extract the
     230             :  * two callback-less refs (ref and proxy).  Used to determine if the
     231             :  * shared references exist and to determine the back link for newly
     232             :  * inserted references.
     233             :  */
     234             : static void
     235     8948820 : get_basic_refs(PyWeakReference *head,
     236             :                PyWeakReference **refp, PyWeakReference **proxyp)
     237             : {
     238     8948820 :     *refp = NULL;
     239     8948820 :     *proxyp = NULL;
     240             : 
     241     8948820 :     if (head != NULL && head->wr_callback == NULL) {
     242             :         /* We need to be careful that the "basic refs" aren't
     243             :            subclasses of the main types.  That complicates this a
     244             :            little. */
     245     1603760 :         if (PyWeakref_CheckRefExact(head)) {
     246     1603760 :             *refp = head;
     247     1603760 :             head = head->wr_next;
     248             :         }
     249     1603760 :         if (head != NULL
     250     1038680 :             && head->wr_callback == NULL
     251          14 :             && PyWeakref_CheckProxy(head)) {
     252          11 :             *proxyp = head;
     253             :             /* head = head->wr_next; */
     254             :         }
     255             :     }
     256     8948820 : }
     257             : 
     258             : /* Insert 'newref' in the list after 'prev'.  Both must be non-NULL. */
     259             : static void
     260      227256 : insert_after(PyWeakReference *newref, PyWeakReference *prev)
     261             : {
     262      227256 :     newref->wr_prev = prev;
     263      227256 :     newref->wr_next = prev->wr_next;
     264      227256 :     if (prev->wr_next != NULL)
     265      108269 :         prev->wr_next->wr_prev = newref;
     266      227256 :     prev->wr_next = newref;
     267      227256 : }
     268             : 
     269             : /* Insert 'newref' at the head of the list; 'list' points to the variable
     270             :  * that stores the head.
     271             :  */
     272             : static void
     273     3686590 : insert_head(PyWeakReference *newref, PyWeakReference **list)
     274             : {
     275     3686590 :     PyWeakReference *next = *list;
     276             : 
     277     3686590 :     newref->wr_prev = NULL;
     278     3686590 :     newref->wr_next = next;
     279     3686590 :     if (next != NULL)
     280      189301 :         next->wr_prev = newref;
     281     3686590 :     *list = newref;
     282     3686590 : }
     283             : 
     284             : static int
     285     2413120 : parse_weakref_init_args(const char *funcname, PyObject *args, PyObject *kwargs,
     286             :                         PyObject **obp, PyObject **callbackp)
     287             : {
     288     2413120 :     return PyArg_UnpackTuple(args, funcname, 1, 2, obp, callbackp);
     289             : }
     290             : 
     291             : static PyObject *
     292     1206570 : weakref___new__(PyTypeObject *type, PyObject *args, PyObject *kwargs)
     293             : {
     294     1206570 :     PyWeakReference *self = NULL;
     295     1206570 :     PyObject *ob, *callback = NULL;
     296             : 
     297     1206570 :     if (parse_weakref_init_args("__new__", args, kwargs, &ob, &callback)) {
     298             :         PyWeakReference *ref, *proxy;
     299             :         PyWeakReference **list;
     300             : 
     301     1206570 :         if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(ob))) {
     302          18 :             PyErr_Format(PyExc_TypeError,
     303             :                          "cannot create weak reference to '%s' object",
     304          18 :                          Py_TYPE(ob)->tp_name);
     305       12634 :             return NULL;
     306             :         }
     307     1206550 :         if (callback == Py_None)
     308           5 :             callback = NULL;
     309     1206550 :         list = GET_WEAKREFS_LISTPTR(ob);
     310     1206550 :         get_basic_refs(*list, &ref, &proxy);
     311     1206550 :         if (callback == NULL && type == &_PyWeakref_RefType) {
     312       40744 :             if (ref != NULL) {
     313             :                 /* We can re-use an existing reference. */
     314       12616 :                 Py_INCREF(ref);
     315       12616 :                 return (PyObject *)ref;
     316             :             }
     317             :         }
     318             :         /* We have to create a new reference. */
     319             :         /* Note: the tp_alloc() can trigger cyclic GC, so the weakref
     320             :            list on ob can be mutated.  This means that the ref and
     321             :            proxy pointers we got back earlier may have been collected,
     322             :            so we need to compute these values again before we use
     323             :            them. */
     324     1193930 :         self = (PyWeakReference *) (type->tp_alloc(type, 0));
     325     1193930 :         if (self != NULL) {
     326     1193930 :             init_weakref(self, ob, callback);
     327     1193930 :             if (callback == NULL && type == &_PyWeakref_RefType) {
     328       28128 :                 insert_head(self, list);
     329             :             }
     330             :             else {
     331             :                 PyWeakReference *prev;
     332             : 
     333     1165810 :                 get_basic_refs(*list, &ref, &proxy);
     334     1165810 :                 prev = (proxy == NULL) ? ref : proxy;
     335     1165810 :                 if (prev == NULL)
     336     1164840 :                     insert_head(self, list);
     337             :                 else
     338         963 :                     insert_after(self, prev);
     339             :             }
     340             :         }
     341             :     }
     342     1193930 :     return (PyObject *)self;
     343             : }
     344             : 
     345             : static int
     346     1206550 : weakref___init__(PyObject *self, PyObject *args, PyObject *kwargs)
     347             : {
     348             :     PyObject *tmp;
     349             : 
     350     1206550 :     if (!_PyArg_NoKeywords("ref", kwargs))
     351           1 :         return -1;
     352             : 
     353     1206550 :     if (parse_weakref_init_args("__init__", args, kwargs, &tmp, &tmp))
     354     1206550 :         return 0;
     355             :     else
     356           1 :         return -1;
     357             : }
     358             : 
     359             : 
     360             : static PyMemberDef weakref_members[] = {
     361             :     {"__callback__", T_OBJECT, offsetof(PyWeakReference, wr_callback), READONLY},
     362             :     {NULL} /* Sentinel */
     363             : };
     364             : 
     365             : static PyMethodDef weakref_methods[] = {
     366             :     {"__class_getitem__",    Py_GenericAlias,
     367             :     METH_O|METH_CLASS,       PyDoc_STR("See PEP 585")},
     368             :     {NULL} /* Sentinel */
     369             : };
     370             : 
     371             : PyTypeObject
     372             : _PyWeakref_RefType = {
     373             :     PyVarObject_HEAD_INIT(&PyType_Type, 0)
     374             :     .tp_name = "weakref.ReferenceType",
     375             :     .tp_basicsize = sizeof(PyWeakReference),
     376             :     .tp_dealloc = weakref_dealloc,
     377             :     .tp_vectorcall_offset = offsetof(PyWeakReference, vectorcall),
     378             :     .tp_call = PyVectorcall_Call,
     379             :     .tp_repr = (reprfunc)weakref_repr,
     380             :     .tp_hash = (hashfunc)weakref_hash,
     381             :     .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
     382             :                 Py_TPFLAGS_HAVE_VECTORCALL | Py_TPFLAGS_BASETYPE,
     383             :     .tp_traverse = (traverseproc)gc_traverse,
     384             :     .tp_clear = (inquiry)gc_clear,
     385             :     .tp_richcompare = (richcmpfunc)weakref_richcompare,
     386             :     .tp_methods = weakref_methods,
     387             :     .tp_members = weakref_members,
     388             :     .tp_init = weakref___init__,
     389             :     .tp_alloc = PyType_GenericAlloc,
     390             :     .tp_new = weakref___new__,
     391             :     .tp_free = PyObject_GC_Del,
     392             : };
     393             : 
     394             : 
     395             : static int
     396       19338 : proxy_checkref(PyWeakReference *proxy)
     397             : {
     398       19338 :     if (PyWeakref_GET_OBJECT(proxy) == Py_None) {
     399          29 :         PyErr_SetString(PyExc_ReferenceError,
     400             :                         "weakly-referenced object no longer exists");
     401          29 :         return 0;
     402             :     }
     403       19309 :     return 1;
     404             : }
     405             : 
     406             : 
     407             : /* If a parameter is a proxy, check that it is still "live" and wrap it,
     408             :  * replacing the original value with the raw object.  Raises ReferenceError
     409             :  * if the param is a dead proxy.
     410             :  */
     411             : #define UNWRAP(o) \
     412             :         if (PyWeakref_CheckProxy(o)) { \
     413             :             if (!proxy_checkref((PyWeakReference *)o)) \
     414             :                 return NULL; \
     415             :             o = PyWeakref_GET_OBJECT(o); \
     416             :         }
     417             : 
     418             : #define WRAP_UNARY(method, generic) \
     419             :     static PyObject * \
     420             :     method(PyObject *proxy) { \
     421             :         UNWRAP(proxy); \
     422             :         Py_INCREF(proxy); \
     423             :         PyObject* res = generic(proxy); \
     424             :         Py_DECREF(proxy); \
     425             :         return res; \
     426             :     }
     427             : 
     428             : #define WRAP_BINARY(method, generic) \
     429             :     static PyObject * \
     430             :     method(PyObject *x, PyObject *y) { \
     431             :         UNWRAP(x); \
     432             :         UNWRAP(y); \
     433             :         Py_INCREF(x); \
     434             :         Py_INCREF(y); \
     435             :         PyObject* res = generic(x, y); \
     436             :         Py_DECREF(x); \
     437             :         Py_DECREF(y); \
     438             :         return res; \
     439             :     }
     440             : 
     441             : /* Note that the third arg needs to be checked for NULL since the tp_call
     442             :  * slot can receive NULL for this arg.
     443             :  */
     444             : #define WRAP_TERNARY(method, generic) \
     445             :     static PyObject * \
     446             :     method(PyObject *proxy, PyObject *v, PyObject *w) { \
     447             :         UNWRAP(proxy); \
     448             :         UNWRAP(v); \
     449             :         if (w != NULL) \
     450             :             UNWRAP(w); \
     451             :         Py_INCREF(proxy); \
     452             :         Py_INCREF(v); \
     453             :         Py_XINCREF(w); \
     454             :         PyObject* res = generic(proxy, v, w); \
     455             :         Py_DECREF(proxy); \
     456             :         Py_DECREF(v); \
     457             :         Py_XDECREF(w); \
     458             :         return res; \
     459             :     }
     460             : 
     461             : #define WRAP_METHOD(method, SPECIAL) \
     462             :     static PyObject * \
     463             :     method(PyObject *proxy, PyObject *Py_UNUSED(ignored)) { \
     464             :             UNWRAP(proxy); \
     465             :             Py_INCREF(proxy); \
     466             :             PyObject* res = PyObject_CallMethodNoArgs(proxy, &_Py_ID(SPECIAL)); \
     467             :             Py_DECREF(proxy); \
     468             :             return res; \
     469             :         }
     470             : 
     471             : 
     472             : /* direct slots */
     473             : 
     474        5734 : WRAP_BINARY(proxy_getattr, PyObject_GetAttr)
     475           8 : WRAP_UNARY(proxy_str, PyObject_Str)
     476           4 : WRAP_TERNARY(proxy_call, PyObject_Call)
     477             : 
     478             : static PyObject *
     479           0 : proxy_repr(PyWeakReference *proxy)
     480             : {
     481           0 :     return PyUnicode_FromFormat(
     482             :         "<weakproxy at %p to %s at %p>",
     483             :         proxy,
     484           0 :         Py_TYPE(PyWeakref_GET_OBJECT(proxy))->tp_name,
     485             :         PyWeakref_GET_OBJECT(proxy));
     486             : }
     487             : 
     488             : 
     489             : static int
     490       13543 : proxy_setattr(PyWeakReference *proxy, PyObject *name, PyObject *value)
     491             : {
     492       13543 :     if (!proxy_checkref(proxy))
     493           0 :         return -1;
     494       13543 :     PyObject *obj = PyWeakref_GET_OBJECT(proxy);
     495       13543 :     Py_INCREF(obj);
     496       13543 :     int res = PyObject_SetAttr(obj, name, value);
     497       13543 :     Py_DECREF(obj);
     498       13543 :     return res;
     499             : }
     500             : 
     501             : static PyObject *
     502           1 : proxy_richcompare(PyObject *proxy, PyObject *v, int op)
     503             : {
     504           1 :     UNWRAP(proxy);
     505           1 :     UNWRAP(v);
     506           1 :     return PyObject_RichCompare(proxy, v, op);
     507             : }
     508             : 
     509             : /* number slots */
     510           2 : WRAP_BINARY(proxy_add, PyNumber_Add)
     511           0 : WRAP_BINARY(proxy_sub, PyNumber_Subtract)
     512           0 : WRAP_BINARY(proxy_mul, PyNumber_Multiply)
     513           1 : WRAP_BINARY(proxy_floor_div, PyNumber_FloorDivide)
     514           0 : WRAP_BINARY(proxy_true_div, PyNumber_TrueDivide)
     515           0 : WRAP_BINARY(proxy_mod, PyNumber_Remainder)
     516           0 : WRAP_BINARY(proxy_divmod, PyNumber_Divmod)
     517           0 : WRAP_TERNARY(proxy_pow, PyNumber_Power)
     518           0 : WRAP_UNARY(proxy_neg, PyNumber_Negative)
     519           0 : WRAP_UNARY(proxy_pos, PyNumber_Positive)
     520           0 : WRAP_UNARY(proxy_abs, PyNumber_Absolute)
     521           0 : WRAP_UNARY(proxy_invert, PyNumber_Invert)
     522           0 : WRAP_BINARY(proxy_lshift, PyNumber_Lshift)
     523           0 : WRAP_BINARY(proxy_rshift, PyNumber_Rshift)
     524           0 : WRAP_BINARY(proxy_and, PyNumber_And)
     525           0 : WRAP_BINARY(proxy_xor, PyNumber_Xor)
     526           0 : WRAP_BINARY(proxy_or, PyNumber_Or)
     527           0 : WRAP_UNARY(proxy_int, PyNumber_Long)
     528           0 : WRAP_UNARY(proxy_float, PyNumber_Float)
     529           0 : WRAP_BINARY(proxy_iadd, PyNumber_InPlaceAdd)
     530           0 : WRAP_BINARY(proxy_isub, PyNumber_InPlaceSubtract)
     531           0 : WRAP_BINARY(proxy_imul, PyNumber_InPlaceMultiply)
     532           1 : WRAP_BINARY(proxy_ifloor_div, PyNumber_InPlaceFloorDivide)
     533           0 : WRAP_BINARY(proxy_itrue_div, PyNumber_InPlaceTrueDivide)
     534           0 : WRAP_BINARY(proxy_imod, PyNumber_InPlaceRemainder)
     535           0 : WRAP_TERNARY(proxy_ipow, PyNumber_InPlacePower)
     536           0 : WRAP_BINARY(proxy_ilshift, PyNumber_InPlaceLshift)
     537           0 : WRAP_BINARY(proxy_irshift, PyNumber_InPlaceRshift)
     538           0 : WRAP_BINARY(proxy_iand, PyNumber_InPlaceAnd)
     539           0 : WRAP_BINARY(proxy_ixor, PyNumber_InPlaceXor)
     540           0 : WRAP_BINARY(proxy_ior, PyNumber_InPlaceOr)
     541           1 : WRAP_UNARY(proxy_index, PyNumber_Index)
     542           2 : WRAP_BINARY(proxy_matmul, PyNumber_MatrixMultiply)
     543           1 : WRAP_BINARY(proxy_imatmul, PyNumber_InPlaceMatrixMultiply)
     544             : 
     545             : static int
     546           4 : proxy_bool(PyWeakReference *proxy)
     547             : {
     548           4 :     PyObject *o = PyWeakref_GET_OBJECT(proxy);
     549           4 :     if (!proxy_checkref(proxy)) {
     550           1 :         return -1;
     551             :     }
     552           3 :     Py_INCREF(o);
     553           3 :     int res = PyObject_IsTrue(o);
     554           3 :     Py_DECREF(o);
     555           3 :     return res;
     556             : }
     557             : 
     558             : static void
     559        6983 : proxy_dealloc(PyWeakReference *self)
     560             : {
     561        6983 :     if (self->wr_callback != NULL)
     562         125 :         PyObject_GC_UnTrack((PyObject *)self);
     563        6983 :     clear_weakref(self);
     564        6983 :     PyObject_GC_Del(self);
     565        6983 : }
     566             : 
     567             : /* sequence slots */
     568             : 
     569             : static int
     570           2 : proxy_contains(PyWeakReference *proxy, PyObject *value)
     571             : {
     572           2 :     if (!proxy_checkref(proxy))
     573           0 :         return -1;
     574             : 
     575           2 :     PyObject *obj = PyWeakref_GET_OBJECT(proxy);
     576           2 :     Py_INCREF(obj);
     577           2 :     int res = PySequence_Contains(obj, value);
     578           2 :     Py_DECREF(obj);
     579           2 :     return res;
     580             : }
     581             : 
     582             : /* mapping slots */
     583             : 
     584             : static Py_ssize_t
     585          16 : proxy_length(PyWeakReference *proxy)
     586             : {
     587          16 :     if (!proxy_checkref(proxy))
     588          13 :         return -1;
     589             : 
     590           3 :     PyObject *obj = PyWeakref_GET_OBJECT(proxy);
     591           3 :     Py_INCREF(obj);
     592           3 :     Py_ssize_t res = PyObject_Length(obj);
     593           3 :     Py_DECREF(obj);
     594           3 :     return res;
     595             : }
     596             : 
     597           5 : WRAP_BINARY(proxy_getitem, PyObject_GetItem)
     598             : 
     599             : static int
     600           3 : proxy_setitem(PyWeakReference *proxy, PyObject *key, PyObject *value)
     601             : {
     602           3 :     if (!proxy_checkref(proxy))
     603           0 :         return -1;
     604             : 
     605           3 :     PyObject *obj = PyWeakref_GET_OBJECT(proxy);
     606           3 :     Py_INCREF(obj);
     607             :     int res;
     608           3 :     if (value == NULL) {
     609           1 :         res = PyObject_DelItem(obj, key);
     610             :     } else {
     611           2 :         res = PyObject_SetItem(obj, key, value);
     612             :     }
     613           3 :     Py_DECREF(obj);
     614           3 :     return res;
     615             : }
     616             : 
     617             : /* iterator slots */
     618             : 
     619             : static PyObject *
     620           2 : proxy_iter(PyWeakReference *proxy)
     621             : {
     622           2 :     if (!proxy_checkref(proxy))
     623           0 :         return NULL;
     624           2 :     PyObject *obj = PyWeakref_GET_OBJECT(proxy);
     625           2 :     Py_INCREF(obj);
     626           2 :     PyObject* res = PyObject_GetIter(obj);
     627           2 :     Py_DECREF(obj);
     628           2 :     return res;
     629             : }
     630             : 
     631             : static PyObject *
     632           5 : proxy_iternext(PyWeakReference *proxy)
     633             : {
     634           5 :     if (!proxy_checkref(proxy))
     635           0 :         return NULL;
     636             : 
     637           5 :     PyObject *obj = PyWeakref_GET_OBJECT(proxy);
     638           5 :     if (!PyIter_Check(obj)) {
     639           1 :         PyErr_Format(PyExc_TypeError,
     640             :             "Weakref proxy referenced a non-iterator '%.200s' object",
     641           1 :             Py_TYPE(obj)->tp_name);
     642           1 :         return NULL;
     643             :     }
     644           4 :     Py_INCREF(obj);
     645           4 :     PyObject* res = PyIter_Next(obj);
     646           4 :     Py_DECREF(obj);
     647           4 :     return res;
     648             : }
     649             : 
     650             : 
     651           1 : WRAP_METHOD(proxy_bytes, __bytes__)
     652           1 : WRAP_METHOD(proxy_reversed, __reversed__)
     653             : 
     654             : 
     655             : static PyMethodDef proxy_methods[] = {
     656             :         {"__bytes__", proxy_bytes, METH_NOARGS},
     657             :         {"__reversed__", proxy_reversed, METH_NOARGS},
     658             :         {NULL, NULL}
     659             : };
     660             : 
     661             : 
     662             : static PyNumberMethods proxy_as_number = {
     663             :     proxy_add,              /*nb_add*/
     664             :     proxy_sub,              /*nb_subtract*/
     665             :     proxy_mul,              /*nb_multiply*/
     666             :     proxy_mod,              /*nb_remainder*/
     667             :     proxy_divmod,           /*nb_divmod*/
     668             :     proxy_pow,              /*nb_power*/
     669             :     proxy_neg,              /*nb_negative*/
     670             :     proxy_pos,              /*nb_positive*/
     671             :     proxy_abs,              /*nb_absolute*/
     672             :     (inquiry)proxy_bool,    /*nb_bool*/
     673             :     proxy_invert,           /*nb_invert*/
     674             :     proxy_lshift,           /*nb_lshift*/
     675             :     proxy_rshift,           /*nb_rshift*/
     676             :     proxy_and,              /*nb_and*/
     677             :     proxy_xor,              /*nb_xor*/
     678             :     proxy_or,               /*nb_or*/
     679             :     proxy_int,              /*nb_int*/
     680             :     0,                      /*nb_reserved*/
     681             :     proxy_float,            /*nb_float*/
     682             :     proxy_iadd,             /*nb_inplace_add*/
     683             :     proxy_isub,             /*nb_inplace_subtract*/
     684             :     proxy_imul,             /*nb_inplace_multiply*/
     685             :     proxy_imod,             /*nb_inplace_remainder*/
     686             :     proxy_ipow,             /*nb_inplace_power*/
     687             :     proxy_ilshift,          /*nb_inplace_lshift*/
     688             :     proxy_irshift,          /*nb_inplace_rshift*/
     689             :     proxy_iand,             /*nb_inplace_and*/
     690             :     proxy_ixor,             /*nb_inplace_xor*/
     691             :     proxy_ior,              /*nb_inplace_or*/
     692             :     proxy_floor_div,        /*nb_floor_divide*/
     693             :     proxy_true_div,         /*nb_true_divide*/
     694             :     proxy_ifloor_div,       /*nb_inplace_floor_divide*/
     695             :     proxy_itrue_div,        /*nb_inplace_true_divide*/
     696             :     proxy_index,            /*nb_index*/
     697             :     proxy_matmul,           /*nb_matrix_multiply*/
     698             :     proxy_imatmul,          /*nb_inplace_matrix_multiply*/
     699             : };
     700             : 
     701             : static PySequenceMethods proxy_as_sequence = {
     702             :     (lenfunc)proxy_length,      /*sq_length*/
     703             :     0,                          /*sq_concat*/
     704             :     0,                          /*sq_repeat*/
     705             :     0,                          /*sq_item*/
     706             :     0,                          /*sq_slice*/
     707             :     0,                          /*sq_ass_item*/
     708             :     0,                           /*sq_ass_slice*/
     709             :     (objobjproc)proxy_contains, /* sq_contains */
     710             : };
     711             : 
     712             : static PyMappingMethods proxy_as_mapping = {
     713             :     (lenfunc)proxy_length,        /*mp_length*/
     714             :     proxy_getitem,                /*mp_subscript*/
     715             :     (objobjargproc)proxy_setitem, /*mp_ass_subscript*/
     716             : };
     717             : 
     718             : 
     719             : PyTypeObject
     720             : _PyWeakref_ProxyType = {
     721             :     PyVarObject_HEAD_INIT(&PyType_Type, 0)
     722             :     "weakref.ProxyType",
     723             :     sizeof(PyWeakReference),
     724             :     0,
     725             :     /* methods */
     726             :     (destructor)proxy_dealloc,          /* tp_dealloc */
     727             :     0,                                  /* tp_vectorcall_offset */
     728             :     0,                                  /* tp_getattr */
     729             :     0,                                  /* tp_setattr */
     730             :     0,                                  /* tp_as_async */
     731             :     (reprfunc)proxy_repr,               /* tp_repr */
     732             :     &proxy_as_number,                   /* tp_as_number */
     733             :     &proxy_as_sequence,                 /* tp_as_sequence */
     734             :     &proxy_as_mapping,                  /* tp_as_mapping */
     735             : // Notice that tp_hash is intentionally omitted as proxies are "mutable" (when the reference dies).
     736             :     0,                                  /* tp_hash */
     737             :     0,                                  /* tp_call */
     738             :     proxy_str,                          /* tp_str */
     739             :     proxy_getattr,                      /* tp_getattro */
     740             :     (setattrofunc)proxy_setattr,        /* tp_setattro */
     741             :     0,                                  /* tp_as_buffer */
     742             :     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
     743             :     0,                                  /* tp_doc */
     744             :     (traverseproc)gc_traverse,          /* tp_traverse */
     745             :     (inquiry)gc_clear,                  /* tp_clear */
     746             :     proxy_richcompare,                  /* tp_richcompare */
     747             :     0,                                  /* tp_weaklistoffset */
     748             :     (getiterfunc)proxy_iter,            /* tp_iter */
     749             :     (iternextfunc)proxy_iternext,       /* tp_iternext */
     750             :         proxy_methods,                      /* tp_methods */
     751             : };
     752             : 
     753             : 
     754             : PyTypeObject
     755             : _PyWeakref_CallableProxyType = {
     756             :     PyVarObject_HEAD_INIT(&PyType_Type, 0)
     757             :     "weakref.CallableProxyType",
     758             :     sizeof(PyWeakReference),
     759             :     0,
     760             :     /* methods */
     761             :     (destructor)proxy_dealloc,          /* tp_dealloc */
     762             :     0,                                  /* tp_vectorcall_offset */
     763             :     0,                                  /* tp_getattr */
     764             :     0,                                  /* tp_setattr */
     765             :     0,                                  /* tp_as_async */
     766             :     (unaryfunc)proxy_repr,              /* tp_repr */
     767             :     &proxy_as_number,                   /* tp_as_number */
     768             :     &proxy_as_sequence,                 /* tp_as_sequence */
     769             :     &proxy_as_mapping,                  /* tp_as_mapping */
     770             :     0,                                  /* tp_hash */
     771             :     proxy_call,                         /* tp_call */
     772             :     proxy_str,                          /* tp_str */
     773             :     proxy_getattr,                      /* tp_getattro */
     774             :     (setattrofunc)proxy_setattr,        /* tp_setattro */
     775             :     0,                                  /* tp_as_buffer */
     776             :     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
     777             :     0,                                  /* tp_doc */
     778             :     (traverseproc)gc_traverse,          /* tp_traverse */
     779             :     (inquiry)gc_clear,                  /* tp_clear */
     780             :     proxy_richcompare,                  /* tp_richcompare */
     781             :     0,                                  /* tp_weaklistoffset */
     782             :     (getiterfunc)proxy_iter,            /* tp_iter */
     783             :     (iternextfunc)proxy_iternext,       /* tp_iternext */
     784             : };
     785             : 
     786             : 
     787             : 
     788             : PyObject *
     789     3849550 : PyWeakref_NewRef(PyObject *ob, PyObject *callback)
     790             : {
     791     3849550 :     PyWeakReference *result = NULL;
     792             :     PyWeakReference **list;
     793             :     PyWeakReference *ref, *proxy;
     794             : 
     795     3849550 :     if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(ob))) {
     796           0 :         PyErr_Format(PyExc_TypeError,
     797             :                      "cannot create weak reference to '%s' object",
     798           0 :                      Py_TYPE(ob)->tp_name);
     799           0 :         return NULL;
     800             :     }
     801     3849550 :     list = GET_WEAKREFS_LISTPTR(ob);
     802     3849550 :     get_basic_refs(*list, &ref, &proxy);
     803     3849550 :     if (callback == Py_None)
     804           0 :         callback = NULL;
     805     3849550 :     if (callback == NULL)
     806             :         /* return existing weak reference if it exists */
     807     3621400 :         result = ref;
     808     3849550 :     if (result != NULL)
     809     1136630 :         Py_INCREF(result);
     810             :     else {
     811             :         /* Note: new_weakref() can trigger cyclic GC, so the weakref
     812             :            list on ob can be mutated.  This means that the ref and
     813             :            proxy pointers we got back earlier may have been collected,
     814             :            so we need to compute these values again before we use
     815             :            them. */
     816     2712920 :         result = new_weakref(ob, callback);
     817     2712920 :         if (result != NULL) {
     818     2712920 :             get_basic_refs(*list, &ref, &proxy);
     819     2712920 :             if (callback == NULL) {
     820     2484770 :                 if (ref == NULL)
     821     2484770 :                     insert_head(result, list);
     822             :                 else {
     823             :                     /* Someone else added a ref without a callback
     824             :                        during GC.  Return that one instead of this one
     825             :                        to avoid violating the invariants of the list
     826             :                        of weakrefs for ob. */
     827           0 :                     Py_DECREF(result);
     828           0 :                     Py_INCREF(ref);
     829           0 :                     result = ref;
     830             :                 }
     831             :             }
     832             :             else {
     833             :                 PyWeakReference *prev;
     834             : 
     835      228153 :                 prev = (proxy == NULL) ? ref : proxy;
     836      228153 :                 if (prev == NULL)
     837        1997 :                     insert_head(result, list);
     838             :                 else
     839      226156 :                     insert_after(result, prev);
     840             :             }
     841             :         }
     842             :     }
     843     3849550 :     return (PyObject *) result;
     844             : }
     845             : 
     846             : 
     847             : PyObject *
     848        6994 : PyWeakref_NewProxy(PyObject *ob, PyObject *callback)
     849             : {
     850        6994 :     PyWeakReference *result = NULL;
     851             :     PyWeakReference **list;
     852             :     PyWeakReference *ref, *proxy;
     853             : 
     854        6994 :     if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(ob))) {
     855           0 :         PyErr_Format(PyExc_TypeError,
     856             :                      "cannot create weak reference to '%s' object",
     857           0 :                      Py_TYPE(ob)->tp_name);
     858           0 :         return NULL;
     859             :     }
     860        6994 :     list = GET_WEAKREFS_LISTPTR(ob);
     861        6994 :     get_basic_refs(*list, &ref, &proxy);
     862        6994 :     if (callback == Py_None)
     863           4 :         callback = NULL;
     864        6994 :     if (callback == NULL)
     865             :         /* attempt to return an existing weak reference if it exists */
     866        6861 :         result = proxy;
     867        6994 :     if (result != NULL)
     868           5 :         Py_INCREF(result);
     869             :     else {
     870             :         /* Note: new_weakref() can trigger cyclic GC, so the weakref
     871             :            list on ob can be mutated.  This means that the ref and
     872             :            proxy pointers we got back earlier may have been collected,
     873             :            so we need to compute these values again before we use
     874             :            them. */
     875        6989 :         result = new_weakref(ob, callback);
     876        6989 :         if (result != NULL) {
     877             :             PyWeakReference *prev;
     878             : 
     879        6989 :             if (PyCallable_Check(ob)) {
     880         138 :                 Py_SET_TYPE(result, &_PyWeakref_CallableProxyType);
     881             :             }
     882             :             else {
     883        6851 :                 Py_SET_TYPE(result, &_PyWeakref_ProxyType);
     884             :             }
     885        6989 :             get_basic_refs(*list, &ref, &proxy);
     886        6989 :             if (callback == NULL) {
     887        6856 :                 if (proxy != NULL) {
     888             :                     /* Someone else added a proxy without a callback
     889             :                        during GC.  Return that one instead of this one
     890             :                        to avoid violating the invariants of the list
     891             :                        of weakrefs for ob. */
     892           0 :                     Py_DECREF(result);
     893           0 :                     result = proxy;
     894           0 :                     Py_INCREF(result);
     895           0 :                     goto skip_insert;
     896             :                 }
     897        6856 :                 prev = ref;
     898             :             }
     899             :             else
     900         133 :                 prev = (proxy == NULL) ? ref : proxy;
     901             : 
     902        6989 :             if (prev == NULL)
     903        6852 :                 insert_head(result, list);
     904             :             else
     905         137 :                 insert_after(result, prev);
     906           0 :         skip_insert:
     907             :             ;
     908             :         }
     909             :     }
     910        6994 :     return (PyObject *) result;
     911             : }
     912             : 
     913             : 
     914             : PyObject *
     915       99268 : PyWeakref_GetObject(PyObject *ref)
     916             : {
     917       99268 :     if (ref == NULL || !PyWeakref_Check(ref)) {
     918           0 :         PyErr_BadInternalCall();
     919           0 :         return NULL;
     920             :     }
     921       99268 :     return PyWeakref_GET_OBJECT(ref);
     922             : }
     923             : 
     924             : /* Note that there's an inlined copy-paste of handle_callback() in gcmodule.c's
     925             :  * handle_weakrefs().
     926             :  */
     927             : static void
     928      687863 : handle_callback(PyWeakReference *ref, PyObject *callback)
     929             : {
     930      687863 :     PyObject *cbresult = PyObject_CallOneArg(callback, (PyObject *)ref);
     931             : 
     932      687863 :     if (cbresult == NULL)
     933          14 :         PyErr_WriteUnraisable(callback);
     934             :     else
     935      687849 :         Py_DECREF(cbresult);
     936      687863 : }
     937             : 
     938             : /* This function is called by the tp_dealloc handler to clear weak references.
     939             :  *
     940             :  * This iterates through the weak references for 'object' and calls callbacks
     941             :  * for those references which have one.  It returns when all callbacks have
     942             :  * been attempted.
     943             :  */
     944             : void
     945    20099900 : PyObject_ClearWeakRefs(PyObject *object)
     946             : {
     947             :     PyWeakReference **list;
     948             : 
     949    20099900 :     if (object == NULL
     950    20099900 :         || !_PyType_SUPPORTS_WEAKREFS(Py_TYPE(object))
     951    20099900 :         || Py_REFCNT(object) != 0)
     952             :     {
     953           0 :         PyErr_BadInternalCall();
     954           0 :         return;
     955             :     }
     956    20099900 :     list = GET_WEAKREFS_LISTPTR(object);
     957             :     /* Remove the callback-less basic and proxy references */
     958    20099900 :     if (*list != NULL && (*list)->wr_callback == NULL) {
     959      107675 :         clear_weakref(*list);
     960      107675 :         if (*list != NULL && (*list)->wr_callback == NULL)
     961           4 :             clear_weakref(*list);
     962             :     }
     963    20099900 :     if (*list != NULL) {
     964      627579 :         PyWeakReference *current = *list;
     965      627579 :         Py_ssize_t count = _PyWeakref_GetWeakrefCount(current);
     966             :         PyObject *err_type, *err_value, *err_tb;
     967             : 
     968      627579 :         PyErr_Fetch(&err_type, &err_value, &err_tb);
     969      627579 :         if (count == 1) {
     970      567353 :             PyObject *callback = current->wr_callback;
     971             : 
     972      567353 :             current->wr_callback = NULL;
     973      567353 :             clear_weakref(current);
     974      567353 :             if (callback != NULL) {
     975      567352 :                 if (Py_REFCNT((PyObject *)current) > 0) {
     976      567351 :                     handle_callback(current, callback);
     977             :                 }
     978      567352 :                 Py_DECREF(callback);
     979             :             }
     980             :         }
     981             :         else {
     982             :             PyObject *tuple;
     983       60226 :             Py_ssize_t i = 0;
     984             : 
     985       60226 :             tuple = PyTuple_New(count * 2);
     986       60226 :             if (tuple == NULL) {
     987           0 :                 _PyErr_ChainExceptions(err_type, err_value, err_tb);
     988           0 :                 return;
     989             :             }
     990             : 
     991      180740 :             for (i = 0; i < count; ++i) {
     992      120514 :                 PyWeakReference *next = current->wr_next;
     993             : 
     994      120514 :                 if (Py_REFCNT((PyObject *)current) > 0) {
     995      120512 :                     Py_INCREF(current);
     996      120512 :                     PyTuple_SET_ITEM(tuple, i * 2, (PyObject *) current);
     997      120512 :                     PyTuple_SET_ITEM(tuple, i * 2 + 1, current->wr_callback);
     998             :                 }
     999             :                 else {
    1000           2 :                     Py_DECREF(current->wr_callback);
    1001             :                 }
    1002      120514 :                 current->wr_callback = NULL;
    1003      120514 :                 clear_weakref(current);
    1004      120514 :                 current = next;
    1005             :             }
    1006      180740 :             for (i = 0; i < count; ++i) {
    1007      120514 :                 PyObject *callback = PyTuple_GET_ITEM(tuple, i * 2 + 1);
    1008             : 
    1009             :                 /* The tuple may have slots left to NULL */
    1010      120514 :                 if (callback != NULL) {
    1011      120512 :                     PyObject *item = PyTuple_GET_ITEM(tuple, i * 2);
    1012      120512 :                     handle_callback((PyWeakReference *)item, callback);
    1013             :                 }
    1014             :             }
    1015       60226 :             Py_DECREF(tuple);
    1016             :         }
    1017      627579 :         assert(!PyErr_Occurred());
    1018      627579 :         PyErr_Restore(err_type, err_value, err_tb);
    1019             :     }
    1020             : }

Generated by: LCOV version 1.14