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 self
._module
= schema
.module_by_fname(info
and info
.fname
)
72 self
._module
.add_entity(self
)
74 def set_module(self
, schema
):
75 self
._set
_module
(schema
, self
.info
)
82 def is_implicit(self
):
85 def visit(self
, visitor
):
90 return "%s '%s'" % (self
.meta
, self
.name
)
93 class QAPISchemaVisitor
:
94 def visit_begin(self
, schema
):
100 def visit_module(self
, name
):
103 def visit_needed(self
, entity
):
104 # Default to visiting everything
107 def visit_include(self
, name
, info
):
110 def visit_builtin_type(self
, name
, info
, json_type
):
113 def visit_enum_type(self
, name
, info
, ifcond
, features
, members
, prefix
):
116 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
119 def visit_object_type(self
, name
, info
, ifcond
, features
,
120 base
, members
, variants
):
123 def visit_object_type_flat(self
, name
, info
, ifcond
, features
,
127 def visit_alternate_type(self
, name
, info
, ifcond
, features
, variants
):
130 def visit_command(self
, name
, info
, ifcond
, features
,
131 arg_type
, ret_type
, gen
, success_response
, boxed
,
132 allow_oob
, allow_preconfig
, coroutine
):
135 def visit_event(self
, name
, info
, ifcond
, features
, arg_type
, boxed
):
139 class QAPISchemaModule
:
140 def __init__(self
, name
):
142 self
._entity
_list
= []
144 def add_entity(self
, ent
):
145 self
._entity
_list
.append(ent
)
147 def visit(self
, visitor
):
148 visitor
.visit_module(self
.name
)
149 for entity
in self
._entity
_list
:
150 if visitor
.visit_needed(entity
):
151 entity
.visit(visitor
)
154 class QAPISchemaInclude(QAPISchemaEntity
):
155 def __init__(self
, sub_module
, info
):
156 super().__init
__(None, info
, None)
157 self
._sub
_module
= sub_module
159 def visit(self
, visitor
):
160 super().visit(visitor
)
161 visitor
.visit_include(self
._sub
_module
.name
, self
.info
)
164 class QAPISchemaType(QAPISchemaEntity
):
165 # Return the C type for common use.
166 # For the types we commonly box, this is a pointer type.
170 # Return the C type to be used in a parameter list.
171 def c_param_type(self
):
174 # Return the C type to be used where we suppress boxing.
175 def c_unboxed_type(self
):
181 def alternate_qtype(self
):
183 'null': 'QTYPE_QNULL',
184 'string': 'QTYPE_QSTRING',
185 'number': 'QTYPE_QNUM',
187 'boolean': 'QTYPE_QBOOL',
188 'object': 'QTYPE_QDICT'
190 return json2qtype
.get(self
.json_type())
193 if self
.is_implicit():
197 def check(self
, schema
):
198 QAPISchemaEntity
.check(self
, schema
)
199 if 'deprecated' in [f
.name
for f
in self
.features
]:
201 self
.info
, "feature 'deprecated' is not supported for types")
205 return "%s type '%s'" % (self
.meta
, self
.name
)
208 class QAPISchemaBuiltinType(QAPISchemaType
):
211 def __init__(self
, name
, json_type
, c_type
):
212 super().__init
__(name
, None, None)
213 assert not c_type
or isinstance(c_type
, str)
214 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
216 self
._json
_type
_name
= json_type
217 self
._c
_type
_name
= c_type
223 return self
._c
_type
_name
225 def c_param_type(self
):
226 if self
.name
== 'str':
227 return 'const ' + self
._c
_type
_name
228 return self
._c
_type
_name
231 return self
._json
_type
_name
234 return self
.json_type()
236 def visit(self
, visitor
):
237 super().visit(visitor
)
238 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
241 class QAPISchemaEnumType(QAPISchemaType
):
244 def __init__(self
, name
, info
, doc
, ifcond
, features
, members
, prefix
):
245 super().__init
__(name
, info
, doc
, ifcond
, features
)
247 assert isinstance(m
, QAPISchemaEnumMember
)
248 m
.set_defined_in(name
)
249 assert prefix
is None or isinstance(prefix
, str)
250 self
.members
= members
253 def check(self
, schema
):
254 super().check(schema
)
256 for m
in self
.members
:
257 m
.check_clash(self
.info
, seen
)
259 def connect_doc(self
, doc
=None):
260 super().connect_doc(doc
)
261 doc
= doc
or self
.doc
262 for m
in self
.members
:
265 def is_implicit(self
):
266 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
267 return self
.name
.endswith('Kind') or self
.name
== 'QType'
270 return c_name(self
.name
)
272 def member_names(self
):
273 return [m
.name
for m
in self
.members
]
278 def visit(self
, visitor
):
279 super().visit(visitor
)
280 visitor
.visit_enum_type(
281 self
.name
, self
.info
, self
.ifcond
, self
.features
,
282 self
.members
, self
.prefix
)
285 class QAPISchemaArrayType(QAPISchemaType
):
288 def __init__(self
, name
, info
, element_type
):
289 super().__init
__(name
, info
, None)
290 assert isinstance(element_type
, str)
291 self
._element
_type
_name
= element_type
292 self
.element_type
= None
294 def check(self
, schema
):
295 super().check(schema
)
296 self
.element_type
= schema
.resolve_type(
297 self
._element
_type
_name
, self
.info
,
298 self
.info
and self
.info
.defn_meta
)
299 assert not isinstance(self
.element_type
, QAPISchemaArrayType
)
301 def set_module(self
, schema
):
302 self
._set
_module
(schema
, self
.element_type
.info
)
307 return self
.element_type
.ifcond
309 def is_implicit(self
):
313 return c_name(self
.name
) + POINTER_SUFFIX
319 elt_doc_type
= self
.element_type
.doc_type()
322 return 'array of ' + elt_doc_type
324 def visit(self
, visitor
):
325 super().visit(visitor
)
326 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
331 return "%s type ['%s']" % (self
.meta
, self
._element
_type
_name
)
334 class QAPISchemaObjectType(QAPISchemaType
):
335 def __init__(self
, name
, info
, doc
, ifcond
, features
,
336 base
, local_members
, variants
):
337 # struct has local_members, optional base, and no variants
338 # flat union has base, variants, and no local_members
339 # simple union has local_members, variants, and no base
340 super().__init
__(name
, info
, doc
, ifcond
, features
)
341 self
.meta
= 'union' if variants
else 'struct'
342 assert base
is None or isinstance(base
, str)
343 for m
in local_members
:
344 assert isinstance(m
, QAPISchemaObjectTypeMember
)
345 m
.set_defined_in(name
)
346 if variants
is not None:
347 assert isinstance(variants
, QAPISchemaVariants
)
348 variants
.set_defined_in(name
)
349 self
._base
_name
= base
351 self
.local_members
= local_members
352 self
.variants
= variants
355 def check(self
, schema
):
356 # This calls another type T's .check() exactly when the C
357 # struct emitted by gen_object() contains that T's C struct
358 # (pointers don't count).
359 if self
.members
is not None:
360 # A previous .check() completed: nothing to do
363 # Recursed: C struct contains itself
364 raise QAPISemError(self
.info
,
365 "object %s contains itself" % self
.name
)
367 super().check(schema
)
368 assert self
._checked
and self
.members
is None
372 self
.base
= schema
.resolve_type(self
._base
_name
, self
.info
,
374 if (not isinstance(self
.base
, QAPISchemaObjectType
)
375 or self
.base
.variants
):
378 "'base' requires a struct type, %s isn't"
379 % self
.base
.describe())
380 self
.base
.check(schema
)
381 self
.base
.check_clash(self
.info
, seen
)
382 for m
in self
.local_members
:
384 m
.check_clash(self
.info
, seen
)
385 members
= seen
.values()
388 self
.variants
.check(schema
, seen
)
389 self
.variants
.check_clash(self
.info
, seen
)
391 self
.members
= members
# mark completed
393 # Check that the members of this type do not cause duplicate JSON members,
394 # and update seen to track the members seen so far. Report any errors
395 # on behalf of info, which is not necessarily self.info
396 def check_clash(self
, info
, seen
):
398 assert not self
.variants
# not implemented
399 for m
in self
.members
:
400 m
.check_clash(info
, seen
)
402 def connect_doc(self
, doc
=None):
403 super().connect_doc(doc
)
404 doc
= doc
or self
.doc
405 if self
.base
and self
.base
.is_implicit():
406 self
.base
.connect_doc(doc
)
407 for m
in self
.local_members
:
413 if isinstance(self
._ifcond
, QAPISchemaType
):
414 # Simple union wrapper type inherits from wrapped type;
415 # see _make_implicit_object_type()
416 return self
._ifcond
.ifcond
419 def is_implicit(self
):
420 # See QAPISchema._make_implicit_object_type(), as well as
422 return self
.name
.startswith('q_')
425 assert self
.members
is not None
426 return not self
.members
and not self
.variants
429 assert self
.name
!= 'q_empty'
430 return super().c_name()
433 assert not self
.is_implicit()
434 return c_name(self
.name
) + POINTER_SUFFIX
436 def c_unboxed_type(self
):
437 return c_name(self
.name
)
442 def visit(self
, visitor
):
443 super().visit(visitor
)
444 visitor
.visit_object_type(
445 self
.name
, self
.info
, self
.ifcond
, self
.features
,
446 self
.base
, self
.local_members
, self
.variants
)
447 visitor
.visit_object_type_flat(
448 self
.name
, self
.info
, self
.ifcond
, self
.features
,
449 self
.members
, self
.variants
)
452 class QAPISchemaAlternateType(QAPISchemaType
):
455 def __init__(self
, name
, info
, doc
, ifcond
, features
, variants
):
456 super().__init
__(name
, info
, doc
, ifcond
, features
)
457 assert isinstance(variants
, QAPISchemaVariants
)
458 assert variants
.tag_member
459 variants
.set_defined_in(name
)
460 variants
.tag_member
.set_defined_in(self
.name
)
461 self
.variants
= variants
463 def check(self
, schema
):
464 super().check(schema
)
465 self
.variants
.tag_member
.check(schema
)
466 # Not calling self.variants.check_clash(), because there's nothing
468 self
.variants
.check(schema
, {})
469 # Alternate branch names have no relation to the tag enum values;
470 # so we have to check for potential name collisions ourselves.
473 for v
in self
.variants
.variants
:
474 v
.check_clash(self
.info
, seen
)
475 qtype
= v
.type.alternate_qtype()
480 % (v
.describe(self
.info
), v
.type.describe()))
481 conflicting
= set([qtype
])
482 if qtype
== 'QTYPE_QSTRING':
483 if isinstance(v
.type, QAPISchemaEnumType
):
484 for m
in v
.type.members
:
485 if m
.name
in ['on', 'off']:
486 conflicting
.add('QTYPE_QBOOL')
487 if re
.match(r
'[-+0-9.]', m
.name
):
488 # lazy, could be tightened
489 conflicting
.add('QTYPE_QNUM')
491 conflicting
.add('QTYPE_QNUM')
492 conflicting
.add('QTYPE_QBOOL')
493 for qt
in conflicting
:
497 "%s can't be distinguished from '%s'"
498 % (v
.describe(self
.info
), types_seen
[qt
]))
499 types_seen
[qt
] = v
.name
501 def connect_doc(self
, doc
=None):
502 super().connect_doc(doc
)
503 doc
= doc
or self
.doc
504 for v
in self
.variants
.variants
:
508 return c_name(self
.name
) + POINTER_SUFFIX
513 def visit(self
, visitor
):
514 super().visit(visitor
)
515 visitor
.visit_alternate_type(
516 self
.name
, self
.info
, self
.ifcond
, self
.features
, self
.variants
)
519 class QAPISchemaVariants
:
520 def __init__(self
, tag_name
, info
, tag_member
, variants
):
521 # Flat unions pass tag_name but not tag_member.
522 # Simple unions and alternates pass tag_member but not tag_name.
523 # After check(), tag_member is always set, and tag_name remains
524 # a reliable witness of being used by a flat union.
525 assert bool(tag_member
) != bool(tag_name
)
526 assert (isinstance(tag_name
, str) or
527 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
529 assert isinstance(v
, QAPISchemaVariant
)
530 self
._tag
_name
= tag_name
532 self
.tag_member
= tag_member
533 self
.variants
= variants
535 def set_defined_in(self
, name
):
536 for v
in self
.variants
:
537 v
.set_defined_in(name
)
539 def check(self
, schema
, seen
):
540 if not self
.tag_member
: # flat union
541 self
.tag_member
= seen
.get(c_name(self
._tag
_name
))
543 # Pointing to the base type when not implicit would be
544 # nice, but we don't know it here
545 if not self
.tag_member
or self
._tag
_name
!= self
.tag_member
.name
:
548 "discriminator '%s' is not a member of %s"
549 % (self
._tag
_name
, base
))
551 base_type
= schema
.lookup_type(self
.tag_member
.defined_in
)
553 if not base_type
.is_implicit():
554 base
= "base type '%s'" % self
.tag_member
.defined_in
555 if not isinstance(self
.tag_member
.type, QAPISchemaEnumType
):
558 "discriminator member '%s' of %s must be of enum type"
559 % (self
._tag
_name
, base
))
560 if self
.tag_member
.optional
:
563 "discriminator member '%s' of %s must not be optional"
564 % (self
._tag
_name
, base
))
565 if self
.tag_member
.ifcond
:
568 "discriminator member '%s' of %s must not be conditional"
569 % (self
._tag
_name
, base
))
571 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
572 assert not self
.tag_member
.optional
573 assert self
.tag_member
.ifcond
== []
574 if self
._tag
_name
: # flat union
575 # branches that are not explicitly covered get an empty type
576 cases
= {v
.name
for v
in self
.variants
}
577 for m
in self
.tag_member
.type.members
:
578 if m
.name
not in cases
:
579 v
= QAPISchemaVariant(m
.name
, self
.info
,
581 v
.set_defined_in(self
.tag_member
.defined_in
)
582 self
.variants
.append(v
)
583 if not self
.variants
:
584 raise QAPISemError(self
.info
, "union has no branches")
585 for v
in self
.variants
:
587 # Union names must match enum values; alternate names are
588 # checked separately. Use 'seen' to tell the two apart.
590 if v
.name
not in self
.tag_member
.type.member_names():
593 "branch '%s' is not a value of %s"
594 % (v
.name
, self
.tag_member
.type.describe()))
595 if (not isinstance(v
.type, QAPISchemaObjectType
)
600 % (v
.describe(self
.info
), v
.type.describe()))
603 def check_clash(self
, info
, seen
):
604 for v
in self
.variants
:
605 # Reset seen map for each variant, since qapi names from one
606 # branch do not affect another branch
607 v
.type.check_clash(info
, dict(seen
))
610 class QAPISchemaMember
:
611 """ Represents object members, enum members and features """
614 def __init__(self
, name
, info
, ifcond
=None):
615 assert isinstance(name
, str)
618 self
.ifcond
= ifcond
or []
619 self
.defined_in
= None
621 def set_defined_in(self
, name
):
622 assert not self
.defined_in
623 self
.defined_in
= name
625 def check_clash(self
, info
, seen
):
626 cname
= c_name(self
.name
)
630 "%s collides with %s"
631 % (self
.describe(info
), seen
[cname
].describe(info
)))
634 def connect_doc(self
, doc
):
636 doc
.connect_member(self
)
638 def describe(self
, info
):
640 defined_in
= self
.defined_in
643 if defined_in
.startswith('q_obj_'):
644 # See QAPISchema._make_implicit_object_type() - reverse the
645 # mapping there to create a nice human-readable description
646 defined_in
= defined_in
[6:]
647 if defined_in
.endswith('-arg'):
648 # Implicit type created for a command's dict 'data'
649 assert role
== 'member'
651 elif defined_in
.endswith('-base'):
652 # Implicit type created for a flat union's dict 'base'
653 role
= 'base ' + role
655 # Implicit type created for a simple union's branch
656 assert defined_in
.endswith('-wrapper')
657 # Unreachable and not implemented
659 elif defined_in
.endswith('Kind'):
660 # See QAPISchema._make_implicit_enum_type()
661 # Implicit enum created for simple union's branches
662 assert role
== 'value'
664 elif defined_in
!= info
.defn_name
:
665 return "%s '%s' of type '%s'" % (role
, self
.name
, defined_in
)
666 return "%s '%s'" % (role
, self
.name
)
669 class QAPISchemaEnumMember(QAPISchemaMember
):
673 class QAPISchemaFeature(QAPISchemaMember
):
677 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
678 def __init__(self
, name
, info
, typ
, optional
, ifcond
=None, features
=None):
679 super().__init
__(name
, info
, ifcond
)
680 assert isinstance(typ
, str)
681 assert isinstance(optional
, bool)
682 for f
in features
or []:
683 assert isinstance(f
, QAPISchemaFeature
)
684 f
.set_defined_in(name
)
685 self
._type
_name
= typ
687 self
.optional
= optional
688 self
.features
= features
or []
690 def check(self
, schema
):
691 assert self
.defined_in
692 self
.type = schema
.resolve_type(self
._type
_name
, self
.info
,
695 for f
in self
.features
:
696 f
.check_clash(self
.info
, seen
)
698 def connect_doc(self
, doc
):
699 super().connect_doc(doc
)
701 for f
in self
.features
:
702 doc
.connect_feature(f
)
705 class QAPISchemaVariant(QAPISchemaObjectTypeMember
):
708 def __init__(self
, name
, info
, typ
, ifcond
=None):
709 super().__init
__(name
, info
, typ
, False, ifcond
)
712 class QAPISchemaCommand(QAPISchemaEntity
):
715 def __init__(self
, name
, info
, doc
, ifcond
, features
,
717 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
,
719 super().__init
__(name
, info
, doc
, ifcond
, features
)
720 assert not arg_type
or isinstance(arg_type
, str)
721 assert not ret_type
or isinstance(ret_type
, str)
722 self
._arg
_type
_name
= arg_type
724 self
._ret
_type
_name
= ret_type
727 self
.success_response
= success_response
729 self
.allow_oob
= allow_oob
730 self
.allow_preconfig
= allow_preconfig
731 self
.coroutine
= coroutine
733 def check(self
, schema
):
734 super().check(schema
)
735 if self
._arg
_type
_name
:
736 self
.arg_type
= schema
.resolve_type(
737 self
._arg
_type
_name
, self
.info
, "command's 'data'")
738 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
741 "command's 'data' cannot take %s"
742 % self
.arg_type
.describe())
743 if self
.arg_type
.variants
and not self
.boxed
:
746 "command's 'data' can take %s only with 'boxed': true"
747 % self
.arg_type
.describe())
748 if self
._ret
_type
_name
:
749 self
.ret_type
= schema
.resolve_type(
750 self
._ret
_type
_name
, self
.info
, "command's 'returns'")
751 if self
.name
not in self
.info
.pragma
.returns_whitelist
:
753 if isinstance(typ
, QAPISchemaArrayType
):
754 typ
= self
.ret_type
.element_type
756 if not isinstance(typ
, QAPISchemaObjectType
):
759 "command's 'returns' cannot take %s"
760 % self
.ret_type
.describe())
762 def connect_doc(self
, doc
=None):
763 super().connect_doc(doc
)
764 doc
= doc
or self
.doc
766 if self
.arg_type
and self
.arg_type
.is_implicit():
767 self
.arg_type
.connect_doc(doc
)
769 def visit(self
, visitor
):
770 super().visit(visitor
)
771 visitor
.visit_command(
772 self
.name
, self
.info
, self
.ifcond
, self
.features
,
773 self
.arg_type
, self
.ret_type
, self
.gen
, self
.success_response
,
774 self
.boxed
, self
.allow_oob
, self
.allow_preconfig
,
778 class QAPISchemaEvent(QAPISchemaEntity
):
781 def __init__(self
, name
, info
, doc
, ifcond
, features
, arg_type
, boxed
):
782 super().__init
__(name
, info
, doc
, ifcond
, features
)
783 assert not arg_type
or isinstance(arg_type
, str)
784 self
._arg
_type
_name
= arg_type
788 def check(self
, schema
):
789 super().check(schema
)
790 if self
._arg
_type
_name
:
791 self
.arg_type
= schema
.resolve_type(
792 self
._arg
_type
_name
, self
.info
, "event's 'data'")
793 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
796 "event's 'data' cannot take %s"
797 % self
.arg_type
.describe())
798 if self
.arg_type
.variants
and not self
.boxed
:
801 "event's 'data' can take %s only with 'boxed': true"
802 % self
.arg_type
.describe())
804 def connect_doc(self
, doc
=None):
805 super().connect_doc(doc
)
806 doc
= doc
or self
.doc
808 if self
.arg_type
and self
.arg_type
.is_implicit():
809 self
.arg_type
.connect_doc(doc
)
811 def visit(self
, visitor
):
812 super().visit(visitor
)
814 self
.name
, self
.info
, self
.ifcond
, self
.features
,
815 self
.arg_type
, self
.boxed
)
819 def __init__(self
, fname
):
821 parser
= QAPISchemaParser(fname
)
822 exprs
= check_exprs(parser
.exprs
)
823 self
.docs
= parser
.docs
824 self
._entity
_list
= []
825 self
._entity
_dict
= {}
826 self
._module
_dict
= OrderedDict()
827 self
._schema
_dir
= os
.path
.dirname(fname
)
828 self
._make
_module
(None) # built-ins
829 self
._make
_module
(fname
)
830 self
._predefining
= True
831 self
._def
_predefineds
()
832 self
._predefining
= False
833 self
._def
_exprs
(exprs
)
836 def _def_entity(self
, ent
):
837 # Only the predefined types are allowed to not have info
838 assert ent
.info
or self
._predefining
839 self
._entity
_list
.append(ent
)
842 # TODO reject names that differ only in '_' vs. '.' vs. '-',
843 # because they're liable to clash in generated C.
844 other_ent
= self
._entity
_dict
.get(ent
.name
)
847 where
= QAPIError(other_ent
.info
, None, "previous definition")
850 "'%s' is already defined\n%s" % (ent
.name
, where
))
852 ent
.info
, "%s is already defined" % other_ent
.describe())
853 self
._entity
_dict
[ent
.name
] = ent
855 def lookup_entity(self
, name
, typ
=None):
856 ent
= self
._entity
_dict
.get(name
)
857 if typ
and not isinstance(ent
, typ
):
861 def lookup_type(self
, name
):
862 return self
.lookup_entity(name
, QAPISchemaType
)
864 def resolve_type(self
, name
, info
, what
):
865 typ
= self
.lookup_type(name
)
870 info
, "%s uses unknown type '%s'" % (what
, name
))
873 def _module_name(self
, fname
):
876 return os
.path
.relpath(fname
, self
._schema
_dir
)
878 def _make_module(self
, fname
):
879 name
= self
._module
_name
(fname
)
880 if name
not in self
._module
_dict
:
881 self
._module
_dict
[name
] = QAPISchemaModule(name
)
882 return self
._module
_dict
[name
]
884 def module_by_fname(self
, fname
):
885 name
= self
._module
_name
(fname
)
886 assert name
in self
._module
_dict
887 return self
._module
_dict
[name
]
889 def _def_include(self
, expr
, info
, doc
):
890 include
= expr
['include']
892 self
._def
_entity
(QAPISchemaInclude(self
._make
_module
(include
), info
))
894 def _def_builtin_type(self
, name
, json_type
, c_type
):
895 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
896 # Instantiating only the arrays that are actually used would
897 # be nice, but we can't as long as their generated code
898 # (qapi-builtin-types.[ch]) may be shared by some other
900 self
._make
_array
_type
(name
, None)
902 def _def_predefineds(self
):
903 for t
in [('str', 'string', 'char' + POINTER_SUFFIX
),
904 ('number', 'number', 'double'),
905 ('int', 'int', 'int64_t'),
906 ('int8', 'int', 'int8_t'),
907 ('int16', 'int', 'int16_t'),
908 ('int32', 'int', 'int32_t'),
909 ('int64', 'int', 'int64_t'),
910 ('uint8', 'int', 'uint8_t'),
911 ('uint16', 'int', 'uint16_t'),
912 ('uint32', 'int', 'uint32_t'),
913 ('uint64', 'int', 'uint64_t'),
914 ('size', 'int', 'uint64_t'),
915 ('bool', 'boolean', 'bool'),
916 ('any', 'value', 'QObject' + POINTER_SUFFIX
),
917 ('null', 'null', 'QNull' + POINTER_SUFFIX
)]:
918 self
._def
_builtin
_type
(*t
)
919 self
.the_empty_object_type
= QAPISchemaObjectType(
920 'q_empty', None, None, None, None, None, [], None)
921 self
._def
_entity
(self
.the_empty_object_type
)
923 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
925 qtype_values
= self
._make
_enum
_members
(
926 [{'name': n
} for n
in qtypes
], None)
928 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None, None,
929 qtype_values
, 'QTYPE'))
931 def _make_features(self
, features
, info
):
934 return [QAPISchemaFeature(f
['name'], info
, f
.get('if'))
937 def _make_enum_members(self
, values
, info
):
938 return [QAPISchemaEnumMember(v
['name'], info
, v
.get('if'))
941 def _make_implicit_enum_type(self
, name
, info
, ifcond
, values
):
942 # See also QAPISchemaObjectTypeMember.describe()
943 name
= name
+ 'Kind' # reserved by check_defn_name_str()
944 self
._def
_entity
(QAPISchemaEnumType(
945 name
, info
, None, ifcond
, None,
946 self
._make
_enum
_members
(values
, info
),
950 def _make_array_type(self
, element_type
, info
):
951 name
= element_type
+ 'List' # reserved by check_defn_name_str()
952 if not self
.lookup_type(name
):
953 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
956 def _make_implicit_object_type(self
, name
, info
, ifcond
, role
, members
):
959 # See also QAPISchemaObjectTypeMember.describe()
960 name
= 'q_obj_%s-%s' % (name
, role
)
961 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
963 # The implicit object type has multiple users. This can
964 # happen only for simple unions' implicit wrapper types.
965 # Its ifcond should be the disjunction of its user's
966 # ifconds. Not implemented. Instead, we always pass the
967 # wrapped type's ifcond, which is trivially the same for all
968 # users. It's also necessary for the wrapper to compile.
969 # But it's not tight: the disjunction need not imply it. We
970 # may end up compiling useless wrapper types.
971 # TODO kill simple unions or implement the disjunction
973 # pylint: disable=protected-access
974 assert (ifcond
or []) == typ
._ifcond
976 self
._def
_entity
(QAPISchemaObjectType(
977 name
, info
, None, ifcond
, None, None, members
, None))
980 def _def_enum_type(self
, expr
, info
, doc
):
983 prefix
= expr
.get('prefix')
984 ifcond
= expr
.get('if')
985 features
= self
._make
_features
(expr
.get('features'), info
)
986 self
._def
_entity
(QAPISchemaEnumType(
987 name
, info
, doc
, ifcond
, features
,
988 self
._make
_enum
_members
(data
, info
), prefix
))
990 def _make_member(self
, name
, typ
, ifcond
, features
, info
):
992 if name
.startswith('*'):
995 if isinstance(typ
, list):
997 typ
= self
._make
_array
_type
(typ
[0], info
)
998 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
,
999 self
._make
_features
(features
, info
))
1001 def _make_members(self
, data
, info
):
1002 return [self
._make
_member
(key
, value
['type'], value
.get('if'),
1003 value
.get('features'), info
)
1004 for (key
, value
) in data
.items()]
1006 def _def_struct_type(self
, expr
, info
, doc
):
1007 name
= expr
['struct']
1008 base
= expr
.get('base')
1010 ifcond
= expr
.get('if')
1011 features
= self
._make
_features
(expr
.get('features'), info
)
1012 self
._def
_entity
(QAPISchemaObjectType(
1013 name
, info
, doc
, ifcond
, features
, base
,
1014 self
._make
_members
(data
, info
),
1017 def _make_variant(self
, case
, typ
, ifcond
, info
):
1018 return QAPISchemaVariant(case
, info
, typ
, ifcond
)
1020 def _make_simple_variant(self
, case
, typ
, ifcond
, info
):
1021 if isinstance(typ
, list):
1022 assert len(typ
) == 1
1023 typ
= self
._make
_array
_type
(typ
[0], info
)
1024 typ
= self
._make
_implicit
_object
_type
(
1025 typ
, info
, self
.lookup_type(typ
),
1026 'wrapper', [self
._make
_member
('data', typ
, None, None, info
)])
1027 return QAPISchemaVariant(case
, info
, typ
, ifcond
)
1029 def _def_union_type(self
, expr
, info
, doc
):
1030 name
= expr
['union']
1032 base
= expr
.get('base')
1033 ifcond
= expr
.get('if')
1034 features
= self
._make
_features
(expr
.get('features'), info
)
1035 tag_name
= expr
.get('discriminator')
1037 if isinstance(base
, dict):
1038 base
= self
._make
_implicit
_object
_type
(
1040 'base', self
._make
_members
(base
, info
))
1042 variants
= [self
._make
_variant
(key
, value
['type'],
1043 value
.get('if'), info
)
1044 for (key
, value
) in data
.items()]
1047 variants
= [self
._make
_simple
_variant
(key
, value
['type'],
1048 value
.get('if'), info
)
1049 for (key
, value
) in data
.items()]
1050 enum
= [{'name': v
.name
, 'if': v
.ifcond
} for v
in variants
]
1051 typ
= self
._make
_implicit
_enum
_type
(name
, info
, ifcond
, enum
)
1052 tag_member
= QAPISchemaObjectTypeMember('type', info
, typ
, False)
1053 members
= [tag_member
]
1055 QAPISchemaObjectType(name
, info
, doc
, ifcond
, features
,
1058 tag_name
, info
, tag_member
, variants
)))
1060 def _def_alternate_type(self
, expr
, info
, doc
):
1061 name
= expr
['alternate']
1063 ifcond
= expr
.get('if')
1064 features
= self
._make
_features
(expr
.get('features'), info
)
1065 variants
= [self
._make
_variant
(key
, value
['type'], value
.get('if'),
1067 for (key
, value
) in data
.items()]
1068 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
1070 QAPISchemaAlternateType(name
, info
, doc
, ifcond
, features
,
1072 None, info
, tag_member
, variants
)))
1074 def _def_command(self
, expr
, info
, doc
):
1075 name
= expr
['command']
1076 data
= expr
.get('data')
1077 rets
= expr
.get('returns')
1078 gen
= expr
.get('gen', True)
1079 success_response
= expr
.get('success-response', True)
1080 boxed
= expr
.get('boxed', False)
1081 allow_oob
= expr
.get('allow-oob', False)
1082 allow_preconfig
= expr
.get('allow-preconfig', False)
1083 coroutine
= expr
.get('coroutine', False)
1084 ifcond
= expr
.get('if')
1085 features
= self
._make
_features
(expr
.get('features'), info
)
1086 if isinstance(data
, OrderedDict
):
1087 data
= self
._make
_implicit
_object
_type
(
1089 'arg', self
._make
_members
(data
, info
))
1090 if isinstance(rets
, list):
1091 assert len(rets
) == 1
1092 rets
= self
._make
_array
_type
(rets
[0], info
)
1093 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, ifcond
, features
,
1095 gen
, success_response
,
1096 boxed
, allow_oob
, allow_preconfig
,
1099 def _def_event(self
, expr
, info
, doc
):
1100 name
= expr
['event']
1101 data
= expr
.get('data')
1102 boxed
= expr
.get('boxed', False)
1103 ifcond
= expr
.get('if')
1104 features
= self
._make
_features
(expr
.get('features'), info
)
1105 if isinstance(data
, OrderedDict
):
1106 data
= self
._make
_implicit
_object
_type
(
1108 'arg', self
._make
_members
(data
, info
))
1109 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, ifcond
, features
,
1112 def _def_exprs(self
, exprs
):
1113 for expr_elem
in exprs
:
1114 expr
= expr_elem
['expr']
1115 info
= expr_elem
['info']
1116 doc
= expr_elem
.get('doc')
1118 self
._def
_enum
_type
(expr
, info
, doc
)
1119 elif 'struct' in expr
:
1120 self
._def
_struct
_type
(expr
, info
, doc
)
1121 elif 'union' in expr
:
1122 self
._def
_union
_type
(expr
, info
, doc
)
1123 elif 'alternate' in expr
:
1124 self
._def
_alternate
_type
(expr
, info
, doc
)
1125 elif 'command' in expr
:
1126 self
._def
_command
(expr
, info
, doc
)
1127 elif 'event' in expr
:
1128 self
._def
_event
(expr
, info
, doc
)
1129 elif 'include' in expr
:
1130 self
._def
_include
(expr
, info
, doc
)
1135 for ent
in self
._entity
_list
:
1139 for ent
in self
._entity
_list
:
1140 ent
.set_module(self
)
1142 def visit(self
, visitor
):
1143 visitor
.visit_begin(self
)
1144 for mod
in self
._module
_dict
.values():