diff options
Diffstat (limited to 'testing/cffi0/test_verify.py')
-rw-r--r-- | testing/cffi0/test_verify.py | 2524 |
1 files changed, 2524 insertions, 0 deletions
diff --git a/testing/cffi0/test_verify.py b/testing/cffi0/test_verify.py new file mode 100644 index 0000000..79e1c6c --- /dev/null +++ b/testing/cffi0/test_verify.py @@ -0,0 +1,2524 @@ +import py, re +import sys, os, math, weakref +from cffi import FFI, VerificationError, VerificationMissing, model, FFIError +from testing.support import * + + +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'] + pass # no obvious -Werror equivalent on MSVC +else: + if (sys.platform == 'darwin' and + [int(x) for x in os.uname()[2].split('.')] >= [11, 0, 0]): + # assume a standard clang or gcc + extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion'] + # special things for clang + extra_compile_args.append('-Qunused-arguments') + else: + # assume a standard gcc + extra_compile_args = ['-Werror', '-Wall', '-Wextra', '-Wconversion'] + + class FFI(FFI): + def verify(self, *args, **kwds): + return super(FFI, self).verify( + *args, extra_compile_args=extra_compile_args, **kwds) + +def setup_module(): + import cffi.verifier + cffi.verifier.cleanup_tmpdir() + # + # check that no $ sign is produced in the C file; it used to be the + # case that anonymous enums would produce '$enum_$1', which was + # used as part of a function name. GCC accepts such names, but it's + # apparently non-standard. + _r_comment = re.compile(r"/\*.*?\*/|//.*?$", re.DOTALL | re.MULTILINE) + _r_string = re.compile(r'\".*?\"') + def _write_source_and_check(self, file=None): + base_write_source(self, file) + if file is None: + f = open(self.sourcefilename) + data = f.read() + f.close() + data = _r_comment.sub(' ', data) + data = _r_string.sub('"skipped"', data) + assert '$' not in data + base_write_source = cffi.verifier.Verifier._write_source + cffi.verifier.Verifier._write_source = _write_source_and_check + + +def test_module_type(): + import cffi.verifier + ffi = FFI() + lib = ffi.verify() + if hasattr(lib, '_cffi_python_module'): + print('verify got a PYTHON module') + if hasattr(lib, '_cffi_generic_module'): + print('verify got a GENERIC module') + expected_generic = (cffi.verifier._FORCE_GENERIC_ENGINE or + '__pypy__' in sys.builtin_module_names) + assert hasattr(lib, '_cffi_python_module') == (not expected_generic) + assert hasattr(lib, '_cffi_generic_module') == expected_generic + +def test_missing_function(ffi=None): + # uses the FFI hacked above with '-Werror' + if ffi is None: + ffi = FFI() + ffi.cdef("void some_completely_unknown_function();") + try: + lib = ffi.verify() + except (VerificationError, OSError): + pass # expected case: we get a VerificationError + else: + # but depending on compiler and loader details, maybe + # 'lib' could actually be imported but will fail if we + # actually try to call the unknown function... Hard + # to test anything more. + pass + +def test_missing_function_import_error(): + # uses the original FFI that just gives a warning during compilation + import cffi + test_missing_function(ffi=cffi.FFI()) + +def test_simple_case(): + ffi = FFI() + ffi.cdef("double sin(double x);") + lib = ffi.verify('#include <math.h>', libraries=lib_m) + assert lib.sin(1.23) == math.sin(1.23) + +def _Wconversion(cdef, source, **kargs): + if sys.platform in ('win32', 'darwin'): + py.test.skip("needs GCC") + ffi = FFI() + ffi.cdef(cdef) + py.test.raises(VerificationError, ffi.verify, source, **kargs) + extra_compile_args_orig = extra_compile_args[:] + extra_compile_args.remove('-Wconversion') + try: + lib = ffi.verify(source, **kargs) + finally: + extra_compile_args[:] = extra_compile_args_orig + return lib + +def test_Wconversion_unsigned(): + _Wconversion("unsigned foo(void);", + "int foo(void) { return -1;}") + +def test_Wconversion_integer(): + _Wconversion("short foo(void);", + "long long foo(void) { return 1<<sizeof(short);}") + +def test_Wconversion_floating(): + lib = _Wconversion("float sin(double);", + "#include <math.h>", libraries=lib_m) + res = lib.sin(1.23) + assert res != math.sin(1.23) # not exact, because of double->float + assert abs(res - math.sin(1.23)) < 1E-5 + +def test_Wconversion_float2int(): + _Wconversion("int sinf(float);", + "#include <math.h>", libraries=lib_m) + +def test_Wconversion_double2int(): + _Wconversion("int sin(double);", + "#include <math.h>", libraries=lib_m) + +def test_rounding_1(): + ffi = FFI() + ffi.cdef("double sinf(float x);") + lib = ffi.verify('#include <math.h>', libraries=lib_m) + res = lib.sinf(1.23) + assert res != math.sin(1.23) # not exact, because of double->float + assert abs(res - math.sin(1.23)) < 1E-5 + +def test_rounding_2(): + ffi = FFI() + ffi.cdef("double sin(float x);") + lib = ffi.verify('#include <math.h>', libraries=lib_m) + res = lib.sin(1.23) + assert res != math.sin(1.23) # not exact, because of double->float + assert abs(res - math.sin(1.23)) < 1E-5 + +def test_strlen_exact(): + ffi = FFI() + ffi.cdef("size_t strlen(const char *s);") + lib = ffi.verify("#include <string.h>") + assert lib.strlen(b"hi there!") == 9 + +def test_strlen_approximate(): + lib = _Wconversion("int strlen(char *s);", + "#include <string.h>") + assert lib.strlen(b"hi there!") == 9 + +def test_return_approximate(): + for typename in ['short', 'int', 'long', 'long long']: + ffi = FFI() + ffi.cdef("%s foo(signed char x);" % typename) + lib = ffi.verify("signed char foo(signed char x) { return x;}") + assert lib.foo(-128) == -128 + assert lib.foo(+127) == +127 + +def test_strlen_array_of_char(): + ffi = FFI() + ffi.cdef("size_t strlen(char[]);") + lib = ffi.verify("#include <string.h>") + assert lib.strlen(b"hello") == 5 + +def test_longdouble(): + ffi = FFI() + ffi.cdef("long double sinl(long double x);") + lib = ffi.verify('#include <math.h>', libraries=lib_m) + for input in [1.23, + ffi.cast("double", 1.23), + ffi.cast("long double", 1.23)]: + x = lib.sinl(input) + assert repr(x).startswith("<cdata 'long double'") + assert (float(x) - math.sin(1.23)) < 1E-10 + +def test_longdouble_precision(): + # Test that we don't loose any precision of 'long double' when + # passing through Python and CFFI. + ffi = FFI() + ffi.cdef("long double step1(long double x);") + SAME_SIZE = ffi.sizeof("long double") == ffi.sizeof("double") + lib = ffi.verify(""" + long double step1(long double x) + { + return 4*x-x*x; + } + """) + def do(cast_to_double): + x = 0.9789 + for i in range(10000): + x = lib.step1(x) + if cast_to_double: + x = float(x) + return float(x) + + more_precise = do(False) + less_precise = do(True) + if SAME_SIZE: + assert more_precise == less_precise + else: + assert abs(more_precise - less_precise) > 0.1 + # Check the particular results on Intel + import platform + if (platform.machine().startswith('i386') or + platform.machine().startswith('i486') or + platform.machine().startswith('i586') or + platform.machine().startswith('i686') or + platform.machine().startswith('x86')): + assert abs(more_precise - 0.656769) < 0.001 + assert abs(less_precise - 3.99091) < 0.001 + else: + py.test.skip("don't know the very exact precision of 'long double'") + + +all_primitive_types = model.PrimitiveType.ALL_PRIMITIVE_TYPES +if sys.platform == 'win32': + all_primitive_types = all_primitive_types.copy() + del all_primitive_types['ssize_t'] +all_integer_types = sorted(tp for tp in all_primitive_types + if all_primitive_types[tp] == 'i') +all_float_types = sorted(tp for tp in all_primitive_types + if all_primitive_types[tp] == 'f') + +def all_signed_integer_types(ffi): + return [x for x in all_integer_types if int(ffi.cast(x, -1)) < 0] + +def all_unsigned_integer_types(ffi): + return [x for x in all_integer_types if int(ffi.cast(x, -1)) > 0] + + +def test_primitive_category(): + for typename in all_primitive_types: + tp = model.PrimitiveType(typename) + C = tp.is_char_type() + F = tp.is_float_type() + X = tp.is_complex_type() + I = tp.is_integer_type() + assert C == (typename in ('char', 'wchar_t', 'char16_t', 'char32_t')) + assert F == (typename in ('float', 'double', 'long double')) + assert X == (typename in ('float _Complex', 'double _Complex')) + assert I + F + C + X == 1 # one and only one of them is true + +def test_all_integer_and_float_types(): + typenames = [] + for typename in all_primitive_types: + if (all_primitive_types[typename] == 'c' or + all_primitive_types[typename] == 'j' or # complex + typename == '_Bool' or typename == 'long double'): + pass + else: + typenames.append(typename) + # + ffi = FFI() + ffi.cdef('\n'.join(["%s foo_%s(%s);" % (tp, tp.replace(' ', '_'), tp) + for tp in typenames])) + lib = ffi.verify('\n'.join(["%s foo_%s(%s x) { return (%s)(x+1); }" % + (tp, tp.replace(' ', '_'), tp, tp) + for tp in typenames])) + for typename in typenames: + foo = getattr(lib, 'foo_%s' % typename.replace(' ', '_')) + assert foo(42) == 43 + if sys.version < '3': + assert foo(long(44)) == 45 + assert foo(ffi.cast(typename, 46)) == 47 + py.test.raises(TypeError, foo, ffi.NULL) + # + # check for overflow cases + if all_primitive_types[typename] == 'f': + continue + for value in [-2**80, -2**40, -2**20, -2**10, -2**5, -1, + 2**5, 2**10, 2**20, 2**40, 2**80]: + overflows = int(ffi.cast(typename, value)) != value + if overflows: + py.test.raises(OverflowError, foo, value) + else: + assert foo(value) == value + 1 + +def test_var_signed_integer_types(): + ffi = FFI() + lst = all_signed_integer_types(ffi) + csource = "\n".join(["%s somevar_%s;" % (tp, tp.replace(' ', '_')) + for tp in lst]) + ffi.cdef(csource) + lib = ffi.verify(csource) + for tp in lst: + varname = 'somevar_%s' % tp.replace(' ', '_') + sz = ffi.sizeof(tp) + max = (1 << (8*sz-1)) - 1 + min = -(1 << (8*sz-1)) + setattr(lib, varname, max) + assert getattr(lib, varname) == max + setattr(lib, varname, min) + assert getattr(lib, varname) == min + py.test.raises(OverflowError, setattr, lib, varname, max+1) + py.test.raises(OverflowError, setattr, lib, varname, min-1) + +def test_var_unsigned_integer_types(): + ffi = FFI() + lst = all_unsigned_integer_types(ffi) + csource = "\n".join(["%s somevar_%s;" % (tp, tp.replace(' ', '_')) + for tp in lst]) + ffi.cdef(csource) + lib = ffi.verify(csource) + for tp in lst: + varname = 'somevar_%s' % tp.replace(' ', '_') + sz = ffi.sizeof(tp) + if tp != '_Bool': + max = (1 << (8*sz)) - 1 + else: + max = 1 + setattr(lib, varname, max) + assert getattr(lib, varname) == max + setattr(lib, varname, 0) + assert getattr(lib, varname) == 0 + py.test.raises(OverflowError, setattr, lib, varname, max+1) + py.test.raises(OverflowError, setattr, lib, varname, -1) + +def test_fn_signed_integer_types(): + ffi = FFI() + lst = all_signed_integer_types(ffi) + cdefsrc = "\n".join(["%s somefn_%s(%s);" % (tp, tp.replace(' ', '_'), tp) + for tp in lst]) + ffi.cdef(cdefsrc) + verifysrc = "\n".join(["%s somefn_%s(%s x) { return x; }" % + (tp, tp.replace(' ', '_'), tp) for tp in lst]) + lib = ffi.verify(verifysrc) + for tp in lst: + fnname = 'somefn_%s' % tp.replace(' ', '_') + sz = ffi.sizeof(tp) + max = (1 << (8*sz-1)) - 1 + min = -(1 << (8*sz-1)) + fn = getattr(lib, fnname) + assert fn(max) == max + assert fn(min) == min + py.test.raises(OverflowError, fn, max + 1) + py.test.raises(OverflowError, fn, min - 1) + +def test_fn_unsigned_integer_types(): + ffi = FFI() + lst = all_unsigned_integer_types(ffi) + cdefsrc = "\n".join(["%s somefn_%s(%s);" % (tp, tp.replace(' ', '_'), tp) + for tp in lst]) + ffi.cdef(cdefsrc) + verifysrc = "\n".join(["%s somefn_%s(%s x) { return x; }" % + (tp, tp.replace(' ', '_'), tp) for tp in lst]) + lib = ffi.verify(verifysrc) + for tp in lst: + fnname = 'somefn_%s' % tp.replace(' ', '_') + sz = ffi.sizeof(tp) + if tp != '_Bool': + max = (1 << (8*sz)) - 1 + else: + max = 1 + fn = getattr(lib, fnname) + assert fn(max) == max + assert fn(0) == 0 + py.test.raises(OverflowError, fn, max + 1) + py.test.raises(OverflowError, fn, -1) + +def test_char_type(): + ffi = FFI() + ffi.cdef("char foo(char);") + lib = ffi.verify("char foo(char x) { return ++x; }") + assert lib.foo(b"A") == b"B" + py.test.raises(TypeError, lib.foo, b"bar") + py.test.raises(TypeError, lib.foo, "bar") + +def test_wchar_type(): + ffi = FFI() + if ffi.sizeof('wchar_t') == 2: + uniexample1 = u+'\u1234' + uniexample2 = u+'\u1235' + else: + uniexample1 = u+'\U00012345' + uniexample2 = u+'\U00012346' + # + ffi.cdef("wchar_t foo(wchar_t);") + lib = ffi.verify("wchar_t foo(wchar_t x) { return x+1; }") + assert lib.foo(uniexample1) == uniexample2 + +def test_char16_char32_type(): + py.test.skip("XXX test or fully prevent char16_t and char32_t from " + "working in ffi.verify() mode") + +def test_no_argument(): + ffi = FFI() + ffi.cdef("int foo(void);") + lib = ffi.verify("int foo(void) { return 42; }") + assert lib.foo() == 42 + +def test_two_arguments(): + ffi = FFI() + ffi.cdef("int foo(int, int);") + lib = ffi.verify("int foo(int a, int b) { return a - b; }") + assert lib.foo(40, -2) == 42 + +def test_macro(): + ffi = FFI() + ffi.cdef("int foo(int, int);") + lib = ffi.verify("#define foo(a, b) ((a) * (b))") + assert lib.foo(-6, -7) == 42 + +def test_ptr(): + ffi = FFI() + ffi.cdef("int *foo(int *);") + lib = ffi.verify("int *foo(int *a) { return a; }") + assert lib.foo(ffi.NULL) == ffi.NULL + p = ffi.new("int *", 42) + q = ffi.new("int *", 42) + assert lib.foo(p) == p + assert lib.foo(q) != p + +def test_bogus_ptr(): + ffi = FFI() + ffi.cdef("int *foo(int *);") + lib = ffi.verify("int *foo(int *a) { return a; }") + py.test.raises(TypeError, lib.foo, ffi.new("short *", 42)) + + +def test_verify_typedefs(): + py.test.skip("ignored so far") + types = ['signed char', 'unsigned char', 'int', 'long'] + for cdefed in types: + for real in types: + ffi = FFI() + ffi.cdef("typedef %s foo_t;" % cdefed) + if cdefed == real: + ffi.verify("typedef %s foo_t;" % real) + else: + py.test.raises(VerificationError, ffi.verify, + "typedef %s foo_t;" % real) + +def test_nondecl_struct(): + ffi = FFI() + ffi.cdef("typedef struct foo_s foo_t; int bar(foo_t *);") + lib = ffi.verify("typedef struct foo_s foo_t;\n" + "int bar(foo_t *f) { (void)f; return 42; }\n") + assert lib.bar(ffi.NULL) == 42 + +def test_ffi_full_struct(): + ffi = FFI() + ffi.cdef("struct foo_s { char x; int y; long *z; };") + ffi.verify("struct foo_s { char x; int y; long *z; };") + # + if sys.platform != 'win32': # XXX fixme: only gives warnings + py.test.raises(VerificationError, ffi.verify, + "struct foo_s { char x; int y; int *z; };") + # + py.test.raises(VerificationError, ffi.verify, + "struct foo_s { int y; long *z; };") + # + e = py.test.raises(VerificationError, ffi.verify, + "struct foo_s { int y; char x; long *z; };") + assert str(e.value) == ( + "struct foo_s: wrong offset for field 'x'" + " (we have 0, but C compiler says 4)") + # + e = py.test.raises(VerificationError, ffi.verify, + "struct foo_s { char x; int y; long *z; char extra; };") + assert str(e.value) == ( + "struct foo_s: wrong total size" + " (we have %d, but C compiler says %d)" % ( + ffi.sizeof("struct foo_s"), + ffi.sizeof("struct foo_s") + ffi.sizeof("long*"))) + # + # a corner case that we cannot really detect, but where it has no + # bad consequences: the size is the same, but there is an extra field + # that replaces what is just padding in our declaration above + ffi.verify("struct foo_s { char x, extra; int y; long *z; };") + # + e = py.test.raises(VerificationError, ffi.verify, + "struct foo_s { char x; short pad; short y; long *z; };") + assert str(e.value) == ( + "struct foo_s: wrong size for field 'y'" + " (we have 4, but C compiler says 2)") + +def test_ffi_nonfull_struct(): + ffi = FFI() + ffi.cdef(""" + struct foo_s { + int x; + ...; + }; + """) + py.test.raises(VerificationMissing, ffi.sizeof, 'struct foo_s') + py.test.raises(VerificationMissing, ffi.offsetof, 'struct foo_s', 'x') + py.test.raises(VerificationMissing, ffi.new, 'struct foo_s *') + ffi.verify(""" + struct foo_s { + int a, b, x, c, d, e; + }; + """) + assert ffi.sizeof('struct foo_s') == 6 * ffi.sizeof('int') + assert ffi.offsetof('struct foo_s', 'x') == 2 * ffi.sizeof('int') + +def test_ffi_nonfull_alignment(): + ffi = FFI() + ffi.cdef("struct foo_s { char x; ...; };") + ffi.verify("struct foo_s { int a, b; char x; };") + assert ffi.sizeof('struct foo_s') == 3 * ffi.sizeof('int') + assert ffi.alignof('struct foo_s') == ffi.sizeof('int') + +def _check_field_match(typename, real, expect_mismatch): + ffi = FFI() + testing_by_size = (expect_mismatch == 'by_size') + if testing_by_size: + expect_mismatch = ffi.sizeof(typename) != ffi.sizeof(real) + ffi.cdef("struct foo_s { %s x; ...; };" % typename) + try: + ffi.verify("struct foo_s { %s x; };" % real) + except VerificationError: + if not expect_mismatch: + if testing_by_size and typename != real: + print("ignoring mismatch between %s* and %s* even though " + "they have the same size" % (typename, real)) + return + raise AssertionError("unexpected mismatch: %s should be accepted " + "as equal to %s" % (typename, real)) + else: + if expect_mismatch: + raise AssertionError("mismatch not detected: " + "%s != %s" % (typename, real)) + +def test_struct_bad_sized_integer(): + for typename in ['int8_t', 'int16_t', 'int32_t', 'int64_t']: + for real in ['int8_t', 'int16_t', 'int32_t', 'int64_t']: + _check_field_match(typename, real, "by_size") + +def test_struct_bad_sized_float(): + for typename in all_float_types: + for real in all_float_types: + _check_field_match(typename, real, "by_size") + +def test_struct_signedness_ignored(): + _check_field_match("int", "unsigned int", expect_mismatch=False) + _check_field_match("unsigned short", "signed short", expect_mismatch=False) + +def test_struct_float_vs_int(): + if sys.platform == 'win32': + py.test.skip("XXX fixme: only gives warnings") + ffi = FFI() + for typename in all_signed_integer_types(ffi): + for real in all_float_types: + _check_field_match(typename, real, expect_mismatch=True) + for typename in all_float_types: + for real in all_signed_integer_types(ffi): + _check_field_match(typename, real, expect_mismatch=True) + +def test_struct_array_field(): + ffi = FFI() + ffi.cdef("struct foo_s { int a[17]; ...; };") + ffi.verify("struct foo_s { int x; int a[17]; int y; };") + assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int') + s = ffi.new("struct foo_s *") + assert ffi.sizeof(s.a) == 17 * ffi.sizeof('int') + +def test_struct_array_no_length(): + ffi = FFI() + ffi.cdef("struct foo_s { int a[]; int y; ...; };\n" + "int bar(struct foo_s *);\n") + lib = ffi.verify("struct foo_s { int x; int a[17]; int y; };\n" + "int bar(struct foo_s *f) { return f->a[14]; }\n") + assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int') + s = ffi.new("struct foo_s *") + assert ffi.typeof(s.a) is ffi.typeof('int[]') # implicit max length + assert len(s.a) == 18 # max length, computed from the size and start offset + s.a[14] = 4242 + assert lib.bar(s) == 4242 + # with no declared length, out-of-bound accesses are not detected + s.a[17] = -521 + assert s.y == s.a[17] == -521 + # + s = ffi.new("struct foo_s *", {'a': list(range(17))}) + assert s.a[16] == 16 + # overflows at construction time not detected either + s = ffi.new("struct foo_s *", {'a': list(range(18))}) + assert s.y == s.a[17] == 17 + +def test_struct_array_guess_length(): + ffi = FFI() + ffi.cdef("struct foo_s { int a[...]; };") + ffi.verify("struct foo_s { int x; int a[17]; int y; };") + assert ffi.sizeof('struct foo_s') == 19 * ffi.sizeof('int') + s = ffi.new("struct foo_s *") + assert ffi.sizeof(s.a) == 17 * ffi.sizeof('int') + py.test.raises(IndexError, 's.a[17]') + +def test_struct_array_c99_1(): + if sys.platform == 'win32': + py.test.skip("requires C99") + ffi = FFI() + ffi.cdef("struct foo_s { int x; int a[]; };") + ffi.verify("struct foo_s { int x; int a[]; };") + assert ffi.sizeof('struct foo_s') == 1 * ffi.sizeof('int') + s = ffi.new("struct foo_s *", [424242, 4]) + assert ffi.sizeof(ffi.typeof(s[0])) == 1 * ffi.sizeof('int') + assert ffi.sizeof(s[0]) == 5 * ffi.sizeof('int') + # ^^^ explanation: if you write in C: "char x[5];", then + # "sizeof(x)" will evaluate to 5. The behavior above is + # a generalization of that to "struct foo_s[len(a)=5] x;" + # if you could do that in C. + assert s.a[3] == 0 + s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]]) + assert ffi.sizeof(s[0]) == 5 * ffi.sizeof('int') + assert s.a[3] == -10 + s = ffi.new("struct foo_s *") + assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int') + s = ffi.new("struct foo_s *", [424242]) + assert ffi.sizeof(s[0]) == 1 * ffi.sizeof('int') + +def test_struct_array_c99_2(): + if sys.platform == 'win32': + py.test.skip("requires C99") + ffi = FFI() + ffi.cdef("struct foo_s { int x; int a[]; ...; };") + ffi.verify("struct foo_s { int x, y; int a[]; };") + assert ffi.sizeof('struct foo_s') == 2 * ffi.sizeof('int') + s = ffi.new("struct foo_s *", [424242, 4]) + assert ffi.sizeof(s[0]) == 6 * ffi.sizeof('int') + assert s.a[3] == 0 + s = ffi.new("struct foo_s *", [424242, [-40, -30, -20, -10]]) + assert ffi.sizeof(s[0]) == 6 * ffi.sizeof('int') + assert s.a[3] == -10 + s = ffi.new("struct foo_s *") + assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int') + s = ffi.new("struct foo_s *", [424242]) + assert ffi.sizeof(s[0]) == 2 * ffi.sizeof('int') + +def test_struct_ptr_to_array_field(): + ffi = FFI() + ffi.cdef("struct foo_s { int (*a)[17]; ...; }; struct bar_s { ...; };") + ffi.verify("struct foo_s { int x; int (*a)[17]; int y; };\n" + "struct bar_s { int x; int *a; int y; };") + assert ffi.sizeof('struct foo_s') == ffi.sizeof("struct bar_s") + s = ffi.new("struct foo_s *") + assert ffi.sizeof(s.a) == ffi.sizeof('int(*)[17]') == ffi.sizeof("int *") + +def test_struct_with_bitfield_exact(): + ffi = FFI() + ffi.cdef("struct foo_s { int a:2, b:3; };") + ffi.verify("struct foo_s { int a:2, b:3; };") + s = ffi.new("struct foo_s *") + s.b = 3 + py.test.raises(OverflowError, "s.b = 4") + assert s.b == 3 + +def test_struct_with_bitfield_enum(): + ffi = FFI() + code = """ + typedef enum { AA, BB, CC } foo_e; + typedef struct { foo_e f:2; } foo_s; + """ + ffi.cdef(code) + ffi.verify(code) + s = ffi.new("foo_s *") + s.f = 2 + assert s.f == 2 + +def test_unsupported_struct_with_bitfield_ellipsis(): + ffi = FFI() + py.test.raises(NotImplementedError, ffi.cdef, + "struct foo_s { int a:2, b:3; ...; };") + +def test_global_constants(): + ffi = FFI() + # use 'static const int', as generally documented, although in this + # case the 'static' is completely ignored. + ffi.cdef("static const int AA, BB, CC, DD;") + lib = ffi.verify("#define AA 42\n" + "#define BB (-43) // blah\n" + "#define CC (22*2) /* foobar */\n" + "#define DD ((unsigned int)142) /* foo\nbar */\n") + assert lib.AA == 42 + assert lib.BB == -43 + assert lib.CC == 44 + assert lib.DD == 142 + +def test_global_const_int_size(): + # integer constants: ignore the declared type, always just use the value + for value in [-2**63, -2**31, -2**15, + 2**15-1, 2**15, 2**31-1, 2**31, 2**32-1, 2**32, + 2**63-1, 2**63, 2**64-1]: + ffi = FFI() + if value == int(ffi.cast("long long", value)): + if value < 0: + vstr = '(-%dLL-1)' % (~value,) + else: + vstr = '%dLL' % value + elif value == int(ffi.cast("unsigned long long", value)): + vstr = '%dULL' % value + else: + raise AssertionError(value) + ffi.cdef("static const unsigned short AA;") + lib = ffi.verify("#define AA %s\n" % vstr) + assert lib.AA == value + assert type(lib.AA) is type(int(lib.AA)) + +def test_global_constants_non_int(): + ffi = FFI() + ffi.cdef("static char *const PP;") + lib = ffi.verify('static char *const PP = "testing!";\n') + assert ffi.typeof(lib.PP) == ffi.typeof("char *") + assert ffi.string(lib.PP) == b"testing!" + +def test_nonfull_enum(): + ffi = FFI() + ffi.cdef("enum ee { EE1, EE2, EE3, ... \n \t };") + py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE2') + ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };") + assert ffi.string(ffi.cast('enum ee', 11)) == "EE2" + assert ffi.string(ffi.cast('enum ee', -10)) == "EE3" + # + # try again + ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };") + assert ffi.string(ffi.cast('enum ee', 11)) == "EE2" + # + assert ffi.typeof("enum ee").relements == {'EE1': 10, 'EE2': 11, 'EE3': -10} + assert ffi.typeof("enum ee").elements == {10: 'EE1', 11: 'EE2', -10: 'EE3'} + +def test_full_enum(): + ffi = FFI() + ffi.cdef("enum ee { EE1, EE2, EE3 };") + ffi.verify("enum ee { EE1, EE2, EE3 };") + py.test.raises(VerificationError, ffi.verify, "enum ee { EE1, EE2 };") + e = py.test.raises(VerificationError, ffi.verify, + "enum ee { EE1, EE3, EE2 };") + assert str(e.value) == 'enum ee: EE2 has the real value 2, not 1' + # extra items cannot be seen and have no bad consequence anyway + lib = ffi.verify("enum ee { EE1, EE2, EE3, EE4 };") + assert lib.EE3 == 2 + +def test_enum_usage(): + ffi = FFI() + ffi.cdef("enum ee { EE1,EE2 }; typedef struct { enum ee x; } *sp;") + lib = ffi.verify("enum ee { EE1,EE2 }; typedef struct { enum ee x; } *sp;") + assert lib.EE2 == 1 + s = ffi.new("sp", [lib.EE2]) + assert s.x == 1 + s.x = 17 + assert s.x == 17 + +def test_anonymous_enum(): + ffi = FFI() + ffi.cdef("enum { EE1 }; enum { EE2, EE3 };") + lib = ffi.verify("enum { EE1 }; enum { EE2, EE3 };") + assert lib.EE1 == 0 + assert lib.EE2 == 0 + assert lib.EE3 == 1 + +def test_nonfull_anonymous_enum(): + ffi = FFI() + ffi.cdef("enum { EE1, ... }; enum { EE3, ... };") + lib = ffi.verify("enum { EE2, EE1 }; enum { EE3 };") + assert lib.EE1 == 1 + assert lib.EE3 == 0 + +def test_nonfull_enum_syntax2(): + ffi = FFI() + ffi.cdef("enum ee { EE1, EE2=\t..., EE3 };") + py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE1') + ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };") + assert ffi.string(ffi.cast('enum ee', 11)) == 'EE2' + assert ffi.string(ffi.cast('enum ee', -10)) == 'EE3' + # + ffi = FFI() + ffi.cdef("enum ee { EE1, EE2=\t... };") + py.test.raises(VerificationMissing, ffi.cast, 'enum ee', 'EE1') + ffi.verify("enum ee { EE1=10, EE2, EE3=-10, EE4 };") + assert ffi.string(ffi.cast('enum ee', 11)) == 'EE2' + # + ffi = FFI() + ffi.cdef("enum ee2 { EE4=..., EE5=..., ... };") + ffi.verify("enum ee2 { EE4=-1234-5, EE5 }; ") + assert ffi.string(ffi.cast('enum ee2', -1239)) == 'EE4' + assert ffi.string(ffi.cast('enum ee2', -1238)) == 'EE5' + +def test_nonfull_enum_bug3(): + ffi = FFI() + ffi.cdef("enum ee2 { EE4=..., EE5=... };") + ffi.cdef("enum ee6 { EE7=10, EE8=..., EE9=... };") + +def test_get_set_errno(): + ffi = FFI() + ffi.cdef("int foo(int);") + lib = ffi.verify(""" + static int foo(int x) + { + errno += 1; + return x * 7; + } + """) + ffi.errno = 15 + assert lib.foo(6) == 42 + assert ffi.errno == 16 + +def test_define_int(): + ffi = FFI() + ffi.cdef("#define FOO ...\n" + "\t#\tdefine\tBAR\t...\t\n" + "#define BAZ ...\n") + lib = ffi.verify("#define FOO 42\n" + "#define BAR (-44)\n" + "#define BAZ 0xffffffffffffffffULL\n") + assert lib.FOO == 42 + assert lib.BAR == -44 + assert lib.BAZ == 0xffffffffffffffff + +def test_access_variable(): + ffi = FFI() + ffi.cdef("int foo(void);\n" + "int somenumber;") + lib = ffi.verify(""" + static int somenumber = 2; + static int foo(void) { + return somenumber * 7; + } + """) + assert lib.somenumber == 2 + assert lib.foo() == 14 + lib.somenumber = -6 + assert lib.foo() == -42 + assert lib.somenumber == -6 + lib.somenumber = 2 # reset for the next run, if any + +def test_access_address_of_variable(): + # access the address of 'somenumber': need a trick + ffi = FFI() + ffi.cdef("int somenumber; static int *const somenumberptr;") + lib = ffi.verify(""" + static int somenumber = 2; + #define somenumberptr (&somenumber) + """) + assert lib.somenumber == 2 + lib.somenumberptr[0] = 42 + assert lib.somenumber == 42 + lib.somenumber = 2 # reset for the next run, if any + +def test_access_array_variable(length=5): + ffi = FFI() + ffi.cdef("int foo(int);\n" + "int somenumber[%s];" % (length,)) + lib = ffi.verify(""" + static int somenumber[] = {2, 2, 3, 4, 5}; + static int foo(int i) { + return somenumber[i] * 7; + } + """) + if length == '': + # a global variable of an unknown array length is implicitly + # transformed into a global pointer variable, because we can only + # work with array instances whose length we know. using a pointer + # instead of an array gives the correct effects. + assert repr(lib.somenumber).startswith("<cdata 'int *' 0x") + py.test.raises(TypeError, len, lib.somenumber) + else: + assert repr(lib.somenumber).startswith("<cdata 'int[%s]' 0x" % length) + assert len(lib.somenumber) == 5 + assert lib.somenumber[3] == 4 + assert lib.foo(3) == 28 + lib.somenumber[3] = -6 + assert lib.foo(3) == -42 + assert lib.somenumber[3] == -6 + assert lib.somenumber[4] == 5 + lib.somenumber[3] = 4 # reset for the next run, if any + +def test_access_array_variable_length_hidden(): + test_access_array_variable(length='') + +def test_access_struct_variable(): + ffi = FFI() + ffi.cdef("struct foo { int x; ...; };\n" + "int foo(int);\n" + "struct foo stuff;") + lib = ffi.verify(""" + struct foo { int x, y, z; }; + static struct foo stuff = {2, 5, 8}; + static int foo(int i) { + switch (i) { + case 0: return stuff.x * 7; + case 1: return stuff.y * 7; + case 2: return stuff.z * 7; + } + return -1; + } + """) + assert lib.stuff.x == 2 + assert lib.foo(0) == 14 + assert lib.foo(1) == 35 + assert lib.foo(2) == 56 + lib.stuff.x = -6 + assert lib.foo(0) == -42 + assert lib.foo(1) == 35 + lib.stuff.x = 2 # reset for the next run, if any + +def test_access_callback(): + ffi = FFI() + ffi.cdef("int (*cb)(int);\n" + "int foo(int);\n" + "void reset_cb(void);") + lib = ffi.verify(""" + static int g(int x) { return x * 7; } + static int (*cb)(int); + static int foo(int i) { return cb(i) - 1; } + static void reset_cb(void) { cb = g; } + """) + lib.reset_cb() + assert lib.foo(6) == 41 + my_callback = ffi.callback("int(*)(int)", lambda n: n * 222) + lib.cb = my_callback + assert lib.foo(4) == 887 + +def test_access_callback_function_typedef(): + ffi = FFI() + ffi.cdef("typedef int mycallback_t(int);\n" + "mycallback_t *cb;\n" + "int foo(int);\n" + "void reset_cb(void);") + lib = ffi.verify(""" + static int g(int x) { return x * 7; } + static int (*cb)(int); + static int foo(int i) { return cb(i) - 1; } + static void reset_cb(void) { cb = g; } + """) + lib.reset_cb() + assert lib.foo(6) == 41 + my_callback = ffi.callback("int(*)(int)", lambda n: n * 222) + lib.cb = my_callback + assert lib.foo(4) == 887 + +def test_ctypes_backend_forces_generic_engine(): + from cffi.backend_ctypes import CTypesBackend + ffi = FFI(backend=CTypesBackend()) + ffi.cdef("int func(int a);") + lib = ffi.verify("int func(int a) { return a * 42; }") + assert not hasattr(lib, '_cffi_python_module') + assert hasattr(lib, '_cffi_generic_module') + assert lib.func(100) == 4200 + +def test_call_with_struct_ptr(): + ffi = FFI() + ffi.cdef("typedef struct { int x; ...; } foo_t; int foo(foo_t *);") + lib = ffi.verify(""" + typedef struct { int y, x; } foo_t; + static int foo(foo_t *f) { return f->x * 7; } + """) + f = ffi.new("foo_t *") + f.x = 6 + assert lib.foo(f) == 42 + +def test_unknown_type(): + ffi = FFI() + ffi.cdef(""" + typedef ... token_t; + int foo(token_t *); + #define TOKEN_SIZE ... + """) + lib = ffi.verify(""" + typedef float token_t; + static int foo(token_t *tk) { + if (!tk) + return -42; + *tk += 1.601f; + return (int)*tk; + } + #define TOKEN_SIZE sizeof(token_t) + """) + # we cannot let ffi.new("token_t *") work, because we don't know ahead of + # time if it's ok to ask 'sizeof(token_t)' in the C code or not. + # See test_unknown_type_2. Workaround. + tkmem = ffi.new("char[]", lib.TOKEN_SIZE) # zero-initialized + tk = ffi.cast("token_t *", tkmem) + results = [lib.foo(tk) for i in range(6)] + assert results == [1, 3, 4, 6, 8, 9] + assert lib.foo(ffi.NULL) == -42 + +def test_unknown_type_2(): + ffi = FFI() + ffi.cdef("typedef ... token_t;") + lib = ffi.verify("typedef struct token_s token_t;") + # assert did not crash, even though 'sizeof(token_t)' is not valid in C. + +def test_unknown_type_3(): + ffi = FFI() + ffi.cdef(""" + typedef ... *token_p; + token_p foo(token_p); + """) + lib = ffi.verify(""" + typedef struct _token_s *token_p; + token_p foo(token_p arg) { + if (arg) + return (token_p)0x12347; + else + return (token_p)0x12345; + } + """) + p = lib.foo(ffi.NULL) + assert int(ffi.cast("intptr_t", p)) == 0x12345 + q = lib.foo(p) + assert int(ffi.cast("intptr_t", q)) == 0x12347 + +def test_varargs(): + ffi = FFI() + ffi.cdef("int foo(int x, ...);") + lib = ffi.verify(""" + int foo(int x, ...) { + va_list vargs; + va_start(vargs, x); + x -= va_arg(vargs, int); + x -= va_arg(vargs, int); + va_end(vargs); + return x; + } + """) + assert lib.foo(50, ffi.cast("int", 5), ffi.cast("int", 3)) == 42 + +def test_varargs_exact(): + if sys.platform == 'win32': + py.test.skip("XXX fixme: only gives warnings") + ffi = FFI() + ffi.cdef("int foo(int x, ...);") + py.test.raises(VerificationError, ffi.verify, """ + int foo(long long x, ...) { + return x; + } + """) + +def test_varargs_struct(): + ffi = FFI() + ffi.cdef("struct foo_s { char a; int b; }; int foo(int x, ...);") + lib = ffi.verify(""" + struct foo_s { + char a; int b; + }; + int foo(int x, ...) { + va_list vargs; + struct foo_s s; + va_start(vargs, x); + s = va_arg(vargs, struct foo_s); + va_end(vargs); + return s.a - s.b; + } + """) + s = ffi.new("struct foo_s *", [b'B', 1]) + assert lib.foo(50, s[0]) == ord('A') + +def test_autofilled_struct_as_argument(): + ffi = FFI() + ffi.cdef("struct foo_s { long a; double b; ...; };\n" + "int foo(struct foo_s);") + lib = ffi.verify(""" + struct foo_s { + double b; + long a; + }; + int foo(struct foo_s s) { + return (int)s.a - (int)s.b; + } + """) + s = ffi.new("struct foo_s *", [100, 1]) + assert lib.foo(s[0]) == 99 + assert lib.foo([100, 1]) == 99 + +def test_autofilled_struct_as_argument_dynamic(): + ffi = FFI() + ffi.cdef("struct foo_s { long a; ...; };\n" + "int (*foo)(struct foo_s);") + lib = ffi.verify(""" + struct foo_s { + double b; + long a; + }; + int foo1(struct foo_s s) { + return (int)s.a - (int)s.b; + } + int (*foo)(struct foo_s s) = &foo1; + """) + e = py.test.raises(NotImplementedError, lib.foo, "?") + msg = ("ctype 'struct foo_s' not supported as argument. 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 argument " + "if the function is 'API mode' and non-variadic (i.e. declared " + "inside ffibuilder.cdef()+ffibuilder.set_source() and not taking " + "a final '...' argument)") + assert str(e.value) == msg + +def test_func_returns_struct(): + ffi = FFI() + ffi.cdef(""" + struct foo_s { int aa, bb; }; + struct foo_s foo(int a, int b); + """) + lib = ffi.verify(""" + struct foo_s { int aa, bb; }; + struct foo_s foo(int a, int b) { + struct foo_s r; + r.aa = a*a; + r.bb = b*b; + return r; + } + """) + s = lib.foo(6, 7) + assert repr(s) == "<cdata 'struct foo_s' owning 8 bytes>" + assert s.aa == 36 + assert s.bb == 49 + +def test_func_as_funcptr(): + ffi = FFI() + ffi.cdef("int *(*const fooptr)(void);") + lib = ffi.verify(""" + int *foo(void) { + return (int*)"foobar"; + } + int *(*fooptr)(void) = foo; + """) + foochar = ffi.cast("char *(*)(void)", lib.fooptr) + s = foochar() + assert ffi.string(s) == b"foobar" + +def test_funcptr_as_argument(): + ffi = FFI() + ffi.cdef(""" + void qsort(void *base, size_t nel, size_t width, + int (*compar)(const void *, const void *)); + """) + ffi.verify("#include <stdlib.h>") + +def test_func_as_argument(): + ffi = FFI() + ffi.cdef(""" + void qsort(void *base, size_t nel, size_t width, + int compar(const void *, const void *)); + """) + ffi.verify("#include <stdlib.h>") + +def test_array_as_argument(): + ffi = FFI() + ffi.cdef(""" + size_t strlen(char string[]); + """) + ffi.verify("#include <string.h>") + +def test_enum_as_argument(): + ffi = FFI() + ffi.cdef(""" + enum foo_e { AA, BB, ... }; + int foo_func(enum foo_e); + """) + lib = ffi.verify(""" + enum foo_e { AA, CC, BB }; + int foo_func(enum foo_e e) { return (int)e; } + """) + assert lib.foo_func(lib.BB) == 2 + py.test.raises(TypeError, lib.foo_func, "BB") + +def test_enum_as_function_result(): + ffi = FFI() + ffi.cdef(""" + enum foo_e { AA, BB, ... }; + enum foo_e foo_func(int x); + """) + lib = ffi.verify(""" + enum foo_e { AA, CC, BB }; + enum foo_e foo_func(int x) { return (enum foo_e)x; } + """) + assert lib.foo_func(lib.BB) == lib.BB == 2 + +def test_enum_values(): + ffi = FFI() + ffi.cdef("enum enum1_e { AA, BB };") + lib = ffi.verify("enum enum1_e { AA, BB };") + assert lib.AA == 0 + assert lib.BB == 1 + assert ffi.string(ffi.cast("enum enum1_e", 1)) == 'BB' + +def test_typedef_complete_enum(): + ffi = FFI() + ffi.cdef("typedef enum { AA, BB } enum1_t;") + lib = ffi.verify("typedef enum { AA, BB } enum1_t;") + assert ffi.string(ffi.cast("enum1_t", 1)) == 'BB' + assert lib.AA == 0 + assert lib.BB == 1 + +def test_typedef_broken_complete_enum(): + ffi = FFI() + ffi.cdef("typedef enum { AA, BB } enum1_t;") + py.test.raises(VerificationError, ffi.verify, + "typedef enum { AA, CC, BB } enum1_t;") + +def test_typedef_incomplete_enum(): + ffi = FFI() + ffi.cdef("typedef enum { AA, BB, ... } enum1_t;") + lib = ffi.verify("typedef enum { AA, CC, BB } enum1_t;") + assert ffi.string(ffi.cast("enum1_t", 1)) == '1' + assert ffi.string(ffi.cast("enum1_t", 2)) == 'BB' + assert lib.AA == 0 + assert lib.BB == 2 + +def test_typedef_enum_as_argument(): + ffi = FFI() + ffi.cdef(""" + typedef enum { AA, BB, ... } foo_t; + int foo_func(foo_t); + """) + lib = ffi.verify(""" + typedef enum { AA, CC, BB } foo_t; + int foo_func(foo_t e) { return (int)e; } + """) + assert lib.foo_func(lib.BB) == lib.BB == 2 + py.test.raises(TypeError, lib.foo_func, "BB") + +def test_typedef_enum_as_function_result(): + ffi = FFI() + ffi.cdef(""" + typedef enum { AA, BB, ... } foo_t; + foo_t foo_func(int x); + """) + lib = ffi.verify(""" + typedef enum { AA, CC, BB } foo_t; + foo_t foo_func(int x) { return (foo_t)x; } + """) + assert lib.foo_func(lib.BB) == lib.BB == 2 + +def test_function_typedef(): + ffi = FFI() + ffi.cdef(""" + typedef double func_t(double); + func_t sin; + """) + lib = ffi.verify('#include <math.h>', libraries=lib_m) + assert lib.sin(1.23) == math.sin(1.23) + +def test_opaque_integer_as_function_result(): + #import platform + #if platform.machine().startswith('sparc'): + # py.test.skip('Breaks horribly on sparc (SIGILL + corrupted stack)') + #elif platform.machine() == 'mips64' and sys.maxsize > 2**32: + # py.test.skip('Segfaults on mips64el') + # XXX bad abuse of "struct { ...; }". It only works a bit by chance + # anyway. XXX think about something better :-( + ffi = FFI() + ffi.cdef(""" + typedef struct { ...; } myhandle_t; + myhandle_t foo(void); + """) + lib = ffi.verify(""" + typedef short myhandle_t; + myhandle_t foo(void) { return 42; } + """) + h = lib.foo() + assert ffi.sizeof(h) == ffi.sizeof("short") + +def test_return_partial_struct(): + ffi = FFI() + ffi.cdef(""" + typedef struct { int x; ...; } foo_t; + foo_t foo(void); + """) + lib = ffi.verify(""" + typedef struct { int y, x; } foo_t; + foo_t foo(void) { foo_t r = { 45, 81 }; return r; } + """) + h = lib.foo() + assert ffi.sizeof(h) == 2 * ffi.sizeof("int") + assert h.x == 81 + +def test_take_and_return_partial_structs(): + ffi = FFI() + ffi.cdef(""" + typedef struct { int x; ...; } foo_t; + foo_t foo(foo_t, foo_t); + """) + lib = ffi.verify(""" + typedef struct { int y, x; } foo_t; + foo_t foo(foo_t a, foo_t b) { + foo_t r = { 100, a.x * 5 + b.x * 7 }; + return r; + } + """) + args = ffi.new("foo_t[3]") + args[0].x = 1000 + args[2].x = -498 + h = lib.foo(args[0], args[2]) + assert ffi.sizeof(h) == 2 * ffi.sizeof("int") + assert h.x == 1000 * 5 - 498 * 7 + +def test_cannot_name_struct_type(): + ffi = FFI() + ffi.cdef("typedef struct { int x; } **sp; void foo(sp);") + e = py.test.raises(VerificationError, ffi.verify, + "typedef struct { int x; } **sp; void foo(sp x) { }") + assert 'in argument of foo: unknown type name' in str(e.value) + +def test_dont_check_unnamable_fields(): + ffi = FFI() + ffi.cdef("struct foo_s { struct { int x; } someone; };") + ffi.verify("struct foo_s { struct { int x; } someone; };") + # assert did not crash + +def test_nested_anonymous_struct_exact(): + if sys.platform == 'win32': + py.test.skip("nested anonymous struct/union") + ffi = FFI() + ffi.cdef(""" + struct foo_s { struct { int a; char b; }; union { char c, d; }; }; + """) + ffi.verify(""" + struct foo_s { struct { int a; char b; }; union { char c, d; }; }; + """) + p = ffi.new("struct foo_s *") + assert ffi.sizeof(p[0]) == 3 * ffi.sizeof("int") # with alignment + p.a = 1234567 + p.b = b'X' + p.c = b'Y' + assert p.a == 1234567 + assert p.b == b'X' + assert p.c == b'Y' + assert p.d == b'Y' + +def test_nested_anonymous_struct_exact_error(): + if sys.platform == 'win32': + py.test.skip("nested anonymous struct/union") + ffi = FFI() + ffi.cdef(""" + struct foo_s { struct { int a; char b; }; union { char c, d; }; }; + """) + py.test.raises(VerificationError, ffi.verify, """ + struct foo_s { struct { int a; short b; }; union { char c, d; }; }; + """) + py.test.raises(VerificationError, ffi.verify, """ + struct foo_s { struct { int a; char e, b; }; union { char c, d; }; }; + """) + +def test_nested_anonymous_struct_inexact_1(): + ffi = FFI() + ffi.cdef(""" + struct foo_s { struct { char b; ...; }; union { char c, d; }; }; + """) + ffi.verify(""" + struct foo_s { int a, padding; char c, d, b; }; + """) + assert ffi.sizeof("struct foo_s") == 3 * ffi.sizeof("int") + +def test_nested_anonymous_struct_inexact_2(): + ffi = FFI() + ffi.cdef(""" + struct foo_s { union { char c, d; }; struct { int a; char b; }; ...; }; + """) + ffi.verify(""" + struct foo_s { int a, padding; char c, d, b; }; + """) + assert ffi.sizeof("struct foo_s") == 3 * ffi.sizeof("int") + +def test_ffi_union(): + ffi = FFI() + ffi.cdef("union foo_u { char x; long *z; };") + ffi.verify("union foo_u { char x; int y; long *z; };") + +def test_ffi_union_partial(): + ffi = FFI() + ffi.cdef("union foo_u { char x; ...; };") + ffi.verify("union foo_u { char x; int y; };") + assert ffi.sizeof("union foo_u") == 4 + +def test_ffi_union_with_partial_struct(): + ffi = FFI() + ffi.cdef("struct foo_s { int x; ...; }; union foo_u { struct foo_s s; };") + ffi.verify("struct foo_s { int a; int x; }; " + "union foo_u { char b[32]; struct foo_s s; };") + assert ffi.sizeof("struct foo_s") == 8 + assert ffi.sizeof("union foo_u") == 32 + +def test_ffi_union_partial_2(): + ffi = FFI() + ffi.cdef("typedef union { char x; ...; } u1;") + ffi.verify("typedef union { char x; int y; } u1;") + assert ffi.sizeof("u1") == 4 + +def test_ffi_union_with_partial_struct_2(): + ffi = FFI() + ffi.cdef("typedef struct { int x; ...; } s1;" + "typedef union { s1 s; } u1;") + ffi.verify("typedef struct { int a; int x; } s1; " + "typedef union { char b[32]; s1 s; } u1;") + assert ffi.sizeof("s1") == 8 + assert ffi.sizeof("u1") == 32 + assert ffi.offsetof("u1", "s") == 0 + +def test_ffi_struct_packed(): + if sys.platform == 'win32': + py.test.skip("needs a GCC extension") + ffi = FFI() + ffi.cdef("struct foo_s { int b; ...; };") + ffi.verify(""" + struct foo_s { + char a; + int b; + } __attribute__((packed)); + """) + +def test_tmpdir(): + import tempfile, os + from testing.udir import udir + tmpdir = tempfile.mkdtemp(dir=str(udir)) + ffi = FFI() + ffi.cdef("int foo(int);") + lib = ffi.verify("int foo(int a) { return a + 42; }", tmpdir=tmpdir) + assert os.listdir(tmpdir) + assert lib.foo(100) == 142 + +def test_relative_to(): + import tempfile, os + from testing.udir import udir + tmpdir = tempfile.mkdtemp(dir=str(udir)) + ffi = FFI() + ffi.cdef("int foo(int);") + f = open(os.path.join(tmpdir, 'foo.h'), 'w') + f.write("int foo(int a) { return a + 42; }\n") + f.close() + lib = ffi.verify('#include "foo.h"', + include_dirs=['.'], + relative_to=os.path.join(tmpdir, 'x')) + assert lib.foo(100) == 142 + +def test_bug1(): + ffi = FFI() + ffi.cdef(""" + typedef struct tdlhandle_s { ...; } *tdl_handle_t; + typedef struct my_error_code_ { + tdl_handle_t *rh; + } my_error_code_t; + """) + ffi.verify(""" + typedef struct tdlhandle_s { int foo; } *tdl_handle_t; + typedef struct my_error_code_ { + tdl_handle_t *rh; + } my_error_code_t; + """) + +def test_bool(): + if sys.platform == 'win32': + py.test.skip("_Bool not in MSVC") + ffi = FFI() + ffi.cdef("struct foo_s { _Bool x; };" + "_Bool foo(_Bool); _Bool (*foop)(_Bool);") + lib = ffi.verify(""" + struct foo_s { _Bool x; }; + int foo(int arg) { + return !arg; + } + _Bool _foofunc(_Bool x) { + return !x; + } + _Bool (*foop)(_Bool) = _foofunc; + """) + p = ffi.new("struct foo_s *") + p.x = 1 + assert p.x is True + py.test.raises(OverflowError, "p.x = -1") + py.test.raises(TypeError, "p.x = 0.0") + assert lib.foop(1) is False + assert lib.foop(True) is False + assert lib.foop(0) is True + py.test.raises(OverflowError, lib.foop, 42) + py.test.raises(TypeError, lib.foop, 0.0) + assert lib.foo(1) is False + assert lib.foo(True) is False + assert lib.foo(0) is True + py.test.raises(OverflowError, lib.foo, 42) + py.test.raises(TypeError, lib.foo, 0.0) + assert int(ffi.cast("_Bool", long(1))) == 1 + assert int(ffi.cast("_Bool", long(0))) == 0 + assert int(ffi.cast("_Bool", long(-1))) == 1 + assert int(ffi.cast("_Bool", 10**200)) == 1 + assert int(ffi.cast("_Bool", 10**40000)) == 1 + # + class Foo(object): + def __int__(self): + self.seen = 1 + return result + f = Foo() + f.seen = 0 + result = 42 + assert int(ffi.cast("_Bool", f)) == 1 + assert f.seen + f.seen = 0 + result = 0 + assert int(ffi.cast("_Bool", f)) == 0 + assert f.seen + # + py.test.raises(TypeError, ffi.cast, "_Bool", []) + +def test_bool_on_long_double(): + if sys.platform == 'win32': + py.test.skip("_Bool not in MSVC") + f = 1E-250 + if f == 0.0 or f*f != 0.0: + py.test.skip("unexpected precision") + ffi = FFI() + ffi.cdef("long double square(long double f); _Bool opposite(_Bool);") + lib = ffi.verify("long double square(long double f) { return f*f; }\n" + "_Bool opposite(_Bool x) { return !x; }") + f0 = lib.square(0.0) + f2 = lib.square(f) + f3 = lib.square(f * 2.0) + if repr(f2) == repr(f3): + py.test.skip("long double doesn't have enough precision") + assert float(f0) == float(f2) == float(f3) == 0.0 # too tiny for 'double' + assert int(ffi.cast("_Bool", f2)) == 1 + assert int(ffi.cast("_Bool", f3)) == 1 + assert int(ffi.cast("_Bool", f0)) == 0 + py.test.raises(TypeError, lib.opposite, f2) + +def test_cannot_pass_float(): + for basetype in ['char', 'short', 'int', 'long', 'long long']: + for sign in ['signed', 'unsigned']: + type = '%s %s' % (sign, basetype) + ffi = FFI() + ffi.cdef("struct foo_s { %s x; };\n" + "int foo(%s);" % (type, type)) + lib = ffi.verify(""" + struct foo_s { %s x; }; + int foo(%s arg) { + return !arg; + } + """ % (type, type)) + p = ffi.new("struct foo_s *") + py.test.raises(TypeError, "p.x = 0.0") + assert lib.foo(42) == 0 + assert lib.foo(0) == 1 + py.test.raises(TypeError, lib.foo, 0.0) + +def test_cast_from_int_type_to_bool(): + ffi = FFI() + for basetype in ['char', 'short', 'int', 'long', 'long long']: + for sign in ['signed', 'unsigned']: + type = '%s %s' % (sign, basetype) + assert int(ffi.cast("_Bool", ffi.cast(type, 42))) == 1 + assert int(ffi.cast("bool", ffi.cast(type, 42))) == 1 + assert int(ffi.cast("_Bool", ffi.cast(type, 0))) == 0 + +def test_addressof(): + ffi = FFI() + ffi.cdef(""" + struct point_s { int x, y; }; + struct foo_s { int z; struct point_s point; }; + struct point_s sum_coord(struct point_s *); + """) + lib = ffi.verify(""" + struct point_s { int x, y; }; + struct foo_s { int z; struct point_s point; }; + struct point_s sum_coord(struct point_s *point) { + struct point_s r; + r.x = point->x + point->y; + r.y = point->x - point->y; + return r; + } + """) + p = ffi.new("struct foo_s *") + p.point.x = 16 + p.point.y = 9 + py.test.raises(TypeError, lib.sum_coord, p.point) + res = lib.sum_coord(ffi.addressof(p.point)) + assert res.x == 25 + assert res.y == 7 + res2 = lib.sum_coord(ffi.addressof(res)) + assert res2.x == 32 + assert res2.y == 18 + py.test.raises(TypeError, lib.sum_coord, res2) + +def test_callback_in_thread(): + if sys.platform == 'win32': + py.test.skip("pthread only") + import os, subprocess, imp + arg = os.path.join(os.path.dirname(__file__), 'callback_in_thread.py') + g = subprocess.Popen([sys.executable, arg, + os.path.dirname(imp.find_module('cffi')[1])]) + result = g.wait() + assert result == 0 + +def test_keepalive_lib(): + ffi = FFI() + ffi.cdef("int foobar(void);") + lib = ffi.verify("int foobar(void) { return 42; }") + func = lib.foobar + ffi_r = weakref.ref(ffi) + lib_r = weakref.ref(lib) + del ffi + import gc; gc.collect() # lib stays alive + assert lib_r() is not None + assert ffi_r() is not None + assert func() == 42 + +def test_keepalive_ffi(): + ffi = FFI() + ffi.cdef("int foobar(void);") + lib = ffi.verify("int foobar(void) { return 42; }") + func = lib.foobar + ffi_r = weakref.ref(ffi) + lib_r = weakref.ref(lib) + del lib + import gc; gc.collect() # ffi stays alive + assert ffi_r() is not None + assert lib_r() is not None + assert func() == 42 + +def test_FILE_stored_in_stdout(): + if not sys.platform.startswith('linux'): + py.test.skip("likely, we cannot assign to stdout") + ffi = FFI() + ffi.cdef("int printf(const char *, ...); FILE *setstdout(FILE *);") + lib = ffi.verify(""" + #include <stdio.h> + FILE *setstdout(FILE *f) { + FILE *result = stdout; + stdout = f; + return result; + } + """) + import os + fdr, fdw = os.pipe() + fw1 = os.fdopen(fdw, 'wb', 256) + old_stdout = lib.setstdout(fw1) + try: + # + fw1.write(b"X") + r = lib.printf(b"hello, %d!\n", ffi.cast("int", 42)) + fw1.close() + assert r == len("hello, 42!\n") + # + finally: + lib.setstdout(old_stdout) + # + result = os.read(fdr, 256) + os.close(fdr) + # the 'X' might remain in the user-level buffer of 'fw1' and + # end up showing up after the 'hello, 42!\n' + assert result == b"Xhello, 42!\n" or result == b"hello, 42!\nX" + +def test_FILE_stored_explicitly(): + ffi = FFI() + ffi.cdef("int myprintf11(const char *, int); FILE *myfile;") + lib = ffi.verify(""" + #include <stdio.h> + FILE *myfile; + int myprintf11(const char *out, int value) { + return fprintf(myfile, out, value); + } + """) + import os + fdr, fdw = os.pipe() + fw1 = os.fdopen(fdw, 'wb', 256) + lib.myfile = ffi.cast("FILE *", fw1) + # + fw1.write(b"X") + r = lib.myprintf11(b"hello, %d!\n", ffi.cast("int", 42)) + fw1.close() + assert r == len("hello, 42!\n") + # + result = os.read(fdr, 256) + os.close(fdr) + # the 'X' might remain in the user-level buffer of 'fw1' and + # end up showing up after the 'hello, 42!\n' + assert result == b"Xhello, 42!\n" or result == b"hello, 42!\nX" + +def test_global_array_with_missing_length(): + ffi = FFI() + ffi.cdef("int fooarray[];") + lib = ffi.verify("int fooarray[50];") + assert repr(lib.fooarray).startswith("<cdata 'int *'") + +def test_global_array_with_dotdotdot_length(): + ffi = FFI() + ffi.cdef("int fooarray[...];") + lib = ffi.verify("int fooarray[50];") + assert repr(lib.fooarray).startswith("<cdata 'int[50]'") + +def test_bad_global_array_with_dotdotdot_length(): + ffi = FFI() + ffi.cdef("int fooarray[...];") + py.test.raises(VerificationError, ffi.verify, "char fooarray[23];") + +def test_struct_containing_struct(): + ffi = FFI() + ffi.cdef("struct foo_s { ...; }; struct bar_s { struct foo_s f; ...; };") + ffi.verify("struct foo_s { int x; }; struct bar_s { struct foo_s f; };") + # + ffi = FFI() + ffi.cdef("struct foo_s { struct bar_s f; ...; }; struct bar_s { ...; };") + ffi.verify("struct bar_s { int x; }; struct foo_s { struct bar_s f; };") + +def test_struct_returned_by_func(): + ffi = FFI() + ffi.cdef("typedef ... foo_t; foo_t myfunc(void);") + e = py.test.raises(TypeError, ffi.verify, + "typedef struct { int x; } foo_t; " + "foo_t myfunc(void) { foo_t x = { 42 }; return x; }") + assert str(e.value) == ( + "function myfunc: 'foo_t' is used as result type, but is opaque") + +def test_include(): + ffi1 = FFI() + ffi1.cdef("typedef struct { int x; ...; } foo_t;") + ffi1.verify("typedef struct { int y, x; } foo_t;") + ffi2 = FFI() + ffi2.include(ffi1) + ffi2.cdef("int myfunc(foo_t *);") + lib = ffi2.verify("typedef struct { int y, x; } foo_t;" + "int myfunc(foo_t *p) { return 42 * p->x; }") + res = lib.myfunc(ffi2.new("foo_t *", {'x': 10})) + assert res == 420 + res = lib.myfunc(ffi1.new("foo_t *", {'x': -10})) + assert res == -420 + +def test_include_enum(): + ffi1 = FFI() + ffi1.cdef("enum foo_e { AA, ... };") + lib1 = ffi1.verify("enum foo_e { CC, BB, AA };") + ffi2 = FFI() + ffi2.include(ffi1) + ffi2.cdef("int myfunc(enum foo_e);") + lib2 = ffi2.verify("enum foo_e { CC, BB, AA };" + "int myfunc(enum foo_e x) { return (int)x; }") + res = lib2.myfunc(lib2.AA) + assert res == 2 + +def test_named_pointer_as_argument(): + ffi = FFI() + ffi.cdef("typedef struct { int x; } *mystruct_p;\n" + "mystruct_p ff5a(mystruct_p);") + lib = ffi.verify("typedef struct { int x; } *mystruct_p;\n" + "mystruct_p ff5a(mystruct_p p) { p->x += 40; return p; }") + p = ffi.new("mystruct_p", [-2]) + q = lib.ff5a(p) + assert q == p + assert p.x == 38 + +def test_enum_size(): + cases = [('123', 4, 4294967295), + ('4294967295U', 4, 4294967295), + ('-123', 4, -1), + ('-2147483647-1', 4, -1), + ] + if FFI().sizeof("long") == 8: + cases += [('4294967296L', 8, 2**64-1), + ('%dUL' % (2**64-1), 8, 2**64-1), + ('-2147483649L', 8, -1), + ('%dL-1L' % (1-2**63), 8, -1)] + for hidden_value, expected_size, expected_minus1 in cases: + if sys.platform == 'win32' and 'U' in hidden_value: + continue # skipped on Windows + ffi = FFI() + ffi.cdef("enum foo_e { AA, BB, ... };") + lib = ffi.verify("enum foo_e { AA, BB=%s };" % hidden_value) + assert lib.AA == 0 + assert lib.BB == eval(hidden_value.replace('U', '').replace('L', '')) + assert ffi.sizeof("enum foo_e") == expected_size + assert int(ffi.cast("enum foo_e", -1)) == expected_minus1 + # test with the large value hidden: + # disabled so far, doesn't work +## for hidden_value, expected_size, expected_minus1 in cases: +## ffi = FFI() +## ffi.cdef("enum foo_e { AA, BB, ... };") +## lib = ffi.verify("enum foo_e { AA, BB=%s };" % hidden_value) +## assert lib.AA == 0 +## assert ffi.sizeof("enum foo_e") == expected_size +## assert int(ffi.cast("enum foo_e", -1)) == expected_minus1 + +def test_enum_bug118(): + maxulong = 256 ** FFI().sizeof("unsigned long") - 1 + for c1, c2, c2c in [(0xffffffff, -1, ''), + (maxulong, -1, ''), + (-1, 0xffffffff, 'U'), + (-1, maxulong, 'UL')]: + if c2c and sys.platform == 'win32': + continue # enums may always be signed with MSVC + ffi = FFI() + ffi.cdef("enum foo_e { AA=%s };" % c1) + e = py.test.raises(VerificationError, ffi.verify, + "enum foo_e { AA=%s%s };" % (c2, c2c)) + assert str(e.value) == ('enum foo_e: AA has the real value %d, not %d' + % (c2, c1)) + +def test_string_to_voidp_arg(): + ffi = FFI() + ffi.cdef("int myfunc(void *);") + lib = ffi.verify("int myfunc(void *p) { return ((signed char *)p)[0]; }") + res = lib.myfunc(b"hi!") + assert res == ord(b"h") + p = ffi.new("char[]", b"gah") + res = lib.myfunc(p) + assert res == ord(b"g") + res = lib.myfunc(ffi.cast("void *", p)) + assert res == ord(b"g") + res = lib.myfunc(ffi.cast("int *", p)) + assert res == ord(b"g") + +def test_callback_indirection(): + ffi = FFI() + ffi.cdef(""" + int (*python_callback)(int how_many, int *values); + int (*const c_callback)(int,...); /* pass this ptr to C routines */ + int some_c_function(int(*cb)(int,...)); + """) + lib = ffi.verify(""" + #include <stdarg.h> + #ifdef _WIN32 + #include <malloc.h> + #define alloca _alloca + #else + # ifdef __FreeBSD__ + # include <stdlib.h> + # else + # include <alloca.h> + # endif + #endif + static int (*python_callback)(int how_many, int *values); + static int c_callback(int how_many, ...) { + va_list ap; + /* collect the "..." arguments into the values[] array */ + int i, *values = alloca((size_t)how_many * sizeof(int)); + va_start(ap, how_many); + for (i=0; i<how_many; i++) + values[i] = va_arg(ap, int); + va_end(ap); + return python_callback(how_many, values); + } + int some_c_function(int(*cb)(int,...)) { + int result = cb(2, 10, 20); + result += cb(3, 30, 40, 50); + return result; + } + """) + seen = [] + @ffi.callback("int(int, int*)") + def python_callback(how_many, values): + seen.append([values[i] for i in range(how_many)]) + return 42 + lib.python_callback = python_callback + + res = lib.some_c_function(lib.c_callback) + assert res == 84 + assert seen == [[10, 20], [30, 40, 50]] + +def test_floatstar_argument(): + ffi = FFI() + ffi.cdef("float sum3floats(float *);") + lib = ffi.verify(""" + float sum3floats(float *f) { + return f[0] + f[1] + f[2]; + } + """) + assert lib.sum3floats((1.5, 2.5, 3.5)) == 7.5 + p = ffi.new("float[]", (1.5, 2.5, 3.5)) + assert lib.sum3floats(p) == 7.5 + +def test_charstar_argument(): + ffi = FFI() + ffi.cdef("char sum3chars(char *);") + lib = ffi.verify(""" + char sum3chars(char *f) { + return (char)(f[0] + f[1] + f[2]); + } + """) + assert lib.sum3chars((b'\x10', b'\x20', b'\x30')) == b'\x60' + p = ffi.new("char[]", b'\x10\x20\x30') + assert lib.sum3chars(p) == b'\x60' + +def test_passing_string_or_NULL(): + ffi = FFI() + ffi.cdef("int seeme1(char *); int seeme2(int *);") + lib = ffi.verify(""" + int seeme1(char *x) { + return (x == NULL); + } + int seeme2(int *x) { + return (x == NULL); + } + """) + assert lib.seeme1(b"foo") == 0 + assert lib.seeme1(ffi.NULL) == 1 + assert lib.seeme2([42, 43]) == 0 + assert lib.seeme2(ffi.NULL) == 1 + py.test.raises(TypeError, lib.seeme1, None) + py.test.raises(TypeError, lib.seeme2, None) + py.test.raises(TypeError, lib.seeme1, 0.0) + py.test.raises(TypeError, lib.seeme2, 0.0) + py.test.raises(TypeError, lib.seeme1, 0) + py.test.raises(TypeError, lib.seeme2, 0) + zeroL = 99999999999999999999 + zeroL -= 99999999999999999999 + py.test.raises(TypeError, lib.seeme2, zeroL) + +def test_typeof_function(): + ffi = FFI() + ffi.cdef("int foo(int, char);") + lib = ffi.verify("int foo(int x, char y) { (void)x; (void)y; return 42; }") + ctype = ffi.typeof(lib.foo) + assert len(ctype.args) == 2 + assert ctype.result == ffi.typeof("int") + +def test_call_with_voidstar_arg(): + ffi = FFI() + ffi.cdef("int f(void *);") + lib = ffi.verify("int f(void *x) { return ((char*)x)[0]; }") + assert lib.f(b"foobar") == ord(b"f") + +def test_dir(): + ffi = FFI() + ffi.cdef("""void somefunc(void); + extern int somevar, somearray[2]; + static char *const sv2; + enum my_e { AA, BB, ... }; + #define FOO ...""") + lib = ffi.verify("""void somefunc(void) { } + int somevar, somearray[2]; + #define sv2 "text" + enum my_e { AA, BB }; + #define FOO 42""") + assert dir(lib) == ['AA', 'BB', 'FOO', 'somearray', + 'somefunc', 'somevar', 'sv2'] + +def test_typeof_func_with_struct_argument(): + ffi = FFI() + ffi.cdef("""struct s { int a; }; int foo(struct s);""") + lib = ffi.verify("""struct s { int a; }; + int foo(struct s x) { return x.a; }""") + s = ffi.new("struct s *", [-1234]) + m = lib.foo(s[0]) + assert m == -1234 + assert repr(ffi.typeof(lib.foo)) == "<ctype 'int(*)(struct s)'>" + +def test_bug_const_char_ptr_array_1(): + ffi = FFI() + ffi.cdef("""const char *a[...];""") + lib = ffi.verify("""const char *a[5];""") + assert repr(ffi.typeof(lib.a)) == "<ctype 'char *[5]'>" + +def test_bug_const_char_ptr_array_2(): + from cffi import FFI # ignore warnings + ffi = FFI() + ffi.cdef("""const int a[];""") + lib = ffi.verify("""const int a[5];""") + assert repr(ffi.typeof(lib.a)) == "<ctype 'int *'>" + +def _test_various_calls(force_libffi): + cdef_source = """ + int xvalue; + long long ivalue, rvalue; + float fvalue; + double dvalue; + long double Dvalue; + signed char tf_bb(signed char x, signed char c); + unsigned char tf_bB(signed char x, unsigned char c); + short tf_bh(signed char x, short c); + unsigned short tf_bH(signed char x, unsigned short c); + int tf_bi(signed char x, int c); + unsigned int tf_bI(signed char x, unsigned int c); + long tf_bl(signed char x, long c); + unsigned long tf_bL(signed char x, unsigned long c); + long long tf_bq(signed char x, long long c); + unsigned long long tf_bQ(signed char x, unsigned long long c); + float tf_bf(signed char x, float c); + double tf_bd(signed char x, double c); + long double tf_bD(signed char x, long double c); + """ + if force_libffi: + cdef_source = (cdef_source + .replace('tf_', '(*const tf_') + .replace('(signed char x', ')(signed char x')) + ffi = FFI() + ffi.cdef(cdef_source) + lib = ffi.verify(""" + int xvalue; + long long ivalue, rvalue; + float fvalue; + double dvalue; + long double Dvalue; + + typedef signed char b_t; + typedef unsigned char B_t; + typedef short h_t; + typedef unsigned short H_t; + typedef int i_t; + typedef unsigned int I_t; + typedef long l_t; + typedef unsigned long L_t; + typedef long long q_t; + typedef unsigned long long Q_t; + typedef float f_t; + typedef double d_t; + typedef long double D_t; + #define S(letter) xvalue = (int)x; letter##value = (letter##_t)c; + #define R(letter) return (letter##_t)rvalue; + + signed char tf_bb(signed char x, signed char c) { S(i) R(b) } + unsigned char tf_bB(signed char x, unsigned char c) { S(i) R(B) } + short tf_bh(signed char x, short c) { S(i) R(h) } + unsigned short tf_bH(signed char x, unsigned short c) { S(i) R(H) } + int tf_bi(signed char x, int c) { S(i) R(i) } + unsigned int tf_bI(signed char x, unsigned int c) { S(i) R(I) } + long tf_bl(signed char x, long c) { S(i) R(l) } + unsigned long tf_bL(signed char x, unsigned long c) { S(i) R(L) } + long long tf_bq(signed char x, long long c) { S(i) R(q) } + unsigned long long tf_bQ(signed char x, unsigned long long c) { S(i) R(Q) } + float tf_bf(signed char x, float c) { S(f) R(f) } + double tf_bd(signed char x, double c) { S(d) R(d) } + long double tf_bD(signed char x, long double c) { S(D) R(D) } + """) + lib.rvalue = 0x7182838485868788 + for kind, cname in [('b', 'signed char'), + ('B', 'unsigned char'), + ('h', 'short'), + ('H', 'unsigned short'), + ('i', 'int'), + ('I', 'unsigned int'), + ('l', 'long'), + ('L', 'unsigned long'), + ('q', 'long long'), + ('Q', 'unsigned long long'), + ('f', 'float'), + ('d', 'double'), + ('D', 'long double')]: + sign = +1 if 'unsigned' in cname else -1 + lib.xvalue = 0 + lib.ivalue = 0 + lib.fvalue = 0 + lib.dvalue = 0 + lib.Dvalue = 0 + fun = getattr(lib, 'tf_b' + kind) + res = fun(-42, sign * 99) + if kind == 'D': + res = float(res) + assert res == int(ffi.cast(cname, 0x7182838485868788)) + assert lib.xvalue == -42 + if kind in 'fdD': + assert float(getattr(lib, kind + 'value')) == -99.0 + else: + assert lib.ivalue == sign * 99 + +def test_various_calls_direct(): + _test_various_calls(force_libffi=False) + +def test_various_calls_libffi(): + _test_various_calls(force_libffi=True) + +def test_ptr_to_opaque(): + ffi = FFI() + ffi.cdef("typedef ... foo_t; int f1(foo_t*); foo_t *f2(int);") + lib = ffi.verify(""" + #include <stdlib.h> + typedef struct { int x; } foo_t; + int f1(foo_t* p) { + int x = p->x; + free(p); + return x; + } + foo_t *f2(int x) { + foo_t *p = malloc(sizeof(foo_t)); + p->x = x; + return p; + } + """) + p = lib.f2(42) + x = lib.f1(p) + assert x == 42 + +def _run_in_multiple_threads(test1): + test1() + import sys + try: + import thread + except ImportError: + import _thread as thread + errors = [] + def wrapper(lock): + try: + test1() + except: + errors.append(sys.exc_info()) + lock.release() + locks = [] + for i in range(10): + _lock = thread.allocate_lock() + _lock.acquire() + thread.start_new_thread(wrapper, (_lock,)) + locks.append(_lock) + for _lock in locks: + _lock.acquire() + if errors: + raise errors[0][1] + +def test_errno_working_even_with_pypys_jit(): + ffi = FFI() + ffi.cdef("int f(int);") + lib = ffi.verify(""" + #include <errno.h> + int f(int x) { return (errno = errno + x); } + """) + @_run_in_multiple_threads + def test1(): + ffi.errno = 0 + for i in range(10000): + e = lib.f(1) + assert e == i + 1 + assert ffi.errno == e + for i in range(10000): + ffi.errno = i + e = lib.f(42) + assert e == i + 42 + +def test_getlasterror_working_even_with_pypys_jit(): + if sys.platform != 'win32': + py.test.skip("win32-only test") + ffi = FFI() + ffi.cdef("void SetLastError(DWORD);") + lib = ffi.dlopen("Kernel32.dll") + @_run_in_multiple_threads + def test1(): + for i in range(10000): + n = (1 << 29) + i + lib.SetLastError(n) + assert ffi.getwinerror()[0] == n + +def test_verify_dlopen_flags(): + # Careful with RTLD_GLOBAL. If by chance the FFI is not deleted + # promptly, like on PyPy, then other tests may see the same + # exported symbols as well. So we must not export a simple name + # like 'foo'! + ffi1 = FFI() + ffi1.cdef("int foo_verify_dlopen_flags;") + + lib1 = ffi1.verify("int foo_verify_dlopen_flags;", + flags=ffi1.RTLD_GLOBAL | ffi1.RTLD_LAZY) + lib2 = get_second_lib() + + lib1.foo_verify_dlopen_flags = 42 + assert lib2.foo_verify_dlopen_flags == 42 + lib2.foo_verify_dlopen_flags += 1 + assert lib1.foo_verify_dlopen_flags == 43 + +def get_second_lib(): + # Hack, using modulename makes the test fail + ffi2 = FFI() + ffi2.cdef("int foo_verify_dlopen_flags;") + lib2 = ffi2.verify("int foo_verify_dlopen_flags;", + flags=ffi2.RTLD_GLOBAL | ffi2.RTLD_LAZY) + return lib2 + +def test_consider_not_implemented_function_type(): + ffi = FFI() + ffi.cdef("typedef union { int a; float b; } Data;" + "typedef struct { int a:2; } MyStr;" + "typedef void (*foofunc_t)(Data);" + "typedef Data (*bazfunc_t)(void);" + "typedef MyStr (*barfunc_t)(void);") + fooptr = ffi.cast("foofunc_t", 123) + bazptr = ffi.cast("bazfunc_t", 123) + barptr = ffi.cast("barfunc_t", 123) + # assert did not crash so far + e = py.test.raises(NotImplementedError, fooptr, ffi.new("Data *")) + assert str(e.value) == ( + "ctype 'Data' not supported as argument by libffi. Unions are only " + "supported as argument if the function is 'API mode' and " + "non-variadic (i.e. declared inside ffibuilder.cdef()+" + "ffibuilder.set_source() and not taking a final '...' argument)") + e = py.test.raises(NotImplementedError, bazptr) + assert str(e.value) == ( + "ctype 'Data' 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)") + e = py.test.raises(NotImplementedError, barptr) + assert str(e.value) == ( + "ctype 'MyStr' 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_verify_extra_arguments(): + ffi = FFI() + ffi.cdef("#define ABA ...") + lib = ffi.verify("", define_macros=[('ABA', '42')]) + assert lib.ABA == 42 + +def test_implicit_unicode_on_windows(): + if sys.platform != 'win32': + py.test.skip("win32-only test") + ffi = FFI() + e = py.test.raises(FFIError, ffi.cdef, "int foo(LPTSTR);") + assert str(e.value) == ("The Windows type 'LPTSTR' is only available after" + " you call ffi.set_unicode()") + for with_unicode in [True, False]: + ffi = FFI() + ffi.set_unicode(with_unicode) + ffi.cdef(""" + DWORD GetModuleFileName(HMODULE hModule, LPTSTR lpFilename, + DWORD nSize); + """) + lib = ffi.verify(""" + #include <windows.h> + """, libraries=['Kernel32']) + outbuf = ffi.new("TCHAR[]", 200) + n = lib.GetModuleFileName(ffi.NULL, outbuf, 500) + assert 0 < n < 500 + for i in range(n): + #print repr(outbuf[i]) + assert ord(outbuf[i]) != 0 + assert ord(outbuf[n]) == 0 + assert ord(outbuf[0]) < 128 # should be a letter, or '\' + +def test_use_local_dir(): + ffi = FFI() + lib = ffi.verify("", modulename="test_use_local_dir") + this_dir = os.path.dirname(__file__) + pycache_files = os.listdir(os.path.join(this_dir, '__pycache__')) + assert any('test_use_local_dir' in s for s in pycache_files) + +def test_define_known_value(): + ffi = FFI() + ffi.cdef("#define FOO 0x123") + lib = ffi.verify("#define FOO 0x123") + assert lib.FOO == 0x123 + +def test_define_wrong_value(): + ffi = FFI() + ffi.cdef("#define FOO 123") + e = py.test.raises(VerificationError, ffi.verify, "#define FOO 124") + assert str(e.value).endswith("FOO has the real value 124, not 123") + +def test_static_const_int_known_value(): + ffi = FFI() + ffi.cdef("static const int FOO = 0x123;") + lib = ffi.verify("#define FOO 0x123") + assert lib.FOO == 0x123 + +def test_static_const_int_wrong_value(): + ffi = FFI() + ffi.cdef("static const int FOO = 123;") + e = py.test.raises(VerificationError, ffi.verify, "#define FOO 124") + assert str(e.value).endswith("FOO has the real value 124, not 123") + +def test_const_struct_global(): + ffi = FFI() + ffi.cdef("typedef struct { int x; ...; } T; const T myglob;") + lib = ffi.verify("typedef struct { double y; int x; } T;" + "const T myglob = { 0.1, 42 };") + assert ffi.typeof(lib.myglob) == ffi.typeof("T") + assert lib.myglob.x == 42 + +def test_dont_support_int_dotdotdot(): + ffi = FFI() + ffi.cdef("typedef int... t1;") + e = py.test.raises(VerificationError, ffi.verify, "") + assert str(e.value) == ("feature not supported with ffi.verify(), but only " + "with ffi.set_source(): 'typedef int... t1'") + ffi = FFI() + ffi.cdef("typedef double ... t1;") + e = py.test.raises(VerificationError, ffi.verify, "") + assert str(e.value) == ("feature not supported with ffi.verify(), but only " + "with ffi.set_source(): 'typedef float... t1'") + +def test_const_fields(): + ffi = FFI() + ffi.cdef("""struct foo_s { const int a; void *const b; };""") + ffi.verify("""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_win32_calling_convention_0(): + ffi = FFI() + ffi.cdef(""" + int call1(int(__cdecl *cb)(int)); + int (*const call2)(int(__stdcall *cb)(int)); + """) + lib = ffi.verify(r""" + #ifndef _MSC_VER + # define __stdcall /* nothing */ + #endif + int call1(int(*cb)(int)) { + int i, result = 0; + //printf("call1: cb = %p\n", cb); + for (i = 0; i < 1000; i++) + result += cb(i); + //printf("result = %d\n", result); + return result; + } + int call2(int(__stdcall *cb)(int)) { + int i, result = 0; + //printf("call2: cb = %p\n", cb); + for (i = 0; i < 1000; i++) + result += cb(-i); + //printf("result = %d\n", result); + return result; + } + """) + @ffi.callback("int(int)") + def cb1(x): + return x * 2 + @ffi.callback("int __stdcall(int)") + def cb2(x): + return x * 3 + #print 'cb1 =', cb1 + res = lib.call1(cb1) + assert res == 500*999*2 + #print 'cb2 =', cb2 + #print ffi.typeof(lib.call2) + #print 'call2 =', lib.call2 + res = lib.call2(cb2) + #print '...' + assert res == -500*999*3 + #print 'done' + if sys.platform == 'win32' and 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 = ffi.verify(r""" + #ifndef _MSC_VER + # define __cdecl + # define __stdcall + #endif + int __cdecl cb1(int x) { return x * 2; } + int __stdcall cb2(int x) { return x * 3; } + + int __cdecl call1(int(__cdecl *cb)(int)) { + int i, result = 0; + //printf("here1\n"); + //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); + for (i = 0; i < 1000; i++) + result += cb(i); + //printf("result = %d\n", result); + return result; + } + int __stdcall call2(int(__stdcall *cb)(int)) { + int i, result = 0; + //printf("here1\n"); + //printf("cb = %p, cb2 = %p\n", cb, (void *)cb2); + for (i = 0; i < 1000; i++) + result += cb(-i); + //printf("result = %d\n", result); + return result; + } + """) + assert lib.call1(lib.cb1) == 500*999*2 + assert lib.call2(lib.cb2) == -500*999*3 + +def test_win32_calling_convention_2(): + # any mistake in the declaration of plain function (including the + # precise argument types and, here, the calling convention) are + # automatically corrected. But this does not apply to the 'cb' + # function pointer argument. + ffi = FFI() + ffi.cdef(""" + int __stdcall call1(int(__cdecl *cb)(int)); + int __cdecl call2(int(__stdcall *cb)(int)); + int (__cdecl *const cb1)(int); + int (__stdcall *const cb2)(int); + """) + lib = ffi.verify(r""" + #ifndef _MSC_VER + # define __cdecl + # define __stdcall + #endif + int __cdecl call1(int(__cdecl *cb)(int)) { + int i, result = 0; + for (i = 0; i < 1000; i++) + result += cb(i); + return result; + } + int __stdcall call2(int(__stdcall *cb)(int)) { + int i, result = 0; + for (i = 0; i < 1000; i++) + result += cb(-i); + return result; + } + int __cdecl cb1(int x) { return x * 2; } + int __stdcall cb2(int x) { return x * 3; } + """) + assert lib.call1(lib.cb1) == 500*999*2 + assert lib.call2(lib.cb2) == -500*999*3 + +def test_win32_calling_convention_3(): + ffi = FFI() + ffi.cdef(""" + struct point { int x, y; }; + + int (*const cb1)(struct point); + int (__stdcall *const cb2)(struct point); + + struct point __stdcall call1(int(*cb)(struct point)); + struct point call2(int(__stdcall *cb)(struct point)); + """) + lib = ffi.verify(r""" + #ifndef _MSC_VER + # define __cdecl + # define __stdcall + #endif + struct point { int x, y; }; + int cb1(struct point pt) { return pt.x + 10 * pt.y; } + int __stdcall cb2(struct point pt) { return pt.x + 100 * pt.y; } + struct point __stdcall call1(int(__cdecl *cb)(struct point)) { + int i; + struct point result = { 0, 0 }; + //printf("here1\n"); + //printf("cb = %p, cb1 = %p\n", cb, (void *)cb1); + for (i = 0; i < 1000; i++) { + struct point p = { i, -i }; + int r = cb(p); + result.x += r; + result.y -= r; + } + return result; + } + struct point __cdecl call2(int(__stdcall *cb)(struct point)) { + int i; + struct point result = { 0, 0 }; + for (i = 0; i < 1000; i++) { + struct point p = { -i, i }; + int r = cb(p); + result.x += r; + result.y -= r; + } + return result; + } + """) + if sys.platform == 'win32' and sys.maxsize < 2**32: + py.test.raises(TypeError, lib.call1, lib.cb2) + py.test.raises(TypeError, lib.call2, lib.cb1) + pt = lib.call1(lib.cb1) + assert (pt.x, pt.y) == (-9*500*999, 9*500*999) + pt = lib.call2(lib.cb2) + assert (pt.x, pt.y) == (99*500*999, -99*500*999) + +def _only_test_on_linux_intel(): + if not sys.platform.startswith('linux'): + py.test.skip('only running the memory-intensive test on Linux') + import platform + machine = platform.machine() + if 'x86' not in machine and 'x64' not in machine: + py.test.skip('only running the memory-intensive test on x86/x64') + +def test_ffi_gc_size_arg(): + # with PyPy's GC, these calls to ffi.gc() would rapidly consume + # 40 GB of RAM without the third argument + _only_test_on_linux_intel() + ffi = FFI() + ffi.cdef("void *malloc(size_t); void free(void *);") + lib = ffi.verify(r""" + #include <stdlib.h> + """) + for i in range(2000): + p = lib.malloc(20*1024*1024) # 20 MB + p1 = ffi.cast("char *", p) + for j in range(0, 20*1024*1024, 4096): + p1[j] = b'!' + p = ffi.gc(p, lib.free, 20*1024*1024) + del p + +def test_ffi_gc_size_arg_2(): + # a variant of the above: this "attack" works on cpython's cyclic gc too + # and I found no obvious way to prevent that. So for now, this test + # is skipped on CPython, where it eats all the memory. + if '__pypy__' not in sys.builtin_module_names: + py.test.skip("find a way to tweak the cyclic GC of CPython") + _only_test_on_linux_intel() + ffi = FFI() + ffi.cdef("void *malloc(size_t); void free(void *);") + lib = ffi.verify(r""" + #include <stdlib.h> + """) + class X(object): + pass + for i in range(2000): + p = lib.malloc(50*1024*1024) # 50 MB + p1 = ffi.cast("char *", p) + for j in range(0, 50*1024*1024, 4096): + p1[j] = b'!' + p = ffi.gc(p, lib.free, 50*1024*1024) + x = X() + x.p = p + x.cyclic = x + del p, x + +def test_ffi_new_with_cycles(): + # still another variant, with ffi.new() + if '__pypy__' not in sys.builtin_module_names: + py.test.skip("find a way to tweak the cyclic GC of CPython") + ffi = FFI() + ffi.cdef("") + lib = ffi.verify("") + class X(object): + pass + for i in range(2000): + p = ffi.new("char[]", 50*1024*1024) # 50 MB + for j in range(0, 50*1024*1024, 4096): + p[j] = b'!' + x = X() + x.p = p + x.cyclic = x + del p, x |