codekingpro/portable-devtools
114k
1import io, os, sys, sysconfig2from . import ffiplatform, model3from .error import VerificationError4from .cffi_opcode import *5 6VERSION_BASE = 0x26017VERSION_EMBEDDED = 0x27018VERSION_CHAR16CHAR32 = 0x28019 10USE_LIMITED_API = ((sys.platform != 'win32' or sys.version_info < (3, 0) or11 sys.version_info >= (3, 5)) and12 not sysconfig.get_config_var("Py_GIL_DISABLED")) # free-threaded doesn't yet support limited API13 14class GlobalExpr:15 def __init__(self, name, address, type_op, size=0, check_value=0):16 self.name = name17 self.address = address18 self.type_op = type_op19 self.size = size20 self.check_value = check_value21 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 = name33 self.field_offset = field_offset34 self.field_size = field_size35 self.fbitsize = fbitsize36 self.field_type_op = field_type_op37 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 NotImplementedError46 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 NotImplementedError54 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 = name62 self.type_index = type_index63 self.flags = flags64 self.size = size65 self.alignment = alignment66 self.comment = comment67 self.first_field_index = first_field_index68 self.c_fields = c_fields69 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 = name90 self.type_index = type_index91 self.size = size92 self.signed = signed93 self.allenums = allenums94 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 = name114 self.type_index = type_index115 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 = 0128 129 def __init__(self, ffi, module_name, target_is_python=False):130 self.ffi = ffi131 self.module_name = module_name132 self.target_is_python = target_is_python133 self._version = VERSION_BASE134 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 first145 self.cffi_types = []146 for tp in all_decls:147 if tp.is_raw_function:148 assert self._typesdict[tp] is None149 self._typesdict[tp] = len(self.cffi_types)150 self.cffi_types.append(tp) # placeholder151 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) # placeholder160 self.cffi_types.append('END') # placeholder161 #162 # prepare all OTHER bytecode sequences163 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) # placeholder167 if tp.is_array_type and tp.length is not None:168 self.cffi_types.append('LEN') # placeholder169 assert None not in self._typesdict.values()170 #171 # collect all structs and unions and enums172 self._struct_unions = {}173 self._enums = {}174 for tp in all_decls:175 if isinstance(tp, model.StructOrUnion):176 self._struct_unions[tp] = None177 elif isinstance(tp, model.EnumType):178 self._enums[tp] = None179 for i, tp in enumerate(sorted(self._struct_unions,180 key=lambda tp: tp.name)):181 self._struct_unions[tp] = i182 for i, tp in enumerate(sorted(self._enums,183 key=lambda tp: tp.name)):184 self._enums[tp] = i185 #186 # emit all bytecode sequences now187 for tp in all_decls:188 method = getattr(self, '_emit_bytecode_' + tp.__class__.__name__)189 method(tp, self._typesdict[tp])190 #191 # consistency check192 for op in self.cffi_types:193 assert isinstance(op, CffiOp)194 self.cffi_types = tuple(self.cffi_types) # don't change any more195 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 the199 # individual fields contained in them. When producing Python,200 # don't do it and instead write it like it is, with the201 # corresponding fields having an empty name. Empty names are202 # recognized at runtime when we import the generated Python203 # file.204 expand_anonymous_struct_union = not self.target_is_python205 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 return213 if tp not in self._typesdict:214 self._typesdict[tp] = None215 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 = quals238 method(tp, realname)239 except Exception as e:240 model.attach_exception_info(e, name)241 raise242 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 more261 #262 # check for a possible internal inconsistency: _cffi_struct_unions263 # should have been generated with exactly self._struct_unions264 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.name268 assert len(lst) == len(self._struct_unions)269 # same with enums270 lst = self._lsts["enum"]271 for tp, i in self._enums.items():272 assert i < len(lst)273 assert lst[i].name == tp.name274 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 None284 self.write_py_source_to_f(f)285 else:286 assert preamble is not None287 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 lines294 295 def write_c_source_to_f(self, f, preamble):296 self._f = f297 prnt = self._prnt298 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 macro310 # and include an extra file311 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 from355 # 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 = 0410 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 function417 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 in437 # 'export_symbols', so instead of fighting it, just give up and438 # give it one439 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 = None467 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 = f480 prnt = self._prnt481 #482 # header483 prnt("# auto-generated file")484 prnt("import _cffi_backend")485 #486 # the 'import' of the included ffis487 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 = None507 #508 # the '_types' keyword argument509 self.cffi_types = tuple(self.cffi_types) # don't change any more510 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_STEPS515 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 argument521 if num_includes > 0:522 prnt(' _includes = (%s,),' % (523 ', '.join(['_ffi%d' % i for i in range(num_includes)]),))524 #525 # the footer526 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.name540 elif isinstance(tp, model.UnknownFloatType):541 # don't check with is_float_type(): it may be a 'long542 # double' here, and _cffi_to_c_double would loose precision543 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 return556 #557 elif (isinstance(tp, model.StructOrUnionOrEnum) or558 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 return565 #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 # typedefs634 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 pass643 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 declarations659 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_python667 assert isinstance(tp, model.FunctionPtrType)668 if tp.ellipsis:669 # cannot support vararg functions better than this: check for its670 # exact type (including the fixed arguments), and build it as a671 # constant function pointer (no CPython wrapper)672 self._generate_cpy_constant_decl(tp, name)673 return674 prnt = self._prnt675 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' % name688 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' % name714 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' % name728 result_decl = ' %s;' % tp.result.get_c_name(' result', context)729 prnt(result_decl)730 prnt(' PyObject *pyresult;')731 else:732 result_decl = None733 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 with779 # pointers, and if the result is a struct/union, insert a first780 # arg that is a pointer to the result. We also do that for781 # complex args and return type.782 def need_indirection(type):783 return (isinstance(type, model.StructOrUnion) or784 (isinstance(type, model.PrimitiveType) and785 type.is_complex_type()))786 difference = False787 arguments = []788 call_arguments = []789 context = 'argument of %s' % name790 for i, type in enumerate(tp.args):791 indirection = ''792 if need_indirection(type):793 indirection = '*'794 difference = True795 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.result799 if need_indirection(tp_result):800 context = 'result of %s' % name801 arg = tp_result.get_c_name(' *result', context)802 arguments.insert(0, arg)803 tp_result = model.void_type804 result_decl = None805 result_code = '*result = '806 difference = True807 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 return831 type_index = self._typesdict[tp.as_raw_function()]832 numargs = len(tp.args)833 if self.target_is_python:834 meth_kind = OP_DLOPEN_FUNC835 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 unions848 849 def _field_type(self, tp_struct, field_name, tp_field):850 if isinstance(tp_field, model.ArrayType):851 actual_length = tp_field.length852 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_field860 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, recursively865 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 return871 prnt = self._prnt872 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 double882 if fname != '':883 prnt(" (void)((p->%s) | 0); /* check that '%s.%s' is "884 "an integer */" % (fname, cname, fname))885 continue886 # 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.item892 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, ignore898 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 = None905 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 and912 (named_ptr is None or913 named_ptr not in self.ffi._parser._included_declarations)):914 if tp.fldtypes is None:915 pass # opaque916 elif tp.partial or any(tp.anonymous_struct_fields()):917 pass # field layout obtained silently from the C compiler918 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() only940 op = OP_NOOP941 if fbitsize >= 0:942 op = OP_BITFIELD943 size = '%d /* bits */' % fbitsize944 elif cname is None or (945 isinstance(fldtype, model.ArrayType) and946 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 None951 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 = '(size_t)(((char *)&((%s)4096)->%s) - (char *)4096)' % (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_unions967 size = '(size_t)-2'968 align = -2969 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 = None978 else:979 size = '(size_t)-1'980 align = -1981 first_field_index = -1982 comment = reason_for_not_expanding983 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.item991 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 missing997 # because they don't have any known C name. Check that they are998 # not partial (we can't complete or verify them!) and emit them999 # 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_collecttype1021 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, '_' + cname1028 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_decl1032 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_ctx1036 1037 # ----------1038 # 'anonymous' declarations. These are produced for anonymous structs1039 # 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._prnt1070 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 None1086 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_CONST1107 else:1108 const_kind = OP_CONSTANT1109 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 # enums1116 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)" % cname1136 signed = "((%s)-1) <= 0" % cname1137 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 integers1150 1151 def _generate_cpy_macro_collecttype(self, tp, name):1152 pass1153 1154 def _generate_cpy_macro_decl(self, tp, name):1155 if tp == '...':1156 check_value = None1157 else:1158 check_value = tp # an integer1159 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 = None1168 else:1169 check_value = tp # an integer1170 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 variables1177 1178 def _global_type(self, tp, global_name):1179 if isinstance(tp, model.ArrayType):1180 actual_length = tp.length1181 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 tp1186 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._prnt1192 tp = self._global_type(tp, name)1193 if isinstance(tp, model.ArrayType) and tp.length is None:1194 tp = tp.item1195 ampersand = ''1196 else:1197 ampersand = '&'1198 # This code assumes that casts from "tp *" to "void *" is a1199 # no-op, i.e. a function that returns a "tp *" can be called1200 # as if it returned a "void *". This should be generally true