codekingpro/portable-devtools
114k
1import os, sys, io
2from . import ffiplatform, model
3from .error import VerificationError
4from .cffi_opcode import *
5
6VERSION_BASE = 0x2601
7VERSION_EMBEDDED = 0x2701
8VERSION_CHAR16CHAR32 = 0x2801
9
10USE_LIMITED_API = (sys.platform != 'win32' or sys.version_info < (3, 0) or
11 sys.version_info >= (3, 5))
12
13
14class GlobalExpr:
15 def __init__(self, name, address, type_op, size=0, check_value=0):
16 self.name = name
17 self.address = address
18 self.type_op = type_op
19 self.size = size
20 self.check_value = check_value
21
22 def as_c_expr(self):
23 return ' { "%s", (void *)%s, %s, (void *)%s },' % (
24 self.name, self.address, self.type_op.as_c_expr(), self.size)
25
26 def as_python_expr(self):
27 return "b'%s%s',%d" % (self.type_op.as_python_bytes(), self.name,
28 self.check_value)
29
30class FieldExpr:
31 def __init__(self, name, field_offset, field_size, fbitsize, field_type_op):
32 self.name = name
33 self.field_offset = field_offset
34 self.field_size = field_size
35 self.fbitsize = fbitsize
36 self.field_type_op = field_type_op
37
38 def as_c_expr(self):
39 spaces = " " * len(self.name)
40 return (' { "%s", %s,\n' % (self.name, self.field_offset) +
41 ' %s %s,\n' % (spaces, self.field_size) +
42 ' %s %s },' % (spaces, self.field_type_op.as_c_expr()))
43
44 def as_python_expr(self):
45 raise NotImplementedError
46
47 def as_field_python_expr(self):
48 if self.field_type_op.op == OP_NOOP:
49 size_expr = ''
50 elif self.field_type_op.op == OP_BITFIELD:
51 size_expr = format_four_bytes(self.fbitsize)
52 else:
53 raise NotImplementedError
54 return "b'%s%s%s'" % (self.field_type_op.as_python_bytes(),
55 size_expr,
56 self.name)
57
58class StructUnionExpr:
59 def __init__(self, name, type_index, flags, size, alignment, comment,
60 first_field_index, c_fields):
61 self.name = name
62 self.type_index = type_index
63 self.flags = flags
64 self.size = size
65 self.alignment = alignment
66 self.comment = comment
67 self.first_field_index = first_field_index
68 self.c_fields = c_fields
69
70 def as_c_expr(self):
71 return (' { "%s", %d, %s,' % (self.name, self.type_index, self.flags)
72 + '\n %s, %s, ' % (self.size, self.alignment)
73 + '%d, %d ' % (self.first_field_index, len(self.c_fields))
74 + ('/* %s */ ' % self.comment if self.comment else '')
75 + '},')
76
77 def as_python_expr(self):
78 flags = eval(self.flags, G_FLAGS)
79 fields_expr = [c_field.as_field_python_expr()
80 for c_field in self.c_fields]
81 return "(b'%s%s%s',%s)" % (
82 format_four_bytes(self.type_index),
83 format_four_bytes(flags),
84 self.name,
85 ','.join(fields_expr))
86
87class EnumExpr:
88 def __init__(self, name, type_index, size, signed, allenums):
89 self.name = name
90 self.type_index = type_index
91 self.size = size
92 self.signed = signed
93 self.allenums = allenums
94
95 def as_c_expr(self):
96 return (' { "%s", %d, _cffi_prim_int(%s, %s),\n'
97 ' "%s" },' % (self.name, self.type_index,
98 self.size, self.signed, self.allenums))
99
100 def as_python_expr(self):
101 prim_index = {
102 (1, 0): PRIM_UINT8, (1, 1): PRIM_INT8,
103 (2, 0): PRIM_UINT16, (2, 1): PRIM_INT16,
104 (4, 0): PRIM_UINT32, (4, 1): PRIM_INT32,
105 (8, 0): PRIM_UINT64, (8, 1): PRIM_INT64,
106 }[self.size, self.signed]
107 return "b'%s%s%s\\x00%s'" % (format_four_bytes(self.type_index),
108 format_four_bytes(prim_index),
109 self.name, self.allenums)
110
111class TypenameExpr:
112 def __init__(self, name, type_index):
113 self.name = name
114 self.type_index = type_index
115
116 def as_c_expr(self):
117 return ' { "%s", %d },' % (self.name, self.type_index)
118
119 def as_python_expr(self):
120 return "b'%s%s'" % (format_four_bytes(self.type_index), self.name)
121
122
123# ____________________________________________________________
124
125
126class Recompiler:
127 _num_externpy = 0
128
129 def __init__(self, ffi, module_name, target_is_python=False):
130 self.ffi = ffi
131 self.module_name = module_name
132 self.target_is_python = target_is_python
133 self._version = VERSION_BASE
134
135 def needs_version(self, ver):
136 self._version = max(self._version, ver)
137
138 def collect_type_table(self):
139 self._typesdict = {}
140 self._generate("collecttype")
141 #
142 all_decls = sorted(self._typesdict, key=str)
143 #
144 # prepare all FUNCTION bytecode sequences first
145 self.cffi_types = []
146 for tp in all_decls:
147 if tp.is_raw_function:
148 assert self._typesdict[tp] is None
149 self._typesdict[tp] = len(self.cffi_types)
150 self.cffi_types.append(tp) # placeholder
151 for tp1 in tp.args:
152 assert isinstance(tp1, (model.VoidType,
153 model.BasePrimitiveType,
154 model.PointerType,
155 model.StructOrUnionOrEnum,
156 model.FunctionPtrType))
157 if self._typesdict[tp1] is None:
158 self._typesdict[tp1] = len(self.cffi_types)
159 self.cffi_types.append(tp1) # placeholder
160 self.cffi_types.append('END') # placeholder
161 #
162 # prepare all OTHER bytecode sequences
163 for tp in all_decls:
164 if not tp.is_raw_function and self._typesdict[tp] is None:
165 self._typesdict[tp] = len(self.cffi_types)
166 self.cffi_types.append(tp) # placeholder
167 if tp.is_array_type and tp.length is not None:
168 self.cffi_types.append('LEN') # placeholder
169 assert None not in self._typesdict.values()
170 #
171 # collect all structs and unions and enums
172 self._struct_unions = {}
173 self._enums = {}
174 for tp in all_decls:
175 if isinstance(tp, model.StructOrUnion):
176 self._struct_unions[tp] = None
177 elif isinstance(tp, model.EnumType):
178 self._enums[tp] = None
179 for i, tp in enumerate(sorted(self._struct_unions,
180 key=lambda tp: tp.name)):
181 self._struct_unions[tp] = i
182 for i, tp in enumerate(sorted(self._enums,
183 key=lambda tp: tp.name)):
184 self._enums[tp] = i
185 #
186 # emit all bytecode sequences now
187 for tp in all_decls:
188 method = getattr(self, '_emit_bytecode_' + tp.__class__.__name__)
189 method(tp, self._typesdict[tp])
190 #
191 # consistency check
192 for op in self.cffi_types:
193 assert isinstance(op, CffiOp)
194 self.cffi_types = tuple(self.cffi_types) # don't change any more
195
196 def _enum_fields(self, tp):
197 # When producing C, expand all anonymous struct/union fields.
198 # That's necessary to have C code checking the offsets of the
199 # individual fields contained in them. When producing Python,
200 # don't do it and instead write it like it is, with the
201 # corresponding fields having an empty name. Empty names are
202 # recognized at runtime when we import the generated Python
203 # file.
204 expand_anonymous_struct_union = not self.target_is_python
205 return tp.enumfields(expand_anonymous_struct_union)
206
207 def _do_collect_type(self, tp):
208 if not isinstance(tp, model.BaseTypeByIdentity):
209 if isinstance(tp, tuple):
210 for x in tp:
211 self._do_collect_type(x)
212 return
213 if tp not in self._typesdict:
214 self._typesdict[tp] = None
215 if isinstance(tp, model.FunctionPtrType):
216 self._do_collect_type(tp.as_raw_function())
217 elif isinstance(tp, model.StructOrUnion):
218 if tp.fldtypes is not None and (
219 tp not in self.ffi._parser._included_declarations):
220 for name1, tp1, _, _ in self._enum_fields(tp):
221 self._do_collect_type(self._field_type(tp, name1, tp1))
222 else:
223 for _, x in tp._get_items():
224 self._do_collect_type(x)
225
226 def _generate(self, step_name):
227 lst = self.ffi._parser._declarations.items()
228 for name, (tp, quals) in sorted(lst):
229 kind, realname = name.split(' ', 1)
230 try:
231 method = getattr(self, '_generate_cpy_%s_%s' % (kind,
232 step_name))
233 except AttributeError:
234 raise VerificationError(
235 "not implemented in recompile(): %r" % name)
236 try:
237 self._current_quals = quals
238 method(tp, realname)
239 except Exception as e:
240 model.attach_exception_info(e, name)
241 raise
242
243 # ----------
244
245 ALL_STEPS = ["global", "field", "struct_union", "enum", "typename"]
246
247 def collect_step_tables(self):
248 # collect the declarations for '_cffi_globals', '_cffi_typenames', etc.
249 self._lsts = {}
250 for step_name in self.ALL_STEPS:
251 self._lsts[step_name] = []
252 self._seen_struct_unions = set()
253 self._generate("ctx")
254 self._add_missing_struct_unions()
255 #
256 for step_name in self.ALL_STEPS:
257 lst = self._lsts[step_name]
258 if step_name != "field":
259 lst.sort(key=lambda entry: entry.name)
260 self._lsts[step_name] = tuple(lst) # don't change any more
261 #
262 # check for a possible internal inconsistency: _cffi_struct_unions
263 # should have been generated with exactly self._struct_unions
264 lst = self._lsts["struct_union"]
265 for tp, i in self._struct_unions.items():
266 assert i < len(lst)
267 assert lst[i].name == tp.name
268 assert len(lst) == len(self._struct_unions)
269 # same with enums
270 lst = self._lsts["enum"]
271 for tp, i in self._enums.items():
272 assert i < len(lst)
273 assert lst[i].name == tp.name
274 assert len(lst) == len(self._enums)
275
276 # ----------
277
278 def _prnt(self, what=''):
279 self._f.write(what + '\n')
280
281 def write_source_to_f(self, f, preamble):
282 if self.target_is_python:
283 assert preamble is None
284 self.write_py_source_to_f(f)
285 else:
286 assert preamble is not None
287 self.write_c_source_to_f(f, preamble)
288
289 def _rel_readlines(self, filename):
290 g = open(os.path.join(os.path.dirname(__file__), filename), 'r')
291 lines = g.readlines()
292 g.close()
293 return lines
294
295 def write_c_source_to_f(self, f, preamble):
296 self._f = f
297 prnt = self._prnt
298 if self.ffi._embedding is not None:
299 prnt('#define _CFFI_USE_EMBEDDING')
300 if not USE_LIMITED_API:
301 prnt('#define _CFFI_NO_LIMITED_API')
302 #
303 # first the '#include' (actually done by inlining the file's content)
304 lines = self._rel_readlines('_cffi_include.h')
305 i = lines.index('#include "parse_c_type.h"\n')
306 lines[i:i+1] = self._rel_readlines('parse_c_type.h')
307 prnt(''.join(lines))
308 #
309 # if we have ffi._embedding != None, we give it here as a macro
310 # and include an extra file
311 base_module_name = self.module_name.split('.')[-1]
312 if self.ffi._embedding is not None:
313 prnt('#define _CFFI_MODULE_NAME "%s"' % (self.module_name,))
314 prnt('static const char _CFFI_PYTHON_STARTUP_CODE[] = {')
315 self._print_string_literal_in_array(self.ffi._embedding)
316 prnt('0 };')
317 prnt('#ifdef PYPY_VERSION')
318 prnt('# define _CFFI_PYTHON_STARTUP_FUNC _cffi_pypyinit_%s' % (
319 base_module_name,))
320 prnt('#elif PY_MAJOR_VERSION >= 3')
321 prnt('# define _CFFI_PYTHON_STARTUP_FUNC PyInit_%s' % (
322 base_module_name,))
323 prnt('#else')
324 prnt('# define _CFFI_PYTHON_STARTUP_FUNC init%s' % (
325 base_module_name,))
326 prnt('#endif')
327 lines = self._rel_readlines('_embedding.h')
328 i = lines.index('#include "_cffi_errors.h"\n')
329 lines[i:i+1] = self._rel_readlines('_cffi_errors.h')
330 prnt(''.join(lines))
331 self.needs_version(VERSION_EMBEDDED)
332 #
333 # then paste the C source given by the user, verbatim.
334 prnt('/************************************************************/')
335 prnt()
336 prnt(preamble)
337 prnt()
338 prnt('/************************************************************/')
339 prnt()
340 #
341 # the declaration of '_cffi_types'
342 prnt('static void *_cffi_types[] = {')
343 typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()])
344 for i, op in enumerate(self.cffi_types):
345 comment = ''
346 if i in typeindex2type:
347 comment = ' // ' + typeindex2type[i]._get_c_name()
348 prnt('/* %2d */ %s,%s' % (i, op.as_c_expr(), comment))
349 if not self.cffi_types:
350 prnt(' 0')
351 prnt('};')
352 prnt()
353 #
354 # call generate_cpy_xxx_decl(), for every xxx found from
355 # ffi._parser._declarations. This generates all the functions.
356 self._seen_constants = set()
357 self._generate("decl")
358 #
359 # the declaration of '_cffi_globals' and '_cffi_typenames'
360 nums = {}
361 for step_name in self.ALL_STEPS:
362 lst = self._lsts[step_name]
363 nums[step_name] = len(lst)
364 if nums[step_name] > 0:
365 prnt('static const struct _cffi_%s_s _cffi_%ss[] = {' % (
366 step_name, step_name))
367 for entry in lst:
368 prnt(entry.as_c_expr())
369 prnt('};')
370 prnt()
371 #
372 # the declaration of '_cffi_includes'
373 if self.ffi._included_ffis:
374 prnt('static const char * const _cffi_includes[] = {')
375 for ffi_to_include in self.ffi._included_ffis:
376 try:
377 included_module_name, included_source = (
378 ffi_to_include._assigned_source[:2])
379 except AttributeError:
380 raise VerificationError(
381 "ffi object %r includes %r, but the latter has not "
382 "been prepared with set_source()" % (
383 self.ffi, ffi_to_include,))
384 if included_source is None:
385 raise VerificationError(
386 "not implemented yet: ffi.include() of a Python-based "
387 "ffi inside a C-based ffi")
388 prnt(' "%s",' % (included_module_name,))
389 prnt(' NULL')
390 prnt('};')
391 prnt()
392 #
393 # the declaration of '_cffi_type_context'
394 prnt('static const struct _cffi_type_context_s _cffi_type_context = {')
395 prnt(' _cffi_types,')
396 for step_name in self.ALL_STEPS:
397 if nums[step_name] > 0:
398 prnt(' _cffi_%ss,' % step_name)
399 else:
400 prnt(' NULL, /* no %ss */' % step_name)
401 for step_name in self.ALL_STEPS:
402 if step_name != "field":
403 prnt(' %d, /* num_%ss */' % (nums[step_name], step_name))
404 if self.ffi._included_ffis:
405 prnt(' _cffi_includes,')
406 else:
407 prnt(' NULL, /* no includes */')
408 prnt(' %d, /* num_types */' % (len(self.cffi_types),))
409 flags = 0
410 if self._num_externpy > 0 or self.ffi._embedding is not None:
411 flags |= 1 # set to mean that we use extern "Python"
412 prnt(' %d, /* flags */' % flags)
413 prnt('};')
414 prnt()
415 #
416 # the init function
417 prnt('#ifdef __GNUC__')
418 prnt('# pragma GCC visibility push(default) /* for -fvisibility= */')
419 prnt('#endif')
420 prnt()
421 prnt('#ifdef PYPY_VERSION')
422 prnt('PyMODINIT_FUNC')
423 prnt('_cffi_pypyinit_%s(const void *p[])' % (base_module_name,))
424 prnt('{')
425 if flags & 1:
426 prnt(' if (((intptr_t)p[0]) >= 0x0A03) {')
427 prnt(' _cffi_call_python_org = '
428 '(void(*)(struct _cffi_externpy_s *, char *))p[1];')
429 prnt(' }')
430 prnt(' p[0] = (const void *)0x%x;' % self._version)
431 prnt(' p[1] = &_cffi_type_context;')
432 prnt('#if PY_MAJOR_VERSION >= 3')
433 prnt(' return NULL;')
434 prnt('#endif')
435 prnt('}')
436 # on Windows, distutils insists on putting init_cffi_xyz in
437 # 'export_symbols', so instead of fighting it, just give up and
438 # give it one
439 prnt('# ifdef _MSC_VER')
440 prnt(' PyMODINIT_FUNC')
441 prnt('# if PY_MAJOR_VERSION >= 3')
442 prnt(' PyInit_%s(void) { return NULL; }' % (base_module_name,))
443 prnt('# else')
444 prnt(' init%s(void) { }' % (base_module_name,))
445 prnt('# endif')
446 prnt('# endif')
447 prnt('#elif PY_MAJOR_VERSION >= 3')
448 prnt('PyMODINIT_FUNC')
449 prnt('PyInit_%s(void)' % (base_module_name,))
450 prnt('{')
451 prnt(' return _cffi_init("%s", 0x%x, &_cffi_type_context);' % (
452 self.module_name, self._version))
453 prnt('}')
454 prnt('#else')
455 prnt('PyMODINIT_FUNC')
456 prnt('init%s(void)' % (base_module_name,))
457 prnt('{')
458 prnt(' _cffi_init("%s", 0x%x, &_cffi_type_context);' % (
459 self.module_name, self._version))
460 prnt('}')
461 prnt('#endif')
462 prnt()
463 prnt('#ifdef __GNUC__')
464 prnt('# pragma GCC visibility pop')
465 prnt('#endif')
466 self._version = None
467
468 def _to_py(self, x):
469 if isinstance(x, str):
470 return "b'%s'" % (x,)
471 if isinstance(x, (list, tuple)):
472 rep = [self._to_py(item) for item in x]
473 if len(rep) == 1:
474 rep.append('')
475 return "(%s)" % (','.join(rep),)
476 return x.as_python_expr() # Py2: unicode unexpected; Py3: bytes unexp.
477
478 def write_py_source_to_f(self, f):
479 self._f = f
480 prnt = self._prnt
481 #
482 # header
483 prnt("# auto-generated file")
484 prnt("import _cffi_backend")
485 #
486 # the 'import' of the included ffis
487 num_includes = len(self.ffi._included_ffis or ())
488 for i in range(num_includes):
489 ffi_to_include = self.ffi._included_ffis[i]
490 try:
491 included_module_name, included_source = (
492 ffi_to_include._assigned_source[:2])
493 except AttributeError:
494 raise VerificationError(
495 "ffi object %r includes %r, but the latter has not "
496 "been prepared with set_source()" % (
497 self.ffi, ffi_to_include,))
498 if included_source is not None:
499 raise VerificationError(
500 "not implemented yet: ffi.include() of a C-based "
501 "ffi inside a Python-based ffi")
502 prnt('from %s import ffi as _ffi%d' % (included_module_name, i))
503 prnt()
504 prnt("ffi = _cffi_backend.FFI('%s'," % (self.module_name,))
505 prnt(" _version = 0x%x," % (self._version,))
506 self._version = None
507 #
508 # the '_types' keyword argument
509 self.cffi_types = tuple(self.cffi_types) # don't change any more
510 types_lst = [op.as_python_bytes() for op in self.cffi_types]
511 prnt(' _types = %s,' % (self._to_py(''.join(types_lst)),))
512 typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()])
513 #
514 # the keyword arguments from ALL_STEPS
515 for step_name in self.ALL_STEPS:
516 lst = self._lsts[step_name]
517 if len(lst) > 0 and step_name != "field":
518 prnt(' _%ss = %s,' % (step_name, self._to_py(lst)))
519 #
520 # the '_includes' keyword argument
521 if num_includes > 0:
522 prnt(' _includes = (%s,),' % (
523 ', '.join(['_ffi%d' % i for i in range(num_includes)]),))
524 #
525 # the footer
526 prnt(')')
527
528 # ----------
529
530 def _gettypenum(self, type):
531 # a KeyError here is a bug. please report it! :-)
532 return self._typesdict[type]
533
534 def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode):
535 extraarg = ''
536 if isinstance(tp, model.BasePrimitiveType) and not tp.is_complex_type():
537 if tp.is_integer_type() and tp.name != '_Bool':
538 converter = '_cffi_to_c_int'
539 extraarg = ', %s' % tp.name
540 elif isinstance(tp, model.UnknownFloatType):
541 # don't check with is_float_type(): it may be a 'long
542 # double' here, and _cffi_to_c_double would loose precision
543 converter = '(%s)_cffi_to_c_double' % (tp.get_c_name(''),)
544 else:
545 cname = tp.get_c_name('')
546 converter = '(%s)_cffi_to_c_%s' % (cname,
547 tp.name.replace(' ', '_'))
548 if cname in ('char16_t', 'char32_t'):
549 self.needs_version(VERSION_CHAR16CHAR32)
550 errvalue = '-1'
551 #
552 elif isinstance(tp, model.PointerType):
553 self._convert_funcarg_to_c_ptr_or_array(tp, fromvar,
554 tovar, errcode)
555 return
556 #
557 elif (isinstance(tp, model.StructOrUnionOrEnum) or
558 isinstance(tp, model.BasePrimitiveType)):
559 # a struct (not a struct pointer) as a function argument;
560 # or, a complex (the same code works)
561 self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)'
562 % (tovar, self._gettypenum(tp), fromvar))
563 self._prnt(' %s;' % errcode)
564 return
565 #
566 elif isinstance(tp, model.FunctionPtrType):
567 converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('')
568 extraarg = ', _cffi_type(%d)' % self._gettypenum(tp)
569 errvalue = 'NULL'
570 #
571 else:
572 raise NotImplementedError(tp)
573 #
574 self._prnt(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg))
575 self._prnt(' if (%s == (%s)%s && PyErr_Occurred())' % (
576 tovar, tp.get_c_name(''), errvalue))
577 self._prnt(' %s;' % errcode)
578
579 def _extra_local_variables(self, tp, localvars, freelines):
580 if isinstance(tp, model.PointerType):
581 localvars.add('Py_ssize_t datasize')
582 localvars.add('struct _cffi_freeme_s *large_args_free = NULL')
583 freelines.add('if (large_args_free != NULL)'
584 ' _cffi_free_array_arguments(large_args_free);')
585
586 def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode):
587 self._prnt(' datasize = _cffi_prepare_pointer_call_argument(')
588 self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % (
589 self._gettypenum(tp), fromvar, tovar))
590 self._prnt(' if (datasize != 0) {')
591 self._prnt(' %s = ((size_t)datasize) <= 640 ? '
592 '(%s)alloca((size_t)datasize) : NULL;' % (
593 tovar, tp.get_c_name('')))
594 self._prnt(' if (_cffi_convert_array_argument(_cffi_type(%d), %s, '
595 '(char **)&%s,' % (self._gettypenum(tp), fromvar, tovar))
596 self._prnt(' datasize, &large_args_free) < 0)')
597 self._prnt(' %s;' % errcode)
598 self._prnt(' }')
599
600 def _convert_expr_from_c(self, tp, var, context):
601 if isinstance(tp, model.BasePrimitiveType):
602 if tp.is_integer_type() and tp.name != '_Bool':
603 return '_cffi_from_c_int(%s, %s)' % (var, tp.name)
604 elif isinstance(tp, model.UnknownFloatType):
605 return '_cffi_from_c_double(%s)' % (var,)
606 elif tp.name != 'long double' and not tp.is_complex_type():
607 cname = tp.name.replace(' ', '_')
608 if cname in ('char16_t', 'char32_t'):
609 self.needs_version(VERSION_CHAR16CHAR32)
610 return '_cffi_from_c_%s(%s)' % (cname, var)
611 else:
612 return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
613 var, self._gettypenum(tp))
614 elif isinstance(tp, (model.PointerType, model.FunctionPtrType)):
615 return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
616 var, self._gettypenum(tp))
617 elif isinstance(tp, model.ArrayType):
618 return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % (
619 var, self._gettypenum(model.PointerType(tp.item)))
620 elif isinstance(tp, model.StructOrUnion):
621 if tp.fldnames is None:
622 raise TypeError("'%s' is used as %s, but is opaque" % (
623 tp._get_c_name(), context))
624 return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % (
625 var, self._gettypenum(tp))
626 elif isinstance(tp, model.EnumType):
627 return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % (
628 var, self._gettypenum(tp))
629 else:
630 raise NotImplementedError(tp)
631
632 # ----------
633 # typedefs
634
635 def _typedef_type(self, tp, name):
636 return self._global_type(tp, "(*(%s *)0)" % (name,))
637
638 def _generate_cpy_typedef_collecttype(self, tp, name):
639 self._do_collect_type(self._typedef_type(tp, name))
640
641 def _generate_cpy_typedef_decl(self, tp, name):
642 pass
643
644 def _typedef_ctx(self, tp, name):
645 type_index = self._typesdict[tp]
646 self._lsts["typename"].append(TypenameExpr(name, type_index))
647
648 def _generate_cpy_typedef_ctx(self, tp, name):
649 tp = self._typedef_type(tp, name)
650 self._typedef_ctx(tp, name)
651 if getattr(tp, "origin", None) == "unknown_type":
652 self._struct_ctx(tp, tp.name, approxname=None)
653 elif isinstance(tp, model.NamedPointerType):
654 self._struct_ctx(tp.totype, tp.totype.name, approxname=tp.name,
655 named_ptr=tp)
656
657 # ----------
658 # function declarations
659
660 def _generate_cpy_function_collecttype(self, tp, name):
661 self._do_collect_type(tp.as_raw_function())
662 if tp.ellipsis and not self.target_is_python:
663 self._do_collect_type(tp)
664
665 def _generate_cpy_function_decl(self, tp, name):
666 assert not self.target_is_python
667 assert isinstance(tp, model.FunctionPtrType)
668 if tp.ellipsis:
669 # cannot support vararg functions better than this: check for its
670 # exact type (including the fixed arguments), and build it as a
671 # constant function pointer (no CPython wrapper)
672 self._generate_cpy_constant_decl(tp, name)
673 return
674 prnt = self._prnt
675 numargs = len(tp.args)
676 if numargs == 0:
677 argname = 'noarg'
678 elif numargs == 1:
679 argname = 'arg0'
680 else:
681 argname = 'args'
682 #
683 # ------------------------------
684 # the 'd' version of the function, only for addressof(lib, 'func')
685 arguments = []
686 call_arguments = []
687 context = 'argument of %s' % name
688 for i, type in enumerate(tp.args):
689 arguments.append(type.get_c_name(' x%d' % i, context))
690 call_arguments.append('x%d' % i)
691 repr_arguments = ', '.join(arguments)
692 repr_arguments = repr_arguments or 'void'
693 if tp.abi:
694 abi = tp.abi + ' '
695 else:
696 abi = ''
697 name_and_arguments = '%s_cffi_d_%s(%s)' % (abi, name, repr_arguments)
698 prnt('static %s' % (tp.result.get_c_name(name_and_arguments),))
699 prnt('{')
700 call_arguments = ', '.join(call_arguments)
701 result_code = 'return '
702 if isinstance(tp.result, model.VoidType):
703 result_code = ''
704 prnt(' %s%s(%s);' % (result_code, name, call_arguments))
705 prnt('}')
706 #
707 prnt('#ifndef PYPY_VERSION') # ------------------------------
708 #
709 prnt('static PyObject *')
710 prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname))
711 prnt('{')
712 #
713 context = 'argument of %s' % name
714 for i, type in enumerate(tp.args):
715 arg = type.get_c_name(' x%d' % i, context)
716 prnt(' %s;' % arg)
717 #
718 localvars = set()
719 freelines = set()
720 for type in tp.args:
721 self._extra_local_variables(type, localvars, freelines)
722 for decl in sorted(localvars):
723 prnt(' %s;' % (decl,))
724 #
725 if not isinstance(tp.result, model.VoidType):
726 result_code = 'result = '
727 context = 'result of %s' % name
728 result_decl = ' %s;' % tp.result.get_c_name(' result', context)
729 prnt(result_decl)
730 prnt(' PyObject *pyresult;')
731 else:
732 result_decl = None
733 result_code = ''
734 #
735 if len(tp.args) > 1:
736 rng = range(len(tp.args))
737 for i in rng:
738 prnt(' PyObject *arg%d;' % i)
739 prnt()
740 prnt(' if (!PyArg_UnpackTuple(args, "%s", %d, %d, %s))' % (
741 name, len(rng), len(rng),
742 ', '.join(['&arg%d' % i for i in rng])))
743 prnt(' return NULL;')
744 prnt()
745 #
746 for i, type in enumerate(tp.args):
747 self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i,
748 'return NULL')
749 prnt()
750 #
751 prnt(' Py_BEGIN_ALLOW_THREADS')
752 prnt(' _cffi_restore_errno();')
753 call_arguments = ['x%d' % i for i in range(len(tp.args))]
754 call_arguments = ', '.join(call_arguments)
755 prnt(' { %s%s(%s); }' % (result_code, name, call_arguments))
756 prnt(' _cffi_save_errno();')
757 prnt(' Py_END_ALLOW_THREADS')
758 prnt()
759 #
760 prnt(' (void)self; /* unused */')
761 if numargs == 0:
762 prnt(' (void)noarg; /* unused */')
763 if result_code:
764 prnt(' pyresult = %s;' %
765 self._convert_expr_from_c(tp.result, 'result', 'result type'))
766 for freeline in freelines:
767 prnt(' ' + freeline)
768 prnt(' return pyresult;')
769 else:
770 for freeline in freelines:
771 prnt(' ' + freeline)
772 prnt(' Py_INCREF(Py_None);')
773 prnt(' return Py_None;')
774 prnt('}')
775 #
776 prnt('#else') # ------------------------------
777 #
778 # the PyPy version: need to replace struct/union arguments with
779 # pointers, and if the result is a struct/union, insert a first
780 # arg that is a pointer to the result. We also do that for
781 # complex args and return type.
782 def need_indirection(type):
783 return (isinstance(type, model.StructOrUnion) or
784 (isinstance(type, model.PrimitiveType) and
785 type.is_complex_type()))
786 difference = False
787 arguments = []
788 call_arguments = []
789 context = 'argument of %s' % name
790 for i, type in enumerate(tp.args):
791 indirection = ''
792 if need_indirection(type):
793 indirection = '*'
794 difference = True
795 arg = type.get_c_name(' %sx%d' % (indirection, i), context)
796 arguments.append(arg)
797 call_arguments.append('%sx%d' % (indirection, i))
798 tp_result = tp.result
799 if need_indirection(tp_result):
800 context = 'result of %s' % name
801 arg = tp_result.get_c_name(' *result', context)
802 arguments.insert(0, arg)
803 tp_result = model.void_type
804 result_decl = None
805 result_code = '*result = '
806 difference = True
807 if difference:
808 repr_arguments = ', '.join(arguments)
809 repr_arguments = repr_arguments or 'void'
810 name_and_arguments = '%s_cffi_f_%s(%s)' % (abi, name,
811 repr_arguments)
812 prnt('static %s' % (tp_result.get_c_name(name_and_arguments),))
813 prnt('{')
814 if result_decl:
815 prnt(result_decl)
816 call_arguments = ', '.join(call_arguments)
817 prnt(' { %s%s(%s); }' % (result_code, name, call_arguments))
818 if result_decl:
819 prnt(' return result;')
820 prnt('}')
821 else:
822 prnt('# define _cffi_f_%s _cffi_d_%s' % (name, name))
823 #
824 prnt('#endif') # ------------------------------
825 prnt()
826
827 def _generate_cpy_function_ctx(self, tp, name):
828 if tp.ellipsis and not self.target_is_python:
829 self._generate_cpy_constant_ctx(tp, name)
830 return
831 type_index = self._typesdict[tp.as_raw_function()]
832 numargs = len(tp.args)
833 if self.target_is_python:
834 meth_kind = OP_DLOPEN_FUNC
835 elif numargs == 0:
836 meth_kind = OP_CPYTHON_BLTN_N # 'METH_NOARGS'
837 elif numargs == 1:
838 meth_kind = OP_CPYTHON_BLTN_O # 'METH_O'
839 else:
840 meth_kind = OP_CPYTHON_BLTN_V # 'METH_VARARGS'
841 self._lsts["global"].append(
842 GlobalExpr(name, '_cffi_f_%s' % name,
843 CffiOp(meth_kind, type_index),
844 size='_cffi_d_%s' % name))
845
846 # ----------
847 # named structs or unions
848
849 def _field_type(self, tp_struct, field_name, tp_field):
850 if isinstance(tp_field, model.ArrayType):
851 actual_length = tp_field.length
852 if actual_length == '...':
853 ptr_struct_name = tp_struct.get_c_name('*')
854 actual_length = '_cffi_array_len(((%s)0)->%s)' % (
855 ptr_struct_name, field_name)
856 tp_item = self._field_type(tp_struct, '%s[0]' % field_name,
857 tp_field.item)
858 tp_field = model.ArrayType(tp_item, actual_length)
859 return tp_field
860
861 def _struct_collecttype(self, tp):
862 self._do_collect_type(tp)
863 if self.target_is_python:
864 # also requires nested anon struct/unions in ABI mode, recursively
865 for fldtype in tp.anonymous_struct_fields():
866 self._struct_collecttype(fldtype)
867
868 def _struct_decl(self, tp, cname, approxname):
869 if tp.fldtypes is None:
870 return
871 prnt = self._prnt
872 checkfuncname = '_cffi_checkfld_%s' % (approxname,)
873 prnt('_CFFI_UNUSED_FN')
874 prnt('static void %s(%s *p)' % (checkfuncname, cname))
875 prnt('{')
876 prnt(' /* only to generate compile-time warnings or errors */')
877 prnt(' (void)p;')
878 for fname, ftype, fbitsize, fqual in self._enum_fields(tp):
879 try:
880 if ftype.is_integer_type() or fbitsize >= 0:
881 # accept all integers, but complain on float or double
882 if fname != '':
883 prnt(" (void)((p->%s) | 0); /* check that '%s.%s' is "
884 "an integer */" % (fname, cname, fname))
885 continue
886 # only accept exactly the type declared, except that '[]'
887 # is interpreted as a '*' and so will match any array length.
888 # (It would also match '*', but that's harder to detect...)
889 while (isinstance(ftype, model.ArrayType)
890 and (ftype.length is None or ftype.length == '...')):
891 ftype = ftype.item
892 fname = fname + '[0]'
893 prnt(' { %s = &p->%s; (void)tmp; }' % (
894 ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual),
895 fname))
896 except VerificationError as e:
897 prnt(' /* %s */' % str(e)) # cannot verify it, ignore
898 prnt('}')
899 prnt('struct _cffi_align_%s { char x; %s y; };' % (approxname, cname))
900 prnt()
901
902 def _struct_ctx(self, tp, cname, approxname, named_ptr=None):
903 type_index = self._typesdict[tp]
904 reason_for_not_expanding = None
905 flags = []
906 if isinstance(tp, model.UnionType):
907 flags.append("_CFFI_F_UNION")
908 if tp.fldtypes is None:
909 flags.append("_CFFI_F_OPAQUE")
910 reason_for_not_expanding = "opaque"
911 if (tp not in self.ffi._parser._included_declarations and
912 (named_ptr is None or
913 named_ptr not in self.ffi._parser._included_declarations)):
914 if tp.fldtypes is None:
915 pass # opaque
916 elif tp.partial or any(tp.anonymous_struct_fields()):
917 pass # field layout obtained silently from the C compiler
918 else:
919 flags.append("_CFFI_F_CHECK_FIELDS")
920 if tp.packed:
921 if tp.packed > 1:
922 raise NotImplementedError(
923 "%r is declared with 'pack=%r'; only 0 or 1 are "
924 "supported in API mode (try to use \"...;\", which "
925 "does not require a 'pack' declaration)" %
926 (tp, tp.packed))
927 flags.append("_CFFI_F_PACKED")
928 else:
929 flags.append("_CFFI_F_EXTERNAL")
930 reason_for_not_expanding = "external"
931 flags = '|'.join(flags) or '0'
932 c_fields = []
933 if reason_for_not_expanding is None:
934 enumfields = list(self._enum_fields(tp))
935 for fldname, fldtype, fbitsize, fqual in enumfields:
936 fldtype = self._field_type(tp, fldname, fldtype)
937 self._check_not_opaque(fldtype,
938 "field '%s.%s'" % (tp.name, fldname))
939 # cname is None for _add_missing_struct_unions() only
940 op = OP_NOOP
941 if fbitsize >= 0:
942 op = OP_BITFIELD
943 size = '%d /* bits */' % fbitsize
944 elif cname is None or (
945 isinstance(fldtype, model.ArrayType) and
946 fldtype.length is None):
947 size = '(size_t)-1'
948 else:
949 size = 'sizeof(((%s)0)->%s)' % (
950 tp.get_c_name('*') if named_ptr is None
951 else named_ptr.name,
952 fldname)
953 if cname is None or fbitsize >= 0:
954 offset = '(size_t)-1'
955 elif named_ptr is not None:
956 offset = '((char *)&((%s)0)->%s) - (char *)0' % (
957 named_ptr.name, fldname)
958 else:
959 offset = 'offsetof(%s, %s)' % (tp.get_c_name(''), fldname)
960 c_fields.append(
961 FieldExpr(fldname, offset, size, fbitsize,
962 CffiOp(op, self._typesdict[fldtype])))
963 first_field_index = len(self._lsts["field"])
964 self._lsts["field"].extend(c_fields)
965 #
966 if cname is None: # unknown name, for _add_missing_struct_unions
967 size = '(size_t)-2'
968 align = -2
969 comment = "unnamed"
970 else:
971 if named_ptr is not None:
972 size = 'sizeof(*(%s)0)' % (named_ptr.name,)
973 align = '-1 /* unknown alignment */'
974 else:
975 size = 'sizeof(%s)' % (cname,)
976 align = 'offsetof(struct _cffi_align_%s, y)' % (approxname,)
977 comment = None
978 else:
979 size = '(size_t)-1'
980 align = -1
981 first_field_index = -1
982 comment = reason_for_not_expanding
983 self._lsts["struct_union"].append(
984 StructUnionExpr(tp.name, type_index, flags, size, align, comment,
985 first_field_index, c_fields))
986 self._seen_struct_unions.add(tp)
987
988 def _check_not_opaque(self, tp, location):
989 while isinstance(tp, model.ArrayType):
990 tp = tp.item
991 if isinstance(tp, model.StructOrUnion) and tp.fldtypes is None:
992 raise TypeError(
993 "%s is of an opaque type (not declared in cdef())" % location)
994
995 def _add_missing_struct_unions(self):
996 # not very nice, but some struct declarations might be missing
997 # because they don't have any known C name. Check that they are
998 # not partial (we can't complete or verify them!) and emit them
999 # anonymously.
1000 lst = list(self._struct_unions.items())
1001 lst.sort(key=lambda tp_order: tp_order[1])
1002 for tp, order in lst:
1003 if tp not in self._seen_struct_unions:
1004 if tp.partial:
1005 raise NotImplementedError("internal inconsistency: %r is "
1006 "partial but was not seen at "
1007 "this point" % (tp,))
1008 if tp.name.startswith('$') and tp.name[1:].isdigit():
1009 approxname = tp.name[1:]
1010 elif tp.name == '_IO_FILE' and tp.forcename == 'FILE':
1011 approxname = 'FILE'
1012 self._typedef_ctx(tp, 'FILE')
1013 else:
1014 raise NotImplementedError("internal inconsistency: %r" %
1015 (tp,))
1016 self._struct_ctx(tp, None, approxname)
1017
1018 def _generate_cpy_struct_collecttype(self, tp, name):
1019 self._struct_collecttype(tp)
1020 _generate_cpy_union_collecttype = _generate_cpy_struct_collecttype
1021
1022 def _struct_names(self, tp):
1023 cname = tp.get_c_name('')
1024 if ' ' in cname:
1025 return cname, cname.replace(' ', '_')
1026 else:
1027 return cname, '_' + cname
1028
1029 def _generate_cpy_struct_decl(self, tp, name):
1030 self._struct_decl(tp, *self._struct_names(tp))
1031 _generate_cpy_union_decl = _generate_cpy_struct_decl
1032
1033 def _generate_cpy_struct_ctx(self, tp, name):
1034 self._struct_ctx(tp, *self._struct_names(tp))
1035 _generate_cpy_union_ctx = _generate_cpy_struct_ctx
1036
1037 # ----------
1038 # 'anonymous' declarations. These are produced for anonymous structs
1039 # or unions; the 'name' is obtained by a typedef.
1040
1041 def _generate_cpy_anonymous_collecttype(self, tp, name):
1042 if isinstance(tp, model.EnumType):
1043 self._generate_cpy_enum_collecttype(tp, name)
1044 else:
1045 self._struct_collecttype(tp)
1046
1047 def _generate_cpy_anonymous_decl(self, tp, name):
1048 if isinstance(tp, model.EnumType):
1049 self._generate_cpy_enum_decl(tp)
1050 else:
1051 self._struct_decl(tp, name, 'typedef_' + name)
1052
1053 def _generate_cpy_anonymous_ctx(self, tp, name):
1054 if isinstance(tp, model.EnumType):
1055 self._enum_ctx(tp, name)
1056 else:
1057 self._struct_ctx(tp, name, 'typedef_' + name)
1058
1059 # ----------
1060 # constants, declared with "static const ..."
1061
1062 def _generate_cpy_const(self, is_int, name, tp=None, category='const',
1063 check_value=None):
1064 if (category, name) in self._seen_constants:
1065 raise VerificationError(
1066 "duplicate declaration of %s '%s'" % (category, name))
1067 self._seen_constants.add((category, name))
1068 #
1069 prnt = self._prnt
1070 funcname = '_cffi_%s_%s' % (category, name)
1071 if is_int:
1072 prnt('static int %s(unsigned long long *o)' % funcname)
1073 prnt('{')
1074 prnt(' int n = (%s) <= 0;' % (name,))
1075 prnt(' *o = (unsigned long long)((%s) | 0);'
1076 ' /* check that %s is an integer */' % (name, name))
1077 if check_value is not None:
1078 if check_value > 0:
1079 check_value = '%dU' % (check_value,)
1080 prnt(' if (!_cffi_check_int(*o, n, %s))' % (check_value,))
1081 prnt(' n |= 2;')
1082 prnt(' return n;')
1083 prnt('}')
1084 else:
1085 assert check_value is None
1086 prnt('static void %s(char *o)' % funcname)
1087 prnt('{')
1088 prnt(' *(%s)o = %s;' % (tp.get_c_name('*'), name))
1089 prnt('}')
1090 prnt()
1091
1092 def _generate_cpy_constant_collecttype(self, tp, name):
1093 is_int = tp.is_integer_type()
1094 if not is_int or self.target_is_python:
1095 self._do_collect_type(tp)
1096
1097 def _generate_cpy_constant_decl(self, tp, name):
1098 is_int = tp.is_integer_type()
1099 self._generate_cpy_const(is_int, name, tp)
1100
1101 def _generate_cpy_constant_ctx(self, tp, name):
1102 if not self.target_is_python and tp.is_integer_type():
1103 type_op = CffiOp(OP_CONSTANT_INT, -1)
1104 else:
1105 if self.target_is_python:
1106 const_kind = OP_DLOPEN_CONST
1107 else:
1108 const_kind = OP_CONSTANT
1109 type_index = self._typesdict[tp]
1110 type_op = CffiOp(const_kind, type_index)
1111 self._lsts["global"].append(
1112 GlobalExpr(name, '_cffi_const_%s' % name, type_op))
1113
1114 # ----------
1115 # enums
1116
1117 def _generate_cpy_enum_collecttype(self, tp, name):
1118 self._do_collect_type(tp)
1119
1120 def _generate_cpy_enum_decl(self, tp, name=None):
1121 for enumerator in tp.enumerators:
1122 self._generate_cpy_const(True, enumerator)
1123
1124 def _enum_ctx(self, tp, cname):
1125 type_index = self._typesdict[tp]
1126 type_op = CffiOp(OP_ENUM, -1)
1127 if self.target_is_python:
1128 tp.check_not_partial()
1129 for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues):
1130 self._lsts["global"].append(
1131 GlobalExpr(enumerator, '_cffi_const_%s' % enumerator, type_op,
1132 check_value=enumvalue))
1133 #
1134 if cname is not None and '$' not in cname and not self.target_is_python:
1135 size = "sizeof(%s)" % cname
1136 signed = "((%s)-1) <= 0" % cname
1137 else:
1138 basetp = tp.build_baseinttype(self.ffi, [])
1139 size = self.ffi.sizeof(basetp)
1140 signed = int(int(self.ffi.cast(basetp, -1)) < 0)
1141 allenums = ",".join(tp.enumerators)
1142 self._lsts["enum"].append(
1143 EnumExpr(tp.name, type_index, size, signed, allenums))
1144
1145 def _generate_cpy_enum_ctx(self, tp, name):
1146 self._enum_ctx(tp, tp._get_c_name())
1147
1148 # ----------
1149 # macros: for now only for integers
1150
1151 def _generate_cpy_macro_collecttype(self, tp, name):
1152 pass
1153
1154 def _generate_cpy_macro_decl(self, tp, name):
1155 if tp == '...':
1156 check_value = None
1157 else:
1158 check_value = tp # an integer
1159 self._generate_cpy_const(True, name, check_value=check_value)
1160
1161 def _generate_cpy_macro_ctx(self, tp, name):
1162 if tp == '...':
1163 if self.target_is_python:
1164 raise VerificationError(
1165 "cannot use the syntax '...' in '#define %s ...' when "
1166 "using the ABI mode" % (name,))
1167 check_value = None
1168 else:
1169 check_value = tp # an integer
1170 type_op = CffiOp(OP_CONSTANT_INT, -1)
1171 self._lsts["global"].append(
1172 GlobalExpr(name, '_cffi_const_%s' % name, type_op,
1173 check_value=check_value))
1174
1175 # ----------
1176 # global variables
1177
1178 def _global_type(self, tp, global_name):
1179 if isinstance(tp, model.ArrayType):
1180 actual_length = tp.length
1181 if actual_length == '...':
1182 actual_length = '_cffi_array_len(%s)' % (global_name,)
1183 tp_item = self._global_type(tp.item, '%s[0]' % global_name)
1184 tp = model.ArrayType(tp_item, actual_length)
1185 return tp
1186
1187 def _generate_cpy_variable_collecttype(self, tp, name):
1188 self._do_collect_type(self._global_type(tp, name))
1189
1190 def _generate_cpy_variable_decl(self, tp, name):
1191 prnt = self._prnt
1192 tp = self._global_type(tp, name)
1193 if isinstance(tp, model.ArrayType) and tp.length is None:
1194 tp = tp.item
1195 ampersand = ''
1196 else:
1197 ampersand = '&'
1198 # This code assumes that casts from "tp *" to "void *" is a
1199 # no-op, i.e. a function that returns a "tp *" can be called
1200 # as if it returned a "void *". This should be generally true
