Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
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

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