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
)
102 def _set_module(self
, schema
, info
):
104 fname
= info
.fname
if info
else QAPISchemaModule
.BUILTIN_MODULE_NAME
105 self
._module
= schema
.module_by_fname(fname
)
106 self
._module
.add_entity(self
)
108 def set_module(self
, schema
):
109 self
._set
_module
(schema
, self
.info
)
116 def is_implicit(self
):
119 def visit(self
, visitor
):
124 return "%s '%s'" % (self
.meta
, self
.name
)
127 class QAPISchemaVisitor
:
128 def visit_begin(self
, schema
):
134 def visit_module(self
, name
):
137 def visit_needed(self
, entity
):
138 # Default to visiting everything
141 def visit_include(self
, name
, info
):
144 def visit_builtin_type(self
, name
, info
, json_type
):
147 def visit_enum_type(self
, name
, info
, ifcond
, features
, members
, prefix
):
150 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
153 def visit_object_type(self
, name
, info
, ifcond
, features
,
154 base
, members
, variants
):
157 def visit_object_type_flat(self
, name
, info
, ifcond
, features
,
161 def visit_alternate_type(self
, name
, info
, ifcond
, features
, variants
):
164 def visit_command(self
, name
, info
, ifcond
, features
,
165 arg_type
, ret_type
, gen
, success_response
, boxed
,
166 allow_oob
, allow_preconfig
, coroutine
):
169 def visit_event(self
, name
, info
, ifcond
, features
, arg_type
, boxed
):
173 class QAPISchemaModule
:
175 BUILTIN_MODULE_NAME
= './builtin'
177 def __init__(self
, name
):
179 self
._entity
_list
= []
182 def is_system_module(name
: str) -> bool:
184 System modules are internally defined modules.
186 Their names start with the "./" prefix.
188 return name
.startswith('./')
191 def is_user_module(cls
, name
: str) -> bool:
193 User modules are those defined by the user in qapi JSON files.
195 They do not start with the "./" prefix.
197 return not cls
.is_system_module(name
)
200 def is_builtin_module(cls
, name
: str) -> bool:
202 The built-in module is a single System module for the built-in types.
204 It is always "./builtin".
206 return name
== cls
.BUILTIN_MODULE_NAME
208 def add_entity(self
, ent
):
209 self
._entity
_list
.append(ent
)
211 def visit(self
, visitor
):
212 visitor
.visit_module(self
.name
)
213 for entity
in self
._entity
_list
:
214 if visitor
.visit_needed(entity
):
215 entity
.visit(visitor
)
218 class QAPISchemaInclude(QAPISchemaEntity
):
219 def __init__(self
, sub_module
, info
):
220 super().__init
__(None, info
, None)
221 self
._sub
_module
= sub_module
223 def visit(self
, visitor
):
224 super().visit(visitor
)
225 visitor
.visit_include(self
._sub
_module
.name
, self
.info
)
228 class QAPISchemaType(QAPISchemaEntity
):
229 # Return the C type for common use.
230 # For the types we commonly box, this is a pointer type.
234 # Return the C type to be used in a parameter list.
235 def c_param_type(self
):
238 # Return the C type to be used where we suppress boxing.
239 def c_unboxed_type(self
):
245 def alternate_qtype(self
):
247 'null': 'QTYPE_QNULL',
248 'string': 'QTYPE_QSTRING',
249 'number': 'QTYPE_QNUM',
251 'boolean': 'QTYPE_QBOOL',
252 'array': 'QTYPE_QLIST',
253 'object': 'QTYPE_QDICT'
255 return json2qtype
.get(self
.json_type())
258 if self
.is_implicit():
262 def need_has_if_optional(self
):
263 # When FOO is a pointer, has_FOO == !!FOO, i.e. has_FOO is redundant.
264 # Except for arrays; see QAPISchemaArrayType.need_has_if_optional().
265 return not self
.c_type().endswith(POINTER_SUFFIX
)
267 def check(self
, schema
):
268 super().check(schema
)
269 for feat
in self
.features
:
270 if feat
.is_special():
273 f
"feature '{feat.name}' is not supported for types")
277 return "%s type '%s'" % (self
.meta
, self
.name
)
280 class QAPISchemaBuiltinType(QAPISchemaType
):
283 def __init__(self
, name
, json_type
, c_type
):
284 super().__init
__(name
, None, None)
285 assert not c_type
or isinstance(c_type
, str)
286 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
288 self
._json
_type
_name
= json_type
289 self
._c
_type
_name
= c_type
295 return self
._c
_type
_name
297 def c_param_type(self
):
298 if self
.name
== 'str':
299 return 'const ' + self
._c
_type
_name
300 return self
._c
_type
_name
303 return self
._json
_type
_name
306 return self
.json_type()
308 def visit(self
, visitor
):
309 super().visit(visitor
)
310 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
313 class QAPISchemaEnumType(QAPISchemaType
):
316 def __init__(self
, name
, info
, doc
, ifcond
, features
, members
, prefix
):
317 super().__init
__(name
, info
, doc
, ifcond
, features
)
319 assert isinstance(m
, QAPISchemaEnumMember
)
320 m
.set_defined_in(name
)
321 assert prefix
is None or isinstance(prefix
, str)
322 self
.members
= members
325 def check(self
, schema
):
326 super().check(schema
)
328 for m
in self
.members
:
329 m
.check_clash(self
.info
, seen
)
331 def connect_doc(self
, doc
=None):
332 super().connect_doc(doc
)
333 doc
= doc
or self
.doc
334 for m
in self
.members
:
337 def is_implicit(self
):
338 # See QAPISchema._def_predefineds()
339 return self
.name
== 'QType'
342 return c_name(self
.name
)
344 def member_names(self
):
345 return [m
.name
for m
in self
.members
]
350 def visit(self
, visitor
):
351 super().visit(visitor
)
352 visitor
.visit_enum_type(
353 self
.name
, self
.info
, self
.ifcond
, self
.features
,
354 self
.members
, self
.prefix
)
357 class QAPISchemaArrayType(QAPISchemaType
):
360 def __init__(self
, name
, info
, element_type
):
361 super().__init
__(name
, info
, None)
362 assert isinstance(element_type
, str)
363 self
._element
_type
_name
= element_type
364 self
.element_type
= None
366 def need_has_if_optional(self
):
367 # When FOO is an array, we still need has_FOO to distinguish
368 # absent (!has_FOO) from present and empty (has_FOO && !FOO).
371 def check(self
, schema
):
372 super().check(schema
)
373 self
.element_type
= schema
.resolve_type(
374 self
._element
_type
_name
, self
.info
,
375 self
.info
and self
.info
.defn_meta
)
376 assert not isinstance(self
.element_type
, QAPISchemaArrayType
)
378 def set_module(self
, schema
):
379 self
._set
_module
(schema
, self
.element_type
.info
)
384 return self
.element_type
.ifcond
386 def is_implicit(self
):
390 return c_name(self
.name
) + POINTER_SUFFIX
396 elt_doc_type
= self
.element_type
.doc_type()
399 return 'array of ' + elt_doc_type
401 def visit(self
, visitor
):
402 super().visit(visitor
)
403 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
408 return "%s type ['%s']" % (self
.meta
, self
._element
_type
_name
)
411 class QAPISchemaObjectType(QAPISchemaType
):
412 def __init__(self
, name
, info
, doc
, ifcond
, features
,
413 base
, local_members
, variants
):
414 # struct has local_members, optional base, and no variants
415 # union has base, variants, and no local_members
416 super().__init
__(name
, info
, doc
, ifcond
, features
)
417 self
.meta
= 'union' if variants
else 'struct'
418 assert base
is None or isinstance(base
, str)
419 for m
in local_members
:
420 assert isinstance(m
, QAPISchemaObjectTypeMember
)
421 m
.set_defined_in(name
)
422 if variants
is not None:
423 assert isinstance(variants
, QAPISchemaVariants
)
424 variants
.set_defined_in(name
)
425 self
._base
_name
= base
427 self
.local_members
= local_members
428 self
.variants
= variants
431 def check(self
, schema
):
432 # This calls another type T's .check() exactly when the C
433 # struct emitted by gen_object() contains that T's C struct
434 # (pointers don't count).
435 if self
.members
is not None:
436 # A previous .check() completed: nothing to do
439 # Recursed: C struct contains itself
440 raise QAPISemError(self
.info
,
441 "object %s contains itself" % self
.name
)
443 super().check(schema
)
444 assert self
._checked
and self
.members
is None
448 self
.base
= schema
.resolve_type(self
._base
_name
, self
.info
,
450 if (not isinstance(self
.base
, QAPISchemaObjectType
)
451 or self
.base
.variants
):
454 "'base' requires a struct type, %s isn't"
455 % self
.base
.describe())
456 self
.base
.check(schema
)
457 self
.base
.check_clash(self
.info
, seen
)
458 for m
in self
.local_members
:
460 m
.check_clash(self
.info
, seen
)
461 members
= seen
.values()
464 self
.variants
.check(schema
, seen
)
465 self
.variants
.check_clash(self
.info
, seen
)
467 self
.members
= members
# mark completed
469 # Check that the members of this type do not cause duplicate JSON members,
470 # and update seen to track the members seen so far. Report any errors
471 # on behalf of info, which is not necessarily self.info
472 def check_clash(self
, info
, seen
):
474 for m
in self
.members
:
475 m
.check_clash(info
, seen
)
477 self
.variants
.check_clash(info
, seen
)
479 def connect_doc(self
, doc
=None):
480 super().connect_doc(doc
)
481 doc
= doc
or self
.doc
482 if self
.base
and self
.base
.is_implicit():
483 self
.base
.connect_doc(doc
)
484 for m
in self
.local_members
:
487 def is_implicit(self
):
488 # See QAPISchema._make_implicit_object_type(), as well as
490 return self
.name
.startswith('q_')
493 assert self
.members
is not None
494 return not self
.members
and not self
.variants
496 def has_conditional_members(self
):
497 assert self
.members
is not None
498 return any(m
.ifcond
.is_present() for m
in self
.members
)
501 assert self
.name
!= 'q_empty'
502 return super().c_name()
505 assert not self
.is_implicit()
506 return c_name(self
.name
) + POINTER_SUFFIX
508 def c_unboxed_type(self
):
509 return c_name(self
.name
)
514 def visit(self
, visitor
):
515 super().visit(visitor
)
516 visitor
.visit_object_type(
517 self
.name
, self
.info
, self
.ifcond
, self
.features
,
518 self
.base
, self
.local_members
, self
.variants
)
519 visitor
.visit_object_type_flat(
520 self
.name
, self
.info
, self
.ifcond
, self
.features
,
521 self
.members
, self
.variants
)
524 class QAPISchemaAlternateType(QAPISchemaType
):
527 def __init__(self
, name
, info
, doc
, ifcond
, features
, variants
):
528 super().__init
__(name
, info
, doc
, ifcond
, features
)
529 assert isinstance(variants
, QAPISchemaVariants
)
530 assert variants
.tag_member
531 variants
.set_defined_in(name
)
532 variants
.tag_member
.set_defined_in(self
.name
)
533 self
.variants
= variants
535 def check(self
, schema
):
536 super().check(schema
)
537 self
.variants
.tag_member
.check(schema
)
538 # Not calling self.variants.check_clash(), because there's nothing
540 self
.variants
.check(schema
, {})
541 # Alternate branch names have no relation to the tag enum values;
542 # so we have to check for potential name collisions ourselves.
545 for v
in self
.variants
.variants
:
546 v
.check_clash(self
.info
, seen
)
547 qtype
= v
.type.alternate_qtype()
552 % (v
.describe(self
.info
), v
.type.describe()))
553 conflicting
= set([qtype
])
554 if qtype
== 'QTYPE_QSTRING':
555 if isinstance(v
.type, QAPISchemaEnumType
):
556 for m
in v
.type.members
:
557 if m
.name
in ['on', 'off']:
558 conflicting
.add('QTYPE_QBOOL')
559 if re
.match(r
'[-+0-9.]', m
.name
):
560 # lazy, could be tightened
561 conflicting
.add('QTYPE_QNUM')
563 conflicting
.add('QTYPE_QNUM')
564 conflicting
.add('QTYPE_QBOOL')
565 for qt
in conflicting
:
569 "%s can't be distinguished from '%s'"
570 % (v
.describe(self
.info
), types_seen
[qt
]))
571 types_seen
[qt
] = v
.name
573 def connect_doc(self
, doc
=None):
574 super().connect_doc(doc
)
575 doc
= doc
or self
.doc
576 for v
in self
.variants
.variants
:
580 return c_name(self
.name
) + POINTER_SUFFIX
585 def visit(self
, visitor
):
586 super().visit(visitor
)
587 visitor
.visit_alternate_type(
588 self
.name
, self
.info
, self
.ifcond
, self
.features
, self
.variants
)
591 class QAPISchemaVariants
:
592 def __init__(self
, tag_name
, info
, tag_member
, variants
):
593 # Unions pass tag_name but not tag_member.
594 # Alternates pass tag_member but not tag_name.
595 # After check(), tag_member is always set.
596 assert bool(tag_member
) != bool(tag_name
)
597 assert (isinstance(tag_name
, str) or
598 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
600 assert isinstance(v
, QAPISchemaVariant
)
601 self
._tag
_name
= tag_name
603 self
.tag_member
= tag_member
604 self
.variants
= variants
606 def set_defined_in(self
, name
):
607 for v
in self
.variants
:
608 v
.set_defined_in(name
)
610 def check(self
, schema
, seen
):
611 if self
._tag
_name
: # union
612 self
.tag_member
= seen
.get(c_name(self
._tag
_name
))
614 # Pointing to the base type when not implicit would be
615 # nice, but we don't know it here
616 if not self
.tag_member
or self
._tag
_name
!= self
.tag_member
.name
:
619 "discriminator '%s' is not a member of %s"
620 % (self
._tag
_name
, base
))
622 base_type
= schema
.lookup_type(self
.tag_member
.defined_in
)
624 if not base_type
.is_implicit():
625 base
= "base type '%s'" % self
.tag_member
.defined_in
626 if not isinstance(self
.tag_member
.type, QAPISchemaEnumType
):
629 "discriminator member '%s' of %s must be of enum type"
630 % (self
._tag
_name
, base
))
631 if self
.tag_member
.optional
:
634 "discriminator member '%s' of %s must not be optional"
635 % (self
._tag
_name
, base
))
636 if self
.tag_member
.ifcond
.is_present():
639 "discriminator member '%s' of %s must not be conditional"
640 % (self
._tag
_name
, base
))
642 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
643 assert not self
.tag_member
.optional
644 assert not self
.tag_member
.ifcond
.is_present()
645 if self
._tag
_name
: # union
646 # branches that are not explicitly covered get an empty type
647 cases
= {v
.name
for v
in self
.variants
}
648 for m
in self
.tag_member
.type.members
:
649 if m
.name
not in cases
:
650 v
= QAPISchemaVariant(m
.name
, self
.info
,
652 v
.set_defined_in(self
.tag_member
.defined_in
)
653 self
.variants
.append(v
)
654 if not self
.variants
:
655 raise QAPISemError(self
.info
, "union has no branches")
656 for v
in self
.variants
:
658 # Union names must match enum values; alternate names are
659 # checked separately. Use 'seen' to tell the two apart.
661 if v
.name
not in self
.tag_member
.type.member_names():
664 "branch '%s' is not a value of %s"
665 % (v
.name
, self
.tag_member
.type.describe()))
666 if not isinstance(v
.type, QAPISchemaObjectType
):
670 % (v
.describe(self
.info
), v
.type.describe()))
673 def check_clash(self
, info
, seen
):
674 for v
in self
.variants
:
675 # Reset seen map for each variant, since qapi names from one
676 # branch do not affect another branch
677 v
.type.check_clash(info
, dict(seen
))
680 class QAPISchemaMember
:
681 """ Represents object members, enum members and features """
684 def __init__(self
, name
, info
, ifcond
=None):
685 assert isinstance(name
, str)
688 self
.ifcond
= ifcond
or QAPISchemaIfCond()
689 self
.defined_in
= None
691 def set_defined_in(self
, name
):
692 assert not self
.defined_in
693 self
.defined_in
= name
695 def check_clash(self
, info
, seen
):
696 cname
= c_name(self
.name
)
700 "%s collides with %s"
701 % (self
.describe(info
), seen
[cname
].describe(info
)))
704 def connect_doc(self
, doc
):
706 doc
.connect_member(self
)
708 def describe(self
, info
):
711 defined_in
= self
.defined_in
714 if defined_in
.startswith('q_obj_'):
715 # See QAPISchema._make_implicit_object_type() - reverse the
716 # mapping there to create a nice human-readable description
717 defined_in
= defined_in
[6:]
718 if defined_in
.endswith('-arg'):
719 # Implicit type created for a command's dict 'data'
720 assert role
== 'member'
723 defined_in
= defined_in
[:-4]
724 elif defined_in
.endswith('-base'):
725 # Implicit type created for a union's dict 'base'
726 role
= 'base ' + role
727 defined_in
= defined_in
[:-5]
731 if defined_in
!= info
.defn_name
:
732 return "%s '%s' of %s '%s'" % (role
, self
.name
, meta
, defined_in
)
733 return "%s '%s'" % (role
, self
.name
)
736 class QAPISchemaEnumMember(QAPISchemaMember
):
739 def __init__(self
, name
, info
, ifcond
=None, features
=None):
740 super().__init
__(name
, info
, ifcond
)
741 for f
in features
or []:
742 assert isinstance(f
, QAPISchemaFeature
)
743 f
.set_defined_in(name
)
744 self
.features
= features
or []
746 def connect_doc(self
, doc
):
747 super().connect_doc(doc
)
749 for f
in self
.features
:
750 doc
.connect_feature(f
)
753 class QAPISchemaFeature(QAPISchemaMember
):
756 def is_special(self
):
757 return self
.name
in ('deprecated', 'unstable')
760 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
761 def __init__(self
, name
, info
, typ
, optional
, ifcond
=None, features
=None):
762 super().__init
__(name
, info
, ifcond
)
763 assert isinstance(typ
, str)
764 assert isinstance(optional
, bool)
765 for f
in features
or []:
766 assert isinstance(f
, QAPISchemaFeature
)
767 f
.set_defined_in(name
)
768 self
._type
_name
= typ
770 self
.optional
= optional
771 self
.features
= features
or []
775 return self
.optional
and self
.type.need_has_if_optional()
777 def check(self
, schema
):
778 assert self
.defined_in
779 self
.type = schema
.resolve_type(self
._type
_name
, self
.info
,
782 for f
in self
.features
:
783 f
.check_clash(self
.info
, seen
)
785 def connect_doc(self
, doc
):
786 super().connect_doc(doc
)
788 for f
in self
.features
:
789 doc
.connect_feature(f
)
792 class QAPISchemaVariant(QAPISchemaObjectTypeMember
):
795 def __init__(self
, name
, info
, typ
, ifcond
=None):
796 super().__init
__(name
, info
, typ
, False, ifcond
)
799 class QAPISchemaCommand(QAPISchemaEntity
):
802 def __init__(self
, name
, info
, doc
, ifcond
, features
,
804 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
,
806 super().__init
__(name
, info
, doc
, ifcond
, features
)
807 assert not arg_type
or isinstance(arg_type
, str)
808 assert not ret_type
or isinstance(ret_type
, str)
809 self
._arg
_type
_name
= arg_type
811 self
._ret
_type
_name
= ret_type
814 self
.success_response
= success_response
816 self
.allow_oob
= allow_oob
817 self
.allow_preconfig
= allow_preconfig
818 self
.coroutine
= coroutine
820 def check(self
, schema
):
821 super().check(schema
)
822 if self
._arg
_type
_name
:
823 self
.arg_type
= schema
.resolve_type(
824 self
._arg
_type
_name
, self
.info
, "command's 'data'")
825 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
828 "command's 'data' cannot take %s"
829 % self
.arg_type
.describe())
830 if self
.arg_type
.variants
and not self
.boxed
:
833 "command's 'data' can take %s only with 'boxed': true"
834 % self
.arg_type
.describe())
835 self
.arg_type
.check(schema
)
836 if self
.arg_type
.has_conditional_members() and not self
.boxed
:
839 "conditional command arguments require 'boxed': true")
840 if self
._ret
_type
_name
:
841 self
.ret_type
= schema
.resolve_type(
842 self
._ret
_type
_name
, self
.info
, "command's 'returns'")
843 if self
.name
not in self
.info
.pragma
.command_returns_exceptions
:
845 if isinstance(typ
, QAPISchemaArrayType
):
846 typ
= self
.ret_type
.element_type
848 if not isinstance(typ
, QAPISchemaObjectType
):
851 "command's 'returns' cannot take %s"
852 % self
.ret_type
.describe())
854 def connect_doc(self
, doc
=None):
855 super().connect_doc(doc
)
856 doc
= doc
or self
.doc
858 if self
.arg_type
and self
.arg_type
.is_implicit():
859 self
.arg_type
.connect_doc(doc
)
861 def visit(self
, visitor
):
862 super().visit(visitor
)
863 visitor
.visit_command(
864 self
.name
, self
.info
, self
.ifcond
, self
.features
,
865 self
.arg_type
, self
.ret_type
, self
.gen
, self
.success_response
,
866 self
.boxed
, self
.allow_oob
, self
.allow_preconfig
,
870 class QAPISchemaEvent(QAPISchemaEntity
):
873 def __init__(self
, name
, info
, doc
, ifcond
, features
, arg_type
, boxed
):
874 super().__init
__(name
, info
, doc
, ifcond
, features
)
875 assert not arg_type
or isinstance(arg_type
, str)
876 self
._arg
_type
_name
= arg_type
880 def check(self
, schema
):
881 super().check(schema
)
882 if self
._arg
_type
_name
:
883 self
.arg_type
= schema
.resolve_type(
884 self
._arg
_type
_name
, self
.info
, "event's 'data'")
885 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
888 "event's 'data' cannot take %s"
889 % self
.arg_type
.describe())
890 if self
.arg_type
.variants
and not self
.boxed
:
893 "event's 'data' can take %s only with 'boxed': true"
894 % self
.arg_type
.describe())
895 self
.arg_type
.check(schema
)
896 if self
.arg_type
.has_conditional_members() and not self
.boxed
:
899 "conditional event arguments require 'boxed': true")
901 def connect_doc(self
, doc
=None):
902 super().connect_doc(doc
)
903 doc
= doc
or self
.doc
905 if self
.arg_type
and self
.arg_type
.is_implicit():
906 self
.arg_type
.connect_doc(doc
)
908 def visit(self
, visitor
):
909 super().visit(visitor
)
911 self
.name
, self
.info
, self
.ifcond
, self
.features
,
912 self
.arg_type
, self
.boxed
)
916 def __init__(self
, fname
):
920 parser
= QAPISchemaParser(fname
)
921 except OSError as err
:
923 f
"can't read schema file '{fname}': {err.strerror}"
926 exprs
= check_exprs(parser
.exprs
)
927 self
.docs
= parser
.docs
928 self
._entity
_list
= []
929 self
._entity
_dict
= {}
930 self
._module
_dict
= OrderedDict()
931 self
._schema
_dir
= os
.path
.dirname(fname
)
932 self
._make
_module
(QAPISchemaModule
.BUILTIN_MODULE_NAME
)
933 self
._make
_module
(fname
)
934 self
._predefining
= True
935 self
._def
_predefineds
()
936 self
._predefining
= False
937 self
._def
_exprs
(exprs
)
940 def _def_entity(self
, ent
):
941 # Only the predefined types are allowed to not have info
942 assert ent
.info
or self
._predefining
943 self
._entity
_list
.append(ent
)
946 # TODO reject names that differ only in '_' vs. '.' vs. '-',
947 # because they're liable to clash in generated C.
948 other_ent
= self
._entity
_dict
.get(ent
.name
)
951 where
= QAPISourceError(other_ent
.info
, "previous definition")
954 "'%s' is already defined\n%s" % (ent
.name
, where
))
956 ent
.info
, "%s is already defined" % other_ent
.describe())
957 self
._entity
_dict
[ent
.name
] = ent
959 def lookup_entity(self
, name
, typ
=None):
960 ent
= self
._entity
_dict
.get(name
)
961 if typ
and not isinstance(ent
, typ
):
965 def lookup_type(self
, name
):
966 return self
.lookup_entity(name
, QAPISchemaType
)
968 def resolve_type(self
, name
, info
, what
):
969 typ
= self
.lookup_type(name
)
974 info
, "%s uses unknown type '%s'" % (what
, name
))
977 def _module_name(self
, fname
: str) -> str:
978 if QAPISchemaModule
.is_system_module(fname
):
980 return os
.path
.relpath(fname
, self
._schema
_dir
)
982 def _make_module(self
, fname
):
983 name
= self
._module
_name
(fname
)
984 if name
not in self
._module
_dict
:
985 self
._module
_dict
[name
] = QAPISchemaModule(name
)
986 return self
._module
_dict
[name
]
988 def module_by_fname(self
, fname
):
989 name
= self
._module
_name
(fname
)
990 return self
._module
_dict
[name
]
992 def _def_include(self
, expr
: QAPIExpression
):
993 include
= expr
['include']
994 assert expr
.doc
is None
996 QAPISchemaInclude(self
._make
_module
(include
), expr
.info
))
998 def _def_builtin_type(self
, name
, json_type
, c_type
):
999 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1000 # Instantiating only the arrays that are actually used would
1001 # be nice, but we can't as long as their generated code
1002 # (qapi-builtin-types.[ch]) may be shared by some other
1004 self
._make
_array
_type
(name
, None)
1006 def _def_predefineds(self
):
1007 for t
in [('str', 'string', 'char' + POINTER_SUFFIX
),
1008 ('number', 'number', 'double'),
1009 ('int', 'int', 'int64_t'),
1010 ('int8', 'int', 'int8_t'),
1011 ('int16', 'int', 'int16_t'),
1012 ('int32', 'int', 'int32_t'),
1013 ('int64', 'int', 'int64_t'),
1014 ('uint8', 'int', 'uint8_t'),
1015 ('uint16', 'int', 'uint16_t'),
1016 ('uint32', 'int', 'uint32_t'),
1017 ('uint64', 'int', 'uint64_t'),
1018 ('size', 'int', 'uint64_t'),
1019 ('bool', 'boolean', 'bool'),
1020 ('any', 'value', 'QObject' + POINTER_SUFFIX
),
1021 ('null', 'null', 'QNull' + POINTER_SUFFIX
)]:
1022 self
._def
_builtin
_type
(*t
)
1023 self
.the_empty_object_type
= QAPISchemaObjectType(
1024 'q_empty', None, None, None, None, None, [], None)
1025 self
._def
_entity
(self
.the_empty_object_type
)
1027 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1029 qtype_values
= self
._make
_enum
_members
(
1030 [{'name': n
} for n
in qtypes
], None)
1032 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None, None,
1033 qtype_values
, 'QTYPE'))
1035 def _make_features(self
, features
, info
):
1036 if features
is None:
1038 return [QAPISchemaFeature(f
['name'], info
,
1039 QAPISchemaIfCond(f
.get('if')))
1042 def _make_enum_member(self
, name
, ifcond
, features
, info
):
1043 return QAPISchemaEnumMember(name
, info
,
1044 QAPISchemaIfCond(ifcond
),
1045 self
._make
_features
(features
, info
))
1047 def _make_enum_members(self
, values
, info
):
1048 return [self
._make
_enum
_member
(v
['name'], v
.get('if'),
1049 v
.get('features'), info
)
1052 def _make_array_type(self
, element_type
, info
):
1053 name
= element_type
+ 'List' # reserved by check_defn_name_str()
1054 if not self
.lookup_type(name
):
1055 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1058 def _make_implicit_object_type(self
, name
, info
, ifcond
, role
, members
):
1061 # See also QAPISchemaObjectTypeMember.describe()
1062 name
= 'q_obj_%s-%s' % (name
, role
)
1063 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
1065 # The implicit object type has multiple users. This can
1066 # only be a duplicate definition, which will be flagged
1070 self
._def
_entity
(QAPISchemaObjectType(
1071 name
, info
, None, ifcond
, None, None, members
, None))
1074 def _def_enum_type(self
, expr
: QAPIExpression
):
1077 prefix
= expr
.get('prefix')
1078 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1080 features
= self
._make
_features
(expr
.get('features'), info
)
1081 self
._def
_entity
(QAPISchemaEnumType(
1082 name
, info
, expr
.doc
, ifcond
, features
,
1083 self
._make
_enum
_members
(data
, info
), prefix
))
1085 def _make_member(self
, name
, typ
, ifcond
, features
, info
):
1087 if name
.startswith('*'):
1090 if isinstance(typ
, list):
1091 assert len(typ
) == 1
1092 typ
= self
._make
_array
_type
(typ
[0], info
)
1093 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
,
1094 self
._make
_features
(features
, info
))
1096 def _make_members(self
, data
, info
):
1097 return [self
._make
_member
(key
, value
['type'],
1098 QAPISchemaIfCond(value
.get('if')),
1099 value
.get('features'), info
)
1100 for (key
, value
) in data
.items()]
1102 def _def_struct_type(self
, expr
: QAPIExpression
):
1103 name
= expr
['struct']
1104 base
= expr
.get('base')
1107 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1108 features
= self
._make
_features
(expr
.get('features'), info
)
1109 self
._def
_entity
(QAPISchemaObjectType(
1110 name
, info
, expr
.doc
, ifcond
, features
, base
,
1111 self
._make
_members
(data
, info
),
1114 def _make_variant(self
, case
, typ
, ifcond
, info
):
1115 if isinstance(typ
, list):
1116 assert len(typ
) == 1
1117 typ
= self
._make
_array
_type
(typ
[0], info
)
1118 return QAPISchemaVariant(case
, info
, typ
, ifcond
)
1120 def _def_union_type(self
, expr
: QAPIExpression
):
1121 name
= expr
['union']
1123 tag_name
= expr
['discriminator']
1125 assert isinstance(data
, dict)
1127 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1128 features
= self
._make
_features
(expr
.get('features'), info
)
1129 if isinstance(base
, dict):
1130 base
= self
._make
_implicit
_object
_type
(
1132 'base', self
._make
_members
(base
, info
))
1134 self
._make
_variant
(key
, value
['type'],
1135 QAPISchemaIfCond(value
.get('if')),
1137 for (key
, value
) in data
.items()]
1138 members
: List
[QAPISchemaObjectTypeMember
] = []
1140 QAPISchemaObjectType(name
, info
, expr
.doc
, ifcond
, features
,
1143 tag_name
, info
, None, variants
)))
1145 def _def_alternate_type(self
, expr
: QAPIExpression
):
1146 name
= expr
['alternate']
1148 assert isinstance(data
, dict)
1149 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1151 features
= self
._make
_features
(expr
.get('features'), info
)
1153 self
._make
_variant
(key
, value
['type'],
1154 QAPISchemaIfCond(value
.get('if')),
1156 for (key
, value
) in data
.items()]
1157 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
1159 QAPISchemaAlternateType(
1160 name
, info
, expr
.doc
, ifcond
, features
,
1161 QAPISchemaVariants(None, info
, tag_member
, variants
)))
1163 def _def_command(self
, expr
: QAPIExpression
):
1164 name
= expr
['command']
1165 data
= expr
.get('data')
1166 rets
= expr
.get('returns')
1167 gen
= expr
.get('gen', True)
1168 success_response
= expr
.get('success-response', True)
1169 boxed
= expr
.get('boxed', False)
1170 allow_oob
= expr
.get('allow-oob', False)
1171 allow_preconfig
= expr
.get('allow-preconfig', False)
1172 coroutine
= expr
.get('coroutine', False)
1173 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1175 features
= self
._make
_features
(expr
.get('features'), info
)
1176 if isinstance(data
, OrderedDict
):
1177 data
= self
._make
_implicit
_object
_type
(
1179 'arg', self
._make
_members
(data
, info
))
1180 if isinstance(rets
, list):
1181 assert len(rets
) == 1
1182 rets
= self
._make
_array
_type
(rets
[0], info
)
1183 self
._def
_entity
(QAPISchemaCommand(name
, info
, expr
.doc
, ifcond
,
1184 features
, data
, rets
,
1185 gen
, success_response
,
1186 boxed
, allow_oob
, allow_preconfig
,
1189 def _def_event(self
, expr
: QAPIExpression
):
1190 name
= expr
['event']
1191 data
= expr
.get('data')
1192 boxed
= expr
.get('boxed', False)
1193 ifcond
= QAPISchemaIfCond(expr
.get('if'))
1195 features
= self
._make
_features
(expr
.get('features'), info
)
1196 if isinstance(data
, OrderedDict
):
1197 data
= self
._make
_implicit
_object
_type
(
1199 'arg', self
._make
_members
(data
, info
))
1200 self
._def
_entity
(QAPISchemaEvent(name
, info
, expr
.doc
, ifcond
,
1201 features
, data
, boxed
))
1203 def _def_exprs(self
, exprs
):
1206 self
._def
_enum
_type
(expr
)
1207 elif 'struct' in expr
:
1208 self
._def
_struct
_type
(expr
)
1209 elif 'union' in expr
:
1210 self
._def
_union
_type
(expr
)
1211 elif 'alternate' in expr
:
1212 self
._def
_alternate
_type
(expr
)
1213 elif 'command' in expr
:
1214 self
._def
_command
(expr
)
1215 elif 'event' in expr
:
1216 self
._def
_event
(expr
)
1217 elif 'include' in expr
:
1218 self
._def
_include
(expr
)
1223 for ent
in self
._entity
_list
:
1227 for ent
in self
._entity
_list
:
1228 ent
.set_module(self
)
1230 def visit(self
, visitor
):
1231 visitor
.visit_begin(self
)
1232 for mod
in self
._module
_dict
.values():