xref: /qemu/scripts/qapi/visit.py (revision 32bafa8fdd098d52fbf1102d5a5e48d29398c0aa)
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
19def gen_visit_decl(name, scalar=False):
20    c_type = c_name(name) + ' *'
21    if not scalar:
22        c_type += '*'
23    return mcgen('''
24void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_type)sobj, Error **errp);
25''',
26                 c_name=c_name(name), c_type=c_type)
27
28
29def gen_visit_members_decl(name):
30    return mcgen('''
31
32void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp);
33''',
34                 c_name=c_name(name))
35
36
37def gen_visit_object_members(name, base, members, variants):
38    ret = mcgen('''
39
40void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
41{
42    Error *err = NULL;
43
44''',
45                c_name=c_name(name))
46
47    if base:
48        ret += mcgen('''
49    visit_type_%(c_type)s_members(v, (%(c_type)s *)obj, &err);
50''',
51                     c_type=base.c_name())
52        ret += gen_err_check()
53
54    for memb in members:
55        if memb.optional:
56            ret += mcgen('''
57    if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) {
58''',
59                         name=memb.name, c_name=c_name(memb.name))
60            push_indent()
61        ret += mcgen('''
62    visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, &err);
63''',
64                     c_type=memb.type.c_name(), name=memb.name,
65                     c_name=c_name(memb.name))
66        ret += gen_err_check()
67        if memb.optional:
68            pop_indent()
69            ret += mcgen('''
70    }
71''')
72
73    if variants:
74        ret += mcgen('''
75    switch (obj->%(c_name)s) {
76''',
77                     c_name=c_name(variants.tag_member.name))
78
79        for var in variants.variants:
80            ret += mcgen('''
81    case %(case)s:
82        visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, &err);
83        break;
84''',
85                         case=c_enum_const(variants.tag_member.type.name,
86                                           var.name,
87                                           variants.tag_member.type.prefix),
88                         c_type=var.type.c_name(), c_name=c_name(var.name))
89
90        ret += mcgen('''
91    default:
92        abort();
93    }
94''')
95
96    # 'goto out' produced for base, for each member, and if variants were
97    # present
98    if base or members or variants:
99        ret += mcgen('''
100
101out:
102''')
103    ret += mcgen('''
104    error_propagate(errp, err);
105}
106''')
107    return ret
108
109
110def gen_visit_list(name, element_type):
111    # FIXME: if *obj is NULL on entry, and the first visit_next_list()
112    # assigns to *obj, while a later one fails, we should clean up *obj
113    # rather than leaving it non-NULL. As currently written, the caller must
114    # call qapi_free_FOOList() to avoid a memory leak of the partial FOOList.
115    return mcgen('''
116
117void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
118{
119    Error *err = NULL;
120    GenericList *i, **prev;
121
122    visit_start_list(v, name, &err);
123    if (err) {
124        goto out;
125    }
126
127    for (prev = (GenericList **)obj;
128         !err && (i = visit_next_list(v, prev, sizeof(**obj))) != NULL;
129         prev = &i) {
130        %(c_name)s *native_i = (%(c_name)s *)i;
131        visit_type_%(c_elt_type)s(v, NULL, &native_i->value, &err);
132    }
133
134    visit_end_list(v);
135out:
136    error_propagate(errp, err);
137}
138''',
139                 c_name=c_name(name), c_elt_type=element_type.c_name())
140
141
142def gen_visit_enum(name):
143    return mcgen('''
144
145void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s *obj, Error **errp)
146{
147    int value = *obj;
148    visit_type_enum(v, name, &value, %(c_name)s_lookup, errp);
149    *obj = value;
150}
151''',
152                 c_name=c_name(name))
153
154
155def gen_visit_alternate(name, variants):
156    promote_int = 'true'
157    ret = ''
158    for var in variants.variants:
159        if var.type.alternate_qtype() == 'QTYPE_QINT':
160            promote_int = 'false'
161
162    ret += mcgen('''
163
164void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
165{
166    Error *err = NULL;
167
168    visit_start_alternate(v, name, (GenericAlternate **)obj, sizeof(**obj),
169                          %(promote_int)s, &err);
170    if (err) {
171        goto out;
172    }
173    switch ((*obj)->type) {
174''',
175                 c_name=c_name(name), promote_int=promote_int)
176
177    for var in variants.variants:
178        ret += mcgen('''
179    case %(case)s:
180''',
181                     case=var.type.alternate_qtype())
182        if isinstance(var.type, QAPISchemaObjectType):
183            ret += mcgen('''
184        visit_start_struct(v, name, NULL, 0, &err);
185        if (err) {
186            break;
187        }
188        visit_type_%(c_type)s_members(v, &(*obj)->u.%(c_name)s, &err);
189        error_propagate(errp, err);
190        err = NULL;
191        visit_end_struct(v, &err);
192''',
193                         c_type=var.type.c_name(),
194                         c_name=c_name(var.name))
195        else:
196            ret += mcgen('''
197        visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, &err);
198''',
199                         c_type=var.type.c_name(),
200                         c_name=c_name(var.name))
201        ret += mcgen('''
202        break;
203''')
204
205    ret += mcgen('''
206    default:
207        error_setg(&err, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
208                   "%(name)s");
209    }
210    visit_end_alternate(v);
211out:
212    error_propagate(errp, err);
213}
214''',
215                 name=name)
216
217    return ret
218
219
220def gen_visit_object(name, base, members, variants):
221    # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
222    # *obj, but then visit_type_FOO_members() fails, we should clean up *obj
223    # rather than leaving it non-NULL. As currently written, the caller must
224    # call qapi_free_FOO() to avoid a memory leak of the partial FOO.
225    return mcgen('''
226
227void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
228{
229    Error *err = NULL;
230
231    visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), &err);
232    if (err) {
233        goto out;
234    }
235    if (!*obj) {
236        goto out_obj;
237    }
238    visit_type_%(c_name)s_members(v, *obj, &err);
239    error_propagate(errp, err);
240    err = NULL;
241out_obj:
242    visit_end_struct(v, &err);
243out:
244    error_propagate(errp, err);
245}
246''',
247                 c_name=c_name(name))
248
249
250class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
251    def __init__(self):
252        self.decl = None
253        self.defn = None
254        self._btin = None
255
256    def visit_begin(self, schema):
257        self.decl = ''
258        self.defn = ''
259        self._btin = guardstart('QAPI_VISIT_BUILTIN')
260
261    def visit_end(self):
262        # To avoid header dependency hell, we always generate
263        # declarations for built-in types in our header files and
264        # simply guard them.  See also do_builtins (command line
265        # option -b).
266        self._btin += guardend('QAPI_VISIT_BUILTIN')
267        self.decl = self._btin + self.decl
268        self._btin = None
269
270    def visit_enum_type(self, name, info, values, prefix):
271        # Special case for our lone builtin enum type
272        # TODO use something cleaner than existence of info
273        if not info:
274            self._btin += gen_visit_decl(name, scalar=True)
275            if do_builtins:
276                self.defn += gen_visit_enum(name)
277        else:
278            self.decl += gen_visit_decl(name, scalar=True)
279            self.defn += gen_visit_enum(name)
280
281    def visit_array_type(self, name, info, element_type):
282        decl = gen_visit_decl(name)
283        defn = gen_visit_list(name, element_type)
284        if isinstance(element_type, QAPISchemaBuiltinType):
285            self._btin += decl
286            if do_builtins:
287                self.defn += defn
288        else:
289            self.decl += decl
290            self.defn += defn
291
292    def visit_object_type(self, name, info, base, members, variants):
293        # Nothing to do for the special empty builtin
294        if name == 'q_empty':
295            return
296        self.decl += gen_visit_members_decl(name)
297        self.defn += gen_visit_object_members(name, base, members, variants)
298        # TODO Worth changing the visitor signature, so we could
299        # directly use rather than repeat type.is_implicit()?
300        if not name.startswith('q_'):
301            # only explicit types need an allocating visit
302            self.decl += gen_visit_decl(name)
303            self.defn += gen_visit_object(name, base, members, variants)
304
305    def visit_alternate_type(self, name, info, variants):
306        self.decl += gen_visit_decl(name)
307        self.defn += gen_visit_alternate(name, variants)
308
309# If you link code generated from multiple schemata, you want only one
310# instance of the code for built-in types.  Generate it only when
311# do_builtins, enabled by command line option -b.  See also
312# QAPISchemaGenVisitVisitor.visit_end().
313do_builtins = False
314
315(input_file, output_dir, do_c, do_h, prefix, opts) = \
316    parse_command_line("b", ["builtins"])
317
318for o, a in opts:
319    if o in ("-b", "--builtins"):
320        do_builtins = True
321
322c_comment = '''
323/*
324 * schema-defined QAPI visitor functions
325 *
326 * Copyright IBM, Corp. 2011
327 *
328 * Authors:
329 *  Anthony Liguori   <aliguori@us.ibm.com>
330 *
331 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
332 * See the COPYING.LIB file in the top-level directory.
333 *
334 */
335'''
336h_comment = '''
337/*
338 * schema-defined QAPI visitor functions
339 *
340 * Copyright IBM, Corp. 2011
341 *
342 * Authors:
343 *  Anthony Liguori   <aliguori@us.ibm.com>
344 *
345 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
346 * See the COPYING.LIB file in the top-level directory.
347 *
348 */
349'''
350
351(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
352                            'qapi-visit.c', 'qapi-visit.h',
353                            c_comment, h_comment)
354
355fdef.write(mcgen('''
356#include "qemu/osdep.h"
357#include "qemu-common.h"
358#include "%(prefix)sqapi-visit.h"
359''',
360                 prefix=prefix))
361
362fdecl.write(mcgen('''
363#include "qapi/visitor.h"
364#include "qapi/qmp/qerror.h"
365#include "%(prefix)sqapi-types.h"
366
367''',
368                  prefix=prefix))
369
370schema = QAPISchema(input_file)
371gen = QAPISchemaGenVisitVisitor()
372schema.visit(gen)
373fdef.write(gen.defn)
374fdecl.write(gen.decl)
375
376close_output(fdef, fdecl)
377