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