Merge tag 'block-pull-request' of https://gitlab.com/stefanha/qemu into staging
[qemu/ar7.git] / scripts / qapi / visit.py
blob380fa197f589d6130e22a96cadc996aee560fdeb
1 """
2 QAPI visitor generator
4 Copyright IBM, Corp. 2011
5 Copyright (C) 2014-2018 Red Hat, Inc.
7 Authors:
8 Anthony Liguori <aliguori@us.ibm.com>
9 Michael Roth <mdroth@linux.vnet.ibm.com>
10 Markus Armbruster <armbru@redhat.com>
12 This work is licensed under the terms of the GNU GPL, version 2.
13 See the COPYING file in the top-level directory.
14 """
16 from typing import List, Optional
18 from .common import (
19 c_enum_const,
20 c_name,
21 indent,
22 mcgen,
24 from .gen import (
25 QAPISchemaModularCVisitor,
26 gen_special_features,
27 ifcontext,
29 from .schema import (
30 QAPISchema,
31 QAPISchemaEnumMember,
32 QAPISchemaEnumType,
33 QAPISchemaFeature,
34 QAPISchemaIfCond,
35 QAPISchemaObjectType,
36 QAPISchemaObjectTypeMember,
37 QAPISchemaType,
38 QAPISchemaVariants,
40 from .source import QAPISourceInfo
43 def gen_visit_decl(name: str, scalar: bool = False) -> str:
44 c_type = c_name(name) + ' *'
45 if not scalar:
46 c_type += '*'
47 return mcgen('''
49 bool visit_type_%(c_name)s(Visitor *v, const char *name,
50 %(c_type)sobj, Error **errp);
51 ''',
52 c_name=c_name(name), c_type=c_type)
55 def gen_visit_members_decl(name: str) -> str:
56 return mcgen('''
58 bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp);
59 ''',
60 c_name=c_name(name))
63 def gen_visit_object_members(name: str,
64 base: Optional[QAPISchemaObjectType],
65 members: List[QAPISchemaObjectTypeMember],
66 variants: Optional[QAPISchemaVariants]) -> str:
67 ret = mcgen('''
69 bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
71 ''',
72 c_name=c_name(name))
74 if base:
75 ret += mcgen('''
76 if (!visit_type_%(c_type)s_members(v, (%(c_type)s *)obj, errp)) {
77 return false;
79 ''',
80 c_type=base.c_name())
82 for memb in members:
83 ret += memb.ifcond.gen_if()
84 if memb.optional:
85 ret += mcgen('''
86 if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) {
87 ''',
88 name=memb.name, c_name=c_name(memb.name))
89 indent.increase()
90 special_features = gen_special_features(memb.features)
91 if special_features != '0':
92 ret += mcgen('''
93 if (visit_policy_reject(v, "%(name)s", %(special_features)s, errp)) {
94 return false;
96 if (!visit_policy_skip(v, "%(name)s", %(special_features)s)) {
97 ''',
98 name=memb.name, special_features=special_features)
99 indent.increase()
100 ret += mcgen('''
101 if (!visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, errp)) {
102 return false;
104 ''',
105 c_type=memb.type.c_name(), name=memb.name,
106 c_name=c_name(memb.name))
107 if special_features != '0':
108 indent.decrease()
109 ret += mcgen('''
111 ''')
112 if memb.optional:
113 indent.decrease()
114 ret += mcgen('''
116 ''')
117 ret += memb.ifcond.gen_endif()
119 if variants:
120 tag_member = variants.tag_member
121 assert isinstance(tag_member.type, QAPISchemaEnumType)
123 ret += mcgen('''
124 switch (obj->%(c_name)s) {
125 ''',
126 c_name=c_name(tag_member.name))
128 for var in variants.variants:
129 case_str = c_enum_const(tag_member.type.name, var.name,
130 tag_member.type.prefix)
131 ret += var.ifcond.gen_if()
132 if var.type.name == 'q_empty':
133 # valid variant and nothing to do
134 ret += mcgen('''
135 case %(case)s:
136 break;
137 ''',
138 case=case_str)
139 else:
140 ret += mcgen('''
141 case %(case)s:
142 return visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, errp);
143 ''',
144 case=case_str,
145 c_type=var.type.c_name(), c_name=c_name(var.name))
147 ret += var.ifcond.gen_endif()
148 ret += mcgen('''
149 default:
150 abort();
152 ''')
154 ret += mcgen('''
155 return true;
157 ''')
158 return ret
161 def gen_visit_list(name: str, element_type: QAPISchemaType) -> str:
162 return mcgen('''
164 bool visit_type_%(c_name)s(Visitor *v, const char *name,
165 %(c_name)s **obj, Error **errp)
167 bool ok = false;
168 %(c_name)s *tail;
169 size_t size = sizeof(**obj);
171 if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) {
172 return false;
175 for (tail = *obj; tail;
176 tail = (%(c_name)s *)visit_next_list(v, (GenericList *)tail, size)) {
177 if (!visit_type_%(c_elt_type)s(v, NULL, &tail->value, errp)) {
178 goto out_obj;
182 ok = visit_check_list(v, errp);
183 out_obj:
184 visit_end_list(v, (void **)obj);
185 if (!ok && visit_is_input(v)) {
186 qapi_free_%(c_name)s(*obj);
187 *obj = NULL;
189 return ok;
191 ''',
192 c_name=c_name(name), c_elt_type=element_type.c_name())
195 def gen_visit_enum(name: str) -> str:
196 return mcgen('''
198 bool visit_type_%(c_name)s(Visitor *v, const char *name,
199 %(c_name)s *obj, Error **errp)
201 int value = *obj;
202 bool ok = visit_type_enum(v, name, &value, &%(c_name)s_lookup, errp);
203 *obj = value;
204 return ok;
206 ''',
207 c_name=c_name(name))
210 def gen_visit_alternate(name: str, variants: QAPISchemaVariants) -> str:
211 ret = mcgen('''
213 bool visit_type_%(c_name)s(Visitor *v, const char *name,
214 %(c_name)s **obj, Error **errp)
216 bool ok = false;
218 if (!visit_start_alternate(v, name, (GenericAlternate **)obj,
219 sizeof(**obj), errp)) {
220 return false;
222 if (!*obj) {
223 /* incomplete */
224 assert(visit_is_dealloc(v));
225 ok = true;
226 goto out_obj;
228 switch ((*obj)->type) {
229 ''',
230 c_name=c_name(name))
232 for var in variants.variants:
233 ret += var.ifcond.gen_if()
234 ret += mcgen('''
235 case %(case)s:
236 ''',
237 case=var.type.alternate_qtype())
238 if isinstance(var.type, QAPISchemaObjectType):
239 ret += mcgen('''
240 if (!visit_start_struct(v, name, NULL, 0, errp)) {
241 break;
243 if (visit_type_%(c_type)s_members(v, &(*obj)->u.%(c_name)s, errp)) {
244 ok = visit_check_struct(v, errp);
246 visit_end_struct(v, NULL);
247 ''',
248 c_type=var.type.c_name(),
249 c_name=c_name(var.name))
250 else:
251 ret += mcgen('''
252 ok = visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, errp);
253 ''',
254 c_type=var.type.c_name(),
255 c_name=c_name(var.name))
256 ret += mcgen('''
257 break;
258 ''')
259 ret += var.ifcond.gen_endif()
261 ret += mcgen('''
262 case QTYPE_NONE:
263 abort();
264 default:
265 assert(visit_is_input(v));
266 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
267 "%(name)s");
268 /* Avoid passing invalid *obj to qapi_free_%(c_name)s() */
269 g_free(*obj);
270 *obj = NULL;
272 out_obj:
273 visit_end_alternate(v, (void **)obj);
274 if (!ok && visit_is_input(v)) {
275 qapi_free_%(c_name)s(*obj);
276 *obj = NULL;
278 return ok;
280 ''',
281 name=name, c_name=c_name(name))
283 return ret
286 def gen_visit_object(name: str) -> str:
287 return mcgen('''
289 bool visit_type_%(c_name)s(Visitor *v, const char *name,
290 %(c_name)s **obj, Error **errp)
292 bool ok = false;
294 if (!visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), errp)) {
295 return false;
297 if (!*obj) {
298 /* incomplete */
299 assert(visit_is_dealloc(v));
300 ok = true;
301 goto out_obj;
303 if (!visit_type_%(c_name)s_members(v, *obj, errp)) {
304 goto out_obj;
306 ok = visit_check_struct(v, errp);
307 out_obj:
308 visit_end_struct(v, (void **)obj);
309 if (!ok && visit_is_input(v)) {
310 qapi_free_%(c_name)s(*obj);
311 *obj = NULL;
313 return ok;
315 ''',
316 c_name=c_name(name))
319 class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
321 def __init__(self, prefix: str):
322 super().__init__(
323 prefix, 'qapi-visit', ' * Schema-defined QAPI visitors',
324 ' * Built-in QAPI visitors', __doc__)
326 def _begin_builtin_module(self) -> None:
327 self._genc.preamble_add(mcgen('''
328 #include "qemu/osdep.h"
329 #include "qapi/error.h"
330 #include "qapi/qapi-builtin-visit.h"
331 '''))
332 self._genh.preamble_add(mcgen('''
333 #include "qapi/visitor.h"
334 #include "qapi/qapi-builtin-types.h"
336 '''))
338 def _begin_user_module(self, name: str) -> None:
339 types = self._module_basename('qapi-types', name)
340 visit = self._module_basename('qapi-visit', name)
341 self._genc.preamble_add(mcgen('''
342 #include "qemu/osdep.h"
343 #include "qapi/error.h"
344 #include "qapi/qmp/qerror.h"
345 #include "%(visit)s.h"
346 ''',
347 visit=visit))
348 self._genh.preamble_add(mcgen('''
349 #include "qapi/qapi-builtin-visit.h"
350 #include "%(types)s.h"
352 ''',
353 types=types))
355 def visit_enum_type(self,
356 name: str,
357 info: Optional[QAPISourceInfo],
358 ifcond: QAPISchemaIfCond,
359 features: List[QAPISchemaFeature],
360 members: List[QAPISchemaEnumMember],
361 prefix: Optional[str]) -> None:
362 with ifcontext(ifcond, self._genh, self._genc):
363 self._genh.add(gen_visit_decl(name, scalar=True))
364 self._genc.add(gen_visit_enum(name))
366 def visit_array_type(self,
367 name: str,
368 info: Optional[QAPISourceInfo],
369 ifcond: QAPISchemaIfCond,
370 element_type: QAPISchemaType) -> None:
371 with ifcontext(ifcond, self._genh, self._genc):
372 self._genh.add(gen_visit_decl(name))
373 self._genc.add(gen_visit_list(name, element_type))
375 def visit_object_type(self,
376 name: str,
377 info: Optional[QAPISourceInfo],
378 ifcond: QAPISchemaIfCond,
379 features: List[QAPISchemaFeature],
380 base: Optional[QAPISchemaObjectType],
381 members: List[QAPISchemaObjectTypeMember],
382 variants: Optional[QAPISchemaVariants]) -> None:
383 # Nothing to do for the special empty builtin
384 if name == 'q_empty':
385 return
386 with ifcontext(ifcond, self._genh, self._genc):
387 self._genh.add(gen_visit_members_decl(name))
388 self._genc.add(gen_visit_object_members(name, base,
389 members, variants))
390 # TODO Worth changing the visitor signature, so we could
391 # directly use rather than repeat type.is_implicit()?
392 if not name.startswith('q_'):
393 # only explicit types need an allocating visit
394 self._genh.add(gen_visit_decl(name))
395 self._genc.add(gen_visit_object(name))
397 def visit_alternate_type(self,
398 name: str,
399 info: Optional[QAPISourceInfo],
400 ifcond: QAPISchemaIfCond,
401 features: List[QAPISchemaFeature],
402 variants: QAPISchemaVariants) -> None:
403 with ifcontext(ifcond, self._genh, self._genc):
404 self._genh.add(gen_visit_decl(name))
405 self._genc.add(gen_visit_alternate(name, variants))
408 def gen_visit(schema: QAPISchema,
409 output_dir: str,
410 prefix: str,
411 opt_builtins: bool) -> None:
412 vis = QAPISchemaGenVisitVisitor(prefix)
413 schema.visit(vis)
414 vis.write(output_dir, opt_builtins)