summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2002-11-14 19:49:16 (GMT)
committerGuido van Rossum <guido@python.org>2002-11-14 19:49:16 (GMT)
commit1f1213120e40e1d1cb58348ada03da27471e9f5e (patch)
tree68e0249ba62804ee129e05c044ec7ad3da63890a
parentc7aaf953faf3f4f22c8a77209927dcacfba91490 (diff)
downloadcpython-1f1213120e40e1d1cb58348ada03da27471e9f5e.zip
cpython-1f1213120e40e1d1cb58348ada03da27471e9f5e.tar.gz
cpython-1f1213120e40e1d1cb58348ada03da27471e9f5e.tar.bz2
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.
-rw-r--r--Objects/typeobject.c179
1 files changed, 105 insertions, 74 deletions
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index e624ec4..e2dace8 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -613,66 +613,6 @@ call_maybe(PyObject *o, char *name, PyObject **nameobj, char *format, ...)
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
fill_classic_mro(PyObject *mro, PyObject *cls)
{
@@ -714,11 +654,87 @@ classic_mro(PyObject *cls)
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 *
mro_implementation(PyTypeObject *type)
{
int i, n, ok;
PyObject *bases, *result;
+ PyObject *to_merge, *bases_aslist;
if(type->tp_dict == NULL) {
if(PyType_Ready(type) < 0)
@@ -727,9 +743,11 @@ mro_implementation(PyTypeObject *type)
bases = type->tp_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;
+
for (i = 0; i < n; i++) {
PyObject *base = PyTuple_GET_ITEM(bases, i);
PyObject *parentMRO;
@@ -739,20 +757,33 @@ mro_implementation(PyTypeObject *type)
else
parentMRO = classic_mro(base);
if (parentMRO == NULL) {
- Py_DECREF(result);
- return NULL;
- }
- if (serious_order_disagreements(result, parentMRO)) {
- Py_DECREF(result);
- return NULL;
- }
- ok = conservative_merge(result, parentMRO);
- Py_DECREF(parentMRO);
- if (ok < 0) {
- Py_DECREF(result);
+ Py_DECREF(to_merge);
return NULL;
- }
+ }
+
+ 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;
}
+
+ ok = pmerge(result, to_merge);
+ Py_DECREF(to_merge);
+ if (ok < 0) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
return result;
}