model.py 21 KB

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