From 760da626ff4124e1344fd8b7dbeb83b2c4b7c12c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 22 Apr 2021 00:10:16 +0200 Subject: [PATCH] bpo-40137: Optimize _PyType_GetModuleByDef() loop (GH-25505) PyType_Ready() now ensures that a type MRO cannot be empty. _PyType_GetModuleByDef() no longer checks "i < PyTuple_GET_SIZE(mro)" at the first loop iteration to optimize the most common case, when the argument is the defining class. --- Objects/typeobject.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/Objects/typeobject.c b/Objects/typeobject.c index ef3833155e0..03af2c5d75d 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -1987,14 +1987,20 @@ mro_invoke(PyTypeObject *type) new_mro = PySequence_Tuple(mro_result); Py_DECREF(mro_result); - if (new_mro == NULL) + if (new_mro == NULL) { return NULL; + } + + if (PyTuple_GET_SIZE(new_mro) == 0) { + Py_DECREF(new_mro); + PyErr_Format(PyExc_TypeError, "type MRO must not be empty"); + return NULL; + } if (custom && mro_check(type, new_mro) < 0) { Py_DECREF(new_mro); return NULL; } - return new_mro; } @@ -2034,8 +2040,9 @@ mro_internal(PyTypeObject *type, PyObject **p_old_mro) new_mro = mro_invoke(type); /* might cause reentrance */ reent = (type->tp_mro != old_mro); Py_XDECREF(old_mro); - if (new_mro == NULL) + if (new_mro == NULL) { return -1; + } if (reent) { Py_DECREF(new_mro); @@ -3590,9 +3597,17 @@ PyObject * _PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def) { assert(PyType_Check(type)); + PyObject *mro = type->tp_mro; + // The type must be ready assert(mro != NULL); - for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(mro); i++) { + assert(PyTuple_Check(mro)); + // mro_invoke() ensures that the type MRO cannot be empty, so we don't have + // to check i < PyTuple_GET_SIZE(mro) at the first loop iteration. + assert(PyTuple_GET_SIZE(mro) >= 1); + + Py_ssize_t i = 0; + do { PyObject *super = PyTuple_GET_ITEM(mro, i); // _PyType_GetModuleByDef() must only be called on a heap type created // by PyType_FromModuleAndSpec() or on its subclasses. @@ -3605,7 +3620,8 @@ _PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def) if (module && PyModule_GetDef(module) == def) { return module; } - } + i++; + } while (i < PyTuple_GET_SIZE(mro)); PyErr_Format( PyExc_TypeError,