[pypy-commit] cffi win32-stdcall: Support directly __stdcall or WINAPI (or __cdecl, ignored) inside

arigo noreply at buildbot.pypy.org
Tue Oct 6 10:36:17 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: win32-stdcall
Changeset: r2311:b1eed93a8c20
Date: 2015-10-06 10:27 +0200
http://bitbucket.org/cffi/cffi/changeset/b1eed93a8c20/

Log:	Support directly __stdcall or WINAPI (or __cdecl, ignored) inside
	cparser.

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, calling_conv=None):
+    def cdef(self, csource, override=False, packed=False):
         """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,8 +104,7 @@
                 raise TypeError("cdef() argument must be a string")
             csource = csource.encode('ascii')
         with self._lock:
-            self._parser.parse(csource, override=override, packed=packed,
-                               calling_conv=calling_conv)
+            self._parser.parse(csource, override=override, packed=packed)
             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
@@ -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
@@ -103,7 +114,6 @@
         self._structnode2type = weakref.WeakKeyDictionary()
         self._override = False
         self._packed = False
-        self._abi = None
         self._int_constants = {}
         self._recomplete = []
         self._uses_new_feature = None
@@ -163,26 +173,16 @@
             msg = 'parse error\n%s' % (msg,)
         raise api.CDefError(msg)
 
-    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,))
+    def parse(self, csource, override=False, packed=False):
         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)
@@ -460,7 +460,13 @@
         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, self._abi)
+        # 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 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
@@ -236,22 +236,13 @@
         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" % (self,))
-                else:
-                    from . import api
-                    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,))
+        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, *abi_args)
 
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))


More information about the pypy-commit mailing list