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
, id(self
)
82 return c_name(self
.name
)
84 def check(self
, schema
):
85 assert not self
._checked
87 for f
in self
.features
:
88 f
.check_clash(self
.info
, seen
)
91 def connect_doc(self
, doc
=None):
94 for f
in self
.features
:
95 doc
.connect_feature(f
)
101 def _set_module(self
, schema
, info
):
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
):
108 self
._set
_module
(schema
, self
.info
)
115 def is_implicit(self
):
118 def visit(self
, visitor
):
123 return "%s '%s'" % (self
.meta
, self
.name
)
126 class QAPISchemaVisitor
:
127 def visit_begin(self
, schema
):
133 def visit_module(self
, name
):
136 def visit_needed(self
, entity
):
137 # Default to visiting everything
140 def visit_include(self
, name
, info
):
143 def visit_builtin_type(self
, name
, info
, json_type
):
146 def visit_enum_type(self
, name
, info
, ifcond
, features
, members
, prefix
):
149 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
152 def visit_object_type(self
, name
, info
, ifcond
, features
,
153 base
, members
, variants
):
156 def visit_object_type_flat(self
, name
, info
, ifcond
, features
,
160 def visit_alternate_type(self
, name
, info
, ifcond
, features
, variants
):
163 def visit_command(self
, name
, info
, ifcond
, features
,
164 arg_type
, ret_type
, gen
, success_response
, boxed
,
165 allow_oob
, allow_preconfig
, coroutine
):
168 def visit_event(self
, name
, info
, ifcond
, features
, arg_type
, boxed
):
172 class QAPISchemaModule
:
174 BUILTIN_MODULE_NAME
= './builtin'
176 def __init__(self
, name
):
178 self
._entity
_list
= []
181 def is_system_module(name
: str) -> bool:
183 System modules are internally defined modules.
185 Their names start with the "./" prefix.
187 return name
.startswith('./')
190 def is_user_module(cls
, name
: str) -> bool:
192 User modules are those defined by the user in qapi JSON files.
194 They do not start with the "./" prefix.
196 return not cls
.is_system_module(name
)
199 def is_builtin_module(cls
, name
: str) -> bool:
201 The built-in module is a single System module for the built-in types.
203 It is always "./builtin".
205 return name
== cls
.BUILTIN_MODULE_NAME
207 def add_entity(self
, ent
):
208 self
._entity
_list
.append(ent
)
210 def visit(self
, visitor
):
211 visitor
.visit_module(self
.name
)
212 for entity
in self
._entity
_list
:
213 if visitor
.visit_needed(entity
):
214 entity
.visit(visitor
)
217 class QAPISchemaInclude(QAPISchemaEntity
):
218 def __init__(self
, sub_module
, info
):
219 super().__init
__(None, info
, None)
220 self
._sub
_module
= sub_module
222 def visit(self
, visitor
):
223 super().visit(visitor
)
224 visitor
.visit_include(self
._sub
_module
.name
, self
.info
)
227 class QAPISchemaType(QAPISchemaEntity
):
228 # Return the C type for common use.
229 # For the types we commonly box, this is a pointer type.
233 # Return the C type to be used in a parameter list.
234 def c_param_type(self
):
237 # Return the C type to be used where we suppress boxing.
238 def c_unboxed_type(self
):
244 def alternate_qtype(self
):
246 'null': 'QTYPE_QNULL',
247 'string': 'QTYPE_QSTRING',
248 'number': 'QTYPE_QNUM',
250 'boolean': 'QTYPE_QBOOL',
251 'array': 'QTYPE_QLIST',
252 'object': 'QTYPE_QDICT'
254 return json2qtype
.get(self
.json_type())
257 if self
.is_implicit():
261 def need_has_if_optional(self
):
262 # When FOO is a pointer, has_FOO == !!FOO, i.e. has_FOO is redundant.
263 # Except for arrays; see QAPISchemaArrayType.need_has_if_optional().
264 return not self
.c_type().endswith(POINTER_SUFFIX
)
266 def check(self
, schema
):
267 super().check(schema
)
268 for feat
in self
.features
:
269 if feat
.is_special():
272 f
"feature '{feat.name}' is not supported for types")
276 return "%s type '%s'" % (self
.meta
, self
.name
)
279 class QAPISchemaBuiltinType(QAPISchemaType
):
282 def __init__(self
, name
, json_type
, c_type
):
283 super().__init
__(name
, None, None)
284 assert not c_type
or isinstance(c_type
, str)
285 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
287 self
._json
_type
_name
= json_type
288 self
._c
_type
_name
= c_type
294 return self
._c
_type
_name
296 def c_param_type(self
):
297 if self
.name
== 'str':
298 return 'const ' + self
._c
_type
_name
299 return self
._c
_type
_name
302 return self
._json
_type
_name
305 return self
.json_type()
307 def visit(self
, visitor
):
308 super().visit(visitor
)
309 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
312 class QAPISchemaEnumType(QAPISchemaType
):
315 def __init__(self
, name
, info
, doc
, ifcond
, features
, members
, prefix
):
316 super().__init
__(name
, info
, doc
, ifcond
, features
)
318 assert isinstance(m
, QAPISchemaEnumMember
)
319 m
.set_defined_in(name
)
320 assert prefix
is None or isinstance(prefix
, str)
321 self
.members
= members
324 def check(self
, schema
):
325 super().check(schema
)
327 for m
in self
.members
:
328 m
.check_clash(self
.info
, seen
)
330 def connect_doc(self
, doc
=None):
331 super().connect_doc(doc
)
332 doc
= doc
or self
.doc
333 for m
in self
.members
:
336 def is_implicit(self
):
337 # See QAPISchema._def_predefineds()
338 return self
.name
== 'QType'
341 return c_name(self
.name
)
343 def member_names(self
):
344 return [m
.name
for m
in self
.members
]
349 def visit(self
, visitor
):
350 super().visit(visitor
)
351 visitor
.visit_enum_type(
352 self
.name
, self
.info
, self
.ifcond
, self
.features
,
353 self
.members
, self
.prefix
)
356 class QAPISchemaArrayType(QAPISchemaType
):
359 def __init__(self
, name
, info
, element_type
):
360 super().__init
__(name
, info
, None)
361 assert isinstance(element_type
, str)
362 self
._element
_type
_name
= element_type
363 self
.element_type
= None
365 def need_has_if_optional(self
):
366 # When FOO is an array, we still need has_FOO to distinguish
367 # absent (!has_FOO) from present and empty (has_FOO && !FOO).
370 def check(self
, schema
):
371 super().check(schema
)
372 self
.element_type
= schema
.resolve_type(
373 self
._element
_type
_name
, self
.info
,
374 self
.info
and self
.info
.defn_meta
)
375 assert not isinstance(self
.element_type
, QAPISchemaArrayType
)
377 def set_module(self
, schema
):
378 self
._set
_module
(schema
, self
.element_type
.info
)
383 return self
.element_type
.ifcond
385 def is_implicit(self
):
389 return c_name(self
.name
) + POINTER_SUFFIX
395 elt_doc_type
= self
.element_type
.doc_type()
398 return 'array of ' + elt_doc_type
400 def visit(self
, visitor
):
401 super().visit(visitor
)
402 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
407 return "%s type ['%s']" % (self
.meta
, self
._element
_type
_name
)
410 class QAPISchemaObjectType(QAPISchemaType
):
411 def __init__(self
, name
, info
, doc
, ifcond
, features
,
412 base
, local_members
, variants
):
413 # struct has local_members, optional base, and no variants
414 # union has base, variants, and no local_members
415 super().__init
__(name
, info
, doc
, ifcond
, features
)
416 self
.meta
= 'union' if variants
else 'struct'
417 assert base
is None or isinstance(base
, str)
418 for m
in local_members
:
419 assert isinstance(m
, QAPISchemaObjectTypeMember
)
420 m
.set_defined_in(name
)
421 if variants
is not None:
422 assert isinstance(variants
, QAPISchemaVariants
)
423 variants
.set_defined_in(name
)
424 self
._base
_name
= base
426 self
.local_members
= local_members
427 self
.variants
= variants
430 def check(self
, schema
):
431 # This calls another type T's .check() exactly when the C
432 # struct emitted by gen_object() contains that T's C struct
433 # (pointers don't count).
434 if self
.members
is not None:
435 # A previous .check() completed: nothing to do
438 # Recursed: C struct contains itself
439 raise QAPISemError(self
.info
,
440 "object %s contains itself" % self
.name
)
442 super().check(schema
)
443 assert self
._checked
and self
.members
is None
447 self
.base
= schema
.resolve_type(self
._base
_name
, self
.info
,
449 if (not isinstance(self
.base
, QAPISchemaObjectType
)
450 or self
.base
.variants
):
453 "'base' requires a struct type, %s isn't"
454 % self
.base
.describe())
455 self
.base
.check(schema
)
456 self
.base
.check_clash(self
.info
, seen
)
457 for m
in self
.local_members
:
459 m
.check_clash(self
.info
, seen
)
460 members
= seen
.values()
463 self
.variants
.check(schema
, seen
)
464 self
.variants
.check_clash(self
.info
, seen
)
466 self
.members
= members
# mark completed
468 # Check that the members of this type do not cause duplicate JSON members,
469 # and update seen to track the members seen so far. Report any errors
470 # on behalf of info, which is not necessarily self.info
471 def check_clash(self
, info
, seen
):
473 for m
in self
.members
:
474 m
.check_clash(info
, seen
)
476 self
.variants
.check_clash(info
, seen
)
478 def connect_doc(self
, doc
=None):
479 super().connect_doc(doc
)
480 doc
= doc
or self
.doc
481 if self
.base
and self
.base
.is_implicit():
482 self
.base
.connect_doc(doc
)
483 for m
in self
.local_members
:
486 def is_implicit(self
):
487 # See QAPISchema._make_implicit_object_type(), as well as
489 return self
.name
.startswith('q_')
492 assert self
.members
is not None
493 return not self
.members
and not self
.variants
495 def has_conditional_members(self
):
496 assert self
.members
is not None
497 return any(m
.ifcond
.is_present() for m
in self
.members
)
500 assert self
.name
!= 'q_empty'
501 return super().c_name()
504 assert not self
.is_implicit()
505 return c_name(self
.name
) + POINTER_SUFFIX
507 def c_unboxed_type(self
):
508 return c_name(self
.name
)
513 def visit(self
, visitor
):
514 super().visit(visitor
)
515 visitor
.visit_object_type(
516 self
.name
, self
.info
, self
.ifcond
, self
.features
,
517 self
.base
, self
.local_members
, self
.variants
)
518 visitor
.visit_object_type_flat(
519 self
.name
, self
.info
, self
.ifcond
, self
.features
,
520 self
.members
, self
.variants
)
523 class QAPISchemaAlternateType(QAPISchemaType
):
526 def __init__(self
, name
, info
, doc
, ifcond
, features
, variants
):
527 super().__init
__(name
, info
, doc
, ifcond
, features
)
528 assert isinstance(variants
, QAPISchemaVariants
)
529 assert variants
.tag_member
530 variants
.set_defined_in(name
)
531 variants
.tag_member
.set_defined_in(self
.name
)
532 self
.variants
= variants
534 def check(self
, schema
):
535 super().check(schema
)
536 self
.variants
.tag_member
.check(schema
)
537 # Not calling self.variants.check_clash(), because there's nothing
539 self
.variants
.check(schema
, {})
540 # Alternate branch names have no relation to the tag enum values;
541 # so we have to check for potential name collisions ourselves.
544 for v
in self
.variants
.variants
:
545 v
.check_clash(self
.info
, seen
)
546 qtype
= v
.type.alternate_qtype()
551 % (v
.describe(self
.info
), v
.type.describe()))
552 conflicting
= set([qtype
])
553 if qtype
== 'QTYPE_QSTRING':
554 if isinstance(v
.type, QAPISchemaEnumType
):
555 for m
in v
.type.members
:
556 if m
.name
in ['on', 'off']:
557 conflicting
.add('QTYPE_QBOOL')
558 if re
.match(r
'[-+0-9.]', m
.name
):
559 # lazy, could be tightened
560 conflicting
.add('QTYPE_QNUM')
562 conflicting
.add('QTYPE_QNUM')
563 conflicting
.add('QTYPE_QBOOL')
564 for qt
in conflicting
:
568 "%s can't be distinguished from '%s'"
569 % (v
.describe(self
.info
), types_seen
[qt
]))
570 types_seen
[qt
] = v
.name
572 def connect_doc(self
, doc
=None):
573 super().connect_doc(doc
)
574 doc
= doc
or self
.doc
575 for v
in self
.variants
.variants
:
579 return c_name(self
.name
) + POINTER_SUFFIX
584 def visit(self
, visitor
):
585 super().visit(visitor
)
586 visitor
.visit_alternate_type(
587 self
.name
, self
.info
, self
.ifcond
, self
.features
, self
.variants
)
590 class QAPISchemaVariants
:
591 def __init__(self
, tag_name
, info
, tag_member
, variants
):
592 # Unions pass tag_name but not tag_member.
593 # Alternates pass tag_member but not tag_name.
594 # After check(), tag_member is always set.
595 assert bool(tag_member
) != bool(tag_name
)
596 assert (isinstance(tag_name
, str) or
597 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
599 assert isinstance(v
, QAPISchemaVariant
)
600 self
._tag
_name
= tag_name
602 self
.tag_member
= tag_member
603 self
.variants
= variants
605 def set_defined_in(self
, name
):
606 for v
in self
.variants
:
607 v
.set_defined_in(name
)
609 def check(self
, schema
, seen
):
610 if self
._tag
_name
: # union
611 self
.tag_member
= seen
.get(c_name(self
._tag
_name
))
613 # Pointing to the base type when not implicit would be
614 # nice, but we don't know it here
615 if not self
.tag_member
or self
._tag
_name
!= self
.tag_member
.name
:
618 "discriminator '%s' is not a member of %s"
619 % (self
._tag
_name
, base
))
621 base_type
= schema
.lookup_type(self
.tag_member
.defined_in
)
623 if not base_type
.is_implicit():
624 base
= "base type '%s'" % self
.tag_member
.defined_in
625 if not isinstance(self
.tag_member
.type, QAPISchemaEnumType
):
628 "discriminator member '%s' of %s must be of enum type"
629 % (self
._tag
_name
, base
))
630 if self
.tag_member
.optional
:
633 "discriminator member '%s' of %s must not be optional"
634 % (self
._tag
_name
, base
))
635 if self
.tag_member
.ifcond
.is_present():
638 "discriminator member '%s' of %s must not be conditional"
639 % (self
._tag
_name
, base
))
641 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
642 assert not self
.tag_member
.optional
643 assert not self
.tag_member
.ifcond
.is_present()
644 if self
._tag
_name
: # union
645 # branches that are not explicitly covered get an empty type
646 cases
= {v
.name
for v
in self
.variants
}
647 for m
in self
.tag_member
.type.members
:
648 if m
.name
not in cases
:
649 v
= QAPISchemaVariant(m
.name
, self
.info
,
651 v
.set_defined_in(self
.tag_member
.defined_in
)
652 self
.variants
.append(v
)
653 if not self
.variants
:
654 raise QAPISemError(self
.info
, "union has no branches")
655 for v
in self
.variants
:
657 # Union names must match enum values; alternate names are
658 # checked separately. Use 'seen' to tell the two apart.
660 if v
.name
not in self
.tag_member
.type.member_names():
663 "branch '%s' is not a value of %s"
664 % (v
.name
, self
.tag_member
.type.describe()))
665 if not isinstance(v
.type, QAPISchemaObjectType
):
669 % (v
.describe(self
.info
), v
.type.describe()))
672 def check_clash(self
, info
, seen
):
673 for v
in self
.variants
:
674 # Reset seen map for each variant, since qapi names from one
675 # branch do not affect another branch
676 v
.type.check_clash(info
, dict(seen
))
679 class QAPISchemaMember
:
680 """ Represents object members, enum members and features """
683 def __init__(self
, name
, info
, ifcond
=None):
684 assert isinstance(name
, str)
687 self
.ifcond
= ifcond
or QAPISchemaIfCond()
688 self
.defined_in
= None
690 def set_defined_in(self
, name
):
691 assert not self
.defined_in
692 self
.defined_in
= name
694 def check_clash(self
, info
, seen
):
695 cname
= c_name(self
.name
)
699 "%s collides with %s"
700 % (self
.describe(info
), seen
[cname
].describe(info
)))
703 def connect_doc(self
, doc
):
705 doc
.connect_member(self
)
707 def describe(self
, info
):
710 defined_in
= self
.defined_in
713 if defined_in
.startswith('q_obj_'):
714 # See QAPISchema._make_implicit_object_type() - reverse the
715 # mapping there to create a nice human-readable description
716 defined_in
= defined_in
[6:]
717 if defined_in
.endswith('-arg'):
718 # Implicit type created for a command's dict 'data'
719 assert role
== 'member'
722 defined_in
= defined_in
[:-4]
723 elif defined_in
.endswith('-base'):
724 # Implicit type created for a union's dict 'base'
725 role
= 'base ' + role
726 defined_in
= defined_in
[:-5]
730 if defined_in
!= info
.defn_name
:
731 return "%s '%s' of %s '%s'" % (role
, self
.name
, meta
, defined_in
)
732 return "%s '%s'" % (role
, self
.name
)
735 class QAPISchemaEnumMember(QAPISchemaMember
):
738 def __init__(self
, name
, info
, ifcond
=None, features
=None):
739 super().__init
__(name
, info
, ifcond
)
740 for f
in features
or []:
741 assert isinstance(f
, QAPISchemaFeature
)
742 f
.set_defined_in(name
)
743 self
.features
= features
or []
745 def connect_doc(self
, doc
):
746 super().connect_doc(doc
)
748 for f
in self
.features
:
749 doc
.connect_feature(f
)
752 class QAPISchemaFeature(QAPISchemaMember
):
755 def is_special(self
):
756 return self
.name
in ('deprecated', 'unstable')
759 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
760 def __init__(self
, name
, info
, typ
, optional
, ifcond
=None, features
=None):
761 super().__init
__(name
, info
, ifcond
)
762 assert isinstance(typ
, str)
763 assert isinstance(optional
, bool)
764 for f
in features
or []:
765 assert isinstance(f
, QAPISchemaFeature
)
766 f
.set_defined_in(name
)
767 self
._type
_name
= typ
769 self
.optional
= optional
770 self
.features
= features
or []
774 return self
.optional
and self
.type.need_has_if_optional()
776 def check(self
, schema
):
777 assert self
.defined_in
778 self
.type = schema
.resolve_type(self
._type
_name
, self
.info
,
781 for f
in self
.features
:
782 f
.check_clash(self
.info
, seen
)
784 def connect_doc(self
, doc
):
785 super().connect_doc(doc
)
787 for f
in self
.features
:
788 doc
.connect_feature(f
)
791 class QAPISchemaVariant(QAPISchemaObjectTypeMember
):
794 def __init__(self
, name
, info
, typ
, ifcond
=None):
795 super().__init
__(name
, info
, typ
, False, ifcond
)
798 class QAPISchemaCommand(QAPISchemaEntity
):
801 def __init__(self
, name
, info
, doc
, ifcond
, features
,
803 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
,
805 super().__init
__(name
, info
, doc
, ifcond
, features
)
806 assert not arg_type
or isinstance(arg_type
, str)
807 assert not ret_type
or isinstance(ret_type
, str)
808 self
._arg
_type
_name
= arg_type
810 self
._ret
_type
_name
= ret_type
813 self
.success_response
= success_response
815 self
.allow_oob
= allow_oob
816 self
.allow_preconfig
= allow_preconfig
817 self
.coroutine
= coroutine
819 def check(self
, schema
):
820 super().check(schema
)
821 if self
._arg
_type
_name
:
822 self
.arg_type
= schema
.resolve_type(
823 self
._arg
_type
_name
, self
.info
, "command's 'data'")
824 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
827 "command's 'data' cannot take %s"
828 % self
.arg_type
.describe())
829 if self
.arg_type
.variants
and not self
.boxed
:
832 "command's 'data' can take %s only with 'boxed': true"
833 % self
.arg_type
.describe())
834 self
.arg_type
.check(schema
)
835 if self
.arg_type
.has_conditional_members() and not self
.boxed
:
838 "conditional command arguments require 'boxed': true")
839 if self
._ret
_type
_name
:
840 self
.ret_type
= schema
.resolve_type(
841 self
._ret
_type
_name
, self
.info
, "command's 'returns'")
842 if self
.name
not in self
.info
.pragma
.command_returns_exceptions
:
844 if isinstance(typ
, QAPISchemaArrayType
):
845 typ
= self
.ret_type
.element_type
847 if not isinstance(typ
, QAPISchemaObjectType
):
850 "command's 'returns' cannot take %s"
851 % self
.ret_type
.describe())
853 def connect_doc(self
, doc
=None):
854 super().connect_doc(doc
)
855 doc
= doc
or self
.doc
857 if self
.arg_type
and self
.arg_type
.is_implicit():
858 self
.arg_type
.connect_doc(doc
)
860 def visit(self
, visitor
):
861 super().visit(visitor
)
862 visitor
.visit_command(
863 self
.name
, self
.info
, self
.ifcond
, self
.features
,
864 self
.arg_type
, self
.ret_type
, self
.gen
, self
.success_response
,
865 self
.boxed
, self
.allow_oob
, self
.allow_preconfig
,
869 class QAPISchemaEvent(QAPISchemaEntity
):
872 def __init__(self
, name
, info
, doc
, ifcond
, features
, arg_type
, boxed
):
873 super().__init
__(name
, info
, doc
, ifcond
, features
)
874 assert not arg_type
or isinstance(arg_type
, str)
875 self
._arg
_type
_name
= arg_type
879 def check(self
, schema
):
880 super().check(schema
)
881 if self
._arg
_type
_name
:
882 self
.arg_type
= schema
.resolve_type(
883 self
._arg
_type
_name
, self
.info
, "event's 'data'")
884 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
887 "event's 'data' cannot take %s"
888 % self
.arg_type
.describe())
889 if self
.arg_type
.variants
and not self
.boxed
:
892 "event's 'data' can take %s only with 'boxed': true"
893 % self
.arg_type
.describe())
894 self
.arg_type
.check(schema
)
895 if self
.arg_type
.has_conditional_members() and not self
.boxed
:
898 "conditional event arguments require 'boxed': true")
900 def connect_doc(self
, doc
=None):
901 super().connect_doc(doc
)
902 doc
= doc
or self
.doc
904 if self
.arg_type
and self
.arg_type
.is_implicit():
905 self
.arg_type
.connect_doc(doc
)
907 def visit(self
, visitor
):
908 super().visit(visitor
)
910 self
.name
, self
.info
, self
.ifcond
, self
.features
,
911 self
.arg_type
, self
.boxed
)
915 def __init__(self
, fname
):
919 parser
= QAPISchemaParser(fname
)
920 except OSError as err
:
922 f
"can't read schema file '{fname}': {err.strerror}"
925 exprs
= check_exprs(parser
.exprs
)
926 self
.docs
= parser
.docs
927 self
._entity
_list
= []
928 self
._entity
_dict
= {}
929 self
._module
_dict
= OrderedDict()
930 self
._schema
_dir
= os
.path
.dirname(fname
)
931 self
._make
_module
(QAPISchemaModule
.BUILTIN_MODULE_NAME
)
932 self
._make
_module
(fname
)
933 self
._predefining
= True
934 self
._def
_predefineds
()
935 self
._predefining
= False
936 self
._def
_exprs
(exprs
)
939 def _def_entity(self
, ent
):
940 # Only the predefined types are allowed to not have info
941 assert ent
.info
or self
._predefining
942 self
._entity
_list
.append(ent
)
945 # TODO reject names that differ only in '_' vs. '.' vs. '-',
946 # because they're liable to clash in generated C.
947 other_ent
= self
._entity
_dict
.get(ent
.name
)
950 where
= QAPISourceError(other_ent
.info
, "previous definition")
953 "'%s' is already defined\n%s" % (ent
.name
, where
))
955 ent
.info
, "%s is already defined" % other_ent
.describe())
956 self
._entity
_dict
[ent
.name
] = ent
958 def lookup_entity(self
, name
, typ
=None):
959 ent
= self
._entity
_dict
.get(name
)
960 if typ
and not isinstance(ent
, typ
):
964 def lookup_type(self
, name
):
965 return self
.lookup_entity(name
, QAPISchemaType
)
967 def resolve_type(self
, name
, info
, what
):
968 typ
= self
.lookup_type(name
)
973 info
, "%s uses unknown type '%s'" % (what
, name
))
976 def _module_name(self
, fname
: str) -> str:
977 if QAPISchemaModule
.is_system_module(fname
):
979 return os
.path
.relpath(fname
, self
._schema
_dir
)
981 def _make_module(self
, fname
):
982 name
= self
._module
_name
(fname
)
983 if name
not in self
._module
_dict
:
984 self
._module
_dict
[name
] = QAPISchemaModule(name
)
985 return self
._module
_dict
[name
]
987 def module_by_fname(self
, fname
):
988 name
= self
._module
_name
(fname
)
989 return self
._module
_dict
[name
]
991 def _def_include(self
, expr
: QAPIExpression
):
992 include
= expr
['include']
993 assert expr
.doc
is None
995 QAPISchemaInclude(self
._make
_module
(include
), expr
.info
))
997 def _def_builtin_type(self
, name
, json_type
, c_type
):
998 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
999 # Instantiating only the arrays that are actually used would
1000 # be nice, but we can't as long as their generated code
1001 # (qapi-builtin-types.[ch]) may be shared by some other
1003 self
._make
_array
_type
(name
, None)
1005 def _def_predefineds(self
):
1006 for t
in [('str', 'string', 'char' + POINTER_SUFFIX
),
1007 ('number', 'number', 'double'),
1008 ('int', 'int', 'int64_t'),
1009 ('int8', 'int', 'int8_t'),
1010 ('int16', 'int', 'int16_t'),
1011 ('int32', 'int', 'int32_t'),
1012 ('int64', 'int', 'int64_t'),
1013 ('uint8', 'int', 'uint8_t'),
1014 ('uint16', 'int', 'uint16_t'),
1015 ('uint32', 'int', 'uint32_t'),
1016 ('uint64', 'int', 'uint64_t'),
1017 ('size', 'int', 'uint64_t'),
1018 ('bool', 'boolean', 'bool'),
1019 ('any', 'value', 'QObject' + POINTER_SUFFIX
),
1020 ('null', 'null', 'QNull' + POINTER_SUFFIX
)]:
1021 self
._def
_builtin
_type
(*t
)
1022 self
.the_empty_object_type
= QAPISchemaObjectType(
1023 'q_empty', None, None, None, None, None, [], None)
1024 self
._def
_entity
(self
.the_empty_object_type
)
1026 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1028 qtype_values
= self
._make
_enum
_members
(
1029 [{'name': n
} for n
in qtypes
], None)
1031 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None, None,
1032 qtype_values
, 'QTYPE'))
1034 def _make_features(self
, features
, info
):
1035 if features
is None:
1037 return [QAPISchemaFeature(f
['name'], info
,
1038 QAPISchemaIfCond(f
.get('if')))
1041 def _make_enum_member(self
, name
, ifcond
, features
, info
):
1042 return QAPISchemaEnumMember(name
, info
,
1043 QAPISchemaIfCond(ifcond
),
1044 self
._make
_features
(features
, info
))
1046 def _make_enum_members(self
, values
, info
):
1047 return [self
._make
_enum
_member
(v
['name'], v
.get('if'),
1048 v
.get('features'), info
)
1051 def _make_array_type(self
, element_type
, info
):
1052 name
= element_type
+ 'List' # reserved by check_defn_name_str()
1053 if not self
.lookup_type(name
):
1054 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1057 def _make_implicit_object_type(self
, name
, info
, ifcond
, role
, members
):
1060 # See also QAPISchemaObjectTypeMember.describe()
1061 name
= 'q_obj_%s-%s' % (name
, role
)
1062 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
1064 # The implicit object type has multiple users. This can
1065 # only be a duplicate definition, which will be flagged
1069 self
._def
_entity
(QAPISchemaObjectType(
1070 name
, info
, None, ifcond
, None, None, members
, None))
1073 def _def_enum_type(self
, expr
: QAPIExpression
):
1076 prefix
= expr
.get('prefix')
1077 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1079 features
= self
._make
_features
(expr
.get('features'), info
)
1080 self
._def
_entity
(QAPISchemaEnumType(
1081 name
, info
, expr
.doc
, ifcond
, features
,
1082 self
._make
_enum
_members
(data
, info
), prefix
))
1084 def _make_member(self
, name
, typ
, ifcond
, features
, info
):
1086 if name
.startswith('*'):
1089 if isinstance(typ
, list):
1090 assert len(typ
) == 1
1091 typ
= self
._make
_array
_type
(typ
[0], info
)
1092 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
,
1093 self
._make
_features
(features
, info
))
1095 def _make_members(self
, data
, info
):
1096 return [self
._make
_member
(key
, value
['type'],
1097 QAPISchemaIfCond(value
.get('if')),
1098 value
.get('features'), info
)
1099 for (key
, value
) in data
.items()]
1101 def _def_struct_type(self
, expr
: QAPIExpression
):
1102 name
= expr
['struct']
1103 base
= expr
.get('base')
1106 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1107 features
= self
._make
_features
(expr
.get('features'), info
)
1108 self
._def
_entity
(QAPISchemaObjectType(
1109 name
, info
, expr
.doc
, ifcond
, features
, base
,
1110 self
._make
_members
(data
, info
),
1113 def _make_variant(self
, case
, typ
, ifcond
, info
):
1114 if isinstance(typ
, list):
1115 assert len(typ
) == 1
1116 typ
= self
._make
_array
_type
(typ
[0], info
)
1117 return QAPISchemaVariant(case
, info
, typ
, ifcond
)
1119 def _def_union_type(self
, expr
: QAPIExpression
):
1120 name
= expr
['union']
1122 tag_name
= expr
['discriminator']
1124 assert isinstance(data
, dict)
1126 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1127 features
= self
._make
_features
(expr
.get('features'), info
)
1128 if isinstance(base
, dict):
1129 base
= self
._make
_implicit
_object
_type
(
1131 'base', self
._make
_members
(base
, info
))
1133 self
._make
_variant
(key
, value
['type'],
1134 QAPISchemaIfCond(value
.get('if')),
1136 for (key
, value
) in data
.items()]
1137 members
: List
[QAPISchemaObjectTypeMember
] = []
1139 QAPISchemaObjectType(name
, info
, expr
.doc
, ifcond
, features
,
1142 tag_name
, info
, None, variants
)))
1144 def _def_alternate_type(self
, expr
: QAPIExpression
):
1145 name
= expr
['alternate']
1147 assert isinstance(data
, dict)
1148 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1150 features
= self
._make
_features
(expr
.get('features'), info
)
1152 self
._make
_variant
(key
, value
['type'],
1153 QAPISchemaIfCond(value
.get('if')),
1155 for (key
, value
) in data
.items()]
1156 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
1158 QAPISchemaAlternateType(
1159 name
, info
, expr
.doc
, ifcond
, features
,
1160 QAPISchemaVariants(None, info
, tag_member
, variants
)))
1162 def _def_command(self
, expr
: QAPIExpression
):
1163 name
= expr
['command']
1164 data
= expr
.get('data')
1165 rets
= expr
.get('returns')
1166 gen
= expr
.get('gen', True)
1167 success_response
= expr
.get('success-response', True)
1168 boxed
= expr
.get('boxed', False)
1169 allow_oob
= expr
.get('allow-oob', False)
1170 allow_preconfig
= expr
.get('allow-preconfig', False)
1171 coroutine
= expr
.get('coroutine', False)
1172 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1174 features
= self
._make
_features
(expr
.get('features'), info
)
1175 if isinstance(data
, OrderedDict
):
1176 data
= self
._make
_implicit
_object
_type
(
1178 'arg', self
._make
_members
(data
, info
))
1179 if isinstance(rets
, list):
1180 assert len(rets
) == 1
1181 rets
= self
._make
_array
_type
(rets
[0], info
)
1182 self
._def
_entity
(QAPISchemaCommand(name
, info
, expr
.doc
, ifcond
,
1183 features
, data
, rets
,
1184 gen
, success_response
,
1185 boxed
, allow_oob
, allow_preconfig
,
1188 def _def_event(self
, expr
: QAPIExpression
):
1189 name
= expr
['event']
1190 data
= expr
.get('data')
1191 boxed
= expr
.get('boxed', False)
1192 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1194 features
= self
._make
_features
(expr
.get('features'), info
)
1195 if isinstance(data
, OrderedDict
):
1196 data
= self
._make
_implicit
_object
_type
(
1198 'arg', self
._make
_members
(data
, info
))
1199 self
._def
_entity
(QAPISchemaEvent(name
, info
, expr
.doc
, ifcond
,
1200 features
, data
, boxed
))
1202 def _def_exprs(self
, exprs
):
1205 self
._def
_enum
_type
(expr
)
1206 elif 'struct' in expr
:
1207 self
._def
_struct
_type
(expr
)
1208 elif 'union' in expr
:
1209 self
._def
_union
_type
(expr
)
1210 elif 'alternate' in expr
:
1211 self
._def
_alternate
_type
(expr
)
1212 elif 'command' in expr
:
1213 self
._def
_command
(expr
)
1214 elif 'event' in expr
:
1215 self
._def
_event
(expr
)
1216 elif 'include' in expr
:
1217 self
._def
_include
(expr
)
1222 for ent
in self
._entity
_list
:
1226 for ent
in self
._entity
_list
:
1227 ent
.set_module(self
)
1229 def visit(self
, visitor
):
1230 visitor
.visit_begin(self
)
1231 for mod
in self
._module
_dict
.values():