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.
This commit is contained in:
Victor Stinner 2021-04-22 00:10:16 +02:00 committed by GitHub
parent d4aaa34798
commit 760da626ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 21 additions and 5 deletions

View File

@ -1987,14 +1987,20 @@ mro_invoke(PyTypeObject *type)
new_mro = PySequence_Tuple(mro_result); new_mro = PySequence_Tuple(mro_result);
Py_DECREF(mro_result); Py_DECREF(mro_result);
if (new_mro == NULL) if (new_mro == NULL) {
return 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) { if (custom && mro_check(type, new_mro) < 0) {
Py_DECREF(new_mro); Py_DECREF(new_mro);
return NULL; return NULL;
} }
return new_mro; return new_mro;
} }
@ -2034,8 +2040,9 @@ mro_internal(PyTypeObject *type, PyObject **p_old_mro)
new_mro = mro_invoke(type); /* might cause reentrance */ new_mro = mro_invoke(type); /* might cause reentrance */
reent = (type->tp_mro != old_mro); reent = (type->tp_mro != old_mro);
Py_XDECREF(old_mro); Py_XDECREF(old_mro);
if (new_mro == NULL) if (new_mro == NULL) {
return -1; return -1;
}
if (reent) { if (reent) {
Py_DECREF(new_mro); Py_DECREF(new_mro);
@ -3590,9 +3597,17 @@ PyObject *
_PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def) _PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def)
{ {
assert(PyType_Check(type)); assert(PyType_Check(type));
PyObject *mro = type->tp_mro; PyObject *mro = type->tp_mro;
// The type must be ready
assert(mro != NULL); 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); PyObject *super = PyTuple_GET_ITEM(mro, i);
// _PyType_GetModuleByDef() must only be called on a heap type created // _PyType_GetModuleByDef() must only be called on a heap type created
// by PyType_FromModuleAndSpec() or on its subclasses. // by PyType_FromModuleAndSpec() or on its subclasses.
@ -3605,7 +3620,8 @@ _PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def)
if (module && PyModule_GetDef(module) == def) { if (module && PyModule_GetDef(module) == def) {
return module; return module;
} }
} i++;
} while (i < PyTuple_GET_SIZE(mro));
PyErr_Format( PyErr_Format(
PyExc_TypeError, PyExc_TypeError,