]> git.proxmox.com Git - mirror_qemu.git/blame - scripts/qapi-visit.py
qapi-visit: Use common idiom in gen_visit_fields_decl()
[mirror_qemu.git] / scripts / qapi-visit.py
CommitLineData
06d64c62
MR
1#
2# QAPI visitor generator
3#
4# Copyright IBM, Corp. 2011
92b09bab 5# Copyright (C) 2014-2016 Red Hat, Inc.
06d64c62
MR
6#
7# Authors:
8# Anthony Liguori <aliguori@us.ibm.com>
9# Michael Roth <mdroth@linux.vnet.ibm.com>
297a3646 10# Markus Armbruster <armbru@redhat.com>
06d64c62 11#
678e48a2
MA
12# This work is licensed under the terms of the GNU GPL, version 2.
13# See the COPYING file in the top-level directory.
06d64c62 14
06d64c62 15from qapi import *
297a3646 16import re
06d64c62 17
d02cf377
EB
18# visit_type_FOO_implicit() is emitted as needed; track if it has already
19# been output.
8c07eddc 20implicit_structs_seen = set()
d02cf377
EB
21
22# visit_type_FOO_fields() is always emitted; track if a forward declaration
23# or implementation has already been output.
8c3f8e77 24struct_fields_seen = set()
be3c7717 25
e98859a9 26
60f8546a
MA
27def gen_visit_decl(name, scalar=False):
28 c_type = c_name(name) + ' *'
29 if not scalar:
30 c_type += '*'
31 return mcgen('''
51e72bc1 32void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_type)sobj, Error **errp);
60f8546a
MA
33''',
34 c_name=c_name(name), c_type=c_type)
35
36
d02cf377 37def gen_visit_fields_decl(typ):
2208d649
EB
38 if typ.name in struct_fields_seen:
39 return ''
40 struct_fields_seen.add(typ.name)
41 return mcgen('''
8c3f8e77 42
65551903 43static void visit_type_%(c_type)s_fields(Visitor *v, %(c_type)s *obj, Error **errp);
8c3f8e77 44''',
2208d649 45 c_type=typ.c_name())
d02cf377
EB
46
47
48def gen_visit_implicit_struct(typ):
49 if typ in implicit_structs_seen:
50 return ''
51 implicit_structs_seen.add(typ)
52
53 ret = gen_visit_fields_decl(typ)
8c3f8e77
MA
54
55 ret += mcgen('''
be3c7717 56
f8b7f1a8 57static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error **errp)
be3c7717
MA
58{
59 Error *err = NULL;
60
f8b7f1a8 61 visit_start_implicit_struct(v, (void **)obj, sizeof(%(c_type)s), &err);
be3c7717 62 if (!err) {
65551903 63 visit_type_%(c_type)s_fields(v, *obj, errp);
08f9541d 64 visit_end_implicit_struct(v);
be3c7717
MA
65 }
66 error_propagate(errp, err);
67}
68''',
e98859a9 69 c_type=typ.c_name())
8c3f8e77 70 return ret
be3c7717 71
e98859a9 72
9a5cd424 73def gen_visit_struct_fields(name, base, members, variants):
d131c897 74 ret = ''
50f2bdc7 75
be3c7717 76 if base:
ddf21908 77 ret += gen_visit_fields_decl(base)
9a5cd424
EB
78 if variants:
79 for var in variants.variants:
80 # Ugly special case for simple union TODO get rid of it
81 if not var.simple_union_type():
82 ret += gen_visit_implicit_struct(var.type)
be3c7717 83
ddf21908 84 struct_fields_seen.add(name)
50f2bdc7
KW
85 ret += mcgen('''
86
65551903 87static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s *obj, Error **errp)
50f2bdc7
KW
88{
89 Error *err = NULL;
3a864e7c 90
50f2bdc7 91''',
e98859a9 92 c_name=c_name(name))
d195325b 93
622f557f
KW
94 if base:
95 ret += mcgen('''
65551903 96 visit_type_%(c_type)s_fields(v, (%(c_type)s *)obj, &err);
622f557f 97''',
ddf21908 98 c_type=base.c_name())
1f353344 99 ret += gen_err_check()
622f557f 100
65551903 101 ret += gen_visit_fields(members, prefix='obj->')
d195325b 102
9a5cd424
EB
103 if variants:
104 ret += mcgen('''
65551903 105 if (!visit_start_union(v, !!obj->u.data, &err) || err) {
9a5cd424
EB
106 goto out;
107 }
65551903 108 switch (obj->%(c_name)s) {
9a5cd424
EB
109''',
110 c_name=c_name(variants.tag_member.name))
111
112 for var in variants.variants:
113 # TODO ugly special case for simple union
114 simple_union_type = var.simple_union_type()
115 ret += mcgen('''
116 case %(case)s:
117''',
118 case=c_enum_const(variants.tag_member.type.name,
119 var.name,
120 variants.tag_member.type.prefix))
121 if simple_union_type:
122 ret += mcgen('''
65551903 123 visit_type_%(c_type)s(v, "data", &obj->u.%(c_name)s, &err);
9a5cd424
EB
124''',
125 c_type=simple_union_type.c_name(),
126 c_name=c_name(var.name))
127 else:
128 ret += mcgen('''
65551903 129 visit_type_implicit_%(c_type)s(v, &obj->u.%(c_name)s, &err);
9a5cd424
EB
130''',
131 c_type=var.type.c_name(),
132 c_name=c_name(var.name))
133 ret += mcgen('''
134 break;
135''')
136
137 ret += mcgen('''
138 default:
139 abort();
140 }
141''')
142
143 # 'goto out' produced for base, by gen_visit_fields() for each member,
144 # and if variants were present
145 if base or members or variants:
297a3646 146 ret += mcgen('''
50f2bdc7 147
297a3646
MA
148out:
149''')
150 ret += mcgen('''
50f2bdc7
KW
151 error_propagate(errp, err);
152}
153''')
d131c897
KW
154 return ret
155
156
441cbac0 157def gen_visit_list(name, element_type):
dd5ee2c2
EB
158 # FIXME: if *obj is NULL on entry, and the first visit_next_list()
159 # assigns to *obj, while a later one fails, we should clean up *obj
160 # rather than leaving it non-NULL. As currently written, the caller must
161 # call qapi_free_FOOList() to avoid a memory leak of the partial FOOList.
06d64c62
MR
162 return mcgen('''
163
51e72bc1 164void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
06d64c62 165{
d195325b 166 Error *err = NULL;
297a3646 167 GenericList *i, **prev;
06d64c62 168
f8b7f1a8 169 visit_start_list(v, name, &err);
297a3646
MA
170 if (err) {
171 goto out;
172 }
173
174 for (prev = (GenericList **)obj;
e65d89bf 175 !err && (i = visit_next_list(v, prev, sizeof(**obj))) != NULL;
297a3646 176 prev = &i) {
e98859a9 177 %(c_name)s *native_i = (%(c_name)s *)i;
51e72bc1 178 visit_type_%(c_elt_type)s(v, NULL, &native_i->value, &err);
06d64c62 179 }
297a3646 180
08f9541d 181 visit_end_list(v);
297a3646
MA
182out:
183 error_propagate(errp, err);
06d64c62
MR
184}
185''',
e98859a9 186 c_name=c_name(name), c_elt_type=element_type.c_name())
06d64c62 187
e98859a9
MA
188
189def gen_visit_enum(name):
06d64c62
MR
190 return mcgen('''
191
51e72bc1 192void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s *obj, Error **errp)
06d64c62 193{
395a233f 194 int value = *obj;
337283df 195 visit_type_enum(v, name, &value, %(c_name)s_lookup, errp);
395a233f 196 *obj = value;
06d64c62
MR
197}
198''',
337283df 199 c_name=c_name(name))
06d64c62 200
e98859a9 201
441cbac0 202def gen_visit_alternate(name, variants):
d00341af
EB
203 promote_int = 'true'
204 for var in variants.variants:
205 if var.type.alternate_qtype() == 'QTYPE_QINT':
206 promote_int = 'false'
207
69dd62df
KW
208 ret = mcgen('''
209
51e72bc1 210void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
69dd62df
KW
211{
212 Error *err = NULL;
213
f8b7f1a8 214 visit_start_implicit_struct(v, (void**) obj, sizeof(%(c_name)s), &err);
297a3646
MA
215 if (err) {
216 goto out;
217 }
51e72bc1 218 visit_get_next_type(v, name, &(*obj)->type, %(promote_int)s, &err);
297a3646 219 if (err) {
f782399c 220 goto out_obj;
297a3646 221 }
150d0564 222 switch ((*obj)->type) {
69dd62df 223''',
d00341af 224 c_name=c_name(name), promote_int=promote_int)
69dd62df 225
441cbac0 226 for var in variants.variants:
69dd62df 227 ret += mcgen('''
e98859a9 228 case %(case)s:
51e72bc1 229 visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, &err);
297a3646 230 break;
69dd62df 231''',
0426d53c 232 case=var.type.alternate_qtype(),
e98859a9
MA
233 c_type=var.type.c_name(),
234 c_name=c_name(var.name))
69dd62df
KW
235
236 ret += mcgen('''
297a3646 237 default:
0426d53c
EB
238 error_setg(&err, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
239 "%(name)s");
69dd62df 240 }
f782399c 241out_obj:
08f9541d 242 visit_end_implicit_struct(v);
297a3646
MA
243out:
244 error_propagate(errp, err);
69dd62df 245}
0426d53c
EB
246''',
247 name=name)
69dd62df
KW
248
249 return ret
250
e98859a9 251
59d9e84c 252def gen_visit_object(name, base, members, variants):
9a5cd424 253 ret = gen_visit_struct_fields(name, base, members, variants)
be3c7717 254
59d9e84c
MA
255 # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
256 # *obj, but then visit_type_FOO_fields() fails, we should clean up *obj
257 # rather than leaving it non-NULL. As currently written, the caller must
258 # call qapi_free_FOO() to avoid a memory leak of the partial FOO.
06d64c62
MR
259 ret += mcgen('''
260
51e72bc1 261void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
06d64c62 262{
dc8fb6df
PB
263 Error *err = NULL;
264
337283df 265 visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), &err);
297a3646
MA
266 if (err) {
267 goto out;
268 }
e36c714e
EB
269 if (!*obj) {
270 goto out_obj;
271 }
65551903 272 visit_type_%(c_name)s_fields(v, *obj, &err);
e36c714e
EB
273 error_propagate(errp, err);
274 err = NULL;
9a5cd424 275out_obj:
f8b7f1a8 276 visit_end_struct(v, &err);
297a3646
MA
277out:
278 error_propagate(errp, err);
dc8fb6df 279}
9a5cd424
EB
280''',
281 c_name=c_name(name))
dc8fb6df 282
06d64c62
MR
283 return ret
284
e98859a9 285
441cbac0
MA
286class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
287 def __init__(self):
288 self.decl = None
289 self.defn = None
290 self._btin = None
291
292 def visit_begin(self, schema):
293 self.decl = ''
294 self.defn = ''
295 self._btin = guardstart('QAPI_VISIT_BUILTIN')
296
297 def visit_end(self):
298 # To avoid header dependency hell, we always generate
299 # declarations for built-in types in our header files and
300 # simply guard them. See also do_builtins (command line
301 # option -b).
302 self._btin += guardend('QAPI_VISIT_BUILTIN')
303 self.decl = self._btin + self.decl
304 self._btin = None
305
25a0d9c9
EB
306 def visit_needed(self, entity):
307 # Visit everything except implicit objects
49823c4b
EB
308 return not (entity.is_implicit() and
309 isinstance(entity, QAPISchemaObjectType))
25a0d9c9 310
441cbac0 311 def visit_enum_type(self, name, info, values, prefix):
7264f5c5
EB
312 # Special case for our lone builtin enum type
313 # TODO use something cleaner than existence of info
314 if not info:
315 self._btin += gen_visit_decl(name, scalar=True)
316 if do_builtins:
317 self.defn += gen_visit_enum(name)
318 else:
319 self.decl += gen_visit_decl(name, scalar=True)
320 self.defn += gen_visit_enum(name)
441cbac0
MA
321
322 def visit_array_type(self, name, info, element_type):
323 decl = gen_visit_decl(name)
324 defn = gen_visit_list(name, element_type)
325 if isinstance(element_type, QAPISchemaBuiltinType):
326 self._btin += decl
327 if do_builtins:
328 self.defn += defn
329 else:
330 self.decl += decl
331 self.defn += defn
332
333 def visit_object_type(self, name, info, base, members, variants):
25a0d9c9 334 self.decl += gen_visit_decl(name)
59d9e84c 335 self.defn += gen_visit_object(name, base, members, variants)
441cbac0
MA
336
337 def visit_alternate_type(self, name, info, variants):
338 self.decl += gen_visit_decl(name)
339 self.defn += gen_visit_alternate(name, variants)
340
341# If you link code generated from multiple schemata, you want only one
342# instance of the code for built-in types. Generate it only when
343# do_builtins, enabled by command line option -b. See also
344# QAPISchemaGenVisitVisitor.visit_end().
7c946bc4 345do_builtins = False
8d3bc517 346
2114f5a9
MA
347(input_file, output_dir, do_c, do_h, prefix, opts) = \
348 parse_command_line("b", ["builtins"])
349
06d64c62 350for o, a in opts:
2114f5a9 351 if o in ("-b", "--builtins"):
7c946bc4 352 do_builtins = True
8d3bc517 353
12f8e1b9 354c_comment = '''
06d64c62
MR
355/*
356 * schema-defined QAPI visitor functions
357 *
358 * Copyright IBM, Corp. 2011
359 *
360 * Authors:
361 * Anthony Liguori <aliguori@us.ibm.com>
362 *
363 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
364 * See the COPYING.LIB file in the top-level directory.
365 *
366 */
12f8e1b9
MA
367'''
368h_comment = '''
06d64c62 369/*
297a3646 370 * schema-defined QAPI visitor functions
06d64c62
MR
371 *
372 * Copyright IBM, Corp. 2011
373 *
374 * Authors:
375 * Anthony Liguori <aliguori@us.ibm.com>
376 *
377 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
378 * See the COPYING.LIB file in the top-level directory.
379 *
380 */
12f8e1b9
MA
381'''
382
383(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
384 'qapi-visit.c', 'qapi-visit.h',
385 c_comment, h_comment)
06d64c62 386
12f8e1b9 387fdef.write(mcgen('''
9167ebd9 388#include "qemu/osdep.h"
12f8e1b9
MA
389#include "qemu-common.h"
390#include "%(prefix)sqapi-visit.h"
391''',
e98859a9 392 prefix=prefix))
06d64c62 393
12f8e1b9 394fdecl.write(mcgen('''
7b1b5d19 395#include "qapi/visitor.h"
0426d53c 396#include "qapi/qmp/qerror.h"
06d64c62 397#include "%(prefix)sqapi-types.h"
7c946bc4 398
06d64c62 399''',
12f8e1b9 400 prefix=prefix))
06d64c62 401
441cbac0
MA
402schema = QAPISchema(input_file)
403gen = QAPISchemaGenVisitVisitor()
404schema.visit(gen)
405fdef.write(gen.defn)
406fdecl.write(gen.decl)
06d64c62 407
12f8e1b9 408close_output(fdef, fdecl)