1 # -*- coding: utf-8 -*-
3 # QAPI schema internal representation
5 # Copyright (c) 2015-2019 Red Hat Inc.
8 # Markus Armbruster <armbru@redhat.com>
9 # Eric Blake <eblake@redhat.com>
10 # Marc-André Lureau <marcandre.lureau@redhat.com>
12 # This work is licensed under the terms of the GNU GPL, version 2.
13 # See the COPYING file in the top-level directory.
15 # TODO catching name collisions in generated code would be nice
17 from collections
import OrderedDict
20 from typing
import List
, Optional
30 from .error
import QAPIError
, QAPISemError
, QAPISourceError
31 from .expr
import check_exprs
32 from .parser
import QAPIExpression
, QAPISchemaParser
35 class QAPISchemaIfCond
:
36 def __init__(self
, ifcond
=None):
40 return cgen_ifcond(self
.ifcond
)
43 return gen_if(self
._cgen
())
46 return gen_endif(self
._cgen
())
49 return docgen_ifcond(self
.ifcond
)
52 return bool(self
.ifcond
)
55 class QAPISchemaEntity
:
56 meta
: Optional
[str] = None
58 def __init__(self
, name
: str, info
, doc
, ifcond
=None, features
=None):
59 assert name
is None or isinstance(name
, str)
60 for f
in features
or []:
61 assert isinstance(f
, QAPISchemaFeature
)
62 f
.set_defined_in(name
)
65 # For explicitly defined entities, info points to the (explicit)
66 # definition. For builtins (and their arrays), info is None.
67 # For implicitly defined entities, info points to a place that
68 # triggered the implicit definition (there may be more than one
72 self
._ifcond
= ifcond
or QAPISchemaIfCond()
73 self
.features
= features
or []
78 return "<%s at 0x%x>" % (type(self
).__name
__, id(self
))
79 return "<%s:%s at 0x%x>" % (type(self
).__name
__, self
.name
,
83 return c_name(self
.name
)
85 def check(self
, schema
):
86 assert not self
._checked
88 for f
in self
.features
:
89 f
.check_clash(self
.info
, seen
)
92 def connect_doc(self
, doc
=None):
95 for f
in self
.features
:
96 doc
.connect_feature(f
)
98 def _set_module(self
, schema
, info
):
100 fname
= info
.fname
if info
else QAPISchemaModule
.BUILTIN_MODULE_NAME
101 self
._module
= schema
.module_by_fname(fname
)
102 self
._module
.add_entity(self
)
104 def set_module(self
, schema
):
105 self
._set
_module
(schema
, self
.info
)
112 def is_implicit(self
):
115 def visit(self
, visitor
):
120 return "%s '%s'" % (self
.meta
, self
.name
)
123 class QAPISchemaVisitor
:
124 def visit_begin(self
, schema
):
130 def visit_module(self
, name
):
133 def visit_needed(self
, entity
):
134 # Default to visiting everything
137 def visit_include(self
, name
, info
):
140 def visit_builtin_type(self
, name
, info
, json_type
):
143 def visit_enum_type(self
, name
, info
, ifcond
, features
, members
, prefix
):
146 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
149 def visit_object_type(self
, name
, info
, ifcond
, features
,
150 base
, members
, variants
):
153 def visit_object_type_flat(self
, name
, info
, ifcond
, features
,
157 def visit_alternate_type(self
, name
, info
, ifcond
, features
, variants
):
160 def visit_command(self
, name
, info
, ifcond
, features
,
161 arg_type
, ret_type
, gen
, success_response
, boxed
,
162 allow_oob
, allow_preconfig
, coroutine
):
165 def visit_event(self
, name
, info
, ifcond
, features
, arg_type
, boxed
):
169 class QAPISchemaModule
:
171 BUILTIN_MODULE_NAME
= './builtin'
173 def __init__(self
, name
):
175 self
._entity
_list
= []
178 def is_system_module(name
: str) -> bool:
180 System modules are internally defined modules.
182 Their names start with the "./" prefix.
184 return name
.startswith('./')
187 def is_user_module(cls
, name
: str) -> bool:
189 User modules are those defined by the user in qapi JSON files.
191 They do not start with the "./" prefix.
193 return not cls
.is_system_module(name
)
196 def is_builtin_module(cls
, name
: str) -> bool:
198 The built-in module is a single System module for the built-in types.
200 It is always "./builtin".
202 return name
== cls
.BUILTIN_MODULE_NAME
204 def add_entity(self
, ent
):
205 self
._entity
_list
.append(ent
)
207 def visit(self
, visitor
):
208 visitor
.visit_module(self
.name
)
209 for entity
in self
._entity
_list
:
210 if visitor
.visit_needed(entity
):
211 entity
.visit(visitor
)
214 class QAPISchemaInclude(QAPISchemaEntity
):
215 def __init__(self
, sub_module
, info
):
216 super().__init
__(None, info
, None)
217 self
._sub
_module
= sub_module
219 def visit(self
, visitor
):
220 super().visit(visitor
)
221 visitor
.visit_include(self
._sub
_module
.name
, self
.info
)
224 class QAPISchemaType(QAPISchemaEntity
):
225 # Return the C type for common use.
226 # For the types we commonly box, this is a pointer type.
230 # Return the C type to be used in a parameter list.
231 def c_param_type(self
):
234 # Return the C type to be used where we suppress boxing.
235 def c_unboxed_type(self
):
241 def alternate_qtype(self
):
243 'null': 'QTYPE_QNULL',
244 'string': 'QTYPE_QSTRING',
245 'number': 'QTYPE_QNUM',
247 'boolean': 'QTYPE_QBOOL',
248 'array': 'QTYPE_QLIST',
249 'object': 'QTYPE_QDICT'
251 return json2qtype
.get(self
.json_type())
254 if self
.is_implicit():
258 def need_has_if_optional(self
):
259 # When FOO is a pointer, has_FOO == !!FOO, i.e. has_FOO is redundant.
260 # Except for arrays; see QAPISchemaArrayType.need_has_if_optional().
261 return not self
.c_type().endswith(POINTER_SUFFIX
)
263 def check(self
, schema
):
264 super().check(schema
)
265 for feat
in self
.features
:
266 if feat
.is_special():
269 f
"feature '{feat.name}' is not supported for types")
273 return "%s type '%s'" % (self
.meta
, self
.name
)
276 class QAPISchemaBuiltinType(QAPISchemaType
):
279 def __init__(self
, name
, json_type
, c_type
):
280 super().__init
__(name
, None, None)
281 assert not c_type
or isinstance(c_type
, str)
282 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
284 self
._json
_type
_name
= json_type
285 self
._c
_type
_name
= c_type
291 return self
._c
_type
_name
293 def c_param_type(self
):
294 if self
.name
== 'str':
295 return 'const ' + self
._c
_type
_name
296 return self
._c
_type
_name
299 return self
._json
_type
_name
302 return self
.json_type()
304 def visit(self
, visitor
):
305 super().visit(visitor
)
306 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
309 class QAPISchemaEnumType(QAPISchemaType
):
312 def __init__(self
, name
, info
, doc
, ifcond
, features
, members
, prefix
):
313 super().__init
__(name
, info
, doc
, ifcond
, features
)
315 assert isinstance(m
, QAPISchemaEnumMember
)
316 m
.set_defined_in(name
)
317 assert prefix
is None or isinstance(prefix
, str)
318 self
.members
= members
321 def check(self
, schema
):
322 super().check(schema
)
324 for m
in self
.members
:
325 m
.check_clash(self
.info
, seen
)
327 def connect_doc(self
, doc
=None):
328 super().connect_doc(doc
)
329 doc
= doc
or self
.doc
330 for m
in self
.members
:
333 def is_implicit(self
):
334 # See QAPISchema._def_predefineds()
335 return self
.name
== 'QType'
338 return c_name(self
.name
)
340 def member_names(self
):
341 return [m
.name
for m
in self
.members
]
346 def visit(self
, visitor
):
347 super().visit(visitor
)
348 visitor
.visit_enum_type(
349 self
.name
, self
.info
, self
.ifcond
, self
.features
,
350 self
.members
, self
.prefix
)
353 class QAPISchemaArrayType(QAPISchemaType
):
356 def __init__(self
, name
, info
, element_type
):
357 super().__init
__(name
, info
, None)
358 assert isinstance(element_type
, str)
359 self
._element
_type
_name
= element_type
360 self
.element_type
= None
362 def need_has_if_optional(self
):
363 # When FOO is an array, we still need has_FOO to distinguish
364 # absent (!has_FOO) from present and empty (has_FOO && !FOO).
367 def check(self
, schema
):
368 super().check(schema
)
369 self
.element_type
= schema
.resolve_type(
370 self
._element
_type
_name
, self
.info
,
371 self
.info
and self
.info
.defn_meta
)
372 assert not isinstance(self
.element_type
, QAPISchemaArrayType
)
374 def set_module(self
, schema
):
375 self
._set
_module
(schema
, self
.element_type
.info
)
380 return self
.element_type
.ifcond
382 def is_implicit(self
):
386 return c_name(self
.name
) + POINTER_SUFFIX
392 elt_doc_type
= self
.element_type
.doc_type()
395 return 'array of ' + elt_doc_type
397 def visit(self
, visitor
):
398 super().visit(visitor
)
399 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
404 return "%s type ['%s']" % (self
.meta
, self
._element
_type
_name
)
407 class QAPISchemaObjectType(QAPISchemaType
):
408 def __init__(self
, name
, info
, doc
, ifcond
, features
,
409 base
, local_members
, variants
):
410 # struct has local_members, optional base, and no variants
411 # union has base, variants, and no local_members
412 super().__init
__(name
, info
, doc
, ifcond
, features
)
413 self
.meta
= 'union' if variants
else 'struct'
414 assert base
is None or isinstance(base
, str)
415 for m
in local_members
:
416 assert isinstance(m
, QAPISchemaObjectTypeMember
)
417 m
.set_defined_in(name
)
418 if variants
is not None:
419 assert isinstance(variants
, QAPISchemaVariants
)
420 variants
.set_defined_in(name
)
421 self
._base
_name
= base
423 self
.local_members
= local_members
424 self
.variants
= variants
427 def check(self
, schema
):
428 # This calls another type T's .check() exactly when the C
429 # struct emitted by gen_object() contains that T's C struct
430 # (pointers don't count).
431 if self
.members
is not None:
432 # A previous .check() completed: nothing to do
435 # Recursed: C struct contains itself
436 raise QAPISemError(self
.info
,
437 "object %s contains itself" % self
.name
)
439 super().check(schema
)
440 assert self
._checked
and self
.members
is None
444 self
.base
= schema
.resolve_type(self
._base
_name
, self
.info
,
446 if (not isinstance(self
.base
, QAPISchemaObjectType
)
447 or self
.base
.variants
):
450 "'base' requires a struct type, %s isn't"
451 % self
.base
.describe())
452 self
.base
.check(schema
)
453 self
.base
.check_clash(self
.info
, seen
)
454 for m
in self
.local_members
:
456 m
.check_clash(self
.info
, seen
)
457 members
= seen
.values()
460 self
.variants
.check(schema
, seen
)
461 self
.variants
.check_clash(self
.info
, seen
)
463 self
.members
= members
# mark completed
465 # Check that the members of this type do not cause duplicate JSON members,
466 # and update seen to track the members seen so far. Report any errors
467 # on behalf of info, which is not necessarily self.info
468 def check_clash(self
, info
, seen
):
470 for m
in self
.members
:
471 m
.check_clash(info
, seen
)
473 self
.variants
.check_clash(info
, seen
)
475 def connect_doc(self
, doc
=None):
476 super().connect_doc(doc
)
477 doc
= doc
or self
.doc
478 if self
.base
and self
.base
.is_implicit():
479 self
.base
.connect_doc(doc
)
480 for m
in self
.local_members
:
483 def is_implicit(self
):
484 # See QAPISchema._make_implicit_object_type(), as well as
486 return self
.name
.startswith('q_')
489 assert self
.members
is not None
490 return not self
.members
and not self
.variants
492 def has_conditional_members(self
):
493 assert self
.members
is not None
494 return any(m
.ifcond
.is_present() for m
in self
.members
)
497 assert self
.name
!= 'q_empty'
498 return super().c_name()
501 assert not self
.is_implicit()
502 return c_name(self
.name
) + POINTER_SUFFIX
504 def c_unboxed_type(self
):
505 return c_name(self
.name
)
510 def visit(self
, visitor
):
511 super().visit(visitor
)
512 visitor
.visit_object_type(
513 self
.name
, self
.info
, self
.ifcond
, self
.features
,
514 self
.base
, self
.local_members
, self
.variants
)
515 visitor
.visit_object_type_flat(
516 self
.name
, self
.info
, self
.ifcond
, self
.features
,
517 self
.members
, self
.variants
)
520 class QAPISchemaAlternateType(QAPISchemaType
):
523 def __init__(self
, name
, info
, doc
, ifcond
, features
, variants
):
524 super().__init
__(name
, info
, doc
, ifcond
, features
)
525 assert isinstance(variants
, QAPISchemaVariants
)
526 assert variants
.tag_member
527 variants
.set_defined_in(name
)
528 variants
.tag_member
.set_defined_in(self
.name
)
529 self
.variants
= variants
531 def check(self
, schema
):
532 super().check(schema
)
533 self
.variants
.tag_member
.check(schema
)
534 # Not calling self.variants.check_clash(), because there's nothing
536 self
.variants
.check(schema
, {})
537 # Alternate branch names have no relation to the tag enum values;
538 # so we have to check for potential name collisions ourselves.
541 for v
in self
.variants
.variants
:
542 v
.check_clash(self
.info
, seen
)
543 qtype
= v
.type.alternate_qtype()
548 % (v
.describe(self
.info
), v
.type.describe()))
549 conflicting
= set([qtype
])
550 if qtype
== 'QTYPE_QSTRING':
551 if isinstance(v
.type, QAPISchemaEnumType
):
552 for m
in v
.type.members
:
553 if m
.name
in ['on', 'off']:
554 conflicting
.add('QTYPE_QBOOL')
555 if re
.match(r
'[-+0-9.]', m
.name
):
556 # lazy, could be tightened
557 conflicting
.add('QTYPE_QNUM')
559 conflicting
.add('QTYPE_QNUM')
560 conflicting
.add('QTYPE_QBOOL')
561 for qt
in conflicting
:
565 "%s can't be distinguished from '%s'"
566 % (v
.describe(self
.info
), types_seen
[qt
]))
567 types_seen
[qt
] = v
.name
569 def connect_doc(self
, doc
=None):
570 super().connect_doc(doc
)
571 doc
= doc
or self
.doc
572 for v
in self
.variants
.variants
:
576 return c_name(self
.name
) + POINTER_SUFFIX
581 def visit(self
, visitor
):
582 super().visit(visitor
)
583 visitor
.visit_alternate_type(
584 self
.name
, self
.info
, self
.ifcond
, self
.features
, self
.variants
)
587 class QAPISchemaVariants
:
588 def __init__(self
, tag_name
, info
, tag_member
, variants
):
589 # Unions pass tag_name but not tag_member.
590 # Alternates pass tag_member but not tag_name.
591 # After check(), tag_member is always set.
592 assert bool(tag_member
) != bool(tag_name
)
593 assert (isinstance(tag_name
, str) or
594 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
596 assert isinstance(v
, QAPISchemaVariant
)
597 self
._tag
_name
= tag_name
599 self
.tag_member
= tag_member
600 self
.variants
= variants
602 def set_defined_in(self
, name
):
603 for v
in self
.variants
:
604 v
.set_defined_in(name
)
606 def check(self
, schema
, seen
):
607 if self
._tag
_name
: # union
608 self
.tag_member
= seen
.get(c_name(self
._tag
_name
))
610 # Pointing to the base type when not implicit would be
611 # nice, but we don't know it here
612 if not self
.tag_member
or self
._tag
_name
!= self
.tag_member
.name
:
615 "discriminator '%s' is not a member of %s"
616 % (self
._tag
_name
, base
))
618 base_type
= schema
.lookup_type(self
.tag_member
.defined_in
)
620 if not base_type
.is_implicit():
621 base
= "base type '%s'" % self
.tag_member
.defined_in
622 if not isinstance(self
.tag_member
.type, QAPISchemaEnumType
):
625 "discriminator member '%s' of %s must be of enum type"
626 % (self
._tag
_name
, base
))
627 if self
.tag_member
.optional
:
630 "discriminator member '%s' of %s must not be optional"
631 % (self
._tag
_name
, base
))
632 if self
.tag_member
.ifcond
.is_present():
635 "discriminator member '%s' of %s must not be conditional"
636 % (self
._tag
_name
, base
))
638 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
639 assert not self
.tag_member
.optional
640 assert not self
.tag_member
.ifcond
.is_present()
641 if self
._tag
_name
: # union
642 # branches that are not explicitly covered get an empty type
643 cases
= {v
.name
for v
in self
.variants
}
644 for m
in self
.tag_member
.type.members
:
645 if m
.name
not in cases
:
646 v
= QAPISchemaVariant(m
.name
, self
.info
,
648 v
.set_defined_in(self
.tag_member
.defined_in
)
649 self
.variants
.append(v
)
650 if not self
.variants
:
651 raise QAPISemError(self
.info
, "union has no branches")
652 for v
in self
.variants
:
654 # Union names must match enum values; alternate names are
655 # checked separately. Use 'seen' to tell the two apart.
657 if v
.name
not in self
.tag_member
.type.member_names():
660 "branch '%s' is not a value of %s"
661 % (v
.name
, self
.tag_member
.type.describe()))
662 if not isinstance(v
.type, QAPISchemaObjectType
):
666 % (v
.describe(self
.info
), v
.type.describe()))
669 def check_clash(self
, info
, seen
):
670 for v
in self
.variants
:
671 # Reset seen map for each variant, since qapi names from one
672 # branch do not affect another branch
673 v
.type.check_clash(info
, dict(seen
))
676 class QAPISchemaMember
:
677 """ Represents object members, enum members and features """
680 def __init__(self
, name
, info
, ifcond
=None):
681 assert isinstance(name
, str)
684 self
.ifcond
= ifcond
or QAPISchemaIfCond()
685 self
.defined_in
= None
687 def set_defined_in(self
, name
):
688 assert not self
.defined_in
689 self
.defined_in
= name
691 def check_clash(self
, info
, seen
):
692 cname
= c_name(self
.name
)
696 "%s collides with %s"
697 % (self
.describe(info
), seen
[cname
].describe(info
)))
700 def connect_doc(self
, doc
):
702 doc
.connect_member(self
)
704 def describe(self
, info
):
707 defined_in
= self
.defined_in
710 if defined_in
.startswith('q_obj_'):
711 # See QAPISchema._make_implicit_object_type() - reverse the
712 # mapping there to create a nice human-readable description
713 defined_in
= defined_in
[6:]
714 if defined_in
.endswith('-arg'):
715 # Implicit type created for a command's dict 'data'
716 assert role
== 'member'
719 defined_in
= defined_in
[:-4]
720 elif defined_in
.endswith('-base'):
721 # Implicit type created for a union's dict 'base'
722 role
= 'base ' + role
723 defined_in
= defined_in
[:-5]
727 if defined_in
!= info
.defn_name
:
728 return "%s '%s' of %s '%s'" % (role
, self
.name
, meta
, defined_in
)
729 return "%s '%s'" % (role
, self
.name
)
732 class QAPISchemaEnumMember(QAPISchemaMember
):
735 def __init__(self
, name
, info
, ifcond
=None, features
=None):
736 super().__init
__(name
, info
, ifcond
)
737 for f
in features
or []:
738 assert isinstance(f
, QAPISchemaFeature
)
739 f
.set_defined_in(name
)
740 self
.features
= features
or []
742 def connect_doc(self
, doc
):
743 super().connect_doc(doc
)
745 for f
in self
.features
:
746 doc
.connect_feature(f
)
749 class QAPISchemaFeature(QAPISchemaMember
):
752 def is_special(self
):
753 return self
.name
in ('deprecated', 'unstable')
756 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
757 def __init__(self
, name
, info
, typ
, optional
, ifcond
=None, features
=None):
758 super().__init
__(name
, info
, ifcond
)
759 assert isinstance(typ
, str)
760 assert isinstance(optional
, bool)
761 for f
in features
or []:
762 assert isinstance(f
, QAPISchemaFeature
)
763 f
.set_defined_in(name
)
764 self
._type
_name
= typ
766 self
.optional
= optional
767 self
.features
= features
or []
771 return self
.optional
and self
.type.need_has_if_optional()
773 def check(self
, schema
):
774 assert self
.defined_in
775 self
.type = schema
.resolve_type(self
._type
_name
, self
.info
,
778 for f
in self
.features
:
779 f
.check_clash(self
.info
, seen
)
781 def connect_doc(self
, doc
):
782 super().connect_doc(doc
)
784 for f
in self
.features
:
785 doc
.connect_feature(f
)
788 class QAPISchemaVariant(QAPISchemaObjectTypeMember
):
791 def __init__(self
, name
, info
, typ
, ifcond
=None):
792 super().__init
__(name
, info
, typ
, False, ifcond
)
795 class QAPISchemaCommand(QAPISchemaEntity
):
798 def __init__(self
, name
, info
, doc
, ifcond
, features
,
800 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
,
802 super().__init
__(name
, info
, doc
, ifcond
, features
)
803 assert not arg_type
or isinstance(arg_type
, str)
804 assert not ret_type
or isinstance(ret_type
, str)
805 self
._arg
_type
_name
= arg_type
807 self
._ret
_type
_name
= ret_type
810 self
.success_response
= success_response
812 self
.allow_oob
= allow_oob
813 self
.allow_preconfig
= allow_preconfig
814 self
.coroutine
= coroutine
816 def check(self
, schema
):
817 super().check(schema
)
818 if self
._arg
_type
_name
:
819 self
.arg_type
= schema
.resolve_type(
820 self
._arg
_type
_name
, self
.info
, "command's 'data'")
821 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
824 "command's 'data' cannot take %s"
825 % self
.arg_type
.describe())
826 if self
.arg_type
.variants
and not self
.boxed
:
829 "command's 'data' can take %s only with 'boxed': true"
830 % self
.arg_type
.describe())
831 self
.arg_type
.check(schema
)
832 if self
.arg_type
.has_conditional_members() and not self
.boxed
:
835 "conditional command arguments require 'boxed': true")
836 if self
._ret
_type
_name
:
837 self
.ret_type
= schema
.resolve_type(
838 self
._ret
_type
_name
, self
.info
, "command's 'returns'")
839 if self
.name
not in self
.info
.pragma
.command_returns_exceptions
:
841 if isinstance(typ
, QAPISchemaArrayType
):
842 typ
= self
.ret_type
.element_type
844 if not isinstance(typ
, QAPISchemaObjectType
):
847 "command's 'returns' cannot take %s"
848 % self
.ret_type
.describe())
850 def connect_doc(self
, doc
=None):
851 super().connect_doc(doc
)
852 doc
= doc
or self
.doc
854 if self
.arg_type
and self
.arg_type
.is_implicit():
855 self
.arg_type
.connect_doc(doc
)
857 def visit(self
, visitor
):
858 super().visit(visitor
)
859 visitor
.visit_command(
860 self
.name
, self
.info
, self
.ifcond
, self
.features
,
861 self
.arg_type
, self
.ret_type
, self
.gen
, self
.success_response
,
862 self
.boxed
, self
.allow_oob
, self
.allow_preconfig
,
866 class QAPISchemaEvent(QAPISchemaEntity
):
869 def __init__(self
, name
, info
, doc
, ifcond
, features
, arg_type
, boxed
):
870 super().__init
__(name
, info
, doc
, ifcond
, features
)
871 assert not arg_type
or isinstance(arg_type
, str)
872 self
._arg
_type
_name
= arg_type
876 def check(self
, schema
):
877 super().check(schema
)
878 if self
._arg
_type
_name
:
879 self
.arg_type
= schema
.resolve_type(
880 self
._arg
_type
_name
, self
.info
, "event's 'data'")
881 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
884 "event's 'data' cannot take %s"
885 % self
.arg_type
.describe())
886 if self
.arg_type
.variants
and not self
.boxed
:
889 "event's 'data' can take %s only with 'boxed': true"
890 % self
.arg_type
.describe())
891 self
.arg_type
.check(schema
)
892 if self
.arg_type
.has_conditional_members() and not self
.boxed
:
895 "conditional event arguments require 'boxed': true")
897 def connect_doc(self
, doc
=None):
898 super().connect_doc(doc
)
899 doc
= doc
or self
.doc
901 if self
.arg_type
and self
.arg_type
.is_implicit():
902 self
.arg_type
.connect_doc(doc
)
904 def visit(self
, visitor
):
905 super().visit(visitor
)
907 self
.name
, self
.info
, self
.ifcond
, self
.features
,
908 self
.arg_type
, self
.boxed
)
912 def __init__(self
, fname
):
916 parser
= QAPISchemaParser(fname
)
917 except OSError as err
:
919 f
"can't read schema file '{fname}': {err.strerror}"
922 exprs
= check_exprs(parser
.exprs
)
923 self
.docs
= parser
.docs
924 self
._entity
_list
= []
925 self
._entity
_dict
= {}
926 self
._module
_dict
= OrderedDict()
927 self
._schema
_dir
= os
.path
.dirname(fname
)
928 self
._make
_module
(QAPISchemaModule
.BUILTIN_MODULE_NAME
)
929 self
._make
_module
(fname
)
930 self
._predefining
= True
931 self
._def
_predefineds
()
932 self
._predefining
= False
933 self
._def
_exprs
(exprs
)
936 def _def_entity(self
, ent
):
937 # Only the predefined types are allowed to not have info
938 assert ent
.info
or self
._predefining
939 self
._entity
_list
.append(ent
)
942 # TODO reject names that differ only in '_' vs. '.' vs. '-',
943 # because they're liable to clash in generated C.
944 other_ent
= self
._entity
_dict
.get(ent
.name
)
947 where
= QAPISourceError(other_ent
.info
, "previous definition")
950 "'%s' is already defined\n%s" % (ent
.name
, where
))
952 ent
.info
, "%s is already defined" % other_ent
.describe())
953 self
._entity
_dict
[ent
.name
] = ent
955 def lookup_entity(self
, name
, typ
=None):
956 ent
= self
._entity
_dict
.get(name
)
957 if typ
and not isinstance(ent
, typ
):
961 def lookup_type(self
, name
):
962 return self
.lookup_entity(name
, QAPISchemaType
)
964 def resolve_type(self
, name
, info
, what
):
965 typ
= self
.lookup_type(name
)
970 info
, "%s uses unknown type '%s'" % (what
, name
))
973 def _module_name(self
, fname
: str) -> str:
974 if QAPISchemaModule
.is_system_module(fname
):
976 return os
.path
.relpath(fname
, self
._schema
_dir
)
978 def _make_module(self
, fname
):
979 name
= self
._module
_name
(fname
)
980 if name
not in self
._module
_dict
:
981 self
._module
_dict
[name
] = QAPISchemaModule(name
)
982 return self
._module
_dict
[name
]
984 def module_by_fname(self
, fname
):
985 name
= self
._module
_name
(fname
)
986 return self
._module
_dict
[name
]
988 def _def_include(self
, expr
: QAPIExpression
):
989 include
= expr
['include']
990 assert expr
.doc
is None
992 QAPISchemaInclude(self
._make
_module
(include
), expr
.info
))
994 def _def_builtin_type(self
, name
, json_type
, c_type
):
995 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
996 # Instantiating only the arrays that are actually used would
997 # be nice, but we can't as long as their generated code
998 # (qapi-builtin-types.[ch]) may be shared by some other
1000 self
._make
_array
_type
(name
, None)
1002 def _def_predefineds(self
):
1003 for t
in [('str', 'string', 'char' + POINTER_SUFFIX
),
1004 ('number', 'number', 'double'),
1005 ('int', 'int', 'int64_t'),
1006 ('int8', 'int', 'int8_t'),
1007 ('int16', 'int', 'int16_t'),
1008 ('int32', 'int', 'int32_t'),
1009 ('int64', 'int', 'int64_t'),
1010 ('uint8', 'int', 'uint8_t'),
1011 ('uint16', 'int', 'uint16_t'),
1012 ('uint32', 'int', 'uint32_t'),
1013 ('uint64', 'int', 'uint64_t'),
1014 ('size', 'int', 'uint64_t'),
1015 ('bool', 'boolean', 'bool'),
1016 ('any', 'value', 'QObject' + POINTER_SUFFIX
),
1017 ('null', 'null', 'QNull' + POINTER_SUFFIX
)]:
1018 self
._def
_builtin
_type
(*t
)
1019 self
.the_empty_object_type
= QAPISchemaObjectType(
1020 'q_empty', None, None, None, None, None, [], None)
1021 self
._def
_entity
(self
.the_empty_object_type
)
1023 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1025 qtype_values
= self
._make
_enum
_members
(
1026 [{'name': n
} for n
in qtypes
], None)
1028 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None, None,
1029 qtype_values
, 'QTYPE'))
1031 def _make_features(self
, features
, info
):
1032 if features
is None:
1034 return [QAPISchemaFeature(f
['name'], info
,
1035 QAPISchemaIfCond(f
.get('if')))
1038 def _make_enum_member(self
, name
, ifcond
, features
, info
):
1039 return QAPISchemaEnumMember(name
, info
,
1040 QAPISchemaIfCond(ifcond
),
1041 self
._make
_features
(features
, info
))
1043 def _make_enum_members(self
, values
, info
):
1044 return [self
._make
_enum
_member
(v
['name'], v
.get('if'),
1045 v
.get('features'), info
)
1048 def _make_array_type(self
, element_type
, info
):
1049 name
= element_type
+ 'List' # reserved by check_defn_name_str()
1050 if not self
.lookup_type(name
):
1051 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1054 def _make_implicit_object_type(self
, name
, info
, ifcond
, role
, members
):
1057 # See also QAPISchemaObjectTypeMember.describe()
1058 name
= 'q_obj_%s-%s' % (name
, role
)
1059 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
1061 # The implicit object type has multiple users. This can
1062 # only be a duplicate definition, which will be flagged
1066 self
._def
_entity
(QAPISchemaObjectType(
1067 name
, info
, None, ifcond
, None, None, members
, None))
1070 def _def_enum_type(self
, expr
: QAPIExpression
):
1073 prefix
= expr
.get('prefix')
1074 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1076 features
= self
._make
_features
(expr
.get('features'), info
)
1077 self
._def
_entity
(QAPISchemaEnumType(
1078 name
, info
, expr
.doc
, ifcond
, features
,
1079 self
._make
_enum
_members
(data
, info
), prefix
))
1081 def _make_member(self
, name
, typ
, ifcond
, features
, info
):
1083 if name
.startswith('*'):
1086 if isinstance(typ
, list):
1087 assert len(typ
) == 1
1088 typ
= self
._make
_array
_type
(typ
[0], info
)
1089 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
,
1090 self
._make
_features
(features
, info
))
1092 def _make_members(self
, data
, info
):
1093 return [self
._make
_member
(key
, value
['type'],
1094 QAPISchemaIfCond(value
.get('if')),
1095 value
.get('features'), info
)
1096 for (key
, value
) in data
.items()]
1098 def _def_struct_type(self
, expr
: QAPIExpression
):
1099 name
= expr
['struct']
1100 base
= expr
.get('base')
1103 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1104 features
= self
._make
_features
(expr
.get('features'), info
)
1105 self
._def
_entity
(QAPISchemaObjectType(
1106 name
, info
, expr
.doc
, ifcond
, features
, base
,
1107 self
._make
_members
(data
, info
),
1110 def _make_variant(self
, case
, typ
, ifcond
, info
):
1111 if isinstance(typ
, list):
1112 assert len(typ
) == 1
1113 typ
= self
._make
_array
_type
(typ
[0], info
)
1114 return QAPISchemaVariant(case
, info
, typ
, ifcond
)
1116 def _def_union_type(self
, expr
: QAPIExpression
):
1117 name
= expr
['union']
1119 tag_name
= expr
['discriminator']
1121 assert isinstance(data
, dict)
1123 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1124 features
= self
._make
_features
(expr
.get('features'), info
)
1125 if isinstance(base
, dict):
1126 base
= self
._make
_implicit
_object
_type
(
1128 'base', self
._make
_members
(base
, info
))
1130 self
._make
_variant
(key
, value
['type'],
1131 QAPISchemaIfCond(value
.get('if')),
1133 for (key
, value
) in data
.items()]
1134 members
: List
[QAPISchemaObjectTypeMember
] = []
1136 QAPISchemaObjectType(name
, info
, expr
.doc
, ifcond
, features
,
1139 tag_name
, info
, None, variants
)))
1141 def _def_alternate_type(self
, expr
: QAPIExpression
):
1142 name
= expr
['alternate']
1144 assert isinstance(data
, dict)
1145 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1147 features
= self
._make
_features
(expr
.get('features'), info
)
1149 self
._make
_variant
(key
, value
['type'],
1150 QAPISchemaIfCond(value
.get('if')),
1152 for (key
, value
) in data
.items()]
1153 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
1155 QAPISchemaAlternateType(
1156 name
, info
, expr
.doc
, ifcond
, features
,
1157 QAPISchemaVariants(None, info
, tag_member
, variants
)))
1159 def _def_command(self
, expr
: QAPIExpression
):
1160 name
= expr
['command']
1161 data
= expr
.get('data')
1162 rets
= expr
.get('returns')
1163 gen
= expr
.get('gen', True)
1164 success_response
= expr
.get('success-response', True)
1165 boxed
= expr
.get('boxed', False)
1166 allow_oob
= expr
.get('allow-oob', False)
1167 allow_preconfig
= expr
.get('allow-preconfig', False)
1168 coroutine
= expr
.get('coroutine', False)
1169 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1171 features
= self
._make
_features
(expr
.get('features'), info
)
1172 if isinstance(data
, OrderedDict
):
1173 data
= self
._make
_implicit
_object
_type
(
1175 'arg', self
._make
_members
(data
, info
))
1176 if isinstance(rets
, list):
1177 assert len(rets
) == 1
1178 rets
= self
._make
_array
_type
(rets
[0], info
)
1179 self
._def
_entity
(QAPISchemaCommand(name
, info
, expr
.doc
, ifcond
,
1180 features
, data
, rets
,
1181 gen
, success_response
,
1182 boxed
, allow_oob
, allow_preconfig
,
1185 def _def_event(self
, expr
: QAPIExpression
):
1186 name
= expr
['event']
1187 data
= expr
.get('data')
1188 boxed
= expr
.get('boxed', False)
1189 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1191 features
= self
._make
_features
(expr
.get('features'), info
)
1192 if isinstance(data
, OrderedDict
):
1193 data
= self
._make
_implicit
_object
_type
(
1195 'arg', self
._make
_members
(data
, info
))
1196 self
._def
_entity
(QAPISchemaEvent(name
, info
, expr
.doc
, ifcond
,
1197 features
, data
, boxed
))
1199 def _def_exprs(self
, exprs
):
1202 self
._def
_enum
_type
(expr
)
1203 elif 'struct' in expr
:
1204 self
._def
_struct
_type
(expr
)
1205 elif 'union' in expr
:
1206 self
._def
_union
_type
(expr
)
1207 elif 'alternate' in expr
:
1208 self
._def
_alternate
_type
(expr
)
1209 elif 'command' in expr
:
1210 self
._def
_command
(expr
)
1211 elif 'event' in expr
:
1212 self
._def
_event
(expr
)
1213 elif 'include' in expr
:
1214 self
._def
_include
(expr
)
1219 for ent
in self
._entity
_list
:
1222 for ent
in self
._entity
_list
:
1223 ent
.set_module(self
)
1224 for doc
in self
.docs
:
1227 def visit(self
, visitor
):
1228 visitor
.visit_begin(self
)
1229 for mod
in self
._module
_dict
.values():