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 branches
: Optional
[QAPISchemaBranches
],
222 def visit_object_type_flat(
225 info
: Optional
[QAPISourceInfo
],
226 ifcond
: QAPISchemaIfCond
,
227 features
: List
[QAPISchemaFeature
],
228 members
: List
[QAPISchemaObjectTypeMember
],
229 branches
: Optional
[QAPISchemaBranches
],
233 def visit_alternate_type(
236 info
: Optional
[QAPISourceInfo
],
237 ifcond
: QAPISchemaIfCond
,
238 features
: List
[QAPISchemaFeature
],
239 alternatives
: 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 branches
: Optional
[QAPISchemaBranches
],
529 # struct has local_members, optional base, and no branches
530 # union has base, branches, and no local_members
531 super().__init
__(name
, info
, doc
, ifcond
, features
)
532 self
.meta
= 'union' if branches
else 'struct'
533 for m
in local_members
:
534 m
.set_defined_in(name
)
535 if branches
is not None:
536 branches
.set_defined_in(name
)
537 self
._base
_name
= base
539 self
.local_members
= local_members
540 self
.branches
= branches
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
.branches
):
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
.branches
.check(schema
, seen
)
582 self
.branches
.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
.branches
.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
.branches
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
.branches
)
639 visitor
.visit_object_type_flat(
640 self
.name
, self
.info
, self
.ifcond
, self
.features
,
641 self
.members
, self
.branches
)
644 class QAPISchemaAlternateType(QAPISchemaType
):
650 info
: QAPISourceInfo
,
651 doc
: Optional
[QAPIDoc
],
652 ifcond
: Optional
[QAPISchemaIfCond
],
653 features
: List
[QAPISchemaFeature
],
654 alternatives
: QAPISchemaAlternatives
,
656 super().__init
__(name
, info
, doc
, ifcond
, features
)
657 assert alternatives
.tag_member
658 alternatives
.set_defined_in(name
)
659 alternatives
.tag_member
.set_defined_in(self
.name
)
660 self
.alternatives
= alternatives
662 def check(self
, schema
: QAPISchema
) -> None:
663 super().check(schema
)
664 self
.alternatives
.tag_member
.check(schema
)
665 # Not calling self.alternatives.check_clash(), because there's
666 # nothing to clash with
667 self
.alternatives
.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
.alternatives
.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
.alternatives
.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
,
719 class QAPISchemaVariants
:
722 info
: QAPISourceInfo
,
723 variants
: List
[QAPISchemaVariant
],
726 self
._tag
_member
: Optional
[QAPISchemaObjectTypeMember
] = None
727 self
.variants
= variants
730 def tag_member(self
) -> QAPISchemaObjectTypeMember
:
731 if self
._tag
_member
is None:
733 "QAPISchemaVariants has no tag_member property until "
734 "after check() has been run."
736 return self
._tag
_member
738 def set_defined_in(self
, name
: str) -> None:
739 for v
in self
.variants
:
740 v
.set_defined_in(name
)
743 self
, schema
: QAPISchema
, seen
: Dict
[str, QAPISchemaMember
]
745 for v
in self
.variants
:
749 class QAPISchemaBranches(QAPISchemaVariants
):
751 info
: QAPISourceInfo
,
752 variants
: List
[QAPISchemaVariant
],
754 super().__init
__(info
, variants
)
755 self
._tag
_name
= tag_name
758 self
, schema
: QAPISchema
, seen
: Dict
[str, QAPISchemaMember
]
760 # We need to narrow the member type:
761 tmp
= seen
.get(c_name(self
._tag
_name
))
762 assert tmp
is None or isinstance(tmp
, QAPISchemaObjectTypeMember
)
763 self
._tag
_member
= tmp
766 # Pointing to the base type when not implicit would be
767 # nice, but we don't know it here
768 if not self
._tag
_member
or self
._tag
_name
!= self
._tag
_member
.name
:
771 "discriminator '%s' is not a member of %s"
772 % (self
._tag
_name
, base
))
774 assert self
.tag_member
.defined_in
775 base_type
= schema
.lookup_type(self
.tag_member
.defined_in
)
777 if not base_type
.is_implicit():
778 base
= "base type '%s'" % self
.tag_member
.defined_in
779 if not isinstance(self
.tag_member
.type, QAPISchemaEnumType
):
782 "discriminator member '%s' of %s must be of enum type"
783 % (self
._tag
_name
, base
))
784 if self
.tag_member
.optional
:
787 "discriminator member '%s' of %s must not be optional"
788 % (self
._tag
_name
, base
))
789 if self
.tag_member
.ifcond
.is_present():
792 "discriminator member '%s' of %s must not be conditional"
793 % (self
._tag
_name
, base
))
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 QAPISchemaAlternatives(QAPISchemaVariants
):
838 info
: QAPISourceInfo
,
839 variants
: List
[QAPISchemaVariant
],
840 tag_member
: QAPISchemaObjectTypeMember
):
841 super().__init
__(info
, variants
)
842 self
._tag
_member
= tag_member
845 self
, schema
: QAPISchema
, seen
: Dict
[str, QAPISchemaMember
]
847 super().check(schema
, seen
)
848 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
849 assert not self
.tag_member
.optional
850 assert not self
.tag_member
.ifcond
.is_present()
853 class QAPISchemaMember
:
854 """ Represents object members, enum members and features """
860 info
: Optional
[QAPISourceInfo
],
861 ifcond
: Optional
[QAPISchemaIfCond
] = None,
865 self
.ifcond
= ifcond
or QAPISchemaIfCond()
866 self
.defined_in
: Optional
[str] = None
868 def set_defined_in(self
, name
: str) -> None:
869 assert not self
.defined_in
870 self
.defined_in
= name
874 info
: Optional
[QAPISourceInfo
],
875 seen
: Dict
[str, QAPISchemaMember
],
877 cname
= c_name(self
.name
)
881 "%s collides with %s"
882 % (self
.describe(info
), seen
[cname
].describe(info
)))
885 def connect_doc(self
, doc
: Optional
[QAPIDoc
]) -> None:
887 doc
.connect_member(self
)
889 def describe(self
, info
: Optional
[QAPISourceInfo
]) -> str:
892 defined_in
= self
.defined_in
895 if defined_in
.startswith('q_obj_'):
896 # See QAPISchema._make_implicit_object_type() - reverse the
897 # mapping there to create a nice human-readable description
898 defined_in
= defined_in
[6:]
899 if defined_in
.endswith('-arg'):
900 # Implicit type created for a command's dict 'data'
901 assert role
== 'member'
904 defined_in
= defined_in
[:-4]
905 elif defined_in
.endswith('-base'):
906 # Implicit type created for a union's dict 'base'
907 role
= 'base ' + role
908 defined_in
= defined_in
[:-5]
912 assert info
is not None
913 if defined_in
!= info
.defn_name
:
914 return "%s '%s' of %s '%s'" % (role
, self
.name
, meta
, defined_in
)
915 return "%s '%s'" % (role
, self
.name
)
918 class QAPISchemaEnumMember(QAPISchemaMember
):
924 info
: Optional
[QAPISourceInfo
],
925 ifcond
: Optional
[QAPISchemaIfCond
] = None,
926 features
: Optional
[List
[QAPISchemaFeature
]] = None,
928 super().__init
__(name
, info
, ifcond
)
929 for f
in features
or []:
930 f
.set_defined_in(name
)
931 self
.features
= features
or []
933 def connect_doc(self
, doc
: Optional
[QAPIDoc
]) -> None:
934 super().connect_doc(doc
)
936 for f
in self
.features
:
937 doc
.connect_feature(f
)
940 class QAPISchemaFeature(QAPISchemaMember
):
943 def is_special(self
) -> bool:
944 return self
.name
in ('deprecated', 'unstable')
947 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
951 info
: QAPISourceInfo
,
954 ifcond
: Optional
[QAPISchemaIfCond
] = None,
955 features
: Optional
[List
[QAPISchemaFeature
]] = None,
957 super().__init
__(name
, info
, ifcond
)
958 for f
in features
or []:
959 f
.set_defined_in(name
)
960 self
._type
_name
= typ
961 self
.type: QAPISchemaType
# set during check()
962 self
.optional
= optional
963 self
.features
= features
or []
965 def need_has(self
) -> bool:
966 return self
.optional
and self
.type.need_has_if_optional()
968 def check(self
, schema
: QAPISchema
) -> None:
969 assert self
.defined_in
970 self
.type = schema
.resolve_type(self
._type
_name
, self
.info
,
972 seen
: Dict
[str, QAPISchemaMember
] = {}
973 for f
in self
.features
:
974 f
.check_clash(self
.info
, seen
)
976 def connect_doc(self
, doc
: Optional
[QAPIDoc
]) -> None:
977 super().connect_doc(doc
)
979 for f
in self
.features
:
980 doc
.connect_feature(f
)
983 class QAPISchemaVariant(QAPISchemaObjectTypeMember
):
989 info
: QAPISourceInfo
,
991 ifcond
: QAPISchemaIfCond
,
993 super().__init
__(name
, info
, typ
, False, ifcond
)
996 class QAPISchemaCommand(QAPISchemaDefinition
):
1002 info
: QAPISourceInfo
,
1003 doc
: Optional
[QAPIDoc
],
1004 ifcond
: QAPISchemaIfCond
,
1005 features
: List
[QAPISchemaFeature
],
1006 arg_type
: Optional
[str],
1007 ret_type
: Optional
[str],
1009 success_response
: bool,
1012 allow_preconfig
: bool,
1015 super().__init
__(name
, info
, doc
, ifcond
, features
)
1016 self
._arg
_type
_name
= arg_type
1017 self
.arg_type
: Optional
[QAPISchemaObjectType
] = None
1018 self
._ret
_type
_name
= ret_type
1019 self
.ret_type
: Optional
[QAPISchemaType
] = None
1021 self
.success_response
= success_response
1023 self
.allow_oob
= allow_oob
1024 self
.allow_preconfig
= allow_preconfig
1025 self
.coroutine
= coroutine
1027 def check(self
, schema
: QAPISchema
) -> None:
1028 assert self
.info
is not None
1029 super().check(schema
)
1030 if self
._arg
_type
_name
:
1031 arg_type
= schema
.resolve_type(
1032 self
._arg
_type
_name
, self
.info
, "command's 'data'")
1033 if not isinstance(arg_type
, QAPISchemaObjectType
):
1036 "command's 'data' cannot take %s"
1037 % arg_type
.describe())
1038 self
.arg_type
= arg_type
1039 if self
.arg_type
.branches
and not self
.boxed
:
1042 "command's 'data' can take %s only with 'boxed': true"
1043 % self
.arg_type
.describe())
1044 self
.arg_type
.check(schema
)
1045 if self
.arg_type
.has_conditional_members() and not self
.boxed
:
1048 "conditional command arguments require 'boxed': true")
1049 if self
._ret
_type
_name
:
1050 self
.ret_type
= schema
.resolve_type(
1051 self
._ret
_type
_name
, self
.info
, "command's 'returns'")
1052 if self
.name
not in self
.info
.pragma
.command_returns_exceptions
:
1054 if isinstance(typ
, QAPISchemaArrayType
):
1055 typ
= typ
.element_type
1056 if not isinstance(typ
, QAPISchemaObjectType
):
1059 "command's 'returns' cannot take %s"
1060 % self
.ret_type
.describe())
1062 def connect_doc(self
, doc
: Optional
[QAPIDoc
] = None) -> None:
1063 super().connect_doc(doc
)
1064 doc
= doc
or self
.doc
1066 if self
.arg_type
and self
.arg_type
.is_implicit():
1067 self
.arg_type
.connect_doc(doc
)
1069 def visit(self
, visitor
: QAPISchemaVisitor
) -> None:
1070 super().visit(visitor
)
1071 visitor
.visit_command(
1072 self
.name
, self
.info
, self
.ifcond
, self
.features
,
1073 self
.arg_type
, self
.ret_type
, self
.gen
, self
.success_response
,
1074 self
.boxed
, self
.allow_oob
, self
.allow_preconfig
,
1078 class QAPISchemaEvent(QAPISchemaDefinition
):
1084 info
: QAPISourceInfo
,
1085 doc
: Optional
[QAPIDoc
],
1086 ifcond
: QAPISchemaIfCond
,
1087 features
: List
[QAPISchemaFeature
],
1088 arg_type
: Optional
[str],
1091 super().__init
__(name
, info
, doc
, ifcond
, features
)
1092 self
._arg
_type
_name
= arg_type
1093 self
.arg_type
: Optional
[QAPISchemaObjectType
] = None
1096 def check(self
, schema
: QAPISchema
) -> None:
1097 super().check(schema
)
1098 if self
._arg
_type
_name
:
1099 typ
= schema
.resolve_type(
1100 self
._arg
_type
_name
, self
.info
, "event's 'data'")
1101 if not isinstance(typ
, QAPISchemaObjectType
):
1104 "event's 'data' cannot take %s"
1107 if self
.arg_type
.branches
and not self
.boxed
:
1110 "event's 'data' can take %s only with 'boxed': true"
1111 % self
.arg_type
.describe())
1112 self
.arg_type
.check(schema
)
1113 if self
.arg_type
.has_conditional_members() and not self
.boxed
:
1116 "conditional event arguments require 'boxed': true")
1118 def connect_doc(self
, doc
: Optional
[QAPIDoc
] = None) -> None:
1119 super().connect_doc(doc
)
1120 doc
= doc
or self
.doc
1122 if self
.arg_type
and self
.arg_type
.is_implicit():
1123 self
.arg_type
.connect_doc(doc
)
1125 def visit(self
, visitor
: QAPISchemaVisitor
) -> None:
1126 super().visit(visitor
)
1127 visitor
.visit_event(
1128 self
.name
, self
.info
, self
.ifcond
, self
.features
,
1129 self
.arg_type
, self
.boxed
)
1133 def __init__(self
, fname
: str):
1137 parser
= QAPISchemaParser(fname
)
1138 except OSError as err
:
1140 f
"can't read schema file '{fname}': {err.strerror}"
1143 exprs
= check_exprs(parser
.exprs
)
1144 self
.docs
= parser
.docs
1145 self
._entity
_list
: List
[QAPISchemaEntity
] = []
1146 self
._entity
_dict
: Dict
[str, QAPISchemaDefinition
] = {}
1147 self
._module
_dict
: Dict
[str, QAPISchemaModule
] = OrderedDict()
1148 self
._schema
_dir
= os
.path
.dirname(fname
)
1149 self
._make
_module
(QAPISchemaModule
.BUILTIN_MODULE_NAME
)
1150 self
._make
_module
(fname
)
1151 self
._predefining
= True
1152 self
._def
_predefineds
()
1153 self
._predefining
= False
1154 self
._def
_exprs
(exprs
)
1157 def _def_entity(self
, ent
: QAPISchemaEntity
) -> None:
1158 self
._entity
_list
.append(ent
)
1160 def _def_definition(self
, defn
: QAPISchemaDefinition
) -> None:
1161 # Only the predefined types are allowed to not have info
1162 assert defn
.info
or self
._predefining
1163 self
._def
_entity
(defn
)
1164 # TODO reject names that differ only in '_' vs. '.' vs. '-',
1165 # because they're liable to clash in generated C.
1166 other_defn
= self
._entity
_dict
.get(defn
.name
)
1169 where
= QAPISourceError(other_defn
.info
, "previous definition")
1172 "'%s' is already defined\n%s" % (defn
.name
, where
))
1174 defn
.info
, "%s is already defined" % other_defn
.describe())
1175 self
._entity
_dict
[defn
.name
] = defn
1177 def lookup_entity(self
,name
: str) -> Optional
[QAPISchemaEntity
]:
1178 return self
._entity
_dict
.get(name
)
1180 def lookup_type(self
, name
: str) -> Optional
[QAPISchemaType
]:
1181 typ
= self
.lookup_entity(name
)
1182 if isinstance(typ
, QAPISchemaType
):
1189 info
: Optional
[QAPISourceInfo
],
1190 what
: Union
[None, str, Callable
[[QAPISourceInfo
], str]],
1191 ) -> QAPISchemaType
:
1192 typ
= self
.lookup_type(name
)
1194 assert info
and what
# built-in types must not fail lookup
1198 info
, "%s uses unknown type '%s'" % (what
, name
))
1201 def _module_name(self
, fname
: str) -> str:
1202 if QAPISchemaModule
.is_system_module(fname
):
1204 return os
.path
.relpath(fname
, self
._schema
_dir
)
1206 def _make_module(self
, fname
: str) -> QAPISchemaModule
:
1207 name
= self
._module
_name
(fname
)
1208 if name
not in self
._module
_dict
:
1209 self
._module
_dict
[name
] = QAPISchemaModule(name
)
1210 return self
._module
_dict
[name
]
1212 def module_by_fname(self
, fname
: str) -> QAPISchemaModule
:
1213 name
= self
._module
_name
(fname
)
1214 return self
._module
_dict
[name
]
1216 def _def_include(self
, expr
: QAPIExpression
) -> None:
1217 include
= expr
['include']
1218 assert expr
.doc
is None
1220 QAPISchemaInclude(self
._make
_module
(include
), expr
.info
))
1222 def _def_builtin_type(
1223 self
, name
: str, json_type
: str, c_type
: str
1225 self
._def
_definition
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1226 # Instantiating only the arrays that are actually used would
1227 # be nice, but we can't as long as their generated code
1228 # (qapi-builtin-types.[ch]) may be shared by some other
1230 self
._make
_array
_type
(name
, None)
1232 def _def_predefineds(self
) -> None:
1233 for t
in [('str', 'string', 'char' + POINTER_SUFFIX
),
1234 ('number', 'number', 'double'),
1235 ('int', 'int', 'int64_t'),
1236 ('int8', 'int', 'int8_t'),
1237 ('int16', 'int', 'int16_t'),
1238 ('int32', 'int', 'int32_t'),
1239 ('int64', 'int', 'int64_t'),
1240 ('uint8', 'int', 'uint8_t'),
1241 ('uint16', 'int', 'uint16_t'),
1242 ('uint32', 'int', 'uint32_t'),
1243 ('uint64', 'int', 'uint64_t'),
1244 ('size', 'int', 'uint64_t'),
1245 ('bool', 'boolean', 'bool'),
1246 ('any', 'value', 'QObject' + POINTER_SUFFIX
),
1247 ('null', 'null', 'QNull' + POINTER_SUFFIX
)]:
1248 self
._def
_builtin
_type
(*t
)
1249 self
.the_empty_object_type
= QAPISchemaObjectType(
1250 'q_empty', None, None, None, None, None, [], None)
1251 self
._def
_definition
(self
.the_empty_object_type
)
1253 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1255 qtype_values
= self
._make
_enum
_members
(
1256 [{'name': n
} for n
in qtypes
], None)
1258 self
._def
_definition
(QAPISchemaEnumType(
1259 'QType', None, None, None, None, qtype_values
, 'QTYPE'))
1263 features
: Optional
[List
[Dict
[str, Any
]]],
1264 info
: Optional
[QAPISourceInfo
],
1265 ) -> List
[QAPISchemaFeature
]:
1266 if features
is None:
1268 return [QAPISchemaFeature(f
['name'], info
,
1269 QAPISchemaIfCond(f
.get('if')))
1272 def _make_enum_member(
1275 ifcond
: Optional
[Union
[str, Dict
[str, Any
]]],
1276 features
: Optional
[List
[Dict
[str, Any
]]],
1277 info
: Optional
[QAPISourceInfo
],
1278 ) -> QAPISchemaEnumMember
:
1279 return QAPISchemaEnumMember(name
, info
,
1280 QAPISchemaIfCond(ifcond
),
1281 self
._make
_features
(features
, info
))
1283 def _make_enum_members(
1284 self
, values
: List
[Dict
[str, Any
]], info
: Optional
[QAPISourceInfo
]
1285 ) -> List
[QAPISchemaEnumMember
]:
1286 return [self
._make
_enum
_member
(v
['name'], v
.get('if'),
1287 v
.get('features'), info
)
1290 def _make_array_type(
1291 self
, element_type
: str, info
: Optional
[QAPISourceInfo
]
1293 name
= element_type
+ 'List' # reserved by check_defn_name_str()
1294 if not self
.lookup_type(name
):
1295 self
._def
_definition
(QAPISchemaArrayType(
1296 name
, info
, element_type
))
1299 def _make_implicit_object_type(
1302 info
: QAPISourceInfo
,
1303 ifcond
: QAPISchemaIfCond
,
1305 members
: List
[QAPISchemaObjectTypeMember
],
1309 # See also QAPISchemaObjectTypeMember.describe()
1310 name
= 'q_obj_%s-%s' % (name
, role
)
1311 typ
= self
.lookup_entity(name
)
1313 assert(isinstance(typ
, QAPISchemaObjectType
))
1314 # The implicit object type has multiple users. This can
1315 # only be a duplicate definition, which will be flagged
1319 self
._def
_definition
(QAPISchemaObjectType(
1320 name
, info
, None, ifcond
, None, None, members
, None))
1323 def _def_enum_type(self
, expr
: QAPIExpression
) -> None:
1326 prefix
= expr
.get('prefix')
1327 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1329 features
= self
._make
_features
(expr
.get('features'), info
)
1330 self
._def
_definition
(QAPISchemaEnumType(
1331 name
, info
, expr
.doc
, ifcond
, features
,
1332 self
._make
_enum
_members
(data
, info
), prefix
))
1337 typ
: Union
[List
[str], str],
1338 ifcond
: QAPISchemaIfCond
,
1339 features
: Optional
[List
[Dict
[str, Any
]]],
1340 info
: QAPISourceInfo
,
1341 ) -> QAPISchemaObjectTypeMember
:
1343 if name
.startswith('*'):
1346 if isinstance(typ
, list):
1347 assert len(typ
) == 1
1348 typ
= self
._make
_array
_type
(typ
[0], info
)
1349 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
,
1350 self
._make
_features
(features
, info
))
1354 data
: Dict
[str, Any
],
1355 info
: QAPISourceInfo
,
1356 ) -> List
[QAPISchemaObjectTypeMember
]:
1357 return [self
._make
_member
(key
, value
['type'],
1358 QAPISchemaIfCond(value
.get('if')),
1359 value
.get('features'), info
)
1360 for (key
, value
) in data
.items()]
1362 def _def_struct_type(self
, expr
: QAPIExpression
) -> None:
1363 name
= expr
['struct']
1364 base
= expr
.get('base')
1367 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1368 features
= self
._make
_features
(expr
.get('features'), info
)
1369 self
._def
_definition
(QAPISchemaObjectType(
1370 name
, info
, expr
.doc
, ifcond
, features
, base
,
1371 self
._make
_members
(data
, info
),
1378 ifcond
: QAPISchemaIfCond
,
1379 info
: QAPISourceInfo
,
1380 ) -> QAPISchemaVariant
:
1381 if isinstance(typ
, list):
1382 assert len(typ
) == 1
1383 typ
= self
._make
_array
_type
(typ
[0], info
)
1384 return QAPISchemaVariant(case
, info
, typ
, ifcond
)
1386 def _def_union_type(self
, expr
: QAPIExpression
) -> None:
1387 name
= expr
['union']
1389 tag_name
= expr
['discriminator']
1391 assert isinstance(data
, dict)
1393 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1394 features
= self
._make
_features
(expr
.get('features'), info
)
1395 if isinstance(base
, dict):
1396 base
= self
._make
_implicit
_object
_type
(
1398 'base', self
._make
_members
(base
, info
))
1400 self
._make
_variant
(key
, value
['type'],
1401 QAPISchemaIfCond(value
.get('if')),
1403 for (key
, value
) in data
.items()]
1404 members
: List
[QAPISchemaObjectTypeMember
] = []
1405 self
._def
_definition
(
1406 QAPISchemaObjectType(name
, info
, expr
.doc
, ifcond
, features
,
1409 info
, variants
, tag_name
)))
1411 def _def_alternate_type(self
, expr
: QAPIExpression
) -> None:
1412 name
= expr
['alternate']
1414 assert isinstance(data
, dict)
1415 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1417 features
= self
._make
_features
(expr
.get('features'), info
)
1419 self
._make
_variant
(key
, value
['type'],
1420 QAPISchemaIfCond(value
.get('if')),
1422 for (key
, value
) in data
.items()]
1423 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
1424 self
._def
_definition
(
1425 QAPISchemaAlternateType(
1426 name
, info
, expr
.doc
, ifcond
, features
,
1427 QAPISchemaAlternatives(info
, variants
, tag_member
)))
1429 def _def_command(self
, expr
: QAPIExpression
) -> None:
1430 name
= expr
['command']
1431 data
= expr
.get('data')
1432 rets
= expr
.get('returns')
1433 gen
= expr
.get('gen', True)
1434 success_response
= expr
.get('success-response', True)
1435 boxed
= expr
.get('boxed', False)
1436 allow_oob
= expr
.get('allow-oob', False)
1437 allow_preconfig
= expr
.get('allow-preconfig', False)
1438 coroutine
= expr
.get('coroutine', False)
1439 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1441 features
= self
._make
_features
(expr
.get('features'), info
)
1442 if isinstance(data
, OrderedDict
):
1443 data
= self
._make
_implicit
_object
_type
(
1445 'arg', self
._make
_members
(data
, info
))
1446 if isinstance(rets
, list):
1447 assert len(rets
) == 1
1448 rets
= self
._make
_array
_type
(rets
[0], info
)
1449 self
._def
_definition
(
1450 QAPISchemaCommand(name
, info
, expr
.doc
, ifcond
, features
, data
,
1451 rets
, gen
, success_response
, boxed
, allow_oob
,
1452 allow_preconfig
, coroutine
))
1454 def _def_event(self
, expr
: QAPIExpression
) -> None:
1455 name
= expr
['event']
1456 data
= expr
.get('data')
1457 boxed
= expr
.get('boxed', False)
1458 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1460 features
= self
._make
_features
(expr
.get('features'), info
)
1461 if isinstance(data
, OrderedDict
):
1462 data
= self
._make
_implicit
_object
_type
(
1464 'arg', self
._make
_members
(data
, info
))
1465 self
._def
_definition
(QAPISchemaEvent(name
, info
, expr
.doc
, ifcond
,
1466 features
, data
, boxed
))
1468 def _def_exprs(self
, exprs
: List
[QAPIExpression
]) -> None:
1471 self
._def
_enum
_type
(expr
)
1472 elif 'struct' in expr
:
1473 self
._def
_struct
_type
(expr
)
1474 elif 'union' in expr
:
1475 self
._def
_union
_type
(expr
)
1476 elif 'alternate' in expr
:
1477 self
._def
_alternate
_type
(expr
)
1478 elif 'command' in expr
:
1479 self
._def
_command
(expr
)
1480 elif 'event' in expr
:
1481 self
._def
_event
(expr
)
1482 elif 'include' in expr
:
1483 self
._def
_include
(expr
)
1487 def check(self
) -> None:
1488 for ent
in self
._entity
_list
:
1491 for ent
in self
._entity
_list
:
1492 ent
.set_module(self
)
1493 for doc
in self
.docs
:
1496 def visit(self
, visitor
: QAPISchemaVisitor
) -> None:
1497 visitor
.visit_begin(self
)
1498 for mod
in self
._module
_dict
.values():