[pypy-commit] pypy cffi-1.0: Struct/union returns

arigo noreply at buildbot.pypy.org
Sat May 9 19:17:07 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: cffi-1.0
Changeset: r77257:52bdca9bbc5c
Date: 2015-05-09 19:04 +0200
http://bitbucket.org/pypy/pypy/changeset/52bdca9bbc5c/

Log:	Struct/union returns

diff --git a/pypy/module/_cffi_backend/realize_c_type.py b/pypy/module/_cffi_backend/realize_c_type.py
--- a/pypy/module/_cffi_backend/realize_c_type.py
+++ b/pypy/module/_cffi_backend/realize_c_type.py
@@ -164,7 +164,10 @@
                     fargs[i] = farg
                     locs.append(i)
             if isinstance(fret, ctypestruct.W_CTypeStructOrUnion):
-                xxx
+                fret = newtype.new_pointer_type(ffi.space, fret)
+                fargs = [fret] + fargs
+                fret = newtype.new_void_type(ffi.space)
+                locs.append(-1)
             ctfuncptr = newtype._new_function_type(
                 ffi.space, fargs, fret, ellipsis)
             if not locs:
diff --git a/pypy/module/_cffi_backend/structwrapper.py b/pypy/module/_cffi_backend/structwrapper.py
--- a/pypy/module/_cffi_backend/structwrapper.py
+++ b/pypy/module/_cffi_backend/structwrapper.py
@@ -1,15 +1,36 @@
 from pypy.interpreter.baseobjspace import W_Root
+from pypy.interpreter.error import oefmt
 from pypy.interpreter.typedef import TypeDef
 from pypy.interpreter.gateway import interp2app
+from rpython.rlib.objectmodel import keepalive_until_here
 
 from pypy.module._cffi_backend.cdataobj import W_CData
+from pypy.module._cffi_backend.cdataobj import W_CDataPtrToStructOrUnion
 from pypy.module._cffi_backend.ctypestruct import W_CTypeStructOrUnion
 from pypy.module._cffi_backend.ctypefunc import W_CTypeFunc
 
 
 class W_StructWrapper(W_Root):
+    """A wrapper around a real W_CData which points to a function
+    generated in the C code.  The real W_CData has got no struct/union
+    argument (only pointers to it), and no struct/union return type
+    (it is replaced by a hidden pointer as first argument).  This
+    wrapper is callable, and the arguments it expects and returns
+    are directly the struct/union.  Calling ffi.typeof(wrapper)
+    also returns the original struct/union signature.
+    """
+    _immutable_ = True
+
     def __init__(self, w_cdata, locs, rawfunctype):
+        ctype = w_cdata.ctype
+        assert isinstance(ctype, W_CTypeFunc)
+        self.ctype = ctype      # this type takes pointers
         self.w_cdata = w_cdata
+        if locs[-1] == -1:      # return value is a struct/union
+            locs = locs[:-1]
+            self.ctresptrtype = ctype.fargs[0]
+        else:
+            self.ctresptrtype = None
         self.locs = locs
         self.rawfunctype = rawfunctype
 
@@ -17,21 +38,42 @@
         return self.rawfunctype.unwrap_as_fnptr(ffi)
 
     def descr_call(self, args_w):
-        ctype = self.w_cdata.ctype
-        assert isinstance(ctype, W_CTypeFunc)
-        args_w = args_w[:]
-        for loc in self.locs:
-            if loc >= len(args_w):
-                continue    # the real call will complain
-            w_arg = args_w[loc]
-            if not isinstance(w_arg, W_CData):
-                continue    # the real call will complain
-            if not isinstance(w_arg.ctype, W_CTypeStructOrUnion):
-                continue    # the real call will complain
-            w_arg = W_CData(w_arg.space, w_arg.unsafe_escaping_ptr(),
-                            ctype.fargs[loc])
-            args_w[loc] = w_arg
-        return self.w_cdata.call(args_w)
+        space = self.w_cdata.space
+        ctype = self.ctype
+        shift = (self.ctresptrtype is not None)
+        expected_num_args = len(ctype.fargs) - shift
+        if len(args_w) != expected_num_args:
+            raise oefmt(space.w_TypeError,
+                        "'%s' expects %d arguments, got %d",
+                        ctype.name, expected_num_args, len(args_w))
+
+        # Fix the arguments that are so far "struct/union" and that need
+        # to be "ptr to struct/union"
+        original_args_w = args_w
+        if len(self.locs) > 0:
+            args_w = args_w[:]
+            for loc in self.locs:
+                w_arg = args_w[loc]
+                if (not isinstance(w_arg, W_CData) or
+                        not isinstance(w_arg.ctype, W_CTypeStructOrUnion)):
+                    raise oefmt(space.w_TypeError,
+                                "wrong type for argument %d", loc)
+                w_arg = W_CData(space, w_arg.unsafe_escaping_ptr(),
+                                self.ctype.fargs[loc + shift])
+                args_w[loc] = w_arg
+
+        # If the result we want to present to the user is "returns struct",
+        # then internally allocate the struct and pass a pointer to it as
+        # a first argument
+        if self.ctresptrtype is not None:
+            w_result_cdata = self.ctresptrtype.newp(space.w_None)
+            self.w_cdata.call([w_result_cdata] + args_w)
+            assert isinstance(w_result_cdata, W_CDataPtrToStructOrUnion)
+            w_result = w_result_cdata.structobj
+        else:
+            w_result = self.w_cdata.call(args_w)
+        keepalive_until_here(original_args_w)
+        return w_result
 
 
 W_StructWrapper.typedef = TypeDef(
diff --git a/pypy/module/_cffi_backend/test/test_recompiler.py b/pypy/module/_cffi_backend/test/test_recompiler.py
--- a/pypy/module/_cffi_backend/test/test_recompiler.py
+++ b/pypy/module/_cffi_backend/test/test_recompiler.py
@@ -630,3 +630,19 @@
         s = lib.f(21)
         assert s.x == 42
         assert ffi.typeof(lib.f) == ffi.typeof("struct foo_s(*)(int)")
+
+    def test_incomplete_struct_as_both(self):
+        ffi, lib = self.prepare(
+            "struct foo_s { int x; ...; }; struct bar_s { int y; ...; };\n"
+            "struct foo_s f(int, struct bar_s);",
+            "test_incomplete_struct_as_both",
+            "struct foo_s { int a, x, z; };\n"
+            "struct bar_s { int b, c, y, d; };\n"
+            "struct foo_s f(int x, struct bar_s b) {\n"
+            "  struct foo_s r; r.x = x * b.y; return r;\n"
+            "}")
+        b = ffi.new("struct bar_s *", [7])
+        s = lib.f(6, b[0])
+        assert s.x == 42
+        assert ffi.typeof(lib.f) == ffi.typeof(
+            "struct foo_s(*)(int, struct bar_s)")


More information about the pypy-commit mailing list