diff options
author | Armin Rigo <arigo@tunes.org> | 2015-10-06 10:27:50 +0200 |
---|---|---|
committer | Armin Rigo <arigo@tunes.org> | 2015-10-06 10:27:50 +0200 |
commit | 7ef565fb0fe6167ba278ad4ca551f7e8441b4599 (patch) | |
tree | f4f67b7698fed3e7eb1982625443ef47b9ce0602 /cffi | |
parent | 92417155a9540b4e436c74813cca635f555624ac (diff) | |
download | cffi-7ef565fb0fe6167ba278ad4ca551f7e8441b4599.tar.gz |
Support directly __stdcall or WINAPI (or __cdecl, ignored) inside
cparser.
Diffstat (limited to 'cffi')
-rw-r--r-- | cffi/api.py | 5 | ||||
-rw-r--r-- | cffi/cparser.py | 32 | ||||
-rw-r--r-- | cffi/model.py | 23 |
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) |