• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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