LCOV - code coverage report
Current view: top level - Objects - weakrefobject.c (source / functions) Hit Total Coverage
Test: CPython lcov report Lines: 163 374 43.6 %
Date: 2017-04-19 Functions: 17 76 22.4 %

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

Generated by: LCOV version 1.10