[pypy-commit] creflect default: Calling function pointer cdata objects. Works only for types that have
arigo
noreply at buildbot.pypy.org
Sat Dec 6 19:34:38 CET 2014
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r188:13d44f67603a
Date: 2014-12-06 19:34 +0100
http://bitbucket.org/cffi/creflect/changeset/13d44f67603a/
Log: Calling function pointer cdata objects. Works only for types that
have been declared in the .crx, but without any libffi dependency.
diff --git a/zeffir/builder.c b/zeffir/builder.c
--- a/zeffir/builder.c
+++ b/zeffir/builder.c
@@ -160,6 +160,7 @@
static _crx_type_t *_zef_function(_crx_builder_t *cb, _crx_type_t *ret,
_crx_qual_type args[], int nargs,
+ _crx_trampoline1_fn trampl,
int dotdotdot)
{
if (PyErr_Occurred())
@@ -223,12 +224,16 @@
ct->ct_size = -1;
ct->ct_flags = CT_FUNCTION;
- /* XXX more... */
put_cached_type(get_types_dict(cb), name_obj, ct);
done:
Py_DECREF(name_obj);
+
+ if (ct->ct_stuff == NULL && trampl != NULL && !PyErr_Occurred()) {
+ assert(!dotdotdot); /* should have 'trampl == NULL' in this case */
+ ct->ct_stuff = make_func_support(ret, args, nargs, trampl, 0);
+ }
return ct;
}
@@ -236,7 +241,7 @@
_crx_qual_type args[], int nargs,
_crx_trampoline1_fn trampl)
{
- return _zef_function(cb, ret, args, nargs, 0);
+ return _zef_function(cb, ret, args, nargs, trampl, 0);
}
static _crx_type_t *zef_get_ellipsis_function_type(_crx_builder_t *cb,
@@ -244,7 +249,7 @@
_crx_qual_type args[],
int nargs)
{
- return _zef_function(cb, ret, args, nargs, 1);
+ return _zef_function(cb, ret, args, nargs, NULL, 1);
}
static CTypeDescrObject *fetch_pointer_type(PyObject *types_dict,
@@ -517,7 +522,8 @@
if (interned_fields == NULL)
return;
- previous = (CFieldObject **)&ct->ct_extra;
+ assert(ct->ct_fields == NULL);
+ previous = &ct->ct_fields;
for (i = 0; i < nfields; i++) {
_crx_field_t *f = &fields[i];
diff --git a/zeffir/cdata.c b/zeffir/cdata.c
--- a/zeffir/cdata.c
+++ b/zeffir/cdata.c
@@ -638,7 +638,7 @@
if (PyList_Check(init) || PyTuple_Check(init)) {
PyObject **items = PySequence_Fast_ITEMS(init);
Py_ssize_t i, n = PySequence_Fast_GET_SIZE(init);
- CFieldObject *cf = (CFieldObject *)ct->ct_extra;
+ CFieldObject *cf = ct->ct_fields;
for (i=0; i<n; i++) {
if (cf == NULL) {
@@ -1611,7 +1611,7 @@
0, /* tp_as_sequence */
&CData_as_mapping, /* tp_as_mapping */
(hashfunc)cdata_hash, /* tp_hash */
- 0,//(ternaryfunc)cdata_call, /* tp_call */
+ (ternaryfunc)cdata_call, /* tp_call */
0, /* tp_str */
(getattrofunc)cdata_getattro, /* tp_getattro */
(setattrofunc)cdata_setattro, /* tp_setattro */
diff --git a/zeffir/cfunc.c b/zeffir/cfunc.c
--- a/zeffir/cfunc.c
+++ b/zeffir/cfunc.c
@@ -3,7 +3,7 @@
PyObject_HEAD
int zfs_nargs;
- _crx_trampoline0_fn zfs_trampl;
+ void *zfs_trampl;
PyMethodDef zfs_md;
size_t zfs_size_args;
CTypeDescrObject *zfs_ret;
@@ -109,9 +109,9 @@
return convert_from_object((char *)output_data, ctptr, init);
}
-static PyObject *zfs_call(PyObject *self, PyObject *args)
+static PyObject *common_call(ZefFuncSupportObject *zfs, PyObject *args,
+ void *func, const char *funcname)
{
- ZefFuncSupportObject *zfs = (ZefFuncSupportObject *)self;
int i, nargs = zfs->zfs_nargs;
Py_ssize_t actualnargs;
void *llargs[nargs];
@@ -123,8 +123,8 @@
if (actualnargs != nargs) {
if (!PyErr_Occurred())
PyErr_Format(PyExc_TypeError,
- "'%s()' expected %d arguments, but got %zd",
- zfs->zfs_md.ml_name, nargs, actualnargs);
+ "'%s' expected %d arguments, but got %zd",
+ funcname, nargs, actualnargs);
return NULL;
}
@@ -165,7 +165,14 @@
lloffset += ct->ct_size;
}
- zfs->zfs_trampl(llargs, llbuffer);
+ if (func == NULL) {
+ assert(zfs->zfs_md.ml_name != NULL);
+ ((_crx_trampoline0_fn)zfs->zfs_trampl)(llargs, llbuffer);
+ }
+ else {
+ assert(zfs->zfs_md.ml_name == NULL);
+ ((_crx_trampoline1_fn)zfs->zfs_trampl)(func, llargs, llbuffer);
+ }
if (returns_void) {
Py_INCREF(Py_None);
@@ -174,21 +181,61 @@
return convert_to_object(llbuffer, zfs->zfs_ret);
}
-static PyObject *make_builtin_func(PyObject *libname_obj,
- const char *funcname, _crx_type_t *ret,
+static PyObject *zfs_call(PyObject *self, PyObject *args)
+{
+ ZefFuncSupportObject *zfs = (ZefFuncSupportObject *)self;
+ return common_call(zfs, args, NULL, zfs->zfs_md.ml_name);
+}
+
+static PyObject*cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds)
+{
+ CTypeDescrObject *ct = cd->c_type->ct_itemdescr;
+ ZefFuncSupportObject *zfs;
+
+ if (!(cd->c_type->ct_flags & CT_POINTER) || !(ct->ct_flags & CT_FUNCTION)) {
+ PyErr_Format(PyExc_TypeError, "cdata '%s' is not callable",
+ cd->c_type->ct_name);
+ return NULL;
+ }
+
+ zfs = (ZefFuncSupportObject *)ct->ct_stuff;
+ if (zfs == NULL) {
+ PyErr_SetString(ZefError, "cdata '%s' cannot be called, because no "
+ "wrapper was generated for functions of this type ");
+ return NULL;
+ }
+
+ if (kwds != NULL && PyDict_Size(kwds) != 0) {
+ PyErr_SetString(PyExc_TypeError,
+ "cdata function pointers cannot be called with keyword arguments");
+ return NULL;
+ }
+ if (cd->c_data == NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "cannot call NULL cdata");
+ return NULL;
+ }
+
+ return common_call(zfs, args, cd->c_data, cd->c_type->ct_name);
+}
+
+static PyObject *make_func_support(_crx_type_t *ret,
_crx_qual_type args[], int nargs,
- _crx_trampoline0_fn trampl)
+ void *trampl, size_t extra_alloc)
{
+ /* note that 'trampl' is either a '_crx_trampoline0_fn' or a
+ '_crx_trampoline1_fn', depending on how the ZefFuncSupportObject
+ is going to be used.
+ */
int i;
- char *p;
size_t size = (sizeof(ZefFuncSupportObject)
+ (nargs - 1) * sizeof(CTypeDescrObject *)
- + strlen(funcname) + 1);
+ + extra_alloc);
ZefFuncSupportObject *zfs = (ZefFuncSupportObject *)PyObject_Malloc(size);
if (zfs == NULL)
return PyErr_NoMemory();
PyObject_Init((PyObject *)zfs, &ZefFuncSupport_Type);
+ memset(&zfs->zfs_md, 0, sizeof(PyMethodDef));
zfs->zfs_nargs = nargs;
zfs->zfs_trampl = trampl;
zfs->zfs_ret = ret;
@@ -207,9 +254,23 @@
size = ROUND_UP(size) + ct->ct_size;
}
zfs->zfs_size_args = size;
+
+ return (PyObject *)zfs;
+}
+
+static PyObject *make_builtin_func(PyObject *libname_obj,
+ const char *funcname, _crx_type_t *ret,
+ _crx_qual_type args[], int nargs,
+ _crx_trampoline0_fn trampl)
+{
+ char *p;
+ ZefFuncSupportObject *zfs;
+ zfs = (ZefFuncSupportObject *)make_func_support(ret, args, nargs, trampl,
+ strlen(funcname) + 1);
+ if (zfs == NULL)
+ return NULL;
+
p = (char *)(zfs->zfs_args + nargs);
-
- memset(&zfs->zfs_md, 0, sizeof(PyMethodDef));
zfs->zfs_md.ml_name = strcpy(p, funcname);
zfs->zfs_md.ml_meth = &zfs_call;
zfs->zfs_md.ml_flags = METH_VARARGS;
diff --git a/zeffir/ctype.c b/zeffir/ctype.c
--- a/zeffir/ctype.c
+++ b/zeffir/ctype.c
@@ -31,11 +31,10 @@
CTypeDescrObject *ct_itemdescr; /* ptrs and arrays: the item type */
PyObject *ct_stuff; /* structs: dict of the fields
arrays: ctypedescr of the ptr type
- function: tuple(abi, ctres, ctargs..)
+ functions: ZefFuncSupportObject
enum: pair {"name":x},{x:"name"}
ptrs: lazily, ctypedescr of array */
- void *ct_extra; /* structs: first field (not a ref!)
- function types: cif_description */
+ CFieldObject *ct_fields; /* structs: first field (not a ref!) */
PyObject *ct_weakreflist; /* weakref support */
diff --git a/zeffir/ffi_obj.c b/zeffir/ffi_obj.c
--- a/zeffir/ffi_obj.c
+++ b/zeffir/ffi_obj.c
@@ -177,7 +177,14 @@
static PyObject *ffi_typeof(ZefFFIObject *self, PyObject *arg)
{
PyObject *x = (PyObject *)_ffi_type(self, arg, ACCEPT_STRING|ACCEPT_CDATA);
- Py_XINCREF(x);
+ if (x != NULL) {
+ Py_INCREF(x);
+ }
+ else if (PyCFunction_Check(arg)) {
+ PyErr_SetString(PyExc_TypeError, "typeof(lib.func) not supported: the "
+ "exact type of functions is unknown (declare that "
+ "function as a function pointer instead)");
+ }
return x;
}
@@ -446,7 +453,7 @@
else
reason = "numeric constants don't have addresses";
- PyErr_Format(PyExc_NotImplementedError,
+ PyErr_Format(PyExc_TypeError,
"cannot take the address of '%s' (%s)",
fieldname, reason);
return NULL;
diff --git a/zeffir/test/funcptr.crx b/zeffir/test/funcptr.crx
new file mode 100644
--- /dev/null
+++ b/zeffir/test/funcptr.crx
@@ -0,0 +1,24 @@
+int add_1(int x)
+{
+ return x + 1;
+}
+
+int add_42(int x)
+{
+ return x + 42;
+}
+
+int (*get_fnptr(int target))(int)
+{
+ if (target == 42)
+ return add_42;
+ else
+ return add_1;
+}
+
+
+// CREFLECT: start
+
+int (*get_fnptr(int target))(int);
+
+// CREFLECT: end
diff --git a/zeffir/test/test_funcptr.py b/zeffir/test/test_funcptr.py
new file mode 100644
--- /dev/null
+++ b/zeffir/test/test_funcptr.py
@@ -0,0 +1,31 @@
+import py
+import support
+
+
+def test_cannot_call_nonfunc():
+ ffi = support.new_ffi()
+ x = ffi.new("int *", 42)
+ py.test.raises(TypeError, x)
+
+def test_cannot_call_unprepared_func_type():
+ ffi = support.new_ffi()
+ x = ffi.cast("int(*)(int)", 42)
+ py.test.raises(ffi.error, x)
+
+def test_get_fnptr():
+ ffi, lib = support.compile_and_open('funcptr')
+ fn = lib.get_fnptr(42)
+ assert fn(100) == 142
+ assert ffi.typeof(fn) == ffi.typeof("int(*)(int)")
+ #
+ e = py.test.raises(TypeError, fn, 100, 200)
+ assert str(e.value) == "'int(*)(int)' expected 1 arguments, but got 2"
+ e = py.test.raises(TypeError, fn, foobar=42)
+ assert str(e.value) == ("cdata function pointers cannot be called "
+ "with keyword arguments")
+
+def test_cannot_call_null():
+ ffi, lib = support.compile_and_open('funcptr')
+ fn = lib.get_fnptr(42)
+ fn = ffi.cast(ffi.typeof(fn), 0)
+ py.test.raises(RuntimeError, fn, 100)
diff --git a/zeffir/test/test_function.py b/zeffir/test/test_function.py
--- a/zeffir/test/test_function.py
+++ b/zeffir/test/test_function.py
@@ -7,6 +7,9 @@
res = lib.simple_function(42)
assert type(res) is int
assert res == 43
+ #
+ e = py.test.raises(TypeError, lib.simple_function, foobar=42)
+ assert str(e.value) == "simple_function() takes no keyword arguments"
def test_function_with_pointer_arg():
ffi, lib = support.compile_and_open('function')
@@ -29,4 +32,11 @@
def test_addressof_function():
ffi, lib = support.compile_and_open('function')
- py.test.raises(NotImplementedError, ffi.addressof, lib, 'simple_function')
+ py.test.raises(TypeError, ffi.addressof, lib, 'simple_function')
+
+def test_typeof_function():
+ ffi, lib = support.compile_and_open('function')
+ e = py.test.raises(TypeError, ffi.typeof, lib.simple_function)
+ assert str(e.value) == ("typeof(lib.func) not supported: the "
+ "exact type of functions is unknown (declare that "
+ "function as a function pointer instead)")
diff --git a/zeffir/zeffir.h b/zeffir/zeffir.h
--- a/zeffir/zeffir.h
+++ b/zeffir/zeffir.h
@@ -29,3 +29,4 @@
size_t extra_text_len);
static PyObject *gc_weakrefs_build(ZefFFIObject *ffi, CDataObject *cd,
PyObject *destructor);
+static PyObject*cdata_call(CDataObject *cd, PyObject *args, PyObject *kwds);
More information about the pypy-commit
mailing list