bpo-28866: No type cache for types with specialized mro, invalidation is hard. (#13157)

* No type cache for types with specialized mro, invalidation is hard.

* FIX: Don't disable method cache custom types that do not implement mro().

* fixing implem.

* Avoid storing error flags, also decref.

* news entry

* Clear as soon as we're getting an error.

* FIX: Reference leak.
This commit is contained in:
Julien Palard 2019-05-26 16:25:47 +02:00 committed by Mark Shannon
parent 135c6a56e5
commit 180dc1b0f4
2 changed files with 32 additions and 8 deletions

View File

@ -0,0 +1,2 @@
Avoid caching attributes of classes which type defines mro() to avoid a hard
cache invalidation problem.

View File

@ -78,6 +78,9 @@ slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
static void static void
clear_slotdefs(void); clear_slotdefs(void);
static PyObject *
lookup_maybe_method(PyObject *self, _Py_Identifier *attrid, int *unbound);
/* /*
* finds the beginning of the docstring's introspection signature. * finds the beginning of the docstring's introspection signature.
* if present, returns a pointer pointing to the first '('. * if present, returns a pointer pointing to the first '('.
@ -287,17 +290,35 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
Unset HAVE_VERSION_TAG and VALID_VERSION_TAG if the type Unset HAVE_VERSION_TAG and VALID_VERSION_TAG if the type
has a custom MRO that includes a type which is not officially has a custom MRO that includes a type which is not officially
super type. super type, or if the type implements its own mro() method.
Called from mro_internal, which will subsequently be called on Called from mro_internal, which will subsequently be called on
each subclass when their mro is recursively updated. each subclass when their mro is recursively updated.
*/ */
Py_ssize_t i, n; Py_ssize_t i, n;
int clear = 0; int custom = (Py_TYPE(type) != &PyType_Type);
int unbound;
PyObject *mro_meth = NULL;
PyObject *type_mro_meth = NULL;
if (!PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG)) if (!PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG))
return; return;
if (custom) {
_Py_IDENTIFIER(mro);
mro_meth = lookup_maybe_method(
(PyObject *)type, &PyId_mro, &unbound);
if (mro_meth == NULL)
goto clear;
type_mro_meth = lookup_maybe_method(
(PyObject *)&PyType_Type, &PyId_mro, &unbound);
if (type_mro_meth == NULL)
goto clear;
if (mro_meth != type_mro_meth)
goto clear;
Py_XDECREF(mro_meth);
Py_XDECREF(type_mro_meth);
}
n = PyTuple_GET_SIZE(bases); n = PyTuple_GET_SIZE(bases);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
PyObject *b = PyTuple_GET_ITEM(bases, i); PyObject *b = PyTuple_GET_ITEM(bases, i);
@ -308,12 +329,13 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
if (!PyType_HasFeature(cls, Py_TPFLAGS_HAVE_VERSION_TAG) || if (!PyType_HasFeature(cls, Py_TPFLAGS_HAVE_VERSION_TAG) ||
!PyType_IsSubtype(type, cls)) { !PyType_IsSubtype(type, cls)) {
clear = 1; goto clear;
break;
} }
} }
return;
if (clear) clear:
Py_XDECREF(mro_meth);
Py_XDECREF(type_mro_meth);
type->tp_flags &= ~(Py_TPFLAGS_HAVE_VERSION_TAG| type->tp_flags &= ~(Py_TPFLAGS_HAVE_VERSION_TAG|
Py_TPFLAGS_VALID_VERSION_TAG); Py_TPFLAGS_VALID_VERSION_TAG);
} }