summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Rigo <arigo@tunes.org>2015-10-06 09:35:59 +0200
committerArmin Rigo <arigo@tunes.org>2015-10-06 09:35:59 +0200
commiteb3f058f2b40c45ed161091e73804c9324f779f5 (patch)
tree77f01beb18d62890867f231f44f75ec7c97b884d
parent0a02433dc5246202113046e3c27eb27546071bcb (diff)
downloadcffi-eb3f058f2b40c45ed161091e73804c9324f779f5.tar.gz
in-progress, but found a problem
-rw-r--r--c/realize_c_type.c22
-rw-r--r--cffi/model.py7
-rw-r--r--cffi/recompiler.py17
-rw-r--r--cffi/vengine_gen.py6
-rw-r--r--testing/cffi0/test_verify.py143
-rw-r--r--testing/cffi1/test_recompiler.py129
6 files changed, 288 insertions, 36 deletions
diff --git a/c/realize_c_type.c b/c/realize_c_type.c
index 87b77a7..7cbe3a5 100644
--- a/c/realize_c_type.c
+++ b/c/realize_c_type.c
@@ -544,7 +544,7 @@ realize_c_type_or_func(builder_c_t *builder,
case _CFFI_OP_FUNCTION:
{
PyObject *fargs;
- int i, base_index, num_args, ellipsis;
+ int i, base_index, num_args, ellipsis, abi;
y = (PyObject *)realize_c_type(builder, opcodes, _CFFI_GETARG(op));
if (y == NULL)
@@ -560,7 +560,22 @@ realize_c_type_or_func(builder_c_t *builder,
_CFFI_OP_FUNCTION_END)
num_args++;
- ellipsis = _CFFI_GETARG(opcodes[base_index + num_args]) & 1;
+ ellipsis = _CFFI_GETARG(opcodes[base_index + num_args]) & 0x01;
+ abi = _CFFI_GETARG(opcodes[base_index + num_args]) & 0xFE;
+ switch (abi) {
+ case 0:
+ abi = FFI_DEFAULT_ABI;
+ break;
+#if defined(MS_WIN32) && !defined(_WIN64)
+ case 2:
+ abi = FFI_STDCALL;
+ break;
+#endif
+ default:
+ PyErr_Format(FFIError, "abi number %d not supported", abi);
+ Py_DECREF(y);
+ return NULL;
+ }
fargs = PyTuple_New(num_args);
if (fargs == NULL) {
@@ -578,8 +593,7 @@ realize_c_type_or_func(builder_c_t *builder,
PyTuple_SET_ITEM(fargs, i, z);
}
- z = new_function_type(fargs, (CTypeDescrObject *)y, ellipsis,
- FFI_DEFAULT_ABI);
+ z = new_function_type(fargs, (CTypeDescrObject *)y, ellipsis, abi);
Py_DECREF(fargs);
Py_DECREF(y);
if (z == NULL)
diff --git a/cffi/model.py b/cffi/model.py
index 20c296c..7296e73 100644
--- a/cffi/model.py
+++ b/cffi/model.py
@@ -243,12 +243,13 @@ class FunctionPtrType(BaseFunctionType):
abi_args = (ffi._backend.FFI_STDCALL,)
except AttributeError:
if sys.platform == "win32":
- raise NotImplementedError("%r: stdcall with ctypes backend"
- % (self,))
+ raise NotImplementedError("%r: stdcall" % (self,))
else:
from . import api
- raise api.CDefError("%r: '__stdcall' only for Windows"
+ 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,))
return global_cache(self, ffi, 'new_function_type',
diff --git a/cffi/recompiler.py b/cffi/recompiler.py
index 9f55bcf..56b9efb 100644
--- a/cffi/recompiler.py
+++ b/cffi/recompiler.py
@@ -607,7 +607,11 @@ class Recompiler:
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 @@ class Recompiler:
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 @@ class Recompiler:
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/cffi/vengine_gen.py b/cffi/vengine_gen.py
index 2dc1cb8..9cc3853 100644
--- a/cffi/vengine_gen.py
+++ b/cffi/vengine_gen.py
@@ -159,7 +159,11 @@ class VGenericEngine(object):
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/testing/cffi0/test_verify.py b/testing/cffi0/test_verify.py
index 9192f8e..bb76cec 100644
--- a/testing/cffi0/test_verify.py
+++ b/testing/cffi0/test_verify.py
@@ -1220,24 +1220,6 @@ def test_function_typedef():
lib = ffi.verify('#include <math.h>', libraries=lib_m)
assert lib.sin(1.23) == math.sin(1.23)
-def test_callback_calling_convention():
- 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'):
@@ -2259,3 +2241,128 @@ def test_const_fields():
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_1():
+ if sys.platform != 'win32':
+ py.test.skip("Windows only")
+ ffi = FFI()
+ ffi.cdef("int call1(int(*cb)(int));", calling_conv="cdecl")
+ ffi.cdef("int call2(int(*cb)(int));", calling_conv="stdcall")
+ lib = ffi.verify(r"""
+ int __cdecl call1(int(__cdecl *cb)(int)) {
+ printf("here1\n");
+ printf("cb = %p, cb1 = %p\n", cb, (void *)cb1);
+ int i, result = 0;
+ 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(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():
+ if sys.platform != 'win32':
+ py.test.skip("Windows only")
+ # 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 call1(int(*cb)(int)); int cb1(int);", calling_conv="cdecl")
+ ffi.cdef("int call2(int(*cb)(int)); int cb2(int);", calling_conv="stdcall")
+ lib = verify(ffi, 'test_win32_calling_convention_2', """
+ int __stdcall call1(int(__cdecl *cb)(int)) {
+ int i, result = 0;
+ for (i = 0; i < 1000; i++)
+ result += cb(i);
+ return result;
+ }
+ int __cdecl call2(int(__stdcall *cb)(int)) {
+ int i, result = 0;
+ for (i = 0; i < 1000; i++)
+ result += cb(-i);
+ return result;
+ }
+ int __stdcall cb1(int x) { return x * 2; }
+ int __cdecl cb2(int x) { return x * 3; }
+ """)
+ ptr_call1 = ffi.addressof(lib, 'call1')
+ ptr_call2 = ffi.addressof(lib, 'call2')
+ 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():
+ if sys.platform != 'win32':
+ py.test.skip("Windows only")
+ ffi = FFI()
+ ffi.cdef("struct point { int x, y; };")
+ ffi.cdef("struct point call1(int(*cb)(struct point)); "
+ "int cb1(struct point);", calling_conv="cdecl")
+ ffi.cdef("struct point call2(int(*cb)(struct point)); "
+ "int cb2(struct point);", calling_conv="stdcall")
+ lib = verify(ffi, 'test_win32_calling_convention_3', r"""
+ struct point { int x, y; };
+ int __stdcall cb1(struct point pt) { return pt.x + 10 * pt.y; }
+ int __cdecl 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')
+ 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'))
+ print '<<< cb1 =', 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/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py
index b0c1a59..4ca6db8 100644
--- a/testing/cffi1/test_recompiler.py
+++ b/testing/cffi1/test_recompiler.py
@@ -1287,24 +1287,37 @@ def test_win32_calling_convention_1():
ffi = FFI()
ffi.cdef("int call1(int(*cb)(int)); int cb1(int);", calling_conv="cdecl")
ffi.cdef("int call2(int(*cb)(int)); int cb2(int);", calling_conv="stdcall")
- lib = verify(ffi, 'test_win32_calling_convention_1', """
- int __cdecl call1(int(*__cdecl cb)(int)) {
+ lib = verify(ffi, 'test_win32_calling_convention_1', r"""
+ int __cdecl cb1(int x) { return x * 2; }
+ int __cdecl call1(int(__cdecl *cb)(int)) {
+ printf("here1\n");
+ printf("cb = %p, cb1 = %p\n", cb, (void *)cb1);
int i, result = 0;
for (i = 0; i < 1000; i++)
result += cb(i);
+ printf("result = %d\n", result);
return result;
}
- int __stdcall call2(int(*__stdcall cb)(int)) {
+ int __stdcall cb2(int x) { return x * 3; }
+ 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;
}
- int __cdecl cb1(int x) { return x * 2; }
- int __stdcall cb2(int x) { return x * 3; }
""")
+ 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():
if sys.platform != 'win32':
@@ -1317,13 +1330,13 @@ def test_win32_calling_convention_2():
ffi.cdef("int call1(int(*cb)(int)); int cb1(int);", calling_conv="cdecl")
ffi.cdef("int call2(int(*cb)(int)); int cb2(int);", calling_conv="stdcall")
lib = verify(ffi, 'test_win32_calling_convention_2', """
- int __stdcall call1(int(*__cdecl cb)(int)) {
+ int __stdcall call1(int(__cdecl *cb)(int)) {
int i, result = 0;
for (i = 0; i < 1000; i++)
result += cb(i);
return result;
}
- int __cdecl call2(int(*__stdcall cb)(int)) {
+ int __cdecl call2(int(__stdcall *cb)(int)) {
int i, result = 0;
for (i = 0; i < 1000; i++)
result += cb(-i);
@@ -1332,5 +1345,107 @@ def test_win32_calling_convention_2():
int __stdcall cb1(int x) { return x * 2; }
int __cdecl cb2(int x) { return x * 3; }
""")
+ ptr_call1 = ffi.addressof(lib, 'call1')
+ ptr_call2 = ffi.addressof(lib, 'call2')
+ 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():
+ if sys.platform != 'win32':
+ py.test.skip("Windows only")
+ ffi = FFI()
+ ffi.cdef("struct point { int x, y; };")
+ ffi.cdef("struct point call1(int(*cb)(struct point)); "
+ "int cb1(struct point);", calling_conv="cdecl")
+ ffi.cdef("struct point call2(int(*cb)(struct point)); "
+ "int cb2(struct point);", calling_conv="stdcall")
+ lib = verify(ffi, 'test_win32_calling_convention_3', r"""
+ struct point { int x, y; };
+ int __stdcall cb1(struct point pt) { return pt.x + 10 * pt.y; }
+ int __cdecl 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')
+ 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'))
+ print '<<< cb1 =', 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)
+
+def test_win32_calling_convention_4():
+ if sys.platform != 'win32':
+ py.test.skip("Windows only")
+ ffi = FFI()
+ ffi.cdef("int call1(int(*cb)(int));", calling_conv="cdecl")
+ ffi.cdef("int call2(int(*cb)(int));", calling_conv="stdcall")
+ lib = verify(ffi, 'test_win32_calling_convention_4', """
+ int __stdcall call1(int(__cdecl *cb)(int)) {
+ int i, result = 0;
+ for (i = 0; i < 1000; i++)
+ result += cb(i);
+ return result;
+ }
+ int __cdecl call2(int(__stdcall *cb)(int)) {
+ int i, result = 0;
+ for (i = 0; i < 1000; i++)
+ result += cb(-i);
+ return result;
+ }
+ """)
+ @ffi.callback("int(int)", calling_conv="cdecl")
+ def cb1(x):
+ return x * 2
+ ...
+ @ffi.callback("int(int)", calling_conv="stdcall")
+ def cb2(x):
+ return x * 2
+
+
+ ptr_call1 = ffi.addressof(lib, 'call1')
+ ptr_call2 = ffi.addressof(lib, 'call2')
+ 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