You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

model.py 21KB

5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  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. 'float _Complex': 'j',
  94. 'double _Complex': '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 = qualify(quals, " *&")
  216. if totype.is_array_type:
  217. extra = "(%s)" % (extra.lstrip(),)
  218. self.c_name_with_marker = totype.c_name_with_marker.replace('&', extra)
  219. def build_backend_type(self, ffi, finishlist):
  220. BItem = self.totype.get_cached_btype(ffi, finishlist, can_delay=True)
  221. return global_cache(self, ffi, 'new_pointer_type', BItem)
  222. voidp_type = PointerType(void_type)
  223. def ConstPointerType(totype):
  224. return PointerType(totype, Q_CONST)
  225. const_voidp_type = ConstPointerType(void_type)
  226. class NamedPointerType(PointerType):
  227. _attrs_ = ('totype', 'name')
  228. def __init__(self, totype, name, quals=0):
  229. PointerType.__init__(self, totype, quals)
  230. self.name = name
  231. self.c_name_with_marker = name + '&'
  232. class ArrayType(BaseType):
  233. _attrs_ = ('item', 'length')
  234. is_array_type = True
  235. def __init__(self, item, length):
  236. self.item = item
  237. self.length = length
  238. #
  239. if length is None:
  240. brackets = '&[]'
  241. elif length == '...':
  242. brackets = '&[/*...*/]'
  243. else:
  244. brackets = '&[%s]' % length
  245. self.c_name_with_marker = (
  246. self.item.c_name_with_marker.replace('&', brackets))
  247. def resolve_length(self, newlength):
  248. return ArrayType(self.item, newlength)
  249. def build_backend_type(self, ffi, finishlist):
  250. if self.length == '...':
  251. raise CDefError("cannot render the type %r: unknown length" %
  252. (self,))
  253. self.item.get_cached_btype(ffi, finishlist) # force the item BType
  254. BPtrItem = PointerType(self.item).get_cached_btype(ffi, finishlist)
  255. return global_cache(self, ffi, 'new_array_type', BPtrItem, self.length)
  256. char_array_type = ArrayType(PrimitiveType('char'), None)
  257. class StructOrUnionOrEnum(BaseTypeByIdentity):
  258. _attrs_ = ('name',)
  259. forcename = None
  260. def build_c_name_with_marker(self):
  261. name = self.forcename or '%s %s' % (self.kind, self.name)
  262. self.c_name_with_marker = name + '&'
  263. def force_the_name(self, forcename):
  264. self.forcename = forcename
  265. self.build_c_name_with_marker()
  266. def get_official_name(self):
  267. assert self.c_name_with_marker.endswith('&')
  268. return self.c_name_with_marker[:-1]
  269. class StructOrUnion(StructOrUnionOrEnum):
  270. fixedlayout = None
  271. completed = 0
  272. partial = False
  273. packed = 0
  274. def __init__(self, name, fldnames, fldtypes, fldbitsize, fldquals=None):
  275. self.name = name
  276. self.fldnames = fldnames
  277. self.fldtypes = fldtypes
  278. self.fldbitsize = fldbitsize
  279. self.fldquals = fldquals
  280. self.build_c_name_with_marker()
  281. def anonymous_struct_fields(self):
  282. if self.fldtypes is not None:
  283. for name, type in zip(self.fldnames, self.fldtypes):
  284. if name == '' and isinstance(type, StructOrUnion):
  285. yield type
  286. def enumfields(self, expand_anonymous_struct_union=True):
  287. fldquals = self.fldquals
  288. if fldquals is None:
  289. fldquals = (0,) * len(self.fldnames)
  290. for name, type, bitsize, quals in zip(self.fldnames, self.fldtypes,
  291. self.fldbitsize, fldquals):
  292. if (name == '' and isinstance(type, StructOrUnion)
  293. and expand_anonymous_struct_union):
  294. # nested anonymous struct/union
  295. for result in type.enumfields():
  296. yield result
  297. else:
  298. yield (name, type, bitsize, quals)
  299. def force_flatten(self):
  300. # force the struct or union to have a declaration that lists
  301. # directly all fields returned by enumfields(), flattening
  302. # nested anonymous structs/unions.
  303. names = []
  304. types = []
  305. bitsizes = []
  306. fldquals = []
  307. for name, type, bitsize, quals in self.enumfields():
  308. names.append(name)
  309. types.append(type)
  310. bitsizes.append(bitsize)
  311. fldquals.append(quals)
  312. self.fldnames = tuple(names)
  313. self.fldtypes = tuple(types)
  314. self.fldbitsize = tuple(bitsizes)
  315. self.fldquals = tuple(fldquals)
  316. def get_cached_btype(self, ffi, finishlist, can_delay=False):
  317. BType = StructOrUnionOrEnum.get_cached_btype(self, ffi, finishlist,
  318. can_delay)
  319. if not can_delay:
  320. self.finish_backend_type(ffi, finishlist)
  321. return BType
  322. def finish_backend_type(self, ffi, finishlist):
  323. if self.completed:
  324. if self.completed != 2:
  325. raise NotImplementedError("recursive structure declaration "
  326. "for '%s'" % (self.name,))
  327. return
  328. BType = ffi._cached_btypes[self]
  329. #
  330. self.completed = 1
  331. #
  332. if self.fldtypes is None:
  333. pass # not completing it: it's an opaque struct
  334. #
  335. elif self.fixedlayout is None:
  336. fldtypes = [tp.get_cached_btype(ffi, finishlist)
  337. for tp in self.fldtypes]
  338. lst = list(zip(self.fldnames, fldtypes, self.fldbitsize))
  339. extra_flags = ()
  340. if self.packed:
  341. if self.packed == 1:
  342. extra_flags = (8,) # SF_PACKED
  343. else:
  344. extra_flags = (0, self.packed)
  345. ffi._backend.complete_struct_or_union(BType, lst, self,
  346. -1, -1, *extra_flags)
  347. #
  348. else:
  349. fldtypes = []
  350. fieldofs, fieldsize, totalsize, totalalignment = self.fixedlayout
  351. for i in range(len(self.fldnames)):
  352. fsize = fieldsize[i]
  353. ftype = self.fldtypes[i]
  354. #
  355. if isinstance(ftype, ArrayType) and ftype.length == '...':
  356. # fix the length to match the total size
  357. BItemType = ftype.item.get_cached_btype(ffi, finishlist)
  358. nlen, nrest = divmod(fsize, ffi.sizeof(BItemType))
  359. if nrest != 0:
  360. self._verification_error(
  361. "field '%s.%s' has a bogus size?" % (
  362. self.name, self.fldnames[i] or '{}'))
  363. ftype = ftype.resolve_length(nlen)
  364. self.fldtypes = (self.fldtypes[:i] + (ftype,) +
  365. self.fldtypes[i+1:])
  366. #
  367. BFieldType = ftype.get_cached_btype(ffi, finishlist)
  368. if isinstance(ftype, ArrayType) and ftype.length is None:
  369. assert fsize == 0
  370. else:
  371. bitemsize = ffi.sizeof(BFieldType)
  372. if bitemsize != fsize:
  373. self._verification_error(
  374. "field '%s.%s' is declared as %d bytes, but is "
  375. "really %d bytes" % (self.name,
  376. self.fldnames[i] or '{}',
  377. bitemsize, fsize))
  378. fldtypes.append(BFieldType)
  379. #
  380. lst = list(zip(self.fldnames, fldtypes, self.fldbitsize, fieldofs))
  381. ffi._backend.complete_struct_or_union(BType, lst, self,
  382. totalsize, totalalignment)
  383. self.completed = 2
  384. def _verification_error(self, msg):
  385. raise VerificationError(msg)
  386. def check_not_partial(self):
  387. if self.partial and self.fixedlayout is None:
  388. raise VerificationMissing(self._get_c_name())
  389. def build_backend_type(self, ffi, finishlist):
  390. self.check_not_partial()
  391. finishlist.append(self)
  392. #
  393. return global_cache(self, ffi, 'new_%s_type' % self.kind,
  394. self.get_official_name(), key=self)
  395. class StructType(StructOrUnion):
  396. kind = 'struct'
  397. class UnionType(StructOrUnion):
  398. kind = 'union'
  399. class EnumType(StructOrUnionOrEnum):
  400. kind = 'enum'
  401. partial = False
  402. partial_resolved = False
  403. def __init__(self, name, enumerators, enumvalues, baseinttype=None):
  404. self.name = name
  405. self.enumerators = enumerators
  406. self.enumvalues = enumvalues
  407. self.baseinttype = baseinttype
  408. self.build_c_name_with_marker()
  409. def force_the_name(self, forcename):
  410. StructOrUnionOrEnum.force_the_name(self, forcename)
  411. if self.forcename is None:
  412. name = self.get_official_name()
  413. self.forcename = '$' + name.replace(' ', '_')
  414. def check_not_partial(self):
  415. if self.partial and not self.partial_resolved:
  416. raise VerificationMissing(self._get_c_name())
  417. def build_backend_type(self, ffi, finishlist):
  418. self.check_not_partial()
  419. base_btype = self.build_baseinttype(ffi, finishlist)
  420. return global_cache(self, ffi, 'new_enum_type',
  421. self.get_official_name(),
  422. self.enumerators, self.enumvalues,
  423. base_btype, key=self)
  424. def build_baseinttype(self, ffi, finishlist):
  425. if self.baseinttype is not None:
  426. return self.baseinttype.get_cached_btype(ffi, finishlist)
  427. #
  428. if self.enumvalues:
  429. smallest_value = min(self.enumvalues)
  430. largest_value = max(self.enumvalues)
  431. else:
  432. import warnings
  433. try:
  434. # XXX! The goal is to ensure that the warnings.warn()
  435. # will not suppress the warning. We want to get it
  436. # several times if we reach this point several times.
  437. __warningregistry__.clear()
  438. except NameError:
  439. pass
  440. warnings.warn("%r has no values explicitly defined; "
  441. "guessing that it is equivalent to 'unsigned int'"
  442. % self._get_c_name())
  443. smallest_value = largest_value = 0
  444. if smallest_value < 0: # needs a signed type
  445. sign = 1
  446. candidate1 = PrimitiveType("int")
  447. candidate2 = PrimitiveType("long")
  448. else:
  449. sign = 0
  450. candidate1 = PrimitiveType("unsigned int")
  451. candidate2 = PrimitiveType("unsigned long")
  452. btype1 = candidate1.get_cached_btype(ffi, finishlist)
  453. btype2 = candidate2.get_cached_btype(ffi, finishlist)
  454. size1 = ffi.sizeof(btype1)
  455. size2 = ffi.sizeof(btype2)
  456. if (smallest_value >= ((-1) << (8*size1-1)) and
  457. largest_value < (1 << (8*size1-sign))):
  458. return btype1
  459. if (smallest_value >= ((-1) << (8*size2-1)) and
  460. largest_value < (1 << (8*size2-sign))):
  461. return btype2
  462. raise CDefError("%s values don't all fit into either 'long' "
  463. "or 'unsigned long'" % self._get_c_name())
  464. def unknown_type(name, structname=None):
  465. if structname is None:
  466. structname = '$%s' % name
  467. tp = StructType(structname, None, None, None)
  468. tp.force_the_name(name)
  469. tp.origin = "unknown_type"
  470. return tp
  471. def unknown_ptr_type(name, structname=None):
  472. if structname is None:
  473. structname = '$$%s' % name
  474. tp = StructType(structname, None, None, None)
  475. return NamedPointerType(tp, name)
  476. global_lock = allocate_lock()
  477. _typecache_cffi_backend = weakref.WeakValueDictionary()
  478. def get_typecache(backend):
  479. # returns _typecache_cffi_backend if backend is the _cffi_backend
  480. # module, or type(backend).__typecache if backend is an instance of
  481. # CTypesBackend (or some FakeBackend class during tests)
  482. if isinstance(backend, types.ModuleType):
  483. return _typecache_cffi_backend
  484. with global_lock:
  485. if not hasattr(type(backend), '__typecache'):
  486. type(backend).__typecache = weakref.WeakValueDictionary()
  487. return type(backend).__typecache
  488. def global_cache(srctype, ffi, funcname, *args, **kwds):
  489. key = kwds.pop('key', (funcname, args))
  490. assert not kwds
  491. try:
  492. return ffi._typecache[key]
  493. except KeyError:
  494. pass
  495. try:
  496. res = getattr(ffi._backend, funcname)(*args)
  497. except NotImplementedError as e:
  498. raise NotImplementedError("%s: %r: %s" % (funcname, srctype, e))
  499. # note that setdefault() on WeakValueDictionary is not atomic
  500. # and contains a rare bug (http://bugs.python.org/issue19542);
  501. # we have to use a lock and do it ourselves
  502. cache = ffi._typecache
  503. with global_lock:
  504. res1 = cache.get(key)
  505. if res1 is None:
  506. cache[key] = res
  507. return res
  508. else:
  509. return res1
  510. def pointer_cache(ffi, BType):
  511. return global_cache('?', ffi, 'new_pointer_type', BType)
  512. def attach_exception_info(e, name):
  513. if e.args and type(e.args[0]) is str:
  514. e.args = ('%s: %s' % (name, e.args[0]),) + e.args[1:]