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