4 Copyright IBM, Corp. 2011
5 Copyright (c) 2013-2018 Red Hat Inc.
8 Anthony Liguori <aliguori@us.ibm.com>
9 Michael Roth <mdroth@linux.vnet.ibm.com>
10 Markus Armbruster <armbru@redhat.com>
12 This work is licensed under the terms of the GNU GPL, version 2.
13 # See the COPYING file in the top-level directory.
16 from typing
import List
, Optional
, Sequence
25 from .gen
import QAPISchemaModularCVisitor
, ifcontext
31 QAPISchemaObjectTypeMember
,
35 from .source
import QAPISourceInfo
38 # variants must be emitted before their container; track what has already
43 def gen_enum_lookup(name
: str,
44 members
: List
[QAPISchemaEnumMember
],
45 prefix
: Optional
[str] = None) -> str:
48 const QEnumLookup %(c_name)s_lookup = {
49 .array = (const char *const[]) {
53 ret
+= gen_if(memb
.ifcond
)
54 index
= c_enum_const(name
, memb
.name
, prefix
)
56 [%(index)s] = "%(name)s",
58 index
=index
, name
=memb
.name
)
59 ret
+= gen_endif(memb
.ifcond
)
66 max_index
=c_enum_const(name
, '_MAX', prefix
))
70 def gen_enum(name
: str,
71 members
: List
[QAPISchemaEnumMember
],
72 prefix
: Optional
[str] = None) -> str:
73 # append automatically generated _MAX value
74 enum_members
= members
+ [QAPISchemaEnumMember('_MAX', None)]
78 typedef enum %(c_name)s {
82 for memb
in enum_members
:
83 ret
+= gen_if(memb
.ifcond
)
87 c_enum
=c_enum_const(name
, memb
.name
, prefix
))
88 ret
+= gen_endif(memb
.ifcond
)
97 #define %(c_name)s_str(val) \\
98 qapi_enum_lookup(&%(c_name)s_lookup, (val))
100 extern const QEnumLookup %(c_name)s_lookup;
106 def gen_fwd_object_or_array(name
: str) -> str:
109 typedef struct %(c_name)s %(c_name)s;
114 def gen_array(name
: str, element_type
: QAPISchemaType
) -> str:
122 c_name
=c_name(name
), c_type
=element_type
.c_type())
125 def gen_struct_members(members
: List
[QAPISchemaObjectTypeMember
]) -> str:
128 ret
+= gen_if(memb
.ifcond
)
133 c_name
=c_name(memb
.name
))
135 %(c_type)s %(c_name)s;
137 c_type
=memb
.type.c_type(), c_name
=c_name(memb
.name
))
138 ret
+= gen_endif(memb
.ifcond
)
142 def gen_object(name
: str, ifcond
: Sequence
[str],
143 base
: Optional
[QAPISchemaObjectType
],
144 members
: List
[QAPISchemaObjectTypeMember
],
145 variants
: Optional
[QAPISchemaVariants
]) -> str:
146 if name
in objects_seen
:
148 objects_seen
.add(name
)
151 for var
in variants
.variants
if variants
else ():
153 if not isinstance(obj
, QAPISchemaObjectType
):
155 ret
+= gen_object(obj
.name
, obj
.ifcond
, obj
.base
,
156 obj
.local_members
, obj
.variants
)
161 ret
+= gen_if(ifcond
)
168 if not base
.is_implicit():
170 /* Members inherited from %(c_name)s: */
172 c_name
=base
.c_name())
173 ret
+= gen_struct_members(base
.members
)
174 if not base
.is_implicit():
178 ret
+= gen_struct_members(members
)
181 ret
+= gen_variants(variants
)
183 # Make sure that all structs have at least one member; this avoids
184 # potential issues with attempting to malloc space for zero-length
185 # structs in C, and also incompatibility with C++ (where an empty
187 if (not base
or base
.is_empty()) and not members
and not variants
:
189 char qapi_dummy_for_empty_struct;
195 ret
+= gen_endif(ifcond
)
200 def gen_upcast(name
: str, base
: QAPISchemaObjectType
) -> str:
201 # C makes const-correctness ugly. We have to cast away const to let
202 # this function work for both const and non-const obj.
205 static inline %(base)s *qapi_%(c_name)s_base(const %(c_name)s *obj)
207 return (%(base)s *)obj;
210 c_name
=c_name(name
), base
=base
.c_name())
213 def gen_variants(variants
: QAPISchemaVariants
) -> str:
215 union { /* union tag is @%(c_name)s */
217 c_name
=c_name(variants
.tag_member
.name
))
219 for var
in variants
.variants
:
220 if var
.type.name
== 'q_empty':
222 ret
+= gen_if(var
.ifcond
)
224 %(c_type)s %(c_name)s;
226 c_type
=var
.type.c_unboxed_type(),
227 c_name
=c_name(var
.name
))
228 ret
+= gen_endif(var
.ifcond
)
237 def gen_type_cleanup_decl(name
: str) -> str:
240 void qapi_free_%(c_name)s(%(c_name)s *obj);
241 G_DEFINE_AUTOPTR_CLEANUP_FUNC(%(c_name)s, qapi_free_%(c_name)s)
247 def gen_type_cleanup(name
: str) -> str:
250 void qapi_free_%(c_name)s(%(c_name)s *obj)
258 v = qapi_dealloc_visitor_new();
259 visit_type_%(c_name)s(v, NULL, &obj, NULL);
267 class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor
):
269 def __init__(self
, prefix
: str):
271 prefix
, 'qapi-types', ' * Schema-defined QAPI types',
272 ' * Built-in QAPI types', __doc__
)
274 def _begin_builtin_module(self
) -> None:
275 self
._genc
.preamble_add(mcgen('''
276 #include "qemu/osdep.h"
277 #include "qapi/dealloc-visitor.h"
278 #include "qapi/qapi-builtin-types.h"
279 #include "qapi/qapi-builtin-visit.h"
281 self
._genh
.preamble_add(mcgen('''
282 #include "qapi/util.h"
285 def _begin_user_module(self
, name
: str) -> None:
286 types
= self
._module
_basename
('qapi-types', name
)
287 visit
= self
._module
_basename
('qapi-visit', name
)
288 self
._genc
.preamble_add(mcgen('''
289 #include "qemu/osdep.h"
290 #include "qapi/dealloc-visitor.h"
291 #include "%(types)s.h"
292 #include "%(visit)s.h"
294 types
=types
, visit
=visit
))
295 self
._genh
.preamble_add(mcgen('''
296 #include "qapi/qapi-builtin-types.h"
299 def visit_begin(self
, schema
: QAPISchema
) -> None:
300 # gen_object() is recursive, ensure it doesn't visit the empty type
301 objects_seen
.add(schema
.the_empty_object_type
.name
)
303 def _gen_type_cleanup(self
, name
: str) -> None:
304 self
._genh
.add(gen_type_cleanup_decl(name
))
305 self
._genc
.add(gen_type_cleanup(name
))
307 def visit_enum_type(self
,
309 info
: Optional
[QAPISourceInfo
],
310 ifcond
: Sequence
[str],
311 features
: List
[QAPISchemaFeature
],
312 members
: List
[QAPISchemaEnumMember
],
313 prefix
: Optional
[str]) -> None:
314 with
ifcontext(ifcond
, self
._genh
, self
._genc
):
315 self
._genh
.preamble_add(gen_enum(name
, members
, prefix
))
316 self
._genc
.add(gen_enum_lookup(name
, members
, prefix
))
318 def visit_array_type(self
,
320 info
: Optional
[QAPISourceInfo
],
321 ifcond
: Sequence
[str],
322 element_type
: QAPISchemaType
) -> None:
323 with
ifcontext(ifcond
, self
._genh
, self
._genc
):
324 self
._genh
.preamble_add(gen_fwd_object_or_array(name
))
325 self
._genh
.add(gen_array(name
, element_type
))
326 self
._gen
_type
_cleanup
(name
)
328 def visit_object_type(self
,
330 info
: Optional
[QAPISourceInfo
],
331 ifcond
: Sequence
[str],
332 features
: List
[QAPISchemaFeature
],
333 base
: Optional
[QAPISchemaObjectType
],
334 members
: List
[QAPISchemaObjectTypeMember
],
335 variants
: Optional
[QAPISchemaVariants
]) -> None:
336 # Nothing to do for the special empty builtin
337 if name
== 'q_empty':
339 with
ifcontext(ifcond
, self
._genh
):
340 self
._genh
.preamble_add(gen_fwd_object_or_array(name
))
341 self
._genh
.add(gen_object(name
, ifcond
, base
, members
, variants
))
342 with
ifcontext(ifcond
, self
._genh
, self
._genc
):
343 if base
and not base
.is_implicit():
344 self
._genh
.add(gen_upcast(name
, base
))
345 # TODO Worth changing the visitor signature, so we could
346 # directly use rather than repeat type.is_implicit()?
347 if not name
.startswith('q_'):
348 # implicit types won't be directly allocated/freed
349 self
._gen
_type
_cleanup
(name
)
351 def visit_alternate_type(self
,
353 info
: Optional
[QAPISourceInfo
],
354 ifcond
: Sequence
[str],
355 features
: List
[QAPISchemaFeature
],
356 variants
: QAPISchemaVariants
) -> None:
357 with
ifcontext(ifcond
, self
._genh
):
358 self
._genh
.preamble_add(gen_fwd_object_or_array(name
))
359 self
._genh
.add(gen_object(name
, ifcond
, None,
360 [variants
.tag_member
], variants
))
361 with
ifcontext(ifcond
, self
._genh
, self
._genc
):
362 self
._gen
_type
_cleanup
(name
)
365 def gen_types(schema
: QAPISchema
,
368 opt_builtins
: bool) -> None:
369 vis
= QAPISchemaGenTypeVisitor(prefix
)
371 vis
.write(output_dir
, opt_builtins
)