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