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
[QAPISchemaVariants
],
222 def visit_object_type_flat(
225 info
: Optional
[QAPISourceInfo
],
226 ifcond
: QAPISchemaIfCond
,
227 features
: List
[QAPISchemaFeature
],
228 members
: List
[QAPISchemaObjectTypeMember
],
229 variants
: Optional
[QAPISchemaVariants
],
233 def visit_alternate_type(
236 info
: Optional
[QAPISourceInfo
],
237 ifcond
: QAPISchemaIfCond
,
238 features
: List
[QAPISchemaFeature
],
239 variants
: QAPISchemaVariants
,
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
[QAPISchemaVariants
],
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
: QAPISchemaVariants
,
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 QAPISchemaMember
:
837 """ Represents object members, enum members and features """
843 info
: Optional
[QAPISourceInfo
],
844 ifcond
: Optional
[QAPISchemaIfCond
] = None,
848 self
.ifcond
= ifcond
or QAPISchemaIfCond()
849 self
.defined_in
: Optional
[str] = None
851 def set_defined_in(self
, name
: str) -> None:
852 assert not self
.defined_in
853 self
.defined_in
= name
857 info
: Optional
[QAPISourceInfo
],
858 seen
: Dict
[str, QAPISchemaMember
],
860 cname
= c_name(self
.name
)
864 "%s collides with %s"
865 % (self
.describe(info
), seen
[cname
].describe(info
)))
868 def connect_doc(self
, doc
: Optional
[QAPIDoc
]) -> None:
870 doc
.connect_member(self
)
872 def describe(self
, info
: Optional
[QAPISourceInfo
]) -> str:
875 defined_in
= self
.defined_in
878 if defined_in
.startswith('q_obj_'):
879 # See QAPISchema._make_implicit_object_type() - reverse the
880 # mapping there to create a nice human-readable description
881 defined_in
= defined_in
[6:]
882 if defined_in
.endswith('-arg'):
883 # Implicit type created for a command's dict 'data'
884 assert role
== 'member'
887 defined_in
= defined_in
[:-4]
888 elif defined_in
.endswith('-base'):
889 # Implicit type created for a union's dict 'base'
890 role
= 'base ' + role
891 defined_in
= defined_in
[:-5]
895 assert info
is not None
896 if defined_in
!= info
.defn_name
:
897 return "%s '%s' of %s '%s'" % (role
, self
.name
, meta
, defined_in
)
898 return "%s '%s'" % (role
, self
.name
)
901 class QAPISchemaEnumMember(QAPISchemaMember
):
907 info
: Optional
[QAPISourceInfo
],
908 ifcond
: Optional
[QAPISchemaIfCond
] = None,
909 features
: Optional
[List
[QAPISchemaFeature
]] = None,
911 super().__init
__(name
, info
, ifcond
)
912 for f
in features
or []:
913 f
.set_defined_in(name
)
914 self
.features
= features
or []
916 def connect_doc(self
, doc
: Optional
[QAPIDoc
]) -> None:
917 super().connect_doc(doc
)
919 for f
in self
.features
:
920 doc
.connect_feature(f
)
923 class QAPISchemaFeature(QAPISchemaMember
):
926 def is_special(self
) -> bool:
927 return self
.name
in ('deprecated', 'unstable')
930 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
934 info
: QAPISourceInfo
,
937 ifcond
: Optional
[QAPISchemaIfCond
] = None,
938 features
: Optional
[List
[QAPISchemaFeature
]] = None,
940 super().__init
__(name
, info
, ifcond
)
941 for f
in features
or []:
942 f
.set_defined_in(name
)
943 self
._type
_name
= typ
944 self
.type: QAPISchemaType
# set during check()
945 self
.optional
= optional
946 self
.features
= features
or []
948 def need_has(self
) -> bool:
949 return self
.optional
and self
.type.need_has_if_optional()
951 def check(self
, schema
: QAPISchema
) -> None:
952 assert self
.defined_in
953 self
.type = schema
.resolve_type(self
._type
_name
, self
.info
,
955 seen
: Dict
[str, QAPISchemaMember
] = {}
956 for f
in self
.features
:
957 f
.check_clash(self
.info
, seen
)
959 def connect_doc(self
, doc
: Optional
[QAPIDoc
]) -> None:
960 super().connect_doc(doc
)
962 for f
in self
.features
:
963 doc
.connect_feature(f
)
966 class QAPISchemaVariant(QAPISchemaObjectTypeMember
):
972 info
: QAPISourceInfo
,
974 ifcond
: QAPISchemaIfCond
,
976 super().__init
__(name
, info
, typ
, False, ifcond
)
979 class QAPISchemaCommand(QAPISchemaDefinition
):
985 info
: QAPISourceInfo
,
986 doc
: Optional
[QAPIDoc
],
987 ifcond
: QAPISchemaIfCond
,
988 features
: List
[QAPISchemaFeature
],
989 arg_type
: Optional
[str],
990 ret_type
: Optional
[str],
992 success_response
: bool,
995 allow_preconfig
: bool,
998 super().__init
__(name
, info
, doc
, ifcond
, features
)
999 self
._arg
_type
_name
= arg_type
1000 self
.arg_type
: Optional
[QAPISchemaObjectType
] = None
1001 self
._ret
_type
_name
= ret_type
1002 self
.ret_type
: Optional
[QAPISchemaType
] = None
1004 self
.success_response
= success_response
1006 self
.allow_oob
= allow_oob
1007 self
.allow_preconfig
= allow_preconfig
1008 self
.coroutine
= coroutine
1010 def check(self
, schema
: QAPISchema
) -> None:
1011 assert self
.info
is not None
1012 super().check(schema
)
1013 if self
._arg
_type
_name
:
1014 arg_type
= schema
.resolve_type(
1015 self
._arg
_type
_name
, self
.info
, "command's 'data'")
1016 if not isinstance(arg_type
, QAPISchemaObjectType
):
1019 "command's 'data' cannot take %s"
1020 % arg_type
.describe())
1021 self
.arg_type
= arg_type
1022 if self
.arg_type
.variants
and not self
.boxed
:
1025 "command's 'data' can take %s only with 'boxed': true"
1026 % self
.arg_type
.describe())
1027 self
.arg_type
.check(schema
)
1028 if self
.arg_type
.has_conditional_members() and not self
.boxed
:
1031 "conditional command arguments require 'boxed': true")
1032 if self
._ret
_type
_name
:
1033 self
.ret_type
= schema
.resolve_type(
1034 self
._ret
_type
_name
, self
.info
, "command's 'returns'")
1035 if self
.name
not in self
.info
.pragma
.command_returns_exceptions
:
1037 if isinstance(typ
, QAPISchemaArrayType
):
1038 typ
= typ
.element_type
1039 if not isinstance(typ
, QAPISchemaObjectType
):
1042 "command's 'returns' cannot take %s"
1043 % self
.ret_type
.describe())
1045 def connect_doc(self
, doc
: Optional
[QAPIDoc
] = None) -> None:
1046 super().connect_doc(doc
)
1047 doc
= doc
or self
.doc
1049 if self
.arg_type
and self
.arg_type
.is_implicit():
1050 self
.arg_type
.connect_doc(doc
)
1052 def visit(self
, visitor
: QAPISchemaVisitor
) -> None:
1053 super().visit(visitor
)
1054 visitor
.visit_command(
1055 self
.name
, self
.info
, self
.ifcond
, self
.features
,
1056 self
.arg_type
, self
.ret_type
, self
.gen
, self
.success_response
,
1057 self
.boxed
, self
.allow_oob
, self
.allow_preconfig
,
1061 class QAPISchemaEvent(QAPISchemaDefinition
):
1067 info
: QAPISourceInfo
,
1068 doc
: Optional
[QAPIDoc
],
1069 ifcond
: QAPISchemaIfCond
,
1070 features
: List
[QAPISchemaFeature
],
1071 arg_type
: Optional
[str],
1074 super().__init
__(name
, info
, doc
, ifcond
, features
)
1075 self
._arg
_type
_name
= arg_type
1076 self
.arg_type
: Optional
[QAPISchemaObjectType
] = None
1079 def check(self
, schema
: QAPISchema
) -> None:
1080 super().check(schema
)
1081 if self
._arg
_type
_name
:
1082 typ
= schema
.resolve_type(
1083 self
._arg
_type
_name
, self
.info
, "event's 'data'")
1084 if not isinstance(typ
, QAPISchemaObjectType
):
1087 "event's 'data' cannot take %s"
1090 if self
.arg_type
.variants
and not self
.boxed
:
1093 "event's 'data' can take %s only with 'boxed': true"
1094 % self
.arg_type
.describe())
1095 self
.arg_type
.check(schema
)
1096 if self
.arg_type
.has_conditional_members() and not self
.boxed
:
1099 "conditional event arguments require 'boxed': true")
1101 def connect_doc(self
, doc
: Optional
[QAPIDoc
] = None) -> None:
1102 super().connect_doc(doc
)
1103 doc
= doc
or self
.doc
1105 if self
.arg_type
and self
.arg_type
.is_implicit():
1106 self
.arg_type
.connect_doc(doc
)
1108 def visit(self
, visitor
: QAPISchemaVisitor
) -> None:
1109 super().visit(visitor
)
1110 visitor
.visit_event(
1111 self
.name
, self
.info
, self
.ifcond
, self
.features
,
1112 self
.arg_type
, self
.boxed
)
1116 def __init__(self
, fname
: str):
1120 parser
= QAPISchemaParser(fname
)
1121 except OSError as err
:
1123 f
"can't read schema file '{fname}': {err.strerror}"
1126 exprs
= check_exprs(parser
.exprs
)
1127 self
.docs
= parser
.docs
1128 self
._entity
_list
: List
[QAPISchemaEntity
] = []
1129 self
._entity
_dict
: Dict
[str, QAPISchemaDefinition
] = {}
1130 self
._module
_dict
: Dict
[str, QAPISchemaModule
] = OrderedDict()
1131 self
._schema
_dir
= os
.path
.dirname(fname
)
1132 self
._make
_module
(QAPISchemaModule
.BUILTIN_MODULE_NAME
)
1133 self
._make
_module
(fname
)
1134 self
._predefining
= True
1135 self
._def
_predefineds
()
1136 self
._predefining
= False
1137 self
._def
_exprs
(exprs
)
1140 def _def_entity(self
, ent
: QAPISchemaEntity
) -> None:
1141 self
._entity
_list
.append(ent
)
1143 def _def_definition(self
, defn
: QAPISchemaDefinition
) -> None:
1144 # Only the predefined types are allowed to not have info
1145 assert defn
.info
or self
._predefining
1146 self
._def
_entity
(defn
)
1147 # TODO reject names that differ only in '_' vs. '.' vs. '-',
1148 # because they're liable to clash in generated C.
1149 other_defn
= self
._entity
_dict
.get(defn
.name
)
1152 where
= QAPISourceError(other_defn
.info
, "previous definition")
1155 "'%s' is already defined\n%s" % (defn
.name
, where
))
1157 defn
.info
, "%s is already defined" % other_defn
.describe())
1158 self
._entity
_dict
[defn
.name
] = defn
1163 typ
: Optional
[type] = None,
1164 ) -> Optional
[QAPISchemaDefinition
]:
1165 ent
= self
._entity
_dict
.get(name
)
1166 if typ
and not isinstance(ent
, typ
):
1170 def lookup_type(self
, name
: str) -> Optional
[QAPISchemaType
]:
1171 typ
= self
.lookup_entity(name
, QAPISchemaType
)
1172 assert typ
is None or isinstance(typ
, QAPISchemaType
)
1178 info
: Optional
[QAPISourceInfo
],
1179 what
: Union
[None, str, Callable
[[QAPISourceInfo
], str]],
1180 ) -> QAPISchemaType
:
1181 typ
= self
.lookup_type(name
)
1183 assert info
and what
# built-in types must not fail lookup
1187 info
, "%s uses unknown type '%s'" % (what
, name
))
1190 def _module_name(self
, fname
: str) -> str:
1191 if QAPISchemaModule
.is_system_module(fname
):
1193 return os
.path
.relpath(fname
, self
._schema
_dir
)
1195 def _make_module(self
, fname
: str) -> QAPISchemaModule
:
1196 name
= self
._module
_name
(fname
)
1197 if name
not in self
._module
_dict
:
1198 self
._module
_dict
[name
] = QAPISchemaModule(name
)
1199 return self
._module
_dict
[name
]
1201 def module_by_fname(self
, fname
: str) -> QAPISchemaModule
:
1202 name
= self
._module
_name
(fname
)
1203 return self
._module
_dict
[name
]
1205 def _def_include(self
, expr
: QAPIExpression
) -> None:
1206 include
= expr
['include']
1207 assert expr
.doc
is None
1209 QAPISchemaInclude(self
._make
_module
(include
), expr
.info
))
1211 def _def_builtin_type(
1212 self
, name
: str, json_type
: str, c_type
: str
1214 self
._def
_definition
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1215 # Instantiating only the arrays that are actually used would
1216 # be nice, but we can't as long as their generated code
1217 # (qapi-builtin-types.[ch]) may be shared by some other
1219 self
._make
_array
_type
(name
, None)
1221 def _def_predefineds(self
) -> None:
1222 for t
in [('str', 'string', 'char' + POINTER_SUFFIX
),
1223 ('number', 'number', 'double'),
1224 ('int', 'int', 'int64_t'),
1225 ('int8', 'int', 'int8_t'),
1226 ('int16', 'int', 'int16_t'),
1227 ('int32', 'int', 'int32_t'),
1228 ('int64', 'int', 'int64_t'),
1229 ('uint8', 'int', 'uint8_t'),
1230 ('uint16', 'int', 'uint16_t'),
1231 ('uint32', 'int', 'uint32_t'),
1232 ('uint64', 'int', 'uint64_t'),
1233 ('size', 'int', 'uint64_t'),
1234 ('bool', 'boolean', 'bool'),
1235 ('any', 'value', 'QObject' + POINTER_SUFFIX
),
1236 ('null', 'null', 'QNull' + POINTER_SUFFIX
)]:
1237 self
._def
_builtin
_type
(*t
)
1238 self
.the_empty_object_type
= QAPISchemaObjectType(
1239 'q_empty', None, None, None, None, None, [], None)
1240 self
._def
_definition
(self
.the_empty_object_type
)
1242 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1244 qtype_values
= self
._make
_enum
_members
(
1245 [{'name': n
} for n
in qtypes
], None)
1247 self
._def
_definition
(QAPISchemaEnumType(
1248 'QType', None, None, None, None, qtype_values
, 'QTYPE'))
1252 features
: Optional
[List
[Dict
[str, Any
]]],
1253 info
: Optional
[QAPISourceInfo
],
1254 ) -> List
[QAPISchemaFeature
]:
1255 if features
is None:
1257 return [QAPISchemaFeature(f
['name'], info
,
1258 QAPISchemaIfCond(f
.get('if')))
1261 def _make_enum_member(
1264 ifcond
: Optional
[Union
[str, Dict
[str, Any
]]],
1265 features
: Optional
[List
[Dict
[str, Any
]]],
1266 info
: Optional
[QAPISourceInfo
],
1267 ) -> QAPISchemaEnumMember
:
1268 return QAPISchemaEnumMember(name
, info
,
1269 QAPISchemaIfCond(ifcond
),
1270 self
._make
_features
(features
, info
))
1272 def _make_enum_members(
1273 self
, values
: List
[Dict
[str, Any
]], info
: Optional
[QAPISourceInfo
]
1274 ) -> List
[QAPISchemaEnumMember
]:
1275 return [self
._make
_enum
_member
(v
['name'], v
.get('if'),
1276 v
.get('features'), info
)
1279 def _make_array_type(
1280 self
, element_type
: str, info
: Optional
[QAPISourceInfo
]
1282 name
= element_type
+ 'List' # reserved by check_defn_name_str()
1283 if not self
.lookup_type(name
):
1284 self
._def
_definition
(QAPISchemaArrayType(
1285 name
, info
, element_type
))
1288 def _make_implicit_object_type(
1291 info
: QAPISourceInfo
,
1292 ifcond
: QAPISchemaIfCond
,
1294 members
: List
[QAPISchemaObjectTypeMember
],
1298 # See also QAPISchemaObjectTypeMember.describe()
1299 name
= 'q_obj_%s-%s' % (name
, role
)
1300 typ
= self
.lookup_entity(name
)
1302 assert(isinstance(typ
, QAPISchemaObjectType
))
1303 # The implicit object type has multiple users. This can
1304 # only be a duplicate definition, which will be flagged
1308 self
._def
_definition
(QAPISchemaObjectType(
1309 name
, info
, None, ifcond
, None, None, members
, None))
1312 def _def_enum_type(self
, expr
: QAPIExpression
) -> None:
1315 prefix
= expr
.get('prefix')
1316 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1318 features
= self
._make
_features
(expr
.get('features'), info
)
1319 self
._def
_definition
(QAPISchemaEnumType(
1320 name
, info
, expr
.doc
, ifcond
, features
,
1321 self
._make
_enum
_members
(data
, info
), prefix
))
1326 typ
: Union
[List
[str], str],
1327 ifcond
: QAPISchemaIfCond
,
1328 features
: Optional
[List
[Dict
[str, Any
]]],
1329 info
: QAPISourceInfo
,
1330 ) -> QAPISchemaObjectTypeMember
:
1332 if name
.startswith('*'):
1335 if isinstance(typ
, list):
1336 assert len(typ
) == 1
1337 typ
= self
._make
_array
_type
(typ
[0], info
)
1338 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
,
1339 self
._make
_features
(features
, info
))
1343 data
: Dict
[str, Any
],
1344 info
: QAPISourceInfo
,
1345 ) -> List
[QAPISchemaObjectTypeMember
]:
1346 return [self
._make
_member
(key
, value
['type'],
1347 QAPISchemaIfCond(value
.get('if')),
1348 value
.get('features'), info
)
1349 for (key
, value
) in data
.items()]
1351 def _def_struct_type(self
, expr
: QAPIExpression
) -> None:
1352 name
= expr
['struct']
1353 base
= expr
.get('base')
1356 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1357 features
= self
._make
_features
(expr
.get('features'), info
)
1358 self
._def
_definition
(QAPISchemaObjectType(
1359 name
, info
, expr
.doc
, ifcond
, features
, base
,
1360 self
._make
_members
(data
, info
),
1367 ifcond
: QAPISchemaIfCond
,
1368 info
: QAPISourceInfo
,
1369 ) -> QAPISchemaVariant
:
1370 if isinstance(typ
, list):
1371 assert len(typ
) == 1
1372 typ
= self
._make
_array
_type
(typ
[0], info
)
1373 return QAPISchemaVariant(case
, info
, typ
, ifcond
)
1375 def _def_union_type(self
, expr
: QAPIExpression
) -> None:
1376 name
= expr
['union']
1378 tag_name
= expr
['discriminator']
1380 assert isinstance(data
, dict)
1382 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1383 features
= self
._make
_features
(expr
.get('features'), info
)
1384 if isinstance(base
, dict):
1385 base
= self
._make
_implicit
_object
_type
(
1387 'base', self
._make
_members
(base
, info
))
1389 self
._make
_variant
(key
, value
['type'],
1390 QAPISchemaIfCond(value
.get('if')),
1392 for (key
, value
) in data
.items()]
1393 members
: List
[QAPISchemaObjectTypeMember
] = []
1394 self
._def
_definition
(
1395 QAPISchemaObjectType(name
, info
, expr
.doc
, ifcond
, features
,
1398 tag_name
, info
, None, variants
)))
1400 def _def_alternate_type(self
, expr
: QAPIExpression
) -> None:
1401 name
= expr
['alternate']
1403 assert isinstance(data
, dict)
1404 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1406 features
= self
._make
_features
(expr
.get('features'), info
)
1408 self
._make
_variant
(key
, value
['type'],
1409 QAPISchemaIfCond(value
.get('if')),
1411 for (key
, value
) in data
.items()]
1412 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
1413 self
._def
_definition
(
1414 QAPISchemaAlternateType(
1415 name
, info
, expr
.doc
, ifcond
, features
,
1416 QAPISchemaVariants(None, info
, tag_member
, variants
)))
1418 def _def_command(self
, expr
: QAPIExpression
) -> None:
1419 name
= expr
['command']
1420 data
= expr
.get('data')
1421 rets
= expr
.get('returns')
1422 gen
= expr
.get('gen', True)
1423 success_response
= expr
.get('success-response', True)
1424 boxed
= expr
.get('boxed', False)
1425 allow_oob
= expr
.get('allow-oob', False)
1426 allow_preconfig
= expr
.get('allow-preconfig', False)
1427 coroutine
= expr
.get('coroutine', False)
1428 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1430 features
= self
._make
_features
(expr
.get('features'), info
)
1431 if isinstance(data
, OrderedDict
):
1432 data
= self
._make
_implicit
_object
_type
(
1434 'arg', self
._make
_members
(data
, info
))
1435 if isinstance(rets
, list):
1436 assert len(rets
) == 1
1437 rets
= self
._make
_array
_type
(rets
[0], info
)
1438 self
._def
_definition
(
1439 QAPISchemaCommand(name
, info
, expr
.doc
, ifcond
, features
, data
,
1440 rets
, gen
, success_response
, boxed
, allow_oob
,
1441 allow_preconfig
, coroutine
))
1443 def _def_event(self
, expr
: QAPIExpression
) -> None:
1444 name
= expr
['event']
1445 data
= expr
.get('data')
1446 boxed
= expr
.get('boxed', False)
1447 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1449 features
= self
._make
_features
(expr
.get('features'), info
)
1450 if isinstance(data
, OrderedDict
):
1451 data
= self
._make
_implicit
_object
_type
(
1453 'arg', self
._make
_members
(data
, info
))
1454 self
._def
_definition
(QAPISchemaEvent(name
, info
, expr
.doc
, ifcond
,
1455 features
, data
, boxed
))
1457 def _def_exprs(self
, exprs
: List
[QAPIExpression
]) -> None:
1460 self
._def
_enum
_type
(expr
)
1461 elif 'struct' in expr
:
1462 self
._def
_struct
_type
(expr
)
1463 elif 'union' in expr
:
1464 self
._def
_union
_type
(expr
)
1465 elif 'alternate' in expr
:
1466 self
._def
_alternate
_type
(expr
)
1467 elif 'command' in expr
:
1468 self
._def
_command
(expr
)
1469 elif 'event' in expr
:
1470 self
._def
_event
(expr
)
1471 elif 'include' in expr
:
1472 self
._def
_include
(expr
)
1476 def check(self
) -> None:
1477 for ent
in self
._entity
_list
:
1480 for ent
in self
._entity
_list
:
1481 ent
.set_module(self
)
1482 for doc
in self
.docs
:
1485 def visit(self
, visitor
: QAPISchemaVisitor
) -> None:
1486 visitor
.visit_begin(self
)
1487 for mod
in self
._module
_dict
.values():