[pypy-commit] cffi win32-stdcall: in-progress, but found a problem

arigo noreply at buildbot.pypy.org
Tue Oct 6 09:35:38 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: win32-stdcall
Changeset: r2309:fc4b1899ac96
Date: 2015-10-06 09:35 +0200
http://bitbucket.org/cffi/cffi/changeset/fc4b1899ac96/

Log:	in-progress, but found a problem

diff --git a/c/realize_c_type.c b/c/realize_c_type.c
--- a/c/realize_c_type.c
+++ b/c/realize_c_type.c
@@ -544,7 +544,7 @@
     case _CFFI_OP_FUNCTION:
     {
         PyObject *fargs;
-        int i, base_index, num_args, ellipsis;
+        int i, base_index, num_args, ellipsis, abi;
 
         y = (PyObject *)realize_c_type(builder, opcodes, _CFFI_GETARG(op));
         if (y == NULL)
@@ -560,7 +560,22 @@
                    _CFFI_OP_FUNCTION_END)
             num_args++;
 
-        ellipsis = _CFFI_GETARG(opcodes[base_index + num_args]) & 1;
+        ellipsis = _CFFI_GETARG(opcodes[base_index + num_args]) & 0x01;
+        abi      = _CFFI_GETARG(opcodes[base_index + num_args]) & 0xFE;
+        switch (abi) {
+        case 0:
+            abi = FFI_DEFAULT_ABI;
+            break;
+#if defined(MS_WIN32) && !defined(_WIN64)
+        case 2:
+            abi = FFI_STDCALL;
+            break;
+#endif
+        default:
+            PyErr_Format(FFIError, "abi number %d not supported", abi);
+            Py_DECREF(y);
+            return NULL;
+        }
 
         fargs = PyTuple_New(num_args);
         if (fargs == NULL) {
@@ -578,8 +593,7 @@
             PyTuple_SET_ITEM(fargs, i, z);
         }
 
-        z = new_function_type(fargs, (CTypeDescrObject *)y, ellipsis,
-                              FFI_DEFAULT_ABI);
+        z = new_function_type(fargs, (CTypeDescrObject *)y, ellipsis, abi);
         Py_DECREF(fargs);
         Py_DECREF(y);
         if (z == NULL)
diff --git a/cffi/model.py b/cffi/model.py
--- a/cffi/model.py
+++ b/cffi/model.py
@@ -243,12 +243,13 @@
                 abi_args = (ffi._backend.FFI_STDCALL,)
             except AttributeError:
                 if sys.platform == "win32":
-                    raise NotImplementedError("%r: stdcall with ctypes backend"
-                                              % (self,))
+                    raise NotImplementedError("%r: stdcall" % (self,))
                 else:
                     from . import api
-                    raise api.CDefError("%r: '__stdcall' only for Windows"
+                    raise api.CDefError("%r: '__stdcall': only on Windows"
                                         % (self,))
+            if self.ellipsis:   # win32: __stdcall is ignored when
+                abi_args = ()   #        applied to variadic functions
         else:
             raise NotImplementedError("abi=%r" % (self.abi,))
         return global_cache(self, ffi, 'new_function_type',
diff --git a/cffi/recompiler.py b/cffi/recompiler.py
--- a/cffi/recompiler.py
+++ b/cffi/recompiler.py
@@ -607,7 +607,11 @@
             call_arguments.append('x%d' % i)
         repr_arguments = ', '.join(arguments)
         repr_arguments = repr_arguments or 'void'
-        name_and_arguments = '_cffi_d_%s(%s)' % (name, repr_arguments)
+        if tp.abi:
+            abi = tp.abi + ' '
+        else:
+            abi = ''
+        name_and_arguments = '%s_cffi_d_%s(%s)' % (abi, name, repr_arguments)
         prnt('static %s' % (tp.result.get_c_name(name_and_arguments),))
         prnt('{')
         call_arguments = ', '.join(call_arguments)
@@ -710,7 +714,8 @@
         if difference:
             repr_arguments = ', '.join(arguments)
             repr_arguments = repr_arguments or 'void'
-            name_and_arguments = '_cffi_f_%s(%s)' % (name, repr_arguments)
+            name_and_arguments = '%s_cffi_f_%s(%s)' % (abi, name,
+                                                       repr_arguments)
             prnt('static %s' % (tp_result.get_c_name(name_and_arguments),))
             prnt('{')
             if result_decl:
@@ -1135,7 +1140,13 @@
                 else:
                     self.cffi_types[index] = CffiOp(OP_NOOP, realindex)
             index += 1
-        self.cffi_types[index] = CffiOp(OP_FUNCTION_END, int(tp.ellipsis))
+        flags = int(tp.ellipsis)
+        if tp.abi is not None:
+            if tp.abi == '__stdcall':
+                flags |= 2
+            else:
+                raise NotImplementedError("abi=%r" % (tp.abi,))
+        self.cffi_types[index] = CffiOp(OP_FUNCTION_END, flags)
 
     def _emit_bytecode_PointerType(self, tp, index):
         self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[tp.totype])
diff --git a/cffi/vengine_gen.py b/cffi/vengine_gen.py
--- a/cffi/vengine_gen.py
+++ b/cffi/vengine_gen.py
@@ -159,7 +159,11 @@
         arglist = ', '.join(arglist) or 'void'
         wrappername = '_cffi_f_%s' % name
         self.export_symbols.append(wrappername)
-        funcdecl = ' %s(%s)' % (wrappername, arglist)
+        if tp.abi:
+            abi = tp.abi + ' '
+        else:
+            abi = ''
+        funcdecl = ' %s%s(%s)' % (abi, wrappername, arglist)
         context = 'result of %s' % name
         prnt(tpresult.get_c_name(funcdecl, context))
         prnt('{')
diff --git a/testing/cffi0/test_verify.py b/testing/cffi0/test_verify.py
--- a/testing/cffi0/test_verify.py
+++ b/testing/cffi0/test_verify.py
@@ -1220,24 +1220,6 @@
     lib = ffi.verify('#include <math.h>', libraries=lib_m)
     assert lib.sin(1.23) == math.sin(1.23)
 
-def test_callback_calling_convention():
-    if sys.platform != 'win32':
-        py.test.skip("Windows only")
-    ffi = FFI()
-    ffi.cdef("""
-        int call1(int(*__cdecl cb)(int));
-        int call2(int(*__stdcall cb)(int));
-    """)
-    lib = ffi.verify("""
-        int call1(int(*__cdecl cb)(int)) {
-            return cb(42) + 1;
-        }
-        int call2(int(*__stdcall cb)(int)) {
-            return cb(-42) - 6;
-        }
-    """)
-    xxx
-
 def test_opaque_integer_as_function_result():
     #import platform
     #if platform.machine().startswith('sparc'):
@@ -2259,3 +2241,128 @@
     assert foo_s.fields[0][1].type is ffi.typeof("int")
     assert foo_s.fields[1][0] == 'b'
     assert foo_s.fields[1][1].type is ffi.typeof("void *")
+
+def test_win32_calling_convention_1():
+    if sys.platform != 'win32':
+        py.test.skip("Windows only")
+    ffi = FFI()
+    ffi.cdef("int call1(int(*cb)(int));", calling_conv="cdecl")
+    ffi.cdef("int call2(int(*cb)(int));", calling_conv="stdcall")
+    lib = ffi.verify(r"""
+        int __cdecl call1(int(__cdecl *cb)(int)) {
+            printf("here1\n");
+            printf("cb = %p, cb1 = %p\n", cb, (void *)cb1);
+            int i, result = 0;
+            for (i = 0; i < 1000; i++)
+                result += cb(i);
+            printf("result = %d\n", result);
+            return result;
+        }
+        int __stdcall call2(int(__stdcall *cb)(int)) {
+            int i, result = 0;
+            printf("here1\n");
+            printf("cb = %p, cb2 = %p\n", cb, (void *)cb2);
+            for (i = 0; i < 1000; i++)
+                result += cb(-i);
+            printf("result = %d\n", result);
+            return result;
+        }
+    """)
+    assert lib.call1(ffi.addressof(lib, 'cb1')) == 500*999*2
+    ...
+    print '<<< cb2 =', ffi.addressof(lib, 'cb2')
+    ptr_call2 = ffi.addressof(lib, 'call2')
+    assert lib.call2(ffi.addressof(lib, 'cb2')) == -500*999*3
+    assert ptr_call2(ffi.addressof(lib, 'cb2')) == -500*999*3
+    print '<<< done'
+
+def test_win32_calling_convention_2():
+    if sys.platform != 'win32':
+        py.test.skip("Windows only")
+    # any mistake in the declaration of plain function (including the
+    # precise argument types and, here, the calling convention) are
+    # automatically corrected.  But this does not apply to the 'cb'
+    # function pointer argument.
+    ffi = FFI()
+    ffi.cdef("int call1(int(*cb)(int)); int cb1(int);", calling_conv="cdecl")
+    ffi.cdef("int call2(int(*cb)(int)); int cb2(int);", calling_conv="stdcall")
+    lib = verify(ffi, 'test_win32_calling_convention_2', """
+        int __stdcall call1(int(__cdecl *cb)(int)) {
+            int i, result = 0;
+            for (i = 0; i < 1000; i++)
+                result += cb(i);
+            return result;
+        }
+        int __cdecl call2(int(__stdcall *cb)(int)) {
+            int i, result = 0;
+            for (i = 0; i < 1000; i++)
+                result += cb(-i);
+            return result;
+        }
+        int __stdcall cb1(int x) { return x * 2; }
+        int __cdecl cb2(int x) { return x * 3; }
+    """)
+    ptr_call1 = ffi.addressof(lib, 'call1')
+    ptr_call2 = ffi.addressof(lib, 'call2')
+    py.test.raises(TypeError, lib.call1, ffi.addressof(lib, 'cb2'))
+    py.test.raises(TypeError, ptr_call1, ffi.addressof(lib, 'cb2'))
+    py.test.raises(TypeError, lib.call2, ffi.addressof(lib, 'cb1'))
+    py.test.raises(TypeError, ptr_call2, ffi.addressof(lib, 'cb1'))
+    assert lib.call1(ffi.addressof(lib, 'cb1')) == 500*999*2
+    assert ptr_call1(ffi.addressof(lib, 'cb1')) == 500*999*2
+    assert lib.call2(ffi.addressof(lib, 'cb2')) == -500*999*3
+    assert ptr_call2(ffi.addressof(lib, 'cb2')) == -500*999*3
+
+def test_win32_calling_convention_3():
+    if sys.platform != 'win32':
+        py.test.skip("Windows only")
+    ffi = FFI()
+    ffi.cdef("struct point { int x, y; };")
+    ffi.cdef("struct point call1(int(*cb)(struct point)); "
+             "int cb1(struct point);", calling_conv="cdecl")
+    ffi.cdef("struct point call2(int(*cb)(struct point)); "
+             "int cb2(struct point);", calling_conv="stdcall")
+    lib = verify(ffi, 'test_win32_calling_convention_3', r"""
+        struct point { int x, y; };
+        int __stdcall cb1(struct point pt) { return pt.x + 10 * pt.y; }
+        int __cdecl cb2(struct point pt) { return pt.x + 100 * pt.y; }
+        struct point __stdcall call1(int(__cdecl *cb)(struct point)) {
+            int i;
+            struct point result = { 0, 0 };
+            printf("here1\n");
+            printf("cb = %p, cb1 = %p\n", cb, (void *)cb1);
+            for (i = 0; i < 1000; i++) {
+                struct point p = { i, -i };
+                int r = cb(p);
+                result.x += r;
+                result.y -= r;
+            }
+            return result;
+        }
+        struct point __cdecl call2(int(__stdcall *cb)(struct point)) {
+            int i;
+            struct point result = { 0, 0 };
+            for (i = 0; i < 1000; i++) {
+                struct point p = { -i, i };
+                int r = cb(p);
+                result.x += r;
+                result.y -= r;
+            }
+            return result;
+        }
+    """)
+    ptr_call1 = ffi.addressof(lib, 'call1')
+    ptr_call2 = ffi.addressof(lib, 'call2')
+    py.test.raises(TypeError, lib.call1, ffi.addressof(lib, 'cb2'))
+    py.test.raises(TypeError, ptr_call1, ffi.addressof(lib, 'cb2'))
+    py.test.raises(TypeError, lib.call2, ffi.addressof(lib, 'cb1'))
+    py.test.raises(TypeError, ptr_call2, ffi.addressof(lib, 'cb1'))
+    print '<<< cb1 =', ffi.addressof(lib, 'cb1')
+    pt = lib.call1(ffi.addressof(lib, 'cb1'))
+    assert (pt.x, pt.y) == (-9*500*999, 9*500*999)
+    pt = ptr_call1(ffi.addressof(lib, 'cb1'))
+    assert (pt.x, pt.y) == (-9*500*999, 9*500*999)
+    pt = lib.call2(ffi.addressof(lib, 'cb2'))
+    assert (pt.x, pt.y) == (99*500*999, -99*500*999)
+    pt = ptr_call2(ffi.addressof(lib, 'cb2'))
+    assert (pt.x, pt.y) == (99*500*999, -99*500*999)
diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py
--- a/testing/cffi1/test_recompiler.py
+++ b/testing/cffi1/test_recompiler.py
@@ -1287,24 +1287,37 @@
     ffi = FFI()
     ffi.cdef("int call1(int(*cb)(int)); int cb1(int);", calling_conv="cdecl")
     ffi.cdef("int call2(int(*cb)(int)); int cb2(int);", calling_conv="stdcall")
-    lib = verify(ffi, 'test_win32_calling_convention_1', """
-        int __cdecl call1(int(*__cdecl cb)(int)) {
+    lib = verify(ffi, 'test_win32_calling_convention_1', r"""
+        int __cdecl cb1(int x) { return x * 2; }
+        int __cdecl call1(int(__cdecl *cb)(int)) {
+            printf("here1\n");
+            printf("cb = %p, cb1 = %p\n", cb, (void *)cb1);
             int i, result = 0;
             for (i = 0; i < 1000; i++)
                 result += cb(i);
+            printf("result = %d\n", result);
             return result;
         }
-        int __stdcall call2(int(*__stdcall cb)(int)) {
+        int __stdcall cb2(int x) { return x * 3; }
+        int __stdcall call2(int(__stdcall *cb)(int)) {
             int i, result = 0;
+            printf("here1\n");
+            printf("cb = %p, cb2 = %p\n", cb, (void *)cb2);
             for (i = 0; i < 1000; i++)
                 result += cb(-i);
+            printf("result = %d\n", result);
             return result;
         }
-        int __cdecl cb1(int x) { return x * 2; }
-        int __stdcall cb2(int x) { return x * 3; }
     """)
+    print '<<< cb1 =', ffi.addressof(lib, 'cb1')
+    ptr_call1 = ffi.addressof(lib, 'call1')
     assert lib.call1(ffi.addressof(lib, 'cb1')) == 500*999*2
+    assert ptr_call1(ffi.addressof(lib, 'cb1')) == 500*999*2
+    print '<<< cb2 =', ffi.addressof(lib, 'cb2')
+    ptr_call2 = ffi.addressof(lib, 'call2')
     assert lib.call2(ffi.addressof(lib, 'cb2')) == -500*999*3
+    assert ptr_call2(ffi.addressof(lib, 'cb2')) == -500*999*3
+    print '<<< done'
 
 def test_win32_calling_convention_2():
     if sys.platform != 'win32':
@@ -1317,13 +1330,13 @@
     ffi.cdef("int call1(int(*cb)(int)); int cb1(int);", calling_conv="cdecl")
     ffi.cdef("int call2(int(*cb)(int)); int cb2(int);", calling_conv="stdcall")
     lib = verify(ffi, 'test_win32_calling_convention_2', """
-        int __stdcall call1(int(*__cdecl cb)(int)) {
+        int __stdcall call1(int(__cdecl *cb)(int)) {
             int i, result = 0;
             for (i = 0; i < 1000; i++)
                 result += cb(i);
             return result;
         }
-        int __cdecl call2(int(*__stdcall cb)(int)) {
+        int __cdecl call2(int(__stdcall *cb)(int)) {
             int i, result = 0;
             for (i = 0; i < 1000; i++)
                 result += cb(-i);
@@ -1332,5 +1345,107 @@
         int __stdcall cb1(int x) { return x * 2; }
         int __cdecl cb2(int x) { return x * 3; }
     """)
+    ptr_call1 = ffi.addressof(lib, 'call1')
+    ptr_call2 = ffi.addressof(lib, 'call2')
+    py.test.raises(TypeError, lib.call1, ffi.addressof(lib, 'cb2'))
+    py.test.raises(TypeError, ptr_call1, ffi.addressof(lib, 'cb2'))
+    py.test.raises(TypeError, lib.call2, ffi.addressof(lib, 'cb1'))
+    py.test.raises(TypeError, ptr_call2, ffi.addressof(lib, 'cb1'))
     assert lib.call1(ffi.addressof(lib, 'cb1')) == 500*999*2
+    assert ptr_call1(ffi.addressof(lib, 'cb1')) == 500*999*2
     assert lib.call2(ffi.addressof(lib, 'cb2')) == -500*999*3
+    assert ptr_call2(ffi.addressof(lib, 'cb2')) == -500*999*3
+
+def test_win32_calling_convention_3():
+    if sys.platform != 'win32':
+        py.test.skip("Windows only")
+    ffi = FFI()
+    ffi.cdef("struct point { int x, y; };")
+    ffi.cdef("struct point call1(int(*cb)(struct point)); "
+             "int cb1(struct point);", calling_conv="cdecl")
+    ffi.cdef("struct point call2(int(*cb)(struct point)); "
+             "int cb2(struct point);", calling_conv="stdcall")
+    lib = verify(ffi, 'test_win32_calling_convention_3', r"""
+        struct point { int x, y; };
+        int __stdcall cb1(struct point pt) { return pt.x + 10 * pt.y; }
+        int __cdecl cb2(struct point pt) { return pt.x + 100 * pt.y; }
+        struct point __stdcall call1(int(__cdecl *cb)(struct point)) {
+            int i;
+            struct point result = { 0, 0 };
+            printf("here1\n");
+            printf("cb = %p, cb1 = %p\n", cb, (void *)cb1);
+            for (i = 0; i < 1000; i++) {
+                struct point p = { i, -i };
+                int r = cb(p);
+                result.x += r;
+                result.y -= r;
+            }
+            return result;
+        }
+        struct point __cdecl call2(int(__stdcall *cb)(struct point)) {
+            int i;
+            struct point result = { 0, 0 };
+            for (i = 0; i < 1000; i++) {
+                struct point p = { -i, i };
+                int r = cb(p);
+                result.x += r;
+                result.y -= r;
+            }
+            return result;
+        }
+    """)
+    ptr_call1 = ffi.addressof(lib, 'call1')
+    ptr_call2 = ffi.addressof(lib, 'call2')
+    py.test.raises(TypeError, lib.call1, ffi.addressof(lib, 'cb2'))
+    py.test.raises(TypeError, ptr_call1, ffi.addressof(lib, 'cb2'))
+    py.test.raises(TypeError, lib.call2, ffi.addressof(lib, 'cb1'))
+    py.test.raises(TypeError, ptr_call2, ffi.addressof(lib, 'cb1'))
+    print '<<< cb1 =', ffi.addressof(lib, 'cb1')
+    pt = lib.call1(ffi.addressof(lib, 'cb1'))
+    assert (pt.x, pt.y) == (-9*500*999, 9*500*999)
+    pt = ptr_call1(ffi.addressof(lib, 'cb1'))
+    assert (pt.x, pt.y) == (-9*500*999, 9*500*999)
+    pt = lib.call2(ffi.addressof(lib, 'cb2'))
+    assert (pt.x, pt.y) == (99*500*999, -99*500*999)
+    pt = ptr_call2(ffi.addressof(lib, 'cb2'))
+    assert (pt.x, pt.y) == (99*500*999, -99*500*999)
+
+def test_win32_calling_convention_4():
+    if sys.platform != 'win32':
+        py.test.skip("Windows only")
+    ffi = FFI()
+    ffi.cdef("int call1(int(*cb)(int));", calling_conv="cdecl")
+    ffi.cdef("int call2(int(*cb)(int));", calling_conv="stdcall")
+    lib = verify(ffi, 'test_win32_calling_convention_4', """
+        int __stdcall call1(int(__cdecl *cb)(int)) {
+            int i, result = 0;
+            for (i = 0; i < 1000; i++)
+                result += cb(i);
+            return result;
+        }
+        int __cdecl call2(int(__stdcall *cb)(int)) {
+            int i, result = 0;
+            for (i = 0; i < 1000; i++)
+                result += cb(-i);
+            return result;
+        }
+    """)
+    @ffi.callback("int(int)", calling_conv="cdecl")
+    def cb1(x):
+        return x * 2
+    ...
+    @ffi.callback("int(int)", calling_conv="stdcall")
+    def cb2(x):
+        return x * 2
+    
+    
+    ptr_call1 = ffi.addressof(lib, 'call1')
+    ptr_call2 = ffi.addressof(lib, 'call2')
+    py.test.raises(TypeError, lib.call1, ffi.addressof(lib, 'cb2'))
+    py.test.raises(TypeError, ptr_call1, ffi.addressof(lib, 'cb2'))
+    py.test.raises(TypeError, lib.call2, ffi.addressof(lib, 'cb1'))
+    py.test.raises(TypeError, ptr_call2, ffi.addressof(lib, 'cb1'))
+    assert lib.call1(ffi.addressof(lib, 'cb1')) == 500*999*2
+    assert ptr_call1(ffi.addressof(lib, 'cb1')) == 500*999*2
+    assert lib.call2(ffi.addressof(lib, 'cb2')) == -500*999*3
+    assert ptr_call2(ffi.addressof(lib, 'cb2')) == -500*999*3


More information about the pypy-commit mailing list