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 []
77 return c_name(self
.name
)
79 def check(self
, schema
):
80 assert not self
._checked
82 for f
in self
.features
:
83 f
.check_clash(self
.info
, seen
)
86 def connect_doc(self
, doc
=None):
89 for f
in self
.features
:
90 doc
.connect_feature(f
)
96 def _set_module(self
, schema
, info
):
98 fname
= info
.fname
if info
else QAPISchemaModule
.BUILTIN_MODULE_NAME
99 self
._module
= schema
.module_by_fname(fname
)
100 self
._module
.add_entity(self
)
102 def set_module(self
, schema
):
103 self
._set
_module
(schema
, self
.info
)
110 def is_implicit(self
):
113 def visit(self
, visitor
):
118 return "%s '%s'" % (self
.meta
, self
.name
)
121 class QAPISchemaVisitor
:
122 def visit_begin(self
, schema
):
128 def visit_module(self
, name
):
131 def visit_needed(self
, entity
):
132 # Default to visiting everything
135 def visit_include(self
, name
, info
):
138 def visit_builtin_type(self
, name
, info
, json_type
):
141 def visit_enum_type(self
, name
, info
, ifcond
, features
, members
, prefix
):
144 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
147 def visit_object_type(self
, name
, info
, ifcond
, features
,
148 base
, members
, variants
):
151 def visit_object_type_flat(self
, name
, info
, ifcond
, features
,
155 def visit_alternate_type(self
, name
, info
, ifcond
, features
, variants
):
158 def visit_command(self
, name
, info
, ifcond
, features
,
159 arg_type
, ret_type
, gen
, success_response
, boxed
,
160 allow_oob
, allow_preconfig
, coroutine
):
163 def visit_event(self
, name
, info
, ifcond
, features
, arg_type
, boxed
):
167 class QAPISchemaModule
:
169 BUILTIN_MODULE_NAME
= './builtin'
171 def __init__(self
, name
):
173 self
._entity
_list
= []
176 def is_system_module(name
: str) -> bool:
178 System modules are internally defined modules.
180 Their names start with the "./" prefix.
182 return name
.startswith('./')
185 def is_user_module(cls
, name
: str) -> bool:
187 User modules are those defined by the user in qapi JSON files.
189 They do not start with the "./" prefix.
191 return not cls
.is_system_module(name
)
194 def is_builtin_module(cls
, name
: str) -> bool:
196 The built-in module is a single System module for the built-in types.
198 It is always "./builtin".
200 return name
== cls
.BUILTIN_MODULE_NAME
202 def add_entity(self
, ent
):
203 self
._entity
_list
.append(ent
)
205 def visit(self
, visitor
):
206 visitor
.visit_module(self
.name
)
207 for entity
in self
._entity
_list
:
208 if visitor
.visit_needed(entity
):
209 entity
.visit(visitor
)
212 class QAPISchemaInclude(QAPISchemaEntity
):
213 def __init__(self
, sub_module
, info
):
214 super().__init
__(None, info
, None)
215 self
._sub
_module
= sub_module
217 def visit(self
, visitor
):
218 super().visit(visitor
)
219 visitor
.visit_include(self
._sub
_module
.name
, self
.info
)
222 class QAPISchemaType(QAPISchemaEntity
):
223 # Return the C type for common use.
224 # For the types we commonly box, this is a pointer type.
228 # Return the C type to be used in a parameter list.
229 def c_param_type(self
):
232 # Return the C type to be used where we suppress boxing.
233 def c_unboxed_type(self
):
239 def alternate_qtype(self
):
241 'null': 'QTYPE_QNULL',
242 'string': 'QTYPE_QSTRING',
243 'number': 'QTYPE_QNUM',
245 'boolean': 'QTYPE_QBOOL',
246 'array': 'QTYPE_QLIST',
247 'object': 'QTYPE_QDICT'
249 return json2qtype
.get(self
.json_type())
252 if self
.is_implicit():
256 def need_has_if_optional(self
):
257 # When FOO is a pointer, has_FOO == !!FOO, i.e. has_FOO is redundant.
258 # Except for arrays; see QAPISchemaArrayType.need_has_if_optional().
259 return not self
.c_type().endswith(POINTER_SUFFIX
)
261 def check(self
, schema
):
262 super().check(schema
)
263 for feat
in self
.features
:
264 if feat
.is_special():
267 f
"feature '{feat.name}' is not supported for types")
271 return "%s type '%s'" % (self
.meta
, self
.name
)
274 class QAPISchemaBuiltinType(QAPISchemaType
):
277 def __init__(self
, name
, json_type
, c_type
):
278 super().__init
__(name
, None, None)
279 assert not c_type
or isinstance(c_type
, str)
280 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
282 self
._json
_type
_name
= json_type
283 self
._c
_type
_name
= c_type
289 return self
._c
_type
_name
291 def c_param_type(self
):
292 if self
.name
== 'str':
293 return 'const ' + self
._c
_type
_name
294 return self
._c
_type
_name
297 return self
._json
_type
_name
300 return self
.json_type()
302 def visit(self
, visitor
):
303 super().visit(visitor
)
304 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
307 class QAPISchemaEnumType(QAPISchemaType
):
310 def __init__(self
, name
, info
, doc
, ifcond
, features
, members
, prefix
):
311 super().__init
__(name
, info
, doc
, ifcond
, features
)
313 assert isinstance(m
, QAPISchemaEnumMember
)
314 m
.set_defined_in(name
)
315 assert prefix
is None or isinstance(prefix
, str)
316 self
.members
= members
319 def check(self
, schema
):
320 super().check(schema
)
322 for m
in self
.members
:
323 m
.check_clash(self
.info
, seen
)
325 def connect_doc(self
, doc
=None):
326 super().connect_doc(doc
)
327 doc
= doc
or self
.doc
328 for m
in self
.members
:
331 def is_implicit(self
):
332 # See QAPISchema._def_predefineds()
333 return self
.name
== 'QType'
336 return c_name(self
.name
)
338 def member_names(self
):
339 return [m
.name
for m
in self
.members
]
344 def visit(self
, visitor
):
345 super().visit(visitor
)
346 visitor
.visit_enum_type(
347 self
.name
, self
.info
, self
.ifcond
, self
.features
,
348 self
.members
, self
.prefix
)
351 class QAPISchemaArrayType(QAPISchemaType
):
354 def __init__(self
, name
, info
, element_type
):
355 super().__init
__(name
, info
, None)
356 assert isinstance(element_type
, str)
357 self
._element
_type
_name
= element_type
358 self
.element_type
= None
360 def need_has_if_optional(self
):
361 # When FOO is an array, we still need has_FOO to distinguish
362 # absent (!has_FOO) from present and empty (has_FOO && !FOO).
365 def check(self
, schema
):
366 super().check(schema
)
367 self
.element_type
= schema
.resolve_type(
368 self
._element
_type
_name
, self
.info
,
369 self
.info
and self
.info
.defn_meta
)
370 assert not isinstance(self
.element_type
, QAPISchemaArrayType
)
372 def set_module(self
, schema
):
373 self
._set
_module
(schema
, self
.element_type
.info
)
378 return self
.element_type
.ifcond
380 def is_implicit(self
):
384 return c_name(self
.name
) + POINTER_SUFFIX
390 elt_doc_type
= self
.element_type
.doc_type()
393 return 'array of ' + elt_doc_type
395 def visit(self
, visitor
):
396 super().visit(visitor
)
397 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
402 return "%s type ['%s']" % (self
.meta
, self
._element
_type
_name
)
405 class QAPISchemaObjectType(QAPISchemaType
):
406 def __init__(self
, name
, info
, doc
, ifcond
, features
,
407 base
, local_members
, variants
):
408 # struct has local_members, optional base, and no variants
409 # union has base, variants, and no local_members
410 super().__init
__(name
, info
, doc
, ifcond
, features
)
411 self
.meta
= 'union' if variants
else 'struct'
412 assert base
is None or isinstance(base
, str)
413 for m
in local_members
:
414 assert isinstance(m
, QAPISchemaObjectTypeMember
)
415 m
.set_defined_in(name
)
416 if variants
is not None:
417 assert isinstance(variants
, QAPISchemaVariants
)
418 variants
.set_defined_in(name
)
419 self
._base
_name
= base
421 self
.local_members
= local_members
422 self
.variants
= variants
425 def check(self
, schema
):
426 # This calls another type T's .check() exactly when the C
427 # struct emitted by gen_object() contains that T's C struct
428 # (pointers don't count).
429 if self
.members
is not None:
430 # A previous .check() completed: nothing to do
433 # Recursed: C struct contains itself
434 raise QAPISemError(self
.info
,
435 "object %s contains itself" % self
.name
)
437 super().check(schema
)
438 assert self
._checked
and self
.members
is None
442 self
.base
= schema
.resolve_type(self
._base
_name
, self
.info
,
444 if (not isinstance(self
.base
, QAPISchemaObjectType
)
445 or self
.base
.variants
):
448 "'base' requires a struct type, %s isn't"
449 % self
.base
.describe())
450 self
.base
.check(schema
)
451 self
.base
.check_clash(self
.info
, seen
)
452 for m
in self
.local_members
:
454 m
.check_clash(self
.info
, seen
)
455 members
= seen
.values()
458 self
.variants
.check(schema
, seen
)
459 self
.variants
.check_clash(self
.info
, seen
)
461 self
.members
= members
# mark completed
463 # Check that the members of this type do not cause duplicate JSON members,
464 # and update seen to track the members seen so far. Report any errors
465 # on behalf of info, which is not necessarily self.info
466 def check_clash(self
, info
, seen
):
468 for m
in self
.members
:
469 m
.check_clash(info
, seen
)
471 self
.variants
.check_clash(info
, seen
)
473 def connect_doc(self
, doc
=None):
474 super().connect_doc(doc
)
475 doc
= doc
or self
.doc
476 if self
.base
and self
.base
.is_implicit():
477 self
.base
.connect_doc(doc
)
478 for m
in self
.local_members
:
481 def is_implicit(self
):
482 # See QAPISchema._make_implicit_object_type(), as well as
484 return self
.name
.startswith('q_')
487 assert self
.members
is not None
488 return not self
.members
and not self
.variants
490 def has_conditional_members(self
):
491 assert self
.members
is not None
492 return any(m
.ifcond
.is_present() for m
in self
.members
)
495 assert self
.name
!= 'q_empty'
496 return super().c_name()
499 assert not self
.is_implicit()
500 return c_name(self
.name
) + POINTER_SUFFIX
502 def c_unboxed_type(self
):
503 return c_name(self
.name
)
508 def visit(self
, visitor
):
509 super().visit(visitor
)
510 visitor
.visit_object_type(
511 self
.name
, self
.info
, self
.ifcond
, self
.features
,
512 self
.base
, self
.local_members
, self
.variants
)
513 visitor
.visit_object_type_flat(
514 self
.name
, self
.info
, self
.ifcond
, self
.features
,
515 self
.members
, self
.variants
)
518 class QAPISchemaAlternateType(QAPISchemaType
):
521 def __init__(self
, name
, info
, doc
, ifcond
, features
, variants
):
522 super().__init
__(name
, info
, doc
, ifcond
, features
)
523 assert isinstance(variants
, QAPISchemaVariants
)
524 assert variants
.tag_member
525 variants
.set_defined_in(name
)
526 variants
.tag_member
.set_defined_in(self
.name
)
527 self
.variants
= variants
529 def check(self
, schema
):
530 super().check(schema
)
531 self
.variants
.tag_member
.check(schema
)
532 # Not calling self.variants.check_clash(), because there's nothing
534 self
.variants
.check(schema
, {})
535 # Alternate branch names have no relation to the tag enum values;
536 # so we have to check for potential name collisions ourselves.
539 for v
in self
.variants
.variants
:
540 v
.check_clash(self
.info
, seen
)
541 qtype
= v
.type.alternate_qtype()
546 % (v
.describe(self
.info
), v
.type.describe()))
547 conflicting
= set([qtype
])
548 if qtype
== 'QTYPE_QSTRING':
549 if isinstance(v
.type, QAPISchemaEnumType
):
550 for m
in v
.type.members
:
551 if m
.name
in ['on', 'off']:
552 conflicting
.add('QTYPE_QBOOL')
553 if re
.match(r
'[-+0-9.]', m
.name
):
554 # lazy, could be tightened
555 conflicting
.add('QTYPE_QNUM')
557 conflicting
.add('QTYPE_QNUM')
558 conflicting
.add('QTYPE_QBOOL')
559 for qt
in conflicting
:
563 "%s can't be distinguished from '%s'"
564 % (v
.describe(self
.info
), types_seen
[qt
]))
565 types_seen
[qt
] = v
.name
567 def connect_doc(self
, doc
=None):
568 super().connect_doc(doc
)
569 doc
= doc
or self
.doc
570 for v
in self
.variants
.variants
:
574 return c_name(self
.name
) + POINTER_SUFFIX
579 def visit(self
, visitor
):
580 super().visit(visitor
)
581 visitor
.visit_alternate_type(
582 self
.name
, self
.info
, self
.ifcond
, self
.features
, self
.variants
)
585 class QAPISchemaVariants
:
586 def __init__(self
, tag_name
, info
, tag_member
, variants
):
587 # Unions pass tag_name but not tag_member.
588 # Alternates pass tag_member but not tag_name.
589 # After check(), tag_member is always set.
590 assert bool(tag_member
) != bool(tag_name
)
591 assert (isinstance(tag_name
, str) or
592 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
594 assert isinstance(v
, QAPISchemaVariant
)
595 self
._tag
_name
= tag_name
597 self
.tag_member
= tag_member
598 self
.variants
= variants
600 def set_defined_in(self
, name
):
601 for v
in self
.variants
:
602 v
.set_defined_in(name
)
604 def check(self
, schema
, seen
):
605 if self
._tag
_name
: # union
606 self
.tag_member
= seen
.get(c_name(self
._tag
_name
))
608 # Pointing to the base type when not implicit would be
609 # nice, but we don't know it here
610 if not self
.tag_member
or self
._tag
_name
!= self
.tag_member
.name
:
613 "discriminator '%s' is not a member of %s"
614 % (self
._tag
_name
, base
))
616 base_type
= schema
.lookup_type(self
.tag_member
.defined_in
)
618 if not base_type
.is_implicit():
619 base
= "base type '%s'" % self
.tag_member
.defined_in
620 if not isinstance(self
.tag_member
.type, QAPISchemaEnumType
):
623 "discriminator member '%s' of %s must be of enum type"
624 % (self
._tag
_name
, base
))
625 if self
.tag_member
.optional
:
628 "discriminator member '%s' of %s must not be optional"
629 % (self
._tag
_name
, base
))
630 if self
.tag_member
.ifcond
.is_present():
633 "discriminator member '%s' of %s must not be conditional"
634 % (self
._tag
_name
, base
))
636 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
637 assert not self
.tag_member
.optional
638 assert not self
.tag_member
.ifcond
.is_present()
639 if self
._tag
_name
: # union
640 # branches that are not explicitly covered get an empty type
641 cases
= {v
.name
for v
in self
.variants
}
642 for m
in self
.tag_member
.type.members
:
643 if m
.name
not in cases
:
644 v
= QAPISchemaVariant(m
.name
, self
.info
,
646 v
.set_defined_in(self
.tag_member
.defined_in
)
647 self
.variants
.append(v
)
648 if not self
.variants
:
649 raise QAPISemError(self
.info
, "union has no branches")
650 for v
in self
.variants
:
652 # Union names must match enum values; alternate names are
653 # checked separately. Use 'seen' to tell the two apart.
655 if v
.name
not in self
.tag_member
.type.member_names():
658 "branch '%s' is not a value of %s"
659 % (v
.name
, self
.tag_member
.type.describe()))
660 if not isinstance(v
.type, QAPISchemaObjectType
):
664 % (v
.describe(self
.info
), v
.type.describe()))
667 def check_clash(self
, info
, seen
):
668 for v
in self
.variants
:
669 # Reset seen map for each variant, since qapi names from one
670 # branch do not affect another branch
671 v
.type.check_clash(info
, dict(seen
))
674 class QAPISchemaMember
:
675 """ Represents object members, enum members and features """
678 def __init__(self
, name
, info
, ifcond
=None):
679 assert isinstance(name
, str)
682 self
.ifcond
= ifcond
or QAPISchemaIfCond()
683 self
.defined_in
= None
685 def set_defined_in(self
, name
):
686 assert not self
.defined_in
687 self
.defined_in
= name
689 def check_clash(self
, info
, seen
):
690 cname
= c_name(self
.name
)
694 "%s collides with %s"
695 % (self
.describe(info
), seen
[cname
].describe(info
)))
698 def connect_doc(self
, doc
):
700 doc
.connect_member(self
)
702 def describe(self
, info
):
705 defined_in
= self
.defined_in
708 if defined_in
.startswith('q_obj_'):
709 # See QAPISchema._make_implicit_object_type() - reverse the
710 # mapping there to create a nice human-readable description
711 defined_in
= defined_in
[6:]
712 if defined_in
.endswith('-arg'):
713 # Implicit type created for a command's dict 'data'
714 assert role
== 'member'
717 defined_in
= defined_in
[:-4]
718 elif defined_in
.endswith('-base'):
719 # Implicit type created for a union's dict 'base'
720 role
= 'base ' + role
721 defined_in
= defined_in
[:-5]
725 if defined_in
!= info
.defn_name
:
726 return "%s '%s' of %s '%s'" % (role
, self
.name
, meta
, defined_in
)
727 return "%s '%s'" % (role
, self
.name
)
730 class QAPISchemaEnumMember(QAPISchemaMember
):
733 def __init__(self
, name
, info
, ifcond
=None, features
=None):
734 super().__init
__(name
, info
, ifcond
)
735 for f
in features
or []:
736 assert isinstance(f
, QAPISchemaFeature
)
737 f
.set_defined_in(name
)
738 self
.features
= features
or []
740 def connect_doc(self
, doc
):
741 super().connect_doc(doc
)
743 for f
in self
.features
:
744 doc
.connect_feature(f
)
747 class QAPISchemaFeature(QAPISchemaMember
):
750 def is_special(self
):
751 return self
.name
in ('deprecated', 'unstable')
754 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
755 def __init__(self
, name
, info
, typ
, optional
, ifcond
=None, features
=None):
756 super().__init
__(name
, info
, ifcond
)
757 assert isinstance(typ
, str)
758 assert isinstance(optional
, bool)
759 for f
in features
or []:
760 assert isinstance(f
, QAPISchemaFeature
)
761 f
.set_defined_in(name
)
762 self
._type
_name
= typ
764 self
.optional
= optional
765 self
.features
= features
or []
769 return self
.optional
and self
.type.need_has_if_optional()
771 def check(self
, schema
):
772 assert self
.defined_in
773 self
.type = schema
.resolve_type(self
._type
_name
, self
.info
,
776 for f
in self
.features
:
777 f
.check_clash(self
.info
, seen
)
779 def connect_doc(self
, doc
):
780 super().connect_doc(doc
)
782 for f
in self
.features
:
783 doc
.connect_feature(f
)
786 class QAPISchemaVariant(QAPISchemaObjectTypeMember
):
789 def __init__(self
, name
, info
, typ
, ifcond
=None):
790 super().__init
__(name
, info
, typ
, False, ifcond
)
793 class QAPISchemaCommand(QAPISchemaEntity
):
796 def __init__(self
, name
, info
, doc
, ifcond
, features
,
798 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
,
800 super().__init
__(name
, info
, doc
, ifcond
, features
)
801 assert not arg_type
or isinstance(arg_type
, str)
802 assert not ret_type
or isinstance(ret_type
, str)
803 self
._arg
_type
_name
= arg_type
805 self
._ret
_type
_name
= ret_type
808 self
.success_response
= success_response
810 self
.allow_oob
= allow_oob
811 self
.allow_preconfig
= allow_preconfig
812 self
.coroutine
= coroutine
814 def check(self
, schema
):
815 super().check(schema
)
816 if self
._arg
_type
_name
:
817 self
.arg_type
= schema
.resolve_type(
818 self
._arg
_type
_name
, self
.info
, "command's 'data'")
819 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
822 "command's 'data' cannot take %s"
823 % self
.arg_type
.describe())
824 if self
.arg_type
.variants
and not self
.boxed
:
827 "command's 'data' can take %s only with 'boxed': true"
828 % self
.arg_type
.describe())
829 self
.arg_type
.check(schema
)
830 if self
.arg_type
.has_conditional_members() and not self
.boxed
:
833 "conditional command arguments require 'boxed': true")
834 if self
._ret
_type
_name
:
835 self
.ret_type
= schema
.resolve_type(
836 self
._ret
_type
_name
, self
.info
, "command's 'returns'")
837 if self
.name
not in self
.info
.pragma
.command_returns_exceptions
:
839 if isinstance(typ
, QAPISchemaArrayType
):
840 typ
= self
.ret_type
.element_type
842 if not isinstance(typ
, QAPISchemaObjectType
):
845 "command's 'returns' cannot take %s"
846 % self
.ret_type
.describe())
848 def connect_doc(self
, doc
=None):
849 super().connect_doc(doc
)
850 doc
= doc
or self
.doc
852 if self
.arg_type
and self
.arg_type
.is_implicit():
853 self
.arg_type
.connect_doc(doc
)
855 def visit(self
, visitor
):
856 super().visit(visitor
)
857 visitor
.visit_command(
858 self
.name
, self
.info
, self
.ifcond
, self
.features
,
859 self
.arg_type
, self
.ret_type
, self
.gen
, self
.success_response
,
860 self
.boxed
, self
.allow_oob
, self
.allow_preconfig
,
864 class QAPISchemaEvent(QAPISchemaEntity
):
867 def __init__(self
, name
, info
, doc
, ifcond
, features
, arg_type
, boxed
):
868 super().__init
__(name
, info
, doc
, ifcond
, features
)
869 assert not arg_type
or isinstance(arg_type
, str)
870 self
._arg
_type
_name
= arg_type
874 def check(self
, schema
):
875 super().check(schema
)
876 if self
._arg
_type
_name
:
877 self
.arg_type
= schema
.resolve_type(
878 self
._arg
_type
_name
, self
.info
, "event's 'data'")
879 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
882 "event's 'data' cannot take %s"
883 % self
.arg_type
.describe())
884 if self
.arg_type
.variants
and not self
.boxed
:
887 "event's 'data' can take %s only with 'boxed': true"
888 % self
.arg_type
.describe())
889 self
.arg_type
.check(schema
)
890 if self
.arg_type
.has_conditional_members() and not self
.boxed
:
893 "conditional event arguments require 'boxed': true")
895 def connect_doc(self
, doc
=None):
896 super().connect_doc(doc
)
897 doc
= doc
or self
.doc
899 if self
.arg_type
and self
.arg_type
.is_implicit():
900 self
.arg_type
.connect_doc(doc
)
902 def visit(self
, visitor
):
903 super().visit(visitor
)
905 self
.name
, self
.info
, self
.ifcond
, self
.features
,
906 self
.arg_type
, self
.boxed
)
910 def __init__(self
, fname
):
914 parser
= QAPISchemaParser(fname
)
915 except OSError as err
:
917 f
"can't read schema file '{fname}': {err.strerror}"
920 exprs
= check_exprs(parser
.exprs
)
921 self
.docs
= parser
.docs
922 self
._entity
_list
= []
923 self
._entity
_dict
= {}
924 self
._module
_dict
= OrderedDict()
925 self
._schema
_dir
= os
.path
.dirname(fname
)
926 self
._make
_module
(QAPISchemaModule
.BUILTIN_MODULE_NAME
)
927 self
._make
_module
(fname
)
928 self
._predefining
= True
929 self
._def
_predefineds
()
930 self
._predefining
= False
931 self
._def
_exprs
(exprs
)
934 def _def_entity(self
, ent
):
935 # Only the predefined types are allowed to not have info
936 assert ent
.info
or self
._predefining
937 self
._entity
_list
.append(ent
)
940 # TODO reject names that differ only in '_' vs. '.' vs. '-',
941 # because they're liable to clash in generated C.
942 other_ent
= self
._entity
_dict
.get(ent
.name
)
945 where
= QAPISourceError(other_ent
.info
, "previous definition")
948 "'%s' is already defined\n%s" % (ent
.name
, where
))
950 ent
.info
, "%s is already defined" % other_ent
.describe())
951 self
._entity
_dict
[ent
.name
] = ent
953 def lookup_entity(self
, name
, typ
=None):
954 ent
= self
._entity
_dict
.get(name
)
955 if typ
and not isinstance(ent
, typ
):
959 def lookup_type(self
, name
):
960 return self
.lookup_entity(name
, QAPISchemaType
)
962 def resolve_type(self
, name
, info
, what
):
963 typ
= self
.lookup_type(name
)
968 info
, "%s uses unknown type '%s'" % (what
, name
))
971 def _module_name(self
, fname
: str) -> str:
972 if QAPISchemaModule
.is_system_module(fname
):
974 return os
.path
.relpath(fname
, self
._schema
_dir
)
976 def _make_module(self
, fname
):
977 name
= self
._module
_name
(fname
)
978 if name
not in self
._module
_dict
:
979 self
._module
_dict
[name
] = QAPISchemaModule(name
)
980 return self
._module
_dict
[name
]
982 def module_by_fname(self
, fname
):
983 name
= self
._module
_name
(fname
)
984 return self
._module
_dict
[name
]
986 def _def_include(self
, expr
: QAPIExpression
):
987 include
= expr
['include']
988 assert expr
.doc
is None
990 QAPISchemaInclude(self
._make
_module
(include
), expr
.info
))
992 def _def_builtin_type(self
, name
, json_type
, c_type
):
993 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
994 # Instantiating only the arrays that are actually used would
995 # be nice, but we can't as long as their generated code
996 # (qapi-builtin-types.[ch]) may be shared by some other
998 self
._make
_array
_type
(name
, None)
1000 def _def_predefineds(self
):
1001 for t
in [('str', 'string', 'char' + POINTER_SUFFIX
),
1002 ('number', 'number', 'double'),
1003 ('int', 'int', 'int64_t'),
1004 ('int8', 'int', 'int8_t'),
1005 ('int16', 'int', 'int16_t'),
1006 ('int32', 'int', 'int32_t'),
1007 ('int64', 'int', 'int64_t'),
1008 ('uint8', 'int', 'uint8_t'),
1009 ('uint16', 'int', 'uint16_t'),
1010 ('uint32', 'int', 'uint32_t'),
1011 ('uint64', 'int', 'uint64_t'),
1012 ('size', 'int', 'uint64_t'),
1013 ('bool', 'boolean', 'bool'),
1014 ('any', 'value', 'QObject' + POINTER_SUFFIX
),
1015 ('null', 'null', 'QNull' + POINTER_SUFFIX
)]:
1016 self
._def
_builtin
_type
(*t
)
1017 self
.the_empty_object_type
= QAPISchemaObjectType(
1018 'q_empty', None, None, None, None, None, [], None)
1019 self
._def
_entity
(self
.the_empty_object_type
)
1021 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1023 qtype_values
= self
._make
_enum
_members
(
1024 [{'name': n
} for n
in qtypes
], None)
1026 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None, None,
1027 qtype_values
, 'QTYPE'))
1029 def _make_features(self
, features
, info
):
1030 if features
is None:
1032 return [QAPISchemaFeature(f
['name'], info
,
1033 QAPISchemaIfCond(f
.get('if')))
1036 def _make_enum_member(self
, name
, ifcond
, features
, info
):
1037 return QAPISchemaEnumMember(name
, info
,
1038 QAPISchemaIfCond(ifcond
),
1039 self
._make
_features
(features
, info
))
1041 def _make_enum_members(self
, values
, info
):
1042 return [self
._make
_enum
_member
(v
['name'], v
.get('if'),
1043 v
.get('features'), info
)
1046 def _make_array_type(self
, element_type
, info
):
1047 name
= element_type
+ 'List' # reserved by check_defn_name_str()
1048 if not self
.lookup_type(name
):
1049 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1052 def _make_implicit_object_type(self
, name
, info
, ifcond
, role
, members
):
1055 # See also QAPISchemaObjectTypeMember.describe()
1056 name
= 'q_obj_%s-%s' % (name
, role
)
1057 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
1059 # The implicit object type has multiple users. This can
1060 # only be a duplicate definition, which will be flagged
1064 self
._def
_entity
(QAPISchemaObjectType(
1065 name
, info
, None, ifcond
, None, None, members
, None))
1068 def _def_enum_type(self
, expr
: QAPIExpression
):
1071 prefix
= expr
.get('prefix')
1072 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1074 features
= self
._make
_features
(expr
.get('features'), info
)
1075 self
._def
_entity
(QAPISchemaEnumType(
1076 name
, info
, expr
.doc
, ifcond
, features
,
1077 self
._make
_enum
_members
(data
, info
), prefix
))
1079 def _make_member(self
, name
, typ
, ifcond
, features
, info
):
1081 if name
.startswith('*'):
1084 if isinstance(typ
, list):
1085 assert len(typ
) == 1
1086 typ
= self
._make
_array
_type
(typ
[0], info
)
1087 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
,
1088 self
._make
_features
(features
, info
))
1090 def _make_members(self
, data
, info
):
1091 return [self
._make
_member
(key
, value
['type'],
1092 QAPISchemaIfCond(value
.get('if')),
1093 value
.get('features'), info
)
1094 for (key
, value
) in data
.items()]
1096 def _def_struct_type(self
, expr
: QAPIExpression
):
1097 name
= expr
['struct']
1098 base
= expr
.get('base')
1101 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1102 features
= self
._make
_features
(expr
.get('features'), info
)
1103 self
._def
_entity
(QAPISchemaObjectType(
1104 name
, info
, expr
.doc
, ifcond
, features
, base
,
1105 self
._make
_members
(data
, info
),
1108 def _make_variant(self
, case
, typ
, ifcond
, info
):
1109 if isinstance(typ
, list):
1110 assert len(typ
) == 1
1111 typ
= self
._make
_array
_type
(typ
[0], info
)
1112 return QAPISchemaVariant(case
, info
, typ
, ifcond
)
1114 def _def_union_type(self
, expr
: QAPIExpression
):
1115 name
= expr
['union']
1117 tag_name
= expr
['discriminator']
1119 assert isinstance(data
, dict)
1121 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1122 features
= self
._make
_features
(expr
.get('features'), info
)
1123 if isinstance(base
, dict):
1124 base
= self
._make
_implicit
_object
_type
(
1126 'base', self
._make
_members
(base
, info
))
1128 self
._make
_variant
(key
, value
['type'],
1129 QAPISchemaIfCond(value
.get('if')),
1131 for (key
, value
) in data
.items()]
1132 members
: List
[QAPISchemaObjectTypeMember
] = []
1134 QAPISchemaObjectType(name
, info
, expr
.doc
, ifcond
, features
,
1137 tag_name
, info
, None, variants
)))
1139 def _def_alternate_type(self
, expr
: QAPIExpression
):
1140 name
= expr
['alternate']
1142 assert isinstance(data
, dict)
1143 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1145 features
= self
._make
_features
(expr
.get('features'), info
)
1147 self
._make
_variant
(key
, value
['type'],
1148 QAPISchemaIfCond(value
.get('if')),
1150 for (key
, value
) in data
.items()]
1151 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
1153 QAPISchemaAlternateType(
1154 name
, info
, expr
.doc
, ifcond
, features
,
1155 QAPISchemaVariants(None, info
, tag_member
, variants
)))
1157 def _def_command(self
, expr
: QAPIExpression
):
1158 name
= expr
['command']
1159 data
= expr
.get('data')
1160 rets
= expr
.get('returns')
1161 gen
= expr
.get('gen', True)
1162 success_response
= expr
.get('success-response', True)
1163 boxed
= expr
.get('boxed', False)
1164 allow_oob
= expr
.get('allow-oob', False)
1165 allow_preconfig
= expr
.get('allow-preconfig', False)
1166 coroutine
= expr
.get('coroutine', False)
1167 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1169 features
= self
._make
_features
(expr
.get('features'), info
)
1170 if isinstance(data
, OrderedDict
):
1171 data
= self
._make
_implicit
_object
_type
(
1173 'arg', self
._make
_members
(data
, info
))
1174 if isinstance(rets
, list):
1175 assert len(rets
) == 1
1176 rets
= self
._make
_array
_type
(rets
[0], info
)
1177 self
._def
_entity
(QAPISchemaCommand(name
, info
, expr
.doc
, ifcond
,
1178 features
, data
, rets
,
1179 gen
, success_response
,
1180 boxed
, allow_oob
, allow_preconfig
,
1183 def _def_event(self
, expr
: QAPIExpression
):
1184 name
= expr
['event']
1185 data
= expr
.get('data')
1186 boxed
= expr
.get('boxed', False)
1187 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1189 features
= self
._make
_features
(expr
.get('features'), info
)
1190 if isinstance(data
, OrderedDict
):
1191 data
= self
._make
_implicit
_object
_type
(
1193 'arg', self
._make
_members
(data
, info
))
1194 self
._def
_entity
(QAPISchemaEvent(name
, info
, expr
.doc
, ifcond
,
1195 features
, data
, boxed
))
1197 def _def_exprs(self
, exprs
):
1200 self
._def
_enum
_type
(expr
)
1201 elif 'struct' in expr
:
1202 self
._def
_struct
_type
(expr
)
1203 elif 'union' in expr
:
1204 self
._def
_union
_type
(expr
)
1205 elif 'alternate' in expr
:
1206 self
._def
_alternate
_type
(expr
)
1207 elif 'command' in expr
:
1208 self
._def
_command
(expr
)
1209 elif 'event' in expr
:
1210 self
._def
_event
(expr
)
1211 elif 'include' in expr
:
1212 self
._def
_include
(expr
)
1217 for ent
in self
._entity
_list
:
1221 for ent
in self
._entity
_list
:
1222 ent
.set_module(self
)
1224 def visit(self
, visitor
):
1225 visitor
.visit_begin(self
)
1226 for mod
in self
._module
_dict
.values():