1import sys, re, os, py 2import cffi 3from cffi import cffi_opcode 4 5if '__pypy__' in sys.builtin_module_names: 6 try: 7 # pytest >= 4.0 8 py.test.skip("not available on pypy", allow_module_level=True) 9 except TypeError: 10 # older pytest 11 py.test.skip("not available on pypy") 12 13cffi_dir = os.path.dirname(cffi_opcode.__file__) 14 15r_macro = re.compile(r"#define \w+[(][^\n]*|#include [^\n]*") 16r_define = re.compile(r"(#define \w+) [^\n]*") 17r_ifdefs = re.compile(r"(#ifdef |#endif)[^\n]*") 18header = open(os.path.join(cffi_dir, 'parse_c_type.h')).read() 19header = r_macro.sub(r"", header) 20header = r_define.sub(r"\1 ...", header) 21header = r_ifdefs.sub(r"", header) 22 23ffi = cffi.FFI() 24ffi.cdef(header) 25 26lib = ffi.verify( 27 open(os.path.join(cffi_dir, '..', 'c', 'parse_c_type.c')).read() + """ 28static const char *get_common_type(const char *search, size_t search_len) { 29 return NULL; 30} 31""", include_dirs=[cffi_dir]) 32 33class ParseError(Exception): 34 pass 35 36struct_names = ["bar_s", "foo", "foo_", "foo_s", "foo_s1", "foo_s12"] 37assert struct_names == sorted(struct_names) 38 39enum_names = ["ebar_s", "efoo", "efoo_", "efoo_s", "efoo_s1", "efoo_s12"] 40assert enum_names == sorted(enum_names) 41 42identifier_names = ["id", "id0", "id05", "id05b", "tail"] 43assert identifier_names == sorted(identifier_names) 44 45global_names = ["FIVE", "NEG", "ZERO"] 46assert global_names == sorted(global_names) 47 48ctx = ffi.new("struct _cffi_type_context_s *") 49c_struct_names = [ffi.new("char[]", _n.encode('ascii')) for _n in struct_names] 50ctx_structs = ffi.new("struct _cffi_struct_union_s[]", len(struct_names)) 51for _i in range(len(struct_names)): 52 ctx_structs[_i].name = c_struct_names[_i] 53ctx_structs[3].flags = lib._CFFI_F_UNION 54ctx.struct_unions = ctx_structs 55ctx.num_struct_unions = len(struct_names) 56 57c_enum_names = [ffi.new("char[]", _n.encode('ascii')) for _n in enum_names] 58ctx_enums = ffi.new("struct _cffi_enum_s[]", len(enum_names)) 59for _i in range(len(enum_names)): 60 ctx_enums[_i].name = c_enum_names[_i] 61ctx.enums = ctx_enums 62ctx.num_enums = len(enum_names) 63 64c_identifier_names = [ffi.new("char[]", _n.encode('ascii')) 65 for _n in identifier_names] 66ctx_identifiers = ffi.new("struct _cffi_typename_s[]", len(identifier_names)) 67for _i in range(len(identifier_names)): 68 ctx_identifiers[_i].name = c_identifier_names[_i] 69 ctx_identifiers[_i].type_index = 100 + _i 70ctx.typenames = ctx_identifiers 71ctx.num_typenames = len(identifier_names) 72 73@ffi.callback("int(unsigned long long *)") 74def fetch_constant_five(p): 75 p[0] = 5 76 return 0 77@ffi.callback("int(unsigned long long *)") 78def fetch_constant_zero(p): 79 p[0] = 0 80 return 1 81@ffi.callback("int(unsigned long long *)") 82def fetch_constant_neg(p): 83 p[0] = 123321 84 return 1 85 86ctx_globals = ffi.new("struct _cffi_global_s[]", len(global_names)) 87c_glob_names = [ffi.new("char[]", _n.encode('ascii')) for _n in global_names] 88for _i, _fn in enumerate([fetch_constant_five, 89 fetch_constant_neg, 90 fetch_constant_zero]): 91 ctx_globals[_i].name = c_glob_names[_i] 92 ctx_globals[_i].address = _fn 93 ctx_globals[_i].type_op = ffi.cast("_cffi_opcode_t", 94 cffi_opcode.OP_CONSTANT_INT if _i != 1 95 else cffi_opcode.OP_ENUM) 96ctx.globals = ctx_globals 97ctx.num_globals = len(global_names) 98 99 100def parse(input): 101 out = ffi.new("_cffi_opcode_t[]", 100) 102 info = ffi.new("struct _cffi_parse_info_s *") 103 info.ctx = ctx 104 info.output = out 105 info.output_size = len(out) 106 for j in range(len(out)): 107 out[j] = ffi.cast("void *", -424242) 108 res = lib.parse_c_type(info, input.encode('ascii')) 109 if res < 0: 110 raise ParseError(ffi.string(info.error_message).decode('ascii'), 111 info.error_location) 112 assert 0 <= res < len(out) 113 result = [] 114 for j in range(len(out)): 115 if out[j] == ffi.cast("void *", -424242): 116 assert res < j 117 break 118 i = int(ffi.cast("intptr_t", out[j])) 119 if j == res: 120 result.append('->') 121 result.append(i) 122 return result 123 124def parsex(input): 125 result = parse(input) 126 def str_if_int(x): 127 if isinstance(x, str): 128 return x 129 return '%d,%d' % (x & 255, x >> 8) 130 return ' '.join(map(str_if_int, result)) 131 132def parse_error(input, expected_msg, expected_location): 133 e = py.test.raises(ParseError, parse, input) 134 assert e.value.args[0] == expected_msg 135 assert e.value.args[1] == expected_location 136 137def make_getter(name): 138 opcode = getattr(lib, '_CFFI_OP_' + name) 139 def getter(value): 140 return opcode | (value << 8) 141 return getter 142 143Prim = make_getter('PRIMITIVE') 144Pointer = make_getter('POINTER') 145Array = make_getter('ARRAY') 146OpenArray = make_getter('OPEN_ARRAY') 147NoOp = make_getter('NOOP') 148Func = make_getter('FUNCTION') 149FuncEnd = make_getter('FUNCTION_END') 150Struct = make_getter('STRUCT_UNION') 151Enum = make_getter('ENUM') 152Typename = make_getter('TYPENAME') 153 154 155def test_simple(): 156 for simple_type, expected in [ 157 ("int", lib._CFFI_PRIM_INT), 158 ("signed int", lib._CFFI_PRIM_INT), 159 (" long ", lib._CFFI_PRIM_LONG), 160 ("long int", lib._CFFI_PRIM_LONG), 161 ("unsigned short", lib._CFFI_PRIM_USHORT), 162 ("long double", lib._CFFI_PRIM_LONGDOUBLE), 163 (" float _Complex", lib._CFFI_PRIM_FLOATCOMPLEX), 164 ("double _Complex ", lib._CFFI_PRIM_DOUBLECOMPLEX), 165 ]: 166 assert parse(simple_type) == ['->', Prim(expected)] 167 168def test_array(): 169 assert parse("int[5]") == [Prim(lib._CFFI_PRIM_INT), '->', Array(0), 5] 170 assert parse("int[]") == [Prim(lib._CFFI_PRIM_INT), '->', OpenArray(0)] 171 assert parse("int[5][8]") == [Prim(lib._CFFI_PRIM_INT), 172 '->', Array(3), 173 5, 174 Array(0), 175 8] 176 assert parse("int[][8]") == [Prim(lib._CFFI_PRIM_INT), 177 '->', OpenArray(2), 178 Array(0), 179 8] 180 181def test_pointer(): 182 assert parse("int*") == [Prim(lib._CFFI_PRIM_INT), '->', Pointer(0)] 183 assert parse("int***") == [Prim(lib._CFFI_PRIM_INT), 184 Pointer(0), Pointer(1), '->', Pointer(2)] 185 186def test_grouping(): 187 assert parse("int*[]") == [Prim(lib._CFFI_PRIM_INT), 188 Pointer(0), '->', OpenArray(1)] 189 assert parse("int**[][8]") == [Prim(lib._CFFI_PRIM_INT), 190 Pointer(0), Pointer(1), 191 '->', OpenArray(4), Array(2), 8] 192 assert parse("int(*)[]") == [Prim(lib._CFFI_PRIM_INT), 193 NoOp(3), '->', Pointer(1), OpenArray(0)] 194 assert parse("int(*)[][8]") == [Prim(lib._CFFI_PRIM_INT), 195 NoOp(3), '->', Pointer(1), 196 OpenArray(4), Array(0), 8] 197 assert parse("int**(**)") == [Prim(lib._CFFI_PRIM_INT), 198 Pointer(0), Pointer(1), 199 NoOp(2), Pointer(3), '->', Pointer(4)] 200 assert parse("int**(**)[]") == [Prim(lib._CFFI_PRIM_INT), 201 Pointer(0), Pointer(1), 202 NoOp(6), Pointer(3), '->', Pointer(4), 203 OpenArray(2)] 204 205def test_simple_function(): 206 assert parse("int()") == [Prim(lib._CFFI_PRIM_INT), 207 '->', Func(0), FuncEnd(0), 0] 208 assert parse("int(int)") == [Prim(lib._CFFI_PRIM_INT), 209 '->', Func(0), NoOp(4), FuncEnd(0), 210 Prim(lib._CFFI_PRIM_INT)] 211 assert parse("int(long, char)") == [ 212 Prim(lib._CFFI_PRIM_INT), 213 '->', Func(0), NoOp(5), NoOp(6), FuncEnd(0), 214 Prim(lib._CFFI_PRIM_LONG), 215 Prim(lib._CFFI_PRIM_CHAR)] 216 assert parse("int(int*)") == [Prim(lib._CFFI_PRIM_INT), 217 '->', Func(0), NoOp(5), FuncEnd(0), 218 Prim(lib._CFFI_PRIM_INT), 219 Pointer(4)] 220 assert parse("int*(void)") == [Prim(lib._CFFI_PRIM_INT), 221 Pointer(0), 222 '->', Func(1), FuncEnd(0), 0] 223 assert parse("int(int, ...)") == [Prim(lib._CFFI_PRIM_INT), 224 '->', Func(0), NoOp(5), FuncEnd(1), 0, 225 Prim(lib._CFFI_PRIM_INT)] 226 227def test_internal_function(): 228 assert parse("int(*)()") == [Prim(lib._CFFI_PRIM_INT), 229 NoOp(3), '->', Pointer(1), 230 Func(0), FuncEnd(0), 0] 231 assert parse("int(*())[]") == [Prim(lib._CFFI_PRIM_INT), 232 NoOp(6), Pointer(1), 233 '->', Func(2), FuncEnd(0), 0, 234 OpenArray(0)] 235 assert parse("int(char(*)(long, short))") == [ 236 Prim(lib._CFFI_PRIM_INT), 237 '->', Func(0), NoOp(6), FuncEnd(0), 238 Prim(lib._CFFI_PRIM_CHAR), 239 NoOp(7), Pointer(5), 240 Func(4), NoOp(11), NoOp(12), FuncEnd(0), 241 Prim(lib._CFFI_PRIM_LONG), 242 Prim(lib._CFFI_PRIM_SHORT)] 243 244def test_fix_arg_types(): 245 assert parse("int(char(long, short))") == [ 246 Prim(lib._CFFI_PRIM_INT), 247 '->', Func(0), Pointer(5), FuncEnd(0), 248 Prim(lib._CFFI_PRIM_CHAR), 249 Func(4), NoOp(9), NoOp(10), FuncEnd(0), 250 Prim(lib._CFFI_PRIM_LONG), 251 Prim(lib._CFFI_PRIM_SHORT)] 252 assert parse("int(char[])") == [ 253 Prim(lib._CFFI_PRIM_INT), 254 '->', Func(0), Pointer(4), FuncEnd(0), 255 Prim(lib._CFFI_PRIM_CHAR), 256 OpenArray(4)] 257 258def test_enum(): 259 for i in range(len(enum_names)): 260 assert parse("enum %s" % (enum_names[i],)) == ['->', Enum(i)] 261 assert parse("enum %s*" % (enum_names[i],)) == [Enum(i), 262 '->', Pointer(0)] 263 264def test_error(): 265 parse_error("short short int", "'short' after another 'short' or 'long'", 6) 266 parse_error("long long long", "'long long long' is too long", 10) 267 parse_error("short long", "'long' after 'short'", 6) 268 parse_error("signed unsigned int", "multiple 'signed' or 'unsigned'", 7) 269 parse_error("unsigned signed int", "multiple 'signed' or 'unsigned'", 9) 270 parse_error("long char", "invalid combination of types", 5) 271 parse_error("short char", "invalid combination of types", 6) 272 parse_error("signed void", "invalid combination of types", 7) 273 parse_error("unsigned struct", "invalid combination of types", 9) 274 # 275 parse_error("", "identifier expected", 0) 276 parse_error("]", "identifier expected", 0) 277 parse_error("*", "identifier expected", 0) 278 parse_error("int ]**", "unexpected symbol", 4) 279 parse_error("char char", "unexpected symbol", 5) 280 parse_error("int(int]", "expected ')'", 7) 281 parse_error("int(*]", "expected ')'", 5) 282 parse_error("int(]", "identifier expected", 4) 283 parse_error("int[?]", "expected a positive integer constant", 4) 284 parse_error("int[24)", "expected ']'", 6) 285 parse_error("struct", "struct or union name expected", 6) 286 parse_error("struct 24", "struct or union name expected", 7) 287 parse_error("int[5](*)", "unexpected symbol", 6) 288 parse_error("int a(*)", "identifier expected", 6) 289 parse_error("int[123456789012345678901234567890]", "number too large", 4) 290 # 291 parse_error("_Complex", "identifier expected", 0) 292 parse_error("int _Complex", "_Complex type combination unsupported", 4) 293 parse_error("long double _Complex", "_Complex type combination unsupported", 294 12) 295 296def test_number_too_large(): 297 num_max = sys.maxsize 298 assert parse("char[%d]" % num_max) == [Prim(lib._CFFI_PRIM_CHAR), 299 '->', Array(0), num_max] 300 parse_error("char[%d]" % (num_max + 1), "number too large", 5) 301 302def test_complexity_limit(): 303 parse_error("int" + "[]" * 2500, "internal type complexity limit reached", 304 202) 305 306def test_struct(): 307 for i in range(len(struct_names)): 308 if i == 3: 309 tag = "union" 310 else: 311 tag = "struct" 312 assert parse("%s %s" % (tag, struct_names[i])) == ['->', Struct(i)] 313 assert parse("%s %s*" % (tag, struct_names[i])) == [Struct(i), 314 '->', Pointer(0)] 315 316def test_exchanging_struct_union(): 317 parse_error("union %s" % (struct_names[0],), 318 "wrong kind of tag: struct vs union", 6) 319 parse_error("struct %s" % (struct_names[3],), 320 "wrong kind of tag: struct vs union", 7) 321 322def test_identifier(): 323 for i in range(len(identifier_names)): 324 assert parse("%s" % (identifier_names[i])) == ['->', Typename(i)] 325 assert parse("%s*" % (identifier_names[i])) == [Typename(i), 326 '->', Pointer(0)] 327 328def test_cffi_opcode_sync(): 329 import cffi.model 330 for name in dir(lib): 331 if name.startswith('_CFFI_'): 332 assert getattr(cffi_opcode, name[6:]) == getattr(lib, name) 333 assert sorted(cffi_opcode.PRIMITIVE_TO_INDEX.keys()) == ( 334 sorted(cffi.model.PrimitiveType.ALL_PRIMITIVE_TYPES.keys())) 335 336def test_array_length_from_constant(): 337 parse_error("int[UNKNOWN]", "expected a positive integer constant", 4) 338 assert parse("int[FIVE]") == [Prim(lib._CFFI_PRIM_INT), '->', Array(0), 5] 339 assert parse("int[ZERO]") == [Prim(lib._CFFI_PRIM_INT), '->', Array(0), 0] 340 parse_error("int[NEG]", "expected a positive integer constant", 4) 341 342def test_various_constant_exprs(): 343 def array(n): 344 return [Prim(lib._CFFI_PRIM_CHAR), '->', Array(0), n] 345 assert parse("char[21]") == array(21) 346 assert parse("char[0x10]") == array(16) 347 assert parse("char[0X21]") == array(33) 348 assert parse("char[0Xb]") == array(11) 349 assert parse("char[0x1C]") == array(0x1C) 350 assert parse("char[0xc6]") == array(0xC6) 351 assert parse("char[010]") == array(8) 352 assert parse("char[021]") == array(17) 353 parse_error("char[08]", "invalid number", 5) 354 parse_error("char[1C]", "invalid number", 5) 355 parse_error("char[0C]", "invalid number", 5) 356 # not supported (really obscure): 357 # "char[+5]" 358 # "char['A']" 359 360def test_stdcall_cdecl(): 361 assert parse("int __stdcall(int)") == [Prim(lib._CFFI_PRIM_INT), 362 '->', Func(0), NoOp(4), FuncEnd(2), 363 Prim(lib._CFFI_PRIM_INT)] 364 assert parse("int __stdcall func(int)") == parse("int __stdcall(int)") 365 assert parse("int (__stdcall *)()") == [Prim(lib._CFFI_PRIM_INT), 366 NoOp(3), '->', Pointer(1), 367 Func(0), FuncEnd(2), 0] 368 assert parse("int (__stdcall *p)()") == parse("int (__stdcall*)()") 369 parse_error("__stdcall int", "identifier expected", 0) 370 parse_error("__cdecl int", "identifier expected", 0) 371 parse_error("int __stdcall", "expected '('", 13) 372 parse_error("int __cdecl", "expected '('", 11) 373