[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