diff options
Diffstat (limited to 'testing/cffi1/test_recompiler.py')
-rw-r--r-- | testing/cffi1/test_recompiler.py | 2316 |
1 files changed, 2316 insertions, 0 deletions
diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py new file mode 100644 index 0000000..6a31110 --- /dev/null +++ b/testing/cffi1/test_recompiler.py @@ -0,0 +1,2316 @@ + +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 <math.h>') + assert lib.cos(1.43) == math.cos(1.43) + +def test_repr_lib(): + ffi = FFI() + lib = verify(ffi, 'test_repr_lib', '') + assert repr(lib) == "<Lib object for '_CFFI_test_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)) == "<cdata 'enum e1' 0: A1>" + +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 <math.h> + void *xxtestfunc(void) { return 0; } + """) + # 'lib.sin' is typed as a <built-in method> object on lib + assert ffi.typeof(lib.sin).cname == "double(*)(double)" + # 'x' is another <built-in method> 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("<cdata 'foo_t *' ") + +def test_verify_anonymous_struct_with_star_typedef(): + ffi = FFI() + ffi.cdef("typedef struct { int a; long b; } *foo_t;") + verify(ffi, 'test_verify_anonymous_struct_with_star_typedef', + "typedef struct { int a; long b; } *foo_t;") + p = ffi.new("foo_t", {'b': 42}) + assert p.b == 42 + +def test_verify_anonymous_enum_with_typedef(): + ffi = FFI() + ffi.cdef("typedef enum { AA, ... } e1;") + lib = verify(ffi, 'test_verify_anonymous_enum_with_typedef1', + "typedef enum { BB, CC, AA } e1;") + assert lib.AA == 2 + assert ffi.sizeof("e1") == ffi.sizeof("int") + assert repr(ffi.cast("e1", 2)) == "<cdata 'e1' 2: AA>" + # + 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 <math.h>'), + 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")) == "<ctype 'foo_t'>" + assert repr(ffi.typeof("bar_p")) == "<ctype 'struct $1 *'>" + assert repr(ffi.typeof("baz_pp")) == "<ctype 'struct $2 * *'>" + +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 <complex.h> + static float _Complex f1(float a, float b) { return a + I*2.0*b; } + """, no_cpp=True) # <complex.h> 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 <complex.h> + static double _Complex f1(double a, double b) { return a + I*2.0*b; } + """, no_cpp=True) # <complex.h> 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 <complex.h> + static float f1(float _Complex x) { return cabsf(x); } + """, no_cpp=True) # <complex.h> 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 <complex.h> + static double f1(double _Complex x) { return cabs(x); } + """, no_cpp=True) # <complex.h> 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 *") |