diff options
author | Armin Rigo <arigo@tunes.org> | 2015-10-06 09:35:59 +0200 |
---|---|---|
committer | Armin Rigo <arigo@tunes.org> | 2015-10-06 09:35:59 +0200 |
commit | eb3f058f2b40c45ed161091e73804c9324f779f5 (patch) | |
tree | 77f01beb18d62890867f231f44f75ec7c97b884d | |
parent | 0a02433dc5246202113046e3c27eb27546071bcb (diff) | |
download | cffi-eb3f058f2b40c45ed161091e73804c9324f779f5.tar.gz |
in-progress, but found a problem
-rw-r--r-- | c/realize_c_type.c | 22 | ||||
-rw-r--r-- | cffi/model.py | 7 | ||||
-rw-r--r-- | cffi/recompiler.py | 17 | ||||
-rw-r--r-- | cffi/vengine_gen.py | 6 | ||||
-rw-r--r-- | testing/cffi0/test_verify.py | 143 | ||||
-rw-r--r-- | testing/cffi1/test_recompiler.py | 129 |
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 |