]> Git Repo - qemu.git/blob - scripts/qapi-visit.py
qapi: Document that input visitor semantics are prone to leaks
[qemu.git] / scripts / qapi-visit.py
1 #
2 # QAPI visitor generator
3 #
4 # Copyright IBM, Corp. 2011
5 # Copyright (C) 2014-2015 Red Hat, Inc.
6 #
7 # Authors:
8 #  Anthony Liguori <[email protected]>
9 #  Michael Roth    <[email protected]>
10 #  Markus Armbruster <[email protected]>
11 #
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
15 from ordereddict import OrderedDict
16 from qapi import *
17 import re
18
19 implicit_structs_seen = set()
20 struct_fields_seen = set()
21
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('''
30
31 static void visit_type_%(c_type)s_fields(Visitor *m, %(c_type)s **obj, Error **errp);
32 ''',
33                      c_type=type_name(type))
34
35     ret += mcgen('''
36
37 static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error **errp)
38 {
39     Error *err = NULL;
40
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);
45     }
46     error_propagate(errp, err);
47 }
48 ''',
49                  c_type=type_name(type))
50     return ret
51
52 def generate_visit_struct_fields(name, members, base = None):
53     struct_fields_seen.add(name)
54
55     ret = ''
56
57     if base:
58         ret += generate_visit_implicit_struct(base)
59
60     ret += mcgen('''
61
62 static void visit_type_%(name)s_fields(Visitor *m, %(name)s **obj, Error **errp)
63 {
64     Error *err = NULL;
65 ''',
66                  name=c_name(name))
67     push_indent()
68
69     if base:
70         ret += mcgen('''
71 visit_type_implicit_%(type)s(m, &(*obj)->%(c_name)s, &err);
72 if (err) {
73     goto out;
74 }
75 ''',
76                      type=type_name(base), c_name=c_name('base'))
77
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()
86
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)
92
93         if optional:
94             pop_indent()
95             ret += mcgen('''
96 }
97 ''')
98         ret += mcgen('''
99 if (err) {
100     goto out;
101 }
102 ''')
103
104     pop_indent()
105     if re.search('^ *goto out\\;', ret, re.MULTILINE):
106         ret += mcgen('''
107
108 out:
109 ''')
110     ret += mcgen('''
111     error_propagate(errp, err);
112 }
113 ''')
114     return ret
115
116
117 def generate_visit_struct_body(name):
118     # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
119     # *obj, but then visit_type_FOO_fields() fails, we should clean up *obj
120     # rather than leaving it non-NULL. As currently written, the caller must
121     # call qapi_free_FOO() to avoid a memory leak of the partial FOO.
122     ret = mcgen('''
123     Error *err = NULL;
124
125     visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
126     if (!err) {
127         if (*obj) {
128             visit_type_%(c_name)s_fields(m, obj, errp);
129         }
130         visit_end_struct(m, &err);
131     }
132     error_propagate(errp, err);
133 ''',
134                 name=name, c_name=c_name(name))
135
136     return ret
137
138 def generate_visit_struct(expr):
139
140     name = expr['struct']
141     members = expr['data']
142     base = expr.get('base')
143
144     ret = generate_visit_struct_fields(name, members, base)
145
146     ret += mcgen('''
147
148 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
149 {
150 ''',
151                  name=c_name(name))
152
153     ret += generate_visit_struct_body(name)
154
155     ret += mcgen('''
156 }
157 ''')
158     return ret
159
160 def generate_visit_list(name):
161     return mcgen('''
162
163 void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp)
164 {
165     Error *err = NULL;
166     GenericList *i, **prev;
167
168     visit_start_list(m, name, &err);
169     if (err) {
170         goto out;
171     }
172
173     for (prev = (GenericList **)obj;
174          !err && (i = visit_next_list(m, prev, &err)) != NULL;
175          prev = &i) {
176         %(name)sList *native_i = (%(name)sList *)i;
177         visit_type_%(name)s(m, &native_i->value, NULL, &err);
178     }
179
180     error_propagate(errp, err);
181     err = NULL;
182     visit_end_list(m, &err);
183 out:
184     error_propagate(errp, err);
185 }
186 ''',
187                 name=type_name(name))
188
189 def generate_visit_enum(name):
190     return mcgen('''
191
192 void visit_type_%(c_name)s(Visitor *m, %(c_name)s *obj, const char *name, Error **errp)
193 {
194     visit_type_enum(m, (int *)obj, %(c_name)s_lookup, "%(name)s", name, errp);
195 }
196 ''',
197                  c_name=c_name(name), name=name)
198
199 def generate_visit_alternate(name, members):
200     ret = mcgen('''
201
202 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
203 {
204     Error *err = NULL;
205
206     visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err);
207     if (err) {
208         goto out;
209     }
210     visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err);
211     if (err) {
212         goto out_end;
213     }
214     switch ((*obj)->kind) {
215 ''',
216                 name=c_name(name))
217
218     # For alternate, always use the default enum type automatically generated
219     # as name + 'Kind'
220     disc_type = c_name(name) + 'Kind'
221
222     for key in members:
223         assert (members[key] in builtin_types.keys()
224             or find_struct(members[key])
225             or find_union(members[key])
226             or find_enum(members[key])), "Invalid alternate member"
227
228         enum_full_value = c_enum_const(disc_type, key)
229         ret += mcgen('''
230     case %(enum_full_value)s:
231         visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, name, &err);
232         break;
233 ''',
234                 enum_full_value = enum_full_value,
235                 c_type = type_name(members[key]),
236                 c_name = c_name(key))
237
238     ret += mcgen('''
239     default:
240         abort();
241     }
242 out_end:
243     error_propagate(errp, err);
244     err = NULL;
245     visit_end_implicit_struct(m, &err);
246 out:
247     error_propagate(errp, err);
248 }
249 ''')
250
251     return ret
252
253
254 def generate_visit_union(expr):
255
256     name = expr['union']
257     members = expr['data']
258
259     base = expr.get('base')
260     discriminator = expr.get('discriminator')
261
262     enum_define = discriminator_find_enum_define(expr)
263     if enum_define:
264         # Use the enum type as discriminator
265         ret = ""
266         disc_type = c_name(enum_define['enum_name'])
267     else:
268         # There will always be a discriminator in the C switch code, by default
269         # it is an enum type generated silently
270         ret = generate_visit_enum(name + 'Kind')
271         disc_type = c_name(name) + 'Kind'
272
273     if base:
274         assert discriminator
275         base_fields = find_struct(base)['data'].copy()
276         del base_fields[discriminator]
277         ret += generate_visit_struct_fields(name, base_fields)
278
279     if discriminator:
280         for key in members:
281             ret += generate_visit_implicit_struct(members[key])
282
283     ret += mcgen('''
284
285 void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp)
286 {
287     Error *err = NULL;
288
289     visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
290     if (err) {
291         goto out;
292     }
293     if (*obj) {
294 ''',
295                  c_name=c_name(name), name=name)
296
297     if base:
298         ret += mcgen('''
299         visit_type_%(name)s_fields(m, obj, &err);
300         if (err) {
301             goto out_obj;
302         }
303 ''',
304                      name=c_name(name))
305
306     if not discriminator:
307         tag = 'kind'
308         disc_key = "type"
309     else:
310         tag = discriminator
311         disc_key = discriminator
312     ret += mcgen('''
313         visit_type_%(disc_type)s(m, &(*obj)->%(c_tag)s, "%(disc_key)s", &err);
314         if (err) {
315             goto out_obj;
316         }
317         if (!visit_start_union(m, !!(*obj)->data, &err) || err) {
318             goto out_obj;
319         }
320         switch ((*obj)->%(c_tag)s) {
321 ''',
322                  disc_type = disc_type,
323                  c_tag=c_name(tag),
324                  disc_key = disc_key)
325
326     for key in members:
327         if not discriminator:
328             fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);'
329         else:
330             fmt = 'visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);'
331
332         enum_full_value = c_enum_const(disc_type, key)
333         ret += mcgen('''
334         case %(enum_full_value)s:
335             ''' + fmt + '''
336             break;
337 ''',
338                 enum_full_value = enum_full_value,
339                 c_type=type_name(members[key]),
340                 c_name=c_name(key))
341
342     ret += mcgen('''
343         default:
344             abort();
345         }
346 out_obj:
347         error_propagate(errp, err);
348         err = NULL;
349         visit_end_union(m, !!(*obj)->data, &err);
350         error_propagate(errp, err);
351         err = NULL;
352     }
353     visit_end_struct(m, &err);
354 out:
355     error_propagate(errp, err);
356 }
357 ''')
358
359     return ret
360
361 def generate_declaration(name, builtin_type=False):
362     ret = ""
363     if not builtin_type:
364         name = c_name(name)
365         ret += mcgen('''
366
367 void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp);
368 ''',
369                      name=name)
370
371     ret += mcgen('''
372 void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp);
373 ''',
374                  name=name)
375
376     return ret
377
378 def generate_enum_declaration(name):
379     ret = mcgen('''
380 void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp);
381 ''',
382                 name=c_name(name))
383
384     return ret
385
386 def generate_decl_enum(name):
387     return mcgen('''
388
389 void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp);
390 ''',
391                  name=c_name(name))
392
393 do_builtins = False
394
395 (input_file, output_dir, do_c, do_h, prefix, opts) = \
396     parse_command_line("b", ["builtins"])
397
398 for o, a in opts:
399     if o in ("-b", "--builtins"):
400         do_builtins = True
401
402 c_comment = '''
403 /*
404  * schema-defined QAPI visitor functions
405  *
406  * Copyright IBM, Corp. 2011
407  *
408  * Authors:
409  *  Anthony Liguori   <[email protected]>
410  *
411  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
412  * See the COPYING.LIB file in the top-level directory.
413  *
414  */
415 '''
416 h_comment = '''
417 /*
418  * schema-defined QAPI visitor functions
419  *
420  * Copyright IBM, Corp. 2011
421  *
422  * Authors:
423  *  Anthony Liguori   <[email protected]>
424  *
425  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
426  * See the COPYING.LIB file in the top-level directory.
427  *
428  */
429 '''
430
431 (fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
432                             'qapi-visit.c', 'qapi-visit.h',
433                             c_comment, h_comment)
434
435 fdef.write(mcgen('''
436 #include "qemu-common.h"
437 #include "%(prefix)sqapi-visit.h"
438 ''',
439                  prefix = prefix))
440
441 fdecl.write(mcgen('''
442 #include "qapi/visitor.h"
443 #include "%(prefix)sqapi-types.h"
444
445 ''',
446                   prefix=prefix))
447
448 exprs = parse_schema(input_file)
449
450 # to avoid header dependency hell, we always generate declarations
451 # for built-in types in our header files and simply guard them
452 fdecl.write(guardstart("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
453 for typename in builtin_types.keys():
454     fdecl.write(generate_declaration(typename, builtin_type=True))
455 fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
456
457 # ...this doesn't work for cases where we link in multiple objects that
458 # have the functions defined, so we use -b option to provide control
459 # over these cases
460 if do_builtins:
461     for typename in builtin_types.keys():
462         fdef.write(generate_visit_list(typename))
463
464 for expr in exprs:
465     if expr.has_key('struct'):
466         ret = generate_visit_struct(expr)
467         ret += generate_visit_list(expr['struct'])
468         fdef.write(ret)
469
470         ret = generate_declaration(expr['struct'])
471         fdecl.write(ret)
472     elif expr.has_key('union'):
473         ret = generate_visit_union(expr)
474         ret += generate_visit_list(expr['union'])
475         fdef.write(ret)
476
477         enum_define = discriminator_find_enum_define(expr)
478         ret = ""
479         if not enum_define:
480             ret = generate_decl_enum('%sKind' % expr['union'])
481         ret += generate_declaration(expr['union'])
482         fdecl.write(ret)
483     elif expr.has_key('alternate'):
484         ret = generate_visit_alternate(expr['alternate'], expr['data'])
485         ret += generate_visit_list(expr['alternate'])
486         fdef.write(ret)
487
488         ret = generate_decl_enum('%sKind' % expr['alternate'])
489         ret += generate_declaration(expr['alternate'])
490         fdecl.write(ret)
491     elif expr.has_key('enum'):
492         ret = generate_visit_list(expr['enum'])
493         ret += generate_visit_enum(expr['enum'])
494         fdef.write(ret)
495
496         ret = generate_decl_enum(expr['enum'])
497         ret += generate_enum_declaration(expr['enum'])
498         fdecl.write(ret)
499
500 close_output(fdef, fdecl)
This page took 0.052407 seconds and 4 git commands to generate.