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
, base
, members
, variants
,
122 def visit_object_type_flat(self
, name
, info
, ifcond
, members
, variants
,
126 def visit_alternate_type(self
, name
, info
, ifcond
, features
, 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
, 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(self
.name
, self
.info
, self
.ifcond
,
440 self
.base
, self
.local_members
, self
.variants
,
442 visitor
.visit_object_type_flat(self
.name
, self
.info
, self
.ifcond
,
443 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(self
.name
, self
.info
, self
.ifcond
,
749 self
.arg_type
, self
.ret_type
,
750 self
.gen
, self
.success_response
,
751 self
.boxed
, self
.allow_oob
,
752 self
.allow_preconfig
,
756 class QAPISchemaEvent(QAPISchemaEntity
):
759 def __init__(self
, name
, info
, doc
, ifcond
, features
, arg_type
, boxed
):
760 super().__init
__(name
, info
, doc
, ifcond
, features
)
761 assert not arg_type
or isinstance(arg_type
, str)
762 self
._arg
_type
_name
= arg_type
766 def check(self
, schema
):
767 super().check(schema
)
768 if self
._arg
_type
_name
:
769 self
.arg_type
= schema
.resolve_type(
770 self
._arg
_type
_name
, self
.info
, "event's 'data'")
771 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
774 "event's 'data' cannot take %s"
775 % self
.arg_type
.describe())
776 if self
.arg_type
.variants
and not self
.boxed
:
779 "event's 'data' can take %s only with 'boxed': true"
780 % self
.arg_type
.describe())
782 def connect_doc(self
, doc
=None):
783 super().connect_doc(doc
)
784 doc
= doc
or self
.doc
786 if self
.arg_type
and self
.arg_type
.is_implicit():
787 self
.arg_type
.connect_doc(doc
)
789 def visit(self
, visitor
):
790 super().visit(visitor
)
792 self
.name
, self
.info
, self
.ifcond
, self
.features
,
793 self
.arg_type
, self
.boxed
)
797 def __init__(self
, fname
):
799 parser
= QAPISchemaParser(fname
)
800 exprs
= check_exprs(parser
.exprs
)
801 self
.docs
= parser
.docs
802 self
._entity
_list
= []
803 self
._entity
_dict
= {}
804 self
._module
_dict
= OrderedDict()
805 self
._schema
_dir
= os
.path
.dirname(fname
)
806 self
._make
_module
(None) # built-ins
807 self
._make
_module
(fname
)
808 self
._predefining
= True
809 self
._def
_predefineds
()
810 self
._predefining
= False
811 self
._def
_exprs
(exprs
)
814 def _def_entity(self
, ent
):
815 # Only the predefined types are allowed to not have info
816 assert ent
.info
or self
._predefining
817 self
._entity
_list
.append(ent
)
820 # TODO reject names that differ only in '_' vs. '.' vs. '-',
821 # because they're liable to clash in generated C.
822 other_ent
= self
._entity
_dict
.get(ent
.name
)
825 where
= QAPIError(other_ent
.info
, None, "previous definition")
828 "'%s' is already defined\n%s" % (ent
.name
, where
))
830 ent
.info
, "%s is already defined" % other_ent
.describe())
831 self
._entity
_dict
[ent
.name
] = ent
833 def lookup_entity(self
, name
, typ
=None):
834 ent
= self
._entity
_dict
.get(name
)
835 if typ
and not isinstance(ent
, typ
):
839 def lookup_type(self
, name
):
840 return self
.lookup_entity(name
, QAPISchemaType
)
842 def resolve_type(self
, name
, info
, what
):
843 typ
= self
.lookup_type(name
)
848 info
, "%s uses unknown type '%s'" % (what
, name
))
851 def _module_name(self
, fname
):
854 return os
.path
.relpath(fname
, self
._schema
_dir
)
856 def _make_module(self
, fname
):
857 name
= self
._module
_name
(fname
)
858 if name
not in self
._module
_dict
:
859 self
._module
_dict
[name
] = QAPISchemaModule(name
)
860 return self
._module
_dict
[name
]
862 def module_by_fname(self
, fname
):
863 name
= self
._module
_name
(fname
)
864 assert name
in self
._module
_dict
865 return self
._module
_dict
[name
]
867 def _def_include(self
, expr
, info
, doc
):
868 include
= expr
['include']
870 self
._def
_entity
(QAPISchemaInclude(self
._make
_module
(include
), info
))
872 def _def_builtin_type(self
, name
, json_type
, c_type
):
873 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
874 # Instantiating only the arrays that are actually used would
875 # be nice, but we can't as long as their generated code
876 # (qapi-builtin-types.[ch]) may be shared by some other
878 self
._make
_array
_type
(name
, None)
880 def _def_predefineds(self
):
881 for t
in [('str', 'string', 'char' + pointer_suffix
),
882 ('number', 'number', 'double'),
883 ('int', 'int', 'int64_t'),
884 ('int8', 'int', 'int8_t'),
885 ('int16', 'int', 'int16_t'),
886 ('int32', 'int', 'int32_t'),
887 ('int64', 'int', 'int64_t'),
888 ('uint8', 'int', 'uint8_t'),
889 ('uint16', 'int', 'uint16_t'),
890 ('uint32', 'int', 'uint32_t'),
891 ('uint64', 'int', 'uint64_t'),
892 ('size', 'int', 'uint64_t'),
893 ('bool', 'boolean', 'bool'),
894 ('any', 'value', 'QObject' + pointer_suffix
),
895 ('null', 'null', 'QNull' + pointer_suffix
)]:
896 self
._def
_builtin
_type
(*t
)
897 self
.the_empty_object_type
= QAPISchemaObjectType(
898 'q_empty', None, None, None, None, None, [], None)
899 self
._def
_entity
(self
.the_empty_object_type
)
901 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
903 qtype_values
= self
._make
_enum
_members
(
904 [{'name': n
} for n
in qtypes
], None)
906 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None, None,
907 qtype_values
, 'QTYPE'))
909 def _make_features(self
, expr
, info
):
910 features
= expr
.get('features', [])
911 return [QAPISchemaFeature(f
['name'], info
, f
.get('if'))
914 def _make_enum_members(self
, values
, info
):
915 return [QAPISchemaEnumMember(v
['name'], info
, v
.get('if'))
918 def _make_implicit_enum_type(self
, name
, info
, ifcond
, values
):
919 # See also QAPISchemaObjectTypeMember.describe()
920 name
= name
+ 'Kind' # reserved by check_defn_name_str()
921 self
._def
_entity
(QAPISchemaEnumType(
922 name
, info
, None, ifcond
, None,
923 self
._make
_enum
_members
(values
, info
),
927 def _make_array_type(self
, element_type
, info
):
928 name
= element_type
+ 'List' # reserved by check_defn_name_str()
929 if not self
.lookup_type(name
):
930 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
933 def _make_implicit_object_type(self
, name
, info
, ifcond
, role
, members
):
936 # See also QAPISchemaObjectTypeMember.describe()
937 name
= 'q_obj_%s-%s' % (name
, role
)
938 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
940 # The implicit object type has multiple users. This can
941 # happen only for simple unions' implicit wrapper types.
942 # Its ifcond should be the disjunction of its user's
943 # ifconds. Not implemented. Instead, we always pass the
944 # wrapped type's ifcond, which is trivially the same for all
945 # users. It's also necessary for the wrapper to compile.
946 # But it's not tight: the disjunction need not imply it. We
947 # may end up compiling useless wrapper types.
948 # TODO kill simple unions or implement the disjunction
949 assert (ifcond
or []) == typ
._ifcond
# pylint: disable=protected-access
951 self
._def
_entity
(QAPISchemaObjectType(
952 name
, info
, None, ifcond
, None, None, members
, None))
955 def _def_enum_type(self
, expr
, info
, doc
):
958 prefix
= expr
.get('prefix')
959 ifcond
= expr
.get('if')
960 features
= self
._make
_features
(expr
, info
)
961 self
._def
_entity
(QAPISchemaEnumType(
962 name
, info
, doc
, ifcond
, features
,
963 self
._make
_enum
_members
(data
, info
), prefix
))
965 def _make_member(self
, name
, typ
, ifcond
, info
):
967 if name
.startswith('*'):
970 if isinstance(typ
, list):
972 typ
= self
._make
_array
_type
(typ
[0], info
)
973 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
)
975 def _make_members(self
, data
, info
):
976 return [self
._make
_member
(key
, value
['type'], value
.get('if'), info
)
977 for (key
, value
) in data
.items()]
979 def _def_struct_type(self
, expr
, info
, doc
):
980 name
= expr
['struct']
981 base
= expr
.get('base')
983 ifcond
= expr
.get('if')
984 features
= self
._make
_features
(expr
, info
)
985 self
._def
_entity
(QAPISchemaObjectType(
986 name
, info
, doc
, ifcond
, features
, base
,
987 self
._make
_members
(data
, info
),
990 def _make_variant(self
, case
, typ
, ifcond
, info
):
991 return QAPISchemaObjectTypeVariant(case
, info
, typ
, ifcond
)
993 def _make_simple_variant(self
, case
, typ
, ifcond
, info
):
994 if isinstance(typ
, list):
996 typ
= self
._make
_array
_type
(typ
[0], info
)
997 typ
= self
._make
_implicit
_object
_type
(
998 typ
, info
, self
.lookup_type(typ
),
999 'wrapper', [self
._make
_member
('data', typ
, None, info
)])
1000 return QAPISchemaObjectTypeVariant(case
, info
, typ
, ifcond
)
1002 def _def_union_type(self
, expr
, info
, doc
):
1003 name
= expr
['union']
1005 base
= expr
.get('base')
1006 ifcond
= expr
.get('if')
1007 features
= self
._make
_features
(expr
, info
)
1008 tag_name
= expr
.get('discriminator')
1010 if isinstance(base
, dict):
1011 base
= self
._make
_implicit
_object
_type
(
1013 'base', self
._make
_members
(base
, info
))
1015 variants
= [self
._make
_variant
(key
, value
['type'],
1016 value
.get('if'), info
)
1017 for (key
, value
) in data
.items()]
1020 variants
= [self
._make
_simple
_variant
(key
, value
['type'],
1021 value
.get('if'), info
)
1022 for (key
, value
) in data
.items()]
1023 enum
= [{'name': v
.name
, 'if': v
.ifcond
} for v
in variants
]
1024 typ
= self
._make
_implicit
_enum
_type
(name
, info
, ifcond
, enum
)
1025 tag_member
= QAPISchemaObjectTypeMember('type', info
, typ
, False)
1026 members
= [tag_member
]
1028 QAPISchemaObjectType(name
, info
, doc
, ifcond
, features
,
1030 QAPISchemaObjectTypeVariants(
1031 tag_name
, info
, tag_member
, variants
)))
1033 def _def_alternate_type(self
, expr
, info
, doc
):
1034 name
= expr
['alternate']
1036 ifcond
= expr
.get('if')
1037 features
= self
._make
_features
(expr
, info
)
1038 variants
= [self
._make
_variant
(key
, value
['type'], value
.get('if'),
1040 for (key
, value
) in data
.items()]
1041 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
1043 QAPISchemaAlternateType(name
, info
, doc
, ifcond
, features
,
1044 QAPISchemaObjectTypeVariants(
1045 None, info
, tag_member
, variants
)))
1047 def _def_command(self
, expr
, info
, doc
):
1048 name
= expr
['command']
1049 data
= expr
.get('data')
1050 rets
= expr
.get('returns')
1051 gen
= expr
.get('gen', True)
1052 success_response
= expr
.get('success-response', True)
1053 boxed
= expr
.get('boxed', False)
1054 allow_oob
= expr
.get('allow-oob', False)
1055 allow_preconfig
= expr
.get('allow-preconfig', False)
1056 ifcond
= expr
.get('if')
1057 features
= self
._make
_features
(expr
, info
)
1058 if isinstance(data
, OrderedDict
):
1059 data
= self
._make
_implicit
_object
_type
(
1061 'arg', self
._make
_members
(data
, info
))
1062 if isinstance(rets
, list):
1063 assert len(rets
) == 1
1064 rets
= self
._make
_array
_type
(rets
[0], info
)
1065 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, ifcond
, features
,
1067 gen
, success_response
,
1068 boxed
, allow_oob
, allow_preconfig
))
1070 def _def_event(self
, expr
, info
, doc
):
1071 name
= expr
['event']
1072 data
= expr
.get('data')
1073 boxed
= expr
.get('boxed', False)
1074 ifcond
= expr
.get('if')
1075 features
= self
._make
_features
(expr
, info
)
1076 if isinstance(data
, OrderedDict
):
1077 data
= self
._make
_implicit
_object
_type
(
1079 'arg', self
._make
_members
(data
, info
))
1080 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, ifcond
, features
,
1083 def _def_exprs(self
, exprs
):
1084 for expr_elem
in exprs
:
1085 expr
= expr_elem
['expr']
1086 info
= expr_elem
['info']
1087 doc
= expr_elem
.get('doc')
1089 self
._def
_enum
_type
(expr
, info
, doc
)
1090 elif 'struct' in expr
:
1091 self
._def
_struct
_type
(expr
, info
, doc
)
1092 elif 'union' in expr
:
1093 self
._def
_union
_type
(expr
, info
, doc
)
1094 elif 'alternate' in expr
:
1095 self
._def
_alternate
_type
(expr
, info
, doc
)
1096 elif 'command' in expr
:
1097 self
._def
_command
(expr
, info
, doc
)
1098 elif 'event' in expr
:
1099 self
._def
_event
(expr
, info
, doc
)
1100 elif 'include' in expr
:
1101 self
._def
_include
(expr
, info
, doc
)
1106 for ent
in self
._entity
_list
:
1110 for ent
in self
._entity
_list
:
1111 ent
.set_module(self
)
1113 def visit(self
, visitor
):
1114 visitor
.visit_begin(self
)
1115 for mod
in self
._module
_dict
.values():