xref: /qemu/scripts/qapi/visit.py (revision 25a0d9c977c2f5db914b0a1619759fd77d97b016)
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 qapi import *
16import re
17
18implicit_structs_seen = set()
19struct_fields_seen = set()
20
21
22def gen_visit_decl(name, scalar=False):
23    c_type = c_name(name) + ' *'
24    if not scalar:
25        c_type += '*'
26    return mcgen('''
27void visit_type_%(c_name)s(Visitor *v, %(c_type)sobj, const char *name, Error **errp);
28''',
29                 c_name=c_name(name), c_type=c_type)
30
31
32def gen_visit_implicit_struct(typ):
33    if typ in implicit_structs_seen:
34        return ''
35    implicit_structs_seen.add(typ)
36
37    ret = ''
38    if typ.name not in struct_fields_seen:
39        # Need a forward declaration
40        ret += mcgen('''
41
42static void visit_type_%(c_type)s_fields(Visitor *v, %(c_type)s **obj, Error **errp);
43''',
44                     c_type=typ.c_name())
45
46    ret += mcgen('''
47
48static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error **errp)
49{
50    Error *err = NULL;
51
52    visit_start_implicit_struct(v, (void **)obj, sizeof(%(c_type)s), &err);
53    if (!err) {
54        visit_type_%(c_type)s_fields(v, obj, errp);
55        visit_end_implicit_struct(v, &err);
56    }
57    error_propagate(errp, err);
58}
59''',
60                 c_type=typ.c_name())
61    return ret
62
63
64def gen_visit_struct_fields(name, base, members):
65    struct_fields_seen.add(name)
66
67    ret = ''
68
69    if base:
70        ret += gen_visit_implicit_struct(base)
71
72    ret += mcgen('''
73
74static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **errp)
75{
76    Error *err = NULL;
77
78''',
79                 c_name=c_name(name))
80
81    if base:
82        ret += mcgen('''
83    visit_type_implicit_%(c_type)s(v, &(*obj)->%(c_name)s, &err);
84''',
85                     c_type=base.c_name(), c_name=c_name('base'))
86        ret += gen_err_check()
87
88    ret += gen_visit_fields(members, prefix='(*obj)->')
89
90    if re.search('^ *goto out;', ret, re.MULTILINE):
91        ret += mcgen('''
92
93out:
94''')
95    ret += mcgen('''
96    error_propagate(errp, err);
97}
98''')
99    return ret
100
101
102def gen_visit_struct(name, base, members):
103    ret = gen_visit_struct_fields(name, base, members)
104
105    # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
106    # *obj, but then visit_type_FOO_fields() fails, we should clean up *obj
107    # rather than leaving it non-NULL. As currently written, the caller must
108    # call qapi_free_FOO() to avoid a memory leak of the partial FOO.
109    ret += mcgen('''
110
111void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
112{
113    Error *err = NULL;
114
115    visit_start_struct(v, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
116    if (!err) {
117        if (*obj) {
118            visit_type_%(c_name)s_fields(v, obj, errp);
119        }
120        visit_end_struct(v, &err);
121    }
122    error_propagate(errp, err);
123}
124''',
125                 name=name, c_name=c_name(name))
126
127    return ret
128
129
130def gen_visit_list(name, element_type):
131    return mcgen('''
132
133void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
134{
135    Error *err = NULL;
136    GenericList *i, **prev;
137
138    visit_start_list(v, name, &err);
139    if (err) {
140        goto out;
141    }
142
143    for (prev = (GenericList **)obj;
144         !err && (i = visit_next_list(v, prev, &err)) != NULL;
145         prev = &i) {
146        %(c_name)s *native_i = (%(c_name)s *)i;
147        visit_type_%(c_elt_type)s(v, &native_i->value, NULL, &err);
148    }
149
150    error_propagate(errp, err);
151    err = NULL;
152    visit_end_list(v, &err);
153out:
154    error_propagate(errp, err);
155}
156''',
157                 c_name=c_name(name), c_elt_type=element_type.c_name())
158
159
160def gen_visit_enum(name):
161    return mcgen('''
162
163void visit_type_%(c_name)s(Visitor *v, %(c_name)s *obj, const char *name, Error **errp)
164{
165    visit_type_enum(v, (int *)obj, %(c_name)s_lookup, "%(name)s", name, errp);
166}
167''',
168                 c_name=c_name(name), name=name)
169
170
171def gen_visit_alternate(name, variants):
172    ret = mcgen('''
173
174void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
175{
176    Error *err = NULL;
177
178    visit_start_implicit_struct(v, (void**) obj, sizeof(%(c_name)s), &err);
179    if (err) {
180        goto out;
181    }
182    visit_get_next_type(v, (int*) &(*obj)->kind, %(c_name)s_qtypes, name, &err);
183    if (err) {
184        goto out_obj;
185    }
186    switch ((*obj)->kind) {
187''',
188                c_name=c_name(name))
189
190    for var in variants.variants:
191        ret += mcgen('''
192    case %(case)s:
193        visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, name, &err);
194        break;
195''',
196                     case=c_enum_const(variants.tag_member.type.name,
197                                       var.name),
198                     c_type=var.type.c_name(),
199                     c_name=c_name(var.name))
200
201    ret += mcgen('''
202    default:
203        abort();
204    }
205out_obj:
206    error_propagate(errp, err);
207    err = NULL;
208    visit_end_implicit_struct(v, &err);
209out:
210    error_propagate(errp, err);
211}
212''')
213
214    return ret
215
216
217def gen_visit_union(name, base, variants):
218    ret = ''
219
220    if base:
221        members = [m for m in base.members if m != variants.tag_member]
222        ret += gen_visit_struct_fields(name, None, members)
223
224    for var in variants.variants:
225        # Ugly special case for simple union TODO get rid of it
226        if not var.simple_union_type():
227            ret += gen_visit_implicit_struct(var.type)
228
229    ret += mcgen('''
230
231void visit_type_%(c_name)s(Visitor *v, %(c_name)s **obj, const char *name, Error **errp)
232{
233    Error *err = NULL;
234
235    visit_start_struct(v, (void **)obj, "%(name)s", name, sizeof(%(c_name)s), &err);
236    if (err) {
237        goto out;
238    }
239    if (!*obj) {
240        goto out_obj;
241    }
242''',
243                 c_name=c_name(name), name=name)
244
245    if base:
246        ret += mcgen('''
247    visit_type_%(c_name)s_fields(v, obj, &err);
248''',
249                     c_name=c_name(name))
250        ret += gen_err_check(label='out_obj')
251
252    tag_key = variants.tag_member.name
253    if not variants.tag_name:
254        # we pointlessly use a different key for simple unions
255        tag_key = 'type'
256    ret += mcgen('''
257    visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, "%(name)s", &err);
258    if (err) {
259        goto out_obj;
260    }
261    if (!visit_start_union(v, !!(*obj)->data, &err) || err) {
262        goto out_obj;
263    }
264    switch ((*obj)->%(c_name)s) {
265''',
266                 c_type=variants.tag_member.type.c_name(),
267                 # TODO ugly special case for simple union
268                 # Use same tag name in C as on the wire to get rid of
269                 # it, then: c_name=c_name(variants.tag_member.name)
270                 c_name=c_name(variants.tag_name or 'kind'),
271                 name=tag_key)
272
273    for var in variants.variants:
274        # TODO ugly special case for simple union
275        simple_union_type = var.simple_union_type()
276        ret += mcgen('''
277    case %(case)s:
278''',
279                     case=c_enum_const(variants.tag_member.type.name,
280                                       var.name))
281        if simple_union_type:
282            ret += mcgen('''
283        visit_type_%(c_type)s(v, &(*obj)->%(c_name)s, "data", &err);
284''',
285                         c_type=simple_union_type.c_name(),
286                         c_name=c_name(var.name))
287        else:
288            ret += mcgen('''
289        visit_type_implicit_%(c_type)s(v, &(*obj)->%(c_name)s, &err);
290''',
291                         c_type=var.type.c_name(),
292                         c_name=c_name(var.name))
293        ret += mcgen('''
294        break;
295''')
296
297    ret += mcgen('''
298    default:
299        abort();
300    }
301out_obj:
302    error_propagate(errp, err);
303    err = NULL;
304    if (*obj) {
305        visit_end_union(v, !!(*obj)->data, &err);
306    }
307    error_propagate(errp, err);
308    err = NULL;
309    visit_end_struct(v, &err);
310out:
311    error_propagate(errp, err);
312}
313''')
314
315    return ret
316
317
318class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
319    def __init__(self):
320        self.decl = None
321        self.defn = None
322        self._btin = None
323
324    def visit_begin(self, schema):
325        self.decl = ''
326        self.defn = ''
327        self._btin = guardstart('QAPI_VISIT_BUILTIN')
328
329    def visit_end(self):
330        # To avoid header dependency hell, we always generate
331        # declarations for built-in types in our header files and
332        # simply guard them.  See also do_builtins (command line
333        # option -b).
334        self._btin += guardend('QAPI_VISIT_BUILTIN')
335        self.decl = self._btin + self.decl
336        self._btin = None
337
338    def visit_needed(self, entity):
339        # Visit everything except implicit objects
340        return not isinstance(entity, QAPISchemaObjectType) or entity.info
341
342    def visit_enum_type(self, name, info, values, prefix):
343        self.decl += gen_visit_decl(name, scalar=True)
344        self.defn += gen_visit_enum(name)
345
346    def visit_array_type(self, name, info, element_type):
347        decl = gen_visit_decl(name)
348        defn = gen_visit_list(name, element_type)
349        if isinstance(element_type, QAPISchemaBuiltinType):
350            self._btin += decl
351            if do_builtins:
352                self.defn += defn
353        else:
354            self.decl += decl
355            self.defn += defn
356
357    def visit_object_type(self, name, info, base, members, variants):
358        self.decl += gen_visit_decl(name)
359        if variants:
360            assert not members      # not implemented
361            self.defn += gen_visit_union(name, base, variants)
362        else:
363            self.defn += gen_visit_struct(name, base, members)
364
365    def visit_alternate_type(self, name, info, variants):
366        self.decl += gen_visit_decl(name)
367        self.defn += gen_visit_alternate(name, variants)
368
369# If you link code generated from multiple schemata, you want only one
370# instance of the code for built-in types.  Generate it only when
371# do_builtins, enabled by command line option -b.  See also
372# QAPISchemaGenVisitVisitor.visit_end().
373do_builtins = False
374
375(input_file, output_dir, do_c, do_h, prefix, opts) = \
376    parse_command_line("b", ["builtins"])
377
378for o, a in opts:
379    if o in ("-b", "--builtins"):
380        do_builtins = True
381
382c_comment = '''
383/*
384 * schema-defined QAPI visitor functions
385 *
386 * Copyright IBM, Corp. 2011
387 *
388 * Authors:
389 *  Anthony Liguori   <aliguori@us.ibm.com>
390 *
391 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
392 * See the COPYING.LIB file in the top-level directory.
393 *
394 */
395'''
396h_comment = '''
397/*
398 * schema-defined QAPI visitor functions
399 *
400 * Copyright IBM, Corp. 2011
401 *
402 * Authors:
403 *  Anthony Liguori   <aliguori@us.ibm.com>
404 *
405 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
406 * See the COPYING.LIB file in the top-level directory.
407 *
408 */
409'''
410
411(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
412                            'qapi-visit.c', 'qapi-visit.h',
413                            c_comment, h_comment)
414
415fdef.write(mcgen('''
416#include "qemu-common.h"
417#include "%(prefix)sqapi-visit.h"
418''',
419                 prefix=prefix))
420
421fdecl.write(mcgen('''
422#include "qapi/visitor.h"
423#include "%(prefix)sqapi-types.h"
424
425''',
426                  prefix=prefix))
427
428schema = QAPISchema(input_file)
429gen = QAPISchemaGenVisitVisitor()
430schema.visit(gen)
431fdef.write(gen.defn)
432fdecl.write(gen.decl)
433
434close_output(fdef, fdecl)
435