import sys, os, py from cffi import FFI, VerificationError, FFIError, CDefError from cffi import recompiler from testing.udir import udir from testing.support import u, long from testing.support import FdWriteCapture, StdErrCapture, _verify try: import importlib except ImportError: importlib = None def check_type_table(input, expected_output, included=None): ffi = FFI() if included: ffi1 = FFI() ffi1.cdef(included) ffi.include(ffi1) ffi.cdef(input) recomp = recompiler.Recompiler(ffi, 'testmod') recomp.collect_type_table() assert ''.join(map(str, recomp.cffi_types)) == expected_output def verify(ffi, module_name, source, *args, **kwds): no_cpp = kwds.pop('no_cpp', False) kwds.setdefault('undef_macros', ['NDEBUG']) module_name = '_CFFI_' + module_name ffi.set_source(module_name, source) if not os.environ.get('NO_CPP') and not no_cpp: # test the .cpp mode too kwds.setdefault('source_extension', '.cpp') source = 'extern "C" {\n%s\n}' % (source,) elif sys.platform != 'win32': # add '-Werror' to the existing 'extra_compile_args' flags kwds['extra_compile_args'] = (kwds.get('extra_compile_args', []) + ['-Werror']) return _verify(ffi, module_name, source, *args, **kwds) def test_set_source_no_slashes(): ffi = FFI() py.test.raises(ValueError, ffi.set_source, "abc/def", None) py.test.raises(ValueError, ffi.set_source, "abc/def", "C code") def test_type_table_func(): check_type_table("double sin(double);", "(FUNCTION 1)(PRIMITIVE 14)(FUNCTION_END 0)") check_type_table("float sin(double);", "(FUNCTION 3)(PRIMITIVE 14)(FUNCTION_END 0)(PRIMITIVE 13)") check_type_table("float sin(void);", "(FUNCTION 2)(FUNCTION_END 0)(PRIMITIVE 13)") check_type_table("double sin(float); double cos(float);", "(FUNCTION 3)(PRIMITIVE 13)(FUNCTION_END 0)(PRIMITIVE 14)") check_type_table("double sin(float); double cos(double);", "(FUNCTION 1)(PRIMITIVE 14)(FUNCTION_END 0)" # cos "(FUNCTION 1)(PRIMITIVE 13)(FUNCTION_END 0)") # sin check_type_table("float sin(double); float cos(float);", "(FUNCTION 4)(PRIMITIVE 14)(FUNCTION_END 0)" # sin "(FUNCTION 4)(PRIMITIVE 13)(FUNCTION_END 0)") # cos def test_type_table_use_noop_for_repeated_args(): check_type_table("double sin(double *, double *);", "(FUNCTION 4)(POINTER 4)(NOOP 1)(FUNCTION_END 0)" "(PRIMITIVE 14)") check_type_table("double sin(double *, double *, double);", "(FUNCTION 3)(POINTER 3)(NOOP 1)(PRIMITIVE 14)" "(FUNCTION_END 0)") def test_type_table_dont_use_noop_for_primitives(): check_type_table("double sin(double, double);", "(FUNCTION 1)(PRIMITIVE 14)(PRIMITIVE 14)(FUNCTION_END 0)") def test_type_table_funcptr_as_argument(): check_type_table("int sin(double(float));", "(FUNCTION 6)(PRIMITIVE 13)(FUNCTION_END 0)" "(FUNCTION 7)(POINTER 0)(FUNCTION_END 0)" "(PRIMITIVE 14)(PRIMITIVE 7)") def test_type_table_variadic_function(): check_type_table("int sin(int, ...);", "(FUNCTION 1)(PRIMITIVE 7)(FUNCTION_END 1)(POINTER 0)") def test_type_table_array(): check_type_table("int a[100];", "(PRIMITIVE 7)(ARRAY 0)(None 100)") def test_type_table_typedef(): check_type_table("typedef int foo_t;", "(PRIMITIVE 7)") def test_type_table_prebuilt_type(): check_type_table("int32_t f(void);", "(FUNCTION 2)(FUNCTION_END 0)(PRIMITIVE 21)") def test_type_table_struct_opaque(): check_type_table("struct foo_s;", "(STRUCT_UNION 0)") def test_type_table_struct(): check_type_table("struct foo_s { int a; long b; };", "(PRIMITIVE 7)(PRIMITIVE 9)(STRUCT_UNION 0)") def test_type_table_union(): check_type_table("union foo_u { int a; long b; };", "(PRIMITIVE 7)(PRIMITIVE 9)(STRUCT_UNION 0)") def test_type_table_struct_used(): check_type_table("struct foo_s { int a; long b; }; int f(struct foo_s*);", "(FUNCTION 3)(POINTER 5)(FUNCTION_END 0)" "(PRIMITIVE 7)(PRIMITIVE 9)" "(STRUCT_UNION 0)") def test_type_table_anonymous_struct_with_typedef(): check_type_table("typedef struct { int a; long b; } foo_t;", "(STRUCT_UNION 0)(PRIMITIVE 7)(PRIMITIVE 9)") def test_type_table_enum(): check_type_table("enum foo_e { AA, BB, ... };", "(ENUM 0)") def test_type_table_include_1(): check_type_table("foo_t sin(foo_t);", "(FUNCTION 1)(PRIMITIVE 14)(FUNCTION_END 0)", included="typedef double foo_t;") def test_type_table_include_2(): check_type_table("struct foo_s *sin(struct foo_s *);", "(FUNCTION 1)(POINTER 3)(FUNCTION_END 0)(STRUCT_UNION 0)", included="struct foo_s { int x, y; };") def test_math_sin(): import math ffi = FFI() ffi.cdef("float sin(double); double cos(double);") lib = verify(ffi, 'test_math_sin', '#include ') assert lib.cos(1.43) == math.cos(1.43) def test_repr_lib(): ffi = FFI() lib = verify(ffi, 'test_repr_lib', '') assert repr(lib) == "" def test_funcarg_ptr(): ffi = FFI() ffi.cdef("int foo(int *);") lib = verify(ffi, 'test_funcarg_ptr', 'int foo(int *p) { return *p; }') assert lib.foo([-12345]) == -12345 def test_funcres_ptr(): ffi = FFI() ffi.cdef("int *foo(void);") lib = verify(ffi, 'test_funcres_ptr', 'int *foo(void) { static int x=-12345; return &x; }') assert lib.foo()[0] == -12345 def test_global_var_array(): ffi = FFI() ffi.cdef("int a[100];") lib = verify(ffi, 'test_global_var_array', 'int a[100] = { 9999 };') lib.a[42] = 123456 assert lib.a[42] == 123456 assert lib.a[0] == 9999 def test_verify_typedef(): ffi = FFI() ffi.cdef("typedef int **foo_t;") lib = verify(ffi, 'test_verify_typedef', 'typedef int **foo_t;') assert ffi.sizeof("foo_t") == ffi.sizeof("void *") def test_verify_typedef_dotdotdot(): ffi = FFI() ffi.cdef("typedef ... foo_t;") verify(ffi, 'test_verify_typedef_dotdotdot', 'typedef int **foo_t;') def test_verify_typedef_star_dotdotdot(): ffi = FFI() ffi.cdef("typedef ... *foo_t;") verify(ffi, 'test_verify_typedef_star_dotdotdot', 'typedef int **foo_t;') def test_global_var_int(): ffi = FFI() ffi.cdef("int a, b, c;") lib = verify(ffi, 'test_global_var_int', 'int a = 999, b, c;') assert lib.a == 999 lib.a -= 1001 assert lib.a == -2 lib.a = -2147483648 assert lib.a == -2147483648 py.test.raises(OverflowError, "lib.a = 2147483648") py.test.raises(OverflowError, "lib.a = -2147483649") lib.b = 525 # try with the first access being in setattr, too assert lib.b == 525 py.test.raises(AttributeError, "del lib.a") py.test.raises(AttributeError, "del lib.c") py.test.raises(AttributeError, "del lib.foobarbaz") def test_macro(): ffi = FFI() ffi.cdef("#define FOOBAR ...") lib = verify(ffi, 'test_macro', "#define FOOBAR (-6912)") assert lib.FOOBAR == -6912 py.test.raises(AttributeError, "lib.FOOBAR = 2") def test_macro_check_value(): # the value '-0x80000000' in C sources does not have a clear meaning # to me; it appears to have a different effect than '-2147483648'... # Moreover, on 32-bits, -2147483648 is actually equal to # -2147483648U, which in turn is equal to 2147483648U and so positive. vals = ['42', '-42', '0x80000000', '-2147483648', '0', '9223372036854775809ULL', '-9223372036854775807LL'] if sys.maxsize <= 2**32 or sys.platform == 'win32': vals.remove('-2147483648') ffi = FFI() cdef_lines = ['#define FOO_%d_%d %s' % (i, j, vals[i]) for i in range(len(vals)) for j in range(len(vals))] ffi.cdef('\n'.join(cdef_lines)) verify_lines = ['#define FOO_%d_%d %s' % (i, j, vals[j]) # [j], not [i] for i in range(len(vals)) for j in range(len(vals))] lib = verify(ffi, 'test_macro_check_value_ok', '\n'.join(verify_lines)) # for j in range(len(vals)): c_got = int(vals[j].replace('U', '').replace('L', ''), 0) c_compiler_msg = str(c_got) if c_got > 0: c_compiler_msg += ' (0x%x)' % (c_got,) # for i in range(len(vals)): attrname = 'FOO_%d_%d' % (i, j) if i == j: x = getattr(lib, attrname) assert x == c_got else: e = py.test.raises(ffi.error, getattr, lib, attrname) assert str(e.value) == ( "the C compiler says '%s' is equal to " "%s, but the cdef disagrees" % (attrname, c_compiler_msg)) def test_constant(): ffi = FFI() ffi.cdef("static const int FOOBAR;") lib = verify(ffi, 'test_constant', "#define FOOBAR (-6912)") assert lib.FOOBAR == -6912 py.test.raises(AttributeError, "lib.FOOBAR = 2") def test_check_value_of_static_const(): ffi = FFI() ffi.cdef("static const int FOOBAR = 042;") lib = verify(ffi, 'test_check_value_of_static_const', "#define FOOBAR (-6912)") e = py.test.raises(ffi.error, getattr, lib, 'FOOBAR') assert str(e.value) == ( "the C compiler says 'FOOBAR' is equal to -6912, but the cdef disagrees") def test_constant_nonint(): ffi = FFI() ffi.cdef("static const double FOOBAR;") lib = verify(ffi, 'test_constant_nonint', "#define FOOBAR (-6912.5)") assert lib.FOOBAR == -6912.5 py.test.raises(AttributeError, "lib.FOOBAR = 2") def test_constant_ptr(): ffi = FFI() ffi.cdef("static double *const FOOBAR;") lib = verify(ffi, 'test_constant_ptr', "#define FOOBAR NULL") assert lib.FOOBAR == ffi.NULL assert ffi.typeof(lib.FOOBAR) == ffi.typeof("double *") def test_dir(): ffi = FFI() ffi.cdef("int ff(int); int aa; static const int my_constant;") lib = verify(ffi, 'test_dir', """ #define my_constant (-45) int aa; int ff(int x) { return x+aa; } """) lib.aa = 5 assert dir(lib) == ['aa', 'ff', 'my_constant'] # aaobj = lib.__dict__['aa'] assert not isinstance(aaobj, int) # some internal object instead assert lib.__dict__ == { 'ff': lib.ff, 'aa': aaobj, 'my_constant': -45} lib.__dict__['ff'] = "??" assert lib.ff(10) == 15 def test_verify_opaque_struct(): ffi = FFI() ffi.cdef("struct foo_s;") lib = verify(ffi, 'test_verify_opaque_struct', "struct foo_s;") assert ffi.typeof("struct foo_s").cname == "struct foo_s" def test_verify_opaque_union(): ffi = FFI() ffi.cdef("union foo_s;") lib = verify(ffi, 'test_verify_opaque_union', "union foo_s;") assert ffi.typeof("union foo_s").cname == "union foo_s" def test_verify_struct(): ffi = FFI() ffi.cdef("""struct foo_s { int b; short a; ...; }; struct bar_s { struct foo_s *f; };""") lib = verify(ffi, 'test_verify_struct', """struct foo_s { short a; int b; }; struct bar_s { struct foo_s *f; };""") ffi.typeof("struct bar_s *") p = ffi.new("struct foo_s *", {'a': -32768, 'b': -2147483648}) assert p.a == -32768 assert p.b == -2147483648 py.test.raises(OverflowError, "p.a -= 1") py.test.raises(OverflowError, "p.b -= 1") q = ffi.new("struct bar_s *", {'f': p}) assert q.f == p # assert ffi.offsetof("struct foo_s", "a") == 0 assert ffi.offsetof("struct foo_s", "b") == 4 assert ffi.offsetof(u+"struct foo_s", u+"b") == 4 # py.test.raises(TypeError, ffi.addressof, p) assert ffi.addressof(p[0]) == p assert ffi.typeof(ffi.addressof(p[0])) is ffi.typeof("struct foo_s *") assert ffi.typeof(ffi.addressof(p, "b")) is ffi.typeof("int *") assert ffi.addressof(p, "b")[0] == p.b def test_verify_exact_field_offset(): ffi = FFI() ffi.cdef("""struct foo_s { int b; short a; };""") lib = verify(ffi, 'test_verify_exact_field_offset', """struct foo_s { short a; int b; };""") e = py.test.raises(ffi.error, ffi.new, "struct foo_s *", []) # lazily assert str(e.value) == ("struct foo_s: wrong offset for field 'b' (cdef " 'says 0, but C compiler says 4). fix it or use "...;" ' "in the cdef for struct foo_s to make it flexible") def test_type_caching(): ffi1 = FFI(); ffi1.cdef("struct foo_s;") ffi2 = FFI(); ffi2.cdef("struct foo_s;") # different one! lib1 = verify(ffi1, 'test_type_caching_1', 'struct foo_s;') lib2 = verify(ffi2, 'test_type_caching_2', 'struct foo_s;') # shared types assert ffi1.typeof("long") is ffi2.typeof("long") assert ffi1.typeof("long**") is ffi2.typeof("long * *") assert ffi1.typeof("long(*)(int, ...)") is ffi2.typeof("long(*)(int, ...)") # non-shared types assert ffi1.typeof("struct foo_s") is not ffi2.typeof("struct foo_s") assert ffi1.typeof("struct foo_s *") is not ffi2.typeof("struct foo_s *") assert ffi1.typeof("struct foo_s*(*)()") is not ( ffi2.typeof("struct foo_s*(*)()")) assert ffi1.typeof("void(*)(struct foo_s*)") is not ( ffi2.typeof("void(*)(struct foo_s*)")) def test_verify_enum(): ffi = FFI() ffi.cdef("""enum e1 { B1, A1, ... }; enum e2 { B2, A2, ... };""") lib = verify(ffi, 'test_verify_enum', "enum e1 { A1, B1, C1=%d };" % sys.maxsize + "enum e2 { A2, B2, C2 };") ffi.typeof("enum e1") ffi.typeof("enum e2") assert lib.A1 == 0 assert lib.B1 == 1 assert lib.A2 == 0 assert lib.B2 == 1 assert ffi.sizeof("enum e1") == ffi.sizeof("long") assert ffi.sizeof("enum e2") == ffi.sizeof("int") assert repr(ffi.cast("enum e1", 0)) == "" def test_duplicate_enum(): ffi = FFI() ffi.cdef("enum e1 { A1, ... }; enum e2 { A1, ... };") py.test.raises(VerificationError, verify, ffi, 'test_duplicate_enum', "enum e1 { A1 }; enum e2 { B1 };") def test_dotdotdot_length_of_array_field(): ffi = FFI() ffi.cdef("struct foo_s { int a[...]; int b[...]; };") verify(ffi, 'test_dotdotdot_length_of_array_field', "struct foo_s { int a[42]; int b[11]; };") assert ffi.sizeof("struct foo_s") == (42 + 11) * 4 p = ffi.new("struct foo_s *") assert p.a[41] == p.b[10] == 0 py.test.raises(IndexError, "p.a[42]") py.test.raises(IndexError, "p.b[11]") def test_dotdotdot_global_array(): ffi = FFI() ffi.cdef("int aa[...]; int bb[...];") lib = verify(ffi, 'test_dotdotdot_global_array', "int aa[41]; int bb[12];") assert ffi.sizeof(lib.aa) == 41 * 4 assert ffi.sizeof(lib.bb) == 12 * 4 assert lib.aa[40] == lib.bb[11] == 0 py.test.raises(IndexError, "lib.aa[41]") py.test.raises(IndexError, "lib.bb[12]") def test_misdeclared_field_1(): ffi = FFI() ffi.cdef("struct foo_s { int a[5]; };") try: verify(ffi, 'test_misdeclared_field_1', "struct foo_s { int a[6]; };") except VerificationError: pass # ok, fail during compilation already (e.g. C++) else: assert ffi.sizeof("struct foo_s") == 24 # found by the actual C code try: # lazily build the fields and boom: p = ffi.new("struct foo_s *") p.a assert False, "should have raised" except ffi.error as e: assert str(e).startswith("struct foo_s: wrong size for field 'a' " "(cdef says 20, but C compiler says 24)") def test_open_array_in_struct(): ffi = FFI() ffi.cdef("struct foo_s { int b; int a[]; };") verify(ffi, 'test_open_array_in_struct', "struct foo_s { int b; int a[]; };") assert ffi.sizeof("struct foo_s") == 4 p = ffi.new("struct foo_s *", [5, [10, 20, 30, 40]]) assert p.a[2] == 30 assert ffi.sizeof(p) == ffi.sizeof("void *") assert ffi.sizeof(p[0]) == 5 * ffi.sizeof("int") def test_math_sin_type(): ffi = FFI() ffi.cdef("double sin(double); void *xxtestfunc();") lib = verify(ffi, 'test_math_sin_type', """ #include void *xxtestfunc(void) { return 0; } """) # 'lib.sin' is typed as a object on lib assert ffi.typeof(lib.sin).cname == "double(*)(double)" # 'x' is another object on lib, made very indirectly x = type(lib).__dir__.__get__(lib) py.test.raises(TypeError, ffi.typeof, x) # # present on built-in functions on CPython; must be emulated on PyPy: assert lib.sin.__name__ == 'sin' assert lib.sin.__module__ == '_CFFI_test_math_sin_type' assert lib.sin.__doc__ == ( "double sin(double);\n" "\n" "CFFI C function from _CFFI_test_math_sin_type.lib") assert ffi.typeof(lib.xxtestfunc).cname == "void *(*)()" assert lib.xxtestfunc.__doc__ == ( "void *xxtestfunc();\n" "\n" "CFFI C function from _CFFI_test_math_sin_type.lib") def test_verify_anonymous_struct_with_typedef(): ffi = FFI() ffi.cdef("typedef struct { int a; long b; ...; } foo_t;") verify(ffi, 'test_verify_anonymous_struct_with_typedef', "typedef struct { long b; int hidden, a; } foo_t;") p = ffi.new("foo_t *", {'b': 42}) assert p.b == 42 assert repr(p).startswith("" # ffi = FFI() ffi.cdef("typedef enum { AA=%d } e1;" % sys.maxsize) lib = verify(ffi, 'test_verify_anonymous_enum_with_typedef2', "typedef enum { AA=%d } e1;" % sys.maxsize) assert lib.AA == int(ffi.cast("long", sys.maxsize)) assert ffi.sizeof("e1") == ffi.sizeof("long") def test_unique_types(): CDEF = "struct foo_s; union foo_u; enum foo_e { AA };" ffi1 = FFI(); ffi1.cdef(CDEF); verify(ffi1, "test_unique_types_1", CDEF) ffi2 = FFI(); ffi2.cdef(CDEF); verify(ffi2, "test_unique_types_2", CDEF) # assert ffi1.typeof("char") is ffi2.typeof("char ") assert ffi1.typeof("long") is ffi2.typeof("signed long int") assert ffi1.typeof("double *") is ffi2.typeof("double*") assert ffi1.typeof("int ***") is ffi2.typeof(" int * * *") assert ffi1.typeof("int[]") is ffi2.typeof("signed int[]") assert ffi1.typeof("signed int*[17]") is ffi2.typeof("int *[17]") assert ffi1.typeof("void") is ffi2.typeof("void") assert ffi1.typeof("int(*)(int,int)") is ffi2.typeof("int(*)(int,int)") # # these depend on user-defined data, so should not be shared for name in ["struct foo_s", "union foo_u *", "enum foo_e", "struct foo_s *(*)()", "void(*)(struct foo_s *)", "struct foo_s *(*[5])[8]", ]: assert ffi1.typeof(name) is not ffi2.typeof(name) # sanity check: twice 'ffi1' assert ffi1.typeof("struct foo_s*") is ffi1.typeof("struct foo_s *") def test_module_name_in_package(): ffi = FFI() ffi.cdef("int foo(int);") recompiler.recompile(ffi, "test_module_name_in_package.mymod", "int foo(int x) { return x + 32; }", tmpdir=str(udir)) old_sys_path = sys.path[:] try: package_dir = udir.join('test_module_name_in_package') for name in os.listdir(str(udir)): assert not name.startswith('test_module_name_in_package.') assert os.path.isdir(str(package_dir)) assert len(os.listdir(str(package_dir))) > 0 assert os.path.exists(str(package_dir.join('mymod.c'))) package_dir.join('__init__.py').write('') # getattr(importlib, 'invalidate_caches', object)() # sys.path.insert(0, str(udir)) import test_module_name_in_package.mymod assert test_module_name_in_package.mymod.lib.foo(10) == 42 assert test_module_name_in_package.mymod.__name__ == ( 'test_module_name_in_package.mymod') finally: sys.path[:] = old_sys_path def test_bad_size_of_global_1(): ffi = FFI() ffi.cdef("short glob;") py.test.raises(VerificationError, verify, ffi, "test_bad_size_of_global_1", "long glob;") def test_bad_size_of_global_2(): ffi = FFI() ffi.cdef("int glob[10];") py.test.raises(VerificationError, verify, ffi, "test_bad_size_of_global_2", "int glob[9];") def test_unspecified_size_of_global_1(): ffi = FFI() ffi.cdef("int glob[];") lib = verify(ffi, "test_unspecified_size_of_global_1", "int glob[10];") assert ffi.typeof(lib.glob) == ffi.typeof("int *") def test_unspecified_size_of_global_2(): ffi = FFI() ffi.cdef("int glob[][5];") lib = verify(ffi, "test_unspecified_size_of_global_2", "int glob[10][5];") assert ffi.typeof(lib.glob) == ffi.typeof("int(*)[5]") def test_unspecified_size_of_global_3(): ffi = FFI() ffi.cdef("int glob[][...];") lib = verify(ffi, "test_unspecified_size_of_global_3", "int glob[10][5];") assert ffi.typeof(lib.glob) == ffi.typeof("int(*)[5]") def test_unspecified_size_of_global_4(): ffi = FFI() ffi.cdef("int glob[...][...];") lib = verify(ffi, "test_unspecified_size_of_global_4", "int glob[10][5];") assert ffi.typeof(lib.glob) == ffi.typeof("int[10][5]") def test_include_1(): ffi1 = FFI() ffi1.cdef("typedef double foo_t;") verify(ffi1, "test_include_1_parent", "typedef double foo_t;") ffi = FFI() ffi.include(ffi1) ffi.cdef("foo_t ff1(foo_t);") lib = verify(ffi, "test_include_1", "double ff1(double x) { return 42.5; }") assert lib.ff1(0) == 42.5 assert ffi1.typeof("foo_t") is ffi.typeof("foo_t") is ffi.typeof("double") def test_include_1b(): ffi1 = FFI() ffi1.cdef("int foo1(int);") lib1 = verify(ffi1, "test_include_1b_parent", "int foo1(int x) { return x + 10; }") ffi = FFI() ffi.include(ffi1) ffi.cdef("int foo2(int);") lib = verify(ffi, "test_include_1b", "int foo2(int x) { return x - 5; }") assert lib.foo2(42) == 37 assert lib.foo1(42) == 52 assert lib.foo1 is lib1.foo1 def test_include_2(): ffi1 = FFI() ffi1.cdef("struct foo_s { int x, y; };") verify(ffi1, "test_include_2_parent", "struct foo_s { int x, y; };") ffi = FFI() ffi.include(ffi1) ffi.cdef("struct foo_s *ff2(struct foo_s *);") lib = verify(ffi, "test_include_2", "struct foo_s { int x, y; }; //usually from a #include\n" "struct foo_s *ff2(struct foo_s *p) { p->y++; return p; }") p = ffi.new("struct foo_s *") p.y = 41 q = lib.ff2(p) assert q == p assert p.y == 42 assert ffi1.typeof("struct foo_s") is ffi.typeof("struct foo_s") def test_include_3(): ffi1 = FFI() ffi1.cdef("typedef short sshort_t;") verify(ffi1, "test_include_3_parent", "typedef short sshort_t;") ffi = FFI() ffi.include(ffi1) ffi.cdef("sshort_t ff3(sshort_t);") lib = verify(ffi, "test_include_3", "typedef short sshort_t; //usually from a #include\n" "sshort_t ff3(sshort_t x) { return x + 42; }") assert lib.ff3(10) == 52 assert ffi.typeof(ffi.cast("sshort_t", 42)) is ffi.typeof("short") assert ffi1.typeof("sshort_t") is ffi.typeof("sshort_t") def test_include_4(): ffi1 = FFI() ffi1.cdef("typedef struct { int x; } mystruct_t;") verify(ffi1, "test_include_4_parent", "typedef struct { int x; } mystruct_t;") ffi = FFI() ffi.include(ffi1) ffi.cdef("mystruct_t *ff4(mystruct_t *);") lib = verify(ffi, "test_include_4", "typedef struct {int x; } mystruct_t; //usually from a #include\n" "mystruct_t *ff4(mystruct_t *p) { p->x += 42; return p; }") p = ffi.new("mystruct_t *", [10]) q = lib.ff4(p) assert q == p assert p.x == 52 assert ffi1.typeof("mystruct_t") is ffi.typeof("mystruct_t") def test_include_5(): ffi1 = FFI() ffi1.cdef("typedef struct { int x[2]; int y; } *mystruct_p;") verify(ffi1, "test_include_5_parent", "typedef struct { int x[2]; int y; } *mystruct_p;") ffi = FFI() ffi.include(ffi1) ffi.cdef("mystruct_p ff5(mystruct_p);") lib = verify(ffi, "test_include_5", "typedef struct {int x[2]; int y; } *mystruct_p; //usually #include\n" "mystruct_p ff5(mystruct_p p) { p->x[1] += 42; return p; }") assert ffi.alignof(ffi.typeof("mystruct_p").item) == 4 assert ffi1.typeof("mystruct_p") is ffi.typeof("mystruct_p") p = ffi.new("mystruct_p", [[5, 10], -17]) q = lib.ff5(p) assert q == p assert p.x[0] == 5 assert p.x[1] == 52 assert p.y == -17 assert ffi.alignof(ffi.typeof(p[0])) == 4 def test_include_6(): ffi1 = FFI() ffi1.cdef("typedef ... mystruct_t;") verify(ffi1, "test_include_6_parent", "typedef struct _mystruct_s mystruct_t;") ffi = FFI() ffi.include(ffi1) ffi.cdef("mystruct_t *ff6(void); int ff6b(mystruct_t *);") lib = verify(ffi, "test_include_6", "typedef struct _mystruct_s mystruct_t; //usually from a #include\n" "struct _mystruct_s { int x; };\n" "static mystruct_t result_struct = { 42 };\n" "mystruct_t *ff6(void) { return &result_struct; }\n" "int ff6b(mystruct_t *p) { return p->x; }") p = lib.ff6() assert ffi.cast("int *", p)[0] == 42 assert lib.ff6b(p) == 42 def test_include_7(): ffi1 = FFI() ffi1.cdef("typedef ... mystruct_t;\n" "int ff7b(mystruct_t *);") verify(ffi1, "test_include_7_parent", "typedef struct { int x; } mystruct_t;\n" "int ff7b(mystruct_t *p) { return p->x; }") ffi = FFI() ffi.include(ffi1) ffi.cdef("mystruct_t *ff7(void);") lib = verify(ffi, "test_include_7", "typedef struct { int x; } mystruct_t; //usually from a #include\n" "static mystruct_t result_struct = { 42 };" "mystruct_t *ff7(void) { return &result_struct; }") p = lib.ff7() assert ffi.cast("int *", p)[0] == 42 assert lib.ff7b(p) == 42 def test_include_8(): ffi1 = FFI() ffi1.cdef("struct foo_s;") verify(ffi1, "test_include_8_parent", "struct foo_s;") ffi = FFI() ffi.include(ffi1) ffi.cdef("struct foo_s { int x, y; };") verify(ffi, "test_include_8", "struct foo_s { int x, y; };") e = py.test.raises(NotImplementedError, ffi.new, "struct foo_s *") assert str(e.value) == ( "'struct foo_s' is opaque in the ffi.include(), but no longer in " "the ffi doing the include (workaround: don't use ffi.include() but" " duplicate the declarations of everything using struct foo_s)") def test_unicode_libraries(): try: unicode except NameError: py.test.skip("for python 2.x") # import math lib_m = "m" if sys.platform == 'win32': #there is a small chance this fails on Mingw via environ $CC import distutils.ccompiler if distutils.ccompiler.get_default_compiler() == 'msvc': lib_m = 'msvcrt' ffi = FFI() ffi.cdef(unicode("float sin(double); double cos(double);")) lib = verify(ffi, 'test_math_sin_unicode', unicode('#include '), libraries=[unicode(lib_m)]) assert lib.cos(1.43) == math.cos(1.43) def test_incomplete_struct_as_arg(): ffi = FFI() ffi.cdef("struct foo_s { int x; ...; }; int f(int, struct foo_s);") lib = verify(ffi, "test_incomplete_struct_as_arg", "struct foo_s { int a, x, z; };\n" "int f(int b, struct foo_s s) { return s.x * b; }") s = ffi.new("struct foo_s *", [21]) assert s.x == 21 assert ffi.sizeof(s[0]) == 12 assert ffi.offsetof(ffi.typeof(s), 'x') == 4 assert lib.f(2, s[0]) == 42 assert ffi.typeof(lib.f) == ffi.typeof("int(*)(int, struct foo_s)") def test_incomplete_struct_as_result(): ffi = FFI() ffi.cdef("struct foo_s { int x; ...; }; struct foo_s f(int);") lib = verify(ffi, "test_incomplete_struct_as_result", "struct foo_s { int a, x, z; };\n" "struct foo_s f(int x) { struct foo_s r; r.x = x * 2; return r; }") s = lib.f(21) assert s.x == 42 assert ffi.typeof(lib.f) == ffi.typeof("struct foo_s(*)(int)") def test_incomplete_struct_as_both(): ffi = FFI() ffi.cdef("struct foo_s { int x; ...; }; struct bar_s { int y; ...; };\n" "struct foo_s f(int, struct bar_s);") lib = verify(ffi, "test_incomplete_struct_as_both", "struct foo_s { int a, x, z; };\n" "struct bar_s { int b, c, y, d; };\n" "struct foo_s f(int x, struct bar_s b) {\n" " struct foo_s r; r.x = x * b.y; return r;\n" "}") b = ffi.new("struct bar_s *", [7]) s = lib.f(6, b[0]) assert s.x == 42 assert ffi.typeof(lib.f) == ffi.typeof( "struct foo_s(*)(int, struct bar_s)") s = lib.f(14, {'y': -3}) assert s.x == -42 def test_name_of_unnamed_struct(): ffi = FFI() ffi.cdef("typedef struct { int x; } foo_t;\n" "typedef struct { int y; } *bar_p;\n" "typedef struct { int y; } **baz_pp;\n") verify(ffi, "test_name_of_unnamed_struct", "typedef struct { int x; } foo_t;\n" "typedef struct { int y; } *bar_p;\n" "typedef struct { int y; } **baz_pp;\n") assert repr(ffi.typeof("foo_t")) == "" assert repr(ffi.typeof("bar_p")) == "" assert repr(ffi.typeof("baz_pp")) == "" def test_address_of_global_var(): ffi = FFI() ffi.cdef(""" long bottom, bottoms[2]; long FetchRectBottom(void); long FetchRectBottoms1(void); #define FOOBAR 42 """) lib = verify(ffi, "test_address_of_global_var", """ long bottom, bottoms[2]; long FetchRectBottom(void) { return bottom; } long FetchRectBottoms1(void) { return bottoms[1]; } #define FOOBAR 42 """) lib.bottom = 300 assert lib.FetchRectBottom() == 300 lib.bottom += 1 assert lib.FetchRectBottom() == 301 lib.bottoms[1] = 500 assert lib.FetchRectBottoms1() == 500 lib.bottoms[1] += 2 assert lib.FetchRectBottoms1() == 502 # p = ffi.addressof(lib, 'bottom') assert ffi.typeof(p) == ffi.typeof("long *") assert p[0] == 301 p[0] += 1 assert lib.FetchRectBottom() == 302 p = ffi.addressof(lib, 'bottoms') assert ffi.typeof(p) == ffi.typeof("long(*)[2]") assert p[0] == lib.bottoms # py.test.raises(AttributeError, ffi.addressof, lib, 'unknown_var') py.test.raises(AttributeError, ffi.addressof, lib, "FOOBAR") def test_defines__CFFI_(): # Check that we define the macro _CFFI_ automatically. # It should be done before including Python.h, so that PyPy's Python.h # can check for it. ffi = FFI() ffi.cdef(""" #define CORRECT 1 """) lib = verify(ffi, "test_defines__CFFI_", """ #ifdef _CFFI_ # define CORRECT 1 #endif """) assert lib.CORRECT == 1 def test_unpack_args(): ffi = FFI() ffi.cdef("void foo0(void); void foo1(int); void foo2(int, int);") lib = verify(ffi, "test_unpack_args", """ void foo0(void) { } void foo1(int x) { } void foo2(int x, int y) { } """) assert 'foo0' in repr(lib.foo0) assert 'foo1' in repr(lib.foo1) assert 'foo2' in repr(lib.foo2) lib.foo0() lib.foo1(42) lib.foo2(43, 44) e1 = py.test.raises(TypeError, lib.foo0, 42) e2 = py.test.raises(TypeError, lib.foo0, 43, 44) e3 = py.test.raises(TypeError, lib.foo1) e4 = py.test.raises(TypeError, lib.foo1, 43, 44) e5 = py.test.raises(TypeError, lib.foo2) e6 = py.test.raises(TypeError, lib.foo2, 42) e7 = py.test.raises(TypeError, lib.foo2, 45, 46, 47) assert str(e1.value) == "foo0() takes no arguments (1 given)" assert str(e2.value) == "foo0() takes no arguments (2 given)" assert str(e3.value) == "foo1() takes exactly one argument (0 given)" assert str(e4.value) == "foo1() takes exactly one argument (2 given)" assert str(e5.value) in ["foo2 expected 2 arguments, got 0", "foo2() takes exactly 2 arguments (0 given)"] assert str(e6.value) in ["foo2 expected 2 arguments, got 1", "foo2() takes exactly 2 arguments (1 given)"] assert str(e7.value) in ["foo2 expected 2 arguments, got 3", "foo2() takes exactly 2 arguments (3 given)"] def test_address_of_function(): ffi = FFI() ffi.cdef("long myfunc(long x);") lib = verify(ffi, "test_addressof_function", """ char myfunc(char x) { return (char)(x + 42); } """) assert lib.myfunc(5) == 47 assert lib.myfunc(0xABC05) == 47 assert not isinstance(lib.myfunc, ffi.CData) assert ffi.typeof(lib.myfunc) == ffi.typeof("long(*)(long)") addr = ffi.addressof(lib, 'myfunc') assert addr(5) == 47 assert addr(0xABC05) == 47 assert isinstance(addr, ffi.CData) assert ffi.typeof(addr) == ffi.typeof("long(*)(long)") def test_address_of_function_with_struct(): ffi = FFI() ffi.cdef("struct foo_s { int x; }; long myfunc(struct foo_s);") lib = verify(ffi, "test_addressof_function_with_struct", """ struct foo_s { int x; }; char myfunc(struct foo_s input) { return (char)(input.x + 42); } """) s = ffi.new("struct foo_s *", [5])[0] assert lib.myfunc(s) == 47 assert not isinstance(lib.myfunc, ffi.CData) assert ffi.typeof(lib.myfunc) == ffi.typeof("long(*)(struct foo_s)") addr = ffi.addressof(lib, 'myfunc') assert addr(s) == 47 assert isinstance(addr, ffi.CData) assert ffi.typeof(addr) == ffi.typeof("long(*)(struct foo_s)") def test_issue198(): ffi = FFI() ffi.cdef(""" typedef struct{...;} opaque_t; const opaque_t CONSTANT; int toint(opaque_t); """) lib = verify(ffi, 'test_issue198', """ typedef int opaque_t; #define CONSTANT ((opaque_t)42) static int toint(opaque_t o) { return o; } """) def random_stuff(): pass assert lib.toint(lib.CONSTANT) == 42 random_stuff() assert lib.toint(lib.CONSTANT) == 42 def test_constant_is_not_a_compiler_constant(): ffi = FFI() ffi.cdef("static const float almost_forty_two;") lib = verify(ffi, 'test_constant_is_not_a_compiler_constant', """ static float f(void) { return 42.25; } #define almost_forty_two (f()) """) assert lib.almost_forty_two == 42.25 def test_constant_of_unknown_size(): ffi = FFI() ffi.cdef(""" typedef ... opaque_t; const opaque_t CONSTANT; """) lib = verify(ffi, 'test_constant_of_unknown_size', "typedef int opaque_t;" "const int CONSTANT = 42;") e = py.test.raises(ffi.error, getattr, lib, 'CONSTANT') assert str(e.value) == ("constant 'CONSTANT' is of " "type 'opaque_t', whose size is not known") def test_variable_of_unknown_size(): ffi = FFI() ffi.cdef(""" typedef ... opaque_t; opaque_t globvar; """) lib = verify(ffi, 'test_variable_of_unknown_size', """ typedef char opaque_t[6]; opaque_t globvar = "hello"; """) # can't read or write it at all e = py.test.raises(TypeError, getattr, lib, 'globvar') assert str(e.value) in ["cdata 'opaque_t' is opaque", "'opaque_t' is opaque or not completed yet"] #pypy e = py.test.raises(TypeError, setattr, lib, 'globvar', []) assert str(e.value) in ["'opaque_t' is opaque", "'opaque_t' is opaque or not completed yet"] #pypy # but we can get its address p = ffi.addressof(lib, 'globvar') assert ffi.typeof(p) == ffi.typeof('opaque_t *') assert ffi.string(ffi.cast("char *", p), 8) == b"hello" def test_constant_of_value_unknown_to_the_compiler(): extra_c_source = udir.join( 'extra_test_constant_of_value_unknown_to_the_compiler.c') extra_c_source.write('const int external_foo = 42;\n') ffi = FFI() ffi.cdef("const int external_foo;") lib = verify(ffi, 'test_constant_of_value_unknown_to_the_compiler', """ extern const int external_foo; """, sources=[str(extra_c_source)]) assert lib.external_foo == 42 def test_dotdot_in_source_file_names(): extra_c_source = udir.join( 'extra_test_dotdot_in_source_file_names.c') extra_c_source.write('const int external_foo = 42;\n') ffi = FFI() ffi.cdef("const int external_foo;") lib = verify(ffi, 'test_dotdot_in_source_file_names', """ extern const int external_foo; """, sources=[os.path.join(os.path.dirname(str(extra_c_source)), 'foobar', '..', os.path.basename(str(extra_c_source)))]) assert lib.external_foo == 42 def test_call_with_incomplete_structs(): ffi = FFI() ffi.cdef("typedef struct {...;} foo_t; " "foo_t myglob; " "foo_t increment(foo_t s); " "double getx(foo_t s);") lib = verify(ffi, 'test_call_with_incomplete_structs', """ typedef double foo_t; double myglob = 42.5; double getx(double x) { return x; } double increment(double x) { return x + 1; } """) assert lib.getx(lib.myglob) == 42.5 assert lib.getx(lib.increment(lib.myglob)) == 43.5 def test_struct_array_guess_length_2(): ffi = FFI() ffi.cdef("struct foo_s { int a[...][...]; };") lib = verify(ffi, 'test_struct_array_guess_length_2', "struct foo_s { int x; int a[5][8]; int y; };") assert ffi.sizeof('struct foo_s') == 42 * ffi.sizeof('int') s = ffi.new("struct foo_s *") assert ffi.typeof(s.a) == ffi.typeof("int[5][8]") assert ffi.sizeof(s.a) == 40 * ffi.sizeof('int') assert s.a[4][7] == 0 py.test.raises(IndexError, 's.a[4][8]') py.test.raises(IndexError, 's.a[5][0]') assert ffi.typeof(s.a) == ffi.typeof("int[5][8]") assert ffi.typeof(s.a[0]) == ffi.typeof("int[8]") def test_struct_array_guess_length_3(): ffi = FFI() ffi.cdef("struct foo_s { int a[][...]; };") lib = verify(ffi, 'test_struct_array_guess_length_3', "struct foo_s { int x; int a[5][7]; int y; };") assert ffi.sizeof('struct foo_s') == 37 * ffi.sizeof('int') s = ffi.new("struct foo_s *") assert ffi.typeof(s.a) == ffi.typeof("int[][7]") assert s.a[4][6] == 0 py.test.raises(IndexError, 's.a[4][7]') assert ffi.typeof(s.a[0]) == ffi.typeof("int[7]") def test_global_var_array_2(): ffi = FFI() ffi.cdef("int a[...][...];") lib = verify(ffi, 'test_global_var_array_2', 'int a[10][8];') lib.a[9][7] = 123456 assert lib.a[9][7] == 123456 py.test.raises(IndexError, 'lib.a[0][8]') py.test.raises(IndexError, 'lib.a[10][0]') assert ffi.typeof(lib.a) == ffi.typeof("int[10][8]") assert ffi.typeof(lib.a[0]) == ffi.typeof("int[8]") def test_global_var_array_3(): ffi = FFI() ffi.cdef("int a[][...];") lib = verify(ffi, 'test_global_var_array_3', 'int a[10][8];') lib.a[9][7] = 123456 assert lib.a[9][7] == 123456 py.test.raises(IndexError, 'lib.a[0][8]') assert ffi.typeof(lib.a) == ffi.typeof("int(*)[8]") assert ffi.typeof(lib.a[0]) == ffi.typeof("int[8]") def test_global_var_array_4(): ffi = FFI() ffi.cdef("int a[10][...];") lib = verify(ffi, 'test_global_var_array_4', 'int a[10][8];') lib.a[9][7] = 123456 assert lib.a[9][7] == 123456 py.test.raises(IndexError, 'lib.a[0][8]') py.test.raises(IndexError, 'lib.a[10][8]') assert ffi.typeof(lib.a) == ffi.typeof("int[10][8]") assert ffi.typeof(lib.a[0]) == ffi.typeof("int[8]") def test_some_integer_type(): ffi = FFI() ffi.cdef(""" typedef int... foo_t; typedef unsigned long... bar_t; typedef struct { foo_t a, b; } mystruct_t; foo_t foobar(bar_t, mystruct_t); static const bar_t mu = -20; static const foo_t nu = 20; """) lib = verify(ffi, 'test_some_integer_type', """ typedef unsigned long long foo_t; typedef short bar_t; typedef struct { foo_t a, b; } mystruct_t; static foo_t foobar(bar_t x, mystruct_t s) { return (foo_t)x + s.a + s.b; } static const bar_t mu = -20; static const foo_t nu = 20; """) assert ffi.sizeof("foo_t") == ffi.sizeof("unsigned long long") assert ffi.sizeof("bar_t") == ffi.sizeof("short") maxulonglong = 2 ** 64 - 1 assert int(ffi.cast("foo_t", -1)) == maxulonglong assert int(ffi.cast("bar_t", -1)) == -1 assert lib.foobar(-1, [0, 0]) == maxulonglong assert lib.foobar(2 ** 15 - 1, [0, 0]) == 2 ** 15 - 1 assert lib.foobar(10, [20, 31]) == 61 assert lib.foobar(0, [0, maxulonglong]) == maxulonglong py.test.raises(OverflowError, lib.foobar, 2 ** 15, [0, 0]) py.test.raises(OverflowError, lib.foobar, -(2 ** 15) - 1, [0, 0]) py.test.raises(OverflowError, ffi.new, "mystruct_t *", [0, -1]) assert lib.mu == -20 assert lib.nu == 20 def test_some_float_type(): ffi = FFI() ffi.cdef(""" typedef double... foo_t; typedef float... bar_t; foo_t sum(foo_t[]); bar_t neg(bar_t); """) lib = verify(ffi, 'test_some_float_type', """ typedef float foo_t; static foo_t sum(foo_t x[]) { return x[0] + x[1]; } typedef double bar_t; static double neg(double x) { return -x; } """) assert lib.sum([40.0, 2.25]) == 42.25 assert lib.sum([12.3, 45.6]) != 12.3 + 45.6 # precision loss assert lib.neg(12.3) == -12.3 # no precision loss assert ffi.sizeof("foo_t") == ffi.sizeof("float") assert ffi.sizeof("bar_t") == ffi.sizeof("double") def test_some_float_invalid_1(): ffi = FFI() py.test.raises((FFIError, # with pycparser <= 2.17 CDefError), # with pycparser >= 2.18 ffi.cdef, "typedef long double... foo_t;") def test_some_float_invalid_2(): ffi = FFI() ffi.cdef("typedef double... foo_t; foo_t neg(foo_t);") lib = verify(ffi, 'test_some_float_invalid_2', """ typedef unsigned long foo_t; foo_t neg(foo_t x) { return -x; } """) e = py.test.raises(ffi.error, getattr, lib, 'neg') assert str(e.value) == ("primitive floating-point type with an unexpected " "size (or not a float type at all)") def test_some_float_invalid_3(): ffi = FFI() ffi.cdef("typedef double... foo_t; foo_t neg(foo_t);") lib = verify(ffi, 'test_some_float_invalid_3', """ typedef long double foo_t; foo_t neg(foo_t x) { return -x; } """) if ffi.sizeof("long double") == ffi.sizeof("double"): assert lib.neg(12.3) == -12.3 else: e = py.test.raises(ffi.error, getattr, lib, 'neg') assert str(e.value) == ("primitive floating-point type is " "'long double', not supported for now with " "the syntax 'typedef double... xxx;'") def test_issue200(): ffi = FFI() ffi.cdef(""" typedef void (function_t)(void*); void function(void *); """) lib = verify(ffi, 'test_issue200', """ static void function(void *p) { (void)p; } """) ffi.typeof('function_t*') lib.function(ffi.NULL) # assert did not crash def test_alignment_of_longlong(): ffi = FFI() x1 = ffi.alignof('unsigned long long') assert x1 in [4, 8] ffi.cdef("struct foo_s { unsigned long long x; };") lib = verify(ffi, 'test_alignment_of_longlong', "struct foo_s { unsigned long long x; };") assert ffi.alignof('unsigned long long') == x1 assert ffi.alignof('struct foo_s') == x1 def test_import_from_lib(): ffi = FFI() ffi.cdef("int mybar(int); int myvar;\n#define MYFOO ...") lib = verify(ffi, 'test_import_from_lib', "#define MYFOO 42\n" "static int mybar(int x) { return x + 1; }\n" "static int myvar = -5;") assert sys.modules['_CFFI_test_import_from_lib'].lib is lib assert sys.modules['_CFFI_test_import_from_lib.lib'] is lib from _CFFI_test_import_from_lib.lib import MYFOO assert MYFOO == 42 assert hasattr(lib, '__dict__') assert lib.__all__ == ['MYFOO', 'mybar'] # but not 'myvar' assert lib.__name__ == '_CFFI_test_import_from_lib.lib' assert lib.__class__ is type(sys) # !! hack for help() def test_macro_var_callback(): ffi = FFI() ffi.cdef("int my_value; int *(*get_my_value)(void);") lib = verify(ffi, 'test_macro_var_callback', "int *(*get_my_value)(void);\n" "#define my_value (*get_my_value())") # values = ffi.new("int[50]") def it(): for i in range(50): yield i it = it() # @ffi.callback("int *(*)(void)") def get_my_value(): for nextvalue in it: return values + nextvalue lib.get_my_value = get_my_value # values[0] = 41 assert lib.my_value == 41 # [0] p = ffi.addressof(lib, 'my_value') # [1] assert p == values + 1 assert p[-1] == 41 assert p[+1] == 0 lib.my_value = 42 # [2] assert values[2] == 42 assert p[-1] == 41 assert p[+1] == 42 # # if get_my_value raises or returns nonsense, the exception is printed # to stderr like with any callback, but then the C expression 'my_value' # expand to '*NULL'. We assume here that '&my_value' will return NULL # without segfaulting, and check for NULL when accessing the variable. @ffi.callback("int *(*)(void)") def get_my_value(): raise LookupError lib.get_my_value = get_my_value py.test.raises(ffi.error, getattr, lib, 'my_value') py.test.raises(ffi.error, setattr, lib, 'my_value', 50) py.test.raises(ffi.error, ffi.addressof, lib, 'my_value') @ffi.callback("int *(*)(void)") def get_my_value(): return "hello" lib.get_my_value = get_my_value py.test.raises(ffi.error, getattr, lib, 'my_value') e = py.test.raises(ffi.error, setattr, lib, 'my_value', 50) assert str(e.value) == "global variable 'my_value' is at address NULL" def test_const_fields(): ffi = FFI() ffi.cdef("""struct foo_s { const int a; void *const b; };""") lib = verify(ffi, 'test_const_fields', """ struct foo_s { const int a; void *const b; };""") foo_s = ffi.typeof("struct foo_s") assert foo_s.fields[0][0] == 'a' 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_restrict_fields(): ffi = FFI() ffi.cdef("""struct foo_s { void * restrict b; };""") lib = verify(ffi, 'test_restrict_fields', """ struct foo_s { void * __restrict b; };""") foo_s = ffi.typeof("struct foo_s") assert foo_s.fields[0][0] == 'b' assert foo_s.fields[0][1].type is ffi.typeof("void *") def test_volatile_fields(): ffi = FFI() ffi.cdef("""struct foo_s { void * volatile b; };""") lib = verify(ffi, 'test_volatile_fields', """ struct foo_s { void * volatile b; };""") foo_s = ffi.typeof("struct foo_s") assert foo_s.fields[0][0] == 'b' assert foo_s.fields[0][1].type is ffi.typeof("void *") def test_const_array_fields(): ffi = FFI() ffi.cdef("""struct foo_s { const int a[4]; };""") lib = verify(ffi, 'test_const_array_fields', """ struct foo_s { const int a[4]; };""") foo_s = ffi.typeof("struct foo_s") assert foo_s.fields[0][0] == 'a' assert foo_s.fields[0][1].type is ffi.typeof("int[4]") def test_const_array_fields_varlength(): ffi = FFI() ffi.cdef("""struct foo_s { const int a[]; ...; };""") lib = verify(ffi, 'test_const_array_fields_varlength', """ struct foo_s { const int a[4]; };""") foo_s = ffi.typeof("struct foo_s") assert foo_s.fields[0][0] == 'a' assert foo_s.fields[0][1].type is ffi.typeof("int[]") def test_const_array_fields_unknownlength(): ffi = FFI() ffi.cdef("""struct foo_s { const int a[...]; ...; };""") lib = verify(ffi, 'test_const_array_fields_unknownlength', """ struct foo_s { const int a[4]; };""") foo_s = ffi.typeof("struct foo_s") assert foo_s.fields[0][0] == 'a' assert foo_s.fields[0][1].type is ffi.typeof("int[4]") def test_const_function_args(): ffi = FFI() ffi.cdef("""int foobar(const int a, const int *b, const int c[]);""") lib = verify(ffi, 'test_const_function_args', """ int foobar(const int a, const int *b, const int c[]) { return a + *b + *c; } """) assert lib.foobar(100, ffi.new("int *", 40), ffi.new("int *", 2)) == 142 def test_const_function_type_args(): ffi = FFI() ffi.cdef("""int (*foobar)(const int a, const int *b, const int c[]);""") lib = verify(ffi, 'test_const_function_type_args', """ int (*foobar)(const int a, const int *b, const int c[]); """) t = ffi.typeof(lib.foobar) assert t.args[0] is ffi.typeof("int") assert t.args[1] is ffi.typeof("int *") assert t.args[2] is ffi.typeof("int *") def test_const_constant(): ffi = FFI() ffi.cdef("""struct foo_s { int x,y; }; const struct foo_s myfoo;""") lib = verify(ffi, 'test_const_constant', """ struct foo_s { int x,y; }; const struct foo_s myfoo = { 40, 2 }; """) assert lib.myfoo.x == 40 assert lib.myfoo.y == 2 def test_const_via_typedef(): ffi = FFI() ffi.cdef("""typedef const int const_t; const_t aaa;""") lib = verify(ffi, 'test_const_via_typedef', """ typedef const int const_t; #define aaa 42 """) 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' and not sys.maxsize > 2**32: 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' and not sys.maxsize > 2**32: 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' and not sys.maxsize > 2**32: 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) def test_extern_python_1(): import warnings ffi = FFI() with warnings.catch_warnings(record=True) as log: ffi.cdef(""" extern "Python" { int bar(int, int); void baz(int, int); int bok(void); void boz(void); } """) assert len(log) == 0, "got a warning: %r" % (log,) lib = verify(ffi, 'test_extern_python_1', """ static void baz(int, int); /* forward */ """) assert ffi.typeof(lib.bar) == ffi.typeof("int(*)(int, int)") with FdWriteCapture() as f: res = lib.bar(4, 5) assert res == 0 assert f.getvalue() == ( b"extern \"Python\": function _CFFI_test_extern_python_1.bar() called, " b"but no code was attached " b"to it yet with @ffi.def_extern(). Returning 0.\n") @ffi.def_extern("bar") def my_bar(x, y): seen.append(("Bar", x, y)) return x * y assert my_bar != lib.bar seen = [] res = lib.bar(6, 7) assert seen == [("Bar", 6, 7)] assert res == 42 def baz(x, y): seen.append(("Baz", x, y)) baz1 = ffi.def_extern()(baz) assert baz1 is baz seen = [] baz(long(40), long(4)) res = lib.baz(long(50), long(8)) assert res is None assert seen == [("Baz", 40, 4), ("Baz", 50, 8)] assert type(seen[0][1]) is type(seen[0][2]) is long assert type(seen[1][1]) is type(seen[1][2]) is int @ffi.def_extern(name="bok") def bokk(): seen.append("Bok") return 42 seen = [] assert lib.bok() == 42 assert seen == ["Bok"] @ffi.def_extern() def boz(): seen.append("Boz") seen = [] assert lib.boz() is None assert seen == ["Boz"] def test_extern_python_bogus_name(): ffi = FFI() ffi.cdef("int abc;") lib = verify(ffi, 'test_extern_python_bogus_name', "int abc;") def fn(): pass py.test.raises(ffi.error, ffi.def_extern("unknown_name"), fn) py.test.raises(ffi.error, ffi.def_extern("abc"), fn) assert lib.abc == 0 e = py.test.raises(ffi.error, ffi.def_extern("abc"), fn) assert str(e.value) == ("ffi.def_extern('abc'): no 'extern \"Python\"' " "function with this name") e = py.test.raises(ffi.error, ffi.def_extern(), fn) assert str(e.value) == ("ffi.def_extern('fn'): no 'extern \"Python\"' " "function with this name") # py.test.raises(TypeError, ffi.def_extern(42), fn) py.test.raises((TypeError, AttributeError), ffi.def_extern(), "foo") class X: pass x = X() x.__name__ = x py.test.raises(TypeError, ffi.def_extern(), x) def test_extern_python_bogus_result_type(): ffi = FFI() ffi.cdef("""extern "Python" void bar(int);""") lib = verify(ffi, 'test_extern_python_bogus_result_type', "") # @ffi.def_extern() def bar(n): return n * 10 with StdErrCapture() as f: res = lib.bar(321) assert res is None assert f.getvalue() == ( "From cffi callback %r:\n" % (bar,) + "Trying to convert the result back to C:\n" "TypeError: callback with the return type 'void' must return None\n") def test_extern_python_redefine(): ffi = FFI() ffi.cdef("""extern "Python" int bar(int);""") lib = verify(ffi, 'test_extern_python_redefine', "") # @ffi.def_extern() def bar(n): return n * 10 assert lib.bar(42) == 420 # @ffi.def_extern() def bar(n): return -n assert lib.bar(42) == -42 def test_extern_python_struct(): ffi = FFI() ffi.cdef(""" struct foo_s { int a, b, c; }; extern "Python" int bar(int, struct foo_s, int); extern "Python" { struct foo_s baz(int, int); struct foo_s bok(void); } """) lib = verify(ffi, 'test_extern_python_struct', "struct foo_s { int a, b, c; };") # @ffi.def_extern() def bar(x, s, z): return x + s.a + s.b + s.c + z res = lib.bar(1000, [1001, 1002, 1004], 1008) assert res == 5015 # @ffi.def_extern() def baz(x, y): return [x + y, x - y, x * y] res = lib.baz(1000, 42) assert res.a == 1042 assert res.b == 958 assert res.c == 42000 # @ffi.def_extern() def bok(): return [10, 20, 30] res = lib.bok() assert [res.a, res.b, res.c] == [10, 20, 30] def test_extern_python_long_double(): ffi = FFI() ffi.cdef(""" extern "Python" int bar(int, long double, int); extern "Python" long double baz(int, int); extern "Python" long double bok(void); """) lib = verify(ffi, 'test_extern_python_long_double', "") # @ffi.def_extern() def bar(x, l, z): seen.append((x, l, z)) return 6 seen = [] lib.bar(10, 3.5, 20) expected = ffi.cast("long double", 3.5) assert repr(seen) == repr([(10, expected, 20)]) # @ffi.def_extern() def baz(x, z): assert x == 10 and z == 20 return expected res = lib.baz(10, 20) assert repr(res) == repr(expected) # @ffi.def_extern() def bok(): return expected res = lib.bok() assert repr(res) == repr(expected) def test_extern_python_signature(): ffi = FFI() lib = verify(ffi, 'test_extern_python_signature', "") py.test.raises(TypeError, ffi.def_extern(425), None) py.test.raises(TypeError, ffi.def_extern, 'a', 'b', 'c', 'd') def test_extern_python_errors(): ffi = FFI() ffi.cdef(""" extern "Python" int bar(int); """) lib = verify(ffi, 'test_extern_python_errors', "") seen = [] def oops(*args): seen.append(args) @ffi.def_extern(onerror=oops) def bar(x): return x + "" assert lib.bar(10) == 0 @ffi.def_extern(name="bar", onerror=oops, error=-66) def bar2(x): return x + "" assert lib.bar(10) == -66 assert len(seen) == 2 exc, val, tb = seen[0] assert exc is TypeError assert isinstance(val, TypeError) assert tb.tb_frame.f_code.co_name == "bar" exc, val, tb = seen[1] assert exc is TypeError assert isinstance(val, TypeError) assert tb.tb_frame.f_code.co_name == "bar2" # # a case where 'onerror' is not callable py.test.raises(TypeError, ffi.def_extern(name='bar', onerror=42), lambda x: x) def test_extern_python_stdcall(): ffi = FFI() ffi.cdef(""" extern "Python" int __stdcall foo(int); extern "Python" int WINAPI bar(int); int (__stdcall * mycb1)(int); int indirect_call(int); """) lib = verify(ffi, 'test_extern_python_stdcall', """ #ifndef _MSC_VER # define __stdcall #endif static int (__stdcall * mycb1)(int); static int indirect_call(int x) { return mycb1(x); } """) # @ffi.def_extern() def foo(x): return x + 42 @ffi.def_extern() def bar(x): return x + 43 assert lib.foo(100) == 142 assert lib.bar(100) == 143 lib.mycb1 = lib.foo assert lib.mycb1(200) == 242 assert lib.indirect_call(300) == 342 def test_extern_python_plus_c(): ffi = FFI() ffi.cdef(""" extern "Python+C" int foo(int); extern "C +\tPython" int bar(int); int call_me(int); """) lib = verify(ffi, 'test_extern_python_plus_c', """ int foo(int); #ifdef __GNUC__ __attribute__((visibility("hidden"))) #endif int bar(int); static int call_me(int x) { return foo(x) - bar(x); } """) # @ffi.def_extern() def foo(x): return x * 42 @ffi.def_extern() def bar(x): return x * 63 assert lib.foo(100) == 4200 assert lib.bar(100) == 6300 assert lib.call_me(100) == -2100 def test_introspect_function(): ffi = FFI() ffi.cdef("float f1(double);") lib = verify(ffi, 'test_introspect_function', """ float f1(double x) { return x; } """) assert dir(lib) == ['f1'] FUNC = ffi.typeof(lib.f1) assert FUNC.kind == 'function' assert FUNC.args[0].cname == 'double' assert FUNC.result.cname == 'float' assert ffi.typeof(ffi.addressof(lib, 'f1')) is FUNC def test_introspect_global_var(): ffi = FFI() ffi.cdef("float g1;") lib = verify(ffi, 'test_introspect_global_var', """ float g1; """) assert dir(lib) == ['g1'] FLOATPTR = ffi.typeof(ffi.addressof(lib, 'g1')) assert FLOATPTR.kind == 'pointer' assert FLOATPTR.item.cname == 'float' def test_introspect_global_var_array(): ffi = FFI() ffi.cdef("float g1[100];") lib = verify(ffi, 'test_introspect_global_var_array', """ float g1[100]; """) assert dir(lib) == ['g1'] FLOATARRAYPTR = ffi.typeof(ffi.addressof(lib, 'g1')) assert FLOATARRAYPTR.kind == 'pointer' assert FLOATARRAYPTR.item.kind == 'array' assert FLOATARRAYPTR.item.length == 100 assert ffi.typeof(lib.g1) is FLOATARRAYPTR.item def test_introspect_integer_const(): ffi = FFI() ffi.cdef("#define FOO 42") lib = verify(ffi, 'test_introspect_integer_const', """ #define FOO 42 """) assert dir(lib) == ['FOO'] assert lib.FOO == ffi.integer_const('FOO') == 42 def test_introspect_typedef(): ffi = FFI() ffi.cdef("typedef int foo_t;") lib = verify(ffi, 'test_introspect_typedef', """ typedef int foo_t; """) assert ffi.list_types() == (['foo_t'], [], []) assert ffi.typeof('foo_t').kind == 'primitive' assert ffi.typeof('foo_t').cname == 'int' def test_introspect_typedef_multiple(): ffi = FFI() ffi.cdef("typedef signed char a_t, c_t, g_t, b_t;") lib = verify(ffi, 'test_introspect_typedef_multiple', """ typedef signed char a_t, c_t, g_t, b_t; """) assert ffi.list_types() == (['a_t', 'b_t', 'c_t', 'g_t'], [], []) def test_introspect_struct(): ffi = FFI() ffi.cdef("struct foo_s { int a; };") lib = verify(ffi, 'test_introspect_struct', """ struct foo_s { int a; }; """) assert ffi.list_types() == ([], ['foo_s'], []) assert ffi.typeof('struct foo_s').kind == 'struct' assert ffi.typeof('struct foo_s').cname == 'struct foo_s' def test_introspect_union(): ffi = FFI() ffi.cdef("union foo_s { int a; };") lib = verify(ffi, 'test_introspect_union', """ union foo_s { int a; }; """) assert ffi.list_types() == ([], [], ['foo_s']) assert ffi.typeof('union foo_s').kind == 'union' assert ffi.typeof('union foo_s').cname == 'union foo_s' def test_introspect_struct_and_typedef(): ffi = FFI() ffi.cdef("typedef struct { int a; } foo_t;") lib = verify(ffi, 'test_introspect_struct_and_typedef', """ typedef struct { int a; } foo_t; """) assert ffi.list_types() == (['foo_t'], [], []) assert ffi.typeof('foo_t').kind == 'struct' assert ffi.typeof('foo_t').cname == 'foo_t' def test_introspect_included_type(): SOURCE = """ typedef signed char schar_t; struct sint_t { int x; }; """ ffi1 = FFI() ffi1.cdef(SOURCE) ffi2 = FFI() ffi2.include(ffi1) verify(ffi1, "test_introspect_included_type_parent", SOURCE) verify(ffi2, "test_introspect_included_type", SOURCE) assert ffi1.list_types() == ffi2.list_types() == ( ['schar_t'], ['sint_t'], []) def test_introspect_order(): ffi = FFI() ffi.cdef("union CFFIaaa { int a; }; typedef struct CFFIccc { int a; } CFFIb;") ffi.cdef("union CFFIg { int a; }; typedef struct CFFIcc { int a; } CFFIbbb;") ffi.cdef("union CFFIaa { int a; }; typedef struct CFFIa { int a; } CFFIbb;") verify(ffi, "test_introspect_order", """ union CFFIaaa { int a; }; typedef struct CFFIccc { int a; } CFFIb; union CFFIg { int a; }; typedef struct CFFIcc { int a; } CFFIbbb; union CFFIaa { int a; }; typedef struct CFFIa { int a; } CFFIbb; """) assert ffi.list_types() == (['CFFIb', 'CFFIbb', 'CFFIbbb'], ['CFFIa', 'CFFIcc', 'CFFIccc'], ['CFFIaa', 'CFFIaaa', 'CFFIg']) def test_bool_in_cpp(): # this works when compiled as C, but in cffi < 1.7 it fails as C++ ffi = FFI() ffi.cdef("bool f(void);") lib = verify(ffi, "test_bool_in_cpp", "char f(void) { return 2; }") assert lib.f() is True def test_bool_in_cpp_2(): ffi = FFI() ffi.cdef('int add(int a, int b);') lib = verify(ffi, "test_bool_bug_cpp", ''' typedef bool _Bool; /* there is a Windows header with this line */ int add(int a, int b) { return a + b; }''', source_extension='.cpp') c = lib.add(2, 3) assert c == 5 def test_struct_field_opaque(): ffi = FFI() ffi.cdef("struct a { struct b b; };") e = py.test.raises(TypeError, verify, ffi, "test_struct_field_opaque", "?") assert str(e.value) == ("struct a: field 'a.b' is of an opaque" " type (not declared in cdef())") ffi = FFI() ffi.cdef("struct a { struct b b[2]; };") e = py.test.raises(TypeError, verify, ffi, "test_struct_field_opaque", "?") assert str(e.value) == ("struct a: field 'a.b' is of an opaque" " type (not declared in cdef())") ffi = FFI() ffi.cdef("struct a { struct b b[]; };") e = py.test.raises(TypeError, verify, ffi, "test_struct_field_opaque", "?") assert str(e.value) == ("struct a: field 'a.b' is of an opaque" " type (not declared in cdef())") def test_function_arg_opaque(): py.test.skip("can currently declare a function with an opaque struct " "as argument, but AFAICT it's impossible to call it later") def test_function_returns_opaque(): ffi = FFI() ffi.cdef("struct a foo(int);") e = py.test.raises(TypeError, verify, ffi, "test_function_returns_opaque", "?") assert str(e.value) == ("function foo: 'struct a' is used as result type," " but is opaque") def test_function_returns_union(): ffi = FFI() ffi.cdef("union u1 { int a, b; }; union u1 f1(int);") lib = verify(ffi, "test_function_returns_union", """ union u1 { int a, b; }; static union u1 f1(int x) { union u1 u; u.b = x; return u; } """) assert lib.f1(51).a == 51 def test_function_returns_partial_struct(): ffi = FFI() ffi.cdef("struct aaa { int a; ...; }; struct aaa f1(int);") lib = verify(ffi, "test_function_returns_partial_struct", """ struct aaa { int b, a, c; }; static struct aaa f1(int x) { struct aaa s = {0}; s.a = x; return s; } """) assert lib.f1(52).a == 52 def test_function_returns_float_complex(): if sys.platform == 'win32': py.test.skip("MSVC may not support _Complex") ffi = FFI() ffi.cdef("float _Complex f1(float a, float b);"); lib = verify(ffi, "test_function_returns_float_complex", """ #include static float _Complex f1(float a, float b) { return a + I*2.0*b; } """, no_cpp=True) # fails on some systems with C++ result = lib.f1(1.25, 5.1) assert type(result) == complex assert result.real == 1.25 # exact assert (result.imag != 2*5.1) and (abs(result.imag - 2*5.1) < 1e-5) # inexact def test_function_returns_double_complex(): if sys.platform == 'win32': py.test.skip("MSVC may not support _Complex") ffi = FFI() ffi.cdef("double _Complex f1(double a, double b);"); lib = verify(ffi, "test_function_returns_double_complex", """ #include static double _Complex f1(double a, double b) { return a + I*2.0*b; } """, no_cpp=True) # fails on some systems with C++ result = lib.f1(1.25, 5.1) assert type(result) == complex assert result.real == 1.25 # exact assert result.imag == 2*5.1 # exact def test_function_argument_float_complex(): if sys.platform == 'win32': py.test.skip("MSVC may not support _Complex") ffi = FFI() ffi.cdef("float f1(float _Complex x);"); lib = verify(ffi, "test_function_argument_float_complex", """ #include static float f1(float _Complex x) { return cabsf(x); } """, no_cpp=True) # fails on some systems with C++ x = complex(12.34, 56.78) result = lib.f1(x) assert abs(result - abs(x)) < 1e-5 def test_function_argument_double_complex(): if sys.platform == 'win32': py.test.skip("MSVC may not support _Complex") ffi = FFI() ffi.cdef("double f1(double _Complex);"); lib = verify(ffi, "test_function_argument_double_complex", """ #include static double f1(double _Complex x) { return cabs(x); } """, no_cpp=True) # fails on some systems with C++ x = complex(12.34, 56.78) result = lib.f1(x) assert abs(result - abs(x)) < 1e-11 def test_typedef_array_dotdotdot(): ffi = FFI() ffi.cdef(""" typedef int foo_t[...], bar_t[...]; int gv[...]; typedef int mat_t[...][...]; typedef int vmat_t[][...]; """) lib = verify(ffi, "test_typedef_array_dotdotdot", """ typedef int foo_t[50], bar_t[50]; int gv[23]; typedef int mat_t[6][7]; typedef int vmat_t[][8]; """) assert ffi.sizeof("foo_t") == 50 * ffi.sizeof("int") assert ffi.sizeof("bar_t") == 50 * ffi.sizeof("int") assert len(ffi.new("foo_t")) == 50 assert len(ffi.new("bar_t")) == 50 assert ffi.sizeof(lib.gv) == 23 * ffi.sizeof("int") assert ffi.sizeof("mat_t") == 6 * 7 * ffi.sizeof("int") assert len(ffi.new("mat_t")) == 6 assert len(ffi.new("mat_t")[3]) == 7 py.test.raises(ffi.error, ffi.sizeof, "vmat_t") p = ffi.new("vmat_t", 4) assert ffi.sizeof(p[3]) == 8 * ffi.sizeof("int") def test_call_with_custom_field_pos(): ffi = FFI() ffi.cdef(""" struct foo { int x; ...; }; struct foo f(void); struct foo g(int, ...); """) lib = verify(ffi, "test_call_with_custom_field_pos", """ struct foo { int y, x; }; struct foo f(void) { struct foo s = { 40, 200 }; return s; } struct foo g(int a, ...) { return f(); } """) assert lib.f().x == 200 e = py.test.raises(NotImplementedError, lib.g, 0) assert str(e.value) == ( 'ctype \'struct foo\' not supported as return value. It is a ' 'struct declared with "...;", but the C calling convention may ' 'depend on the missing fields; or, it contains anonymous ' 'struct/unions. Such structs are only supported ' 'as return value if the function is \'API mode\' and non-variadic ' '(i.e. declared inside ffibuilder.cdef()+ffibuilder.set_source() ' 'and not taking a final \'...\' argument)') def test_call_with_nested_anonymous_struct(): if sys.platform == 'win32': py.test.skip("needs a GCC extension") ffi = FFI() ffi.cdef(""" struct foo { int a; union { int b, c; }; }; struct foo f(void); struct foo g(int, ...); """) lib = verify(ffi, "test_call_with_nested_anonymous_struct", """ struct foo { int a; union { int b, c; }; }; struct foo f(void) { struct foo s = { 40 }; s.b = 200; return s; } struct foo g(int a, ...) { return f(); } """) assert lib.f().b == 200 e = py.test.raises(NotImplementedError, lib.g, 0) assert str(e.value) == ( 'ctype \'struct foo\' not supported as return value. It is a ' 'struct declared with "...;", but the C calling convention may ' 'depend on the missing fields; or, it contains anonymous ' 'struct/unions. Such structs are only supported ' 'as return value if the function is \'API mode\' and non-variadic ' '(i.e. declared inside ffibuilder.cdef()+ffibuilder.set_source() ' 'and not taking a final \'...\' argument)') def test_call_with_bitfield(): ffi = FFI() ffi.cdef(""" struct foo { int x:5; }; struct foo f(void); struct foo g(int, ...); """) lib = verify(ffi, "test_call_with_bitfield", """ struct foo { int x:5; }; struct foo f(void) { struct foo s = { 11 }; return s; } struct foo g(int a, ...) { return f(); } """) assert lib.f().x == 11 e = py.test.raises(NotImplementedError, lib.g, 0) assert str(e.value) == ( "ctype 'struct foo' not supported as return value. It is a struct " "with bit fields, which libffi does not support. Such structs are " "only supported as return value if the function is 'API mode' and " "non-variadic (i.e. declared inside ffibuilder.cdef()+ffibuilder." "set_source() and not taking a final '...' argument)") def test_call_with_zero_length_field(): if sys.platform == 'win32': py.test.skip("zero-length field not supported by MSVC") ffi = FFI() ffi.cdef(""" struct foo { int a; int x[0]; }; struct foo f(void); struct foo g(int, ...); """) lib = verify(ffi, "test_call_with_zero_length_field", """ struct foo { int a; int x[0]; }; struct foo f(void) { struct foo s = { 42 }; return s; } struct foo g(int a, ...) { return f(); } """) assert lib.f().a == 42 e = py.test.raises(NotImplementedError, lib.g, 0) assert str(e.value) == ( "ctype 'struct foo' not supported as return value. It is a " "struct with a zero-length array, which libffi does not support." " Such structs are only supported as return value if the function is " "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()" "+ffibuilder.set_source() and not taking a final '...' argument)") def test_call_with_union(): ffi = FFI() ffi.cdef(""" union foo { int a; char b; }; union foo f(void); union foo g(int, ...); """) lib = verify(ffi, "test_call_with_union", """ union foo { int a; char b; }; union foo f(void) { union foo s = { 42 }; return s; } union foo g(int a, ...) { return f(); } """) assert lib.f().a == 42 e = py.test.raises(NotImplementedError, lib.g, 0) assert str(e.value) == ( "ctype 'union foo' not supported as return value by libffi. " "Unions are only supported as return value if the function is " "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()" "+ffibuilder.set_source() and not taking a final '...' argument)") def test_call_with_packed_struct(): if sys.platform == 'win32': py.test.skip("needs a GCC extension") ffi = FFI() ffi.cdef(""" struct foo { char y; int x; }; struct foo f(void); struct foo g(int, ...); """, packed=True) lib = verify(ffi, "test_call_with_packed_struct", """ struct foo { char y; int x; } __attribute__((packed)); struct foo f(void) { struct foo s = { 40, 200 }; return s; } struct foo g(int a, ...) { struct foo s = { 41, 201 }; return s; } """) assert ord(lib.f().y) == 40 assert lib.f().x == 200 e = py.test.raises(NotImplementedError, lib.g, 0) assert str(e.value) == ( "ctype 'struct foo' not supported as return value. It is a " "'packed' structure, with a different layout than expected by libffi." " Such structs are only supported as return value if the function is " "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()" "+ffibuilder.set_source() and not taking a final '...' argument)") def test_pack_not_supported(): ffi = FFI() ffi.cdef("""struct foo { char y; int x; };""", pack=2) py.test.raises(NotImplementedError, verify, ffi, "test_pack_not_supported", "") def test_gcc_visibility_hidden(): if sys.platform == 'win32': py.test.skip("test for gcc/clang") ffi = FFI() ffi.cdef(""" int f(int); """) lib = verify(ffi, "test_gcc_visibility_hidden", """ int f(int a) { return a + 40; } """, extra_compile_args=['-fvisibility=hidden']) assert lib.f(2) == 42 def test_override_default_definition(): ffi = FFI() ffi.cdef("typedef long int16_t, char16_t;") lib = verify(ffi, "test_override_default_definition", "") assert ffi.typeof("int16_t") is ffi.typeof("char16_t") is ffi.typeof("long") def test_char16_char32_type(no_cpp=False): if no_cpp is False and sys.platform == "win32": py.test.skip("aaaaaaa why do modern MSVC compilers still define " "a very old __cplusplus value") ffi = FFI() ffi.cdef(""" char16_t foo_2bytes(char16_t); char32_t foo_4bytes(char32_t); """) lib = verify(ffi, "test_char16_char32_type" + no_cpp * "_nocpp", """ #if !defined(__cplusplus) || (!defined(_LIBCPP_VERSION) && __cplusplus < 201103L) typedef uint_least16_t char16_t; typedef uint_least32_t char32_t; #endif char16_t foo_2bytes(char16_t a) { return (char16_t)(a + 42); } char32_t foo_4bytes(char32_t a) { return (char32_t)(a + 42); } """, no_cpp=no_cpp) assert lib.foo_2bytes(u+'\u1234') == u+'\u125e' assert lib.foo_4bytes(u+'\u1234') == u+'\u125e' assert lib.foo_4bytes(u+'\U00012345') == u+'\U0001236f' py.test.raises(TypeError, lib.foo_2bytes, u+'\U00012345') py.test.raises(TypeError, lib.foo_2bytes, 1234) py.test.raises(TypeError, lib.foo_4bytes, 1234) def test_char16_char32_plain_c(): test_char16_char32_type(no_cpp=True) def test_loader_spec(): ffi = FFI() lib = verify(ffi, "test_loader_spec", "") if sys.version_info < (3,): assert not hasattr(lib, '__loader__') assert not hasattr(lib, '__spec__') else: assert lib.__loader__ is None assert lib.__spec__ is None def test_realize_struct_error(): ffi = FFI() ffi.cdef("""typedef ... foo_t; struct foo_s { void (*x)(foo_t); };""") lib = verify(ffi, "test_realize_struct_error", """ typedef int foo_t; struct foo_s { void (*x)(foo_t); }; """) py.test.raises(TypeError, ffi.new, "struct foo_s *")