xref: /qemu/scripts/qapi/visit.py (revision 0f61af3eb396ae163cd1572ce12e05f5d08d7c15)
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        tag = 'kind'
292        disc_key = "type"
293    else:
294        tag = discriminator
295        disc_key = discriminator
296    ret += mcgen('''
297        visit_type_%(disc_type)s(m, &(*obj)->%(c_tag)s, "%(disc_key)s", &err);
298        if (err) {
299            goto out_obj;
300        }
301        if (!visit_start_union(m, !!(*obj)->data, &err) || err) {
302            goto out_obj;
303        }
304        switch ((*obj)->%(c_tag)s) {
305''',
306                 disc_type = disc_type,
307                 c_tag=c_name(tag),
308                 disc_key = disc_key)
309
310    for key in members:
311        if not discriminator:
312            fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);'
313        else:
314            fmt = 'visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);'
315
316        enum_full_value = c_enum_const(disc_type, key)
317        ret += mcgen('''
318        case %(enum_full_value)s:
319            ''' + fmt + '''
320            break;
321''',
322                enum_full_value = enum_full_value,
323                c_type=type_name(members[key]),
324                c_name=c_name(key))
325
326    ret += mcgen('''
327        default:
328            abort();
329        }
330out_obj:
331        error_propagate(errp, err);
332        err = NULL;
333        visit_end_union(m, !!(*obj)->data, &err);
334        error_propagate(errp, err);
335        err = NULL;
336    }
337    visit_end_struct(m, &err);
338out:
339    error_propagate(errp, err);
340}
341''')
342
343    return ret
344
345def generate_declaration(name, builtin_type=False):
346    ret = ""
347    if not builtin_type:
348        name = c_name(name)
349        ret += mcgen('''
350
351void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp);
352''',
353                     name=name)
354
355    ret += mcgen('''
356void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp);
357''',
358                 name=name)
359
360    return ret
361
362def generate_enum_declaration(name):
363    ret = mcgen('''
364void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp);
365''',
366                name=c_name(name))
367
368    return ret
369
370def generate_decl_enum(name):
371    return mcgen('''
372
373void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp);
374''',
375                 name=c_name(name))
376
377do_builtins = False
378
379(input_file, output_dir, do_c, do_h, prefix, opts) = \
380    parse_command_line("b", ["builtins"])
381
382for o, a in opts:
383    if o in ("-b", "--builtins"):
384        do_builtins = True
385
386c_comment = '''
387/*
388 * schema-defined QAPI visitor functions
389 *
390 * Copyright IBM, Corp. 2011
391 *
392 * Authors:
393 *  Anthony Liguori   <aliguori@us.ibm.com>
394 *
395 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
396 * See the COPYING.LIB file in the top-level directory.
397 *
398 */
399'''
400h_comment = '''
401/*
402 * schema-defined QAPI visitor functions
403 *
404 * Copyright IBM, Corp. 2011
405 *
406 * Authors:
407 *  Anthony Liguori   <aliguori@us.ibm.com>
408 *
409 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
410 * See the COPYING.LIB file in the top-level directory.
411 *
412 */
413'''
414
415(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
416                            'qapi-visit.c', 'qapi-visit.h',
417                            c_comment, h_comment)
418
419fdef.write(mcgen('''
420#include "qemu-common.h"
421#include "%(prefix)sqapi-visit.h"
422''',
423                 prefix = prefix))
424
425fdecl.write(mcgen('''
426#include "qapi/visitor.h"
427#include "%(prefix)sqapi-types.h"
428
429''',
430                  prefix=prefix))
431
432exprs = parse_schema(input_file)
433
434# to avoid header dependency hell, we always generate declarations
435# for built-in types in our header files and simply guard them
436fdecl.write(guardstart("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
437for typename in builtin_types.keys():
438    fdecl.write(generate_declaration(typename, builtin_type=True))
439fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
440
441# ...this doesn't work for cases where we link in multiple objects that
442# have the functions defined, so we use -b option to provide control
443# over these cases
444if do_builtins:
445    for typename in builtin_types.keys():
446        fdef.write(generate_visit_list(typename))
447
448for expr in exprs:
449    if expr.has_key('struct'):
450        ret = generate_visit_struct(expr)
451        ret += generate_visit_list(expr['struct'])
452        fdef.write(ret)
453
454        ret = generate_declaration(expr['struct'])
455        fdecl.write(ret)
456    elif expr.has_key('union'):
457        ret = generate_visit_union(expr)
458        ret += generate_visit_list(expr['union'])
459        fdef.write(ret)
460
461        enum_define = discriminator_find_enum_define(expr)
462        ret = ""
463        if not enum_define:
464            ret = generate_decl_enum('%sKind' % expr['union'])
465        ret += generate_declaration(expr['union'])
466        fdecl.write(ret)
467    elif expr.has_key('alternate'):
468        ret = generate_visit_alternate(expr['alternate'], expr['data'])
469        ret += generate_visit_list(expr['alternate'])
470        fdef.write(ret)
471
472        ret = generate_decl_enum('%sKind' % expr['alternate'])
473        ret += generate_declaration(expr['alternate'])
474        fdecl.write(ret)
475    elif expr.has_key('enum'):
476        ret = generate_visit_list(expr['enum'])
477        ret += generate_visit_enum(expr['enum'])
478        fdef.write(ret)
479
480        ret = generate_decl_enum(expr['enum'])
481        ret += generate_enum_declaration(expr['enum'])
482        fdecl.write(ret)
483
484close_output(fdef, fdecl)
485