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
|
We don't assign new version tags eagerly, but only as
|
||||||
needed.
|
needed.
|
||||||
*/
|
*/
|
||||||
PyObject *raw, *ref;
|
if (!_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) {
|
||||||
Py_ssize_t i;
|
|
||||||
|
|
||||||
if (!_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG))
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
raw = type->tp_subclasses;
|
PyObject *subclasses = type->tp_subclasses;
|
||||||
if (raw != NULL) {
|
if (subclasses != NULL) {
|
||||||
assert(PyDict_CheckExact(raw));
|
assert(PyDict_CheckExact(subclasses));
|
||||||
i = 0;
|
|
||||||
while (PyDict_Next(raw, &i, NULL, &ref)) {
|
Py_ssize_t i = 0;
|
||||||
|
PyObject *ref;
|
||||||
|
while (PyDict_Next(subclasses, &i, NULL, &ref)) {
|
||||||
assert(PyWeakref_CheckRef(ref));
|
assert(PyWeakref_CheckRef(ref));
|
||||||
ref = PyWeakref_GET_OBJECT(ref);
|
PyObject *obj = PyWeakref_GET_OBJECT(ref);
|
||||||
if (ref != Py_None) {
|
if (obj == Py_None) {
|
||||||
PyType_Modified(_PyType_CAST(ref));
|
continue;
|
||||||
}
|
}
|
||||||
|
PyType_Modified(_PyType_CAST(obj));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG;
|
type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG;
|
||||||
type->tp_version_tag = 0; /* 0 is not a 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
|
must first be done on all super classes. Return 0 if this
|
||||||
cannot be done, 1 if Py_TPFLAGS_VALID_VERSION_TAG.
|
cannot be done, 1 if Py_TPFLAGS_VALID_VERSION_TAG.
|
||||||
*/
|
*/
|
||||||
Py_ssize_t i, n;
|
if (_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) {
|
||||||
PyObject *bases;
|
|
||||||
|
|
||||||
if (_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG))
|
|
||||||
return 1;
|
return 1;
|
||||||
if (!_PyType_HasFeature(type, Py_TPFLAGS_READY))
|
}
|
||||||
|
if (!_PyType_HasFeature(type, Py_TPFLAGS_READY)) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (next_version_tag == 0) {
|
if (next_version_tag == 0) {
|
||||||
/* We have run out of version numbers */
|
/* 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++;
|
type->tp_version_tag = next_version_tag++;
|
||||||
assert (type->tp_version_tag != 0);
|
assert (type->tp_version_tag != 0);
|
||||||
|
|
||||||
bases = type->tp_bases;
|
PyObject *bases = type->tp_bases;
|
||||||
n = PyTuple_GET_SIZE(bases);
|
Py_ssize_t n = PyTuple_GET_SIZE(bases);
|
||||||
for (i = 0; i < n; i++) {
|
for (Py_ssize_t i = 0; i < n; i++) {
|
||||||
PyObject *b = PyTuple_GET_ITEM(bases, i);
|
PyObject *b = PyTuple_GET_ITEM(bases, i);
|
||||||
if (!assign_version_tag(cache, _PyType_CAST(b)))
|
if (!assign_version_tag(cache, _PyType_CAST(b)))
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -679,7 +680,7 @@ static void remove_all_subclasses(PyTypeObject *type, PyObject *bases);
|
||||||
static void update_all_slots(PyTypeObject *);
|
static void update_all_slots(PyTypeObject *);
|
||||||
|
|
||||||
typedef int (*update_callback)(PyTypeObject *, void *);
|
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);
|
update_callback callback, void *data);
|
||||||
static int recurse_down_subclasses(PyTypeObject *type, PyObject *name,
|
static int recurse_down_subclasses(PyTypeObject *type, PyObject *name,
|
||||||
update_callback callback, void *data);
|
update_callback callback, void *data);
|
||||||
|
@ -718,30 +719,33 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp)
|
||||||
}
|
}
|
||||||
Py_XDECREF(old_mro);
|
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
|
Otherwise type->tp_subclasses might be altered
|
||||||
in the middle of the loop, for example, through a custom mro(),
|
in the middle of the loop, for example, through a custom mro(),
|
||||||
by invoking type_set_bases on some subclass of the type
|
by invoking type_set_bases on some subclass of the type
|
||||||
which in turn calls remove_subclass/add_subclass on this type.
|
which in turn calls remove_subclass/add_subclass on this type.
|
||||||
|
|
||||||
Finally, this makes things simple avoiding the need to deal
|
Finally, this makes things simple avoiding the need to deal
|
||||||
with dictionary iterators and weak references.
|
with dictionary iterators and weak references.
|
||||||
*/
|
*/
|
||||||
PyObject *subclasses = _PyType_GetSubclasses(type);
|
PyObject *subclasses = _PyType_GetSubclasses(type);
|
||||||
if (subclasses == NULL) {
|
if (subclasses == NULL) {
|
||||||
return -1;
|
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_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;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -749,14 +753,12 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp)
|
||||||
static int
|
static int
|
||||||
type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
|
type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
|
||||||
{
|
{
|
||||||
int res = 0;
|
// Check arguments
|
||||||
PyObject *temp;
|
if (!check_set_special_type_attr(type, new_bases, "__bases__")) {
|
||||||
PyObject *old_bases;
|
|
||||||
PyTypeObject *new_base, *old_base;
|
|
||||||
Py_ssize_t i;
|
|
||||||
|
|
||||||
if (!check_set_special_type_attr(type, new_bases, "__bases__"))
|
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
assert(new_bases != NULL);
|
||||||
|
|
||||||
if (!PyTuple_Check(new_bases)) {
|
if (!PyTuple_Check(new_bases)) {
|
||||||
PyErr_Format(PyExc_TypeError,
|
PyErr_Format(PyExc_TypeError,
|
||||||
"can only assign tuple to %s.__bases__, not %s",
|
"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);
|
type->tp_name);
|
||||||
return -1;
|
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);
|
PyObject *ob = PyTuple_GET_ITEM(new_bases, i);
|
||||||
if (!PyType_Check(ob)) {
|
if (!PyType_Check(ob)) {
|
||||||
PyErr_Format(PyExc_TypeError,
|
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
|
below), which in turn may cause an inheritance cycle
|
||||||
through tp_base chain. And this is definitely
|
through tp_base chain. And this is definitely
|
||||||
not what you want to ever happen. */
|
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,
|
PyErr_SetString(PyExc_TypeError,
|
||||||
"a __bases__ item causes an inheritance cycle");
|
"a __bases__ item causes an inheritance cycle");
|
||||||
return -1;
|
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)
|
if (new_base == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (!compatible_for_assignment(type->tp_base, new_base, "__bases__"))
|
if (!compatible_for_assignment(type->tp_base, new_base, "__bases__")) {
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
Py_INCREF(new_bases);
|
PyObject *old_bases = type->tp_bases;
|
||||||
Py_INCREF(new_base);
|
assert(old_bases != NULL);
|
||||||
|
PyTypeObject *old_base = type->tp_base;
|
||||||
|
|
||||||
old_bases = type->tp_bases;
|
type->tp_bases = Py_NewRef(new_bases);
|
||||||
old_base = type->tp_base;
|
type->tp_base = (PyTypeObject *)Py_NewRef(new_base);
|
||||||
|
|
||||||
type->tp_bases = new_bases;
|
PyObject *temp = PyList_New(0);
|
||||||
type->tp_base = new_base;
|
if (temp == NULL) {
|
||||||
|
|
||||||
temp = PyList_New(0);
|
|
||||||
if (temp == NULL)
|
|
||||||
goto bail;
|
goto bail;
|
||||||
if (mro_hierarchy(type, temp) < 0)
|
}
|
||||||
|
if (mro_hierarchy(type, temp) < 0) {
|
||||||
goto undo;
|
goto undo;
|
||||||
|
}
|
||||||
Py_DECREF(temp);
|
Py_DECREF(temp);
|
||||||
|
|
||||||
/* Take no action in case if type->tp_bases has been replaced
|
/* Take no action in case if type->tp_bases has been replaced
|
||||||
through reentrance. */
|
through reentrance. */
|
||||||
|
int res;
|
||||||
if (type->tp_bases == new_bases) {
|
if (type->tp_bases == new_bases) {
|
||||||
/* any base that was in __bases__ but now isn't, we
|
/* any base that was in __bases__ but now isn't, we
|
||||||
need to remove |type| from its tp_subclasses.
|
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);
|
res = add_all_subclasses(type, new_bases);
|
||||||
update_all_slots(type);
|
update_all_slots(type);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
res = 0;
|
||||||
|
}
|
||||||
|
|
||||||
Py_DECREF(old_bases);
|
Py_DECREF(old_bases);
|
||||||
Py_DECREF(old_base);
|
Py_DECREF(old_base);
|
||||||
|
@ -842,7 +851,8 @@ type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
undo:
|
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;
|
PyTypeObject *cls;
|
||||||
PyObject *new_mro, *old_mro = NULL;
|
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
|
and if self is tracked at that point, it will look like trash to GC and GC
|
||||||
will try to delete self again.
|
will try to delete self again.
|
||||||
*/
|
*/
|
||||||
if (type->tp_weaklistoffset && !base->tp_weaklistoffset)
|
if (type->tp_weaklistoffset && !base->tp_weaklistoffset) {
|
||||||
PyObject_ClearWeakRefs(self);
|
PyObject_ClearWeakRefs(self);
|
||||||
|
}
|
||||||
|
|
||||||
if (type->tp_del) {
|
if (type->tp_del) {
|
||||||
_PyObject_GC_TRACK(self);
|
_PyObject_GC_TRACK(self);
|
||||||
|
@ -1929,20 +1940,14 @@ pmerge(PyObject *acc, PyObject **to_merge, Py_ssize_t to_merge_size)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
mro_implementation(PyTypeObject *type)
|
mro_implementation(PyTypeObject *type)
|
||||||
{
|
{
|
||||||
PyObject *result;
|
|
||||||
PyObject *bases;
|
|
||||||
PyObject **to_merge;
|
|
||||||
Py_ssize_t i, n;
|
|
||||||
|
|
||||||
if (!_PyType_IsReady(type)) {
|
if (!_PyType_IsReady(type)) {
|
||||||
if (PyType_Ready(type) < 0)
|
if (PyType_Ready(type) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bases = type->tp_bases;
|
PyObject *bases = type->tp_bases;
|
||||||
assert(PyTuple_Check(bases));
|
Py_ssize_t n = PyTuple_GET_SIZE(bases);
|
||||||
n = PyTuple_GET_SIZE(bases);
|
for (Py_ssize_t i = 0; i < n; i++) {
|
||||||
for (i = 0; i < n; i++) {
|
|
||||||
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, i));
|
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, i));
|
||||||
if (base->tp_mro == NULL) {
|
if (base->tp_mro == NULL) {
|
||||||
PyErr_Format(PyExc_TypeError,
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
@ -1959,13 +1964,14 @@ mro_implementation(PyTypeObject *type)
|
||||||
*/
|
*/
|
||||||
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, 0));
|
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, 0));
|
||||||
Py_ssize_t k = PyTuple_GET_SIZE(base->tp_mro);
|
Py_ssize_t k = PyTuple_GET_SIZE(base->tp_mro);
|
||||||
result = PyTuple_New(k + 1);
|
PyObject *result = PyTuple_New(k + 1);
|
||||||
if (result == NULL) {
|
if (result == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_INCREF(type);
|
Py_INCREF(type);
|
||||||
PyTuple_SET_ITEM(result, 0, (PyObject *) 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);
|
PyObject *cls = PyTuple_GET_ITEM(base->tp_mro, i);
|
||||||
Py_INCREF(cls);
|
Py_INCREF(cls);
|
||||||
PyTuple_SET_ITEM(result, i + 1, 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
|
linearization implied by a base class. The last element of
|
||||||
to_merge is the declared tuple of bases.
|
to_merge is the declared tuple of bases.
|
||||||
*/
|
*/
|
||||||
|
PyObject **to_merge = PyMem_New(PyObject *, n + 1);
|
||||||
to_merge = PyMem_New(PyObject *, n + 1);
|
|
||||||
if (to_merge == NULL) {
|
if (to_merge == NULL) {
|
||||||
PyErr_NoMemory();
|
PyErr_NoMemory();
|
||||||
return NULL;
|
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));
|
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, i));
|
||||||
to_merge[i] = base->tp_mro;
|
to_merge[i] = base->tp_mro;
|
||||||
}
|
}
|
||||||
to_merge[n] = bases;
|
to_merge[n] = bases;
|
||||||
|
|
||||||
result = PyList_New(1);
|
PyObject *result = PyList_New(1);
|
||||||
if (result == NULL) {
|
if (result == NULL) {
|
||||||
PyMem_Free(to_merge);
|
PyMem_Free(to_merge);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -2010,8 +2015,8 @@ mro_implementation(PyTypeObject *type)
|
||||||
if (pmerge(result, to_merge, n + 1) < 0) {
|
if (pmerge(result, to_merge, n + 1) < 0) {
|
||||||
Py_CLEAR(result);
|
Py_CLEAR(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyMem_Free(to_merge);
|
PyMem_Free(to_merge);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2651,20 +2656,20 @@ type_new_slots_bases(type_new_ctx *ctx)
|
||||||
(ctx->may_add_weak && ctx->add_weak == 0)))
|
(ctx->may_add_weak && ctx->add_weak == 0)))
|
||||||
{
|
{
|
||||||
for (Py_ssize_t i = 0; i < nbases; i++) {
|
for (Py_ssize_t i = 0; i < nbases; i++) {
|
||||||
PyObject *base = PyTuple_GET_ITEM(ctx->bases, i);
|
PyObject *obj = PyTuple_GET_ITEM(ctx->bases, i);
|
||||||
if (base == (PyObject *)ctx->base) {
|
if (obj == (PyObject *)ctx->base) {
|
||||||
/* Skip primary base */
|
/* Skip primary base */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
PyTypeObject *type = _PyType_CAST(base);
|
PyTypeObject *base = _PyType_CAST(obj);
|
||||||
|
|
||||||
if (ctx->may_add_dict && ctx->add_dict == 0 &&
|
if (ctx->may_add_dict && ctx->add_dict == 0 &&
|
||||||
type->tp_dictoffset != 0)
|
base->tp_dictoffset != 0)
|
||||||
{
|
{
|
||||||
ctx->add_dict++;
|
ctx->add_dict++;
|
||||||
}
|
}
|
||||||
if (ctx->may_add_weak && ctx->add_weak == 0 &&
|
if (ctx->may_add_weak && ctx->add_weak == 0 &&
|
||||||
type->tp_weaklistoffset != 0)
|
base->tp_weaklistoffset != 0)
|
||||||
{
|
{
|
||||||
ctx->add_weak++;
|
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.
|
// to check i < PyTuple_GET_SIZE(mro) at the first loop iteration.
|
||||||
assert(PyTuple_GET_SIZE(mro) >= 1);
|
assert(PyTuple_GET_SIZE(mro) >= 1);
|
||||||
|
|
||||||
Py_ssize_t i = 0;
|
Py_ssize_t n = PyTuple_GET_SIZE(mro);
|
||||||
do {
|
for (Py_ssize_t i = 0; i < n; i++) {
|
||||||
PyObject *super = PyTuple_GET_ITEM(mro, i);
|
PyObject *super = PyTuple_GET_ITEM(mro, i);
|
||||||
// _PyType_GetModuleByDef() must only be called on a heap type created
|
// _PyType_GetModuleByDef() must only be called on a heap type created
|
||||||
// by PyType_FromModuleAndSpec() or on its subclasses.
|
// by PyType_FromModuleAndSpec() or on its subclasses.
|
||||||
|
@ -3753,8 +3758,7 @@ _PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def)
|
||||||
if (module && _PyModule_GetDef(module) == def) {
|
if (module && _PyModule_GetDef(module) == def) {
|
||||||
return module;
|
return module;
|
||||||
}
|
}
|
||||||
i++;
|
}
|
||||||
} while (i < PyTuple_GET_SIZE(mro));
|
|
||||||
|
|
||||||
PyErr_Format(
|
PyErr_Format(
|
||||||
PyExc_TypeError,
|
PyExc_TypeError,
|
||||||
|
@ -3770,10 +3774,7 @@ _PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
find_name_in_mro(PyTypeObject *type, PyObject *name, int *error)
|
find_name_in_mro(PyTypeObject *type, PyObject *name, int *error)
|
||||||
{
|
{
|
||||||
Py_ssize_t i, n;
|
|
||||||
PyObject *mro, *res, *base, *dict;
|
|
||||||
Py_hash_t hash;
|
Py_hash_t hash;
|
||||||
|
|
||||||
if (!PyUnicode_CheckExact(name) ||
|
if (!PyUnicode_CheckExact(name) ||
|
||||||
(hash = ((PyASCIIObject *) name)->hash) == -1)
|
(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 */
|
/* Look in tp_dict of types in MRO */
|
||||||
mro = type->tp_mro;
|
PyObject *mro = type->tp_mro;
|
||||||
|
|
||||||
if (mro == NULL) {
|
if (mro == NULL) {
|
||||||
if ((type->tp_flags & Py_TPFLAGS_READYING) == 0) {
|
if ((type->tp_flags & Py_TPFLAGS_READYING) == 0) {
|
||||||
if (PyType_Ready(type) < 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
|
/* Keep a strong reference to mro because type->tp_mro can be replaced
|
||||||
during dict lookup, e.g. when comparing to non-string keys. */
|
during dict lookup, e.g. when comparing to non-string keys. */
|
||||||
Py_INCREF(mro);
|
Py_INCREF(mro);
|
||||||
assert(PyTuple_Check(mro));
|
Py_ssize_t n = PyTuple_GET_SIZE(mro);
|
||||||
n = PyTuple_GET_SIZE(mro);
|
for (Py_ssize_t i = 0; i < n; i++) {
|
||||||
for (i = 0; i < n; i++) {
|
PyObject *base = PyTuple_GET_ITEM(mro, i);
|
||||||
base = PyTuple_GET_ITEM(mro, i);
|
PyObject *dict = _PyType_CAST(base)->tp_dict;
|
||||||
assert(PyType_Check(base));
|
|
||||||
dict = _PyType_CAST(base)->tp_dict;
|
|
||||||
assert(dict && PyDict_Check(dict));
|
assert(dict && PyDict_Check(dict));
|
||||||
res = _PyDict_GetItem_KnownHash(dict, name, hash);
|
res = _PyDict_GetItem_KnownHash(dict, name, hash);
|
||||||
if (res != NULL)
|
if (res != NULL) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
if (PyErr_Occurred()) {
|
if (PyErr_Occurred()) {
|
||||||
*error = -1;
|
*error = -1;
|
||||||
goto done;
|
goto done;
|
||||||
|
@ -4066,10 +4065,12 @@ _PyDictKeys_DecRef(PyDictKeysObject *keys);
|
||||||
static void
|
static void
|
||||||
type_dealloc_common(PyTypeObject *type)
|
type_dealloc_common(PyTypeObject *type)
|
||||||
{
|
{
|
||||||
PyObject *tp, *val, *tb;
|
if (type->tp_bases != NULL) {
|
||||||
PyErr_Fetch(&tp, &val, &tb);
|
PyObject *tp, *val, *tb;
|
||||||
remove_all_subclasses(type, type->tp_bases);
|
PyErr_Fetch(&tp, &val, &tb);
|
||||||
PyErr_Restore(tp, val, tb);
|
remove_all_subclasses(type, type->tp_bases);
|
||||||
|
PyErr_Restore(tp, val, tb);
|
||||||
|
}
|
||||||
|
|
||||||
PyObject_ClearWeakRefs((PyObject *)type);
|
PyObject_ClearWeakRefs((PyObject *)type);
|
||||||
}
|
}
|
||||||
|
@ -4089,7 +4090,7 @@ _PyStaticType_Dealloc(PyTypeObject *type)
|
||||||
Py_CLEAR(type->tp_bases);
|
Py_CLEAR(type->tp_bases);
|
||||||
Py_CLEAR(type->tp_mro);
|
Py_CLEAR(type->tp_mro);
|
||||||
Py_CLEAR(type->tp_cache);
|
Py_CLEAR(type->tp_cache);
|
||||||
Py_CLEAR(type->tp_subclasses);
|
// type->tp_subclasses is NULL
|
||||||
|
|
||||||
type->tp_flags &= ~Py_TPFLAGS_READY;
|
type->tp_flags &= ~Py_TPFLAGS_READY;
|
||||||
}
|
}
|
||||||
|
@ -4154,6 +4155,7 @@ _PyType_GetSubclasses(PyTypeObject *self)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
assert(PyType_Check(obj));
|
assert(PyType_Check(obj));
|
||||||
|
|
||||||
if (PyList_Append(list, obj) < 0) {
|
if (PyList_Append(list, obj) < 0) {
|
||||||
Py_DECREF(list);
|
Py_DECREF(list);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -6227,7 +6229,7 @@ type_ready_mro(PyTypeObject *type)
|
||||||
Py_ssize_t n = PyTuple_GET_SIZE(mro);
|
Py_ssize_t n = PyTuple_GET_SIZE(mro);
|
||||||
for (Py_ssize_t i = 0; i < n; i++) {
|
for (Py_ssize_t i = 0; i < n; i++) {
|
||||||
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(mro, 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,
|
PyErr_Format(PyExc_TypeError,
|
||||||
"type '%.100s' is not dynamically allocated but "
|
"type '%.100s' is not dynamically allocated but "
|
||||||
"its base type '%.100s' is dynamically allocated",
|
"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.
|
// Only get tp_subclasses after creating the key and value.
|
||||||
// PyWeakref_NewRef() can trigger a garbage collection which can execute
|
// PyWeakref_NewRef() can trigger a garbage collection which can execute
|
||||||
// arbitrary Python code and so modify base->tp_subclasses.
|
// arbitrary Python code and so modify base->tp_subclasses.
|
||||||
PyObject *dict = base->tp_subclasses;
|
PyObject *subclasses = base->tp_subclasses;
|
||||||
if (dict == NULL) {
|
if (subclasses == NULL) {
|
||||||
base->tp_subclasses = dict = PyDict_New();
|
base->tp_subclasses = subclasses = PyDict_New();
|
||||||
if (dict == NULL)
|
if (subclasses == NULL)
|
||||||
return -1;
|
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(ref);
|
||||||
Py_DECREF(key);
|
Py_DECREF(key);
|
||||||
return result;
|
return result;
|
||||||
|
@ -6532,35 +6534,30 @@ add_subclass(PyTypeObject *base, PyTypeObject *type)
|
||||||
static int
|
static int
|
||||||
add_all_subclasses(PyTypeObject *type, PyObject *bases)
|
add_all_subclasses(PyTypeObject *type, PyObject *bases)
|
||||||
{
|
{
|
||||||
|
Py_ssize_t n = PyTuple_GET_SIZE(bases);
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
for (Py_ssize_t i = 0; i < n; i++) {
|
||||||
if (bases) {
|
PyObject *obj = PyTuple_GET_ITEM(bases, i);
|
||||||
Py_ssize_t i;
|
// bases tuple must only contain types
|
||||||
for (i = 0; i < PyTuple_GET_SIZE(bases); i++) {
|
PyTypeObject *base = _PyType_CAST(obj);
|
||||||
PyObject *base = PyTuple_GET_ITEM(bases, i);
|
if (add_subclass(base, type) < 0) {
|
||||||
if (PyType_Check(base) &&
|
res = -1;
|
||||||
add_subclass((PyTypeObject*)base, type) < 0)
|
|
||||||
{
|
|
||||||
res = -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
remove_subclass(PyTypeObject *base, PyTypeObject *type)
|
remove_subclass(PyTypeObject *base, PyTypeObject *type)
|
||||||
{
|
{
|
||||||
PyObject *dict, *key;
|
PyObject *subclasses = base->tp_subclasses; // borrowed ref
|
||||||
|
if (subclasses == NULL) {
|
||||||
dict = base->tp_subclasses;
|
|
||||||
if (dict == NULL) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assert(PyDict_CheckExact(dict));
|
assert(PyDict_CheckExact(subclasses));
|
||||||
key = PyLong_FromVoidPtr((void *) type);
|
|
||||||
if (key == NULL || PyDict_DelItem(dict, key)) {
|
PyObject *key = PyLong_FromVoidPtr((void *) type);
|
||||||
|
if (key == NULL || PyDict_DelItem(subclasses, key)) {
|
||||||
/* This can happen if the type initialization errored out before
|
/* This can happen if the type initialization errored out before
|
||||||
the base subclasses were updated (e.g. a non-str __qualname__
|
the base subclasses were updated (e.g. a non-str __qualname__
|
||||||
was passed in the type dict). */
|
was passed in the type dict). */
|
||||||
|
@ -6568,7 +6565,7 @@ remove_subclass(PyTypeObject *base, PyTypeObject *type)
|
||||||
}
|
}
|
||||||
Py_XDECREF(key);
|
Py_XDECREF(key);
|
||||||
|
|
||||||
if (PyDict_Size(dict) == 0) {
|
if (PyDict_Size(subclasses) == 0) {
|
||||||
// Delete the dictionary to save memory. _PyStaticType_Dealloc()
|
// Delete the dictionary to save memory. _PyStaticType_Dealloc()
|
||||||
// callers also test if tp_subclasses is NULL to check if a static type
|
// callers also test if tp_subclasses is NULL to check if a static type
|
||||||
// has no subclass.
|
// has no subclass.
|
||||||
|
@ -6579,15 +6576,17 @@ remove_subclass(PyTypeObject *base, PyTypeObject *type)
|
||||||
static void
|
static void
|
||||||
remove_all_subclasses(PyTypeObject *type, PyObject *bases)
|
remove_all_subclasses(PyTypeObject *type, PyObject *bases)
|
||||||
{
|
{
|
||||||
if (bases) {
|
assert(bases != NULL);
|
||||||
Py_ssize_t i;
|
// remove_subclass() can clear the current exception
|
||||||
for (i = 0; i < PyTuple_GET_SIZE(bases); i++) {
|
assert(!PyErr_Occurred());
|
||||||
PyObject *base = PyTuple_GET_ITEM(bases, i);
|
|
||||||
if (PyType_Check(base)) {
|
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(bases); i++) {
|
||||||
remove_subclass((PyTypeObject*) base, type);
|
PyObject *base = PyTuple_GET_ITEM(bases, i);
|
||||||
}
|
if (PyType_Check(base)) {
|
||||||
|
remove_subclass((PyTypeObject*) base, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
assert(!PyErr_Occurred());
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -8466,9 +8465,9 @@ static int
|
||||||
update_slots_callback(PyTypeObject *type, void *data)
|
update_slots_callback(PyTypeObject *type, void *data)
|
||||||
{
|
{
|
||||||
slotdef **pp = (slotdef **)data;
|
slotdef **pp = (slotdef **)data;
|
||||||
|
for (; *pp; pp++) {
|
||||||
for (; *pp; pp++)
|
|
||||||
update_one_slot(type, *pp);
|
update_one_slot(type, *pp);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8654,29 +8653,33 @@ type_new_init_subclass(PyTypeObject *type, PyObject *kwds)
|
||||||
|
|
||||||
/* recurse_down_subclasses() and update_subclasses() are mutually
|
/* recurse_down_subclasses() and update_subclasses() are mutually
|
||||||
recursive functions to call a callback for all subclasses,
|
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
|
static int
|
||||||
update_subclasses(PyTypeObject *type, PyObject *name,
|
update_subclasses(PyTypeObject *type, PyObject *attr_name,
|
||||||
update_callback callback, void *data)
|
update_callback callback, void *data)
|
||||||
{
|
{
|
||||||
if (callback(type, data) < 0)
|
if (callback(type, data) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
return recurse_down_subclasses(type, name, callback, data);
|
}
|
||||||
|
return recurse_down_subclasses(type, attr_name, callback, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
recurse_down_subclasses(PyTypeObject *type, PyObject *name,
|
recurse_down_subclasses(PyTypeObject *type, PyObject *attr_name,
|
||||||
update_callback callback, void *data)
|
update_callback callback, void *data)
|
||||||
{
|
{
|
||||||
PyObject *ref, *subclasses, *dict;
|
// It is safe to use a borrowed reference because update_subclasses() is
|
||||||
Py_ssize_t i;
|
// only used with update_slots_callback() which doesn't modify
|
||||||
|
// tp_subclasses.
|
||||||
subclasses = type->tp_subclasses;
|
PyObject *subclasses = type->tp_subclasses; // borrowed ref
|
||||||
if (subclasses == NULL)
|
if (subclasses == NULL) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
assert(PyDict_CheckExact(subclasses));
|
assert(PyDict_CheckExact(subclasses));
|
||||||
i = 0;
|
|
||||||
|
Py_ssize_t i = 0;
|
||||||
|
PyObject *ref;
|
||||||
while (PyDict_Next(subclasses, &i, NULL, &ref)) {
|
while (PyDict_Next(subclasses, &i, NULL, &ref)) {
|
||||||
assert(PyWeakref_CheckRef(ref));
|
assert(PyWeakref_CheckRef(ref));
|
||||||
PyObject *obj = PyWeakref_GET_OBJECT(ref);
|
PyObject *obj = PyWeakref_GET_OBJECT(ref);
|
||||||
|
@ -8687,18 +8690,20 @@ recurse_down_subclasses(PyTypeObject *type, PyObject *name,
|
||||||
PyTypeObject *subclass = _PyType_CAST(obj);
|
PyTypeObject *subclass = _PyType_CAST(obj);
|
||||||
|
|
||||||
/* Avoid recursing down into unaffected classes */
|
/* Avoid recursing down into unaffected classes */
|
||||||
dict = subclass->tp_dict;
|
PyObject *dict = subclass->tp_dict;
|
||||||
if (dict != NULL && PyDict_Check(dict)) {
|
if (dict != NULL && PyDict_Check(dict)) {
|
||||||
int r = PyDict_Contains(dict, name);
|
int r = PyDict_Contains(dict, attr_name);
|
||||||
if (r > 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
return -1;
|
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 -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue