qapi-visit: Fix generated code when schema has forward refs
[qemu/kevin.git] / scripts / qapi-visit.py
blobb3a308fb51ad5e8cb22db94f37bb3d6d83b75c82
2 # QAPI visitor generator
4 # Copyright IBM, Corp. 2011
5 # Copyright (C) 2014-2015 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.
15 from ordereddict import OrderedDict
16 from qapi import *
17 import re
19 implicit_structs = []
20 struct_fields_seen = set()
22 def generate_visit_implicit_struct(type):
23 global implicit_structs
24 if type in implicit_structs:
25 return ''
26 implicit_structs.append(type)
27 ret = ''
28 if type not in struct_fields_seen:
29 # Need a forward declaration
30 ret += mcgen('''
32 static void visit_type_%(c_type)s_fields(Visitor *m, %(c_type)s **obj, Error **errp);
33 ''',
34 c_type=type_name(type))
36 ret += mcgen('''
38 static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error **errp)
40 Error *err = NULL;
42 visit_start_implicit_struct(m, (void **)obj, sizeof(%(c_type)s), &err);
43 if (!err) {
44 visit_type_%(c_type)s_fields(m, obj, errp);
45 visit_end_implicit_struct(m, &err);
47 error_propagate(errp, err);
49 ''',
50 c_type=type_name(type))
51 return ret
53 def generate_visit_struct_fields(name, members, base = None):
54 struct_fields_seen.add(name)
56 ret = ''
58 if base:
59 ret += generate_visit_implicit_struct(base)
61 ret += mcgen('''
63 static void visit_type_%(name)s_fields(Visitor *m, %(name)s **obj, Error **errp)
65 Error *err = NULL;
66 ''',
67 name=c_name(name))
68 push_indent()
70 if base:
71 ret += mcgen('''
72 visit_type_implicit_%(type)s(m, &(*obj)->%(c_name)s, &err);
73 if (err) {
74 goto out;
76 ''',
77 type=type_name(base), c_name=c_name('base'))
79 for argname, argentry, optional in parse_args(members):
80 if optional:
81 ret += mcgen('''
82 visit_optional(m, &(*obj)->has_%(c_name)s, "%(name)s", &err);
83 if (!err && (*obj)->has_%(c_name)s) {
84 ''',
85 c_name=c_name(argname), name=argname)
86 push_indent()
88 ret += mcgen('''
89 visit_type_%(type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err);
90 ''',
91 type=type_name(argentry), c_name=c_name(argname),
92 name=argname)
94 if optional:
95 pop_indent()
96 ret += mcgen('''
98 ''')
99 ret += mcgen('''
100 if (err) {
101 goto out;
103 ''')
105 pop_indent()
106 if re.search('^ *goto out\\;', ret, re.MULTILINE):
107 ret += mcgen('''
109 out:
110 ''')
111 ret += mcgen('''
112 error_propagate(errp, err);
114 ''')
115 return ret
118 def generate_visit_struct_body(name):
119 ret = mcgen('''
120 Error *err = NULL;
122 visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
123 if (!err) {
124 if (*obj) {
125 visit_type_%(c_name)s_fields(m, obj, errp);
127 visit_end_struct(m, &err);
129 error_propagate(errp, err);
130 ''',
131 name=name, c_name=c_name(name))
133 return ret
135 def generate_visit_struct(expr):
137 name = expr['struct']
138 members = expr['data']
139 base = expr.get('base')
141 ret = generate_visit_struct_fields(name, members, base)
143 ret += mcgen('''
145 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
147 ''',
148 name=c_name(name))
150 ret += generate_visit_struct_body(name)
152 ret += mcgen('''
154 ''')
155 return ret
157 def generate_visit_list(name):
158 return mcgen('''
160 void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp)
162 Error *err = NULL;
163 GenericList *i, **prev;
165 visit_start_list(m, name, &err);
166 if (err) {
167 goto out;
170 for (prev = (GenericList **)obj;
171 !err && (i = visit_next_list(m, prev, &err)) != NULL;
172 prev = &i) {
173 %(name)sList *native_i = (%(name)sList *)i;
174 visit_type_%(name)s(m, &native_i->value, NULL, &err);
177 error_propagate(errp, err);
178 err = NULL;
179 visit_end_list(m, &err);
180 out:
181 error_propagate(errp, err);
183 ''',
184 name=type_name(name))
186 def generate_visit_enum(name):
187 return mcgen('''
189 void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp)
191 visit_type_enum(m, (int *)obj, %(name)s_lookup, "%(name)s", name, errp);
193 ''',
194 name=c_name(name))
196 def generate_visit_alternate(name, members):
197 ret = mcgen('''
199 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
201 Error *err = NULL;
203 visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err);
204 if (err) {
205 goto out;
207 visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err);
208 if (err) {
209 goto out_end;
211 switch ((*obj)->kind) {
212 ''',
213 name=c_name(name))
215 # For alternate, always use the default enum type automatically generated
216 # as name + 'Kind'
217 disc_type = c_name(name) + 'Kind'
219 for key in members:
220 assert (members[key] in builtin_types.keys()
221 or find_struct(members[key])
222 or find_union(members[key])
223 or find_enum(members[key])), "Invalid alternate member"
225 enum_full_value = c_enum_const(disc_type, key)
226 ret += mcgen('''
227 case %(enum_full_value)s:
228 visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, name, &err);
229 break;
230 ''',
231 enum_full_value = enum_full_value,
232 c_type = type_name(members[key]),
233 c_name = c_name(key))
235 ret += mcgen('''
236 default:
237 abort();
239 out_end:
240 error_propagate(errp, err);
241 err = NULL;
242 visit_end_implicit_struct(m, &err);
243 out:
244 error_propagate(errp, err);
246 ''')
248 return ret
251 def generate_visit_union(expr):
253 name = expr['union']
254 members = expr['data']
256 base = expr.get('base')
257 discriminator = expr.get('discriminator')
259 enum_define = discriminator_find_enum_define(expr)
260 if enum_define:
261 # Use the enum type as discriminator
262 ret = ""
263 disc_type = c_name(enum_define['enum_name'])
264 else:
265 # There will always be a discriminator in the C switch code, by default
266 # it is an enum type generated silently
267 ret = generate_visit_enum(name + 'Kind')
268 disc_type = c_name(name) + 'Kind'
270 if base:
271 assert discriminator
272 base_fields = find_struct(base)['data'].copy()
273 del base_fields[discriminator]
274 ret += generate_visit_struct_fields(name, base_fields)
276 if discriminator:
277 for key in members:
278 ret += generate_visit_implicit_struct(members[key])
280 ret += mcgen('''
282 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
284 Error *err = NULL;
286 visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
287 if (err) {
288 goto out;
290 if (*obj) {
291 ''',
292 name=c_name(name))
294 if base:
295 ret += mcgen('''
296 visit_type_%(name)s_fields(m, obj, &err);
297 if (err) {
298 goto out_obj;
300 ''',
301 name=c_name(name))
303 if not discriminator:
304 tag = 'kind'
305 disc_key = "type"
306 else:
307 tag = discriminator
308 disc_key = discriminator
309 ret += mcgen('''
310 visit_type_%(disc_type)s(m, &(*obj)->%(c_tag)s, "%(disc_key)s", &err);
311 if (err) {
312 goto out_obj;
314 if (!visit_start_union(m, !!(*obj)->data, &err) || err) {
315 goto out_obj;
317 switch ((*obj)->%(c_tag)s) {
318 ''',
319 disc_type = disc_type,
320 c_tag=c_name(tag),
321 disc_key = disc_key)
323 for key in members:
324 if not discriminator:
325 fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);'
326 else:
327 fmt = 'visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);'
329 enum_full_value = c_enum_const(disc_type, key)
330 ret += mcgen('''
331 case %(enum_full_value)s:
332 ''' + fmt + '''
333 break;
334 ''',
335 enum_full_value = enum_full_value,
336 c_type=type_name(members[key]),
337 c_name=c_name(key))
339 ret += mcgen('''
340 default:
341 abort();
343 out_obj:
344 error_propagate(errp, err);
345 err = NULL;
346 visit_end_union(m, !!(*obj)->data, &err);
347 error_propagate(errp, err);
348 err = NULL;
350 visit_end_struct(m, &err);
351 out:
352 error_propagate(errp, err);
354 ''')
356 return ret
358 def generate_declaration(name, builtin_type=False):
359 ret = ""
360 if not builtin_type:
361 name = c_name(name)
362 ret += mcgen('''
364 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp);
365 ''',
366 name=name)
368 ret += mcgen('''
369 void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp);
370 ''',
371 name=name)
373 return ret
375 def generate_enum_declaration(name):
376 ret = mcgen('''
377 void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp);
378 ''',
379 name=c_name(name))
381 return ret
383 def generate_decl_enum(name):
384 return mcgen('''
386 void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp);
387 ''',
388 name=c_name(name))
390 do_builtins = False
392 (input_file, output_dir, do_c, do_h, prefix, opts) = \
393 parse_command_line("b", ["builtins"])
395 for o, a in opts:
396 if o in ("-b", "--builtins"):
397 do_builtins = True
399 c_comment = '''
401 * schema-defined QAPI visitor functions
403 * Copyright IBM, Corp. 2011
405 * Authors:
406 * Anthony Liguori <aliguori@us.ibm.com>
408 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
409 * See the COPYING.LIB file in the top-level directory.
413 h_comment = '''
415 * schema-defined QAPI visitor functions
417 * Copyright IBM, Corp. 2011
419 * Authors:
420 * Anthony Liguori <aliguori@us.ibm.com>
422 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
423 * See the COPYING.LIB file in the top-level directory.
428 (fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
429 'qapi-visit.c', 'qapi-visit.h',
430 c_comment, h_comment)
432 fdef.write(mcgen('''
433 #include "qemu-common.h"
434 #include "%(prefix)sqapi-visit.h"
435 ''',
436 prefix = prefix))
438 fdecl.write(mcgen('''
439 #include "qapi/visitor.h"
440 #include "%(prefix)sqapi-types.h"
442 ''',
443 prefix=prefix))
445 exprs = parse_schema(input_file)
447 # to avoid header dependency hell, we always generate declarations
448 # for built-in types in our header files and simply guard them
449 fdecl.write(guardstart("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
450 for typename in builtin_types.keys():
451 fdecl.write(generate_declaration(typename, builtin_type=True))
452 fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
454 # ...this doesn't work for cases where we link in multiple objects that
455 # have the functions defined, so we use -b option to provide control
456 # over these cases
457 if do_builtins:
458 for typename in builtin_types.keys():
459 fdef.write(generate_visit_list(typename))
461 for expr in exprs:
462 if expr.has_key('struct'):
463 ret = generate_visit_struct(expr)
464 ret += generate_visit_list(expr['struct'])
465 fdef.write(ret)
467 ret = generate_declaration(expr['struct'])
468 fdecl.write(ret)
469 elif expr.has_key('union'):
470 ret = generate_visit_union(expr)
471 ret += generate_visit_list(expr['union'])
472 fdef.write(ret)
474 enum_define = discriminator_find_enum_define(expr)
475 ret = ""
476 if not enum_define:
477 ret = generate_decl_enum('%sKind' % expr['union'])
478 ret += generate_declaration(expr['union'])
479 fdecl.write(ret)
480 elif expr.has_key('alternate'):
481 ret = generate_visit_alternate(expr['alternate'], expr['data'])
482 ret += generate_visit_list(expr['alternate'])
483 fdef.write(ret)
485 ret = generate_decl_enum('%sKind' % expr['alternate'])
486 ret += generate_declaration(expr['alternate'])
487 fdecl.write(ret)
488 elif expr.has_key('enum'):
489 ret = generate_visit_list(expr['enum'])
490 ret += generate_visit_enum(expr['enum'])
491 fdef.write(ret)
493 ret = generate_decl_enum(expr['enum'])
494 ret += generate_enum_declaration(expr['enum'])
495 fdecl.write(ret)
497 close_output(fdef, fdecl)