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