types.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. """
  2. QAPI types generator
  3. Copyright IBM, Corp. 2011
  4. Copyright (c) 2013-2018 Red Hat Inc.
  5. Authors:
  6. Anthony Liguori <aliguori@us.ibm.com>
  7. Michael Roth <mdroth@linux.vnet.ibm.com>
  8. Markus Armbruster <armbru@redhat.com>
  9. This work is licensed under the terms of the GNU GPL, version 2.
  10. # See the COPYING file in the top-level directory.
  11. """
  12. from qapi.common import *
  13. from qapi.gen import QAPISchemaModularCVisitor, ifcontext
  14. from qapi.schema import QAPISchemaEnumMember, QAPISchemaObjectType
  15. # variants must be emitted before their container; track what has already
  16. # been output
  17. objects_seen = set()
  18. def gen_enum_lookup(name, members, prefix=None):
  19. ret = mcgen('''
  20. const QEnumLookup %(c_name)s_lookup = {
  21. .array = (const char *const[]) {
  22. ''',
  23. c_name=c_name(name))
  24. for m in members:
  25. ret += gen_if(m.ifcond)
  26. index = c_enum_const(name, m.name, prefix)
  27. ret += mcgen('''
  28. [%(index)s] = "%(name)s",
  29. ''',
  30. index=index, name=m.name)
  31. ret += gen_endif(m.ifcond)
  32. ret += mcgen('''
  33. },
  34. .size = %(max_index)s
  35. };
  36. ''',
  37. max_index=c_enum_const(name, '_MAX', prefix))
  38. return ret
  39. def gen_enum(name, members, prefix=None):
  40. # append automatically generated _MAX value
  41. enum_members = members + [QAPISchemaEnumMember('_MAX', None)]
  42. ret = mcgen('''
  43. typedef enum %(c_name)s {
  44. ''',
  45. c_name=c_name(name))
  46. for m in enum_members:
  47. ret += gen_if(m.ifcond)
  48. ret += mcgen('''
  49. %(c_enum)s,
  50. ''',
  51. c_enum=c_enum_const(name, m.name, prefix))
  52. ret += gen_endif(m.ifcond)
  53. ret += mcgen('''
  54. } %(c_name)s;
  55. ''',
  56. c_name=c_name(name))
  57. ret += mcgen('''
  58. #define %(c_name)s_str(val) \\
  59. qapi_enum_lookup(&%(c_name)s_lookup, (val))
  60. extern const QEnumLookup %(c_name)s_lookup;
  61. ''',
  62. c_name=c_name(name))
  63. return ret
  64. def gen_fwd_object_or_array(name):
  65. return mcgen('''
  66. typedef struct %(c_name)s %(c_name)s;
  67. ''',
  68. c_name=c_name(name))
  69. def gen_array(name, element_type):
  70. return mcgen('''
  71. struct %(c_name)s {
  72. %(c_name)s *next;
  73. %(c_type)s value;
  74. };
  75. ''',
  76. c_name=c_name(name), c_type=element_type.c_type())
  77. def gen_struct_members(members):
  78. ret = ''
  79. for memb in members:
  80. ret += gen_if(memb.ifcond)
  81. if memb.optional:
  82. ret += mcgen('''
  83. bool has_%(c_name)s;
  84. ''',
  85. c_name=c_name(memb.name))
  86. ret += mcgen('''
  87. %(c_type)s %(c_name)s;
  88. ''',
  89. c_type=memb.type.c_type(), c_name=c_name(memb.name))
  90. ret += gen_endif(memb.ifcond)
  91. return ret
  92. def gen_object(name, ifcond, base, members, variants):
  93. if name in objects_seen:
  94. return ''
  95. objects_seen.add(name)
  96. ret = ''
  97. if variants:
  98. for v in variants.variants:
  99. if isinstance(v.type, QAPISchemaObjectType):
  100. ret += gen_object(v.type.name, v.type.ifcond, v.type.base,
  101. v.type.local_members, v.type.variants)
  102. ret += mcgen('''
  103. ''')
  104. ret += gen_if(ifcond)
  105. ret += mcgen('''
  106. struct %(c_name)s {
  107. ''',
  108. c_name=c_name(name))
  109. if base:
  110. if not base.is_implicit():
  111. ret += mcgen('''
  112. /* Members inherited from %(c_name)s: */
  113. ''',
  114. c_name=base.c_name())
  115. ret += gen_struct_members(base.members)
  116. if not base.is_implicit():
  117. ret += mcgen('''
  118. /* Own members: */
  119. ''')
  120. ret += gen_struct_members(members)
  121. if variants:
  122. ret += gen_variants(variants)
  123. # Make sure that all structs have at least one member; this avoids
  124. # potential issues with attempting to malloc space for zero-length
  125. # structs in C, and also incompatibility with C++ (where an empty
  126. # struct is size 1).
  127. if (not base or base.is_empty()) and not members and not variants:
  128. ret += mcgen('''
  129. char qapi_dummy_for_empty_struct;
  130. ''')
  131. ret += mcgen('''
  132. };
  133. ''')
  134. ret += gen_endif(ifcond)
  135. return ret
  136. def gen_upcast(name, base):
  137. # C makes const-correctness ugly. We have to cast away const to let
  138. # this function work for both const and non-const obj.
  139. return mcgen('''
  140. static inline %(base)s *qapi_%(c_name)s_base(const %(c_name)s *obj)
  141. {
  142. return (%(base)s *)obj;
  143. }
  144. ''',
  145. c_name=c_name(name), base=base.c_name())
  146. def gen_variants(variants):
  147. ret = mcgen('''
  148. union { /* union tag is @%(c_name)s */
  149. ''',
  150. c_name=c_name(variants.tag_member.name))
  151. for var in variants.variants:
  152. if var.type.name == 'q_empty':
  153. continue
  154. ret += gen_if(var.ifcond)
  155. ret += mcgen('''
  156. %(c_type)s %(c_name)s;
  157. ''',
  158. c_type=var.type.c_unboxed_type(),
  159. c_name=c_name(var.name))
  160. ret += gen_endif(var.ifcond)
  161. ret += mcgen('''
  162. } u;
  163. ''')
  164. return ret
  165. def gen_type_cleanup_decl(name):
  166. ret = mcgen('''
  167. void qapi_free_%(c_name)s(%(c_name)s *obj);
  168. ''',
  169. c_name=c_name(name))
  170. return ret
  171. def gen_type_cleanup(name):
  172. ret = mcgen('''
  173. void qapi_free_%(c_name)s(%(c_name)s *obj)
  174. {
  175. Visitor *v;
  176. if (!obj) {
  177. return;
  178. }
  179. v = qapi_dealloc_visitor_new();
  180. visit_type_%(c_name)s(v, NULL, &obj, NULL);
  181. visit_free(v);
  182. }
  183. ''',
  184. c_name=c_name(name))
  185. return ret
  186. class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
  187. def __init__(self, prefix):
  188. QAPISchemaModularCVisitor.__init__(
  189. self, prefix, 'qapi-types', ' * Schema-defined QAPI types',
  190. ' * Built-in QAPI types', __doc__)
  191. def _begin_system_module(self, name):
  192. self._genc.preamble_add(mcgen('''
  193. #include "qemu/osdep.h"
  194. #include "qapi/dealloc-visitor.h"
  195. #include "qapi/qapi-builtin-types.h"
  196. #include "qapi/qapi-builtin-visit.h"
  197. '''))
  198. self._genh.preamble_add(mcgen('''
  199. #include "qapi/util.h"
  200. '''))
  201. def _begin_user_module(self, name):
  202. types = self._module_basename('qapi-types', name)
  203. visit = self._module_basename('qapi-visit', name)
  204. self._genc.preamble_add(mcgen('''
  205. #include "qemu/osdep.h"
  206. #include "qapi/dealloc-visitor.h"
  207. #include "%(types)s.h"
  208. #include "%(visit)s.h"
  209. ''',
  210. types=types, visit=visit))
  211. self._genh.preamble_add(mcgen('''
  212. #include "qapi/qapi-builtin-types.h"
  213. '''))
  214. def visit_begin(self, schema):
  215. # gen_object() is recursive, ensure it doesn't visit the empty type
  216. objects_seen.add(schema.the_empty_object_type.name)
  217. def _gen_type_cleanup(self, name):
  218. self._genh.add(gen_type_cleanup_decl(name))
  219. self._genc.add(gen_type_cleanup(name))
  220. def visit_enum_type(self, name, info, ifcond, members, prefix):
  221. with ifcontext(ifcond, self._genh, self._genc):
  222. self._genh.preamble_add(gen_enum(name, members, prefix))
  223. self._genc.add(gen_enum_lookup(name, members, prefix))
  224. def visit_array_type(self, name, info, ifcond, element_type):
  225. with ifcontext(ifcond, self._genh, self._genc):
  226. self._genh.preamble_add(gen_fwd_object_or_array(name))
  227. self._genh.add(gen_array(name, element_type))
  228. self._gen_type_cleanup(name)
  229. def visit_object_type(self, name, info, ifcond, base, members, variants,
  230. features):
  231. # Nothing to do for the special empty builtin
  232. if name == 'q_empty':
  233. return
  234. with ifcontext(ifcond, self._genh):
  235. self._genh.preamble_add(gen_fwd_object_or_array(name))
  236. self._genh.add(gen_object(name, ifcond, base, members, variants))
  237. with ifcontext(ifcond, self._genh, self._genc):
  238. if base and not base.is_implicit():
  239. self._genh.add(gen_upcast(name, base))
  240. # TODO Worth changing the visitor signature, so we could
  241. # directly use rather than repeat type.is_implicit()?
  242. if not name.startswith('q_'):
  243. # implicit types won't be directly allocated/freed
  244. self._gen_type_cleanup(name)
  245. def visit_alternate_type(self, name, info, ifcond, variants):
  246. with ifcontext(ifcond, self._genh):
  247. self._genh.preamble_add(gen_fwd_object_or_array(name))
  248. self._genh.add(gen_object(name, ifcond, None,
  249. [variants.tag_member], variants))
  250. with ifcontext(ifcond, self._genh, self._genc):
  251. self._gen_type_cleanup(name)
  252. def gen_types(schema, output_dir, prefix, opt_builtins):
  253. vis = QAPISchemaGenTypeVisitor(prefix)
  254. schema.visit(vis)
  255. vis.write(output_dir, opt_builtins)