Author: Armin Rigo <[email protected]>
Branch: win32-stdcall
Changeset: r2305:91834f9534f8
Date: 2015-10-05 20:05 +0200
http://bitbucket.org/cffi/cffi/changeset/91834f9534f8/

Log:    Starting, with exactly two function types: no-abi (i.e. cdecl on
        windows), or stdcall.

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -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;
 
diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -91,7 +91,7 @@
             self.NULL = self.cast(self.BVoidP, 0)
             self.CData, self.CType = backend._get_types()
 
-    def cdef(self, csource, override=False, packed=False):
+    def cdef(self, csource, override=False, packed=False, calling_conv=None):
         """Parse the given C source.  This registers all declared functions,
         types, and global variables.  The functions and global variables can
         then be accessed via either 'ffi.dlopen()' or 'ffi.verify()'.
@@ -104,7 +104,8 @@
                 raise TypeError("cdef() argument must be a string")
             csource = csource.encode('ascii')
         with self._lock:
-            self._parser.parse(csource, override=override, packed=packed)
+            self._parser.parse(csource, override=override, packed=packed,
+                               calling_conv=calling_conv)
             self._cdefsources.append(csource)
             if override:
                 for cache in self._function_caches:
diff --git a/cffi/cparser.py b/cffi/cparser.py
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -103,6 +103,7 @@
         self._structnode2type = weakref.WeakKeyDictionary()
         self._override = False
         self._packed = False
+        self._abi = None
         self._int_constants = {}
         self._recomplete = []
         self._uses_new_feature = None
@@ -162,16 +163,26 @@
             msg = 'parse error\n%s' % (msg,)
         raise api.CDefError(msg)
 
-    def parse(self, csource, override=False, packed=False):
+    def parse(self, csource, override=False, packed=False, calling_conv=None):
+        if calling_conv is None or calling_conv == "cdecl":
+            abi = None
+        elif calling_conv == "stdcall":
+            abi = "stdcall"
+        else:
+            raise api.CDefError("calling_conv must be 'cdecl' or 'stdcall';"
+                                " got %r" % (calling_conv,))
         prev_override = self._override
         prev_packed = self._packed
+        prev_abi = self._abi
         try:
             self._override = override
             self._packed = packed
+            self._abi = abi
             self._internal_parse(csource)
         finally:
             self._override = prev_override
             self._packed = prev_packed
+            self._abi = prev_abi
 
     def _internal_parse(self, csource):
         ast, macros, csource = self._parse(csource)
@@ -449,7 +460,7 @@
         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)
+        return model.RawFunctionType(tuple(args), result, ellipsis, self._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
@@ -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,25 @@
         args = []
         for tp in self.args:
             args.append(tp.get_cached_btype(ffi, finishlist))
+        if self.abi is None:
+            abi_args = ()
+        elif self.abi == "stdcall":
+            try:
+                abi_args = (ffi._backend.FFI_STDCALL,)
+            except AttributeError:
+                if sys.platform == "win32":
+                    raise NotImplementedError("%r: stdcall with ctypes 
backend")
+                else:
+                    from . import api
+                    raise api.CDefError("%r: '__stdcall' only for Windows")
+            import pdb;pdb.set_trace()
+        else:
+            raise NotImplementedError("abi=%r" % (self.abi,))
         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/testing/cffi0/test_function.py b/testing/cffi0/test_function.py
--- a/testing/cffi0/test_function.py
+++ b/testing/cffi0/test_function.py
@@ -440,9 +440,7 @@
         """)
         m = ffi.dlopen("Kernel32.dll")
         tp = ffi.typeof(m.QueryPerformanceFrequency)
-        assert 'stdcall' not in str(tp) and 'cdecl' not in str(tp)
-        assert tp is (
-            ffi.typeof(ffi.addressof(m, 'QueryPerformanceFrequency')).item)
+        assert str(tp) == "<ctype 'int(*)(long long *)'>"
         #
         ffi = FFI(backend=self.Backend())
         ffi.cdef("""
@@ -451,8 +449,6 @@
         m = ffi.dlopen("Kernel32.dll")
         tpc = ffi.typeof(m.QueryPerformanceFrequency)
         assert tpc is tp
-        assert tpc is (
-            ffi.typeof(ffi.addressof(m, 'QueryPerformanceFrequency')).item)
         #
         ffi = FFI(backend=self.Backend())
         ffi.cdef("""
@@ -461,9 +457,7 @@
         m = ffi.dlopen("Kernel32.dll")
         tps = ffi.typeof(m.QueryPerformanceFrequency)
         assert tps is not tpc
-        assert '__stdcall' in str(tps) and 'cdecl' not in str(tps)
-        assert tps is (
-            ffi.typeof(ffi.addressof(m, 'QueryPerformanceFrequency')).item)
+        assert str(tps) == "<ctype 'int(__stdcall *)(long long *)'>"
         #
         ffi = FFI(backend=self.Backend())
         ffi.cdef("typedef int (*fnc_t)(int);", calling_conv="cdecl")
@@ -472,3 +466,13 @@
         tps = ffi.typeof("fns_t")
         assert str(tpc) == "<ctype 'int(*)(int)'>"
         assert str(tps) == "<ctype 'int(__stdcall *)(int)'>"
+
+    def test_stdcall_only_on_windows(self):
+        if sys.platform == 'win32':
+            py.test.skip("not-Windows-only test")
+        ffi = FFI(backend=self.Backend())
+        e = py.test.raises(CDefError, ffi.cdef, """
+            BOOL QueryPerformanceFrequency(LONGLONG *lpFrequency);
+        """, calling_conv="stdcall")
+        assert str(e.value) == (
+            "<int(__stdcall *)(int)>: '__stdcall' only for Windows")
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to