@@ -65,6 +65,8 @@ lookup_maybe_method(PyObject *self, PyObject *attr, int *unbound);
65
65
static int
66
66
slot_tp_setattro (PyObject * self , PyObject * name , PyObject * value );
67
67
68
+ static inline PyTypeObject * subclass_from_ref (PyObject * ref );
69
+
68
70
/*
69
71
* finds the beginning of the docstring's introspection signature.
70
72
* if present, returns a pointer pointing to the first '('.
@@ -309,12 +311,11 @@ PyType_Modified(PyTypeObject *type)
309
311
Py_ssize_t i = 0 ;
310
312
PyObject * ref ;
311
313
while (PyDict_Next (subclasses , & i , NULL , & ref )) {
312
- assert (PyWeakref_CheckRef (ref ));
313
- PyObject * obj = PyWeakref_GET_OBJECT (ref );
314
- if (obj == Py_None ) {
314
+ PyTypeObject * subclass = subclass_from_ref (ref ); // borrowed
315
+ if (subclass == NULL ) {
315
316
continue ;
316
317
}
317
- PyType_Modified (_PyType_CAST ( obj ) );
318
+ PyType_Modified (subclass );
318
319
}
319
320
}
320
321
@@ -4211,23 +4212,48 @@ type_dealloc_common(PyTypeObject *type)
4211
4212
}
4212
4213
4213
4214
4214
- void
4215
- _PyStaticType_Dealloc (PyTypeObject * type )
4215
+ static void
4216
+ clear_static_tp_subclasses (PyTypeObject * type )
4216
4217
{
4217
- // If a type still has subtypes, it cannot be deallocated.
4218
- // A subtype can inherit attributes and methods of its parent type,
4219
- // and a type must no longer be used once it's deallocated.
4220
- if (type -> tp_subclasses != NULL ) {
4218
+ if (type -> tp_subclasses == NULL ) {
4221
4219
return ;
4222
4220
}
4223
4221
4222
+ /* Normally it would be a problem to finalize the type if its
4223
+ tp_subclasses wasn't cleared first. However, this is only
4224
+ ever called at the end of runtime finalization, so we can be
4225
+ more liberal in cleaning up. If the given type still has
4226
+ subtypes at this point then some extension module did not
4227
+ correctly finalize its objects.
4228
+
4229
+ We can safely obliterate such subtypes since the extension
4230
+ module and its objects won't be used again, except maybe if
4231
+ the runtime were re-initialized. In that case the sticky
4232
+ situation would only happen if the module were re-imported
4233
+ then and only if the subtype were stored in a global and only
4234
+ if that global were not overwritten during import. We'd be
4235
+ fine since the extension is otherwise unsafe and unsupported
4236
+ in that situation, and likely problematic already.
4237
+
4238
+ In any case, this situation means at least some memory is
4239
+ going to leak. This mostly only affects embedding scenarios.
4240
+ */
4241
+
4242
+ // For now we just clear tp_subclasses.
4243
+
4244
+ Py_CLEAR (type -> tp_subclasses );
4245
+ }
4246
+
4247
+ void
4248
+ _PyStaticType_Dealloc (PyTypeObject * type )
4249
+ {
4224
4250
type_dealloc_common (type );
4225
4251
4226
4252
Py_CLEAR (type -> tp_dict );
4227
4253
Py_CLEAR (type -> tp_bases );
4228
4254
Py_CLEAR (type -> tp_mro );
4229
4255
Py_CLEAR (type -> tp_cache );
4230
- // type->tp_subclasses is NULL
4256
+ clear_static_tp_subclasses ( type );
4231
4257
4232
4258
// PyObject_ClearWeakRefs() raises an exception if Py_REFCNT() != 0
4233
4259
if (Py_REFCNT (type ) == 0 ) {
@@ -4296,14 +4322,12 @@ _PyType_GetSubclasses(PyTypeObject *self)
4296
4322
Py_ssize_t i = 0 ;
4297
4323
PyObject * ref ; // borrowed ref
4298
4324
while (PyDict_Next (subclasses , & i , NULL , & ref )) {
4299
- assert (PyWeakref_CheckRef (ref ));
4300
- PyObject * obj = PyWeakref_GET_OBJECT (ref ); // borrowed ref
4301
- if (obj == Py_None ) {
4325
+ PyTypeObject * subclass = subclass_from_ref (ref ); // borrowed
4326
+ if (subclass == NULL ) {
4302
4327
continue ;
4303
4328
}
4304
- assert (PyType_Check (obj ));
4305
4329
4306
- if (PyList_Append (list , obj ) < 0 ) {
4330
+ if (PyList_Append (list , _PyObject_CAST ( subclass ) ) < 0 ) {
4307
4331
Py_DECREF (list );
4308
4332
return NULL ;
4309
4333
}
@@ -6708,6 +6732,42 @@ add_all_subclasses(PyTypeObject *type, PyObject *bases)
6708
6732
return res ;
6709
6733
}
6710
6734
6735
+ static inline PyTypeObject *
6736
+ subclass_from_ref (PyObject * ref )
6737
+ {
6738
+ assert (PyWeakref_CheckRef (ref ));
6739
+ PyObject * obj = PyWeakref_GET_OBJECT (ref ); // borrowed ref
6740
+ assert (obj != NULL );
6741
+ if (obj == Py_None ) {
6742
+ return NULL ;
6743
+ }
6744
+ assert (PyType_Check (obj ));
6745
+ return _PyType_CAST (obj );
6746
+ }
6747
+
6748
+ static PyObject *
6749
+ get_subclasses_key (PyTypeObject * type , PyTypeObject * base )
6750
+ {
6751
+ PyObject * key = PyLong_FromVoidPtr ((void * ) type );
6752
+ if (key != NULL ) {
6753
+ return key ;
6754
+ }
6755
+ PyErr_Clear ();
6756
+
6757
+ /* This basically means we're out of memory.
6758
+ We fall back to manually traversing the values. */
6759
+ Py_ssize_t i = 0 ;
6760
+ PyObject * ref ; // borrowed ref
6761
+ while (PyDict_Next ((PyObject * )base -> tp_subclasses , & i , & key , & ref )) {
6762
+ PyTypeObject * subclass = subclass_from_ref (ref ); // borrowed
6763
+ if (subclass == type ) {
6764
+ return Py_NewRef (key );
6765
+ }
6766
+ }
6767
+ /* It wasn't found. */
6768
+ return NULL ;
6769
+ }
6770
+
6711
6771
static void
6712
6772
remove_subclass (PyTypeObject * base , PyTypeObject * type )
6713
6773
{
@@ -6717,8 +6777,8 @@ remove_subclass(PyTypeObject *base, PyTypeObject *type)
6717
6777
}
6718
6778
assert (PyDict_CheckExact (subclasses ));
6719
6779
6720
- PyObject * key = PyLong_FromVoidPtr (( void * ) type );
6721
- if (key == NULL || PyDict_DelItem (subclasses , key )) {
6780
+ PyObject * key = get_subclasses_key ( type , base );
6781
+ if (key != NULL && PyDict_DelItem (subclasses , key )) {
6722
6782
/* This can happen if the type initialization errored out before
6723
6783
the base subclasses were updated (e.g. a non-str __qualname__
6724
6784
was passed in the type dict). */
@@ -8811,13 +8871,10 @@ recurse_down_subclasses(PyTypeObject *type, PyObject *attr_name,
8811
8871
Py_ssize_t i = 0 ;
8812
8872
PyObject * ref ;
8813
8873
while (PyDict_Next (subclasses , & i , NULL , & ref )) {
8814
- assert (PyWeakref_CheckRef (ref ));
8815
- PyObject * obj = PyWeakref_GET_OBJECT (ref );
8816
- assert (obj != NULL );
8817
- if (obj == Py_None ) {
8874
+ PyTypeObject * subclass = subclass_from_ref (ref ); // borrowed
8875
+ if (subclass == NULL ) {
8818
8876
continue ;
8819
8877
}
8820
- PyTypeObject * subclass = _PyType_CAST (obj );
8821
8878
8822
8879
/* Avoid recursing down into unaffected classes */
8823
8880
PyObject * dict = subclass -> tp_dict ;
0 commit comments