diff --git a/Objects/typeobject.c b/Objects/typeobject.c --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -438,6 +438,7 @@ static int compatible_for_assignment(PyTypeObject *, PyTypeObject *, char *); static int add_subclass(PyTypeObject*, PyTypeObject*); static void remove_subclass(PyTypeObject *, PyTypeObject *); +static void remove_all_subclasses(PyTypeObject *type); static void update_all_slots(PyTypeObject *); typedef int (*update_callback)(PyTypeObject *, void *); @@ -2713,6 +2714,7 @@ et = (PyHeapTypeObject *)type; Py_XDECREF(type->tp_base); Py_XDECREF(type->tp_dict); + remove_all_subclasses(type); Py_XDECREF(type->tp_bases); Py_XDECREF(type->tp_mro); Py_XDECREF(type->tp_cache); @@ -4317,9 +4319,8 @@ static int add_subclass(PyTypeObject *base, PyTypeObject *type) { - Py_ssize_t i; int result; - PyObject *list, *ref, *newobj; + PyObject *list, *newobj; list = base->tp_subclasses; if (list == NULL) { @@ -4329,37 +4330,53 @@ } assert(PyList_Check(list)); newobj = PyWeakref_NewRef((PyObject *)type, NULL); - i = PyList_GET_SIZE(list); + result = PyList_Append(list, newobj); + Py_DECREF(newobj); + return result; +} + +static void +remove_subclass(PyTypeObject *base, PyTypeObject *type) +{ + Py_ssize_t i, k, n; + PyObject *list, *ref, *target; + + list = base->tp_subclasses; + if (list == NULL) { + return; + } + assert(PyList_Check(list)); + i = k = n = PyList_GET_SIZE(list); while (--i >= 0) { ref = PyList_GET_ITEM(list, i); assert(PyWeakref_CheckRef(ref)); - if (PyWeakref_GET_OBJECT(ref) == Py_None) - return PyList_SetItem(list, i, newobj); - } - result = PyList_Append(list, newobj); - Py_DECREF(newobj); - return result; + target = PyWeakref_GET_OBJECT(ref); + if (target == Py_None || target == (PyObject*) type) { + /* swap the item with the last one */ + k--; + if (i != k) { + PyObject *tmp = PyList_GET_ITEM(list, k); + PyList_SET_ITEM(list, k, ref) ; + PyList_SET_ITEM(list, i, tmp); + } + if (target == (PyObject *) type) + break; + } + } + if (k < n) + PyList_SetSlice(list, k, n, NULL); } static void -remove_subclass(PyTypeObject *base, PyTypeObject *type) -{ +remove_all_subclasses(PyTypeObject *type) +{ + PyObject *bases = type->tp_bases; Py_ssize_t i; - PyObject *list, *ref; - - list = base->tp_subclasses; - if (list == NULL) { - return; - } - assert(PyList_Check(list)); - i = PyList_GET_SIZE(list); - while (--i >= 0) { - ref = PyList_GET_ITEM(list, i); - assert(PyWeakref_CheckRef(ref)); - if (PyWeakref_GET_OBJECT(ref) == (PyObject*)type) { - /* this can't fail, right? */ - PySequence_DelItem(list, i); - return; + if (bases) { + 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); } } }