diff --git a/Include/internal/pycore_object.h b/Include/internal/pycore_object.h index 002b70007b6..5c3d3cae235 100644 --- a/Include/internal/pycore_object.h +++ b/Include/internal/pycore_object.h @@ -8,7 +8,7 @@ extern "C" { # error "this header requires Py_BUILD_CORE define" #endif -#include "pycore_pystate.h" /* _PyRuntime.gc */ +#include "pycore_pystate.h" /* PyInterpreterState.gc */ PyAPI_FUNC(int) _PyType_CheckConsistency(PyTypeObject *type); PyAPI_FUNC(int) _PyDict_CheckConsistency(PyObject *mp, int check_content); @@ -94,6 +94,15 @@ _PyObject_GET_WEAKREFS_LISTPTR(PyObject *op) return (PyObject **)((char *)op + offset); } +// Fast inlined version of PyType_HasFeature() +static inline int +_PyType_HasFeature(PyTypeObject *type, unsigned long feature) { + return ((type->tp_flags & feature) != 0); +} + +// Fast inlined version of PyType_IS_GC() +#define _PyType_IS_GC(t) _PyType_HasFeature((t), Py_TPFLAGS_HAVE_GC) + #ifdef __cplusplus } #endif diff --git a/Include/object.h b/Include/object.h index 6b6c66a52a6..564ba290328 100644 --- a/Include/object.h +++ b/Include/object.h @@ -614,11 +614,7 @@ times. static inline int PyType_HasFeature(PyTypeObject *type, unsigned long feature) { -#ifdef Py_LIMITED_API return ((PyType_GetFlags(type) & feature) != 0); -#else - return ((type->tp_flags & feature) != 0); -#endif } #define PyType_FastSubclass(type, flag) PyType_HasFeature(type, flag) diff --git a/Misc/NEWS.d/next/C API/2020-04-05-00-21-38.bpo-40170.Tx0vy6.rst b/Misc/NEWS.d/next/C API/2020-04-05-00-21-38.bpo-40170.Tx0vy6.rst new file mode 100644 index 00000000000..858611df905 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-04-05-00-21-38.bpo-40170.Tx0vy6.rst @@ -0,0 +1,4 @@ +:c:func:`PyType_HasFeature` now always calls :c:func:`PyType_GetFlags` to +hide implementation details. Previously, it accessed directly the +:c:member:`PyTypeObject.tp_flags` member when the limited C API was not +used. diff --git a/Objects/object.c b/Objects/object.c index 069afc0ac99..ef4ba997de4 100644 --- a/Objects/object.c +++ b/Objects/object.c @@ -188,11 +188,11 @@ PyObject_CallFinalizer(PyObject *self) if (tp->tp_finalize == NULL) return; /* tp_finalize should only be called once. */ - if (PyType_IS_GC(tp) && _PyGC_FINALIZED(self)) + if (_PyType_IS_GC(tp) && _PyGC_FINALIZED(self)) return; tp->tp_finalize(self); - if (PyType_IS_GC(tp)) { + if (_PyType_IS_GC(tp)) { _PyGC_SET_FINALIZED(self); } } @@ -229,7 +229,7 @@ PyObject_CallFinalizerFromDealloc(PyObject *self) Py_SET_REFCNT(self, refcnt); _PyObject_ASSERT(self, - (!PyType_IS_GC(Py_TYPE(self)) + (!_PyType_IS_GC(Py_TYPE(self)) || _PyObject_GC_IS_TRACKED(self))); /* If Py_REF_DEBUG macro is defined, _Py_NewReference() increased _Py_RefTotal, so we need to undo that. */ @@ -1104,7 +1104,7 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method) descr = _PyType_Lookup(tp, name); if (descr != NULL) { Py_INCREF(descr); - if (PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)) { + if (_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)) { meth_found = 1; } else { f = Py_TYPE(descr)->tp_descr_get; @@ -2177,7 +2177,7 @@ _PyObject_AssertFailed(PyObject *obj, const char *expr, const char *msg, to crash than dumping the traceback. */ void *ptr; PyTypeObject *type = Py_TYPE(obj); - if (PyType_IS_GC(type)) { + if (_PyType_IS_GC(type)) { ptr = (void *)((char *)obj - sizeof(PyGC_Head)); } else { diff --git a/Objects/typeobject.c b/Objects/typeobject.c index bdd16af9290..ca26d960643 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -269,7 +269,7 @@ PyType_Modified(PyTypeObject *type) PyObject *raw, *ref; Py_ssize_t i; - if (!PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) + if (!_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) return; raw = type->tp_subclasses; @@ -307,7 +307,7 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) { PyObject *mro_meth = NULL; PyObject *type_mro_meth = NULL; - if (!PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG)) + if (!_PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG)) return; if (custom) { @@ -332,7 +332,7 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) { assert(PyType_Check(b)); cls = (PyTypeObject *)b; - if (!PyType_HasFeature(cls, Py_TPFLAGS_HAVE_VERSION_TAG) || + if (!_PyType_HasFeature(cls, Py_TPFLAGS_HAVE_VERSION_TAG) || !PyType_IsSubtype(type, cls)) { goto clear; } @@ -356,11 +356,11 @@ assign_version_tag(PyTypeObject *type) Py_ssize_t i, n; PyObject *bases; - if (PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) + if (_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) return 1; - if (!PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG)) + if (!_PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG)) return 0; - if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + if (!_PyType_HasFeature(type, Py_TPFLAGS_READY)) return 0; type->tp_version_tag = next_version_tag++; @@ -1028,23 +1028,29 @@ PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems) const size_t size = _PyObject_VAR_SIZE(type, nitems+1); /* note that we need to add one, for the sentinel */ - if (PyType_IS_GC(type)) + if (_PyType_IS_GC(type)) { obj = _PyObject_GC_Malloc(size); - else + } + else { obj = (PyObject *)PyObject_MALLOC(size); + } - if (obj == NULL) + if (obj == NULL) { return PyErr_NoMemory(); + } memset(obj, '\0', size); - if (type->tp_itemsize == 0) + if (type->tp_itemsize == 0) { (void)PyObject_INIT(obj, type); - else + } + else { (void) PyObject_INIT_VAR((PyVarObject *)obj, type, nitems); + } - if (PyType_IS_GC(type)) + if (_PyType_IS_GC(type)) { _PyObject_GC_TRACK(obj); + } return obj; } @@ -1178,7 +1184,7 @@ subtype_dealloc(PyObject *self) /* Test whether the type has GC exactly once */ - if (!PyType_IS_GC(type)) { + if (!_PyType_IS_GC(type)) { /* A non GC dynamic type allows certain simplifications: there's no need to call clear_slots(), or DECREF the dict, or clear weakrefs. */ @@ -1304,8 +1310,9 @@ subtype_dealloc(PyObject *self) /* Call the base tp_dealloc(); first retrack self if * basedealloc knows about gc. */ - if (PyType_IS_GC(base)) + if (_PyType_IS_GC(base)) { _PyObject_GC_TRACK(self); + } assert(basedealloc); basedealloc(self); @@ -1435,7 +1442,7 @@ lookup_maybe_method(PyObject *self, _Py_Identifier *attrid, int *unbound) return NULL; } - if (PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)) { + if (_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)) { /* Avoid temporary PyMethodObject */ *unbound = 1; Py_INCREF(res); @@ -2026,7 +2033,7 @@ best_base(PyObject *bases) if (PyType_Ready(base_i) < 0) return NULL; } - if (!PyType_HasFeature(base_i, Py_TPFLAGS_BASETYPE)) { + if (!_PyType_HasFeature(base_i, Py_TPFLAGS_BASETYPE)) { PyErr_Format(PyExc_TypeError, "type '%.100s' is not an acceptable base type", base_i->tp_name); @@ -2933,7 +2940,7 @@ PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases) if (base == NULL) { goto fail; } - if (!PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) { + if (!_PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) { PyErr_Format(PyExc_TypeError, "type '%.100s' is not an acceptable base type", base->tp_name); @@ -3051,7 +3058,7 @@ PyType_FromSpec(PyType_Spec *spec) void * PyType_GetSlot(PyTypeObject *type, int slot) { - if (!PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE) || slot < 0) { + if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE) || slot < 0) { PyErr_BadInternalCall(); return NULL; } @@ -3134,7 +3141,7 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name) unsigned int h; if (MCACHE_CACHEABLE_NAME(name) && - PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) { + _PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) { /* fast path */ h = MCACHE_HASH_METHOD(type, name); if (method_cache[h].version == type->tp_version_tag && @@ -5404,7 +5411,7 @@ PyType_Ready(PyTypeObject *type) } /* Sanity check for tp_free. */ - if (PyType_IS_GC(type) && (type->tp_flags & Py_TPFLAGS_BASETYPE) && + if (_PyType_IS_GC(type) && (type->tp_flags & Py_TPFLAGS_BASETYPE) && (type->tp_free == NULL || type->tp_free == PyObject_Del)) { /* This base class needs to call tp_free, but doesn't have * one, or its tp_free is for non-gc'ed objects.