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