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