Skip to content

Commit cb388c9

Browse files
authored
gh-105927: Add _PyWeakref_GET_REF() internal function (#105929)
Add new pycore_weakref.h internal header file.
1 parent 03f1a13 commit cb388c9

File tree

5 files changed

+88
-49
lines changed

5 files changed

+88
-49
lines changed

Include/internal/pycore_weakref.h

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#ifndef Py_INTERNAL_WEAKREF_H
2+
#define Py_INTERNAL_WEAKREF_H
3+
#ifdef __cplusplus
4+
extern "C" {
5+
#endif
6+
7+
#ifndef Py_BUILD_CORE
8+
# error "this header requires Py_BUILD_CORE define"
9+
#endif
10+
11+
static inline PyObject* _PyWeakref_GET_REF(PyObject *ref_obj) {
12+
assert(PyWeakref_Check(ref_obj));
13+
PyWeakReference *ref = _Py_CAST(PyWeakReference*, ref_obj);
14+
PyObject *obj = ref->wr_object;
15+
16+
if (obj == Py_None) {
17+
// clear_weakref() was called
18+
return NULL;
19+
}
20+
21+
// Explanation for the Py_REFCNT() check: when a weakref's target is part
22+
// of a long chain of deallocations which triggers the trashcan mechanism,
23+
// clearing the weakrefs can be delayed long after the target's refcount
24+
// has dropped to zero. In the meantime, code accessing the weakref will
25+
// be able to "see" the target object even though it is supposed to be
26+
// unreachable. See issue gh-60806.
27+
Py_ssize_t refcnt = Py_REFCNT(obj);
28+
if (refcnt == 0) {
29+
return NULL;
30+
}
31+
32+
assert(refcnt > 0);
33+
return Py_NewRef(obj);
34+
}
35+
36+
#ifdef __cplusplus
37+
}
38+
#endif
39+
#endif /* !Py_INTERNAL_WEAKREF_H */
40+

Makefile.pre.in

+1
Original file line numberDiff line numberDiff line change
@@ -1801,6 +1801,7 @@ PYTHON_HEADERS= \
18011801
$(srcdir)/Include/internal/pycore_unicodeobject.h \
18021802
$(srcdir)/Include/internal/pycore_unicodeobject_generated.h \
18031803
$(srcdir)/Include/internal/pycore_warnings.h \
1804+
$(srcdir)/Include/internal/pycore_weakref.h \
18041805
$(DTRACE_HEADERS) \
18051806
@PLATFORM_HEADERS@ \
18061807
\

Objects/weakrefobject.c

+43-49
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "Python.h"
22
#include "pycore_object.h" // _PyObject_GET_WEAKREFS_LISTPTR()
3+
#include "pycore_weakref.h" // _PyWeakref_GET_REF()
34
#include "structmember.h" // PyMemberDef
45

56

@@ -147,12 +148,11 @@ weakref_hash(PyWeakReference *self)
147148
{
148149
if (self->hash != -1)
149150
return self->hash;
150-
PyObject* obj = PyWeakref_GET_OBJECT(self);
151-
if (obj == Py_None) {
151+
PyObject* obj = _PyWeakref_GET_REF((PyObject*)self);
152+
if (obj == NULL) {
152153
PyErr_SetString(PyExc_TypeError, "weak object has gone away");
153154
return -1;
154155
}
155-
Py_INCREF(obj);
156156
self->hash = PyObject_Hash(obj);
157157
Py_DECREF(obj);
158158
return self->hash;
@@ -162,15 +162,13 @@ weakref_hash(PyWeakReference *self)
162162
static PyObject *
163163
weakref_repr(PyObject *self)
164164
{
165-
PyObject *name, *repr;
166-
PyObject* obj = PyWeakref_GET_OBJECT(self);
167-
168-
if (obj == Py_None) {
165+
PyObject* obj = _PyWeakref_GET_REF(self);
166+
if (obj == NULL) {
169167
return PyUnicode_FromFormat("<weakref at %p; dead>", self);
170168
}
171169

172-
Py_INCREF(obj);
173-
name = _PyObject_LookupSpecial(obj, &_Py_ID(__name__));
170+
PyObject *name = _PyObject_LookupSpecial(obj, &_Py_ID(__name__));
171+
PyObject *repr;
174172
if (name == NULL || !PyUnicode_Check(name)) {
175173
repr = PyUnicode_FromFormat(
176174
"<weakref at %p; to '%s' at %p>",
@@ -191,16 +189,18 @@ weakref_repr(PyObject *self)
191189
gone away, they are equal if they are identical. */
192190

193191
static PyObject *
194-
weakref_richcompare(PyWeakReference* self, PyWeakReference* other, int op)
192+
weakref_richcompare(PyObject* self, PyObject* other, int op)
195193
{
196194
if ((op != Py_EQ && op != Py_NE) ||
197195
!PyWeakref_Check(self) ||
198196
!PyWeakref_Check(other)) {
199197
Py_RETURN_NOTIMPLEMENTED;
200198
}
201-
PyObject* obj = PyWeakref_GET_OBJECT(self);
202-
PyObject* other_obj = PyWeakref_GET_OBJECT(other);
203-
if (obj == Py_None || other_obj == Py_None) {
199+
PyObject* obj = _PyWeakref_GET_REF(self);
200+
PyObject* other_obj = _PyWeakref_GET_REF(other);
201+
if (obj == NULL || other_obj == NULL) {
202+
Py_XDECREF(obj);
203+
Py_XDECREF(other_obj);
204204
int res = (self == other);
205205
if (op == Py_NE)
206206
res = !res;
@@ -209,8 +209,6 @@ weakref_richcompare(PyWeakReference* self, PyWeakReference* other, int op)
209209
else
210210
Py_RETURN_FALSE;
211211
}
212-
Py_INCREF(obj);
213-
Py_INCREF(other_obj);
214212
PyObject* res = PyObject_RichCompare(obj, other_obj, op);
215213
Py_DECREF(obj);
216214
Py_DECREF(other_obj);
@@ -372,7 +370,7 @@ _PyWeakref_RefType = {
372370
Py_TPFLAGS_HAVE_VECTORCALL | Py_TPFLAGS_BASETYPE,
373371
.tp_traverse = (traverseproc)gc_traverse,
374372
.tp_clear = (inquiry)gc_clear,
375-
.tp_richcompare = (richcmpfunc)weakref_richcompare,
373+
.tp_richcompare = weakref_richcompare,
376374
.tp_methods = weakref_methods,
377375
.tp_members = weakref_members,
378376
.tp_init = weakref___init__,
@@ -385,7 +383,7 @@ _PyWeakref_RefType = {
385383
static bool
386384
proxy_check_ref(PyObject *obj)
387385
{
388-
if (obj == Py_None) {
386+
if (obj == NULL) {
389387
PyErr_SetString(PyExc_ReferenceError,
390388
"weakly-referenced object no longer exists");
391389
return false;
@@ -400,16 +398,19 @@ proxy_check_ref(PyObject *obj)
400398
*/
401399
#define UNWRAP(o) \
402400
if (PyWeakref_CheckProxy(o)) { \
403-
o = PyWeakref_GET_OBJECT(o); \
404-
if (!proxy_check_ref(o)) \
401+
o = _PyWeakref_GET_REF(o); \
402+
if (!proxy_check_ref(o)) { \
405403
return NULL; \
404+
} \
405+
} \
406+
else { \
407+
Py_INCREF(o); \
406408
}
407409

408410
#define WRAP_UNARY(method, generic) \
409411
static PyObject * \
410412
method(PyObject *proxy) { \
411413
UNWRAP(proxy); \
412-
Py_INCREF(proxy); \
413414
PyObject* res = generic(proxy); \
414415
Py_DECREF(proxy); \
415416
return res; \
@@ -420,8 +421,6 @@ proxy_check_ref(PyObject *obj)
420421
method(PyObject *x, PyObject *y) { \
421422
UNWRAP(x); \
422423
UNWRAP(y); \
423-
Py_INCREF(x); \
424-
Py_INCREF(y); \
425424
PyObject* res = generic(x, y); \
426425
Py_DECREF(x); \
427426
Py_DECREF(y); \
@@ -436,11 +435,9 @@ proxy_check_ref(PyObject *obj)
436435
method(PyObject *proxy, PyObject *v, PyObject *w) { \
437436
UNWRAP(proxy); \
438437
UNWRAP(v); \
439-
if (w != NULL) \
438+
if (w != NULL) { \
440439
UNWRAP(w); \
441-
Py_INCREF(proxy); \
442-
Py_INCREF(v); \
443-
Py_XINCREF(w); \
440+
} \
444441
PyObject* res = generic(proxy, v, w); \
445442
Py_DECREF(proxy); \
446443
Py_DECREF(v); \
@@ -452,7 +449,6 @@ proxy_check_ref(PyObject *obj)
452449
static PyObject * \
453450
method(PyObject *proxy, PyObject *Py_UNUSED(ignored)) { \
454451
UNWRAP(proxy); \
455-
Py_INCREF(proxy); \
456452
PyObject* res = PyObject_CallMethodNoArgs(proxy, &_Py_ID(SPECIAL)); \
457453
Py_DECREF(proxy); \
458454
return res; \
@@ -466,24 +462,24 @@ WRAP_UNARY(proxy_str, PyObject_Str)
466462
WRAP_TERNARY(proxy_call, PyObject_Call)
467463

468464
static PyObject *
469-
proxy_repr(PyWeakReference *proxy)
465+
proxy_repr(PyObject *proxy)
470466
{
471-
return PyUnicode_FromFormat(
467+
PyObject *obj = _PyWeakref_GET_REF(proxy);
468+
PyObject *repr = PyUnicode_FromFormat(
472469
"<weakproxy at %p to %s at %p>",
473-
proxy,
474-
Py_TYPE(PyWeakref_GET_OBJECT(proxy))->tp_name,
475-
PyWeakref_GET_OBJECT(proxy));
470+
proxy, Py_TYPE(obj)->tp_name, obj);
471+
Py_DECREF(obj);
472+
return repr;
476473
}
477474

478475

479476
static int
480477
proxy_setattr(PyObject *proxy, PyObject *name, PyObject *value)
481478
{
482-
PyObject *obj = PyWeakref_GET_OBJECT(proxy);
479+
PyObject *obj = _PyWeakref_GET_REF(proxy);
483480
if (!proxy_check_ref(obj)) {
484481
return -1;
485482
}
486-
Py_INCREF(obj);
487483
int res = PyObject_SetAttr(obj, name, value);
488484
Py_DECREF(obj);
489485
return res;
@@ -494,7 +490,10 @@ proxy_richcompare(PyObject *proxy, PyObject *v, int op)
494490
{
495491
UNWRAP(proxy);
496492
UNWRAP(v);
497-
return PyObject_RichCompare(proxy, v, op);
493+
PyObject* res = PyObject_RichCompare(proxy, v, op);
494+
Py_DECREF(proxy);
495+
Py_DECREF(v);
496+
return res;
498497
}
499498

500499
/* number slots */
@@ -536,11 +535,10 @@ WRAP_BINARY(proxy_imatmul, PyNumber_InPlaceMatrixMultiply)
536535
static int
537536
proxy_bool(PyObject *proxy)
538537
{
539-
PyObject *o = PyWeakref_GET_OBJECT(proxy);
538+
PyObject *o = _PyWeakref_GET_REF(proxy);
540539
if (!proxy_check_ref(o)) {
541540
return -1;
542541
}
543-
Py_INCREF(o);
544542
int res = PyObject_IsTrue(o);
545543
Py_DECREF(o);
546544
return res;
@@ -561,11 +559,10 @@ proxy_dealloc(PyWeakReference *self)
561559
static int
562560
proxy_contains(PyObject *proxy, PyObject *value)
563561
{
564-
PyObject *obj = PyWeakref_GET_OBJECT(proxy);
562+
PyObject *obj = _PyWeakref_GET_REF(proxy);
565563
if (!proxy_check_ref(obj)) {
566564
return -1;
567565
}
568-
Py_INCREF(obj);
569566
int res = PySequence_Contains(obj, value);
570567
Py_DECREF(obj);
571568
return res;
@@ -576,11 +573,10 @@ proxy_contains(PyObject *proxy, PyObject *value)
576573
static Py_ssize_t
577574
proxy_length(PyObject *proxy)
578575
{
579-
PyObject *obj = PyWeakref_GET_OBJECT(proxy);
576+
PyObject *obj = _PyWeakref_GET_REF(proxy);
580577
if (!proxy_check_ref(obj)) {
581578
return -1;
582579
}
583-
Py_INCREF(obj);
584580
Py_ssize_t res = PyObject_Length(obj);
585581
Py_DECREF(obj);
586582
return res;
@@ -591,11 +587,10 @@ WRAP_BINARY(proxy_getitem, PyObject_GetItem)
591587
static int
592588
proxy_setitem(PyObject *proxy, PyObject *key, PyObject *value)
593589
{
594-
PyObject *obj = PyWeakref_GET_OBJECT(proxy);
590+
PyObject *obj = _PyWeakref_GET_REF(proxy);
595591
if (!proxy_check_ref(obj)) {
596592
return -1;
597593
}
598-
Py_INCREF(obj);
599594
int res;
600595
if (value == NULL) {
601596
res = PyObject_DelItem(obj, key);
@@ -611,11 +606,10 @@ proxy_setitem(PyObject *proxy, PyObject *key, PyObject *value)
611606
static PyObject *
612607
proxy_iter(PyObject *proxy)
613608
{
614-
PyObject *obj = PyWeakref_GET_OBJECT(proxy);
609+
PyObject *obj = _PyWeakref_GET_REF(proxy);
615610
if (!proxy_check_ref(obj)) {
616611
return NULL;
617612
}
618-
Py_INCREF(obj);
619613
PyObject* res = PyObject_GetIter(obj);
620614
Py_DECREF(obj);
621615
return res;
@@ -624,17 +618,17 @@ proxy_iter(PyObject *proxy)
624618
static PyObject *
625619
proxy_iternext(PyObject *proxy)
626620
{
627-
PyObject *obj = PyWeakref_GET_OBJECT(proxy);
621+
PyObject *obj = _PyWeakref_GET_REF(proxy);
628622
if (!proxy_check_ref(obj)) {
629623
return NULL;
630624
}
631625
if (!PyIter_Check(obj)) {
632626
PyErr_Format(PyExc_TypeError,
633627
"Weakref proxy referenced a non-iterator '%.200s' object",
634628
Py_TYPE(obj)->tp_name);
629+
Py_DECREF(obj);
635630
return NULL;
636631
}
637-
Py_INCREF(obj);
638632
PyObject* res = PyIter_Next(obj);
639633
Py_DECREF(obj);
640634
return res;
@@ -721,7 +715,7 @@ _PyWeakref_ProxyType = {
721715
0, /* tp_getattr */
722716
0, /* tp_setattr */
723717
0, /* tp_as_async */
724-
(reprfunc)proxy_repr, /* tp_repr */
718+
proxy_repr, /* tp_repr */
725719
&proxy_as_number, /* tp_as_number */
726720
&proxy_as_sequence, /* tp_as_sequence */
727721
&proxy_as_mapping, /* tp_as_mapping */
@@ -756,7 +750,7 @@ _PyWeakref_CallableProxyType = {
756750
0, /* tp_getattr */
757751
0, /* tp_setattr */
758752
0, /* tp_as_async */
759-
(unaryfunc)proxy_repr, /* tp_repr */
753+
proxy_repr, /* tp_repr */
760754
&proxy_as_number, /* tp_as_number */
761755
&proxy_as_sequence, /* tp_as_sequence */
762756
&proxy_as_mapping, /* tp_as_mapping */

PCbuild/pythoncore.vcxproj

+1
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@
275275
<ClInclude Include="..\Include\internal\pycore_unicodeobject.h" />
276276
<ClInclude Include="..\Include\internal\pycore_unicodeobject_generated.h" />
277277
<ClInclude Include="..\Include\internal\pycore_warnings.h" />
278+
<ClInclude Include="..\Include\internal\pycore_weakref.h" />
278279
<ClInclude Include="..\Include\interpreteridobject.h" />
279280
<ClInclude Include="..\Include\intrcheck.h" />
280281
<ClInclude Include="..\Include\iterobject.h" />

PCbuild/pythoncore.vcxproj.filters

+3
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,9 @@
492492
<ClInclude Include="..\Include\internal\pycore_warnings.h">
493493
<Filter>Include\internal</Filter>
494494
</ClInclude>
495+
<ClInclude Include="..\Include\internal\pycore_weakref.h">
496+
<Filter>Include\internal</Filter>
497+
</ClInclude>
495498
<ClInclude Include="..\Include\internal\pycore_abstract.h">
496499
<Filter>Include\internal</Filter>
497500
</ClInclude>

0 commit comments

Comments
 (0)