github.com/whamcloud/lemur@v0.0.0-20190827193804-4655df8a52af/packaging/ci/lambda/GitPullS3/cffi/model.py (about) 1 import types, sys 2 import weakref 3 4 from .lock import allocate_lock 5 6 7 # type qualifiers 8 Q_CONST = 0x01 9 Q_RESTRICT = 0x02 10 Q_VOLATILE = 0x04 11 12 def qualify(quals, replace_with): 13 if quals & Q_CONST: 14 replace_with = ' const ' + replace_with.lstrip() 15 if quals & Q_VOLATILE: 16 replace_with = ' volatile ' + replace_with.lstrip() 17 if quals & Q_RESTRICT: 18 # It seems that __restrict is supported by gcc and msvc. 19 # If you hit some different compiler, add a #define in 20 # _cffi_include.h for it (and in its copies, documented there) 21 replace_with = ' __restrict ' + replace_with.lstrip() 22 return replace_with 23 24 25 class BaseTypeByIdentity(object): 26 is_array_type = False 27 is_raw_function = False 28 29 def get_c_name(self, replace_with='', context='a C file', quals=0): 30 result = self.c_name_with_marker 31 assert result.count('&') == 1 32 # some logic duplication with ffi.getctype()... :-( 33 replace_with = replace_with.strip() 34 if replace_with: 35 if replace_with.startswith('*') and '&[' in result: 36 replace_with = '(%s)' % replace_with 37 elif not replace_with[0] in '[(': 38 replace_with = ' ' + replace_with 39 replace_with = qualify(quals, replace_with) 40 result = result.replace('&', replace_with) 41 if '$' in result: 42 from .ffiplatform import VerificationError 43 raise VerificationError( 44 "cannot generate '%s' in %s: unknown type name" 45 % (self._get_c_name(), context)) 46 return result 47 48 def _get_c_name(self): 49 return self.c_name_with_marker.replace('&', '') 50 51 def has_c_name(self): 52 return '$' not in self._get_c_name() 53 54 def is_integer_type(self): 55 return False 56 57 def get_cached_btype(self, ffi, finishlist, can_delay=False): 58 try: 59 BType = ffi._cached_btypes[self] 60 except KeyError: 61 BType = self.build_backend_type(ffi, finishlist) 62 BType2 = ffi._cached_btypes.setdefault(self, BType) 63 assert BType2 is BType 64 return BType 65 66 def __repr__(self): 67 return '<%s>' % (self._get_c_name(),) 68 69 def _get_items(self): 70 return [(name, getattr(self, name)) for name in self._attrs_] 71 72 73 class BaseType(BaseTypeByIdentity): 74 75 def __eq__(self, other): 76 return (self.__class__ == other.__class__ and 77 self._get_items() == other._get_items()) 78 79 def __ne__(self, other): 80 return not self == other 81 82 def __hash__(self): 83 return hash((self.__class__, tuple(self._get_items()))) 84 85 86 class VoidType(BaseType): 87 _attrs_ = () 88 89 def __init__(self): 90 self.c_name_with_marker = 'void&' 91 92 def build_backend_type(self, ffi, finishlist): 93 return global_cache(self, ffi, 'new_void_type') 94 95 void_type = VoidType() 96 97 98 class BasePrimitiveType(BaseType): 99 pass 100 101 102 class PrimitiveType(BasePrimitiveType): 103 _attrs_ = ('name',) 104 105 ALL_PRIMITIVE_TYPES = { 106 'char': 'c', 107 'short': 'i', 108 'int': 'i', 109 'long': 'i', 110 'long long': 'i', 111 'signed char': 'i', 112 'unsigned char': 'i', 113 'unsigned short': 'i', 114 'unsigned int': 'i', 115 'unsigned long': 'i', 116 'unsigned long long': 'i', 117 'float': 'f', 118 'double': 'f', 119 'long double': 'f', 120 '_Bool': 'i', 121 # the following types are not primitive in the C sense 122 'wchar_t': 'c', 123 'int8_t': 'i', 124 'uint8_t': 'i', 125 'int16_t': 'i', 126 'uint16_t': 'i', 127 'int32_t': 'i', 128 'uint32_t': 'i', 129 'int64_t': 'i', 130 'uint64_t': 'i', 131 'int_least8_t': 'i', 132 'uint_least8_t': 'i', 133 'int_least16_t': 'i', 134 'uint_least16_t': 'i', 135 'int_least32_t': 'i', 136 'uint_least32_t': 'i', 137 'int_least64_t': 'i', 138 'uint_least64_t': 'i', 139 'int_fast8_t': 'i', 140 'uint_fast8_t': 'i', 141 'int_fast16_t': 'i', 142 'uint_fast16_t': 'i', 143 'int_fast32_t': 'i', 144 'uint_fast32_t': 'i', 145 'int_fast64_t': 'i', 146 'uint_fast64_t': 'i', 147 'intptr_t': 'i', 148 'uintptr_t': 'i', 149 'intmax_t': 'i', 150 'uintmax_t': 'i', 151 'ptrdiff_t': 'i', 152 'size_t': 'i', 153 'ssize_t': 'i', 154 } 155 156 def __init__(self, name): 157 assert name in self.ALL_PRIMITIVE_TYPES 158 self.name = name 159 self.c_name_with_marker = name + '&' 160 161 def is_char_type(self): 162 return self.ALL_PRIMITIVE_TYPES[self.name] == 'c' 163 def is_integer_type(self): 164 return self.ALL_PRIMITIVE_TYPES[self.name] == 'i' 165 def is_float_type(self): 166 return self.ALL_PRIMITIVE_TYPES[self.name] == 'f' 167 168 def build_backend_type(self, ffi, finishlist): 169 return global_cache(self, ffi, 'new_primitive_type', self.name) 170 171 172 class UnknownIntegerType(BasePrimitiveType): 173 _attrs_ = ('name',) 174 175 def __init__(self, name): 176 self.name = name 177 self.c_name_with_marker = name + '&' 178 179 def is_integer_type(self): 180 return True 181 182 def build_backend_type(self, ffi, finishlist): 183 raise NotImplementedError("integer type '%s' can only be used after " 184 "compilation" % self.name) 185 186 class UnknownFloatType(BasePrimitiveType): 187 _attrs_ = ('name', ) 188 189 def __init__(self, name): 190 self.name = name 191 self.c_name_with_marker = name + '&' 192 193 def build_backend_type(self, ffi, finishlist): 194 raise NotImplementedError("float type '%s' can only be used after " 195 "compilation" % self.name) 196 197 198 class BaseFunctionType(BaseType): 199 _attrs_ = ('args', 'result', 'ellipsis', 'abi') 200 201 def __init__(self, args, result, ellipsis, abi=None): 202 self.args = args 203 self.result = result 204 self.ellipsis = ellipsis 205 self.abi = abi 206 # 207 reprargs = [arg._get_c_name() for arg in self.args] 208 if self.ellipsis: 209 reprargs.append('...') 210 reprargs = reprargs or ['void'] 211 replace_with = self._base_pattern % (', '.join(reprargs),) 212 if abi is not None: 213 replace_with = replace_with[:1] + abi + ' ' + replace_with[1:] 214 self.c_name_with_marker = ( 215 self.result.c_name_with_marker.replace('&', replace_with)) 216 217 218 class RawFunctionType(BaseFunctionType): 219 # Corresponds to a C type like 'int(int)', which is the C type of 220 # a function, but not a pointer-to-function. The backend has no 221 # notion of such a type; it's used temporarily by parsing. 222 _base_pattern = '(&)(%s)' 223 is_raw_function = True 224 225 def build_backend_type(self, ffi, finishlist): 226 from . import api 227 raise api.CDefError("cannot render the type %r: it is a function " 228 "type, not a pointer-to-function type" % (self,)) 229 230 def as_function_pointer(self): 231 return FunctionPtrType(self.args, self.result, self.ellipsis, self.abi) 232 233 234 class FunctionPtrType(BaseFunctionType): 235 _base_pattern = '(*&)(%s)' 236 237 def build_backend_type(self, ffi, finishlist): 238 result = self.result.get_cached_btype(ffi, finishlist) 239 args = [] 240 for tp in self.args: 241 args.append(tp.get_cached_btype(ffi, finishlist)) 242 abi_args = () 243 if self.abi == "__stdcall": 244 if not self.ellipsis: # __stdcall ignored for variadic funcs 245 try: 246 abi_args = (ffi._backend.FFI_STDCALL,) 247 except AttributeError: 248 pass 249 return global_cache(self, ffi, 'new_function_type', 250 tuple(args), result, self.ellipsis, *abi_args) 251 252 def as_raw_function(self): 253 return RawFunctionType(self.args, self.result, self.ellipsis, self.abi) 254 255 256 class PointerType(BaseType): 257 _attrs_ = ('totype', 'quals') 258 259 def __init__(self, totype, quals=0): 260 self.totype = totype 261 self.quals = quals 262 extra = qualify(quals, " *&") 263 if totype.is_array_type: 264 extra = "(%s)" % (extra.lstrip(),) 265 self.c_name_with_marker = totype.c_name_with_marker.replace('&', extra) 266 267 def build_backend_type(self, ffi, finishlist): 268 BItem = self.totype.get_cached_btype(ffi, finishlist, can_delay=True) 269 return global_cache(self, ffi, 'new_pointer_type', BItem) 270 271 voidp_type = PointerType(void_type) 272 273 def ConstPointerType(totype): 274 return PointerType(totype, Q_CONST) 275 276 const_voidp_type = ConstPointerType(void_type) 277 278 279 class NamedPointerType(PointerType): 280 _attrs_ = ('totype', 'name') 281 282 def __init__(self, totype, name, quals=0): 283 PointerType.__init__(self, totype, quals) 284 self.name = name 285 self.c_name_with_marker = name + '&' 286 287 288 class ArrayType(BaseType): 289 _attrs_ = ('item', 'length') 290 is_array_type = True 291 292 def __init__(self, item, length): 293 self.item = item 294 self.length = length 295 # 296 if length is None: 297 brackets = '&[]' 298 elif length == '...': 299 brackets = '&[/*...*/]' 300 else: 301 brackets = '&[%s]' % length 302 self.c_name_with_marker = ( 303 self.item.c_name_with_marker.replace('&', brackets)) 304 305 def resolve_length(self, newlength): 306 return ArrayType(self.item, newlength) 307 308 def build_backend_type(self, ffi, finishlist): 309 if self.length == '...': 310 from . import api 311 raise api.CDefError("cannot render the type %r: unknown length" % 312 (self,)) 313 self.item.get_cached_btype(ffi, finishlist) # force the item BType 314 BPtrItem = PointerType(self.item).get_cached_btype(ffi, finishlist) 315 return global_cache(self, ffi, 'new_array_type', BPtrItem, self.length) 316 317 char_array_type = ArrayType(PrimitiveType('char'), None) 318 319 320 class StructOrUnionOrEnum(BaseTypeByIdentity): 321 _attrs_ = ('name',) 322 forcename = None 323 324 def build_c_name_with_marker(self): 325 name = self.forcename or '%s %s' % (self.kind, self.name) 326 self.c_name_with_marker = name + '&' 327 328 def force_the_name(self, forcename): 329 self.forcename = forcename 330 self.build_c_name_with_marker() 331 332 def get_official_name(self): 333 assert self.c_name_with_marker.endswith('&') 334 return self.c_name_with_marker[:-1] 335 336 337 class StructOrUnion(StructOrUnionOrEnum): 338 fixedlayout = None 339 completed = 0 340 partial = False 341 packed = False 342 343 def __init__(self, name, fldnames, fldtypes, fldbitsize, fldquals=None): 344 self.name = name 345 self.fldnames = fldnames 346 self.fldtypes = fldtypes 347 self.fldbitsize = fldbitsize 348 self.fldquals = fldquals 349 self.build_c_name_with_marker() 350 351 def has_anonymous_struct_fields(self): 352 if self.fldtypes is None: 353 return False 354 for name, type in zip(self.fldnames, self.fldtypes): 355 if name == '' and isinstance(type, StructOrUnion): 356 return True 357 return False 358 359 def enumfields(self): 360 fldquals = self.fldquals 361 if fldquals is None: 362 fldquals = (0,) * len(self.fldnames) 363 for name, type, bitsize, quals in zip(self.fldnames, self.fldtypes, 364 self.fldbitsize, fldquals): 365 if name == '' and isinstance(type, StructOrUnion): 366 # nested anonymous struct/union 367 for result in type.enumfields(): 368 yield result 369 else: 370 yield (name, type, bitsize, quals) 371 372 def force_flatten(self): 373 # force the struct or union to have a declaration that lists 374 # directly all fields returned by enumfields(), flattening 375 # nested anonymous structs/unions. 376 names = [] 377 types = [] 378 bitsizes = [] 379 fldquals = [] 380 for name, type, bitsize, quals in self.enumfields(): 381 names.append(name) 382 types.append(type) 383 bitsizes.append(bitsize) 384 fldquals.append(quals) 385 self.fldnames = tuple(names) 386 self.fldtypes = tuple(types) 387 self.fldbitsize = tuple(bitsizes) 388 self.fldquals = tuple(fldquals) 389 390 def get_cached_btype(self, ffi, finishlist, can_delay=False): 391 BType = StructOrUnionOrEnum.get_cached_btype(self, ffi, finishlist, 392 can_delay) 393 if not can_delay: 394 self.finish_backend_type(ffi, finishlist) 395 return BType 396 397 def finish_backend_type(self, ffi, finishlist): 398 if self.completed: 399 if self.completed != 2: 400 raise NotImplementedError("recursive structure declaration " 401 "for '%s'" % (self.name,)) 402 return 403 BType = ffi._cached_btypes[self] 404 # 405 self.completed = 1 406 # 407 if self.fldtypes is None: 408 pass # not completing it: it's an opaque struct 409 # 410 elif self.fixedlayout is None: 411 fldtypes = [tp.get_cached_btype(ffi, finishlist) 412 for tp in self.fldtypes] 413 lst = list(zip(self.fldnames, fldtypes, self.fldbitsize)) 414 sflags = 0 415 if self.packed: 416 sflags = 8 # SF_PACKED 417 ffi._backend.complete_struct_or_union(BType, lst, self, 418 -1, -1, sflags) 419 # 420 else: 421 fldtypes = [] 422 fieldofs, fieldsize, totalsize, totalalignment = self.fixedlayout 423 for i in range(len(self.fldnames)): 424 fsize = fieldsize[i] 425 ftype = self.fldtypes[i] 426 # 427 if isinstance(ftype, ArrayType) and ftype.length == '...': 428 # fix the length to match the total size 429 BItemType = ftype.item.get_cached_btype(ffi, finishlist) 430 nlen, nrest = divmod(fsize, ffi.sizeof(BItemType)) 431 if nrest != 0: 432 self._verification_error( 433 "field '%s.%s' has a bogus size?" % ( 434 self.name, self.fldnames[i] or '{}')) 435 ftype = ftype.resolve_length(nlen) 436 self.fldtypes = (self.fldtypes[:i] + (ftype,) + 437 self.fldtypes[i+1:]) 438 # 439 BFieldType = ftype.get_cached_btype(ffi, finishlist) 440 if isinstance(ftype, ArrayType) and ftype.length is None: 441 assert fsize == 0 442 else: 443 bitemsize = ffi.sizeof(BFieldType) 444 if bitemsize != fsize: 445 self._verification_error( 446 "field '%s.%s' is declared as %d bytes, but is " 447 "really %d bytes" % (self.name, 448 self.fldnames[i] or '{}', 449 bitemsize, fsize)) 450 fldtypes.append(BFieldType) 451 # 452 lst = list(zip(self.fldnames, fldtypes, self.fldbitsize, fieldofs)) 453 ffi._backend.complete_struct_or_union(BType, lst, self, 454 totalsize, totalalignment) 455 self.completed = 2 456 457 def _verification_error(self, msg): 458 from .ffiplatform import VerificationError 459 raise VerificationError(msg) 460 461 def check_not_partial(self): 462 if self.partial and self.fixedlayout is None: 463 from . import ffiplatform 464 raise ffiplatform.VerificationMissing(self._get_c_name()) 465 466 def build_backend_type(self, ffi, finishlist): 467 self.check_not_partial() 468 finishlist.append(self) 469 # 470 return global_cache(self, ffi, 'new_%s_type' % self.kind, 471 self.get_official_name(), key=self) 472 473 474 class StructType(StructOrUnion): 475 kind = 'struct' 476 477 478 class UnionType(StructOrUnion): 479 kind = 'union' 480 481 482 class EnumType(StructOrUnionOrEnum): 483 kind = 'enum' 484 partial = False 485 partial_resolved = False 486 487 def __init__(self, name, enumerators, enumvalues, baseinttype=None): 488 self.name = name 489 self.enumerators = enumerators 490 self.enumvalues = enumvalues 491 self.baseinttype = baseinttype 492 self.build_c_name_with_marker() 493 494 def force_the_name(self, forcename): 495 StructOrUnionOrEnum.force_the_name(self, forcename) 496 if self.forcename is None: 497 name = self.get_official_name() 498 self.forcename = '$' + name.replace(' ', '_') 499 500 def check_not_partial(self): 501 if self.partial and not self.partial_resolved: 502 from . import ffiplatform 503 raise ffiplatform.VerificationMissing(self._get_c_name()) 504 505 def build_backend_type(self, ffi, finishlist): 506 self.check_not_partial() 507 base_btype = self.build_baseinttype(ffi, finishlist) 508 return global_cache(self, ffi, 'new_enum_type', 509 self.get_official_name(), 510 self.enumerators, self.enumvalues, 511 base_btype, key=self) 512 513 def build_baseinttype(self, ffi, finishlist): 514 if self.baseinttype is not None: 515 return self.baseinttype.get_cached_btype(ffi, finishlist) 516 # 517 from . import api 518 if self.enumvalues: 519 smallest_value = min(self.enumvalues) 520 largest_value = max(self.enumvalues) 521 else: 522 import warnings 523 warnings.warn("%r has no values explicitly defined; next version " 524 "will refuse to guess which integer type it is " 525 "meant to be (unsigned/signed, int/long)" 526 % self._get_c_name()) 527 smallest_value = largest_value = 0 528 if smallest_value < 0: # needs a signed type 529 sign = 1 530 candidate1 = PrimitiveType("int") 531 candidate2 = PrimitiveType("long") 532 else: 533 sign = 0 534 candidate1 = PrimitiveType("unsigned int") 535 candidate2 = PrimitiveType("unsigned long") 536 btype1 = candidate1.get_cached_btype(ffi, finishlist) 537 btype2 = candidate2.get_cached_btype(ffi, finishlist) 538 size1 = ffi.sizeof(btype1) 539 size2 = ffi.sizeof(btype2) 540 if (smallest_value >= ((-1) << (8*size1-1)) and 541 largest_value < (1 << (8*size1-sign))): 542 return btype1 543 if (smallest_value >= ((-1) << (8*size2-1)) and 544 largest_value < (1 << (8*size2-sign))): 545 return btype2 546 raise api.CDefError("%s values don't all fit into either 'long' " 547 "or 'unsigned long'" % self._get_c_name()) 548 549 def unknown_type(name, structname=None): 550 if structname is None: 551 structname = '$%s' % name 552 tp = StructType(structname, None, None, None) 553 tp.force_the_name(name) 554 tp.origin = "unknown_type" 555 return tp 556 557 def unknown_ptr_type(name, structname=None): 558 if structname is None: 559 structname = '$$%s' % name 560 tp = StructType(structname, None, None, None) 561 return NamedPointerType(tp, name) 562 563 564 global_lock = allocate_lock() 565 566 def global_cache(srctype, ffi, funcname, *args, **kwds): 567 key = kwds.pop('key', (funcname, args)) 568 assert not kwds 569 try: 570 return ffi._backend.__typecache[key] 571 except KeyError: 572 pass 573 except AttributeError: 574 # initialize the __typecache attribute, either at the module level 575 # if ffi._backend is a module, or at the class level if ffi._backend 576 # is some instance. 577 if isinstance(ffi._backend, types.ModuleType): 578 ffi._backend.__typecache = weakref.WeakValueDictionary() 579 else: 580 type(ffi._backend).__typecache = weakref.WeakValueDictionary() 581 try: 582 res = getattr(ffi._backend, funcname)(*args) 583 except NotImplementedError as e: 584 raise NotImplementedError("%s: %r: %s" % (funcname, srctype, e)) 585 # note that setdefault() on WeakValueDictionary is not atomic 586 # and contains a rare bug (http://bugs.python.org/issue19542); 587 # we have to use a lock and do it ourselves 588 cache = ffi._backend.__typecache 589 with global_lock: 590 res1 = cache.get(key) 591 if res1 is None: 592 cache[key] = res 593 return res 594 else: 595 return res1 596 597 def pointer_cache(ffi, BType): 598 return global_cache('?', ffi, 'new_pointer_type', BType) 599 600 def attach_exception_info(e, name): 601 if e.args and type(e.args[0]) is str: 602 e.args = ('%s: %s' % (name, e.args[0]),) + e.args[1:]