Commit 1f121312 authored by Guido van Rossum's avatar Guido van Rossum

Use the new C3 MRO algorithm, implemented by Samuele Pedroni (SF patch

619475; also closing SF bug 618704).  I tweaked his code a bit for
style.

This raises TypeError for MRO order disagreements, which is an
improvement (previously these went undetected) but also a degradation:
what if the order disagreement doesn't affect any method lookups?
I don't think I care.
parent c7aaf953
...@@ -613,66 +613,6 @@ call_maybe(PyObject *o, char *name, PyObject **nameobj, char *format, ...) ...@@ -613,66 +613,6 @@ call_maybe(PyObject *o, char *name, PyObject **nameobj, char *format, ...)
return retval; return retval;
} }
/* Method resolution order algorithm from "Putting Metaclasses to Work"
by Forman and Danforth (Addison-Wesley 1999). */
static int
conservative_merge(PyObject *left, PyObject *right)
{
int left_size;
int right_size;
int i, j, r, ok;
PyObject *temp, *rr;
assert(PyList_Check(left));
assert(PyList_Check(right));
again:
left_size = PyList_GET_SIZE(left);
right_size = PyList_GET_SIZE(right);
for (i = 0; i < left_size; i++) {
for (j = 0; j < right_size; j++) {
if (PyList_GET_ITEM(left, i) ==
PyList_GET_ITEM(right, j)) {
/* found a merge point */
temp = PyList_New(0);
if (temp == NULL)
return -1;
for (r = 0; r < j; r++) {
rr = PyList_GET_ITEM(right, r);
ok = PySequence_Contains(left, rr);
if (ok < 0) {
Py_DECREF(temp);
return -1;
}
if (!ok) {
ok = PyList_Append(temp, rr);
if (ok < 0) {
Py_DECREF(temp);
return -1;
}
}
}
ok = PyList_SetSlice(left, i, i, temp);
Py_DECREF(temp);
if (ok < 0)
return -1;
ok = PyList_SetSlice(right, 0, j+1, NULL);
if (ok < 0)
return -1;
goto again;
}
}
}
return PyList_SetSlice(left, left_size, left_size, right);
}
static int
serious_order_disagreements(PyObject *left, PyObject *right)
{
return 0; /* XXX later -- for now, we cheat: "don't do that" */
}
static int static int
fill_classic_mro(PyObject *mro, PyObject *cls) fill_classic_mro(PyObject *mro, PyObject *cls)
{ {
...@@ -714,11 +654,87 @@ classic_mro(PyObject *cls) ...@@ -714,11 +654,87 @@ classic_mro(PyObject *cls)
return NULL; return NULL;
} }
/*
Method resolution order algorithm C3 described in
"A Monotonic Superclass Linearization for Dylan",
by Kim Barrett, Bob Cassel, Paul Haahr,
David A. Moon, Keith Playford, and P. Tucker Withington.
(OOPSLA 1996)
*/
static int
tail_contains(PyObject *list, int whence, PyObject *o) {
int j, size;
size = PyList_GET_SIZE(list);
for (j = whence+1; j < size; j++) {
if (PyList_GET_ITEM(list, j) == o)
return 1;
}
return 0;
}
static int
pmerge(PyObject *acc, PyObject* to_merge) {
int i, j, to_merge_size;
int *remain;
int ok, empty_cnt;
to_merge_size = PyList_GET_SIZE(to_merge);
remain = PyMem_MALLOC(SIZEOF_INT*to_merge_size);
if (remain == NULL)
return -1;
for (i = 0; i < to_merge_size; i++)
remain[i] = 0;
again:
empty_cnt = 0;
for (i = 0; i < to_merge_size; i++) {
PyObject *candidate;
PyObject *cur_list = PyList_GET_ITEM(to_merge, i);
if (remain[i] >= PyList_GET_SIZE(cur_list)) {
empty_cnt++;
continue;
}
candidate = PyList_GET_ITEM(cur_list, remain[i]);
for (j = 0; j < to_merge_size; j++) {
PyObject *j_lst = PyList_GET_ITEM(to_merge, j);
if (tail_contains(j_lst, remain[j], candidate))
goto skip; /* continue outer loop */
}
ok = PyList_Append(acc, candidate);
if (ok < 0) {
PyMem_Free(remain);
return -1;
}
for (j = 0; j < to_merge_size; j++) {
PyObject *j_lst = PyList_GET_ITEM(to_merge, j);
if (PyList_GET_ITEM(j_lst, remain[j]) == candidate) {
remain[j]++;
}
}
goto again;
skip:
}
PyMem_FREE(remain);
if (empty_cnt == to_merge_size)
return 0;
PyErr_SetString(PyExc_TypeError, "MRO order disagreement");
return -1;
}
static PyObject * static PyObject *
mro_implementation(PyTypeObject *type) mro_implementation(PyTypeObject *type)
{ {
int i, n, ok; int i, n, ok;
PyObject *bases, *result; PyObject *bases, *result;
PyObject *to_merge, *bases_aslist;
if(type->tp_dict == NULL) { if(type->tp_dict == NULL) {
if(PyType_Ready(type) < 0) if(PyType_Ready(type) < 0)
...@@ -727,9 +743,11 @@ mro_implementation(PyTypeObject *type) ...@@ -727,9 +743,11 @@ mro_implementation(PyTypeObject *type)
bases = type->tp_bases; bases = type->tp_bases;
n = PyTuple_GET_SIZE(bases); n = PyTuple_GET_SIZE(bases);
result = Py_BuildValue("[O]", (PyObject *)type);
if (result == NULL) to_merge = PyList_New(n+1);
if (to_merge == NULL)
return NULL; return NULL;
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
PyObject *base = PyTuple_GET_ITEM(bases, i); PyObject *base = PyTuple_GET_ITEM(bases, i);
PyObject *parentMRO; PyObject *parentMRO;
...@@ -739,20 +757,33 @@ mro_implementation(PyTypeObject *type) ...@@ -739,20 +757,33 @@ mro_implementation(PyTypeObject *type)
else else
parentMRO = classic_mro(base); parentMRO = classic_mro(base);
if (parentMRO == NULL) { if (parentMRO == NULL) {
Py_DECREF(result); Py_DECREF(to_merge);
return NULL; return NULL;
} }
if (serious_order_disagreements(result, parentMRO)) {
Py_DECREF(result); PyList_SET_ITEM(to_merge, i, parentMRO);
}
bases_aslist = PySequence_List(bases);
if (bases_aslist == NULL) {
Py_DECREF(to_merge);
return NULL;
}
PyList_SET_ITEM(to_merge, n, bases_aslist);
result = Py_BuildValue("[O]", (PyObject *)type);
if (result == NULL) {
Py_DECREF(to_merge);
return NULL; return NULL;
} }
ok = conservative_merge(result, parentMRO);
Py_DECREF(parentMRO); ok = pmerge(result, to_merge);
Py_DECREF(to_merge);
if (ok < 0) { if (ok < 0) {
Py_DECREF(result); Py_DECREF(result);
return NULL; return NULL;
} }
}
return result; return result;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment