qapi: Call QAPIDoc.check() always
[qemu/kevin.git] / scripts / qapi / schema.py
blob8ba5665bc68bf7a6f8f9bd92a5e1d9d82de84b43
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 List, 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 QAPIExpression, 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 __repr__(self):
77 if self.name is None:
78 return "<%s at 0x%x>" % (type(self).__name__, id(self))
79 return "<%s:%s at 0x%x>" % (type(self).__name__, self.name,
80 id(self))
82 def c_name(self):
83 return c_name(self.name)
85 def check(self, schema):
86 assert not self._checked
87 seen = {}
88 for f in self.features:
89 f.check_clash(self.info, seen)
90 self._checked = True
92 def connect_doc(self, doc=None):
93 doc = doc or self.doc
94 if doc:
95 for f in self.features:
96 doc.connect_feature(f)
98 def _set_module(self, schema, info):
99 assert self._checked
100 fname = info.fname if info else QAPISchemaModule.BUILTIN_MODULE_NAME
101 self._module = schema.module_by_fname(fname)
102 self._module.add_entity(self)
104 def set_module(self, schema):
105 self._set_module(schema, self.info)
107 @property
108 def ifcond(self):
109 assert self._checked
110 return self._ifcond
112 def is_implicit(self):
113 return not self.info
115 def visit(self, visitor):
116 assert self._checked
118 def describe(self):
119 assert self.meta
120 return "%s '%s'" % (self.meta, self.name)
123 class QAPISchemaVisitor:
124 def visit_begin(self, schema):
125 pass
127 def visit_end(self):
128 pass
130 def visit_module(self, name):
131 pass
133 def visit_needed(self, entity):
134 # Default to visiting everything
135 return True
137 def visit_include(self, name, info):
138 pass
140 def visit_builtin_type(self, name, info, json_type):
141 pass
143 def visit_enum_type(self, name, info, ifcond, features, members, prefix):
144 pass
146 def visit_array_type(self, name, info, ifcond, element_type):
147 pass
149 def visit_object_type(self, name, info, ifcond, features,
150 base, members, variants):
151 pass
153 def visit_object_type_flat(self, name, info, ifcond, features,
154 members, variants):
155 pass
157 def visit_alternate_type(self, name, info, ifcond, features, variants):
158 pass
160 def visit_command(self, name, info, ifcond, features,
161 arg_type, ret_type, gen, success_response, boxed,
162 allow_oob, allow_preconfig, coroutine):
163 pass
165 def visit_event(self, name, info, ifcond, features, arg_type, boxed):
166 pass
169 class QAPISchemaModule:
171 BUILTIN_MODULE_NAME = './builtin'
173 def __init__(self, name):
174 self.name = name
175 self._entity_list = []
177 @staticmethod
178 def is_system_module(name: str) -> bool:
180 System modules are internally defined modules.
182 Their names start with the "./" prefix.
184 return name.startswith('./')
186 @classmethod
187 def is_user_module(cls, name: str) -> bool:
189 User modules are those defined by the user in qapi JSON files.
191 They do not start with the "./" prefix.
193 return not cls.is_system_module(name)
195 @classmethod
196 def is_builtin_module(cls, name: str) -> bool:
198 The built-in module is a single System module for the built-in types.
200 It is always "./builtin".
202 return name == cls.BUILTIN_MODULE_NAME
204 def add_entity(self, ent):
205 self._entity_list.append(ent)
207 def visit(self, visitor):
208 visitor.visit_module(self.name)
209 for entity in self._entity_list:
210 if visitor.visit_needed(entity):
211 entity.visit(visitor)
214 class QAPISchemaInclude(QAPISchemaEntity):
215 def __init__(self, sub_module, info):
216 super().__init__(None, info, None)
217 self._sub_module = sub_module
219 def visit(self, visitor):
220 super().visit(visitor)
221 visitor.visit_include(self._sub_module.name, self.info)
224 class QAPISchemaType(QAPISchemaEntity):
225 # Return the C type for common use.
226 # For the types we commonly box, this is a pointer type.
227 def c_type(self):
228 pass
230 # Return the C type to be used in a parameter list.
231 def c_param_type(self):
232 return self.c_type()
234 # Return the C type to be used where we suppress boxing.
235 def c_unboxed_type(self):
236 return self.c_type()
238 def json_type(self):
239 pass
241 def alternate_qtype(self):
242 json2qtype = {
243 'null': 'QTYPE_QNULL',
244 'string': 'QTYPE_QSTRING',
245 'number': 'QTYPE_QNUM',
246 'int': 'QTYPE_QNUM',
247 'boolean': 'QTYPE_QBOOL',
248 'array': 'QTYPE_QLIST',
249 'object': 'QTYPE_QDICT'
251 return json2qtype.get(self.json_type())
253 def doc_type(self):
254 if self.is_implicit():
255 return None
256 return self.name
258 def need_has_if_optional(self):
259 # When FOO is a pointer, has_FOO == !!FOO, i.e. has_FOO is redundant.
260 # Except for arrays; see QAPISchemaArrayType.need_has_if_optional().
261 return not self.c_type().endswith(POINTER_SUFFIX)
263 def check(self, schema):
264 super().check(schema)
265 for feat in self.features:
266 if feat.is_special():
267 raise QAPISemError(
268 self.info,
269 f"feature '{feat.name}' is not supported for types")
271 def describe(self):
272 assert self.meta
273 return "%s type '%s'" % (self.meta, self.name)
276 class QAPISchemaBuiltinType(QAPISchemaType):
277 meta = 'built-in'
279 def __init__(self, name, json_type, c_type):
280 super().__init__(name, None, None)
281 assert not c_type or isinstance(c_type, str)
282 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
283 'value')
284 self._json_type_name = json_type
285 self._c_type_name = c_type
287 def c_name(self):
288 return self.name
290 def c_type(self):
291 return self._c_type_name
293 def c_param_type(self):
294 if self.name == 'str':
295 return 'const ' + self._c_type_name
296 return self._c_type_name
298 def json_type(self):
299 return self._json_type_name
301 def doc_type(self):
302 return self.json_type()
304 def visit(self, visitor):
305 super().visit(visitor)
306 visitor.visit_builtin_type(self.name, self.info, self.json_type())
309 class QAPISchemaEnumType(QAPISchemaType):
310 meta = 'enum'
312 def __init__(self, name, info, doc, ifcond, features, members, prefix):
313 super().__init__(name, info, doc, ifcond, features)
314 for m in members:
315 assert isinstance(m, QAPISchemaEnumMember)
316 m.set_defined_in(name)
317 assert prefix is None or isinstance(prefix, str)
318 self.members = members
319 self.prefix = prefix
321 def check(self, schema):
322 super().check(schema)
323 seen = {}
324 for m in self.members:
325 m.check_clash(self.info, seen)
327 def connect_doc(self, doc=None):
328 super().connect_doc(doc)
329 doc = doc or self.doc
330 for m in self.members:
331 m.connect_doc(doc)
333 def is_implicit(self):
334 # See QAPISchema._def_predefineds()
335 return self.name == 'QType'
337 def c_type(self):
338 return c_name(self.name)
340 def member_names(self):
341 return [m.name for m in self.members]
343 def json_type(self):
344 return 'string'
346 def visit(self, visitor):
347 super().visit(visitor)
348 visitor.visit_enum_type(
349 self.name, self.info, self.ifcond, self.features,
350 self.members, self.prefix)
353 class QAPISchemaArrayType(QAPISchemaType):
354 meta = 'array'
356 def __init__(self, name, info, element_type):
357 super().__init__(name, info, None)
358 assert isinstance(element_type, str)
359 self._element_type_name = element_type
360 self.element_type = None
362 def need_has_if_optional(self):
363 # When FOO is an array, we still need has_FOO to distinguish
364 # absent (!has_FOO) from present and empty (has_FOO && !FOO).
365 return True
367 def check(self, schema):
368 super().check(schema)
369 self.element_type = schema.resolve_type(
370 self._element_type_name, self.info,
371 self.info and self.info.defn_meta)
372 assert not isinstance(self.element_type, QAPISchemaArrayType)
374 def set_module(self, schema):
375 self._set_module(schema, self.element_type.info)
377 @property
378 def ifcond(self):
379 assert self._checked
380 return self.element_type.ifcond
382 def is_implicit(self):
383 return True
385 def c_type(self):
386 return c_name(self.name) + POINTER_SUFFIX
388 def json_type(self):
389 return 'array'
391 def doc_type(self):
392 elt_doc_type = self.element_type.doc_type()
393 if not elt_doc_type:
394 return None
395 return 'array of ' + elt_doc_type
397 def visit(self, visitor):
398 super().visit(visitor)
399 visitor.visit_array_type(self.name, self.info, self.ifcond,
400 self.element_type)
402 def describe(self):
403 assert self.meta
404 return "%s type ['%s']" % (self.meta, self._element_type_name)
407 class QAPISchemaObjectType(QAPISchemaType):
408 def __init__(self, name, info, doc, ifcond, features,
409 base, local_members, variants):
410 # struct has local_members, optional base, and no variants
411 # union has base, variants, and no local_members
412 super().__init__(name, info, doc, ifcond, features)
413 self.meta = 'union' if variants else 'struct'
414 assert base is None or isinstance(base, str)
415 for m in local_members:
416 assert isinstance(m, QAPISchemaObjectTypeMember)
417 m.set_defined_in(name)
418 if variants is not None:
419 assert isinstance(variants, QAPISchemaVariants)
420 variants.set_defined_in(name)
421 self._base_name = base
422 self.base = None
423 self.local_members = local_members
424 self.variants = variants
425 self.members = None
427 def check(self, schema):
428 # This calls another type T's .check() exactly when the C
429 # struct emitted by gen_object() contains that T's C struct
430 # (pointers don't count).
431 if self.members is not None:
432 # A previous .check() completed: nothing to do
433 return
434 if self._checked:
435 # Recursed: C struct contains itself
436 raise QAPISemError(self.info,
437 "object %s contains itself" % self.name)
439 super().check(schema)
440 assert self._checked and self.members is None
442 seen = OrderedDict()
443 if self._base_name:
444 self.base = schema.resolve_type(self._base_name, self.info,
445 "'base'")
446 if (not isinstance(self.base, QAPISchemaObjectType)
447 or self.base.variants):
448 raise QAPISemError(
449 self.info,
450 "'base' requires a struct type, %s isn't"
451 % self.base.describe())
452 self.base.check(schema)
453 self.base.check_clash(self.info, seen)
454 for m in self.local_members:
455 m.check(schema)
456 m.check_clash(self.info, seen)
457 members = seen.values()
459 if self.variants:
460 self.variants.check(schema, seen)
461 self.variants.check_clash(self.info, seen)
463 self.members = members # mark completed
465 # Check that the members of this type do not cause duplicate JSON members,
466 # and update seen to track the members seen so far. Report any errors
467 # on behalf of info, which is not necessarily self.info
468 def check_clash(self, info, seen):
469 assert self._checked
470 for m in self.members:
471 m.check_clash(info, seen)
472 if self.variants:
473 self.variants.check_clash(info, seen)
475 def connect_doc(self, doc=None):
476 super().connect_doc(doc)
477 doc = doc or self.doc
478 if self.base and self.base.is_implicit():
479 self.base.connect_doc(doc)
480 for m in self.local_members:
481 m.connect_doc(doc)
483 def is_implicit(self):
484 # See QAPISchema._make_implicit_object_type(), as well as
485 # _def_predefineds()
486 return self.name.startswith('q_')
488 def is_empty(self):
489 assert self.members is not None
490 return not self.members and not self.variants
492 def has_conditional_members(self):
493 assert self.members is not None
494 return any(m.ifcond.is_present() for m in self.members)
496 def c_name(self):
497 assert self.name != 'q_empty'
498 return super().c_name()
500 def c_type(self):
501 assert not self.is_implicit()
502 return c_name(self.name) + POINTER_SUFFIX
504 def c_unboxed_type(self):
505 return c_name(self.name)
507 def json_type(self):
508 return 'object'
510 def visit(self, visitor):
511 super().visit(visitor)
512 visitor.visit_object_type(
513 self.name, self.info, self.ifcond, self.features,
514 self.base, self.local_members, self.variants)
515 visitor.visit_object_type_flat(
516 self.name, self.info, self.ifcond, self.features,
517 self.members, self.variants)
520 class QAPISchemaAlternateType(QAPISchemaType):
521 meta = 'alternate'
523 def __init__(self, name, info, doc, ifcond, features, variants):
524 super().__init__(name, info, doc, ifcond, features)
525 assert isinstance(variants, QAPISchemaVariants)
526 assert variants.tag_member
527 variants.set_defined_in(name)
528 variants.tag_member.set_defined_in(self.name)
529 self.variants = variants
531 def check(self, schema):
532 super().check(schema)
533 self.variants.tag_member.check(schema)
534 # Not calling self.variants.check_clash(), because there's nothing
535 # to clash with
536 self.variants.check(schema, {})
537 # Alternate branch names have no relation to the tag enum values;
538 # so we have to check for potential name collisions ourselves.
539 seen = {}
540 types_seen = {}
541 for v in self.variants.variants:
542 v.check_clash(self.info, seen)
543 qtype = v.type.alternate_qtype()
544 if not qtype:
545 raise QAPISemError(
546 self.info,
547 "%s cannot use %s"
548 % (v.describe(self.info), v.type.describe()))
549 conflicting = set([qtype])
550 if qtype == 'QTYPE_QSTRING':
551 if isinstance(v.type, QAPISchemaEnumType):
552 for m in v.type.members:
553 if m.name in ['on', 'off']:
554 conflicting.add('QTYPE_QBOOL')
555 if re.match(r'[-+0-9.]', m.name):
556 # lazy, could be tightened
557 conflicting.add('QTYPE_QNUM')
558 else:
559 conflicting.add('QTYPE_QNUM')
560 conflicting.add('QTYPE_QBOOL')
561 for qt in conflicting:
562 if qt in types_seen:
563 raise QAPISemError(
564 self.info,
565 "%s can't be distinguished from '%s'"
566 % (v.describe(self.info), types_seen[qt]))
567 types_seen[qt] = v.name
569 def connect_doc(self, doc=None):
570 super().connect_doc(doc)
571 doc = doc or self.doc
572 for v in self.variants.variants:
573 v.connect_doc(doc)
575 def c_type(self):
576 return c_name(self.name) + POINTER_SUFFIX
578 def json_type(self):
579 return 'value'
581 def visit(self, visitor):
582 super().visit(visitor)
583 visitor.visit_alternate_type(
584 self.name, self.info, self.ifcond, self.features, self.variants)
587 class QAPISchemaVariants:
588 def __init__(self, tag_name, info, tag_member, variants):
589 # Unions pass tag_name but not tag_member.
590 # Alternates pass tag_member but not tag_name.
591 # After check(), tag_member is always set.
592 assert bool(tag_member) != bool(tag_name)
593 assert (isinstance(tag_name, str) or
594 isinstance(tag_member, QAPISchemaObjectTypeMember))
595 for v in variants:
596 assert isinstance(v, QAPISchemaVariant)
597 self._tag_name = tag_name
598 self.info = info
599 self.tag_member = tag_member
600 self.variants = variants
602 def set_defined_in(self, name):
603 for v in self.variants:
604 v.set_defined_in(name)
606 def check(self, schema, seen):
607 if self._tag_name: # union
608 self.tag_member = seen.get(c_name(self._tag_name))
609 base = "'base'"
610 # Pointing to the base type when not implicit would be
611 # nice, but we don't know it here
612 if not self.tag_member or self._tag_name != self.tag_member.name:
613 raise QAPISemError(
614 self.info,
615 "discriminator '%s' is not a member of %s"
616 % (self._tag_name, base))
617 # Here we do:
618 base_type = schema.lookup_type(self.tag_member.defined_in)
619 assert base_type
620 if not base_type.is_implicit():
621 base = "base type '%s'" % self.tag_member.defined_in
622 if not isinstance(self.tag_member.type, QAPISchemaEnumType):
623 raise QAPISemError(
624 self.info,
625 "discriminator member '%s' of %s must be of enum type"
626 % (self._tag_name, base))
627 if self.tag_member.optional:
628 raise QAPISemError(
629 self.info,
630 "discriminator member '%s' of %s must not be optional"
631 % (self._tag_name, base))
632 if self.tag_member.ifcond.is_present():
633 raise QAPISemError(
634 self.info,
635 "discriminator member '%s' of %s must not be conditional"
636 % (self._tag_name, base))
637 else: # alternate
638 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
639 assert not self.tag_member.optional
640 assert not self.tag_member.ifcond.is_present()
641 if self._tag_name: # union
642 # branches that are not explicitly covered get an empty type
643 cases = {v.name for v in self.variants}
644 for m in self.tag_member.type.members:
645 if m.name not in cases:
646 v = QAPISchemaVariant(m.name, self.info,
647 'q_empty', m.ifcond)
648 v.set_defined_in(self.tag_member.defined_in)
649 self.variants.append(v)
650 if not self.variants:
651 raise QAPISemError(self.info, "union has no branches")
652 for v in self.variants:
653 v.check(schema)
654 # Union names must match enum values; alternate names are
655 # checked separately. Use 'seen' to tell the two apart.
656 if seen:
657 if v.name not in self.tag_member.type.member_names():
658 raise QAPISemError(
659 self.info,
660 "branch '%s' is not a value of %s"
661 % (v.name, self.tag_member.type.describe()))
662 if not isinstance(v.type, QAPISchemaObjectType):
663 raise QAPISemError(
664 self.info,
665 "%s cannot use %s"
666 % (v.describe(self.info), v.type.describe()))
667 v.type.check(schema)
669 def check_clash(self, info, seen):
670 for v in self.variants:
671 # Reset seen map for each variant, since qapi names from one
672 # branch do not affect another branch
673 v.type.check_clash(info, dict(seen))
676 class QAPISchemaMember:
677 """ Represents object members, enum members and features """
678 role = 'member'
680 def __init__(self, name, info, ifcond=None):
681 assert isinstance(name, str)
682 self.name = name
683 self.info = info
684 self.ifcond = ifcond or QAPISchemaIfCond()
685 self.defined_in = None
687 def set_defined_in(self, name):
688 assert not self.defined_in
689 self.defined_in = name
691 def check_clash(self, info, seen):
692 cname = c_name(self.name)
693 if cname in seen:
694 raise QAPISemError(
695 info,
696 "%s collides with %s"
697 % (self.describe(info), seen[cname].describe(info)))
698 seen[cname] = self
700 def connect_doc(self, doc):
701 if doc:
702 doc.connect_member(self)
704 def describe(self, info):
705 role = self.role
706 meta = 'type'
707 defined_in = self.defined_in
708 assert defined_in
710 if defined_in.startswith('q_obj_'):
711 # See QAPISchema._make_implicit_object_type() - reverse the
712 # mapping there to create a nice human-readable description
713 defined_in = defined_in[6:]
714 if defined_in.endswith('-arg'):
715 # Implicit type created for a command's dict 'data'
716 assert role == 'member'
717 role = 'parameter'
718 meta = 'command'
719 defined_in = defined_in[:-4]
720 elif defined_in.endswith('-base'):
721 # Implicit type created for a union's dict 'base'
722 role = 'base ' + role
723 defined_in = defined_in[:-5]
724 else:
725 assert False
727 if defined_in != info.defn_name:
728 return "%s '%s' of %s '%s'" % (role, self.name, meta, defined_in)
729 return "%s '%s'" % (role, self.name)
732 class QAPISchemaEnumMember(QAPISchemaMember):
733 role = 'value'
735 def __init__(self, name, info, ifcond=None, features=None):
736 super().__init__(name, info, ifcond)
737 for f in features or []:
738 assert isinstance(f, QAPISchemaFeature)
739 f.set_defined_in(name)
740 self.features = features or []
742 def connect_doc(self, doc):
743 super().connect_doc(doc)
744 if doc:
745 for f in self.features:
746 doc.connect_feature(f)
749 class QAPISchemaFeature(QAPISchemaMember):
750 role = 'feature'
752 def is_special(self):
753 return self.name in ('deprecated', 'unstable')
756 class QAPISchemaObjectTypeMember(QAPISchemaMember):
757 def __init__(self, name, info, typ, optional, ifcond=None, features=None):
758 super().__init__(name, info, ifcond)
759 assert isinstance(typ, str)
760 assert isinstance(optional, bool)
761 for f in features or []:
762 assert isinstance(f, QAPISchemaFeature)
763 f.set_defined_in(name)
764 self._type_name = typ
765 self.type = None
766 self.optional = optional
767 self.features = features or []
769 def need_has(self):
770 assert self.type
771 return self.optional and self.type.need_has_if_optional()
773 def check(self, schema):
774 assert self.defined_in
775 self.type = schema.resolve_type(self._type_name, self.info,
776 self.describe)
777 seen = {}
778 for f in self.features:
779 f.check_clash(self.info, seen)
781 def connect_doc(self, doc):
782 super().connect_doc(doc)
783 if doc:
784 for f in self.features:
785 doc.connect_feature(f)
788 class QAPISchemaVariant(QAPISchemaObjectTypeMember):
789 role = 'branch'
791 def __init__(self, name, info, typ, ifcond=None):
792 super().__init__(name, info, typ, False, ifcond)
795 class QAPISchemaCommand(QAPISchemaEntity):
796 meta = 'command'
798 def __init__(self, name, info, doc, ifcond, features,
799 arg_type, ret_type,
800 gen, success_response, boxed, allow_oob, allow_preconfig,
801 coroutine):
802 super().__init__(name, info, doc, ifcond, features)
803 assert not arg_type or isinstance(arg_type, str)
804 assert not ret_type or isinstance(ret_type, str)
805 self._arg_type_name = arg_type
806 self.arg_type = None
807 self._ret_type_name = ret_type
808 self.ret_type = None
809 self.gen = gen
810 self.success_response = success_response
811 self.boxed = boxed
812 self.allow_oob = allow_oob
813 self.allow_preconfig = allow_preconfig
814 self.coroutine = coroutine
816 def check(self, schema):
817 super().check(schema)
818 if self._arg_type_name:
819 self.arg_type = schema.resolve_type(
820 self._arg_type_name, self.info, "command's 'data'")
821 if not isinstance(self.arg_type, QAPISchemaObjectType):
822 raise QAPISemError(
823 self.info,
824 "command's 'data' cannot take %s"
825 % self.arg_type.describe())
826 if self.arg_type.variants and not self.boxed:
827 raise QAPISemError(
828 self.info,
829 "command's 'data' can take %s only with 'boxed': true"
830 % self.arg_type.describe())
831 self.arg_type.check(schema)
832 if self.arg_type.has_conditional_members() and not self.boxed:
833 raise QAPISemError(
834 self.info,
835 "conditional command arguments require 'boxed': true")
836 if self._ret_type_name:
837 self.ret_type = schema.resolve_type(
838 self._ret_type_name, self.info, "command's 'returns'")
839 if self.name not in self.info.pragma.command_returns_exceptions:
840 typ = self.ret_type
841 if isinstance(typ, QAPISchemaArrayType):
842 typ = self.ret_type.element_type
843 assert typ
844 if not isinstance(typ, QAPISchemaObjectType):
845 raise QAPISemError(
846 self.info,
847 "command's 'returns' cannot take %s"
848 % self.ret_type.describe())
850 def connect_doc(self, doc=None):
851 super().connect_doc(doc)
852 doc = doc or self.doc
853 if doc:
854 if self.arg_type and self.arg_type.is_implicit():
855 self.arg_type.connect_doc(doc)
857 def visit(self, visitor):
858 super().visit(visitor)
859 visitor.visit_command(
860 self.name, self.info, self.ifcond, self.features,
861 self.arg_type, self.ret_type, self.gen, self.success_response,
862 self.boxed, self.allow_oob, self.allow_preconfig,
863 self.coroutine)
866 class QAPISchemaEvent(QAPISchemaEntity):
867 meta = 'event'
869 def __init__(self, name, info, doc, ifcond, features, arg_type, boxed):
870 super().__init__(name, info, doc, ifcond, features)
871 assert not arg_type or isinstance(arg_type, str)
872 self._arg_type_name = arg_type
873 self.arg_type = None
874 self.boxed = boxed
876 def check(self, schema):
877 super().check(schema)
878 if self._arg_type_name:
879 self.arg_type = schema.resolve_type(
880 self._arg_type_name, self.info, "event's 'data'")
881 if not isinstance(self.arg_type, QAPISchemaObjectType):
882 raise QAPISemError(
883 self.info,
884 "event's 'data' cannot take %s"
885 % self.arg_type.describe())
886 if self.arg_type.variants and not self.boxed:
887 raise QAPISemError(
888 self.info,
889 "event's 'data' can take %s only with 'boxed': true"
890 % self.arg_type.describe())
891 self.arg_type.check(schema)
892 if self.arg_type.has_conditional_members() and not self.boxed:
893 raise QAPISemError(
894 self.info,
895 "conditional event arguments require 'boxed': true")
897 def connect_doc(self, doc=None):
898 super().connect_doc(doc)
899 doc = doc or self.doc
900 if doc:
901 if self.arg_type and self.arg_type.is_implicit():
902 self.arg_type.connect_doc(doc)
904 def visit(self, visitor):
905 super().visit(visitor)
906 visitor.visit_event(
907 self.name, self.info, self.ifcond, self.features,
908 self.arg_type, self.boxed)
911 class QAPISchema:
912 def __init__(self, fname):
913 self.fname = fname
915 try:
916 parser = QAPISchemaParser(fname)
917 except OSError as err:
918 raise QAPIError(
919 f"can't read schema file '{fname}': {err.strerror}"
920 ) from err
922 exprs = check_exprs(parser.exprs)
923 self.docs = parser.docs
924 self._entity_list = []
925 self._entity_dict = {}
926 self._module_dict = OrderedDict()
927 self._schema_dir = os.path.dirname(fname)
928 self._make_module(QAPISchemaModule.BUILTIN_MODULE_NAME)
929 self._make_module(fname)
930 self._predefining = True
931 self._def_predefineds()
932 self._predefining = False
933 self._def_exprs(exprs)
934 self.check()
936 def _def_entity(self, ent):
937 # Only the predefined types are allowed to not have info
938 assert ent.info or self._predefining
939 self._entity_list.append(ent)
940 if ent.name is None:
941 return
942 # TODO reject names that differ only in '_' vs. '.' vs. '-',
943 # because they're liable to clash in generated C.
944 other_ent = self._entity_dict.get(ent.name)
945 if other_ent:
946 if other_ent.info:
947 where = QAPISourceError(other_ent.info, "previous definition")
948 raise QAPISemError(
949 ent.info,
950 "'%s' is already defined\n%s" % (ent.name, where))
951 raise QAPISemError(
952 ent.info, "%s is already defined" % other_ent.describe())
953 self._entity_dict[ent.name] = ent
955 def lookup_entity(self, name, typ=None):
956 ent = self._entity_dict.get(name)
957 if typ and not isinstance(ent, typ):
958 return None
959 return ent
961 def lookup_type(self, name):
962 return self.lookup_entity(name, QAPISchemaType)
964 def resolve_type(self, name, info, what):
965 typ = self.lookup_type(name)
966 if not typ:
967 if callable(what):
968 what = what(info)
969 raise QAPISemError(
970 info, "%s uses unknown type '%s'" % (what, name))
971 return typ
973 def _module_name(self, fname: str) -> str:
974 if QAPISchemaModule.is_system_module(fname):
975 return fname
976 return os.path.relpath(fname, self._schema_dir)
978 def _make_module(self, fname):
979 name = self._module_name(fname)
980 if name not in self._module_dict:
981 self._module_dict[name] = QAPISchemaModule(name)
982 return self._module_dict[name]
984 def module_by_fname(self, fname):
985 name = self._module_name(fname)
986 return self._module_dict[name]
988 def _def_include(self, expr: QAPIExpression):
989 include = expr['include']
990 assert expr.doc is None
991 self._def_entity(
992 QAPISchemaInclude(self._make_module(include), expr.info))
994 def _def_builtin_type(self, name, json_type, c_type):
995 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
996 # Instantiating only the arrays that are actually used would
997 # be nice, but we can't as long as their generated code
998 # (qapi-builtin-types.[ch]) may be shared by some other
999 # schema.
1000 self._make_array_type(name, None)
1002 def _def_predefineds(self):
1003 for t in [('str', 'string', 'char' + POINTER_SUFFIX),
1004 ('number', 'number', 'double'),
1005 ('int', 'int', 'int64_t'),
1006 ('int8', 'int', 'int8_t'),
1007 ('int16', 'int', 'int16_t'),
1008 ('int32', 'int', 'int32_t'),
1009 ('int64', 'int', 'int64_t'),
1010 ('uint8', 'int', 'uint8_t'),
1011 ('uint16', 'int', 'uint16_t'),
1012 ('uint32', 'int', 'uint32_t'),
1013 ('uint64', 'int', 'uint64_t'),
1014 ('size', 'int', 'uint64_t'),
1015 ('bool', 'boolean', 'bool'),
1016 ('any', 'value', 'QObject' + POINTER_SUFFIX),
1017 ('null', 'null', 'QNull' + POINTER_SUFFIX)]:
1018 self._def_builtin_type(*t)
1019 self.the_empty_object_type = QAPISchemaObjectType(
1020 'q_empty', None, None, None, None, None, [], None)
1021 self._def_entity(self.the_empty_object_type)
1023 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1024 'qbool']
1025 qtype_values = self._make_enum_members(
1026 [{'name': n} for n in qtypes], None)
1028 self._def_entity(QAPISchemaEnumType('QType', None, None, None, None,
1029 qtype_values, 'QTYPE'))
1031 def _make_features(self, features, info):
1032 if features is None:
1033 return []
1034 return [QAPISchemaFeature(f['name'], info,
1035 QAPISchemaIfCond(f.get('if')))
1036 for f in features]
1038 def _make_enum_member(self, name, ifcond, features, info):
1039 return QAPISchemaEnumMember(name, info,
1040 QAPISchemaIfCond(ifcond),
1041 self._make_features(features, info))
1043 def _make_enum_members(self, values, info):
1044 return [self._make_enum_member(v['name'], v.get('if'),
1045 v.get('features'), info)
1046 for v in values]
1048 def _make_array_type(self, element_type, info):
1049 name = element_type + 'List' # reserved by check_defn_name_str()
1050 if not self.lookup_type(name):
1051 self._def_entity(QAPISchemaArrayType(name, info, element_type))
1052 return name
1054 def _make_implicit_object_type(self, name, info, ifcond, role, members):
1055 if not members:
1056 return None
1057 # See also QAPISchemaObjectTypeMember.describe()
1058 name = 'q_obj_%s-%s' % (name, role)
1059 typ = self.lookup_entity(name, QAPISchemaObjectType)
1060 if typ:
1061 # The implicit object type has multiple users. This can
1062 # only be a duplicate definition, which will be flagged
1063 # later.
1064 pass
1065 else:
1066 self._def_entity(QAPISchemaObjectType(
1067 name, info, None, ifcond, None, None, members, None))
1068 return name
1070 def _def_enum_type(self, expr: QAPIExpression):
1071 name = expr['enum']
1072 data = expr['data']
1073 prefix = expr.get('prefix')
1074 ifcond = QAPISchemaIfCond(expr.get('if'))
1075 info = expr.info
1076 features = self._make_features(expr.get('features'), info)
1077 self._def_entity(QAPISchemaEnumType(
1078 name, info, expr.doc, ifcond, features,
1079 self._make_enum_members(data, info), prefix))
1081 def _make_member(self, name, typ, ifcond, features, info):
1082 optional = False
1083 if name.startswith('*'):
1084 name = name[1:]
1085 optional = True
1086 if isinstance(typ, list):
1087 assert len(typ) == 1
1088 typ = self._make_array_type(typ[0], info)
1089 return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond,
1090 self._make_features(features, info))
1092 def _make_members(self, data, info):
1093 return [self._make_member(key, value['type'],
1094 QAPISchemaIfCond(value.get('if')),
1095 value.get('features'), info)
1096 for (key, value) in data.items()]
1098 def _def_struct_type(self, expr: QAPIExpression):
1099 name = expr['struct']
1100 base = expr.get('base')
1101 data = expr['data']
1102 info = expr.info
1103 ifcond = QAPISchemaIfCond(expr.get('if'))
1104 features = self._make_features(expr.get('features'), info)
1105 self._def_entity(QAPISchemaObjectType(
1106 name, info, expr.doc, ifcond, features, base,
1107 self._make_members(data, info),
1108 None))
1110 def _make_variant(self, case, typ, ifcond, info):
1111 if isinstance(typ, list):
1112 assert len(typ) == 1
1113 typ = self._make_array_type(typ[0], info)
1114 return QAPISchemaVariant(case, info, typ, ifcond)
1116 def _def_union_type(self, expr: QAPIExpression):
1117 name = expr['union']
1118 base = expr['base']
1119 tag_name = expr['discriminator']
1120 data = expr['data']
1121 assert isinstance(data, dict)
1122 info = expr.info
1123 ifcond = QAPISchemaIfCond(expr.get('if'))
1124 features = self._make_features(expr.get('features'), info)
1125 if isinstance(base, dict):
1126 base = self._make_implicit_object_type(
1127 name, info, ifcond,
1128 'base', self._make_members(base, info))
1129 variants = [
1130 self._make_variant(key, value['type'],
1131 QAPISchemaIfCond(value.get('if')),
1132 info)
1133 for (key, value) in data.items()]
1134 members: List[QAPISchemaObjectTypeMember] = []
1135 self._def_entity(
1136 QAPISchemaObjectType(name, info, expr.doc, ifcond, features,
1137 base, members,
1138 QAPISchemaVariants(
1139 tag_name, info, None, variants)))
1141 def _def_alternate_type(self, expr: QAPIExpression):
1142 name = expr['alternate']
1143 data = expr['data']
1144 assert isinstance(data, dict)
1145 ifcond = QAPISchemaIfCond(expr.get('if'))
1146 info = expr.info
1147 features = self._make_features(expr.get('features'), info)
1148 variants = [
1149 self._make_variant(key, value['type'],
1150 QAPISchemaIfCond(value.get('if')),
1151 info)
1152 for (key, value) in data.items()]
1153 tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
1154 self._def_entity(
1155 QAPISchemaAlternateType(
1156 name, info, expr.doc, ifcond, features,
1157 QAPISchemaVariants(None, info, tag_member, variants)))
1159 def _def_command(self, expr: QAPIExpression):
1160 name = expr['command']
1161 data = expr.get('data')
1162 rets = expr.get('returns')
1163 gen = expr.get('gen', True)
1164 success_response = expr.get('success-response', True)
1165 boxed = expr.get('boxed', False)
1166 allow_oob = expr.get('allow-oob', False)
1167 allow_preconfig = expr.get('allow-preconfig', False)
1168 coroutine = expr.get('coroutine', False)
1169 ifcond = QAPISchemaIfCond(expr.get('if'))
1170 info = expr.info
1171 features = self._make_features(expr.get('features'), info)
1172 if isinstance(data, OrderedDict):
1173 data = self._make_implicit_object_type(
1174 name, info, ifcond,
1175 'arg', self._make_members(data, info))
1176 if isinstance(rets, list):
1177 assert len(rets) == 1
1178 rets = self._make_array_type(rets[0], info)
1179 self._def_entity(QAPISchemaCommand(name, info, expr.doc, ifcond,
1180 features, data, rets,
1181 gen, success_response,
1182 boxed, allow_oob, allow_preconfig,
1183 coroutine))
1185 def _def_event(self, expr: QAPIExpression):
1186 name = expr['event']
1187 data = expr.get('data')
1188 boxed = expr.get('boxed', False)
1189 ifcond = QAPISchemaIfCond(expr.get('if'))
1190 info = expr.info
1191 features = self._make_features(expr.get('features'), info)
1192 if isinstance(data, OrderedDict):
1193 data = self._make_implicit_object_type(
1194 name, info, ifcond,
1195 'arg', self._make_members(data, info))
1196 self._def_entity(QAPISchemaEvent(name, info, expr.doc, ifcond,
1197 features, data, boxed))
1199 def _def_exprs(self, exprs):
1200 for expr in exprs:
1201 if 'enum' in expr:
1202 self._def_enum_type(expr)
1203 elif 'struct' in expr:
1204 self._def_struct_type(expr)
1205 elif 'union' in expr:
1206 self._def_union_type(expr)
1207 elif 'alternate' in expr:
1208 self._def_alternate_type(expr)
1209 elif 'command' in expr:
1210 self._def_command(expr)
1211 elif 'event' in expr:
1212 self._def_event(expr)
1213 elif 'include' in expr:
1214 self._def_include(expr)
1215 else:
1216 assert False
1218 def check(self):
1219 for ent in self._entity_list:
1220 ent.check(self)
1221 ent.connect_doc()
1222 for ent in self._entity_list:
1223 ent.set_module(self)
1224 for doc in self.docs:
1225 doc.check()
1227 def visit(self, visitor):
1228 visitor.visit_begin(self)
1229 for mod in self._module_dict.values():
1230 mod.visit(visitor)
1231 visitor.visit_end()