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
tail_contains(PyObject *list, int whence, PyObject *o) {
tail_contains(PyObject *tuple, int whence, PyObject *o)
{
Py_ssize_t j, size;
size = PyList_GET_SIZE(list);
size = PyTuple_GET_SIZE(tuple);
for (j = whence+1; j < size; j++) {
if (PyList_GET_ITEM(list, j) == o)
if (PyTuple_GET_ITEM(tuple, j) == o)
return 1;
}
return 0;
@ -1593,17 +1594,17 @@ class_name(PyObject *cls)
}
static int
check_duplicates(PyObject *list)
check_duplicates(PyObject *tuple)
{
Py_ssize_t i, j, n;
/* 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++) {
PyObject *o = PyList_GET_ITEM(list, i);
PyObject *o = PyTuple_GET_ITEM(tuple, i);
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);
if (o != NULL) {
PyErr_Format(PyExc_TypeError,
@ -1631,19 +1632,18 @@ check_duplicates(PyObject *list)
*/
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];
PyObject *k, *v;
PyObject *set = PyDict_New();
if (!set) return;
to_merge_size = PyList_GET_SIZE(to_merge);
for (i = 0; i < to_merge_size; i++) {
PyObject *L = PyList_GET_ITEM(to_merge, i);
if (remain[i] < PyList_GET_SIZE(L)) {
PyObject *c = PyList_GET_ITEM(L, remain[i]);
PyObject *L = to_merge[i];
if (remain[i] < PyTuple_GET_SIZE(L)) {
PyObject *c = PyTuple_GET_ITEM(L, remain[i]);
if (PyDict_SetItem(set, c, Py_None) < 0) {
Py_DECREF(set);
return;
@ -1676,19 +1676,17 @@ consistent method resolution\norder (MRO) for bases");
}
static int
pmerge(PyObject *acc, PyObject* to_merge)
pmerge(PyObject *acc, PyObject **to_merge, Py_ssize_t to_merge_size)
{
int res = 0;
Py_ssize_t i, j, to_merge_size, empty_cnt;
Py_ssize_t i, j, empty_cnt;
int *remain;
to_merge_size = PyList_GET_SIZE(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]
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) {
PyErr_NoMemory();
return -1;
@ -1701,9 +1699,9 @@ pmerge(PyObject *acc, PyObject* to_merge)
for (i = 0; i < to_merge_size; i++) {
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++;
continue;
}
@ -1715,9 +1713,9 @@ pmerge(PyObject *acc, PyObject* to_merge)
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++) {
PyObject *j_lst = PyList_GET_ITEM(to_merge, j);
PyObject *j_lst = to_merge[j];
if (tail_contains(j_lst, remain[j], candidate))
goto skip; /* continue outer loop */
}
@ -1726,9 +1724,9 @@ pmerge(PyObject *acc, PyObject* to_merge)
goto out;
for (j = 0; j < to_merge_size; j++) {
PyObject *j_lst = PyList_GET_ITEM(to_merge, j);
if (remain[j] < PyList_GET_SIZE(j_lst) &&
PyList_GET_ITEM(j_lst, remain[j]) == candidate) {
PyObject *j_lst = to_merge[j];
if (remain[j] < PyTuple_GET_SIZE(j_lst) &&
PyTuple_GET_ITEM(j_lst, remain[j]) == candidate) {
remain[j]++;
}
}
@ -1737,12 +1735,12 @@ pmerge(PyObject *acc, PyObject* to_merge)
}
if (empty_cnt != to_merge_size) {
set_mro_error(to_merge, remain);
set_mro_error(to_merge, to_merge_size, remain);
res = -1;
}
out:
PyMem_FREE(remain);
PyMem_Del(remain);
return res;
}
@ -1750,10 +1748,9 @@ pmerge(PyObject *acc, PyObject* to_merge)
static PyObject *
mro_implementation(PyTypeObject *type)
{
PyObject *result = NULL;
PyObject *result;
PyObject *bases;
PyObject *to_merge, *bases_aslist;
int res;
PyObject **to_merge;
Py_ssize_t i, n;
if (type->tp_dict == NULL) {
@ -1762,21 +1759,25 @@ mro_implementation(PyTypeObject *type)
}
bases = type->tp_bases;
assert(PyTuple_Check(bases));
n = PyTuple_GET_SIZE(bases);
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;
for (i = 0; i < n; i++) {
PyTypeObject *base = (PyTypeObject *)PyTuple_GET_ITEM(bases, i);
if (base->tp_mro == NULL) {
PyErr_Format(PyExc_TypeError,
"Cannot extend an incomplete type '%.100s'",
base->tp_name);
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);
if (result == NULL) {
return NULL;
@ -1791,59 +1792,45 @@ mro_implementation(PyTypeObject *type)
return result;
}
/* This is just a basic sanity check. */
if (check_duplicates(bases) < 0) {
return NULL;
}
/* 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.
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
to_merge is the declared list of bases.
to_merge is the declared tuple of bases.
*/
to_merge = PyList_New(n+1);
if (to_merge == NULL)
to_merge = PyMem_New(PyObject *, n + 1);
if (to_merge == NULL) {
PyErr_NoMemory();
return NULL;
}
for (i = 0; i < n; i++) {
PyTypeObject *base;
PyObject *base_mro_aslist;
PyTypeObject *base = (PyTypeObject *)PyTuple_GET_ITEM(bases, i);
to_merge[i] = base->tp_mro;
}
to_merge[n] = bases;
base = (PyTypeObject *)PyTuple_GET_ITEM(bases, i);
if (base->tp_mro == NULL) {
PyErr_Format(PyExc_TypeError,
"Cannot extend an incomplete type '%.100s'",
base->tp_name);
goto out;
result = PyList_New(1);
if (result == NULL) {
PyMem_Del(to_merge);
return NULL;
}
base_mro_aslist = PySequence_List(base->tp_mro);
if (base_mro_aslist == NULL)
goto out;
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_INCREF(type);
PyList_SET_ITEM(result, 0, (PyObject *)type);
if (pmerge(result, to_merge, n + 1) < 0) {
Py_CLEAR(result);
}
out:
Py_DECREF(to_merge);
PyMem_Del(to_merge);
return result;
}