From 675d9a3d7afc767a2818c84da7ba4bf4181dcf26 Mon Sep 17 00:00:00 2001 From: Hai Shi Date: Wed, 15 Apr 2020 02:11:20 +0800 Subject: [PATCH] bpo-40170: Convert PyObject_IS_GC() macro to a function (GH-19464) --- Doc/c-api/gcsupport.rst | 9 ++++++++ Include/cpython/objimpl.h | 7 +++--- Include/internal/pycore_object.h | 9 ++++++++ .../2020-04-11-06-12-44.bpo-40170.cmM9oK.rst | 2 ++ Modules/gcmodule.c | 22 ++++++++++++------- Objects/object.c | 4 ++-- Python/sysmodule.c | 3 ++- 7 files changed, 41 insertions(+), 15 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2020-04-11-06-12-44.bpo-40170.cmM9oK.rst diff --git a/Doc/c-api/gcsupport.rst b/Doc/c-api/gcsupport.rst index 4cab0f544ed..eee114c19d5 100644 --- a/Doc/c-api/gcsupport.rst +++ b/Doc/c-api/gcsupport.rst @@ -60,6 +60,15 @@ Constructors for container types must conform to two rules: followed by the :c:member:`~PyTypeObject.tp_traverse` handler become valid, usually near the end of the constructor. + +.. c:function:: int PyObject_IS_GC(PyObject *obj) + + Returns non-zero if the object implements the garbage collector protocol, + otherwise returns 0. + + The object cannot be tracked by the garbage collector if this function returns 0. + + .. c:function:: int PyObject_GC_IsTracked(PyObject *op) Returns 1 if the object type of *op* implements the GC protocol and *op* is being diff --git a/Include/cpython/objimpl.h b/Include/cpython/objimpl.h index 6634f29c8c8..b835936db70 100644 --- a/Include/cpython/objimpl.h +++ b/Include/cpython/objimpl.h @@ -120,10 +120,9 @@ PyAPI_FUNC(Py_ssize_t) _PyGC_CollectNoFail(void); PyAPI_FUNC(Py_ssize_t) _PyGC_CollectIfEnabled(void); -/* Test if an object has a GC head */ -#define PyObject_IS_GC(o) \ - (PyType_IS_GC(Py_TYPE(o)) \ - && (Py_TYPE(o)->tp_is_gc == NULL || Py_TYPE(o)->tp_is_gc(o))) +/* Test if an object implements the garbage collector protocol */ +PyAPI_FUNC(int) PyObject_IS_GC(PyObject *obj); + /* Code built with Py_BUILD_CORE must include pycore_gc.h instead which defines a different _PyGC_FINALIZED() macro. */ diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 7c0f24ac07d..32e86d06db5 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -102,6 +102,15 @@ _PyType_HasFeature(PyTypeObject *type, unsigned long feature) { return ((type->tp_flags & feature) != 0); } +// Fast inlined version of PyObject_IS_GC() +static inline int +_PyObject_IS_GC(PyObject *obj) +{ + return (PyType_IS_GC(Py_TYPE(obj)) + && (Py_TYPE(obj)->tp_is_gc == NULL + || Py_TYPE(obj)->tp_is_gc(obj))); +} + // Fast inlined version of PyType_IS_GC() #define _PyType_IS_GC(t) _PyType_HasFeature((t), Py_TPFLAGS_HAVE_GC) diff --git a/Misc/NEWS.d/next/C API/2020-04-11-06-12-44.bpo-40170.cmM9oK.rst b/Misc/NEWS.d/next/C API/2020-04-11-06-12-44.bpo-40170.cmM9oK.rst new file mode 100644 index 00000000000..832b7f6e081 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-04-11-06-12-44.bpo-40170.cmM9oK.rst @@ -0,0 +1,2 @@ +Convert :c:func:`PyObject_IS_GC` macro to a function to hide +implementation details. diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 281ab33e065..d2cd2c92966 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -442,7 +442,7 @@ visit_decref(PyObject *op, void *parent) { _PyObject_ASSERT(_PyObject_CAST(parent), !_PyObject_IsFreed(op)); - if (PyObject_IS_GC(op)) { + if (_PyObject_IS_GC(op)) { PyGC_Head *gc = AS_GC(op); /* We're only interested in gc_refs for objects in the * generation being collected, which can be recognized @@ -478,7 +478,7 @@ subtract_refs(PyGC_Head *containers) static int visit_reachable(PyObject *op, PyGC_Head *reachable) { - if (!PyObject_IS_GC(op)) { + if (!_PyObject_IS_GC(op)) { return 0; } @@ -705,7 +705,7 @@ clear_unreachable_mask(PyGC_Head *unreachable) static int visit_move(PyObject *op, PyGC_Head *tolist) { - if (PyObject_IS_GC(op)) { + if (_PyObject_IS_GC(op)) { PyGC_Head *gc = AS_GC(op); if (gc_is_collecting(gc)) { gc_list_move(gc, tolist); @@ -1716,7 +1716,7 @@ gc_get_referents(PyObject *self, PyObject *args) traverseproc traverse; PyObject *obj = PyTuple_GET_ITEM(args, i); - if (! PyObject_IS_GC(obj)) + if (!_PyObject_IS_GC(obj)) continue; traverse = Py_TYPE(obj)->tp_traverse; if (! traverse) @@ -1856,7 +1856,7 @@ gc_is_tracked(PyObject *module, PyObject *obj) { PyObject *result; - if (PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj)) + if (_PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj)) result = Py_True; else result = Py_False; @@ -1877,7 +1877,7 @@ static PyObject * gc_is_finalized(PyObject *module, PyObject *obj) /*[clinic end generated code: output=e1516ac119a918ed input=201d0c58f69ae390]*/ { - if (PyObject_IS_GC(obj) && _PyGCHead_FINALIZED(AS_GC(obj))) { + if (_PyObject_IS_GC(obj) && _PyGCHead_FINALIZED(AS_GC(obj))) { Py_RETURN_TRUE; } Py_RETURN_FALSE; @@ -2204,6 +2204,12 @@ PyObject_GC_UnTrack(void *op_raw) } } +int +PyObject_IS_GC(PyObject *obj) +{ + return _PyObject_IS_GC(obj); +} + static PyObject * _PyObject_GC_Alloc(int use_calloc, size_t basicsize) { @@ -2317,7 +2323,7 @@ PyObject_GC_Del(void *op) int PyObject_GC_IsTracked(PyObject* obj) { - if (PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj)) { + if (_PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj)) { return 1; } return 0; @@ -2326,7 +2332,7 @@ PyObject_GC_IsTracked(PyObject* obj) int PyObject_GC_IsFinalized(PyObject *obj) { - if (PyObject_IS_GC(obj) && _PyGCHead_FINALIZED(AS_GC(obj))) { + if (_PyObject_IS_GC(obj) && _PyGCHead_FINALIZED(AS_GC(obj))) { return 1; } return 0; diff --git a/Objects/object.c b/Objects/object.c index c759ccc5684..75ea92ad900 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -2031,7 +2031,7 @@ _PyTrash_deposit_object(PyObject *op) PyThreadState *tstate = _PyThreadState_GET(); struct _gc_runtime_state *gcstate = &tstate->interp->gc; - _PyObject_ASSERT(op, PyObject_IS_GC(op)); + _PyObject_ASSERT(op, _PyObject_IS_GC(op)); _PyObject_ASSERT(op, !_PyObject_GC_IS_TRACKED(op)); _PyObject_ASSERT(op, Py_REFCNT(op) == 0); _PyGCHead_SET_PREV(_Py_AS_GC(op), gcstate->trash_delete_later); @@ -2043,7 +2043,7 @@ void _PyTrash_thread_deposit_object(PyObject *op) { PyThreadState *tstate = _PyThreadState_GET(); - _PyObject_ASSERT(op, PyObject_IS_GC(op)); + _PyObject_ASSERT(op, _PyObject_IS_GC(op)); _PyObject_ASSERT(op, !_PyObject_GC_IS_TRACKED(op)); _PyObject_ASSERT(op, Py_REFCNT(op) == 0); _PyGCHead_SET_PREV(_Py_AS_GC(op), tstate->trash_delete_later); diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 79e5df06d30..741979a09ac 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -19,6 +19,7 @@ Data members: #include "frameobject.h" #include "pycore_ceval.h" // _Py_RecursionLimitLowerWaterMark() #include "pycore_initconfig.h" +#include "pycore_object.h" #include "pycore_pathconfig.h" #include "pycore_pyerrors.h" #include "pycore_pylifecycle.h" @@ -1679,7 +1680,7 @@ _PySys_GetSizeOf(PyObject *o) } /* add gc_head size */ - if (PyObject_IS_GC(o)) + if (_PyObject_IS_GC(o)) return ((size_t)size) + sizeof(PyGC_Head); return (size_t)size; }