[pypy-commit] cffi default: hg merge win32-stdcall

arigo noreply at buildbot.pypy.org
Tue Oct 6 13:52:14 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r2325:256e52bacdb1
Date: 2015-10-06 13:53 +0200
http://bitbucket.org/cffi/cffi/changeset/256e52bacdb1/

Log:	hg merge win32-stdcall

	Win32: full support for '__stdcall' in function types.

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -3644,13 +3644,13 @@
            funcptr    [ctresult, ellipsis+abi, num_args, ctargs...]
     */
     PyObject *key, *y;
-    const void **pkey;
+    void *pkey;
 
     key = PyBytes_FromStringAndSize(NULL, keylength * sizeof(void *));
     if (key == NULL)
         goto error;
 
-    pkey = (const void **)PyBytes_AS_STRING(key);
+    pkey = PyBytes_AS_STRING(key);
     memcpy(pkey, unique_key, keylength * sizeof(void *));
 
     y = PyDict_GetItem(unique_cache, key);
@@ -4661,7 +4661,7 @@
 }
 
 static int fb_build_name(struct funcbuilder_s *fb, PyObject *fargs,
-                         CTypeDescrObject *fresult, int ellipsis)
+                         CTypeDescrObject *fresult, int ellipsis, int fabi)
 {
     Py_ssize_t i, nargs = PyTuple_GET_SIZE(fargs);
     fb->nargs = nargs;
@@ -4672,9 +4672,17 @@
          RESULT_TYPE_HEAD (*)(ARG_1_TYPE, ARG_2_TYPE, etc) RESULT_TYPE_TAIL
     */
     fb_cat_name(fb, fresult->ct_name, fresult->ct_name_position);
-    fb_cat_name(fb, "(*)(", 4);
+    fb_cat_name(fb, "(", 1);
+    i = 2;
+#if defined(MS_WIN32) && !defined(_WIN64)
+    if (fabi == FFI_STDCALL) {
+        fb_cat_name(fb, "__stdcall ", 10);
+        i += 10;
+    }
+#endif
+    fb_cat_name(fb, "*)(", 3);
     if (fb->fct) {
-        i = fresult->ct_name_position + 2;  /* between '(*' and ')(' */
+        i = fresult->ct_name_position + i;  /* between '(*' and ')(' */
         fb->fct->ct_name_position = i;
     }
 
@@ -4710,7 +4718,7 @@
 static CTypeDescrObject *fb_prepare_ctype(struct funcbuilder_s *fb,
                                           PyObject *fargs,
                                           CTypeDescrObject *fresult,
-                                          int ellipsis)
+                                          int ellipsis, int fabi)
 {
     CTypeDescrObject *fct;
 
@@ -4719,7 +4727,7 @@
     fb->fct = NULL;
 
     /* compute the total size needed for the name */
-    if (fb_build_name(fb, fargs, fresult, ellipsis) < 0)
+    if (fb_build_name(fb, fargs, fresult, ellipsis, fabi) < 0)
         return NULL;
 
     /* allocate the function type */
@@ -4730,7 +4738,7 @@
 
     /* call again fb_build_name() to really build the ct_name */
     fb->bufferp = fct->ct_name;
-    if (fb_build_name(fb, fargs, fresult, ellipsis) < 0)
+    if (fb_build_name(fb, fargs, fresult, ellipsis, fabi) < 0)
         goto error;
     assert(fb->bufferp == fct->ct_name + fb->nb_bytes);
 
@@ -4807,7 +4815,7 @@
         return NULL;
     }
 
-    fct = fb_prepare_ctype(&funcbuilder, fargs, fresult, ellipsis);
+    fct = fb_prepare_ctype(&funcbuilder, fargs, fresult, ellipsis, fabi);
     if (fct == NULL)
         return NULL;
 
@@ -6373,11 +6381,7 @@
 #if defined(MS_WIN32) && !defined(_WIN64)
         PyModule_AddIntConstant(m, "FFI_STDCALL", FFI_STDCALL) < 0 ||
 #endif
-#ifdef FFI_CDECL
-        PyModule_AddIntConstant(m, "FFI_CDECL", FFI_CDECL) < 0 ||   /* win32 */
-#else
         PyModule_AddIntConstant(m, "FFI_CDECL", FFI_DEFAULT_ABI) < 0 ||
-#endif
 
 #ifdef MS_WIN32
 #  ifdef _WIN64
diff --git a/c/parse_c_type.c b/c/parse_c_type.c
--- a/c/parse_c_type.c
+++ b/c/parse_c_type.c
@@ -40,6 +40,9 @@
     TOK_UNSIGNED,
     TOK_VOID,
     TOK_VOLATILE,
+
+    TOK_CDECL,
+    TOK_STDCALL,
 };
 
 typedef struct {
@@ -154,6 +157,8 @@
     switch (*p) {
     case '_':
         if (tok->size == 5 && !memcmp(p, "_Bool", 5))  tok->kind = TOK__BOOL;
+        if (tok->size == 7 && !memcmp(p,"__cdecl",7))  tok->kind = TOK_CDECL;
+        if (tok->size == 9 && !memcmp(p,"__stdcall",9))tok->kind = TOK_STDCALL;
         break;
     case 'c':
         if (tok->size == 4 && !memcmp(p, "char", 4))   tok->kind = TOK_CHAR;
@@ -225,7 +230,7 @@
        type).  The 'outer' argument is the index of the opcode outside
        this "sequel".
      */
-    int check_for_grouping;
+    int check_for_grouping, abi=0;
     _cffi_opcode_t result, *p_current;
 
  header:
@@ -242,6 +247,12 @@
         /* ignored for now */
         next_token(tok);
         goto header;
+    case TOK_CDECL:
+    case TOK_STDCALL:
+        /* must be in a function; checked below */
+        abi = tok->kind;
+        next_token(tok);
+        goto header;
     default:
         break;
     }
@@ -258,6 +269,11 @@
     while (tok->kind == TOK_OPEN_PAREN) {
         next_token(tok);
 
+        if (tok->kind == TOK_CDECL || tok->kind == TOK_STDCALL) {
+            abi = tok->kind;
+            next_token(tok);
+        }
+
         if ((check_for_grouping--) == 1 && (tok->kind == TOK_STAR ||
                                             tok->kind == TOK_CONST ||
                                             tok->kind == TOK_VOLATILE ||
@@ -275,7 +291,14 @@
         }
         else {
             /* function type */
-            int arg_total, base_index, arg_next, has_ellipsis=0;
+            int arg_total, base_index, arg_next, flags=0;
+
+            if (abi == TOK_STDCALL) {
+                flags = 2;
+                /* note that an ellipsis below will overwrite this flags,
+                   which is the goal: variadic functions are always cdecl */
+            }
+            abi = 0;
 
             if (tok->kind == TOK_VOID && get_following_char(tok) == ')') {
                 next_token(tok);
@@ -304,7 +327,7 @@
                     _cffi_opcode_t oarg;
 
                     if (tok->kind == TOK_DOTDOTDOT) {
-                        has_ellipsis = 1;
+                        flags = 1;   /* ellipsis */
                         next_token(tok);
                         break;
                     }
@@ -328,8 +351,7 @@
                     next_token(tok);
                 }
             }
-            tok->output[arg_next] = _CFFI_OP(_CFFI_OP_FUNCTION_END,
-                                             has_ellipsis);
+            tok->output[arg_next] = _CFFI_OP(_CFFI_OP_FUNCTION_END, flags);
         }
 
         if (tok->kind != TOK_CLOSE_PAREN)
@@ -337,6 +359,9 @@
         next_token(tok);
     }
 
+    if (abi != 0)
+        return parse_error(tok, "expected '('");
+
     while (tok->kind == TOK_OPEN_BRACKET) {
         *p_current = _CFFI_OP(_CFFI_GETOP(*p_current), tok->output_index);
         p_current = tok->output + tok->output_index;
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,24 @@
                    _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;
+        case 2:
+#if defined(MS_WIN32) && !defined(_WIN64)
+            abi = FFI_STDCALL;
+#else
+            abi = FFI_DEFAULT_ABI;
+#endif
+            break;
+        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 +595,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/cparser.py b/cffi/cparser.py
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -26,6 +26,9 @@
 _r_words = re.compile(r"\w+|\S")
 _parser_cache = None
 _r_int_literal = re.compile(r"-?0?x?[0-9a-f]+[lu]*$", re.IGNORECASE)
+_r_stdcall1 = re.compile(r"\b(__stdcall|WINAPI)\b")
+_r_stdcall2 = re.compile(r"[(]\s*(__stdcall|WINAPI)\b")
+_r_cdecl = re.compile(r"\b__cdecl\b")
 
 def _get_parser():
     global _parser_cache
@@ -44,6 +47,14 @@
         macrovalue = macrovalue.replace('\\\n', '').strip()
         macros[macroname] = macrovalue
     csource = _r_define.sub('', csource)
+    # BIG HACK: replace WINAPI or __stdcall with "volatile const".
+    # It doesn't make sense for the return type of a function to be
+    # "volatile volatile const", so we abuse it to detect __stdcall...
+    # Hack number 2 is that "int(volatile *fptr)();" is not valid C
+    # syntax, so we place the "volatile" before the opening parenthesis.
+    csource = _r_stdcall2.sub(' volatile volatile const(', csource)
+    csource = _r_stdcall1.sub(' volatile volatile const ', csource)
+    csource = _r_cdecl.sub(' ', csource)
     # Replace "[...]" with "[__dotdotdotarray__]"
     csource = _r_partial_array.sub('[__dotdotdotarray__]', csource)
     # Replace "...}" with "__dotdotdotNUM__}".  This construction should
@@ -449,7 +460,14 @@
         if not ellipsis and args == [model.void_type]:
             args = []
         result, quals = self._get_type_and_quals(typenode.type)
-        return model.RawFunctionType(tuple(args), result, ellipsis)
+        # the 'quals' on the result type are ignored.  HACK: we absure them
+        # to detect __stdcall functions: we textually replace "__stdcall"
+        # with "volatile volatile const" above.
+        abi = None
+        if hasattr(typenode.type, 'quals'): # else, probable syntax error anyway
+            if typenode.type.quals[-3:] == ['volatile', 'volatile', 'const']:
+                abi = '__stdcall'
+        return model.RawFunctionType(tuple(args), result, ellipsis, abi)
 
     def _as_func_arg(self, type, quals):
         if isinstance(type, model.ArrayType):
diff --git a/cffi/model.py b/cffi/model.py
--- a/cffi/model.py
+++ b/cffi/model.py
@@ -1,4 +1,4 @@
-import types
+import types, sys
 import weakref
 
 from .lock import allocate_lock
@@ -193,18 +193,21 @@
 
 
 class BaseFunctionType(BaseType):
-    _attrs_ = ('args', 'result', 'ellipsis')
+    _attrs_ = ('args', 'result', 'ellipsis', 'abi')
 
-    def __init__(self, args, result, ellipsis):
+    def __init__(self, args, result, ellipsis, abi=None):
         self.args = args
         self.result = result
         self.ellipsis = ellipsis
+        self.abi = abi
         #
         reprargs = [arg._get_c_name() for arg in self.args]
         if self.ellipsis:
             reprargs.append('...')
         reprargs = reprargs or ['void']
         replace_with = self._base_pattern % (', '.join(reprargs),)
+        if abi is not None:
+            replace_with = replace_with[:1] + abi + ' ' + replace_with[1:]
         self.c_name_with_marker = (
             self.result.c_name_with_marker.replace('&', replace_with))
 
@@ -222,7 +225,7 @@
                             "type, not a pointer-to-function type" % (self,))
 
     def as_function_pointer(self):
-        return FunctionPtrType(self.args, self.result, self.ellipsis)
+        return FunctionPtrType(self.args, self.result, self.ellipsis, self.abi)
 
 
 class FunctionPtrType(BaseFunctionType):
@@ -233,11 +236,18 @@
         args = []
         for tp in self.args:
             args.append(tp.get_cached_btype(ffi, finishlist))
+        abi_args = ()
+        if self.abi == "__stdcall":
+            if not self.ellipsis:    # __stdcall ignored for variadic funcs
+                try:
+                    abi_args = (ffi._backend.FFI_STDCALL,)
+                except AttributeError:
+                    pass
         return global_cache(self, ffi, 'new_function_type',
-                            tuple(args), result, self.ellipsis)
+                            tuple(args), result, self.ellipsis, *abi_args)
 
     def as_raw_function(self):
-        return RawFunctionType(self.args, self.result, self.ellipsis)
+        return RawFunctionType(self.args, self.result, self.ellipsis, self.abi)
 
 
 class PointerType(BaseType):
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_function.py b/testing/cffi0/test_function.py
--- a/testing/cffi0/test_function.py
+++ b/testing/cffi0/test_function.py
@@ -1,5 +1,5 @@
 import py
-from cffi import FFI
+from cffi import FFI, CDefError
 import math, os, sys
 import ctypes.util
 from cffi.backend_ctypes import CTypesBackend
@@ -427,3 +427,59 @@
         res = m.QueryPerformanceFrequency(p_freq)
         assert res != 0
         assert p_freq[0] != 0
+
+    def test_explicit_cdecl_stdcall(self):
+        if sys.platform != 'win32':
+            py.test.skip("Windows-only test")
+        if self.Backend is CTypesBackend:
+            py.test.skip("not with the ctypes backend")
+        #
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            BOOL QueryPerformanceFrequency(LONGLONG *lpFrequency);
+        """)
+        m = ffi.dlopen("Kernel32.dll")
+        tp = ffi.typeof(m.QueryPerformanceFrequency)
+        assert str(tp) == "<ctype 'int(*)(long long *)'>"
+        #
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            BOOL __cdecl QueryPerformanceFrequency(LONGLONG *lpFrequency);
+        """)
+        m = ffi.dlopen("Kernel32.dll")
+        tpc = ffi.typeof(m.QueryPerformanceFrequency)
+        assert tpc is tp
+        #
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            BOOL WINAPI QueryPerformanceFrequency(LONGLONG *lpFrequency);
+        """)
+        m = ffi.dlopen("Kernel32.dll")
+        tps = ffi.typeof(m.QueryPerformanceFrequency)
+        assert tps is not tpc
+        assert str(tps) == "<ctype 'int(__stdcall *)(long long *)'>"
+        #
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("typedef int (__cdecl *fnc_t)(int);")
+        ffi.cdef("typedef int (__stdcall *fns_t)(int);")
+        tpc = ffi.typeof("fnc_t")
+        tps = ffi.typeof("fns_t")
+        assert str(tpc) == "<ctype 'int(*)(int)'>"
+        assert str(tps) == "<ctype 'int(__stdcall *)(int)'>"
+        #
+        fnc = ffi.cast("fnc_t", 0)
+        fns = ffi.cast("fns_t", 0)
+        ffi.new("fnc_t[]", [fnc])
+        py.test.raises(TypeError, ffi.new, "fnc_t[]", [fns])
+        py.test.raises(TypeError, ffi.new, "fns_t[]", [fnc])
+        ffi.new("fns_t[]", [fns])
+
+    def test_stdcall_only_on_windows(self):
+        if sys.platform == 'win32':
+            py.test.skip("not-Windows-only test")
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("double __stdcall sin(double x);")     # stdcall ignored
+        m = ffi.dlopen(lib_m)
+        assert "double(*)(double)" in str(ffi.typeof(m.sin))
+        x = m.sin(1.23)
+        assert x == math.sin(1.23)
diff --git a/testing/cffi0/test_parsing.py b/testing/cffi0/test_parsing.py
--- a/testing/cffi0/test_parsing.py
+++ b/testing/cffi0/test_parsing.py
@@ -364,3 +364,17 @@
     assert C.TWO == 2
     assert C.NIL == 0
     assert C.NEG == -1
+
+def test_stdcall():
+    ffi = FFI()
+    tp = ffi.typeof("int(*)(int __stdcall x(int),"
+                    "       long (__cdecl*y)(void),"
+                    "       short(WINAPI *z)(short))")
+    if sys.platform == 'win32':
+        stdcall = '__stdcall '
+    else:
+        stdcall = ''
+    assert str(tp) == (
+        "<ctype 'int(*)(int(%s*)(int), "
+                        "long(*)(), "
+                        "short(%s*)(short))'>" % (stdcall, stdcall))
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,25 +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():
-    py.test.skip("later")
-    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'):
@@ -2260,3 +2241,180 @@
     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_0():
+    ffi = FFI()
+    ffi.cdef("""
+        int call1(int(__cdecl   *cb)(int));
+        int (*const call2)(int(__stdcall *cb)(int));
+    """)
+    lib = ffi.verify(r"""
+        #ifndef _MSC_VER
+        #  define __stdcall  /* nothing */
+        #endif
+        int call1(int(*cb)(int)) {
+            int i, result = 0;
+            //printf("call1: cb = %p\n", cb);
+            for (i = 0; i < 1000; i++)
+                result += cb(i);
+            //printf("result = %d\n", result);
+            return result;
+        }
+        int call2(int(__stdcall *cb)(int)) {
+            int i, result = 0;
+            //printf("call2: cb = %p\n", cb);
+            for (i = 0; i < 1000; i++)
+                result += cb(-i);
+            //printf("result = %d\n", result);
+            return result;
+        }
+    """)
+    @ffi.callback("int(int)")
+    def cb1(x):
+        return x * 2
+    @ffi.callback("int __stdcall(int)")
+    def cb2(x):
+        return x * 3
+    print 'cb1 =', cb1
+    res = lib.call1(cb1)
+    assert res == 500*999*2
+    print 'cb2 =', cb2
+    print ffi.typeof(lib.call2)
+    print 'call2 =', lib.call2
+    res = lib.call2(cb2)
+    print '...'
+    assert res == -500*999*3
+    print 'done'
+    if sys.platform == 'win32':
+        assert '__stdcall' in str(ffi.typeof(cb2))
+        assert '__stdcall' not in str(ffi.typeof(cb1))
+        py.test.raises(TypeError, lib.call1, cb2)
+        py.test.raises(TypeError, lib.call2, cb1)
+    else:
+        assert '__stdcall' not in str(ffi.typeof(cb2))
+        assert ffi.typeof(cb2) is ffi.typeof(cb1)
+
+def test_win32_calling_convention_1():
+    ffi = FFI()
+    ffi.cdef("""
+        int __cdecl   call1(int(__cdecl   *cb)(int));
+        int __stdcall call2(int(__stdcall *cb)(int));
+        int (__cdecl   *const cb1)(int);
+        int (__stdcall *const cb2)(int);
+    """)
+    lib = ffi.verify(r"""
+        #ifndef _MSC_VER
+        #  define __cdecl
+        #  define __stdcall
+        #endif
+        int __cdecl   cb1(int x) { return x * 2; }
+        int __stdcall cb2(int x) { return x * 3; }
+
+        int __cdecl call1(int(__cdecl *cb)(int)) {
+            int i, result = 0;
+            //printf("here1\n");
+            //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1);
+            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(lib.cb1) == 500*999*2
+    assert lib.call2(lib.cb2) == -500*999*3
+
+def test_win32_calling_convention_2():
+    # 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 __stdcall call1(int(__cdecl   *cb)(int));
+        int __cdecl   call2(int(__stdcall *cb)(int));
+        int (__cdecl   *const cb1)(int);
+        int (__stdcall *const cb2)(int);
+    """)
+    lib = ffi.verify(r"""
+        #ifndef _MSC_VER
+        #  define __cdecl
+        #  define __stdcall
+        #endif
+        int __cdecl call1(int(__cdecl *cb)(int)) {
+            int i, result = 0;
+            for (i = 0; i < 1000; i++)
+                result += cb(i);
+            return result;
+        }
+        int __stdcall call2(int(__stdcall *cb)(int)) {
+            int i, result = 0;
+            for (i = 0; i < 1000; i++)
+                result += cb(-i);
+            return result;
+        }
+        int __cdecl   cb1(int x) { return x * 2; }
+        int __stdcall cb2(int x) { return x * 3; }
+    """)
+    assert lib.call1(lib.cb1) == 500*999*2
+    assert lib.call2(lib.cb2) == -500*999*3
+
+def test_win32_calling_convention_3():
+    ffi = FFI()
+    ffi.cdef("""
+        struct point { int x, y; };
+
+        int (*const cb1)(struct point);
+        int (__stdcall *const cb2)(struct point);
+
+        struct point __stdcall call1(int(*cb)(struct point));
+        struct point call2(int(__stdcall *cb)(struct point));
+    """)
+    lib = ffi.verify(r"""
+        #ifndef _MSC_VER
+        #  define __cdecl
+        #  define __stdcall
+        #endif
+        struct point { int x, y; };
+        int           cb1(struct point pt) { return pt.x + 10 * pt.y; }
+        int __stdcall 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;
+        }
+    """)
+    if sys.platform == 'win32':
+        py.test.raises(TypeError, lib.call1, lib.cb2)
+        py.test.raises(TypeError, lib.call2, lib.cb1)
+    pt = lib.call1(lib.cb1)
+    assert (pt.x, pt.y) == (-9*500*999, 9*500*999)
+    pt = lib.call2(lib.cb2)
+    assert (pt.x, pt.y) == (99*500*999, -99*500*999)
diff --git a/testing/cffi1/test_parse_c_type.py b/testing/cffi1/test_parse_c_type.py
--- a/testing/cffi1/test_parse_c_type.py
+++ b/testing/cffi1/test_parse_c_type.py
@@ -341,3 +341,17 @@
     # not supported (really obscure):
     #    "char[+5]"
     #    "char['A']"
+
+def test_stdcall_cdecl():
+    assert parse("int __stdcall(int)") == [Prim(lib._CFFI_PRIM_INT),
+                                           '->', Func(0), NoOp(4), FuncEnd(2),
+                                           Prim(lib._CFFI_PRIM_INT)]
+    assert parse("int __stdcall func(int)") == parse("int __stdcall(int)")
+    assert parse("int (__stdcall *)()") == [Prim(lib._CFFI_PRIM_INT),
+                                            NoOp(3), '->', Pointer(1),
+                                            Func(0), FuncEnd(2), 0]
+    assert parse("int (__stdcall *p)()") == parse("int (__stdcall*)()")
+    parse_error("__stdcall int", "identifier expected", 0)
+    parse_error("__cdecl int", "identifier expected", 0)
+    parse_error("int __stdcall", "expected '('", 13)
+    parse_error("int __cdecl", "expected '('", 11)
diff --git a/testing/cffi1/test_realize_c_type.py b/testing/cffi1/test_realize_c_type.py
--- a/testing/cffi1/test_realize_c_type.py
+++ b/testing/cffi1/test_realize_c_type.py
@@ -1,4 +1,4 @@
-import py
+import py, sys
 from cffi import cffi_opcode
 
 
@@ -46,3 +46,29 @@
 def test_all_primitives():
     for name in cffi_opcode.PRIMITIVE_TO_INDEX:
         check(name, name)
+
+
+def check_func(input, expected_output=None):
+    import _cffi_backend
+    ffi = _cffi_backend.FFI()
+    ct = ffi.typeof(ffi.callback(input, lambda: None))
+    assert isinstance(ct, ffi.CType)
+    if sys.platform != 'win32':
+        expected_output = expected_output.replace('__stdcall *', '*')
+    assert ct.cname == expected_output
+
+def test_funcptr_stdcall():
+    check_func("int(int)", "int(*)(int)")
+    check_func("int foobar(int)", "int(*)(int)")
+    check_func("int __stdcall(int)", "int(__stdcall *)(int)")
+    check_func("int __stdcall foobar(int)", "int(__stdcall *)(int)")
+    check_func("void __cdecl(void)", "void(*)()")
+    check_func("void __cdecl foobar(void)", "void(*)()")
+    check_func("void __stdcall(void)", "void(__stdcall *)()")
+    check_func("void __stdcall foobar(long, short)",
+                   "void(__stdcall *)(long, short)")
+    check_func("void(void __cdecl(void), void __stdcall(void))",
+                   "void(*)(void(*)(), void(__stdcall *)())")
+
+def test_variadic_overrides_stdcall():
+    check("void (__stdcall*)(int, ...)", "void(*)(int, ...)")
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
@@ -1280,3 +1280,200 @@
     """)
     assert lib.aaa == 42
     py.test.raises(AttributeError, "lib.aaa = 43")
+
+def test_win32_calling_convention_0():
+    ffi = FFI()
+    ffi.cdef("""
+        int call1(int(__cdecl   *cb)(int));
+        int (*const call2)(int(__stdcall *cb)(int));
+    """)
+    lib = verify(ffi, 'test_win32_calling_convention_0', r"""
+        #ifndef _MSC_VER
+        #  define __stdcall  /* nothing */
+        #endif
+        int call1(int(*cb)(int)) {
+            int i, result = 0;
+            //printf("call1: cb = %p\n", cb);
+            for (i = 0; i < 1000; i++)
+                result += cb(i);
+            //printf("result = %d\n", result);
+            return result;
+        }
+        int call2(int(__stdcall *cb)(int)) {
+            int i, result = 0;
+            //printf("call2: cb = %p\n", cb);
+            for (i = 0; i < 1000; i++)
+                result += cb(-i);
+            //printf("result = %d\n", result);
+            return result;
+        }
+    """)
+    @ffi.callback("int(int)")
+    def cb1(x):
+        return x * 2
+    @ffi.callback("int __stdcall(int)")
+    def cb2(x):
+        return x * 3
+    res = lib.call1(cb1)
+    assert res == 500*999*2
+    assert res == ffi.addressof(lib, 'call1')(cb1)
+    res = lib.call2(cb2)
+    assert res == -500*999*3
+    assert res == ffi.addressof(lib, 'call2')(cb2)
+    if sys.platform == 'win32':
+        assert '__stdcall' in str(ffi.typeof(cb2))
+        assert '__stdcall' not in str(ffi.typeof(cb1))
+        py.test.raises(TypeError, lib.call1, cb2)
+        py.test.raises(TypeError, lib.call2, cb1)
+    else:
+        assert '__stdcall' not in str(ffi.typeof(cb2))
+        assert ffi.typeof(cb2) is ffi.typeof(cb1)
+
+def test_win32_calling_convention_1():
+    ffi = FFI()
+    ffi.cdef("""
+        int __cdecl   call1(int(__cdecl   *cb)(int));
+        int __stdcall call2(int(__stdcall *cb)(int));
+        int (__cdecl   *const cb1)(int);
+        int (__stdcall *const cb2)(int);
+    """)
+    lib = verify(ffi, 'test_win32_calling_convention_1', r"""
+        #ifndef _MSC_VER
+        #  define __cdecl
+        #  define __stdcall
+        #endif
+        int __cdecl   cb1(int x) { return x * 2; }
+        int __stdcall cb2(int x) { return x * 3; }
+
+        int __cdecl call1(int(__cdecl *cb)(int)) {
+            int i, result = 0;
+            //printf("here1\n");
+            //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1);
+            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;
+        }
+    """)
+    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():
+    # 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 __stdcall call1(int(__cdecl   *cb)(int));
+        int __cdecl   call2(int(__stdcall *cb)(int));
+        int (__cdecl   *const cb1)(int);
+        int (__stdcall *const cb2)(int);
+    """)
+    lib = verify(ffi, 'test_win32_calling_convention_2', """
+        #ifndef _MSC_VER
+        #  define __cdecl
+        #  define __stdcall
+        #endif
+        int __cdecl call1(int(__cdecl *cb)(int)) {
+            int i, result = 0;
+            for (i = 0; i < 1000; i++)
+                result += cb(i);
+            return result;
+        }
+        int __stdcall call2(int(__stdcall *cb)(int)) {
+            int i, result = 0;
+            for (i = 0; i < 1000; i++)
+                result += cb(-i);
+            return result;
+        }
+        int __cdecl   cb1(int x) { return x * 2; }
+        int __stdcall cb2(int x) { return x * 3; }
+    """)
+    ptr_call1 = ffi.addressof(lib, 'call1')
+    ptr_call2 = ffi.addressof(lib, 'call2')
+    if sys.platform == 'win32':
+        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():
+    ffi = FFI()
+    ffi.cdef("""
+        struct point { int x, y; };
+
+        int (*const cb1)(struct point);
+        int (__stdcall *const cb2)(struct point);
+
+        struct point __stdcall call1(int(*cb)(struct point));
+        struct point call2(int(__stdcall *cb)(struct point));
+    """)
+    lib = verify(ffi, 'test_win32_calling_convention_3', r"""
+        #ifndef _MSC_VER
+        #  define __cdecl
+        #  define __stdcall
+        #endif
+        struct point { int x, y; };
+        int           cb1(struct point pt) { return pt.x + 10 * pt.y; }
+        int __stdcall 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')
+    if sys.platform == 'win32':
+        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'))
+    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_verify1.py b/testing/cffi1/test_verify1.py
--- a/testing/cffi1/test_verify1.py
+++ b/testing/cffi1/test_verify1.py
@@ -1200,25 +1200,6 @@
     lib = ffi.verify('#include <math.h>', libraries=lib_m)
     assert lib.sin(1.23) == math.sin(1.23)
 
-def test_callback_calling_convention():
-    py.test.skip("later")
-    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'):


More information about the pypy-commit mailing list