Skip to content

bpo-40170: PyType_HasFeature() no longer acccess directly tp_flags #19378

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion Include/internal/pycore_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
4 changes: 0 additions & 4 deletions Include/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
10 changes: 5 additions & 5 deletions Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,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);
}
}
Expand Down Expand Up @@ -228,7 +228,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. */
Expand Down Expand Up @@ -1103,7 +1103,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;
Expand Down Expand Up @@ -2176,7 +2176,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 {
Expand Down
47 changes: 27 additions & 20 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
}
Expand All @@ -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++;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 &&
Expand Down Expand Up @@ -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.
Expand Down