bpo-32385: Clean up the C3 MRO algorithm implementation. (#4942)

Use tuples and raw arrays instead of lists.
This commit is contained in:
Serhiy Storchaka 2017-12-20 19:21:02 +02:00 committed by GitHub
parent ca719ac42b
commit 6b91a59721
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 65 additions and 78 deletions

View File

@ -1564,12 +1564,13 @@ call_maybe(PyObject *obj, _Py_Identifier *name,
*/ */
static int static int
tail_contains(PyObject *list, int whence, PyObject *o) { tail_contains(PyObject *tuple, int whence, PyObject *o)
{
Py_ssize_t j, size; Py_ssize_t j, size;
size = PyList_GET_SIZE(list); size = PyTuple_GET_SIZE(tuple);
for (j = whence+1; j < size; j++) { for (j = whence+1; j < size; j++) {
if (PyList_GET_ITEM(list, j) == o) if (PyTuple_GET_ITEM(tuple, j) == o)
return 1; return 1;
} }
return 0; return 0;
@ -1593,17 +1594,17 @@ class_name(PyObject *cls)
} }
static int static int
check_duplicates(PyObject *list) check_duplicates(PyObject *tuple)
{ {
Py_ssize_t i, j, n; Py_ssize_t i, j, n;
/* Let's use a quadratic time algorithm, /* Let's use a quadratic time algorithm,
assuming that the bases lists is short. assuming that the bases tuples is short.
*/ */
n = PyList_GET_SIZE(list); n = PyTuple_GET_SIZE(tuple);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
PyObject *o = PyList_GET_ITEM(list, i); PyObject *o = PyTuple_GET_ITEM(tuple, i);
for (j = i + 1; j < n; j++) { for (j = i + 1; j < n; j++) {
if (PyList_GET_ITEM(list, j) == o) { if (PyTuple_GET_ITEM(tuple, j) == o) {
o = class_name(o); o = class_name(o);
if (o != NULL) { if (o != NULL) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
@ -1631,19 +1632,18 @@ check_duplicates(PyObject *list)
*/ */
static void static void
set_mro_error(PyObject *to_merge, int *remain) set_mro_error(PyObject **to_merge, Py_ssize_t to_merge_size, int *remain)
{ {
Py_ssize_t i, n, off, to_merge_size; Py_ssize_t i, n, off;
char buf[1000]; char buf[1000];
PyObject *k, *v; PyObject *k, *v;
PyObject *set = PyDict_New(); PyObject *set = PyDict_New();
if (!set) return; if (!set) return;
to_merge_size = PyList_GET_SIZE(to_merge);
for (i = 0; i < to_merge_size; i++) { for (i = 0; i < to_merge_size; i++) {
PyObject *L = PyList_GET_ITEM(to_merge, i); PyObject *L = to_merge[i];
if (remain[i] < PyList_GET_SIZE(L)) { if (remain[i] < PyTuple_GET_SIZE(L)) {
PyObject *c = PyList_GET_ITEM(L, remain[i]); PyObject *c = PyTuple_GET_ITEM(L, remain[i]);
if (PyDict_SetItem(set, c, Py_None) < 0) { if (PyDict_SetItem(set, c, Py_None) < 0) {
Py_DECREF(set); Py_DECREF(set);
return; return;
@ -1676,19 +1676,17 @@ consistent method resolution\norder (MRO) for bases");
} }
static int static int
pmerge(PyObject *acc, PyObject* to_merge) pmerge(PyObject *acc, PyObject **to_merge, Py_ssize_t to_merge_size)
{ {
int res = 0; int res = 0;
Py_ssize_t i, j, to_merge_size, empty_cnt; Py_ssize_t i, j, empty_cnt;
int *remain; int *remain;
to_merge_size = PyList_GET_SIZE(to_merge);
/* remain stores an index into each sublist of to_merge. /* remain stores an index into each sublist of to_merge.
remain[i] is the index of the next base in to_merge[i] remain[i] is the index of the next base in to_merge[i]
that is not included in acc. that is not included in acc.
*/ */
remain = (int *)PyMem_MALLOC(SIZEOF_INT*to_merge_size); remain = PyMem_New(int, to_merge_size);
if (remain == NULL) { if (remain == NULL) {
PyErr_NoMemory(); PyErr_NoMemory();
return -1; return -1;
@ -1701,9 +1699,9 @@ pmerge(PyObject *acc, PyObject* to_merge)
for (i = 0; i < to_merge_size; i++) { for (i = 0; i < to_merge_size; i++) {
PyObject *candidate; PyObject *candidate;
PyObject *cur_list = PyList_GET_ITEM(to_merge, i); PyObject *cur_tuple = to_merge[i];
if (remain[i] >= PyList_GET_SIZE(cur_list)) { if (remain[i] >= PyTuple_GET_SIZE(cur_tuple)) {
empty_cnt++; empty_cnt++;
continue; continue;
} }
@ -1715,9 +1713,9 @@ pmerge(PyObject *acc, PyObject* to_merge)
of the earliest direct superclass of the new class. of the earliest direct superclass of the new class.
*/ */
candidate = PyList_GET_ITEM(cur_list, remain[i]); candidate = PyTuple_GET_ITEM(cur_tuple, remain[i]);
for (j = 0; j < to_merge_size; j++) { for (j = 0; j < to_merge_size; j++) {
PyObject *j_lst = PyList_GET_ITEM(to_merge, j); PyObject *j_lst = to_merge[j];
if (tail_contains(j_lst, remain[j], candidate)) if (tail_contains(j_lst, remain[j], candidate))
goto skip; /* continue outer loop */ goto skip; /* continue outer loop */
} }
@ -1726,9 +1724,9 @@ pmerge(PyObject *acc, PyObject* to_merge)
goto out; goto out;
for (j = 0; j < to_merge_size; j++) { for (j = 0; j < to_merge_size; j++) {
PyObject *j_lst = PyList_GET_ITEM(to_merge, j); PyObject *j_lst = to_merge[j];
if (remain[j] < PyList_GET_SIZE(j_lst) && if (remain[j] < PyTuple_GET_SIZE(j_lst) &&
PyList_GET_ITEM(j_lst, remain[j]) == candidate) { PyTuple_GET_ITEM(j_lst, remain[j]) == candidate) {
remain[j]++; remain[j]++;
} }
} }
@ -1737,12 +1735,12 @@ pmerge(PyObject *acc, PyObject* to_merge)
} }
if (empty_cnt != to_merge_size) { if (empty_cnt != to_merge_size) {
set_mro_error(to_merge, remain); set_mro_error(to_merge, to_merge_size, remain);
res = -1; res = -1;
} }
out: out:
PyMem_FREE(remain); PyMem_Del(remain);
return res; return res;
} }
@ -1750,10 +1748,9 @@ pmerge(PyObject *acc, PyObject* to_merge)
static PyObject * static PyObject *
mro_implementation(PyTypeObject *type) mro_implementation(PyTypeObject *type)
{ {
PyObject *result = NULL; PyObject *result;
PyObject *bases; PyObject *bases;
PyObject *to_merge, *bases_aslist; PyObject **to_merge;
int res;
Py_ssize_t i, n; Py_ssize_t i, n;
if (type->tp_dict == NULL) { if (type->tp_dict == NULL) {
@ -1762,21 +1759,25 @@ mro_implementation(PyTypeObject *type)
} }
bases = type->tp_bases; bases = type->tp_bases;
assert(PyTuple_Check(bases));
n = PyTuple_GET_SIZE(bases); n = PyTuple_GET_SIZE(bases);
if (n == 1) { for (i = 0; i < n; i++) {
/* Fast path: if there is a single base, constructing the MRO PyTypeObject *base = (PyTypeObject *)PyTuple_GET_ITEM(bases, i);
* is trivial.
*/
PyTypeObject *base = (PyTypeObject *)PyTuple_GET_ITEM(bases, 0);
Py_ssize_t k;
if (base->tp_mro == NULL) { if (base->tp_mro == NULL) {
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"Cannot extend an incomplete type '%.100s'", "Cannot extend an incomplete type '%.100s'",
base->tp_name); base->tp_name);
return NULL; return NULL;
} }
k = PyTuple_GET_SIZE(base->tp_mro); assert(PyTuple_Check(base->tp_mro));
}
if (n == 1) {
/* Fast path: if there is a single base, constructing the MRO
* is trivial.
*/
PyTypeObject *base = (PyTypeObject *)PyTuple_GET_ITEM(bases, 0);
Py_ssize_t k = PyTuple_GET_SIZE(base->tp_mro);
result = PyTuple_New(k + 1); result = PyTuple_New(k + 1);
if (result == NULL) { if (result == NULL) {
return NULL; return NULL;
@ -1791,59 +1792,45 @@ mro_implementation(PyTypeObject *type)
return result; return result;
} }
/* This is just a basic sanity check. */
if (check_duplicates(bases) < 0) {
return NULL;
}
/* Find a superclass linearization that honors the constraints /* Find a superclass linearization that honors the constraints
of the explicit lists of bases and the constraints implied by of the explicit tuples of bases and the constraints implied by
each base class. each base class.
to_merge is a list of lists, where each list is a superclass to_merge is an array of tuples, where each tuple is a superclass
linearization implied by a base class. The last element of linearization implied by a base class. The last element of
to_merge is the declared list of bases. to_merge is the declared tuple of bases.
*/ */
to_merge = PyList_New(n+1); to_merge = PyMem_New(PyObject *, n + 1);
if (to_merge == NULL) if (to_merge == NULL) {
PyErr_NoMemory();
return NULL; return NULL;
}
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
PyTypeObject *base; PyTypeObject *base = (PyTypeObject *)PyTuple_GET_ITEM(bases, i);
PyObject *base_mro_aslist; to_merge[i] = base->tp_mro;
}
to_merge[n] = bases;
base = (PyTypeObject *)PyTuple_GET_ITEM(bases, i); result = PyList_New(1);
if (base->tp_mro == NULL) { if (result == NULL) {
PyErr_Format(PyExc_TypeError, PyMem_Del(to_merge);
"Cannot extend an incomplete type '%.100s'", return NULL;
base->tp_name);
goto out;
} }
base_mro_aslist = PySequence_List(base->tp_mro); Py_INCREF(type);
if (base_mro_aslist == NULL) PyList_SET_ITEM(result, 0, (PyObject *)type);
goto out; if (pmerge(result, to_merge, n + 1) < 0) {
PyList_SET_ITEM(to_merge, i, base_mro_aslist);
}
bases_aslist = PySequence_List(bases);
if (bases_aslist == NULL)
goto out;
/* This is just a basic sanity check. */
if (check_duplicates(bases_aslist) < 0) {
Py_DECREF(bases_aslist);
goto out;
}
PyList_SET_ITEM(to_merge, n, bases_aslist);
result = Py_BuildValue("[O]", (PyObject *)type);
if (result == NULL)
goto out;
res = pmerge(result, to_merge);
if (res < 0)
Py_CLEAR(result); Py_CLEAR(result);
}
out: PyMem_Del(to_merge);
Py_DECREF(to_merge);
return result; return result;
} }