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 __future__
import annotations
21 from abc
import ABC
, abstractmethod
22 from collections
import OrderedDict
43 from .error
import QAPIError
, QAPISemError
, QAPISourceError
44 from .expr
import check_exprs
45 from .parser
import QAPIDoc
, QAPIExpression
, QAPISchemaParser
46 from .source
import QAPISourceInfo
49 class QAPISchemaIfCond
:
52 ifcond
: Optional
[Union
[str, Dict
[str, object]]] = None,
56 def _cgen(self
) -> str:
57 return cgen_ifcond(self
.ifcond
)
59 def gen_if(self
) -> str:
60 return gen_if(self
._cgen
())
62 def gen_endif(self
) -> str:
63 return gen_endif(self
._cgen
())
65 def docgen(self
) -> str:
66 return docgen_ifcond(self
.ifcond
)
68 def is_present(self
) -> bool:
69 return bool(self
.ifcond
)
72 class QAPISchemaEntity
:
76 This is either a directive, such as include, or a definition.
77 The latter uses sub-class `QAPISchemaDefinition`.
79 def __init__(self
, info
: Optional
[QAPISourceInfo
]):
80 self
._module
: Optional
[QAPISchemaModule
] = None
81 # For explicitly defined entities, info points to the (explicit)
82 # definition. For builtins (and their arrays), info is None.
83 # For implicitly defined entities, info points to a place that
84 # triggered the implicit definition (there may be more than one
89 def __repr__(self
) -> str:
90 return "<%s at 0x%x>" % (type(self
).__name
__, id(self
))
92 def check(self
, schema
: QAPISchema
) -> None:
93 # pylint: disable=unused-argument
96 def connect_doc(self
, doc
: Optional
[QAPIDoc
] = None) -> None:
100 self
, schema
: QAPISchema
, info
: Optional
[QAPISourceInfo
]
103 fname
= info
.fname
if info
else QAPISchemaModule
.BUILTIN_MODULE_NAME
104 self
._module
= schema
.module_by_fname(fname
)
105 self
._module
.add_entity(self
)
107 def set_module(self
, schema
: QAPISchema
) -> None:
108 self
._set
_module
(schema
, self
.info
)
110 def visit(self
, visitor
: QAPISchemaVisitor
) -> None:
111 # pylint: disable=unused-argument
115 class QAPISchemaDefinition(QAPISchemaEntity
):
121 info
: Optional
[QAPISourceInfo
],
122 doc
: Optional
[QAPIDoc
],
123 ifcond
: Optional
[QAPISchemaIfCond
] = None,
124 features
: Optional
[List
[QAPISchemaFeature
]] = None,
126 super().__init
__(info
)
127 for f
in features
or []:
128 f
.set_defined_in(name
)
131 self
._ifcond
= ifcond
or QAPISchemaIfCond()
132 self
.features
= features
or []
134 def __repr__(self
) -> str:
135 return "<%s:%s at 0x%x>" % (type(self
).__name
__, self
.name
,
138 def c_name(self
) -> str:
139 return c_name(self
.name
)
141 def check(self
, schema
: QAPISchema
) -> None:
142 assert not self
._checked
143 super().check(schema
)
144 seen
: Dict
[str, QAPISchemaMember
] = {}
145 for f
in self
.features
:
146 f
.check_clash(self
.info
, seen
)
148 def connect_doc(self
, doc
: Optional
[QAPIDoc
] = None) -> None:
149 super().connect_doc(doc
)
150 doc
= doc
or self
.doc
152 for f
in self
.features
:
153 doc
.connect_feature(f
)
156 def ifcond(self
) -> QAPISchemaIfCond
:
160 def is_implicit(self
) -> bool:
163 def describe(self
) -> str:
164 return "%s '%s'" % (self
.meta
, self
.name
)
167 class QAPISchemaVisitor
:
168 def visit_begin(self
, schema
: QAPISchema
) -> None:
171 def visit_end(self
) -> None:
174 def visit_module(self
, name
: str) -> None:
177 def visit_needed(self
, entity
: QAPISchemaEntity
) -> bool:
178 # pylint: disable=unused-argument
179 # Default to visiting everything
182 def visit_include(self
, name
: str, info
: Optional
[QAPISourceInfo
]) -> None:
185 def visit_builtin_type(
186 self
, name
: str, info
: Optional
[QAPISourceInfo
], json_type
: str
193 info
: Optional
[QAPISourceInfo
],
194 ifcond
: QAPISchemaIfCond
,
195 features
: List
[QAPISchemaFeature
],
196 members
: List
[QAPISchemaEnumMember
],
197 prefix
: Optional
[str],
201 def visit_array_type(
204 info
: Optional
[QAPISourceInfo
],
205 ifcond
: QAPISchemaIfCond
,
206 element_type
: QAPISchemaType
,
210 def visit_object_type(
213 info
: Optional
[QAPISourceInfo
],
214 ifcond
: QAPISchemaIfCond
,
215 features
: List
[QAPISchemaFeature
],
216 base
: Optional
[QAPISchemaObjectType
],
217 members
: List
[QAPISchemaObjectTypeMember
],
218 variants
: Optional
[QAPISchemaBranches
],
222 def visit_object_type_flat(
225 info
: Optional
[QAPISourceInfo
],
226 ifcond
: QAPISchemaIfCond
,
227 features
: List
[QAPISchemaFeature
],
228 members
: List
[QAPISchemaObjectTypeMember
],
229 variants
: Optional
[QAPISchemaBranches
],
233 def visit_alternate_type(
236 info
: Optional
[QAPISourceInfo
],
237 ifcond
: QAPISchemaIfCond
,
238 features
: List
[QAPISchemaFeature
],
239 variants
: QAPISchemaAlternatives
,
246 info
: Optional
[QAPISourceInfo
],
247 ifcond
: QAPISchemaIfCond
,
248 features
: List
[QAPISchemaFeature
],
249 arg_type
: Optional
[QAPISchemaObjectType
],
250 ret_type
: Optional
[QAPISchemaType
],
252 success_response
: bool,
255 allow_preconfig
: bool,
263 info
: Optional
[QAPISourceInfo
],
264 ifcond
: QAPISchemaIfCond
,
265 features
: List
[QAPISchemaFeature
],
266 arg_type
: Optional
[QAPISchemaObjectType
],
272 class QAPISchemaModule
:
274 BUILTIN_MODULE_NAME
= './builtin'
276 def __init__(self
, name
: str):
278 self
._entity
_list
: List
[QAPISchemaEntity
] = []
281 def is_system_module(name
: str) -> bool:
283 System modules are internally defined modules.
285 Their names start with the "./" prefix.
287 return name
.startswith('./')
290 def is_user_module(cls
, name
: str) -> bool:
292 User modules are those defined by the user in qapi JSON files.
294 They do not start with the "./" prefix.
296 return not cls
.is_system_module(name
)
299 def is_builtin_module(cls
, name
: str) -> bool:
301 The built-in module is a single System module for the built-in types.
303 It is always "./builtin".
305 return name
== cls
.BUILTIN_MODULE_NAME
307 def add_entity(self
, ent
: QAPISchemaEntity
) -> None:
308 self
._entity
_list
.append(ent
)
310 def visit(self
, visitor
: QAPISchemaVisitor
) -> None:
311 visitor
.visit_module(self
.name
)
312 for entity
in self
._entity
_list
:
313 if visitor
.visit_needed(entity
):
314 entity
.visit(visitor
)
317 class QAPISchemaInclude(QAPISchemaEntity
):
318 def __init__(self
, sub_module
: QAPISchemaModule
, info
: QAPISourceInfo
):
319 super().__init
__(info
)
320 self
._sub
_module
= sub_module
322 def visit(self
, visitor
: QAPISchemaVisitor
) -> None:
323 super().visit(visitor
)
324 visitor
.visit_include(self
._sub
_module
.name
, self
.info
)
327 class QAPISchemaType(QAPISchemaDefinition
, ABC
):
328 # Return the C type for common use.
329 # For the types we commonly box, this is a pointer type.
331 def c_type(self
) -> str:
334 # Return the C type to be used in a parameter list.
335 def c_param_type(self
) -> str:
338 # Return the C type to be used where we suppress boxing.
339 def c_unboxed_type(self
) -> str:
343 def json_type(self
) -> str:
346 def alternate_qtype(self
) -> Optional
[str]:
348 'null': 'QTYPE_QNULL',
349 'string': 'QTYPE_QSTRING',
350 'number': 'QTYPE_QNUM',
352 'boolean': 'QTYPE_QBOOL',
353 'array': 'QTYPE_QLIST',
354 'object': 'QTYPE_QDICT'
356 return json2qtype
.get(self
.json_type())
358 def doc_type(self
) -> Optional
[str]:
359 if self
.is_implicit():
363 def need_has_if_optional(self
) -> bool:
364 # When FOO is a pointer, has_FOO == !!FOO, i.e. has_FOO is redundant.
365 # Except for arrays; see QAPISchemaArrayType.need_has_if_optional().
366 return not self
.c_type().endswith(POINTER_SUFFIX
)
368 def check(self
, schema
: QAPISchema
) -> None:
369 super().check(schema
)
370 for feat
in self
.features
:
371 if feat
.is_special():
374 f
"feature '{feat.name}' is not supported for types")
376 def describe(self
) -> str:
377 return "%s type '%s'" % (self
.meta
, self
.name
)
380 class QAPISchemaBuiltinType(QAPISchemaType
):
383 def __init__(self
, name
: str, json_type
: str, c_type
: str):
384 super().__init
__(name
, None, None)
385 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
387 self
._json
_type
_name
= json_type
388 self
._c
_type
_name
= c_type
390 def c_name(self
) -> str:
393 def c_type(self
) -> str:
394 return self
._c
_type
_name
396 def c_param_type(self
) -> str:
397 if self
.name
== 'str':
398 return 'const ' + self
._c
_type
_name
399 return self
._c
_type
_name
401 def json_type(self
) -> str:
402 return self
._json
_type
_name
404 def doc_type(self
) -> str:
405 return self
.json_type()
407 def visit(self
, visitor
: QAPISchemaVisitor
) -> None:
408 super().visit(visitor
)
409 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
412 class QAPISchemaEnumType(QAPISchemaType
):
418 info
: Optional
[QAPISourceInfo
],
419 doc
: Optional
[QAPIDoc
],
420 ifcond
: Optional
[QAPISchemaIfCond
],
421 features
: Optional
[List
[QAPISchemaFeature
]],
422 members
: List
[QAPISchemaEnumMember
],
423 prefix
: Optional
[str],
425 super().__init
__(name
, info
, doc
, ifcond
, features
)
427 m
.set_defined_in(name
)
428 self
.members
= members
431 def check(self
, schema
: QAPISchema
) -> None:
432 super().check(schema
)
433 seen
: Dict
[str, QAPISchemaMember
] = {}
434 for m
in self
.members
:
435 m
.check_clash(self
.info
, seen
)
437 def connect_doc(self
, doc
: Optional
[QAPIDoc
] = None) -> None:
438 super().connect_doc(doc
)
439 doc
= doc
or self
.doc
440 for m
in self
.members
:
443 def is_implicit(self
) -> bool:
444 # See QAPISchema._def_predefineds()
445 return self
.name
== 'QType'
447 def c_type(self
) -> str:
448 return c_name(self
.name
)
450 def member_names(self
) -> List
[str]:
451 return [m
.name
for m
in self
.members
]
453 def json_type(self
) -> str:
456 def visit(self
, visitor
: QAPISchemaVisitor
) -> None:
457 super().visit(visitor
)
458 visitor
.visit_enum_type(
459 self
.name
, self
.info
, self
.ifcond
, self
.features
,
460 self
.members
, self
.prefix
)
463 class QAPISchemaArrayType(QAPISchemaType
):
467 self
, name
: str, info
: Optional
[QAPISourceInfo
], element_type
: str
469 super().__init
__(name
, info
, None)
470 self
._element
_type
_name
= element_type
471 self
.element_type
: QAPISchemaType
473 def need_has_if_optional(self
) -> bool:
474 # When FOO is an array, we still need has_FOO to distinguish
475 # absent (!has_FOO) from present and empty (has_FOO && !FOO).
478 def check(self
, schema
: QAPISchema
) -> None:
479 super().check(schema
)
480 self
.element_type
= schema
.resolve_type(
481 self
._element
_type
_name
, self
.info
,
482 self
.info
.defn_meta
if self
.info
else None)
483 assert not isinstance(self
.element_type
, QAPISchemaArrayType
)
485 def set_module(self
, schema
: QAPISchema
) -> None:
486 self
._set
_module
(schema
, self
.element_type
.info
)
489 def ifcond(self
) -> QAPISchemaIfCond
:
491 return self
.element_type
.ifcond
493 def is_implicit(self
) -> bool:
496 def c_type(self
) -> str:
497 return c_name(self
.name
) + POINTER_SUFFIX
499 def json_type(self
) -> str:
502 def doc_type(self
) -> Optional
[str]:
503 elt_doc_type
= self
.element_type
.doc_type()
506 return 'array of ' + elt_doc_type
508 def visit(self
, visitor
: QAPISchemaVisitor
) -> None:
509 super().visit(visitor
)
510 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
513 def describe(self
) -> str:
514 return "%s type ['%s']" % (self
.meta
, self
._element
_type
_name
)
517 class QAPISchemaObjectType(QAPISchemaType
):
521 info
: Optional
[QAPISourceInfo
],
522 doc
: Optional
[QAPIDoc
],
523 ifcond
: Optional
[QAPISchemaIfCond
],
524 features
: Optional
[List
[QAPISchemaFeature
]],
526 local_members
: List
[QAPISchemaObjectTypeMember
],
527 variants
: Optional
[QAPISchemaBranches
],
529 # struct has local_members, optional base, and no variants
530 # union has base, variants, and no local_members
531 super().__init
__(name
, info
, doc
, ifcond
, features
)
532 self
.meta
= 'union' if variants
else 'struct'
533 for m
in local_members
:
534 m
.set_defined_in(name
)
535 if variants
is not None:
536 variants
.set_defined_in(name
)
537 self
._base
_name
= base
539 self
.local_members
= local_members
540 self
.variants
= variants
541 self
.members
: List
[QAPISchemaObjectTypeMember
]
542 self
._check
_complete
= False
544 def check(self
, schema
: QAPISchema
) -> None:
545 # This calls another type T's .check() exactly when the C
546 # struct emitted by gen_object() contains that T's C struct
547 # (pointers don't count).
548 if self
._check
_complete
:
549 # A previous .check() completed: nothing to do
552 # Recursed: C struct contains itself
553 raise QAPISemError(self
.info
,
554 "object %s contains itself" % self
.name
)
556 super().check(schema
)
557 assert self
._checked
and not self
._check
_complete
561 self
.base
= schema
.resolve_type(self
._base
_name
, self
.info
,
563 if (not isinstance(self
.base
, QAPISchemaObjectType
)
564 or self
.base
.variants
):
567 "'base' requires a struct type, %s isn't"
568 % self
.base
.describe())
569 self
.base
.check(schema
)
570 self
.base
.check_clash(self
.info
, seen
)
571 for m
in self
.local_members
:
573 m
.check_clash(self
.info
, seen
)
575 # self.check_clash() works in terms of the supertype, but
576 # self.members is declared List[QAPISchemaObjectTypeMember].
577 # Cast down to the subtype.
578 members
= cast(List
[QAPISchemaObjectTypeMember
], list(seen
.values()))
581 self
.variants
.check(schema
, seen
)
582 self
.variants
.check_clash(self
.info
, seen
)
584 self
.members
= members
585 self
._check
_complete
= True # mark completed
587 # Check that the members of this type do not cause duplicate JSON members,
588 # and update seen to track the members seen so far. Report any errors
589 # on behalf of info, which is not necessarily self.info
592 info
: Optional
[QAPISourceInfo
],
593 seen
: Dict
[str, QAPISchemaMember
],
596 for m
in self
.members
:
597 m
.check_clash(info
, seen
)
599 self
.variants
.check_clash(info
, seen
)
601 def connect_doc(self
, doc
: Optional
[QAPIDoc
] = None) -> None:
602 super().connect_doc(doc
)
603 doc
= doc
or self
.doc
604 if self
.base
and self
.base
.is_implicit():
605 self
.base
.connect_doc(doc
)
606 for m
in self
.local_members
:
609 def is_implicit(self
) -> bool:
610 # See QAPISchema._make_implicit_object_type(), as well as
612 return self
.name
.startswith('q_')
614 def is_empty(self
) -> bool:
615 return not self
.members
and not self
.variants
617 def has_conditional_members(self
) -> bool:
618 return any(m
.ifcond
.is_present() for m
in self
.members
)
620 def c_name(self
) -> str:
621 assert self
.name
!= 'q_empty'
622 return super().c_name()
624 def c_type(self
) -> str:
625 assert not self
.is_implicit()
626 return c_name(self
.name
) + POINTER_SUFFIX
628 def c_unboxed_type(self
) -> str:
629 return c_name(self
.name
)
631 def json_type(self
) -> str:
634 def visit(self
, visitor
: QAPISchemaVisitor
) -> None:
635 super().visit(visitor
)
636 visitor
.visit_object_type(
637 self
.name
, self
.info
, self
.ifcond
, self
.features
,
638 self
.base
, self
.local_members
, self
.variants
)
639 visitor
.visit_object_type_flat(
640 self
.name
, self
.info
, self
.ifcond
, self
.features
,
641 self
.members
, self
.variants
)
644 class QAPISchemaAlternateType(QAPISchemaType
):
650 info
: QAPISourceInfo
,
651 doc
: Optional
[QAPIDoc
],
652 ifcond
: Optional
[QAPISchemaIfCond
],
653 features
: List
[QAPISchemaFeature
],
654 variants
: QAPISchemaAlternatives
,
656 super().__init
__(name
, info
, doc
, ifcond
, features
)
657 assert variants
.tag_member
658 variants
.set_defined_in(name
)
659 variants
.tag_member
.set_defined_in(self
.name
)
660 self
.variants
= variants
662 def check(self
, schema
: QAPISchema
) -> None:
663 super().check(schema
)
664 self
.variants
.tag_member
.check(schema
)
665 # Not calling self.variants.check_clash(), because there's nothing
667 self
.variants
.check(schema
, {})
668 # Alternate branch names have no relation to the tag enum values;
669 # so we have to check for potential name collisions ourselves.
670 seen
: Dict
[str, QAPISchemaMember
] = {}
671 types_seen
: Dict
[str, str] = {}
672 for v
in self
.variants
.variants
:
673 v
.check_clash(self
.info
, seen
)
674 qtype
= v
.type.alternate_qtype()
679 % (v
.describe(self
.info
), v
.type.describe()))
680 conflicting
= set([qtype
])
681 if qtype
== 'QTYPE_QSTRING':
682 if isinstance(v
.type, QAPISchemaEnumType
):
683 for m
in v
.type.members
:
684 if m
.name
in ['on', 'off']:
685 conflicting
.add('QTYPE_QBOOL')
686 if re
.match(r
'[-+0-9.]', m
.name
):
687 # lazy, could be tightened
688 conflicting
.add('QTYPE_QNUM')
690 conflicting
.add('QTYPE_QNUM')
691 conflicting
.add('QTYPE_QBOOL')
692 for qt
in conflicting
:
696 "%s can't be distinguished from '%s'"
697 % (v
.describe(self
.info
), types_seen
[qt
]))
698 types_seen
[qt
] = v
.name
700 def connect_doc(self
, doc
: Optional
[QAPIDoc
] = None) -> None:
701 super().connect_doc(doc
)
702 doc
= doc
or self
.doc
703 for v
in self
.variants
.variants
:
706 def c_type(self
) -> str:
707 return c_name(self
.name
) + POINTER_SUFFIX
709 def json_type(self
) -> str:
712 def visit(self
, visitor
: QAPISchemaVisitor
) -> None:
713 super().visit(visitor
)
714 visitor
.visit_alternate_type(
715 self
.name
, self
.info
, self
.ifcond
, self
.features
, self
.variants
)
718 class QAPISchemaVariants
:
721 tag_name
: Optional
[str],
722 info
: QAPISourceInfo
,
723 tag_member
: Optional
[QAPISchemaObjectTypeMember
],
724 variants
: List
[QAPISchemaVariant
],
726 # Unions pass tag_name but not tag_member.
727 # Alternates pass tag_member but not tag_name.
728 # After check(), tag_member is always set.
729 assert bool(tag_member
) != bool(tag_name
)
730 assert (isinstance(tag_name
, str) or
731 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
732 self
._tag
_name
= tag_name
734 self
._tag
_member
= tag_member
735 self
.variants
= variants
738 def tag_member(self
) -> QAPISchemaObjectTypeMember
:
739 if self
._tag
_member
is None:
741 "QAPISchemaVariants has no tag_member property until "
742 "after check() has been run."
744 return self
._tag
_member
746 def set_defined_in(self
, name
: str) -> None:
747 for v
in self
.variants
:
748 v
.set_defined_in(name
)
751 self
, schema
: QAPISchema
, seen
: Dict
[str, QAPISchemaMember
]
753 if self
._tag
_name
: # union
754 # We need to narrow the member type:
755 tmp
= seen
.get(c_name(self
._tag
_name
))
756 assert tmp
is None or isinstance(tmp
, QAPISchemaObjectTypeMember
)
757 self
._tag
_member
= tmp
760 # Pointing to the base type when not implicit would be
761 # nice, but we don't know it here
762 if not self
._tag
_member
or self
._tag
_name
!= self
._tag
_member
.name
:
765 "discriminator '%s' is not a member of %s"
766 % (self
._tag
_name
, base
))
768 assert self
.tag_member
.defined_in
769 base_type
= schema
.lookup_type(self
.tag_member
.defined_in
)
771 if not base_type
.is_implicit():
772 base
= "base type '%s'" % self
.tag_member
.defined_in
773 if not isinstance(self
.tag_member
.type, QAPISchemaEnumType
):
776 "discriminator member '%s' of %s must be of enum type"
777 % (self
._tag
_name
, base
))
778 if self
.tag_member
.optional
:
781 "discriminator member '%s' of %s must not be optional"
782 % (self
._tag
_name
, base
))
783 if self
.tag_member
.ifcond
.is_present():
786 "discriminator member '%s' of %s must not be conditional"
787 % (self
._tag
_name
, base
))
789 assert self
._tag
_member
790 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
791 assert not self
.tag_member
.optional
792 assert not self
.tag_member
.ifcond
.is_present()
793 if self
._tag
_name
: # union
794 # branches that are not explicitly covered get an empty type
795 assert self
.tag_member
.defined_in
796 cases
= {v
.name
for v
in self
.variants
}
797 for m
in self
.tag_member
.type.members
:
798 if m
.name
not in cases
:
799 v
= QAPISchemaVariant(m
.name
, self
.info
,
801 v
.set_defined_in(self
.tag_member
.defined_in
)
802 self
.variants
.append(v
)
803 if not self
.variants
:
804 raise QAPISemError(self
.info
, "union has no branches")
805 for v
in self
.variants
:
807 # Union names must match enum values; alternate names are
808 # checked separately. Use 'seen' to tell the two apart.
810 if v
.name
not in self
.tag_member
.type.member_names():
813 "branch '%s' is not a value of %s"
814 % (v
.name
, self
.tag_member
.type.describe()))
815 if not isinstance(v
.type, QAPISchemaObjectType
):
819 % (v
.describe(self
.info
), v
.type.describe()))
824 info
: Optional
[QAPISourceInfo
],
825 seen
: Dict
[str, QAPISchemaMember
],
827 for v
in self
.variants
:
828 # Reset seen map for each variant, since qapi names from one
829 # branch do not affect another branch.
831 # v.type's typing is enforced in check() above.
832 assert isinstance(v
.type, QAPISchemaObjectType
)
833 v
.type.check_clash(info
, dict(seen
))
836 class QAPISchemaBranches(QAPISchemaVariants
):
838 info
: QAPISourceInfo
,
839 variants
: List
[QAPISchemaVariant
],
841 super().__init
__(tag_name
, info
, None, variants
)
844 class QAPISchemaAlternatives(QAPISchemaVariants
):
846 info
: QAPISourceInfo
,
847 variants
: List
[QAPISchemaVariant
],
848 tag_member
: QAPISchemaObjectTypeMember
):
849 super().__init
__(None, info
, tag_member
, variants
)
852 class QAPISchemaMember
:
853 """ Represents object members, enum members and features """
859 info
: Optional
[QAPISourceInfo
],
860 ifcond
: Optional
[QAPISchemaIfCond
] = None,
864 self
.ifcond
= ifcond
or QAPISchemaIfCond()
865 self
.defined_in
: Optional
[str] = None
867 def set_defined_in(self
, name
: str) -> None:
868 assert not self
.defined_in
869 self
.defined_in
= name
873 info
: Optional
[QAPISourceInfo
],
874 seen
: Dict
[str, QAPISchemaMember
],
876 cname
= c_name(self
.name
)
880 "%s collides with %s"
881 % (self
.describe(info
), seen
[cname
].describe(info
)))
884 def connect_doc(self
, doc
: Optional
[QAPIDoc
]) -> None:
886 doc
.connect_member(self
)
888 def describe(self
, info
: Optional
[QAPISourceInfo
]) -> str:
891 defined_in
= self
.defined_in
894 if defined_in
.startswith('q_obj_'):
895 # See QAPISchema._make_implicit_object_type() - reverse the
896 # mapping there to create a nice human-readable description
897 defined_in
= defined_in
[6:]
898 if defined_in
.endswith('-arg'):
899 # Implicit type created for a command's dict 'data'
900 assert role
== 'member'
903 defined_in
= defined_in
[:-4]
904 elif defined_in
.endswith('-base'):
905 # Implicit type created for a union's dict 'base'
906 role
= 'base ' + role
907 defined_in
= defined_in
[:-5]
911 assert info
is not None
912 if defined_in
!= info
.defn_name
:
913 return "%s '%s' of %s '%s'" % (role
, self
.name
, meta
, defined_in
)
914 return "%s '%s'" % (role
, self
.name
)
917 class QAPISchemaEnumMember(QAPISchemaMember
):
923 info
: Optional
[QAPISourceInfo
],
924 ifcond
: Optional
[QAPISchemaIfCond
] = None,
925 features
: Optional
[List
[QAPISchemaFeature
]] = None,
927 super().__init
__(name
, info
, ifcond
)
928 for f
in features
or []:
929 f
.set_defined_in(name
)
930 self
.features
= features
or []
932 def connect_doc(self
, doc
: Optional
[QAPIDoc
]) -> None:
933 super().connect_doc(doc
)
935 for f
in self
.features
:
936 doc
.connect_feature(f
)
939 class QAPISchemaFeature(QAPISchemaMember
):
942 def is_special(self
) -> bool:
943 return self
.name
in ('deprecated', 'unstable')
946 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
950 info
: QAPISourceInfo
,
953 ifcond
: Optional
[QAPISchemaIfCond
] = None,
954 features
: Optional
[List
[QAPISchemaFeature
]] = None,
956 super().__init
__(name
, info
, ifcond
)
957 for f
in features
or []:
958 f
.set_defined_in(name
)
959 self
._type
_name
= typ
960 self
.type: QAPISchemaType
# set during check()
961 self
.optional
= optional
962 self
.features
= features
or []
964 def need_has(self
) -> bool:
965 return self
.optional
and self
.type.need_has_if_optional()
967 def check(self
, schema
: QAPISchema
) -> None:
968 assert self
.defined_in
969 self
.type = schema
.resolve_type(self
._type
_name
, self
.info
,
971 seen
: Dict
[str, QAPISchemaMember
] = {}
972 for f
in self
.features
:
973 f
.check_clash(self
.info
, seen
)
975 def connect_doc(self
, doc
: Optional
[QAPIDoc
]) -> None:
976 super().connect_doc(doc
)
978 for f
in self
.features
:
979 doc
.connect_feature(f
)
982 class QAPISchemaVariant(QAPISchemaObjectTypeMember
):
988 info
: QAPISourceInfo
,
990 ifcond
: QAPISchemaIfCond
,
992 super().__init
__(name
, info
, typ
, False, ifcond
)
995 class QAPISchemaCommand(QAPISchemaDefinition
):
1001 info
: QAPISourceInfo
,
1002 doc
: Optional
[QAPIDoc
],
1003 ifcond
: QAPISchemaIfCond
,
1004 features
: List
[QAPISchemaFeature
],
1005 arg_type
: Optional
[str],
1006 ret_type
: Optional
[str],
1008 success_response
: bool,
1011 allow_preconfig
: bool,
1014 super().__init
__(name
, info
, doc
, ifcond
, features
)
1015 self
._arg
_type
_name
= arg_type
1016 self
.arg_type
: Optional
[QAPISchemaObjectType
] = None
1017 self
._ret
_type
_name
= ret_type
1018 self
.ret_type
: Optional
[QAPISchemaType
] = None
1020 self
.success_response
= success_response
1022 self
.allow_oob
= allow_oob
1023 self
.allow_preconfig
= allow_preconfig
1024 self
.coroutine
= coroutine
1026 def check(self
, schema
: QAPISchema
) -> None:
1027 assert self
.info
is not None
1028 super().check(schema
)
1029 if self
._arg
_type
_name
:
1030 arg_type
= schema
.resolve_type(
1031 self
._arg
_type
_name
, self
.info
, "command's 'data'")
1032 if not isinstance(arg_type
, QAPISchemaObjectType
):
1035 "command's 'data' cannot take %s"
1036 % arg_type
.describe())
1037 self
.arg_type
= arg_type
1038 if self
.arg_type
.variants
and not self
.boxed
:
1041 "command's 'data' can take %s only with 'boxed': true"
1042 % self
.arg_type
.describe())
1043 self
.arg_type
.check(schema
)
1044 if self
.arg_type
.has_conditional_members() and not self
.boxed
:
1047 "conditional command arguments require 'boxed': true")
1048 if self
._ret
_type
_name
:
1049 self
.ret_type
= schema
.resolve_type(
1050 self
._ret
_type
_name
, self
.info
, "command's 'returns'")
1051 if self
.name
not in self
.info
.pragma
.command_returns_exceptions
:
1053 if isinstance(typ
, QAPISchemaArrayType
):
1054 typ
= typ
.element_type
1055 if not isinstance(typ
, QAPISchemaObjectType
):
1058 "command's 'returns' cannot take %s"
1059 % self
.ret_type
.describe())
1061 def connect_doc(self
, doc
: Optional
[QAPIDoc
] = None) -> None:
1062 super().connect_doc(doc
)
1063 doc
= doc
or self
.doc
1065 if self
.arg_type
and self
.arg_type
.is_implicit():
1066 self
.arg_type
.connect_doc(doc
)
1068 def visit(self
, visitor
: QAPISchemaVisitor
) -> None:
1069 super().visit(visitor
)
1070 visitor
.visit_command(
1071 self
.name
, self
.info
, self
.ifcond
, self
.features
,
1072 self
.arg_type
, self
.ret_type
, self
.gen
, self
.success_response
,
1073 self
.boxed
, self
.allow_oob
, self
.allow_preconfig
,
1077 class QAPISchemaEvent(QAPISchemaDefinition
):
1083 info
: QAPISourceInfo
,
1084 doc
: Optional
[QAPIDoc
],
1085 ifcond
: QAPISchemaIfCond
,
1086 features
: List
[QAPISchemaFeature
],
1087 arg_type
: Optional
[str],
1090 super().__init
__(name
, info
, doc
, ifcond
, features
)
1091 self
._arg
_type
_name
= arg_type
1092 self
.arg_type
: Optional
[QAPISchemaObjectType
] = None
1095 def check(self
, schema
: QAPISchema
) -> None:
1096 super().check(schema
)
1097 if self
._arg
_type
_name
:
1098 typ
= schema
.resolve_type(
1099 self
._arg
_type
_name
, self
.info
, "event's 'data'")
1100 if not isinstance(typ
, QAPISchemaObjectType
):
1103 "event's 'data' cannot take %s"
1106 if self
.arg_type
.variants
and not self
.boxed
:
1109 "event's 'data' can take %s only with 'boxed': true"
1110 % self
.arg_type
.describe())
1111 self
.arg_type
.check(schema
)
1112 if self
.arg_type
.has_conditional_members() and not self
.boxed
:
1115 "conditional event arguments require 'boxed': true")
1117 def connect_doc(self
, doc
: Optional
[QAPIDoc
] = None) -> None:
1118 super().connect_doc(doc
)
1119 doc
= doc
or self
.doc
1121 if self
.arg_type
and self
.arg_type
.is_implicit():
1122 self
.arg_type
.connect_doc(doc
)
1124 def visit(self
, visitor
: QAPISchemaVisitor
) -> None:
1125 super().visit(visitor
)
1126 visitor
.visit_event(
1127 self
.name
, self
.info
, self
.ifcond
, self
.features
,
1128 self
.arg_type
, self
.boxed
)
1132 def __init__(self
, fname
: str):
1136 parser
= QAPISchemaParser(fname
)
1137 except OSError as err
:
1139 f
"can't read schema file '{fname}': {err.strerror}"
1142 exprs
= check_exprs(parser
.exprs
)
1143 self
.docs
= parser
.docs
1144 self
._entity
_list
: List
[QAPISchemaEntity
] = []
1145 self
._entity
_dict
: Dict
[str, QAPISchemaDefinition
] = {}
1146 self
._module
_dict
: Dict
[str, QAPISchemaModule
] = OrderedDict()
1147 self
._schema
_dir
= os
.path
.dirname(fname
)
1148 self
._make
_module
(QAPISchemaModule
.BUILTIN_MODULE_NAME
)
1149 self
._make
_module
(fname
)
1150 self
._predefining
= True
1151 self
._def
_predefineds
()
1152 self
._predefining
= False
1153 self
._def
_exprs
(exprs
)
1156 def _def_entity(self
, ent
: QAPISchemaEntity
) -> None:
1157 self
._entity
_list
.append(ent
)
1159 def _def_definition(self
, defn
: QAPISchemaDefinition
) -> None:
1160 # Only the predefined types are allowed to not have info
1161 assert defn
.info
or self
._predefining
1162 self
._def
_entity
(defn
)
1163 # TODO reject names that differ only in '_' vs. '.' vs. '-',
1164 # because they're liable to clash in generated C.
1165 other_defn
= self
._entity
_dict
.get(defn
.name
)
1168 where
= QAPISourceError(other_defn
.info
, "previous definition")
1171 "'%s' is already defined\n%s" % (defn
.name
, where
))
1173 defn
.info
, "%s is already defined" % other_defn
.describe())
1174 self
._entity
_dict
[defn
.name
] = defn
1176 def lookup_entity(self
,name
: str) -> Optional
[QAPISchemaEntity
]:
1177 return self
._entity
_dict
.get(name
)
1179 def lookup_type(self
, name
: str) -> Optional
[QAPISchemaType
]:
1180 typ
= self
.lookup_entity(name
)
1181 if isinstance(typ
, QAPISchemaType
):
1188 info
: Optional
[QAPISourceInfo
],
1189 what
: Union
[None, str, Callable
[[QAPISourceInfo
], str]],
1190 ) -> QAPISchemaType
:
1191 typ
= self
.lookup_type(name
)
1193 assert info
and what
# built-in types must not fail lookup
1197 info
, "%s uses unknown type '%s'" % (what
, name
))
1200 def _module_name(self
, fname
: str) -> str:
1201 if QAPISchemaModule
.is_system_module(fname
):
1203 return os
.path
.relpath(fname
, self
._schema
_dir
)
1205 def _make_module(self
, fname
: str) -> QAPISchemaModule
:
1206 name
= self
._module
_name
(fname
)
1207 if name
not in self
._module
_dict
:
1208 self
._module
_dict
[name
] = QAPISchemaModule(name
)
1209 return self
._module
_dict
[name
]
1211 def module_by_fname(self
, fname
: str) -> QAPISchemaModule
:
1212 name
= self
._module
_name
(fname
)
1213 return self
._module
_dict
[name
]
1215 def _def_include(self
, expr
: QAPIExpression
) -> None:
1216 include
= expr
['include']
1217 assert expr
.doc
is None
1219 QAPISchemaInclude(self
._make
_module
(include
), expr
.info
))
1221 def _def_builtin_type(
1222 self
, name
: str, json_type
: str, c_type
: str
1224 self
._def
_definition
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1225 # Instantiating only the arrays that are actually used would
1226 # be nice, but we can't as long as their generated code
1227 # (qapi-builtin-types.[ch]) may be shared by some other
1229 self
._make
_array
_type
(name
, None)
1231 def _def_predefineds(self
) -> None:
1232 for t
in [('str', 'string', 'char' + POINTER_SUFFIX
),
1233 ('number', 'number', 'double'),
1234 ('int', 'int', 'int64_t'),
1235 ('int8', 'int', 'int8_t'),
1236 ('int16', 'int', 'int16_t'),
1237 ('int32', 'int', 'int32_t'),
1238 ('int64', 'int', 'int64_t'),
1239 ('uint8', 'int', 'uint8_t'),
1240 ('uint16', 'int', 'uint16_t'),
1241 ('uint32', 'int', 'uint32_t'),
1242 ('uint64', 'int', 'uint64_t'),
1243 ('size', 'int', 'uint64_t'),
1244 ('bool', 'boolean', 'bool'),
1245 ('any', 'value', 'QObject' + POINTER_SUFFIX
),
1246 ('null', 'null', 'QNull' + POINTER_SUFFIX
)]:
1247 self
._def
_builtin
_type
(*t
)
1248 self
.the_empty_object_type
= QAPISchemaObjectType(
1249 'q_empty', None, None, None, None, None, [], None)
1250 self
._def
_definition
(self
.the_empty_object_type
)
1252 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1254 qtype_values
= self
._make
_enum
_members
(
1255 [{'name': n
} for n
in qtypes
], None)
1257 self
._def
_definition
(QAPISchemaEnumType(
1258 'QType', None, None, None, None, qtype_values
, 'QTYPE'))
1262 features
: Optional
[List
[Dict
[str, Any
]]],
1263 info
: Optional
[QAPISourceInfo
],
1264 ) -> List
[QAPISchemaFeature
]:
1265 if features
is None:
1267 return [QAPISchemaFeature(f
['name'], info
,
1268 QAPISchemaIfCond(f
.get('if')))
1271 def _make_enum_member(
1274 ifcond
: Optional
[Union
[str, Dict
[str, Any
]]],
1275 features
: Optional
[List
[Dict
[str, Any
]]],
1276 info
: Optional
[QAPISourceInfo
],
1277 ) -> QAPISchemaEnumMember
:
1278 return QAPISchemaEnumMember(name
, info
,
1279 QAPISchemaIfCond(ifcond
),
1280 self
._make
_features
(features
, info
))
1282 def _make_enum_members(
1283 self
, values
: List
[Dict
[str, Any
]], info
: Optional
[QAPISourceInfo
]
1284 ) -> List
[QAPISchemaEnumMember
]:
1285 return [self
._make
_enum
_member
(v
['name'], v
.get('if'),
1286 v
.get('features'), info
)
1289 def _make_array_type(
1290 self
, element_type
: str, info
: Optional
[QAPISourceInfo
]
1292 name
= element_type
+ 'List' # reserved by check_defn_name_str()
1293 if not self
.lookup_type(name
):
1294 self
._def
_definition
(QAPISchemaArrayType(
1295 name
, info
, element_type
))
1298 def _make_implicit_object_type(
1301 info
: QAPISourceInfo
,
1302 ifcond
: QAPISchemaIfCond
,
1304 members
: List
[QAPISchemaObjectTypeMember
],
1308 # See also QAPISchemaObjectTypeMember.describe()
1309 name
= 'q_obj_%s-%s' % (name
, role
)
1310 typ
= self
.lookup_entity(name
)
1312 assert(isinstance(typ
, QAPISchemaObjectType
))
1313 # The implicit object type has multiple users. This can
1314 # only be a duplicate definition, which will be flagged
1318 self
._def
_definition
(QAPISchemaObjectType(
1319 name
, info
, None, ifcond
, None, None, members
, None))
1322 def _def_enum_type(self
, expr
: QAPIExpression
) -> None:
1325 prefix
= expr
.get('prefix')
1326 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1328 features
= self
._make
_features
(expr
.get('features'), info
)
1329 self
._def
_definition
(QAPISchemaEnumType(
1330 name
, info
, expr
.doc
, ifcond
, features
,
1331 self
._make
_enum
_members
(data
, info
), prefix
))
1336 typ
: Union
[List
[str], str],
1337 ifcond
: QAPISchemaIfCond
,
1338 features
: Optional
[List
[Dict
[str, Any
]]],
1339 info
: QAPISourceInfo
,
1340 ) -> QAPISchemaObjectTypeMember
:
1342 if name
.startswith('*'):
1345 if isinstance(typ
, list):
1346 assert len(typ
) == 1
1347 typ
= self
._make
_array
_type
(typ
[0], info
)
1348 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
,
1349 self
._make
_features
(features
, info
))
1353 data
: Dict
[str, Any
],
1354 info
: QAPISourceInfo
,
1355 ) -> List
[QAPISchemaObjectTypeMember
]:
1356 return [self
._make
_member
(key
, value
['type'],
1357 QAPISchemaIfCond(value
.get('if')),
1358 value
.get('features'), info
)
1359 for (key
, value
) in data
.items()]
1361 def _def_struct_type(self
, expr
: QAPIExpression
) -> None:
1362 name
= expr
['struct']
1363 base
= expr
.get('base')
1366 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1367 features
= self
._make
_features
(expr
.get('features'), info
)
1368 self
._def
_definition
(QAPISchemaObjectType(
1369 name
, info
, expr
.doc
, ifcond
, features
, base
,
1370 self
._make
_members
(data
, info
),
1377 ifcond
: QAPISchemaIfCond
,
1378 info
: QAPISourceInfo
,
1379 ) -> QAPISchemaVariant
:
1380 if isinstance(typ
, list):
1381 assert len(typ
) == 1
1382 typ
= self
._make
_array
_type
(typ
[0], info
)
1383 return QAPISchemaVariant(case
, info
, typ
, ifcond
)
1385 def _def_union_type(self
, expr
: QAPIExpression
) -> None:
1386 name
= expr
['union']
1388 tag_name
= expr
['discriminator']
1390 assert isinstance(data
, dict)
1392 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1393 features
= self
._make
_features
(expr
.get('features'), info
)
1394 if isinstance(base
, dict):
1395 base
= self
._make
_implicit
_object
_type
(
1397 'base', self
._make
_members
(base
, info
))
1399 self
._make
_variant
(key
, value
['type'],
1400 QAPISchemaIfCond(value
.get('if')),
1402 for (key
, value
) in data
.items()]
1403 members
: List
[QAPISchemaObjectTypeMember
] = []
1404 self
._def
_definition
(
1405 QAPISchemaObjectType(name
, info
, expr
.doc
, ifcond
, features
,
1408 info
, variants
, tag_name
)))
1410 def _def_alternate_type(self
, expr
: QAPIExpression
) -> None:
1411 name
= expr
['alternate']
1413 assert isinstance(data
, dict)
1414 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1416 features
= self
._make
_features
(expr
.get('features'), info
)
1418 self
._make
_variant
(key
, value
['type'],
1419 QAPISchemaIfCond(value
.get('if')),
1421 for (key
, value
) in data
.items()]
1422 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
1423 self
._def
_definition
(
1424 QAPISchemaAlternateType(
1425 name
, info
, expr
.doc
, ifcond
, features
,
1426 QAPISchemaAlternatives(info
, variants
, tag_member
)))
1428 def _def_command(self
, expr
: QAPIExpression
) -> None:
1429 name
= expr
['command']
1430 data
= expr
.get('data')
1431 rets
= expr
.get('returns')
1432 gen
= expr
.get('gen', True)
1433 success_response
= expr
.get('success-response', True)
1434 boxed
= expr
.get('boxed', False)
1435 allow_oob
= expr
.get('allow-oob', False)
1436 allow_preconfig
= expr
.get('allow-preconfig', False)
1437 coroutine
= expr
.get('coroutine', False)
1438 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1440 features
= self
._make
_features
(expr
.get('features'), info
)
1441 if isinstance(data
, OrderedDict
):
1442 data
= self
._make
_implicit
_object
_type
(
1444 'arg', self
._make
_members
(data
, info
))
1445 if isinstance(rets
, list):
1446 assert len(rets
) == 1
1447 rets
= self
._make
_array
_type
(rets
[0], info
)
1448 self
._def
_definition
(
1449 QAPISchemaCommand(name
, info
, expr
.doc
, ifcond
, features
, data
,
1450 rets
, gen
, success_response
, boxed
, allow_oob
,
1451 allow_preconfig
, coroutine
))
1453 def _def_event(self
, expr
: QAPIExpression
) -> None:
1454 name
= expr
['event']
1455 data
= expr
.get('data')
1456 boxed
= expr
.get('boxed', False)
1457 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1459 features
= self
._make
_features
(expr
.get('features'), info
)
1460 if isinstance(data
, OrderedDict
):
1461 data
= self
._make
_implicit
_object
_type
(
1463 'arg', self
._make
_members
(data
, info
))
1464 self
._def
_definition
(QAPISchemaEvent(name
, info
, expr
.doc
, ifcond
,
1465 features
, data
, boxed
))
1467 def _def_exprs(self
, exprs
: List
[QAPIExpression
]) -> None:
1470 self
._def
_enum
_type
(expr
)
1471 elif 'struct' in expr
:
1472 self
._def
_struct
_type
(expr
)
1473 elif 'union' in expr
:
1474 self
._def
_union
_type
(expr
)
1475 elif 'alternate' in expr
:
1476 self
._def
_alternate
_type
(expr
)
1477 elif 'command' in expr
:
1478 self
._def
_command
(expr
)
1479 elif 'event' in expr
:
1480 self
._def
_event
(expr
)
1481 elif 'include' in expr
:
1482 self
._def
_include
(expr
)
1486 def check(self
) -> None:
1487 for ent
in self
._entity
_list
:
1490 for ent
in self
._entity
_list
:
1491 ent
.set_module(self
)
1492 for doc
in self
.docs
:
1495 def visit(self
, visitor
: QAPISchemaVisitor
) -> None:
1496 visitor
.visit_begin(self
)
1497 for mod
in self
._module
_dict
.values():