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