qapi-visit: Replace list implicit_structs by set
[qemu/ar7.git] / scripts / qapi-visit.py
blob9fc040e0f67c3a1847da694bce3e76bfe61ef318
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_seen = set()
20 struct_fields_seen = set()
22 def generate_visit_implicit_struct(type):
23 if type in implicit_structs_seen:
24 return ''
25 implicit_structs_seen.add(type)
26 ret = ''
27 if type not in struct_fields_seen:
28 # Need a forward declaration
29 ret += mcgen('''
31 static void visit_type_%(c_type)s_fields(Visitor *m, %(c_type)s **obj, Error **errp);
32 ''',
33 c_type=type_name(type))
35 ret += mcgen('''
37 static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error **errp)
39 Error *err = NULL;
41 visit_start_implicit_struct(m, (void **)obj, sizeof(%(c_type)s), &err);
42 if (!err) {
43 visit_type_%(c_type)s_fields(m, obj, errp);
44 visit_end_implicit_struct(m, &err);
46 error_propagate(errp, err);
48 ''',
49 c_type=type_name(type))
50 return ret
52 def generate_visit_struct_fields(name, members, base = None):
53 struct_fields_seen.add(name)
55 ret = ''
57 if base:
58 ret += generate_visit_implicit_struct(base)
60 ret += mcgen('''
62 static void visit_type_%(name)s_fields(Visitor *m, %(name)s **obj, Error **errp)
64 Error *err = NULL;
65 ''',
66 name=c_name(name))
67 push_indent()
69 if base:
70 ret += mcgen('''
71 visit_type_implicit_%(type)s(m, &(*obj)->%(c_name)s, &err);
72 if (err) {
73 goto out;
75 ''',
76 type=type_name(base), c_name=c_name('base'))
78 for argname, argentry, optional in parse_args(members):
79 if optional:
80 ret += mcgen('''
81 visit_optional(m, &(*obj)->has_%(c_name)s, "%(name)s", &err);
82 if (!err && (*obj)->has_%(c_name)s) {
83 ''',
84 c_name=c_name(argname), name=argname)
85 push_indent()
87 ret += mcgen('''
88 visit_type_%(type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err);
89 ''',
90 type=type_name(argentry), c_name=c_name(argname),
91 name=argname)
93 if optional:
94 pop_indent()
95 ret += mcgen('''
97 ''')
98 ret += mcgen('''
99 if (err) {
100 goto out;
102 ''')
104 pop_indent()
105 if re.search('^ *goto out\\;', ret, re.MULTILINE):
106 ret += mcgen('''
108 out:
109 ''')
110 ret += mcgen('''
111 error_propagate(errp, err);
113 ''')
114 return ret
117 def generate_visit_struct_body(name):
118 ret = mcgen('''
119 Error *err = NULL;
121 visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
122 if (!err) {
123 if (*obj) {
124 visit_type_%(c_name)s_fields(m, obj, errp);
126 visit_end_struct(m, &err);
128 error_propagate(errp, err);
129 ''',
130 name=name, c_name=c_name(name))
132 return ret
134 def generate_visit_struct(expr):
136 name = expr['struct']
137 members = expr['data']
138 base = expr.get('base')
140 ret = generate_visit_struct_fields(name, members, base)
142 ret += mcgen('''
144 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
146 ''',
147 name=c_name(name))
149 ret += generate_visit_struct_body(name)
151 ret += mcgen('''
153 ''')
154 return ret
156 def generate_visit_list(name):
157 return mcgen('''
159 void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp)
161 Error *err = NULL;
162 GenericList *i, **prev;
164 visit_start_list(m, name, &err);
165 if (err) {
166 goto out;
169 for (prev = (GenericList **)obj;
170 !err && (i = visit_next_list(m, prev, &err)) != NULL;
171 prev = &i) {
172 %(name)sList *native_i = (%(name)sList *)i;
173 visit_type_%(name)s(m, &native_i->value, NULL, &err);
176 error_propagate(errp, err);
177 err = NULL;
178 visit_end_list(m, &err);
179 out:
180 error_propagate(errp, err);
182 ''',
183 name=type_name(name))
185 def generate_visit_enum(name):
186 return mcgen('''
188 void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp)
190 visit_type_enum(m, (int *)obj, %(name)s_lookup, "%(name)s", name, errp);
192 ''',
193 name=c_name(name))
195 def generate_visit_alternate(name, members):
196 ret = mcgen('''
198 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
200 Error *err = NULL;
202 visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err);
203 if (err) {
204 goto out;
206 visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err);
207 if (err) {
208 goto out_end;
210 switch ((*obj)->kind) {
211 ''',
212 name=c_name(name))
214 # For alternate, always use the default enum type automatically generated
215 # as name + 'Kind'
216 disc_type = c_name(name) + 'Kind'
218 for key in members:
219 assert (members[key] in builtin_types.keys()
220 or find_struct(members[key])
221 or find_union(members[key])
222 or find_enum(members[key])), "Invalid alternate member"
224 enum_full_value = c_enum_const(disc_type, key)
225 ret += mcgen('''
226 case %(enum_full_value)s:
227 visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, name, &err);
228 break;
229 ''',
230 enum_full_value = enum_full_value,
231 c_type = type_name(members[key]),
232 c_name = c_name(key))
234 ret += mcgen('''
235 default:
236 abort();
238 out_end:
239 error_propagate(errp, err);
240 err = NULL;
241 visit_end_implicit_struct(m, &err);
242 out:
243 error_propagate(errp, err);
245 ''')
247 return ret
250 def generate_visit_union(expr):
252 name = expr['union']
253 members = expr['data']
255 base = expr.get('base')
256 discriminator = expr.get('discriminator')
258 enum_define = discriminator_find_enum_define(expr)
259 if enum_define:
260 # Use the enum type as discriminator
261 ret = ""
262 disc_type = c_name(enum_define['enum_name'])
263 else:
264 # There will always be a discriminator in the C switch code, by default
265 # it is an enum type generated silently
266 ret = generate_visit_enum(name + 'Kind')
267 disc_type = c_name(name) + 'Kind'
269 if base:
270 assert discriminator
271 base_fields = find_struct(base)['data'].copy()
272 del base_fields[discriminator]
273 ret += generate_visit_struct_fields(name, base_fields)
275 if discriminator:
276 for key in members:
277 ret += generate_visit_implicit_struct(members[key])
279 ret += mcgen('''
281 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
283 Error *err = NULL;
285 visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
286 if (err) {
287 goto out;
289 if (*obj) {
290 ''',
291 name=c_name(name))
293 if base:
294 ret += mcgen('''
295 visit_type_%(name)s_fields(m, obj, &err);
296 if (err) {
297 goto out_obj;
299 ''',
300 name=c_name(name))
302 if not discriminator:
303 tag = 'kind'
304 disc_key = "type"
305 else:
306 tag = discriminator
307 disc_key = discriminator
308 ret += mcgen('''
309 visit_type_%(disc_type)s(m, &(*obj)->%(c_tag)s, "%(disc_key)s", &err);
310 if (err) {
311 goto out_obj;
313 if (!visit_start_union(m, !!(*obj)->data, &err) || err) {
314 goto out_obj;
316 switch ((*obj)->%(c_tag)s) {
317 ''',
318 disc_type = disc_type,
319 c_tag=c_name(tag),
320 disc_key = disc_key)
322 for key in members:
323 if not discriminator:
324 fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);'
325 else:
326 fmt = 'visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);'
328 enum_full_value = c_enum_const(disc_type, key)
329 ret += mcgen('''
330 case %(enum_full_value)s:
331 ''' + fmt + '''
332 break;
333 ''',
334 enum_full_value = enum_full_value,
335 c_type=type_name(members[key]),
336 c_name=c_name(key))
338 ret += mcgen('''
339 default:
340 abort();
342 out_obj:
343 error_propagate(errp, err);
344 err = NULL;
345 visit_end_union(m, !!(*obj)->data, &err);
346 error_propagate(errp, err);
347 err = NULL;
349 visit_end_struct(m, &err);
350 out:
351 error_propagate(errp, err);
353 ''')
355 return ret
357 def generate_declaration(name, builtin_type=False):
358 ret = ""
359 if not builtin_type:
360 name = c_name(name)
361 ret += mcgen('''
363 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp);
364 ''',
365 name=name)
367 ret += mcgen('''
368 void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp);
369 ''',
370 name=name)
372 return ret
374 def generate_enum_declaration(name):
375 ret = mcgen('''
376 void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp);
377 ''',
378 name=c_name(name))
380 return ret
382 def generate_decl_enum(name):
383 return mcgen('''
385 void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp);
386 ''',
387 name=c_name(name))
389 do_builtins = False
391 (input_file, output_dir, do_c, do_h, prefix, opts) = \
392 parse_command_line("b", ["builtins"])
394 for o, a in opts:
395 if o in ("-b", "--builtins"):
396 do_builtins = True
398 c_comment = '''
400 * schema-defined QAPI visitor functions
402 * Copyright IBM, Corp. 2011
404 * Authors:
405 * Anthony Liguori <aliguori@us.ibm.com>
407 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
408 * See the COPYING.LIB file in the top-level directory.
412 h_comment = '''
414 * schema-defined QAPI visitor functions
416 * Copyright IBM, Corp. 2011
418 * Authors:
419 * Anthony Liguori <aliguori@us.ibm.com>
421 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
422 * See the COPYING.LIB file in the top-level directory.
427 (fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
428 'qapi-visit.c', 'qapi-visit.h',
429 c_comment, h_comment)
431 fdef.write(mcgen('''
432 #include "qemu-common.h"
433 #include "%(prefix)sqapi-visit.h"
434 ''',
435 prefix = prefix))
437 fdecl.write(mcgen('''
438 #include "qapi/visitor.h"
439 #include "%(prefix)sqapi-types.h"
441 ''',
442 prefix=prefix))
444 exprs = parse_schema(input_file)
446 # to avoid header dependency hell, we always generate declarations
447 # for built-in types in our header files and simply guard them
448 fdecl.write(guardstart("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
449 for typename in builtin_types.keys():
450 fdecl.write(generate_declaration(typename, builtin_type=True))
451 fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
453 # ...this doesn't work for cases where we link in multiple objects that
454 # have the functions defined, so we use -b option to provide control
455 # over these cases
456 if do_builtins:
457 for typename in builtin_types.keys():
458 fdef.write(generate_visit_list(typename))
460 for expr in exprs:
461 if expr.has_key('struct'):
462 ret = generate_visit_struct(expr)
463 ret += generate_visit_list(expr['struct'])
464 fdef.write(ret)
466 ret = generate_declaration(expr['struct'])
467 fdecl.write(ret)
468 elif expr.has_key('union'):
469 ret = generate_visit_union(expr)
470 ret += generate_visit_list(expr['union'])
471 fdef.write(ret)
473 enum_define = discriminator_find_enum_define(expr)
474 ret = ""
475 if not enum_define:
476 ret = generate_decl_enum('%sKind' % expr['union'])
477 ret += generate_declaration(expr['union'])
478 fdecl.write(ret)
479 elif expr.has_key('alternate'):
480 ret = generate_visit_alternate(expr['alternate'], expr['data'])
481 ret += generate_visit_list(expr['alternate'])
482 fdef.write(ret)
484 ret = generate_decl_enum('%sKind' % expr['alternate'])
485 ret += generate_declaration(expr['alternate'])
486 fdecl.write(ret)
487 elif expr.has_key('enum'):
488 ret = generate_visit_list(expr['enum'])
489 ret += generate_visit_enum(expr['enum'])
490 fdef.write(ret)
492 ret = generate_decl_enum(expr['enum'])
493 ret += generate_enum_declaration(expr['enum'])
494 fdecl.write(ret)
496 close_output(fdef, fdecl)