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