xref: /openbmc/qemu/scripts/qapi/visit.py (revision 62a35aaa)
1"""
2QAPI visitor generator
3
4Copyright IBM, Corp. 2011
5Copyright (C) 2014-2018 Red Hat, Inc.
6
7Authors:
8 Anthony Liguori <aliguori@us.ibm.com>
9 Michael Roth    <mdroth@linux.vnet.ibm.com>
10 Markus Armbruster <armbru@redhat.com>
11
12This work is licensed under the terms of the GNU GPL, version 2.
13See the COPYING file in the top-level directory.
14"""
15
16from qapi.common import *
17from qapi.gen import QAPISchemaModularCVisitor, ifcontext
18from qapi.schema import QAPISchemaObjectType
19
20
21def gen_visit_decl(name, scalar=False):
22    c_type = c_name(name) + ' *'
23    if not scalar:
24        c_type += '*'
25    return mcgen('''
26bool visit_type_%(c_name)s(Visitor *v, const char *name, %(c_type)sobj, Error **errp);
27''',
28                 c_name=c_name(name), c_type=c_type)
29
30
31def gen_visit_members_decl(name):
32    return mcgen('''
33
34bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp);
35''',
36                 c_name=c_name(name))
37
38
39def gen_visit_object_members(name, base, members, variants):
40    ret = mcgen('''
41
42bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
43{
44    Error *err = NULL;
45
46''',
47                c_name=c_name(name))
48
49    if base:
50        ret += mcgen('''
51    if (!visit_type_%(c_type)s_members(v, (%(c_type)s *)obj, errp)) {
52        return false;
53    }
54''',
55                     c_type=base.c_name())
56
57    for memb in members:
58        ret += gen_if(memb.ifcond)
59        if memb.optional:
60            ret += mcgen('''
61    if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) {
62''',
63                         name=memb.name, c_name=c_name(memb.name))
64            push_indent()
65        ret += mcgen('''
66    if (!visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, errp)) {
67        return false;
68    }
69''',
70                     c_type=memb.type.c_name(), name=memb.name,
71                     c_name=c_name(memb.name))
72        if memb.optional:
73            pop_indent()
74            ret += mcgen('''
75    }
76''')
77        ret += gen_endif(memb.ifcond)
78
79    if variants:
80        ret += mcgen('''
81    switch (obj->%(c_name)s) {
82''',
83                     c_name=c_name(variants.tag_member.name))
84
85        for var in variants.variants:
86            case_str = c_enum_const(variants.tag_member.type.name,
87                                    var.name,
88                                    variants.tag_member.type.prefix)
89            ret += gen_if(var.ifcond)
90            if var.type.name == 'q_empty':
91                # valid variant and nothing to do
92                ret += mcgen('''
93    case %(case)s:
94        break;
95''',
96                             case=case_str)
97            else:
98                ret += mcgen('''
99    case %(case)s:
100        visit_type_%(c_type)s_members(v, &obj->u.%(c_name)s, &err);
101        break;
102''',
103                             case=case_str,
104                             c_type=var.type.c_name(), c_name=c_name(var.name))
105
106            ret += gen_endif(var.ifcond)
107        ret += mcgen('''
108    default:
109        abort();
110    }
111''')
112
113    ret += mcgen('''
114    error_propagate(errp, err);
115    return !err;
116}
117''')
118    return ret
119
120
121def gen_visit_list(name, element_type):
122    return mcgen('''
123
124bool visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
125{
126    Error *err = NULL;
127    %(c_name)s *tail;
128    size_t size = sizeof(**obj);
129
130    if (!visit_start_list(v, name, (GenericList **)obj, size, errp)) {
131        return false;
132    }
133
134    for (tail = *obj; tail;
135         tail = (%(c_name)s *)visit_next_list(v, (GenericList *)tail, size)) {
136        visit_type_%(c_elt_type)s(v, NULL, &tail->value, &err);
137        if (err) {
138            break;
139        }
140    }
141
142    if (!err) {
143        visit_check_list(v, &err);
144    }
145    visit_end_list(v, (void **)obj);
146    if (err && visit_is_input(v)) {
147        qapi_free_%(c_name)s(*obj);
148        *obj = NULL;
149    }
150    error_propagate(errp, err);
151    return !err;
152}
153''',
154                 c_name=c_name(name), c_elt_type=element_type.c_name())
155
156
157def gen_visit_enum(name):
158    return mcgen('''
159
160bool visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s *obj, Error **errp)
161{
162    int value = *obj;
163    bool ok = visit_type_enum(v, name, &value, &%(c_name)s_lookup, errp);
164    *obj = value;
165    return ok;
166}
167''',
168                 c_name=c_name(name))
169
170
171def gen_visit_alternate(name, variants):
172    ret = mcgen('''
173
174bool visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
175{
176    Error *err = NULL;
177
178    if (!visit_start_alternate(v, name, (GenericAlternate **)obj,
179                               sizeof(**obj), errp)) {
180        return false;
181    }
182    if (!*obj) {
183        /* incomplete */
184        assert(visit_is_dealloc(v));
185        goto out_obj;
186    }
187    switch ((*obj)->type) {
188''',
189                c_name=c_name(name))
190
191    for var in variants.variants:
192        ret += gen_if(var.ifcond)
193        ret += mcgen('''
194    case %(case)s:
195''',
196                     case=var.type.alternate_qtype())
197        if isinstance(var.type, QAPISchemaObjectType):
198            ret += mcgen('''
199        visit_start_struct(v, name, NULL, 0, &err);
200        if (err) {
201            break;
202        }
203        visit_type_%(c_type)s_members(v, &(*obj)->u.%(c_name)s, &err);
204        if (!err) {
205            visit_check_struct(v, &err);
206        }
207        visit_end_struct(v, NULL);
208''',
209                         c_type=var.type.c_name(),
210                         c_name=c_name(var.name))
211        else:
212            ret += mcgen('''
213        visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, &err);
214''',
215                         c_type=var.type.c_name(),
216                         c_name=c_name(var.name))
217        ret += mcgen('''
218        break;
219''')
220        ret += gen_endif(var.ifcond)
221
222    ret += mcgen('''
223    case QTYPE_NONE:
224        abort();
225    default:
226        assert(visit_is_input(v));
227        error_setg(&err, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
228                   "%(name)s");
229        /* Avoid passing invalid *obj to qapi_free_%(c_name)s() */
230        g_free(*obj);
231        *obj = NULL;
232    }
233out_obj:
234    visit_end_alternate(v, (void **)obj);
235    if (err && visit_is_input(v)) {
236        qapi_free_%(c_name)s(*obj);
237        *obj = NULL;
238    }
239    error_propagate(errp, err);
240    return !err;
241}
242''',
243                 name=name, c_name=c_name(name))
244
245    return ret
246
247
248def gen_visit_object(name, base, members, variants):
249    return mcgen('''
250
251bool visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
252{
253    Error *err = NULL;
254
255    if (!visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), errp)) {
256        return false;
257    }
258    if (!*obj) {
259        /* incomplete */
260        assert(visit_is_dealloc(v));
261        goto out_obj;
262    }
263    visit_type_%(c_name)s_members(v, *obj, &err);
264    if (err) {
265        goto out_obj;
266    }
267    visit_check_struct(v, &err);
268out_obj:
269    visit_end_struct(v, (void **)obj);
270    if (err && visit_is_input(v)) {
271        qapi_free_%(c_name)s(*obj);
272        *obj = NULL;
273    }
274    error_propagate(errp, err);
275    return !err;
276}
277''',
278                 c_name=c_name(name))
279
280
281class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
282
283    def __init__(self, prefix):
284        super().__init__(
285            prefix, 'qapi-visit', ' * Schema-defined QAPI visitors',
286            ' * Built-in QAPI visitors', __doc__)
287
288    def _begin_system_module(self, name):
289        self._genc.preamble_add(mcgen('''
290#include "qemu/osdep.h"
291#include "qapi/error.h"
292#include "qapi/qapi-builtin-visit.h"
293'''))
294        self._genh.preamble_add(mcgen('''
295#include "qapi/visitor.h"
296#include "qapi/qapi-builtin-types.h"
297
298'''))
299
300    def _begin_user_module(self, name):
301        types = self._module_basename('qapi-types', name)
302        visit = self._module_basename('qapi-visit', name)
303        self._genc.preamble_add(mcgen('''
304#include "qemu/osdep.h"
305#include "qapi/error.h"
306#include "qapi/qmp/qerror.h"
307#include "%(visit)s.h"
308''',
309                                      visit=visit))
310        self._genh.preamble_add(mcgen('''
311#include "qapi/qapi-builtin-visit.h"
312#include "%(types)s.h"
313
314''',
315                                      types=types))
316
317    def visit_enum_type(self, name, info, ifcond, features, members, prefix):
318        with ifcontext(ifcond, self._genh, self._genc):
319            self._genh.add(gen_visit_decl(name, scalar=True))
320            self._genc.add(gen_visit_enum(name))
321
322    def visit_array_type(self, name, info, ifcond, element_type):
323        with ifcontext(ifcond, self._genh, self._genc):
324            self._genh.add(gen_visit_decl(name))
325            self._genc.add(gen_visit_list(name, element_type))
326
327    def visit_object_type(self, name, info, ifcond, features,
328                          base, members, variants):
329        # Nothing to do for the special empty builtin
330        if name == 'q_empty':
331            return
332        with ifcontext(ifcond, self._genh, self._genc):
333            self._genh.add(gen_visit_members_decl(name))
334            self._genc.add(gen_visit_object_members(name, base,
335                                                    members, variants))
336            # TODO Worth changing the visitor signature, so we could
337            # directly use rather than repeat type.is_implicit()?
338            if not name.startswith('q_'):
339                # only explicit types need an allocating visit
340                self._genh.add(gen_visit_decl(name))
341                self._genc.add(gen_visit_object(name, base, members, variants))
342
343    def visit_alternate_type(self, name, info, ifcond, features, variants):
344        with ifcontext(ifcond, self._genh, self._genc):
345            self._genh.add(gen_visit_decl(name))
346            self._genc.add(gen_visit_alternate(name, variants))
347
348
349def gen_visit(schema, output_dir, prefix, opt_builtins):
350    vis = QAPISchemaGenVisitVisitor(prefix)
351    schema.visit(vis)
352    vis.write(output_dir, opt_builtins)
353