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
18 from .common
import c_enum_const
, c_name
, mcgen
20 QAPISchemaModularCVisitor
,
30 QAPISchemaObjectTypeMember
,
34 from .source
import QAPISourceInfo
37 # variants must be emitted before their container; track what has already
42 def gen_enum_lookup(name
: str,
43 members
: List
[QAPISchemaEnumMember
],
44 prefix
: Optional
[str] = None) -> str:
45 max_index
= c_enum_const(name
, '_MAX', prefix
)
49 const QEnumLookup %(c_name)s_lookup = {
50 .array = (const char *const[]) {
54 ret
+= memb
.ifcond
.gen_if()
55 index
= c_enum_const(name
, memb
.name
, prefix
)
57 [%(index)s] = "%(name)s",
59 index
=index
, name
=memb
.name
)
60 ret
+= memb
.ifcond
.gen_endif()
62 special_features
= gen_special_features(memb
.features
)
63 if special_features
!= '0':
65 [%(index)s] = %(special_features)s,
67 index
=index
, special_features
=special_features
)
72 .special_features = (const unsigned char[%(max_index)s]) {
86 def gen_enum(name
: str,
87 members
: List
[QAPISchemaEnumMember
],
88 prefix
: Optional
[str] = None) -> str:
89 # append automatically generated _MAX value
90 enum_members
= members
+ [QAPISchemaEnumMember('_MAX', None)]
94 typedef enum %(c_name)s {
98 for memb
in enum_members
:
99 ret
+= memb
.ifcond
.gen_if()
103 c_enum
=c_enum_const(name
, memb
.name
, prefix
))
104 ret
+= memb
.ifcond
.gen_endif()
113 #define %(c_name)s_str(val) \\
114 qapi_enum_lookup(&%(c_name)s_lookup, (val))
116 extern const QEnumLookup %(c_name)s_lookup;
122 def gen_fwd_object_or_array(name
: str) -> str:
125 typedef struct %(c_name)s %(c_name)s;
130 def gen_array(name
: str, element_type
: QAPISchemaType
) -> str:
138 c_name
=c_name(name
), c_type
=element_type
.c_type())
141 def gen_struct_members(members
: List
[QAPISchemaObjectTypeMember
]) -> str:
144 ret
+= memb
.ifcond
.gen_if()
149 c_name
=c_name(memb
.name
))
151 %(c_type)s %(c_name)s;
153 c_type
=memb
.type.c_type(), c_name
=c_name(memb
.name
))
154 ret
+= memb
.ifcond
.gen_endif()
158 def gen_object(name
: str, ifcond
: QAPISchemaIfCond
,
159 base
: Optional
[QAPISchemaObjectType
],
160 members
: List
[QAPISchemaObjectTypeMember
],
161 variants
: Optional
[QAPISchemaVariants
]) -> str:
162 if name
in objects_seen
:
164 objects_seen
.add(name
)
167 for var
in variants
.variants
if variants
else ():
169 if not isinstance(obj
, QAPISchemaObjectType
):
171 ret
+= gen_object(obj
.name
, obj
.ifcond
, obj
.base
,
172 obj
.local_members
, obj
.variants
)
177 ret
+= ifcond
.gen_if()
184 if not base
.is_implicit():
186 /* Members inherited from %(c_name)s: */
188 c_name
=base
.c_name())
189 ret
+= gen_struct_members(base
.members
)
190 if not base
.is_implicit():
194 ret
+= gen_struct_members(members
)
197 ret
+= gen_variants(variants
)
199 # Make sure that all structs have at least one member; this avoids
200 # potential issues with attempting to malloc space for zero-length
201 # structs in C, and also incompatibility with C++ (where an empty
203 if (not base
or base
.is_empty()) and not members
and not variants
:
205 char qapi_dummy_for_empty_struct;
211 ret
+= ifcond
.gen_endif()
216 def gen_upcast(name
: str, base
: QAPISchemaObjectType
) -> str:
217 # C makes const-correctness ugly. We have to cast away const to let
218 # this function work for both const and non-const obj.
221 static inline %(base)s *qapi_%(c_name)s_base(const %(c_name)s *obj)
223 return (%(base)s *)obj;
226 c_name
=c_name(name
), base
=base
.c_name())
229 def gen_variants(variants
: QAPISchemaVariants
) -> str:
231 union { /* union tag is @%(c_name)s */
233 c_name
=c_name(variants
.tag_member
.name
))
235 for var
in variants
.variants
:
236 if var
.type.name
== 'q_empty':
238 ret
+= var
.ifcond
.gen_if()
240 %(c_type)s %(c_name)s;
242 c_type
=var
.type.c_unboxed_type(),
243 c_name
=c_name(var
.name
))
244 ret
+= var
.ifcond
.gen_endif()
253 def gen_type_cleanup_decl(name
: str) -> str:
256 void qapi_free_%(c_name)s(%(c_name)s *obj);
257 G_DEFINE_AUTOPTR_CLEANUP_FUNC(%(c_name)s, qapi_free_%(c_name)s)
263 def gen_type_cleanup(name
: str) -> str:
266 void qapi_free_%(c_name)s(%(c_name)s *obj)
274 v = qapi_dealloc_visitor_new();
275 visit_type_%(c_name)s(v, NULL, &obj, NULL);
283 class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor
):
285 def __init__(self
, prefix
: str):
287 prefix
, 'qapi-types', ' * Schema-defined QAPI types',
288 ' * Built-in QAPI types', __doc__
)
290 def _begin_builtin_module(self
) -> None:
291 self
._genc
.preamble_add(mcgen('''
292 #include "qemu/osdep.h"
293 #include "qapi/dealloc-visitor.h"
294 #include "qapi/qapi-builtin-types.h"
295 #include "qapi/qapi-builtin-visit.h"
297 self
._genh
.preamble_add(mcgen('''
298 #include "qapi/util.h"
301 def _begin_user_module(self
, name
: str) -> None:
302 types
= self
._module
_basename
('qapi-types', name
)
303 visit
= self
._module
_basename
('qapi-visit', name
)
304 self
._genc
.preamble_add(mcgen('''
305 #include "qemu/osdep.h"
306 #include "qapi/dealloc-visitor.h"
307 #include "%(types)s.h"
308 #include "%(visit)s.h"
310 types
=types
, visit
=visit
))
311 self
._genh
.preamble_add(mcgen('''
312 #include "qapi/qapi-builtin-types.h"
315 def visit_begin(self
, schema
: QAPISchema
) -> None:
316 # gen_object() is recursive, ensure it doesn't visit the empty type
317 objects_seen
.add(schema
.the_empty_object_type
.name
)
319 def _gen_type_cleanup(self
, name
: str) -> None:
320 self
._genh
.add(gen_type_cleanup_decl(name
))
321 self
._genc
.add(gen_type_cleanup(name
))
323 def visit_enum_type(self
,
325 info
: Optional
[QAPISourceInfo
],
326 ifcond
: QAPISchemaIfCond
,
327 features
: List
[QAPISchemaFeature
],
328 members
: List
[QAPISchemaEnumMember
],
329 prefix
: Optional
[str]) -> None:
330 with
ifcontext(ifcond
, self
._genh
, self
._genc
):
331 self
._genh
.preamble_add(gen_enum(name
, members
, prefix
))
332 self
._genc
.add(gen_enum_lookup(name
, members
, prefix
))
334 def visit_array_type(self
,
336 info
: Optional
[QAPISourceInfo
],
337 ifcond
: QAPISchemaIfCond
,
338 element_type
: QAPISchemaType
) -> None:
339 with
ifcontext(ifcond
, self
._genh
, self
._genc
):
340 self
._genh
.preamble_add(gen_fwd_object_or_array(name
))
341 self
._genh
.add(gen_array(name
, element_type
))
342 self
._gen
_type
_cleanup
(name
)
344 def visit_object_type(self
,
346 info
: Optional
[QAPISourceInfo
],
347 ifcond
: QAPISchemaIfCond
,
348 features
: List
[QAPISchemaFeature
],
349 base
: Optional
[QAPISchemaObjectType
],
350 members
: List
[QAPISchemaObjectTypeMember
],
351 variants
: Optional
[QAPISchemaVariants
]) -> None:
352 # Nothing to do for the special empty builtin
353 if name
== 'q_empty':
355 with
ifcontext(ifcond
, self
._genh
):
356 self
._genh
.preamble_add(gen_fwd_object_or_array(name
))
357 self
._genh
.add(gen_object(name
, ifcond
, base
, members
, variants
))
358 with
ifcontext(ifcond
, self
._genh
, self
._genc
):
359 if base
and not base
.is_implicit():
360 self
._genh
.add(gen_upcast(name
, base
))
361 # TODO Worth changing the visitor signature, so we could
362 # directly use rather than repeat type.is_implicit()?
363 if not name
.startswith('q_'):
364 # implicit types won't be directly allocated/freed
365 self
._gen
_type
_cleanup
(name
)
367 def visit_alternate_type(self
,
369 info
: Optional
[QAPISourceInfo
],
370 ifcond
: QAPISchemaIfCond
,
371 features
: List
[QAPISchemaFeature
],
372 variants
: QAPISchemaVariants
) -> None:
373 with
ifcontext(ifcond
, self
._genh
):
374 self
._genh
.preamble_add(gen_fwd_object_or_array(name
))
375 self
._genh
.add(gen_object(name
, ifcond
, None,
376 [variants
.tag_member
], variants
))
377 with
ifcontext(ifcond
, self
._genh
, self
._genc
):
378 self
._gen
_type
_cleanup
(name
)
381 def gen_types(schema
: QAPISchema
,
384 opt_builtins
: bool) -> None:
385 vis
= QAPISchemaGenTypeVisitor(prefix
)
387 vis
.write(output_dir
, opt_builtins
)