Commit fca39ba9 authored by Marius Wachtler's avatar Marius Wachtler Committed by GitHub

Merge pull request #1310 from Daetalus/list_cpython

Switch to CPython list sort(Not its list implementation)
parents 6f34e11b d7993a95
...@@ -96,6 +96,7 @@ file(GLOB_RECURSE STDOBJECT_SRCS Objects ...@@ -96,6 +96,7 @@ file(GLOB_RECURSE STDOBJECT_SRCS Objects
fileobject.c fileobject.c
import.c import.c
iterobject.c iterobject.c
listobject.c
memoryobject.c memoryobject.c
obmalloc.c obmalloc.c
sliceobject.c sliceobject.c
......
# expected: fail
from test import test_support from test import test_support
import random import random
import sys import sys
......
This diff is collapsed.
...@@ -452,17 +452,44 @@ Box* notimplementedRepr(Box* self) { ...@@ -452,17 +452,44 @@ Box* notimplementedRepr(Box* self) {
return boxString("NotImplemented"); return boxString("NotImplemented");
} }
Box* sorted(Box* obj, Box* cmp, Box* key, Box** args) { // Copied from CPython with some minor modifications
Box* reverse = args[0]; static PyObject* builtin_sorted(PyObject* self, PyObject* args, PyObject* kwds) {
PyObject* newlist, *v, *seq, * compare = NULL, * keyfunc = NULL, *newargs;
PyObject* callable;
static const char* kwlist[] = { "iterable", "cmp", "key", "reverse", 0 };
int reverse;
/* args 1-4 should match listsort in Objects/listobject.c */
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOi:sorted", const_cast<char**>(kwlist), &seq, &compare, &keyfunc,
&reverse))
return NULL;
BoxedList* rtn = new BoxedList(); newlist = PySequence_List(seq);
AUTO_DECREF(rtn); if (newlist == NULL)
for (Box* e : obj->pyElements()) { return NULL;
listAppendInternalStolen(rtn, e);
callable = PyObject_GetAttrString(newlist, "sort");
if (callable == NULL) {
Py_DECREF(newlist);
return NULL;
} }
listSort(rtn, cmp, key, reverse); newargs = PyTuple_GetSlice(args, 1, 4);
return incref(rtn); if (newargs == NULL) {
Py_DECREF(newlist);
Py_DECREF(callable);
return NULL;
}
v = PyObject_Call(callable, newargs, kwds);
Py_DECREF(newargs);
Py_DECREF(callable);
if (v == NULL) {
Py_DECREF(newlist);
return NULL;
}
Py_DECREF(v);
return newlist;
} }
Box* isinstance_func(Box* obj, Box* cls) { Box* isinstance_func(Box* obj, Box* cls) {
...@@ -2487,13 +2514,6 @@ void setupBuiltins() { ...@@ -2487,13 +2514,6 @@ void setupBuiltins() {
enumerate_cls->tp_iter = PyObject_SelfIter; enumerate_cls->tp_iter = PyObject_SelfIter;
builtins_module->giveAttrBorrowed("enumerate", enumerate_cls); builtins_module->giveAttrBorrowed("enumerate", enumerate_cls);
FunctionMetadata* sorted_func
= new FunctionMetadata(4, false, false, ParamNames({ "", "cmp", "key", "reverse" }, "", ""));
sorted_func->addVersion((void*)sorted, LIST, { UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN });
builtins_module->giveAttr("sorted", new BoxedBuiltinFunctionOrMethod(
sorted_func, "sorted", { Py_None, Py_None, Py_False }, NULL, sorted_doc));
builtins_module->giveAttrBorrowed("True", Py_True); builtins_module->giveAttrBorrowed("True", Py_True);
builtins_module->giveAttrBorrowed("False", Py_False); builtins_module->giveAttrBorrowed("False", Py_False);
...@@ -2611,6 +2631,7 @@ void setupBuiltins() { ...@@ -2611,6 +2631,7 @@ void setupBuiltins() {
{ "oct", builtin_oct, METH_O, oct_doc }, { "oct", builtin_oct, METH_O, oct_doc },
{ "print", (PyCFunction)builtin_print, METH_VARARGS | METH_KEYWORDS, print_doc }, { "print", (PyCFunction)builtin_print, METH_VARARGS | METH_KEYWORDS, print_doc },
{ "reload", builtin_reload, METH_O, reload_doc }, { "reload", builtin_reload, METH_O, reload_doc },
{ "sorted", (PyCFunction)builtin_sorted, METH_VARARGS | METH_KEYWORDS, sorted_doc },
}; };
for (auto& md : builtin_methods) { for (auto& md : builtin_methods) {
builtins_module->giveAttr(md.ml_name, new BoxedCApiFunction(&md, NULL, autoDecref(boxString("__builtin__")))); builtins_module->giveAttr(md.ml_name, new BoxedCApiFunction(&md, NULL, autoDecref(boxString("__builtin__"))));
......
...@@ -131,15 +131,15 @@ const int BoxedList::INITIAL_CAPACITY = 8; ...@@ -131,15 +131,15 @@ const int BoxedList::INITIAL_CAPACITY = 8;
// TODO the inliner doesn't want to inline these; is there any point to having them in the inline section? // TODO the inliner doesn't want to inline these; is there any point to having them in the inline section?
void BoxedList::shrink() { void BoxedList::shrink() {
// TODO more attention to the shrink condition to avoid frequent shrink and alloc // TODO more attention to the shrink condition to avoid frequent shrink and alloc
if (capacity > size * 3) { if (allocated > size * 3) {
int new_capacity = std::max(static_cast<int64_t>(INITIAL_CAPACITY), capacity / 2); int new_allocated = std::max(static_cast<int64_t>(INITIAL_CAPACITY), allocated / 2);
if (size > 0) { if (size > 0) {
elts = GCdArray::grow(elts, new_capacity); elts = GCdArray::grow(elts, new_allocated);
capacity = new_capacity; allocated = new_allocated;
} else if (size == 0) { } else if (size == 0) {
delete elts; delete elts;
elts = NULL; elts = NULL;
capacity = 0; allocated = 0;
} }
} }
} }
...@@ -149,14 +149,14 @@ extern "C" void listAppendArrayInternal(Box* s, Box** v, int nelts) { ...@@ -149,14 +149,14 @@ extern "C" void listAppendArrayInternal(Box* s, Box** v, int nelts) {
assert(PyList_Check(s)); assert(PyList_Check(s));
BoxedList* self = static_cast<BoxedList*>(s); BoxedList* self = static_cast<BoxedList*>(s);
assert(self->size <= self->capacity); assert(self->size <= self->allocated);
self->ensure(nelts); self->ensure(nelts);
for (int i = 0; i < nelts; i++) { for (int i = 0; i < nelts; i++) {
Py_INCREF(v[i]); Py_INCREF(v[i]);
} }
assert(self->size <= self->capacity); assert(self->size <= self->allocated);
memcpy(&self->elts->elts[self->size], &v[0], nelts * sizeof(Box*)); memcpy(&self->elts->elts[self->size], &v[0], nelts * sizeof(Box*));
self->size += nelts; self->size += nelts;
......
...@@ -22,23 +22,23 @@ namespace pyston { ...@@ -22,23 +22,23 @@ namespace pyston {
// TODO the inliner doesn't want to inline these; is there any point to having them in the inline section? // TODO the inliner doesn't want to inline these; is there any point to having them in the inline section?
inline void BoxedList::grow(int min_free) { inline void BoxedList::grow(int min_free) {
if (capacity == 0) { if (allocated == 0) {
const int INITIAL_CAPACITY = 8; const int INITIAL_CAPACITY = 8;
int initial = std::max(INITIAL_CAPACITY, min_free); int initial = std::max(INITIAL_CAPACITY, min_free);
elts = new (initial) GCdArray(); elts = new (initial) GCdArray();
capacity = initial; allocated = initial;
} else { } else {
int new_capacity = std::max(capacity * 2, size + min_free); int new_allocated = std::max(allocated * 2, size + min_free);
elts = GCdArray::grow(elts, new_capacity); elts = GCdArray::grow(elts, new_allocated);
capacity = new_capacity; allocated = new_allocated;
} }
} }
inline void BoxedList::ensure(int min_free) { inline void BoxedList::ensure(int min_free) {
if (unlikely(size + min_free > capacity)) { if (unlikely(size + min_free > allocated)) {
grow(min_free); grow(min_free);
} }
assert(capacity >= size + min_free); assert(allocated >= size + min_free);
} }
// TODO the inliner doesn't want to inline these; is there any point to having them in the inline section? // TODO the inliner doesn't want to inline these; is there any point to having them in the inline section?
...@@ -48,10 +48,10 @@ extern "C" inline void listAppendInternalStolen(Box* s, Box* v) { ...@@ -48,10 +48,10 @@ extern "C" inline void listAppendInternalStolen(Box* s, Box* v) {
assert(PyList_Check(s)); assert(PyList_Check(s));
BoxedList* self = static_cast<BoxedList*>(s); BoxedList* self = static_cast<BoxedList*>(s);
assert(self->size <= self->capacity); assert(self->size <= self->allocated || self->allocated == -1);
self->ensure(1); self->ensure(1);
assert(self->size < self->capacity); assert(self->size < self->allocated);
self->elts->elts[self->size] = v; self->elts->elts[self->size] = v;
self->size++; self->size++;
} }
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <algorithm> #include <algorithm>
#include <cstring> #include <cstring>
#include "capi/typeobject.h"
#include "capi/types.h" #include "capi/types.h"
#include "core/ast.h" #include "core/ast.h"
#include "core/common.h" #include "core/common.h"
...@@ -27,6 +28,8 @@ ...@@ -27,6 +28,8 @@
#include "runtime/types.h" #include "runtime/types.h"
#include "runtime/util.h" #include "runtime/util.h"
extern "C" PyObject* listsort(PyListObject* self, PyObject* args, PyObject* kwds) noexcept;
namespace pyston { namespace pyston {
static int list_ass_slice(PyListObject* a, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject* v); static int list_ass_slice(PyListObject* a, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject* v);
...@@ -991,64 +994,6 @@ void _sortArray(Box** elts, long num_elts, Box* cmp, Box* key) { ...@@ -991,64 +994,6 @@ void _sortArray(Box** elts, long num_elts, Box* cmp, Box* key) {
} }
} }
void listSort(BoxedList* self, Box* cmp, Box* key, Box* reverse) {
assert(PyList_Check(self));
if (cmp == Py_None)
cmp = NULL;
if (key == Py_None)
key = NULL;
RELEASE_ASSERT(!cmp || !key, "Specifying both the 'cmp' and 'key' keywords is currently not supported");
auto orig_size = self->size;
auto orig_elts = self->elts;
self->elts = new (0) GCdArray();
self->size = 0;
try {
_sortArray(orig_elts->elts, orig_size, cmp, key);
} catch (ExcInfo e) {
delete self->elts;
self->elts = orig_elts;
self->size = orig_size;
throw e;
}
delete self->elts;
self->elts = orig_elts;
self->size = orig_size;
if (nonzero(reverse)) {
Box* r = listReverse(self);
Py_DECREF(r);
}
}
Box* listSortFunc(BoxedList* self, Box* cmp, Box* key, Box** _args) {
Box* reverse = _args[0];
listSort(self, cmp, key, reverse);
Py_RETURN_NONE;
}
extern "C" int PyList_Sort(PyObject* v) noexcept {
if (v == NULL || !PyList_Check(v)) {
PyErr_BadInternalCall();
return -1;
}
try {
listSort((BoxedList*)v, Py_None, Py_None, Py_False);
} catch (ExcInfo e) {
setCAPIException(e);
return -1;
}
return 0;
}
extern "C" Box* PyList_GetSlice(PyObject* a, Py_ssize_t ilow, Py_ssize_t ihigh) noexcept { extern "C" Box* PyList_GetSlice(PyObject* a, Py_ssize_t ilow, Py_ssize_t ihigh) noexcept {
assert(PyList_Check(a)); assert(PyList_Check(a));
BoxedList* self = static_cast<BoxedList*>(a); BoxedList* self = static_cast<BoxedList*>(a);
...@@ -1481,7 +1426,7 @@ int BoxedList::clear(Box* _a) noexcept { ...@@ -1481,7 +1426,7 @@ int BoxedList::clear(Box* _a) noexcept {
this list, we make it empty first. */ this list, we make it empty first. */
i = Py_SIZE(a); i = Py_SIZE(a);
Py_SIZE(a) = 0; Py_SIZE(a) = 0;
a->capacity = 0; a->allocated = 0;
auto old_elts = a->elts; auto old_elts = a->elts;
a->elts = NULL; a->elts = NULL;
while (--i >= 0) { while (--i >= 0) {
...@@ -1495,6 +1440,12 @@ int BoxedList::clear(Box* _a) noexcept { ...@@ -1495,6 +1440,12 @@ int BoxedList::clear(Box* _a) noexcept {
return 0; return 0;
} }
PyDoc_STRVAR(sort_doc, "L.sort(cmp=None, key=None, reverse=False) -- stable sort *IN PLACE*;\n\
cmp(x, y) -> -1, 0, 1");
static PyMethodDef list_methods[]
= { { "sort", (PyCFunction)listsort, METH_VARARGS | METH_KEYWORDS, sort_doc }, { NULL, NULL, 0, NULL } };
void setupList() { void setupList() {
static PySequenceMethods list_as_sequence; static PySequenceMethods list_as_sequence;
list_cls->tp_as_sequence = &list_as_sequence; list_cls->tp_as_sequence = &list_as_sequence;
...@@ -1568,10 +1519,6 @@ void setupList() { ...@@ -1568,10 +1519,6 @@ void setupList() {
list_cls->giveAttr("__iadd__", new BoxedFunction(FunctionMetadata::create((void*)listIAdd, UNKNOWN, 2))); list_cls->giveAttr("__iadd__", new BoxedFunction(FunctionMetadata::create((void*)listIAdd, UNKNOWN, 2)));
list_cls->giveAttr("__add__", new BoxedFunction(FunctionMetadata::create((void*)listAdd, UNKNOWN, 2))); list_cls->giveAttr("__add__", new BoxedFunction(FunctionMetadata::create((void*)listAdd, UNKNOWN, 2)));
list_cls->giveAttr("sort",
new BoxedFunction(FunctionMetadata::create((void*)listSortFunc, NONE, 4, false, false,
ParamNames({ "", "cmp", "key", "reverse" }, "", "")),
{ Py_None, Py_None, Py_False }));
list_cls->giveAttr("__contains__", new BoxedFunction(FunctionMetadata::create((void*)listContains, BOXED_BOOL, 2))); list_cls->giveAttr("__contains__", new BoxedFunction(FunctionMetadata::create((void*)listContains, BOXED_BOOL, 2)));
list_cls->giveAttr( list_cls->giveAttr(
...@@ -1585,6 +1532,7 @@ void setupList() { ...@@ -1585,6 +1532,7 @@ void setupList() {
list_cls->giveAttr("reverse", new BoxedFunction(FunctionMetadata::create((void*)listReverse, NONE, 1))); list_cls->giveAttr("reverse", new BoxedFunction(FunctionMetadata::create((void*)listReverse, NONE, 1)));
list_cls->giveAttrBorrowed("__hash__", Py_None); list_cls->giveAttrBorrowed("__hash__", Py_None);
add_methods(list_cls, list_methods);
list_cls->freeze(); list_cls->freeze();
list_cls->tp_iter = listIter; list_cls->tp_iter = listIter;
list_cls->tp_repr = list_repr; list_cls->tp_repr = list_repr;
......
...@@ -51,7 +51,6 @@ Box* listreviterHasnext(Box* self); ...@@ -51,7 +51,6 @@ Box* listreviterHasnext(Box* self);
llvm_compat_bool listreviterHasnextUnboxed(Box* self); llvm_compat_bool listreviterHasnextUnboxed(Box* self);
Box* listreviterNext(Box* self); Box* listreviterNext(Box* self);
Box* listreviter_next(Box* s) noexcept; Box* listreviter_next(Box* s) noexcept;
void listSort(BoxedList* self, Box* cmp, Box* key, Box* reverse);
extern "C" Box* listAppend(Box* self, Box* v); extern "C" Box* listAppend(Box* self, Box* v);
} }
......
...@@ -718,9 +718,9 @@ private: ...@@ -718,9 +718,9 @@ private:
public: public:
Py_ssize_t size; Py_ssize_t size;
GCdArray* elts; GCdArray* elts;
Py_ssize_t capacity; Py_ssize_t allocated;
BoxedList() __attribute__((visibility("default"))) : size(0), elts(NULL), capacity(0) {} BoxedList() __attribute__((visibility("default"))) : size(0), elts(NULL), allocated(0) {}
void ensure(int min_free); void ensure(int min_free);
void shrink(); void shrink();
...@@ -737,7 +737,7 @@ static_assert(sizeof(BoxedList) >= sizeof(PyListObject), ""); ...@@ -737,7 +737,7 @@ static_assert(sizeof(BoxedList) >= sizeof(PyListObject), "");
static_assert(offsetof(BoxedList, size) == offsetof(PyListObject, ob_size), ""); static_assert(offsetof(BoxedList, size) == offsetof(PyListObject, ob_size), "");
static_assert(offsetof(BoxedList, elts) == offsetof(PyListObject, ob_item), ""); static_assert(offsetof(BoxedList, elts) == offsetof(PyListObject, ob_item), "");
static_assert(offsetof(GCdArray, elts) == 0, ""); static_assert(offsetof(GCdArray, elts) == 0, "");
static_assert(offsetof(BoxedList, capacity) == offsetof(PyListObject, allocated), ""); static_assert(offsetof(BoxedList, allocated) == offsetof(PyListObject, allocated), "");
#define PyTuple_MAXSAVESIZE 20 /* Largest tuple to save on free list */ #define PyTuple_MAXSAVESIZE 20 /* Largest tuple to save on free list */
#define PyTuple_MAXFREELIST 2000 /* Maximum number of tuples of each size to save */ #define PyTuple_MAXFREELIST 2000 /* Maximum number of tuples of each size to save */
......
...@@ -144,7 +144,6 @@ test_scope eval of code object from existing function (not currentl ...@@ -144,7 +144,6 @@ test_scope eval of code object from existing function (not currentl
test_scriptpackages [unknown] test_scriptpackages [unknown]
test_site [unknown] test_site [unknown]
test_socket [unknown] test_socket [unknown]
test_sort argument specification issue in listSort?
test_startfile [unknown] test_startfile [unknown]
test_str memory leak? test_str memory leak?
test_structmembers [unknown] test_structmembers [unknown]
......
...@@ -50,10 +50,10 @@ set_name(f, "b\0b") ...@@ -50,10 +50,10 @@ set_name(f, "b\0b")
#del_name(f) #del_name(f)
set_name(f, "") set_name(f, "")
print sorted.__name__ print cmp.__name__
# should all fail: # should all fail:
set_name(sorted, "blah") set_name(cmp, "blah")
set_name(sorted, 5) set_name(cmp, 5)
import abc import abc
class D(C): class D(C):
......
...@@ -279,3 +279,7 @@ try: ...@@ -279,3 +279,7 @@ try:
a.index(None) a.index(None)
except ValueError as e: except ValueError as e:
print e print e
a_list = [(0, 0, 0), (1.0, 2, 0.0), (1.0, 3, 0.0)]
reverse_list = sorted(a_list, key=lambda x: x[0], reverse=True)
print(reverse_list)
# expected: fail
# - we don't support the cmp= keyword yet
# (apparently it's removed in Python 3)
printed = False printed = False
def mycmp(x, y): def mycmp(x, y):
global printed global printed
......
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