Commits

Armin Rigo  committed 34052d6

Test and fix

  • Participants
  • Parent commits adf3dfc
  • Branches verifier2

Comments (0)

Files changed (4)

File c/_cffi_backend.c

 #endif
         }
         if (convert_from_object(data, argtype, obj) < 0) {
-            if (CData_Check(obj) && argtype->ct_flags & CT_POINTER &&
+            if (CData_Check(obj) && (argtype->ct_flags & CT_POINTER) &&
                    argtype->ct_itemdescr == ((CDataObject *)obj)->c_type) {
                 /* special case to make the life of verifier.py easier:
                    if the formal argument type is 'struct foo *' but
     return result;
 }
 
+static short _testfunc18(struct _testfunc7_s *ptr)
+{
+    return ptr->a1 + ptr->a2;
+}
+
 static PyObject *b__testfunc(PyObject *self, PyObject *args)
 {
     /* for testing only */
     case 15: f = &_testfunc15; break;
     case 16: f = &_testfunc16; break;
     case 17: f = &_testfunc17; break;
+    case 18: f = &_testfunc18; break;
     default:
         PyErr_SetNone(PyExc_ValueError);
         return NULL;
     res = f(x[0])
     assert res == -4042 + ord('A')
 
+def test_call_function_18():
+    BChar = new_primitive_type("char")
+    BShort = new_primitive_type("short")
+    BStruct = new_struct_type("foo")
+    BStructPtr = new_pointer_type(BStruct)
+    complete_struct_or_union(BStruct, [('a1', BChar, -1),
+                                       ('a2', BShort, -1)])
+    BFunc18 = new_function_type((BStructPtr,), BShort, False)
+    f = cast(BFunc18, _testfunc(18))
+    x = newp(BStructPtr, {'a1': 'A', 'a2': -4042})
+    # test the exception that allows us to pass a 'struct foo' where the
+    # function really expects a 'struct foo *'.
+    res = f(x[0])
+    assert res == -4042 + ord('A')
+    assert res == f(x)
+
 def test_call_function_9():
     BInt = new_primitive_type("int")
     BFunc9 = new_function_type((BInt,), BInt, True)    # vararg

File cffi/verifier.py

         if tp.ellipsis:
             newfunction = self._load_constant(False, tp, name, module)
         else:
+            indirections = []
             if any(isinstance(type, model.StructOrUnion) for type in tp.args):
                 indirect_args = []
                 for i, type in enumerate(tp.args):
                     if isinstance(type, model.StructOrUnion):
                         type = model.PointerType(type)
+                        indirections.append((i, type))
                     indirect_args.append(type)
                 tp = model.FunctionPtrType(tuple(indirect_args),
                                            tp.result, tp.ellipsis)
             BFunc = self.ffi._get_cached_btype(tp)
             wrappername = '_cffi_f_%s' % name
             newfunction = module.load_function(BFunc, wrappername)
+            for i, type in indirections:
+                newfunction = self._make_struct_wrapper(newfunction, i, type)
         setattr(library, name, newfunction)
 
+    def _make_struct_wrapper(self, oldfunc, i, tp):
+        backend = self.ffi._backend
+        BType = self.ffi._get_cached_btype(tp)
+        def newfunc(*args):
+            args = args[:i] + (backend.newp(BType, args[i]),) + args[i+1:]
+            return oldfunc(*args)
+        return newfunc
+
     # ----------
     # named structs
 

File testing/test_verify.py

     """)
     s = ffi.new("struct foo_s *", [100, 1])
     assert lib.foo(s[0]) == 99
+    assert lib.foo([100, 1]) == 99
 
 def test_autofilled_struct_as_argument_dynamic():
     ffi = FFI()