Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/cffi/recompiler.py: 36%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import os, sys, io
2from . import ffiplatform, model
3from .error import VerificationError
4from .cffi_opcode import *
6VERSION_BASE = 0x2601
7VERSION_EMBEDDED = 0x2701
8VERSION_CHAR16CHAR32 = 0x2801
10USE_LIMITED_API = (sys.platform != 'win32' or sys.version_info < (3, 0) or
11 sys.version_info >= (3, 5))
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
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)
26 def as_python_expr(self):
27 return "b'%s%s',%d" % (self.type_op.as_python_bytes(), self.name,
28 self.check_value)
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
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()))
44 def as_python_expr(self):
45 raise NotImplementedError
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)
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
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 + '},')
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))
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
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))
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)
111class TypenameExpr:
112 def __init__(self, name, type_index):
113 self.name = name
114 self.type_index = type_index
116 def as_c_expr(self):
117 return ' { "%s", %d },' % (self.name, self.type_index)
119 def as_python_expr(self):
120 return "b'%s%s'" % (format_four_bytes(self.type_index), self.name)
123# ____________________________________________________________
126class Recompiler:
127 _num_externpy = 0
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
135 def needs_version(self, ver):
136 self._version = max(self._version, ver)
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
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)
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)
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
243 # ----------
245 ALL_STEPS = ["global", "field", "struct_union", "enum", "typename"]
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)
276 # ----------
278 def _prnt(self, what=''):
279 self._f.write(what + '\n')
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)
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
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
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.
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(')')
528 # ----------
530 def _gettypenum(self, type):
531 # a KeyError here is a bug. please report it! :-)
532 return self._typesdict[type]
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)
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);')
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(' }')
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)
632 # ----------
633 # typedefs
635 def _typedef_type(self, tp, name):
636 return self._global_type(tp, "(*(%s *)0)" % (name,))
638 def _generate_cpy_typedef_collecttype(self, tp, name):
639 self._do_collect_type(self._typedef_type(tp, name))
641 def _generate_cpy_typedef_decl(self, tp, name):
642 pass
644 def _typedef_ctx(self, tp, name):
645 type_index = self._typesdict[tp]
646 self._lsts["typename"].append(TypenameExpr(name, type_index))
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)
657 # ----------
658 # function declarations
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)
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()
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))
846 # ----------
847 # named structs or unions
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
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)
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()
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)
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)
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)
1018 def _generate_cpy_struct_collecttype(self, tp, name):
1019 self._struct_collecttype(tp)
1020 _generate_cpy_union_collecttype = _generate_cpy_struct_collecttype
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
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
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
1037 # ----------
1038 # 'anonymous' declarations. These are produced for anonymous structs
1039 # or unions; the 'name' is obtained by a typedef.
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)
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)
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)
1059 # ----------
1060 # constants, declared with "static const ..."
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()
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)
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)
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))
1114 # ----------
1115 # enums
1117 def _generate_cpy_enum_collecttype(self, tp, name):
1118 self._do_collect_type(tp)
1120 def _generate_cpy_enum_decl(self, tp, name=None):
1121 for enumerator in tp.enumerators:
1122 self._generate_cpy_const(True, enumerator)
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))
1145 def _generate_cpy_enum_ctx(self, tp, name):
1146 self._enum_ctx(tp, tp._get_c_name())
1148 # ----------
1149 # macros: for now only for integers
1151 def _generate_cpy_macro_collecttype(self, tp, name):
1152 pass
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)
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))
1175 # ----------
1176 # global variables
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
1187 def _generate_cpy_variable_collecttype(self, tp, name):
1188 self._do_collect_type(self._global_type(tp, name))
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
1201 # on any modern machine. The only exception to that rule (on
1202 # uncommon architectures, and as far as I can tell) might be
1203 # if 'tp' were a function type, but that is not possible here.
1204 # (If 'tp' is a function _pointer_ type, then casts from "fn_t
1205 # **" to "void *" are again no-ops, as far as I can tell.)
1206 decl = '*_cffi_var_%s(void)' % (name,)
1207 prnt('static ' + tp.get_c_name(decl, quals=self._current_quals))
1208 prnt('{')
1209 prnt(' return %s(%s);' % (ampersand, name))
1210 prnt('}')
1211 prnt()
1213 def _generate_cpy_variable_ctx(self, tp, name):
1214 tp = self._global_type(tp, name)
1215 type_index = self._typesdict[tp]
1216 if self.target_is_python:
1217 op = OP_GLOBAL_VAR
1218 else:
1219 op = OP_GLOBAL_VAR_F
1220 self._lsts["global"].append(
1221 GlobalExpr(name, '_cffi_var_%s' % name, CffiOp(op, type_index)))
1223 # ----------
1224 # extern "Python"
1226 def _generate_cpy_extern_python_collecttype(self, tp, name):
1227 assert isinstance(tp, model.FunctionPtrType)
1228 self._do_collect_type(tp)
1229 _generate_cpy_dllexport_python_collecttype = \
1230 _generate_cpy_extern_python_plus_c_collecttype = \
1231 _generate_cpy_extern_python_collecttype
1233 def _extern_python_decl(self, tp, name, tag_and_space):
1234 prnt = self._prnt
1235 if isinstance(tp.result, model.VoidType):
1236 size_of_result = '0'
1237 else:
1238 context = 'result of %s' % name
1239 size_of_result = '(int)sizeof(%s)' % (
1240 tp.result.get_c_name('', context),)
1241 prnt('static struct _cffi_externpy_s _cffi_externpy__%s =' % name)
1242 prnt(' { "%s.%s", %s, 0, 0 };' % (
1243 self.module_name, name, size_of_result))
1244 prnt()
1245 #
1246 arguments = []
1247 context = 'argument of %s' % name
1248 for i, type in enumerate(tp.args):
1249 arg = type.get_c_name(' a%d' % i, context)
1250 arguments.append(arg)
1251 #
1252 repr_arguments = ', '.join(arguments)
1253 repr_arguments = repr_arguments or 'void'
1254 name_and_arguments = '%s(%s)' % (name, repr_arguments)
1255 if tp.abi == "__stdcall":
1256 name_and_arguments = '_cffi_stdcall ' + name_and_arguments
1257 #
1258 def may_need_128_bits(tp):
1259 return (isinstance(tp, model.PrimitiveType) and
1260 tp.name == 'long double')
1261 #
1262 size_of_a = max(len(tp.args)*8, 8)
1263 if may_need_128_bits(tp.result):
1264 size_of_a = max(size_of_a, 16)
1265 if isinstance(tp.result, model.StructOrUnion):
1266 size_of_a = 'sizeof(%s) > %d ? sizeof(%s) : %d' % (
1267 tp.result.get_c_name(''), size_of_a,
1268 tp.result.get_c_name(''), size_of_a)
1269 prnt('%s%s' % (tag_and_space, tp.result.get_c_name(name_and_arguments)))
1270 prnt('{')
1271 prnt(' char a[%s];' % size_of_a)
1272 prnt(' char *p = a;')
1273 for i, type in enumerate(tp.args):
1274 arg = 'a%d' % i
1275 if (isinstance(type, model.StructOrUnion) or
1276 may_need_128_bits(type)):
1277 arg = '&' + arg
1278 type = model.PointerType(type)
1279 prnt(' *(%s)(p + %d) = %s;' % (type.get_c_name('*'), i*8, arg))
1280 prnt(' _cffi_call_python(&_cffi_externpy__%s, p);' % name)
1281 if not isinstance(tp.result, model.VoidType):
1282 prnt(' return *(%s)p;' % (tp.result.get_c_name('*'),))
1283 prnt('}')
1284 prnt()
1285 self._num_externpy += 1
1287 def _generate_cpy_extern_python_decl(self, tp, name):
1288 self._extern_python_decl(tp, name, 'static ')
1290 def _generate_cpy_dllexport_python_decl(self, tp, name):
1291 self._extern_python_decl(tp, name, 'CFFI_DLLEXPORT ')
1293 def _generate_cpy_extern_python_plus_c_decl(self, tp, name):
1294 self._extern_python_decl(tp, name, '')
1296 def _generate_cpy_extern_python_ctx(self, tp, name):
1297 if self.target_is_python:
1298 raise VerificationError(
1299 "cannot use 'extern \"Python\"' in the ABI mode")
1300 if tp.ellipsis:
1301 raise NotImplementedError("a vararg function is extern \"Python\"")
1302 type_index = self._typesdict[tp]
1303 type_op = CffiOp(OP_EXTERN_PYTHON, type_index)
1304 self._lsts["global"].append(
1305 GlobalExpr(name, '&_cffi_externpy__%s' % name, type_op, name))
1307 _generate_cpy_dllexport_python_ctx = \
1308 _generate_cpy_extern_python_plus_c_ctx = \
1309 _generate_cpy_extern_python_ctx
1311 def _print_string_literal_in_array(self, s):
1312 prnt = self._prnt
1313 prnt('// # NB. this is not a string because of a size limit in MSVC')
1314 if not isinstance(s, bytes): # unicode
1315 s = s.encode('utf-8') # -> bytes
1316 else:
1317 s.decode('utf-8') # got bytes, check for valid utf-8
1318 try:
1319 s.decode('ascii')
1320 except UnicodeDecodeError:
1321 s = b'# -*- encoding: utf8 -*-\n' + s
1322 for line in s.splitlines(True):
1323 comment = line
1324 if type('//') is bytes: # python2
1325 line = map(ord, line) # make a list of integers
1326 else: # python3
1327 # type(line) is bytes, which enumerates like a list of integers
1328 comment = ascii(comment)[1:-1]
1329 prnt(('// ' + comment).rstrip())
1330 printed_line = ''
1331 for c in line:
1332 if len(printed_line) >= 76:
1333 prnt(printed_line)
1334 printed_line = ''
1335 printed_line += '%d,' % (c,)
1336 prnt(printed_line)
1338 # ----------
1339 # emitting the opcodes for individual types
1341 def _emit_bytecode_VoidType(self, tp, index):
1342 self.cffi_types[index] = CffiOp(OP_PRIMITIVE, PRIM_VOID)
1344 def _emit_bytecode_PrimitiveType(self, tp, index):
1345 prim_index = PRIMITIVE_TO_INDEX[tp.name]
1346 self.cffi_types[index] = CffiOp(OP_PRIMITIVE, prim_index)
1348 def _emit_bytecode_UnknownIntegerType(self, tp, index):
1349 s = ('_cffi_prim_int(sizeof(%s), (\n'
1350 ' ((%s)-1) | 0 /* check that %s is an integer type */\n'
1351 ' ) <= 0)' % (tp.name, tp.name, tp.name))
1352 self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s)
1354 def _emit_bytecode_UnknownFloatType(self, tp, index):
1355 s = ('_cffi_prim_float(sizeof(%s) *\n'
1356 ' (((%s)1) / 2) * 2 /* integer => 0, float => 1 */\n'
1357 ' )' % (tp.name, tp.name))
1358 self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s)
1360 def _emit_bytecode_RawFunctionType(self, tp, index):
1361 self.cffi_types[index] = CffiOp(OP_FUNCTION, self._typesdict[tp.result])
1362 index += 1
1363 for tp1 in tp.args:
1364 realindex = self._typesdict[tp1]
1365 if index != realindex:
1366 if isinstance(tp1, model.PrimitiveType):
1367 self._emit_bytecode_PrimitiveType(tp1, index)
1368 else:
1369 self.cffi_types[index] = CffiOp(OP_NOOP, realindex)
1370 index += 1
1371 flags = int(tp.ellipsis)
1372 if tp.abi is not None:
1373 if tp.abi == '__stdcall':
1374 flags |= 2
1375 else:
1376 raise NotImplementedError("abi=%r" % (tp.abi,))
1377 self.cffi_types[index] = CffiOp(OP_FUNCTION_END, flags)
1379 def _emit_bytecode_PointerType(self, tp, index):
1380 self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[tp.totype])
1382 _emit_bytecode_ConstPointerType = _emit_bytecode_PointerType
1383 _emit_bytecode_NamedPointerType = _emit_bytecode_PointerType
1385 def _emit_bytecode_FunctionPtrType(self, tp, index):
1386 raw = tp.as_raw_function()
1387 self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[raw])
1389 def _emit_bytecode_ArrayType(self, tp, index):
1390 item_index = self._typesdict[tp.item]
1391 if tp.length is None:
1392 self.cffi_types[index] = CffiOp(OP_OPEN_ARRAY, item_index)
1393 elif tp.length == '...':
1394 raise VerificationError(
1395 "type %s badly placed: the '...' array length can only be "
1396 "used on global arrays or on fields of structures" % (
1397 str(tp).replace('/*...*/', '...'),))
1398 else:
1399 assert self.cffi_types[index + 1] == 'LEN'
1400 self.cffi_types[index] = CffiOp(OP_ARRAY, item_index)
1401 self.cffi_types[index + 1] = CffiOp(None, str(tp.length))
1403 def _emit_bytecode_StructType(self, tp, index):
1404 struct_index = self._struct_unions[tp]
1405 self.cffi_types[index] = CffiOp(OP_STRUCT_UNION, struct_index)
1406 _emit_bytecode_UnionType = _emit_bytecode_StructType
1408 def _emit_bytecode_EnumType(self, tp, index):
1409 enum_index = self._enums[tp]
1410 self.cffi_types[index] = CffiOp(OP_ENUM, enum_index)
1413if sys.version_info >= (3,):
1414 NativeIO = io.StringIO
1415else:
1416 class NativeIO(io.BytesIO):
1417 def write(self, s):
1418 if isinstance(s, unicode):
1419 s = s.encode('ascii')
1420 super(NativeIO, self).write(s)
1422def _make_c_or_py_source(ffi, module_name, preamble, target_file, verbose):
1423 if verbose:
1424 print("generating %s" % (target_file,))
1425 recompiler = Recompiler(ffi, module_name,
1426 target_is_python=(preamble is None))
1427 recompiler.collect_type_table()
1428 recompiler.collect_step_tables()
1429 f = NativeIO()
1430 recompiler.write_source_to_f(f, preamble)
1431 output = f.getvalue()
1432 try:
1433 with open(target_file, 'r') as f1:
1434 if f1.read(len(output) + 1) != output:
1435 raise IOError
1436 if verbose:
1437 print("(already up-to-date)")
1438 return False # already up-to-date
1439 except IOError:
1440 tmp_file = '%s.~%d' % (target_file, os.getpid())
1441 with open(tmp_file, 'w') as f1:
1442 f1.write(output)
1443 try:
1444 os.rename(tmp_file, target_file)
1445 except OSError:
1446 os.unlink(target_file)
1447 os.rename(tmp_file, target_file)
1448 return True
1450def make_c_source(ffi, module_name, preamble, target_c_file, verbose=False):
1451 assert preamble is not None
1452 return _make_c_or_py_source(ffi, module_name, preamble, target_c_file,
1453 verbose)
1455def make_py_source(ffi, module_name, target_py_file, verbose=False):
1456 return _make_c_or_py_source(ffi, module_name, None, target_py_file,
1457 verbose)
1459def _modname_to_file(outputdir, modname, extension):
1460 parts = modname.split('.')
1461 try:
1462 os.makedirs(os.path.join(outputdir, *parts[:-1]))
1463 except OSError:
1464 pass
1465 parts[-1] += extension
1466 return os.path.join(outputdir, *parts), parts
1469# Aaargh. Distutils is not tested at all for the purpose of compiling
1470# DLLs that are not extension modules. Here are some hacks to work
1471# around that, in the _patch_for_*() functions...
1473def _patch_meth(patchlist, cls, name, new_meth):
1474 old = getattr(cls, name)
1475 patchlist.append((cls, name, old))
1476 setattr(cls, name, new_meth)
1477 return old
1479def _unpatch_meths(patchlist):
1480 for cls, name, old_meth in reversed(patchlist):
1481 setattr(cls, name, old_meth)
1483def _patch_for_embedding(patchlist):
1484 if sys.platform == 'win32':
1485 # we must not remove the manifest when building for embedding!
1486 from distutils.msvc9compiler import MSVCCompiler
1487 _patch_meth(patchlist, MSVCCompiler, '_remove_visual_c_ref',
1488 lambda self, manifest_file: manifest_file)
1490 if sys.platform == 'darwin':
1491 # we must not make a '-bundle', but a '-dynamiclib' instead
1492 from distutils.ccompiler import CCompiler
1493 def my_link_shared_object(self, *args, **kwds):
1494 if '-bundle' in self.linker_so:
1495 self.linker_so = list(self.linker_so)
1496 i = self.linker_so.index('-bundle')
1497 self.linker_so[i] = '-dynamiclib'
1498 return old_link_shared_object(self, *args, **kwds)
1499 old_link_shared_object = _patch_meth(patchlist, CCompiler,
1500 'link_shared_object',
1501 my_link_shared_object)
1503def _patch_for_target(patchlist, target):
1504 from distutils.command.build_ext import build_ext
1505 # if 'target' is different from '*', we need to patch some internal
1506 # method to just return this 'target' value, instead of having it
1507 # built from module_name
1508 if target.endswith('.*'):
1509 target = target[:-2]
1510 if sys.platform == 'win32':
1511 target += '.dll'
1512 elif sys.platform == 'darwin':
1513 target += '.dylib'
1514 else:
1515 target += '.so'
1516 _patch_meth(patchlist, build_ext, 'get_ext_filename',
1517 lambda self, ext_name: target)
1520def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True,
1521 c_file=None, source_extension='.c', extradir=None,
1522 compiler_verbose=1, target=None, debug=None, **kwds):
1523 if not isinstance(module_name, str):
1524 module_name = module_name.encode('ascii')
1525 if ffi._windows_unicode:
1526 ffi._apply_windows_unicode(kwds)
1527 if preamble is not None:
1528 embedding = (ffi._embedding is not None)
1529 if embedding:
1530 ffi._apply_embedding_fix(kwds)
1531 if c_file is None:
1532 c_file, parts = _modname_to_file(tmpdir, module_name,
1533 source_extension)
1534 if extradir:
1535 parts = [extradir] + parts
1536 ext_c_file = os.path.join(*parts)
1537 else:
1538 ext_c_file = c_file
1539 #
1540 if target is None:
1541 if embedding:
1542 target = '%s.*' % module_name
1543 else:
1544 target = '*'
1545 #
1546 ext = ffiplatform.get_extension(ext_c_file, module_name, **kwds)
1547 updated = make_c_source(ffi, module_name, preamble, c_file,
1548 verbose=compiler_verbose)
1549 if call_c_compiler:
1550 patchlist = []
1551 cwd = os.getcwd()
1552 try:
1553 if embedding:
1554 _patch_for_embedding(patchlist)
1555 if target != '*':
1556 _patch_for_target(patchlist, target)
1557 if compiler_verbose:
1558 if tmpdir == '.':
1559 msg = 'the current directory is'
1560 else:
1561 msg = 'setting the current directory to'
1562 print('%s %r' % (msg, os.path.abspath(tmpdir)))
1563 os.chdir(tmpdir)
1564 outputfilename = ffiplatform.compile('.', ext,
1565 compiler_verbose, debug)
1566 finally:
1567 os.chdir(cwd)
1568 _unpatch_meths(patchlist)
1569 return outputfilename
1570 else:
1571 return ext, updated
1572 else:
1573 if c_file is None:
1574 c_file, _ = _modname_to_file(tmpdir, module_name, '.py')
1575 updated = make_py_source(ffi, module_name, c_file,
1576 verbose=compiler_verbose)
1577 if call_c_compiler:
1578 return c_file
1579 else:
1580 return None, updated