2
0

visit.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. """
  2. QAPI visitor generator
  3. Copyright IBM, Corp. 2011
  4. Copyright (C) 2014-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 .common import (
  13. c_enum_const,
  14. c_name,
  15. gen_endif,
  16. gen_if,
  17. indent,
  18. mcgen,
  19. )
  20. from .gen import QAPISchemaModularCVisitor, ifcontext
  21. from .schema import QAPISchemaEnumType, QAPISchemaObjectType
  22. def gen_visit_decl(name, scalar=False):
  23. c_type = c_name(name) + ' *'
  24. if not scalar:
  25. c_type += '*'
  26. return mcgen('''
  27. bool visit_type_%(c_name)s(Visitor *v, const char *name,
  28. %(c_type)sobj, Error **errp);
  29. ''',
  30. c_name=c_name(name), c_type=c_type)
  31. def gen_visit_members_decl(name):
  32. return mcgen('''
  33. bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp);
  34. ''',
  35. c_name=c_name(name))
  36. def gen_visit_object_members(name, base, members, variants):
  37. ret = mcgen('''
  38. bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
  39. {
  40. ''',
  41. c_name=c_name(name))
  42. if base:
  43. ret += mcgen('''
  44. if (!visit_type_%(c_type)s_members(v, (%(c_type)s *)obj, errp)) {
  45. return false;
  46. }
  47. ''',
  48. c_type=base.c_name())
  49. for memb in members:
  50. ret += gen_if(memb.ifcond)
  51. if memb.optional:
  52. ret += mcgen('''
  53. if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) {
  54. ''',
  55. name=memb.name, c_name=c_name(memb.name))
  56. indent.increase()
  57. ret += mcgen('''
  58. if (!visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, errp)) {
  59. return false;
  60. }
  61. ''',
  62. c_type=memb.type.c_name(), name=memb.name,
  63. c_name=c_name(memb.name))
  64. if memb.optional:
  65. indent.decrease()
  66. ret += mcgen('''
  67. }
  68. ''')
  69. ret += gen_endif(memb.ifcond)
  70. if variants:
  71. tag_member = variants.tag_member
  72. assert isinstance(tag_member.type, QAPISchemaEnumType)
  73. ret += mcgen('''
  74. switch (obj->%(c_name)s) {
  75. ''',
  76. c_name=c_name(tag_member.name))
  77. for var in variants.variants:
  78. case_str = c_enum_const(tag_member.type.name, var.name,
  79. tag_member.type.prefix)
  80. ret += gen_if(var.ifcond)
  81. if var.type.name == 'q_empty':
  82. # valid variant and nothing to do
  83. ret += mcgen('''
  84. case %(case)s:
  85. break;
  86. ''',
  87. case=case_str)
  88. else:
  89. ret += mcgen('''
  90. case %(case)s:
  91. return visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, errp);
  92. ''',
  93. case=case_str,
  94. c_type=var.type.c_name(), c_name=c_name(var.name))
  95. ret += gen_endif(var.ifcond)
  96. ret += mcgen('''
  97. default:
  98. abort();
  99. }
  100. ''')
  101. ret += mcgen('''
  102. return true;
  103. }
  104. ''')
  105. return ret
  106. def gen_visit_list(name, element_type):
  107. return mcgen('''
  108. bool visit_type_%(c_name)s(Visitor *v, const char *name,
  109. %(c_name)s **obj, Error **errp)
  110. {
  111. bool ok = false;
  112. %(c_name)s *tail;
  113. size_t size = sizeof(**obj);
  114. if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) {
  115. return false;
  116. }
  117. for (tail = *obj; tail;
  118. tail = (%(c_name)s *)visit_next_list(v, (GenericList *)tail, size)) {
  119. if (!visit_type_%(c_elt_type)s(v, NULL, &tail->value, errp)) {
  120. goto out_obj;
  121. }
  122. }
  123. ok = visit_check_list(v, errp);
  124. out_obj:
  125. visit_end_list(v, (void **)obj);
  126. if (!ok && visit_is_input(v)) {
  127. qapi_free_%(c_name)s(*obj);
  128. *obj = NULL;
  129. }
  130. return ok;
  131. }
  132. ''',
  133. c_name=c_name(name), c_elt_type=element_type.c_name())
  134. def gen_visit_enum(name):
  135. return mcgen('''
  136. bool visit_type_%(c_name)s(Visitor *v, const char *name,
  137. %(c_name)s *obj, Error **errp)
  138. {
  139. int value = *obj;
  140. bool ok = visit_type_enum(v, name, &value, &%(c_name)s_lookup, errp);
  141. *obj = value;
  142. return ok;
  143. }
  144. ''',
  145. c_name=c_name(name))
  146. def gen_visit_alternate(name, variants):
  147. ret = mcgen('''
  148. bool visit_type_%(c_name)s(Visitor *v, const char *name,
  149. %(c_name)s **obj, Error **errp)
  150. {
  151. bool ok = false;
  152. if (!visit_start_alternate(v, name, (GenericAlternate **)obj,
  153. sizeof(**obj), errp)) {
  154. return false;
  155. }
  156. if (!*obj) {
  157. /* incomplete */
  158. assert(visit_is_dealloc(v));
  159. ok = true;
  160. goto out_obj;
  161. }
  162. switch ((*obj)->type) {
  163. ''',
  164. c_name=c_name(name))
  165. for var in variants.variants:
  166. ret += gen_if(var.ifcond)
  167. ret += mcgen('''
  168. case %(case)s:
  169. ''',
  170. case=var.type.alternate_qtype())
  171. if isinstance(var.type, QAPISchemaObjectType):
  172. ret += mcgen('''
  173. if (!visit_start_struct(v, name, NULL, 0, errp)) {
  174. break;
  175. }
  176. if (visit_type_%(c_type)s_members(v, &(*obj)->u.%(c_name)s, errp)) {
  177. ok = visit_check_struct(v, errp);
  178. }
  179. visit_end_struct(v, NULL);
  180. ''',
  181. c_type=var.type.c_name(),
  182. c_name=c_name(var.name))
  183. else:
  184. ret += mcgen('''
  185. ok = visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, errp);
  186. ''',
  187. c_type=var.type.c_name(),
  188. c_name=c_name(var.name))
  189. ret += mcgen('''
  190. break;
  191. ''')
  192. ret += gen_endif(var.ifcond)
  193. ret += mcgen('''
  194. case QTYPE_NONE:
  195. abort();
  196. default:
  197. assert(visit_is_input(v));
  198. error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
  199. "%(name)s");
  200. /* Avoid passing invalid *obj to qapi_free_%(c_name)s() */
  201. g_free(*obj);
  202. *obj = NULL;
  203. }
  204. out_obj:
  205. visit_end_alternate(v, (void **)obj);
  206. if (!ok && visit_is_input(v)) {
  207. qapi_free_%(c_name)s(*obj);
  208. *obj = NULL;
  209. }
  210. return ok;
  211. }
  212. ''',
  213. name=name, c_name=c_name(name))
  214. return ret
  215. def gen_visit_object(name, base, members, variants):
  216. return mcgen('''
  217. bool visit_type_%(c_name)s(Visitor *v, const char *name,
  218. %(c_name)s **obj, Error **errp)
  219. {
  220. bool ok = false;
  221. if (!visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), errp)) {
  222. return false;
  223. }
  224. if (!*obj) {
  225. /* incomplete */
  226. assert(visit_is_dealloc(v));
  227. ok = true;
  228. goto out_obj;
  229. }
  230. if (!visit_type_%(c_name)s_members(v, *obj, errp)) {
  231. goto out_obj;
  232. }
  233. ok = visit_check_struct(v, errp);
  234. out_obj:
  235. visit_end_struct(v, (void **)obj);
  236. if (!ok && visit_is_input(v)) {
  237. qapi_free_%(c_name)s(*obj);
  238. *obj = NULL;
  239. }
  240. return ok;
  241. }
  242. ''',
  243. c_name=c_name(name))
  244. class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
  245. def __init__(self, prefix):
  246. super().__init__(
  247. prefix, 'qapi-visit', ' * Schema-defined QAPI visitors',
  248. ' * Built-in QAPI visitors', __doc__)
  249. def _begin_system_module(self, name):
  250. self._genc.preamble_add(mcgen('''
  251. #include "qemu/osdep.h"
  252. #include "qapi/error.h"
  253. #include "qapi/qapi-builtin-visit.h"
  254. '''))
  255. self._genh.preamble_add(mcgen('''
  256. #include "qapi/visitor.h"
  257. #include "qapi/qapi-builtin-types.h"
  258. '''))
  259. def _begin_user_module(self, name):
  260. types = self._module_basename('qapi-types', name)
  261. visit = self._module_basename('qapi-visit', name)
  262. self._genc.preamble_add(mcgen('''
  263. #include "qemu/osdep.h"
  264. #include "qapi/error.h"
  265. #include "qapi/qmp/qerror.h"
  266. #include "%(visit)s.h"
  267. ''',
  268. visit=visit))
  269. self._genh.preamble_add(mcgen('''
  270. #include "qapi/qapi-builtin-visit.h"
  271. #include "%(types)s.h"
  272. ''',
  273. types=types))
  274. def visit_enum_type(self, name, info, ifcond, features, members, prefix):
  275. with ifcontext(ifcond, self._genh, self._genc):
  276. self._genh.add(gen_visit_decl(name, scalar=True))
  277. self._genc.add(gen_visit_enum(name))
  278. def visit_array_type(self, name, info, ifcond, element_type):
  279. with ifcontext(ifcond, self._genh, self._genc):
  280. self._genh.add(gen_visit_decl(name))
  281. self._genc.add(gen_visit_list(name, element_type))
  282. def visit_object_type(self, name, info, ifcond, features,
  283. base, members, variants):
  284. # Nothing to do for the special empty builtin
  285. if name == 'q_empty':
  286. return
  287. with ifcontext(ifcond, self._genh, self._genc):
  288. self._genh.add(gen_visit_members_decl(name))
  289. self._genc.add(gen_visit_object_members(name, base,
  290. members, variants))
  291. # TODO Worth changing the visitor signature, so we could
  292. # directly use rather than repeat type.is_implicit()?
  293. if not name.startswith('q_'):
  294. # only explicit types need an allocating visit
  295. self._genh.add(gen_visit_decl(name))
  296. self._genc.add(gen_visit_object(name, base, members, variants))
  297. def visit_alternate_type(self, name, info, ifcond, features, variants):
  298. with ifcontext(ifcond, self._genh, self._genc):
  299. self._genh.add(gen_visit_decl(name))
  300. self._genc.add(gen_visit_alternate(name, variants))
  301. def gen_visit(schema, output_dir, prefix, opt_builtins):
  302. vis = QAPISchemaGenVisitVisitor(prefix)
  303. schema.visit(vis)
  304. vis.write(output_dir, opt_builtins)