visit.py 12 KB

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