Coverage Report

Created: 2026-06-09 06:53

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