Skip to content

Commit 45ec5b9

Browse files
authored
bpo-40170: PyType_HasFeature() now always calls PyType_GetFlags() (pythonGH-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.
1 parent ef5c615 commit 45ec5b9

File tree

5 files changed

+46
-30
lines changed

5 files changed

+46
-30
lines changed

Include/internal/pycore_object.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ extern "C" {
88
# error "this header requires Py_BUILD_CORE define"
99
#endif
1010

11-
#include "pycore_pystate.h" /* _PyRuntime.gc */
11+
#include "pycore_pystate.h" /* PyInterpreterState.gc */
1212

1313
PyAPI_FUNC(int) _PyType_CheckConsistency(PyTypeObject *type);
1414
PyAPI_FUNC(int) _PyDict_CheckConsistency(PyObject *mp, int check_content);
@@ -94,6 +94,15 @@ _PyObject_GET_WEAKREFS_LISTPTR(PyObject *op)
9494
return (PyObject **)((char *)op + offset);
9595
}
9696

97+
// Fast inlined version of PyType_HasFeature()
98+
static inline int
99+
_PyType_HasFeature(PyTypeObject *type, unsigned long feature) {
100+
return ((type->tp_flags & feature) != 0);
101+
}
102+
103+
// Fast inlined version of PyType_IS_GC()
104+
#define _PyType_IS_GC(t) _PyType_HasFeature((t), Py_TPFLAGS_HAVE_GC)
105+
97106
#ifdef __cplusplus
98107
}
99108
#endif

Include/object.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -614,11 +614,7 @@ times.
614614

615615
static inline int
616616
PyType_HasFeature(PyTypeObject *type, unsigned long feature) {
617-
#ifdef Py_LIMITED_API
618617
return ((PyType_GetFlags(type) & feature) != 0);
619-
#else
620-
return ((type->tp_flags & feature) != 0);
621-
#endif
622618
}
623619

624620
#define PyType_FastSubclass(type, flag) PyType_HasFeature(type, flag)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:c:func:`PyType_HasFeature` now always calls :c:func:`PyType_GetFlags` to
2+
hide implementation details. Previously, it accessed directly the
3+
:c:member:`PyTypeObject.tp_flags` member when the limited C API was not
4+
used.

Objects/object.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,11 @@ PyObject_CallFinalizer(PyObject *self)
188188
if (tp->tp_finalize == NULL)
189189
return;
190190
/* tp_finalize should only be called once. */
191-
if (PyType_IS_GC(tp) && _PyGC_FINALIZED(self))
191+
if (_PyType_IS_GC(tp) && _PyGC_FINALIZED(self))
192192
return;
193193

194194
tp->tp_finalize(self);
195-
if (PyType_IS_GC(tp)) {
195+
if (_PyType_IS_GC(tp)) {
196196
_PyGC_SET_FINALIZED(self);
197197
}
198198
}
@@ -229,7 +229,7 @@ PyObject_CallFinalizerFromDealloc(PyObject *self)
229229
Py_SET_REFCNT(self, refcnt);
230230

231231
_PyObject_ASSERT(self,
232-
(!PyType_IS_GC(Py_TYPE(self))
232+
(!_PyType_IS_GC(Py_TYPE(self))
233233
|| _PyObject_GC_IS_TRACKED(self)));
234234
/* If Py_REF_DEBUG macro is defined, _Py_NewReference() increased
235235
_Py_RefTotal, so we need to undo that. */
@@ -1104,7 +1104,7 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method)
11041104
descr = _PyType_Lookup(tp, name);
11051105
if (descr != NULL) {
11061106
Py_INCREF(descr);
1107-
if (PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)) {
1107+
if (_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)) {
11081108
meth_found = 1;
11091109
} else {
11101110
f = Py_TYPE(descr)->tp_descr_get;
@@ -2177,7 +2177,7 @@ _PyObject_AssertFailed(PyObject *obj, const char *expr, const char *msg,
21772177
to crash than dumping the traceback. */
21782178
void *ptr;
21792179
PyTypeObject *type = Py_TYPE(obj);
2180-
if (PyType_IS_GC(type)) {
2180+
if (_PyType_IS_GC(type)) {
21812181
ptr = (void *)((char *)obj - sizeof(PyGC_Head));
21822182
}
21832183
else {

Objects/typeobject.c

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ PyType_Modified(PyTypeObject *type)
269269
PyObject *raw, *ref;
270270
Py_ssize_t i;
271271

272-
if (!PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG))
272+
if (!_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG))
273273
return;
274274

275275
raw = type->tp_subclasses;
@@ -307,7 +307,7 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
307307
PyObject *mro_meth = NULL;
308308
PyObject *type_mro_meth = NULL;
309309

310-
if (!PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG))
310+
if (!_PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG))
311311
return;
312312

313313
if (custom) {
@@ -332,7 +332,7 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
332332
assert(PyType_Check(b));
333333
cls = (PyTypeObject *)b;
334334

335-
if (!PyType_HasFeature(cls, Py_TPFLAGS_HAVE_VERSION_TAG) ||
335+
if (!_PyType_HasFeature(cls, Py_TPFLAGS_HAVE_VERSION_TAG) ||
336336
!PyType_IsSubtype(type, cls)) {
337337
goto clear;
338338
}
@@ -356,11 +356,11 @@ assign_version_tag(PyTypeObject *type)
356356
Py_ssize_t i, n;
357357
PyObject *bases;
358358

359-
if (PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG))
359+
if (_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG))
360360
return 1;
361-
if (!PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG))
361+
if (!_PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG))
362362
return 0;
363-
if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
363+
if (!_PyType_HasFeature(type, Py_TPFLAGS_READY))
364364
return 0;
365365

366366
type->tp_version_tag = next_version_tag++;
@@ -1028,23 +1028,29 @@ PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)
10281028
const size_t size = _PyObject_VAR_SIZE(type, nitems+1);
10291029
/* note that we need to add one, for the sentinel */
10301030

1031-
if (PyType_IS_GC(type))
1031+
if (_PyType_IS_GC(type)) {
10321032
obj = _PyObject_GC_Malloc(size);
1033-
else
1033+
}
1034+
else {
10341035
obj = (PyObject *)PyObject_MALLOC(size);
1036+
}
10351037

1036-
if (obj == NULL)
1038+
if (obj == NULL) {
10371039
return PyErr_NoMemory();
1040+
}
10381041

10391042
memset(obj, '\0', size);
10401043

1041-
if (type->tp_itemsize == 0)
1044+
if (type->tp_itemsize == 0) {
10421045
(void)PyObject_INIT(obj, type);
1043-
else
1046+
}
1047+
else {
10441048
(void) PyObject_INIT_VAR((PyVarObject *)obj, type, nitems);
1049+
}
10451050

1046-
if (PyType_IS_GC(type))
1051+
if (_PyType_IS_GC(type)) {
10471052
_PyObject_GC_TRACK(obj);
1053+
}
10481054
return obj;
10491055
}
10501056

@@ -1178,7 +1184,7 @@ subtype_dealloc(PyObject *self)
11781184

11791185
/* Test whether the type has GC exactly once */
11801186

1181-
if (!PyType_IS_GC(type)) {
1187+
if (!_PyType_IS_GC(type)) {
11821188
/* A non GC dynamic type allows certain simplifications:
11831189
there's no need to call clear_slots(), or DECREF the dict,
11841190
or clear weakrefs. */
@@ -1304,8 +1310,9 @@ subtype_dealloc(PyObject *self)
13041310
/* Call the base tp_dealloc(); first retrack self if
13051311
* basedealloc knows about gc.
13061312
*/
1307-
if (PyType_IS_GC(base))
1313+
if (_PyType_IS_GC(base)) {
13081314
_PyObject_GC_TRACK(self);
1315+
}
13091316
assert(basedealloc);
13101317
basedealloc(self);
13111318

@@ -1435,7 +1442,7 @@ lookup_maybe_method(PyObject *self, _Py_Identifier *attrid, int *unbound)
14351442
return NULL;
14361443
}
14371444

1438-
if (PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)) {
1445+
if (_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)) {
14391446
/* Avoid temporary PyMethodObject */
14401447
*unbound = 1;
14411448
Py_INCREF(res);
@@ -2026,7 +2033,7 @@ best_base(PyObject *bases)
20262033
if (PyType_Ready(base_i) < 0)
20272034
return NULL;
20282035
}
2029-
if (!PyType_HasFeature(base_i, Py_TPFLAGS_BASETYPE)) {
2036+
if (!_PyType_HasFeature(base_i, Py_TPFLAGS_BASETYPE)) {
20302037
PyErr_Format(PyExc_TypeError,
20312038
"type '%.100s' is not an acceptable base type",
20322039
base_i->tp_name);
@@ -2933,7 +2940,7 @@ PyType_FromSpecWithBases(PyType_Spec *spec, PyObject *bases)
29332940
if (base == NULL) {
29342941
goto fail;
29352942
}
2936-
if (!PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) {
2943+
if (!_PyType_HasFeature(base, Py_TPFLAGS_BASETYPE)) {
29372944
PyErr_Format(PyExc_TypeError,
29382945
"type '%.100s' is not an acceptable base type",
29392946
base->tp_name);
@@ -3051,7 +3058,7 @@ PyType_FromSpec(PyType_Spec *spec)
30513058
void *
30523059
PyType_GetSlot(PyTypeObject *type, int slot)
30533060
{
3054-
if (!PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE) || slot < 0) {
3061+
if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE) || slot < 0) {
30553062
PyErr_BadInternalCall();
30563063
return NULL;
30573064
}
@@ -3134,7 +3141,7 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
31343141
unsigned int h;
31353142

31363143
if (MCACHE_CACHEABLE_NAME(name) &&
3137-
PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) {
3144+
_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) {
31383145
/* fast path */
31393146
h = MCACHE_HASH_METHOD(type, name);
31403147
if (method_cache[h].version == type->tp_version_tag &&
@@ -5404,7 +5411,7 @@ PyType_Ready(PyTypeObject *type)
54045411
}
54055412

54065413
/* Sanity check for tp_free. */
5407-
if (PyType_IS_GC(type) && (type->tp_flags & Py_TPFLAGS_BASETYPE) &&
5414+
if (_PyType_IS_GC(type) && (type->tp_flags & Py_TPFLAGS_BASETYPE) &&
54085415
(type->tp_free == NULL || type->tp_free == PyObject_Del)) {
54095416
/* This base class needs to call tp_free, but doesn't have
54105417
* one, or its tp_free is for non-gc'ed objects.

0 commit comments

Comments
 (0)