summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2017-12-20 17:21:02 (GMT)
committerGitHub <noreply@github.com>2017-12-20 17:21:02 (GMT)
commit6b91a5972107ec8dd5334f4f2005626baa2b8847 (patch)
treea4b4fec2b885b4d4ccf26761b01b7b1250391b75 /Objects
parentca719ac42b3d58f7c3bcdf63f45b6d62b08b0d01 (diff)
downloadcpython-6b91a5972107ec8dd5334f4f2005626baa2b8847.zip
cpython-6b91a5972107ec8dd5334f4f2005626baa2b8847.tar.gz
cpython-6b91a5972107ec8dd5334f4f2005626baa2b8847.tar.bz2
bpo-32385: Clean up the C3 MRO algorithm implementation. (#4942)
Use tuples and raw arrays instead of lists.
Diffstat (limited to 'Objects')
-rw-r--r--Objects/typeobject.c141
1 files changed, 64 insertions, 77 deletions
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 849c6dc..40c8fad 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -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;
-
- 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;
- }
-
- base_mro_aslist = PySequence_List(base->tp_mro);
- if (base_mro_aslist == NULL)
- goto out;
-
- PyList_SET_ITEM(to_merge, i, base_mro_aslist);
+ PyTypeObject *base = (PyTypeObject *)PyTuple_GET_ITEM(bases, i);
+ to_merge[i] = base->tp_mro;
}
+ to_merge[n] = bases;
- 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;
+ result = PyList_New(1);
+ if (result == NULL) {
+ PyMem_Del(to_merge);
+ return NULL;
}
- 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;
}