Coverage Report

Created: 2022-07-08 09:39

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