Coverage Report

Created: 2026-10-17 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Objects/weakrefobject.c
Line
Count
Source
1
#include "Python.h"
2
#include "pycore_critical_section.h"
3
#include "pycore_lock.h"
4
#include "pycore_modsupport.h"    // _PyArg_NoKwnames()
5
#include "pycore_object.h"        // _PyObject_GET_WEAKREFS_LISTPTR()
6
#include "pycore_pyerrors.h"      // _PyErr_ChainExceptions1()
7
#include "pycore_pystate.h"
8
#include "pycore_weakref.h"       // _PyWeakref_GET_REF()
9
10
#ifdef Py_GIL_DISABLED
11
/*
12
 * Thread-safety for free-threaded builds
13
 * ======================================
14
 *
15
 * In free-threaded builds we need to protect mutable state of:
16
 *
17
 * - The weakref (wr_object, hash, wr_callback)
18
 * - The referenced object (its head-of-list pointer)
19
 * - The linked list of weakrefs
20
 *
21
 * For now we've chosen to address this in a straightforward way:
22
 *
23
 * - The weakref's hash is protected using the weakref's per-object lock.
24
 * - The other mutable is protected by a striped lock keyed on the referenced
25
 *   object's address.
26
 * - The striped lock must be locked using `_Py_LOCK_DONT_DETACH` in order to
27
 *   support atomic deletion from WeakValueDictionaries. As a result, we must
28
 *   be careful not to perform any operations that could suspend while the
29
 *   lock is held.
30
 *
31
 * Since the world is stopped when the GC runs, it is free to clear weakrefs
32
 * without acquiring any locks.
33
 */
34
35
#endif
36
37
#define GET_WEAKREFS_LISTPTR(o) \
38
51.2M
        ((PyWeakReference **) _PyObject_GET_WEAKREFS_LISTPTR(o))
39
40
41
Py_ssize_t
42
_PyWeakref_GetWeakrefCount(PyObject *obj)
43
26.5k
{
44
26.5k
    if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(obj))) {
45
0
        return 0;
46
0
    }
47
48
26.5k
    LOCK_WEAKREFS(obj);
49
26.5k
    Py_ssize_t count = 0;
50
26.5k
    PyWeakReference *head = *GET_WEAKREFS_LISTPTR(obj);
51
53.1k
    while (head != NULL) {
52
26.5k
        ++count;
53
26.5k
        head = head->wr_next;
54
26.5k
    }
55
26.5k
    UNLOCK_WEAKREFS(obj);
56
26.5k
    return count;
57
26.5k
}
58
59
static PyObject *weakref_vectorcall(PyObject *self, PyObject *const *args, size_t nargsf, PyObject *kwnames);
60
61
static void
62
init_weakref(PyWeakReference *self, PyObject *ob, PyObject *callback)
63
826k
{
64
826k
    self->hash = -1;
65
826k
    self->wr_object = ob;
66
826k
    self->wr_prev = NULL;
67
826k
    self->wr_next = NULL;
68
826k
    self->wr_callback = Py_XNewRef(callback);
69
826k
    self->vectorcall = weakref_vectorcall;
70
#ifdef Py_GIL_DISABLED
71
    self->weakrefs_lock = &WEAKREF_LIST_LOCK(ob);
72
    _PyObject_SetMaybeWeakref(ob);
73
    _PyObject_SetMaybeWeakref((PyObject *)self);
74
#endif
75
826k
}
76
77
// Clear the weakref and steal its callback into `callback`, if provided.
78
static void
79
clear_weakref_lock_held(PyWeakReference *self, PyObject **callback)
80
1.05M
{
81
1.05M
    if (self->wr_object != Py_None) {
82
797k
        PyWeakReference **list = GET_WEAKREFS_LISTPTR(self->wr_object);
83
797k
        if (*list == self) {
84
            /* If 'self' is the end of the list (and thus self->wr_next ==
85
               NULL) then the weakref list itself (and thus the value of *list)
86
               will end up being set to NULL. */
87
797k
            FT_ATOMIC_STORE_PTR(*list, self->wr_next);
88
797k
        }
89
797k
        FT_ATOMIC_STORE_PTR(self->wr_object, Py_None);
90
797k
        if (self->wr_prev != NULL) {
91
627
            self->wr_prev->wr_next = self->wr_next;
92
627
        }
93
797k
        if (self->wr_next != NULL) {
94
184k
            self->wr_next->wr_prev = self->wr_prev;
95
184k
        }
96
797k
        self->wr_prev = NULL;
97
797k
        self->wr_next = NULL;
98
797k
    }
99
1.05M
    if (callback != NULL) {
100
824k
        *callback = self->wr_callback;
101
824k
        self->wr_callback = NULL;
102
824k
    }
103
1.05M
}
104
105
// Clear the weakref and its callback
106
static void
107
clear_weakref(PyObject *op)
108
797k
{
109
797k
    PyWeakReference *self = _PyWeakref_CAST(op);
110
797k
    PyObject *callback = NULL;
111
112
    // self->wr_object may be Py_None if the GC cleared the weakref, so lock
113
    // using the pointer in the weakref.
114
797k
    LOCK_WEAKREFS_FOR_WR(self);
115
797k
    clear_weakref_lock_held(self, &callback);
116
797k
    UNLOCK_WEAKREFS_FOR_WR(self);
117
797k
    Py_XDECREF(callback);
118
797k
}
119
120
121
/* Cyclic gc uses this to *just* clear the passed-in reference, leaving
122
 * the callback intact and uncalled.  It must be possible to call self's
123
 * tp_dealloc() after calling this, so self has to be left in a sane enough
124
 * state for that to work.  We expect tp_dealloc to decref the callback
125
 * then.  The reason for not letting clear_weakref() decref the callback
126
 * right now is that if the callback goes away, that may in turn trigger
127
 * another callback (if a weak reference to the callback exists) -- running
128
 * arbitrary Python code in the middle of gc is a disaster.  The convolution
129
 * here allows gc to delay triggering such callbacks until the world is in
130
 * a sane state again.
131
 */
132
void
133
_PyWeakref_ClearRef(PyWeakReference *self)
134
233k
{
135
233k
    assert(self != NULL);
136
233k
    assert(PyWeakref_Check(self));
137
233k
    clear_weakref_lock_held(self, NULL);
138
233k
}
139
140
static void
141
weakref_dealloc(PyObject *self)
142
797k
{
143
797k
    PyObject_GC_UnTrack(self);
144
797k
    clear_weakref(self);
145
797k
    Py_TYPE(self)->tp_free(self);
146
797k
}
147
148
149
static int
150
gc_traverse(PyObject *op, visitproc visit, void *arg)
151
1.83M
{
152
1.83M
    PyWeakReference *self = _PyWeakref_CAST(op);
153
1.83M
    Py_VISIT(self->wr_callback);
154
1.83M
    return 0;
155
1.83M
}
156
157
158
static int
159
gc_clear(PyObject *op)
160
0
{
161
0
    PyWeakReference *self = _PyWeakref_CAST(op);
162
0
    PyObject *callback;
163
    // The world is stopped during GC in free-threaded builds. It's safe to
164
    // call this without holding the lock.
165
0
    clear_weakref_lock_held(self, &callback);
166
0
    Py_XDECREF(callback);
167
0
    return 0;
168
0
}
169
170
171
static PyObject *
172
weakref_vectorcall(PyObject *self, PyObject *const *args,
173
                   size_t nargsf, PyObject *kwnames)
174
215k
{
175
215k
    if (!_PyArg_NoKwnames("weakref", kwnames)) {
176
0
        return NULL;
177
0
    }
178
215k
    Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
179
215k
    if (!_PyArg_CheckPositional("weakref", nargs, 0, 0)) {
180
0
        return NULL;
181
0
    }
182
215k
    PyObject *obj = _PyWeakref_GET_REF(self);
183
215k
    if (obj == NULL) {
184
0
        Py_RETURN_NONE;
185
0
    }
186
215k
    return obj;
187
215k
}
188
189
static Py_hash_t
190
weakref_hash_lock_held(PyWeakReference *self)
191
760k
{
192
760k
    if (self->hash != -1)
193
203k
        return self->hash;
194
557k
    PyObject* obj = _PyWeakref_GET_REF((PyObject*)self);
195
557k
    if (obj == NULL) {
196
0
        PyErr_SetString(PyExc_TypeError, "weak object has gone away");
197
0
        return -1;
198
0
    }
199
557k
    self->hash = PyObject_Hash(obj);
200
557k
    Py_DECREF(obj);
201
557k
    return self->hash;
202
557k
}
203
204
static Py_hash_t
205
weakref_hash(PyObject *op)
206
760k
{
207
760k
    PyWeakReference *self = _PyWeakref_CAST(op);
208
760k
    Py_hash_t hash;
209
760k
    Py_BEGIN_CRITICAL_SECTION(self);
210
760k
    hash = weakref_hash_lock_held(self);
211
760k
    Py_END_CRITICAL_SECTION();
212
760k
    return hash;
213
760k
}
214
215
static PyObject *
216
weakref_repr(PyObject *self)
217
0
{
218
0
    PyObject* obj = _PyWeakref_GET_REF(self);
219
0
    if (obj == NULL) {
220
0
        return PyUnicode_FromFormat("<weakref at %p; dead>", self);
221
0
    }
222
223
0
    PyObject *name = _PyObject_LookupSpecial(obj, &_Py_ID(__name__));
224
0
    if (name == NULL && PyErr_Occurred()) {
225
0
        Py_DECREF(obj);
226
0
        return NULL;
227
0
    }
228
0
    PyObject *repr;
229
0
    if (name == NULL || !PyUnicode_Check(name)) {
230
0
        repr = PyUnicode_FromFormat(
231
0
            "<weakref at %p; to '%T' at %p>",
232
0
            self, obj, obj);
233
0
    }
234
0
    else {
235
0
        repr = PyUnicode_FromFormat(
236
0
            "<weakref at %p; to '%T' at %p (%U)>",
237
0
            self, obj, obj, name);
238
0
    }
239
0
    Py_DECREF(obj);
240
0
    Py_XDECREF(name);
241
0
    return repr;
242
0
}
243
244
/* Weak references only support equality, not ordering. Two weak references
245
   are equal if the underlying objects are equal. If the underlying object has
246
   gone away, they are equal if they are identical. */
247
248
static PyObject *
249
weakref_richcompare(PyObject* self, PyObject* other, int op)
250
282k
{
251
282k
    if ((op != Py_EQ && op != Py_NE) ||
252
282k
        !PyWeakref_Check(self) ||
253
282k
        !PyWeakref_Check(other)) {
254
0
        Py_RETURN_NOTIMPLEMENTED;
255
0
    }
256
282k
    PyObject* obj = _PyWeakref_GET_REF(self);
257
282k
    PyObject* other_obj = _PyWeakref_GET_REF(other);
258
282k
    if (obj == NULL || other_obj == NULL) {
259
0
        Py_XDECREF(obj);
260
0
        Py_XDECREF(other_obj);
261
0
        int res = (self == other);
262
0
        if (op == Py_NE)
263
0
            res = !res;
264
0
        if (res)
265
0
            Py_RETURN_TRUE;
266
0
        else
267
0
            Py_RETURN_FALSE;
268
0
    }
269
282k
    PyObject* res = PyObject_RichCompare(obj, other_obj, op);
270
282k
    Py_DECREF(obj);
271
282k
    Py_DECREF(other_obj);
272
282k
    return res;
273
282k
}
274
275
/* Given the head of an object's list of weak references, extract the
276
 * two callback-less refs (ref and proxy).  Used to determine if the
277
 * shared references exist and to determine the back link for newly
278
 * inserted references.
279
 */
280
static void
281
get_basic_refs(PyWeakReference *head,
282
               PyWeakReference **refp, PyWeakReference **proxyp)
283
1.67M
{
284
1.67M
    *refp = NULL;
285
1.67M
    *proxyp = NULL;
286
287
1.67M
    if (head != NULL && head->wr_callback == NULL) {
288
        /* We need to be careful that the "basic refs" aren't
289
           subclasses of the main types.  That complicates this a
290
           little. */
291
401k
        if (PyWeakref_CheckRefExact(head)) {
292
401k
            *refp = head;
293
401k
            head = head->wr_next;
294
401k
        }
295
401k
        if (head != NULL
296
183k
            && head->wr_callback == NULL
297
0
            && PyWeakref_CheckProxy(head)) {
298
0
            *proxyp = head;
299
            /* head = head->wr_next; */
300
0
        }
301
401k
    }
302
1.67M
}
303
304
/* Insert 'newref' in the list after 'prev'.  Both must be non-NULL. */
305
static void
306
insert_after(PyWeakReference *newref, PyWeakReference *prev)
307
2.94k
{
308
2.94k
    newref->wr_prev = prev;
309
2.94k
    newref->wr_next = prev->wr_next;
310
2.94k
    if (prev->wr_next != NULL)
311
1.41k
        prev->wr_next->wr_prev = newref;
312
2.94k
    prev->wr_next = newref;
313
2.94k
}
314
315
/* Insert 'newref' at the head of the list; 'list' points to the variable
316
 * that stores the head.
317
 */
318
static void
319
insert_head(PyWeakReference *newref, PyWeakReference **list)
320
823k
{
321
823k
    PyWeakReference *next = *list;
322
323
823k
    newref->wr_prev = NULL;
324
823k
    newref->wr_next = next;
325
823k
    if (next != NULL)
326
184k
        next->wr_prev = newref;
327
823k
    *list = newref;
328
823k
}
329
330
/* See if we can reuse either the basic ref or proxy in list instead of
331
 * creating a new weakref
332
 */
333
static PyWeakReference *
334
try_reuse_basic_ref(PyWeakReference *list, PyTypeObject *type,
335
                    PyObject *callback)
336
1.21M
{
337
1.21M
    if (callback != NULL) {
338
366k
        return NULL;
339
366k
    }
340
341
843k
    PyWeakReference *ref, *proxy;
342
843k
    get_basic_refs(list, &ref, &proxy);
343
344
843k
    PyWeakReference *cand = NULL;
345
843k
    if (type == &_PyWeakref_RefType) {
346
843k
        cand = ref;
347
843k
    }
348
843k
    if ((type == &_PyWeakref_ProxyType) ||
349
843k
        (type == &_PyWeakref_CallableProxyType)) {
350
0
        cand = proxy;
351
0
    }
352
353
843k
    if (cand != NULL && _Py_TryIncref((PyObject *) cand)) {
354
398k
        return cand;
355
398k
    }
356
445k
    return NULL;
357
843k
}
358
359
static int
360
is_basic_ref(PyWeakReference *ref)
361
879k
{
362
879k
    return (ref->wr_callback == NULL) && PyWeakref_CheckRefExact(ref);
363
879k
}
364
365
static int
366
is_basic_proxy(PyWeakReference *proxy)
367
434k
{
368
434k
    return (proxy->wr_callback == NULL) && PyWeakref_CheckProxy(proxy);
369
434k
}
370
371
static int
372
is_basic_ref_or_proxy(PyWeakReference *wr)
373
53.1k
{
374
53.1k
    return is_basic_ref(wr) || is_basic_proxy(wr);
375
53.1k
}
376
377
/* Insert `newref` in the appropriate position in `list` */
378
static void
379
insert_weakref(PyWeakReference *newref, PyWeakReference **list)
380
826k
{
381
826k
    PyWeakReference *ref, *proxy;
382
826k
    get_basic_refs(*list, &ref, &proxy);
383
384
826k
    PyWeakReference *prev;
385
826k
    if (is_basic_ref(newref)) {
386
445k
        prev = NULL;
387
445k
    }
388
381k
    else if (is_basic_proxy(newref)) {
389
0
        prev = ref;
390
0
    }
391
381k
    else {
392
381k
        prev = (proxy == NULL) ? ref : proxy;
393
381k
    }
394
395
826k
    if (prev == NULL) {
396
823k
        insert_head(newref, list);
397
823k
    }
398
2.94k
    else {
399
2.94k
        insert_after(newref, prev);
400
2.94k
    }
401
826k
}
402
403
static PyWeakReference *
404
allocate_weakref(PyTypeObject *type, PyObject *obj, PyObject *callback)
405
826k
{
406
826k
    PyWeakReference *newref = (PyWeakReference *) type->tp_alloc(type, 0);
407
826k
    if (newref == NULL) {
408
0
        return NULL;
409
0
    }
410
826k
    init_weakref(newref, obj, callback);
411
826k
    return newref;
412
826k
}
413
414
static PyWeakReference *
415
get_or_create_weakref(PyTypeObject *type, PyObject *obj, PyObject *callback)
416
1.23M
{
417
1.23M
    if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(obj))) {
418
8.85k
        PyErr_Format(PyExc_TypeError,
419
8.85k
                     "cannot create weak reference to '%s' object",
420
8.85k
                     Py_TYPE(obj)->tp_name);
421
8.85k
        return NULL;
422
8.85k
    }
423
1.22M
    if (callback == Py_None) {
424
0
        callback = NULL;
425
0
    }
426
1.22M
    if (callback != NULL && !PyCallable_Check(callback)) {
427
0
        PyErr_Format(PyExc_TypeError,
428
0
                     "callback must be callable or None, not '%T'",
429
0
                     callback);
430
0
        return NULL;
431
0
    }
432
433
1.22M
    PyWeakReference **list = GET_WEAKREFS_LISTPTR(obj);
434
1.22M
    if ((type == &_PyWeakref_RefType) ||
435
14.2k
        (type == &_PyWeakref_ProxyType) ||
436
14.2k
        (type == &_PyWeakref_CallableProxyType))
437
1.21M
    {
438
1.21M
        LOCK_WEAKREFS(obj);
439
1.21M
        PyWeakReference *basic_ref = try_reuse_basic_ref(*list, type, callback);
440
1.21M
        if (basic_ref != NULL) {
441
398k
            UNLOCK_WEAKREFS(obj);
442
398k
            return basic_ref;
443
398k
        }
444
812k
        PyWeakReference *newref = allocate_weakref(type, obj, callback);
445
812k
        if (newref == NULL) {
446
0
            UNLOCK_WEAKREFS(obj);
447
0
            return NULL;
448
0
        }
449
812k
        insert_weakref(newref, list);
450
812k
        UNLOCK_WEAKREFS(obj);
451
812k
        return newref;
452
812k
    }
453
14.2k
    else {
454
        // We may not be able to safely allocate inside the lock
455
14.2k
        PyWeakReference *newref = allocate_weakref(type, obj, callback);
456
14.2k
        if (newref == NULL) {
457
0
            return NULL;
458
0
        }
459
14.2k
        LOCK_WEAKREFS(obj);
460
14.2k
        insert_weakref(newref, list);
461
14.2k
        UNLOCK_WEAKREFS(obj);
462
14.2k
        return newref;
463
14.2k
    }
464
1.22M
}
465
466
static int
467
parse_weakref_init_args(const char *funcname, PyObject *args, PyObject *kwargs,
468
                        PyObject **obp, PyObject **callbackp)
469
1.17M
{
470
1.17M
    return PyArg_UnpackTuple(args, funcname, 1, 2, obp, callbackp);
471
1.17M
}
472
473
static PyObject *
474
weakref___new__(PyTypeObject *type, PyObject *args, PyObject *kwargs)
475
592k
{
476
592k
    PyObject *ob, *callback = NULL;
477
592k
    if (parse_weakref_init_args("__new__", args, kwargs, &ob, &callback)) {
478
592k
        return (PyObject *)get_or_create_weakref(type, ob, callback);
479
592k
    }
480
0
    return NULL;
481
592k
}
482
483
static int
484
weakref___init__(PyObject *self, PyObject *args, PyObject *kwargs)
485
583k
{
486
583k
    PyObject *tmp;
487
488
583k
    if (!_PyArg_NoKeywords("ref", kwargs))
489
0
        return -1;
490
491
583k
    if (parse_weakref_init_args("__init__", args, kwargs, &tmp, &tmp))
492
583k
        return 0;
493
0
    else
494
0
        return -1;
495
583k
}
496
497
498
static PyMemberDef weakref_members[] = {
499
    {"__callback__", _Py_T_OBJECT, offsetof(PyWeakReference, wr_callback), Py_READONLY},
500
    {NULL} /* Sentinel */
501
};
502
503
static PyMethodDef weakref_methods[] = {
504
    {"__class_getitem__",    Py_GenericAlias,
505
    METH_O|METH_CLASS,
506
    PyDoc_STR("Weakrefs are generic over the type of the referenced object.")},
507
    {NULL} /* Sentinel */
508
};
509
510
PyTypeObject
511
_PyWeakref_RefType = {
512
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
513
    .tp_name = "weakref.ReferenceType",
514
    .tp_basicsize = sizeof(PyWeakReference),
515
    .tp_dealloc = weakref_dealloc,
516
    .tp_vectorcall_offset = offsetof(PyWeakReference, vectorcall),
517
    .tp_call = PyVectorcall_Call,
518
    .tp_repr = weakref_repr,
519
    .tp_hash = weakref_hash,
520
    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
521
                Py_TPFLAGS_HAVE_VECTORCALL | Py_TPFLAGS_BASETYPE,
522
    .tp_traverse = gc_traverse,
523
    .tp_clear = gc_clear,
524
    .tp_richcompare = weakref_richcompare,
525
    .tp_methods = weakref_methods,
526
    .tp_members = weakref_members,
527
    .tp_init = weakref___init__,
528
    .tp_alloc = PyType_GenericAlloc,
529
    .tp_new = weakref___new__,
530
    .tp_free = PyObject_GC_Del,
531
};
532
533
534
static bool
535
proxy_check_ref(PyObject *obj)
536
0
{
537
0
    if (obj == NULL) {
538
0
        PyErr_SetString(PyExc_ReferenceError,
539
0
                        "weakly-referenced object no longer exists");
540
0
        return false;
541
0
    }
542
0
    return true;
543
0
}
544
545
546
/* If a parameter is a proxy, check that it is still "live" and wrap it,
547
 * replacing the original value with the raw object.  Raises ReferenceError
548
 * if the param is a dead proxy.
549
 */
550
#define UNWRAP(o) \
551
0
        if (PyWeakref_CheckProxy(o)) { \
552
0
            o = _PyWeakref_GET_REF(o); \
553
0
            if (!proxy_check_ref(o)) { \
554
0
                return NULL; \
555
0
            } \
556
0
        } \
557
0
        else { \
558
0
            Py_INCREF(o); \
559
0
        }
560
561
#define WRAP_UNARY(method, generic) \
562
    static PyObject * \
563
0
    method(PyObject *proxy) { \
564
0
        UNWRAP(proxy); \
565
0
        PyObject* res = generic(proxy); \
566
0
        Py_DECREF(proxy); \
567
0
        return res; \
568
0
    }
569
570
#define WRAP_BINARY(method, generic) \
571
    static PyObject * \
572
0
    method(PyObject *x, PyObject *y) { \
573
0
        UNWRAP(x); \
574
0
        UNWRAP(y); \
575
0
        PyObject* res = generic(x, y); \
576
0
        Py_DECREF(x); \
577
0
        Py_DECREF(y); \
578
0
        return res; \
579
0
    }
580
581
/* Note that the third arg needs to be checked for NULL since the tp_call
582
 * slot can receive NULL for this arg.
583
 */
584
#define WRAP_TERNARY(method, generic) \
585
    static PyObject * \
586
0
    method(PyObject *proxy, PyObject *v, PyObject *w) { \
587
0
        UNWRAP(proxy); \
588
0
        UNWRAP(v); \
589
0
        if (w != NULL) { \
590
0
            UNWRAP(w); \
591
0
        } \
592
0
        PyObject* res = generic(proxy, v, w); \
593
0
        Py_DECREF(proxy); \
594
0
        Py_DECREF(v); \
595
0
        Py_XDECREF(w); \
596
0
        return res; \
597
0
    }
598
599
#define WRAP_METHOD(method, SPECIAL) \
600
    static PyObject * \
601
0
    method(PyObject *proxy, PyObject *Py_UNUSED(ignored)) { \
602
0
            UNWRAP(proxy); \
603
0
            PyObject* res = PyObject_CallMethodNoArgs(proxy, &_Py_ID(SPECIAL)); \
604
0
            Py_DECREF(proxy); \
605
0
            return res; \
606
0
        }
607
608
609
/* direct slots */
610
611
0
WRAP_BINARY(proxy_getattr, PyObject_GetAttr)
612
0
WRAP_UNARY(proxy_str, PyObject_Str)
613
0
WRAP_TERNARY(proxy_call, PyObject_Call)
614
615
static PyObject *
616
proxy_repr(PyObject *proxy)
617
0
{
618
0
    PyObject *obj = _PyWeakref_GET_REF(proxy);
619
0
    PyObject *repr;
620
0
    if (obj != NULL) {
621
0
        repr = PyUnicode_FromFormat(
622
0
            "<weakproxy at %p; to '%T' at %p>",
623
0
            proxy, obj, obj);
624
0
        Py_DECREF(obj);
625
0
    }
626
0
    else {
627
0
        repr = PyUnicode_FromFormat(
628
0
            "<weakproxy at %p; dead>",
629
0
            proxy);
630
0
    }
631
0
    return repr;
632
0
}
633
634
635
static int
636
proxy_setattr(PyObject *proxy, PyObject *name, PyObject *value)
637
0
{
638
0
    PyObject *obj = _PyWeakref_GET_REF(proxy);
639
0
    if (!proxy_check_ref(obj)) {
640
0
        return -1;
641
0
    }
642
0
    int res = PyObject_SetAttr(obj, name, value);
643
0
    Py_DECREF(obj);
644
0
    return res;
645
0
}
646
647
static PyObject *
648
proxy_richcompare(PyObject *proxy, PyObject *v, int op)
649
0
{
650
0
    UNWRAP(proxy);
651
0
    UNWRAP(v);
652
0
    PyObject* res = PyObject_RichCompare(proxy, v, op);
653
0
    Py_DECREF(proxy);
654
0
    Py_DECREF(v);
655
0
    return res;
656
0
}
657
658
/* number slots */
659
0
WRAP_BINARY(proxy_add, PyNumber_Add)
660
0
WRAP_BINARY(proxy_sub, PyNumber_Subtract)
661
0
WRAP_BINARY(proxy_mul, PyNumber_Multiply)
662
0
WRAP_BINARY(proxy_floor_div, PyNumber_FloorDivide)
663
0
WRAP_BINARY(proxy_true_div, PyNumber_TrueDivide)
664
0
WRAP_BINARY(proxy_mod, PyNumber_Remainder)
665
0
WRAP_BINARY(proxy_divmod, PyNumber_Divmod)
666
0
WRAP_TERNARY(proxy_pow, PyNumber_Power)
667
0
WRAP_UNARY(proxy_neg, PyNumber_Negative)
668
0
WRAP_UNARY(proxy_pos, PyNumber_Positive)
669
0
WRAP_UNARY(proxy_abs, PyNumber_Absolute)
670
0
WRAP_UNARY(proxy_invert, PyNumber_Invert)
671
0
WRAP_BINARY(proxy_lshift, PyNumber_Lshift)
672
0
WRAP_BINARY(proxy_rshift, PyNumber_Rshift)
673
0
WRAP_BINARY(proxy_and, PyNumber_And)
674
0
WRAP_BINARY(proxy_xor, PyNumber_Xor)
675
0
WRAP_BINARY(proxy_or, PyNumber_Or)
676
0
WRAP_UNARY(proxy_int, PyNumber_Long)
677
0
WRAP_UNARY(proxy_float, PyNumber_Float)
678
0
WRAP_BINARY(proxy_iadd, PyNumber_InPlaceAdd)
679
0
WRAP_BINARY(proxy_isub, PyNumber_InPlaceSubtract)
680
0
WRAP_BINARY(proxy_imul, PyNumber_InPlaceMultiply)
681
0
WRAP_BINARY(proxy_ifloor_div, PyNumber_InPlaceFloorDivide)
682
0
WRAP_BINARY(proxy_itrue_div, PyNumber_InPlaceTrueDivide)
683
0
WRAP_BINARY(proxy_imod, PyNumber_InPlaceRemainder)
684
0
WRAP_TERNARY(proxy_ipow, PyNumber_InPlacePower)
685
0
WRAP_BINARY(proxy_ilshift, PyNumber_InPlaceLshift)
686
0
WRAP_BINARY(proxy_irshift, PyNumber_InPlaceRshift)
687
0
WRAP_BINARY(proxy_iand, PyNumber_InPlaceAnd)
688
0
WRAP_BINARY(proxy_ixor, PyNumber_InPlaceXor)
689
0
WRAP_BINARY(proxy_ior, PyNumber_InPlaceOr)
690
0
WRAP_UNARY(proxy_index, PyNumber_Index)
691
0
WRAP_BINARY(proxy_matmul, PyNumber_MatrixMultiply)
692
0
WRAP_BINARY(proxy_imatmul, PyNumber_InPlaceMatrixMultiply)
693
694
static int
695
proxy_bool(PyObject *proxy)
696
0
{
697
0
    PyObject *o = _PyWeakref_GET_REF(proxy);
698
0
    if (!proxy_check_ref(o)) {
699
0
        return -1;
700
0
    }
701
0
    int res = PyObject_IsTrue(o);
702
0
    Py_DECREF(o);
703
0
    return res;
704
0
}
705
706
static void
707
proxy_dealloc(PyObject *self)
708
0
{
709
0
    PyObject_GC_UnTrack(self);
710
0
    clear_weakref(self);
711
0
    PyObject_GC_Del(self);
712
0
}
713
714
/* sequence slots */
715
716
static int
717
proxy_contains(PyObject *proxy, PyObject *value)
718
0
{
719
0
    PyObject *obj = _PyWeakref_GET_REF(proxy);
720
0
    if (!proxy_check_ref(obj)) {
721
0
        return -1;
722
0
    }
723
0
    int res = PySequence_Contains(obj, value);
724
0
    Py_DECREF(obj);
725
0
    return res;
726
0
}
727
728
/* mapping slots */
729
730
static Py_ssize_t
731
proxy_length(PyObject *proxy)
732
0
{
733
0
    PyObject *obj = _PyWeakref_GET_REF(proxy);
734
0
    if (!proxy_check_ref(obj)) {
735
0
        return -1;
736
0
    }
737
0
    Py_ssize_t res = PyObject_Length(obj);
738
0
    Py_DECREF(obj);
739
0
    return res;
740
0
}
741
742
0
WRAP_BINARY(proxy_getitem, PyObject_GetItem)
743
744
static int
745
proxy_setitem(PyObject *proxy, PyObject *key, PyObject *value)
746
0
{
747
0
    PyObject *obj = _PyWeakref_GET_REF(proxy);
748
0
    if (!proxy_check_ref(obj)) {
749
0
        return -1;
750
0
    }
751
0
    int res;
752
0
    if (value == NULL) {
753
0
        res = PyObject_DelItem(obj, key);
754
0
    } else {
755
0
        res = PyObject_SetItem(obj, key, value);
756
0
    }
757
0
    Py_DECREF(obj);
758
0
    return res;
759
0
}
760
761
/* iterator slots */
762
763
static PyObject *
764
proxy_iter(PyObject *proxy)
765
0
{
766
0
    PyObject *obj = _PyWeakref_GET_REF(proxy);
767
0
    if (!proxy_check_ref(obj)) {
768
0
        return NULL;
769
0
    }
770
0
    PyObject* res = PyObject_GetIter(obj);
771
0
    Py_DECREF(obj);
772
0
    return res;
773
0
}
774
775
static PyObject *
776
proxy_iternext(PyObject *proxy)
777
0
{
778
0
    PyObject *obj = _PyWeakref_GET_REF(proxy);
779
0
    if (!proxy_check_ref(obj)) {
780
0
        return NULL;
781
0
    }
782
0
    if (!PyIter_Check(obj)) {
783
0
        PyErr_Format(PyExc_TypeError,
784
0
            "Weakref proxy referenced a non-iterator '%.200s' object",
785
0
            Py_TYPE(obj)->tp_name);
786
0
        Py_DECREF(obj);
787
0
        return NULL;
788
0
    }
789
0
    PyObject* res = PyIter_Next(obj);
790
0
    Py_DECREF(obj);
791
0
    return res;
792
0
}
793
794
795
0
WRAP_METHOD(proxy_bytes, __bytes__)
796
0
WRAP_METHOD(proxy_reversed, __reversed__)
797
798
799
static PyMethodDef proxy_methods[] = {
800
        {"__bytes__", proxy_bytes, METH_NOARGS},
801
        {"__reversed__", proxy_reversed, METH_NOARGS},
802
        {NULL, NULL}
803
};
804
805
806
static PyNumberMethods proxy_as_number = {
807
    proxy_add,              /*nb_add*/
808
    proxy_sub,              /*nb_subtract*/
809
    proxy_mul,              /*nb_multiply*/
810
    proxy_mod,              /*nb_remainder*/
811
    proxy_divmod,           /*nb_divmod*/
812
    proxy_pow,              /*nb_power*/
813
    proxy_neg,              /*nb_negative*/
814
    proxy_pos,              /*nb_positive*/
815
    proxy_abs,              /*nb_absolute*/
816
    proxy_bool,             /*nb_bool*/
817
    proxy_invert,           /*nb_invert*/
818
    proxy_lshift,           /*nb_lshift*/
819
    proxy_rshift,           /*nb_rshift*/
820
    proxy_and,              /*nb_and*/
821
    proxy_xor,              /*nb_xor*/
822
    proxy_or,               /*nb_or*/
823
    proxy_int,              /*nb_int*/
824
    0,                      /*nb_reserved*/
825
    proxy_float,            /*nb_float*/
826
    proxy_iadd,             /*nb_inplace_add*/
827
    proxy_isub,             /*nb_inplace_subtract*/
828
    proxy_imul,             /*nb_inplace_multiply*/
829
    proxy_imod,             /*nb_inplace_remainder*/
830
    proxy_ipow,             /*nb_inplace_power*/
831
    proxy_ilshift,          /*nb_inplace_lshift*/
832
    proxy_irshift,          /*nb_inplace_rshift*/
833
    proxy_iand,             /*nb_inplace_and*/
834
    proxy_ixor,             /*nb_inplace_xor*/
835
    proxy_ior,              /*nb_inplace_or*/
836
    proxy_floor_div,        /*nb_floor_divide*/
837
    proxy_true_div,         /*nb_true_divide*/
838
    proxy_ifloor_div,       /*nb_inplace_floor_divide*/
839
    proxy_itrue_div,        /*nb_inplace_true_divide*/
840
    proxy_index,            /*nb_index*/
841
    proxy_matmul,           /*nb_matrix_multiply*/
842
    proxy_imatmul,          /*nb_inplace_matrix_multiply*/
843
};
844
845
static PySequenceMethods proxy_as_sequence = {
846
    proxy_length,               /*sq_length*/
847
    0,                          /*sq_concat*/
848
    0,                          /*sq_repeat*/
849
    0,                          /*sq_item*/
850
    0,                          /*sq_slice*/
851
    0,                          /*sq_ass_item*/
852
    0,                          /*sq_ass_slice*/
853
    proxy_contains,             /* sq_contains */
854
};
855
856
static PyMappingMethods proxy_as_mapping = {
857
    proxy_length,                 /*mp_length*/
858
    proxy_getitem,                /*mp_subscript*/
859
    proxy_setitem,                /*mp_ass_subscript*/
860
};
861
862
863
PyTypeObject
864
_PyWeakref_ProxyType = {
865
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
866
    "weakref.ProxyType",
867
    sizeof(PyWeakReference),
868
    0,
869
    /* methods */
870
    proxy_dealloc,                      /* tp_dealloc */
871
    0,                                  /* tp_vectorcall_offset */
872
    0,                                  /* tp_getattr */
873
    0,                                  /* tp_setattr */
874
    0,                                  /* tp_as_async */
875
    proxy_repr,                         /* tp_repr */
876
    &proxy_as_number,                   /* tp_as_number */
877
    &proxy_as_sequence,                 /* tp_as_sequence */
878
    &proxy_as_mapping,                  /* tp_as_mapping */
879
// Notice that tp_hash is intentionally omitted as proxies are "mutable" (when the reference dies).
880
    0,                                  /* tp_hash */
881
    0,                                  /* tp_call */
882
    proxy_str,                          /* tp_str */
883
    proxy_getattr,                      /* tp_getattro */
884
    proxy_setattr,                      /* tp_setattro */
885
    0,                                  /* tp_as_buffer */
886
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
887
    0,                                  /* tp_doc */
888
    gc_traverse,                        /* tp_traverse */
889
    gc_clear,                           /* tp_clear */
890
    proxy_richcompare,                  /* tp_richcompare */
891
    0,                                  /* tp_weaklistoffset */
892
    proxy_iter,                         /* tp_iter */
893
    proxy_iternext,                     /* tp_iternext */
894
    proxy_methods,                      /* tp_methods */
895
};
896
897
898
PyTypeObject
899
_PyWeakref_CallableProxyType = {
900
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
901
    "weakref.CallableProxyType",
902
    sizeof(PyWeakReference),
903
    0,
904
    /* methods */
905
    proxy_dealloc,                      /* tp_dealloc */
906
    0,                                  /* tp_vectorcall_offset */
907
    0,                                  /* tp_getattr */
908
    0,                                  /* tp_setattr */
909
    0,                                  /* tp_as_async */
910
    proxy_repr,                         /* tp_repr */
911
    &proxy_as_number,                   /* tp_as_number */
912
    &proxy_as_sequence,                 /* tp_as_sequence */
913
    &proxy_as_mapping,                  /* tp_as_mapping */
914
    0,                                  /* tp_hash */
915
    proxy_call,                         /* tp_call */
916
    proxy_str,                          /* tp_str */
917
    proxy_getattr,                      /* tp_getattro */
918
    proxy_setattr,                      /* tp_setattro */
919
    0,                                  /* tp_as_buffer */
920
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
921
    0,                                  /* tp_doc */
922
    gc_traverse,                        /* tp_traverse */
923
    gc_clear,                           /* tp_clear */
924
    proxy_richcompare,                  /* tp_richcompare */
925
    0,                                  /* tp_weaklistoffset */
926
    proxy_iter,                         /* tp_iter */
927
    proxy_iternext,                     /* tp_iternext */
928
};
929
930
PyObject *
931
PyWeakref_NewRef(PyObject *ob, PyObject *callback)
932
641k
{
933
641k
    return (PyObject *)get_or_create_weakref(&_PyWeakref_RefType, ob,
934
641k
                                             callback);
935
641k
}
936
937
PyObject *
938
PyWeakref_NewProxy(PyObject *ob, PyObject *callback)
939
0
{
940
0
    PyTypeObject *type = &_PyWeakref_ProxyType;
941
0
    if (PyCallable_Check(ob)) {
942
0
        type = &_PyWeakref_CallableProxyType;
943
0
    }
944
0
    return (PyObject *)get_or_create_weakref(type, ob, callback);
945
0
}
946
947
int
948
PyWeakref_IsDead(PyObject *ref)
949
0
{
950
0
    if (ref == NULL) {
951
0
        PyErr_BadInternalCall();
952
0
        return -1;
953
0
    }
954
0
    if (!PyWeakref_Check(ref)) {
955
0
        PyErr_Format(PyExc_TypeError, "expected a weakref, got %T", ref);
956
0
        return -1;
957
0
    }
958
0
    return _PyWeakref_IS_DEAD(ref);
959
0
}
960
961
int
962
PyWeakref_GetRef(PyObject *ref, PyObject **pobj)
963
27.9k
{
964
27.9k
    if (ref == NULL) {
965
0
        *pobj = NULL;
966
0
        PyErr_BadInternalCall();
967
0
        return -1;
968
0
    }
969
27.9k
    if (!PyWeakref_Check(ref)) {
970
0
        *pobj = NULL;
971
0
        PyErr_SetString(PyExc_TypeError, "expected a weakref");
972
0
        return -1;
973
0
    }
974
27.9k
    *pobj = _PyWeakref_GET_REF(ref);
975
27.9k
    return (*pobj != NULL);
976
27.9k
}
977
978
979
/* removed in 3.15, but kept for stable ABI compatibility */
980
PyAPI_FUNC(PyObject *)
981
PyWeakref_GetObject(PyObject *ref)
982
0
{
983
0
    if (ref == NULL || !PyWeakref_Check(ref)) {
984
0
        PyErr_BadInternalCall();
985
0
        return NULL;
986
0
    }
987
0
    PyObject *obj = _PyWeakref_GET_REF(ref);
988
0
    if (obj == NULL) {
989
0
        return Py_None;
990
0
    }
991
0
    Py_DECREF(obj);
992
0
    return obj;  // borrowed reference
993
0
}
994
995
/* Note that there's an inlined copy-paste of handle_callback() in gcmodule.c's
996
 * handle_weakrefs().
997
 */
998
static void
999
handle_callback(PyWeakReference *ref, PyObject *callback)
1000
26.5k
{
1001
26.5k
    PyObject *cbresult = PyObject_CallOneArg(callback, (PyObject *)ref);
1002
1003
26.5k
    if (cbresult == NULL) {
1004
0
        PyErr_FormatUnraisable("Exception ignored while "
1005
0
                               "calling weakref callback %R", callback);
1006
0
    }
1007
26.5k
    else {
1008
26.5k
        Py_DECREF(cbresult);
1009
26.5k
    }
1010
26.5k
}
1011
1012
/* This function is called by the tp_dealloc handler to clear weak references.
1013
 *
1014
 * This iterates through the weak references for 'object' and calls callbacks
1015
 * for those references which have one.  It returns when all callbacks have
1016
 * been attempted.
1017
 */
1018
void
1019
PyObject_ClearWeakRefs(PyObject *object)
1020
49.1M
{
1021
49.1M
    PyWeakReference **list;
1022
1023
49.1M
    if (object == NULL
1024
49.1M
        || !_PyType_SUPPORTS_WEAKREFS(Py_TYPE(object))
1025
49.1M
        || Py_REFCNT(object) != 0)
1026
0
    {
1027
0
        PyErr_BadInternalCall();
1028
0
        return;
1029
0
    }
1030
1031
49.1M
    list = GET_WEAKREFS_LISTPTR(object);
1032
49.1M
    if (FT_ATOMIC_LOAD_PTR(*list) == NULL) {
1033
        // Fast path for the common case
1034
49.1M
        return;
1035
49.1M
    }
1036
1037
    /* Remove the callback-less basic and proxy references, which always appear
1038
       at the head of the list.
1039
    */
1040
53.1k
    for (int done = 0; !done;) {
1041
26.5k
        LOCK_WEAKREFS(object);
1042
26.5k
        if (*list != NULL && is_basic_ref_or_proxy(*list)) {
1043
5
            PyObject *callback;
1044
5
            clear_weakref_lock_held(*list, &callback);
1045
5
            assert(callback == NULL);
1046
5
        }
1047
26.5k
        done = (*list == NULL) || !is_basic_ref_or_proxy(*list);
1048
26.5k
        UNLOCK_WEAKREFS(object);
1049
26.5k
    }
1050
1051
    /* Deal with non-canonical (subtypes or refs with callbacks) references. */
1052
26.5k
    Py_ssize_t num_weakrefs = _PyWeakref_GetWeakrefCount(object);
1053
26.5k
    if (num_weakrefs == 0) {
1054
5
        return;
1055
5
    }
1056
1057
26.5k
    PyObject *exc = PyErr_GetRaisedException();
1058
26.5k
    PyObject *tuple = PyTuple_New(num_weakrefs * 2);
1059
26.5k
    if (tuple == NULL) {
1060
0
        _PyWeakref_ClearWeakRefsNoCallbacks(object);
1061
0
        PyErr_FormatUnraisable("Exception ignored while "
1062
0
                               "clearing object weakrefs");
1063
0
        PyErr_SetRaisedException(exc);
1064
0
        return;
1065
0
    }
1066
1067
26.5k
    Py_ssize_t num_items = 0;
1068
53.1k
    for (int done = 0; !done;) {
1069
26.5k
        PyObject *callback = NULL;
1070
26.5k
        LOCK_WEAKREFS(object);
1071
26.5k
        PyWeakReference *cur = *list;
1072
26.5k
        if (cur != NULL) {
1073
26.5k
            clear_weakref_lock_held(cur, &callback);
1074
26.5k
            if (_Py_TryIncref((PyObject *) cur)) {
1075
26.5k
                assert(num_items / 2 < num_weakrefs);
1076
26.5k
                PyTuple_SET_ITEM(tuple, num_items, (PyObject *) cur);
1077
26.5k
                PyTuple_SET_ITEM(tuple, num_items + 1, callback);
1078
26.5k
                num_items += 2;
1079
26.5k
                callback = NULL;
1080
26.5k
            }
1081
26.5k
        }
1082
26.5k
        done = (*list == NULL);
1083
26.5k
        UNLOCK_WEAKREFS(object);
1084
1085
26.5k
        Py_XDECREF(callback);
1086
26.5k
    }
1087
1088
53.1k
    for (Py_ssize_t i = 0; i < num_items; i += 2) {
1089
26.5k
        PyObject *callback = PyTuple_GET_ITEM(tuple, i + 1);
1090
26.5k
        if (callback != NULL) {
1091
26.5k
            PyObject *weakref = PyTuple_GET_ITEM(tuple, i);
1092
26.5k
            handle_callback((PyWeakReference *)weakref, callback);
1093
26.5k
        }
1094
26.5k
    }
1095
1096
26.5k
    Py_DECREF(tuple);
1097
1098
26.5k
    assert(!PyErr_Occurred());
1099
26.5k
    PyErr_SetRaisedException(exc);
1100
26.5k
}
1101
1102
void
1103
PyUnstable_Object_ClearWeakRefsNoCallbacks(PyObject *obj)
1104
0
{
1105
0
    if (_PyType_SUPPORTS_WEAKREFS(Py_TYPE(obj))) {
1106
0
        _PyWeakref_ClearWeakRefsNoCallbacks(obj);
1107
0
    }
1108
0
}
1109
1110
/* This function is called by _PyStaticType_Dealloc() to clear weak references.
1111
 *
1112
 * This is called at the end of runtime finalization, so we can just
1113
 * wipe out the type's weaklist.  We don't bother with callbacks
1114
 * or anything else.
1115
 */
1116
void
1117
_PyStaticType_ClearWeakRefs(PyInterpreterState *interp, PyTypeObject *type)
1118
0
{
1119
0
    managed_static_type_state *state = _PyStaticType_GetState(interp, type);
1120
0
    PyObject **list = _PyStaticType_GET_WEAKREFS_LISTPTR(state);
1121
    // This is safe to do without holding the lock in free-threaded builds;
1122
    // there is only one thread running and no new threads can be created.
1123
0
    while (*list) {
1124
0
        _PyWeakref_ClearRef((PyWeakReference *)*list);
1125
0
    }
1126
0
}
1127
1128
void
1129
_PyWeakref_ClearWeakRefsNoCallbacks(PyObject *obj)
1130
11.8k
{
1131
    /* Modeled after GET_WEAKREFS_LISTPTR().
1132
1133
       This is never triggered for static types so we can avoid the
1134
       (slightly) more costly _PyObject_GET_WEAKREFS_LISTPTR(). */
1135
11.8k
    PyWeakReference **list = _PyObject_GET_WEAKREFS_LISTPTR_FROM_OFFSET(obj);
1136
11.8k
    LOCK_WEAKREFS(obj);
1137
11.8k
    while (*list) {
1138
0
        _PyWeakref_ClearRef(*list);
1139
0
    }
1140
11.8k
    UNLOCK_WEAKREFS(obj);
1141
11.8k
}
1142
1143
int
1144
_PyWeakref_IsDead(PyObject *weakref)
1145
0
{
1146
0
    return _PyWeakref_IS_DEAD(weakref);
1147
0
}