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):
31 assert name
is None or isinstance(name
, str)
34 # For explicitly defined entities, info points to the (explicit)
35 # definition. For builtins (and their arrays), info is None.
36 # For implicitly defined entities, info points to a place that
37 # triggered the implicit definition (there may be more than one
41 self
._ifcond
= ifcond
or []
45 return c_name(self
.name
)
47 def check(self
, schema
):
48 assert not self
._checked
50 self
._module
= os
.path
.relpath(self
.info
.fname
,
51 os
.path
.dirname(schema
.fname
))
64 def is_implicit(self
):
67 def visit(self
, visitor
):
72 return "%s '%s'" % (self
.meta
, self
.name
)
75 class QAPISchemaVisitor(object):
76 def visit_begin(self
, schema
):
82 def visit_module(self
, fname
):
85 def visit_needed(self
, entity
):
86 # Default to visiting everything
89 def visit_include(self
, fname
, info
):
92 def visit_builtin_type(self
, name
, info
, json_type
):
95 def visit_enum_type(self
, name
, info
, ifcond
, members
, prefix
):
98 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
101 def visit_object_type(self
, name
, info
, ifcond
, base
, members
, variants
,
105 def visit_object_type_flat(self
, name
, info
, ifcond
, members
, variants
,
109 def visit_alternate_type(self
, name
, info
, ifcond
, variants
):
112 def visit_command(self
, name
, info
, ifcond
, arg_type
, ret_type
, gen
,
113 success_response
, boxed
, allow_oob
, allow_preconfig
,
117 def visit_event(self
, name
, info
, ifcond
, arg_type
, boxed
):
121 class QAPISchemaInclude(QAPISchemaEntity
):
123 def __init__(self
, fname
, info
):
124 QAPISchemaEntity
.__init
__(self
, None, info
, None)
127 def visit(self
, visitor
):
128 QAPISchemaEntity
.visit(self
, visitor
)
129 visitor
.visit_include(self
.fname
, self
.info
)
132 class QAPISchemaType(QAPISchemaEntity
):
133 # Return the C type for common use.
134 # For the types we commonly box, this is a pointer type.
138 # Return the C type to be used in a parameter list.
139 def c_param_type(self
):
142 # Return the C type to be used where we suppress boxing.
143 def c_unboxed_type(self
):
149 def alternate_qtype(self
):
151 'null': 'QTYPE_QNULL',
152 'string': 'QTYPE_QSTRING',
153 'number': 'QTYPE_QNUM',
155 'boolean': 'QTYPE_QBOOL',
156 'object': 'QTYPE_QDICT'
158 return json2qtype
.get(self
.json_type())
161 if self
.is_implicit():
167 return "%s type '%s'" % (self
.meta
, self
.name
)
170 class QAPISchemaBuiltinType(QAPISchemaType
):
173 def __init__(self
, name
, json_type
, c_type
):
174 QAPISchemaType
.__init
__(self
, name
, None, None)
175 assert not c_type
or isinstance(c_type
, str)
176 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
178 self
._json
_type
_name
= json_type
179 self
._c
_type
_name
= c_type
185 return self
._c
_type
_name
187 def c_param_type(self
):
188 if self
.name
== 'str':
189 return 'const ' + self
._c
_type
_name
190 return self
._c
_type
_name
193 return self
._json
_type
_name
196 return self
.json_type()
198 def visit(self
, visitor
):
199 QAPISchemaType
.visit(self
, visitor
)
200 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
203 class QAPISchemaEnumType(QAPISchemaType
):
206 def __init__(self
, name
, info
, doc
, ifcond
, members
, prefix
):
207 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
209 assert isinstance(m
, QAPISchemaEnumMember
)
210 m
.set_defined_in(name
)
211 assert prefix
is None or isinstance(prefix
, str)
212 self
.members
= members
215 def check(self
, schema
):
216 QAPISchemaType
.check(self
, schema
)
218 for m
in self
.members
:
219 m
.check_clash(self
.info
, seen
)
221 self
.doc
.connect_member(m
)
223 def is_implicit(self
):
224 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
225 return self
.name
.endswith('Kind') or self
.name
== 'QType'
228 return c_name(self
.name
)
230 def member_names(self
):
231 return [m
.name
for m
in self
.members
]
236 def visit(self
, visitor
):
237 QAPISchemaType
.visit(self
, visitor
)
238 visitor
.visit_enum_type(self
.name
, self
.info
, self
.ifcond
,
239 self
.members
, self
.prefix
)
242 class QAPISchemaArrayType(QAPISchemaType
):
245 def __init__(self
, name
, info
, element_type
):
246 QAPISchemaType
.__init
__(self
, name
, info
, None, None)
247 assert isinstance(element_type
, str)
248 self
._element
_type
_name
= element_type
249 self
.element_type
= None
251 def check(self
, schema
):
252 QAPISchemaType
.check(self
, schema
)
253 self
.element_type
= schema
.resolve_type(
254 self
._element
_type
_name
, self
.info
,
255 self
.info
and self
.info
.defn_meta
)
256 assert not isinstance(self
.element_type
, QAPISchemaArrayType
)
261 return self
.element_type
.ifcond
266 return self
.element_type
.module
268 def is_implicit(self
):
272 return c_name(self
.name
) + pointer_suffix
278 elt_doc_type
= self
.element_type
.doc_type()
281 return 'array of ' + elt_doc_type
283 def visit(self
, visitor
):
284 QAPISchemaType
.visit(self
, visitor
)
285 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
290 return "%s type ['%s']" % (self
.meta
, self
._element
_type
_name
)
293 class QAPISchemaObjectType(QAPISchemaType
):
294 def __init__(self
, name
, info
, doc
, ifcond
,
295 base
, local_members
, variants
, features
):
296 # struct has local_members, optional base, and no variants
297 # flat union has base, variants, and no local_members
298 # simple union has local_members, variants, and no base
299 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
300 self
.meta
= 'union' if variants
else 'struct'
301 assert base
is None or isinstance(base
, str)
302 for m
in local_members
:
303 assert isinstance(m
, QAPISchemaObjectTypeMember
)
304 m
.set_defined_in(name
)
305 if variants
is not None:
306 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
307 variants
.set_defined_in(name
)
309 assert isinstance(f
, QAPISchemaFeature
)
310 f
.set_defined_in(name
)
311 self
._base
_name
= base
313 self
.local_members
= local_members
314 self
.variants
= variants
316 self
.features
= features
318 def check(self
, schema
):
319 # This calls another type T's .check() exactly when the C
320 # struct emitted by gen_object() contains that T's C struct
321 # (pointers don't count).
322 if self
.members
is not None:
323 # A previous .check() completed: nothing to do
326 # Recursed: C struct contains itself
327 raise QAPISemError(self
.info
,
328 "object %s contains itself" % self
.name
)
330 QAPISchemaType
.check(self
, schema
)
331 assert self
._checked
and self
.members
is None
335 self
.base
= schema
.resolve_type(self
._base
_name
, self
.info
,
337 if (not isinstance(self
.base
, QAPISchemaObjectType
)
338 or self
.base
.variants
):
341 "'base' requires a struct type, %s isn't"
342 % self
.base
.describe())
343 self
.base
.check(schema
)
344 self
.base
.check_clash(self
.info
, seen
)
345 for m
in self
.local_members
:
347 m
.check_clash(self
.info
, seen
)
349 self
.doc
.connect_member(m
)
350 members
= seen
.values()
353 self
.variants
.check(schema
, seen
)
354 self
.variants
.check_clash(self
.info
, seen
)
356 # Features are in a name space separate from members
358 for f
in self
.features
:
359 f
.check_clash(self
.info
, seen
)
364 self
.members
= members
# mark completed
366 # Check that the members of this type do not cause duplicate JSON members,
367 # and update seen to track the members seen so far. Report any errors
368 # on behalf of info, which is not necessarily self.info
369 def check_clash(self
, info
, seen
):
371 assert not self
.variants
# not implemented
372 for m
in self
.members
:
373 m
.check_clash(info
, seen
)
378 if isinstance(self
._ifcond
, QAPISchemaType
):
379 # Simple union wrapper type inherits from wrapped type;
380 # see _make_implicit_object_type()
381 return self
._ifcond
.ifcond
384 def is_implicit(self
):
385 # See QAPISchema._make_implicit_object_type(), as well as
387 return self
.name
.startswith('q_')
390 assert self
.members
is not None
391 return not self
.members
and not self
.variants
394 assert self
.name
!= 'q_empty'
395 return QAPISchemaType
.c_name(self
)
398 assert not self
.is_implicit()
399 return c_name(self
.name
) + pointer_suffix
401 def c_unboxed_type(self
):
402 return c_name(self
.name
)
407 def visit(self
, visitor
):
408 QAPISchemaType
.visit(self
, visitor
)
409 visitor
.visit_object_type(self
.name
, self
.info
, self
.ifcond
,
410 self
.base
, self
.local_members
, self
.variants
,
412 visitor
.visit_object_type_flat(self
.name
, self
.info
, self
.ifcond
,
413 self
.members
, self
.variants
,
417 class QAPISchemaMember(object):
418 """ Represents object members, enum members and features """
421 def __init__(self
, name
, info
, ifcond
=None):
422 assert isinstance(name
, str)
425 self
.ifcond
= ifcond
or []
426 self
.defined_in
= None
428 def set_defined_in(self
, name
):
429 assert not self
.defined_in
430 self
.defined_in
= name
432 def check_clash(self
, info
, seen
):
433 cname
= c_name(self
.name
)
437 "%s collides with %s"
438 % (self
.describe(info
), seen
[cname
].describe(info
)))
441 def describe(self
, info
):
443 defined_in
= self
.defined_in
446 if defined_in
.startswith('q_obj_'):
447 # See QAPISchema._make_implicit_object_type() - reverse the
448 # mapping there to create a nice human-readable description
449 defined_in
= defined_in
[6:]
450 if defined_in
.endswith('-arg'):
451 # Implicit type created for a command's dict 'data'
452 assert role
== 'member'
454 elif defined_in
.endswith('-base'):
455 # Implicit type created for a flat union's dict 'base'
456 role
= 'base ' + role
458 # Implicit type created for a simple union's branch
459 assert defined_in
.endswith('-wrapper')
460 # Unreachable and not implemented
462 elif defined_in
.endswith('Kind'):
463 # See QAPISchema._make_implicit_enum_type()
464 # Implicit enum created for simple union's branches
465 assert role
== 'value'
467 elif defined_in
!= info
.defn_name
:
468 return "%s '%s' of type '%s'" % (role
, self
.name
, defined_in
)
469 return "%s '%s'" % (role
, self
.name
)
472 class QAPISchemaEnumMember(QAPISchemaMember
):
476 class QAPISchemaFeature(QAPISchemaMember
):
480 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
481 def __init__(self
, name
, info
, typ
, optional
, ifcond
=None):
482 QAPISchemaMember
.__init
__(self
, name
, info
, ifcond
)
483 assert isinstance(typ
, str)
484 assert isinstance(optional
, bool)
485 self
._type
_name
= typ
487 self
.optional
= optional
489 def check(self
, schema
):
490 assert self
.defined_in
491 self
.type = schema
.resolve_type(self
._type
_name
, self
.info
,
495 class QAPISchemaObjectTypeVariants(object):
496 def __init__(self
, tag_name
, info
, tag_member
, variants
):
497 # Flat unions pass tag_name but not tag_member.
498 # Simple unions and alternates pass tag_member but not tag_name.
499 # After check(), tag_member is always set, and tag_name remains
500 # a reliable witness of being used by a flat union.
501 assert bool(tag_member
) != bool(tag_name
)
502 assert (isinstance(tag_name
, str) or
503 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
505 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
506 self
._tag
_name
= tag_name
508 self
.tag_member
= tag_member
509 self
.variants
= variants
511 def set_defined_in(self
, name
):
512 for v
in self
.variants
:
513 v
.set_defined_in(name
)
515 def check(self
, schema
, seen
):
516 if not self
.tag_member
: # flat union
517 self
.tag_member
= seen
.get(c_name(self
._tag
_name
))
519 # Pointing to the base type when not implicit would be
520 # nice, but we don't know it here
521 if not self
.tag_member
or self
._tag
_name
!= self
.tag_member
.name
:
524 "discriminator '%s' is not a member of %s"
525 % (self
._tag
_name
, base
))
527 base_type
= schema
.lookup_type(self
.tag_member
.defined_in
)
529 if not base_type
.is_implicit():
530 base
= "base type '%s'" % self
.tag_member
.defined_in
531 if not isinstance(self
.tag_member
.type, QAPISchemaEnumType
):
534 "discriminator member '%s' of %s must be of enum type"
535 % (self
._tag
_name
, base
))
536 if self
.tag_member
.optional
:
539 "discriminator member '%s' of %s must not be optional"
540 % (self
._tag
_name
, base
))
541 if self
.tag_member
.ifcond
:
544 "discriminator member '%s' of %s must not be conditional"
545 % (self
._tag
_name
, base
))
547 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
548 assert not self
.tag_member
.optional
549 assert self
.tag_member
.ifcond
== []
550 if self
._tag
_name
: # flat union
551 # branches that are not explicitly covered get an empty type
552 cases
= set([v
.name
for v
in self
.variants
])
553 for m
in self
.tag_member
.type.members
:
554 if m
.name
not in cases
:
555 v
= QAPISchemaObjectTypeVariant(m
.name
, self
.info
,
557 v
.set_defined_in(self
.tag_member
.defined_in
)
558 self
.variants
.append(v
)
559 if not self
.variants
:
560 raise QAPISemError(self
.info
, "union has no branches")
561 for v
in self
.variants
:
563 # Union names must match enum values; alternate names are
564 # checked separately. Use 'seen' to tell the two apart.
566 if v
.name
not in self
.tag_member
.type.member_names():
569 "branch '%s' is not a value of %s"
570 % (v
.name
, self
.tag_member
.type.describe()))
571 if (not isinstance(v
.type, QAPISchemaObjectType
)
576 % (v
.describe(self
.info
), v
.type.describe()))
579 def check_clash(self
, info
, seen
):
580 for v
in self
.variants
:
581 # Reset seen map for each variant, since qapi names from one
582 # branch do not affect another branch
583 v
.type.check_clash(info
, dict(seen
))
586 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
589 def __init__(self
, name
, info
, typ
, ifcond
=None):
590 QAPISchemaObjectTypeMember
.__init
__(self
, name
, info
, typ
,
594 class QAPISchemaAlternateType(QAPISchemaType
):
597 def __init__(self
, name
, info
, doc
, ifcond
, variants
):
598 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
599 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
600 assert variants
.tag_member
601 variants
.set_defined_in(name
)
602 variants
.tag_member
.set_defined_in(self
.name
)
603 self
.variants
= variants
605 def check(self
, schema
):
606 QAPISchemaType
.check(self
, schema
)
607 self
.variants
.tag_member
.check(schema
)
608 # Not calling self.variants.check_clash(), because there's nothing
610 self
.variants
.check(schema
, {})
611 # Alternate branch names have no relation to the tag enum values;
612 # so we have to check for potential name collisions ourselves.
615 for v
in self
.variants
.variants
:
616 v
.check_clash(self
.info
, seen
)
617 qtype
= v
.type.alternate_qtype()
622 % (v
.describe(self
.info
), v
.type.describe()))
623 conflicting
= set([qtype
])
624 if qtype
== 'QTYPE_QSTRING':
625 if isinstance(v
.type, QAPISchemaEnumType
):
626 for m
in v
.type.members
:
627 if m
.name
in ['on', 'off']:
628 conflicting
.add('QTYPE_QBOOL')
629 if re
.match(r
'[-+0-9.]', m
.name
):
630 # lazy, could be tightened
631 conflicting
.add('QTYPE_QNUM')
633 conflicting
.add('QTYPE_QNUM')
634 conflicting
.add('QTYPE_QBOOL')
635 for qt
in conflicting
:
639 "%s can't be distinguished from '%s'"
640 % (v
.describe(self
.info
), types_seen
[qt
]))
641 types_seen
[qt
] = v
.name
643 self
.doc
.connect_member(v
)
648 return c_name(self
.name
) + pointer_suffix
653 def visit(self
, visitor
):
654 QAPISchemaType
.visit(self
, visitor
)
655 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.ifcond
,
659 class QAPISchemaCommand(QAPISchemaEntity
):
662 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, ret_type
,
663 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
,
665 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
666 assert not arg_type
or isinstance(arg_type
, str)
667 assert not ret_type
or isinstance(ret_type
, str)
669 assert isinstance(f
, QAPISchemaFeature
)
670 f
.set_defined_in(name
)
671 self
._arg
_type
_name
= arg_type
673 self
._ret
_type
_name
= ret_type
676 self
.success_response
= success_response
678 self
.allow_oob
= allow_oob
679 self
.allow_preconfig
= allow_preconfig
680 self
.features
= features
682 def check(self
, schema
):
683 QAPISchemaEntity
.check(self
, schema
)
684 if self
._arg
_type
_name
:
685 self
.arg_type
= schema
.resolve_type(
686 self
._arg
_type
_name
, self
.info
, "command's 'data'")
687 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
690 "command's 'data' cannot take %s"
691 % self
.arg_type
.describe())
692 if self
.arg_type
.variants
and not self
.boxed
:
695 "command's 'data' can take %s only with 'boxed': true"
696 % self
.arg_type
.describe())
697 if self
._ret
_type
_name
:
698 self
.ret_type
= schema
.resolve_type(
699 self
._ret
_type
_name
, self
.info
, "command's 'returns'")
700 if self
.name
not in self
.info
.pragma
.returns_whitelist
:
701 if not (isinstance(self
.ret_type
, QAPISchemaObjectType
)
702 or (isinstance(self
.ret_type
, QAPISchemaArrayType
)
703 and isinstance(self
.ret_type
.element_type
,
704 QAPISchemaObjectType
))):
707 "command's 'returns' cannot take %s"
708 % self
.ret_type
.describe())
710 # Features are in a name space separate from members
712 for f
in self
.features
:
713 f
.check_clash(self
.info
, seen
)
715 def visit(self
, visitor
):
716 QAPISchemaEntity
.visit(self
, visitor
)
717 visitor
.visit_command(self
.name
, self
.info
, self
.ifcond
,
718 self
.arg_type
, self
.ret_type
,
719 self
.gen
, self
.success_response
,
720 self
.boxed
, self
.allow_oob
,
721 self
.allow_preconfig
,
725 class QAPISchemaEvent(QAPISchemaEntity
):
728 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, boxed
):
729 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
730 assert not arg_type
or isinstance(arg_type
, str)
731 self
._arg
_type
_name
= arg_type
735 def check(self
, schema
):
736 QAPISchemaEntity
.check(self
, schema
)
737 if self
._arg
_type
_name
:
738 self
.arg_type
= schema
.resolve_type(
739 self
._arg
_type
_name
, self
.info
, "event's 'data'")
740 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
743 "event's 'data' cannot take %s"
744 % self
.arg_type
.describe())
745 if self
.arg_type
.variants
and not self
.boxed
:
748 "event's 'data' can take %s only with 'boxed': true"
749 % self
.arg_type
.describe())
751 def visit(self
, visitor
):
752 QAPISchemaEntity
.visit(self
, visitor
)
753 visitor
.visit_event(self
.name
, self
.info
, self
.ifcond
,
754 self
.arg_type
, self
.boxed
)
757 class QAPISchema(object):
758 def __init__(self
, fname
):
760 parser
= QAPISchemaParser(fname
)
761 exprs
= check_exprs(parser
.exprs
)
762 self
.docs
= parser
.docs
763 self
._entity
_list
= []
764 self
._entity
_dict
= {}
765 self
._predefining
= True
766 self
._def
_predefineds
()
767 self
._predefining
= False
768 self
._def
_exprs
(exprs
)
771 def _def_entity(self
, ent
):
772 # Only the predefined types are allowed to not have info
773 assert ent
.info
or self
._predefining
774 self
._entity
_list
.append(ent
)
777 # TODO reject names that differ only in '_' vs. '.' vs. '-',
778 # because they're liable to clash in generated C.
779 other_ent
= self
._entity
_dict
.get(ent
.name
)
782 where
= QAPIError(other_ent
.info
, None, "previous definition")
785 "'%s' is already defined\n%s" % (ent
.name
, where
))
787 ent
.info
, "%s is already defined" % other_ent
.describe())
788 self
._entity
_dict
[ent
.name
] = ent
790 def lookup_entity(self
, name
, typ
=None):
791 ent
= self
._entity
_dict
.get(name
)
792 if typ
and not isinstance(ent
, typ
):
796 def lookup_type(self
, name
):
797 return self
.lookup_entity(name
, QAPISchemaType
)
799 def resolve_type(self
, name
, info
, what
):
800 typ
= self
.lookup_type(name
)
805 info
, "%s uses unknown type '%s'" % (what
, name
))
808 def _def_include(self
, expr
, info
, doc
):
809 include
= expr
['include']
812 while main_info
.parent
:
813 main_info
= main_info
.parent
814 fname
= os
.path
.relpath(include
, os
.path
.dirname(main_info
.fname
))
815 self
._def
_entity
(QAPISchemaInclude(fname
, info
))
817 def _def_builtin_type(self
, name
, json_type
, c_type
):
818 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
819 # Instantiating only the arrays that are actually used would
820 # be nice, but we can't as long as their generated code
821 # (qapi-builtin-types.[ch]) may be shared by some other
823 self
._make
_array
_type
(name
, None)
825 def _def_predefineds(self
):
826 for t
in [('str', 'string', 'char' + pointer_suffix
),
827 ('number', 'number', 'double'),
828 ('int', 'int', 'int64_t'),
829 ('int8', 'int', 'int8_t'),
830 ('int16', 'int', 'int16_t'),
831 ('int32', 'int', 'int32_t'),
832 ('int64', 'int', 'int64_t'),
833 ('uint8', 'int', 'uint8_t'),
834 ('uint16', 'int', 'uint16_t'),
835 ('uint32', 'int', 'uint32_t'),
836 ('uint64', 'int', 'uint64_t'),
837 ('size', 'int', 'uint64_t'),
838 ('bool', 'boolean', 'bool'),
839 ('any', 'value', 'QObject' + pointer_suffix
),
840 ('null', 'null', 'QNull' + pointer_suffix
)]:
841 self
._def
_builtin
_type
(*t
)
842 self
.the_empty_object_type
= QAPISchemaObjectType(
843 'q_empty', None, None, None, None, [], None, [])
844 self
._def
_entity
(self
.the_empty_object_type
)
846 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
848 qtype_values
= self
._make
_enum
_members
(
849 [{'name': n
} for n
in qtypes
], None)
851 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None,
852 qtype_values
, 'QTYPE'))
854 def _make_features(self
, features
, info
):
855 return [QAPISchemaFeature(f
['name'], info
, f
.get('if'))
858 def _make_enum_members(self
, values
, info
):
859 return [QAPISchemaEnumMember(v
['name'], info
, v
.get('if'))
862 def _make_implicit_enum_type(self
, name
, info
, ifcond
, values
):
863 # See also QAPISchemaObjectTypeMember.describe()
864 name
= name
+ 'Kind' # reserved by check_defn_name_str()
865 self
._def
_entity
(QAPISchemaEnumType(
866 name
, info
, None, ifcond
, self
._make
_enum
_members
(values
, info
),
870 def _make_array_type(self
, element_type
, info
):
871 name
= element_type
+ 'List' # reserved by check_defn_name_str()
872 if not self
.lookup_type(name
):
873 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
876 def _make_implicit_object_type(self
, name
, info
, doc
, ifcond
,
880 # See also QAPISchemaObjectTypeMember.describe()
881 name
= 'q_obj_%s-%s' % (name
, role
)
882 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
884 # The implicit object type has multiple users. This can
885 # happen only for simple unions' implicit wrapper types.
886 # Its ifcond should be the disjunction of its user's
887 # ifconds. Not implemented. Instead, we always pass the
888 # wrapped type's ifcond, which is trivially the same for all
889 # users. It's also necessary for the wrapper to compile.
890 # But it's not tight: the disjunction need not imply it. We
891 # may end up compiling useless wrapper types.
892 # TODO kill simple unions or implement the disjunction
893 assert (ifcond
or []) == typ
._ifcond
# pylint: disable=protected-access
895 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, ifcond
,
896 None, members
, None, []))
899 def _def_enum_type(self
, expr
, info
, doc
):
902 prefix
= expr
.get('prefix')
903 ifcond
= expr
.get('if')
904 self
._def
_entity
(QAPISchemaEnumType(
905 name
, info
, doc
, ifcond
,
906 self
._make
_enum
_members
(data
, info
), prefix
))
908 def _make_member(self
, name
, typ
, ifcond
, info
):
910 if name
.startswith('*'):
913 if isinstance(typ
, list):
915 typ
= self
._make
_array
_type
(typ
[0], info
)
916 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
)
918 def _make_members(self
, data
, info
):
919 return [self
._make
_member
(key
, value
['type'], value
.get('if'), info
)
920 for (key
, value
) in data
.items()]
922 def _def_struct_type(self
, expr
, info
, doc
):
923 name
= expr
['struct']
924 base
= expr
.get('base')
926 ifcond
= expr
.get('if')
927 features
= expr
.get('features', [])
928 self
._def
_entity
(QAPISchemaObjectType(
929 name
, info
, doc
, ifcond
, base
,
930 self
._make
_members
(data
, info
),
932 self
._make
_features
(features
, info
)))
934 def _make_variant(self
, case
, typ
, ifcond
, info
):
935 return QAPISchemaObjectTypeVariant(case
, info
, typ
, ifcond
)
937 def _make_simple_variant(self
, case
, typ
, ifcond
, info
):
938 if isinstance(typ
, list):
940 typ
= self
._make
_array
_type
(typ
[0], info
)
941 typ
= self
._make
_implicit
_object
_type
(
942 typ
, info
, None, self
.lookup_type(typ
),
943 'wrapper', [self
._make
_member
('data', typ
, None, info
)])
944 return QAPISchemaObjectTypeVariant(case
, info
, typ
, ifcond
)
946 def _def_union_type(self
, expr
, info
, doc
):
949 base
= expr
.get('base')
950 ifcond
= expr
.get('if')
951 tag_name
= expr
.get('discriminator')
953 if isinstance(base
, dict):
954 base
= self
._make
_implicit
_object
_type
(
955 name
, info
, doc
, ifcond
,
956 'base', self
._make
_members
(base
, info
))
958 variants
= [self
._make
_variant
(key
, value
['type'],
959 value
.get('if'), info
)
960 for (key
, value
) in data
.items()]
963 variants
= [self
._make
_simple
_variant
(key
, value
['type'],
964 value
.get('if'), info
)
965 for (key
, value
) in data
.items()]
966 enum
= [{'name': v
.name
, 'if': v
.ifcond
} for v
in variants
]
967 typ
= self
._make
_implicit
_enum
_type
(name
, info
, ifcond
, enum
)
968 tag_member
= QAPISchemaObjectTypeMember('type', info
, typ
, False)
969 members
= [tag_member
]
971 QAPISchemaObjectType(name
, info
, doc
, ifcond
, base
, members
,
972 QAPISchemaObjectTypeVariants(
973 tag_name
, info
, tag_member
, variants
),
976 def _def_alternate_type(self
, expr
, info
, doc
):
977 name
= expr
['alternate']
979 ifcond
= expr
.get('if')
980 variants
= [self
._make
_variant
(key
, value
['type'], value
.get('if'),
982 for (key
, value
) in data
.items()]
983 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
985 QAPISchemaAlternateType(name
, info
, doc
, ifcond
,
986 QAPISchemaObjectTypeVariants(
987 None, info
, tag_member
, variants
)))
989 def _def_command(self
, expr
, info
, doc
):
990 name
= expr
['command']
991 data
= expr
.get('data')
992 rets
= expr
.get('returns')
993 gen
= expr
.get('gen', True)
994 success_response
= expr
.get('success-response', True)
995 boxed
= expr
.get('boxed', False)
996 allow_oob
= expr
.get('allow-oob', False)
997 allow_preconfig
= expr
.get('allow-preconfig', False)
998 ifcond
= expr
.get('if')
999 features
= expr
.get('features', [])
1000 if isinstance(data
, OrderedDict
):
1001 data
= self
._make
_implicit
_object
_type
(
1002 name
, info
, doc
, ifcond
, 'arg', self
._make
_members
(data
, info
))
1003 if isinstance(rets
, list):
1004 assert len(rets
) == 1
1005 rets
= self
._make
_array
_type
(rets
[0], info
)
1006 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, ifcond
, data
, rets
,
1007 gen
, success_response
,
1008 boxed
, allow_oob
, allow_preconfig
,
1009 self
._make
_features
(features
, info
)))
1011 def _def_event(self
, expr
, info
, doc
):
1012 name
= expr
['event']
1013 data
= expr
.get('data')
1014 boxed
= expr
.get('boxed', False)
1015 ifcond
= expr
.get('if')
1016 if isinstance(data
, OrderedDict
):
1017 data
= self
._make
_implicit
_object
_type
(
1018 name
, info
, doc
, ifcond
, 'arg', self
._make
_members
(data
, info
))
1019 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, ifcond
, data
, boxed
))
1021 def _def_exprs(self
, exprs
):
1022 for expr_elem
in exprs
:
1023 expr
= expr_elem
['expr']
1024 info
= expr_elem
['info']
1025 doc
= expr_elem
.get('doc')
1027 self
._def
_enum
_type
(expr
, info
, doc
)
1028 elif 'struct' in expr
:
1029 self
._def
_struct
_type
(expr
, info
, doc
)
1030 elif 'union' in expr
:
1031 self
._def
_union
_type
(expr
, info
, doc
)
1032 elif 'alternate' in expr
:
1033 self
._def
_alternate
_type
(expr
, info
, doc
)
1034 elif 'command' in expr
:
1035 self
._def
_command
(expr
, info
, doc
)
1036 elif 'event' in expr
:
1037 self
._def
_event
(expr
, info
, doc
)
1038 elif 'include' in expr
:
1039 self
._def
_include
(expr
, info
, doc
)
1044 for ent
in self
._entity
_list
:
1047 def visit(self
, visitor
):
1048 visitor
.visit_begin(self
)
1050 visitor
.visit_module(module
)
1051 for entity
in self
._entity
_list
:
1052 if visitor
.visit_needed(entity
):
1053 if entity
.module
!= module
:
1054 module
= entity
.module
1055 visitor
.visit_module(module
)
1056 entity
.visit(visitor
)