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