github.com/whamcloud/lemur@v0.0.0-20190827193804-4655df8a52af/packaging/ci/lambda/GitPullS3/cffi/recompiler.py (about) 1 import os, sys, io 2 from . import ffiplatform, model 3 from .cffi_opcode import * 4 5 VERSION = "0x2601" 6 VERSION_EMBEDDED = "0x2701" 7 8 9 class GlobalExpr: 10 def __init__(self, name, address, type_op, size=0, check_value=0): 11 self.name = name 12 self.address = address 13 self.type_op = type_op 14 self.size = size 15 self.check_value = check_value 16 17 def as_c_expr(self): 18 return ' { "%s", (void *)%s, %s, (void *)%s },' % ( 19 self.name, self.address, self.type_op.as_c_expr(), self.size) 20 21 def as_python_expr(self): 22 return "b'%s%s',%d" % (self.type_op.as_python_bytes(), self.name, 23 self.check_value) 24 25 class FieldExpr: 26 def __init__(self, name, field_offset, field_size, fbitsize, field_type_op): 27 self.name = name 28 self.field_offset = field_offset 29 self.field_size = field_size 30 self.fbitsize = fbitsize 31 self.field_type_op = field_type_op 32 33 def as_c_expr(self): 34 spaces = " " * len(self.name) 35 return (' { "%s", %s,\n' % (self.name, self.field_offset) + 36 ' %s %s,\n' % (spaces, self.field_size) + 37 ' %s %s },' % (spaces, self.field_type_op.as_c_expr())) 38 39 def as_python_expr(self): 40 raise NotImplementedError 41 42 def as_field_python_expr(self): 43 if self.field_type_op.op == OP_NOOP: 44 size_expr = '' 45 elif self.field_type_op.op == OP_BITFIELD: 46 size_expr = format_four_bytes(self.fbitsize) 47 else: 48 raise NotImplementedError 49 return "b'%s%s%s'" % (self.field_type_op.as_python_bytes(), 50 size_expr, 51 self.name) 52 53 class StructUnionExpr: 54 def __init__(self, name, type_index, flags, size, alignment, comment, 55 first_field_index, c_fields): 56 self.name = name 57 self.type_index = type_index 58 self.flags = flags 59 self.size = size 60 self.alignment = alignment 61 self.comment = comment 62 self.first_field_index = first_field_index 63 self.c_fields = c_fields 64 65 def as_c_expr(self): 66 return (' { "%s", %d, %s,' % (self.name, self.type_index, self.flags) 67 + '\n %s, %s, ' % (self.size, self.alignment) 68 + '%d, %d ' % (self.first_field_index, len(self.c_fields)) 69 + ('/* %s */ ' % self.comment if self.comment else '') 70 + '},') 71 72 def as_python_expr(self): 73 flags = eval(self.flags, G_FLAGS) 74 fields_expr = [c_field.as_field_python_expr() 75 for c_field in self.c_fields] 76 return "(b'%s%s%s',%s)" % ( 77 format_four_bytes(self.type_index), 78 format_four_bytes(flags), 79 self.name, 80 ','.join(fields_expr)) 81 82 class EnumExpr: 83 def __init__(self, name, type_index, size, signed, allenums): 84 self.name = name 85 self.type_index = type_index 86 self.size = size 87 self.signed = signed 88 self.allenums = allenums 89 90 def as_c_expr(self): 91 return (' { "%s", %d, _cffi_prim_int(%s, %s),\n' 92 ' "%s" },' % (self.name, self.type_index, 93 self.size, self.signed, self.allenums)) 94 95 def as_python_expr(self): 96 prim_index = { 97 (1, 0): PRIM_UINT8, (1, 1): PRIM_INT8, 98 (2, 0): PRIM_UINT16, (2, 1): PRIM_INT16, 99 (4, 0): PRIM_UINT32, (4, 1): PRIM_INT32, 100 (8, 0): PRIM_UINT64, (8, 1): PRIM_INT64, 101 }[self.size, self.signed] 102 return "b'%s%s%s\\x00%s'" % (format_four_bytes(self.type_index), 103 format_four_bytes(prim_index), 104 self.name, self.allenums) 105 106 class TypenameExpr: 107 def __init__(self, name, type_index): 108 self.name = name 109 self.type_index = type_index 110 111 def as_c_expr(self): 112 return ' { "%s", %d },' % (self.name, self.type_index) 113 114 def as_python_expr(self): 115 return "b'%s%s'" % (format_four_bytes(self.type_index), self.name) 116 117 118 # ____________________________________________________________ 119 120 121 class Recompiler: 122 _num_externpy = 0 123 124 def __init__(self, ffi, module_name, target_is_python=False): 125 self.ffi = ffi 126 self.module_name = module_name 127 self.target_is_python = target_is_python 128 129 def collect_type_table(self): 130 self._typesdict = {} 131 self._generate("collecttype") 132 # 133 all_decls = sorted(self._typesdict, key=str) 134 # 135 # prepare all FUNCTION bytecode sequences first 136 self.cffi_types = [] 137 for tp in all_decls: 138 if tp.is_raw_function: 139 assert self._typesdict[tp] is None 140 self._typesdict[tp] = len(self.cffi_types) 141 self.cffi_types.append(tp) # placeholder 142 for tp1 in tp.args: 143 assert isinstance(tp1, (model.VoidType, 144 model.BasePrimitiveType, 145 model.PointerType, 146 model.StructOrUnionOrEnum, 147 model.FunctionPtrType)) 148 if self._typesdict[tp1] is None: 149 self._typesdict[tp1] = len(self.cffi_types) 150 self.cffi_types.append(tp1) # placeholder 151 self.cffi_types.append('END') # placeholder 152 # 153 # prepare all OTHER bytecode sequences 154 for tp in all_decls: 155 if not tp.is_raw_function and self._typesdict[tp] is None: 156 self._typesdict[tp] = len(self.cffi_types) 157 self.cffi_types.append(tp) # placeholder 158 if tp.is_array_type and tp.length is not None: 159 self.cffi_types.append('LEN') # placeholder 160 assert None not in self._typesdict.values() 161 # 162 # collect all structs and unions and enums 163 self._struct_unions = {} 164 self._enums = {} 165 for tp in all_decls: 166 if isinstance(tp, model.StructOrUnion): 167 self._struct_unions[tp] = None 168 elif isinstance(tp, model.EnumType): 169 self._enums[tp] = None 170 for i, tp in enumerate(sorted(self._struct_unions, 171 key=lambda tp: tp.name)): 172 self._struct_unions[tp] = i 173 for i, tp in enumerate(sorted(self._enums, 174 key=lambda tp: tp.name)): 175 self._enums[tp] = i 176 # 177 # emit all bytecode sequences now 178 for tp in all_decls: 179 method = getattr(self, '_emit_bytecode_' + tp.__class__.__name__) 180 method(tp, self._typesdict[tp]) 181 # 182 # consistency check 183 for op in self.cffi_types: 184 assert isinstance(op, CffiOp) 185 self.cffi_types = tuple(self.cffi_types) # don't change any more 186 187 def _do_collect_type(self, tp): 188 if not isinstance(tp, model.BaseTypeByIdentity): 189 if isinstance(tp, tuple): 190 for x in tp: 191 self._do_collect_type(x) 192 return 193 if tp not in self._typesdict: 194 self._typesdict[tp] = None 195 if isinstance(tp, model.FunctionPtrType): 196 self._do_collect_type(tp.as_raw_function()) 197 elif isinstance(tp, model.StructOrUnion): 198 if tp.fldtypes is not None and ( 199 tp not in self.ffi._parser._included_declarations): 200 for name1, tp1, _, _ in tp.enumfields(): 201 self._do_collect_type(self._field_type(tp, name1, tp1)) 202 else: 203 for _, x in tp._get_items(): 204 self._do_collect_type(x) 205 206 def _generate(self, step_name): 207 lst = self.ffi._parser._declarations.items() 208 for name, (tp, quals) in sorted(lst): 209 kind, realname = name.split(' ', 1) 210 try: 211 method = getattr(self, '_generate_cpy_%s_%s' % (kind, 212 step_name)) 213 except AttributeError: 214 raise ffiplatform.VerificationError( 215 "not implemented in recompile(): %r" % name) 216 try: 217 self._current_quals = quals 218 method(tp, realname) 219 except Exception as e: 220 model.attach_exception_info(e, name) 221 raise 222 223 # ---------- 224 225 ALL_STEPS = ["global", "field", "struct_union", "enum", "typename"] 226 227 def collect_step_tables(self): 228 # collect the declarations for '_cffi_globals', '_cffi_typenames', etc. 229 self._lsts = {} 230 for step_name in self.ALL_STEPS: 231 self._lsts[step_name] = [] 232 self._seen_struct_unions = set() 233 self._generate("ctx") 234 self._add_missing_struct_unions() 235 # 236 for step_name in self.ALL_STEPS: 237 lst = self._lsts[step_name] 238 if step_name != "field": 239 lst.sort(key=lambda entry: entry.name) 240 self._lsts[step_name] = tuple(lst) # don't change any more 241 # 242 # check for a possible internal inconsistency: _cffi_struct_unions 243 # should have been generated with exactly self._struct_unions 244 lst = self._lsts["struct_union"] 245 for tp, i in self._struct_unions.items(): 246 assert i < len(lst) 247 assert lst[i].name == tp.name 248 assert len(lst) == len(self._struct_unions) 249 # same with enums 250 lst = self._lsts["enum"] 251 for tp, i in self._enums.items(): 252 assert i < len(lst) 253 assert lst[i].name == tp.name 254 assert len(lst) == len(self._enums) 255 256 # ---------- 257 258 def _prnt(self, what=''): 259 self._f.write(what + '\n') 260 261 def write_source_to_f(self, f, preamble): 262 if self.target_is_python: 263 assert preamble is None 264 self.write_py_source_to_f(f) 265 else: 266 assert preamble is not None 267 self.write_c_source_to_f(f, preamble) 268 269 def _rel_readlines(self, filename): 270 g = open(os.path.join(os.path.dirname(__file__), filename), 'r') 271 lines = g.readlines() 272 g.close() 273 return lines 274 275 def write_c_source_to_f(self, f, preamble): 276 self._f = f 277 prnt = self._prnt 278 # 279 # first the '#include' (actually done by inlining the file's content) 280 lines = self._rel_readlines('_cffi_include.h') 281 i = lines.index('#include "parse_c_type.h"\n') 282 lines[i:i+1] = self._rel_readlines('parse_c_type.h') 283 prnt(''.join(lines)) 284 # 285 # if we have ffi._embedding != None, we give it here as a macro 286 # and include an extra file 287 base_module_name = self.module_name.split('.')[-1] 288 if self.ffi._embedding is not None: 289 prnt('#define _CFFI_MODULE_NAME "%s"' % (self.module_name,)) 290 prnt('#define _CFFI_PYTHON_STARTUP_CODE %s' % 291 (self._string_literal(self.ffi._embedding),)) 292 prnt('#ifdef PYPY_VERSION') 293 prnt('# define _CFFI_PYTHON_STARTUP_FUNC _cffi_pypyinit_%s' % ( 294 base_module_name,)) 295 prnt('#elif PY_MAJOR_VERSION >= 3') 296 prnt('# define _CFFI_PYTHON_STARTUP_FUNC PyInit_%s' % ( 297 base_module_name,)) 298 prnt('#else') 299 prnt('# define _CFFI_PYTHON_STARTUP_FUNC init%s' % ( 300 base_module_name,)) 301 prnt('#endif') 302 lines = self._rel_readlines('_embedding.h') 303 prnt(''.join(lines)) 304 version = VERSION_EMBEDDED 305 else: 306 version = VERSION 307 # 308 # then paste the C source given by the user, verbatim. 309 prnt('/************************************************************/') 310 prnt() 311 prnt(preamble) 312 prnt() 313 prnt('/************************************************************/') 314 prnt() 315 # 316 # the declaration of '_cffi_types' 317 prnt('static void *_cffi_types[] = {') 318 typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()]) 319 for i, op in enumerate(self.cffi_types): 320 comment = '' 321 if i in typeindex2type: 322 comment = ' // ' + typeindex2type[i]._get_c_name() 323 prnt('/* %2d */ %s,%s' % (i, op.as_c_expr(), comment)) 324 if not self.cffi_types: 325 prnt(' 0') 326 prnt('};') 327 prnt() 328 # 329 # call generate_cpy_xxx_decl(), for every xxx found from 330 # ffi._parser._declarations. This generates all the functions. 331 self._seen_constants = set() 332 self._generate("decl") 333 # 334 # the declaration of '_cffi_globals' and '_cffi_typenames' 335 nums = {} 336 for step_name in self.ALL_STEPS: 337 lst = self._lsts[step_name] 338 nums[step_name] = len(lst) 339 if nums[step_name] > 0: 340 prnt('static const struct _cffi_%s_s _cffi_%ss[] = {' % ( 341 step_name, step_name)) 342 for entry in lst: 343 prnt(entry.as_c_expr()) 344 prnt('};') 345 prnt() 346 # 347 # the declaration of '_cffi_includes' 348 if self.ffi._included_ffis: 349 prnt('static const char * const _cffi_includes[] = {') 350 for ffi_to_include in self.ffi._included_ffis: 351 try: 352 included_module_name, included_source = ( 353 ffi_to_include._assigned_source[:2]) 354 except AttributeError: 355 raise ffiplatform.VerificationError( 356 "ffi object %r includes %r, but the latter has not " 357 "been prepared with set_source()" % ( 358 self.ffi, ffi_to_include,)) 359 if included_source is None: 360 raise ffiplatform.VerificationError( 361 "not implemented yet: ffi.include() of a Python-based " 362 "ffi inside a C-based ffi") 363 prnt(' "%s",' % (included_module_name,)) 364 prnt(' NULL') 365 prnt('};') 366 prnt() 367 # 368 # the declaration of '_cffi_type_context' 369 prnt('static const struct _cffi_type_context_s _cffi_type_context = {') 370 prnt(' _cffi_types,') 371 for step_name in self.ALL_STEPS: 372 if nums[step_name] > 0: 373 prnt(' _cffi_%ss,' % step_name) 374 else: 375 prnt(' NULL, /* no %ss */' % step_name) 376 for step_name in self.ALL_STEPS: 377 if step_name != "field": 378 prnt(' %d, /* num_%ss */' % (nums[step_name], step_name)) 379 if self.ffi._included_ffis: 380 prnt(' _cffi_includes,') 381 else: 382 prnt(' NULL, /* no includes */') 383 prnt(' %d, /* num_types */' % (len(self.cffi_types),)) 384 flags = 0 385 if self._num_externpy: 386 flags |= 1 # set to mean that we use extern "Python" 387 prnt(' %d, /* flags */' % flags) 388 prnt('};') 389 prnt() 390 # 391 # the init function 392 prnt('#ifdef PYPY_VERSION') 393 prnt('PyMODINIT_FUNC') 394 prnt('_cffi_pypyinit_%s(const void *p[])' % (base_module_name,)) 395 prnt('{') 396 if self._num_externpy: 397 prnt(' if (((intptr_t)p[0]) >= 0x0A03) {') 398 prnt(' _cffi_call_python_org = ' 399 '(void(*)(struct _cffi_externpy_s *, char *))p[1];') 400 prnt(' }') 401 prnt(' p[0] = (const void *)%s;' % version) 402 prnt(' p[1] = &_cffi_type_context;') 403 prnt('}') 404 # on Windows, distutils insists on putting init_cffi_xyz in 405 # 'export_symbols', so instead of fighting it, just give up and 406 # give it one 407 prnt('# ifdef _MSC_VER') 408 prnt(' PyMODINIT_FUNC') 409 prnt('# if PY_MAJOR_VERSION >= 3') 410 prnt(' PyInit_%s(void) { return NULL; }' % (base_module_name,)) 411 prnt('# else') 412 prnt(' init%s(void) { }' % (base_module_name,)) 413 prnt('# endif') 414 prnt('# endif') 415 prnt('#elif PY_MAJOR_VERSION >= 3') 416 prnt('PyMODINIT_FUNC') 417 prnt('PyInit_%s(void)' % (base_module_name,)) 418 prnt('{') 419 prnt(' return _cffi_init("%s", %s, &_cffi_type_context);' % ( 420 self.module_name, version)) 421 prnt('}') 422 prnt('#else') 423 prnt('PyMODINIT_FUNC') 424 prnt('init%s(void)' % (base_module_name,)) 425 prnt('{') 426 prnt(' _cffi_init("%s", %s, &_cffi_type_context);' % ( 427 self.module_name, version)) 428 prnt('}') 429 prnt('#endif') 430 431 def _to_py(self, x): 432 if isinstance(x, str): 433 return "b'%s'" % (x,) 434 if isinstance(x, (list, tuple)): 435 rep = [self._to_py(item) for item in x] 436 if len(rep) == 1: 437 rep.append('') 438 return "(%s)" % (','.join(rep),) 439 return x.as_python_expr() # Py2: unicode unexpected; Py3: bytes unexp. 440 441 def write_py_source_to_f(self, f): 442 self._f = f 443 prnt = self._prnt 444 # 445 # header 446 prnt("# auto-generated file") 447 prnt("import _cffi_backend") 448 # 449 # the 'import' of the included ffis 450 num_includes = len(self.ffi._included_ffis or ()) 451 for i in range(num_includes): 452 ffi_to_include = self.ffi._included_ffis[i] 453 try: 454 included_module_name, included_source = ( 455 ffi_to_include._assigned_source[:2]) 456 except AttributeError: 457 raise ffiplatform.VerificationError( 458 "ffi object %r includes %r, but the latter has not " 459 "been prepared with set_source()" % ( 460 self.ffi, ffi_to_include,)) 461 if included_source is not None: 462 raise ffiplatform.VerificationError( 463 "not implemented yet: ffi.include() of a C-based " 464 "ffi inside a Python-based ffi") 465 prnt('from %s import ffi as _ffi%d' % (included_module_name, i)) 466 prnt() 467 prnt("ffi = _cffi_backend.FFI('%s'," % (self.module_name,)) 468 prnt(" _version = %s," % (VERSION,)) 469 # 470 # the '_types' keyword argument 471 self.cffi_types = tuple(self.cffi_types) # don't change any more 472 types_lst = [op.as_python_bytes() for op in self.cffi_types] 473 prnt(' _types = %s,' % (self._to_py(''.join(types_lst)),)) 474 typeindex2type = dict([(i, tp) for (tp, i) in self._typesdict.items()]) 475 # 476 # the keyword arguments from ALL_STEPS 477 for step_name in self.ALL_STEPS: 478 lst = self._lsts[step_name] 479 if len(lst) > 0 and step_name != "field": 480 prnt(' _%ss = %s,' % (step_name, self._to_py(lst))) 481 # 482 # the '_includes' keyword argument 483 if num_includes > 0: 484 prnt(' _includes = (%s,),' % ( 485 ', '.join(['_ffi%d' % i for i in range(num_includes)]),)) 486 # 487 # the footer 488 prnt(')') 489 490 # ---------- 491 492 def _gettypenum(self, type): 493 # a KeyError here is a bug. please report it! :-) 494 return self._typesdict[type] 495 496 def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode): 497 extraarg = '' 498 if isinstance(tp, model.BasePrimitiveType): 499 if tp.is_integer_type() and tp.name != '_Bool': 500 converter = '_cffi_to_c_int' 501 extraarg = ', %s' % tp.name 502 elif isinstance(tp, model.UnknownFloatType): 503 # don't check with is_float_type(): it may be a 'long 504 # double' here, and _cffi_to_c_double would loose precision 505 converter = '(%s)_cffi_to_c_double' % (tp.get_c_name(''),) 506 else: 507 converter = '(%s)_cffi_to_c_%s' % (tp.get_c_name(''), 508 tp.name.replace(' ', '_')) 509 errvalue = '-1' 510 # 511 elif isinstance(tp, model.PointerType): 512 self._convert_funcarg_to_c_ptr_or_array(tp, fromvar, 513 tovar, errcode) 514 return 515 # 516 elif isinstance(tp, (model.StructOrUnion, model.EnumType)): 517 # a struct (not a struct pointer) as a function argument 518 self._prnt(' if (_cffi_to_c((char *)&%s, _cffi_type(%d), %s) < 0)' 519 % (tovar, self._gettypenum(tp), fromvar)) 520 self._prnt(' %s;' % errcode) 521 return 522 # 523 elif isinstance(tp, model.FunctionPtrType): 524 converter = '(%s)_cffi_to_c_pointer' % tp.get_c_name('') 525 extraarg = ', _cffi_type(%d)' % self._gettypenum(tp) 526 errvalue = 'NULL' 527 # 528 else: 529 raise NotImplementedError(tp) 530 # 531 self._prnt(' %s = %s(%s%s);' % (tovar, converter, fromvar, extraarg)) 532 self._prnt(' if (%s == (%s)%s && PyErr_Occurred())' % ( 533 tovar, tp.get_c_name(''), errvalue)) 534 self._prnt(' %s;' % errcode) 535 536 def _extra_local_variables(self, tp, localvars): 537 if isinstance(tp, model.PointerType): 538 localvars.add('Py_ssize_t datasize') 539 540 def _convert_funcarg_to_c_ptr_or_array(self, tp, fromvar, tovar, errcode): 541 self._prnt(' datasize = _cffi_prepare_pointer_call_argument(') 542 self._prnt(' _cffi_type(%d), %s, (char **)&%s);' % ( 543 self._gettypenum(tp), fromvar, tovar)) 544 self._prnt(' if (datasize != 0) {') 545 self._prnt(' if (datasize < 0)') 546 self._prnt(' %s;' % errcode) 547 self._prnt(' %s = (%s)alloca((size_t)datasize);' % ( 548 tovar, tp.get_c_name(''))) 549 self._prnt(' memset((void *)%s, 0, (size_t)datasize);' % (tovar,)) 550 self._prnt(' if (_cffi_convert_array_from_object(' 551 '(char *)%s, _cffi_type(%d), %s) < 0)' % ( 552 tovar, self._gettypenum(tp), fromvar)) 553 self._prnt(' %s;' % errcode) 554 self._prnt(' }') 555 556 def _convert_expr_from_c(self, tp, var, context): 557 if isinstance(tp, model.BasePrimitiveType): 558 if tp.is_integer_type(): 559 return '_cffi_from_c_int(%s, %s)' % (var, tp.name) 560 elif isinstance(tp, model.UnknownFloatType): 561 return '_cffi_from_c_double(%s)' % (var,) 562 elif tp.name != 'long double': 563 return '_cffi_from_c_%s(%s)' % (tp.name.replace(' ', '_'), var) 564 else: 565 return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( 566 var, self._gettypenum(tp)) 567 elif isinstance(tp, (model.PointerType, model.FunctionPtrType)): 568 return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( 569 var, self._gettypenum(tp)) 570 elif isinstance(tp, model.ArrayType): 571 return '_cffi_from_c_pointer((char *)%s, _cffi_type(%d))' % ( 572 var, self._gettypenum(model.PointerType(tp.item))) 573 elif isinstance(tp, model.StructType): 574 if tp.fldnames is None: 575 raise TypeError("'%s' is used as %s, but is opaque" % ( 576 tp._get_c_name(), context)) 577 return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % ( 578 var, self._gettypenum(tp)) 579 elif isinstance(tp, model.EnumType): 580 return '_cffi_from_c_deref((char *)&%s, _cffi_type(%d))' % ( 581 var, self._gettypenum(tp)) 582 else: 583 raise NotImplementedError(tp) 584 585 # ---------- 586 # typedefs 587 588 def _generate_cpy_typedef_collecttype(self, tp, name): 589 self._do_collect_type(tp) 590 591 def _generate_cpy_typedef_decl(self, tp, name): 592 pass 593 594 def _typedef_ctx(self, tp, name): 595 type_index = self._typesdict[tp] 596 self._lsts["typename"].append(TypenameExpr(name, type_index)) 597 598 def _generate_cpy_typedef_ctx(self, tp, name): 599 self._typedef_ctx(tp, name) 600 if getattr(tp, "origin", None) == "unknown_type": 601 self._struct_ctx(tp, tp.name, approxname=None) 602 elif isinstance(tp, model.NamedPointerType): 603 self._struct_ctx(tp.totype, tp.totype.name, approxname=tp.name, 604 named_ptr=tp) 605 606 # ---------- 607 # function declarations 608 609 def _generate_cpy_function_collecttype(self, tp, name): 610 self._do_collect_type(tp.as_raw_function()) 611 if tp.ellipsis and not self.target_is_python: 612 self._do_collect_type(tp) 613 614 def _generate_cpy_function_decl(self, tp, name): 615 assert not self.target_is_python 616 assert isinstance(tp, model.FunctionPtrType) 617 if tp.ellipsis: 618 # cannot support vararg functions better than this: check for its 619 # exact type (including the fixed arguments), and build it as a 620 # constant function pointer (no CPython wrapper) 621 self._generate_cpy_constant_decl(tp, name) 622 return 623 prnt = self._prnt 624 numargs = len(tp.args) 625 if numargs == 0: 626 argname = 'noarg' 627 elif numargs == 1: 628 argname = 'arg0' 629 else: 630 argname = 'args' 631 # 632 # ------------------------------ 633 # the 'd' version of the function, only for addressof(lib, 'func') 634 arguments = [] 635 call_arguments = [] 636 context = 'argument of %s' % name 637 for i, type in enumerate(tp.args): 638 arguments.append(type.get_c_name(' x%d' % i, context)) 639 call_arguments.append('x%d' % i) 640 repr_arguments = ', '.join(arguments) 641 repr_arguments = repr_arguments or 'void' 642 if tp.abi: 643 abi = tp.abi + ' ' 644 else: 645 abi = '' 646 name_and_arguments = '%s_cffi_d_%s(%s)' % (abi, name, repr_arguments) 647 prnt('static %s' % (tp.result.get_c_name(name_and_arguments),)) 648 prnt('{') 649 call_arguments = ', '.join(call_arguments) 650 result_code = 'return ' 651 if isinstance(tp.result, model.VoidType): 652 result_code = '' 653 prnt(' %s%s(%s);' % (result_code, name, call_arguments)) 654 prnt('}') 655 # 656 prnt('#ifndef PYPY_VERSION') # ------------------------------ 657 # 658 prnt('static PyObject *') 659 prnt('_cffi_f_%s(PyObject *self, PyObject *%s)' % (name, argname)) 660 prnt('{') 661 # 662 context = 'argument of %s' % name 663 for i, type in enumerate(tp.args): 664 arg = type.get_c_name(' x%d' % i, context) 665 prnt(' %s;' % arg) 666 # 667 localvars = set() 668 for type in tp.args: 669 self._extra_local_variables(type, localvars) 670 for decl in localvars: 671 prnt(' %s;' % (decl,)) 672 # 673 if not isinstance(tp.result, model.VoidType): 674 result_code = 'result = ' 675 context = 'result of %s' % name 676 result_decl = ' %s;' % tp.result.get_c_name(' result', context) 677 prnt(result_decl) 678 else: 679 result_decl = None 680 result_code = '' 681 # 682 if len(tp.args) > 1: 683 rng = range(len(tp.args)) 684 for i in rng: 685 prnt(' PyObject *arg%d;' % i) 686 prnt(' PyObject **aa;') 687 prnt() 688 prnt(' aa = _cffi_unpack_args(args, %d, "%s");' % (len(rng), name)) 689 prnt(' if (aa == NULL)') 690 prnt(' return NULL;') 691 for i in rng: 692 prnt(' arg%d = aa[%d];' % (i, i)) 693 prnt() 694 # 695 for i, type in enumerate(tp.args): 696 self._convert_funcarg_to_c(type, 'arg%d' % i, 'x%d' % i, 697 'return NULL') 698 prnt() 699 # 700 prnt(' Py_BEGIN_ALLOW_THREADS') 701 prnt(' _cffi_restore_errno();') 702 call_arguments = ['x%d' % i for i in range(len(tp.args))] 703 call_arguments = ', '.join(call_arguments) 704 prnt(' { %s%s(%s); }' % (result_code, name, call_arguments)) 705 prnt(' _cffi_save_errno();') 706 prnt(' Py_END_ALLOW_THREADS') 707 prnt() 708 # 709 prnt(' (void)self; /* unused */') 710 if numargs == 0: 711 prnt(' (void)noarg; /* unused */') 712 if result_code: 713 prnt(' return %s;' % 714 self._convert_expr_from_c(tp.result, 'result', 'result type')) 715 else: 716 prnt(' Py_INCREF(Py_None);') 717 prnt(' return Py_None;') 718 prnt('}') 719 # 720 prnt('#else') # ------------------------------ 721 # 722 # the PyPy version: need to replace struct/union arguments with 723 # pointers, and if the result is a struct/union, insert a first 724 # arg that is a pointer to the result. 725 difference = False 726 arguments = [] 727 call_arguments = [] 728 context = 'argument of %s' % name 729 for i, type in enumerate(tp.args): 730 indirection = '' 731 if isinstance(type, model.StructOrUnion): 732 indirection = '*' 733 difference = True 734 arg = type.get_c_name(' %sx%d' % (indirection, i), context) 735 arguments.append(arg) 736 call_arguments.append('%sx%d' % (indirection, i)) 737 tp_result = tp.result 738 if isinstance(tp_result, model.StructOrUnion): 739 context = 'result of %s' % name 740 arg = tp_result.get_c_name(' *result', context) 741 arguments.insert(0, arg) 742 tp_result = model.void_type 743 result_decl = None 744 result_code = '*result = ' 745 difference = True 746 if difference: 747 repr_arguments = ', '.join(arguments) 748 repr_arguments = repr_arguments or 'void' 749 name_and_arguments = '%s_cffi_f_%s(%s)' % (abi, name, 750 repr_arguments) 751 prnt('static %s' % (tp_result.get_c_name(name_and_arguments),)) 752 prnt('{') 753 if result_decl: 754 prnt(result_decl) 755 call_arguments = ', '.join(call_arguments) 756 prnt(' { %s%s(%s); }' % (result_code, name, call_arguments)) 757 if result_decl: 758 prnt(' return result;') 759 prnt('}') 760 else: 761 prnt('# define _cffi_f_%s _cffi_d_%s' % (name, name)) 762 # 763 prnt('#endif') # ------------------------------ 764 prnt() 765 766 def _generate_cpy_function_ctx(self, tp, name): 767 if tp.ellipsis and not self.target_is_python: 768 self._generate_cpy_constant_ctx(tp, name) 769 return 770 type_index = self._typesdict[tp.as_raw_function()] 771 numargs = len(tp.args) 772 if self.target_is_python: 773 meth_kind = OP_DLOPEN_FUNC 774 elif numargs == 0: 775 meth_kind = OP_CPYTHON_BLTN_N # 'METH_NOARGS' 776 elif numargs == 1: 777 meth_kind = OP_CPYTHON_BLTN_O # 'METH_O' 778 else: 779 meth_kind = OP_CPYTHON_BLTN_V # 'METH_VARARGS' 780 self._lsts["global"].append( 781 GlobalExpr(name, '_cffi_f_%s' % name, 782 CffiOp(meth_kind, type_index), 783 size='_cffi_d_%s' % name)) 784 785 # ---------- 786 # named structs or unions 787 788 def _field_type(self, tp_struct, field_name, tp_field): 789 if isinstance(tp_field, model.ArrayType): 790 actual_length = tp_field.length 791 if actual_length == '...': 792 ptr_struct_name = tp_struct.get_c_name('*') 793 actual_length = '_cffi_array_len(((%s)0)->%s)' % ( 794 ptr_struct_name, field_name) 795 tp_item = self._field_type(tp_struct, '%s[0]' % field_name, 796 tp_field.item) 797 tp_field = model.ArrayType(tp_item, actual_length) 798 return tp_field 799 800 def _struct_collecttype(self, tp): 801 self._do_collect_type(tp) 802 803 def _struct_decl(self, tp, cname, approxname): 804 if tp.fldtypes is None: 805 return 806 prnt = self._prnt 807 checkfuncname = '_cffi_checkfld_%s' % (approxname,) 808 prnt('_CFFI_UNUSED_FN') 809 prnt('static void %s(%s *p)' % (checkfuncname, cname)) 810 prnt('{') 811 prnt(' /* only to generate compile-time warnings or errors */') 812 prnt(' (void)p;') 813 for fname, ftype, fbitsize, fqual in tp.enumfields(): 814 try: 815 if ftype.is_integer_type() or fbitsize >= 0: 816 # accept all integers, but complain on float or double 817 prnt(" (void)((p->%s) | 0); /* check that '%s.%s' is " 818 "an integer */" % (fname, cname, fname)) 819 continue 820 # only accept exactly the type declared, except that '[]' 821 # is interpreted as a '*' and so will match any array length. 822 # (It would also match '*', but that's harder to detect...) 823 while (isinstance(ftype, model.ArrayType) 824 and (ftype.length is None or ftype.length == '...')): 825 ftype = ftype.item 826 fname = fname + '[0]' 827 prnt(' { %s = &p->%s; (void)tmp; }' % ( 828 ftype.get_c_name('*tmp', 'field %r'%fname, quals=fqual), 829 fname)) 830 except ffiplatform.VerificationError as e: 831 prnt(' /* %s */' % str(e)) # cannot verify it, ignore 832 prnt('}') 833 prnt('struct _cffi_align_%s { char x; %s y; };' % (approxname, cname)) 834 prnt() 835 836 def _struct_ctx(self, tp, cname, approxname, named_ptr=None): 837 type_index = self._typesdict[tp] 838 reason_for_not_expanding = None 839 flags = [] 840 if isinstance(tp, model.UnionType): 841 flags.append("_CFFI_F_UNION") 842 if tp.fldtypes is None: 843 flags.append("_CFFI_F_OPAQUE") 844 reason_for_not_expanding = "opaque" 845 if (tp not in self.ffi._parser._included_declarations and 846 (named_ptr is None or 847 named_ptr not in self.ffi._parser._included_declarations)): 848 if tp.fldtypes is None: 849 pass # opaque 850 elif tp.partial or tp.has_anonymous_struct_fields(): 851 pass # field layout obtained silently from the C compiler 852 else: 853 flags.append("_CFFI_F_CHECK_FIELDS") 854 if tp.packed: 855 flags.append("_CFFI_F_PACKED") 856 else: 857 flags.append("_CFFI_F_EXTERNAL") 858 reason_for_not_expanding = "external" 859 flags = '|'.join(flags) or '0' 860 c_fields = [] 861 if reason_for_not_expanding is None: 862 enumfields = list(tp.enumfields()) 863 for fldname, fldtype, fbitsize, fqual in enumfields: 864 fldtype = self._field_type(tp, fldname, fldtype) 865 # cname is None for _add_missing_struct_unions() only 866 op = OP_NOOP 867 if fbitsize >= 0: 868 op = OP_BITFIELD 869 size = '%d /* bits */' % fbitsize 870 elif cname is None or ( 871 isinstance(fldtype, model.ArrayType) and 872 fldtype.length is None): 873 size = '(size_t)-1' 874 else: 875 size = 'sizeof(((%s)0)->%s)' % ( 876 tp.get_c_name('*') if named_ptr is None 877 else named_ptr.name, 878 fldname) 879 if cname is None or fbitsize >= 0: 880 offset = '(size_t)-1' 881 elif named_ptr is not None: 882 offset = '((char *)&((%s)0)->%s) - (char *)0' % ( 883 named_ptr.name, fldname) 884 else: 885 offset = 'offsetof(%s, %s)' % (tp.get_c_name(''), fldname) 886 c_fields.append( 887 FieldExpr(fldname, offset, size, fbitsize, 888 CffiOp(op, self._typesdict[fldtype]))) 889 first_field_index = len(self._lsts["field"]) 890 self._lsts["field"].extend(c_fields) 891 # 892 if cname is None: # unknown name, for _add_missing_struct_unions 893 size = '(size_t)-2' 894 align = -2 895 comment = "unnamed" 896 else: 897 if named_ptr is not None: 898 size = 'sizeof(*(%s)0)' % (named_ptr.name,) 899 align = '-1 /* unknown alignment */' 900 else: 901 size = 'sizeof(%s)' % (cname,) 902 align = 'offsetof(struct _cffi_align_%s, y)' % (approxname,) 903 comment = None 904 else: 905 size = '(size_t)-1' 906 align = -1 907 first_field_index = -1 908 comment = reason_for_not_expanding 909 self._lsts["struct_union"].append( 910 StructUnionExpr(tp.name, type_index, flags, size, align, comment, 911 first_field_index, c_fields)) 912 self._seen_struct_unions.add(tp) 913 914 def _add_missing_struct_unions(self): 915 # not very nice, but some struct declarations might be missing 916 # because they don't have any known C name. Check that they are 917 # not partial (we can't complete or verify them!) and emit them 918 # anonymously. 919 lst = list(self._struct_unions.items()) 920 lst.sort(key=lambda tp_order: tp_order[1]) 921 for tp, order in lst: 922 if tp not in self._seen_struct_unions: 923 if tp.partial: 924 raise NotImplementedError("internal inconsistency: %r is " 925 "partial but was not seen at " 926 "this point" % (tp,)) 927 if tp.name.startswith('$') and tp.name[1:].isdigit(): 928 approxname = tp.name[1:] 929 elif tp.name == '_IO_FILE' and tp.forcename == 'FILE': 930 approxname = 'FILE' 931 self._typedef_ctx(tp, 'FILE') 932 else: 933 raise NotImplementedError("internal inconsistency: %r" % 934 (tp,)) 935 self._struct_ctx(tp, None, approxname) 936 937 def _generate_cpy_struct_collecttype(self, tp, name): 938 self._struct_collecttype(tp) 939 _generate_cpy_union_collecttype = _generate_cpy_struct_collecttype 940 941 def _struct_names(self, tp): 942 cname = tp.get_c_name('') 943 if ' ' in cname: 944 return cname, cname.replace(' ', '_') 945 else: 946 return cname, '_' + cname 947 948 def _generate_cpy_struct_decl(self, tp, name): 949 self._struct_decl(tp, *self._struct_names(tp)) 950 _generate_cpy_union_decl = _generate_cpy_struct_decl 951 952 def _generate_cpy_struct_ctx(self, tp, name): 953 self._struct_ctx(tp, *self._struct_names(tp)) 954 _generate_cpy_union_ctx = _generate_cpy_struct_ctx 955 956 # ---------- 957 # 'anonymous' declarations. These are produced for anonymous structs 958 # or unions; the 'name' is obtained by a typedef. 959 960 def _generate_cpy_anonymous_collecttype(self, tp, name): 961 if isinstance(tp, model.EnumType): 962 self._generate_cpy_enum_collecttype(tp, name) 963 else: 964 self._struct_collecttype(tp) 965 966 def _generate_cpy_anonymous_decl(self, tp, name): 967 if isinstance(tp, model.EnumType): 968 self._generate_cpy_enum_decl(tp) 969 else: 970 self._struct_decl(tp, name, 'typedef_' + name) 971 972 def _generate_cpy_anonymous_ctx(self, tp, name): 973 if isinstance(tp, model.EnumType): 974 self._enum_ctx(tp, name) 975 else: 976 self._struct_ctx(tp, name, 'typedef_' + name) 977 978 # ---------- 979 # constants, declared with "static const ..." 980 981 def _generate_cpy_const(self, is_int, name, tp=None, category='const', 982 check_value=None): 983 if (category, name) in self._seen_constants: 984 raise ffiplatform.VerificationError( 985 "duplicate declaration of %s '%s'" % (category, name)) 986 self._seen_constants.add((category, name)) 987 # 988 prnt = self._prnt 989 funcname = '_cffi_%s_%s' % (category, name) 990 if is_int: 991 prnt('static int %s(unsigned long long *o)' % funcname) 992 prnt('{') 993 prnt(' int n = (%s) <= 0;' % (name,)) 994 prnt(' *o = (unsigned long long)((%s) | 0);' 995 ' /* check that %s is an integer */' % (name, name)) 996 if check_value is not None: 997 if check_value > 0: 998 check_value = '%dU' % (check_value,) 999 prnt(' if (!_cffi_check_int(*o, n, %s))' % (check_value,)) 1000 prnt(' n |= 2;') 1001 prnt(' return n;') 1002 prnt('}') 1003 else: 1004 assert check_value is None 1005 prnt('static void %s(char *o)' % funcname) 1006 prnt('{') 1007 prnt(' *(%s)o = %s;' % (tp.get_c_name('*'), name)) 1008 prnt('}') 1009 prnt() 1010 1011 def _generate_cpy_constant_collecttype(self, tp, name): 1012 is_int = tp.is_integer_type() 1013 if not is_int or self.target_is_python: 1014 self._do_collect_type(tp) 1015 1016 def _generate_cpy_constant_decl(self, tp, name): 1017 is_int = tp.is_integer_type() 1018 self._generate_cpy_const(is_int, name, tp) 1019 1020 def _generate_cpy_constant_ctx(self, tp, name): 1021 if not self.target_is_python and tp.is_integer_type(): 1022 type_op = CffiOp(OP_CONSTANT_INT, -1) 1023 else: 1024 if self.target_is_python: 1025 const_kind = OP_DLOPEN_CONST 1026 else: 1027 const_kind = OP_CONSTANT 1028 type_index = self._typesdict[tp] 1029 type_op = CffiOp(const_kind, type_index) 1030 self._lsts["global"].append( 1031 GlobalExpr(name, '_cffi_const_%s' % name, type_op)) 1032 1033 # ---------- 1034 # enums 1035 1036 def _generate_cpy_enum_collecttype(self, tp, name): 1037 self._do_collect_type(tp) 1038 1039 def _generate_cpy_enum_decl(self, tp, name=None): 1040 for enumerator in tp.enumerators: 1041 self._generate_cpy_const(True, enumerator) 1042 1043 def _enum_ctx(self, tp, cname): 1044 type_index = self._typesdict[tp] 1045 type_op = CffiOp(OP_ENUM, -1) 1046 if self.target_is_python: 1047 tp.check_not_partial() 1048 for enumerator, enumvalue in zip(tp.enumerators, tp.enumvalues): 1049 self._lsts["global"].append( 1050 GlobalExpr(enumerator, '_cffi_const_%s' % enumerator, type_op, 1051 check_value=enumvalue)) 1052 # 1053 if cname is not None and '$' not in cname and not self.target_is_python: 1054 size = "sizeof(%s)" % cname 1055 signed = "((%s)-1) <= 0" % cname 1056 else: 1057 basetp = tp.build_baseinttype(self.ffi, []) 1058 size = self.ffi.sizeof(basetp) 1059 signed = int(int(self.ffi.cast(basetp, -1)) < 0) 1060 allenums = ",".join(tp.enumerators) 1061 self._lsts["enum"].append( 1062 EnumExpr(tp.name, type_index, size, signed, allenums)) 1063 1064 def _generate_cpy_enum_ctx(self, tp, name): 1065 self._enum_ctx(tp, tp._get_c_name()) 1066 1067 # ---------- 1068 # macros: for now only for integers 1069 1070 def _generate_cpy_macro_collecttype(self, tp, name): 1071 pass 1072 1073 def _generate_cpy_macro_decl(self, tp, name): 1074 if tp == '...': 1075 check_value = None 1076 else: 1077 check_value = tp # an integer 1078 self._generate_cpy_const(True, name, check_value=check_value) 1079 1080 def _generate_cpy_macro_ctx(self, tp, name): 1081 if tp == '...': 1082 if self.target_is_python: 1083 raise ffiplatform.VerificationError( 1084 "cannot use the syntax '...' in '#define %s ...' when " 1085 "using the ABI mode" % (name,)) 1086 check_value = None 1087 else: 1088 check_value = tp # an integer 1089 type_op = CffiOp(OP_CONSTANT_INT, -1) 1090 self._lsts["global"].append( 1091 GlobalExpr(name, '_cffi_const_%s' % name, type_op, 1092 check_value=check_value)) 1093 1094 # ---------- 1095 # global variables 1096 1097 def _global_type(self, tp, global_name): 1098 if isinstance(tp, model.ArrayType): 1099 actual_length = tp.length 1100 if actual_length == '...': 1101 actual_length = '_cffi_array_len(%s)' % (global_name,) 1102 tp_item = self._global_type(tp.item, '%s[0]' % global_name) 1103 tp = model.ArrayType(tp_item, actual_length) 1104 return tp 1105 1106 def _generate_cpy_variable_collecttype(self, tp, name): 1107 self._do_collect_type(self._global_type(tp, name)) 1108 1109 def _generate_cpy_variable_decl(self, tp, name): 1110 prnt = self._prnt 1111 tp = self._global_type(tp, name) 1112 if isinstance(tp, model.ArrayType) and tp.length is None: 1113 tp = tp.item 1114 ampersand = '' 1115 else: 1116 ampersand = '&' 1117 # This code assumes that casts from "tp *" to "void *" is a 1118 # no-op, i.e. a function that returns a "tp *" can be called 1119 # as if it returned a "void *". This should be generally true 1120 # on any modern machine. The only exception to that rule (on 1121 # uncommon architectures, and as far as I can tell) might be 1122 # if 'tp' were a function type, but that is not possible here. 1123 # (If 'tp' is a function _pointer_ type, then casts from "fn_t 1124 # **" to "void *" are again no-ops, as far as I can tell.) 1125 decl = '*_cffi_var_%s(void)' % (name,) 1126 prnt('static ' + tp.get_c_name(decl, quals=self._current_quals)) 1127 prnt('{') 1128 prnt(' return %s(%s);' % (ampersand, name)) 1129 prnt('}') 1130 prnt() 1131 1132 def _generate_cpy_variable_ctx(self, tp, name): 1133 tp = self._global_type(tp, name) 1134 type_index = self._typesdict[tp] 1135 if self.target_is_python: 1136 op = OP_GLOBAL_VAR 1137 else: 1138 op = OP_GLOBAL_VAR_F 1139 self._lsts["global"].append( 1140 GlobalExpr(name, '_cffi_var_%s' % name, CffiOp(op, type_index))) 1141 1142 # ---------- 1143 # extern "Python" 1144 1145 def _generate_cpy_extern_python_collecttype(self, tp, name): 1146 assert isinstance(tp, model.FunctionPtrType) 1147 self._do_collect_type(tp) 1148 _generate_cpy_dllexport_python_collecttype = \ 1149 _generate_cpy_extern_python_plus_c_collecttype = \ 1150 _generate_cpy_extern_python_collecttype 1151 1152 def _extern_python_decl(self, tp, name, tag_and_space): 1153 prnt = self._prnt 1154 if isinstance(tp.result, model.VoidType): 1155 size_of_result = '0' 1156 else: 1157 context = 'result of %s' % name 1158 size_of_result = '(int)sizeof(%s)' % ( 1159 tp.result.get_c_name('', context),) 1160 prnt('static struct _cffi_externpy_s _cffi_externpy__%s =' % name) 1161 prnt(' { "%s", %s };' % (name, size_of_result)) 1162 prnt() 1163 # 1164 arguments = [] 1165 context = 'argument of %s' % name 1166 for i, type in enumerate(tp.args): 1167 arg = type.get_c_name(' a%d' % i, context) 1168 arguments.append(arg) 1169 # 1170 repr_arguments = ', '.join(arguments) 1171 repr_arguments = repr_arguments or 'void' 1172 name_and_arguments = '%s(%s)' % (name, repr_arguments) 1173 if tp.abi == "__stdcall": 1174 name_and_arguments = '_cffi_stdcall ' + name_and_arguments 1175 # 1176 def may_need_128_bits(tp): 1177 return (isinstance(tp, model.PrimitiveType) and 1178 tp.name == 'long double') 1179 # 1180 size_of_a = max(len(tp.args)*8, 8) 1181 if may_need_128_bits(tp.result): 1182 size_of_a = max(size_of_a, 16) 1183 if isinstance(tp.result, model.StructOrUnion): 1184 size_of_a = 'sizeof(%s) > %d ? sizeof(%s) : %d' % ( 1185 tp.result.get_c_name(''), size_of_a, 1186 tp.result.get_c_name(''), size_of_a) 1187 prnt('%s%s' % (tag_and_space, tp.result.get_c_name(name_and_arguments))) 1188 prnt('{') 1189 prnt(' char a[%s];' % size_of_a) 1190 prnt(' char *p = a;') 1191 for i, type in enumerate(tp.args): 1192 arg = 'a%d' % i 1193 if (isinstance(type, model.StructOrUnion) or 1194 may_need_128_bits(type)): 1195 arg = '&' + arg 1196 type = model.PointerType(type) 1197 prnt(' *(%s)(p + %d) = %s;' % (type.get_c_name('*'), i*8, arg)) 1198 prnt(' _cffi_call_python(&_cffi_externpy__%s, p);' % name) 1199 if not isinstance(tp.result, model.VoidType): 1200 prnt(' return *(%s)p;' % (tp.result.get_c_name('*'),)) 1201 prnt('}') 1202 prnt() 1203 self._num_externpy += 1 1204 1205 def _generate_cpy_extern_python_decl(self, tp, name): 1206 self._extern_python_decl(tp, name, 'static ') 1207 1208 def _generate_cpy_dllexport_python_decl(self, tp, name): 1209 self._extern_python_decl(tp, name, 'CFFI_DLLEXPORT ') 1210 1211 def _generate_cpy_extern_python_plus_c_decl(self, tp, name): 1212 self._extern_python_decl(tp, name, '') 1213 1214 def _generate_cpy_extern_python_ctx(self, tp, name): 1215 if self.target_is_python: 1216 raise ffiplatform.VerificationError( 1217 "cannot use 'extern \"Python\"' in the ABI mode") 1218 if tp.ellipsis: 1219 raise NotImplementedError("a vararg function is extern \"Python\"") 1220 type_index = self._typesdict[tp] 1221 type_op = CffiOp(OP_EXTERN_PYTHON, type_index) 1222 self._lsts["global"].append( 1223 GlobalExpr(name, '&_cffi_externpy__%s' % name, type_op, name)) 1224 1225 _generate_cpy_dllexport_python_ctx = \ 1226 _generate_cpy_extern_python_plus_c_ctx = \ 1227 _generate_cpy_extern_python_ctx 1228 1229 def _string_literal(self, s): 1230 def _char_repr(c): 1231 # escape with a '\' the characters '\', '"' or (for trigraphs) '?' 1232 if c in '\\"?': return '\\' + c 1233 if ' ' <= c < '\x7F': return c 1234 if c == '\n': return '\\n' 1235 return '\\%03o' % ord(c) 1236 lines = [] 1237 for line in s.splitlines(True) or ['']: 1238 lines.append('"%s"' % ''.join([_char_repr(c) for c in line])) 1239 return ' \\\n'.join(lines) 1240 1241 # ---------- 1242 # emitting the opcodes for individual types 1243 1244 def _emit_bytecode_VoidType(self, tp, index): 1245 self.cffi_types[index] = CffiOp(OP_PRIMITIVE, PRIM_VOID) 1246 1247 def _emit_bytecode_PrimitiveType(self, tp, index): 1248 prim_index = PRIMITIVE_TO_INDEX[tp.name] 1249 self.cffi_types[index] = CffiOp(OP_PRIMITIVE, prim_index) 1250 1251 def _emit_bytecode_UnknownIntegerType(self, tp, index): 1252 s = ('_cffi_prim_int(sizeof(%s), (\n' 1253 ' ((%s)-1) | 0 /* check that %s is an integer type */\n' 1254 ' ) <= 0)' % (tp.name, tp.name, tp.name)) 1255 self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s) 1256 1257 def _emit_bytecode_UnknownFloatType(self, tp, index): 1258 s = ('_cffi_prim_float(sizeof(%s) *\n' 1259 ' (((%s)1) / 2) * 2 /* integer => 0, float => 1 */\n' 1260 ' )' % (tp.name, tp.name)) 1261 self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s) 1262 1263 def _emit_bytecode_RawFunctionType(self, tp, index): 1264 self.cffi_types[index] = CffiOp(OP_FUNCTION, self._typesdict[tp.result]) 1265 index += 1 1266 for tp1 in tp.args: 1267 realindex = self._typesdict[tp1] 1268 if index != realindex: 1269 if isinstance(tp1, model.PrimitiveType): 1270 self._emit_bytecode_PrimitiveType(tp1, index) 1271 else: 1272 self.cffi_types[index] = CffiOp(OP_NOOP, realindex) 1273 index += 1 1274 flags = int(tp.ellipsis) 1275 if tp.abi is not None: 1276 if tp.abi == '__stdcall': 1277 flags |= 2 1278 else: 1279 raise NotImplementedError("abi=%r" % (tp.abi,)) 1280 self.cffi_types[index] = CffiOp(OP_FUNCTION_END, flags) 1281 1282 def _emit_bytecode_PointerType(self, tp, index): 1283 self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[tp.totype]) 1284 1285 _emit_bytecode_ConstPointerType = _emit_bytecode_PointerType 1286 _emit_bytecode_NamedPointerType = _emit_bytecode_PointerType 1287 1288 def _emit_bytecode_FunctionPtrType(self, tp, index): 1289 raw = tp.as_raw_function() 1290 self.cffi_types[index] = CffiOp(OP_POINTER, self._typesdict[raw]) 1291 1292 def _emit_bytecode_ArrayType(self, tp, index): 1293 item_index = self._typesdict[tp.item] 1294 if tp.length is None: 1295 self.cffi_types[index] = CffiOp(OP_OPEN_ARRAY, item_index) 1296 elif tp.length == '...': 1297 raise ffiplatform.VerificationError( 1298 "type %s badly placed: the '...' array length can only be " 1299 "used on global arrays or on fields of structures" % ( 1300 str(tp).replace('/*...*/', '...'),)) 1301 else: 1302 assert self.cffi_types[index + 1] == 'LEN' 1303 self.cffi_types[index] = CffiOp(OP_ARRAY, item_index) 1304 self.cffi_types[index + 1] = CffiOp(None, str(tp.length)) 1305 1306 def _emit_bytecode_StructType(self, tp, index): 1307 struct_index = self._struct_unions[tp] 1308 self.cffi_types[index] = CffiOp(OP_STRUCT_UNION, struct_index) 1309 _emit_bytecode_UnionType = _emit_bytecode_StructType 1310 1311 def _emit_bytecode_EnumType(self, tp, index): 1312 enum_index = self._enums[tp] 1313 self.cffi_types[index] = CffiOp(OP_ENUM, enum_index) 1314 1315 1316 if sys.version_info >= (3,): 1317 NativeIO = io.StringIO 1318 else: 1319 class NativeIO(io.BytesIO): 1320 def write(self, s): 1321 if isinstance(s, unicode): 1322 s = s.encode('ascii') 1323 super(NativeIO, self).write(s) 1324 1325 def _make_c_or_py_source(ffi, module_name, preamble, target_file, verbose): 1326 if verbose: 1327 print("generating %s" % (target_file,)) 1328 recompiler = Recompiler(ffi, module_name, 1329 target_is_python=(preamble is None)) 1330 recompiler.collect_type_table() 1331 recompiler.collect_step_tables() 1332 f = NativeIO() 1333 recompiler.write_source_to_f(f, preamble) 1334 output = f.getvalue() 1335 try: 1336 with open(target_file, 'r') as f1: 1337 if f1.read(len(output) + 1) != output: 1338 raise IOError 1339 if verbose: 1340 print("(already up-to-date)") 1341 return False # already up-to-date 1342 except IOError: 1343 tmp_file = '%s.~%d' % (target_file, os.getpid()) 1344 with open(tmp_file, 'w') as f1: 1345 f1.write(output) 1346 try: 1347 os.rename(tmp_file, target_file) 1348 except OSError: 1349 os.unlink(target_file) 1350 os.rename(tmp_file, target_file) 1351 return True 1352 1353 def make_c_source(ffi, module_name, preamble, target_c_file, verbose=False): 1354 assert preamble is not None 1355 return _make_c_or_py_source(ffi, module_name, preamble, target_c_file, 1356 verbose) 1357 1358 def make_py_source(ffi, module_name, target_py_file, verbose=False): 1359 return _make_c_or_py_source(ffi, module_name, None, target_py_file, 1360 verbose) 1361 1362 def _modname_to_file(outputdir, modname, extension): 1363 parts = modname.split('.') 1364 try: 1365 os.makedirs(os.path.join(outputdir, *parts[:-1])) 1366 except OSError: 1367 pass 1368 parts[-1] += extension 1369 return os.path.join(outputdir, *parts), parts 1370 1371 1372 # Aaargh. Distutils is not tested at all for the purpose of compiling 1373 # DLLs that are not extension modules. Here are some hacks to work 1374 # around that, in the _patch_for_*() functions... 1375 1376 def _patch_meth(patchlist, cls, name, new_meth): 1377 old = getattr(cls, name) 1378 patchlist.append((cls, name, old)) 1379 setattr(cls, name, new_meth) 1380 return old 1381 1382 def _unpatch_meths(patchlist): 1383 for cls, name, old_meth in reversed(patchlist): 1384 setattr(cls, name, old_meth) 1385 1386 def _patch_for_embedding(patchlist): 1387 if sys.platform == 'win32': 1388 # we must not remove the manifest when building for embedding! 1389 from distutils.msvc9compiler import MSVCCompiler 1390 _patch_meth(patchlist, MSVCCompiler, '_remove_visual_c_ref', 1391 lambda self, manifest_file: manifest_file) 1392 1393 if sys.platform == 'darwin': 1394 # we must not make a '-bundle', but a '-dynamiclib' instead 1395 from distutils.ccompiler import CCompiler 1396 def my_link_shared_object(self, *args, **kwds): 1397 if '-bundle' in self.linker_so: 1398 self.linker_so = list(self.linker_so) 1399 i = self.linker_so.index('-bundle') 1400 self.linker_so[i] = '-dynamiclib' 1401 return old_link_shared_object(self, *args, **kwds) 1402 old_link_shared_object = _patch_meth(patchlist, CCompiler, 1403 'link_shared_object', 1404 my_link_shared_object) 1405 1406 def _patch_for_target(patchlist, target): 1407 from distutils.command.build_ext import build_ext 1408 # if 'target' is different from '*', we need to patch some internal 1409 # method to just return this 'target' value, instead of having it 1410 # built from module_name 1411 if target.endswith('.*'): 1412 target = target[:-2] 1413 if sys.platform == 'win32': 1414 target += '.dll' 1415 elif sys.platform == 'darwin': 1416 target += '.dylib' 1417 else: 1418 target += '.so' 1419 _patch_meth(patchlist, build_ext, 'get_ext_filename', 1420 lambda self, ext_name: target) 1421 1422 1423 def recompile(ffi, module_name, preamble, tmpdir='.', call_c_compiler=True, 1424 c_file=None, source_extension='.c', extradir=None, 1425 compiler_verbose=1, target=None, **kwds): 1426 if not isinstance(module_name, str): 1427 module_name = module_name.encode('ascii') 1428 if ffi._windows_unicode: 1429 ffi._apply_windows_unicode(kwds) 1430 if preamble is not None: 1431 embedding = (ffi._embedding is not None) 1432 if embedding: 1433 ffi._apply_embedding_fix(kwds) 1434 if c_file is None: 1435 c_file, parts = _modname_to_file(tmpdir, module_name, 1436 source_extension) 1437 if extradir: 1438 parts = [extradir] + parts 1439 ext_c_file = os.path.join(*parts) 1440 else: 1441 ext_c_file = c_file 1442 # 1443 if target is None: 1444 if embedding: 1445 target = '%s.*' % module_name 1446 else: 1447 target = '*' 1448 # 1449 ext = ffiplatform.get_extension(ext_c_file, module_name, **kwds) 1450 updated = make_c_source(ffi, module_name, preamble, c_file, 1451 verbose=compiler_verbose) 1452 if call_c_compiler: 1453 patchlist = [] 1454 cwd = os.getcwd() 1455 try: 1456 if embedding: 1457 _patch_for_embedding(patchlist) 1458 if target != '*': 1459 _patch_for_target(patchlist, target) 1460 os.chdir(tmpdir) 1461 outputfilename = ffiplatform.compile('.', ext, compiler_verbose) 1462 finally: 1463 os.chdir(cwd) 1464 _unpatch_meths(patchlist) 1465 return outputfilename 1466 else: 1467 return ext, updated 1468 else: 1469 if c_file is None: 1470 c_file, _ = _modname_to_file(tmpdir, module_name, '.py') 1471 updated = make_py_source(ffi, module_name, c_file, 1472 verbose=compiler_verbose) 1473 if call_c_compiler: 1474 return c_file 1475 else: 1476 return None, updated 1477 1478 def _verify(ffi, module_name, preamble, *args, **kwds): 1479 # FOR TESTS ONLY 1480 from testing.udir import udir 1481 import imp 1482 assert module_name not in sys.modules, "module name conflict: %r" % ( 1483 module_name,) 1484 kwds.setdefault('tmpdir', str(udir)) 1485 outputfilename = recompile(ffi, module_name, preamble, *args, **kwds) 1486 module = imp.load_dynamic(module_name, outputfilename) 1487 # 1488 # hack hack hack: copy all *bound methods* from module.ffi back to the 1489 # ffi instance. Then calls like ffi.new() will invoke module.ffi.new(). 1490 for name in dir(module.ffi): 1491 if not name.startswith('_'): 1492 attr = getattr(module.ffi, name) 1493 if attr is not getattr(ffi, name, object()): 1494 setattr(ffi, name, attr) 1495 def typeof_disabled(*args, **kwds): 1496 raise NotImplementedError 1497 ffi._typeof = typeof_disabled 1498 for name in dir(ffi): 1499 if not name.startswith('_') and not hasattr(module.ffi, name): 1500 setattr(ffi, name, NotImplemented) 1501 return module.lib