From 180dc1b0f4a57c3f66351568ae8488fa8576d7f0 Mon Sep 17 00:00:00 2001 From: Julien Palard Date: Sun, 26 May 2019 16:25:47 +0200 Subject: [PATCH] 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. --- .../2019-05-08-16-36-51.bpo-28866.qCv_bj.rst | 2 + Objects/typeobject.c | 38 +++++++++++++++---- 2 files changed, 32 insertions(+), 8 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2019-05-08-16-36-51.bpo-28866.qCv_bj.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-05-08-16-36-51.bpo-28866.qCv_bj.rst b/Misc/NEWS.d/next/Core and Builtins/2019-05-08-16-36-51.bpo-28866.qCv_bj.rst new file mode 100644 index 00000000000..69017293649 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-05-08-16-36-51.bpo-28866.qCv_bj.rst @@ -0,0 +1,2 @@ +Avoid caching attributes of classes which type defines mro() to avoid a hard +cache invalidation problem. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 339f7285292..fc809d36e10 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -78,6 +78,9 @@ slot_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds); static 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. * 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 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 each subclass when their mro is recursively updated. */ 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)) 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); for (i = 0; i < n; i++) { PyObject *b = PyTuple_GET_ITEM(bases, i); @@ -308,14 +329,15 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) { if (!PyType_HasFeature(cls, Py_TPFLAGS_HAVE_VERSION_TAG) || !PyType_IsSubtype(type, cls)) { - clear = 1; - break; + goto clear; } } - - if (clear) - type->tp_flags &= ~(Py_TPFLAGS_HAVE_VERSION_TAG| - Py_TPFLAGS_VALID_VERSION_TAG); + return; + clear: + Py_XDECREF(mro_meth); + Py_XDECREF(type_mro_meth); + type->tp_flags &= ~(Py_TPFLAGS_HAVE_VERSION_TAG| + Py_TPFLAGS_VALID_VERSION_TAG); } static int