xref: /qemu/scripts/qapi/types.py (revision 6f0dd6c5)
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_fwd_object_or_array(name):
25    return mcgen('''
26
27typedef struct %(c_name)s %(c_name)s;
28''',
29                 c_name=c_name(name))
30
31
32def gen_array(name, element_type):
33    return mcgen('''
34
35struct %(c_name)s {
36    %(c_name)s *next;
37    %(c_type)s value;
38};
39''',
40                 c_name=c_name(name), c_type=element_type.c_type())
41
42
43def gen_struct_members(members):
44    ret = ''
45    for memb in members:
46        ret += gen_if(memb.ifcond)
47        if memb.optional:
48            ret += mcgen('''
49    bool has_%(c_name)s;
50''',
51                         c_name=c_name(memb.name))
52        ret += mcgen('''
53    %(c_type)s %(c_name)s;
54''',
55                     c_type=memb.type.c_type(), c_name=c_name(memb.name))
56        ret += gen_endif(memb.ifcond)
57    return ret
58
59
60def gen_object(name, ifcond, base, members, variants):
61    if name in objects_seen:
62        return ''
63    objects_seen.add(name)
64
65    ret = ''
66    if variants:
67        for v in variants.variants:
68            if isinstance(v.type, QAPISchemaObjectType):
69                ret += gen_object(v.type.name, v.type.ifcond, v.type.base,
70                                  v.type.local_members, v.type.variants)
71
72    ret += mcgen('''
73
74''')
75    ret += gen_if(ifcond)
76    ret += mcgen('''
77struct %(c_name)s {
78''',
79                 c_name=c_name(name))
80
81    if base:
82        if not base.is_implicit():
83            ret += mcgen('''
84    /* Members inherited from %(c_name)s: */
85''',
86                         c_name=base.c_name())
87        ret += gen_struct_members(base.members)
88        if not base.is_implicit():
89            ret += mcgen('''
90    /* Own members: */
91''')
92    ret += gen_struct_members(members)
93
94    if variants:
95        ret += gen_variants(variants)
96
97    # Make sure that all structs have at least one member; this avoids
98    # potential issues with attempting to malloc space for zero-length
99    # structs in C, and also incompatibility with C++ (where an empty
100    # struct is size 1).
101    if (not base or base.is_empty()) and not members and not variants:
102        ret += mcgen('''
103    char qapi_dummy_for_empty_struct;
104''')
105
106    ret += mcgen('''
107};
108''')
109    ret += gen_endif(ifcond)
110
111    return ret
112
113
114def gen_upcast(name, base):
115    # C makes const-correctness ugly.  We have to cast away const to let
116    # this function work for both const and non-const obj.
117    return mcgen('''
118
119static inline %(base)s *qapi_%(c_name)s_base(const %(c_name)s *obj)
120{
121    return (%(base)s *)obj;
122}
123''',
124                 c_name=c_name(name), base=base.c_name())
125
126
127def gen_variants(variants):
128    ret = mcgen('''
129    union { /* union tag is @%(c_name)s */
130''',
131                c_name=c_name(variants.tag_member.name))
132
133    for var in variants.variants:
134        if var.type.name == 'q_empty':
135            continue
136        ret += gen_if(var.ifcond)
137        ret += mcgen('''
138        %(c_type)s %(c_name)s;
139''',
140                     c_type=var.type.c_unboxed_type(),
141                     c_name=c_name(var.name))
142        ret += gen_endif(var.ifcond)
143
144    ret += mcgen('''
145    } u;
146''')
147
148    return ret
149
150
151def gen_type_cleanup_decl(name):
152    ret = mcgen('''
153
154void qapi_free_%(c_name)s(%(c_name)s *obj);
155''',
156                c_name=c_name(name))
157    return ret
158
159
160def gen_type_cleanup(name):
161    ret = mcgen('''
162
163void qapi_free_%(c_name)s(%(c_name)s *obj)
164{
165    Visitor *v;
166
167    if (!obj) {
168        return;
169    }
170
171    v = qapi_dealloc_visitor_new();
172    visit_type_%(c_name)s(v, NULL, &obj, NULL);
173    visit_free(v);
174}
175''',
176                c_name=c_name(name))
177    return ret
178
179
180class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
181
182    def __init__(self, prefix):
183        QAPISchemaModularCVisitor.__init__(
184            self, prefix, 'qapi-types', ' * Schema-defined QAPI types',
185            __doc__)
186        self._add_system_module(None, ' * Built-in QAPI types')
187        self._genc.preamble_add(mcgen('''
188#include "qemu/osdep.h"
189#include "qapi/dealloc-visitor.h"
190#include "qapi/qapi-builtin-types.h"
191#include "qapi/qapi-builtin-visit.h"
192'''))
193        self._genh.preamble_add(mcgen('''
194#include "qapi/util.h"
195'''))
196
197    def _begin_user_module(self, name):
198        types = self._module_basename('qapi-types', name)
199        visit = self._module_basename('qapi-visit', name)
200        self._genc.preamble_add(mcgen('''
201#include "qemu/osdep.h"
202#include "qapi/dealloc-visitor.h"
203#include "%(types)s.h"
204#include "%(visit)s.h"
205''',
206                                      types=types, visit=visit))
207        self._genh.preamble_add(mcgen('''
208#include "qapi/qapi-builtin-types.h"
209'''))
210
211    def visit_begin(self, schema):
212        # gen_object() is recursive, ensure it doesn't visit the empty type
213        objects_seen.add(schema.the_empty_object_type.name)
214
215    def _gen_type_cleanup(self, name):
216        self._genh.add(gen_type_cleanup_decl(name))
217        self._genc.add(gen_type_cleanup(name))
218
219    def visit_enum_type(self, name, info, ifcond, members, prefix):
220        with ifcontext(ifcond, self._genh, self._genc):
221            self._genh.preamble_add(gen_enum(name, members, prefix))
222            self._genc.add(gen_enum_lookup(name, members, prefix))
223
224    def visit_array_type(self, name, info, ifcond, element_type):
225        with ifcontext(ifcond, self._genh, self._genc):
226            self._genh.preamble_add(gen_fwd_object_or_array(name))
227            self._genh.add(gen_array(name, element_type))
228            self._gen_type_cleanup(name)
229
230    def visit_object_type(self, name, info, ifcond, base, members, variants,
231                          features):
232        # Nothing to do for the special empty builtin
233        if name == 'q_empty':
234            return
235        with ifcontext(ifcond, self._genh):
236            self._genh.preamble_add(gen_fwd_object_or_array(name))
237        self._genh.add(gen_object(name, ifcond, base, members, variants))
238        with ifcontext(ifcond, self._genh, self._genc):
239            if base and not base.is_implicit():
240                self._genh.add(gen_upcast(name, base))
241            # TODO Worth changing the visitor signature, so we could
242            # directly use rather than repeat type.is_implicit()?
243            if not name.startswith('q_'):
244                # implicit types won't be directly allocated/freed
245                self._gen_type_cleanup(name)
246
247    def visit_alternate_type(self, name, info, ifcond, variants):
248        with ifcontext(ifcond, self._genh):
249            self._genh.preamble_add(gen_fwd_object_or_array(name))
250        self._genh.add(gen_object(name, ifcond, None,
251                                  [variants.tag_member], variants))
252        with ifcontext(ifcond, self._genh, self._genc):
253            self._gen_type_cleanup(name)
254
255
256def gen_types(schema, output_dir, prefix, opt_builtins):
257    vis = QAPISchemaGenTypeVisitor(prefix)
258    schema.visit(vis)
259    vis.write(output_dir, opt_builtins)
260