visit.py 12 KB

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