Skip to content
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

gh-94673: Hide Objects in PyTypeObject Behind Accessors #104074

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Add accessors for tp_mro.
  • Loading branch information
ericsnowcurrently committed May 2, 2023
commit bd5c65f1cd15b30a27f443c043a9efb6675fd2f3
1 change: 1 addition & 0 deletions Include/internal/pycore_typeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ extern static_builtin_state * _PyStaticType_GetState(PyInterpreterState *, PyTyp
extern void _PyStaticType_ClearWeakRefs(PyInterpreterState *, PyTypeObject *type);
extern void _PyStaticType_Dealloc(PyInterpreterState *, PyTypeObject *);

extern PyObject * _PyType_GetMRO(PyTypeObject *type);
extern PyObject* _PyType_GetSubclasses(PyTypeObject *);

PyObject *
Expand Down
2 changes: 1 addition & 1 deletion Modules/_abc.c
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,7 @@ _abc__abc_subclasscheck_impl(PyObject *module, PyObject *self,
Py_DECREF(ok);

/* 4. Check if it's a direct subclass. */
PyObject *mro = ((PyTypeObject *)subclass)->tp_mro;
PyObject *mro = _PyType_GetMRO((PyTypeObject *)subclass);
assert(PyTuple_Check(mro));
for (pos = 0; pos < PyTuple_GET_SIZE(mro); pos++) {
PyObject *mro_item = PyTuple_GET_ITEM(mro, pos);
Expand Down
81 changes: 54 additions & 27 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,31 @@ static_builtin_state_clear(PyInterpreterState *interp, PyTypeObject *self)

/* accessors for objects stored on PyTypeObject */

static inline PyObject *
lookup_tp_mro(PyTypeObject *self)
{
return self->tp_mro;
}

PyObject *
_PyType_GetMRO(PyTypeObject *self)
{
return lookup_tp_mro(self);
}

static inline void
set_tp_mro(PyTypeObject *self, PyObject *mro)
{
self->tp_mro = mro;
}

static inline void
clear_tp_mro(PyTypeObject *self)
{
Py_CLEAR(self->tp_mro);
}


static PyObject *
init_tp_subclasses(PyTypeObject *self)
{
Expand Down Expand Up @@ -201,7 +226,7 @@ clear_tp_subclasses(PyTypeObject *self)
Py_CLEAR(self->tp_subclasses);
}

static PyObject *
static inline PyObject *
lookup_tp_subclasses(PyTypeObject *self)
{
if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
Expand Down Expand Up @@ -972,7 +997,7 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp)
/* error / reentrance */
return res;
}
PyObject *new_mro = type->tp_mro;
PyObject *new_mro = lookup_tp_mro(type);

PyObject *tuple;
if (old_mro != NULL) {
Expand All @@ -991,7 +1016,7 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp)
Py_XDECREF(tuple);

if (res < 0) {
type->tp_mro = old_mro;
set_tp_mro(type, old_mro);
Py_DECREF(new_mro);
return -1;
}
Expand Down Expand Up @@ -1070,7 +1095,8 @@ type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
below), which in turn may cause an inheritance cycle
through tp_base chain. And this is definitely
not what you want to ever happen. */
(base->tp_mro != NULL && type_is_subtype_base_chain(base, type)))
(lookup_tp_mro(base) != NULL
&& type_is_subtype_base_chain(base, type)))
{
PyErr_SetString(PyExc_TypeError,
"a __bases__ item causes an inheritance cycle");
Expand Down Expand Up @@ -1137,8 +1163,8 @@ type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
PyArg_UnpackTuple(PyList_GET_ITEM(temp, i),
"", 2, 3, &cls, &new_mro, &old_mro);
/* Do not rollback if cls has a newer version of MRO. */
if (cls->tp_mro == new_mro) {
cls->tp_mro = Py_XNewRef(old_mro);
if (lookup_tp_mro(cls) == new_mro) {
set_tp_mro(cls, Py_XNewRef(old_mro));
Py_DECREF(new_mro);
}
}
Expand Down Expand Up @@ -1858,7 +1884,7 @@ PyType_IsSubtype(PyTypeObject *a, PyTypeObject *b)
{
PyObject *mro;

mro = a->tp_mro;
mro = lookup_tp_mro(a);
if (mro != NULL) {
/* Deal with multiple inheritance without recursion
by walking the MRO tuple */
Expand Down Expand Up @@ -2246,21 +2272,22 @@ mro_implementation(PyTypeObject *type)
Py_ssize_t n = PyTuple_GET_SIZE(bases);
for (Py_ssize_t i = 0; i < n; i++) {
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, i));
if (base->tp_mro == NULL) {
if (lookup_tp_mro(base) == NULL) {
PyErr_Format(PyExc_TypeError,
"Cannot extend an incomplete type '%.100s'",
base->tp_name);
return NULL;
}
assert(PyTuple_Check(base->tp_mro));
assert(PyTuple_Check(lookup_tp_mro(base)));
}

if (n == 1) {
/* Fast path: if there is a single base, constructing the MRO
* is trivial.
*/
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, 0));
Py_ssize_t k = PyTuple_GET_SIZE(base->tp_mro);
PyObject *base_mro = lookup_tp_mro(base);
Py_ssize_t k = PyTuple_GET_SIZE(base_mro);
PyObject *result = PyTuple_New(k + 1);
if (result == NULL) {
return NULL;
Expand All @@ -2269,7 +2296,7 @@ mro_implementation(PyTypeObject *type)
;
PyTuple_SET_ITEM(result, 0, Py_NewRef(type));
for (Py_ssize_t i = 0; i < k; i++) {
PyObject *cls = PyTuple_GET_ITEM(base->tp_mro, i);
PyObject *cls = PyTuple_GET_ITEM(base_mro, i);
PyTuple_SET_ITEM(result, i + 1, Py_NewRef(cls));
}
return result;
Expand All @@ -2296,7 +2323,7 @@ mro_implementation(PyTypeObject *type)

for (Py_ssize_t i = 0; i < n; i++) {
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, i));
to_merge[i] = base->tp_mro;
to_merge[i] = lookup_tp_mro(base);
}
to_merge[n] = bases;

Expand Down Expand Up @@ -2451,9 +2478,9 @@ mro_internal(PyTypeObject *type, PyObject **p_old_mro)
/* Keep a reference to be able to do a reentrancy check below.
Don't let old_mro be GC'ed and its address be reused for
another object, like (suddenly!) a new tp_mro. */
old_mro = Py_XNewRef(type->tp_mro);
old_mro = Py_XNewRef(lookup_tp_mro(type));
new_mro = mro_invoke(type); /* might cause reentrance */
reent = (type->tp_mro != old_mro);
reent = (lookup_tp_mro(type) != old_mro);
Py_XDECREF(old_mro);
if (new_mro == NULL) {
return -1;
Expand All @@ -2464,9 +2491,9 @@ mro_internal(PyTypeObject *type, PyObject **p_old_mro)
return 0;
}

type->tp_mro = new_mro;
set_tp_mro(type, new_mro);

type_mro_modified(type, type->tp_mro);
type_mro_modified(type, new_mro);
/* corner case: the super class might have been hidden
from the custom MRO */
type_mro_modified(type, type->tp_bases);
Expand Down Expand Up @@ -4201,7 +4228,7 @@ PyType_GetModuleByDef(PyTypeObject *type, PyModuleDef *def)
{
assert(PyType_Check(type));

PyObject *mro = type->tp_mro;
PyObject *mro = lookup_tp_mro(type);
// The type must be ready
assert(mro != NULL);
assert(PyTuple_Check(mro));
Expand Down Expand Up @@ -4250,14 +4277,14 @@ find_name_in_mro(PyTypeObject *type, PyObject *name, int *error)
}

/* Look in tp_dict of types in MRO */
PyObject *mro = type->tp_mro;
PyObject *mro = lookup_tp_mro(type);
if (mro == NULL) {
if ((type->tp_flags & Py_TPFLAGS_READYING) == 0) {
if (PyType_Ready(type) < 0) {
*error = -1;
return NULL;
}
mro = type->tp_mro;
mro = lookup_tp_mro(type);
}
if (mro == NULL) {
*error = 1;
Expand Down Expand Up @@ -4603,7 +4630,7 @@ clear_static_type_objects(PyInterpreterState *interp, PyTypeObject *type)
if (_Py_IsMainInterpreter(interp)) {
Py_CLEAR(type->tp_dict);
Py_CLEAR(type->tp_bases);
Py_CLEAR(type->tp_mro);
clear_tp_mro(type);
Py_CLEAR(type->tp_cache);
}
clear_static_tp_subclasses(type);
Expand Down Expand Up @@ -6752,14 +6779,14 @@ type_ready_mro(PyTypeObject *type)
if (mro_internal(type, NULL) < 0) {
return -1;
}
assert(type->tp_mro != NULL);
assert(PyTuple_Check(type->tp_mro));
PyObject *mro = lookup_tp_mro(type);
assert(mro != NULL);
assert(PyTuple_Check(mro));

/* All bases of statically allocated type should be statically allocated,
and static builtin types must have static builtin bases. */
if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
assert(type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE);
PyObject *mro = type->tp_mro;
Py_ssize_t n = PyTuple_GET_SIZE(mro);
for (Py_ssize_t i = 0; i < n; i++) {
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(mro, i));
Expand Down Expand Up @@ -6820,8 +6847,8 @@ type_ready_inherit(PyTypeObject *type)
}

// Inherit slots
PyObject *mro = type->tp_mro;
Py_ssize_t n = PyTuple_GET_SIZE(type->tp_mro);
PyObject *mro = lookup_tp_mro(type);
Py_ssize_t n = PyTuple_GET_SIZE(mro);
for (Py_ssize_t i = 1; i < n; i++) {
PyObject *b = PyTuple_GET_ITEM(mro, i);
if (PyType_Check(b)) {
Expand Down Expand Up @@ -7518,7 +7545,7 @@ static int
hackcheck(PyObject *self, setattrofunc func, const char *what)
{
PyTypeObject *type = Py_TYPE(self);
PyObject *mro = type->tp_mro;
PyObject *mro = lookup_tp_mro(type);
if (!mro) {
/* Probably ok not to check the call in this case. */
return 1;
Expand Down Expand Up @@ -9433,7 +9460,7 @@ _super_lookup_descr(PyTypeObject *su_type, PyTypeObject *su_obj_type, PyObject *
PyObject *mro, *res;
Py_ssize_t i, n;

mro = su_obj_type->tp_mro;
mro = lookup_tp_mro(su_obj_type);
if (mro == NULL)
return NULL;

Expand Down