Author: Armin Rigo <[email protected]>
Branch: cffi-1.0
Changeset: r77256:6c7eb0e3b230
Date: 2015-05-09 17:07 +0200
http://bitbucket.org/pypy/pypy/changeset/6c7eb0e3b230/

Log:    Test and fix fix fix in progress

diff --git a/pypy/module/_cffi_backend/ctypefunc.py 
b/pypy/module/_cffi_backend/ctypefunc.py
--- a/pypy/module/_cffi_backend/ctypefunc.py
+++ b/pypy/module/_cffi_backend/ctypefunc.py
@@ -294,29 +294,33 @@
         space = self.space
         ctype.force_lazy_struct()
         if ctype._custom_field_pos:
-            raise OperationError(space.w_TypeError,
-                                 space.wrap(
-               "cannot pass as an argument a struct that was completed "
-               "with verify() (see pypy/module/_cffi_backend/ctypefunc.py "
-               "for details)"))
+            # these NotImplementedErrors may be caught and ignored until
+            # a real call is made to a function of this type
+            place = "return value" if is_result_type else "argument"
+            raise oefmt(space.w_NotImplementedError,
+                "ctype '%s' not supported as %s (it is a struct declared "
+                "with \"...;\", but the C calling convention may depend "
+                "on the missing fields)", ctype.name, place)
 
         # walk the fields, expanding arrays into repetitions; first,
         # only count how many flattened fields there are
         nflat = 0
         for i, cf in enumerate(ctype._fields_list):
             if cf.is_bitfield():
+                place = "return value" if is_result_type else "argument"
                 raise oefmt(space.w_NotImplementedError,
-                    "ctype '%s' not supported as argument or return value"
-                    " (it is a struct with bit fields)", ctype.name)
+                    "ctype '%s' not supported as %s"
+                    " (it is a struct with bit fields)", ctype.name, place)
             flat = 1
             ct = cf.ctype
             while isinstance(ct, ctypearray.W_CTypeArray):
                 flat *= ct.length
                 ct = ct.ctitem
             if flat <= 0:
+                place = "return value" if is_result_type else "argument"
                 raise oefmt(space.w_NotImplementedError,
-                    "ctype '%s' not supported as argument or return value"
-                    " (it is a struct with a zero-length array)", ctype.name)
+                    "ctype '%s' not supported as %s (it is a struct"
+                    " with a zero-length array)", ctype.name, place)
             nflat += flat
 
         if USE_C_LIBFFI_MSVC and is_result_type:
diff --git a/pypy/module/_cffi_backend/ffi_obj.py 
b/pypy/module/_cffi_backend/ffi_obj.py
--- a/pypy/module/_cffi_backend/ffi_obj.py
+++ b/pypy/module/_cffi_backend/ffi_obj.py
@@ -9,7 +9,7 @@
 from pypy.module._cffi_backend import parse_c_type, realize_c_type
 from pypy.module._cffi_backend import newtype, cerrno, ccallback, ctypearray
 from pypy.module._cffi_backend import ctypestruct, ctypeptr, handle
-from pypy.module._cffi_backend import cbuffer, func, cgc
+from pypy.module._cffi_backend import cbuffer, func, cgc, structwrapper
 from pypy.module._cffi_backend.ctypeobj import W_CType
 from pypy.module._cffi_backend.cdataobj import W_CData
 
@@ -55,10 +55,12 @@
         x = self.types_dict[string]     # KeyError if not found
         if isinstance(x, W_CType):
             return x
-        elif consider_fn_as_fnptr:
-            return realize_c_type.unwrap_fn_as_fnptr(x)
         else:
-            return realize_c_type.unexpected_fn_type(self, x)
+            assert isinstance(x, realize_c_type.W_RawFuncType)
+            if consider_fn_as_fnptr:
+                return x.unwrap_as_fnptr(self)
+            else:
+                return x.unexpected_fn_type(self)
 
     @jit.dont_look_inside
     def parse_string_to_type(self, string, consider_fn_as_fnptr):
@@ -78,6 +80,7 @@
                             " " * num_spaces)
             x = realize_c_type.realize_c_type_or_func(
                 self, self.ctxobj.info.c_output, index)
+            assert x is not None
             self.types_dict[string] = x
         return self.get_string_to_type(string, consider_fn_as_fnptr)
 
@@ -402,6 +405,8 @@
 corresponding <ctype> object.
 It can also be used on 'cdata' instance to get its C type."""
         #
+        if isinstance(w_arg, structwrapper.W_StructWrapper):
+            return w_arg.typeof(self)
         return self.ffi_type(w_arg, ACCEPT_STRING | ACCEPT_CDATA)
 
 
diff --git a/pypy/module/_cffi_backend/lib_obj.py 
b/pypy/module/_cffi_backend/lib_obj.py
--- a/pypy/module/_cffi_backend/lib_obj.py
+++ b/pypy/module/_cffi_backend/lib_obj.py
@@ -10,6 +10,7 @@
 from pypy.module._cffi_backend import cffi_opcode, cglob
 from pypy.module._cffi_backend.realize_c_type import getop, getarg
 from pypy.module._cffi_backend.cdataobj import W_CData
+from pypy.module._cffi_backend.structwrapper import W_StructWrapper
 
 
 class W_LibObject(W_Root):
@@ -38,6 +39,25 @@
             num += 1
         self.ffi.included_libs = includes[:]
 
+    def _build_cpython_func(self, g):
+        # Build a function: in the PyPy version, these are all equivalent
+        # and 'g->address' is a pointer to a function of exactly the
+        # C type specified --- almost: arguments that are structs or
+        # unions are replaced with pointers, and a return value that
+        # would be struct or union is instead handled by passing
+        # inside the function a hidden first pointer argument.
+        rawfunctype = realize_c_type.realize_c_type_or_func(
+            self.ffi, self.ctx.c_types, getarg(g.c_type_op))
+        assert isinstance(rawfunctype, realize_c_type.W_RawFuncType)
+        #
+        w_ct, locs = rawfunctype.unwrap_as_nostruct_fnptr(self.ffi)
+        #
+        ptr = rffi.cast(rffi.CCHARP, g.c_address)
+        w_cdata = W_CData(self.space, ptr, w_ct)
+        if locs is not None:
+            w_cdata = W_StructWrapper(w_cdata, locs, rawfunctype)
+        return w_cdata
+
     @jit.elidable_promote()
     def _get_attr_elidable(self, attr):
         return self.dict_w[attr]     # KeyError if not found
@@ -63,14 +83,8 @@
             if (op == cffi_opcode.OP_CPYTHON_BLTN_V or
                 op == cffi_opcode.OP_CPYTHON_BLTN_N or
                 op == cffi_opcode.OP_CPYTHON_BLTN_O):
-                # A function: in the PyPy version, these are all equivalent
-                # and 'g->address' is a pointer to a function of exactly the
-                # C type specified
-                w_ct = realize_c_type.realize_c_type_or_func(
-                    self.ffi, self.ctx.c_types, getarg(g.c_type_op))
-                w_ct = realize_c_type.unwrap_fn_as_fnptr(w_ct)
-                ptr = rffi.cast(rffi.CCHARP, g.c_address)
-                w_result = W_CData(space, ptr, w_ct)
+                # A function
+                w_result = self._build_cpython_func(g)
                 #
             elif op == cffi_opcode.OP_GLOBAL_VAR:
                 # A global variable of the exact type specified here
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
@@ -124,23 +124,64 @@
 
 class W_RawFuncType(W_Root):
     """Temporary: represents a C function type (not a function pointer)"""
-    def __init__(self, w_ctfuncptr):
-        self.w_ctfuncptr = w_ctfuncptr
+    _ctfuncptr = None
+    _nostruct_ctfuncptr = (None, None)
 
-def unwrap_fn_as_fnptr(x):
-    assert isinstance(x, W_RawFuncType)
-    return x.w_ctfuncptr
+    def __init__(self, opcodes, base_index):
+        self.opcodes = opcodes
+        self.base_index = base_index
 
-def unexpected_fn_type(ffi, x):
-    x = unwrap_fn_as_fnptr(x)
-    # here, x.name is for example 'int(*)(int)'
-    #                                   ^
-    j = x.name_position - 2
-    assert j >= 0
-    text1 = x.name[:j]
-    text2 = x.name[x.name_position+1:]
-    raise oefmt(ffi.w_FFIError, "the type '%s%s' is a function type, not a "
-                                "pointer-to-function type", text1, text2)
+    def _unpack(self, ffi):
+        opcodes = self.opcodes
+        base_index = self.base_index
+        assert getop(opcodes[base_index]) == cffi_opcode.OP_FUNCTION
+        fret = realize_c_type(ffi, opcodes, getarg(opcodes[base_index]))
+        base_index += 1
+        num_args = 0
+        OP_FUNCTION_END = cffi_opcode.OP_FUNCTION_END
+        while getop(opcodes[base_index + num_args]) != OP_FUNCTION_END:
+            num_args += 1
+        ellipsis = (getarg(opcodes[base_index + num_args]) & 1) != 0
+        fargs = [realize_c_type(ffi, opcodes, base_index + i)
+                 for i in range(num_args)]
+        return fargs, fret, ellipsis
+
+    def unwrap_as_fnptr(self, ffi):
+        if self._ctfuncptr is None:
+            fargs, fret, ellipsis = self._unpack(ffi)
+            self._ctfuncptr = newtype._new_function_type(
+                ffi.space, fargs, fret, ellipsis)
+        return self._ctfuncptr
+
+    def unwrap_as_nostruct_fnptr(self, ffi):
+        if self._nostruct_ctfuncptr[0] is None:
+            fargs, fret, ellipsis = self._unpack(ffi)
+            locs = []
+            for i in range(len(fargs)):
+                farg = fargs[i]
+                if isinstance(farg, ctypestruct.W_CTypeStructOrUnion):
+                    farg = newtype.new_pointer_type(ffi.space, farg)
+                    fargs[i] = farg
+                    locs.append(i)
+            if isinstance(fret, ctypestruct.W_CTypeStructOrUnion):
+                xxx
+            ctfuncptr = newtype._new_function_type(
+                ffi.space, fargs, fret, ellipsis)
+            if not locs:
+                locs = None
+            else:
+                locs = locs[:]
+            self._nostruct_ctfuncptr = (ctfuncptr, locs)
+        return self._nostruct_ctfuncptr
+
+    def unexpected_fn_type(self, ffi):
+        fargs, fret, ellipsis = self._unpack(ffi)
+        sargs = ', '.join([farg.name for farg in fargs])
+        sret1 = fret.name[:fret.name_position]
+        sret2 = fret.name[fret.name_position:]
+        raise oefmt(ffi.w_FFIError,
+                    "the type '%s(%s)%s' is a function type, not a "
+                    "pointer-to-function type", sret1, sargs, sret2)
 
 
 def realize_c_type(ffi, opcodes, index):
@@ -149,7 +190,8 @@
     """
     x = realize_c_type_or_func(ffi, opcodes, index)
     if not isinstance(x, W_CType):
-        unexpected_fn_type(ffi, x)
+        assert isinstance(x, W_RawFuncType)
+        raise x.unexpected_fn_type(ffi)
     return x
 
 
@@ -270,7 +312,7 @@
         if isinstance(y, W_CType):
             x = newtype.new_pointer_type(ffi.space, y)
         elif isinstance(y, W_RawFuncType):
-            x = y.w_ctfuncptr
+            x = y.unwrap_as_fnptr(ffi)
         else:
             raise NotImplementedError
 
@@ -288,17 +330,7 @@
         x = _realize_c_enum(ffi, getarg(op))
 
     elif case == cffi_opcode.OP_FUNCTION:
-        y = realize_c_type(ffi, opcodes, getarg(op))
-        base_index = index + 1
-        num_args = 0
-        OP_FUNCTION_END = cffi_opcode.OP_FUNCTION_END
-        while getop(opcodes[base_index + num_args]) != OP_FUNCTION_END:
-            num_args += 1
-        ellipsis = (getarg(opcodes[base_index + num_args]) & 1) != 0
-        fargs = [realize_c_type(ffi, opcodes, base_index + i)
-                 for i in range(num_args)]
-        w_ctfuncptr = newtype._new_function_type(ffi.space, fargs, y, ellipsis)
-        x = W_RawFuncType(w_ctfuncptr)
+        x = W_RawFuncType(opcodes, index)
 
     elif case == cffi_opcode.OP_NOOP:
         x = realize_c_type_or_func(ffi, opcodes, getarg(op))
diff --git a/pypy/module/_cffi_backend/structwrapper.py 
b/pypy/module/_cffi_backend/structwrapper.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_cffi_backend/structwrapper.py
@@ -0,0 +1,41 @@
+from pypy.interpreter.baseobjspace import W_Root
+from pypy.interpreter.typedef import TypeDef
+from pypy.interpreter.gateway import interp2app
+
+from pypy.module._cffi_backend.cdataobj import W_CData
+from pypy.module._cffi_backend.ctypestruct import W_CTypeStructOrUnion
+from pypy.module._cffi_backend.ctypefunc import W_CTypeFunc
+
+
+class W_StructWrapper(W_Root):
+    def __init__(self, w_cdata, locs, rawfunctype):
+        self.w_cdata = w_cdata
+        self.locs = locs
+        self.rawfunctype = rawfunctype
+
+    def typeof(self, ffi):
+        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)
+
+
+W_StructWrapper.typedef = TypeDef(
+        'FFIStructWrapper',
+        __call__ = interp2app(W_StructWrapper.descr_call),
+        )
+W_StructWrapper.typedef.acceptable_as_base_class = False
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
@@ -607,3 +607,26 @@
         s.a = -512
         raises(OverflowError, "s.a = -513")
         assert s.a == -512
+
+    def test_incomplete_struct_as_arg(self):
+        ffi, lib = self.prepare(
+            "struct foo_s { int x; ...; }; int f(int, struct foo_s);",
+            "test_incomplete_struct_as_arg",
+            "struct foo_s { int a, x, z; };\n"
+            "int f(int b, struct foo_s s) { return s.x * b; }")
+        s = ffi.new("struct foo_s *", [21])
+        assert s.x == 21
+        assert ffi.sizeof(s[0]) == 12
+        assert ffi.offsetof(ffi.typeof(s), 'x') == 4
+        assert lib.f(2, s[0]) == 42
+        assert ffi.typeof(lib.f) == ffi.typeof("int(*)(int, struct foo_s)")
+
+    def test_incomplete_struct_as_result(self):
+        ffi, lib = self.prepare(
+            "struct foo_s { int x; ...; }; struct foo_s f(int);",
+            "test_incomplete_struct_as_result",
+            "struct foo_s { int a, x, z; };\n"
+            "struct foo_s f(int x) { struct foo_s r; r.x = x * 2; return r; }")
+        s = lib.f(21)
+        assert s.x == 42
+        assert ffi.typeof(lib.f) == ffi.typeof("struct foo_s(*)(int)")
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to