model.py 21 KB

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