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 # pylint: disable=too-many-lines
17 # TODO catching name collisions in generated code would be nice
19 from abc
import ABC
, abstractmethod
20 from collections
import OrderedDict
23 from typing
import List
, Optional
33 from .error
import QAPIError
, QAPISemError
, QAPISourceError
34 from .expr
import check_exprs
35 from .parser
import QAPIExpression
, QAPISchemaParser
38 class QAPISchemaIfCond
:
39 def __init__(self
, ifcond
=None):
43 return cgen_ifcond(self
.ifcond
)
46 return gen_if(self
._cgen
())
49 return gen_endif(self
._cgen
())
52 return docgen_ifcond(self
.ifcond
)
55 return bool(self
.ifcond
)
58 class QAPISchemaEntity
:
62 This is either a directive, such as include, or a definition.
63 The latter uses sub-class `QAPISchemaDefinition`.
65 def __init__(self
, info
):
67 # For explicitly defined entities, info points to the (explicit)
68 # definition. For builtins (and their arrays), info is None.
69 # For implicitly defined entities, info points to a place that
70 # triggered the implicit definition (there may be more than one
76 return "<%s at 0x%x>" % (type(self
).__name
__, id(self
))
78 def check(self
, schema
):
79 # pylint: disable=unused-argument
82 def connect_doc(self
, doc
=None):
85 def _set_module(self
, schema
, info
):
87 fname
= info
.fname
if info
else QAPISchemaModule
.BUILTIN_MODULE_NAME
88 self
._module
= schema
.module_by_fname(fname
)
89 self
._module
.add_entity(self
)
91 def set_module(self
, schema
):
92 self
._set
_module
(schema
, self
.info
)
94 def visit(self
, visitor
):
95 # pylint: disable=unused-argument
99 class QAPISchemaDefinition(QAPISchemaEntity
):
100 meta
: Optional
[str] = None
102 def __init__(self
, name
: str, info
, doc
, ifcond
=None, features
=None):
103 assert isinstance(name
, str)
104 super().__init
__(info
)
105 for f
in features
or []:
106 assert isinstance(f
, QAPISchemaFeature
)
107 f
.set_defined_in(name
)
110 self
._ifcond
= ifcond
or QAPISchemaIfCond()
111 self
.features
= features
or []
114 return "<%s:%s at 0x%x>" % (type(self
).__name
__, self
.name
,
118 return c_name(self
.name
)
120 def check(self
, schema
):
121 assert not self
._checked
122 super().check(schema
)
124 for f
in self
.features
:
125 f
.check_clash(self
.info
, seen
)
127 def connect_doc(self
, doc
=None):
128 super().connect_doc(doc
)
129 doc
= doc
or self
.doc
131 for f
in self
.features
:
132 doc
.connect_feature(f
)
139 def is_implicit(self
):
144 return "%s '%s'" % (self
.meta
, self
.name
)
147 class QAPISchemaVisitor
:
148 def visit_begin(self
, schema
):
154 def visit_module(self
, name
):
157 def visit_needed(self
, entity
):
158 # pylint: disable=unused-argument
159 # Default to visiting everything
162 def visit_include(self
, name
, info
):
165 def visit_builtin_type(self
, name
, info
, json_type
):
168 def visit_enum_type(self
, name
, info
, ifcond
, features
, members
, prefix
):
171 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
174 def visit_object_type(self
, name
, info
, ifcond
, features
,
175 base
, members
, variants
):
178 def visit_object_type_flat(self
, name
, info
, ifcond
, features
,
182 def visit_alternate_type(self
, name
, info
, ifcond
, features
, variants
):
185 def visit_command(self
, name
, info
, ifcond
, features
,
186 arg_type
, ret_type
, gen
, success_response
, boxed
,
187 allow_oob
, allow_preconfig
, coroutine
):
190 def visit_event(self
, name
, info
, ifcond
, features
, arg_type
, boxed
):
194 class QAPISchemaModule
:
196 BUILTIN_MODULE_NAME
= './builtin'
198 def __init__(self
, name
):
200 self
._entity
_list
= []
203 def is_system_module(name
: str) -> bool:
205 System modules are internally defined modules.
207 Their names start with the "./" prefix.
209 return name
.startswith('./')
212 def is_user_module(cls
, name
: str) -> bool:
214 User modules are those defined by the user in qapi JSON files.
216 They do not start with the "./" prefix.
218 return not cls
.is_system_module(name
)
221 def is_builtin_module(cls
, name
: str) -> bool:
223 The built-in module is a single System module for the built-in types.
225 It is always "./builtin".
227 return name
== cls
.BUILTIN_MODULE_NAME
229 def add_entity(self
, ent
):
230 self
._entity
_list
.append(ent
)
232 def visit(self
, visitor
):
233 visitor
.visit_module(self
.name
)
234 for entity
in self
._entity
_list
:
235 if visitor
.visit_needed(entity
):
236 entity
.visit(visitor
)
239 class QAPISchemaInclude(QAPISchemaEntity
):
240 def __init__(self
, sub_module
, info
):
241 super().__init
__(info
)
242 self
._sub
_module
= sub_module
244 def visit(self
, visitor
):
245 super().visit(visitor
)
246 visitor
.visit_include(self
._sub
_module
.name
, self
.info
)
249 class QAPISchemaType(QAPISchemaDefinition
, ABC
):
250 # Return the C type for common use.
251 # For the types we commonly box, this is a pointer type.
256 # Return the C type to be used in a parameter list.
257 def c_param_type(self
):
260 # Return the C type to be used where we suppress boxing.
261 def c_unboxed_type(self
):
268 def alternate_qtype(self
):
270 'null': 'QTYPE_QNULL',
271 'string': 'QTYPE_QSTRING',
272 'number': 'QTYPE_QNUM',
274 'boolean': 'QTYPE_QBOOL',
275 'array': 'QTYPE_QLIST',
276 'object': 'QTYPE_QDICT'
278 return json2qtype
.get(self
.json_type())
281 if self
.is_implicit():
285 def need_has_if_optional(self
):
286 # When FOO is a pointer, has_FOO == !!FOO, i.e. has_FOO is redundant.
287 # Except for arrays; see QAPISchemaArrayType.need_has_if_optional().
288 return not self
.c_type().endswith(POINTER_SUFFIX
)
290 def check(self
, schema
):
291 super().check(schema
)
292 for feat
in self
.features
:
293 if feat
.is_special():
296 f
"feature '{feat.name}' is not supported for types")
300 return "%s type '%s'" % (self
.meta
, self
.name
)
303 class QAPISchemaBuiltinType(QAPISchemaType
):
306 def __init__(self
, name
, json_type
, c_type
):
307 super().__init
__(name
, None, None)
308 assert not c_type
or isinstance(c_type
, str)
309 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
311 self
._json
_type
_name
= json_type
312 self
._c
_type
_name
= c_type
318 return self
._c
_type
_name
320 def c_param_type(self
):
321 if self
.name
== 'str':
322 return 'const ' + self
._c
_type
_name
323 return self
._c
_type
_name
326 return self
._json
_type
_name
329 return self
.json_type()
331 def visit(self
, visitor
):
332 super().visit(visitor
)
333 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
336 class QAPISchemaEnumType(QAPISchemaType
):
339 def __init__(self
, name
, info
, doc
, ifcond
, features
, members
, prefix
):
340 super().__init
__(name
, info
, doc
, ifcond
, features
)
342 assert isinstance(m
, QAPISchemaEnumMember
)
343 m
.set_defined_in(name
)
344 assert prefix
is None or isinstance(prefix
, str)
345 self
.members
= members
348 def check(self
, schema
):
349 super().check(schema
)
351 for m
in self
.members
:
352 m
.check_clash(self
.info
, seen
)
354 def connect_doc(self
, doc
=None):
355 super().connect_doc(doc
)
356 doc
= doc
or self
.doc
357 for m
in self
.members
:
360 def is_implicit(self
):
361 # See QAPISchema._def_predefineds()
362 return self
.name
== 'QType'
365 return c_name(self
.name
)
367 def member_names(self
):
368 return [m
.name
for m
in self
.members
]
373 def visit(self
, visitor
):
374 super().visit(visitor
)
375 visitor
.visit_enum_type(
376 self
.name
, self
.info
, self
.ifcond
, self
.features
,
377 self
.members
, self
.prefix
)
380 class QAPISchemaArrayType(QAPISchemaType
):
383 def __init__(self
, name
, info
, element_type
):
384 super().__init
__(name
, info
, None)
385 assert isinstance(element_type
, str)
386 self
._element
_type
_name
= element_type
387 self
.element_type
: QAPISchemaType
389 def need_has_if_optional(self
):
390 # When FOO is an array, we still need has_FOO to distinguish
391 # absent (!has_FOO) from present and empty (has_FOO && !FOO).
394 def check(self
, schema
):
395 super().check(schema
)
396 self
.element_type
= schema
.resolve_type(
397 self
._element
_type
_name
, self
.info
,
398 self
.info
and self
.info
.defn_meta
)
399 assert not isinstance(self
.element_type
, QAPISchemaArrayType
)
401 def set_module(self
, schema
):
402 self
._set
_module
(schema
, self
.element_type
.info
)
407 return self
.element_type
.ifcond
409 def is_implicit(self
):
413 return c_name(self
.name
) + POINTER_SUFFIX
419 elt_doc_type
= self
.element_type
.doc_type()
422 return 'array of ' + elt_doc_type
424 def visit(self
, visitor
):
425 super().visit(visitor
)
426 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
431 return "%s type ['%s']" % (self
.meta
, self
._element
_type
_name
)
434 class QAPISchemaObjectType(QAPISchemaType
):
435 def __init__(self
, name
, info
, doc
, ifcond
, features
,
436 base
, local_members
, variants
):
437 # struct has local_members, optional base, and no variants
438 # union has base, variants, and no local_members
439 super().__init
__(name
, info
, doc
, ifcond
, features
)
440 self
.meta
= 'union' if variants
else 'struct'
441 assert base
is None or isinstance(base
, str)
442 for m
in local_members
:
443 assert isinstance(m
, QAPISchemaObjectTypeMember
)
444 m
.set_defined_in(name
)
445 if variants
is not None:
446 assert isinstance(variants
, QAPISchemaVariants
)
447 variants
.set_defined_in(name
)
448 self
._base
_name
= base
450 self
.local_members
= local_members
451 self
.variants
= variants
454 def check(self
, schema
):
455 # This calls another type T's .check() exactly when the C
456 # struct emitted by gen_object() contains that T's C struct
457 # (pointers don't count).
458 if self
.members
is not None:
459 # A previous .check() completed: nothing to do
462 # Recursed: C struct contains itself
463 raise QAPISemError(self
.info
,
464 "object %s contains itself" % self
.name
)
466 super().check(schema
)
467 assert self
._checked
and self
.members
is None
471 self
.base
= schema
.resolve_type(self
._base
_name
, self
.info
,
473 if (not isinstance(self
.base
, QAPISchemaObjectType
)
474 or self
.base
.variants
):
477 "'base' requires a struct type, %s isn't"
478 % self
.base
.describe())
479 self
.base
.check(schema
)
480 self
.base
.check_clash(self
.info
, seen
)
481 for m
in self
.local_members
:
483 m
.check_clash(self
.info
, seen
)
484 members
= seen
.values()
487 self
.variants
.check(schema
, seen
)
488 self
.variants
.check_clash(self
.info
, seen
)
490 self
.members
= members
# mark completed
492 # Check that the members of this type do not cause duplicate JSON members,
493 # and update seen to track the members seen so far. Report any errors
494 # on behalf of info, which is not necessarily self.info
495 def check_clash(self
, info
, seen
):
497 for m
in self
.members
:
498 m
.check_clash(info
, seen
)
500 self
.variants
.check_clash(info
, seen
)
502 def connect_doc(self
, doc
=None):
503 super().connect_doc(doc
)
504 doc
= doc
or self
.doc
505 if self
.base
and self
.base
.is_implicit():
506 self
.base
.connect_doc(doc
)
507 for m
in self
.local_members
:
510 def is_implicit(self
):
511 # See QAPISchema._make_implicit_object_type(), as well as
513 return self
.name
.startswith('q_')
516 assert self
.members
is not None
517 return not self
.members
and not self
.variants
519 def has_conditional_members(self
):
520 assert self
.members
is not None
521 return any(m
.ifcond
.is_present() for m
in self
.members
)
524 assert self
.name
!= 'q_empty'
525 return super().c_name()
528 assert not self
.is_implicit()
529 return c_name(self
.name
) + POINTER_SUFFIX
531 def c_unboxed_type(self
):
532 return c_name(self
.name
)
537 def visit(self
, visitor
):
538 super().visit(visitor
)
539 visitor
.visit_object_type(
540 self
.name
, self
.info
, self
.ifcond
, self
.features
,
541 self
.base
, self
.local_members
, self
.variants
)
542 visitor
.visit_object_type_flat(
543 self
.name
, self
.info
, self
.ifcond
, self
.features
,
544 self
.members
, self
.variants
)
547 class QAPISchemaAlternateType(QAPISchemaType
):
550 def __init__(self
, name
, info
, doc
, ifcond
, features
, variants
):
551 super().__init
__(name
, info
, doc
, ifcond
, features
)
552 assert isinstance(variants
, QAPISchemaVariants
)
553 assert variants
.tag_member
554 variants
.set_defined_in(name
)
555 variants
.tag_member
.set_defined_in(self
.name
)
556 self
.variants
= variants
558 def check(self
, schema
):
559 super().check(schema
)
560 self
.variants
.tag_member
.check(schema
)
561 # Not calling self.variants.check_clash(), because there's nothing
563 self
.variants
.check(schema
, {})
564 # Alternate branch names have no relation to the tag enum values;
565 # so we have to check for potential name collisions ourselves.
568 for v
in self
.variants
.variants
:
569 v
.check_clash(self
.info
, seen
)
570 qtype
= v
.type.alternate_qtype()
575 % (v
.describe(self
.info
), v
.type.describe()))
576 conflicting
= set([qtype
])
577 if qtype
== 'QTYPE_QSTRING':
578 if isinstance(v
.type, QAPISchemaEnumType
):
579 for m
in v
.type.members
:
580 if m
.name
in ['on', 'off']:
581 conflicting
.add('QTYPE_QBOOL')
582 if re
.match(r
'[-+0-9.]', m
.name
):
583 # lazy, could be tightened
584 conflicting
.add('QTYPE_QNUM')
586 conflicting
.add('QTYPE_QNUM')
587 conflicting
.add('QTYPE_QBOOL')
588 for qt
in conflicting
:
592 "%s can't be distinguished from '%s'"
593 % (v
.describe(self
.info
), types_seen
[qt
]))
594 types_seen
[qt
] = v
.name
596 def connect_doc(self
, doc
=None):
597 super().connect_doc(doc
)
598 doc
= doc
or self
.doc
599 for v
in self
.variants
.variants
:
603 return c_name(self
.name
) + POINTER_SUFFIX
608 def visit(self
, visitor
):
609 super().visit(visitor
)
610 visitor
.visit_alternate_type(
611 self
.name
, self
.info
, self
.ifcond
, self
.features
, self
.variants
)
614 class QAPISchemaVariants
:
615 def __init__(self
, tag_name
, info
, tag_member
, variants
):
616 # Unions pass tag_name but not tag_member.
617 # Alternates pass tag_member but not tag_name.
618 # After check(), tag_member is always set.
619 assert bool(tag_member
) != bool(tag_name
)
620 assert (isinstance(tag_name
, str) or
621 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
623 assert isinstance(v
, QAPISchemaVariant
)
624 self
._tag
_name
= tag_name
626 self
.tag_member
= tag_member
627 self
.variants
= variants
629 def set_defined_in(self
, name
):
630 for v
in self
.variants
:
631 v
.set_defined_in(name
)
633 def check(self
, schema
, seen
):
634 if self
._tag
_name
: # union
635 self
.tag_member
= seen
.get(c_name(self
._tag
_name
))
637 # Pointing to the base type when not implicit would be
638 # nice, but we don't know it here
639 if not self
.tag_member
or self
._tag
_name
!= self
.tag_member
.name
:
642 "discriminator '%s' is not a member of %s"
643 % (self
._tag
_name
, base
))
645 base_type
= schema
.lookup_type(self
.tag_member
.defined_in
)
647 if not base_type
.is_implicit():
648 base
= "base type '%s'" % self
.tag_member
.defined_in
649 if not isinstance(self
.tag_member
.type, QAPISchemaEnumType
):
652 "discriminator member '%s' of %s must be of enum type"
653 % (self
._tag
_name
, base
))
654 if self
.tag_member
.optional
:
657 "discriminator member '%s' of %s must not be optional"
658 % (self
._tag
_name
, base
))
659 if self
.tag_member
.ifcond
.is_present():
662 "discriminator member '%s' of %s must not be conditional"
663 % (self
._tag
_name
, base
))
665 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
666 assert not self
.tag_member
.optional
667 assert not self
.tag_member
.ifcond
.is_present()
668 if self
._tag
_name
: # union
669 # branches that are not explicitly covered get an empty type
670 cases
= {v
.name
for v
in self
.variants
}
671 for m
in self
.tag_member
.type.members
:
672 if m
.name
not in cases
:
673 v
= QAPISchemaVariant(m
.name
, self
.info
,
675 v
.set_defined_in(self
.tag_member
.defined_in
)
676 self
.variants
.append(v
)
677 if not self
.variants
:
678 raise QAPISemError(self
.info
, "union has no branches")
679 for v
in self
.variants
:
681 # Union names must match enum values; alternate names are
682 # checked separately. Use 'seen' to tell the two apart.
684 if v
.name
not in self
.tag_member
.type.member_names():
687 "branch '%s' is not a value of %s"
688 % (v
.name
, self
.tag_member
.type.describe()))
689 if not isinstance(v
.type, QAPISchemaObjectType
):
693 % (v
.describe(self
.info
), v
.type.describe()))
696 def check_clash(self
, info
, seen
):
697 for v
in self
.variants
:
698 # Reset seen map for each variant, since qapi names from one
699 # branch do not affect another branch
700 v
.type.check_clash(info
, dict(seen
))
703 class QAPISchemaMember
:
704 """ Represents object members, enum members and features """
707 def __init__(self
, name
, info
, ifcond
=None):
708 assert isinstance(name
, str)
711 self
.ifcond
= ifcond
or QAPISchemaIfCond()
712 self
.defined_in
= None
714 def set_defined_in(self
, name
):
715 assert not self
.defined_in
716 self
.defined_in
= name
718 def check_clash(self
, info
, seen
):
719 cname
= c_name(self
.name
)
723 "%s collides with %s"
724 % (self
.describe(info
), seen
[cname
].describe(info
)))
727 def connect_doc(self
, doc
):
729 doc
.connect_member(self
)
731 def describe(self
, info
):
734 defined_in
= self
.defined_in
737 if defined_in
.startswith('q_obj_'):
738 # See QAPISchema._make_implicit_object_type() - reverse the
739 # mapping there to create a nice human-readable description
740 defined_in
= defined_in
[6:]
741 if defined_in
.endswith('-arg'):
742 # Implicit type created for a command's dict 'data'
743 assert role
== 'member'
746 defined_in
= defined_in
[:-4]
747 elif defined_in
.endswith('-base'):
748 # Implicit type created for a union's dict 'base'
749 role
= 'base ' + role
750 defined_in
= defined_in
[:-5]
754 if defined_in
!= info
.defn_name
:
755 return "%s '%s' of %s '%s'" % (role
, self
.name
, meta
, defined_in
)
756 return "%s '%s'" % (role
, self
.name
)
759 class QAPISchemaEnumMember(QAPISchemaMember
):
762 def __init__(self
, name
, info
, ifcond
=None, features
=None):
763 super().__init
__(name
, info
, ifcond
)
764 for f
in features
or []:
765 assert isinstance(f
, QAPISchemaFeature
)
766 f
.set_defined_in(name
)
767 self
.features
= features
or []
769 def connect_doc(self
, doc
):
770 super().connect_doc(doc
)
772 for f
in self
.features
:
773 doc
.connect_feature(f
)
776 class QAPISchemaFeature(QAPISchemaMember
):
779 def is_special(self
):
780 return self
.name
in ('deprecated', 'unstable')
783 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
784 def __init__(self
, name
, info
, typ
, optional
, ifcond
=None, features
=None):
785 super().__init
__(name
, info
, ifcond
)
786 assert isinstance(typ
, str)
787 assert isinstance(optional
, bool)
788 for f
in features
or []:
789 assert isinstance(f
, QAPISchemaFeature
)
790 f
.set_defined_in(name
)
791 self
._type
_name
= typ
792 self
.type: QAPISchemaType
# set during check()
793 self
.optional
= optional
794 self
.features
= features
or []
798 return self
.optional
and self
.type.need_has_if_optional()
800 def check(self
, schema
):
801 assert self
.defined_in
802 self
.type = schema
.resolve_type(self
._type
_name
, self
.info
,
805 for f
in self
.features
:
806 f
.check_clash(self
.info
, seen
)
808 def connect_doc(self
, doc
):
809 super().connect_doc(doc
)
811 for f
in self
.features
:
812 doc
.connect_feature(f
)
815 class QAPISchemaVariant(QAPISchemaObjectTypeMember
):
818 def __init__(self
, name
, info
, typ
, ifcond
=None):
819 super().__init
__(name
, info
, typ
, False, ifcond
)
822 class QAPISchemaCommand(QAPISchemaDefinition
):
825 def __init__(self
, name
, info
, doc
, ifcond
, features
,
827 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
,
829 super().__init
__(name
, info
, doc
, ifcond
, features
)
830 assert not arg_type
or isinstance(arg_type
, str)
831 assert not ret_type
or isinstance(ret_type
, str)
832 self
._arg
_type
_name
= arg_type
834 self
._ret
_type
_name
= ret_type
837 self
.success_response
= success_response
839 self
.allow_oob
= allow_oob
840 self
.allow_preconfig
= allow_preconfig
841 self
.coroutine
= coroutine
843 def check(self
, schema
):
844 super().check(schema
)
845 if self
._arg
_type
_name
:
846 arg_type
= schema
.resolve_type(
847 self
._arg
_type
_name
, self
.info
, "command's 'data'")
848 if not isinstance(arg_type
, QAPISchemaObjectType
):
851 "command's 'data' cannot take %s"
852 % arg_type
.describe())
853 self
.arg_type
= arg_type
854 if self
.arg_type
.variants
and not self
.boxed
:
857 "command's 'data' can take %s only with 'boxed': true"
858 % self
.arg_type
.describe())
859 self
.arg_type
.check(schema
)
860 if self
.arg_type
.has_conditional_members() and not self
.boxed
:
863 "conditional command arguments require 'boxed': true")
864 if self
._ret
_type
_name
:
865 self
.ret_type
= schema
.resolve_type(
866 self
._ret
_type
_name
, self
.info
, "command's 'returns'")
867 if self
.name
not in self
.info
.pragma
.command_returns_exceptions
:
869 if isinstance(typ
, QAPISchemaArrayType
):
871 typ
= typ
.element_type
872 if not isinstance(typ
, QAPISchemaObjectType
):
875 "command's 'returns' cannot take %s"
876 % self
.ret_type
.describe())
878 def connect_doc(self
, doc
=None):
879 super().connect_doc(doc
)
880 doc
= doc
or self
.doc
882 if self
.arg_type
and self
.arg_type
.is_implicit():
883 self
.arg_type
.connect_doc(doc
)
885 def visit(self
, visitor
):
886 super().visit(visitor
)
887 visitor
.visit_command(
888 self
.name
, self
.info
, self
.ifcond
, self
.features
,
889 self
.arg_type
, self
.ret_type
, self
.gen
, self
.success_response
,
890 self
.boxed
, self
.allow_oob
, self
.allow_preconfig
,
894 class QAPISchemaEvent(QAPISchemaDefinition
):
897 def __init__(self
, name
, info
, doc
, ifcond
, features
, arg_type
, boxed
):
898 super().__init
__(name
, info
, doc
, ifcond
, features
)
899 assert not arg_type
or isinstance(arg_type
, str)
900 self
._arg
_type
_name
= arg_type
904 def check(self
, schema
):
905 super().check(schema
)
906 if self
._arg
_type
_name
:
907 typ
= schema
.resolve_type(
908 self
._arg
_type
_name
, self
.info
, "event's 'data'")
909 if not isinstance(typ
, QAPISchemaObjectType
):
912 "event's 'data' cannot take %s"
915 if self
.arg_type
.variants
and not self
.boxed
:
918 "event's 'data' can take %s only with 'boxed': true"
919 % self
.arg_type
.describe())
920 self
.arg_type
.check(schema
)
921 if self
.arg_type
.has_conditional_members() and not self
.boxed
:
924 "conditional event arguments require 'boxed': true")
926 def connect_doc(self
, doc
=None):
927 super().connect_doc(doc
)
928 doc
= doc
or self
.doc
930 if self
.arg_type
and self
.arg_type
.is_implicit():
931 self
.arg_type
.connect_doc(doc
)
933 def visit(self
, visitor
):
934 super().visit(visitor
)
936 self
.name
, self
.info
, self
.ifcond
, self
.features
,
937 self
.arg_type
, self
.boxed
)
941 def __init__(self
, fname
):
945 parser
= QAPISchemaParser(fname
)
946 except OSError as err
:
948 f
"can't read schema file '{fname}': {err.strerror}"
951 exprs
= check_exprs(parser
.exprs
)
952 self
.docs
= parser
.docs
953 self
._entity
_list
= []
954 self
._entity
_dict
= {}
955 self
._module
_dict
= OrderedDict()
956 self
._schema
_dir
= os
.path
.dirname(fname
)
957 self
._make
_module
(QAPISchemaModule
.BUILTIN_MODULE_NAME
)
958 self
._make
_module
(fname
)
959 self
._predefining
= True
960 self
._def
_predefineds
()
961 self
._predefining
= False
962 self
._def
_exprs
(exprs
)
965 def _def_entity(self
, ent
):
966 self
._entity
_list
.append(ent
)
968 def _def_definition(self
, defn
):
969 # Only the predefined types are allowed to not have info
970 assert defn
.info
or self
._predefining
971 self
._def
_entity
(defn
)
972 # TODO reject names that differ only in '_' vs. '.' vs. '-',
973 # because they're liable to clash in generated C.
974 other_defn
= self
._entity
_dict
.get(defn
.name
)
977 where
= QAPISourceError(other_defn
.info
, "previous definition")
980 "'%s' is already defined\n%s" % (defn
.name
, where
))
982 defn
.info
, "%s is already defined" % other_defn
.describe())
983 self
._entity
_dict
[defn
.name
] = defn
985 def lookup_entity(self
, name
, typ
=None):
986 ent
= self
._entity
_dict
.get(name
)
987 if typ
and not isinstance(ent
, typ
):
991 def lookup_type(self
, name
):
992 return self
.lookup_entity(name
, QAPISchemaType
)
994 def resolve_type(self
, name
, info
, what
):
995 typ
= self
.lookup_type(name
)
1000 info
, "%s uses unknown type '%s'" % (what
, name
))
1003 def _module_name(self
, fname
: str) -> str:
1004 if QAPISchemaModule
.is_system_module(fname
):
1006 return os
.path
.relpath(fname
, self
._schema
_dir
)
1008 def _make_module(self
, fname
):
1009 name
= self
._module
_name
(fname
)
1010 if name
not in self
._module
_dict
:
1011 self
._module
_dict
[name
] = QAPISchemaModule(name
)
1012 return self
._module
_dict
[name
]
1014 def module_by_fname(self
, fname
):
1015 name
= self
._module
_name
(fname
)
1016 return self
._module
_dict
[name
]
1018 def _def_include(self
, expr
: QAPIExpression
):
1019 include
= expr
['include']
1020 assert expr
.doc
is None
1022 QAPISchemaInclude(self
._make
_module
(include
), expr
.info
))
1024 def _def_builtin_type(self
, name
, json_type
, c_type
):
1025 self
._def
_definition
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1026 # Instantiating only the arrays that are actually used would
1027 # be nice, but we can't as long as their generated code
1028 # (qapi-builtin-types.[ch]) may be shared by some other
1030 self
._make
_array
_type
(name
, None)
1032 def _def_predefineds(self
):
1033 for t
in [('str', 'string', 'char' + POINTER_SUFFIX
),
1034 ('number', 'number', 'double'),
1035 ('int', 'int', 'int64_t'),
1036 ('int8', 'int', 'int8_t'),
1037 ('int16', 'int', 'int16_t'),
1038 ('int32', 'int', 'int32_t'),
1039 ('int64', 'int', 'int64_t'),
1040 ('uint8', 'int', 'uint8_t'),
1041 ('uint16', 'int', 'uint16_t'),
1042 ('uint32', 'int', 'uint32_t'),
1043 ('uint64', 'int', 'uint64_t'),
1044 ('size', 'int', 'uint64_t'),
1045 ('bool', 'boolean', 'bool'),
1046 ('any', 'value', 'QObject' + POINTER_SUFFIX
),
1047 ('null', 'null', 'QNull' + POINTER_SUFFIX
)]:
1048 self
._def
_builtin
_type
(*t
)
1049 self
.the_empty_object_type
= QAPISchemaObjectType(
1050 'q_empty', None, None, None, None, None, [], None)
1051 self
._def
_definition
(self
.the_empty_object_type
)
1053 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1055 qtype_values
= self
._make
_enum
_members
(
1056 [{'name': n
} for n
in qtypes
], None)
1058 self
._def
_definition
(QAPISchemaEnumType(
1059 'QType', None, None, None, None, qtype_values
, 'QTYPE'))
1061 def _make_features(self
, features
, info
):
1062 if features
is None:
1064 return [QAPISchemaFeature(f
['name'], info
,
1065 QAPISchemaIfCond(f
.get('if')))
1068 def _make_enum_member(self
, name
, ifcond
, features
, info
):
1069 return QAPISchemaEnumMember(name
, info
,
1070 QAPISchemaIfCond(ifcond
),
1071 self
._make
_features
(features
, info
))
1073 def _make_enum_members(self
, values
, info
):
1074 return [self
._make
_enum
_member
(v
['name'], v
.get('if'),
1075 v
.get('features'), info
)
1078 def _make_array_type(self
, element_type
, info
):
1079 name
= element_type
+ 'List' # reserved by check_defn_name_str()
1080 if not self
.lookup_type(name
):
1081 self
._def
_definition
(QAPISchemaArrayType(
1082 name
, info
, element_type
))
1085 def _make_implicit_object_type(self
, name
, info
, ifcond
, role
, members
):
1088 # See also QAPISchemaObjectTypeMember.describe()
1089 name
= 'q_obj_%s-%s' % (name
, role
)
1090 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
1092 # The implicit object type has multiple users. This can
1093 # only be a duplicate definition, which will be flagged
1097 self
._def
_definition
(QAPISchemaObjectType(
1098 name
, info
, None, ifcond
, None, None, members
, None))
1101 def _def_enum_type(self
, expr
: QAPIExpression
):
1104 prefix
= expr
.get('prefix')
1105 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1107 features
= self
._make
_features
(expr
.get('features'), info
)
1108 self
._def
_definition
(QAPISchemaEnumType(
1109 name
, info
, expr
.doc
, ifcond
, features
,
1110 self
._make
_enum
_members
(data
, info
), prefix
))
1112 def _make_member(self
, name
, typ
, ifcond
, features
, info
):
1114 if name
.startswith('*'):
1117 if isinstance(typ
, list):
1118 assert len(typ
) == 1
1119 typ
= self
._make
_array
_type
(typ
[0], info
)
1120 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
,
1121 self
._make
_features
(features
, info
))
1123 def _make_members(self
, data
, info
):
1124 return [self
._make
_member
(key
, value
['type'],
1125 QAPISchemaIfCond(value
.get('if')),
1126 value
.get('features'), info
)
1127 for (key
, value
) in data
.items()]
1129 def _def_struct_type(self
, expr
: QAPIExpression
):
1130 name
= expr
['struct']
1131 base
= expr
.get('base')
1134 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1135 features
= self
._make
_features
(expr
.get('features'), info
)
1136 self
._def
_definition
(QAPISchemaObjectType(
1137 name
, info
, expr
.doc
, ifcond
, features
, base
,
1138 self
._make
_members
(data
, info
),
1141 def _make_variant(self
, case
, typ
, ifcond
, info
):
1142 if isinstance(typ
, list):
1143 assert len(typ
) == 1
1144 typ
= self
._make
_array
_type
(typ
[0], info
)
1145 return QAPISchemaVariant(case
, info
, typ
, ifcond
)
1147 def _def_union_type(self
, expr
: QAPIExpression
):
1148 name
= expr
['union']
1150 tag_name
= expr
['discriminator']
1152 assert isinstance(data
, dict)
1154 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1155 features
= self
._make
_features
(expr
.get('features'), info
)
1156 if isinstance(base
, dict):
1157 base
= self
._make
_implicit
_object
_type
(
1159 'base', self
._make
_members
(base
, info
))
1161 self
._make
_variant
(key
, value
['type'],
1162 QAPISchemaIfCond(value
.get('if')),
1164 for (key
, value
) in data
.items()]
1165 members
: List
[QAPISchemaObjectTypeMember
] = []
1166 self
._def
_definition
(
1167 QAPISchemaObjectType(name
, info
, expr
.doc
, ifcond
, features
,
1170 tag_name
, info
, None, variants
)))
1172 def _def_alternate_type(self
, expr
: QAPIExpression
):
1173 name
= expr
['alternate']
1175 assert isinstance(data
, dict)
1176 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1178 features
= self
._make
_features
(expr
.get('features'), info
)
1180 self
._make
_variant
(key
, value
['type'],
1181 QAPISchemaIfCond(value
.get('if')),
1183 for (key
, value
) in data
.items()]
1184 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
1185 self
._def
_definition
(
1186 QAPISchemaAlternateType(
1187 name
, info
, expr
.doc
, ifcond
, features
,
1188 QAPISchemaVariants(None, info
, tag_member
, variants
)))
1190 def _def_command(self
, expr
: QAPIExpression
):
1191 name
= expr
['command']
1192 data
= expr
.get('data')
1193 rets
= expr
.get('returns')
1194 gen
= expr
.get('gen', True)
1195 success_response
= expr
.get('success-response', True)
1196 boxed
= expr
.get('boxed', False)
1197 allow_oob
= expr
.get('allow-oob', False)
1198 allow_preconfig
= expr
.get('allow-preconfig', False)
1199 coroutine
= expr
.get('coroutine', False)
1200 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1202 features
= self
._make
_features
(expr
.get('features'), info
)
1203 if isinstance(data
, OrderedDict
):
1204 data
= self
._make
_implicit
_object
_type
(
1206 'arg', self
._make
_members
(data
, info
))
1207 if isinstance(rets
, list):
1208 assert len(rets
) == 1
1209 rets
= self
._make
_array
_type
(rets
[0], info
)
1210 self
._def
_definition
(
1211 QAPISchemaCommand(name
, info
, expr
.doc
, ifcond
, features
, data
,
1212 rets
, gen
, success_response
, boxed
, allow_oob
,
1213 allow_preconfig
, coroutine
))
1215 def _def_event(self
, expr
: QAPIExpression
):
1216 name
= expr
['event']
1217 data
= expr
.get('data')
1218 boxed
= expr
.get('boxed', False)
1219 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1221 features
= self
._make
_features
(expr
.get('features'), info
)
1222 if isinstance(data
, OrderedDict
):
1223 data
= self
._make
_implicit
_object
_type
(
1225 'arg', self
._make
_members
(data
, info
))
1226 self
._def
_definition
(QAPISchemaEvent(name
, info
, expr
.doc
, ifcond
,
1227 features
, data
, boxed
))
1229 def _def_exprs(self
, exprs
):
1232 self
._def
_enum
_type
(expr
)
1233 elif 'struct' in expr
:
1234 self
._def
_struct
_type
(expr
)
1235 elif 'union' in expr
:
1236 self
._def
_union
_type
(expr
)
1237 elif 'alternate' in expr
:
1238 self
._def
_alternate
_type
(expr
)
1239 elif 'command' in expr
:
1240 self
._def
_command
(expr
)
1241 elif 'event' in expr
:
1242 self
._def
_event
(expr
)
1243 elif 'include' in expr
:
1244 self
._def
_include
(expr
)
1249 for ent
in self
._entity
_list
:
1252 for ent
in self
._entity
_list
:
1253 ent
.set_module(self
)
1254 for doc
in self
.docs
:
1257 def visit(self
, visitor
):
1258 visitor
.visit_begin(self
)
1259 for mod
in self
._module
_dict
.values():