xref: /qemu/scripts/qapi/visit.py (revision 15c2f669e3fb2bc97f7b42d1871f595c0ac24af8)
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        if (!err) {
190            visit_check_struct(v, &err);
191        }
192        visit_end_struct(v);
193''',
194                         c_type=var.type.c_name(),
195                         c_name=c_name(var.name))
196        else:
197            ret += mcgen('''
198        visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, &err);
199''',
200                         c_type=var.type.c_name(),
201                         c_name=c_name(var.name))
202        ret += mcgen('''
203        break;
204''')
205
206    ret += mcgen('''
207    default:
208        error_setg(&err, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
209                   "%(name)s");
210    }
211    visit_end_alternate(v);
212out:
213    error_propagate(errp, err);
214}
215''',
216                 name=name)
217
218    return ret
219
220
221def gen_visit_object(name, base, members, variants):
222    # FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
223    # *obj, but then visit_type_FOO_members() fails, we should clean up *obj
224    # rather than leaving it non-NULL. As currently written, the caller must
225    # call qapi_free_FOO() to avoid a memory leak of the partial FOO.
226    return mcgen('''
227
228void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
229{
230    Error *err = NULL;
231
232    visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), &err);
233    if (err) {
234        goto out;
235    }
236    if (!*obj) {
237        goto out_obj;
238    }
239    visit_type_%(c_name)s_members(v, *obj, &err);
240    if (err) {
241        goto out_obj;
242    }
243    visit_check_struct(v, &err);
244out_obj:
245    visit_end_struct(v);
246out:
247    error_propagate(errp, err);
248}
249''',
250                 c_name=c_name(name))
251
252
253class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
254    def __init__(self):
255        self.decl = None
256        self.defn = None
257        self._btin = None
258
259    def visit_begin(self, schema):
260        self.decl = ''
261        self.defn = ''
262        self._btin = guardstart('QAPI_VISIT_BUILTIN')
263
264    def visit_end(self):
265        # To avoid header dependency hell, we always generate
266        # declarations for built-in types in our header files and
267        # simply guard them.  See also do_builtins (command line
268        # option -b).
269        self._btin += guardend('QAPI_VISIT_BUILTIN')
270        self.decl = self._btin + self.decl
271        self._btin = None
272
273    def visit_enum_type(self, name, info, values, prefix):
274        # Special case for our lone builtin enum type
275        # TODO use something cleaner than existence of info
276        if not info:
277            self._btin += gen_visit_decl(name, scalar=True)
278            if do_builtins:
279                self.defn += gen_visit_enum(name)
280        else:
281            self.decl += gen_visit_decl(name, scalar=True)
282            self.defn += gen_visit_enum(name)
283
284    def visit_array_type(self, name, info, element_type):
285        decl = gen_visit_decl(name)
286        defn = gen_visit_list(name, element_type)
287        if isinstance(element_type, QAPISchemaBuiltinType):
288            self._btin += decl
289            if do_builtins:
290                self.defn += defn
291        else:
292            self.decl += decl
293            self.defn += defn
294
295    def visit_object_type(self, name, info, base, members, variants):
296        # Nothing to do for the special empty builtin
297        if name == 'q_empty':
298            return
299        self.decl += gen_visit_members_decl(name)
300        self.defn += gen_visit_object_members(name, base, members, variants)
301        # TODO Worth changing the visitor signature, so we could
302        # directly use rather than repeat type.is_implicit()?
303        if not name.startswith('q_'):
304            # only explicit types need an allocating visit
305            self.decl += gen_visit_decl(name)
306            self.defn += gen_visit_object(name, base, members, variants)
307
308    def visit_alternate_type(self, name, info, variants):
309        self.decl += gen_visit_decl(name)
310        self.defn += gen_visit_alternate(name, variants)
311
312# If you link code generated from multiple schemata, you want only one
313# instance of the code for built-in types.  Generate it only when
314# do_builtins, enabled by command line option -b.  See also
315# QAPISchemaGenVisitVisitor.visit_end().
316do_builtins = False
317
318(input_file, output_dir, do_c, do_h, prefix, opts) = \
319    parse_command_line("b", ["builtins"])
320
321for o, a in opts:
322    if o in ("-b", "--builtins"):
323        do_builtins = True
324
325c_comment = '''
326/*
327 * schema-defined QAPI visitor functions
328 *
329 * Copyright IBM, Corp. 2011
330 *
331 * Authors:
332 *  Anthony Liguori   <aliguori@us.ibm.com>
333 *
334 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
335 * See the COPYING.LIB file in the top-level directory.
336 *
337 */
338'''
339h_comment = '''
340/*
341 * schema-defined QAPI visitor functions
342 *
343 * Copyright IBM, Corp. 2011
344 *
345 * Authors:
346 *  Anthony Liguori   <aliguori@us.ibm.com>
347 *
348 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
349 * See the COPYING.LIB file in the top-level directory.
350 *
351 */
352'''
353
354(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
355                            'qapi-visit.c', 'qapi-visit.h',
356                            c_comment, h_comment)
357
358fdef.write(mcgen('''
359#include "qemu/osdep.h"
360#include "qemu-common.h"
361#include "qapi/error.h"
362#include "%(prefix)sqapi-visit.h"
363''',
364                 prefix=prefix))
365
366fdecl.write(mcgen('''
367#include "qapi/visitor.h"
368#include "qapi/qmp/qerror.h"
369#include "%(prefix)sqapi-types.h"
370
371''',
372                  prefix=prefix))
373
374schema = QAPISchema(input_file)
375gen = QAPISchemaGenVisitVisitor()
376schema.visit(gen)
377fdef.write(gen.defn)
378fdecl.write(gen.decl)
379
380close_output(fdef, fdecl)
381