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
19 from .gen
import QAPISchemaModularCVisitor
, gen_special_features
, ifcontext
26 QAPISchemaObjectTypeMember
,
30 from .source
import QAPISourceInfo
33 # variants must be emitted before their container; track what has already
38 def gen_enum_lookup(name
: str,
39 members
: List
[QAPISchemaEnumMember
],
40 prefix
: Optional
[str] = None) -> str:
41 max_index
= c_enum_const(name
, '_MAX', prefix
)
45 const QEnumLookup %(c_name)s_lookup = {
46 .array = (const char *const[]) {
50 ret
+= memb
.ifcond
.gen_if()
51 index
= c_enum_const(name
, memb
.name
, prefix
)
53 [%(index)s] = "%(name)s",
55 index
=index
, name
=memb
.name
)
56 ret
+= memb
.ifcond
.gen_endif()
58 special_features
= gen_special_features(memb
.features
)
59 if special_features
!= '0':
61 [%(index)s] = %(special_features)s,
63 index
=index
, special_features
=special_features
)
68 .special_features = (const unsigned char[%(max_index)s]) {
82 def gen_enum(name
: str,
83 members
: List
[QAPISchemaEnumMember
],
84 prefix
: Optional
[str] = None) -> str:
85 # append automatically generated _MAX value
86 enum_members
= members
+ [QAPISchemaEnumMember('_MAX', None)]
90 typedef enum %(c_name)s {
94 for memb
in enum_members
:
95 ret
+= memb
.ifcond
.gen_if()
99 c_enum
=c_enum_const(name
, memb
.name
, prefix
))
100 ret
+= memb
.ifcond
.gen_endif()
109 #define %(c_name)s_str(val) \\
110 qapi_enum_lookup(&%(c_name)s_lookup, (val))
112 extern const QEnumLookup %(c_name)s_lookup;
118 def gen_fwd_object_or_array(name
: str) -> str:
121 typedef struct %(c_name)s %(c_name)s;
126 def gen_array(name
: str, element_type
: QAPISchemaType
) -> str:
134 c_name
=c_name(name
), c_type
=element_type
.c_type())
137 def gen_struct_members(members
: List
[QAPISchemaObjectTypeMember
]) -> str:
140 ret
+= memb
.ifcond
.gen_if()
145 c_name
=c_name(memb
.name
))
147 %(c_type)s %(c_name)s;
149 c_type
=memb
.type.c_type(), c_name
=c_name(memb
.name
))
150 ret
+= memb
.ifcond
.gen_endif()
154 def gen_object(name
: str, ifcond
: QAPISchemaIfCond
,
155 base
: Optional
[QAPISchemaObjectType
],
156 members
: List
[QAPISchemaObjectTypeMember
],
157 variants
: Optional
[QAPISchemaVariants
]) -> str:
158 if name
in objects_seen
:
160 objects_seen
.add(name
)
163 for var
in variants
.variants
if variants
else ():
165 if not isinstance(obj
, QAPISchemaObjectType
):
167 ret
+= gen_object(obj
.name
, obj
.ifcond
, obj
.base
,
168 obj
.local_members
, obj
.variants
)
173 ret
+= ifcond
.gen_if()
180 if not base
.is_implicit():
182 /* Members inherited from %(c_name)s: */
184 c_name
=base
.c_name())
185 ret
+= gen_struct_members(base
.members
)
186 if not base
.is_implicit():
190 ret
+= gen_struct_members(members
)
193 ret
+= gen_variants(variants
)
195 # Make sure that all structs have at least one member; this avoids
196 # potential issues with attempting to malloc space for zero-length
197 # structs in C, and also incompatibility with C++ (where an empty
199 if (not base
or base
.is_empty()) and not members
and not variants
:
201 char qapi_dummy_for_empty_struct;
207 ret
+= ifcond
.gen_endif()
212 def gen_upcast(name
: str, base
: QAPISchemaObjectType
) -> str:
213 # C makes const-correctness ugly. We have to cast away const to let
214 # this function work for both const and non-const obj.
217 static inline %(base)s *qapi_%(c_name)s_base(const %(c_name)s *obj)
219 return (%(base)s *)obj;
222 c_name
=c_name(name
), base
=base
.c_name())
225 def gen_variants(variants
: QAPISchemaVariants
) -> str:
227 union { /* union tag is @%(c_name)s */
229 c_name
=c_name(variants
.tag_member
.name
))
231 for var
in variants
.variants
:
232 if var
.type.name
== 'q_empty':
234 ret
+= var
.ifcond
.gen_if()
236 %(c_type)s %(c_name)s;
238 c_type
=var
.type.c_unboxed_type(),
239 c_name
=c_name(var
.name
))
240 ret
+= var
.ifcond
.gen_endif()
249 def gen_type_cleanup_decl(name
: str) -> str:
252 void qapi_free_%(c_name)s(%(c_name)s *obj);
253 G_DEFINE_AUTOPTR_CLEANUP_FUNC(%(c_name)s, qapi_free_%(c_name)s)
259 def gen_type_cleanup(name
: str) -> str:
262 void qapi_free_%(c_name)s(%(c_name)s *obj)
270 v = qapi_dealloc_visitor_new();
271 visit_type_%(c_name)s(v, NULL, &obj, NULL);
279 class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor
):
281 def __init__(self
, prefix
: str):
283 prefix
, 'qapi-types', ' * Schema-defined QAPI types',
284 ' * Built-in QAPI types', __doc__
)
286 def _begin_builtin_module(self
) -> None:
287 self
._genc
.preamble_add(mcgen('''
288 #include "qemu/osdep.h"
289 #include "qapi/dealloc-visitor.h"
290 #include "qapi/qapi-builtin-types.h"
291 #include "qapi/qapi-builtin-visit.h"
293 self
._genh
.preamble_add(mcgen('''
294 #include "qapi/util.h"
297 def _begin_user_module(self
, name
: str) -> None:
298 types
= self
._module
_basename
('qapi-types', name
)
299 visit
= self
._module
_basename
('qapi-visit', name
)
300 self
._genc
.preamble_add(mcgen('''
301 #include "qemu/osdep.h"
302 #include "qapi/dealloc-visitor.h"
303 #include "%(types)s.h"
304 #include "%(visit)s.h"
306 types
=types
, visit
=visit
))
307 self
._genh
.preamble_add(mcgen('''
308 #include "qapi/qapi-builtin-types.h"
311 def visit_begin(self
, schema
: QAPISchema
) -> None:
312 # gen_object() is recursive, ensure it doesn't visit the empty type
313 objects_seen
.add(schema
.the_empty_object_type
.name
)
315 def _gen_type_cleanup(self
, name
: str) -> None:
316 self
._genh
.add(gen_type_cleanup_decl(name
))
317 self
._genc
.add(gen_type_cleanup(name
))
319 def visit_enum_type(self
,
321 info
: Optional
[QAPISourceInfo
],
322 ifcond
: QAPISchemaIfCond
,
323 features
: List
[QAPISchemaFeature
],
324 members
: List
[QAPISchemaEnumMember
],
325 prefix
: Optional
[str]) -> None:
326 with
ifcontext(ifcond
, self
._genh
, self
._genc
):
327 self
._genh
.preamble_add(gen_enum(name
, members
, prefix
))
328 self
._genc
.add(gen_enum_lookup(name
, members
, prefix
))
330 def visit_array_type(self
,
332 info
: Optional
[QAPISourceInfo
],
333 ifcond
: QAPISchemaIfCond
,
334 element_type
: QAPISchemaType
) -> None:
335 with
ifcontext(ifcond
, self
._genh
, self
._genc
):
336 self
._genh
.preamble_add(gen_fwd_object_or_array(name
))
337 self
._genh
.add(gen_array(name
, element_type
))
338 self
._gen
_type
_cleanup
(name
)
340 def visit_object_type(self
,
342 info
: Optional
[QAPISourceInfo
],
343 ifcond
: QAPISchemaIfCond
,
344 features
: List
[QAPISchemaFeature
],
345 base
: Optional
[QAPISchemaObjectType
],
346 members
: List
[QAPISchemaObjectTypeMember
],
347 variants
: Optional
[QAPISchemaVariants
]) -> None:
348 # Nothing to do for the special empty builtin
349 if name
== 'q_empty':
351 with
ifcontext(ifcond
, self
._genh
):
352 self
._genh
.preamble_add(gen_fwd_object_or_array(name
))
353 self
._genh
.add(gen_object(name
, ifcond
, base
, members
, variants
))
354 with
ifcontext(ifcond
, self
._genh
, self
._genc
):
355 if base
and not base
.is_implicit():
356 self
._genh
.add(gen_upcast(name
, base
))
357 # TODO Worth changing the visitor signature, so we could
358 # directly use rather than repeat type.is_implicit()?
359 if not name
.startswith('q_'):
360 # implicit types won't be directly allocated/freed
361 self
._gen
_type
_cleanup
(name
)
363 def visit_alternate_type(self
,
365 info
: Optional
[QAPISourceInfo
],
366 ifcond
: QAPISchemaIfCond
,
367 features
: List
[QAPISchemaFeature
],
368 variants
: QAPISchemaVariants
) -> None:
369 with
ifcontext(ifcond
, self
._genh
):
370 self
._genh
.preamble_add(gen_fwd_object_or_array(name
))
371 self
._genh
.add(gen_object(name
, ifcond
, None,
372 [variants
.tag_member
], variants
))
373 with
ifcontext(ifcond
, self
._genh
, self
._genc
):
374 self
._gen
_type
_cleanup
(name
)
377 def gen_types(schema
: QAPISchema
,
380 opt_builtins
: bool) -> None:
381 vis
= QAPISchemaGenTypeVisitor(prefix
)
383 vis
.write(output_dir
, opt_builtins
)