1 # -*- coding: utf-8 -*-
3 # QAPI schema internal representation
5 # Copyright (c) 2015-2019 Red Hat Inc.
8 # Markus Armbruster <armbru@redhat.com>
9 # Eric Blake <eblake@redhat.com>
10 # Marc-André Lureau <marcandre.lureau@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.
15 # TODO catching name collisions in generated code would be nice
19 from collections
import OrderedDict
21 from qapi
.common
import c_name
, pointer_suffix
22 from qapi
.error
import QAPIError
, QAPIParseError
, QAPISemError
23 from qapi
.expr
import check_exprs
24 from qapi
.parser
import QAPISchemaParser
27 class QAPISchemaEntity(object):
30 def __init__(self
, name
, info
, doc
, ifcond
=None, features
=None):
31 assert name
is None or isinstance(name
, str)
32 for f
in features
or []:
33 assert isinstance(f
, QAPISchemaFeature
)
34 f
.set_defined_in(name
)
37 # For explicitly defined entities, info points to the (explicit)
38 # definition. For builtins (and their arrays), info is None.
39 # For implicitly defined entities, info points to a place that
40 # triggered the implicit definition (there may be more than one
44 self
._ifcond
= ifcond
or []
45 self
.features
= features
or []
49 return c_name(self
.name
)
51 def check(self
, schema
):
52 assert not self
._checked
54 self
._module
= os
.path
.relpath(self
.info
.fname
,
55 os
.path
.dirname(schema
.fname
))
57 for f
in self
.features
:
58 f
.check_clash(self
.info
, seen
)
60 self
.doc
.connect_feature(f
)
64 def connect_doc(self
, doc
=None):
81 def is_implicit(self
):
84 def visit(self
, visitor
):
89 return "%s '%s'" % (self
.meta
, self
.name
)
92 class QAPISchemaVisitor(object):
93 def visit_begin(self
, schema
):
99 def visit_module(self
, fname
):
102 def visit_needed(self
, entity
):
103 # Default to visiting everything
106 def visit_include(self
, fname
, info
):
109 def visit_builtin_type(self
, name
, info
, json_type
):
112 def visit_enum_type(self
, name
, info
, ifcond
, members
, prefix
):
115 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
118 def visit_object_type(self
, name
, info
, ifcond
, base
, members
, variants
,
122 def visit_object_type_flat(self
, name
, info
, ifcond
, members
, variants
,
126 def visit_alternate_type(self
, name
, info
, ifcond
, variants
):
129 def visit_command(self
, name
, info
, ifcond
, arg_type
, ret_type
, gen
,
130 success_response
, boxed
, allow_oob
, allow_preconfig
,
134 def visit_event(self
, name
, info
, ifcond
, arg_type
, boxed
):
138 class QAPISchemaInclude(QAPISchemaEntity
):
140 def __init__(self
, fname
, info
):
141 QAPISchemaEntity
.__init
__(self
, None, info
, None)
144 def visit(self
, visitor
):
145 QAPISchemaEntity
.visit(self
, visitor
)
146 visitor
.visit_include(self
.fname
, self
.info
)
149 class QAPISchemaType(QAPISchemaEntity
):
150 # Return the C type for common use.
151 # For the types we commonly box, this is a pointer type.
155 # Return the C type to be used in a parameter list.
156 def c_param_type(self
):
159 # Return the C type to be used where we suppress boxing.
160 def c_unboxed_type(self
):
166 def alternate_qtype(self
):
168 'null': 'QTYPE_QNULL',
169 'string': 'QTYPE_QSTRING',
170 'number': 'QTYPE_QNUM',
172 'boolean': 'QTYPE_QBOOL',
173 'object': 'QTYPE_QDICT'
175 return json2qtype
.get(self
.json_type())
178 if self
.is_implicit():
184 return "%s type '%s'" % (self
.meta
, self
.name
)
187 class QAPISchemaBuiltinType(QAPISchemaType
):
190 def __init__(self
, name
, json_type
, c_type
):
191 QAPISchemaType
.__init
__(self
, name
, None, None)
192 assert not c_type
or isinstance(c_type
, str)
193 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
195 self
._json
_type
_name
= json_type
196 self
._c
_type
_name
= c_type
202 return self
._c
_type
_name
204 def c_param_type(self
):
205 if self
.name
== 'str':
206 return 'const ' + self
._c
_type
_name
207 return self
._c
_type
_name
210 return self
._json
_type
_name
213 return self
.json_type()
215 def visit(self
, visitor
):
216 QAPISchemaType
.visit(self
, visitor
)
217 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
220 class QAPISchemaEnumType(QAPISchemaType
):
223 def __init__(self
, name
, info
, doc
, ifcond
, members
, prefix
):
224 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
226 assert isinstance(m
, QAPISchemaEnumMember
)
227 m
.set_defined_in(name
)
228 assert prefix
is None or isinstance(prefix
, str)
229 self
.members
= members
232 def check(self
, schema
):
233 QAPISchemaType
.check(self
, schema
)
235 for m
in self
.members
:
236 m
.check_clash(self
.info
, seen
)
238 def connect_doc(self
, doc
=None):
239 doc
= doc
or self
.doc
241 for m
in self
.members
:
242 doc
.connect_member(m
)
244 def is_implicit(self
):
245 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
246 return self
.name
.endswith('Kind') or self
.name
== 'QType'
249 return c_name(self
.name
)
251 def member_names(self
):
252 return [m
.name
for m
in self
.members
]
257 def visit(self
, visitor
):
258 QAPISchemaType
.visit(self
, visitor
)
259 visitor
.visit_enum_type(self
.name
, self
.info
, self
.ifcond
,
260 self
.members
, self
.prefix
)
263 class QAPISchemaArrayType(QAPISchemaType
):
266 def __init__(self
, name
, info
, element_type
):
267 QAPISchemaType
.__init
__(self
, name
, info
, None, None)
268 assert isinstance(element_type
, str)
269 self
._element
_type
_name
= element_type
270 self
.element_type
= None
272 def check(self
, schema
):
273 QAPISchemaType
.check(self
, schema
)
274 self
.element_type
= schema
.resolve_type(
275 self
._element
_type
_name
, self
.info
,
276 self
.info
and self
.info
.defn_meta
)
277 assert not isinstance(self
.element_type
, QAPISchemaArrayType
)
282 return self
.element_type
.ifcond
287 return self
.element_type
.module
289 def is_implicit(self
):
293 return c_name(self
.name
) + pointer_suffix
299 elt_doc_type
= self
.element_type
.doc_type()
302 return 'array of ' + elt_doc_type
304 def visit(self
, visitor
):
305 QAPISchemaType
.visit(self
, visitor
)
306 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
311 return "%s type ['%s']" % (self
.meta
, self
._element
_type
_name
)
314 class QAPISchemaObjectType(QAPISchemaType
):
315 def __init__(self
, name
, info
, doc
, ifcond
,
316 base
, local_members
, variants
, features
):
317 # struct has local_members, optional base, and no variants
318 # flat union has base, variants, and no local_members
319 # simple union has local_members, variants, and no base
320 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
, features
)
321 self
.meta
= 'union' if variants
else 'struct'
322 assert base
is None or isinstance(base
, str)
323 for m
in local_members
:
324 assert isinstance(m
, QAPISchemaObjectTypeMember
)
325 m
.set_defined_in(name
)
326 if variants
is not None:
327 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
328 variants
.set_defined_in(name
)
329 self
._base
_name
= base
331 self
.local_members
= local_members
332 self
.variants
= variants
335 def check(self
, schema
):
336 # This calls another type T's .check() exactly when the C
337 # struct emitted by gen_object() contains that T's C struct
338 # (pointers don't count).
339 if self
.members
is not None:
340 # A previous .check() completed: nothing to do
343 # Recursed: C struct contains itself
344 raise QAPISemError(self
.info
,
345 "object %s contains itself" % self
.name
)
347 QAPISchemaType
.check(self
, schema
)
348 assert self
._checked
and self
.members
is None
352 self
.base
= schema
.resolve_type(self
._base
_name
, self
.info
,
354 if (not isinstance(self
.base
, QAPISchemaObjectType
)
355 or self
.base
.variants
):
358 "'base' requires a struct type, %s isn't"
359 % self
.base
.describe())
360 self
.base
.check(schema
)
361 self
.base
.check_clash(self
.info
, seen
)
362 for m
in self
.local_members
:
364 m
.check_clash(self
.info
, seen
)
365 members
= seen
.values()
368 self
.variants
.check(schema
, seen
)
369 self
.variants
.check_clash(self
.info
, seen
)
371 self
.members
= members
# mark completed
373 # Check that the members of this type do not cause duplicate JSON members,
374 # and update seen to track the members seen so far. Report any errors
375 # on behalf of info, which is not necessarily self.info
376 def check_clash(self
, info
, seen
):
378 assert not self
.variants
# not implemented
379 for m
in self
.members
:
380 m
.check_clash(info
, seen
)
382 def connect_doc(self
, doc
=None):
383 doc
= doc
or self
.doc
385 if self
.base
and self
.base
.is_implicit():
386 self
.base
.connect_doc(doc
)
387 for m
in self
.local_members
:
388 doc
.connect_member(m
)
393 if isinstance(self
._ifcond
, QAPISchemaType
):
394 # Simple union wrapper type inherits from wrapped type;
395 # see _make_implicit_object_type()
396 return self
._ifcond
.ifcond
399 def is_implicit(self
):
400 # See QAPISchema._make_implicit_object_type(), as well as
402 return self
.name
.startswith('q_')
405 assert self
.members
is not None
406 return not self
.members
and not self
.variants
409 assert self
.name
!= 'q_empty'
410 return QAPISchemaType
.c_name(self
)
413 assert not self
.is_implicit()
414 return c_name(self
.name
) + pointer_suffix
416 def c_unboxed_type(self
):
417 return c_name(self
.name
)
422 def visit(self
, visitor
):
423 QAPISchemaType
.visit(self
, visitor
)
424 visitor
.visit_object_type(self
.name
, self
.info
, self
.ifcond
,
425 self
.base
, self
.local_members
, self
.variants
,
427 visitor
.visit_object_type_flat(self
.name
, self
.info
, self
.ifcond
,
428 self
.members
, self
.variants
,
432 class QAPISchemaMember(object):
433 """ Represents object members, enum members and features """
436 def __init__(self
, name
, info
, ifcond
=None):
437 assert isinstance(name
, str)
440 self
.ifcond
= ifcond
or []
441 self
.defined_in
= None
443 def set_defined_in(self
, name
):
444 assert not self
.defined_in
445 self
.defined_in
= name
447 def check_clash(self
, info
, seen
):
448 cname
= c_name(self
.name
)
452 "%s collides with %s"
453 % (self
.describe(info
), seen
[cname
].describe(info
)))
456 def describe(self
, info
):
458 defined_in
= self
.defined_in
461 if defined_in
.startswith('q_obj_'):
462 # See QAPISchema._make_implicit_object_type() - reverse the
463 # mapping there to create a nice human-readable description
464 defined_in
= defined_in
[6:]
465 if defined_in
.endswith('-arg'):
466 # Implicit type created for a command's dict 'data'
467 assert role
== 'member'
469 elif defined_in
.endswith('-base'):
470 # Implicit type created for a flat union's dict 'base'
471 role
= 'base ' + role
473 # Implicit type created for a simple union's branch
474 assert defined_in
.endswith('-wrapper')
475 # Unreachable and not implemented
477 elif defined_in
.endswith('Kind'):
478 # See QAPISchema._make_implicit_enum_type()
479 # Implicit enum created for simple union's branches
480 assert role
== 'value'
482 elif defined_in
!= info
.defn_name
:
483 return "%s '%s' of type '%s'" % (role
, self
.name
, defined_in
)
484 return "%s '%s'" % (role
, self
.name
)
487 class QAPISchemaEnumMember(QAPISchemaMember
):
491 class QAPISchemaFeature(QAPISchemaMember
):
495 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
496 def __init__(self
, name
, info
, typ
, optional
, ifcond
=None):
497 QAPISchemaMember
.__init
__(self
, name
, info
, ifcond
)
498 assert isinstance(typ
, str)
499 assert isinstance(optional
, bool)
500 self
._type
_name
= typ
502 self
.optional
= optional
504 def check(self
, schema
):
505 assert self
.defined_in
506 self
.type = schema
.resolve_type(self
._type
_name
, self
.info
,
510 class QAPISchemaObjectTypeVariants(object):
511 def __init__(self
, tag_name
, info
, tag_member
, variants
):
512 # Flat unions pass tag_name but not tag_member.
513 # Simple unions and alternates pass tag_member but not tag_name.
514 # After check(), tag_member is always set, and tag_name remains
515 # a reliable witness of being used by a flat union.
516 assert bool(tag_member
) != bool(tag_name
)
517 assert (isinstance(tag_name
, str) or
518 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
520 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
521 self
._tag
_name
= tag_name
523 self
.tag_member
= tag_member
524 self
.variants
= variants
526 def set_defined_in(self
, name
):
527 for v
in self
.variants
:
528 v
.set_defined_in(name
)
530 def check(self
, schema
, seen
):
531 if not self
.tag_member
: # flat union
532 self
.tag_member
= seen
.get(c_name(self
._tag
_name
))
534 # Pointing to the base type when not implicit would be
535 # nice, but we don't know it here
536 if not self
.tag_member
or self
._tag
_name
!= self
.tag_member
.name
:
539 "discriminator '%s' is not a member of %s"
540 % (self
._tag
_name
, base
))
542 base_type
= schema
.lookup_type(self
.tag_member
.defined_in
)
544 if not base_type
.is_implicit():
545 base
= "base type '%s'" % self
.tag_member
.defined_in
546 if not isinstance(self
.tag_member
.type, QAPISchemaEnumType
):
549 "discriminator member '%s' of %s must be of enum type"
550 % (self
._tag
_name
, base
))
551 if self
.tag_member
.optional
:
554 "discriminator member '%s' of %s must not be optional"
555 % (self
._tag
_name
, base
))
556 if self
.tag_member
.ifcond
:
559 "discriminator member '%s' of %s must not be conditional"
560 % (self
._tag
_name
, base
))
562 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
563 assert not self
.tag_member
.optional
564 assert self
.tag_member
.ifcond
== []
565 if self
._tag
_name
: # flat union
566 # branches that are not explicitly covered get an empty type
567 cases
= set([v
.name
for v
in self
.variants
])
568 for m
in self
.tag_member
.type.members
:
569 if m
.name
not in cases
:
570 v
= QAPISchemaObjectTypeVariant(m
.name
, self
.info
,
572 v
.set_defined_in(self
.tag_member
.defined_in
)
573 self
.variants
.append(v
)
574 if not self
.variants
:
575 raise QAPISemError(self
.info
, "union has no branches")
576 for v
in self
.variants
:
578 # Union names must match enum values; alternate names are
579 # checked separately. Use 'seen' to tell the two apart.
581 if v
.name
not in self
.tag_member
.type.member_names():
584 "branch '%s' is not a value of %s"
585 % (v
.name
, self
.tag_member
.type.describe()))
586 if (not isinstance(v
.type, QAPISchemaObjectType
)
591 % (v
.describe(self
.info
), v
.type.describe()))
594 def check_clash(self
, info
, seen
):
595 for v
in self
.variants
:
596 # Reset seen map for each variant, since qapi names from one
597 # branch do not affect another branch
598 v
.type.check_clash(info
, dict(seen
))
601 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
604 def __init__(self
, name
, info
, typ
, ifcond
=None):
605 QAPISchemaObjectTypeMember
.__init
__(self
, name
, info
, typ
,
609 class QAPISchemaAlternateType(QAPISchemaType
):
612 def __init__(self
, name
, info
, doc
, ifcond
, variants
):
613 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
614 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
615 assert variants
.tag_member
616 variants
.set_defined_in(name
)
617 variants
.tag_member
.set_defined_in(self
.name
)
618 self
.variants
= variants
620 def check(self
, schema
):
621 QAPISchemaType
.check(self
, schema
)
622 self
.variants
.tag_member
.check(schema
)
623 # Not calling self.variants.check_clash(), because there's nothing
625 self
.variants
.check(schema
, {})
626 # Alternate branch names have no relation to the tag enum values;
627 # so we have to check for potential name collisions ourselves.
630 for v
in self
.variants
.variants
:
631 v
.check_clash(self
.info
, seen
)
632 qtype
= v
.type.alternate_qtype()
637 % (v
.describe(self
.info
), v
.type.describe()))
638 conflicting
= set([qtype
])
639 if qtype
== 'QTYPE_QSTRING':
640 if isinstance(v
.type, QAPISchemaEnumType
):
641 for m
in v
.type.members
:
642 if m
.name
in ['on', 'off']:
643 conflicting
.add('QTYPE_QBOOL')
644 if re
.match(r
'[-+0-9.]', m
.name
):
645 # lazy, could be tightened
646 conflicting
.add('QTYPE_QNUM')
648 conflicting
.add('QTYPE_QNUM')
649 conflicting
.add('QTYPE_QBOOL')
650 for qt
in conflicting
:
654 "%s can't be distinguished from '%s'"
655 % (v
.describe(self
.info
), types_seen
[qt
]))
656 types_seen
[qt
] = v
.name
658 def connect_doc(self
, doc
=None):
659 doc
= doc
or self
.doc
661 for v
in self
.variants
.variants
:
662 doc
.connect_member(v
)
665 return c_name(self
.name
) + pointer_suffix
670 def visit(self
, visitor
):
671 QAPISchemaType
.visit(self
, visitor
)
672 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.ifcond
,
676 class QAPISchemaCommand(QAPISchemaEntity
):
679 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, ret_type
,
680 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
,
682 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
, features
)
683 assert not arg_type
or isinstance(arg_type
, str)
684 assert not ret_type
or isinstance(ret_type
, str)
685 self
._arg
_type
_name
= arg_type
687 self
._ret
_type
_name
= ret_type
690 self
.success_response
= success_response
692 self
.allow_oob
= allow_oob
693 self
.allow_preconfig
= allow_preconfig
695 def check(self
, schema
):
696 QAPISchemaEntity
.check(self
, schema
)
697 if self
._arg
_type
_name
:
698 self
.arg_type
= schema
.resolve_type(
699 self
._arg
_type
_name
, self
.info
, "command's 'data'")
700 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
703 "command's 'data' cannot take %s"
704 % self
.arg_type
.describe())
705 if self
.arg_type
.variants
and not self
.boxed
:
708 "command's 'data' can take %s only with 'boxed': true"
709 % self
.arg_type
.describe())
710 if self
._ret
_type
_name
:
711 self
.ret_type
= schema
.resolve_type(
712 self
._ret
_type
_name
, self
.info
, "command's 'returns'")
713 if self
.name
not in self
.info
.pragma
.returns_whitelist
:
714 if not (isinstance(self
.ret_type
, QAPISchemaObjectType
)
715 or (isinstance(self
.ret_type
, QAPISchemaArrayType
)
716 and isinstance(self
.ret_type
.element_type
,
717 QAPISchemaObjectType
))):
720 "command's 'returns' cannot take %s"
721 % self
.ret_type
.describe())
723 def connect_doc(self
, doc
=None):
724 doc
= doc
or self
.doc
726 if self
.arg_type
and self
.arg_type
.is_implicit():
727 self
.arg_type
.connect_doc(doc
)
729 def visit(self
, visitor
):
730 QAPISchemaEntity
.visit(self
, visitor
)
731 visitor
.visit_command(self
.name
, self
.info
, self
.ifcond
,
732 self
.arg_type
, self
.ret_type
,
733 self
.gen
, self
.success_response
,
734 self
.boxed
, self
.allow_oob
,
735 self
.allow_preconfig
,
739 class QAPISchemaEvent(QAPISchemaEntity
):
742 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, boxed
):
743 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
744 assert not arg_type
or isinstance(arg_type
, str)
745 self
._arg
_type
_name
= arg_type
749 def check(self
, schema
):
750 QAPISchemaEntity
.check(self
, schema
)
751 if self
._arg
_type
_name
:
752 self
.arg_type
= schema
.resolve_type(
753 self
._arg
_type
_name
, self
.info
, "event's 'data'")
754 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
757 "event's 'data' cannot take %s"
758 % self
.arg_type
.describe())
759 if self
.arg_type
.variants
and not self
.boxed
:
762 "event's 'data' can take %s only with 'boxed': true"
763 % self
.arg_type
.describe())
765 def connect_doc(self
, doc
=None):
766 doc
= doc
or self
.doc
768 if self
.arg_type
and self
.arg_type
.is_implicit():
769 self
.arg_type
.connect_doc(doc
)
771 def visit(self
, visitor
):
772 QAPISchemaEntity
.visit(self
, visitor
)
773 visitor
.visit_event(self
.name
, self
.info
, self
.ifcond
,
774 self
.arg_type
, self
.boxed
)
777 class QAPISchema(object):
778 def __init__(self
, fname
):
780 parser
= QAPISchemaParser(fname
)
781 exprs
= check_exprs(parser
.exprs
)
782 self
.docs
= parser
.docs
783 self
._entity
_list
= []
784 self
._entity
_dict
= {}
785 self
._predefining
= True
786 self
._def
_predefineds
()
787 self
._predefining
= False
788 self
._def
_exprs
(exprs
)
791 def _def_entity(self
, ent
):
792 # Only the predefined types are allowed to not have info
793 assert ent
.info
or self
._predefining
794 self
._entity
_list
.append(ent
)
797 # TODO reject names that differ only in '_' vs. '.' vs. '-',
798 # because they're liable to clash in generated C.
799 other_ent
= self
._entity
_dict
.get(ent
.name
)
802 where
= QAPIError(other_ent
.info
, None, "previous definition")
805 "'%s' is already defined\n%s" % (ent
.name
, where
))
807 ent
.info
, "%s is already defined" % other_ent
.describe())
808 self
._entity
_dict
[ent
.name
] = ent
810 def lookup_entity(self
, name
, typ
=None):
811 ent
= self
._entity
_dict
.get(name
)
812 if typ
and not isinstance(ent
, typ
):
816 def lookup_type(self
, name
):
817 return self
.lookup_entity(name
, QAPISchemaType
)
819 def resolve_type(self
, name
, info
, what
):
820 typ
= self
.lookup_type(name
)
825 info
, "%s uses unknown type '%s'" % (what
, name
))
828 def _def_include(self
, expr
, info
, doc
):
829 include
= expr
['include']
832 while main_info
.parent
:
833 main_info
= main_info
.parent
834 fname
= os
.path
.relpath(include
, os
.path
.dirname(main_info
.fname
))
835 self
._def
_entity
(QAPISchemaInclude(fname
, info
))
837 def _def_builtin_type(self
, name
, json_type
, c_type
):
838 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
839 # Instantiating only the arrays that are actually used would
840 # be nice, but we can't as long as their generated code
841 # (qapi-builtin-types.[ch]) may be shared by some other
843 self
._make
_array
_type
(name
, None)
845 def _def_predefineds(self
):
846 for t
in [('str', 'string', 'char' + pointer_suffix
),
847 ('number', 'number', 'double'),
848 ('int', 'int', 'int64_t'),
849 ('int8', 'int', 'int8_t'),
850 ('int16', 'int', 'int16_t'),
851 ('int32', 'int', 'int32_t'),
852 ('int64', 'int', 'int64_t'),
853 ('uint8', 'int', 'uint8_t'),
854 ('uint16', 'int', 'uint16_t'),
855 ('uint32', 'int', 'uint32_t'),
856 ('uint64', 'int', 'uint64_t'),
857 ('size', 'int', 'uint64_t'),
858 ('bool', 'boolean', 'bool'),
859 ('any', 'value', 'QObject' + pointer_suffix
),
860 ('null', 'null', 'QNull' + pointer_suffix
)]:
861 self
._def
_builtin
_type
(*t
)
862 self
.the_empty_object_type
= QAPISchemaObjectType(
863 'q_empty', None, None, None, None, [], None, [])
864 self
._def
_entity
(self
.the_empty_object_type
)
866 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
868 qtype_values
= self
._make
_enum
_members
(
869 [{'name': n
} for n
in qtypes
], None)
871 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None,
872 qtype_values
, 'QTYPE'))
874 def _make_features(self
, features
, info
):
875 return [QAPISchemaFeature(f
['name'], info
, f
.get('if'))
878 def _make_enum_members(self
, values
, info
):
879 return [QAPISchemaEnumMember(v
['name'], info
, v
.get('if'))
882 def _make_implicit_enum_type(self
, name
, info
, ifcond
, values
):
883 # See also QAPISchemaObjectTypeMember.describe()
884 name
= name
+ 'Kind' # reserved by check_defn_name_str()
885 self
._def
_entity
(QAPISchemaEnumType(
886 name
, info
, None, ifcond
, self
._make
_enum
_members
(values
, info
),
890 def _make_array_type(self
, element_type
, info
):
891 name
= element_type
+ 'List' # reserved by check_defn_name_str()
892 if not self
.lookup_type(name
):
893 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
896 def _make_implicit_object_type(self
, name
, info
, ifcond
, role
, members
):
899 # See also QAPISchemaObjectTypeMember.describe()
900 name
= 'q_obj_%s-%s' % (name
, role
)
901 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
903 # The implicit object type has multiple users. This can
904 # happen only for simple unions' implicit wrapper types.
905 # Its ifcond should be the disjunction of its user's
906 # ifconds. Not implemented. Instead, we always pass the
907 # wrapped type's ifcond, which is trivially the same for all
908 # users. It's also necessary for the wrapper to compile.
909 # But it's not tight: the disjunction need not imply it. We
910 # may end up compiling useless wrapper types.
911 # TODO kill simple unions or implement the disjunction
912 assert (ifcond
or []) == typ
._ifcond
# pylint: disable=protected-access
914 self
._def
_entity
(QAPISchemaObjectType(name
, info
, None, ifcond
,
915 None, members
, None, []))
918 def _def_enum_type(self
, expr
, info
, doc
):
921 prefix
= expr
.get('prefix')
922 ifcond
= expr
.get('if')
923 self
._def
_entity
(QAPISchemaEnumType(
924 name
, info
, doc
, ifcond
,
925 self
._make
_enum
_members
(data
, info
), prefix
))
927 def _make_member(self
, name
, typ
, ifcond
, info
):
929 if name
.startswith('*'):
932 if isinstance(typ
, list):
934 typ
= self
._make
_array
_type
(typ
[0], info
)
935 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
)
937 def _make_members(self
, data
, info
):
938 return [self
._make
_member
(key
, value
['type'], value
.get('if'), info
)
939 for (key
, value
) in data
.items()]
941 def _def_struct_type(self
, expr
, info
, doc
):
942 name
= expr
['struct']
943 base
= expr
.get('base')
945 ifcond
= expr
.get('if')
946 features
= expr
.get('features', [])
947 self
._def
_entity
(QAPISchemaObjectType(
948 name
, info
, doc
, ifcond
, base
,
949 self
._make
_members
(data
, info
),
951 self
._make
_features
(features
, info
)))
953 def _make_variant(self
, case
, typ
, ifcond
, info
):
954 return QAPISchemaObjectTypeVariant(case
, info
, typ
, ifcond
)
956 def _make_simple_variant(self
, case
, typ
, ifcond
, info
):
957 if isinstance(typ
, list):
959 typ
= self
._make
_array
_type
(typ
[0], info
)
960 typ
= self
._make
_implicit
_object
_type
(
961 typ
, info
, self
.lookup_type(typ
),
962 'wrapper', [self
._make
_member
('data', typ
, None, info
)])
963 return QAPISchemaObjectTypeVariant(case
, info
, typ
, ifcond
)
965 def _def_union_type(self
, expr
, info
, doc
):
968 base
= expr
.get('base')
969 ifcond
= expr
.get('if')
970 tag_name
= expr
.get('discriminator')
972 if isinstance(base
, dict):
973 base
= self
._make
_implicit
_object
_type
(
975 'base', self
._make
_members
(base
, info
))
977 variants
= [self
._make
_variant
(key
, value
['type'],
978 value
.get('if'), info
)
979 for (key
, value
) in data
.items()]
982 variants
= [self
._make
_simple
_variant
(key
, value
['type'],
983 value
.get('if'), info
)
984 for (key
, value
) in data
.items()]
985 enum
= [{'name': v
.name
, 'if': v
.ifcond
} for v
in variants
]
986 typ
= self
._make
_implicit
_enum
_type
(name
, info
, ifcond
, enum
)
987 tag_member
= QAPISchemaObjectTypeMember('type', info
, typ
, False)
988 members
= [tag_member
]
990 QAPISchemaObjectType(name
, info
, doc
, ifcond
, base
, members
,
991 QAPISchemaObjectTypeVariants(
992 tag_name
, info
, tag_member
, variants
),
995 def _def_alternate_type(self
, expr
, info
, doc
):
996 name
= expr
['alternate']
998 ifcond
= expr
.get('if')
999 variants
= [self
._make
_variant
(key
, value
['type'], value
.get('if'),
1001 for (key
, value
) in data
.items()]
1002 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
1004 QAPISchemaAlternateType(name
, info
, doc
, ifcond
,
1005 QAPISchemaObjectTypeVariants(
1006 None, info
, tag_member
, variants
)))
1008 def _def_command(self
, expr
, info
, doc
):
1009 name
= expr
['command']
1010 data
= expr
.get('data')
1011 rets
= expr
.get('returns')
1012 gen
= expr
.get('gen', True)
1013 success_response
= expr
.get('success-response', True)
1014 boxed
= expr
.get('boxed', False)
1015 allow_oob
= expr
.get('allow-oob', False)
1016 allow_preconfig
= expr
.get('allow-preconfig', False)
1017 ifcond
= expr
.get('if')
1018 features
= expr
.get('features', [])
1019 if isinstance(data
, OrderedDict
):
1020 data
= self
._make
_implicit
_object
_type
(
1021 name
, info
, ifcond
, 'arg', self
._make
_members
(data
, info
))
1022 if isinstance(rets
, list):
1023 assert len(rets
) == 1
1024 rets
= self
._make
_array
_type
(rets
[0], info
)
1025 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, ifcond
, data
, rets
,
1026 gen
, success_response
,
1027 boxed
, allow_oob
, allow_preconfig
,
1028 self
._make
_features
(features
, info
)))
1030 def _def_event(self
, expr
, info
, doc
):
1031 name
= expr
['event']
1032 data
= expr
.get('data')
1033 boxed
= expr
.get('boxed', False)
1034 ifcond
= expr
.get('if')
1035 if isinstance(data
, OrderedDict
):
1036 data
= self
._make
_implicit
_object
_type
(
1037 name
, info
, ifcond
, 'arg', self
._make
_members
(data
, info
))
1038 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, ifcond
, data
, boxed
))
1040 def _def_exprs(self
, exprs
):
1041 for expr_elem
in exprs
:
1042 expr
= expr_elem
['expr']
1043 info
= expr_elem
['info']
1044 doc
= expr_elem
.get('doc')
1046 self
._def
_enum
_type
(expr
, info
, doc
)
1047 elif 'struct' in expr
:
1048 self
._def
_struct
_type
(expr
, info
, doc
)
1049 elif 'union' in expr
:
1050 self
._def
_union
_type
(expr
, info
, doc
)
1051 elif 'alternate' in expr
:
1052 self
._def
_alternate
_type
(expr
, info
, doc
)
1053 elif 'command' in expr
:
1054 self
._def
_command
(expr
, info
, doc
)
1055 elif 'event' in expr
:
1056 self
._def
_event
(expr
, info
, doc
)
1057 elif 'include' in expr
:
1058 self
._def
_include
(expr
, info
, doc
)
1063 for ent
in self
._entity
_list
:
1068 def visit(self
, visitor
):
1069 visitor
.visit_begin(self
)
1071 visitor
.visit_module(module
)
1072 for entity
in self
._entity
_list
:
1073 if visitor
.visit_needed(entity
):
1074 if entity
.module
!= module
:
1075 module
= entity
.module
1076 visitor
.visit_module(module
)
1077 entity
.visit(visitor
)