Coverage Report

Created: 2026-07-14 06:16

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