xref: /openbmc/qemu/scripts/qapi/types.py (revision 61bfb2e1a4666817b9d94f0a96109f8ef51b812b)
1"""
2QAPI types generator
3
4Copyright IBM, Corp. 2011
5Copyright (c) 2013-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.
13# See the COPYING file in the top-level directory.
14"""
15
16from qapi.common import *
17
18
19# variants must be emitted before their container; track what has already
20# been output
21objects_seen = set()
22
23
24def gen_enum_lookup(name, members, prefix=None):
25    ret = mcgen('''
26
27const QEnumLookup %(c_name)s_lookup = {
28    .array = (const char *const[]) {
29''',
30                c_name=c_name(name))
31    for m in members:
32        ret += gen_if(m.ifcond)
33        index = c_enum_const(name, m.name, prefix)
34        ret += mcgen('''
35        [%(index)s] = "%(name)s",
36''',
37                     index=index, name=m.name)
38        ret += gen_endif(m.ifcond)
39
40    ret += mcgen('''
41    },
42    .size = %(max_index)s
43};
44''',
45                 max_index=c_enum_const(name, '_MAX', prefix))
46    return ret
47
48
49def gen_enum(name, members, prefix=None):
50    # append automatically generated _MAX value
51    enum_members = members + [QAPISchemaEnumMember('_MAX', None)]
52
53    ret = mcgen('''
54
55typedef enum %(c_name)s {
56''',
57                c_name=c_name(name))
58
59    for m in enum_members:
60        ret += gen_if(m.ifcond)
61        ret += mcgen('''
62    %(c_enum)s,
63''',
64                     c_enum=c_enum_const(name, m.name, prefix))
65        ret += gen_endif(m.ifcond)
66
67    ret += mcgen('''
68} %(c_name)s;
69''',
70                 c_name=c_name(name))
71
72    ret += mcgen('''
73
74#define %(c_name)s_str(val) \\
75    qapi_enum_lookup(&%(c_name)s_lookup, (val))
76
77extern const QEnumLookup %(c_name)s_lookup;
78''',
79                 c_name=c_name(name))
80    return ret
81
82
83def gen_fwd_object_or_array(name):
84    return mcgen('''
85
86typedef struct %(c_name)s %(c_name)s;
87''',
88                 c_name=c_name(name))
89
90
91def gen_array(name, element_type):
92    return mcgen('''
93
94struct %(c_name)s {
95    %(c_name)s *next;
96    %(c_type)s value;
97};
98''',
99                 c_name=c_name(name), c_type=element_type.c_type())
100
101
102def gen_struct_members(members):
103    ret = ''
104    for memb in members:
105        ret += gen_if(memb.ifcond)
106        if memb.optional:
107            ret += mcgen('''
108    bool has_%(c_name)s;
109''',
110                         c_name=c_name(memb.name))
111        ret += mcgen('''
112    %(c_type)s %(c_name)s;
113''',
114                     c_type=memb.type.c_type(), c_name=c_name(memb.name))
115        ret += gen_endif(memb.ifcond)
116    return ret
117
118
119def gen_object(name, ifcond, base, members, variants):
120    if name in objects_seen:
121        return ''
122    objects_seen.add(name)
123
124    ret = ''
125    if variants:
126        for v in variants.variants:
127            if isinstance(v.type, QAPISchemaObjectType):
128                ret += gen_object(v.type.name, v.type.ifcond, v.type.base,
129                                  v.type.local_members, v.type.variants)
130
131    ret += mcgen('''
132
133''')
134    ret += gen_if(ifcond)
135    ret += mcgen('''
136struct %(c_name)s {
137''',
138                 c_name=c_name(name))
139
140    if base:
141        if not base.is_implicit():
142            ret += mcgen('''
143    /* Members inherited from %(c_name)s: */
144''',
145                         c_name=base.c_name())
146        ret += gen_struct_members(base.members)
147        if not base.is_implicit():
148            ret += mcgen('''
149    /* Own members: */
150''')
151    ret += gen_struct_members(members)
152
153    if variants:
154        ret += gen_variants(variants)
155
156    # Make sure that all structs have at least one member; this avoids
157    # potential issues with attempting to malloc space for zero-length
158    # structs in C, and also incompatibility with C++ (where an empty
159    # struct is size 1).
160    if (not base or base.is_empty()) and not members and not variants:
161        ret += mcgen('''
162    char qapi_dummy_for_empty_struct;
163''')
164
165    ret += mcgen('''
166};
167''')
168    ret += gen_endif(ifcond)
169
170    return ret
171
172
173def gen_upcast(name, base):
174    # C makes const-correctness ugly.  We have to cast away const to let
175    # this function work for both const and non-const obj.
176    return mcgen('''
177
178static inline %(base)s *qapi_%(c_name)s_base(const %(c_name)s *obj)
179{
180    return (%(base)s *)obj;
181}
182''',
183                 c_name=c_name(name), base=base.c_name())
184
185
186def gen_variants(variants):
187    ret = mcgen('''
188    union { /* union tag is @%(c_name)s */
189''',
190                c_name=c_name(variants.tag_member.name))
191
192    for var in variants.variants:
193        if var.type.name == 'q_empty':
194            continue
195        ret += gen_if(var.ifcond)
196        ret += mcgen('''
197        %(c_type)s %(c_name)s;
198''',
199                     c_type=var.type.c_unboxed_type(),
200                     c_name=c_name(var.name))
201        ret += gen_endif(var.ifcond)
202
203    ret += mcgen('''
204    } u;
205''')
206
207    return ret
208
209
210def gen_type_cleanup_decl(name):
211    ret = mcgen('''
212
213void qapi_free_%(c_name)s(%(c_name)s *obj);
214''',
215                c_name=c_name(name))
216    return ret
217
218
219def gen_type_cleanup(name):
220    ret = mcgen('''
221
222void qapi_free_%(c_name)s(%(c_name)s *obj)
223{
224    Visitor *v;
225
226    if (!obj) {
227        return;
228    }
229
230    v = qapi_dealloc_visitor_new();
231    visit_type_%(c_name)s(v, NULL, &obj, NULL);
232    visit_free(v);
233}
234''',
235                c_name=c_name(name))
236    return ret
237
238
239class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
240
241    def __init__(self, prefix):
242        QAPISchemaModularCVisitor.__init__(
243            self, prefix, 'qapi-types', ' * Schema-defined QAPI types',
244            __doc__)
245        self._add_system_module(None, ' * Built-in QAPI types')
246        self._genc.preamble_add(mcgen('''
247#include "qemu/osdep.h"
248#include "qapi/dealloc-visitor.h"
249#include "qapi/qapi-builtin-types.h"
250#include "qapi/qapi-builtin-visit.h"
251'''))
252        self._genh.preamble_add(mcgen('''
253#include "qapi/util.h"
254'''))
255
256    def _begin_user_module(self, name):
257        types = self._module_basename('qapi-types', name)
258        visit = self._module_basename('qapi-visit', name)
259        self._genc.preamble_add(mcgen('''
260#include "qemu/osdep.h"
261#include "qapi/dealloc-visitor.h"
262#include "%(types)s.h"
263#include "%(visit)s.h"
264''',
265                                      types=types, visit=visit))
266        self._genh.preamble_add(mcgen('''
267#include "qapi/qapi-builtin-types.h"
268'''))
269
270    def visit_begin(self, schema):
271        # gen_object() is recursive, ensure it doesn't visit the empty type
272        objects_seen.add(schema.the_empty_object_type.name)
273
274    def _gen_type_cleanup(self, name):
275        self._genh.add(gen_type_cleanup_decl(name))
276        self._genc.add(gen_type_cleanup(name))
277
278    def visit_enum_type(self, name, info, ifcond, members, prefix):
279        with ifcontext(ifcond, self._genh, self._genc):
280            self._genh.preamble_add(gen_enum(name, members, prefix))
281            self._genc.add(gen_enum_lookup(name, members, prefix))
282
283    def visit_array_type(self, name, info, ifcond, element_type):
284        with ifcontext(ifcond, self._genh, self._genc):
285            self._genh.preamble_add(gen_fwd_object_or_array(name))
286            self._genh.add(gen_array(name, element_type))
287            self._gen_type_cleanup(name)
288
289    def visit_object_type(self, name, info, ifcond, base, members, variants,
290                          features):
291        # Nothing to do for the special empty builtin
292        if name == 'q_empty':
293            return
294        with ifcontext(ifcond, self._genh):
295            self._genh.preamble_add(gen_fwd_object_or_array(name))
296        self._genh.add(gen_object(name, ifcond, base, members, variants))
297        with ifcontext(ifcond, self._genh, self._genc):
298            if base and not base.is_implicit():
299                self._genh.add(gen_upcast(name, base))
300            # TODO Worth changing the visitor signature, so we could
301            # directly use rather than repeat type.is_implicit()?
302            if not name.startswith('q_'):
303                # implicit types won't be directly allocated/freed
304                self._gen_type_cleanup(name)
305
306    def visit_alternate_type(self, name, info, ifcond, variants):
307        with ifcontext(ifcond, self._genh):
308            self._genh.preamble_add(gen_fwd_object_or_array(name))
309        self._genh.add(gen_object(name, ifcond, None,
310                                  [variants.tag_member], variants))
311        with ifcontext(ifcond, self._genh, self._genc):
312            self._gen_type_cleanup(name)
313
314
315def gen_types(schema, output_dir, prefix, opt_builtins):
316    vis = QAPISchemaGenTypeVisitor(prefix)
317    schema.visit(vis)
318    vis.write(output_dir, opt_builtins)
319