[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