[pypy-commit] cffi default: Do again struct-returning funcs

arigo noreply at buildbot.pypy.org
Fri Jun 29 10:50:01 CEST 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r562:d1ae2e54055d
Date: 2012-06-29 10:42 +0200
http://bitbucket.org/cffi/cffi/changeset/d1ae2e54055d/

Log:	Do again struct-returning funcs

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -1447,6 +1447,9 @@
     return PyObject_GenericSetAttr((PyObject *)cd, attr, value);
 }
 
+static PyObject *
+convert_struct_to_owning_object(char *data, CTypeDescrObject *ct); /*forward*/
+
 static cif_description_t *
 fb_prepare_cif(PyObject *fargs, CTypeDescrObject *fresult);    /* forward */
 
@@ -1569,6 +1572,9 @@
         res = Py_None;
         Py_INCREF(res);
     }
+    else if (fresult->ct_flags & CT_STRUCT) {
+        res = convert_struct_to_owning_object(resultdata, fresult);
+    }
     else {
         res = convert_to_object(resultdata, fresult);
     }
@@ -1782,6 +1788,41 @@
 
 /************************************************************/
 
+static CDataObject_own_base *allocate_owning_object(Py_ssize_t size,
+                                                    CTypeDescrObject *ct)
+{
+    CDataObject_own_base *cdb;
+    cdb = (CDataObject_own_base *)PyObject_Malloc(size);
+    if (PyObject_Init((PyObject *)cdb, &CDataOwning_Type) == NULL)
+        return NULL;
+
+    Py_INCREF(ct);
+    cdb->head.c_type = ct;
+    cdb->weakreflist = NULL;
+    return cdb;
+}
+
+static PyObject *
+convert_struct_to_owning_object(char *data, CTypeDescrObject *ct)
+{
+    CDataObject_own_base *cdb;
+    Py_ssize_t dataoffset = offsetof(CDataObject_own_nolength, alignment);
+    Py_ssize_t datasize = ct->ct_size;
+
+    if ((ct->ct_flags & (CT_STRUCT|CT_IS_OPAQUE)) != CT_STRUCT) {
+        PyErr_SetString(PyExc_TypeError,
+                        "return type is not a struct or is opaque");
+        return NULL;
+    }
+    cdb = allocate_owning_object(dataoffset + datasize, ct);
+    if (cdb == NULL)
+        return NULL;
+    cdb->head.c_data = ((char *)cdb) + dataoffset;
+
+    memcpy(cdb->head.c_data, data, datasize);
+    return (PyObject *)cdb;
+}
+
 static PyObject *b_newp(PyObject *self, PyObject *args)
 {
     CTypeDescrObject *ct, *ctitem;
@@ -1843,41 +1884,39 @@
         return NULL;
     }
 
-    cdb = (CDataObject_own_base *)PyObject_Malloc(dataoffset + datasize);
-    if (PyObject_Init((PyObject *)cdb, &CDataOwning_Type) == NULL)
-        return NULL;
-    cdb->weakreflist = NULL;
-    cdb->head.c_data = ((char *)cdb) + dataoffset;
-    if (explicitlength >= 0)
-        ((CDataObject_own_length*)cdb)->length = explicitlength;
-
     if (ct->ct_flags & CT_IS_PTR_TO_OWNED) {
         /* common case of ptr-to-struct (or ptr-to-union): for this case
            we build two objects instead of one, with the memory-owning
            one being really the struct (or union) and the returned one
            having a strong reference to it */
-        CDataObject_own_structptr *cdp;
-
-        ctitem = ct->ct_itemdescr;
-        Py_INCREF(ctitem);
-        cdb->head.c_type = ctitem;
-
-        cdp = (CDataObject_own_structptr *)
-            PyObject_Malloc(sizeof(CDataObject_own_structptr));
-        if (PyObject_Init((PyObject *)cdp, &CDataOwning_Type) == NULL) {
+        CDataObject_own_base *cdp;
+
+        cdb = allocate_owning_object(dataoffset + datasize, ct->ct_itemdescr);
+        if (cdb == NULL)
+            return NULL;
+
+        cdp = allocate_owning_object(sizeof(CDataObject_own_structptr), ct);
+        if (cdp == NULL) {
             Py_DECREF(cdb);
             return NULL;
         }
-        cdp->head.weakreflist = NULL;
-        cdp->head.head.c_data = cdb->head.c_data;
-        cdp->structobj = (PyObject *)cdb;   /* store away the only reference */
-        cd = &cdp->head.head;
+        /* store the only reference to cdb into cdp */
+        ((CDataObject_own_structptr *)cdp)->structobj = (PyObject *)cdb;
+        assert(explicitlength < 0);
+
+        cdb->head.c_data = cdp->head.c_data = ((char *)cdb) + dataoffset;
+        cd = &cdp->head;
     }
     else {
+        cdb = allocate_owning_object(dataoffset + datasize, ct);
+        if (cdb == NULL)
+            return NULL;
+
+        cdb->head.c_data = ((char *)cdb) + dataoffset;
+        if (explicitlength >= 0)
+            ((CDataObject_own_length*)cdb)->length = explicitlength;
         cd = &cdb->head;
     }
-    Py_INCREF(ct);
-    cd->c_type = ct;
 
     memset(cd->c_data, 0, datasize);
     if (init != Py_None) {
@@ -2804,11 +2843,6 @@
     if (PyErr_Occurred())
         return -1;
     if (cif_descr != NULL) {
-        if (fb->rtype->type == FFI_TYPE_STRUCT) {
-            PyErr_SetString(PyExc_NotImplementedError,
-                            "functions returning structs are not supported");
-            return -1;
-        }
         /* exchange data size */
         /* first, enough room for an array of 'nargs' pointers */
         exchange_offset = nargs * sizeof(void*);
@@ -3006,9 +3040,9 @@
                           &ellipsis))
         return NULL;
 
-    if (fresult->ct_flags & (CT_STRUCT|CT_UNION)) {
+    if (fresult->ct_flags & CT_UNION) {
         PyErr_SetString(PyExc_NotImplementedError,
-                        "functions returning a struct or a union");
+                        "function returning a union");
         return NULL;
     }
     if ((fresult->ct_size < 0 && !(fresult->ct_flags & CT_VOID)) ||
@@ -3481,6 +3515,14 @@
     return total;
 }
 
+static struct _testfunc7_s _testfunc10(int n)
+{
+    struct _testfunc7_s result;
+    result.a1 = n;
+    result.a2 = n * n;
+    return result;
+}
+
 static PyObject *b__testfunc(PyObject *self, PyObject *args)
 {
     /* for testing only */
@@ -3499,6 +3541,7 @@
     case 7: f = &_testfunc7; break;
     case 8: f = stderr; break;
     case 9: f = &_testfunc9; break;
+    case 10: f = &_testfunc10; break;
     default:
         PyErr_SetNone(PyExc_ValueError);
         return NULL;
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -1140,18 +1140,30 @@
     p.a1 = ['x', 'y']
     assert str(p.a1) == 'xyo'
 
-def test_no_struct_return_in_func():
+def test_invalid_function_result_types():
     BFunc = new_function_type((), new_void_type())
     BArray = new_array_type(new_pointer_type(BFunc), 5)        # works
     new_function_type((), BFunc)    # works
     new_function_type((), new_primitive_type("int"))
     new_function_type((), new_pointer_type(BFunc))
     py.test.raises(NotImplementedError, new_function_type, (),
-                   new_struct_type("foo_s"))
-    py.test.raises(NotImplementedError, new_function_type, (),
                    new_union_type("foo_u"))
     py.test.raises(TypeError, new_function_type, (), BArray)
 
+def test_struct_return_in_func():
+    BChar = new_primitive_type("char")
+    BShort = new_primitive_type("short")
+    BInt = new_primitive_type("int")
+    BStruct = new_struct_type("foo_s")
+    complete_struct_or_union(BStruct, [('a1', BChar, -1),
+                                       ('a2', BShort, -1)])
+    BFunc10 = new_function_type((BInt,), BStruct)
+    f = cast(BFunc10, _testfunc(10))
+    s = f(40)
+    assert repr(s) == "<cdata 'struct foo_s' owning 4 bytes>"
+    assert s.a1 == chr(40)
+    assert s.a2 == 40 * 40
+
 def test_cast_with_functionptr():
     BFunc = new_function_type((), new_void_type())
     BFunc2 = new_function_type((), new_primitive_type("short"))


More information about the pypy-commit mailing list