blob: fdb4d5ab02b4480af85fe564199bc01a6d06e840 [file] [log] [blame]
import sys, os, py
import pytest
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)
ignore_warnings = kwds.pop('ignore_warnings', 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' and not ignore_warnings:
# add '-Werror' to the existing 'extra_compile_args' flags
from testing.support import extra_compile_args
kwds['extra_compile_args'] = (kwds.get('extra_compile_args', []) +
extra_compile_args)
if sys.platform == 'darwin':
kwds['extra_link_args'] = (kwds.get('extra_link_args', []) +
['-stdlib=libc++'])
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("extern 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>',
ignore_warnings=True)
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("extern 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("extern 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
with pytest.raises(OverflowError):
lib.a = 2147483648
with pytest.raises(OverflowError):
lib.a = -2147483649
lib.b = 525 # try with the first access being in setattr, too
assert lib.b == 525
with pytest.raises(AttributeError):
del lib.a
with pytest.raises(AttributeError):
del lib.c
with pytest.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
with pytest.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
with pytest.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
with pytest.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); extern 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
with pytest.raises(OverflowError):
p.a -= 1
with pytest.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).startswith(
"struct foo_s: wrong offset for field 'b' (cdef "
'says 0, but C compiler says 4). fix it or use "...;" ')
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
with pytest.raises(IndexError):
p.a[42]
with pytest.raises(IndexError):
p.b[11]
def test_dotdotdot_global_array():
ffi = FFI()
ffi.cdef("extern int aa[...]; extern 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
with pytest.raises(IndexError):
lib.aa[41]
with pytest.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("extern 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("extern 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("extern 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("extern 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("extern 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("extern 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 (sshort_t)(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)], ignore_warnings=True)
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("""
extern 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)
def st1(s):
s = str(s)
if s.startswith("_CFFI_test_unpack_args.Lib."):
s = s[len("_CFFI_test_unpack_args.Lib."):]
return s
assert st1(e1.value) == "foo0() takes no arguments (1 given)"
assert st1(e2.value) == "foo0() takes no arguments (2 given)"
assert st1(e3.value) == "foo1() takes exactly one argument (0 given)"
assert st1(e4.value) == "foo1() takes exactly one argument (2 given)"
assert st1(e5.value) in ["foo2 expected 2 arguments, got 0",
"foo2() takes exactly 2 arguments (0 given)"]
assert st1(e6.value) in ["foo2 expected 2 arguments, got 1",
"foo2() takes exactly 2 arguments (1 given)"]
assert st1(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); }
""", ignore_warnings=True)
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;
extern 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; "
"extern 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
with pytest.raises(IndexError):
s.a[4][8]
with pytest.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
with pytest.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("extern 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
with pytest.raises(IndexError):
lib.a[0][8]
with pytest.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("extern 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
with pytest.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("extern 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
with pytest.raises(IndexError):
lib.a[0][8]
with pytest.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; }
""", ignore_warnings=True)
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); static 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("extern int my_value; extern 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("""extern 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
with pytest.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("extern 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
msg = f.getvalue()
assert "rom cffi callback %r" % (bar,) in msg
assert "rying to convert the result back to C:\n" in msg
assert msg.endswith(
"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);
static int (__stdcall * mycb1)(int);
static 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 (float)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("extern 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("extern 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.0f*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[...];
extern 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_typedef_array_dotdotdot_usage():
ffi = FFI()
ffi.cdef("""
typedef int foo_t[...];
typedef int mat_t[...][...];
struct s { foo_t a; foo_t *b; foo_t **c; };
int myfunc(foo_t a, foo_t *b, foo_t **c);
struct sm { mat_t a; mat_t *b; mat_t **c; };
int myfuncm(mat_t a, mat_t *b, mat_t **c);
""")
lib = verify(ffi, "test_typedef_array_dotdotdot_usage", """
typedef int foo_t[50];
typedef int mat_t[6][7];
struct s { foo_t a; foo_t *b; foo_t **c; };
static int myfunc(foo_t a, foo_t *b, foo_t **c) { return (**c)[49]; }
struct sm { mat_t a; mat_t *b; mat_t **c; };
static int myfuncm(mat_t a, mat_t *b, mat_t **c) { return (**c)[5][6]; }
""")
assert ffi.sizeof("foo_t") == 50 * ffi.sizeof("int")
p = ffi.new("struct s *")
assert ffi.sizeof(p[0]) == 50 * ffi.sizeof("int") + 2 * ffi.sizeof("void *")
p.a[49] = 321
p.b = ffi.addressof(p, 'a')
p.c = ffi.addressof(p, 'b')
assert lib.myfunc(ffi.NULL, ffi.NULL, p.c) == 321
#
assert ffi.sizeof("mat_t") == 42 * ffi.sizeof("int")
p = ffi.new("struct sm *")
assert ffi.sizeof(p[0]) == 42 * ffi.sizeof("int") + 2 * ffi.sizeof("void *")
p.a[5][6] = -321
p.b = ffi.addressof(p, 'a')
p.c = ffi.addressof(p, 'b')
assert lib.myfuncm(ffi.NULL, ffi.NULL, p.c) == -321
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;
s.a = 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 *")
def test_from_buffer_struct():
ffi = FFI()
ffi.cdef("""struct foo_s { int a, b; };""")
lib = verify(ffi, "test_from_buffer_struct_p", """
struct foo_s { int a, b; };
""")
p = ffi.new("struct foo_s *", [-219239, 58974983])
q = ffi.from_buffer("struct foo_s[]", ffi.buffer(p))
assert ffi.typeof(q) == ffi.typeof("struct foo_s[]")
assert len(q) == 1
assert q[0].a == p.a
assert q[0].b == p.b
assert q == p
q = ffi.from_buffer("struct foo_s *", ffi.buffer(p))
assert ffi.typeof(q) == ffi.typeof("struct foo_s *")
assert q.a == p.a
assert q.b == p.b
assert q[0].a == p.a
assert q[0].b == p.b
assert q == p
def test_unnamed_bitfield_1():
ffi = FFI()
ffi.cdef("""struct A { char : 1; };""")
lib = verify(ffi, "test_unnamed_bitfield_1", """
struct A { char : 1; };
""")
p = ffi.new("struct A *")
assert ffi.sizeof(p[0]) == 1
# Note: on gcc, the type name is ignored for anonymous bitfields
# and that's why the result is 1. On MSVC, the result is
# sizeof("char") which is also 1.
def test_unnamed_bitfield_2():
ffi = FFI()
ffi.cdef("""struct A {
short c : 1; short : 1; short d : 1; short : 1; };""")
lib = verify(ffi, "test_unnamed_bitfield_2", """
struct A {
short c : 1; short : 1; short d : 1; short : 1;
};
""")
p = ffi.new("struct A *")
assert ffi.sizeof(p[0]) == ffi.sizeof("short")
def test_unnamed_bitfield_3():
ffi = FFI()
ffi.cdef("""struct A { struct { char : 1; char : 1; } b; };""")
lib = verify(ffi, "test_unnamed_bitfield_3", """
struct A { struct { char : 1; char : 1; } b; };
""")
p = ffi.new("struct A *")
assert ffi.sizeof(p[0]) == 1
# Note: on gcc, the type name is ignored for anonymous bitfields
# and that's why the result is 1. On MSVC, the result is
# sizeof("char") which is also 1.
def test_unnamed_bitfield_4():
ffi = FFI()
ffi.cdef("""struct A { struct {
unsigned c : 1; unsigned : 1; unsigned d : 1; unsigned : 1; } a;
};
struct B { struct A a; };""")
lib = verify(ffi, "test_unnamed_bitfield_4", """
struct A { struct {
unsigned c : 1; unsigned : 1; unsigned d : 1; unsigned : 1; } a;
};
struct B { struct A a; };
""")
b = ffi.new("struct B *")
a = ffi.new("struct A *")
assert ffi.sizeof(a[0]) == ffi.sizeof("unsigned")
assert ffi.sizeof(b[0]) == ffi.sizeof(a[0])
def test_struct_with_func_with_struct_pointer_arg():
ffi = FFI()
ffi.cdef("""struct BinaryTree {
int (* CompareKey)(struct BinaryTree *tree);
};""")
lib = verify(ffi, "test_struct_with_func_with_struct_pointer_arg", """
struct BinaryTree {
int (* CompareKey)(struct BinaryTree *tree);
};
""")
ffi.new("struct BinaryTree *")
def test_struct_with_func_with_struct_arg():
ffi = FFI()
ffi.cdef("""struct BinaryTree {
int (* CompareKey)(struct BinaryTree tree);
};""")
lib = verify(ffi, "test_struct_with_func_with_struct_arg", """
struct BinaryTree {
int (* CompareKey)(struct BinaryTree tree);
};
""")
py.test.raises(RuntimeError, ffi.new, "struct BinaryTree *")
def test_passing_large_list():
ffi = FFI()
ffi.cdef("""void passing_large_list(long[]);""")
lib = verify(ffi, "test_passing_large_list", """
static void passing_large_list(long a[]) { }
""")
arg = list(range(20000000))
lib.passing_large_list(arg)
# assert did not segfault