xref: /qemu/scripts/qapi/visit.py (revision 40b3adec13a9e022ff5a2e2b81c243fc0a026746)
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                 name=c_name(name))
67    push_indent()
68
69    if base:
70        ret += mcgen('''
71visit_type_implicit_%(type)s(m, &(*obj)->%(c_name)s, &err);
72if (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('''
81visit_optional(m, &(*obj)->has_%(c_name)s, "%(name)s", &err);
82if (!err && (*obj)->has_%(c_name)s) {
83''',
84                         c_name=c_name(argname), name=argname)
85            push_indent()
86
87        ret += mcgen('''
88visit_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('''
99if (err) {
100    goto out;
101}
102''')
103
104    pop_indent()
105    if re.search('^ *goto out\\;', ret, re.MULTILINE):
106        ret += mcgen('''
107
108out:
109''')
110    ret += mcgen('''
111    error_propagate(errp, err);
112}
113''')
114    return ret
115
116
117def generate_visit_struct_body(name):
118    ret = mcgen('''
119    Error *err = NULL;
120
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);
125        }
126        visit_end_struct(m, &err);
127    }
128    error_propagate(errp, err);
129''',
130                name=name, c_name=c_name(name))
131
132    return ret
133
134def generate_visit_struct(expr):
135
136    name = expr['struct']
137    members = expr['data']
138    base = expr.get('base')
139
140    ret = generate_visit_struct_fields(name, members, base)
141
142    ret += mcgen('''
143
144void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
145{
146''',
147                 name=c_name(name))
148
149    ret += generate_visit_struct_body(name)
150
151    ret += mcgen('''
152}
153''')
154    return ret
155
156def generate_visit_list(name):
157    return mcgen('''
158
159void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp)
160{
161    Error *err = NULL;
162    GenericList *i, **prev;
163
164    visit_start_list(m, name, &err);
165    if (err) {
166        goto out;
167    }
168
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);
174    }
175
176    error_propagate(errp, err);
177    err = NULL;
178    visit_end_list(m, &err);
179out:
180    error_propagate(errp, err);
181}
182''',
183                name=type_name(name))
184
185def generate_visit_enum(name):
186    return mcgen('''
187
188void visit_type_%(c_name)s(Visitor *m, %(c_name)s *obj, const char *name, Error **errp)
189{
190    visit_type_enum(m, (int *)obj, %(c_name)s_lookup, "%(name)s", name, errp);
191}
192''',
193                 c_name=c_name(name), name=name)
194
195def generate_visit_alternate(name, members):
196    ret = mcgen('''
197
198void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
199{
200    Error *err = NULL;
201
202    visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err);
203    if (err) {
204        goto out;
205    }
206    visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err);
207    if (err) {
208        goto out_end;
209    }
210    switch ((*obj)->kind) {
211''',
212                name=c_name(name))
213
214    # For alternate, always use the default enum type automatically generated
215    # as name + 'Kind'
216    disc_type = c_name(name) + 'Kind'
217
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"
223
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))
233
234    ret += mcgen('''
235    default:
236        abort();
237    }
238out_end:
239    error_propagate(errp, err);
240    err = NULL;
241    visit_end_implicit_struct(m, &err);
242out:
243    error_propagate(errp, err);
244}
245''')
246
247    return ret
248
249
250def generate_visit_union(expr):
251
252    name = expr['union']
253    members = expr['data']
254
255    base = expr.get('base')
256    discriminator = expr.get('discriminator')
257
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'
268
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)
274
275    if discriminator:
276        for key in members:
277            ret += generate_visit_implicit_struct(members[key])
278
279    ret += mcgen('''
280
281void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error **errp)
282{
283    Error *err = NULL;
284
285    visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
286    if (err) {
287        goto out;
288    }
289    if (*obj) {
290''',
291                 c_name=c_name(name), name=name)
292
293    if base:
294        ret += mcgen('''
295        visit_type_%(name)s_fields(m, obj, &err);
296        if (err) {
297            goto out_obj;
298        }
299''',
300                     name=c_name(name))
301
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;
312        }
313        if (!visit_start_union(m, !!(*obj)->data, &err) || err) {
314            goto out_obj;
315        }
316        switch ((*obj)->%(c_tag)s) {
317''',
318                 disc_type = disc_type,
319                 c_tag=c_name(tag),
320                 disc_key = disc_key)
321
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);'
327
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))
337
338    ret += mcgen('''
339        default:
340            abort();
341        }
342out_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;
348    }
349    visit_end_struct(m, &err);
350out:
351    error_propagate(errp, err);
352}
353''')
354
355    return ret
356
357def generate_declaration(name, builtin_type=False):
358    ret = ""
359    if not builtin_type:
360        name = c_name(name)
361        ret += mcgen('''
362
363void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp);
364''',
365                     name=name)
366
367    ret += mcgen('''
368void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp);
369''',
370                 name=name)
371
372    return ret
373
374def generate_enum_declaration(name):
375    ret = mcgen('''
376void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp);
377''',
378                name=c_name(name))
379
380    return ret
381
382def generate_decl_enum(name):
383    return mcgen('''
384
385void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp);
386''',
387                 name=c_name(name))
388
389do_builtins = False
390
391(input_file, output_dir, do_c, do_h, prefix, opts) = \
392    parse_command_line("b", ["builtins"])
393
394for o, a in opts:
395    if o in ("-b", "--builtins"):
396        do_builtins = True
397
398c_comment = '''
399/*
400 * schema-defined QAPI visitor functions
401 *
402 * Copyright IBM, Corp. 2011
403 *
404 * Authors:
405 *  Anthony Liguori   <aliguori@us.ibm.com>
406 *
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.
409 *
410 */
411'''
412h_comment = '''
413/*
414 * schema-defined QAPI visitor functions
415 *
416 * Copyright IBM, Corp. 2011
417 *
418 * Authors:
419 *  Anthony Liguori   <aliguori@us.ibm.com>
420 *
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.
423 *
424 */
425'''
426
427(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
428                            'qapi-visit.c', 'qapi-visit.h',
429                            c_comment, h_comment)
430
431fdef.write(mcgen('''
432#include "qemu-common.h"
433#include "%(prefix)sqapi-visit.h"
434''',
435                 prefix = prefix))
436
437fdecl.write(mcgen('''
438#include "qapi/visitor.h"
439#include "%(prefix)sqapi-types.h"
440
441''',
442                  prefix=prefix))
443
444exprs = parse_schema(input_file)
445
446# to avoid header dependency hell, we always generate declarations
447# for built-in types in our header files and simply guard them
448fdecl.write(guardstart("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
449for typename in builtin_types.keys():
450    fdecl.write(generate_declaration(typename, builtin_type=True))
451fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
452
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
456if do_builtins:
457    for typename in builtin_types.keys():
458        fdef.write(generate_visit_list(typename))
459
460for 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)
465
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)
472
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)
483
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)
491
492        ret = generate_decl_enum(expr['enum'])
493        ret += generate_enum_declaration(expr['enum'])
494        fdecl.write(ret)
495
496close_output(fdef, fdecl)
497