Skip to content

Commit 675d9a3

Browse files
authored
bpo-40170: Convert PyObject_IS_GC() macro to a function (GH-19464)
1 parent a5900ec commit 675d9a3

File tree

7 files changed

+41
-15
lines changed

7 files changed

+41
-15
lines changed

Doc/c-api/gcsupport.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ Constructors for container types must conform to two rules:
6060
followed by the :c:member:`~PyTypeObject.tp_traverse` handler become valid, usually near the
6161
end of the constructor.
6262
63+
64+
.. c:function:: int PyObject_IS_GC(PyObject *obj)
65+
66+
Returns non-zero if the object implements the garbage collector protocol,
67+
otherwise returns 0.
68+
69+
The object cannot be tracked by the garbage collector if this function returns 0.
70+
71+
6372
.. c:function:: int PyObject_GC_IsTracked(PyObject *op)
6473
6574
Returns 1 if the object type of *op* implements the GC protocol and *op* is being

Include/cpython/objimpl.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,9 @@ PyAPI_FUNC(Py_ssize_t) _PyGC_CollectNoFail(void);
120120
PyAPI_FUNC(Py_ssize_t) _PyGC_CollectIfEnabled(void);
121121

122122

123-
/* Test if an object has a GC head */
124-
#define PyObject_IS_GC(o) \
125-
(PyType_IS_GC(Py_TYPE(o)) \
126-
&& (Py_TYPE(o)->tp_is_gc == NULL || Py_TYPE(o)->tp_is_gc(o)))
123+
/* Test if an object implements the garbage collector protocol */
124+
PyAPI_FUNC(int) PyObject_IS_GC(PyObject *obj);
125+
127126

128127
/* Code built with Py_BUILD_CORE must include pycore_gc.h instead which
129128
defines a different _PyGC_FINALIZED() macro. */

Include/internal/pycore_object.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,15 @@ _PyType_HasFeature(PyTypeObject *type, unsigned long feature) {
102102
return ((type->tp_flags & feature) != 0);
103103
}
104104

105+
// Fast inlined version of PyObject_IS_GC()
106+
static inline int
107+
_PyObject_IS_GC(PyObject *obj)
108+
{
109+
return (PyType_IS_GC(Py_TYPE(obj))
110+
&& (Py_TYPE(obj)->tp_is_gc == NULL
111+
|| Py_TYPE(obj)->tp_is_gc(obj)));
112+
}
113+
105114
// Fast inlined version of PyType_IS_GC()
106115
#define _PyType_IS_GC(t) _PyType_HasFeature((t), Py_TPFLAGS_HAVE_GC)
107116

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Convert :c:func:`PyObject_IS_GC` macro to a function to hide
2+
implementation details.

Modules/gcmodule.c

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ visit_decref(PyObject *op, void *parent)
442442
{
443443
_PyObject_ASSERT(_PyObject_CAST(parent), !_PyObject_IsFreed(op));
444444

445-
if (PyObject_IS_GC(op)) {
445+
if (_PyObject_IS_GC(op)) {
446446
PyGC_Head *gc = AS_GC(op);
447447
/* We're only interested in gc_refs for objects in the
448448
* generation being collected, which can be recognized
@@ -478,7 +478,7 @@ subtract_refs(PyGC_Head *containers)
478478
static int
479479
visit_reachable(PyObject *op, PyGC_Head *reachable)
480480
{
481-
if (!PyObject_IS_GC(op)) {
481+
if (!_PyObject_IS_GC(op)) {
482482
return 0;
483483
}
484484

@@ -705,7 +705,7 @@ clear_unreachable_mask(PyGC_Head *unreachable)
705705
static int
706706
visit_move(PyObject *op, PyGC_Head *tolist)
707707
{
708-
if (PyObject_IS_GC(op)) {
708+
if (_PyObject_IS_GC(op)) {
709709
PyGC_Head *gc = AS_GC(op);
710710
if (gc_is_collecting(gc)) {
711711
gc_list_move(gc, tolist);
@@ -1716,7 +1716,7 @@ gc_get_referents(PyObject *self, PyObject *args)
17161716
traverseproc traverse;
17171717
PyObject *obj = PyTuple_GET_ITEM(args, i);
17181718

1719-
if (! PyObject_IS_GC(obj))
1719+
if (!_PyObject_IS_GC(obj))
17201720
continue;
17211721
traverse = Py_TYPE(obj)->tp_traverse;
17221722
if (! traverse)
@@ -1856,7 +1856,7 @@ gc_is_tracked(PyObject *module, PyObject *obj)
18561856
{
18571857
PyObject *result;
18581858

1859-
if (PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj))
1859+
if (_PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj))
18601860
result = Py_True;
18611861
else
18621862
result = Py_False;
@@ -1877,7 +1877,7 @@ static PyObject *
18771877
gc_is_finalized(PyObject *module, PyObject *obj)
18781878
/*[clinic end generated code: output=e1516ac119a918ed input=201d0c58f69ae390]*/
18791879
{
1880-
if (PyObject_IS_GC(obj) && _PyGCHead_FINALIZED(AS_GC(obj))) {
1880+
if (_PyObject_IS_GC(obj) && _PyGCHead_FINALIZED(AS_GC(obj))) {
18811881
Py_RETURN_TRUE;
18821882
}
18831883
Py_RETURN_FALSE;
@@ -2204,6 +2204,12 @@ PyObject_GC_UnTrack(void *op_raw)
22042204
}
22052205
}
22062206

2207+
int
2208+
PyObject_IS_GC(PyObject *obj)
2209+
{
2210+
return _PyObject_IS_GC(obj);
2211+
}
2212+
22072213
static PyObject *
22082214
_PyObject_GC_Alloc(int use_calloc, size_t basicsize)
22092215
{
@@ -2317,7 +2323,7 @@ PyObject_GC_Del(void *op)
23172323
int
23182324
PyObject_GC_IsTracked(PyObject* obj)
23192325
{
2320-
if (PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj)) {
2326+
if (_PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj)) {
23212327
return 1;
23222328
}
23232329
return 0;
@@ -2326,7 +2332,7 @@ PyObject_GC_IsTracked(PyObject* obj)
23262332
int
23272333
PyObject_GC_IsFinalized(PyObject *obj)
23282334
{
2329-
if (PyObject_IS_GC(obj) && _PyGCHead_FINALIZED(AS_GC(obj))) {
2335+
if (_PyObject_IS_GC(obj) && _PyGCHead_FINALIZED(AS_GC(obj))) {
23302336
return 1;
23312337
}
23322338
return 0;

Objects/object.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2031,7 +2031,7 @@ _PyTrash_deposit_object(PyObject *op)
20312031
PyThreadState *tstate = _PyThreadState_GET();
20322032
struct _gc_runtime_state *gcstate = &tstate->interp->gc;
20332033

2034-
_PyObject_ASSERT(op, PyObject_IS_GC(op));
2034+
_PyObject_ASSERT(op, _PyObject_IS_GC(op));
20352035
_PyObject_ASSERT(op, !_PyObject_GC_IS_TRACKED(op));
20362036
_PyObject_ASSERT(op, Py_REFCNT(op) == 0);
20372037
_PyGCHead_SET_PREV(_Py_AS_GC(op), gcstate->trash_delete_later);
@@ -2043,7 +2043,7 @@ void
20432043
_PyTrash_thread_deposit_object(PyObject *op)
20442044
{
20452045
PyThreadState *tstate = _PyThreadState_GET();
2046-
_PyObject_ASSERT(op, PyObject_IS_GC(op));
2046+
_PyObject_ASSERT(op, _PyObject_IS_GC(op));
20472047
_PyObject_ASSERT(op, !_PyObject_GC_IS_TRACKED(op));
20482048
_PyObject_ASSERT(op, Py_REFCNT(op) == 0);
20492049
_PyGCHead_SET_PREV(_Py_AS_GC(op), tstate->trash_delete_later);

Python/sysmodule.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Data members:
1919
#include "frameobject.h"
2020
#include "pycore_ceval.h" // _Py_RecursionLimitLowerWaterMark()
2121
#include "pycore_initconfig.h"
22+
#include "pycore_object.h"
2223
#include "pycore_pathconfig.h"
2324
#include "pycore_pyerrors.h"
2425
#include "pycore_pylifecycle.h"
@@ -1679,7 +1680,7 @@ _PySys_GetSizeOf(PyObject *o)
16791680
}
16801681

16811682
/* add gc_head size */
1682-
if (PyObject_IS_GC(o))
1683+
if (_PyObject_IS_GC(o))
16831684
return ((size_t)size) + sizeof(PyGC_Head);
16841685
return (size_t)size;
16851686
}

0 commit comments

Comments
 (0)