1import py, sys, re 2from cffi import FFI, FFIError, CDefError, VerificationError 3from .backend_tests import needs_dlopen_none 4 5 6class FakeBackend(object): 7 8 def nonstandard_integer_types(self): 9 return {} 10 11 def sizeof(self, name): 12 return 1 13 14 def load_library(self, name, flags): 15 if sys.platform == 'win32': 16 assert name is None or "msvcr" in name 17 else: 18 assert name is None or "libc" in name or "libm" in name 19 return FakeLibrary() 20 21 def new_function_type(self, args, result, has_varargs): 22 args = [arg.cdecl for arg in args] 23 result = result.cdecl 24 return FakeType( 25 '<func (%s), %s, %s>' % (', '.join(args), result, has_varargs)) 26 27 def new_primitive_type(self, name): 28 assert name == name.lower() 29 return FakeType('<%s>' % name) 30 31 def new_pointer_type(self, itemtype): 32 return FakeType('<pointer to %s>' % (itemtype,)) 33 34 def new_struct_type(self, name): 35 return FakeStruct(name) 36 37 def complete_struct_or_union(self, s, fields, tp=None, 38 totalsize=-1, totalalignment=-1, sflags=0): 39 assert isinstance(s, FakeStruct) 40 s.fields = fields 41 42 def new_array_type(self, ptrtype, length): 43 return FakeType('<array %s x %s>' % (ptrtype, length)) 44 45 def new_void_type(self): 46 return FakeType("<void>") 47 def cast(self, x, y): 48 return 'casted!' 49 def _get_types(self): 50 return "CData", "CType" 51 52 buffer = "buffer type" 53 54class FakeType(object): 55 def __init__(self, cdecl): 56 self.cdecl = cdecl 57 def __str__(self): 58 return self.cdecl 59 60class FakeStruct(object): 61 def __init__(self, name): 62 self.name = name 63 def __str__(self): 64 return ', '.join([str(y) + str(x) for x, y, z in self.fields]) 65 66class FakeLibrary(object): 67 68 def load_function(self, BType, name): 69 return FakeFunction(BType, name) 70 71class FakeFunction(object): 72 73 def __init__(self, BType, name): 74 self.BType = str(BType) 75 self.name = name 76 77lib_m = "m" 78if sys.platform == 'win32': 79 #there is a small chance this fails on Mingw via environ $CC 80 import distutils.ccompiler 81 if distutils.ccompiler.get_default_compiler() == 'msvc': 82 lib_m = 'msvcrt' 83 84def test_simple(): 85 ffi = FFI(backend=FakeBackend()) 86 ffi.cdef("double sin(double x);") 87 m = ffi.dlopen(lib_m) 88 func = m.sin # should be a callable on real backends 89 assert func.name == 'sin' 90 assert func.BType == '<func (<double>), <double>, False>' 91 92def test_pipe(): 93 ffi = FFI(backend=FakeBackend()) 94 ffi.cdef("int pipe(int pipefd[2]);") 95 needs_dlopen_none() 96 C = ffi.dlopen(None) 97 func = C.pipe 98 assert func.name == 'pipe' 99 assert func.BType == '<func (<pointer to <int>>), <int>, False>' 100 101def test_vararg(): 102 ffi = FFI(backend=FakeBackend()) 103 ffi.cdef("short foo(int, ...);") 104 needs_dlopen_none() 105 C = ffi.dlopen(None) 106 func = C.foo 107 assert func.name == 'foo' 108 assert func.BType == '<func (<int>), <short>, True>' 109 110def test_no_args(): 111 ffi = FFI(backend=FakeBackend()) 112 ffi.cdef(""" 113 int foo(void); 114 """) 115 needs_dlopen_none() 116 C = ffi.dlopen(None) 117 assert C.foo.BType == '<func (), <int>, False>' 118 119def test_typedef(): 120 ffi = FFI(backend=FakeBackend()) 121 ffi.cdef(""" 122 typedef unsigned int UInt; 123 typedef UInt UIntReally; 124 UInt foo(void); 125 """) 126 needs_dlopen_none() 127 C = ffi.dlopen(None) 128 assert str(ffi.typeof("UIntReally")) == '<unsigned int>' 129 assert C.foo.BType == '<func (), <unsigned int>, False>' 130 131def test_typedef_more_complex(): 132 ffi = FFI(backend=FakeBackend()) 133 ffi.cdef(""" 134 typedef struct { int a, b; } foo_t, *foo_p; 135 int foo(foo_p[]); 136 """) 137 needs_dlopen_none() 138 C = ffi.dlopen(None) 139 assert str(ffi.typeof("foo_t")) == '<int>a, <int>b' 140 assert str(ffi.typeof("foo_p")) == '<pointer to <int>a, <int>b>' 141 assert C.foo.BType == ('<func (<pointer to <pointer to ' 142 '<int>a, <int>b>>), <int>, False>') 143 144def test_typedef_array_convert_array_to_pointer(): 145 ffi = FFI(backend=FakeBackend()) 146 ffi.cdef(""" 147 typedef int (*fn_t)(int[5]); 148 """) 149 with ffi._lock: 150 type = ffi._parser.parse_type("fn_t") 151 BType = ffi._get_cached_btype(type) 152 assert str(BType) == '<func (<pointer to <int>>), <int>, False>' 153 154def test_remove_comments(): 155 ffi = FFI(backend=FakeBackend()) 156 ffi.cdef(""" 157 double /*comment here*/ sin // blah blah 158 /* multi- 159 line- 160 //comment */ ( 161 // foo 162 double // bar /* <- ignored, because it's in a comment itself 163 x, double/*several*//*comment*/y) /*on the same line*/ 164 ; 165 """) 166 m = ffi.dlopen(lib_m) 167 func = m.sin 168 assert func.name == 'sin' 169 assert func.BType == '<func (<double>, <double>), <double>, False>' 170 171def test_remove_line_continuation_comments(): 172 ffi = FFI(backend=FakeBackend()) 173 ffi.cdef(""" 174 double // blah \\ 175 more comments 176 x(void); 177 double // blah // blah\\\\ 178 y(void); 179 double // blah\\ \ 180 etc 181 z(void); 182 """) 183 m = ffi.dlopen(lib_m) 184 m.x 185 m.y 186 m.z 187 188def test_dont_remove_comment_in_line_directives(): 189 ffi = FFI(backend=FakeBackend()) 190 e = py.test.raises(CDefError, ffi.cdef, """ 191 \t # \t line \t 8 \t "baz.c" \t 192 193 some syntax error here 194 """) 195 assert str(e.value) == "parse error\nbaz.c:9:14: before: syntax" 196 # 197 e = py.test.raises(CDefError, ffi.cdef, """ 198 #line 7 "foo//bar.c" 199 200 some syntax error here 201 """) 202 # 203 assert str(e.value) == "parse error\nfoo//bar.c:8:14: before: syntax" 204 ffi = FFI(backend=FakeBackend()) 205 e = py.test.raises(CDefError, ffi.cdef, """ 206 \t # \t 8 \t "baz.c" \t 207 208 some syntax error here 209 """) 210 assert str(e.value) == "parse error\nbaz.c:9:14: before: syntax" 211 # 212 e = py.test.raises(CDefError, ffi.cdef, """ 213 # 7 "foo//bar.c" 214 215 some syntax error here 216 """) 217 assert str(e.value) == "parse error\nfoo//bar.c:8:14: before: syntax" 218 219def test_multiple_line_directives(): 220 ffi = FFI(backend=FakeBackend()) 221 e = py.test.raises(CDefError, ffi.cdef, 222 """ #line 5 "foo.c" 223 extern int xx; 224 #line 6 "bar.c" 225 extern int yy; 226 #line 7 "baz.c" 227 some syntax error here 228 #line 8 "yadda.c" 229 extern int zz; 230 """) 231 assert str(e.value) == "parse error\nbaz.c:7:14: before: syntax" 232 # 233 e = py.test.raises(CDefError, ffi.cdef, 234 """ # 5 "foo.c" 235 extern int xx; 236 # 6 "bar.c" 237 extern int yy; 238 # 7 "baz.c" 239 some syntax error here 240 # 8 "yadda.c" 241 extern int zz; 242 """) 243 assert str(e.value) == "parse error\nbaz.c:7:14: before: syntax" 244 245def test_commented_line_directive(): 246 ffi = FFI(backend=FakeBackend()) 247 e = py.test.raises(CDefError, ffi.cdef, """ 248 /* 249 #line 5 "foo.c" 250 */ 251 void xx(void); 252 253 #line 6 "bar.c" 254 /* 255 #line 35 "foo.c" 256 */ 257 some syntax error 258 """) 259 # 260 assert str(e.value) == "parse error\nbar.c:9:14: before: syntax" 261 e = py.test.raises(CDefError, ffi.cdef, """ 262 /* 263 # 5 "foo.c" 264 */ 265 void xx(void); 266 267 # 6 "bar.c" 268 /* 269 # 35 "foo.c" 270 */ 271 some syntax error 272 """) 273 assert str(e.value) == "parse error\nbar.c:9:14: before: syntax" 274 275def test_line_continuation_in_defines(): 276 ffi = FFI(backend=FakeBackend()) 277 ffi.cdef(""" 278 #define ABC\\ 279 42 280 #define BCD \\ 281 43 282 """) 283 m = ffi.dlopen(lib_m) 284 assert m.ABC == 42 285 assert m.BCD == 43 286 287def test_define_not_supported_for_now(): 288 ffi = FFI(backend=FakeBackend()) 289 e = py.test.raises(CDefError, ffi.cdef, '#define FOO "blah"') 290 assert str(e.value) == ( 291 'only supports one of the following syntax:\n' 292 ' #define FOO ... (literally dot-dot-dot)\n' 293 ' #define FOO NUMBER (with NUMBER an integer' 294 ' constant, decimal/hex/octal)\n' 295 'got:\n' 296 ' #define FOO "blah"') 297 298def test_unnamed_struct(): 299 ffi = FFI(backend=FakeBackend()) 300 ffi.cdef("typedef struct { int x; } foo_t;\n" 301 "typedef struct { int y; } *bar_p;\n") 302 assert 'typedef foo_t' in ffi._parser._declarations 303 assert 'typedef bar_p' in ffi._parser._declarations 304 assert 'anonymous foo_t' in ffi._parser._declarations 305 type_foo = ffi._parser.parse_type("foo_t") 306 type_bar = ffi._parser.parse_type("bar_p").totype 307 assert repr(type_foo) == "<foo_t>" 308 assert repr(type_bar) == "<struct $1>" 309 py.test.raises(VerificationError, type_bar.get_c_name) 310 assert type_foo.get_c_name() == "foo_t" 311 312def test_override(): 313 ffi = FFI(backend=FakeBackend()) 314 needs_dlopen_none() 315 C = ffi.dlopen(None) 316 ffi.cdef("int foo(void);") 317 py.test.raises(FFIError, ffi.cdef, "long foo(void);") 318 assert C.foo.BType == '<func (), <int>, False>' 319 ffi.cdef("long foo(void);", override=True) 320 assert C.foo.BType == '<func (), <long>, False>' 321 322def test_cannot_have_only_variadic_part(): 323 # this checks that we get a sensible error if we try "int foo(...);" 324 ffi = FFI() 325 e = py.test.raises(CDefError, ffi.cdef, "int foo(...);") 326 assert str(e.value) == ( 327 "<cdef source string>:1: foo: a function with only '(...)' " 328 "as argument is not correct C") 329 330def test_parse_error(): 331 ffi = FFI() 332 e = py.test.raises(CDefError, ffi.cdef, " x y z ") 333 assert str(e.value).startswith( 334 'cannot parse "x y z"\n<cdef source string>:1:') 335 e = py.test.raises(CDefError, ffi.cdef, "\n\n\n x y z ") 336 assert str(e.value).startswith( 337 'cannot parse "x y z"\n<cdef source string>:4:') 338 339def test_error_custom_lineno(): 340 ffi = FFI() 341 e = py.test.raises(CDefError, ffi.cdef, """ 342# 42 "foobar" 343 344 a b c d 345 """) 346 assert str(e.value).startswith('parse error\nfoobar:43:') 347 348def test_cannot_declare_enum_later(): 349 ffi = FFI() 350 e = py.test.raises(NotImplementedError, ffi.cdef, 351 "typedef enum foo_e foo_t; enum foo_e { AA, BB };") 352 assert str(e.value) == ( 353 "enum foo_e: the '{}' declaration should appear on the " 354 "first time the enum is mentioned, not later") 355 356def test_unknown_name(): 357 ffi = FFI() 358 e = py.test.raises(CDefError, ffi.cast, "foobarbazunknown", 0) 359 assert str(e.value) == "unknown identifier 'foobarbazunknown'" 360 e = py.test.raises(CDefError, ffi.cast, "foobarbazunknown*", 0) 361 assert str(e.value).startswith('cannot parse "foobarbazunknown*"') 362 e = py.test.raises(CDefError, ffi.cast, "int(*)(foobarbazunknown)", 0) 363 assert str(e.value).startswith('cannot parse "int(*)(foobarbazunknown)"') 364 365def test_redefine_common_type(): 366 prefix = "" if sys.version_info < (3,) else "b" 367 ffi = FFI() 368 ffi.cdef("typedef char FILE;") 369 assert repr(ffi.cast("FILE", 123)) == "<cdata 'char' %s'{'>" % prefix 370 ffi.cdef("typedef char int32_t;") 371 assert repr(ffi.cast("int32_t", 123)) == "<cdata 'char' %s'{'>" % prefix 372 ffi = FFI() 373 ffi.cdef("typedef int bool, *FILE;") 374 assert repr(ffi.cast("bool", 123)) == "<cdata 'int' 123>" 375 assert re.match(r"<cdata 'int [*]' 0[xX]?0*7[bB]>", 376 repr(ffi.cast("FILE", 123))) 377 ffi = FFI() 378 ffi.cdef("typedef bool (*fn_t)(bool, bool);") # "bool," but within "( )" 379 380def test_bool(): 381 ffi = FFI() 382 ffi.cdef("void f(bool);") 383 # 384 ffi = FFI() 385 ffi.cdef("typedef _Bool bool; void f(bool);") 386 387def test_unknown_argument_type(): 388 ffi = FFI() 389 e = py.test.raises(CDefError, ffi.cdef, "void f(foobarbazzz);") 390 assert str(e.value) == ("<cdef source string>:1: f arg 1:" 391 " unknown type 'foobarbazzz' (if you meant" 392 " to use the old C syntax of giving untyped" 393 " arguments, it is not supported)") 394 395def test_void_renamed_as_only_arg(): 396 ffi = FFI() 397 ffi.cdef("typedef void void_t1;" 398 "typedef void_t1 void_t;" 399 "typedef int (*func_t)(void_t);") 400 assert ffi.typeof("func_t").args == () 401 402def test_WPARAM_on_windows(): 403 if sys.platform != 'win32': 404 py.test.skip("Only for Windows") 405 ffi = FFI() 406 ffi.cdef("void f(WPARAM);") 407 # 408 # WPARAM -> UINT_PTR -> unsigned 32/64-bit integer 409 ffi = FFI() 410 value = int(ffi.cast("WPARAM", -42)) 411 assert value == sys.maxsize * 2 - 40 412 413def test__is_constant_globalvar(): 414 import warnings 415 for input, expected_output in [ 416 ("int a;", False), 417 ("const int a;", True), 418 ("int *a;", False), 419 ("const int *a;", False), 420 ("int const *a;", False), 421 ("int *const a;", True), 422 ("int a[5];", False), 423 ("const int a[5];", False), 424 ("int *a[5];", False), 425 ("const int *a[5];", False), 426 ("int const *a[5];", False), 427 ("int *const a[5];", False), 428 ("int a[5][6];", False), 429 ("const int a[5][6];", False), 430 ]: 431 ffi = FFI() 432 with warnings.catch_warnings(record=True) as log: 433 warnings.simplefilter("always") 434 ffi.cdef(input) 435 declarations = ffi._parser._declarations 436 assert ('constant a' in declarations) == expected_output 437 assert ('variable a' in declarations) == (not expected_output) 438 assert len(log) == (1 - expected_output) 439 440def test_restrict(): 441 from cffi import model 442 for input, expected_output in [ 443 ("int a;", False), 444 ("restrict int a;", True), 445 ("int *a;", False), 446 ]: 447 ffi = FFI() 448 ffi.cdef("extern " + input) 449 tp, quals = ffi._parser._declarations['variable a'] 450 assert bool(quals & model.Q_RESTRICT) == expected_output 451 452def test_different_const_funcptr_types(): 453 lst = [] 454 for input in [ 455 "int(*)(int *a)", 456 "int(*)(int const *a)", 457 "int(*)(int * const a)", 458 "int(*)(int const a[])"]: 459 ffi = FFI(backend=FakeBackend()) 460 lst.append(ffi._parser.parse_type(input)) 461 assert lst[0] != lst[1] 462 assert lst[0] == lst[2] 463 assert lst[1] == lst[3] 464 465def test_const_pointer_to_pointer(): 466 from cffi import model 467 ffi = FFI(backend=FakeBackend()) 468 # 469 tp, qual = ffi._parser.parse_type_and_quals("char * * (* const)") 470 assert (str(tp), qual) == ("<char * * *>", model.Q_CONST) 471 tp, qual = ffi._parser.parse_type_and_quals("char * (* const (*))") 472 assert (str(tp), qual) == ("<char * * const *>", 0) 473 tp, qual = ffi._parser.parse_type_and_quals("char (* const (* (*)))") 474 assert (str(tp), qual) == ("<char * const * *>", 0) 475 tp, qual = ffi._parser.parse_type_and_quals("char const * * *") 476 assert (str(tp), qual) == ("<char const * * *>", 0) 477 tp, qual = ffi._parser.parse_type_and_quals("const char * * *") 478 assert (str(tp), qual) == ("<char const * * *>", 0) 479 # 480 tp, qual = ffi._parser.parse_type_and_quals("char * * * const const") 481 assert (str(tp), qual) == ("<char * * *>", model.Q_CONST) 482 tp, qual = ffi._parser.parse_type_and_quals("char * * volatile *") 483 assert (str(tp), qual) == ("<char * * volatile *>", 0) 484 tp, qual = ffi._parser.parse_type_and_quals("char * volatile restrict * *") 485 assert (str(tp), qual) == ("<char * __restrict volatile * *>", 0) 486 tp, qual = ffi._parser.parse_type_and_quals("char const volatile * * *") 487 assert (str(tp), qual) == ("<char volatile const * * *>", 0) 488 tp, qual = ffi._parser.parse_type_and_quals("const char * * *") 489 assert (str(tp), qual) == ("<char const * * *>", 0) 490 # 491 tp, qual = ffi._parser.parse_type_and_quals( 492 "int(char*const*, short****const*)") 493 assert (str(tp), qual) == ( 494 "<int()(char * const *, short * * * * const *)>", 0) 495 tp, qual = ffi._parser.parse_type_and_quals( 496 "char*const*(short*const****)") 497 assert (str(tp), qual) == ( 498 "<char * const *()(short * const * * * *)>", 0) 499 500def test_enum(): 501 ffi = FFI() 502 ffi.cdef(""" 503 enum Enum { 504 POS = +1, 505 TWO = 2, 506 NIL = 0, 507 NEG = -1, 508 ADDSUB = (POS+TWO)-1, 509 DIVMULINT = (3 * 3) / 2, 510 SHIFT = (1 << 3) >> 1, 511 BINOPS = (0x7 & 0x1) | 0x8, 512 XOR = 0xf ^ 0xa 513 }; 514 """) 515 needs_dlopen_none() 516 C = ffi.dlopen(None) 517 assert C.POS == 1 518 assert C.TWO == 2 519 assert C.NIL == 0 520 assert C.NEG == -1 521 assert C.ADDSUB == 2 522 assert C.DIVMULINT == 4 523 assert C.SHIFT == 4 524 assert C.BINOPS == 0b1001 525 assert C.XOR == 0b0101 526 527def test_stdcall(): 528 ffi = FFI() 529 tp = ffi.typeof("int(*)(int __stdcall x(int)," 530 " long (__cdecl*y)(void)," 531 " short(WINAPI *z)(short))") 532 if sys.platform == 'win32' and sys.maxsize < 2**32: 533 stdcall = '__stdcall ' 534 else: 535 stdcall = '' 536 assert str(tp) == ( 537 "<ctype 'int(*)(int(%s*)(int), " 538 "long(*)(), " 539 "short(%s*)(short))'>" % (stdcall, stdcall)) 540 541def test_extern_python(): 542 ffi = FFI() 543 ffi.cdef(""" 544 int bok(int, int); 545 extern "Python" int foobar(int, int); 546 int baz(int, int); 547 """) 548 assert sorted(ffi._parser._declarations) == [ 549 'extern_python foobar', 'function baz', 'function bok'] 550 assert (ffi._parser._declarations['function bok'] == 551 ffi._parser._declarations['extern_python foobar'] == 552 ffi._parser._declarations['function baz']) 553 554def test_extern_python_group(): 555 ffi = FFI() 556 ffi.cdef(""" 557 int bok(int); 558 extern "Python" {int foobar(int, int);int bzrrr(int);} 559 int baz(int, int); 560 """) 561 assert sorted(ffi._parser._declarations) == [ 562 'extern_python bzrrr', 'extern_python foobar', 563 'function baz', 'function bok'] 564 assert (ffi._parser._declarations['function baz'] == 565 ffi._parser._declarations['extern_python foobar'] != 566 ffi._parser._declarations['function bok'] == 567 ffi._parser._declarations['extern_python bzrrr']) 568 569def test_error_invalid_syntax_for_cdef(): 570 ffi = FFI() 571 e = py.test.raises(CDefError, ffi.cdef, 'void foo(void) {}') 572 assert str(e.value) == ('<cdef source string>:1: unexpected <FuncDef>: ' 573 'this construct is valid C but not valid in cdef()') 574 575def test_unsigned_int_suffix_for_constant(): 576 ffi = FFI() 577 ffi.cdef("""enum e { 578 bin_0=0b10, 579 bin_1=0b10u, 580 bin_2=0b10U, 581 bin_3=0b10l, 582 bin_4=0b10L, 583 bin_5=0b10ll, 584 bin_6=0b10LL, 585 oct_0=010, 586 oct_1=010u, 587 oct_2=010U, 588 oct_3=010l, 589 oct_4=010L, 590 oct_5=010ll, 591 oct_6=010LL, 592 dec_0=10, 593 dec_1=10u, 594 dec_2=10U, 595 dec_3=10l, 596 dec_4=10L, 597 dec_5=10ll, 598 dec_6=10LL, 599 hex_0=0x10, 600 hex_1=0x10u, 601 hex_2=0x10U, 602 hex_3=0x10l, 603 hex_4=0x10L, 604 hex_5=0x10ll, 605 hex_6=0x10LL,};""") 606 needs_dlopen_none() 607 C = ffi.dlopen(None) 608 for base, expected_result in (('bin', 2), ('oct', 8), ('dec', 10), ('hex', 16)): 609 for index in range(7): 610 assert getattr(C, '{base}_{index}'.format(base=base, index=index)) == expected_result 611