summaryrefslogtreecommitdiff
path: root/testing/cffi1/test_parse_c_type.py
diff options
context:
space:
mode:
Diffstat (limited to 'testing/cffi1/test_parse_c_type.py')
-rw-r--r--testing/cffi1/test_parse_c_type.py372
1 files changed, 372 insertions, 0 deletions
diff --git a/testing/cffi1/test_parse_c_type.py b/testing/cffi1/test_parse_c_type.py
new file mode 100644
index 0000000..a9f5fcb
--- /dev/null
+++ b/testing/cffi1/test_parse_c_type.py
@@ -0,0 +1,372 @@
+import sys, re, os, py
+import cffi
+from cffi import cffi_opcode
+
+if '__pypy__' in sys.builtin_module_names:
+ try:
+ # pytest >= 4.0
+ py.test.skip("not available on pypy", allow_module_level=True)
+ except TypeError:
+ # older pytest
+ py.test.skip("not available on pypy")
+
+cffi_dir = os.path.dirname(cffi_opcode.__file__)
+
+r_macro = re.compile(r"#define \w+[(][^\n]*|#include [^\n]*")
+r_define = re.compile(r"(#define \w+) [^\n]*")
+r_ifdefs = re.compile(r"(#ifdef |#endif)[^\n]*")
+header = open(os.path.join(cffi_dir, 'parse_c_type.h')).read()
+header = r_macro.sub(r"", header)
+header = r_define.sub(r"\1 ...", header)
+header = r_ifdefs.sub(r"", header)
+
+ffi = cffi.FFI()
+ffi.cdef(header)
+
+lib = ffi.verify(
+ open(os.path.join(cffi_dir, '..', 'c', 'parse_c_type.c')).read() + """
+static const char *get_common_type(const char *search, size_t search_len) {
+ return NULL;
+}
+""", include_dirs=[cffi_dir])
+
+class ParseError(Exception):
+ pass
+
+struct_names = ["bar_s", "foo", "foo_", "foo_s", "foo_s1", "foo_s12"]
+assert struct_names == sorted(struct_names)
+
+enum_names = ["ebar_s", "efoo", "efoo_", "efoo_s", "efoo_s1", "efoo_s12"]
+assert enum_names == sorted(enum_names)
+
+identifier_names = ["id", "id0", "id05", "id05b", "tail"]
+assert identifier_names == sorted(identifier_names)
+
+global_names = ["FIVE", "NEG", "ZERO"]
+assert global_names == sorted(global_names)
+
+ctx = ffi.new("struct _cffi_type_context_s *")
+c_struct_names = [ffi.new("char[]", _n.encode('ascii')) for _n in struct_names]
+ctx_structs = ffi.new("struct _cffi_struct_union_s[]", len(struct_names))
+for _i in range(len(struct_names)):
+ ctx_structs[_i].name = c_struct_names[_i]
+ctx_structs[3].flags = lib._CFFI_F_UNION
+ctx.struct_unions = ctx_structs
+ctx.num_struct_unions = len(struct_names)
+
+c_enum_names = [ffi.new("char[]", _n.encode('ascii')) for _n in enum_names]
+ctx_enums = ffi.new("struct _cffi_enum_s[]", len(enum_names))
+for _i in range(len(enum_names)):
+ ctx_enums[_i].name = c_enum_names[_i]
+ctx.enums = ctx_enums
+ctx.num_enums = len(enum_names)
+
+c_identifier_names = [ffi.new("char[]", _n.encode('ascii'))
+ for _n in identifier_names]
+ctx_identifiers = ffi.new("struct _cffi_typename_s[]", len(identifier_names))
+for _i in range(len(identifier_names)):
+ ctx_identifiers[_i].name = c_identifier_names[_i]
+ ctx_identifiers[_i].type_index = 100 + _i
+ctx.typenames = ctx_identifiers
+ctx.num_typenames = len(identifier_names)
+
+@ffi.callback("int(unsigned long long *)")
+def fetch_constant_five(p):
+ p[0] = 5
+ return 0
+@ffi.callback("int(unsigned long long *)")
+def fetch_constant_zero(p):
+ p[0] = 0
+ return 1
+@ffi.callback("int(unsigned long long *)")
+def fetch_constant_neg(p):
+ p[0] = 123321
+ return 1
+
+ctx_globals = ffi.new("struct _cffi_global_s[]", len(global_names))
+c_glob_names = [ffi.new("char[]", _n.encode('ascii')) for _n in global_names]
+for _i, _fn in enumerate([fetch_constant_five,
+ fetch_constant_neg,
+ fetch_constant_zero]):
+ ctx_globals[_i].name = c_glob_names[_i]
+ ctx_globals[_i].address = _fn
+ ctx_globals[_i].type_op = ffi.cast("_cffi_opcode_t",
+ cffi_opcode.OP_CONSTANT_INT if _i != 1
+ else cffi_opcode.OP_ENUM)
+ctx.globals = ctx_globals
+ctx.num_globals = len(global_names)
+
+
+def parse(input):
+ out = ffi.new("_cffi_opcode_t[]", 100)
+ info = ffi.new("struct _cffi_parse_info_s *")
+ info.ctx = ctx
+ info.output = out
+ info.output_size = len(out)
+ for j in range(len(out)):
+ out[j] = ffi.cast("void *", -424242)
+ res = lib.parse_c_type(info, input.encode('ascii'))
+ if res < 0:
+ raise ParseError(ffi.string(info.error_message).decode('ascii'),
+ info.error_location)
+ assert 0 <= res < len(out)
+ result = []
+ for j in range(len(out)):
+ if out[j] == ffi.cast("void *", -424242):
+ assert res < j
+ break
+ i = int(ffi.cast("intptr_t", out[j]))
+ if j == res:
+ result.append('->')
+ result.append(i)
+ return result
+
+def parsex(input):
+ result = parse(input)
+ def str_if_int(x):
+ if isinstance(x, str):
+ return x
+ return '%d,%d' % (x & 255, x >> 8)
+ return ' '.join(map(str_if_int, result))
+
+def parse_error(input, expected_msg, expected_location):
+ e = py.test.raises(ParseError, parse, input)
+ assert e.value.args[0] == expected_msg
+ assert e.value.args[1] == expected_location
+
+def make_getter(name):
+ opcode = getattr(lib, '_CFFI_OP_' + name)
+ def getter(value):
+ return opcode | (value << 8)
+ return getter
+
+Prim = make_getter('PRIMITIVE')
+Pointer = make_getter('POINTER')
+Array = make_getter('ARRAY')
+OpenArray = make_getter('OPEN_ARRAY')
+NoOp = make_getter('NOOP')
+Func = make_getter('FUNCTION')
+FuncEnd = make_getter('FUNCTION_END')
+Struct = make_getter('STRUCT_UNION')
+Enum = make_getter('ENUM')
+Typename = make_getter('TYPENAME')
+
+
+def test_simple():
+ for simple_type, expected in [
+ ("int", lib._CFFI_PRIM_INT),
+ ("signed int", lib._CFFI_PRIM_INT),
+ (" long ", lib._CFFI_PRIM_LONG),
+ ("long int", lib._CFFI_PRIM_LONG),
+ ("unsigned short", lib._CFFI_PRIM_USHORT),
+ ("long double", lib._CFFI_PRIM_LONGDOUBLE),
+ (" float _Complex", lib._CFFI_PRIM_FLOATCOMPLEX),
+ ("double _Complex ", lib._CFFI_PRIM_DOUBLECOMPLEX),
+ ]:
+ assert parse(simple_type) == ['->', Prim(expected)]
+
+def test_array():
+ assert parse("int[5]") == [Prim(lib._CFFI_PRIM_INT), '->', Array(0), 5]
+ assert parse("int[]") == [Prim(lib._CFFI_PRIM_INT), '->', OpenArray(0)]
+ assert parse("int[5][8]") == [Prim(lib._CFFI_PRIM_INT),
+ '->', Array(3),
+ 5,
+ Array(0),
+ 8]
+ assert parse("int[][8]") == [Prim(lib._CFFI_PRIM_INT),
+ '->', OpenArray(2),
+ Array(0),
+ 8]
+
+def test_pointer():
+ assert parse("int*") == [Prim(lib._CFFI_PRIM_INT), '->', Pointer(0)]
+ assert parse("int***") == [Prim(lib._CFFI_PRIM_INT),
+ Pointer(0), Pointer(1), '->', Pointer(2)]
+
+def test_grouping():
+ assert parse("int*[]") == [Prim(lib._CFFI_PRIM_INT),
+ Pointer(0), '->', OpenArray(1)]
+ assert parse("int**[][8]") == [Prim(lib._CFFI_PRIM_INT),
+ Pointer(0), Pointer(1),
+ '->', OpenArray(4), Array(2), 8]
+ assert parse("int(*)[]") == [Prim(lib._CFFI_PRIM_INT),
+ NoOp(3), '->', Pointer(1), OpenArray(0)]
+ assert parse("int(*)[][8]") == [Prim(lib._CFFI_PRIM_INT),
+ NoOp(3), '->', Pointer(1),
+ OpenArray(4), Array(0), 8]
+ assert parse("int**(**)") == [Prim(lib._CFFI_PRIM_INT),
+ Pointer(0), Pointer(1),
+ NoOp(2), Pointer(3), '->', Pointer(4)]
+ assert parse("int**(**)[]") == [Prim(lib._CFFI_PRIM_INT),
+ Pointer(0), Pointer(1),
+ NoOp(6), Pointer(3), '->', Pointer(4),
+ OpenArray(2)]
+
+def test_simple_function():
+ assert parse("int()") == [Prim(lib._CFFI_PRIM_INT),
+ '->', Func(0), FuncEnd(0), 0]
+ assert parse("int(int)") == [Prim(lib._CFFI_PRIM_INT),
+ '->', Func(0), NoOp(4), FuncEnd(0),
+ Prim(lib._CFFI_PRIM_INT)]
+ assert parse("int(long, char)") == [
+ Prim(lib._CFFI_PRIM_INT),
+ '->', Func(0), NoOp(5), NoOp(6), FuncEnd(0),
+ Prim(lib._CFFI_PRIM_LONG),
+ Prim(lib._CFFI_PRIM_CHAR)]
+ assert parse("int(int*)") == [Prim(lib._CFFI_PRIM_INT),
+ '->', Func(0), NoOp(5), FuncEnd(0),
+ Prim(lib._CFFI_PRIM_INT),
+ Pointer(4)]
+ assert parse("int*(void)") == [Prim(lib._CFFI_PRIM_INT),
+ Pointer(0),
+ '->', Func(1), FuncEnd(0), 0]
+ assert parse("int(int, ...)") == [Prim(lib._CFFI_PRIM_INT),
+ '->', Func(0), NoOp(5), FuncEnd(1), 0,
+ Prim(lib._CFFI_PRIM_INT)]
+
+def test_internal_function():
+ assert parse("int(*)()") == [Prim(lib._CFFI_PRIM_INT),
+ NoOp(3), '->', Pointer(1),
+ Func(0), FuncEnd(0), 0]
+ assert parse("int(*())[]") == [Prim(lib._CFFI_PRIM_INT),
+ NoOp(6), Pointer(1),
+ '->', Func(2), FuncEnd(0), 0,
+ OpenArray(0)]
+ assert parse("int(char(*)(long, short))") == [
+ Prim(lib._CFFI_PRIM_INT),
+ '->', Func(0), NoOp(6), FuncEnd(0),
+ Prim(lib._CFFI_PRIM_CHAR),
+ NoOp(7), Pointer(5),
+ Func(4), NoOp(11), NoOp(12), FuncEnd(0),
+ Prim(lib._CFFI_PRIM_LONG),
+ Prim(lib._CFFI_PRIM_SHORT)]
+
+def test_fix_arg_types():
+ assert parse("int(char(long, short))") == [
+ Prim(lib._CFFI_PRIM_INT),
+ '->', Func(0), Pointer(5), FuncEnd(0),
+ Prim(lib._CFFI_PRIM_CHAR),
+ Func(4), NoOp(9), NoOp(10), FuncEnd(0),
+ Prim(lib._CFFI_PRIM_LONG),
+ Prim(lib._CFFI_PRIM_SHORT)]
+ assert parse("int(char[])") == [
+ Prim(lib._CFFI_PRIM_INT),
+ '->', Func(0), Pointer(4), FuncEnd(0),
+ Prim(lib._CFFI_PRIM_CHAR),
+ OpenArray(4)]
+
+def test_enum():
+ for i in range(len(enum_names)):
+ assert parse("enum %s" % (enum_names[i],)) == ['->', Enum(i)]
+ assert parse("enum %s*" % (enum_names[i],)) == [Enum(i),
+ '->', Pointer(0)]
+
+def test_error():
+ parse_error("short short int", "'short' after another 'short' or 'long'", 6)
+ parse_error("long long long", "'long long long' is too long", 10)
+ parse_error("short long", "'long' after 'short'", 6)
+ parse_error("signed unsigned int", "multiple 'signed' or 'unsigned'", 7)
+ parse_error("unsigned signed int", "multiple 'signed' or 'unsigned'", 9)
+ parse_error("long char", "invalid combination of types", 5)
+ parse_error("short char", "invalid combination of types", 6)
+ parse_error("signed void", "invalid combination of types", 7)
+ parse_error("unsigned struct", "invalid combination of types", 9)
+ #
+ parse_error("", "identifier expected", 0)
+ parse_error("]", "identifier expected", 0)
+ parse_error("*", "identifier expected", 0)
+ parse_error("int ]**", "unexpected symbol", 4)
+ parse_error("char char", "unexpected symbol", 5)
+ parse_error("int(int]", "expected ')'", 7)
+ parse_error("int(*]", "expected ')'", 5)
+ parse_error("int(]", "identifier expected", 4)
+ parse_error("int[?]", "expected a positive integer constant", 4)
+ parse_error("int[24)", "expected ']'", 6)
+ parse_error("struct", "struct or union name expected", 6)
+ parse_error("struct 24", "struct or union name expected", 7)
+ parse_error("int[5](*)", "unexpected symbol", 6)
+ parse_error("int a(*)", "identifier expected", 6)
+ parse_error("int[123456789012345678901234567890]", "number too large", 4)
+ #
+ parse_error("_Complex", "identifier expected", 0)
+ parse_error("int _Complex", "_Complex type combination unsupported", 4)
+ parse_error("long double _Complex", "_Complex type combination unsupported",
+ 12)
+
+def test_number_too_large():
+ num_max = sys.maxsize
+ assert parse("char[%d]" % num_max) == [Prim(lib._CFFI_PRIM_CHAR),
+ '->', Array(0), num_max]
+ parse_error("char[%d]" % (num_max + 1), "number too large", 5)
+
+def test_complexity_limit():
+ parse_error("int" + "[]" * 2500, "internal type complexity limit reached",
+ 202)
+
+def test_struct():
+ for i in range(len(struct_names)):
+ if i == 3:
+ tag = "union"
+ else:
+ tag = "struct"
+ assert parse("%s %s" % (tag, struct_names[i])) == ['->', Struct(i)]
+ assert parse("%s %s*" % (tag, struct_names[i])) == [Struct(i),
+ '->', Pointer(0)]
+
+def test_exchanging_struct_union():
+ parse_error("union %s" % (struct_names[0],),
+ "wrong kind of tag: struct vs union", 6)
+ parse_error("struct %s" % (struct_names[3],),
+ "wrong kind of tag: struct vs union", 7)
+
+def test_identifier():
+ for i in range(len(identifier_names)):
+ assert parse("%s" % (identifier_names[i])) == ['->', Typename(i)]
+ assert parse("%s*" % (identifier_names[i])) == [Typename(i),
+ '->', Pointer(0)]
+
+def test_cffi_opcode_sync():
+ import cffi.model
+ for name in dir(lib):
+ if name.startswith('_CFFI_'):
+ assert getattr(cffi_opcode, name[6:]) == getattr(lib, name)
+ assert sorted(cffi_opcode.PRIMITIVE_TO_INDEX.keys()) == (
+ sorted(cffi.model.PrimitiveType.ALL_PRIMITIVE_TYPES.keys()))
+
+def test_array_length_from_constant():
+ parse_error("int[UNKNOWN]", "expected a positive integer constant", 4)
+ assert parse("int[FIVE]") == [Prim(lib._CFFI_PRIM_INT), '->', Array(0), 5]
+ assert parse("int[ZERO]") == [Prim(lib._CFFI_PRIM_INT), '->', Array(0), 0]
+ parse_error("int[NEG]", "expected a positive integer constant", 4)
+
+def test_various_constant_exprs():
+ def array(n):
+ return [Prim(lib._CFFI_PRIM_CHAR), '->', Array(0), n]
+ assert parse("char[21]") == array(21)
+ assert parse("char[0x10]") == array(16)
+ assert parse("char[0X21]") == array(33)
+ assert parse("char[0Xb]") == array(11)
+ assert parse("char[0x1C]") == array(0x1C)
+ assert parse("char[0xc6]") == array(0xC6)
+ assert parse("char[010]") == array(8)
+ assert parse("char[021]") == array(17)
+ parse_error("char[08]", "invalid number", 5)
+ parse_error("char[1C]", "invalid number", 5)
+ parse_error("char[0C]", "invalid number", 5)
+ # not supported (really obscure):
+ # "char[+5]"
+ # "char['A']"
+
+def test_stdcall_cdecl():
+ assert parse("int __stdcall(int)") == [Prim(lib._CFFI_PRIM_INT),
+ '->', Func(0), NoOp(4), FuncEnd(2),
+ Prim(lib._CFFI_PRIM_INT)]
+ assert parse("int __stdcall func(int)") == parse("int __stdcall(int)")
+ assert parse("int (__stdcall *)()") == [Prim(lib._CFFI_PRIM_INT),
+ NoOp(3), '->', Pointer(1),
+ Func(0), FuncEnd(2), 0]
+ assert parse("int (__stdcall *p)()") == parse("int (__stdcall*)()")
+ parse_error("__stdcall int", "identifier expected", 0)
+ parse_error("__cdecl int", "identifier expected", 0)
+ parse_error("int __stdcall", "expected '('", 13)
+ parse_error("int __cdecl", "expected '('", 11)