qapi qga: Elide redundant has_FOO in generated C
[qemu.git] / scripts / qapi / schema.py
blob4594c69d0b8a0db43f4ea8eb1fba44a3f6e2923e
1 # -*- coding: utf-8 -*-
3 # QAPI schema internal representation
5 # Copyright (c) 2015-2019 Red Hat Inc.
7 # Authors:
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
18 import os
19 import re
20 from typing import Optional
22 from .common import (
23 POINTER_SUFFIX,
24 c_name,
25 cgen_ifcond,
26 docgen_ifcond,
27 gen_endif,
28 gen_if,
30 from .error import QAPIError, QAPISemError, QAPISourceError
31 from .expr import check_exprs
32 from .parser import QAPISchemaParser
35 class QAPISchemaIfCond:
36 def __init__(self, ifcond=None):
37 self.ifcond = ifcond
39 def _cgen(self):
40 return cgen_ifcond(self.ifcond)
42 def gen_if(self):
43 return gen_if(self._cgen())
45 def gen_endif(self):
46 return gen_endif(self._cgen())
48 def docgen(self):
49 return docgen_ifcond(self.ifcond)
51 def is_present(self):
52 return bool(self.ifcond)
55 class QAPISchemaEntity:
56 meta: Optional[str] = None
58 def __init__(self, name: str, info, doc, ifcond=None, features=None):
59 assert name is None or isinstance(name, str)
60 for f in features or []:
61 assert isinstance(f, QAPISchemaFeature)
62 f.set_defined_in(name)
63 self.name = name
64 self._module = None
65 # For explicitly defined entities, info points to the (explicit)
66 # definition. For builtins (and their arrays), info is None.
67 # For implicitly defined entities, info points to a place that
68 # triggered the implicit definition (there may be more than one
69 # such place).
70 self.info = info
71 self.doc = doc
72 self._ifcond = ifcond or QAPISchemaIfCond()
73 self.features = features or []
74 self._checked = False
76 def c_name(self):
77 return c_name(self.name)
79 def check(self, schema):
80 assert not self._checked
81 seen = {}
82 for f in self.features:
83 f.check_clash(self.info, seen)
84 self._checked = True
86 def connect_doc(self, doc=None):
87 doc = doc or self.doc
88 if doc:
89 for f in self.features:
90 doc.connect_feature(f)
92 def check_doc(self):
93 if self.doc:
94 self.doc.check()
96 def _set_module(self, schema, info):
97 assert self._checked
98 fname = info.fname if info else QAPISchemaModule.BUILTIN_MODULE_NAME
99 self._module = schema.module_by_fname(fname)
100 self._module.add_entity(self)
102 def set_module(self, schema):
103 self._set_module(schema, self.info)
105 @property
106 def ifcond(self):
107 assert self._checked
108 return self._ifcond
110 def is_implicit(self):
111 return not self.info
113 def visit(self, visitor):
114 assert self._checked
116 def describe(self):
117 assert self.meta
118 return "%s '%s'" % (self.meta, self.name)
121 class QAPISchemaVisitor:
122 def visit_begin(self, schema):
123 pass
125 def visit_end(self):
126 pass
128 def visit_module(self, name):
129 pass
131 def visit_needed(self, entity):
132 # Default to visiting everything
133 return True
135 def visit_include(self, name, info):
136 pass
138 def visit_builtin_type(self, name, info, json_type):
139 pass
141 def visit_enum_type(self, name, info, ifcond, features, members, prefix):
142 pass
144 def visit_array_type(self, name, info, ifcond, element_type):
145 pass
147 def visit_object_type(self, name, info, ifcond, features,
148 base, members, variants):
149 pass
151 def visit_object_type_flat(self, name, info, ifcond, features,
152 members, variants):
153 pass
155 def visit_alternate_type(self, name, info, ifcond, features, variants):
156 pass
158 def visit_command(self, name, info, ifcond, features,
159 arg_type, ret_type, gen, success_response, boxed,
160 allow_oob, allow_preconfig, coroutine):
161 pass
163 def visit_event(self, name, info, ifcond, features, arg_type, boxed):
164 pass
167 class QAPISchemaModule:
169 BUILTIN_MODULE_NAME = './builtin'
171 def __init__(self, name):
172 self.name = name
173 self._entity_list = []
175 @staticmethod
176 def is_system_module(name: str) -> bool:
178 System modules are internally defined modules.
180 Their names start with the "./" prefix.
182 return name.startswith('./')
184 @classmethod
185 def is_user_module(cls, name: str) -> bool:
187 User modules are those defined by the user in qapi JSON files.
189 They do not start with the "./" prefix.
191 return not cls.is_system_module(name)
193 @classmethod
194 def is_builtin_module(cls, name: str) -> bool:
196 The built-in module is a single System module for the built-in types.
198 It is always "./builtin".
200 return name == cls.BUILTIN_MODULE_NAME
202 def add_entity(self, ent):
203 self._entity_list.append(ent)
205 def visit(self, visitor):
206 visitor.visit_module(self.name)
207 for entity in self._entity_list:
208 if visitor.visit_needed(entity):
209 entity.visit(visitor)
212 class QAPISchemaInclude(QAPISchemaEntity):
213 def __init__(self, sub_module, info):
214 super().__init__(None, info, None)
215 self._sub_module = sub_module
217 def visit(self, visitor):
218 super().visit(visitor)
219 visitor.visit_include(self._sub_module.name, self.info)
222 class QAPISchemaType(QAPISchemaEntity):
223 # Return the C type for common use.
224 # For the types we commonly box, this is a pointer type.
225 def c_type(self):
226 pass
228 # Return the C type to be used in a parameter list.
229 def c_param_type(self):
230 return self.c_type()
232 # Return the C type to be used where we suppress boxing.
233 def c_unboxed_type(self):
234 return self.c_type()
236 def json_type(self):
237 pass
239 def alternate_qtype(self):
240 json2qtype = {
241 'null': 'QTYPE_QNULL',
242 'string': 'QTYPE_QSTRING',
243 'number': 'QTYPE_QNUM',
244 'int': 'QTYPE_QNUM',
245 'boolean': 'QTYPE_QBOOL',
246 'array': 'QTYPE_QLIST',
247 'object': 'QTYPE_QDICT'
249 return json2qtype.get(self.json_type())
251 def doc_type(self):
252 if self.is_implicit():
253 return None
254 return self.name
256 def need_has_if_optional(self):
257 # When FOO is a pointer, has_FOO == !!FOO, i.e. has_FOO is redundant.
258 # Except for arrays; see QAPISchemaArrayType.need_has_if_optional().
259 return not self.c_type().endswith(POINTER_SUFFIX)
261 def check(self, schema):
262 QAPISchemaEntity.check(self, schema)
263 for feat in self.features:
264 if feat.is_special():
265 raise QAPISemError(
266 self.info,
267 f"feature '{feat.name}' is not supported for types")
269 def describe(self):
270 assert self.meta
271 return "%s type '%s'" % (self.meta, self.name)
274 class QAPISchemaBuiltinType(QAPISchemaType):
275 meta = 'built-in'
277 def __init__(self, name, json_type, c_type):
278 super().__init__(name, None, None)
279 assert not c_type or isinstance(c_type, str)
280 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
281 'value')
282 self._json_type_name = json_type
283 self._c_type_name = c_type
285 def c_name(self):
286 return self.name
288 def c_type(self):
289 return self._c_type_name
291 def c_param_type(self):
292 if self.name == 'str':
293 return 'const ' + self._c_type_name
294 return self._c_type_name
296 def json_type(self):
297 return self._json_type_name
299 def doc_type(self):
300 return self.json_type()
302 def visit(self, visitor):
303 super().visit(visitor)
304 visitor.visit_builtin_type(self.name, self.info, self.json_type())
307 class QAPISchemaEnumType(QAPISchemaType):
308 meta = 'enum'
310 def __init__(self, name, info, doc, ifcond, features, members, prefix):
311 super().__init__(name, info, doc, ifcond, features)
312 for m in members:
313 assert isinstance(m, QAPISchemaEnumMember)
314 m.set_defined_in(name)
315 assert prefix is None or isinstance(prefix, str)
316 self.members = members
317 self.prefix = prefix
319 def check(self, schema):
320 super().check(schema)
321 seen = {}
322 for m in self.members:
323 m.check_clash(self.info, seen)
325 def connect_doc(self, doc=None):
326 super().connect_doc(doc)
327 doc = doc or self.doc
328 for m in self.members:
329 m.connect_doc(doc)
331 def is_implicit(self):
332 # See QAPISchema._def_predefineds()
333 return self.name == 'QType'
335 def c_type(self):
336 return c_name(self.name)
338 def member_names(self):
339 return [m.name for m in self.members]
341 def json_type(self):
342 return 'string'
344 def visit(self, visitor):
345 super().visit(visitor)
346 visitor.visit_enum_type(
347 self.name, self.info, self.ifcond, self.features,
348 self.members, self.prefix)
351 class QAPISchemaArrayType(QAPISchemaType):
352 meta = 'array'
354 def __init__(self, name, info, element_type):
355 super().__init__(name, info, None)
356 assert isinstance(element_type, str)
357 self._element_type_name = element_type
358 self.element_type = None
360 def need_has_if_optional(self):
361 # When FOO is an array, we still need has_FOO to distinguish
362 # absent (!has_FOO) from present and empty (has_FOO && !FOO).
363 return True
365 def check(self, schema):
366 super().check(schema)
367 self.element_type = schema.resolve_type(
368 self._element_type_name, self.info,
369 self.info and self.info.defn_meta)
370 assert not isinstance(self.element_type, QAPISchemaArrayType)
372 def set_module(self, schema):
373 self._set_module(schema, self.element_type.info)
375 @property
376 def ifcond(self):
377 assert self._checked
378 return self.element_type.ifcond
380 def is_implicit(self):
381 return True
383 def c_type(self):
384 return c_name(self.name) + POINTER_SUFFIX
386 def json_type(self):
387 return 'array'
389 def doc_type(self):
390 elt_doc_type = self.element_type.doc_type()
391 if not elt_doc_type:
392 return None
393 return 'array of ' + elt_doc_type
395 def visit(self, visitor):
396 super().visit(visitor)
397 visitor.visit_array_type(self.name, self.info, self.ifcond,
398 self.element_type)
400 def describe(self):
401 assert self.meta
402 return "%s type ['%s']" % (self.meta, self._element_type_name)
405 class QAPISchemaObjectType(QAPISchemaType):
406 def __init__(self, name, info, doc, ifcond, features,
407 base, local_members, variants):
408 # struct has local_members, optional base, and no variants
409 # union has base, variants, and no local_members
410 super().__init__(name, info, doc, ifcond, features)
411 self.meta = 'union' if variants else 'struct'
412 assert base is None or isinstance(base, str)
413 for m in local_members:
414 assert isinstance(m, QAPISchemaObjectTypeMember)
415 m.set_defined_in(name)
416 if variants is not None:
417 assert isinstance(variants, QAPISchemaVariants)
418 variants.set_defined_in(name)
419 self._base_name = base
420 self.base = None
421 self.local_members = local_members
422 self.variants = variants
423 self.members = None
425 def check(self, schema):
426 # This calls another type T's .check() exactly when the C
427 # struct emitted by gen_object() contains that T's C struct
428 # (pointers don't count).
429 if self.members is not None:
430 # A previous .check() completed: nothing to do
431 return
432 if self._checked:
433 # Recursed: C struct contains itself
434 raise QAPISemError(self.info,
435 "object %s contains itself" % self.name)
437 super().check(schema)
438 assert self._checked and self.members is None
440 seen = OrderedDict()
441 if self._base_name:
442 self.base = schema.resolve_type(self._base_name, self.info,
443 "'base'")
444 if (not isinstance(self.base, QAPISchemaObjectType)
445 or self.base.variants):
446 raise QAPISemError(
447 self.info,
448 "'base' requires a struct type, %s isn't"
449 % self.base.describe())
450 self.base.check(schema)
451 self.base.check_clash(self.info, seen)
452 for m in self.local_members:
453 m.check(schema)
454 m.check_clash(self.info, seen)
455 members = seen.values()
457 if self.variants:
458 self.variants.check(schema, seen)
459 self.variants.check_clash(self.info, seen)
461 self.members = members # mark completed
463 # Check that the members of this type do not cause duplicate JSON members,
464 # and update seen to track the members seen so far. Report any errors
465 # on behalf of info, which is not necessarily self.info
466 def check_clash(self, info, seen):
467 assert self._checked
468 assert not self.variants # not implemented
469 for m in self.members:
470 m.check_clash(info, seen)
472 def connect_doc(self, doc=None):
473 super().connect_doc(doc)
474 doc = doc or self.doc
475 if self.base and self.base.is_implicit():
476 self.base.connect_doc(doc)
477 for m in self.local_members:
478 m.connect_doc(doc)
480 def is_implicit(self):
481 # See QAPISchema._make_implicit_object_type(), as well as
482 # _def_predefineds()
483 return self.name.startswith('q_')
485 def is_empty(self):
486 assert self.members is not None
487 return not self.members and not self.variants
489 def c_name(self):
490 assert self.name != 'q_empty'
491 return super().c_name()
493 def c_type(self):
494 assert not self.is_implicit()
495 return c_name(self.name) + POINTER_SUFFIX
497 def c_unboxed_type(self):
498 return c_name(self.name)
500 def json_type(self):
501 return 'object'
503 def visit(self, visitor):
504 super().visit(visitor)
505 visitor.visit_object_type(
506 self.name, self.info, self.ifcond, self.features,
507 self.base, self.local_members, self.variants)
508 visitor.visit_object_type_flat(
509 self.name, self.info, self.ifcond, self.features,
510 self.members, self.variants)
513 class QAPISchemaAlternateType(QAPISchemaType):
514 meta = 'alternate'
516 def __init__(self, name, info, doc, ifcond, features, variants):
517 super().__init__(name, info, doc, ifcond, features)
518 assert isinstance(variants, QAPISchemaVariants)
519 assert variants.tag_member
520 variants.set_defined_in(name)
521 variants.tag_member.set_defined_in(self.name)
522 self.variants = variants
524 def check(self, schema):
525 super().check(schema)
526 self.variants.tag_member.check(schema)
527 # Not calling self.variants.check_clash(), because there's nothing
528 # to clash with
529 self.variants.check(schema, {})
530 # Alternate branch names have no relation to the tag enum values;
531 # so we have to check for potential name collisions ourselves.
532 seen = {}
533 types_seen = {}
534 for v in self.variants.variants:
535 v.check_clash(self.info, seen)
536 qtype = v.type.alternate_qtype()
537 if not qtype:
538 raise QAPISemError(
539 self.info,
540 "%s cannot use %s"
541 % (v.describe(self.info), v.type.describe()))
542 conflicting = set([qtype])
543 if qtype == 'QTYPE_QSTRING':
544 if isinstance(v.type, QAPISchemaEnumType):
545 for m in v.type.members:
546 if m.name in ['on', 'off']:
547 conflicting.add('QTYPE_QBOOL')
548 if re.match(r'[-+0-9.]', m.name):
549 # lazy, could be tightened
550 conflicting.add('QTYPE_QNUM')
551 else:
552 conflicting.add('QTYPE_QNUM')
553 conflicting.add('QTYPE_QBOOL')
554 for qt in conflicting:
555 if qt in types_seen:
556 raise QAPISemError(
557 self.info,
558 "%s can't be distinguished from '%s'"
559 % (v.describe(self.info), types_seen[qt]))
560 types_seen[qt] = v.name
562 def connect_doc(self, doc=None):
563 super().connect_doc(doc)
564 doc = doc or self.doc
565 for v in self.variants.variants:
566 v.connect_doc(doc)
568 def c_type(self):
569 return c_name(self.name) + POINTER_SUFFIX
571 def json_type(self):
572 return 'value'
574 def visit(self, visitor):
575 super().visit(visitor)
576 visitor.visit_alternate_type(
577 self.name, self.info, self.ifcond, self.features, self.variants)
580 class QAPISchemaVariants:
581 def __init__(self, tag_name, info, tag_member, variants):
582 # Unions pass tag_name but not tag_member.
583 # Alternates pass tag_member but not tag_name.
584 # After check(), tag_member is always set.
585 assert bool(tag_member) != bool(tag_name)
586 assert (isinstance(tag_name, str) or
587 isinstance(tag_member, QAPISchemaObjectTypeMember))
588 for v in variants:
589 assert isinstance(v, QAPISchemaVariant)
590 self._tag_name = tag_name
591 self.info = info
592 self.tag_member = tag_member
593 self.variants = variants
595 def set_defined_in(self, name):
596 for v in self.variants:
597 v.set_defined_in(name)
599 def check(self, schema, seen):
600 if self._tag_name: # union
601 self.tag_member = seen.get(c_name(self._tag_name))
602 base = "'base'"
603 # Pointing to the base type when not implicit would be
604 # nice, but we don't know it here
605 if not self.tag_member or self._tag_name != self.tag_member.name:
606 raise QAPISemError(
607 self.info,
608 "discriminator '%s' is not a member of %s"
609 % (self._tag_name, base))
610 # Here we do:
611 base_type = schema.lookup_type(self.tag_member.defined_in)
612 assert base_type
613 if not base_type.is_implicit():
614 base = "base type '%s'" % self.tag_member.defined_in
615 if not isinstance(self.tag_member.type, QAPISchemaEnumType):
616 raise QAPISemError(
617 self.info,
618 "discriminator member '%s' of %s must be of enum type"
619 % (self._tag_name, base))
620 if self.tag_member.optional:
621 raise QAPISemError(
622 self.info,
623 "discriminator member '%s' of %s must not be optional"
624 % (self._tag_name, base))
625 if self.tag_member.ifcond.is_present():
626 raise QAPISemError(
627 self.info,
628 "discriminator member '%s' of %s must not be conditional"
629 % (self._tag_name, base))
630 else: # alternate
631 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
632 assert not self.tag_member.optional
633 assert not self.tag_member.ifcond.is_present()
634 if self._tag_name: # union
635 # branches that are not explicitly covered get an empty type
636 cases = {v.name for v in self.variants}
637 for m in self.tag_member.type.members:
638 if m.name not in cases:
639 v = QAPISchemaVariant(m.name, self.info,
640 'q_empty', m.ifcond)
641 v.set_defined_in(self.tag_member.defined_in)
642 self.variants.append(v)
643 if not self.variants:
644 raise QAPISemError(self.info, "union has no branches")
645 for v in self.variants:
646 v.check(schema)
647 # Union names must match enum values; alternate names are
648 # checked separately. Use 'seen' to tell the two apart.
649 if seen:
650 if v.name not in self.tag_member.type.member_names():
651 raise QAPISemError(
652 self.info,
653 "branch '%s' is not a value of %s"
654 % (v.name, self.tag_member.type.describe()))
655 if (not isinstance(v.type, QAPISchemaObjectType)
656 or v.type.variants):
657 raise QAPISemError(
658 self.info,
659 "%s cannot use %s"
660 % (v.describe(self.info), v.type.describe()))
661 v.type.check(schema)
663 def check_clash(self, info, seen):
664 for v in self.variants:
665 # Reset seen map for each variant, since qapi names from one
666 # branch do not affect another branch
667 v.type.check_clash(info, dict(seen))
670 class QAPISchemaMember:
671 """ Represents object members, enum members and features """
672 role = 'member'
674 def __init__(self, name, info, ifcond=None):
675 assert isinstance(name, str)
676 self.name = name
677 self.info = info
678 self.ifcond = ifcond or QAPISchemaIfCond()
679 self.defined_in = None
681 def set_defined_in(self, name):
682 assert not self.defined_in
683 self.defined_in = name
685 def check_clash(self, info, seen):
686 cname = c_name(self.name)
687 if cname in seen:
688 raise QAPISemError(
689 info,
690 "%s collides with %s"
691 % (self.describe(info), seen[cname].describe(info)))
692 seen[cname] = self
694 def connect_doc(self, doc):
695 if doc:
696 doc.connect_member(self)
698 def describe(self, info):
699 role = self.role
700 defined_in = self.defined_in
701 assert defined_in
703 if defined_in.startswith('q_obj_'):
704 # See QAPISchema._make_implicit_object_type() - reverse the
705 # mapping there to create a nice human-readable description
706 defined_in = defined_in[6:]
707 if defined_in.endswith('-arg'):
708 # Implicit type created for a command's dict 'data'
709 assert role == 'member'
710 role = 'parameter'
711 elif defined_in.endswith('-base'):
712 # Implicit type created for a union's dict 'base'
713 role = 'base ' + role
714 else:
715 assert False
716 elif defined_in != info.defn_name:
717 return "%s '%s' of type '%s'" % (role, self.name, defined_in)
718 return "%s '%s'" % (role, self.name)
721 class QAPISchemaEnumMember(QAPISchemaMember):
722 role = 'value'
724 def __init__(self, name, info, ifcond=None, features=None):
725 super().__init__(name, info, ifcond)
726 for f in features or []:
727 assert isinstance(f, QAPISchemaFeature)
728 f.set_defined_in(name)
729 self.features = features or []
731 def connect_doc(self, doc):
732 super().connect_doc(doc)
733 if doc:
734 for f in self.features:
735 doc.connect_feature(f)
738 class QAPISchemaFeature(QAPISchemaMember):
739 role = 'feature'
741 def is_special(self):
742 return self.name in ('deprecated', 'unstable')
745 class QAPISchemaObjectTypeMember(QAPISchemaMember):
746 def __init__(self, name, info, typ, optional, ifcond=None, features=None):
747 super().__init__(name, info, ifcond)
748 assert isinstance(typ, str)
749 assert isinstance(optional, bool)
750 for f in features or []:
751 assert isinstance(f, QAPISchemaFeature)
752 f.set_defined_in(name)
753 self._type_name = typ
754 self.type = None
755 self.optional = optional
756 self.features = features or []
758 def need_has(self):
759 assert self.type
760 # Temporary hack to support dropping the has_FOO in reviewable chunks
761 opt_out = []
762 if self.info and any(self.info.fname.endswith(mod)
763 for mod in opt_out):
764 return self.optional
765 # End of temporary hack
766 return self.optional and self.type.need_has_if_optional()
768 def check(self, schema):
769 assert self.defined_in
770 self.type = schema.resolve_type(self._type_name, self.info,
771 self.describe)
772 seen = {}
773 for f in self.features:
774 f.check_clash(self.info, seen)
776 def connect_doc(self, doc):
777 super().connect_doc(doc)
778 if doc:
779 for f in self.features:
780 doc.connect_feature(f)
783 class QAPISchemaVariant(QAPISchemaObjectTypeMember):
784 role = 'branch'
786 def __init__(self, name, info, typ, ifcond=None):
787 super().__init__(name, info, typ, False, ifcond)
790 class QAPISchemaCommand(QAPISchemaEntity):
791 meta = 'command'
793 def __init__(self, name, info, doc, ifcond, features,
794 arg_type, ret_type,
795 gen, success_response, boxed, allow_oob, allow_preconfig,
796 coroutine):
797 super().__init__(name, info, doc, ifcond, features)
798 assert not arg_type or isinstance(arg_type, str)
799 assert not ret_type or isinstance(ret_type, str)
800 self._arg_type_name = arg_type
801 self.arg_type = None
802 self._ret_type_name = ret_type
803 self.ret_type = None
804 self.gen = gen
805 self.success_response = success_response
806 self.boxed = boxed
807 self.allow_oob = allow_oob
808 self.allow_preconfig = allow_preconfig
809 self.coroutine = coroutine
811 def check(self, schema):
812 super().check(schema)
813 if self._arg_type_name:
814 self.arg_type = schema.resolve_type(
815 self._arg_type_name, self.info, "command's 'data'")
816 if not isinstance(self.arg_type, QAPISchemaObjectType):
817 raise QAPISemError(
818 self.info,
819 "command's 'data' cannot take %s"
820 % self.arg_type.describe())
821 if self.arg_type.variants and not self.boxed:
822 raise QAPISemError(
823 self.info,
824 "command's 'data' can take %s only with 'boxed': true"
825 % self.arg_type.describe())
826 if self._ret_type_name:
827 self.ret_type = schema.resolve_type(
828 self._ret_type_name, self.info, "command's 'returns'")
829 if self.name not in self.info.pragma.command_returns_exceptions:
830 typ = self.ret_type
831 if isinstance(typ, QAPISchemaArrayType):
832 typ = self.ret_type.element_type
833 assert typ
834 if not isinstance(typ, QAPISchemaObjectType):
835 raise QAPISemError(
836 self.info,
837 "command's 'returns' cannot take %s"
838 % self.ret_type.describe())
840 def connect_doc(self, doc=None):
841 super().connect_doc(doc)
842 doc = doc or self.doc
843 if doc:
844 if self.arg_type and self.arg_type.is_implicit():
845 self.arg_type.connect_doc(doc)
847 def visit(self, visitor):
848 super().visit(visitor)
849 visitor.visit_command(
850 self.name, self.info, self.ifcond, self.features,
851 self.arg_type, self.ret_type, self.gen, self.success_response,
852 self.boxed, self.allow_oob, self.allow_preconfig,
853 self.coroutine)
856 class QAPISchemaEvent(QAPISchemaEntity):
857 meta = 'event'
859 def __init__(self, name, info, doc, ifcond, features, arg_type, boxed):
860 super().__init__(name, info, doc, ifcond, features)
861 assert not arg_type or isinstance(arg_type, str)
862 self._arg_type_name = arg_type
863 self.arg_type = None
864 self.boxed = boxed
866 def check(self, schema):
867 super().check(schema)
868 if self._arg_type_name:
869 self.arg_type = schema.resolve_type(
870 self._arg_type_name, self.info, "event's 'data'")
871 if not isinstance(self.arg_type, QAPISchemaObjectType):
872 raise QAPISemError(
873 self.info,
874 "event's 'data' cannot take %s"
875 % self.arg_type.describe())
876 if self.arg_type.variants and not self.boxed:
877 raise QAPISemError(
878 self.info,
879 "event's 'data' can take %s only with 'boxed': true"
880 % self.arg_type.describe())
882 def connect_doc(self, doc=None):
883 super().connect_doc(doc)
884 doc = doc or self.doc
885 if doc:
886 if self.arg_type and self.arg_type.is_implicit():
887 self.arg_type.connect_doc(doc)
889 def visit(self, visitor):
890 super().visit(visitor)
891 visitor.visit_event(
892 self.name, self.info, self.ifcond, self.features,
893 self.arg_type, self.boxed)
896 class QAPISchema:
897 def __init__(self, fname):
898 self.fname = fname
900 try:
901 parser = QAPISchemaParser(fname)
902 except OSError as err:
903 raise QAPIError(
904 f"can't read schema file '{fname}': {err.strerror}"
905 ) from err
907 exprs = check_exprs(parser.exprs)
908 self.docs = parser.docs
909 self._entity_list = []
910 self._entity_dict = {}
911 self._module_dict = OrderedDict()
912 self._schema_dir = os.path.dirname(fname)
913 self._make_module(QAPISchemaModule.BUILTIN_MODULE_NAME)
914 self._make_module(fname)
915 self._predefining = True
916 self._def_predefineds()
917 self._predefining = False
918 self._def_exprs(exprs)
919 self.check()
921 def _def_entity(self, ent):
922 # Only the predefined types are allowed to not have info
923 assert ent.info or self._predefining
924 self._entity_list.append(ent)
925 if ent.name is None:
926 return
927 # TODO reject names that differ only in '_' vs. '.' vs. '-',
928 # because they're liable to clash in generated C.
929 other_ent = self._entity_dict.get(ent.name)
930 if other_ent:
931 if other_ent.info:
932 where = QAPISourceError(other_ent.info, "previous definition")
933 raise QAPISemError(
934 ent.info,
935 "'%s' is already defined\n%s" % (ent.name, where))
936 raise QAPISemError(
937 ent.info, "%s is already defined" % other_ent.describe())
938 self._entity_dict[ent.name] = ent
940 def lookup_entity(self, name, typ=None):
941 ent = self._entity_dict.get(name)
942 if typ and not isinstance(ent, typ):
943 return None
944 return ent
946 def lookup_type(self, name):
947 return self.lookup_entity(name, QAPISchemaType)
949 def resolve_type(self, name, info, what):
950 typ = self.lookup_type(name)
951 if not typ:
952 if callable(what):
953 what = what(info)
954 raise QAPISemError(
955 info, "%s uses unknown type '%s'" % (what, name))
956 return typ
958 def _module_name(self, fname: str) -> str:
959 if QAPISchemaModule.is_system_module(fname):
960 return fname
961 return os.path.relpath(fname, self._schema_dir)
963 def _make_module(self, fname):
964 name = self._module_name(fname)
965 if name not in self._module_dict:
966 self._module_dict[name] = QAPISchemaModule(name)
967 return self._module_dict[name]
969 def module_by_fname(self, fname):
970 name = self._module_name(fname)
971 return self._module_dict[name]
973 def _def_include(self, expr, info, doc):
974 include = expr['include']
975 assert doc is None
976 self._def_entity(QAPISchemaInclude(self._make_module(include), info))
978 def _def_builtin_type(self, name, json_type, c_type):
979 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
980 # Instantiating only the arrays that are actually used would
981 # be nice, but we can't as long as their generated code
982 # (qapi-builtin-types.[ch]) may be shared by some other
983 # schema.
984 self._make_array_type(name, None)
986 def _def_predefineds(self):
987 for t in [('str', 'string', 'char' + POINTER_SUFFIX),
988 ('number', 'number', 'double'),
989 ('int', 'int', 'int64_t'),
990 ('int8', 'int', 'int8_t'),
991 ('int16', 'int', 'int16_t'),
992 ('int32', 'int', 'int32_t'),
993 ('int64', 'int', 'int64_t'),
994 ('uint8', 'int', 'uint8_t'),
995 ('uint16', 'int', 'uint16_t'),
996 ('uint32', 'int', 'uint32_t'),
997 ('uint64', 'int', 'uint64_t'),
998 ('size', 'int', 'uint64_t'),
999 ('bool', 'boolean', 'bool'),
1000 ('any', 'value', 'QObject' + POINTER_SUFFIX),
1001 ('null', 'null', 'QNull' + POINTER_SUFFIX)]:
1002 self._def_builtin_type(*t)
1003 self.the_empty_object_type = QAPISchemaObjectType(
1004 'q_empty', None, None, None, None, None, [], None)
1005 self._def_entity(self.the_empty_object_type)
1007 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1008 'qbool']
1009 qtype_values = self._make_enum_members(
1010 [{'name': n} for n in qtypes], None)
1012 self._def_entity(QAPISchemaEnumType('QType', None, None, None, None,
1013 qtype_values, 'QTYPE'))
1015 def _make_features(self, features, info):
1016 if features is None:
1017 return []
1018 return [QAPISchemaFeature(f['name'], info,
1019 QAPISchemaIfCond(f.get('if')))
1020 for f in features]
1022 def _make_enum_member(self, name, ifcond, features, info):
1023 return QAPISchemaEnumMember(name, info,
1024 QAPISchemaIfCond(ifcond),
1025 self._make_features(features, info))
1027 def _make_enum_members(self, values, info):
1028 return [self._make_enum_member(v['name'], v.get('if'),
1029 v.get('features'), info)
1030 for v in values]
1032 def _make_array_type(self, element_type, info):
1033 name = element_type + 'List' # reserved by check_defn_name_str()
1034 if not self.lookup_type(name):
1035 self._def_entity(QAPISchemaArrayType(name, info, element_type))
1036 return name
1038 def _make_implicit_object_type(self, name, info, ifcond, role, members):
1039 if not members:
1040 return None
1041 # See also QAPISchemaObjectTypeMember.describe()
1042 name = 'q_obj_%s-%s' % (name, role)
1043 typ = self.lookup_entity(name, QAPISchemaObjectType)
1044 if typ:
1045 # The implicit object type has multiple users. This can
1046 # only be a duplicate definition, which will be flagged
1047 # later.
1048 pass
1049 else:
1050 self._def_entity(QAPISchemaObjectType(
1051 name, info, None, ifcond, None, None, members, None))
1052 return name
1054 def _def_enum_type(self, expr, info, doc):
1055 name = expr['enum']
1056 data = expr['data']
1057 prefix = expr.get('prefix')
1058 ifcond = QAPISchemaIfCond(expr.get('if'))
1059 features = self._make_features(expr.get('features'), info)
1060 self._def_entity(QAPISchemaEnumType(
1061 name, info, doc, ifcond, features,
1062 self._make_enum_members(data, info), prefix))
1064 def _make_member(self, name, typ, ifcond, features, info):
1065 optional = False
1066 if name.startswith('*'):
1067 name = name[1:]
1068 optional = True
1069 if isinstance(typ, list):
1070 assert len(typ) == 1
1071 typ = self._make_array_type(typ[0], info)
1072 return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond,
1073 self._make_features(features, info))
1075 def _make_members(self, data, info):
1076 return [self._make_member(key, value['type'],
1077 QAPISchemaIfCond(value.get('if')),
1078 value.get('features'), info)
1079 for (key, value) in data.items()]
1081 def _def_struct_type(self, expr, info, doc):
1082 name = expr['struct']
1083 base = expr.get('base')
1084 data = expr['data']
1085 ifcond = QAPISchemaIfCond(expr.get('if'))
1086 features = self._make_features(expr.get('features'), info)
1087 self._def_entity(QAPISchemaObjectType(
1088 name, info, doc, ifcond, features, base,
1089 self._make_members(data, info),
1090 None))
1092 def _make_variant(self, case, typ, ifcond, info):
1093 if isinstance(typ, list):
1094 assert len(typ) == 1
1095 typ = self._make_array_type(typ[0], info)
1096 return QAPISchemaVariant(case, info, typ, ifcond)
1098 def _def_union_type(self, expr, info, doc):
1099 name = expr['union']
1100 base = expr['base']
1101 tag_name = expr['discriminator']
1102 data = expr['data']
1103 ifcond = QAPISchemaIfCond(expr.get('if'))
1104 features = self._make_features(expr.get('features'), info)
1105 if isinstance(base, dict):
1106 base = self._make_implicit_object_type(
1107 name, info, ifcond,
1108 'base', self._make_members(base, info))
1109 variants = [
1110 self._make_variant(key, value['type'],
1111 QAPISchemaIfCond(value.get('if')),
1112 info)
1113 for (key, value) in data.items()]
1114 members = []
1115 self._def_entity(
1116 QAPISchemaObjectType(name, info, doc, ifcond, features,
1117 base, members,
1118 QAPISchemaVariants(
1119 tag_name, info, None, variants)))
1121 def _def_alternate_type(self, expr, info, doc):
1122 name = expr['alternate']
1123 data = expr['data']
1124 ifcond = QAPISchemaIfCond(expr.get('if'))
1125 features = self._make_features(expr.get('features'), info)
1126 variants = [
1127 self._make_variant(key, value['type'],
1128 QAPISchemaIfCond(value.get('if')),
1129 info)
1130 for (key, value) in data.items()]
1131 tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
1132 self._def_entity(
1133 QAPISchemaAlternateType(name, info, doc, ifcond, features,
1134 QAPISchemaVariants(
1135 None, info, tag_member, variants)))
1137 def _def_command(self, expr, info, doc):
1138 name = expr['command']
1139 data = expr.get('data')
1140 rets = expr.get('returns')
1141 gen = expr.get('gen', True)
1142 success_response = expr.get('success-response', True)
1143 boxed = expr.get('boxed', False)
1144 allow_oob = expr.get('allow-oob', False)
1145 allow_preconfig = expr.get('allow-preconfig', False)
1146 coroutine = expr.get('coroutine', False)
1147 ifcond = QAPISchemaIfCond(expr.get('if'))
1148 features = self._make_features(expr.get('features'), info)
1149 if isinstance(data, OrderedDict):
1150 data = self._make_implicit_object_type(
1151 name, info, ifcond,
1152 'arg', self._make_members(data, info))
1153 if isinstance(rets, list):
1154 assert len(rets) == 1
1155 rets = self._make_array_type(rets[0], info)
1156 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, features,
1157 data, rets,
1158 gen, success_response,
1159 boxed, allow_oob, allow_preconfig,
1160 coroutine))
1162 def _def_event(self, expr, info, doc):
1163 name = expr['event']
1164 data = expr.get('data')
1165 boxed = expr.get('boxed', False)
1166 ifcond = QAPISchemaIfCond(expr.get('if'))
1167 features = self._make_features(expr.get('features'), info)
1168 if isinstance(data, OrderedDict):
1169 data = self._make_implicit_object_type(
1170 name, info, ifcond,
1171 'arg', self._make_members(data, info))
1172 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, features,
1173 data, boxed))
1175 def _def_exprs(self, exprs):
1176 for expr_elem in exprs:
1177 expr = expr_elem['expr']
1178 info = expr_elem['info']
1179 doc = expr_elem.get('doc')
1180 if 'enum' in expr:
1181 self._def_enum_type(expr, info, doc)
1182 elif 'struct' in expr:
1183 self._def_struct_type(expr, info, doc)
1184 elif 'union' in expr:
1185 self._def_union_type(expr, info, doc)
1186 elif 'alternate' in expr:
1187 self._def_alternate_type(expr, info, doc)
1188 elif 'command' in expr:
1189 self._def_command(expr, info, doc)
1190 elif 'event' in expr:
1191 self._def_event(expr, info, doc)
1192 elif 'include' in expr:
1193 self._def_include(expr, info, doc)
1194 else:
1195 assert False
1197 def check(self):
1198 for ent in self._entity_list:
1199 ent.check(self)
1200 ent.connect_doc()
1201 ent.check_doc()
1202 for ent in self._entity_list:
1203 ent.set_module(self)
1205 def visit(self, visitor):
1206 visitor.visit_begin(self)
1207 for mod in self._module_dict.values():
1208 mod.visit(visitor)
1209 visitor.visit_end()