[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