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
19 from collections
import OrderedDict
21 from qapi
.common
import c_name
, pointer_suffix
22 from qapi
.error
import QAPIError
, QAPIParseError
, QAPISemError
23 from qapi
.expr
import check_exprs
24 from qapi
.parser
import QAPISchemaParser
27 class QAPISchemaEntity(object):
30 def __init__(self
, name
, info
, doc
, ifcond
=None, features
=None):
31 assert name
is None or isinstance(name
, str)
32 for f
in features
or []:
33 assert isinstance(f
, QAPISchemaFeature
)
34 f
.set_defined_in(name
)
37 # For explicitly defined entities, info points to the (explicit)
38 # definition. For builtins (and their arrays), info is None.
39 # For implicitly defined entities, info points to a place that
40 # triggered the implicit definition (there may be more than one
44 self
._ifcond
= ifcond
or []
45 self
.features
= features
or []
49 return c_name(self
.name
)
51 def check(self
, schema
):
52 assert not self
._checked
54 for f
in self
.features
:
55 f
.check_clash(self
.info
, seen
)
57 self
.doc
.connect_feature(f
)
61 def connect_doc(self
, doc
=None):
68 def _set_module(self
, schema
, info
):
70 self
._module
= schema
.module_by_fname(info
and info
.fname
)
72 def set_module(self
, schema
):
73 self
._set
_module
(schema
, self
.info
)
82 assert self
._module
or not self
.info
85 def is_implicit(self
):
88 def visit(self
, visitor
):
93 return "%s '%s'" % (self
.meta
, self
.name
)
96 class QAPISchemaVisitor(object):
97 def visit_begin(self
, schema
):
103 def visit_module(self
, fname
):
106 def visit_needed(self
, entity
):
107 # Default to visiting everything
110 def visit_include(self
, fname
, info
):
113 def visit_builtin_type(self
, name
, info
, json_type
):
116 def visit_enum_type(self
, name
, info
, ifcond
, members
, prefix
):
119 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
122 def visit_object_type(self
, name
, info
, ifcond
, base
, members
, variants
,
126 def visit_object_type_flat(self
, name
, info
, ifcond
, members
, variants
,
130 def visit_alternate_type(self
, name
, info
, ifcond
, variants
):
133 def visit_command(self
, name
, info
, ifcond
, arg_type
, ret_type
, gen
,
134 success_response
, boxed
, allow_oob
, allow_preconfig
,
138 def visit_event(self
, name
, info
, ifcond
, arg_type
, boxed
):
142 class QAPISchemaModule(object):
143 def __init__(self
, name
):
147 class QAPISchemaInclude(QAPISchemaEntity
):
148 def __init__(self
, sub_module
, info
):
149 QAPISchemaEntity
.__init
__(self
, None, info
, None)
150 self
._sub
_module
= sub_module
152 def visit(self
, visitor
):
153 QAPISchemaEntity
.visit(self
, visitor
)
154 visitor
.visit_include(self
._sub
_module
.name
, self
.info
)
157 class QAPISchemaType(QAPISchemaEntity
):
158 # Return the C type for common use.
159 # For the types we commonly box, this is a pointer type.
163 # Return the C type to be used in a parameter list.
164 def c_param_type(self
):
167 # Return the C type to be used where we suppress boxing.
168 def c_unboxed_type(self
):
174 def alternate_qtype(self
):
176 'null': 'QTYPE_QNULL',
177 'string': 'QTYPE_QSTRING',
178 'number': 'QTYPE_QNUM',
180 'boolean': 'QTYPE_QBOOL',
181 'object': 'QTYPE_QDICT'
183 return json2qtype
.get(self
.json_type())
186 if self
.is_implicit():
192 return "%s type '%s'" % (self
.meta
, self
.name
)
195 class QAPISchemaBuiltinType(QAPISchemaType
):
198 def __init__(self
, name
, json_type
, c_type
):
199 QAPISchemaType
.__init
__(self
, name
, None, None)
200 assert not c_type
or isinstance(c_type
, str)
201 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
203 self
._json
_type
_name
= json_type
204 self
._c
_type
_name
= c_type
210 return self
._c
_type
_name
212 def c_param_type(self
):
213 if self
.name
== 'str':
214 return 'const ' + self
._c
_type
_name
215 return self
._c
_type
_name
218 return self
._json
_type
_name
221 return self
.json_type()
223 def visit(self
, visitor
):
224 QAPISchemaType
.visit(self
, visitor
)
225 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
228 class QAPISchemaEnumType(QAPISchemaType
):
231 def __init__(self
, name
, info
, doc
, ifcond
, members
, prefix
):
232 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
234 assert isinstance(m
, QAPISchemaEnumMember
)
235 m
.set_defined_in(name
)
236 assert prefix
is None or isinstance(prefix
, str)
237 self
.members
= members
240 def check(self
, schema
):
241 QAPISchemaType
.check(self
, schema
)
243 for m
in self
.members
:
244 m
.check_clash(self
.info
, seen
)
246 def connect_doc(self
, doc
=None):
247 doc
= doc
or self
.doc
249 for m
in self
.members
:
250 doc
.connect_member(m
)
252 def is_implicit(self
):
253 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
254 return self
.name
.endswith('Kind') or self
.name
== 'QType'
257 return c_name(self
.name
)
259 def member_names(self
):
260 return [m
.name
for m
in self
.members
]
265 def visit(self
, visitor
):
266 QAPISchemaType
.visit(self
, visitor
)
267 visitor
.visit_enum_type(self
.name
, self
.info
, self
.ifcond
,
268 self
.members
, self
.prefix
)
271 class QAPISchemaArrayType(QAPISchemaType
):
274 def __init__(self
, name
, info
, element_type
):
275 QAPISchemaType
.__init
__(self
, name
, info
, None, None)
276 assert isinstance(element_type
, str)
277 self
._element
_type
_name
= element_type
278 self
.element_type
= None
280 def check(self
, schema
):
281 QAPISchemaType
.check(self
, schema
)
282 self
.element_type
= schema
.resolve_type(
283 self
._element
_type
_name
, self
.info
,
284 self
.info
and self
.info
.defn_meta
)
285 assert not isinstance(self
.element_type
, QAPISchemaArrayType
)
287 def set_module(self
, schema
):
288 self
._set
_module
(schema
, self
.element_type
.info
)
293 return self
.element_type
.ifcond
295 def is_implicit(self
):
299 return c_name(self
.name
) + pointer_suffix
305 elt_doc_type
= self
.element_type
.doc_type()
308 return 'array of ' + elt_doc_type
310 def visit(self
, visitor
):
311 QAPISchemaType
.visit(self
, visitor
)
312 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
317 return "%s type ['%s']" % (self
.meta
, self
._element
_type
_name
)
320 class QAPISchemaObjectType(QAPISchemaType
):
321 def __init__(self
, name
, info
, doc
, ifcond
,
322 base
, local_members
, variants
, features
):
323 # struct has local_members, optional base, and no variants
324 # flat union has base, variants, and no local_members
325 # simple union has local_members, variants, and no base
326 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
, features
)
327 self
.meta
= 'union' if variants
else 'struct'
328 assert base
is None or isinstance(base
, str)
329 for m
in local_members
:
330 assert isinstance(m
, QAPISchemaObjectTypeMember
)
331 m
.set_defined_in(name
)
332 if variants
is not None:
333 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
334 variants
.set_defined_in(name
)
335 self
._base
_name
= base
337 self
.local_members
= local_members
338 self
.variants
= variants
341 def check(self
, schema
):
342 # This calls another type T's .check() exactly when the C
343 # struct emitted by gen_object() contains that T's C struct
344 # (pointers don't count).
345 if self
.members
is not None:
346 # A previous .check() completed: nothing to do
349 # Recursed: C struct contains itself
350 raise QAPISemError(self
.info
,
351 "object %s contains itself" % self
.name
)
353 QAPISchemaType
.check(self
, schema
)
354 assert self
._checked
and self
.members
is None
358 self
.base
= schema
.resolve_type(self
._base
_name
, self
.info
,
360 if (not isinstance(self
.base
, QAPISchemaObjectType
)
361 or self
.base
.variants
):
364 "'base' requires a struct type, %s isn't"
365 % self
.base
.describe())
366 self
.base
.check(schema
)
367 self
.base
.check_clash(self
.info
, seen
)
368 for m
in self
.local_members
:
370 m
.check_clash(self
.info
, seen
)
371 members
= seen
.values()
374 self
.variants
.check(schema
, seen
)
375 self
.variants
.check_clash(self
.info
, seen
)
377 self
.members
= members
# mark completed
379 # Check that the members of this type do not cause duplicate JSON members,
380 # and update seen to track the members seen so far. Report any errors
381 # on behalf of info, which is not necessarily self.info
382 def check_clash(self
, info
, seen
):
384 assert not self
.variants
# not implemented
385 for m
in self
.members
:
386 m
.check_clash(info
, seen
)
388 def connect_doc(self
, doc
=None):
389 doc
= doc
or self
.doc
391 if self
.base
and self
.base
.is_implicit():
392 self
.base
.connect_doc(doc
)
393 for m
in self
.local_members
:
394 doc
.connect_member(m
)
399 if isinstance(self
._ifcond
, QAPISchemaType
):
400 # Simple union wrapper type inherits from wrapped type;
401 # see _make_implicit_object_type()
402 return self
._ifcond
.ifcond
405 def is_implicit(self
):
406 # See QAPISchema._make_implicit_object_type(), as well as
408 return self
.name
.startswith('q_')
411 assert self
.members
is not None
412 return not self
.members
and not self
.variants
415 assert self
.name
!= 'q_empty'
416 return QAPISchemaType
.c_name(self
)
419 assert not self
.is_implicit()
420 return c_name(self
.name
) + pointer_suffix
422 def c_unboxed_type(self
):
423 return c_name(self
.name
)
428 def visit(self
, visitor
):
429 QAPISchemaType
.visit(self
, visitor
)
430 visitor
.visit_object_type(self
.name
, self
.info
, self
.ifcond
,
431 self
.base
, self
.local_members
, self
.variants
,
433 visitor
.visit_object_type_flat(self
.name
, self
.info
, self
.ifcond
,
434 self
.members
, self
.variants
,
438 class QAPISchemaMember(object):
439 """ Represents object members, enum members and features """
442 def __init__(self
, name
, info
, ifcond
=None):
443 assert isinstance(name
, str)
446 self
.ifcond
= ifcond
or []
447 self
.defined_in
= None
449 def set_defined_in(self
, name
):
450 assert not self
.defined_in
451 self
.defined_in
= name
453 def check_clash(self
, info
, seen
):
454 cname
= c_name(self
.name
)
458 "%s collides with %s"
459 % (self
.describe(info
), seen
[cname
].describe(info
)))
462 def describe(self
, info
):
464 defined_in
= self
.defined_in
467 if defined_in
.startswith('q_obj_'):
468 # See QAPISchema._make_implicit_object_type() - reverse the
469 # mapping there to create a nice human-readable description
470 defined_in
= defined_in
[6:]
471 if defined_in
.endswith('-arg'):
472 # Implicit type created for a command's dict 'data'
473 assert role
== 'member'
475 elif defined_in
.endswith('-base'):
476 # Implicit type created for a flat union's dict 'base'
477 role
= 'base ' + role
479 # Implicit type created for a simple union's branch
480 assert defined_in
.endswith('-wrapper')
481 # Unreachable and not implemented
483 elif defined_in
.endswith('Kind'):
484 # See QAPISchema._make_implicit_enum_type()
485 # Implicit enum created for simple union's branches
486 assert role
== 'value'
488 elif defined_in
!= info
.defn_name
:
489 return "%s '%s' of type '%s'" % (role
, self
.name
, defined_in
)
490 return "%s '%s'" % (role
, self
.name
)
493 class QAPISchemaEnumMember(QAPISchemaMember
):
497 class QAPISchemaFeature(QAPISchemaMember
):
501 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
502 def __init__(self
, name
, info
, typ
, optional
, ifcond
=None):
503 QAPISchemaMember
.__init
__(self
, name
, info
, ifcond
)
504 assert isinstance(typ
, str)
505 assert isinstance(optional
, bool)
506 self
._type
_name
= typ
508 self
.optional
= optional
510 def check(self
, schema
):
511 assert self
.defined_in
512 self
.type = schema
.resolve_type(self
._type
_name
, self
.info
,
516 class QAPISchemaObjectTypeVariants(object):
517 def __init__(self
, tag_name
, info
, tag_member
, variants
):
518 # Flat unions pass tag_name but not tag_member.
519 # Simple unions and alternates pass tag_member but not tag_name.
520 # After check(), tag_member is always set, and tag_name remains
521 # a reliable witness of being used by a flat union.
522 assert bool(tag_member
) != bool(tag_name
)
523 assert (isinstance(tag_name
, str) or
524 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
526 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
527 self
._tag
_name
= tag_name
529 self
.tag_member
= tag_member
530 self
.variants
= variants
532 def set_defined_in(self
, name
):
533 for v
in self
.variants
:
534 v
.set_defined_in(name
)
536 def check(self
, schema
, seen
):
537 if not self
.tag_member
: # flat union
538 self
.tag_member
= seen
.get(c_name(self
._tag
_name
))
540 # Pointing to the base type when not implicit would be
541 # nice, but we don't know it here
542 if not self
.tag_member
or self
._tag
_name
!= self
.tag_member
.name
:
545 "discriminator '%s' is not a member of %s"
546 % (self
._tag
_name
, base
))
548 base_type
= schema
.lookup_type(self
.tag_member
.defined_in
)
550 if not base_type
.is_implicit():
551 base
= "base type '%s'" % self
.tag_member
.defined_in
552 if not isinstance(self
.tag_member
.type, QAPISchemaEnumType
):
555 "discriminator member '%s' of %s must be of enum type"
556 % (self
._tag
_name
, base
))
557 if self
.tag_member
.optional
:
560 "discriminator member '%s' of %s must not be optional"
561 % (self
._tag
_name
, base
))
562 if self
.tag_member
.ifcond
:
565 "discriminator member '%s' of %s must not be conditional"
566 % (self
._tag
_name
, base
))
568 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
569 assert not self
.tag_member
.optional
570 assert self
.tag_member
.ifcond
== []
571 if self
._tag
_name
: # flat union
572 # branches that are not explicitly covered get an empty type
573 cases
= set([v
.name
for v
in self
.variants
])
574 for m
in self
.tag_member
.type.members
:
575 if m
.name
not in cases
:
576 v
= QAPISchemaObjectTypeVariant(m
.name
, self
.info
,
578 v
.set_defined_in(self
.tag_member
.defined_in
)
579 self
.variants
.append(v
)
580 if not self
.variants
:
581 raise QAPISemError(self
.info
, "union has no branches")
582 for v
in self
.variants
:
584 # Union names must match enum values; alternate names are
585 # checked separately. Use 'seen' to tell the two apart.
587 if v
.name
not in self
.tag_member
.type.member_names():
590 "branch '%s' is not a value of %s"
591 % (v
.name
, self
.tag_member
.type.describe()))
592 if (not isinstance(v
.type, QAPISchemaObjectType
)
597 % (v
.describe(self
.info
), v
.type.describe()))
600 def check_clash(self
, info
, seen
):
601 for v
in self
.variants
:
602 # Reset seen map for each variant, since qapi names from one
603 # branch do not affect another branch
604 v
.type.check_clash(info
, dict(seen
))
607 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
610 def __init__(self
, name
, info
, typ
, ifcond
=None):
611 QAPISchemaObjectTypeMember
.__init
__(self
, name
, info
, typ
,
615 class QAPISchemaAlternateType(QAPISchemaType
):
618 def __init__(self
, name
, info
, doc
, ifcond
, variants
):
619 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
620 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
621 assert variants
.tag_member
622 variants
.set_defined_in(name
)
623 variants
.tag_member
.set_defined_in(self
.name
)
624 self
.variants
= variants
626 def check(self
, schema
):
627 QAPISchemaType
.check(self
, schema
)
628 self
.variants
.tag_member
.check(schema
)
629 # Not calling self.variants.check_clash(), because there's nothing
631 self
.variants
.check(schema
, {})
632 # Alternate branch names have no relation to the tag enum values;
633 # so we have to check for potential name collisions ourselves.
636 for v
in self
.variants
.variants
:
637 v
.check_clash(self
.info
, seen
)
638 qtype
= v
.type.alternate_qtype()
643 % (v
.describe(self
.info
), v
.type.describe()))
644 conflicting
= set([qtype
])
645 if qtype
== 'QTYPE_QSTRING':
646 if isinstance(v
.type, QAPISchemaEnumType
):
647 for m
in v
.type.members
:
648 if m
.name
in ['on', 'off']:
649 conflicting
.add('QTYPE_QBOOL')
650 if re
.match(r
'[-+0-9.]', m
.name
):
651 # lazy, could be tightened
652 conflicting
.add('QTYPE_QNUM')
654 conflicting
.add('QTYPE_QNUM')
655 conflicting
.add('QTYPE_QBOOL')
656 for qt
in conflicting
:
660 "%s can't be distinguished from '%s'"
661 % (v
.describe(self
.info
), types_seen
[qt
]))
662 types_seen
[qt
] = v
.name
664 def connect_doc(self
, doc
=None):
665 doc
= doc
or self
.doc
667 for v
in self
.variants
.variants
:
668 doc
.connect_member(v
)
671 return c_name(self
.name
) + pointer_suffix
676 def visit(self
, visitor
):
677 QAPISchemaType
.visit(self
, visitor
)
678 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.ifcond
,
682 class QAPISchemaCommand(QAPISchemaEntity
):
685 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, ret_type
,
686 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
,
688 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
, features
)
689 assert not arg_type
or isinstance(arg_type
, str)
690 assert not ret_type
or isinstance(ret_type
, str)
691 self
._arg
_type
_name
= arg_type
693 self
._ret
_type
_name
= ret_type
696 self
.success_response
= success_response
698 self
.allow_oob
= allow_oob
699 self
.allow_preconfig
= allow_preconfig
701 def check(self
, schema
):
702 QAPISchemaEntity
.check(self
, schema
)
703 if self
._arg
_type
_name
:
704 self
.arg_type
= schema
.resolve_type(
705 self
._arg
_type
_name
, self
.info
, "command's 'data'")
706 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
709 "command's 'data' cannot take %s"
710 % self
.arg_type
.describe())
711 if self
.arg_type
.variants
and not self
.boxed
:
714 "command's 'data' can take %s only with 'boxed': true"
715 % self
.arg_type
.describe())
716 if self
._ret
_type
_name
:
717 self
.ret_type
= schema
.resolve_type(
718 self
._ret
_type
_name
, self
.info
, "command's 'returns'")
719 if self
.name
not in self
.info
.pragma
.returns_whitelist
:
721 if isinstance(typ
, QAPISchemaArrayType
):
722 typ
= self
.ret_type
.element_type
724 if not isinstance(typ
, QAPISchemaObjectType
):
727 "command's 'returns' cannot take %s"
728 % self
.ret_type
.describe())
730 def connect_doc(self
, doc
=None):
731 doc
= doc
or self
.doc
733 if self
.arg_type
and self
.arg_type
.is_implicit():
734 self
.arg_type
.connect_doc(doc
)
736 def visit(self
, visitor
):
737 QAPISchemaEntity
.visit(self
, visitor
)
738 visitor
.visit_command(self
.name
, self
.info
, self
.ifcond
,
739 self
.arg_type
, self
.ret_type
,
740 self
.gen
, self
.success_response
,
741 self
.boxed
, self
.allow_oob
,
742 self
.allow_preconfig
,
746 class QAPISchemaEvent(QAPISchemaEntity
):
749 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, boxed
):
750 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
751 assert not arg_type
or isinstance(arg_type
, str)
752 self
._arg
_type
_name
= arg_type
756 def check(self
, schema
):
757 QAPISchemaEntity
.check(self
, schema
)
758 if self
._arg
_type
_name
:
759 self
.arg_type
= schema
.resolve_type(
760 self
._arg
_type
_name
, self
.info
, "event's 'data'")
761 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
764 "event's 'data' cannot take %s"
765 % self
.arg_type
.describe())
766 if self
.arg_type
.variants
and not self
.boxed
:
769 "event's 'data' can take %s only with 'boxed': true"
770 % self
.arg_type
.describe())
772 def connect_doc(self
, doc
=None):
773 doc
= doc
or self
.doc
775 if self
.arg_type
and self
.arg_type
.is_implicit():
776 self
.arg_type
.connect_doc(doc
)
778 def visit(self
, visitor
):
779 QAPISchemaEntity
.visit(self
, visitor
)
780 visitor
.visit_event(self
.name
, self
.info
, self
.ifcond
,
781 self
.arg_type
, self
.boxed
)
784 class QAPISchema(object):
785 def __init__(self
, fname
):
787 parser
= QAPISchemaParser(fname
)
788 exprs
= check_exprs(parser
.exprs
)
789 self
.docs
= parser
.docs
790 self
._entity
_list
= []
791 self
._entity
_dict
= {}
792 self
._module
_dict
= {}
793 self
._schema
_dir
= os
.path
.dirname(fname
)
794 self
._make
_module
(None) # built-ins
795 self
._make
_module
(fname
)
796 self
._predefining
= True
797 self
._def
_predefineds
()
798 self
._predefining
= False
799 self
._def
_exprs
(exprs
)
802 def _def_entity(self
, ent
):
803 # Only the predefined types are allowed to not have info
804 assert ent
.info
or self
._predefining
805 self
._entity
_list
.append(ent
)
808 # TODO reject names that differ only in '_' vs. '.' vs. '-',
809 # because they're liable to clash in generated C.
810 other_ent
= self
._entity
_dict
.get(ent
.name
)
813 where
= QAPIError(other_ent
.info
, None, "previous definition")
816 "'%s' is already defined\n%s" % (ent
.name
, where
))
818 ent
.info
, "%s is already defined" % other_ent
.describe())
819 self
._entity
_dict
[ent
.name
] = ent
821 def lookup_entity(self
, name
, typ
=None):
822 ent
= self
._entity
_dict
.get(name
)
823 if typ
and not isinstance(ent
, typ
):
827 def lookup_type(self
, name
):
828 return self
.lookup_entity(name
, QAPISchemaType
)
830 def resolve_type(self
, name
, info
, what
):
831 typ
= self
.lookup_type(name
)
836 info
, "%s uses unknown type '%s'" % (what
, name
))
839 def _module_name(self
, fname
):
842 return os
.path
.relpath(fname
, self
._schema
_dir
)
844 def _make_module(self
, fname
):
845 name
= self
._module
_name
(fname
)
846 if not name
in self
._module
_dict
:
847 self
._module
_dict
[name
] = QAPISchemaModule(name
)
848 return self
._module
_dict
[name
]
850 def module_by_fname(self
, fname
):
851 name
= self
._module
_name
(fname
)
852 assert name
in self
._module
_dict
853 return self
._module
_dict
[name
]
855 def _def_include(self
, expr
, info
, doc
):
856 include
= expr
['include']
858 self
._def
_entity
(QAPISchemaInclude(self
._make
_module
(include
), info
))
860 def _def_builtin_type(self
, name
, json_type
, c_type
):
861 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
862 # Instantiating only the arrays that are actually used would
863 # be nice, but we can't as long as their generated code
864 # (qapi-builtin-types.[ch]) may be shared by some other
866 self
._make
_array
_type
(name
, None)
868 def _def_predefineds(self
):
869 for t
in [('str', 'string', 'char' + pointer_suffix
),
870 ('number', 'number', 'double'),
871 ('int', 'int', 'int64_t'),
872 ('int8', 'int', 'int8_t'),
873 ('int16', 'int', 'int16_t'),
874 ('int32', 'int', 'int32_t'),
875 ('int64', 'int', 'int64_t'),
876 ('uint8', 'int', 'uint8_t'),
877 ('uint16', 'int', 'uint16_t'),
878 ('uint32', 'int', 'uint32_t'),
879 ('uint64', 'int', 'uint64_t'),
880 ('size', 'int', 'uint64_t'),
881 ('bool', 'boolean', 'bool'),
882 ('any', 'value', 'QObject' + pointer_suffix
),
883 ('null', 'null', 'QNull' + pointer_suffix
)]:
884 self
._def
_builtin
_type
(*t
)
885 self
.the_empty_object_type
= QAPISchemaObjectType(
886 'q_empty', None, None, None, None, [], None, [])
887 self
._def
_entity
(self
.the_empty_object_type
)
889 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
891 qtype_values
= self
._make
_enum
_members
(
892 [{'name': n
} for n
in qtypes
], None)
894 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None,
895 qtype_values
, 'QTYPE'))
897 def _make_features(self
, features
, info
):
898 return [QAPISchemaFeature(f
['name'], info
, f
.get('if'))
901 def _make_enum_members(self
, values
, info
):
902 return [QAPISchemaEnumMember(v
['name'], info
, v
.get('if'))
905 def _make_implicit_enum_type(self
, name
, info
, ifcond
, values
):
906 # See also QAPISchemaObjectTypeMember.describe()
907 name
= name
+ 'Kind' # reserved by check_defn_name_str()
908 self
._def
_entity
(QAPISchemaEnumType(
909 name
, info
, None, ifcond
, self
._make
_enum
_members
(values
, info
),
913 def _make_array_type(self
, element_type
, info
):
914 name
= element_type
+ 'List' # reserved by check_defn_name_str()
915 if not self
.lookup_type(name
):
916 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
919 def _make_implicit_object_type(self
, name
, info
, ifcond
, role
, members
):
922 # See also QAPISchemaObjectTypeMember.describe()
923 name
= 'q_obj_%s-%s' % (name
, role
)
924 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
926 # The implicit object type has multiple users. This can
927 # happen only for simple unions' implicit wrapper types.
928 # Its ifcond should be the disjunction of its user's
929 # ifconds. Not implemented. Instead, we always pass the
930 # wrapped type's ifcond, which is trivially the same for all
931 # users. It's also necessary for the wrapper to compile.
932 # But it's not tight: the disjunction need not imply it. We
933 # may end up compiling useless wrapper types.
934 # TODO kill simple unions or implement the disjunction
935 assert (ifcond
or []) == typ
._ifcond
# pylint: disable=protected-access
937 self
._def
_entity
(QAPISchemaObjectType(name
, info
, None, ifcond
,
938 None, members
, None, []))
941 def _def_enum_type(self
, expr
, info
, doc
):
944 prefix
= expr
.get('prefix')
945 ifcond
= expr
.get('if')
946 self
._def
_entity
(QAPISchemaEnumType(
947 name
, info
, doc
, ifcond
,
948 self
._make
_enum
_members
(data
, info
), prefix
))
950 def _make_member(self
, name
, typ
, ifcond
, info
):
952 if name
.startswith('*'):
955 if isinstance(typ
, list):
957 typ
= self
._make
_array
_type
(typ
[0], info
)
958 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
)
960 def _make_members(self
, data
, info
):
961 return [self
._make
_member
(key
, value
['type'], value
.get('if'), info
)
962 for (key
, value
) in data
.items()]
964 def _def_struct_type(self
, expr
, info
, doc
):
965 name
= expr
['struct']
966 base
= expr
.get('base')
968 ifcond
= expr
.get('if')
969 features
= expr
.get('features', [])
970 self
._def
_entity
(QAPISchemaObjectType(
971 name
, info
, doc
, ifcond
, base
,
972 self
._make
_members
(data
, info
),
974 self
._make
_features
(features
, info
)))
976 def _make_variant(self
, case
, typ
, ifcond
, info
):
977 return QAPISchemaObjectTypeVariant(case
, info
, typ
, ifcond
)
979 def _make_simple_variant(self
, case
, typ
, ifcond
, info
):
980 if isinstance(typ
, list):
982 typ
= self
._make
_array
_type
(typ
[0], info
)
983 typ
= self
._make
_implicit
_object
_type
(
984 typ
, info
, self
.lookup_type(typ
),
985 'wrapper', [self
._make
_member
('data', typ
, None, info
)])
986 return QAPISchemaObjectTypeVariant(case
, info
, typ
, ifcond
)
988 def _def_union_type(self
, expr
, info
, doc
):
991 base
= expr
.get('base')
992 ifcond
= expr
.get('if')
993 tag_name
= expr
.get('discriminator')
995 if isinstance(base
, dict):
996 base
= self
._make
_implicit
_object
_type
(
998 'base', self
._make
_members
(base
, info
))
1000 variants
= [self
._make
_variant
(key
, value
['type'],
1001 value
.get('if'), info
)
1002 for (key
, value
) in data
.items()]
1005 variants
= [self
._make
_simple
_variant
(key
, value
['type'],
1006 value
.get('if'), info
)
1007 for (key
, value
) in data
.items()]
1008 enum
= [{'name': v
.name
, 'if': v
.ifcond
} for v
in variants
]
1009 typ
= self
._make
_implicit
_enum
_type
(name
, info
, ifcond
, enum
)
1010 tag_member
= QAPISchemaObjectTypeMember('type', info
, typ
, False)
1011 members
= [tag_member
]
1013 QAPISchemaObjectType(name
, info
, doc
, ifcond
, base
, members
,
1014 QAPISchemaObjectTypeVariants(
1015 tag_name
, info
, tag_member
, variants
),
1018 def _def_alternate_type(self
, expr
, info
, doc
):
1019 name
= expr
['alternate']
1021 ifcond
= expr
.get('if')
1022 variants
= [self
._make
_variant
(key
, value
['type'], value
.get('if'),
1024 for (key
, value
) in data
.items()]
1025 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
1027 QAPISchemaAlternateType(name
, info
, doc
, ifcond
,
1028 QAPISchemaObjectTypeVariants(
1029 None, info
, tag_member
, variants
)))
1031 def _def_command(self
, expr
, info
, doc
):
1032 name
= expr
['command']
1033 data
= expr
.get('data')
1034 rets
= expr
.get('returns')
1035 gen
= expr
.get('gen', True)
1036 success_response
= expr
.get('success-response', True)
1037 boxed
= expr
.get('boxed', False)
1038 allow_oob
= expr
.get('allow-oob', False)
1039 allow_preconfig
= expr
.get('allow-preconfig', False)
1040 ifcond
= expr
.get('if')
1041 features
= expr
.get('features', [])
1042 if isinstance(data
, OrderedDict
):
1043 data
= self
._make
_implicit
_object
_type
(
1044 name
, info
, ifcond
, 'arg', self
._make
_members
(data
, info
))
1045 if isinstance(rets
, list):
1046 assert len(rets
) == 1
1047 rets
= self
._make
_array
_type
(rets
[0], info
)
1048 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, ifcond
, data
, rets
,
1049 gen
, success_response
,
1050 boxed
, allow_oob
, allow_preconfig
,
1051 self
._make
_features
(features
, info
)))
1053 def _def_event(self
, expr
, info
, doc
):
1054 name
= expr
['event']
1055 data
= expr
.get('data')
1056 boxed
= expr
.get('boxed', False)
1057 ifcond
= expr
.get('if')
1058 if isinstance(data
, OrderedDict
):
1059 data
= self
._make
_implicit
_object
_type
(
1060 name
, info
, ifcond
, 'arg', self
._make
_members
(data
, info
))
1061 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, ifcond
, data
, boxed
))
1063 def _def_exprs(self
, exprs
):
1064 for expr_elem
in exprs
:
1065 expr
= expr_elem
['expr']
1066 info
= expr_elem
['info']
1067 doc
= expr_elem
.get('doc')
1069 self
._def
_enum
_type
(expr
, info
, doc
)
1070 elif 'struct' in expr
:
1071 self
._def
_struct
_type
(expr
, info
, doc
)
1072 elif 'union' in expr
:
1073 self
._def
_union
_type
(expr
, info
, doc
)
1074 elif 'alternate' in expr
:
1075 self
._def
_alternate
_type
(expr
, info
, doc
)
1076 elif 'command' in expr
:
1077 self
._def
_command
(expr
, info
, doc
)
1078 elif 'event' in expr
:
1079 self
._def
_event
(expr
, info
, doc
)
1080 elif 'include' in expr
:
1081 self
._def
_include
(expr
, info
, doc
)
1086 for ent
in self
._entity
_list
:
1090 for ent
in self
._entity
_list
:
1091 ent
.set_module(self
)
1093 def visit(self
, visitor
):
1094 visitor
.visit_begin(self
)
1096 for entity
in self
._entity
_list
:
1097 if visitor
.visit_needed(entity
):
1098 if entity
.module
!= module
:
1099 module
= entity
.module
1100 visitor
.visit_module(module
.name
)
1101 entity
.visit(visitor
)