[pypy-commit] pypy default: hg merge cffi-stdcall
arigo
noreply at buildbot.pypy.org
Wed Oct 7 12:26:06 CEST 2015
Author: Armin Rigo <arigo at 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 @@
# ____________________________________________________________
- at unwrap_spec(w_fresult=ctypeobj.W_CType, ellipsis=int)
-def new_function_type(space, w_fargs, w_fresult, ellipsis=0):
+ at 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'):
More information about the pypy-commit
mailing list