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
,
26 QAPISchemaAlternatives
,
32 QAPISchemaObjectTypeMember
,
36 from .source
import QAPISourceInfo
39 # variants must be emitted before their container; track what has already
44 def gen_enum_lookup(name
: str,
45 members
: List
[QAPISchemaEnumMember
],
46 prefix
: Optional
[str] = None) -> str:
47 max_index
= c_enum_const(name
, '_MAX', prefix
)
51 const QEnumLookup %(c_name)s_lookup = {
52 .array = (const char *const[]) {
56 ret
+= memb
.ifcond
.gen_if()
57 index
= c_enum_const(name
, memb
.name
, prefix
)
59 [%(index)s] = "%(name)s",
61 index
=index
, name
=memb
.name
)
62 ret
+= memb
.ifcond
.gen_endif()
64 special_features
= gen_special_features(memb
.features
)
65 if special_features
!= '0':
67 [%(index)s] = %(special_features)s,
69 index
=index
, special_features
=special_features
)
74 .special_features = (const unsigned char[%(max_index)s]) {
88 def gen_enum(name
: str,
89 members
: List
[QAPISchemaEnumMember
],
90 prefix
: Optional
[str] = None) -> str:
91 # append automatically generated _MAX value
92 enum_members
= members
+ [QAPISchemaEnumMember('_MAX', None)]
96 typedef enum %(c_name)s {
100 for memb
in enum_members
:
101 ret
+= memb
.ifcond
.gen_if()
105 c_enum
=c_enum_const(name
, memb
.name
, prefix
))
106 ret
+= memb
.ifcond
.gen_endif()
115 #define %(c_name)s_str(val) \\
116 qapi_enum_lookup(&%(c_name)s_lookup, (val))
118 extern const QEnumLookup %(c_name)s_lookup;
124 def gen_fwd_object_or_array(name
: str) -> str:
127 typedef struct %(c_name)s %(c_name)s;
132 def gen_array(name
: str, element_type
: QAPISchemaType
) -> str:
140 c_name
=c_name(name
), c_type
=element_type
.c_type())
143 def gen_struct_members(members
: List
[QAPISchemaObjectTypeMember
]) -> str:
146 ret
+= memb
.ifcond
.gen_if()
151 c_name
=c_name(memb
.name
))
153 %(c_type)s %(c_name)s;
155 c_type
=memb
.type.c_type(), c_name
=c_name(memb
.name
))
156 ret
+= memb
.ifcond
.gen_endif()
160 def gen_object(name
: str, ifcond
: QAPISchemaIfCond
,
161 base
: Optional
[QAPISchemaObjectType
],
162 members
: List
[QAPISchemaObjectTypeMember
],
163 variants
: Optional
[QAPISchemaVariants
]) -> str:
164 if name
in objects_seen
:
166 objects_seen
.add(name
)
169 for var
in variants
.variants
if variants
else ():
171 if not isinstance(obj
, QAPISchemaObjectType
):
173 ret
+= gen_object(obj
.name
, obj
.ifcond
, obj
.base
,
174 obj
.local_members
, obj
.branches
)
179 ret
+= ifcond
.gen_if()
186 if not base
.is_implicit():
188 /* Members inherited from %(c_name)s: */
190 c_name
=base
.c_name())
191 ret
+= gen_struct_members(base
.members
)
192 if not base
.is_implicit():
196 ret
+= gen_struct_members(members
)
199 ret
+= gen_variants(variants
)
201 # Make sure that all structs have at least one member; this avoids
202 # potential issues with attempting to malloc space for zero-length
203 # structs in C, and also incompatibility with C++ (where an empty
205 if (not base
or base
.is_empty()) and not members
and not variants
:
207 char qapi_dummy_for_empty_struct;
213 ret
+= ifcond
.gen_endif()
218 def gen_upcast(name
: str, base
: QAPISchemaObjectType
) -> str:
219 # C makes const-correctness ugly. We have to cast away const to let
220 # this function work for both const and non-const obj.
223 static inline %(base)s *qapi_%(c_name)s_base(const %(c_name)s *obj)
225 return (%(base)s *)obj;
228 c_name
=c_name(name
), base
=base
.c_name())
231 def gen_variants(variants
: QAPISchemaVariants
) -> str:
233 union { /* union tag is @%(c_name)s */
235 c_name
=c_name(variants
.tag_member
.name
))
237 for var
in variants
.variants
:
238 if var
.type.name
== 'q_empty':
240 ret
+= var
.ifcond
.gen_if()
242 %(c_type)s %(c_name)s;
244 c_type
=var
.type.c_unboxed_type(),
245 c_name
=c_name(var
.name
))
246 ret
+= var
.ifcond
.gen_endif()
255 def gen_type_cleanup_decl(name
: str) -> str:
258 void qapi_free_%(c_name)s(%(c_name)s *obj);
259 G_DEFINE_AUTOPTR_CLEANUP_FUNC(%(c_name)s, qapi_free_%(c_name)s)
265 def gen_type_cleanup(name
: str) -> str:
268 void qapi_free_%(c_name)s(%(c_name)s *obj)
276 v = qapi_dealloc_visitor_new();
277 visit_type_%(c_name)s(v, NULL, &obj, NULL);
285 class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor
):
287 def __init__(self
, prefix
: str):
289 prefix
, 'qapi-types', ' * Schema-defined QAPI types',
290 ' * Built-in QAPI types', __doc__
)
292 def _begin_builtin_module(self
) -> None:
293 self
._genc
.preamble_add(mcgen('''
294 #include "qemu/osdep.h"
295 #include "qapi/dealloc-visitor.h"
296 #include "qapi/qapi-builtin-types.h"
297 #include "qapi/qapi-builtin-visit.h"
299 self
._genh
.preamble_add(mcgen('''
300 #include "qapi/util.h"
303 def _begin_user_module(self
, name
: str) -> None:
304 types
= self
._module
_basename
('qapi-types', name
)
305 visit
= self
._module
_basename
('qapi-visit', name
)
306 self
._genc
.preamble_add(mcgen('''
307 #include "qemu/osdep.h"
308 #include "qapi/dealloc-visitor.h"
309 #include "%(types)s.h"
310 #include "%(visit)s.h"
312 types
=types
, visit
=visit
))
313 self
._genh
.preamble_add(mcgen('''
314 #include "qapi/qapi-builtin-types.h"
317 def visit_begin(self
, schema
: QAPISchema
) -> None:
318 # gen_object() is recursive, ensure it doesn't visit the empty type
319 objects_seen
.add(schema
.the_empty_object_type
.name
)
321 def _gen_type_cleanup(self
, name
: str) -> None:
322 self
._genh
.add(gen_type_cleanup_decl(name
))
323 self
._genc
.add(gen_type_cleanup(name
))
325 def visit_enum_type(self
,
327 info
: Optional
[QAPISourceInfo
],
328 ifcond
: QAPISchemaIfCond
,
329 features
: List
[QAPISchemaFeature
],
330 members
: List
[QAPISchemaEnumMember
],
331 prefix
: Optional
[str]) -> None:
332 with
ifcontext(ifcond
, self
._genh
, self
._genc
):
333 self
._genh
.preamble_add(gen_enum(name
, members
, prefix
))
334 self
._genc
.add(gen_enum_lookup(name
, members
, prefix
))
336 def visit_array_type(self
,
338 info
: Optional
[QAPISourceInfo
],
339 ifcond
: QAPISchemaIfCond
,
340 element_type
: QAPISchemaType
) -> None:
341 with
ifcontext(ifcond
, self
._genh
, self
._genc
):
342 self
._genh
.preamble_add(gen_fwd_object_or_array(name
))
343 self
._genh
.add(gen_array(name
, element_type
))
344 self
._gen
_type
_cleanup
(name
)
346 def visit_object_type(self
,
348 info
: Optional
[QAPISourceInfo
],
349 ifcond
: QAPISchemaIfCond
,
350 features
: List
[QAPISchemaFeature
],
351 base
: Optional
[QAPISchemaObjectType
],
352 members
: List
[QAPISchemaObjectTypeMember
],
353 branches
: Optional
[QAPISchemaBranches
]) -> None:
354 # Nothing to do for the special empty builtin
355 if name
== 'q_empty':
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
, base
, members
, branches
))
360 with
ifcontext(ifcond
, self
._genh
, self
._genc
):
361 if base
and not base
.is_implicit():
362 self
._genh
.add(gen_upcast(name
, base
))
363 # TODO Worth changing the visitor signature, so we could
364 # directly use rather than repeat type.is_implicit()?
365 if not name
.startswith('q_'):
366 # implicit types won't be directly allocated/freed
367 self
._gen
_type
_cleanup
(name
)
369 def visit_alternate_type(self
,
371 info
: Optional
[QAPISourceInfo
],
372 ifcond
: QAPISchemaIfCond
,
373 features
: List
[QAPISchemaFeature
],
374 alternatives
: QAPISchemaAlternatives
) -> None:
375 with
ifcontext(ifcond
, self
._genh
):
376 self
._genh
.preamble_add(gen_fwd_object_or_array(name
))
377 self
._genh
.add(gen_object(name
, ifcond
, None,
378 [alternatives
.tag_member
], alternatives
))
379 with
ifcontext(ifcond
, self
._genh
, self
._genc
):
380 self
._gen
_type
_cleanup
(name
)
383 def gen_types(schema
: QAPISchema
,
386 opt_builtins
: bool) -> None:
387 vis
= QAPISchemaGenTypeVisitor(prefix
)
389 vis
.write(output_dir
, opt_builtins
)