Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r80015:773e6136f72e Date: 2015-10-07 12:25 +0200 http://bitbucket.org/pypy/pypy/changeset/773e6136f72e/
Log: hg merge cffi-stdcall Win32 support for the "__stdcall" calling convention diff --git a/lib_pypy/cffi/cparser.py b/lib_pypy/cffi/cparser.py --- a/lib_pypy/cffi/cparser.py +++ b/lib_pypy/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 @@ -449,7 +460,14 @@ 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) + # 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 hasattr(typenode.type, 'quals'): # else, probable syntax error anyway + 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/lib_pypy/cffi/model.py b/lib_pypy/cffi/model.py --- a/lib_pypy/cffi/model.py +++ b/lib_pypy/cffi/model.py @@ -1,4 +1,4 @@ -import types +import types, sys import weakref from .lock import allocate_lock @@ -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,18 @@ args = [] for tp in self.args: args.append(tp.get_cached_btype(ffi, finishlist)) + 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) + 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/lib_pypy/cffi/parse_c_type.h b/lib_pypy/cffi/parse_c_type.h --- a/lib_pypy/cffi/parse_c_type.h +++ b/lib_pypy/cffi/parse_c_type.h @@ -5,7 +5,7 @@ #define _CFFI_OP(opcode, arg) (_cffi_opcode_t)(opcode | (((uintptr_t)(arg)) << 8)) #define _CFFI_GETOP(cffi_opcode) ((unsigned char)(uintptr_t)cffi_opcode) -#define _CFFI_GETARG(cffi_opcode) (((uintptr_t)cffi_opcode) >> 8) +#define _CFFI_GETARG(cffi_opcode) (((intptr_t)cffi_opcode) >> 8) #define _CFFI_OP_PRIMITIVE 1 #define _CFFI_OP_POINTER 3 diff --git a/lib_pypy/cffi/recompiler.py b/lib_pypy/cffi/recompiler.py --- a/lib_pypy/cffi/recompiler.py +++ b/lib_pypy/cffi/recompiler.py @@ -607,7 +607,11 @@ call_arguments.append('x%d' % i) repr_arguments = ', '.join(arguments) repr_arguments = repr_arguments or 'void' - name_and_arguments = '_cffi_d_%s(%s)' % (name, repr_arguments) + if tp.abi: + abi = tp.abi + ' ' + else: + abi = '' + name_and_arguments = '%s_cffi_d_%s(%s)' % (abi, name, repr_arguments) prnt('static %s' % (tp.result.get_c_name(name_and_arguments),)) prnt('{') call_arguments = ', '.join(call_arguments) @@ -710,7 +714,8 @@ if difference: repr_arguments = ', '.join(arguments) repr_arguments = repr_arguments or 'void' - name_and_arguments = '_cffi_f_%s(%s)' % (name, repr_arguments) + name_and_arguments = '%s_cffi_f_%s(%s)' % (abi, name, + repr_arguments) prnt('static %s' % (tp_result.get_c_name(name_and_arguments),)) prnt('{') if result_decl: @@ -1135,7 +1140,13 @@ else: self.cffi_types[index] = CffiOp(OP_NOOP, realindex) index += 1 - self.cffi_types[index] = CffiOp(OP_FUNCTION_END, int(tp.ellipsis)) + flags = int(tp.ellipsis) + if tp.abi is not None: + if tp.abi == '__stdcall': + flags |= 2 + else: + raise NotImplementedError("abi=%r" % (tp.abi,)) + self.cffi_types[index] = CffiOp(OP_FUNCTION_END, flags) def _emit_bytecode_PointerType(self, tp, index): self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[tp.totype]) diff --git a/lib_pypy/cffi/vengine_gen.py b/lib_pypy/cffi/vengine_gen.py --- a/lib_pypy/cffi/vengine_gen.py +++ b/lib_pypy/cffi/vengine_gen.py @@ -159,7 +159,11 @@ arglist = ', '.join(arglist) or 'void' wrappername = '_cffi_f_%s' % name self.export_symbols.append(wrappername) - funcdecl = ' %s(%s)' % (wrappername, arglist) + if tp.abi: + abi = tp.abi + ' ' + else: + abi = '' + funcdecl = ' %s%s(%s)' % (abi, wrappername, arglist) context = 'result of %s' % name prnt(tpresult.get_c_name(funcdecl, context)) prnt('{') diff --git a/pypy/module/_cffi_backend/__init__.py b/pypy/module/_cffi_backend/__init__.py --- a/pypy/module/_cffi_backend/__init__.py +++ b/pypy/module/_cffi_backend/__init__.py @@ -1,9 +1,16 @@ import sys from pypy.interpreter.mixedmodule import MixedModule -from rpython.rlib import rdynload +from rpython.rlib import rdynload, clibffi VERSION = "1.3.0" +FFI_DEFAULT_ABI = clibffi.FFI_DEFAULT_ABI +try: + FFI_STDCALL = clibffi.FFI_STDCALL + has_stdcall = True +except AttributeError: + has_stdcall = False + class Module(MixedModule): @@ -44,8 +51,8 @@ 'get_errno': 'cerrno.get_errno', 'set_errno': 'cerrno.set_errno', - 'FFI_DEFAULT_ABI': 'ctypefunc._get_abi(space, "FFI_DEFAULT_ABI")', - 'FFI_CDECL': 'ctypefunc._get_abi(space,"FFI_DEFAULT_ABI")',#win32 name + 'FFI_DEFAULT_ABI': 'space.wrap(%d)' % FFI_DEFAULT_ABI, + 'FFI_CDECL': 'space.wrap(%d)' % FFI_DEFAULT_ABI, # win32 name # CFFI 1.0 'FFI': 'ffi_obj.W_FFIObject', @@ -53,6 +60,9 @@ if sys.platform == 'win32': interpleveldefs['getwinerror'] = 'cerrno.getwinerror' + if has_stdcall: + interpleveldefs['FFI_STDCALL'] = 'space.wrap(%d)' % FFI_STDCALL + def get_dict_rtld_constants(): found = {} diff --git a/pypy/module/_cffi_backend/ctypefunc.py b/pypy/module/_cffi_backend/ctypefunc.py --- a/pypy/module/_cffi_backend/ctypefunc.py +++ b/pypy/module/_cffi_backend/ctypefunc.py @@ -12,6 +12,7 @@ from rpython.rtyper.lltypesystem import lltype, llmemory, rffi from pypy.interpreter.error import OperationError, oefmt +from pypy.module import _cffi_backend from pypy.module._cffi_backend import ctypearray, cdataobj, cerrno from pypy.module._cffi_backend.ctypeobj import W_CType from pypy.module._cffi_backend.ctypeptr import W_CTypePtrBase, W_CTypePointer @@ -23,20 +24,21 @@ class W_CTypeFunc(W_CTypePtrBase): - _attrs_ = ['fargs', 'ellipsis', 'cif_descr'] - _immutable_fields_ = ['fargs[*]', 'ellipsis', 'cif_descr'] + _attrs_ = ['fargs', 'ellipsis', 'abi', 'cif_descr'] + _immutable_fields_ = ['fargs[*]', 'ellipsis', 'abi', 'cif_descr'] kind = "function" cif_descr = lltype.nullptr(CIF_DESCRIPTION) - def __init__(self, space, fargs, fresult, ellipsis): + def __init__(self, space, fargs, fresult, ellipsis, abi): assert isinstance(ellipsis, bool) - extra = self._compute_extra_text(fargs, fresult, ellipsis) + extra, xpos = self._compute_extra_text(fargs, fresult, ellipsis, abi) size = rffi.sizeof(rffi.VOIDP) - W_CTypePtrBase.__init__(self, space, size, extra, 2, fresult, + W_CTypePtrBase.__init__(self, space, size, extra, xpos, fresult, could_cast_anything=False) self.fargs = fargs self.ellipsis = ellipsis + self.abi = abi # fresult is stored in self.ctitem if not ellipsis: @@ -44,7 +46,7 @@ # at all. The cif is computed on every call from the actual # types passed in. For all other functions, the cif_descr # is computed here. - builder = CifDescrBuilder(fargs, fresult) + builder = CifDescrBuilder(fargs, fresult, abi) try: builder.rawallocate(self) except OperationError, e: @@ -76,7 +78,7 @@ ctypefunc.fargs = fvarargs ctypefunc.ctitem = self.ctitem #ctypefunc.cif_descr = NULL --- already provided as the default - CifDescrBuilder(fvarargs, self.ctitem).rawallocate(ctypefunc) + CifDescrBuilder(fvarargs, self.ctitem, self.abi).rawallocate(ctypefunc) return ctypefunc @rgc.must_be_light_finalizer @@ -84,8 +86,13 @@ if self.cif_descr: lltype.free(self.cif_descr, flavor='raw') - def _compute_extra_text(self, fargs, fresult, ellipsis): + def _compute_extra_text(self, fargs, fresult, ellipsis, abi): + from pypy.module._cffi_backend import newtype argnames = ['(*)('] + xpos = 2 + if _cffi_backend.has_stdcall and abi == _cffi_backend.FFI_STDCALL: + argnames[0] = '(__stdcall *)(' + xpos += len('__stdcall ') for i, farg in enumerate(fargs): if i > 0: argnames.append(', ') @@ -95,7 +102,7 @@ argnames.append(', ') argnames.append('...') argnames.append(')') - return ''.join(argnames) + return ''.join(argnames), xpos def _fget(self, attrchar): if attrchar == 'a': # args @@ -106,7 +113,7 @@ if attrchar == 'E': # ellipsis return self.space.wrap(self.ellipsis) if attrchar == 'A': # abi - return self.space.wrap(clibffi.FFI_DEFAULT_ABI) # XXX + return self.space.wrap(self.abi) return W_CTypePtrBase._fget(self, attrchar) def call(self, funcaddr, args_w): @@ -181,11 +188,6 @@ def set_mustfree_flag(data, flag): rffi.ptradd(data, -1)[0] = chr(flag) -def _get_abi(space, name): - abi = getattr(clibffi, name) - assert isinstance(abi, int) - return space.wrap(abi) - # ____________________________________________________________ @@ -260,9 +262,10 @@ class CifDescrBuilder(object): rawmem = lltype.nullptr(rffi.CCHARP.TO) - def __init__(self, fargs, fresult): + def __init__(self, fargs, fresult, fabi): self.fargs = fargs self.fresult = fresult + self.fabi = fabi def fb_alloc(self, size): size = llmemory.raw_malloc_usage(size) @@ -421,7 +424,7 @@ cif_descr.exchange_size = exchange_offset def fb_extra_fields(self, cif_descr): - cif_descr.abi = clibffi.FFI_DEFAULT_ABI # XXX + cif_descr.abi = self.fabi cif_descr.nargs = len(self.fargs) cif_descr.rtype = self.rtype cif_descr.atypes = self.atypes diff --git a/pypy/module/_cffi_backend/ctypeobj.py b/pypy/module/_cffi_backend/ctypeobj.py --- a/pypy/module/_cffi_backend/ctypeobj.py +++ b/pypy/module/_cffi_backend/ctypeobj.py @@ -143,7 +143,7 @@ # obscure hack when untranslated, maybe, approximate, don't use if isinstance(align, llmemory.FieldOffset): align = rffi.sizeof(align.TYPE.y) - if (1 << (8*align-2)) > sys.maxint: + if sys.platform != 'win32' and (1 << (8*align-2)) > sys.maxint: align /= 2 else: # a different hack when translated, to avoid seeing constants diff --git a/pypy/module/_cffi_backend/newtype.py b/pypy/module/_cffi_backend/newtype.py --- a/pypy/module/_cffi_backend/newtype.py +++ b/pypy/module/_cffi_backend/newtype.py @@ -4,10 +4,11 @@ from rpython.rlib.objectmodel import specialize, r_dict, compute_identity_hash from rpython.rlib.rarithmetic import ovfcheck, intmask -from rpython.rlib import jit, rweakref +from rpython.rlib import jit, rweakref, clibffi from rpython.rtyper.lltypesystem import lltype, rffi from rpython.rtyper.tool import rffi_platform +from pypy.module import _cffi_backend from pypy.module._cffi_backend import (ctypeobj, ctypeprim, ctypeptr, ctypearray, ctypestruct, ctypevoid, ctypeenum) @@ -592,8 +593,9 @@ # ____________________________________________________________ -@unwrap_spec(w_fresult=ctypeobj.W_CType, ellipsis=int) -def new_function_type(space, w_fargs, w_fresult, ellipsis=0): +@unwrap_spec(w_fresult=ctypeobj.W_CType, ellipsis=int, abi=int) +def new_function_type(space, w_fargs, w_fresult, ellipsis=0, + abi=_cffi_backend.FFI_DEFAULT_ABI): fargs = [] for w_farg in space.fixedview(w_fargs): if not isinstance(w_farg, ctypeobj.W_CType): @@ -602,28 +604,28 @@ if isinstance(w_farg, ctypearray.W_CTypeArray): w_farg = w_farg.ctptr fargs.append(w_farg) - return _new_function_type(space, fargs, w_fresult, bool(ellipsis)) + return _new_function_type(space, fargs, w_fresult, bool(ellipsis), abi) -def _func_key_hash(unique_cache, fargs, fresult, ellipsis): +def _func_key_hash(unique_cache, fargs, fresult, ellipsis, abi): x = compute_identity_hash(fresult) for w_arg in fargs: y = compute_identity_hash(w_arg) x = intmask((1000003 * x) ^ y) - x ^= ellipsis + x ^= (ellipsis - abi) if unique_cache.for_testing: # constant-folded to False in translation; x &= 3 # but for test, keep only 2 bits of hash return x # can't use @jit.elidable here, because it might call back to random # space functions via force_lazy_struct() -def _new_function_type(space, fargs, fresult, ellipsis=False): +def _new_function_type(space, fargs, fresult, ellipsis, abi): try: - return _get_function_type(space, fargs, fresult, ellipsis) + return _get_function_type(space, fargs, fresult, ellipsis, abi) except KeyError: - return _build_function_type(space, fargs, fresult, ellipsis) + return _build_function_type(space, fargs, fresult, ellipsis, abi) @jit.elidable -def _get_function_type(space, fargs, fresult, ellipsis): +def _get_function_type(space, fargs, fresult, ellipsis, abi): # This function is elidable because if called again with exactly the # same arguments (and if it didn't raise KeyError), it would give # the same result, at least as long as this result is still live. @@ -633,18 +635,19 @@ # one such dict, but in case of hash collision, there might be # more. unique_cache = space.fromcache(UniqueCache) - func_hash = _func_key_hash(unique_cache, fargs, fresult, ellipsis) + func_hash = _func_key_hash(unique_cache, fargs, fresult, ellipsis, abi) for weakdict in unique_cache.functions: ctype = weakdict.get(func_hash) if (ctype is not None and ctype.ctitem is fresult and ctype.fargs == fargs and - ctype.ellipsis == ellipsis): + ctype.ellipsis == ellipsis and + ctype.abi == abi): return ctype raise KeyError @jit.dont_look_inside -def _build_function_type(space, fargs, fresult, ellipsis): +def _build_function_type(space, fargs, fresult, ellipsis, abi): from pypy.module._cffi_backend import ctypefunc # if ((fresult.size < 0 and @@ -658,9 +661,9 @@ raise oefmt(space.w_TypeError, "invalid result type: '%s'", fresult.name) # - fct = ctypefunc.W_CTypeFunc(space, fargs, fresult, ellipsis) + fct = ctypefunc.W_CTypeFunc(space, fargs, fresult, ellipsis, abi) unique_cache = space.fromcache(UniqueCache) - func_hash = _func_key_hash(unique_cache, fargs, fresult, ellipsis) + func_hash = _func_key_hash(unique_cache, fargs, fresult, ellipsis, abi) for weakdict in unique_cache.functions: if weakdict.get(func_hash) is None: weakdict.set(func_hash, fct) diff --git a/pypy/module/_cffi_backend/realize_c_type.py b/pypy/module/_cffi_backend/realize_c_type.py --- a/pypy/module/_cffi_backend/realize_c_type.py +++ b/pypy/module/_cffi_backend/realize_c_type.py @@ -5,6 +5,7 @@ from rpython.rtyper.lltypesystem import lltype, rffi from pypy.interpreter.error import oefmt from pypy.interpreter.baseobjspace import W_Root +from pypy.module import _cffi_backend from pypy.module._cffi_backend.ctypeobj import W_CType from pypy.module._cffi_backend import cffi_opcode, newtype, ctypestruct from pypy.module._cffi_backend import parse_c_type @@ -164,16 +165,28 @@ OP_FUNCTION_END = cffi_opcode.OP_FUNCTION_END while getop(opcodes[base_index + num_args]) != OP_FUNCTION_END: num_args += 1 - ellipsis = (getarg(opcodes[base_index + num_args]) & 1) != 0 + # + ellipsis = (getarg(opcodes[base_index + num_args]) & 0x01) != 0 + abi = (getarg(opcodes[base_index + num_args]) & 0xFE) + if abi == 0: + abi = _cffi_backend.FFI_DEFAULT_ABI + elif abi == 2: + if _cffi_backend.has_stdcall: + abi = _cffi_backend.FFI_STDCALL + else: + abi = _cffi_backend.FFI_DEFAULT_ABI + else: + raise oefmt(ffi.w_FFIError, "abi number %d not supported", abi) + # fargs = [realize_c_type(ffi, opcodes, base_index + i) for i in range(num_args)] - return fargs, fret, ellipsis + return fargs, fret, ellipsis, abi def unwrap_as_fnptr(self, ffi): if self._ctfuncptr is None: - fargs, fret, ellipsis = self._unpack(ffi) + fargs, fret, ellipsis, abi = self._unpack(ffi) self._ctfuncptr = newtype._new_function_type( - ffi.space, fargs, fret, ellipsis) + ffi.space, fargs, fret, ellipsis, abi) return self._ctfuncptr def unwrap_as_fnptr_in_elidable(self): @@ -190,7 +203,7 @@ # type ptr-to-struct. This is how recompiler.py produces # trampoline functions for PyPy. if self.nostruct_ctype is None: - fargs, fret, ellipsis = self._unpack(ffi) + fargs, fret, ellipsis, abi = self._unpack(ffi) # 'locs' will be a string of the same length as the final fargs, # containing 'A' where a struct argument was detected, and 'R' # in first position if a struct return value was detected @@ -207,7 +220,7 @@ locs = ['R'] + locs fret = newtype.new_void_type(ffi.space) ctfuncptr = newtype._new_function_type( - ffi.space, fargs, fret, ellipsis) + ffi.space, fargs, fret, ellipsis, abi) if locs == ['\x00'] * len(locs): locs = None else: @@ -218,7 +231,7 @@ locs[0] == 'R') def unexpected_fn_type(self, ffi): - fargs, fret, ellipsis = self._unpack(ffi) + fargs, fret, ellipsis, abi = self._unpack(ffi) argnames = [farg.name for farg in fargs] if ellipsis: argnames.append('...') diff --git a/pypy/module/_cffi_backend/src/parse_c_type.c b/pypy/module/_cffi_backend/src/parse_c_type.c --- a/pypy/module/_cffi_backend/src/parse_c_type.c +++ b/pypy/module/_cffi_backend/src/parse_c_type.c @@ -51,6 +51,9 @@ TOK_UNSIGNED, TOK_VOID, TOK_VOLATILE, + + TOK_CDECL, + TOK_STDCALL, }; typedef struct { @@ -165,6 +168,8 @@ switch (*p) { case '_': if (tok->size == 5 && !memcmp(p, "_Bool", 5)) tok->kind = TOK__BOOL; + if (tok->size == 7 && !memcmp(p,"__cdecl",7)) tok->kind = TOK_CDECL; + if (tok->size == 9 && !memcmp(p,"__stdcall",9))tok->kind = TOK_STDCALL; break; case 'c': if (tok->size == 4 && !memcmp(p, "char", 4)) tok->kind = TOK_CHAR; @@ -236,7 +241,7 @@ type). The 'outer' argument is the index of the opcode outside this "sequel". */ - int check_for_grouping; + int check_for_grouping, abi=0; _cffi_opcode_t result, *p_current; header: @@ -253,6 +258,12 @@ /* ignored for now */ next_token(tok); goto header; + case TOK_CDECL: + case TOK_STDCALL: + /* must be in a function; checked below */ + abi = tok->kind; + next_token(tok); + goto header; default: break; } @@ -269,6 +280,11 @@ while (tok->kind == TOK_OPEN_PAREN) { next_token(tok); + if (tok->kind == TOK_CDECL || tok->kind == TOK_STDCALL) { + abi = tok->kind; + next_token(tok); + } + if ((check_for_grouping--) == 1 && (tok->kind == TOK_STAR || tok->kind == TOK_CONST || tok->kind == TOK_VOLATILE || @@ -286,7 +302,14 @@ } else { /* function type */ - int arg_total, base_index, arg_next, has_ellipsis=0; + int arg_total, base_index, arg_next, flags=0; + + if (abi == TOK_STDCALL) { + flags = 2; + /* note that an ellipsis below will overwrite this flags, + which is the goal: variadic functions are always cdecl */ + } + abi = 0; if (tok->kind == TOK_VOID && get_following_char(tok) == ')') { next_token(tok); @@ -315,7 +338,7 @@ _cffi_opcode_t oarg; if (tok->kind == TOK_DOTDOTDOT) { - has_ellipsis = 1; + flags = 1; /* ellipsis */ next_token(tok); break; } @@ -339,8 +362,7 @@ next_token(tok); } } - tok->output[arg_next] = _CFFI_OP(_CFFI_OP_FUNCTION_END, - has_ellipsis); + tok->output[arg_next] = _CFFI_OP(_CFFI_OP_FUNCTION_END, flags); } if (tok->kind != TOK_CLOSE_PAREN) @@ -348,6 +370,9 @@ next_token(tok); } + if (abi != 0) + return parse_error(tok, "expected '('"); + while (tok->kind == TOK_OPEN_BRACKET) { *p_current = _CFFI_OP(_CFFI_GETOP(*p_current), tok->output_index); p_current = tok->output + tok->output_index; diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py --- a/pypy/module/_cffi_backend/test/_backend_test_c.py +++ b/pypy/module/_cffi_backend/test/_backend_test_c.py @@ -2316,9 +2316,6 @@ f(); f() assert get_errno() == 77 -def test_abi(): - assert isinstance(FFI_DEFAULT_ABI, int) - def test_cast_to_array(): # not valid in C! extension to get a non-owning <cdata 'int[3]'> BInt = new_primitive_type("int") @@ -3427,3 +3424,16 @@ "be 'foo *', but the types are different (check " "that you are not e.g. mixing up different ffi " "instances)") + +def test_stdcall_function_type(): + assert FFI_CDECL == FFI_DEFAULT_ABI + try: + stdcall = FFI_STDCALL + except NameError: + stdcall = FFI_DEFAULT_ABI + BInt = new_primitive_type("int") + BFunc = new_function_type((BInt, BInt), BInt, False, stdcall) + if stdcall != FFI_DEFAULT_ABI: + assert repr(BFunc) == "<ctype 'int(__stdcall *)(int, int)'>" + else: + assert repr(BFunc) == "<ctype 'int(*)(int, int)'>" diff --git a/pypy/module/_cffi_backend/test/test_parse_c_type.py b/pypy/module/_cffi_backend/test/test_parse_c_type.py --- a/pypy/module/_cffi_backend/test/test_parse_c_type.py +++ b/pypy/module/_cffi_backend/test/test_parse_c_type.py @@ -338,3 +338,17 @@ # not supported (really obscure): # "char[+5]" # "char['A']" + +def test_stdcall_cdecl(): + assert parse("int __stdcall(int)") == [Prim(cffi_opcode.PRIM_INT), + '->', Func(0), NoOp(4), FuncEnd(2), + Prim(cffi_opcode.PRIM_INT)] + assert parse("int __stdcall func(int)") == parse("int __stdcall(int)") + assert parse("int (__stdcall *)()") == [Prim(cffi_opcode.PRIM_INT), + NoOp(3), '->', Pointer(1), + Func(0), FuncEnd(2), 0] + assert parse("int (__stdcall *p)()") == parse("int (__stdcall*)()") + parse_error("__stdcall int", "identifier expected", 0) + parse_error("__cdecl int", "identifier expected", 0) + parse_error("int __stdcall", "expected '('", 13) + parse_error("int __cdecl", "expected '('", 11) diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_function.py b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_function.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_function.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_function.py @@ -1,6 +1,6 @@ # Generated by pypy/tool/import_cffi.py import py -from cffi import FFI +from cffi import FFI, CDefError import math, os, sys import ctypes.util from cffi.backend_ctypes import CTypesBackend @@ -428,3 +428,59 @@ res = m.QueryPerformanceFrequency(p_freq) assert res != 0 assert p_freq[0] != 0 + + def test_explicit_cdecl_stdcall(self): + if sys.platform != 'win32': + py.test.skip("Windows-only test") + if self.Backend is CTypesBackend: + py.test.skip("not with the ctypes backend") + # + ffi = FFI(backend=self.Backend()) + ffi.cdef(""" + BOOL QueryPerformanceFrequency(LONGLONG *lpFrequency); + """) + m = ffi.dlopen("Kernel32.dll") + tp = ffi.typeof(m.QueryPerformanceFrequency) + assert str(tp) == "<ctype 'int(*)(long long *)'>" + # + ffi = FFI(backend=self.Backend()) + ffi.cdef(""" + BOOL __cdecl QueryPerformanceFrequency(LONGLONG *lpFrequency); + """) + m = ffi.dlopen("Kernel32.dll") + tpc = ffi.typeof(m.QueryPerformanceFrequency) + assert tpc is tp + # + ffi = FFI(backend=self.Backend()) + ffi.cdef(""" + BOOL WINAPI QueryPerformanceFrequency(LONGLONG *lpFrequency); + """) + m = ffi.dlopen("Kernel32.dll") + tps = ffi.typeof(m.QueryPerformanceFrequency) + assert tps is not tpc + assert str(tps) == "<ctype 'int(__stdcall *)(long long *)'>" + # + ffi = FFI(backend=self.Backend()) + ffi.cdef("typedef int (__cdecl *fnc_t)(int);") + ffi.cdef("typedef int (__stdcall *fns_t)(int);") + tpc = ffi.typeof("fnc_t") + tps = ffi.typeof("fns_t") + assert str(tpc) == "<ctype 'int(*)(int)'>" + assert str(tps) == "<ctype 'int(__stdcall *)(int)'>" + # + fnc = ffi.cast("fnc_t", 0) + fns = ffi.cast("fns_t", 0) + ffi.new("fnc_t[]", [fnc]) + py.test.raises(TypeError, ffi.new, "fnc_t[]", [fns]) + py.test.raises(TypeError, ffi.new, "fns_t[]", [fnc]) + ffi.new("fns_t[]", [fns]) + + def test_stdcall_only_on_windows(self): + if sys.platform == 'win32': + py.test.skip("not-Windows-only test") + ffi = FFI(backend=self.Backend()) + ffi.cdef("double __stdcall sin(double x);") # stdcall ignored + m = ffi.dlopen(lib_m) + assert "double(*)(double)" in str(ffi.typeof(m.sin)) + x = m.sin(1.23) + assert x == math.sin(1.23) diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_parsing.py b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_parsing.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_parsing.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_parsing.py @@ -365,3 +365,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)) diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_verify.py b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_verify.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_verify.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_verify.py @@ -1221,25 +1221,6 @@ lib = ffi.verify('#include <math.h>', libraries=lib_m) assert lib.sin(1.23) == math.sin(1.23) -def test_callback_calling_convention(): - py.test.skip("later") - if sys.platform != 'win32': - py.test.skip("Windows only") - ffi = FFI() - ffi.cdef(""" - int call1(int(*__cdecl cb)(int)); - int call2(int(*__stdcall cb)(int)); - """) - lib = ffi.verify(""" - int call1(int(*__cdecl cb)(int)) { - return cb(42) + 1; - } - int call2(int(*__stdcall cb)(int)) { - return cb(-42) - 6; - } - """) - xxx - def test_opaque_integer_as_function_result(): #import platform #if platform.machine().startswith('sparc'): @@ -2261,3 +2242,180 @@ assert foo_s.fields[0][1].type is ffi.typeof("int") assert foo_s.fields[1][0] == 'b' assert foo_s.fields[1][1].type is ffi.typeof("void *") + +def test_win32_calling_convention_0(): + ffi = FFI() + ffi.cdef(""" + int call1(int(__cdecl *cb)(int)); + int (*const call2)(int(__stdcall *cb)(int)); + """) + lib = ffi.verify(r""" + #ifndef _MSC_VER + # define __stdcall /* nothing */ + #endif + int call1(int(*cb)(int)) { + int i, result = 0; + //printf("call1: cb = %p\n", cb); + for (i = 0; i < 1000; i++) + result += cb(i); + //printf("result = %d\n", result); + return result; + } + int call2(int(__stdcall *cb)(int)) { + int i, result = 0; + //printf("call2: cb = %p\n", cb); + for (i = 0; i < 1000; i++) + result += cb(-i); + //printf("result = %d\n", result); + return result; + } + """) + @ffi.callback("int(int)") + def cb1(x): + return x * 2 + @ffi.callback("int __stdcall(int)") + def cb2(x): + return x * 3 + print 'cb1 =', cb1 + res = lib.call1(cb1) + assert res == 500*999*2 + print 'cb2 =', cb2 + print ffi.typeof(lib.call2) + print 'call2 =', lib.call2 + res = lib.call2(cb2) + print '...' + assert res == -500*999*3 + print 'done' + if sys.platform == 'win32': + assert '__stdcall' in str(ffi.typeof(cb2)) + assert '__stdcall' not in str(ffi.typeof(cb1)) + py.test.raises(TypeError, lib.call1, cb2) + py.test.raises(TypeError, lib.call2, cb1) + else: + assert '__stdcall' not in str(ffi.typeof(cb2)) + assert ffi.typeof(cb2) is ffi.typeof(cb1) + +def test_win32_calling_convention_1(): + ffi = FFI() + ffi.cdef(""" + int __cdecl call1(int(__cdecl *cb)(int)); + int __stdcall call2(int(__stdcall *cb)(int)); + int (__cdecl *const cb1)(int); + int (__stdcall *const cb2)(int); + """) + lib = ffi.verify(r""" + #ifndef _MSC_VER + # define __cdecl + # define __stdcall + #endif + int __cdecl cb1(int x) { return x * 2; } + int __stdcall cb2(int x) { return x * 3; } + + int __cdecl call1(int(__cdecl *cb)(int)) { + int i, result = 0; + //printf("here1\n"); + //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); + for (i = 0; i < 1000; i++) + result += cb(i); + //printf("result = %d\n", result); + return result; + } + int __stdcall call2(int(__stdcall *cb)(int)) { + int i, result = 0; + //printf("here1\n"); + //printf("cb = %p, cb2 = %p\n", cb, (void *)cb2); + for (i = 0; i < 1000; i++) + result += cb(-i); + //printf("result = %d\n", result); + return result; + } + """) + assert lib.call1(lib.cb1) == 500*999*2 + assert lib.call2(lib.cb2) == -500*999*3 + +def test_win32_calling_convention_2(): + # any mistake in the declaration of plain function (including the + # precise argument types and, here, the calling convention) are + # automatically corrected. But this does not apply to the 'cb' + # function pointer argument. + ffi = FFI() + ffi.cdef(""" + int __stdcall call1(int(__cdecl *cb)(int)); + int __cdecl call2(int(__stdcall *cb)(int)); + int (__cdecl *const cb1)(int); + int (__stdcall *const cb2)(int); + """) + lib = ffi.verify(r""" + #ifndef _MSC_VER + # define __cdecl + # define __stdcall + #endif + int __cdecl call1(int(__cdecl *cb)(int)) { + int i, result = 0; + for (i = 0; i < 1000; i++) + result += cb(i); + return result; + } + int __stdcall call2(int(__stdcall *cb)(int)) { + int i, result = 0; + for (i = 0; i < 1000; i++) + result += cb(-i); + return result; + } + int __cdecl cb1(int x) { return x * 2; } + int __stdcall cb2(int x) { return x * 3; } + """) + assert lib.call1(lib.cb1) == 500*999*2 + assert lib.call2(lib.cb2) == -500*999*3 + +def test_win32_calling_convention_3(): + ffi = FFI() + ffi.cdef(""" + struct point { int x, y; }; + + int (*const cb1)(struct point); + int (__stdcall *const cb2)(struct point); + + struct point __stdcall call1(int(*cb)(struct point)); + struct point call2(int(__stdcall *cb)(struct point)); + """) + lib = ffi.verify(r""" + #ifndef _MSC_VER + # define __cdecl + # define __stdcall + #endif + struct point { int x, y; }; + int cb1(struct point pt) { return pt.x + 10 * pt.y; } + int __stdcall cb2(struct point pt) { return pt.x + 100 * pt.y; } + struct point __stdcall call1(int(__cdecl *cb)(struct point)) { + int i; + struct point result = { 0, 0 }; + //printf("here1\n"); + //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); + for (i = 0; i < 1000; i++) { + struct point p = { i, -i }; + int r = cb(p); + result.x += r; + result.y -= r; + } + return result; + } + struct point __cdecl call2(int(__stdcall *cb)(struct point)) { + int i; + struct point result = { 0, 0 }; + for (i = 0; i < 1000; i++) { + struct point p = { -i, i }; + int r = cb(p); + result.x += r; + result.y -= r; + } + return result; + } + """) + if sys.platform == 'win32': + py.test.raises(TypeError, lib.call1, lib.cb2) + py.test.raises(TypeError, lib.call2, lib.cb1) + pt = lib.call1(lib.cb1) + assert (pt.x, pt.y) == (-9*500*999, 9*500*999) + pt = lib.call2(lib.cb2) + assert (pt.x, pt.y) == (99*500*999, -99*500*999) diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_parse_c_type.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_parse_c_type.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_parse_c_type.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_parse_c_type.py @@ -342,3 +342,17 @@ # not supported (really obscure): # "char[+5]" # "char['A']" + +def test_stdcall_cdecl(): + assert parse("int __stdcall(int)") == [Prim(lib._CFFI_PRIM_INT), + '->', Func(0), NoOp(4), FuncEnd(2), + Prim(lib._CFFI_PRIM_INT)] + assert parse("int __stdcall func(int)") == parse("int __stdcall(int)") + assert parse("int (__stdcall *)()") == [Prim(lib._CFFI_PRIM_INT), + NoOp(3), '->', Pointer(1), + Func(0), FuncEnd(2), 0] + assert parse("int (__stdcall *p)()") == parse("int (__stdcall*)()") + parse_error("__stdcall int", "identifier expected", 0) + parse_error("__cdecl int", "identifier expected", 0) + parse_error("int __stdcall", "expected '('", 13) + parse_error("int __cdecl", "expected '('", 11) diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_realize_c_type.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_realize_c_type.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_realize_c_type.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_realize_c_type.py @@ -1,5 +1,5 @@ # Generated by pypy/tool/import_cffi.py -import py +import py, sys from cffi import cffi_opcode @@ -47,3 +47,29 @@ def test_all_primitives(): for name in cffi_opcode.PRIMITIVE_TO_INDEX: check(name, name) + + +def check_func(input, expected_output=None): + import _cffi_backend + ffi = _cffi_backend.FFI() + ct = ffi.typeof(ffi.callback(input, lambda: None)) + assert isinstance(ct, ffi.CType) + if sys.platform != 'win32': + expected_output = expected_output.replace('__stdcall *', '*') + assert ct.cname == expected_output + +def test_funcptr_stdcall(): + check_func("int(int)", "int(*)(int)") + check_func("int foobar(int)", "int(*)(int)") + check_func("int __stdcall(int)", "int(__stdcall *)(int)") + check_func("int __stdcall foobar(int)", "int(__stdcall *)(int)") + check_func("void __cdecl(void)", "void(*)()") + check_func("void __cdecl foobar(void)", "void(*)()") + check_func("void __stdcall(void)", "void(__stdcall *)()") + check_func("void __stdcall foobar(long, short)", + "void(__stdcall *)(long, short)") + check_func("void(void __cdecl(void), void __stdcall(void))", + "void(*)(void(*)(), void(__stdcall *)())") + +def test_variadic_overrides_stdcall(): + check("void (__stdcall*)(int, ...)", "void(*)(int, ...)") diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py @@ -1281,3 +1281,200 @@ """) assert lib.aaa == 42 py.test.raises(AttributeError, "lib.aaa = 43") + +def test_win32_calling_convention_0(): + ffi = FFI() + ffi.cdef(""" + int call1(int(__cdecl *cb)(int)); + int (*const call2)(int(__stdcall *cb)(int)); + """) + lib = verify(ffi, 'test_win32_calling_convention_0', r""" + #ifndef _MSC_VER + # define __stdcall /* nothing */ + #endif + int call1(int(*cb)(int)) { + int i, result = 0; + //printf("call1: cb = %p\n", cb); + for (i = 0; i < 1000; i++) + result += cb(i); + //printf("result = %d\n", result); + return result; + } + int call2(int(__stdcall *cb)(int)) { + int i, result = 0; + //printf("call2: cb = %p\n", cb); + for (i = 0; i < 1000; i++) + result += cb(-i); + //printf("result = %d\n", result); + return result; + } + """) + @ffi.callback("int(int)") + def cb1(x): + return x * 2 + @ffi.callback("int __stdcall(int)") + def cb2(x): + return x * 3 + res = lib.call1(cb1) + assert res == 500*999*2 + assert res == ffi.addressof(lib, 'call1')(cb1) + res = lib.call2(cb2) + assert res == -500*999*3 + assert res == ffi.addressof(lib, 'call2')(cb2) + if sys.platform == 'win32': + assert '__stdcall' in str(ffi.typeof(cb2)) + assert '__stdcall' not in str(ffi.typeof(cb1)) + py.test.raises(TypeError, lib.call1, cb2) + py.test.raises(TypeError, lib.call2, cb1) + else: + assert '__stdcall' not in str(ffi.typeof(cb2)) + assert ffi.typeof(cb2) is ffi.typeof(cb1) + +def test_win32_calling_convention_1(): + ffi = FFI() + ffi.cdef(""" + int __cdecl call1(int(__cdecl *cb)(int)); + int __stdcall call2(int(__stdcall *cb)(int)); + int (__cdecl *const cb1)(int); + int (__stdcall *const cb2)(int); + """) + lib = verify(ffi, 'test_win32_calling_convention_1', r""" + #ifndef _MSC_VER + # define __cdecl + # define __stdcall + #endif + int __cdecl cb1(int x) { return x * 2; } + int __stdcall cb2(int x) { return x * 3; } + + int __cdecl call1(int(__cdecl *cb)(int)) { + int i, result = 0; + //printf("here1\n"); + //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); + for (i = 0; i < 1000; i++) + result += cb(i); + //printf("result = %d\n", result); + return result; + } + int __stdcall call2(int(__stdcall *cb)(int)) { + int i, result = 0; + //printf("here1\n"); + //printf("cb = %p, cb2 = %p\n", cb, (void *)cb2); + for (i = 0; i < 1000; i++) + result += cb(-i); + //printf("result = %d\n", result); + return result; + } + """) + print '<<< cb1 =', ffi.addressof(lib, 'cb1') + ptr_call1 = ffi.addressof(lib, 'call1') + assert lib.call1(ffi.addressof(lib, 'cb1')) == 500*999*2 + assert ptr_call1(ffi.addressof(lib, 'cb1')) == 500*999*2 + print '<<< cb2 =', ffi.addressof(lib, 'cb2') + ptr_call2 = ffi.addressof(lib, 'call2') + assert lib.call2(ffi.addressof(lib, 'cb2')) == -500*999*3 + assert ptr_call2(ffi.addressof(lib, 'cb2')) == -500*999*3 + print '<<< done' + +def test_win32_calling_convention_2(): + # any mistake in the declaration of plain function (including the + # precise argument types and, here, the calling convention) are + # automatically corrected. But this does not apply to the 'cb' + # function pointer argument. + ffi = FFI() + ffi.cdef(""" + int __stdcall call1(int(__cdecl *cb)(int)); + int __cdecl call2(int(__stdcall *cb)(int)); + int (__cdecl *const cb1)(int); + int (__stdcall *const cb2)(int); + """) + lib = verify(ffi, 'test_win32_calling_convention_2', """ + #ifndef _MSC_VER + # define __cdecl + # define __stdcall + #endif + int __cdecl call1(int(__cdecl *cb)(int)) { + int i, result = 0; + for (i = 0; i < 1000; i++) + result += cb(i); + return result; + } + int __stdcall call2(int(__stdcall *cb)(int)) { + int i, result = 0; + for (i = 0; i < 1000; i++) + result += cb(-i); + return result; + } + int __cdecl cb1(int x) { return x * 2; } + int __stdcall cb2(int x) { return x * 3; } + """) + ptr_call1 = ffi.addressof(lib, 'call1') + ptr_call2 = ffi.addressof(lib, 'call2') + if sys.platform == 'win32': + py.test.raises(TypeError, lib.call1, ffi.addressof(lib, 'cb2')) + py.test.raises(TypeError, ptr_call1, ffi.addressof(lib, 'cb2')) + py.test.raises(TypeError, lib.call2, ffi.addressof(lib, 'cb1')) + py.test.raises(TypeError, ptr_call2, ffi.addressof(lib, 'cb1')) + assert lib.call1(ffi.addressof(lib, 'cb1')) == 500*999*2 + assert ptr_call1(ffi.addressof(lib, 'cb1')) == 500*999*2 + assert lib.call2(ffi.addressof(lib, 'cb2')) == -500*999*3 + assert ptr_call2(ffi.addressof(lib, 'cb2')) == -500*999*3 + +def test_win32_calling_convention_3(): + ffi = FFI() + ffi.cdef(""" + struct point { int x, y; }; + + int (*const cb1)(struct point); + int (__stdcall *const cb2)(struct point); + + struct point __stdcall call1(int(*cb)(struct point)); + struct point call2(int(__stdcall *cb)(struct point)); + """) + lib = verify(ffi, 'test_win32_calling_convention_3', r""" + #ifndef _MSC_VER + # define __cdecl + # define __stdcall + #endif + struct point { int x, y; }; + int cb1(struct point pt) { return pt.x + 10 * pt.y; } + int __stdcall cb2(struct point pt) { return pt.x + 100 * pt.y; } + struct point __stdcall call1(int(__cdecl *cb)(struct point)) { + int i; + struct point result = { 0, 0 }; + //printf("here1\n"); + //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); + for (i = 0; i < 1000; i++) { + struct point p = { i, -i }; + int r = cb(p); + result.x += r; + result.y -= r; + } + return result; + } + struct point __cdecl call2(int(__stdcall *cb)(struct point)) { + int i; + struct point result = { 0, 0 }; + for (i = 0; i < 1000; i++) { + struct point p = { -i, i }; + int r = cb(p); + result.x += r; + result.y -= r; + } + return result; + } + """) + ptr_call1 = ffi.addressof(lib, 'call1') + ptr_call2 = ffi.addressof(lib, 'call2') + if sys.platform == 'win32': + py.test.raises(TypeError, lib.call1, ffi.addressof(lib, 'cb2')) + py.test.raises(TypeError, ptr_call1, ffi.addressof(lib, 'cb2')) + py.test.raises(TypeError, lib.call2, ffi.addressof(lib, 'cb1')) + py.test.raises(TypeError, ptr_call2, ffi.addressof(lib, 'cb1')) + pt = lib.call1(ffi.addressof(lib, 'cb1')) + assert (pt.x, pt.y) == (-9*500*999, 9*500*999) + pt = ptr_call1(ffi.addressof(lib, 'cb1')) + assert (pt.x, pt.y) == (-9*500*999, 9*500*999) + pt = lib.call2(ffi.addressof(lib, 'cb2')) + assert (pt.x, pt.y) == (99*500*999, -99*500*999) + pt = ptr_call2(ffi.addressof(lib, 'cb2')) + assert (pt.x, pt.y) == (99*500*999, -99*500*999) diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_verify1.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_verify1.py --- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_verify1.py +++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_verify1.py @@ -1201,25 +1201,6 @@ lib = ffi.verify('#include <math.h>', libraries=lib_m) assert lib.sin(1.23) == math.sin(1.23) -def test_callback_calling_convention(): - py.test.skip("later") - if sys.platform != 'win32': - py.test.skip("Windows only") - ffi = FFI() - ffi.cdef(""" - int call1(int(*__cdecl cb)(int)); - int call2(int(*__stdcall cb)(int)); - """) - lib = ffi.verify(""" - int call1(int(*__cdecl cb)(int)) { - return cb(42) + 1; - } - int call2(int(*__stdcall cb)(int)) { - return cb(-42) - 6; - } - """) - xxx - def test_opaque_integer_as_function_result(): #import platform #if platform.machine().startswith('sparc'): _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit