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
, QAPISemError
23 from qapi
.expr
import check_exprs
24 from qapi
.parser
import QAPISchemaParser
27 class QAPISchemaEntity
:
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 for f
in self
.features
:
55 f
.check_clash(self
.info
, seen
)
58 def connect_doc(self
, doc
=None):
61 for f
in self
.features
:
62 doc
.connect_feature(f
)
68 def _set_module(self
, schema
, info
):
70 self
._module
= schema
.module_by_fname(info
and info
.fname
)
71 self
._module
.add_entity(self
)
73 def set_module(self
, schema
):
74 self
._set
_module
(schema
, self
.info
)
81 def is_implicit(self
):
84 def visit(self
, visitor
):
89 return "%s '%s'" % (self
.meta
, self
.name
)
92 class QAPISchemaVisitor
:
93 def visit_begin(self
, schema
):
99 def visit_module(self
, name
):
102 def visit_needed(self
, entity
):
103 # Default to visiting everything
106 def visit_include(self
, name
, info
):
109 def visit_builtin_type(self
, name
, info
, json_type
):
112 def visit_enum_type(self
, name
, info
, ifcond
, features
, members
, prefix
):
115 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
118 def visit_object_type(self
, name
, info
, ifcond
, features
,
119 base
, members
, variants
):
122 def visit_object_type_flat(self
, name
, info
, ifcond
, features
,
126 def visit_alternate_type(self
, name
, info
, ifcond
, features
, variants
):
129 def visit_command(self
, name
, info
, ifcond
, features
,
130 arg_type
, ret_type
, gen
, success_response
, boxed
,
131 allow_oob
, allow_preconfig
):
134 def visit_event(self
, name
, info
, ifcond
, features
, arg_type
, boxed
):
138 class QAPISchemaModule
:
139 def __init__(self
, name
):
141 self
._entity
_list
= []
143 def add_entity(self
, ent
):
144 self
._entity
_list
.append(ent
)
146 def visit(self
, visitor
):
147 visitor
.visit_module(self
.name
)
148 for entity
in self
._entity
_list
:
149 if visitor
.visit_needed(entity
):
150 entity
.visit(visitor
)
153 class QAPISchemaInclude(QAPISchemaEntity
):
154 def __init__(self
, sub_module
, info
):
155 super().__init
__(None, info
, None)
156 self
._sub
_module
= sub_module
158 def visit(self
, visitor
):
159 super().visit(visitor
)
160 visitor
.visit_include(self
._sub
_module
.name
, self
.info
)
163 class QAPISchemaType(QAPISchemaEntity
):
164 # Return the C type for common use.
165 # For the types we commonly box, this is a pointer type.
169 # Return the C type to be used in a parameter list.
170 def c_param_type(self
):
173 # Return the C type to be used where we suppress boxing.
174 def c_unboxed_type(self
):
180 def alternate_qtype(self
):
182 'null': 'QTYPE_QNULL',
183 'string': 'QTYPE_QSTRING',
184 'number': 'QTYPE_QNUM',
186 'boolean': 'QTYPE_QBOOL',
187 'object': 'QTYPE_QDICT'
189 return json2qtype
.get(self
.json_type())
192 if self
.is_implicit():
198 return "%s type '%s'" % (self
.meta
, self
.name
)
201 class QAPISchemaBuiltinType(QAPISchemaType
):
204 def __init__(self
, name
, json_type
, c_type
):
205 super().__init
__(name
, None, None)
206 assert not c_type
or isinstance(c_type
, str)
207 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
209 self
._json
_type
_name
= json_type
210 self
._c
_type
_name
= c_type
216 return self
._c
_type
_name
218 def c_param_type(self
):
219 if self
.name
== 'str':
220 return 'const ' + self
._c
_type
_name
221 return self
._c
_type
_name
224 return self
._json
_type
_name
227 return self
.json_type()
229 def visit(self
, visitor
):
230 super().visit(visitor
)
231 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
234 class QAPISchemaEnumType(QAPISchemaType
):
237 def __init__(self
, name
, info
, doc
, ifcond
, features
, members
, prefix
):
238 super().__init
__(name
, info
, doc
, ifcond
, features
)
240 assert isinstance(m
, QAPISchemaEnumMember
)
241 m
.set_defined_in(name
)
242 assert prefix
is None or isinstance(prefix
, str)
243 self
.members
= members
246 def check(self
, schema
):
247 super().check(schema
)
249 for m
in self
.members
:
250 m
.check_clash(self
.info
, seen
)
252 def connect_doc(self
, doc
=None):
253 super().connect_doc(doc
)
254 doc
= doc
or self
.doc
256 for m
in self
.members
:
257 doc
.connect_member(m
)
259 def is_implicit(self
):
260 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
261 return self
.name
.endswith('Kind') or self
.name
== 'QType'
264 return c_name(self
.name
)
266 def member_names(self
):
267 return [m
.name
for m
in self
.members
]
272 def visit(self
, visitor
):
273 super().visit(visitor
)
274 visitor
.visit_enum_type(
275 self
.name
, self
.info
, self
.ifcond
, self
.features
,
276 self
.members
, self
.prefix
)
279 class QAPISchemaArrayType(QAPISchemaType
):
282 def __init__(self
, name
, info
, element_type
):
283 super().__init
__(name
, info
, None)
284 assert isinstance(element_type
, str)
285 self
._element
_type
_name
= element_type
286 self
.element_type
= None
288 def check(self
, schema
):
289 super().check(schema
)
290 self
.element_type
= schema
.resolve_type(
291 self
._element
_type
_name
, self
.info
,
292 self
.info
and self
.info
.defn_meta
)
293 assert not isinstance(self
.element_type
, QAPISchemaArrayType
)
295 def set_module(self
, schema
):
296 self
._set
_module
(schema
, self
.element_type
.info
)
301 return self
.element_type
.ifcond
303 def is_implicit(self
):
307 return c_name(self
.name
) + pointer_suffix
313 elt_doc_type
= self
.element_type
.doc_type()
316 return 'array of ' + elt_doc_type
318 def visit(self
, visitor
):
319 super().visit(visitor
)
320 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
325 return "%s type ['%s']" % (self
.meta
, self
._element
_type
_name
)
328 class QAPISchemaObjectType(QAPISchemaType
):
329 def __init__(self
, name
, info
, doc
, ifcond
, features
,
330 base
, local_members
, variants
):
331 # struct has local_members, optional base, and no variants
332 # flat union has base, variants, and no local_members
333 # simple union has local_members, variants, and no base
334 super().__init
__(name
, info
, doc
, ifcond
, features
)
335 self
.meta
= 'union' if variants
else 'struct'
336 assert base
is None or isinstance(base
, str)
337 for m
in local_members
:
338 assert isinstance(m
, QAPISchemaObjectTypeMember
)
339 m
.set_defined_in(name
)
340 if variants
is not None:
341 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
342 variants
.set_defined_in(name
)
343 self
._base
_name
= base
345 self
.local_members
= local_members
346 self
.variants
= variants
349 def check(self
, schema
):
350 # This calls another type T's .check() exactly when the C
351 # struct emitted by gen_object() contains that T's C struct
352 # (pointers don't count).
353 if self
.members
is not None:
354 # A previous .check() completed: nothing to do
357 # Recursed: C struct contains itself
358 raise QAPISemError(self
.info
,
359 "object %s contains itself" % self
.name
)
361 super().check(schema
)
362 assert self
._checked
and self
.members
is None
366 self
.base
= schema
.resolve_type(self
._base
_name
, self
.info
,
368 if (not isinstance(self
.base
, QAPISchemaObjectType
)
369 or self
.base
.variants
):
372 "'base' requires a struct type, %s isn't"
373 % self
.base
.describe())
374 self
.base
.check(schema
)
375 self
.base
.check_clash(self
.info
, seen
)
376 for m
in self
.local_members
:
378 m
.check_clash(self
.info
, seen
)
379 members
= seen
.values()
382 self
.variants
.check(schema
, seen
)
383 self
.variants
.check_clash(self
.info
, seen
)
385 self
.members
= members
# mark completed
387 # Check that the members of this type do not cause duplicate JSON members,
388 # and update seen to track the members seen so far. Report any errors
389 # on behalf of info, which is not necessarily self.info
390 def check_clash(self
, info
, seen
):
392 assert not self
.variants
# not implemented
393 for m
in self
.members
:
394 m
.check_clash(info
, seen
)
396 def connect_doc(self
, doc
=None):
397 super().connect_doc(doc
)
398 doc
= doc
or self
.doc
400 if self
.base
and self
.base
.is_implicit():
401 self
.base
.connect_doc(doc
)
402 for m
in self
.local_members
:
403 doc
.connect_member(m
)
408 if isinstance(self
._ifcond
, QAPISchemaType
):
409 # Simple union wrapper type inherits from wrapped type;
410 # see _make_implicit_object_type()
411 return self
._ifcond
.ifcond
414 def is_implicit(self
):
415 # See QAPISchema._make_implicit_object_type(), as well as
417 return self
.name
.startswith('q_')
420 assert self
.members
is not None
421 return not self
.members
and not self
.variants
424 assert self
.name
!= 'q_empty'
425 return super().c_name()
428 assert not self
.is_implicit()
429 return c_name(self
.name
) + pointer_suffix
431 def c_unboxed_type(self
):
432 return c_name(self
.name
)
437 def visit(self
, visitor
):
438 super().visit(visitor
)
439 visitor
.visit_object_type(
440 self
.name
, self
.info
, self
.ifcond
, self
.features
,
441 self
.base
, self
.local_members
, self
.variants
)
442 visitor
.visit_object_type_flat(
443 self
.name
, self
.info
, self
.ifcond
, self
.features
,
444 self
.members
, self
.variants
)
447 class QAPISchemaMember
:
448 """ Represents object members, enum members and features """
451 def __init__(self
, name
, info
, ifcond
=None):
452 assert isinstance(name
, str)
455 self
.ifcond
= ifcond
or []
456 self
.defined_in
= None
458 def set_defined_in(self
, name
):
459 assert not self
.defined_in
460 self
.defined_in
= name
462 def check_clash(self
, info
, seen
):
463 cname
= c_name(self
.name
)
467 "%s collides with %s"
468 % (self
.describe(info
), seen
[cname
].describe(info
)))
471 def describe(self
, info
):
473 defined_in
= self
.defined_in
476 if defined_in
.startswith('q_obj_'):
477 # See QAPISchema._make_implicit_object_type() - reverse the
478 # mapping there to create a nice human-readable description
479 defined_in
= defined_in
[6:]
480 if defined_in
.endswith('-arg'):
481 # Implicit type created for a command's dict 'data'
482 assert role
== 'member'
484 elif defined_in
.endswith('-base'):
485 # Implicit type created for a flat union's dict 'base'
486 role
= 'base ' + role
488 # Implicit type created for a simple union's branch
489 assert defined_in
.endswith('-wrapper')
490 # Unreachable and not implemented
492 elif defined_in
.endswith('Kind'):
493 # See QAPISchema._make_implicit_enum_type()
494 # Implicit enum created for simple union's branches
495 assert role
== 'value'
497 elif defined_in
!= info
.defn_name
:
498 return "%s '%s' of type '%s'" % (role
, self
.name
, defined_in
)
499 return "%s '%s'" % (role
, self
.name
)
502 class QAPISchemaEnumMember(QAPISchemaMember
):
506 class QAPISchemaFeature(QAPISchemaMember
):
510 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
511 def __init__(self
, name
, info
, typ
, optional
, ifcond
=None):
512 super().__init
__(name
, info
, ifcond
)
513 assert isinstance(typ
, str)
514 assert isinstance(optional
, bool)
515 self
._type
_name
= typ
517 self
.optional
= optional
519 def check(self
, schema
):
520 assert self
.defined_in
521 self
.type = schema
.resolve_type(self
._type
_name
, self
.info
,
525 class QAPISchemaObjectTypeVariants
:
526 def __init__(self
, tag_name
, info
, tag_member
, variants
):
527 # Flat unions pass tag_name but not tag_member.
528 # Simple unions and alternates pass tag_member but not tag_name.
529 # After check(), tag_member is always set, and tag_name remains
530 # a reliable witness of being used by a flat union.
531 assert bool(tag_member
) != bool(tag_name
)
532 assert (isinstance(tag_name
, str) or
533 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
535 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
536 self
._tag
_name
= tag_name
538 self
.tag_member
= tag_member
539 self
.variants
= variants
541 def set_defined_in(self
, name
):
542 for v
in self
.variants
:
543 v
.set_defined_in(name
)
545 def check(self
, schema
, seen
):
546 if not self
.tag_member
: # flat union
547 self
.tag_member
= seen
.get(c_name(self
._tag
_name
))
549 # Pointing to the base type when not implicit would be
550 # nice, but we don't know it here
551 if not self
.tag_member
or self
._tag
_name
!= self
.tag_member
.name
:
554 "discriminator '%s' is not a member of %s"
555 % (self
._tag
_name
, base
))
557 base_type
= schema
.lookup_type(self
.tag_member
.defined_in
)
559 if not base_type
.is_implicit():
560 base
= "base type '%s'" % self
.tag_member
.defined_in
561 if not isinstance(self
.tag_member
.type, QAPISchemaEnumType
):
564 "discriminator member '%s' of %s must be of enum type"
565 % (self
._tag
_name
, base
))
566 if self
.tag_member
.optional
:
569 "discriminator member '%s' of %s must not be optional"
570 % (self
._tag
_name
, base
))
571 if self
.tag_member
.ifcond
:
574 "discriminator member '%s' of %s must not be conditional"
575 % (self
._tag
_name
, base
))
577 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
578 assert not self
.tag_member
.optional
579 assert self
.tag_member
.ifcond
== []
580 if self
._tag
_name
: # flat union
581 # branches that are not explicitly covered get an empty type
582 cases
= {v
.name
for v
in self
.variants
}
583 for m
in self
.tag_member
.type.members
:
584 if m
.name
not in cases
:
585 v
= QAPISchemaObjectTypeVariant(m
.name
, self
.info
,
587 v
.set_defined_in(self
.tag_member
.defined_in
)
588 self
.variants
.append(v
)
589 if not self
.variants
:
590 raise QAPISemError(self
.info
, "union has no branches")
591 for v
in self
.variants
:
593 # Union names must match enum values; alternate names are
594 # checked separately. Use 'seen' to tell the two apart.
596 if v
.name
not in self
.tag_member
.type.member_names():
599 "branch '%s' is not a value of %s"
600 % (v
.name
, self
.tag_member
.type.describe()))
601 if (not isinstance(v
.type, QAPISchemaObjectType
)
606 % (v
.describe(self
.info
), v
.type.describe()))
609 def check_clash(self
, info
, seen
):
610 for v
in self
.variants
:
611 # Reset seen map for each variant, since qapi names from one
612 # branch do not affect another branch
613 v
.type.check_clash(info
, dict(seen
))
616 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
619 def __init__(self
, name
, info
, typ
, ifcond
=None):
620 super().__init
__(name
, info
, typ
, False, ifcond
)
623 class QAPISchemaAlternateType(QAPISchemaType
):
626 def __init__(self
, name
, info
, doc
, ifcond
, features
, variants
):
627 super().__init
__(name
, info
, doc
, ifcond
, features
)
628 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
629 assert variants
.tag_member
630 variants
.set_defined_in(name
)
631 variants
.tag_member
.set_defined_in(self
.name
)
632 self
.variants
= variants
634 def check(self
, schema
):
635 super().check(schema
)
636 self
.variants
.tag_member
.check(schema
)
637 # Not calling self.variants.check_clash(), because there's nothing
639 self
.variants
.check(schema
, {})
640 # Alternate branch names have no relation to the tag enum values;
641 # so we have to check for potential name collisions ourselves.
644 for v
in self
.variants
.variants
:
645 v
.check_clash(self
.info
, seen
)
646 qtype
= v
.type.alternate_qtype()
651 % (v
.describe(self
.info
), v
.type.describe()))
652 conflicting
= set([qtype
])
653 if qtype
== 'QTYPE_QSTRING':
654 if isinstance(v
.type, QAPISchemaEnumType
):
655 for m
in v
.type.members
:
656 if m
.name
in ['on', 'off']:
657 conflicting
.add('QTYPE_QBOOL')
658 if re
.match(r
'[-+0-9.]', m
.name
):
659 # lazy, could be tightened
660 conflicting
.add('QTYPE_QNUM')
662 conflicting
.add('QTYPE_QNUM')
663 conflicting
.add('QTYPE_QBOOL')
664 for qt
in conflicting
:
668 "%s can't be distinguished from '%s'"
669 % (v
.describe(self
.info
), types_seen
[qt
]))
670 types_seen
[qt
] = v
.name
672 def connect_doc(self
, doc
=None):
673 super().connect_doc(doc
)
674 doc
= doc
or self
.doc
676 for v
in self
.variants
.variants
:
677 doc
.connect_member(v
)
680 return c_name(self
.name
) + pointer_suffix
685 def visit(self
, visitor
):
686 super().visit(visitor
)
687 visitor
.visit_alternate_type(
688 self
.name
, self
.info
, self
.ifcond
, self
.features
, self
.variants
)
691 class QAPISchemaCommand(QAPISchemaEntity
):
694 def __init__(self
, name
, info
, doc
, ifcond
, features
,
696 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
):
697 super().__init
__(name
, info
, doc
, ifcond
, features
)
698 assert not arg_type
or isinstance(arg_type
, str)
699 assert not ret_type
or isinstance(ret_type
, str)
700 self
._arg
_type
_name
= arg_type
702 self
._ret
_type
_name
= ret_type
705 self
.success_response
= success_response
707 self
.allow_oob
= allow_oob
708 self
.allow_preconfig
= allow_preconfig
710 def check(self
, schema
):
711 super().check(schema
)
712 if self
._arg
_type
_name
:
713 self
.arg_type
= schema
.resolve_type(
714 self
._arg
_type
_name
, self
.info
, "command's 'data'")
715 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
718 "command's 'data' cannot take %s"
719 % self
.arg_type
.describe())
720 if self
.arg_type
.variants
and not self
.boxed
:
723 "command's 'data' can take %s only with 'boxed': true"
724 % self
.arg_type
.describe())
725 if self
._ret
_type
_name
:
726 self
.ret_type
= schema
.resolve_type(
727 self
._ret
_type
_name
, self
.info
, "command's 'returns'")
728 if self
.name
not in self
.info
.pragma
.returns_whitelist
:
730 if isinstance(typ
, QAPISchemaArrayType
):
731 typ
= self
.ret_type
.element_type
733 if not isinstance(typ
, QAPISchemaObjectType
):
736 "command's 'returns' cannot take %s"
737 % self
.ret_type
.describe())
739 def connect_doc(self
, doc
=None):
740 super().connect_doc(doc
)
741 doc
= doc
or self
.doc
743 if self
.arg_type
and self
.arg_type
.is_implicit():
744 self
.arg_type
.connect_doc(doc
)
746 def visit(self
, visitor
):
747 super().visit(visitor
)
748 visitor
.visit_command(
749 self
.name
, self
.info
, self
.ifcond
, self
.features
,
750 self
.arg_type
, self
.ret_type
, self
.gen
, self
.success_response
,
751 self
.boxed
, self
.allow_oob
, self
.allow_preconfig
)
754 class QAPISchemaEvent(QAPISchemaEntity
):
757 def __init__(self
, name
, info
, doc
, ifcond
, features
, arg_type
, boxed
):
758 super().__init
__(name
, info
, doc
, ifcond
, features
)
759 assert not arg_type
or isinstance(arg_type
, str)
760 self
._arg
_type
_name
= arg_type
764 def check(self
, schema
):
765 super().check(schema
)
766 if self
._arg
_type
_name
:
767 self
.arg_type
= schema
.resolve_type(
768 self
._arg
_type
_name
, self
.info
, "event's 'data'")
769 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
772 "event's 'data' cannot take %s"
773 % self
.arg_type
.describe())
774 if self
.arg_type
.variants
and not self
.boxed
:
777 "event's 'data' can take %s only with 'boxed': true"
778 % self
.arg_type
.describe())
780 def connect_doc(self
, doc
=None):
781 super().connect_doc(doc
)
782 doc
= doc
or self
.doc
784 if self
.arg_type
and self
.arg_type
.is_implicit():
785 self
.arg_type
.connect_doc(doc
)
787 def visit(self
, visitor
):
788 super().visit(visitor
)
790 self
.name
, self
.info
, self
.ifcond
, self
.features
,
791 self
.arg_type
, self
.boxed
)
795 def __init__(self
, fname
):
797 parser
= QAPISchemaParser(fname
)
798 exprs
= check_exprs(parser
.exprs
)
799 self
.docs
= parser
.docs
800 self
._entity
_list
= []
801 self
._entity
_dict
= {}
802 self
._module
_dict
= OrderedDict()
803 self
._schema
_dir
= os
.path
.dirname(fname
)
804 self
._make
_module
(None) # built-ins
805 self
._make
_module
(fname
)
806 self
._predefining
= True
807 self
._def
_predefineds
()
808 self
._predefining
= False
809 self
._def
_exprs
(exprs
)
812 def _def_entity(self
, ent
):
813 # Only the predefined types are allowed to not have info
814 assert ent
.info
or self
._predefining
815 self
._entity
_list
.append(ent
)
818 # TODO reject names that differ only in '_' vs. '.' vs. '-',
819 # because they're liable to clash in generated C.
820 other_ent
= self
._entity
_dict
.get(ent
.name
)
823 where
= QAPIError(other_ent
.info
, None, "previous definition")
826 "'%s' is already defined\n%s" % (ent
.name
, where
))
828 ent
.info
, "%s is already defined" % other_ent
.describe())
829 self
._entity
_dict
[ent
.name
] = ent
831 def lookup_entity(self
, name
, typ
=None):
832 ent
= self
._entity
_dict
.get(name
)
833 if typ
and not isinstance(ent
, typ
):
837 def lookup_type(self
, name
):
838 return self
.lookup_entity(name
, QAPISchemaType
)
840 def resolve_type(self
, name
, info
, what
):
841 typ
= self
.lookup_type(name
)
846 info
, "%s uses unknown type '%s'" % (what
, name
))
849 def _module_name(self
, fname
):
852 return os
.path
.relpath(fname
, self
._schema
_dir
)
854 def _make_module(self
, fname
):
855 name
= self
._module
_name
(fname
)
856 if name
not in self
._module
_dict
:
857 self
._module
_dict
[name
] = QAPISchemaModule(name
)
858 return self
._module
_dict
[name
]
860 def module_by_fname(self
, fname
):
861 name
= self
._module
_name
(fname
)
862 assert name
in self
._module
_dict
863 return self
._module
_dict
[name
]
865 def _def_include(self
, expr
, info
, doc
):
866 include
= expr
['include']
868 self
._def
_entity
(QAPISchemaInclude(self
._make
_module
(include
), info
))
870 def _def_builtin_type(self
, name
, json_type
, c_type
):
871 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
872 # Instantiating only the arrays that are actually used would
873 # be nice, but we can't as long as their generated code
874 # (qapi-builtin-types.[ch]) may be shared by some other
876 self
._make
_array
_type
(name
, None)
878 def _def_predefineds(self
):
879 for t
in [('str', 'string', 'char' + pointer_suffix
),
880 ('number', 'number', 'double'),
881 ('int', 'int', 'int64_t'),
882 ('int8', 'int', 'int8_t'),
883 ('int16', 'int', 'int16_t'),
884 ('int32', 'int', 'int32_t'),
885 ('int64', 'int', 'int64_t'),
886 ('uint8', 'int', 'uint8_t'),
887 ('uint16', 'int', 'uint16_t'),
888 ('uint32', 'int', 'uint32_t'),
889 ('uint64', 'int', 'uint64_t'),
890 ('size', 'int', 'uint64_t'),
891 ('bool', 'boolean', 'bool'),
892 ('any', 'value', 'QObject' + pointer_suffix
),
893 ('null', 'null', 'QNull' + pointer_suffix
)]:
894 self
._def
_builtin
_type
(*t
)
895 self
.the_empty_object_type
= QAPISchemaObjectType(
896 'q_empty', None, None, None, None, None, [], None)
897 self
._def
_entity
(self
.the_empty_object_type
)
899 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
901 qtype_values
= self
._make
_enum
_members
(
902 [{'name': n
} for n
in qtypes
], None)
904 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None, None,
905 qtype_values
, 'QTYPE'))
907 def _make_features(self
, features
, info
):
910 return [QAPISchemaFeature(f
['name'], info
, f
.get('if'))
913 def _make_enum_members(self
, values
, info
):
914 return [QAPISchemaEnumMember(v
['name'], info
, v
.get('if'))
917 def _make_implicit_enum_type(self
, name
, info
, ifcond
, values
):
918 # See also QAPISchemaObjectTypeMember.describe()
919 name
= name
+ 'Kind' # reserved by check_defn_name_str()
920 self
._def
_entity
(QAPISchemaEnumType(
921 name
, info
, None, ifcond
, None,
922 self
._make
_enum
_members
(values
, info
),
926 def _make_array_type(self
, element_type
, info
):
927 name
= element_type
+ 'List' # reserved by check_defn_name_str()
928 if not self
.lookup_type(name
):
929 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
932 def _make_implicit_object_type(self
, name
, info
, ifcond
, role
, members
):
935 # See also QAPISchemaObjectTypeMember.describe()
936 name
= 'q_obj_%s-%s' % (name
, role
)
937 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
939 # The implicit object type has multiple users. This can
940 # happen only for simple unions' implicit wrapper types.
941 # Its ifcond should be the disjunction of its user's
942 # ifconds. Not implemented. Instead, we always pass the
943 # wrapped type's ifcond, which is trivially the same for all
944 # users. It's also necessary for the wrapper to compile.
945 # But it's not tight: the disjunction need not imply it. We
946 # may end up compiling useless wrapper types.
947 # TODO kill simple unions or implement the disjunction
948 assert (ifcond
or []) == typ
._ifcond
# pylint: disable=protected-access
950 self
._def
_entity
(QAPISchemaObjectType(
951 name
, info
, None, ifcond
, None, None, members
, None))
954 def _def_enum_type(self
, expr
, info
, doc
):
957 prefix
= expr
.get('prefix')
958 ifcond
= expr
.get('if')
959 features
= self
._make
_features
(expr
.get('features'), info
)
960 self
._def
_entity
(QAPISchemaEnumType(
961 name
, info
, doc
, ifcond
, features
,
962 self
._make
_enum
_members
(data
, info
), prefix
))
964 def _make_member(self
, name
, typ
, ifcond
, info
):
966 if name
.startswith('*'):
969 if isinstance(typ
, list):
971 typ
= self
._make
_array
_type
(typ
[0], info
)
972 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
)
974 def _make_members(self
, data
, info
):
975 return [self
._make
_member
(key
, value
['type'], value
.get('if'), info
)
976 for (key
, value
) in data
.items()]
978 def _def_struct_type(self
, expr
, info
, doc
):
979 name
= expr
['struct']
980 base
= expr
.get('base')
982 ifcond
= expr
.get('if')
983 features
= self
._make
_features
(expr
.get('features'), info
)
984 self
._def
_entity
(QAPISchemaObjectType(
985 name
, info
, doc
, ifcond
, features
, base
,
986 self
._make
_members
(data
, info
),
989 def _make_variant(self
, case
, typ
, ifcond
, info
):
990 return QAPISchemaObjectTypeVariant(case
, info
, typ
, ifcond
)
992 def _make_simple_variant(self
, case
, typ
, ifcond
, info
):
993 if isinstance(typ
, list):
995 typ
= self
._make
_array
_type
(typ
[0], info
)
996 typ
= self
._make
_implicit
_object
_type
(
997 typ
, info
, self
.lookup_type(typ
),
998 'wrapper', [self
._make
_member
('data', typ
, None, info
)])
999 return QAPISchemaObjectTypeVariant(case
, info
, typ
, ifcond
)
1001 def _def_union_type(self
, expr
, info
, doc
):
1002 name
= expr
['union']
1004 base
= expr
.get('base')
1005 ifcond
= expr
.get('if')
1006 features
= self
._make
_features
(expr
.get('features'), info
)
1007 tag_name
= expr
.get('discriminator')
1009 if isinstance(base
, dict):
1010 base
= self
._make
_implicit
_object
_type
(
1012 'base', self
._make
_members
(base
, info
))
1014 variants
= [self
._make
_variant
(key
, value
['type'],
1015 value
.get('if'), info
)
1016 for (key
, value
) in data
.items()]
1019 variants
= [self
._make
_simple
_variant
(key
, value
['type'],
1020 value
.get('if'), info
)
1021 for (key
, value
) in data
.items()]
1022 enum
= [{'name': v
.name
, 'if': v
.ifcond
} for v
in variants
]
1023 typ
= self
._make
_implicit
_enum
_type
(name
, info
, ifcond
, enum
)
1024 tag_member
= QAPISchemaObjectTypeMember('type', info
, typ
, False)
1025 members
= [tag_member
]
1027 QAPISchemaObjectType(name
, info
, doc
, ifcond
, features
,
1029 QAPISchemaObjectTypeVariants(
1030 tag_name
, info
, tag_member
, variants
)))
1032 def _def_alternate_type(self
, expr
, info
, doc
):
1033 name
= expr
['alternate']
1035 ifcond
= expr
.get('if')
1036 features
= self
._make
_features
(expr
.get('features'), info
)
1037 variants
= [self
._make
_variant
(key
, value
['type'], value
.get('if'),
1039 for (key
, value
) in data
.items()]
1040 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
1042 QAPISchemaAlternateType(name
, info
, doc
, ifcond
, features
,
1043 QAPISchemaObjectTypeVariants(
1044 None, info
, tag_member
, variants
)))
1046 def _def_command(self
, expr
, info
, doc
):
1047 name
= expr
['command']
1048 data
= expr
.get('data')
1049 rets
= expr
.get('returns')
1050 gen
= expr
.get('gen', True)
1051 success_response
= expr
.get('success-response', True)
1052 boxed
= expr
.get('boxed', False)
1053 allow_oob
= expr
.get('allow-oob', False)
1054 allow_preconfig
= expr
.get('allow-preconfig', False)
1055 ifcond
= expr
.get('if')
1056 features
= self
._make
_features
(expr
.get('features'), info
)
1057 if isinstance(data
, OrderedDict
):
1058 data
= self
._make
_implicit
_object
_type
(
1060 'arg', self
._make
_members
(data
, info
))
1061 if isinstance(rets
, list):
1062 assert len(rets
) == 1
1063 rets
= self
._make
_array
_type
(rets
[0], info
)
1064 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, ifcond
, features
,
1066 gen
, success_response
,
1067 boxed
, allow_oob
, allow_preconfig
))
1069 def _def_event(self
, expr
, info
, doc
):
1070 name
= expr
['event']
1071 data
= expr
.get('data')
1072 boxed
= expr
.get('boxed', False)
1073 ifcond
= expr
.get('if')
1074 features
= self
._make
_features
(expr
.get('features'), info
)
1075 if isinstance(data
, OrderedDict
):
1076 data
= self
._make
_implicit
_object
_type
(
1078 'arg', self
._make
_members
(data
, info
))
1079 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, ifcond
, features
,
1082 def _def_exprs(self
, exprs
):
1083 for expr_elem
in exprs
:
1084 expr
= expr_elem
['expr']
1085 info
= expr_elem
['info']
1086 doc
= expr_elem
.get('doc')
1088 self
._def
_enum
_type
(expr
, info
, doc
)
1089 elif 'struct' in expr
:
1090 self
._def
_struct
_type
(expr
, info
, doc
)
1091 elif 'union' in expr
:
1092 self
._def
_union
_type
(expr
, info
, doc
)
1093 elif 'alternate' in expr
:
1094 self
._def
_alternate
_type
(expr
, info
, doc
)
1095 elif 'command' in expr
:
1096 self
._def
_command
(expr
, info
, doc
)
1097 elif 'event' in expr
:
1098 self
._def
_event
(expr
, info
, doc
)
1099 elif 'include' in expr
:
1100 self
._def
_include
(expr
, info
, doc
)
1105 for ent
in self
._entity
_list
:
1109 for ent
in self
._entity
_list
:
1110 ent
.set_module(self
)
1112 def visit(self
, visitor
):
1113 visitor
.visit_begin(self
)
1114 for mod
in self
._module
_dict
.values():