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 Optional
22 from .common
import POINTER_SUFFIX
, c_name
23 from .error
import QAPIError
, QAPISemError
24 from .expr
import check_exprs
25 from .parser
import QAPISchemaParser
28 class QAPISchemaEntity
:
29 meta
: Optional
[str] = None
31 def __init__(self
, name
, info
, doc
, ifcond
=None, features
=None):
32 assert name
is None or isinstance(name
, str)
33 for f
in features
or []:
34 assert isinstance(f
, QAPISchemaFeature
)
35 f
.set_defined_in(name
)
38 # For explicitly defined entities, info points to the (explicit)
39 # definition. For builtins (and their arrays), info is None.
40 # For implicitly defined entities, info points to a place that
41 # triggered the implicit definition (there may be more than one
45 self
._ifcond
= ifcond
or []
46 self
.features
= features
or []
50 return c_name(self
.name
)
52 def check(self
, schema
):
53 assert not self
._checked
55 for f
in self
.features
:
56 f
.check_clash(self
.info
, seen
)
59 def connect_doc(self
, doc
=None):
62 for f
in self
.features
:
63 doc
.connect_feature(f
)
69 def _set_module(self
, schema
, info
):
71 fname
= info
.fname
if info
else './builtin'
72 self
._module
= schema
.module_by_fname(fname
)
73 self
._module
.add_entity(self
)
75 def set_module(self
, schema
):
76 self
._set
_module
(schema
, self
.info
)
83 def is_implicit(self
):
86 def visit(self
, visitor
):
91 return "%s '%s'" % (self
.meta
, self
.name
)
94 class QAPISchemaVisitor
:
95 def visit_begin(self
, schema
):
101 def visit_module(self
, name
):
104 def visit_needed(self
, entity
):
105 # Default to visiting everything
108 def visit_include(self
, name
, info
):
111 def visit_builtin_type(self
, name
, info
, json_type
):
114 def visit_enum_type(self
, name
, info
, ifcond
, features
, members
, prefix
):
117 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
120 def visit_object_type(self
, name
, info
, ifcond
, features
,
121 base
, members
, variants
):
124 def visit_object_type_flat(self
, name
, info
, ifcond
, features
,
128 def visit_alternate_type(self
, name
, info
, ifcond
, features
, variants
):
131 def visit_command(self
, name
, info
, ifcond
, features
,
132 arg_type
, ret_type
, gen
, success_response
, boxed
,
133 allow_oob
, allow_preconfig
, coroutine
):
136 def visit_event(self
, name
, info
, ifcond
, features
, arg_type
, boxed
):
140 class QAPISchemaModule
:
141 def __init__(self
, name
):
143 self
._entity
_list
= []
146 def is_system_module(name
: str) -> bool:
148 System modules are internally defined modules.
150 Their names start with the "./" prefix.
152 return name
.startswith('./')
155 def is_user_module(cls
, name
: str) -> bool:
157 User modules are those defined by the user in qapi JSON files.
159 They do not start with the "./" prefix.
161 return not cls
.is_system_module(name
)
164 def is_builtin_module(name
: str) -> bool:
166 The built-in module is a single System module for the built-in types.
168 It is always "./builtin".
170 return name
== './builtin'
172 def add_entity(self
, ent
):
173 self
._entity
_list
.append(ent
)
175 def visit(self
, visitor
):
176 visitor
.visit_module(self
.name
)
177 for entity
in self
._entity
_list
:
178 if visitor
.visit_needed(entity
):
179 entity
.visit(visitor
)
182 class QAPISchemaInclude(QAPISchemaEntity
):
183 def __init__(self
, sub_module
, info
):
184 super().__init
__(None, info
, None)
185 self
._sub
_module
= sub_module
187 def visit(self
, visitor
):
188 super().visit(visitor
)
189 visitor
.visit_include(self
._sub
_module
.name
, self
.info
)
192 class QAPISchemaType(QAPISchemaEntity
):
193 # Return the C type for common use.
194 # For the types we commonly box, this is a pointer type.
198 # Return the C type to be used in a parameter list.
199 def c_param_type(self
):
202 # Return the C type to be used where we suppress boxing.
203 def c_unboxed_type(self
):
209 def alternate_qtype(self
):
211 'null': 'QTYPE_QNULL',
212 'string': 'QTYPE_QSTRING',
213 'number': 'QTYPE_QNUM',
215 'boolean': 'QTYPE_QBOOL',
216 'object': 'QTYPE_QDICT'
218 return json2qtype
.get(self
.json_type())
221 if self
.is_implicit():
225 def check(self
, schema
):
226 QAPISchemaEntity
.check(self
, schema
)
227 if 'deprecated' in [f
.name
for f
in self
.features
]:
229 self
.info
, "feature 'deprecated' is not supported for types")
233 return "%s type '%s'" % (self
.meta
, self
.name
)
236 class QAPISchemaBuiltinType(QAPISchemaType
):
239 def __init__(self
, name
, json_type
, c_type
):
240 super().__init
__(name
, None, None)
241 assert not c_type
or isinstance(c_type
, str)
242 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
244 self
._json
_type
_name
= json_type
245 self
._c
_type
_name
= c_type
251 return self
._c
_type
_name
253 def c_param_type(self
):
254 if self
.name
== 'str':
255 return 'const ' + self
._c
_type
_name
256 return self
._c
_type
_name
259 return self
._json
_type
_name
262 return self
.json_type()
264 def visit(self
, visitor
):
265 super().visit(visitor
)
266 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
269 class QAPISchemaEnumType(QAPISchemaType
):
272 def __init__(self
, name
, info
, doc
, ifcond
, features
, members
, prefix
):
273 super().__init
__(name
, info
, doc
, ifcond
, features
)
275 assert isinstance(m
, QAPISchemaEnumMember
)
276 m
.set_defined_in(name
)
277 assert prefix
is None or isinstance(prefix
, str)
278 self
.members
= members
281 def check(self
, schema
):
282 super().check(schema
)
284 for m
in self
.members
:
285 m
.check_clash(self
.info
, seen
)
287 def connect_doc(self
, doc
=None):
288 super().connect_doc(doc
)
289 doc
= doc
or self
.doc
290 for m
in self
.members
:
293 def is_implicit(self
):
294 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
295 return self
.name
.endswith('Kind') or self
.name
== 'QType'
298 return c_name(self
.name
)
300 def member_names(self
):
301 return [m
.name
for m
in self
.members
]
306 def visit(self
, visitor
):
307 super().visit(visitor
)
308 visitor
.visit_enum_type(
309 self
.name
, self
.info
, self
.ifcond
, self
.features
,
310 self
.members
, self
.prefix
)
313 class QAPISchemaArrayType(QAPISchemaType
):
316 def __init__(self
, name
, info
, element_type
):
317 super().__init
__(name
, info
, None)
318 assert isinstance(element_type
, str)
319 self
._element
_type
_name
= element_type
320 self
.element_type
= None
322 def check(self
, schema
):
323 super().check(schema
)
324 self
.element_type
= schema
.resolve_type(
325 self
._element
_type
_name
, self
.info
,
326 self
.info
and self
.info
.defn_meta
)
327 assert not isinstance(self
.element_type
, QAPISchemaArrayType
)
329 def set_module(self
, schema
):
330 self
._set
_module
(schema
, self
.element_type
.info
)
335 return self
.element_type
.ifcond
337 def is_implicit(self
):
341 return c_name(self
.name
) + POINTER_SUFFIX
347 elt_doc_type
= self
.element_type
.doc_type()
350 return 'array of ' + elt_doc_type
352 def visit(self
, visitor
):
353 super().visit(visitor
)
354 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
359 return "%s type ['%s']" % (self
.meta
, self
._element
_type
_name
)
362 class QAPISchemaObjectType(QAPISchemaType
):
363 def __init__(self
, name
, info
, doc
, ifcond
, features
,
364 base
, local_members
, variants
):
365 # struct has local_members, optional base, and no variants
366 # flat union has base, variants, and no local_members
367 # simple union has local_members, variants, and no base
368 super().__init
__(name
, info
, doc
, ifcond
, features
)
369 self
.meta
= 'union' if variants
else 'struct'
370 assert base
is None or isinstance(base
, str)
371 for m
in local_members
:
372 assert isinstance(m
, QAPISchemaObjectTypeMember
)
373 m
.set_defined_in(name
)
374 if variants
is not None:
375 assert isinstance(variants
, QAPISchemaVariants
)
376 variants
.set_defined_in(name
)
377 self
._base
_name
= base
379 self
.local_members
= local_members
380 self
.variants
= variants
383 def check(self
, schema
):
384 # This calls another type T's .check() exactly when the C
385 # struct emitted by gen_object() contains that T's C struct
386 # (pointers don't count).
387 if self
.members
is not None:
388 # A previous .check() completed: nothing to do
391 # Recursed: C struct contains itself
392 raise QAPISemError(self
.info
,
393 "object %s contains itself" % self
.name
)
395 super().check(schema
)
396 assert self
._checked
and self
.members
is None
400 self
.base
= schema
.resolve_type(self
._base
_name
, self
.info
,
402 if (not isinstance(self
.base
, QAPISchemaObjectType
)
403 or self
.base
.variants
):
406 "'base' requires a struct type, %s isn't"
407 % self
.base
.describe())
408 self
.base
.check(schema
)
409 self
.base
.check_clash(self
.info
, seen
)
410 for m
in self
.local_members
:
412 m
.check_clash(self
.info
, seen
)
413 members
= seen
.values()
416 self
.variants
.check(schema
, seen
)
417 self
.variants
.check_clash(self
.info
, seen
)
419 self
.members
= members
# mark completed
421 # Check that the members of this type do not cause duplicate JSON members,
422 # and update seen to track the members seen so far. Report any errors
423 # on behalf of info, which is not necessarily self.info
424 def check_clash(self
, info
, seen
):
426 assert not self
.variants
# not implemented
427 for m
in self
.members
:
428 m
.check_clash(info
, seen
)
430 def connect_doc(self
, doc
=None):
431 super().connect_doc(doc
)
432 doc
= doc
or self
.doc
433 if self
.base
and self
.base
.is_implicit():
434 self
.base
.connect_doc(doc
)
435 for m
in self
.local_members
:
441 if isinstance(self
._ifcond
, QAPISchemaType
):
442 # Simple union wrapper type inherits from wrapped type;
443 # see _make_implicit_object_type()
444 return self
._ifcond
.ifcond
447 def is_implicit(self
):
448 # See QAPISchema._make_implicit_object_type(), as well as
450 return self
.name
.startswith('q_')
453 assert self
.members
is not None
454 return not self
.members
and not self
.variants
457 assert self
.name
!= 'q_empty'
458 return super().c_name()
461 assert not self
.is_implicit()
462 return c_name(self
.name
) + POINTER_SUFFIX
464 def c_unboxed_type(self
):
465 return c_name(self
.name
)
470 def visit(self
, visitor
):
471 super().visit(visitor
)
472 visitor
.visit_object_type(
473 self
.name
, self
.info
, self
.ifcond
, self
.features
,
474 self
.base
, self
.local_members
, self
.variants
)
475 visitor
.visit_object_type_flat(
476 self
.name
, self
.info
, self
.ifcond
, self
.features
,
477 self
.members
, self
.variants
)
480 class QAPISchemaAlternateType(QAPISchemaType
):
483 def __init__(self
, name
, info
, doc
, ifcond
, features
, variants
):
484 super().__init
__(name
, info
, doc
, ifcond
, features
)
485 assert isinstance(variants
, QAPISchemaVariants
)
486 assert variants
.tag_member
487 variants
.set_defined_in(name
)
488 variants
.tag_member
.set_defined_in(self
.name
)
489 self
.variants
= variants
491 def check(self
, schema
):
492 super().check(schema
)
493 self
.variants
.tag_member
.check(schema
)
494 # Not calling self.variants.check_clash(), because there's nothing
496 self
.variants
.check(schema
, {})
497 # Alternate branch names have no relation to the tag enum values;
498 # so we have to check for potential name collisions ourselves.
501 for v
in self
.variants
.variants
:
502 v
.check_clash(self
.info
, seen
)
503 qtype
= v
.type.alternate_qtype()
508 % (v
.describe(self
.info
), v
.type.describe()))
509 conflicting
= set([qtype
])
510 if qtype
== 'QTYPE_QSTRING':
511 if isinstance(v
.type, QAPISchemaEnumType
):
512 for m
in v
.type.members
:
513 if m
.name
in ['on', 'off']:
514 conflicting
.add('QTYPE_QBOOL')
515 if re
.match(r
'[-+0-9.]', m
.name
):
516 # lazy, could be tightened
517 conflicting
.add('QTYPE_QNUM')
519 conflicting
.add('QTYPE_QNUM')
520 conflicting
.add('QTYPE_QBOOL')
521 for qt
in conflicting
:
525 "%s can't be distinguished from '%s'"
526 % (v
.describe(self
.info
), types_seen
[qt
]))
527 types_seen
[qt
] = v
.name
529 def connect_doc(self
, doc
=None):
530 super().connect_doc(doc
)
531 doc
= doc
or self
.doc
532 for v
in self
.variants
.variants
:
536 return c_name(self
.name
) + POINTER_SUFFIX
541 def visit(self
, visitor
):
542 super().visit(visitor
)
543 visitor
.visit_alternate_type(
544 self
.name
, self
.info
, self
.ifcond
, self
.features
, self
.variants
)
547 class QAPISchemaVariants
:
548 def __init__(self
, tag_name
, info
, tag_member
, variants
):
549 # Flat unions pass tag_name but not tag_member.
550 # Simple unions and alternates pass tag_member but not tag_name.
551 # After check(), tag_member is always set, and tag_name remains
552 # a reliable witness of being used by a flat union.
553 assert bool(tag_member
) != bool(tag_name
)
554 assert (isinstance(tag_name
, str) or
555 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
557 assert isinstance(v
, QAPISchemaVariant
)
558 self
._tag
_name
= tag_name
560 self
.tag_member
= tag_member
561 self
.variants
= variants
563 def set_defined_in(self
, name
):
564 for v
in self
.variants
:
565 v
.set_defined_in(name
)
567 def check(self
, schema
, seen
):
568 if not self
.tag_member
: # flat union
569 self
.tag_member
= seen
.get(c_name(self
._tag
_name
))
571 # Pointing to the base type when not implicit would be
572 # nice, but we don't know it here
573 if not self
.tag_member
or self
._tag
_name
!= self
.tag_member
.name
:
576 "discriminator '%s' is not a member of %s"
577 % (self
._tag
_name
, base
))
579 base_type
= schema
.lookup_type(self
.tag_member
.defined_in
)
581 if not base_type
.is_implicit():
582 base
= "base type '%s'" % self
.tag_member
.defined_in
583 if not isinstance(self
.tag_member
.type, QAPISchemaEnumType
):
586 "discriminator member '%s' of %s must be of enum type"
587 % (self
._tag
_name
, base
))
588 if self
.tag_member
.optional
:
591 "discriminator member '%s' of %s must not be optional"
592 % (self
._tag
_name
, base
))
593 if self
.tag_member
.ifcond
:
596 "discriminator member '%s' of %s must not be conditional"
597 % (self
._tag
_name
, base
))
599 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
600 assert not self
.tag_member
.optional
601 assert self
.tag_member
.ifcond
== []
602 if self
._tag
_name
: # flat union
603 # branches that are not explicitly covered get an empty type
604 cases
= {v
.name
for v
in self
.variants
}
605 for m
in self
.tag_member
.type.members
:
606 if m
.name
not in cases
:
607 v
= QAPISchemaVariant(m
.name
, self
.info
,
609 v
.set_defined_in(self
.tag_member
.defined_in
)
610 self
.variants
.append(v
)
611 if not self
.variants
:
612 raise QAPISemError(self
.info
, "union has no branches")
613 for v
in self
.variants
:
615 # Union names must match enum values; alternate names are
616 # checked separately. Use 'seen' to tell the two apart.
618 if v
.name
not in self
.tag_member
.type.member_names():
621 "branch '%s' is not a value of %s"
622 % (v
.name
, self
.tag_member
.type.describe()))
623 if (not isinstance(v
.type, QAPISchemaObjectType
)
628 % (v
.describe(self
.info
), v
.type.describe()))
631 def check_clash(self
, info
, seen
):
632 for v
in self
.variants
:
633 # Reset seen map for each variant, since qapi names from one
634 # branch do not affect another branch
635 v
.type.check_clash(info
, dict(seen
))
638 class QAPISchemaMember
:
639 """ Represents object members, enum members and features """
642 def __init__(self
, name
, info
, ifcond
=None):
643 assert isinstance(name
, str)
646 self
.ifcond
= ifcond
or []
647 self
.defined_in
= None
649 def set_defined_in(self
, name
):
650 assert not self
.defined_in
651 self
.defined_in
= name
653 def check_clash(self
, info
, seen
):
654 cname
= c_name(self
.name
)
658 "%s collides with %s"
659 % (self
.describe(info
), seen
[cname
].describe(info
)))
662 def connect_doc(self
, doc
):
664 doc
.connect_member(self
)
666 def describe(self
, info
):
668 defined_in
= self
.defined_in
671 if defined_in
.startswith('q_obj_'):
672 # See QAPISchema._make_implicit_object_type() - reverse the
673 # mapping there to create a nice human-readable description
674 defined_in
= defined_in
[6:]
675 if defined_in
.endswith('-arg'):
676 # Implicit type created for a command's dict 'data'
677 assert role
== 'member'
679 elif defined_in
.endswith('-base'):
680 # Implicit type created for a flat union's dict 'base'
681 role
= 'base ' + role
683 # Implicit type created for a simple union's branch
684 assert defined_in
.endswith('-wrapper')
685 # Unreachable and not implemented
687 elif defined_in
.endswith('Kind'):
688 # See QAPISchema._make_implicit_enum_type()
689 # Implicit enum created for simple union's branches
690 assert role
== 'value'
692 elif defined_in
!= info
.defn_name
:
693 return "%s '%s' of type '%s'" % (role
, self
.name
, defined_in
)
694 return "%s '%s'" % (role
, self
.name
)
697 class QAPISchemaEnumMember(QAPISchemaMember
):
701 class QAPISchemaFeature(QAPISchemaMember
):
705 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
706 def __init__(self
, name
, info
, typ
, optional
, ifcond
=None, features
=None):
707 super().__init
__(name
, info
, ifcond
)
708 assert isinstance(typ
, str)
709 assert isinstance(optional
, bool)
710 for f
in features
or []:
711 assert isinstance(f
, QAPISchemaFeature
)
712 f
.set_defined_in(name
)
713 self
._type
_name
= typ
715 self
.optional
= optional
716 self
.features
= features
or []
718 def check(self
, schema
):
719 assert self
.defined_in
720 self
.type = schema
.resolve_type(self
._type
_name
, self
.info
,
723 for f
in self
.features
:
724 f
.check_clash(self
.info
, seen
)
726 def connect_doc(self
, doc
):
727 super().connect_doc(doc
)
729 for f
in self
.features
:
730 doc
.connect_feature(f
)
733 class QAPISchemaVariant(QAPISchemaObjectTypeMember
):
736 def __init__(self
, name
, info
, typ
, ifcond
=None):
737 super().__init
__(name
, info
, typ
, False, ifcond
)
740 class QAPISchemaCommand(QAPISchemaEntity
):
743 def __init__(self
, name
, info
, doc
, ifcond
, features
,
745 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
,
747 super().__init
__(name
, info
, doc
, ifcond
, features
)
748 assert not arg_type
or isinstance(arg_type
, str)
749 assert not ret_type
or isinstance(ret_type
, str)
750 self
._arg
_type
_name
= arg_type
752 self
._ret
_type
_name
= ret_type
755 self
.success_response
= success_response
757 self
.allow_oob
= allow_oob
758 self
.allow_preconfig
= allow_preconfig
759 self
.coroutine
= coroutine
761 def check(self
, schema
):
762 super().check(schema
)
763 if self
._arg
_type
_name
:
764 self
.arg_type
= schema
.resolve_type(
765 self
._arg
_type
_name
, self
.info
, "command's 'data'")
766 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
769 "command's 'data' cannot take %s"
770 % self
.arg_type
.describe())
771 if self
.arg_type
.variants
and not self
.boxed
:
774 "command's 'data' can take %s only with 'boxed': true"
775 % self
.arg_type
.describe())
776 if self
._ret
_type
_name
:
777 self
.ret_type
= schema
.resolve_type(
778 self
._ret
_type
_name
, self
.info
, "command's 'returns'")
779 if self
.name
not in self
.info
.pragma
.returns_whitelist
:
781 if isinstance(typ
, QAPISchemaArrayType
):
782 typ
= self
.ret_type
.element_type
784 if not isinstance(typ
, QAPISchemaObjectType
):
787 "command's 'returns' cannot take %s"
788 % self
.ret_type
.describe())
790 def connect_doc(self
, doc
=None):
791 super().connect_doc(doc
)
792 doc
= doc
or self
.doc
794 if self
.arg_type
and self
.arg_type
.is_implicit():
795 self
.arg_type
.connect_doc(doc
)
797 def visit(self
, visitor
):
798 super().visit(visitor
)
799 visitor
.visit_command(
800 self
.name
, self
.info
, self
.ifcond
, self
.features
,
801 self
.arg_type
, self
.ret_type
, self
.gen
, self
.success_response
,
802 self
.boxed
, self
.allow_oob
, self
.allow_preconfig
,
806 class QAPISchemaEvent(QAPISchemaEntity
):
809 def __init__(self
, name
, info
, doc
, ifcond
, features
, arg_type
, boxed
):
810 super().__init
__(name
, info
, doc
, ifcond
, features
)
811 assert not arg_type
or isinstance(arg_type
, str)
812 self
._arg
_type
_name
= arg_type
816 def check(self
, schema
):
817 super().check(schema
)
818 if self
._arg
_type
_name
:
819 self
.arg_type
= schema
.resolve_type(
820 self
._arg
_type
_name
, self
.info
, "event's 'data'")
821 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
824 "event's 'data' cannot take %s"
825 % self
.arg_type
.describe())
826 if self
.arg_type
.variants
and not self
.boxed
:
829 "event's 'data' can take %s only with 'boxed': true"
830 % self
.arg_type
.describe())
832 def connect_doc(self
, doc
=None):
833 super().connect_doc(doc
)
834 doc
= doc
or self
.doc
836 if self
.arg_type
and self
.arg_type
.is_implicit():
837 self
.arg_type
.connect_doc(doc
)
839 def visit(self
, visitor
):
840 super().visit(visitor
)
842 self
.name
, self
.info
, self
.ifcond
, self
.features
,
843 self
.arg_type
, self
.boxed
)
847 def __init__(self
, fname
):
849 parser
= QAPISchemaParser(fname
)
850 exprs
= check_exprs(parser
.exprs
)
851 self
.docs
= parser
.docs
852 self
._entity
_list
= []
853 self
._entity
_dict
= {}
854 self
._module
_dict
= OrderedDict()
855 self
._schema
_dir
= os
.path
.dirname(fname
)
856 self
._make
_module
('./builtin')
857 self
._make
_module
(fname
)
858 self
._predefining
= True
859 self
._def
_predefineds
()
860 self
._predefining
= False
861 self
._def
_exprs
(exprs
)
864 def _def_entity(self
, ent
):
865 # Only the predefined types are allowed to not have info
866 assert ent
.info
or self
._predefining
867 self
._entity
_list
.append(ent
)
870 # TODO reject names that differ only in '_' vs. '.' vs. '-',
871 # because they're liable to clash in generated C.
872 other_ent
= self
._entity
_dict
.get(ent
.name
)
875 where
= QAPIError(other_ent
.info
, None, "previous definition")
878 "'%s' is already defined\n%s" % (ent
.name
, where
))
880 ent
.info
, "%s is already defined" % other_ent
.describe())
881 self
._entity
_dict
[ent
.name
] = ent
883 def lookup_entity(self
, name
, typ
=None):
884 ent
= self
._entity
_dict
.get(name
)
885 if typ
and not isinstance(ent
, typ
):
889 def lookup_type(self
, name
):
890 return self
.lookup_entity(name
, QAPISchemaType
)
892 def resolve_type(self
, name
, info
, what
):
893 typ
= self
.lookup_type(name
)
898 info
, "%s uses unknown type '%s'" % (what
, name
))
901 def _module_name(self
, fname
: str) -> str:
902 if QAPISchemaModule
.is_system_module(fname
):
904 return os
.path
.relpath(fname
, self
._schema
_dir
)
906 def _make_module(self
, fname
):
907 name
= self
._module
_name
(fname
)
908 if name
not in self
._module
_dict
:
909 self
._module
_dict
[name
] = QAPISchemaModule(name
)
910 return self
._module
_dict
[name
]
912 def module_by_fname(self
, fname
):
913 name
= self
._module
_name
(fname
)
914 return self
._module
_dict
[name
]
916 def _def_include(self
, expr
, info
, doc
):
917 include
= expr
['include']
919 self
._def
_entity
(QAPISchemaInclude(self
._make
_module
(include
), info
))
921 def _def_builtin_type(self
, name
, json_type
, c_type
):
922 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
923 # Instantiating only the arrays that are actually used would
924 # be nice, but we can't as long as their generated code
925 # (qapi-builtin-types.[ch]) may be shared by some other
927 self
._make
_array
_type
(name
, None)
929 def _def_predefineds(self
):
930 for t
in [('str', 'string', 'char' + POINTER_SUFFIX
),
931 ('number', 'number', 'double'),
932 ('int', 'int', 'int64_t'),
933 ('int8', 'int', 'int8_t'),
934 ('int16', 'int', 'int16_t'),
935 ('int32', 'int', 'int32_t'),
936 ('int64', 'int', 'int64_t'),
937 ('uint8', 'int', 'uint8_t'),
938 ('uint16', 'int', 'uint16_t'),
939 ('uint32', 'int', 'uint32_t'),
940 ('uint64', 'int', 'uint64_t'),
941 ('size', 'int', 'uint64_t'),
942 ('bool', 'boolean', 'bool'),
943 ('any', 'value', 'QObject' + POINTER_SUFFIX
),
944 ('null', 'null', 'QNull' + POINTER_SUFFIX
)]:
945 self
._def
_builtin
_type
(*t
)
946 self
.the_empty_object_type
= QAPISchemaObjectType(
947 'q_empty', None, None, None, None, None, [], None)
948 self
._def
_entity
(self
.the_empty_object_type
)
950 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
952 qtype_values
= self
._make
_enum
_members
(
953 [{'name': n
} for n
in qtypes
], None)
955 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None, None,
956 qtype_values
, 'QTYPE'))
958 def _make_features(self
, features
, info
):
961 return [QAPISchemaFeature(f
['name'], info
, f
.get('if'))
964 def _make_enum_members(self
, values
, info
):
965 return [QAPISchemaEnumMember(v
['name'], info
, v
.get('if'))
968 def _make_implicit_enum_type(self
, name
, info
, ifcond
, values
):
969 # See also QAPISchemaObjectTypeMember.describe()
970 name
= name
+ 'Kind' # reserved by check_defn_name_str()
971 self
._def
_entity
(QAPISchemaEnumType(
972 name
, info
, None, ifcond
, None,
973 self
._make
_enum
_members
(values
, info
),
977 def _make_array_type(self
, element_type
, info
):
978 name
= element_type
+ 'List' # reserved by check_defn_name_str()
979 if not self
.lookup_type(name
):
980 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
983 def _make_implicit_object_type(self
, name
, info
, ifcond
, role
, members
):
986 # See also QAPISchemaObjectTypeMember.describe()
987 name
= 'q_obj_%s-%s' % (name
, role
)
988 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
990 # The implicit object type has multiple users. This can
991 # happen only for simple unions' implicit wrapper types.
992 # Its ifcond should be the disjunction of its user's
993 # ifconds. Not implemented. Instead, we always pass the
994 # wrapped type's ifcond, which is trivially the same for all
995 # users. It's also necessary for the wrapper to compile.
996 # But it's not tight: the disjunction need not imply it. We
997 # may end up compiling useless wrapper types.
998 # TODO kill simple unions or implement the disjunction
1000 # pylint: disable=protected-access
1001 assert (ifcond
or []) == typ
._ifcond
1003 self
._def
_entity
(QAPISchemaObjectType(
1004 name
, info
, None, ifcond
, None, None, members
, None))
1007 def _def_enum_type(self
, expr
, info
, doc
):
1010 prefix
= expr
.get('prefix')
1011 ifcond
= expr
.get('if')
1012 features
= self
._make
_features
(expr
.get('features'), info
)
1013 self
._def
_entity
(QAPISchemaEnumType(
1014 name
, info
, doc
, ifcond
, features
,
1015 self
._make
_enum
_members
(data
, info
), prefix
))
1017 def _make_member(self
, name
, typ
, ifcond
, features
, info
):
1019 if name
.startswith('*'):
1022 if isinstance(typ
, list):
1023 assert len(typ
) == 1
1024 typ
= self
._make
_array
_type
(typ
[0], info
)
1025 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
,
1026 self
._make
_features
(features
, info
))
1028 def _make_members(self
, data
, info
):
1029 return [self
._make
_member
(key
, value
['type'], value
.get('if'),
1030 value
.get('features'), info
)
1031 for (key
, value
) in data
.items()]
1033 def _def_struct_type(self
, expr
, info
, doc
):
1034 name
= expr
['struct']
1035 base
= expr
.get('base')
1037 ifcond
= expr
.get('if')
1038 features
= self
._make
_features
(expr
.get('features'), info
)
1039 self
._def
_entity
(QAPISchemaObjectType(
1040 name
, info
, doc
, ifcond
, features
, base
,
1041 self
._make
_members
(data
, info
),
1044 def _make_variant(self
, case
, typ
, ifcond
, info
):
1045 return QAPISchemaVariant(case
, info
, typ
, ifcond
)
1047 def _make_simple_variant(self
, case
, typ
, ifcond
, info
):
1048 if isinstance(typ
, list):
1049 assert len(typ
) == 1
1050 typ
= self
._make
_array
_type
(typ
[0], info
)
1051 typ
= self
._make
_implicit
_object
_type
(
1052 typ
, info
, self
.lookup_type(typ
),
1053 'wrapper', [self
._make
_member
('data', typ
, None, None, info
)])
1054 return QAPISchemaVariant(case
, info
, typ
, ifcond
)
1056 def _def_union_type(self
, expr
, info
, doc
):
1057 name
= expr
['union']
1059 base
= expr
.get('base')
1060 ifcond
= expr
.get('if')
1061 features
= self
._make
_features
(expr
.get('features'), info
)
1062 tag_name
= expr
.get('discriminator')
1064 if isinstance(base
, dict):
1065 base
= self
._make
_implicit
_object
_type
(
1067 'base', self
._make
_members
(base
, info
))
1069 variants
= [self
._make
_variant
(key
, value
['type'],
1070 value
.get('if'), info
)
1071 for (key
, value
) in data
.items()]
1074 variants
= [self
._make
_simple
_variant
(key
, value
['type'],
1075 value
.get('if'), info
)
1076 for (key
, value
) in data
.items()]
1077 enum
= [{'name': v
.name
, 'if': v
.ifcond
} for v
in variants
]
1078 typ
= self
._make
_implicit
_enum
_type
(name
, info
, ifcond
, enum
)
1079 tag_member
= QAPISchemaObjectTypeMember('type', info
, typ
, False)
1080 members
= [tag_member
]
1082 QAPISchemaObjectType(name
, info
, doc
, ifcond
, features
,
1085 tag_name
, info
, tag_member
, variants
)))
1087 def _def_alternate_type(self
, expr
, info
, doc
):
1088 name
= expr
['alternate']
1090 ifcond
= expr
.get('if')
1091 features
= self
._make
_features
(expr
.get('features'), info
)
1092 variants
= [self
._make
_variant
(key
, value
['type'], value
.get('if'),
1094 for (key
, value
) in data
.items()]
1095 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
1097 QAPISchemaAlternateType(name
, info
, doc
, ifcond
, features
,
1099 None, info
, tag_member
, variants
)))
1101 def _def_command(self
, expr
, info
, doc
):
1102 name
= expr
['command']
1103 data
= expr
.get('data')
1104 rets
= expr
.get('returns')
1105 gen
= expr
.get('gen', True)
1106 success_response
= expr
.get('success-response', True)
1107 boxed
= expr
.get('boxed', False)
1108 allow_oob
= expr
.get('allow-oob', False)
1109 allow_preconfig
= expr
.get('allow-preconfig', False)
1110 coroutine
= expr
.get('coroutine', False)
1111 ifcond
= expr
.get('if')
1112 features
= self
._make
_features
(expr
.get('features'), info
)
1113 if isinstance(data
, OrderedDict
):
1114 data
= self
._make
_implicit
_object
_type
(
1116 'arg', self
._make
_members
(data
, info
))
1117 if isinstance(rets
, list):
1118 assert len(rets
) == 1
1119 rets
= self
._make
_array
_type
(rets
[0], info
)
1120 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, ifcond
, features
,
1122 gen
, success_response
,
1123 boxed
, allow_oob
, allow_preconfig
,
1126 def _def_event(self
, expr
, info
, doc
):
1127 name
= expr
['event']
1128 data
= expr
.get('data')
1129 boxed
= expr
.get('boxed', False)
1130 ifcond
= expr
.get('if')
1131 features
= self
._make
_features
(expr
.get('features'), info
)
1132 if isinstance(data
, OrderedDict
):
1133 data
= self
._make
_implicit
_object
_type
(
1135 'arg', self
._make
_members
(data
, info
))
1136 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, ifcond
, features
,
1139 def _def_exprs(self
, exprs
):
1140 for expr_elem
in exprs
:
1141 expr
= expr_elem
['expr']
1142 info
= expr_elem
['info']
1143 doc
= expr_elem
.get('doc')
1145 self
._def
_enum
_type
(expr
, info
, doc
)
1146 elif 'struct' in expr
:
1147 self
._def
_struct
_type
(expr
, info
, doc
)
1148 elif 'union' in expr
:
1149 self
._def
_union
_type
(expr
, info
, doc
)
1150 elif 'alternate' in expr
:
1151 self
._def
_alternate
_type
(expr
, info
, doc
)
1152 elif 'command' in expr
:
1153 self
._def
_command
(expr
, info
, doc
)
1154 elif 'event' in expr
:
1155 self
._def
_event
(expr
, info
, doc
)
1156 elif 'include' in expr
:
1157 self
._def
_include
(expr
, info
, doc
)
1162 for ent
in self
._entity
_list
:
1166 for ent
in self
._entity
_list
:
1167 ent
.set_module(self
)
1169 def visit(self
, visitor
):
1170 visitor
.visit_begin(self
)
1171 for mod
in self
._module
_dict
.values():