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
, QAPISemError
23 from qapi
.expr
import check_exprs
24 from qapi
.parser
import QAPISchemaParser
27 class QAPISchemaEntity
:
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
)
58 def connect_doc(self
, doc
=None):
61 for f
in self
.features
:
62 doc
.connect_feature(f
)
68 def _set_module(self
, schema
, info
):
70 self
._module
= schema
.module_by_fname(info
and info
.fname
)
71 self
._module
.add_entity(self
)
73 def set_module(self
, schema
):
74 self
._set
_module
(schema
, self
.info
)
81 def is_implicit(self
):
84 def visit(self
, visitor
):
89 return "%s '%s'" % (self
.meta
, self
.name
)
92 class QAPISchemaVisitor
:
93 def visit_begin(self
, schema
):
99 def visit_module(self
, name
):
102 def visit_needed(self
, entity
):
103 # Default to visiting everything
106 def visit_include(self
, name
, info
):
109 def visit_builtin_type(self
, name
, info
, json_type
):
112 def visit_enum_type(self
, name
, info
, ifcond
, features
, members
, prefix
):
115 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
118 def visit_object_type(self
, name
, info
, ifcond
, features
,
119 base
, members
, variants
):
122 def visit_object_type_flat(self
, name
, info
, ifcond
, features
,
126 def visit_alternate_type(self
, name
, info
, ifcond
, features
, variants
):
129 def visit_command(self
, name
, info
, ifcond
, features
,
130 arg_type
, ret_type
, gen
, success_response
, boxed
,
131 allow_oob
, allow_preconfig
):
134 def visit_event(self
, name
, info
, ifcond
, features
, arg_type
, boxed
):
138 class QAPISchemaModule
:
139 def __init__(self
, name
):
141 self
._entity
_list
= []
143 def add_entity(self
, ent
):
144 self
._entity
_list
.append(ent
)
146 def visit(self
, visitor
):
147 visitor
.visit_module(self
.name
)
148 for entity
in self
._entity
_list
:
149 if visitor
.visit_needed(entity
):
150 entity
.visit(visitor
)
153 class QAPISchemaInclude(QAPISchemaEntity
):
154 def __init__(self
, sub_module
, info
):
155 super().__init
__(None, info
, None)
156 self
._sub
_module
= sub_module
158 def visit(self
, visitor
):
159 super().visit(visitor
)
160 visitor
.visit_include(self
._sub
_module
.name
, self
.info
)
163 class QAPISchemaType(QAPISchemaEntity
):
164 # Return the C type for common use.
165 # For the types we commonly box, this is a pointer type.
169 # Return the C type to be used in a parameter list.
170 def c_param_type(self
):
173 # Return the C type to be used where we suppress boxing.
174 def c_unboxed_type(self
):
180 def alternate_qtype(self
):
182 'null': 'QTYPE_QNULL',
183 'string': 'QTYPE_QSTRING',
184 'number': 'QTYPE_QNUM',
186 'boolean': 'QTYPE_QBOOL',
187 'object': 'QTYPE_QDICT'
189 return json2qtype
.get(self
.json_type())
192 if self
.is_implicit():
196 def check(self
, schema
):
197 QAPISchemaEntity
.check(self
, schema
)
198 if 'deprecated' in [f
.name
for f
in self
.features
]:
200 self
.info
, "feature 'deprecated' is not supported for types")
204 return "%s type '%s'" % (self
.meta
, self
.name
)
207 class QAPISchemaBuiltinType(QAPISchemaType
):
210 def __init__(self
, name
, json_type
, c_type
):
211 super().__init
__(name
, None, None)
212 assert not c_type
or isinstance(c_type
, str)
213 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
215 self
._json
_type
_name
= json_type
216 self
._c
_type
_name
= c_type
222 return self
._c
_type
_name
224 def c_param_type(self
):
225 if self
.name
== 'str':
226 return 'const ' + self
._c
_type
_name
227 return self
._c
_type
_name
230 return self
._json
_type
_name
233 return self
.json_type()
235 def visit(self
, visitor
):
236 super().visit(visitor
)
237 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
240 class QAPISchemaEnumType(QAPISchemaType
):
243 def __init__(self
, name
, info
, doc
, ifcond
, features
, members
, prefix
):
244 super().__init
__(name
, info
, doc
, ifcond
, features
)
246 assert isinstance(m
, QAPISchemaEnumMember
)
247 m
.set_defined_in(name
)
248 assert prefix
is None or isinstance(prefix
, str)
249 self
.members
= members
252 def check(self
, schema
):
253 super().check(schema
)
255 for m
in self
.members
:
256 m
.check_clash(self
.info
, seen
)
258 def connect_doc(self
, doc
=None):
259 super().connect_doc(doc
)
260 doc
= doc
or self
.doc
261 for m
in self
.members
:
264 def is_implicit(self
):
265 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
266 return self
.name
.endswith('Kind') or self
.name
== 'QType'
269 return c_name(self
.name
)
271 def member_names(self
):
272 return [m
.name
for m
in self
.members
]
277 def visit(self
, visitor
):
278 super().visit(visitor
)
279 visitor
.visit_enum_type(
280 self
.name
, self
.info
, self
.ifcond
, self
.features
,
281 self
.members
, self
.prefix
)
284 class QAPISchemaArrayType(QAPISchemaType
):
287 def __init__(self
, name
, info
, element_type
):
288 super().__init
__(name
, info
, None)
289 assert isinstance(element_type
, str)
290 self
._element
_type
_name
= element_type
291 self
.element_type
= None
293 def check(self
, schema
):
294 super().check(schema
)
295 self
.element_type
= schema
.resolve_type(
296 self
._element
_type
_name
, self
.info
,
297 self
.info
and self
.info
.defn_meta
)
298 assert not isinstance(self
.element_type
, QAPISchemaArrayType
)
300 def set_module(self
, schema
):
301 self
._set
_module
(schema
, self
.element_type
.info
)
306 return self
.element_type
.ifcond
308 def is_implicit(self
):
312 return c_name(self
.name
) + pointer_suffix
318 elt_doc_type
= self
.element_type
.doc_type()
321 return 'array of ' + elt_doc_type
323 def visit(self
, visitor
):
324 super().visit(visitor
)
325 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
330 return "%s type ['%s']" % (self
.meta
, self
._element
_type
_name
)
333 class QAPISchemaObjectType(QAPISchemaType
):
334 def __init__(self
, name
, info
, doc
, ifcond
, features
,
335 base
, local_members
, variants
):
336 # struct has local_members, optional base, and no variants
337 # flat union has base, variants, and no local_members
338 # simple union has local_members, variants, and no base
339 super().__init
__(name
, info
, doc
, ifcond
, features
)
340 self
.meta
= 'union' if variants
else 'struct'
341 assert base
is None or isinstance(base
, str)
342 for m
in local_members
:
343 assert isinstance(m
, QAPISchemaObjectTypeMember
)
344 m
.set_defined_in(name
)
345 if variants
is not None:
346 assert isinstance(variants
, QAPISchemaVariants
)
347 variants
.set_defined_in(name
)
348 self
._base
_name
= base
350 self
.local_members
= local_members
351 self
.variants
= variants
354 def check(self
, schema
):
355 # This calls another type T's .check() exactly when the C
356 # struct emitted by gen_object() contains that T's C struct
357 # (pointers don't count).
358 if self
.members
is not None:
359 # A previous .check() completed: nothing to do
362 # Recursed: C struct contains itself
363 raise QAPISemError(self
.info
,
364 "object %s contains itself" % self
.name
)
366 super().check(schema
)
367 assert self
._checked
and self
.members
is None
371 self
.base
= schema
.resolve_type(self
._base
_name
, self
.info
,
373 if (not isinstance(self
.base
, QAPISchemaObjectType
)
374 or self
.base
.variants
):
377 "'base' requires a struct type, %s isn't"
378 % self
.base
.describe())
379 self
.base
.check(schema
)
380 self
.base
.check_clash(self
.info
, seen
)
381 for m
in self
.local_members
:
383 m
.check_clash(self
.info
, seen
)
384 members
= seen
.values()
387 self
.variants
.check(schema
, seen
)
388 self
.variants
.check_clash(self
.info
, seen
)
390 self
.members
= members
# mark completed
392 # Check that the members of this type do not cause duplicate JSON members,
393 # and update seen to track the members seen so far. Report any errors
394 # on behalf of info, which is not necessarily self.info
395 def check_clash(self
, info
, seen
):
397 assert not self
.variants
# not implemented
398 for m
in self
.members
:
399 m
.check_clash(info
, seen
)
401 def connect_doc(self
, doc
=None):
402 super().connect_doc(doc
)
403 doc
= doc
or self
.doc
404 if self
.base
and self
.base
.is_implicit():
405 self
.base
.connect_doc(doc
)
406 for m
in self
.local_members
:
412 if isinstance(self
._ifcond
, QAPISchemaType
):
413 # Simple union wrapper type inherits from wrapped type;
414 # see _make_implicit_object_type()
415 return self
._ifcond
.ifcond
418 def is_implicit(self
):
419 # See QAPISchema._make_implicit_object_type(), as well as
421 return self
.name
.startswith('q_')
424 assert self
.members
is not None
425 return not self
.members
and not self
.variants
428 assert self
.name
!= 'q_empty'
429 return super().c_name()
432 assert not self
.is_implicit()
433 return c_name(self
.name
) + pointer_suffix
435 def c_unboxed_type(self
):
436 return c_name(self
.name
)
441 def visit(self
, visitor
):
442 super().visit(visitor
)
443 visitor
.visit_object_type(
444 self
.name
, self
.info
, self
.ifcond
, self
.features
,
445 self
.base
, self
.local_members
, self
.variants
)
446 visitor
.visit_object_type_flat(
447 self
.name
, self
.info
, self
.ifcond
, self
.features
,
448 self
.members
, self
.variants
)
451 class QAPISchemaAlternateType(QAPISchemaType
):
454 def __init__(self
, name
, info
, doc
, ifcond
, features
, variants
):
455 super().__init
__(name
, info
, doc
, ifcond
, features
)
456 assert isinstance(variants
, QAPISchemaVariants
)
457 assert variants
.tag_member
458 variants
.set_defined_in(name
)
459 variants
.tag_member
.set_defined_in(self
.name
)
460 self
.variants
= variants
462 def check(self
, schema
):
463 super().check(schema
)
464 self
.variants
.tag_member
.check(schema
)
465 # Not calling self.variants.check_clash(), because there's nothing
467 self
.variants
.check(schema
, {})
468 # Alternate branch names have no relation to the tag enum values;
469 # so we have to check for potential name collisions ourselves.
472 for v
in self
.variants
.variants
:
473 v
.check_clash(self
.info
, seen
)
474 qtype
= v
.type.alternate_qtype()
479 % (v
.describe(self
.info
), v
.type.describe()))
480 conflicting
= set([qtype
])
481 if qtype
== 'QTYPE_QSTRING':
482 if isinstance(v
.type, QAPISchemaEnumType
):
483 for m
in v
.type.members
:
484 if m
.name
in ['on', 'off']:
485 conflicting
.add('QTYPE_QBOOL')
486 if re
.match(r
'[-+0-9.]', m
.name
):
487 # lazy, could be tightened
488 conflicting
.add('QTYPE_QNUM')
490 conflicting
.add('QTYPE_QNUM')
491 conflicting
.add('QTYPE_QBOOL')
492 for qt
in conflicting
:
496 "%s can't be distinguished from '%s'"
497 % (v
.describe(self
.info
), types_seen
[qt
]))
498 types_seen
[qt
] = v
.name
500 def connect_doc(self
, doc
=None):
501 super().connect_doc(doc
)
502 doc
= doc
or self
.doc
503 for v
in self
.variants
.variants
:
507 return c_name(self
.name
) + pointer_suffix
512 def visit(self
, visitor
):
513 super().visit(visitor
)
514 visitor
.visit_alternate_type(
515 self
.name
, self
.info
, self
.ifcond
, self
.features
, self
.variants
)
518 class QAPISchemaVariants
:
519 def __init__(self
, tag_name
, info
, tag_member
, variants
):
520 # Flat unions pass tag_name but not tag_member.
521 # Simple unions and alternates pass tag_member but not tag_name.
522 # After check(), tag_member is always set, and tag_name remains
523 # a reliable witness of being used by a flat union.
524 assert bool(tag_member
) != bool(tag_name
)
525 assert (isinstance(tag_name
, str) or
526 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
528 assert isinstance(v
, QAPISchemaVariant
)
529 self
._tag
_name
= tag_name
531 self
.tag_member
= tag_member
532 self
.variants
= variants
534 def set_defined_in(self
, name
):
535 for v
in self
.variants
:
536 v
.set_defined_in(name
)
538 def check(self
, schema
, seen
):
539 if not self
.tag_member
: # flat union
540 self
.tag_member
= seen
.get(c_name(self
._tag
_name
))
542 # Pointing to the base type when not implicit would be
543 # nice, but we don't know it here
544 if not self
.tag_member
or self
._tag
_name
!= self
.tag_member
.name
:
547 "discriminator '%s' is not a member of %s"
548 % (self
._tag
_name
, base
))
550 base_type
= schema
.lookup_type(self
.tag_member
.defined_in
)
552 if not base_type
.is_implicit():
553 base
= "base type '%s'" % self
.tag_member
.defined_in
554 if not isinstance(self
.tag_member
.type, QAPISchemaEnumType
):
557 "discriminator member '%s' of %s must be of enum type"
558 % (self
._tag
_name
, base
))
559 if self
.tag_member
.optional
:
562 "discriminator member '%s' of %s must not be optional"
563 % (self
._tag
_name
, base
))
564 if self
.tag_member
.ifcond
:
567 "discriminator member '%s' of %s must not be conditional"
568 % (self
._tag
_name
, base
))
570 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
571 assert not self
.tag_member
.optional
572 assert self
.tag_member
.ifcond
== []
573 if self
._tag
_name
: # flat union
574 # branches that are not explicitly covered get an empty type
575 cases
= {v
.name
for v
in self
.variants
}
576 for m
in self
.tag_member
.type.members
:
577 if m
.name
not in cases
:
578 v
= QAPISchemaVariant(m
.name
, self
.info
,
580 v
.set_defined_in(self
.tag_member
.defined_in
)
581 self
.variants
.append(v
)
582 if not self
.variants
:
583 raise QAPISemError(self
.info
, "union has no branches")
584 for v
in self
.variants
:
586 # Union names must match enum values; alternate names are
587 # checked separately. Use 'seen' to tell the two apart.
589 if v
.name
not in self
.tag_member
.type.member_names():
592 "branch '%s' is not a value of %s"
593 % (v
.name
, self
.tag_member
.type.describe()))
594 if (not isinstance(v
.type, QAPISchemaObjectType
)
599 % (v
.describe(self
.info
), v
.type.describe()))
602 def check_clash(self
, info
, seen
):
603 for v
in self
.variants
:
604 # Reset seen map for each variant, since qapi names from one
605 # branch do not affect another branch
606 v
.type.check_clash(info
, dict(seen
))
609 class QAPISchemaMember
:
610 """ Represents object members, enum members and features """
613 def __init__(self
, name
, info
, ifcond
=None):
614 assert isinstance(name
, str)
617 self
.ifcond
= ifcond
or []
618 self
.defined_in
= None
620 def set_defined_in(self
, name
):
621 assert not self
.defined_in
622 self
.defined_in
= name
624 def check_clash(self
, info
, seen
):
625 cname
= c_name(self
.name
)
629 "%s collides with %s"
630 % (self
.describe(info
), seen
[cname
].describe(info
)))
633 def connect_doc(self
, doc
):
635 doc
.connect_member(self
)
637 def describe(self
, info
):
639 defined_in
= self
.defined_in
642 if defined_in
.startswith('q_obj_'):
643 # See QAPISchema._make_implicit_object_type() - reverse the
644 # mapping there to create a nice human-readable description
645 defined_in
= defined_in
[6:]
646 if defined_in
.endswith('-arg'):
647 # Implicit type created for a command's dict 'data'
648 assert role
== 'member'
650 elif defined_in
.endswith('-base'):
651 # Implicit type created for a flat union's dict 'base'
652 role
= 'base ' + role
654 # Implicit type created for a simple union's branch
655 assert defined_in
.endswith('-wrapper')
656 # Unreachable and not implemented
658 elif defined_in
.endswith('Kind'):
659 # See QAPISchema._make_implicit_enum_type()
660 # Implicit enum created for simple union's branches
661 assert role
== 'value'
663 elif defined_in
!= info
.defn_name
:
664 return "%s '%s' of type '%s'" % (role
, self
.name
, defined_in
)
665 return "%s '%s'" % (role
, self
.name
)
668 class QAPISchemaEnumMember(QAPISchemaMember
):
672 class QAPISchemaFeature(QAPISchemaMember
):
676 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
677 def __init__(self
, name
, info
, typ
, optional
, ifcond
=None, features
=None):
678 super().__init
__(name
, info
, ifcond
)
679 assert isinstance(typ
, str)
680 assert isinstance(optional
, bool)
681 for f
in features
or []:
682 assert isinstance(f
, QAPISchemaFeature
)
683 f
.set_defined_in(name
)
684 self
._type
_name
= typ
686 self
.optional
= optional
687 self
.features
= features
or []
689 def check(self
, schema
):
690 assert self
.defined_in
691 self
.type = schema
.resolve_type(self
._type
_name
, self
.info
,
694 for f
in self
.features
:
695 f
.check_clash(self
.info
, seen
)
697 def connect_doc(self
, doc
):
698 super().connect_doc(doc
)
700 for f
in self
.features
:
701 doc
.connect_feature(f
)
704 class QAPISchemaVariant(QAPISchemaObjectTypeMember
):
707 def __init__(self
, name
, info
, typ
, ifcond
=None):
708 super().__init
__(name
, info
, typ
, False, ifcond
)
711 class QAPISchemaCommand(QAPISchemaEntity
):
714 def __init__(self
, name
, info
, doc
, ifcond
, features
,
716 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
):
717 super().__init
__(name
, info
, doc
, ifcond
, features
)
718 assert not arg_type
or isinstance(arg_type
, str)
719 assert not ret_type
or isinstance(ret_type
, str)
720 self
._arg
_type
_name
= arg_type
722 self
._ret
_type
_name
= ret_type
725 self
.success_response
= success_response
727 self
.allow_oob
= allow_oob
728 self
.allow_preconfig
= allow_preconfig
730 def check(self
, schema
):
731 super().check(schema
)
732 if self
._arg
_type
_name
:
733 self
.arg_type
= schema
.resolve_type(
734 self
._arg
_type
_name
, self
.info
, "command's 'data'")
735 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
738 "command's 'data' cannot take %s"
739 % self
.arg_type
.describe())
740 if self
.arg_type
.variants
and not self
.boxed
:
743 "command's 'data' can take %s only with 'boxed': true"
744 % self
.arg_type
.describe())
745 if self
._ret
_type
_name
:
746 self
.ret_type
= schema
.resolve_type(
747 self
._ret
_type
_name
, self
.info
, "command's 'returns'")
748 if self
.name
not in self
.info
.pragma
.returns_whitelist
:
750 if isinstance(typ
, QAPISchemaArrayType
):
751 typ
= self
.ret_type
.element_type
753 if not isinstance(typ
, QAPISchemaObjectType
):
756 "command's 'returns' cannot take %s"
757 % self
.ret_type
.describe())
759 def connect_doc(self
, doc
=None):
760 super().connect_doc(doc
)
761 doc
= doc
or self
.doc
763 if self
.arg_type
and self
.arg_type
.is_implicit():
764 self
.arg_type
.connect_doc(doc
)
766 def visit(self
, visitor
):
767 super().visit(visitor
)
768 visitor
.visit_command(
769 self
.name
, self
.info
, self
.ifcond
, self
.features
,
770 self
.arg_type
, self
.ret_type
, self
.gen
, self
.success_response
,
771 self
.boxed
, self
.allow_oob
, self
.allow_preconfig
)
774 class QAPISchemaEvent(QAPISchemaEntity
):
777 def __init__(self
, name
, info
, doc
, ifcond
, features
, arg_type
, boxed
):
778 super().__init
__(name
, info
, doc
, ifcond
, features
)
779 assert not arg_type
or isinstance(arg_type
, str)
780 self
._arg
_type
_name
= arg_type
784 def check(self
, schema
):
785 super().check(schema
)
786 if self
._arg
_type
_name
:
787 self
.arg_type
= schema
.resolve_type(
788 self
._arg
_type
_name
, self
.info
, "event's 'data'")
789 if not isinstance(self
.arg_type
, QAPISchemaObjectType
):
792 "event's 'data' cannot take %s"
793 % self
.arg_type
.describe())
794 if self
.arg_type
.variants
and not self
.boxed
:
797 "event's 'data' can take %s only with 'boxed': true"
798 % self
.arg_type
.describe())
800 def connect_doc(self
, doc
=None):
801 super().connect_doc(doc
)
802 doc
= doc
or self
.doc
804 if self
.arg_type
and self
.arg_type
.is_implicit():
805 self
.arg_type
.connect_doc(doc
)
807 def visit(self
, visitor
):
808 super().visit(visitor
)
810 self
.name
, self
.info
, self
.ifcond
, self
.features
,
811 self
.arg_type
, self
.boxed
)
815 def __init__(self
, fname
):
817 parser
= QAPISchemaParser(fname
)
818 exprs
= check_exprs(parser
.exprs
)
819 self
.docs
= parser
.docs
820 self
._entity
_list
= []
821 self
._entity
_dict
= {}
822 self
._module
_dict
= OrderedDict()
823 self
._schema
_dir
= os
.path
.dirname(fname
)
824 self
._make
_module
(None) # built-ins
825 self
._make
_module
(fname
)
826 self
._predefining
= True
827 self
._def
_predefineds
()
828 self
._predefining
= False
829 self
._def
_exprs
(exprs
)
832 def _def_entity(self
, ent
):
833 # Only the predefined types are allowed to not have info
834 assert ent
.info
or self
._predefining
835 self
._entity
_list
.append(ent
)
838 # TODO reject names that differ only in '_' vs. '.' vs. '-',
839 # because they're liable to clash in generated C.
840 other_ent
= self
._entity
_dict
.get(ent
.name
)
843 where
= QAPIError(other_ent
.info
, None, "previous definition")
846 "'%s' is already defined\n%s" % (ent
.name
, where
))
848 ent
.info
, "%s is already defined" % other_ent
.describe())
849 self
._entity
_dict
[ent
.name
] = ent
851 def lookup_entity(self
, name
, typ
=None):
852 ent
= self
._entity
_dict
.get(name
)
853 if typ
and not isinstance(ent
, typ
):
857 def lookup_type(self
, name
):
858 return self
.lookup_entity(name
, QAPISchemaType
)
860 def resolve_type(self
, name
, info
, what
):
861 typ
= self
.lookup_type(name
)
866 info
, "%s uses unknown type '%s'" % (what
, name
))
869 def _module_name(self
, fname
):
872 return os
.path
.relpath(fname
, self
._schema
_dir
)
874 def _make_module(self
, fname
):
875 name
= self
._module
_name
(fname
)
876 if name
not in self
._module
_dict
:
877 self
._module
_dict
[name
] = QAPISchemaModule(name
)
878 return self
._module
_dict
[name
]
880 def module_by_fname(self
, fname
):
881 name
= self
._module
_name
(fname
)
882 assert name
in self
._module
_dict
883 return self
._module
_dict
[name
]
885 def _def_include(self
, expr
, info
, doc
):
886 include
= expr
['include']
888 self
._def
_entity
(QAPISchemaInclude(self
._make
_module
(include
), info
))
890 def _def_builtin_type(self
, name
, json_type
, c_type
):
891 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
892 # Instantiating only the arrays that are actually used would
893 # be nice, but we can't as long as their generated code
894 # (qapi-builtin-types.[ch]) may be shared by some other
896 self
._make
_array
_type
(name
, None)
898 def _def_predefineds(self
):
899 for t
in [('str', 'string', 'char' + pointer_suffix
),
900 ('number', 'number', 'double'),
901 ('int', 'int', 'int64_t'),
902 ('int8', 'int', 'int8_t'),
903 ('int16', 'int', 'int16_t'),
904 ('int32', 'int', 'int32_t'),
905 ('int64', 'int', 'int64_t'),
906 ('uint8', 'int', 'uint8_t'),
907 ('uint16', 'int', 'uint16_t'),
908 ('uint32', 'int', 'uint32_t'),
909 ('uint64', 'int', 'uint64_t'),
910 ('size', 'int', 'uint64_t'),
911 ('bool', 'boolean', 'bool'),
912 ('any', 'value', 'QObject' + pointer_suffix
),
913 ('null', 'null', 'QNull' + pointer_suffix
)]:
914 self
._def
_builtin
_type
(*t
)
915 self
.the_empty_object_type
= QAPISchemaObjectType(
916 'q_empty', None, None, None, None, None, [], None)
917 self
._def
_entity
(self
.the_empty_object_type
)
919 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
921 qtype_values
= self
._make
_enum
_members
(
922 [{'name': n
} for n
in qtypes
], None)
924 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None, None,
925 qtype_values
, 'QTYPE'))
927 def _make_features(self
, features
, info
):
930 return [QAPISchemaFeature(f
['name'], info
, f
.get('if'))
933 def _make_enum_members(self
, values
, info
):
934 return [QAPISchemaEnumMember(v
['name'], info
, v
.get('if'))
937 def _make_implicit_enum_type(self
, name
, info
, ifcond
, values
):
938 # See also QAPISchemaObjectTypeMember.describe()
939 name
= name
+ 'Kind' # reserved by check_defn_name_str()
940 self
._def
_entity
(QAPISchemaEnumType(
941 name
, info
, None, ifcond
, None,
942 self
._make
_enum
_members
(values
, info
),
946 def _make_array_type(self
, element_type
, info
):
947 name
= element_type
+ 'List' # reserved by check_defn_name_str()
948 if not self
.lookup_type(name
):
949 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
952 def _make_implicit_object_type(self
, name
, info
, ifcond
, role
, members
):
955 # See also QAPISchemaObjectTypeMember.describe()
956 name
= 'q_obj_%s-%s' % (name
, role
)
957 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
959 # The implicit object type has multiple users. This can
960 # happen only for simple unions' implicit wrapper types.
961 # Its ifcond should be the disjunction of its user's
962 # ifconds. Not implemented. Instead, we always pass the
963 # wrapped type's ifcond, which is trivially the same for all
964 # users. It's also necessary for the wrapper to compile.
965 # But it's not tight: the disjunction need not imply it. We
966 # may end up compiling useless wrapper types.
967 # TODO kill simple unions or implement the disjunction
968 assert (ifcond
or []) == typ
._ifcond
# pylint: disable=protected-access
970 self
._def
_entity
(QAPISchemaObjectType(
971 name
, info
, None, ifcond
, None, None, members
, None))
974 def _def_enum_type(self
, expr
, info
, doc
):
977 prefix
= expr
.get('prefix')
978 ifcond
= expr
.get('if')
979 features
= self
._make
_features
(expr
.get('features'), info
)
980 self
._def
_entity
(QAPISchemaEnumType(
981 name
, info
, doc
, ifcond
, features
,
982 self
._make
_enum
_members
(data
, info
), prefix
))
984 def _make_member(self
, name
, typ
, ifcond
, features
, info
):
986 if name
.startswith('*'):
989 if isinstance(typ
, list):
991 typ
= self
._make
_array
_type
(typ
[0], info
)
992 return QAPISchemaObjectTypeMember(name
, info
, typ
, optional
, ifcond
,
993 self
._make
_features
(features
, info
))
995 def _make_members(self
, data
, info
):
996 return [self
._make
_member
(key
, value
['type'], value
.get('if'),
997 value
.get('features'), info
)
998 for (key
, value
) in data
.items()]
1000 def _def_struct_type(self
, expr
, info
, doc
):
1001 name
= expr
['struct']
1002 base
= expr
.get('base')
1004 ifcond
= expr
.get('if')
1005 features
= self
._make
_features
(expr
.get('features'), info
)
1006 self
._def
_entity
(QAPISchemaObjectType(
1007 name
, info
, doc
, ifcond
, features
, base
,
1008 self
._make
_members
(data
, info
),
1011 def _make_variant(self
, case
, typ
, ifcond
, info
):
1012 return QAPISchemaVariant(case
, info
, typ
, ifcond
)
1014 def _make_simple_variant(self
, case
, typ
, ifcond
, info
):
1015 if isinstance(typ
, list):
1016 assert len(typ
) == 1
1017 typ
= self
._make
_array
_type
(typ
[0], info
)
1018 typ
= self
._make
_implicit
_object
_type
(
1019 typ
, info
, self
.lookup_type(typ
),
1020 'wrapper', [self
._make
_member
('data', typ
, None, None, info
)])
1021 return QAPISchemaVariant(case
, info
, typ
, ifcond
)
1023 def _def_union_type(self
, expr
, info
, doc
):
1024 name
= expr
['union']
1026 base
= expr
.get('base')
1027 ifcond
= expr
.get('if')
1028 features
= self
._make
_features
(expr
.get('features'), info
)
1029 tag_name
= expr
.get('discriminator')
1031 if isinstance(base
, dict):
1032 base
= self
._make
_implicit
_object
_type
(
1034 'base', self
._make
_members
(base
, info
))
1036 variants
= [self
._make
_variant
(key
, value
['type'],
1037 value
.get('if'), info
)
1038 for (key
, value
) in data
.items()]
1041 variants
= [self
._make
_simple
_variant
(key
, value
['type'],
1042 value
.get('if'), info
)
1043 for (key
, value
) in data
.items()]
1044 enum
= [{'name': v
.name
, 'if': v
.ifcond
} for v
in variants
]
1045 typ
= self
._make
_implicit
_enum
_type
(name
, info
, ifcond
, enum
)
1046 tag_member
= QAPISchemaObjectTypeMember('type', info
, typ
, False)
1047 members
= [tag_member
]
1049 QAPISchemaObjectType(name
, info
, doc
, ifcond
, features
,
1052 tag_name
, info
, tag_member
, variants
)))
1054 def _def_alternate_type(self
, expr
, info
, doc
):
1055 name
= expr
['alternate']
1057 ifcond
= expr
.get('if')
1058 features
= self
._make
_features
(expr
.get('features'), info
)
1059 variants
= [self
._make
_variant
(key
, value
['type'], value
.get('if'),
1061 for (key
, value
) in data
.items()]
1062 tag_member
= QAPISchemaObjectTypeMember('type', info
, 'QType', False)
1064 QAPISchemaAlternateType(name
, info
, doc
, ifcond
, features
,
1066 None, info
, tag_member
, variants
)))
1068 def _def_command(self
, expr
, info
, doc
):
1069 name
= expr
['command']
1070 data
= expr
.get('data')
1071 rets
= expr
.get('returns')
1072 gen
= expr
.get('gen', True)
1073 success_response
= expr
.get('success-response', True)
1074 boxed
= expr
.get('boxed', False)
1075 allow_oob
= expr
.get('allow-oob', False)
1076 allow_preconfig
= expr
.get('allow-preconfig', False)
1077 ifcond
= expr
.get('if')
1078 features
= self
._make
_features
(expr
.get('features'), info
)
1079 if isinstance(data
, OrderedDict
):
1080 data
= self
._make
_implicit
_object
_type
(
1082 'arg', self
._make
_members
(data
, info
))
1083 if isinstance(rets
, list):
1084 assert len(rets
) == 1
1085 rets
= self
._make
_array
_type
(rets
[0], info
)
1086 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, ifcond
, features
,
1088 gen
, success_response
,
1089 boxed
, allow_oob
, allow_preconfig
))
1091 def _def_event(self
, expr
, info
, doc
):
1092 name
= expr
['event']
1093 data
= expr
.get('data')
1094 boxed
= expr
.get('boxed', False)
1095 ifcond
= expr
.get('if')
1096 features
= self
._make
_features
(expr
.get('features'), info
)
1097 if isinstance(data
, OrderedDict
):
1098 data
= self
._make
_implicit
_object
_type
(
1100 'arg', self
._make
_members
(data
, info
))
1101 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, ifcond
, features
,
1104 def _def_exprs(self
, exprs
):
1105 for expr_elem
in exprs
:
1106 expr
= expr_elem
['expr']
1107 info
= expr_elem
['info']
1108 doc
= expr_elem
.get('doc')
1110 self
._def
_enum
_type
(expr
, info
, doc
)
1111 elif 'struct' in expr
:
1112 self
._def
_struct
_type
(expr
, info
, doc
)
1113 elif 'union' in expr
:
1114 self
._def
_union
_type
(expr
, info
, doc
)
1115 elif 'alternate' in expr
:
1116 self
._def
_alternate
_type
(expr
, info
, doc
)
1117 elif 'command' in expr
:
1118 self
._def
_command
(expr
, info
, doc
)
1119 elif 'event' in expr
:
1120 self
._def
_event
(expr
, info
, doc
)
1121 elif 'include' in expr
:
1122 self
._def
_include
(expr
, info
, doc
)
1127 for ent
in self
._entity
_list
:
1131 for ent
in self
._entity
_list
:
1132 ent
.set_module(self
)
1134 def visit(self
, visitor
):
1135 visitor
.visit_begin(self
)
1136 for mod
in self
._module
_dict
.values():