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