mirror of https://github.com/python/cpython
bpo-46417: Cleanup typeobject.c code (GH-30795)
* Add comment to recurse_down_subclasses() explaining why it's safe to use a borrowed reference to tp_subclasses. * remove_all_subclasses() no longer accept NULL cases * type_set_bases() now relies on the fact that new_bases is not NULL. * type_dealloc_common() avoids PyErr_Fetch/PyErr_Restore if tp_bases is NULL. * remove_all_subclasses() makes sure that no exception is raised. * Don't test at runtime if tp_mro only contains types: rely on _PyType_CAST() assertion for that. * _PyStaticType_Dealloc() no longer clears tp_subclasses which is already NULL. * mro_hierarchy() avoids calling _PyType_GetSubclasses() if tp_subclasses is NULL. Coding style: * Use Py_NewRef(). * Add braces and move variable declarations to the first variable assignement. * Rename a few variables and parameters to use better names.
This commit is contained in:
parent
500c146387
commit
3a4c15bb98
|
@ -328,24 +328,26 @@ PyType_Modified(PyTypeObject *type)
|
|||
We don't assign new version tags eagerly, but only as
|
||||
needed.
|
||||
*/
|
||||
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;
|
||||
if (raw != NULL) {
|
||||
assert(PyDict_CheckExact(raw));
|
||||
i = 0;
|
||||
while (PyDict_Next(raw, &i, NULL, &ref)) {
|
||||
PyObject *subclasses = type->tp_subclasses;
|
||||
if (subclasses != NULL) {
|
||||
assert(PyDict_CheckExact(subclasses));
|
||||
|
||||
Py_ssize_t i = 0;
|
||||
PyObject *ref;
|
||||
while (PyDict_Next(subclasses, &i, NULL, &ref)) {
|
||||
assert(PyWeakref_CheckRef(ref));
|
||||
ref = PyWeakref_GET_OBJECT(ref);
|
||||
if (ref != Py_None) {
|
||||
PyType_Modified(_PyType_CAST(ref));
|
||||
PyObject *obj = PyWeakref_GET_OBJECT(ref);
|
||||
if (obj == Py_None) {
|
||||
continue;
|
||||
}
|
||||
PyType_Modified(_PyType_CAST(obj));
|
||||
}
|
||||
}
|
||||
|
||||
type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG;
|
||||
type->tp_version_tag = 0; /* 0 is not a valid version tag */
|
||||
}
|
||||
|
@ -409,13 +411,12 @@ assign_version_tag(struct type_cache *cache, PyTypeObject *type)
|
|||
must first be done on all super classes. Return 0 if this
|
||||
cannot be done, 1 if Py_TPFLAGS_VALID_VERSION_TAG.
|
||||
*/
|
||||
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_READY))
|
||||
}
|
||||
if (!_PyType_HasFeature(type, Py_TPFLAGS_READY)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (next_version_tag == 0) {
|
||||
/* We have run out of version numbers */
|
||||
|
@ -424,9 +425,9 @@ assign_version_tag(struct type_cache *cache, PyTypeObject *type)
|
|||
type->tp_version_tag = next_version_tag++;
|
||||
assert (type->tp_version_tag != 0);
|
||||
|
||||
bases = type->tp_bases;
|
||||
n = PyTuple_GET_SIZE(bases);
|
||||
for (i = 0; i < n; i++) {
|
||||
PyObject *bases = type->tp_bases;
|
||||
Py_ssize_t n = PyTuple_GET_SIZE(bases);
|
||||
for (Py_ssize_t i = 0; i < n; i++) {
|
||||
PyObject *b = PyTuple_GET_ITEM(bases, i);
|
||||
if (!assign_version_tag(cache, _PyType_CAST(b)))
|
||||
return 0;
|
||||
|
@ -679,7 +680,7 @@ static void remove_all_subclasses(PyTypeObject *type, PyObject *bases);
|
|||
static void update_all_slots(PyTypeObject *);
|
||||
|
||||
typedef int (*update_callback)(PyTypeObject *, void *);
|
||||
static int update_subclasses(PyTypeObject *type, PyObject *name,
|
||||
static int update_subclasses(PyTypeObject *type, PyObject *attr_name,
|
||||
update_callback callback, void *data);
|
||||
static int recurse_down_subclasses(PyTypeObject *type, PyObject *name,
|
||||
update_callback callback, void *data);
|
||||
|
@ -718,30 +719,33 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp)
|
|||
}
|
||||
Py_XDECREF(old_mro);
|
||||
|
||||
/* Obtain a copy of subclasses list to iterate over.
|
||||
// Avoid creating an empty list if there is no subclass
|
||||
if (type->tp_subclasses != NULL) {
|
||||
/* Obtain a copy of subclasses list to iterate over.
|
||||
|
||||
Otherwise type->tp_subclasses might be altered
|
||||
in the middle of the loop, for example, through a custom mro(),
|
||||
by invoking type_set_bases on some subclass of the type
|
||||
which in turn calls remove_subclass/add_subclass on this type.
|
||||
Otherwise type->tp_subclasses might be altered
|
||||
in the middle of the loop, for example, through a custom mro(),
|
||||
by invoking type_set_bases on some subclass of the type
|
||||
which in turn calls remove_subclass/add_subclass on this type.
|
||||
|
||||
Finally, this makes things simple avoiding the need to deal
|
||||
with dictionary iterators and weak references.
|
||||
*/
|
||||
PyObject *subclasses = _PyType_GetSubclasses(type);
|
||||
if (subclasses == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Py_ssize_t n = PyList_GET_SIZE(subclasses);
|
||||
for (Py_ssize_t i = 0; i < n; i++) {
|
||||
PyTypeObject *subclass = _PyType_CAST(PyList_GET_ITEM(subclasses, i));
|
||||
res = mro_hierarchy(subclass, temp);
|
||||
if (res < 0) {
|
||||
break;
|
||||
Finally, this makes things simple avoiding the need to deal
|
||||
with dictionary iterators and weak references.
|
||||
*/
|
||||
PyObject *subclasses = _PyType_GetSubclasses(type);
|
||||
if (subclasses == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Py_ssize_t n = PyList_GET_SIZE(subclasses);
|
||||
for (Py_ssize_t i = 0; i < n; i++) {
|
||||
PyTypeObject *subclass = _PyType_CAST(PyList_GET_ITEM(subclasses, i));
|
||||
res = mro_hierarchy(subclass, temp);
|
||||
if (res < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Py_DECREF(subclasses);
|
||||
}
|
||||
Py_DECREF(subclasses);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -749,14 +753,12 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp)
|
|||
static int
|
||||
type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
|
||||
{
|
||||
int res = 0;
|
||||
PyObject *temp;
|
||||
PyObject *old_bases;
|
||||
PyTypeObject *new_base, *old_base;
|
||||
Py_ssize_t i;
|
||||
|
||||
if (!check_set_special_type_attr(type, new_bases, "__bases__"))
|
||||
// Check arguments
|
||||
if (!check_set_special_type_attr(type, new_bases, "__bases__")) {
|
||||
return -1;
|
||||
}
|
||||
assert(new_bases != NULL);
|
||||
|
||||
if (!PyTuple_Check(new_bases)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"can only assign tuple to %s.__bases__, not %s",
|
||||
|
@ -769,7 +771,8 @@ type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
|
|||
type->tp_name);
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < PyTuple_GET_SIZE(new_bases); i++) {
|
||||
Py_ssize_t n = PyTuple_GET_SIZE(new_bases);
|
||||
for (Py_ssize_t i = 0; i < n; i++) {
|
||||
PyObject *ob = PyTuple_GET_ITEM(new_bases, i);
|
||||
if (!PyType_Check(ob)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
|
@ -789,39 +792,42 @@ 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))) {
|
||||
|
||||
(base->tp_mro != NULL && type_is_subtype_base_chain(base, type)))
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"a __bases__ item causes an inheritance cycle");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
new_base = best_base(new_bases);
|
||||
// Compute the new MRO and the new base class
|
||||
PyTypeObject *new_base = best_base(new_bases);
|
||||
if (new_base == NULL)
|
||||
return -1;
|
||||
|
||||
if (!compatible_for_assignment(type->tp_base, new_base, "__bases__"))
|
||||
if (!compatible_for_assignment(type->tp_base, new_base, "__bases__")) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Py_INCREF(new_bases);
|
||||
Py_INCREF(new_base);
|
||||
PyObject *old_bases = type->tp_bases;
|
||||
assert(old_bases != NULL);
|
||||
PyTypeObject *old_base = type->tp_base;
|
||||
|
||||
old_bases = type->tp_bases;
|
||||
old_base = type->tp_base;
|
||||
type->tp_bases = Py_NewRef(new_bases);
|
||||
type->tp_base = (PyTypeObject *)Py_NewRef(new_base);
|
||||
|
||||
type->tp_bases = new_bases;
|
||||
type->tp_base = new_base;
|
||||
|
||||
temp = PyList_New(0);
|
||||
if (temp == NULL)
|
||||
PyObject *temp = PyList_New(0);
|
||||
if (temp == NULL) {
|
||||
goto bail;
|
||||
if (mro_hierarchy(type, temp) < 0)
|
||||
}
|
||||
if (mro_hierarchy(type, temp) < 0) {
|
||||
goto undo;
|
||||
}
|
||||
Py_DECREF(temp);
|
||||
|
||||
/* Take no action in case if type->tp_bases has been replaced
|
||||
through reentrance. */
|
||||
int res;
|
||||
if (type->tp_bases == new_bases) {
|
||||
/* any base that was in __bases__ but now isn't, we
|
||||
need to remove |type| from its tp_subclasses.
|
||||
|
@ -834,6 +840,9 @@ type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
|
|||
res = add_all_subclasses(type, new_bases);
|
||||
update_all_slots(type);
|
||||
}
|
||||
else {
|
||||
res = 0;
|
||||
}
|
||||
|
||||
Py_DECREF(old_bases);
|
||||
Py_DECREF(old_base);
|
||||
|
@ -842,7 +851,8 @@ type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
|
|||
return res;
|
||||
|
||||
undo:
|
||||
for (i = PyList_GET_SIZE(temp) - 1; i >= 0; i--) {
|
||||
n = PyList_GET_SIZE(temp);
|
||||
for (Py_ssize_t i = n - 1; i >= 0; i--) {
|
||||
PyTypeObject *cls;
|
||||
PyObject *new_mro, *old_mro = NULL;
|
||||
|
||||
|
@ -1413,8 +1423,9 @@ subtype_dealloc(PyObject *self)
|
|||
and if self is tracked at that point, it will look like trash to GC and GC
|
||||
will try to delete self again.
|
||||
*/
|
||||
if (type->tp_weaklistoffset && !base->tp_weaklistoffset)
|
||||
if (type->tp_weaklistoffset && !base->tp_weaklistoffset) {
|
||||
PyObject_ClearWeakRefs(self);
|
||||
}
|
||||
|
||||
if (type->tp_del) {
|
||||
_PyObject_GC_TRACK(self);
|
||||
|
@ -1929,20 +1940,14 @@ pmerge(PyObject *acc, PyObject **to_merge, Py_ssize_t to_merge_size)
|
|||
static PyObject *
|
||||
mro_implementation(PyTypeObject *type)
|
||||
{
|
||||
PyObject *result;
|
||||
PyObject *bases;
|
||||
PyObject **to_merge;
|
||||
Py_ssize_t i, n;
|
||||
|
||||
if (!_PyType_IsReady(type)) {
|
||||
if (PyType_Ready(type) < 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bases = type->tp_bases;
|
||||
assert(PyTuple_Check(bases));
|
||||
n = PyTuple_GET_SIZE(bases);
|
||||
for (i = 0; i < n; i++) {
|
||||
PyObject *bases = type->tp_bases;
|
||||
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) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
|
@ -1959,13 +1964,14 @@ mro_implementation(PyTypeObject *type)
|
|||
*/
|
||||
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, 0));
|
||||
Py_ssize_t k = PyTuple_GET_SIZE(base->tp_mro);
|
||||
result = PyTuple_New(k + 1);
|
||||
PyObject *result = PyTuple_New(k + 1);
|
||||
if (result == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_INCREF(type);
|
||||
PyTuple_SET_ITEM(result, 0, (PyObject *) type);
|
||||
for (i = 0; i < k; i++) {
|
||||
for (Py_ssize_t i = 0; i < k; i++) {
|
||||
PyObject *cls = PyTuple_GET_ITEM(base->tp_mro, i);
|
||||
Py_INCREF(cls);
|
||||
PyTuple_SET_ITEM(result, i + 1, cls);
|
||||
|
@ -1986,20 +1992,19 @@ mro_implementation(PyTypeObject *type)
|
|||
linearization implied by a base class. The last element of
|
||||
to_merge is the declared tuple of bases.
|
||||
*/
|
||||
|
||||
to_merge = PyMem_New(PyObject *, n + 1);
|
||||
PyObject **to_merge = PyMem_New(PyObject *, n + 1);
|
||||
if (to_merge == NULL) {
|
||||
PyErr_NoMemory();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
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[n] = bases;
|
||||
|
||||
result = PyList_New(1);
|
||||
PyObject *result = PyList_New(1);
|
||||
if (result == NULL) {
|
||||
PyMem_Free(to_merge);
|
||||
return NULL;
|
||||
|
@ -2010,8 +2015,8 @@ mro_implementation(PyTypeObject *type)
|
|||
if (pmerge(result, to_merge, n + 1) < 0) {
|
||||
Py_CLEAR(result);
|
||||
}
|
||||
|
||||
PyMem_Free(to_merge);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -2651,20 +2656,20 @@ type_new_slots_bases(type_new_ctx *ctx)
|
|||
(ctx->may_add_weak && ctx->add_weak == 0)))
|
||||
{
|
||||
for (Py_ssize_t i = 0; i < nbases; i++) {
|
||||
PyObject *base = PyTuple_GET_ITEM(ctx->bases, i);
|
||||
if (base == (PyObject *)ctx->base) {
|
||||
PyObject *obj = PyTuple_GET_ITEM(ctx->bases, i);
|
||||
if (obj == (PyObject *)ctx->base) {
|
||||
/* Skip primary base */
|
||||
continue;
|
||||
}
|
||||
PyTypeObject *type = _PyType_CAST(base);
|
||||
PyTypeObject *base = _PyType_CAST(obj);
|
||||
|
||||
if (ctx->may_add_dict && ctx->add_dict == 0 &&
|
||||
type->tp_dictoffset != 0)
|
||||
base->tp_dictoffset != 0)
|
||||
{
|
||||
ctx->add_dict++;
|
||||
}
|
||||
if (ctx->may_add_weak && ctx->add_weak == 0 &&
|
||||
type->tp_weaklistoffset != 0)
|
||||
base->tp_weaklistoffset != 0)
|
||||
{
|
||||
ctx->add_weak++;
|
||||
}
|
||||
|
@ -3739,8 +3744,8 @@ _PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def)
|
|||
// to check i < PyTuple_GET_SIZE(mro) at the first loop iteration.
|
||||
assert(PyTuple_GET_SIZE(mro) >= 1);
|
||||
|
||||
Py_ssize_t i = 0;
|
||||
do {
|
||||
Py_ssize_t n = PyTuple_GET_SIZE(mro);
|
||||
for (Py_ssize_t i = 0; i < n; i++) {
|
||||
PyObject *super = PyTuple_GET_ITEM(mro, i);
|
||||
// _PyType_GetModuleByDef() must only be called on a heap type created
|
||||
// by PyType_FromModuleAndSpec() or on its subclasses.
|
||||
|
@ -3753,8 +3758,7 @@ _PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def)
|
|||
if (module && _PyModule_GetDef(module) == def) {
|
||||
return module;
|
||||
}
|
||||
i++;
|
||||
} while (i < PyTuple_GET_SIZE(mro));
|
||||
}
|
||||
|
||||
PyErr_Format(
|
||||
PyExc_TypeError,
|
||||
|
@ -3770,10 +3774,7 @@ _PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def)
|
|||
static PyObject *
|
||||
find_name_in_mro(PyTypeObject *type, PyObject *name, int *error)
|
||||
{
|
||||
Py_ssize_t i, n;
|
||||
PyObject *mro, *res, *base, *dict;
|
||||
Py_hash_t hash;
|
||||
|
||||
if (!PyUnicode_CheckExact(name) ||
|
||||
(hash = ((PyASCIIObject *) name)->hash) == -1)
|
||||
{
|
||||
|
@ -3785,8 +3786,7 @@ find_name_in_mro(PyTypeObject *type, PyObject *name, int *error)
|
|||
}
|
||||
|
||||
/* Look in tp_dict of types in MRO */
|
||||
mro = type->tp_mro;
|
||||
|
||||
PyObject *mro = type->tp_mro;
|
||||
if (mro == NULL) {
|
||||
if ((type->tp_flags & Py_TPFLAGS_READYING) == 0) {
|
||||
if (PyType_Ready(type) < 0) {
|
||||
|
@ -3801,20 +3801,19 @@ find_name_in_mro(PyTypeObject *type, PyObject *name, int *error)
|
|||
}
|
||||
}
|
||||
|
||||
res = NULL;
|
||||
PyObject *res = NULL;
|
||||
/* Keep a strong reference to mro because type->tp_mro can be replaced
|
||||
during dict lookup, e.g. when comparing to non-string keys. */
|
||||
Py_INCREF(mro);
|
||||
assert(PyTuple_Check(mro));
|
||||
n = PyTuple_GET_SIZE(mro);
|
||||
for (i = 0; i < n; i++) {
|
||||
base = PyTuple_GET_ITEM(mro, i);
|
||||
assert(PyType_Check(base));
|
||||
dict = _PyType_CAST(base)->tp_dict;
|
||||
Py_ssize_t n = PyTuple_GET_SIZE(mro);
|
||||
for (Py_ssize_t i = 0; i < n; i++) {
|
||||
PyObject *base = PyTuple_GET_ITEM(mro, i);
|
||||
PyObject *dict = _PyType_CAST(base)->tp_dict;
|
||||
assert(dict && PyDict_Check(dict));
|
||||
res = _PyDict_GetItem_KnownHash(dict, name, hash);
|
||||
if (res != NULL)
|
||||
if (res != NULL) {
|
||||
break;
|
||||
}
|
||||
if (PyErr_Occurred()) {
|
||||
*error = -1;
|
||||
goto done;
|
||||
|
@ -4066,10 +4065,12 @@ _PyDictKeys_DecRef(PyDictKeysObject *keys);
|
|||
static void
|
||||
type_dealloc_common(PyTypeObject *type)
|
||||
{
|
||||
PyObject *tp, *val, *tb;
|
||||
PyErr_Fetch(&tp, &val, &tb);
|
||||
remove_all_subclasses(type, type->tp_bases);
|
||||
PyErr_Restore(tp, val, tb);
|
||||
if (type->tp_bases != NULL) {
|
||||
PyObject *tp, *val, *tb;
|
||||
PyErr_Fetch(&tp, &val, &tb);
|
||||
remove_all_subclasses(type, type->tp_bases);
|
||||
PyErr_Restore(tp, val, tb);
|
||||
}
|
||||
|
||||
PyObject_ClearWeakRefs((PyObject *)type);
|
||||
}
|
||||
|
@ -4089,7 +4090,7 @@ _PyStaticType_Dealloc(PyTypeObject *type)
|
|||
Py_CLEAR(type->tp_bases);
|
||||
Py_CLEAR(type->tp_mro);
|
||||
Py_CLEAR(type->tp_cache);
|
||||
Py_CLEAR(type->tp_subclasses);
|
||||
// type->tp_subclasses is NULL
|
||||
|
||||
type->tp_flags &= ~Py_TPFLAGS_READY;
|
||||
}
|
||||
|
@ -4154,6 +4155,7 @@ _PyType_GetSubclasses(PyTypeObject *self)
|
|||
continue;
|
||||
}
|
||||
assert(PyType_Check(obj));
|
||||
|
||||
if (PyList_Append(list, obj) < 0) {
|
||||
Py_DECREF(list);
|
||||
return NULL;
|
||||
|
@ -6227,7 +6229,7 @@ type_ready_mro(PyTypeObject *type)
|
|||
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));
|
||||
if (PyType_Check(base) && (base->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
|
||||
if (base->tp_flags & Py_TPFLAGS_HEAPTYPE) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"type '%.100s' is not dynamically allocated but "
|
||||
"its base type '%.100s' is dynamically allocated",
|
||||
|
@ -6515,15 +6517,15 @@ add_subclass(PyTypeObject *base, PyTypeObject *type)
|
|||
// Only get tp_subclasses after creating the key and value.
|
||||
// PyWeakref_NewRef() can trigger a garbage collection which can execute
|
||||
// arbitrary Python code and so modify base->tp_subclasses.
|
||||
PyObject *dict = base->tp_subclasses;
|
||||
if (dict == NULL) {
|
||||
base->tp_subclasses = dict = PyDict_New();
|
||||
if (dict == NULL)
|
||||
PyObject *subclasses = base->tp_subclasses;
|
||||
if (subclasses == NULL) {
|
||||
base->tp_subclasses = subclasses = PyDict_New();
|
||||
if (subclasses == NULL)
|
||||
return -1;
|
||||
}
|
||||
assert(PyDict_CheckExact(dict));
|
||||
assert(PyDict_CheckExact(subclasses));
|
||||
|
||||
int result = PyDict_SetItem(dict, key, ref);
|
||||
int result = PyDict_SetItem(subclasses, key, ref);
|
||||
Py_DECREF(ref);
|
||||
Py_DECREF(key);
|
||||
return result;
|
||||
|
@ -6532,35 +6534,30 @@ add_subclass(PyTypeObject *base, PyTypeObject *type)
|
|||
static int
|
||||
add_all_subclasses(PyTypeObject *type, PyObject *bases)
|
||||
{
|
||||
Py_ssize_t n = PyTuple_GET_SIZE(bases);
|
||||
int res = 0;
|
||||
|
||||
if (bases) {
|
||||
Py_ssize_t i;
|
||||
for (i = 0; i < PyTuple_GET_SIZE(bases); i++) {
|
||||
PyObject *base = PyTuple_GET_ITEM(bases, i);
|
||||
if (PyType_Check(base) &&
|
||||
add_subclass((PyTypeObject*)base, type) < 0)
|
||||
{
|
||||
res = -1;
|
||||
}
|
||||
for (Py_ssize_t i = 0; i < n; i++) {
|
||||
PyObject *obj = PyTuple_GET_ITEM(bases, i);
|
||||
// bases tuple must only contain types
|
||||
PyTypeObject *base = _PyType_CAST(obj);
|
||||
if (add_subclass(base, type) < 0) {
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void
|
||||
remove_subclass(PyTypeObject *base, PyTypeObject *type)
|
||||
{
|
||||
PyObject *dict, *key;
|
||||
|
||||
dict = base->tp_subclasses;
|
||||
if (dict == NULL) {
|
||||
PyObject *subclasses = base->tp_subclasses; // borrowed ref
|
||||
if (subclasses == NULL) {
|
||||
return;
|
||||
}
|
||||
assert(PyDict_CheckExact(dict));
|
||||
key = PyLong_FromVoidPtr((void *) type);
|
||||
if (key == NULL || PyDict_DelItem(dict, key)) {
|
||||
assert(PyDict_CheckExact(subclasses));
|
||||
|
||||
PyObject *key = PyLong_FromVoidPtr((void *) type);
|
||||
if (key == NULL || PyDict_DelItem(subclasses, key)) {
|
||||
/* This can happen if the type initialization errored out before
|
||||
the base subclasses were updated (e.g. a non-str __qualname__
|
||||
was passed in the type dict). */
|
||||
|
@ -6568,7 +6565,7 @@ remove_subclass(PyTypeObject *base, PyTypeObject *type)
|
|||
}
|
||||
Py_XDECREF(key);
|
||||
|
||||
if (PyDict_Size(dict) == 0) {
|
||||
if (PyDict_Size(subclasses) == 0) {
|
||||
// Delete the dictionary to save memory. _PyStaticType_Dealloc()
|
||||
// callers also test if tp_subclasses is NULL to check if a static type
|
||||
// has no subclass.
|
||||
|
@ -6579,15 +6576,17 @@ remove_subclass(PyTypeObject *base, PyTypeObject *type)
|
|||
static void
|
||||
remove_all_subclasses(PyTypeObject *type, PyObject *bases)
|
||||
{
|
||||
if (bases) {
|
||||
Py_ssize_t i;
|
||||
for (i = 0; i < PyTuple_GET_SIZE(bases); i++) {
|
||||
PyObject *base = PyTuple_GET_ITEM(bases, i);
|
||||
if (PyType_Check(base)) {
|
||||
remove_subclass((PyTypeObject*) base, type);
|
||||
}
|
||||
assert(bases != NULL);
|
||||
// remove_subclass() can clear the current exception
|
||||
assert(!PyErr_Occurred());
|
||||
|
||||
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(bases); i++) {
|
||||
PyObject *base = PyTuple_GET_ITEM(bases, i);
|
||||
if (PyType_Check(base)) {
|
||||
remove_subclass((PyTypeObject*) base, type);
|
||||
}
|
||||
}
|
||||
assert(!PyErr_Occurred());
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -8466,9 +8465,9 @@ static int
|
|||
update_slots_callback(PyTypeObject *type, void *data)
|
||||
{
|
||||
slotdef **pp = (slotdef **)data;
|
||||
|
||||
for (; *pp; pp++)
|
||||
for (; *pp; pp++) {
|
||||
update_one_slot(type, *pp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -8654,29 +8653,33 @@ type_new_init_subclass(PyTypeObject *type, PyObject *kwds)
|
|||
|
||||
/* recurse_down_subclasses() and update_subclasses() are mutually
|
||||
recursive functions to call a callback for all subclasses,
|
||||
but refraining from recursing into subclasses that define 'name'. */
|
||||
but refraining from recursing into subclasses that define 'attr_name'. */
|
||||
|
||||
static int
|
||||
update_subclasses(PyTypeObject *type, PyObject *name,
|
||||
update_subclasses(PyTypeObject *type, PyObject *attr_name,
|
||||
update_callback callback, void *data)
|
||||
{
|
||||
if (callback(type, data) < 0)
|
||||
if (callback(type, data) < 0) {
|
||||
return -1;
|
||||
return recurse_down_subclasses(type, name, callback, data);
|
||||
}
|
||||
return recurse_down_subclasses(type, attr_name, callback, data);
|
||||
}
|
||||
|
||||
static int
|
||||
recurse_down_subclasses(PyTypeObject *type, PyObject *name,
|
||||
recurse_down_subclasses(PyTypeObject *type, PyObject *attr_name,
|
||||
update_callback callback, void *data)
|
||||
{
|
||||
PyObject *ref, *subclasses, *dict;
|
||||
Py_ssize_t i;
|
||||
|
||||
subclasses = type->tp_subclasses;
|
||||
if (subclasses == NULL)
|
||||
// It is safe to use a borrowed reference because update_subclasses() is
|
||||
// only used with update_slots_callback() which doesn't modify
|
||||
// tp_subclasses.
|
||||
PyObject *subclasses = type->tp_subclasses; // borrowed ref
|
||||
if (subclasses == NULL) {
|
||||
return 0;
|
||||
}
|
||||
assert(PyDict_CheckExact(subclasses));
|
||||
i = 0;
|
||||
|
||||
Py_ssize_t i = 0;
|
||||
PyObject *ref;
|
||||
while (PyDict_Next(subclasses, &i, NULL, &ref)) {
|
||||
assert(PyWeakref_CheckRef(ref));
|
||||
PyObject *obj = PyWeakref_GET_OBJECT(ref);
|
||||
|
@ -8687,18 +8690,20 @@ recurse_down_subclasses(PyTypeObject *type, PyObject *name,
|
|||
PyTypeObject *subclass = _PyType_CAST(obj);
|
||||
|
||||
/* Avoid recursing down into unaffected classes */
|
||||
dict = subclass->tp_dict;
|
||||
PyObject *dict = subclass->tp_dict;
|
||||
if (dict != NULL && PyDict_Check(dict)) {
|
||||
int r = PyDict_Contains(dict, name);
|
||||
if (r > 0) {
|
||||
continue;
|
||||
}
|
||||
int r = PyDict_Contains(dict, attr_name);
|
||||
if (r < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (r > 0) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (update_subclasses(subclass, name, callback, data) < 0)
|
||||
|
||||
if (update_subclasses(subclass, attr_name, callback, data) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue