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