summaryrefslogtreecommitdiff
path: root/cffi
diff options
context:
space:
mode:
authorArmin Rigo <arigo@tunes.org>2015-10-06 10:27:50 +0200
committerArmin Rigo <arigo@tunes.org>2015-10-06 10:27:50 +0200
commit7ef565fb0fe6167ba278ad4ca551f7e8441b4599 (patch)
treef4f67b7698fed3e7eb1982625443ef47b9ce0602 /cffi
parent92417155a9540b4e436c74813cca635f555624ac (diff)
downloadcffi-7ef565fb0fe6167ba278ad4ca551f7e8441b4599.tar.gz
Support directly __stdcall or WINAPI (or __cdecl, ignored) inside
cparser.
Diffstat (limited to 'cffi')
-rw-r--r--cffi/api.py5
-rw-r--r--cffi/cparser.py32
-rw-r--r--cffi/model.py23
3 files changed, 28 insertions, 32 deletions
diff --git a/cffi/api.py b/cffi/api.py
index d1dea51..0874481 100644
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -91,7 +91,7 @@ class FFI(object):
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 @@ class FFI(object):
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
index 9e9d270..4b1caf1 100644
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -26,6 +26,9 @@ _r_partial_array = re.compile(r"\[\s*\.\.\.\s*\]")
_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 @@ def _preprocess(csource):
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 @@ class Parser(object):
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 @@ class Parser(object):
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 @@ class Parser(object):
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
index 7296e73..b9da706 100644
--- a/cffi/model.py
+++ b/cffi/model.py
@@ -236,22 +236,13 @@ class FunctionPtrType(BaseFunctionType):
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)