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