mirror of https://github.com/python/cpython
bpo-40170: PyType_HasFeature() now always calls PyType_GetFlags() (GH-19378)
PyType_HasFeature() now always calls PyType_GetFlags() to hide implementation details. Previously, it accessed directly the PyTypeObject.tp_flags member when the limited C API was not used. Add fast inlined version _PyType_HasFeature() and _PyType_IS_GC() for object.c and typeobject.c.
This commit is contained in:
parent
ef5c615f5a
commit
45ec5b99ae
|
@ -8,7 +8,7 @@ extern "C" {
|
||||||
# error "this header requires Py_BUILD_CORE define"
|
# error "this header requires Py_BUILD_CORE define"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "pycore_pystate.h" /* _PyRuntime.gc */
|
#include "pycore_pystate.h" /* PyInterpreterState.gc */
|
||||||
|
|
||||||
PyAPI_FUNC(int) _PyType_CheckConsistency(PyTypeObject *type);
|
PyAPI_FUNC(int) _PyType_CheckConsistency(PyTypeObject *type);
|
||||||
PyAPI_FUNC(int) _PyDict_CheckConsistency(PyObject *mp, int check_content);
|
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);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -614,11 +614,7 @@ times.
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
PyType_HasFeature(PyTypeObject *type, unsigned long feature) {
|
PyType_HasFeature(PyTypeObject *type, unsigned long feature) {
|
||||||
#ifdef Py_LIMITED_API
|
|
||||||
return ((PyType_GetFlags(type) & feature) != 0);
|
return ((PyType_GetFlags(type) & feature) != 0);
|
||||||
#else
|
|
||||||
return ((type->tp_flags & feature) != 0);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define PyType_FastSubclass(type, flag) PyType_HasFeature(type, flag)
|
#define PyType_FastSubclass(type, flag) PyType_HasFeature(type, flag)
|
||||||
|
|
|
@ -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.
|
|
@ -188,11 +188,11 @@ PyObject_CallFinalizer(PyObject *self)
|
||||||
if (tp->tp_finalize == NULL)
|
if (tp->tp_finalize == NULL)
|
||||||
return;
|
return;
|
||||||
/* tp_finalize should only be called once. */
|
/* tp_finalize should only be called once. */
|
||||||
if (PyType_IS_GC(tp) && _PyGC_FINALIZED(self))
|
if (_PyType_IS_GC(tp) && _PyGC_FINALIZED(self))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
tp->tp_finalize(self);
|
tp->tp_finalize(self);
|
||||||
if (PyType_IS_GC(tp)) {
|
if (_PyType_IS_GC(tp)) {
|
||||||
_PyGC_SET_FINALIZED(self);
|
_PyGC_SET_FINALIZED(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -229,7 +229,7 @@ PyObject_CallFinalizerFromDealloc(PyObject *self)
|
||||||
Py_SET_REFCNT(self, refcnt);
|
Py_SET_REFCNT(self, refcnt);
|
||||||
|
|
||||||
_PyObject_ASSERT(self,
|
_PyObject_ASSERT(self,
|
||||||
(!PyType_IS_GC(Py_TYPE(self))
|
(!_PyType_IS_GC(Py_TYPE(self))
|
||||||
|| _PyObject_GC_IS_TRACKED(self)));
|
|| _PyObject_GC_IS_TRACKED(self)));
|
||||||
/* If Py_REF_DEBUG macro is defined, _Py_NewReference() increased
|
/* If Py_REF_DEBUG macro is defined, _Py_NewReference() increased
|
||||||
_Py_RefTotal, so we need to undo that. */
|
_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);
|
descr = _PyType_Lookup(tp, name);
|
||||||
if (descr != NULL) {
|
if (descr != NULL) {
|
||||||
Py_INCREF(descr);
|
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;
|
meth_found = 1;
|
||||||
} else {
|
} else {
|
||||||
f = Py_TYPE(descr)->tp_descr_get;
|
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. */
|
to crash than dumping the traceback. */
|
||||||
void *ptr;
|
void *ptr;
|
||||||
PyTypeObject *type = Py_TYPE(obj);
|
PyTypeObject *type = Py_TYPE(obj);
|
||||||
if (PyType_IS_GC(type)) {
|
if (_PyType_IS_GC(type)) {
|
||||||
ptr = (void *)((char *)obj - sizeof(PyGC_Head));
|
ptr = (void *)((char *)obj - sizeof(PyGC_Head));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -269,7 +269,7 @@ PyType_Modified(PyTypeObject *type)
|
||||||
PyObject *raw, *ref;
|
PyObject *raw, *ref;
|
||||||
Py_ssize_t i;
|
Py_ssize_t i;
|
||||||
|
|
||||||
if (!PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG))
|
if (!_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
raw = type->tp_subclasses;
|
raw = type->tp_subclasses;
|
||||||
|
@ -307,7 +307,7 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
|
||||||
PyObject *mro_meth = NULL;
|
PyObject *mro_meth = NULL;
|
||||||
PyObject *type_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;
|
return;
|
||||||
|
|
||||||
if (custom) {
|
if (custom) {
|
||||||
|
@ -332,7 +332,7 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
|
||||||
assert(PyType_Check(b));
|
assert(PyType_Check(b));
|
||||||
cls = (PyTypeObject *)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)) {
|
!PyType_IsSubtype(type, cls)) {
|
||||||
goto clear;
|
goto clear;
|
||||||
}
|
}
|
||||||
|
@ -356,11 +356,11 @@ assign_version_tag(PyTypeObject *type)
|
||||||
Py_ssize_t i, n;
|
Py_ssize_t i, n;
|
||||||
PyObject *bases;
|
PyObject *bases;
|
||||||
|
|
||||||
if (PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG))
|
if (_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG))
|
||||||
return 1;
|
return 1;
|
||||||
if (!PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG))
|
if (!_PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG))
|
||||||
return 0;
|
return 0;
|
||||||
if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
|
if (!_PyType_HasFeature(type, Py_TPFLAGS_READY))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
type->tp_version_tag = next_version_tag++;
|
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);
|
const size_t size = _PyObject_VAR_SIZE(type, nitems+1);
|
||||||
/* note that we need to add one, for the sentinel */
|
/* 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);
|
obj = _PyObject_GC_Malloc(size);
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
obj = (PyObject *)PyObject_MALLOC(size);
|
obj = (PyObject *)PyObject_MALLOC(size);
|
||||||
|
}
|
||||||
|
|
||||||
if (obj == NULL)
|
if (obj == NULL) {
|
||||||
return PyErr_NoMemory();
|
return PyErr_NoMemory();
|
||||||
|
}
|
||||||
|
|
||||||
memset(obj, '\0', size);
|
memset(obj, '\0', size);
|
||||||
|
|
||||||
if (type->tp_itemsize == 0)
|
if (type->tp_itemsize == 0) {
|
||||||
(void)PyObject_INIT(obj, type);
|
(void)PyObject_INIT(obj, type);
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
(void) PyObject_INIT_VAR((PyVarObject *)obj, type, nitems);
|
(void) PyObject_INIT_VAR((PyVarObject *)obj, type, nitems);
|
||||||
|
}
|
||||||
|
|
||||||
if (PyType_IS_GC(type))
|
if (_PyType_IS_GC(type)) {
|
||||||
_PyObject_GC_TRACK(obj);
|
_PyObject_GC_TRACK(obj);
|
||||||
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1178,7 +1184,7 @@ subtype_dealloc(PyObject *self)
|
||||||
|
|
||||||
/* Test whether the type has GC exactly once */
|
/* 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:
|
/* A non GC dynamic type allows certain simplifications:
|
||||||
there's no need to call clear_slots(), or DECREF the dict,
|
there's no need to call clear_slots(), or DECREF the dict,
|
||||||
or clear weakrefs. */
|
or clear weakrefs. */
|
||||||
|
@ -1304,8 +1310,9 @@ subtype_dealloc(PyObject *self)
|
||||||
/* Call the base tp_dealloc(); first retrack self if
|
/* Call the base tp_dealloc(); first retrack self if
|
||||||
* basedealloc knows about gc.
|
* basedealloc knows about gc.
|
||||||
*/
|
*/
|
||||||
if (PyType_IS_GC(base))
|
if (_PyType_IS_GC(base)) {
|
||||||
_PyObject_GC_TRACK(self);
|
_PyObject_GC_TRACK(self);
|
||||||
|
}
|
||||||
assert(basedealloc);
|
assert(basedealloc);
|
||||||
basedealloc(self);
|
basedealloc(self);
|
||||||
|
|
||||||
|
@ -1435,7 +1442,7 @@ lookup_maybe_method(PyObject *self, _Py_Identifier *attrid, int *unbound)
|
||||||
return NULL;
|
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 */
|
/* Avoid temporary PyMethodObject */
|
||||||
*unbound = 1;
|
*unbound = 1;
|
||||||
Py_INCREF(res);
|
Py_INCREF(res);
|
||||||
|
@ -2026,7 +2033,7 @@ best_base(PyObject *bases)
|
||||||
if (PyType_Ready(base_i) < 0)
|
if (PyType_Ready(base_i) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (!PyType_HasFeature(base_i, Py_TPFLAGS_BASETYPE)) {
|
if (!_PyType_HasFeature(base_i, Py_TPFLAGS_BASETYPE)) {
|
||||||
PyErr_Format(PyExc_TypeError,
|
PyErr_Format(PyExc_TypeError,
|
||||||
"type '%.100s' is not an acceptable base type",
|
"type '%.100s' is not an acceptable base type",
|
||||||
base_i->tp_name);
|
base_i->tp_name);
|
||||||
|
@ -2933,7 +2940,7 @@ PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases)
|
||||||
if (base == NULL) {
|
if (base == NULL) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (!PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) {
|
if (!_PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) {
|
||||||
PyErr_Format(PyExc_TypeError,
|
PyErr_Format(PyExc_TypeError,
|
||||||
"type '%.100s' is not an acceptable base type",
|
"type '%.100s' is not an acceptable base type",
|
||||||
base->tp_name);
|
base->tp_name);
|
||||||
|
@ -3051,7 +3058,7 @@ PyType_FromSpec(PyType_Spec *spec)
|
||||||
void *
|
void *
|
||||||
PyType_GetSlot(PyTypeObject *type, int slot)
|
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();
|
PyErr_BadInternalCall();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -3134,7 +3141,7 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
|
||||||
unsigned int h;
|
unsigned int h;
|
||||||
|
|
||||||
if (MCACHE_CACHEABLE_NAME(name) &&
|
if (MCACHE_CACHEABLE_NAME(name) &&
|
||||||
PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) {
|
_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) {
|
||||||
/* fast path */
|
/* fast path */
|
||||||
h = MCACHE_HASH_METHOD(type, name);
|
h = MCACHE_HASH_METHOD(type, name);
|
||||||
if (method_cache[h].version == type->tp_version_tag &&
|
if (method_cache[h].version == type->tp_version_tag &&
|
||||||
|
@ -5404,7 +5411,7 @@ PyType_Ready(PyTypeObject *type)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sanity check for tp_free. */
|
/* 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)) {
|
(type->tp_free == NULL || type->tp_free == PyObject_Del)) {
|
||||||
/* This base class needs to call tp_free, but doesn't have
|
/* This base class needs to call tp_free, but doesn't have
|
||||||
* one, or its tp_free is for non-gc'ed objects.
|
* one, or its tp_free is for non-gc'ed objects.
|
||||||
|
|
Loading…
Reference in New Issue