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 QAPISemError
, QAPISourceError
24 from .expr
import check_exprs
25 from .parser
import QAPISchemaParser
28 class QAPISchemaEntity
:
29 meta
: Optional
[str] = None
31 def __init__(self
, name
: str, 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 QAPISchemaModule
.BUILTIN_MODULE_NAME
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
:
142 BUILTIN_MODULE_NAME
= './builtin'
144 def __init__(self
, name
):
146 self
._entity
_list
= []
149 def is_system_module(name
: str) -> bool:
151 System modules are internally defined modules.
153 Their names start with the "./" prefix.
155 return name
.startswith('./')
158 def is_user_module(cls
, name
: str) -> bool:
160 User modules are those defined by the user in qapi JSON files.
162 They do not start with the "./" prefix.
164 return not cls
.is_system_module(name
)
167 def is_builtin_module(cls
, name
: str) -> bool:
169 The built-in module is a single System module for the built-in types.
171 It is always "./builtin".
173 return name
== cls
.BUILTIN_MODULE_NAME
175 def add_entity(self
, ent
):
176 self
._entity
_list
.append(ent
)
178 def visit(self
, visitor
):
179 visitor
.visit_module(self
.name
)
180 for entity
in self
._entity
_list
:
181 if visitor
.visit_needed(entity
):
182 entity
.visit(visitor
)
185 class QAPISchemaInclude(QAPISchemaEntity
):
186 def __init__(self
, sub_module
, info
):
187 super().__init
__(None, info
, None)
188 self
._sub
_module
= sub_module
190 def visit(self
, visitor
):
191 super().visit(visitor
)
192 visitor
.visit_include(self
._sub
_module
.name
, self
.info
)
195 class QAPISchemaType(QAPISchemaEntity
):
196 # Return the C type for common use.
197 # For the types we commonly box, this is a pointer type.
201 # Return the C type to be used in a parameter list.
202 def c_param_type(self
):
205 # Return the C type to be used where we suppress boxing.
206 def c_unboxed_type(self
):
212 def alternate_qtype(self
):
214 'null': 'QTYPE_QNULL',
215 'string': 'QTYPE_QSTRING',
216 'number': 'QTYPE_QNUM',
218 'boolean': 'QTYPE_QBOOL',
219 'object': 'QTYPE_QDICT'
221 return json2qtype
.get(self
.json_type())
224 if self
.is_implicit():
228 def check(self
, schema
):
229 QAPISchemaEntity
.check(self
, schema
)
230 if 'deprecated' in [f
.name
for f
in self
.features
]:
232 self
.info
, "feature 'deprecated' is not supported for types")
236 return "%s type '%s'" % (self
.meta
, self
.name
)
239 class QAPISchemaBuiltinType(QAPISchemaType
):
242 def __init__(self
, name
, json_type
, c_type
):
243 super().__init
__(name
, None, None)
244 assert not c_type
or isinstance(c_type
, str)
245 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
247 self
._json
_type
_name
= json_type
248 self
._c
_type
_name
= c_type
254 return self
._c
_type
_name
256 def c_param_type(self
):
257 if self
.name
== 'str':
258 return 'const ' + self
._c
_type
_name
259 return self
._c
_type
_name
262 return self
._json
_type
_name
265 return self
.json_type()
267 def visit(self
, visitor
):
268 super().visit(visitor
)
269 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
272 class QAPISchemaEnumType(QAPISchemaType
):
275 def __init__(self
, name
, info
, doc
, ifcond
, features
, members
, prefix
):
276 super().__init
__(name
, info
, doc
, ifcond
, features
)
278 assert isinstance(m
, QAPISchemaEnumMember
)
279 m
.set_defined_in(name
)
280 assert prefix
is None or isinstance(prefix
, str)
281 self
.members
= members
284 def check(self
, schema
):
285 super().check(schema
)
287 for m
in self
.members
:
288 m
.check_clash(self
.info
, seen
)
290 def connect_doc(self
, doc
=None):
291 super().connect_doc(doc
)
292 doc
= doc
or self
.doc
293 for m
in self
.members
:
296 def is_implicit(self
):
297 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
298 return self
.name
.endswith('Kind') or self
.name
== 'QType'
301 return c_name(self
.name
)
303 def member_names(self
):
304 return [m
.name
for m
in self
.members
]
309 def visit(self
, visitor
):
310 super().visit(visitor
)
311 visitor
.visit_enum_type(
312 self
.name
, self
.info
, self
.ifcond
, self
.features
,
313 self
.members
, self
.prefix
)
316 class QAPISchemaArrayType(QAPISchemaType
):
319 def __init__(self
, name
, info
, element_type
):
320 super().__init
__(name
, info
, None)
321 assert isinstance(element_type
, str)
322 self
._element
_type
_name
= element_type
323 self
.element_type
= None
325 def check(self
, schema
):
326 super().check(schema
)
327 self
.element_type
= schema
.resolve_type(
328 self
._element
_type
_name
, self
.info
,
329 self
.info
and self
.info
.defn_meta
)
330 assert not isinstance(self
.element_type
, QAPISchemaArrayType
)
332 def set_module(self
, schema
):
333 self
._set
_module
(schema
, self
.element_type
.info
)
338 return self
.element_type
.ifcond
340 def is_implicit(self
):
344 return c_name(self
.name
) + POINTER_SUFFIX
350 elt_doc_type
= self
.element_type
.doc_type()
353 return 'array of ' + elt_doc_type
355 def visit(self
, visitor
):
356 super().visit(visitor
)
357 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
362 return "%s type ['%s']" % (self
.meta
, self
._element
_type
_name
)
365 class QAPISchemaObjectType(QAPISchemaType
):
366 def __init__(self
, name
, info
, doc
, ifcond
, features
,
367 base
, local_members
, variants
):
368 # struct has local_members, optional base, and no variants
369 # flat union has base, variants, and no local_members
370 # simple union has local_members, variants, and no base
371 super().__init
__(name
, info
, doc
, ifcond
, features
)
372 self
.meta
= 'union' if variants
else 'struct'
373 assert base
is None or isinstance(base
, str)
374 for m
in local_members
:
375 assert isinstance(m
, QAPISchemaObjectTypeMember
)
376 m
.set_defined_in(name
)
377 if variants
is not None:
378 assert isinstance(variants
, QAPISchemaVariants
)
379 variants
.set_defined_in(name
)
380 self
._base
_name
= base
382 self
.local_members
= local_members
383 self
.variants
= variants
386 def check(self
, schema
):
387 # This calls another type T's .check() exactly when the C
388 # struct emitted by gen_object() contains that T's C struct
389 # (pointers don't count).
390 if self
.members
is not None:
391 # A previous .check() completed: nothing to do
394 # Recursed: C struct contains itself
395 raise QAPISemError(self
.info
,
396 "object %s contains itself" % self
.name
)
398 super().check(schema
)
399 assert self
._checked
and self
.members
is None
403 self
.base
= schema
.resolve_type(self
._base
_name
, self
.info
,
405 if (not isinstance(self
.base
, QAPISchemaObjectType
)
406 or self
.base
.variants
):
409 "'base' requires a struct type, %s isn't"
410 % self
.base
.describe())
411 self
.base
.check(schema
)
412 self
.base
.check_clash(self
.info
, seen
)
413 for m
in self
.local_members
:
415 m
.check_clash(self
.info
, seen
)
416 members
= seen
.values()
419 self
.variants
.check(schema
, seen
)
420 self
.variants
.check_clash(self
.info
, seen
)
422 self
.members
= members
# mark completed
424 # Check that the members of this type do not cause duplicate JSON members,
425 # and update seen to track the members seen so far. Report any errors
426 # on behalf of info, which is not necessarily self.info
427 def check_clash(self
, info
, seen
):
429 assert not self
.variants
# not implemented
430 for m
in self
.members
:
431 m
.check_clash(info
, seen
)
433 def connect_doc(self
, doc
=None):
434 super().connect_doc(doc
)
435 doc
= doc
or self
.doc
436 if self
.base
and self
.base
.is_implicit():
437 self
.base
.connect_doc(doc
)
438 for m
in self
.local_members
:
444 if isinstance(self
._ifcond
, QAPISchemaType
):
445 # Simple union wrapper type inherits from wrapped type;
446 # see _make_implicit_object_type()
447 return self
._ifcond
.ifcond
450 def is_implicit(self
):
451 # See QAPISchema._make_implicit_object_type(), as well as
453 return self
.name
.startswith('q_')
456 assert self
.members
is not None
457 return not self
.members
and not self
.variants
460 assert self
.name
!= 'q_empty'
461 return super().c_name()
464 assert not self
.is_implicit()
465 return c_name(self
.name
) + POINTER_SUFFIX
467 def c_unboxed_type(self
):
468 return c_name(self
.name
)
473 def visit(self
, visitor
):
474 super().visit(visitor
)
475 visitor
.visit_object_type(
476 self
.name
, self
.info
, self
.ifcond
, self
.features
,
477 self
.base
, self
.local_members
, self
.variants
)
478 visitor
.visit_object_type_flat(
479 self
.name
, self
.info
, self
.ifcond
, self
.features
,
480 self
.members
, self
.variants
)
483 class QAPISchemaAlternateType(QAPISchemaType
):
486 def __init__(self
, name
, info
, doc
, ifcond
, features
, variants
):
487 super().__init
__(name
, info
, doc
, ifcond
, features
)
488 assert isinstance(variants
, QAPISchemaVariants
)
489 assert variants
.tag_member
490 variants
.set_defined_in(name
)
491 variants
.tag_member
.set_defined_in(self
.name
)
492 self
.variants
= variants
494 def check(self
, schema
):
495 super().check(schema
)
496 self
.variants
.tag_member
.check(schema
)
497 # Not calling self.variants.check_clash(), because there's nothing
499 self
.variants
.check(schema
, {})
500 # Alternate branch names have no relation to the tag enum values;
501 # so we have to check for potential name collisions ourselves.
504 for v
in self
.variants
.variants
:
505 v
.check_clash(self
.info
, seen
)
506 qtype
= v
.type.alternate_qtype()
511 % (v
.describe(self
.info
), v
.type.describe()))
512 conflicting
= set([qtype
])
513 if qtype
== 'QTYPE_QSTRING':
514 if isinstance(v
.type, QAPISchemaEnumType
):
515 for m
in v
.type.members
:
516 if m
.name
in ['on', 'off']:
517 conflicting
.add('QTYPE_QBOOL')
518 if re
.match(r
'[-+0-9.]', m
.name
):
519 # lazy, could be tightened
520 conflicting
.add('QTYPE_QNUM')
522 conflicting
.add('QTYPE_QNUM')
523 conflicting
.add('QTYPE_QBOOL')
524 for qt
in conflicting
:
528 "%s can't be distinguished from '%s'"
529 % (v
.describe(self
.info
), types_seen
[qt
]))
530 types_seen
[qt
] = v
.name
532 def connect_doc(self
, doc
=None):
533 super().connect_doc(doc
)
534 doc
= doc
or self
.doc
535 for v
in self
.variants
.variants
:
539 return c_name(self
.name
) + POINTER_SUFFIX
544 def visit(self
, visitor
):
545 super().visit(visitor
)
546 visitor
.visit_alternate_type(
547 self
.name
, self
.info
, self
.ifcond
, self
.features
, self
.variants
)
550 class QAPISchemaVariants
:
551 def __init__(self
, tag_name
, info
, tag_member
, variants
):
552 # Flat unions pass tag_name but not tag_member.
553 # Simple unions and alternates pass tag_member but not tag_name.
554 # After check(), tag_member is always set, and tag_name remains
555 # a reliable witness of being used by a flat union.
556 assert bool(tag_member
) != bool(tag_name
)
557 assert (isinstance(tag_name
, str) or
558 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
560 assert isinstance(v
, QAPISchemaVariant
)
561 self
._tag
_name
= tag_name
563 self
.tag_member
= tag_member
564 self
.variants
= variants
566 def set_defined_in(self
, name
):
567 for v
in self
.variants
:
568 v
.set_defined_in(name
)
570 def check(self
, schema
, seen
):
571 if not self
.tag_member
: # flat union
572 self
.tag_member
= seen
.get(c_name(self
._tag
_name
))
574 # Pointing to the base type when not implicit would be
575 # nice, but we don't know it here
576 if not self
.tag_member
or self
._tag
_name
!= self
.tag_member
.name
:
579 "discriminator '%s' is not a member of %s"
580 % (self
._tag
_name
, base
))
582 base_type
= schema
.lookup_type(self
.tag_member
.defined_in
)
584 if not base_type
.is_implicit():
585 base
= "base type '%s'" % self
.tag_member
.defined_in
586 if not isinstance(self
.tag_member
.type, QAPISchemaEnumType
):
589 "discriminator member '%s' of %s must be of enum type"
590 % (self
._tag
_name
, base
))
591 if self
.tag_member
.optional
:
594 "discriminator member '%s' of %s must not be optional"
595 % (self
._tag
_name
, base
))
596 if self
.tag_member
.ifcond
:
599 "discriminator member '%s' of %s must not be conditional"
600 % (self
._tag
_name
, base
))
602 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
603 assert not self
.tag_member
.optional
604 assert self
.tag_member
.ifcond
== []
605 if self
._tag
_name
: # flat union
606 # branches that are not explicitly covered get an empty type
607 cases
= {v
.name
for v
in self
.variants
}
608 for m
in self
.tag_member
.type.members
:
609 if m
.name
not in cases
:
610 v
= QAPISchemaVariant(m
.name
, self
.info
,
612 v
.set_defined_in(self
.tag_member
.defined_in
)
613 self
.variants
.append(v
)
614 if not self
.variants
:
615 raise QAPISemError(self
.info
, "union has no branches")
616 for v
in self
.variants
:
618 # Union names must match enum values; alternate names are
619 # checked separately. Use 'seen' to tell the two apart.
621 if v
.name
not in self
.tag_member
.type.member_names():
624 "branch '%s' is not a value of %s"
625 % (v
.name
, self
.tag_member
.type.describe()))
626 if (not isinstance(v
.type, QAPISchemaObjectType
)
631 % (v
.describe(self
.info
), v
.type.describe()))
634 def check_clash(self
, info
, seen
):
635 for v
in self
.variants
:
636 # Reset seen map for each variant, since qapi names from one
637 # branch do not affect another branch
638 v
.type.check_clash(info
, dict(seen
))
641 class QAPISchemaMember
:
642 """ Represents object members, enum members and features """
645 def __init__(self
, name
, info
, ifcond
=None):
646 assert isinstance(name
, str)
649 self
.ifcond
= ifcond
or []
650 self
.defined_in
= None
652 def set_defined_in(self
, name
):
653 assert not self
.defined_in
654 self
.defined_in
= name
656 def check_clash(self
, info
, seen
):
657 cname
= c_name(self
.name
)
661 "%s collides with %s"
662 % (self
.describe(info
), seen
[cname
].describe(info
)))
665 def connect_doc(self
, doc
):
667 doc
.connect_member(self
)
669 def describe(self
, info
):
671 defined_in
= self
.defined_in
674 if defined_in
.startswith('q_obj_'):
675 # See QAPISchema._make_implicit_object_type() - reverse the
676 # mapping there to create a nice human-readable description
677 defined_in
= defined_in
[6:]
678 if defined_in
.endswith('-arg'):
679 # Implicit type created for a command's dict 'data'
680 assert role
== 'member'
682 elif defined_in
.endswith('-base'):
683 # Implicit type created for a flat union's dict 'base'
684 role
= 'base ' + role
686 # Implicit type created for a simple union's branch
687 assert defined_in
.endswith('-wrapper')
688 # Unreachable and not implemented
690 elif defined_in
.endswith('Kind'):
691 # See QAPISchema._make_implicit_enum_type()
692 # Implicit enum created for simple union's branches
693 assert role
== 'value'
695 elif defined_in
!= info
.defn_name
:
696 return "%s '%s' of type '%s'" % (role
, self
.name
, defined_in
)
697 return "%s '%s'" % (role
, self
.name
)
700 class QAPISchemaEnumMember(QAPISchemaMember
):
704 class QAPISchemaFeature(QAPISchemaMember
):
708 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
709 def __init__(self
, name
, info
, typ
, optional
, ifcond
=None, features
=None):
710 super().__init
__(name
, info
, ifcond
)
711 assert isinstance(typ
, str)
712 assert isinstance(optional
, bool)
713 for f
in features
or []:
714 assert isinstance(f
, QAPISchemaFeature
)
715 f
.set_defined_in(name
)
716 self
._type
_name
= typ
718 self
.optional
= optional
719 self
.features
= features
or []
721 def check(self
, schema
):
722 assert self
.defined_in
723 self
.type = schema
.resolve_type(self
._type
_name
, self
.info
,
726 for f
in self
.features
:
727 f
.check_clash(self
.info
, seen
)
729 def connect_doc(self
, doc
):
730 super().connect_doc(doc
)
732 for f
in self
.features
:
733 doc
.connect_feature(f
)
736 class QAPISchemaVariant(QAPISchemaObjectTypeMember
):
739 def __init__(self
, name
, info
, typ
, ifcond
=None):
740 super().__init
__(name
, info
, typ
, False, ifcond
)
743 class QAPISchemaCommand(QAPISchemaEntity
):
746 def __init__(self
, name
, info
, doc
, ifcond
, features
,
748 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
,
750 super().__init
__(name
, info
, doc
, ifcond
, features
)
751 assert not arg_type
or isinstance(arg_type
, str)
752 assert not ret_type
or isinstance(ret_type
, str)
753 self
._arg
_type
_name
= arg_type
755 self
._ret
_type
_name
= ret_type
758 self
.success_response
= success_response
760 self
.allow_oob
= allow_oob
761 self
.allow_preconfig
= allow_preconfig
762 self
.coroutine
= coroutine
764 def check(self
, schema
):
765 super().check(schema
)
766 if self
._arg
_type
_name
:
767 self
.arg_type
= schema
.resolve_type(
768 self
._arg
_type
_name
, self
.info
, "command's 'data'")
769 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
772 "command's 'data' cannot take %s"
773 % self
.arg_type
.describe())
774 if self
.arg_type
.variants
and not self
.boxed
:
777 "command's 'data' can take %s only with 'boxed': true"
778 % self
.arg_type
.describe())
779 if self
._ret
_type
_name
:
780 self
.ret_type
= schema
.resolve_type(
781 self
._ret
_type
_name
, self
.info
, "command's 'returns'")
782 if self
.name
not in self
.info
.pragma
.command_returns_exceptions
:
784 if isinstance(typ
, QAPISchemaArrayType
):
785 typ
= self
.ret_type
.element_type
787 if not isinstance(typ
, QAPISchemaObjectType
):
790 "command's 'returns' cannot take %s"
791 % self
.ret_type
.describe())
793 def connect_doc(self
, doc
=None):
794 super().connect_doc(doc
)
795 doc
= doc
or self
.doc
797 if self
.arg_type
and self
.arg_type
.is_implicit():
798 self
.arg_type
.connect_doc(doc
)
800 def visit(self
, visitor
):
801 super().visit(visitor
)
802 visitor
.visit_command(
803 self
.name
, self
.info
, self
.ifcond
, self
.features
,
804 self
.arg_type
, self
.ret_type
, self
.gen
, self
.success_response
,
805 self
.boxed
, self
.allow_oob
, self
.allow_preconfig
,
809 class QAPISchemaEvent(QAPISchemaEntity
):
812 def __init__(self
, name
, info
, doc
, ifcond
, features
, arg_type
, boxed
):
813 super().__init
__(name
, info
, doc
, ifcond
, features
)
814 assert not arg_type
or isinstance(arg_type
, str)
815 self
._arg
_type
_name
= arg_type
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
, "event's 'data'")
824 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
827 "event's 'data' cannot take %s"
828 % self
.arg_type
.describe())
829 if self
.arg_type
.variants
and not self
.boxed
:
832 "event's 'data' can take %s only with 'boxed': true"
833 % self
.arg_type
.describe())
835 def connect_doc(self
, doc
=None):
836 super().connect_doc(doc
)
837 doc
= doc
or self
.doc
839 if self
.arg_type
and self
.arg_type
.is_implicit():
840 self
.arg_type
.connect_doc(doc
)
842 def visit(self
, visitor
):
843 super().visit(visitor
)
845 self
.name
, self
.info
, self
.ifcond
, self
.features
,
846 self
.arg_type
, self
.boxed
)
850 def __init__(self
, fname
):
852 parser
= QAPISchemaParser(fname
)
853 exprs
= check_exprs(parser
.exprs
)
854 self
.docs
= parser
.docs
855 self
._entity
_list
= []
856 self
._entity
_dict
= {}
857 self
._module
_dict
= OrderedDict()
858 self
._schema
_dir
= os
.path
.dirname(fname
)
859 self
._make
_module
(QAPISchemaModule
.BUILTIN_MODULE_NAME
)
860 self
._make
_module
(fname
)
861 self
._predefining
= True
862 self
._def
_predefineds
()
863 self
._predefining
= False
864 self
._def
_exprs
(exprs
)
867 def _def_entity(self
, ent
):
868 # Only the predefined types are allowed to not have info
869 assert ent
.info
or self
._predefining
870 self
._entity
_list
.append(ent
)
873 # TODO reject names that differ only in '_' vs. '.' vs. '-',
874 # because they're liable to clash in generated C.
875 other_ent
= self
._entity
_dict
.get(ent
.name
)
878 where
= QAPISourceError(other_ent
.info
, "previous definition")
881 "'%s' is already defined\n%s" % (ent
.name
, where
))
883 ent
.info
, "%s is already defined" % other_ent
.describe())
884 self
._entity
_dict
[ent
.name
] = ent
886 def lookup_entity(self
, name
, typ
=None):
887 ent
= self
._entity
_dict
.get(name
)
888 if typ
and not isinstance(ent
, typ
):
892 def lookup_type(self
, name
):
893 return self
.lookup_entity(name
, QAPISchemaType
)
895 def resolve_type(self
, name
, info
, what
):
896 typ
= self
.lookup_type(name
)
901 info
, "%s uses unknown type '%s'" % (what
, name
))
904 def _module_name(self
, fname
: str) -> str:
905 if QAPISchemaModule
.is_system_module(fname
):
907 return os
.path
.relpath(fname
, self
._schema
_dir
)
909 def _make_module(self
, fname
):
910 name
= self
._module
_name
(fname
)
911 if name
not in self
._module
_dict
:
912 self
._module
_dict
[name
] = QAPISchemaModule(name
)
913 return self
._module
_dict
[name
]
915 def module_by_fname(self
, fname
):
916 name
= self
._module
_name
(fname
)
917 return self
._module
_dict
[name
]
919 def _def_include(self
, expr
, info
, doc
):
920 include
= expr
['include']
922 self
._def
_entity
(QAPISchemaInclude(self
._make
_module
(include
), info
))
924 def _def_builtin_type(self
, name
, json_type
, c_type
):
925 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
926 # Instantiating only the arrays that are actually used would
927 # be nice, but we can't as long as their generated code
928 # (qapi-builtin-types.[ch]) may be shared by some other
930 self
._make
_array
_type
(name
, None)
932 def _def_predefineds(self
):
933 for t
in [('str', 'string', 'char' + POINTER_SUFFIX
),
934 ('number', 'number', 'double'),
935 ('int', 'int', 'int64_t'),
936 ('int8', 'int', 'int8_t'),
937 ('int16', 'int', 'int16_t'),
938 ('int32', 'int', 'int32_t'),
939 ('int64', 'int', 'int64_t'),
940 ('uint8', 'int', 'uint8_t'),
941 ('uint16', 'int', 'uint16_t'),
942 ('uint32', 'int', 'uint32_t'),
943 ('uint64', 'int', 'uint64_t'),
944 ('size', 'int', 'uint64_t'),
945 ('bool', 'boolean', 'bool'),
946 ('any', 'value', 'QObject' + POINTER_SUFFIX
),
947 ('null', 'null', 'QNull' + POINTER_SUFFIX
)]:
948 self
._def
_builtin
_type
(*t
)
949 self
.the_empty_object_type
= QAPISchemaObjectType(
950 'q_empty', None, None, None, None, None, [], None)
951 self
._def
_entity
(self
.the_empty_object_type
)
953 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
955 qtype_values
= self
._make
_enum
_members
(
956 [{'name': n
} for n
in qtypes
], None)
958 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None, None,
959 qtype_values
, 'QTYPE'))
961 def _make_features(self
, features
, info
):
964 return [QAPISchemaFeature(f
['name'], info
, f
.get('if'))
967 def _make_enum_members(self
, values
, info
):
968 return [QAPISchemaEnumMember(v
['name'], info
, v
.get('if'))
971 def _make_implicit_enum_type(self
, name
, info
, ifcond
, values
):
972 # See also QAPISchemaObjectTypeMember.describe()
973 name
= name
+ 'Kind' # reserved by check_defn_name_str()
974 self
._def
_entity
(QAPISchemaEnumType(
975 name
, info
, None, ifcond
, None,
976 self
._make
_enum
_members
(values
, info
),
980 def _make_array_type(self
, element_type
, info
):
981 name
= element_type
+ 'List' # reserved by check_defn_name_str()
982 if not self
.lookup_type(name
):
983 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
986 def _make_implicit_object_type(self
, name
, info
, ifcond
, role
, members
):
989 # See also QAPISchemaObjectTypeMember.describe()
990 name
= 'q_obj_%s-%s' % (name
, role
)
991 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
993 # The implicit object type has multiple users. This can
994 # happen only for simple unions' implicit wrapper types.
995 # Its ifcond should be the disjunction of its user's
996 # ifconds. Not implemented. Instead, we always pass the
997 # wrapped type's ifcond, which is trivially the same for all
998 # users. It's also necessary for the wrapper to compile.
999 # But it's not tight: the disjunction need not imply it. We
1000 # may end up compiling useless wrapper types.
1001 # TODO kill simple unions or implement the disjunction
1003 # pylint: disable=protected-access
1004 assert (ifcond
or []) == typ
._ifcond
1006 self
._def
_entity
(QAPISchemaObjectType(
1007 name
, info
, None, ifcond
, None, None, members
, None))
1010 def _def_enum_type(self
, expr
, info
, doc
):
1013 prefix
= expr
.get('prefix')
1014 ifcond
= expr
.get('if')
1015 features
= self
._make
_features
(expr
.get('features'), info
)
1016 self
._def
_entity
(QAPISchemaEnumType(
1017 name
, info
, doc
, ifcond
, features
,
1018 self
._make
_enum
_members
(data
, info
), prefix
))
1020 def _make_member(self
, name
, typ
, ifcond
, features
, info
):
1022 if name
.startswith('*'):
1025 if isinstance(typ
, list):
1026 assert len(typ
) == 1
1027 typ
= self
._make
_array
_type
(typ
[0], info
)
1028 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
,
1029 self
._make
_features
(features
, info
))
1031 def _make_members(self
, data
, info
):
1032 return [self
._make
_member
(key
, value
['type'], value
.get('if'),
1033 value
.get('features'), info
)
1034 for (key
, value
) in data
.items()]
1036 def _def_struct_type(self
, expr
, info
, doc
):
1037 name
= expr
['struct']
1038 base
= expr
.get('base')
1040 ifcond
= expr
.get('if')
1041 features
= self
._make
_features
(expr
.get('features'), info
)
1042 self
._def
_entity
(QAPISchemaObjectType(
1043 name
, info
, doc
, ifcond
, features
, base
,
1044 self
._make
_members
(data
, info
),
1047 def _make_variant(self
, case
, typ
, ifcond
, info
):
1048 return QAPISchemaVariant(case
, info
, typ
, ifcond
)
1050 def _make_simple_variant(self
, case
, typ
, ifcond
, info
):
1051 if isinstance(typ
, list):
1052 assert len(typ
) == 1
1053 typ
= self
._make
_array
_type
(typ
[0], info
)
1054 typ
= self
._make
_implicit
_object
_type
(
1055 typ
, info
, self
.lookup_type(typ
),
1056 'wrapper', [self
._make
_member
('data', typ
, None, None, info
)])
1057 return QAPISchemaVariant(case
, info
, typ
, ifcond
)
1059 def _def_union_type(self
, expr
, info
, doc
):
1060 name
= expr
['union']
1062 base
= expr
.get('base')
1063 ifcond
= expr
.get('if')
1064 features
= self
._make
_features
(expr
.get('features'), info
)
1065 tag_name
= expr
.get('discriminator')
1067 if isinstance(base
, dict):
1068 base
= self
._make
_implicit
_object
_type
(
1070 'base', self
._make
_members
(base
, info
))
1072 variants
= [self
._make
_variant
(key
, value
['type'],
1073 value
.get('if'), info
)
1074 for (key
, value
) in data
.items()]
1077 variants
= [self
._make
_simple
_variant
(key
, value
['type'],
1078 value
.get('if'), info
)
1079 for (key
, value
) in data
.items()]
1080 enum
= [{'name': v
.name
, 'if': v
.ifcond
} for v
in variants
]
1081 typ
= self
._make
_implicit
_enum
_type
(name
, info
, ifcond
, enum
)
1082 tag_member
= QAPISchemaObjectTypeMember('type', info
, typ
, False)
1083 members
= [tag_member
]
1085 QAPISchemaObjectType(name
, info
, doc
, ifcond
, features
,
1088 tag_name
, info
, tag_member
, variants
)))
1090 def _def_alternate_type(self
, expr
, info
, doc
):
1091 name
= expr
['alternate']
1093 ifcond
= expr
.get('if')
1094 features
= self
._make
_features
(expr
.get('features'), info
)
1095 variants
= [self
._make
_variant
(key
, value
['type'], value
.get('if'),
1097 for (key
, value
) in data
.items()]
1098 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
1100 QAPISchemaAlternateType(name
, info
, doc
, ifcond
, features
,
1102 None, info
, tag_member
, variants
)))
1104 def _def_command(self
, expr
, info
, doc
):
1105 name
= expr
['command']
1106 data
= expr
.get('data')
1107 rets
= expr
.get('returns')
1108 gen
= expr
.get('gen', True)
1109 success_response
= expr
.get('success-response', True)
1110 boxed
= expr
.get('boxed', False)
1111 allow_oob
= expr
.get('allow-oob', False)
1112 allow_preconfig
= expr
.get('allow-preconfig', False)
1113 coroutine
= expr
.get('coroutine', False)
1114 ifcond
= expr
.get('if')
1115 features
= self
._make
_features
(expr
.get('features'), info
)
1116 if isinstance(data
, OrderedDict
):
1117 data
= self
._make
_implicit
_object
_type
(
1119 'arg', self
._make
_members
(data
, info
))
1120 if isinstance(rets
, list):
1121 assert len(rets
) == 1
1122 rets
= self
._make
_array
_type
(rets
[0], info
)
1123 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, ifcond
, features
,
1125 gen
, success_response
,
1126 boxed
, allow_oob
, allow_preconfig
,
1129 def _def_event(self
, expr
, info
, doc
):
1130 name
= expr
['event']
1131 data
= expr
.get('data')
1132 boxed
= expr
.get('boxed', False)
1133 ifcond
= expr
.get('if')
1134 features
= self
._make
_features
(expr
.get('features'), info
)
1135 if isinstance(data
, OrderedDict
):
1136 data
= self
._make
_implicit
_object
_type
(
1138 'arg', self
._make
_members
(data
, info
))
1139 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, ifcond
, features
,
1142 def _def_exprs(self
, exprs
):
1143 for expr_elem
in exprs
:
1144 expr
= expr_elem
['expr']
1145 info
= expr_elem
['info']
1146 doc
= expr_elem
.get('doc')
1148 self
._def
_enum
_type
(expr
, info
, doc
)
1149 elif 'struct' in expr
:
1150 self
._def
_struct
_type
(expr
, info
, doc
)
1151 elif 'union' in expr
:
1152 self
._def
_union
_type
(expr
, info
, doc
)
1153 elif 'alternate' in expr
:
1154 self
._def
_alternate
_type
(expr
, info
, doc
)
1155 elif 'command' in expr
:
1156 self
._def
_command
(expr
, info
, doc
)
1157 elif 'event' in expr
:
1158 self
._def
_event
(expr
, info
, doc
)
1159 elif 'include' in expr
:
1160 self
._def
_include
(expr
, info
, doc
)
1165 for ent
in self
._entity
_list
:
1169 for ent
in self
._entity
_list
:
1170 ent
.set_module(self
)
1172 def visit(self
, visitor
):
1173 visitor
.visit_begin(self
)
1174 for mod
in self
._module
_dict
.values():