Commit 0921f462 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Start moving towards the way CPython implements slots + wrappers

- Start moving towards slotdefs
- add tp_repr support
- add an slots_tester extension module so we can test these more narrowly
parent d34a8e95
......@@ -928,7 +928,7 @@ test_cpp_ll:
less test.ll
rm test.ll
TEST_EXT_MODULE_NAMES := basic_test descr_test
TEST_EXT_MODULE_NAMES := basic_test descr_test slots_test
.PHONY: ext_pyston
ext_pyston: $(TEST_EXT_MODULE_NAMES:%=$(TEST_DIR)/test_extension/%.pyston.so)
......
......@@ -27,6 +27,31 @@ extern "C" void conservativeGCHandler(GCVisitor* v, Box* b) {
v->visitPotentialRange((void* const*)b, (void* const*)((char*)b + b->cls->tp_basicsize));
}
static int check_num_args(PyObject* ob, int n) {
if (!PyTuple_CheckExact(ob)) {
PyErr_SetString(PyExc_SystemError, "PyArg_UnpackTuple() argument list is not a tuple");
return 0;
}
if (n == PyTuple_GET_SIZE(ob))
return 1;
PyErr_Format(PyExc_TypeError, "expected %d arguments, got %zd", n, PyTuple_GET_SIZE(ob));
return 0;
}
static PyObject* wrap_call(PyObject* self, PyObject* args, void* wrapped, PyObject* kwds) {
ternaryfunc func = (ternaryfunc)wrapped;
return (*func)(self, args, kwds);
}
static PyObject* wrap_unaryfunc(PyObject* self, PyObject* args, void* wrapped) {
unaryfunc func = (unaryfunc)wrapped;
if (!check_num_args(args, 0))
return NULL;
return (*func)(self);
}
PyObject* Py_CallPythonNew(PyTypeObject* self, PyObject* args, PyObject* kwds) {
try {
Py_FatalError("this function is untested");
......@@ -53,6 +78,13 @@ PyObject* Py_CallPythonCall(PyObject* self, PyObject* args, PyObject* kwds) {
}
}
PyObject* Py_CallPythonRepr(PyObject* self) {
try {
Py_FatalError("unimplemented");
} catch (Box* e) {
abort();
}
}
bool update_slot(BoxedClass* self, const std::string& attr) {
if (attr == "__new__") {
......@@ -64,6 +96,14 @@ bool update_slot(BoxedClass* self, const std::string& attr) {
if (attr == "__call__") {
self->tp_call = &Py_CallPythonCall;
// TODO update subclasses
return true;
}
if (attr == "__repr__") {
self->tp_repr = &Py_CallPythonRepr;
// TODO update subclasses
return true;
}
......@@ -83,9 +123,19 @@ void fixup_slot_dispatchers(BoxedClass* self) {
} else if (self->tp_call != Py_CallPythonCall) {
ASSERT(0, "need to set __call__?");
}
if (!self->tp_repr) {
self->tp_repr = &PyObject_Repr;
} else if (self->tp_repr != Py_CallPythonRepr) {
ASSERT(0, "need to set __repr__?");
}
}
wrapper_def call_wrapper = { "__call__", offsetof(PyTypeObject, tp_call), PyWrapperFlag_KEYWORDS };
wrapper_def call_wrapper = { "__call__", offsetof(PyTypeObject, tp_call), (void*)&Py_CallPythonCall,
(wrapperfunc)wrap_call, PyWrapperFlag_KEYWORDS };
wrapper_def repr_wrapper
= { "__repr__", offsetof(PyTypeObject, tp_repr), (void*)&Py_CallPythonRepr, wrap_unaryfunc, 0 };
PyObject* tp_new_wrapper(PyTypeObject* self, BoxedTuple* args, Box* kwds) {
RELEASE_ASSERT(isSubclass(self->cls, type_cls), "");
......@@ -105,6 +155,7 @@ PyObject* tp_new_wrapper(PyTypeObject* self, BoxedTuple* args, Box* kwds) {
return self->tp_new(subtype, new_args, kwds);
}
static void add_operators(PyTypeObject* cls) {
if (cls->tp_new) {
cls->giveAttr("__new__",
......@@ -114,10 +165,14 @@ static void add_operators(PyTypeObject* cls) {
if (cls->tp_call) {
cls->giveAttr("__call__", new BoxedWrapperDescriptor(&call_wrapper, cls));
}
if (cls->tp_repr) {
cls->giveAttr("__repr__", new BoxedWrapperDescriptor(&repr_wrapper, cls));
}
}
extern "C" int PyType_IsSubtype(PyTypeObject*, PyTypeObject*) {
Py_FatalError("unimplemented");
extern "C" int PyType_IsSubtype(PyTypeObject* a, PyTypeObject* b) {
return isSubclass(a, b);
}
extern "C" int PyType_Ready(PyTypeObject* cls) {
......@@ -128,7 +183,6 @@ extern "C" int PyType_Ready(PyTypeObject* cls) {
RELEASE_ASSERT(cls->tp_getattr == NULL, "");
RELEASE_ASSERT(cls->tp_setattr == NULL, "");
RELEASE_ASSERT(cls->tp_compare == NULL, "");
RELEASE_ASSERT(cls->tp_repr == NULL, "");
RELEASE_ASSERT(cls->tp_as_number == NULL, "");
RELEASE_ASSERT(cls->tp_as_sequence == NULL, "");
RELEASE_ASSERT(cls->tp_as_mapping == NULL, "");
......@@ -140,14 +194,11 @@ extern "C" int PyType_Ready(PyTypeObject* cls) {
int ALLOWABLE_FLAGS = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC;
RELEASE_ASSERT((cls->tp_flags & ~ALLOWABLE_FLAGS) == 0, "");
// RELEASE_ASSERT(cls->tp_traverse == NULL, "");
// RELEASE_ASSERT(cls->tp_clear == NULL, "");
RELEASE_ASSERT(cls->tp_richcompare == NULL, "");
RELEASE_ASSERT(cls->tp_iter == NULL, "");
RELEASE_ASSERT(cls->tp_iternext == NULL, "");
RELEASE_ASSERT(cls->tp_base == NULL, "");
RELEASE_ASSERT(cls->tp_dict == NULL, "");
RELEASE_ASSERT(cls->tp_descr_get == NULL, "");
RELEASE_ASSERT(cls->tp_descr_set == NULL, "");
RELEASE_ASSERT(cls->tp_init == NULL, "");
......@@ -162,8 +213,10 @@ extern "C" int PyType_Ready(PyTypeObject* cls) {
RELEASE_ASSERT(cls->tp_del == NULL, "");
RELEASE_ASSERT(cls->tp_version_tag == 0, "");
// I think it is safe to ignore tp_weaklistoffset for now:
// I think it is safe to ignore these for for now:
// RELEASE_ASSERT(cls->tp_weaklistoffset == 0, "");
// RELEASE_ASSERT(cls->tp_traverse == NULL, "");
// RELEASE_ASSERT(cls->tp_clear == NULL, "");
#define INITIALIZE(a) new (&(a)) decltype(a)
INITIALIZE(cls->attrs);
......@@ -174,6 +227,9 @@ extern "C" int PyType_Ready(PyTypeObject* cls) {
if (!cls->cls)
cls->cls = cls->base->cls;
assert(cls->tp_dict == NULL);
cls->tp_dict = makeAttrWrapper(cls);
assert(cls->tp_name);
cls->giveAttr("__name__", boxStrConstant(cls->tp_name));
// tp_name
......
......@@ -19,13 +19,19 @@
namespace pyston {
typedef PyObject* (*wrapperfunc)(PyObject* self, PyObject* args, void* wrapped);
typedef PyObject* (*wrapperfunc_kwds)(PyObject* self, PyObject* args, void* wrapped, PyObject* kwds);
struct wrapper_def {
const char* name;
int offset;
void* function; // "generic" handler that gets put in the tp_* slot which proxies to the python version
wrapperfunc wrapper; // "wrapper" that ends up getting called by the Python-visible WrapperDescr
int flags;
};
extern BoxedClass* capifunc_cls, *wrapperdescr_cls, *wrapperobject_cls;
class BoxedCApiFunction : public Box {
private:
int ml_flags;
......@@ -94,13 +100,16 @@ public:
assert(kwds->cls == dict_cls);
int flags = self->descr->wrapper->flags;
wrapperfunc wrapper = self->descr->wrapper->wrapper;
assert(self->descr->wrapper->offset > 0);
char* ptr = (char*)self->descr->type + self->descr->wrapper->offset;
void* wrapped = *reinterpret_cast<void**>(ptr);
if (flags & PyWrapperFlag_KEYWORDS) {
PyCFunctionWithKeywords f = *(PyCFunctionWithKeywords*)ptr;
return f(self->obj, args, kwds);
wrapperfunc_kwds wk = (wrapperfunc_kwds)wrapper;
return (*wk)(self->obj, args, wrapped, kwds);
} else {
abort();
return (*wrapper)(self->obj, args, wrapped);
}
abort();
}
......
......@@ -143,12 +143,12 @@ static Box* importSub(const std::string& name, const std::string& full_name, Box
}
}
if (name == "basic_test") {
if (name == "basic_test")
return importTestExtension("basic_test");
}
if (name == "descr_test") {
if (name == "descr_test")
return importTestExtension("descr_test");
}
if (name == "slots_test")
return importTestExtension("slots_test");
return NULL;
}
......
......@@ -6,5 +6,6 @@ setup(name="test",
ext_modules=[
Extension("basic_test", sources = ["basic_test.c"]),
Extension("descr_test", sources = ["descr_test.c"]),
Extension("slots_test", sources = ["slots_test.c"]),
],
)
#include <Python.h>
typedef struct {
PyObject_HEAD
int n;
} slots_tester_object;
static PyObject *
slots_tester_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
slots_tester_object* obj;
if (!_PyArg_NoKeywords("attrgetter()", kwds))
return NULL;
int n;
if (!PyArg_ParseTuple(args, "n", &n))
return NULL;
/* create attrgetterobject structure */
obj = PyObject_New(slots_tester_object, type);
if (obj == NULL)
return NULL;
obj->n = n;
return (PyObject *)obj;
}
static PyObject *
slots_tester_repr(slots_tester_object *obj)
{
char buf[80];
snprintf(buf, sizeof(buf), "<my custom repr: %d>", obj->n);
return PyString_FromString(buf);
}
static PyObject *
slots_tester_call(slots_tester_object *obj, PyObject *args, PyObject *kw)
{
if (!PyArg_ParseTuple(args, ""))
return NULL;
return PyInt_FromLong(obj->n);
}
PyDoc_STRVAR(slots_tester_doc, "slots_tester doc");
static PyTypeObject slots_tester = {
PyVarObject_HEAD_INIT(NULL, 0)
"slots_test.slots_tester", /* tp_name */
sizeof(slots_tester_object), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
(reprfunc)slots_tester_repr, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
(ternaryfunc)slots_tester_call, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
slots_tester_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
slots_tester_new, /* tp_new */
0, /* tp_free */
};
static PyMethodDef SlotsMethods[] = {
{NULL, NULL, 0, NULL} /* Sentinel */
};
PyMODINIT_FUNC
initslots_test(void)
{
PyObject *m;
m = Py_InitModule("slots_test", SlotsMethods);
if (m == NULL)
return;
int res = PyType_Ready(&slots_tester);
if (res < 0)
return;
// Not sure if the result of PyInt_FromLong needs to be decref'd
PyDict_SetItemString(slots_tester.tp_dict, "set_through_tpdict", PyInt_FromLong(123));
PyModule_AddObject(m, "SlotsTester", (PyObject *)&slots_tester);
}
import slots_test
for i in xrange(3):
t = slots_test.SlotsTester(i + 5)
print t, repr(t), t()
print slots_test.SlotsTester.set_through_tpdict, slots_test.SlotsTester(5).set_through_tpdict
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