Merge tag 'block-pull-request' of https://gitlab.com/stefanha/qemu into staging
[qemu/ar7.git] / scripts / qapi / schema.py
blob3728340c37290cca3bef2434fb68136dc9f98588
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 check(self, schema):
257 QAPISchemaEntity.check(self, schema)
258 for feat in self.features:
259 if feat.is_special():
260 raise QAPISemError(
261 self.info,
262 f"feature '{feat.name}' is not supported for types")
264 def describe(self):
265 assert self.meta
266 return "%s type '%s'" % (self.meta, self.name)
269 class QAPISchemaBuiltinType(QAPISchemaType):
270 meta = 'built-in'
272 def __init__(self, name, json_type, c_type):
273 super().__init__(name, None, None)
274 assert not c_type or isinstance(c_type, str)
275 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
276 'value')
277 self._json_type_name = json_type
278 self._c_type_name = c_type
280 def c_name(self):
281 return self.name
283 def c_type(self):
284 return self._c_type_name
286 def c_param_type(self):
287 if self.name == 'str':
288 return 'const ' + self._c_type_name
289 return self._c_type_name
291 def json_type(self):
292 return self._json_type_name
294 def doc_type(self):
295 return self.json_type()
297 def visit(self, visitor):
298 super().visit(visitor)
299 visitor.visit_builtin_type(self.name, self.info, self.json_type())
302 class QAPISchemaEnumType(QAPISchemaType):
303 meta = 'enum'
305 def __init__(self, name, info, doc, ifcond, features, members, prefix):
306 super().__init__(name, info, doc, ifcond, features)
307 for m in members:
308 assert isinstance(m, QAPISchemaEnumMember)
309 m.set_defined_in(name)
310 assert prefix is None or isinstance(prefix, str)
311 self.members = members
312 self.prefix = prefix
314 def check(self, schema):
315 super().check(schema)
316 seen = {}
317 for m in self.members:
318 m.check_clash(self.info, seen)
320 def connect_doc(self, doc=None):
321 super().connect_doc(doc)
322 doc = doc or self.doc
323 for m in self.members:
324 m.connect_doc(doc)
326 def is_implicit(self):
327 # See QAPISchema._def_predefineds()
328 return self.name == 'QType'
330 def c_type(self):
331 return c_name(self.name)
333 def member_names(self):
334 return [m.name for m in self.members]
336 def json_type(self):
337 return 'string'
339 def visit(self, visitor):
340 super().visit(visitor)
341 visitor.visit_enum_type(
342 self.name, self.info, self.ifcond, self.features,
343 self.members, self.prefix)
346 class QAPISchemaArrayType(QAPISchemaType):
347 meta = 'array'
349 def __init__(self, name, info, element_type):
350 super().__init__(name, info, None)
351 assert isinstance(element_type, str)
352 self._element_type_name = element_type
353 self.element_type = None
355 def check(self, schema):
356 super().check(schema)
357 self.element_type = schema.resolve_type(
358 self._element_type_name, self.info,
359 self.info and self.info.defn_meta)
360 assert not isinstance(self.element_type, QAPISchemaArrayType)
362 def set_module(self, schema):
363 self._set_module(schema, self.element_type.info)
365 @property
366 def ifcond(self):
367 assert self._checked
368 return self.element_type.ifcond
370 def is_implicit(self):
371 return True
373 def c_type(self):
374 return c_name(self.name) + POINTER_SUFFIX
376 def json_type(self):
377 return 'array'
379 def doc_type(self):
380 elt_doc_type = self.element_type.doc_type()
381 if not elt_doc_type:
382 return None
383 return 'array of ' + elt_doc_type
385 def visit(self, visitor):
386 super().visit(visitor)
387 visitor.visit_array_type(self.name, self.info, self.ifcond,
388 self.element_type)
390 def describe(self):
391 assert self.meta
392 return "%s type ['%s']" % (self.meta, self._element_type_name)
395 class QAPISchemaObjectType(QAPISchemaType):
396 def __init__(self, name, info, doc, ifcond, features,
397 base, local_members, variants):
398 # struct has local_members, optional base, and no variants
399 # union has base, variants, and no local_members
400 super().__init__(name, info, doc, ifcond, features)
401 self.meta = 'union' if variants else 'struct'
402 assert base is None or isinstance(base, str)
403 for m in local_members:
404 assert isinstance(m, QAPISchemaObjectTypeMember)
405 m.set_defined_in(name)
406 if variants is not None:
407 assert isinstance(variants, QAPISchemaVariants)
408 variants.set_defined_in(name)
409 self._base_name = base
410 self.base = None
411 self.local_members = local_members
412 self.variants = variants
413 self.members = None
415 def check(self, schema):
416 # This calls another type T's .check() exactly when the C
417 # struct emitted by gen_object() contains that T's C struct
418 # (pointers don't count).
419 if self.members is not None:
420 # A previous .check() completed: nothing to do
421 return
422 if self._checked:
423 # Recursed: C struct contains itself
424 raise QAPISemError(self.info,
425 "object %s contains itself" % self.name)
427 super().check(schema)
428 assert self._checked and self.members is None
430 seen = OrderedDict()
431 if self._base_name:
432 self.base = schema.resolve_type(self._base_name, self.info,
433 "'base'")
434 if (not isinstance(self.base, QAPISchemaObjectType)
435 or self.base.variants):
436 raise QAPISemError(
437 self.info,
438 "'base' requires a struct type, %s isn't"
439 % self.base.describe())
440 self.base.check(schema)
441 self.base.check_clash(self.info, seen)
442 for m in self.local_members:
443 m.check(schema)
444 m.check_clash(self.info, seen)
445 members = seen.values()
447 if self.variants:
448 self.variants.check(schema, seen)
449 self.variants.check_clash(self.info, seen)
451 self.members = members # mark completed
453 # Check that the members of this type do not cause duplicate JSON members,
454 # and update seen to track the members seen so far. Report any errors
455 # on behalf of info, which is not necessarily self.info
456 def check_clash(self, info, seen):
457 assert self._checked
458 assert not self.variants # not implemented
459 for m in self.members:
460 m.check_clash(info, seen)
462 def connect_doc(self, doc=None):
463 super().connect_doc(doc)
464 doc = doc or self.doc
465 if self.base and self.base.is_implicit():
466 self.base.connect_doc(doc)
467 for m in self.local_members:
468 m.connect_doc(doc)
470 def is_implicit(self):
471 # See QAPISchema._make_implicit_object_type(), as well as
472 # _def_predefineds()
473 return self.name.startswith('q_')
475 def is_empty(self):
476 assert self.members is not None
477 return not self.members and not self.variants
479 def c_name(self):
480 assert self.name != 'q_empty'
481 return super().c_name()
483 def c_type(self):
484 assert not self.is_implicit()
485 return c_name(self.name) + POINTER_SUFFIX
487 def c_unboxed_type(self):
488 return c_name(self.name)
490 def json_type(self):
491 return 'object'
493 def visit(self, visitor):
494 super().visit(visitor)
495 visitor.visit_object_type(
496 self.name, self.info, self.ifcond, self.features,
497 self.base, self.local_members, self.variants)
498 visitor.visit_object_type_flat(
499 self.name, self.info, self.ifcond, self.features,
500 self.members, self.variants)
503 class QAPISchemaAlternateType(QAPISchemaType):
504 meta = 'alternate'
506 def __init__(self, name, info, doc, ifcond, features, variants):
507 super().__init__(name, info, doc, ifcond, features)
508 assert isinstance(variants, QAPISchemaVariants)
509 assert variants.tag_member
510 variants.set_defined_in(name)
511 variants.tag_member.set_defined_in(self.name)
512 self.variants = variants
514 def check(self, schema):
515 super().check(schema)
516 self.variants.tag_member.check(schema)
517 # Not calling self.variants.check_clash(), because there's nothing
518 # to clash with
519 self.variants.check(schema, {})
520 # Alternate branch names have no relation to the tag enum values;
521 # so we have to check for potential name collisions ourselves.
522 seen = {}
523 types_seen = {}
524 for v in self.variants.variants:
525 v.check_clash(self.info, seen)
526 qtype = v.type.alternate_qtype()
527 if not qtype:
528 raise QAPISemError(
529 self.info,
530 "%s cannot use %s"
531 % (v.describe(self.info), v.type.describe()))
532 conflicting = set([qtype])
533 if qtype == 'QTYPE_QSTRING':
534 if isinstance(v.type, QAPISchemaEnumType):
535 for m in v.type.members:
536 if m.name in ['on', 'off']:
537 conflicting.add('QTYPE_QBOOL')
538 if re.match(r'[-+0-9.]', m.name):
539 # lazy, could be tightened
540 conflicting.add('QTYPE_QNUM')
541 else:
542 conflicting.add('QTYPE_QNUM')
543 conflicting.add('QTYPE_QBOOL')
544 for qt in conflicting:
545 if qt in types_seen:
546 raise QAPISemError(
547 self.info,
548 "%s can't be distinguished from '%s'"
549 % (v.describe(self.info), types_seen[qt]))
550 types_seen[qt] = v.name
552 def connect_doc(self, doc=None):
553 super().connect_doc(doc)
554 doc = doc or self.doc
555 for v in self.variants.variants:
556 v.connect_doc(doc)
558 def c_type(self):
559 return c_name(self.name) + POINTER_SUFFIX
561 def json_type(self):
562 return 'value'
564 def visit(self, visitor):
565 super().visit(visitor)
566 visitor.visit_alternate_type(
567 self.name, self.info, self.ifcond, self.features, self.variants)
570 class QAPISchemaVariants:
571 def __init__(self, tag_name, info, tag_member, variants):
572 # Unions pass tag_name but not tag_member.
573 # Alternates pass tag_member but not tag_name.
574 # After check(), tag_member is always set.
575 assert bool(tag_member) != bool(tag_name)
576 assert (isinstance(tag_name, str) or
577 isinstance(tag_member, QAPISchemaObjectTypeMember))
578 for v in variants:
579 assert isinstance(v, QAPISchemaVariant)
580 self._tag_name = tag_name
581 self.info = info
582 self.tag_member = tag_member
583 self.variants = variants
585 def set_defined_in(self, name):
586 for v in self.variants:
587 v.set_defined_in(name)
589 def check(self, schema, seen):
590 if self._tag_name: # union
591 self.tag_member = seen.get(c_name(self._tag_name))
592 base = "'base'"
593 # Pointing to the base type when not implicit would be
594 # nice, but we don't know it here
595 if not self.tag_member or self._tag_name != self.tag_member.name:
596 raise QAPISemError(
597 self.info,
598 "discriminator '%s' is not a member of %s"
599 % (self._tag_name, base))
600 # Here we do:
601 base_type = schema.lookup_type(self.tag_member.defined_in)
602 assert base_type
603 if not base_type.is_implicit():
604 base = "base type '%s'" % self.tag_member.defined_in
605 if not isinstance(self.tag_member.type, QAPISchemaEnumType):
606 raise QAPISemError(
607 self.info,
608 "discriminator member '%s' of %s must be of enum type"
609 % (self._tag_name, base))
610 if self.tag_member.optional:
611 raise QAPISemError(
612 self.info,
613 "discriminator member '%s' of %s must not be optional"
614 % (self._tag_name, base))
615 if self.tag_member.ifcond.is_present():
616 raise QAPISemError(
617 self.info,
618 "discriminator member '%s' of %s must not be conditional"
619 % (self._tag_name, base))
620 else: # alternate
621 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
622 assert not self.tag_member.optional
623 assert not self.tag_member.ifcond.is_present()
624 if self._tag_name: # union
625 # branches that are not explicitly covered get an empty type
626 cases = {v.name for v in self.variants}
627 for m in self.tag_member.type.members:
628 if m.name not in cases:
629 v = QAPISchemaVariant(m.name, self.info,
630 'q_empty', m.ifcond)
631 v.set_defined_in(self.tag_member.defined_in)
632 self.variants.append(v)
633 if not self.variants:
634 raise QAPISemError(self.info, "union has no branches")
635 for v in self.variants:
636 v.check(schema)
637 # Union names must match enum values; alternate names are
638 # checked separately. Use 'seen' to tell the two apart.
639 if seen:
640 if v.name not in self.tag_member.type.member_names():
641 raise QAPISemError(
642 self.info,
643 "branch '%s' is not a value of %s"
644 % (v.name, self.tag_member.type.describe()))
645 if (not isinstance(v.type, QAPISchemaObjectType)
646 or v.type.variants):
647 raise QAPISemError(
648 self.info,
649 "%s cannot use %s"
650 % (v.describe(self.info), v.type.describe()))
651 v.type.check(schema)
653 def check_clash(self, info, seen):
654 for v in self.variants:
655 # Reset seen map for each variant, since qapi names from one
656 # branch do not affect another branch
657 v.type.check_clash(info, dict(seen))
660 class QAPISchemaMember:
661 """ Represents object members, enum members and features """
662 role = 'member'
664 def __init__(self, name, info, ifcond=None):
665 assert isinstance(name, str)
666 self.name = name
667 self.info = info
668 self.ifcond = ifcond or QAPISchemaIfCond()
669 self.defined_in = None
671 def set_defined_in(self, name):
672 assert not self.defined_in
673 self.defined_in = name
675 def check_clash(self, info, seen):
676 cname = c_name(self.name)
677 if cname in seen:
678 raise QAPISemError(
679 info,
680 "%s collides with %s"
681 % (self.describe(info), seen[cname].describe(info)))
682 seen[cname] = self
684 def connect_doc(self, doc):
685 if doc:
686 doc.connect_member(self)
688 def describe(self, info):
689 role = self.role
690 defined_in = self.defined_in
691 assert defined_in
693 if defined_in.startswith('q_obj_'):
694 # See QAPISchema._make_implicit_object_type() - reverse the
695 # mapping there to create a nice human-readable description
696 defined_in = defined_in[6:]
697 if defined_in.endswith('-arg'):
698 # Implicit type created for a command's dict 'data'
699 assert role == 'member'
700 role = 'parameter'
701 elif defined_in.endswith('-base'):
702 # Implicit type created for a union's dict 'base'
703 role = 'base ' + role
704 else:
705 assert False
706 elif defined_in != info.defn_name:
707 return "%s '%s' of type '%s'" % (role, self.name, defined_in)
708 return "%s '%s'" % (role, self.name)
711 class QAPISchemaEnumMember(QAPISchemaMember):
712 role = 'value'
714 def __init__(self, name, info, ifcond=None, features=None):
715 super().__init__(name, info, ifcond)
716 for f in features or []:
717 assert isinstance(f, QAPISchemaFeature)
718 f.set_defined_in(name)
719 self.features = features or []
721 def connect_doc(self, doc):
722 super().connect_doc(doc)
723 if doc:
724 for f in self.features:
725 doc.connect_feature(f)
728 class QAPISchemaFeature(QAPISchemaMember):
729 role = 'feature'
731 def is_special(self):
732 return self.name in ('deprecated', 'unstable')
735 class QAPISchemaObjectTypeMember(QAPISchemaMember):
736 def __init__(self, name, info, typ, optional, ifcond=None, features=None):
737 super().__init__(name, info, ifcond)
738 assert isinstance(typ, str)
739 assert isinstance(optional, bool)
740 for f in features or []:
741 assert isinstance(f, QAPISchemaFeature)
742 f.set_defined_in(name)
743 self._type_name = typ
744 self.type = None
745 self.optional = optional
746 self.features = features or []
748 def check(self, schema):
749 assert self.defined_in
750 self.type = schema.resolve_type(self._type_name, self.info,
751 self.describe)
752 seen = {}
753 for f in self.features:
754 f.check_clash(self.info, seen)
756 def connect_doc(self, doc):
757 super().connect_doc(doc)
758 if doc:
759 for f in self.features:
760 doc.connect_feature(f)
763 class QAPISchemaVariant(QAPISchemaObjectTypeMember):
764 role = 'branch'
766 def __init__(self, name, info, typ, ifcond=None):
767 super().__init__(name, info, typ, False, ifcond)
770 class QAPISchemaCommand(QAPISchemaEntity):
771 meta = 'command'
773 def __init__(self, name, info, doc, ifcond, features,
774 arg_type, ret_type,
775 gen, success_response, boxed, allow_oob, allow_preconfig,
776 coroutine):
777 super().__init__(name, info, doc, ifcond, features)
778 assert not arg_type or isinstance(arg_type, str)
779 assert not ret_type or isinstance(ret_type, str)
780 self._arg_type_name = arg_type
781 self.arg_type = None
782 self._ret_type_name = ret_type
783 self.ret_type = None
784 self.gen = gen
785 self.success_response = success_response
786 self.boxed = boxed
787 self.allow_oob = allow_oob
788 self.allow_preconfig = allow_preconfig
789 self.coroutine = coroutine
791 def check(self, schema):
792 super().check(schema)
793 if self._arg_type_name:
794 self.arg_type = schema.resolve_type(
795 self._arg_type_name, self.info, "command's 'data'")
796 if not isinstance(self.arg_type, QAPISchemaObjectType):
797 raise QAPISemError(
798 self.info,
799 "command's 'data' cannot take %s"
800 % self.arg_type.describe())
801 if self.arg_type.variants and not self.boxed:
802 raise QAPISemError(
803 self.info,
804 "command's 'data' can take %s only with 'boxed': true"
805 % self.arg_type.describe())
806 if self._ret_type_name:
807 self.ret_type = schema.resolve_type(
808 self._ret_type_name, self.info, "command's 'returns'")
809 if self.name not in self.info.pragma.command_returns_exceptions:
810 typ = self.ret_type
811 if isinstance(typ, QAPISchemaArrayType):
812 typ = self.ret_type.element_type
813 assert typ
814 if not isinstance(typ, QAPISchemaObjectType):
815 raise QAPISemError(
816 self.info,
817 "command's 'returns' cannot take %s"
818 % self.ret_type.describe())
820 def connect_doc(self, doc=None):
821 super().connect_doc(doc)
822 doc = doc or self.doc
823 if doc:
824 if self.arg_type and self.arg_type.is_implicit():
825 self.arg_type.connect_doc(doc)
827 def visit(self, visitor):
828 super().visit(visitor)
829 visitor.visit_command(
830 self.name, self.info, self.ifcond, self.features,
831 self.arg_type, self.ret_type, self.gen, self.success_response,
832 self.boxed, self.allow_oob, self.allow_preconfig,
833 self.coroutine)
836 class QAPISchemaEvent(QAPISchemaEntity):
837 meta = 'event'
839 def __init__(self, name, info, doc, ifcond, features, arg_type, boxed):
840 super().__init__(name, info, doc, ifcond, features)
841 assert not arg_type or isinstance(arg_type, str)
842 self._arg_type_name = arg_type
843 self.arg_type = None
844 self.boxed = boxed
846 def check(self, schema):
847 super().check(schema)
848 if self._arg_type_name:
849 self.arg_type = schema.resolve_type(
850 self._arg_type_name, self.info, "event's 'data'")
851 if not isinstance(self.arg_type, QAPISchemaObjectType):
852 raise QAPISemError(
853 self.info,
854 "event's 'data' cannot take %s"
855 % self.arg_type.describe())
856 if self.arg_type.variants and not self.boxed:
857 raise QAPISemError(
858 self.info,
859 "event's 'data' can take %s only with 'boxed': true"
860 % self.arg_type.describe())
862 def connect_doc(self, doc=None):
863 super().connect_doc(doc)
864 doc = doc or self.doc
865 if doc:
866 if self.arg_type and self.arg_type.is_implicit():
867 self.arg_type.connect_doc(doc)
869 def visit(self, visitor):
870 super().visit(visitor)
871 visitor.visit_event(
872 self.name, self.info, self.ifcond, self.features,
873 self.arg_type, self.boxed)
876 class QAPISchema:
877 def __init__(self, fname):
878 self.fname = fname
880 try:
881 parser = QAPISchemaParser(fname)
882 except OSError as err:
883 raise QAPIError(
884 f"can't read schema file '{fname}': {err.strerror}"
885 ) from err
887 exprs = check_exprs(parser.exprs)
888 self.docs = parser.docs
889 self._entity_list = []
890 self._entity_dict = {}
891 self._module_dict = OrderedDict()
892 self._schema_dir = os.path.dirname(fname)
893 self._make_module(QAPISchemaModule.BUILTIN_MODULE_NAME)
894 self._make_module(fname)
895 self._predefining = True
896 self._def_predefineds()
897 self._predefining = False
898 self._def_exprs(exprs)
899 self.check()
901 def _def_entity(self, ent):
902 # Only the predefined types are allowed to not have info
903 assert ent.info or self._predefining
904 self._entity_list.append(ent)
905 if ent.name is None:
906 return
907 # TODO reject names that differ only in '_' vs. '.' vs. '-',
908 # because they're liable to clash in generated C.
909 other_ent = self._entity_dict.get(ent.name)
910 if other_ent:
911 if other_ent.info:
912 where = QAPISourceError(other_ent.info, "previous definition")
913 raise QAPISemError(
914 ent.info,
915 "'%s' is already defined\n%s" % (ent.name, where))
916 raise QAPISemError(
917 ent.info, "%s is already defined" % other_ent.describe())
918 self._entity_dict[ent.name] = ent
920 def lookup_entity(self, name, typ=None):
921 ent = self._entity_dict.get(name)
922 if typ and not isinstance(ent, typ):
923 return None
924 return ent
926 def lookup_type(self, name):
927 return self.lookup_entity(name, QAPISchemaType)
929 def resolve_type(self, name, info, what):
930 typ = self.lookup_type(name)
931 if not typ:
932 if callable(what):
933 what = what(info)
934 raise QAPISemError(
935 info, "%s uses unknown type '%s'" % (what, name))
936 return typ
938 def _module_name(self, fname: str) -> str:
939 if QAPISchemaModule.is_system_module(fname):
940 return fname
941 return os.path.relpath(fname, self._schema_dir)
943 def _make_module(self, fname):
944 name = self._module_name(fname)
945 if name not in self._module_dict:
946 self._module_dict[name] = QAPISchemaModule(name)
947 return self._module_dict[name]
949 def module_by_fname(self, fname):
950 name = self._module_name(fname)
951 return self._module_dict[name]
953 def _def_include(self, expr, info, doc):
954 include = expr['include']
955 assert doc is None
956 self._def_entity(QAPISchemaInclude(self._make_module(include), info))
958 def _def_builtin_type(self, name, json_type, c_type):
959 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
960 # Instantiating only the arrays that are actually used would
961 # be nice, but we can't as long as their generated code
962 # (qapi-builtin-types.[ch]) may be shared by some other
963 # schema.
964 self._make_array_type(name, None)
966 def _def_predefineds(self):
967 for t in [('str', 'string', 'char' + POINTER_SUFFIX),
968 ('number', 'number', 'double'),
969 ('int', 'int', 'int64_t'),
970 ('int8', 'int', 'int8_t'),
971 ('int16', 'int', 'int16_t'),
972 ('int32', 'int', 'int32_t'),
973 ('int64', 'int', 'int64_t'),
974 ('uint8', 'int', 'uint8_t'),
975 ('uint16', 'int', 'uint16_t'),
976 ('uint32', 'int', 'uint32_t'),
977 ('uint64', 'int', 'uint64_t'),
978 ('size', 'int', 'uint64_t'),
979 ('bool', 'boolean', 'bool'),
980 ('any', 'value', 'QObject' + POINTER_SUFFIX),
981 ('null', 'null', 'QNull' + POINTER_SUFFIX)]:
982 self._def_builtin_type(*t)
983 self.the_empty_object_type = QAPISchemaObjectType(
984 'q_empty', None, None, None, None, None, [], None)
985 self._def_entity(self.the_empty_object_type)
987 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
988 'qbool']
989 qtype_values = self._make_enum_members(
990 [{'name': n} for n in qtypes], None)
992 self._def_entity(QAPISchemaEnumType('QType', None, None, None, None,
993 qtype_values, 'QTYPE'))
995 def _make_features(self, features, info):
996 if features is None:
997 return []
998 return [QAPISchemaFeature(f['name'], info,
999 QAPISchemaIfCond(f.get('if')))
1000 for f in features]
1002 def _make_enum_member(self, name, ifcond, features, info):
1003 return QAPISchemaEnumMember(name, info,
1004 QAPISchemaIfCond(ifcond),
1005 self._make_features(features, info))
1007 def _make_enum_members(self, values, info):
1008 return [self._make_enum_member(v['name'], v.get('if'),
1009 v.get('features'), info)
1010 for v in values]
1012 def _make_array_type(self, element_type, info):
1013 name = element_type + 'List' # reserved by check_defn_name_str()
1014 if not self.lookup_type(name):
1015 self._def_entity(QAPISchemaArrayType(name, info, element_type))
1016 return name
1018 def _make_implicit_object_type(self, name, info, ifcond, role, members):
1019 if not members:
1020 return None
1021 # See also QAPISchemaObjectTypeMember.describe()
1022 name = 'q_obj_%s-%s' % (name, role)
1023 typ = self.lookup_entity(name, QAPISchemaObjectType)
1024 if typ:
1025 # The implicit object type has multiple users. This can
1026 # only be a duplicate definition, which will be flagged
1027 # later.
1028 pass
1029 else:
1030 self._def_entity(QAPISchemaObjectType(
1031 name, info, None, ifcond, None, None, members, None))
1032 return name
1034 def _def_enum_type(self, expr, info, doc):
1035 name = expr['enum']
1036 data = expr['data']
1037 prefix = expr.get('prefix')
1038 ifcond = QAPISchemaIfCond(expr.get('if'))
1039 features = self._make_features(expr.get('features'), info)
1040 self._def_entity(QAPISchemaEnumType(
1041 name, info, doc, ifcond, features,
1042 self._make_enum_members(data, info), prefix))
1044 def _make_member(self, name, typ, ifcond, features, info):
1045 optional = False
1046 if name.startswith('*'):
1047 name = name[1:]
1048 optional = True
1049 if isinstance(typ, list):
1050 assert len(typ) == 1
1051 typ = self._make_array_type(typ[0], info)
1052 return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond,
1053 self._make_features(features, info))
1055 def _make_members(self, data, info):
1056 return [self._make_member(key, value['type'],
1057 QAPISchemaIfCond(value.get('if')),
1058 value.get('features'), info)
1059 for (key, value) in data.items()]
1061 def _def_struct_type(self, expr, info, doc):
1062 name = expr['struct']
1063 base = expr.get('base')
1064 data = expr['data']
1065 ifcond = QAPISchemaIfCond(expr.get('if'))
1066 features = self._make_features(expr.get('features'), info)
1067 self._def_entity(QAPISchemaObjectType(
1068 name, info, doc, ifcond, features, base,
1069 self._make_members(data, info),
1070 None))
1072 def _make_variant(self, case, typ, ifcond, info):
1073 if isinstance(typ, list):
1074 assert len(typ) == 1
1075 typ = self._make_array_type(typ[0], info)
1076 return QAPISchemaVariant(case, info, typ, ifcond)
1078 def _def_union_type(self, expr, info, doc):
1079 name = expr['union']
1080 base = expr['base']
1081 tag_name = expr['discriminator']
1082 data = expr['data']
1083 ifcond = QAPISchemaIfCond(expr.get('if'))
1084 features = self._make_features(expr.get('features'), info)
1085 if isinstance(base, dict):
1086 base = self._make_implicit_object_type(
1087 name, info, ifcond,
1088 'base', self._make_members(base, info))
1089 variants = [
1090 self._make_variant(key, value['type'],
1091 QAPISchemaIfCond(value.get('if')),
1092 info)
1093 for (key, value) in data.items()]
1094 members = []
1095 self._def_entity(
1096 QAPISchemaObjectType(name, info, doc, ifcond, features,
1097 base, members,
1098 QAPISchemaVariants(
1099 tag_name, info, None, variants)))
1101 def _def_alternate_type(self, expr, info, doc):
1102 name = expr['alternate']
1103 data = expr['data']
1104 ifcond = QAPISchemaIfCond(expr.get('if'))
1105 features = self._make_features(expr.get('features'), info)
1106 variants = [
1107 self._make_variant(key, value['type'],
1108 QAPISchemaIfCond(value.get('if')),
1109 info)
1110 for (key, value) in data.items()]
1111 tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
1112 self._def_entity(
1113 QAPISchemaAlternateType(name, info, doc, ifcond, features,
1114 QAPISchemaVariants(
1115 None, info, tag_member, variants)))
1117 def _def_command(self, expr, info, doc):
1118 name = expr['command']
1119 data = expr.get('data')
1120 rets = expr.get('returns')
1121 gen = expr.get('gen', True)
1122 success_response = expr.get('success-response', True)
1123 boxed = expr.get('boxed', False)
1124 allow_oob = expr.get('allow-oob', False)
1125 allow_preconfig = expr.get('allow-preconfig', False)
1126 coroutine = expr.get('coroutine', False)
1127 ifcond = QAPISchemaIfCond(expr.get('if'))
1128 features = self._make_features(expr.get('features'), info)
1129 if isinstance(data, OrderedDict):
1130 data = self._make_implicit_object_type(
1131 name, info, ifcond,
1132 'arg', self._make_members(data, info))
1133 if isinstance(rets, list):
1134 assert len(rets) == 1
1135 rets = self._make_array_type(rets[0], info)
1136 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, features,
1137 data, rets,
1138 gen, success_response,
1139 boxed, allow_oob, allow_preconfig,
1140 coroutine))
1142 def _def_event(self, expr, info, doc):
1143 name = expr['event']
1144 data = expr.get('data')
1145 boxed = expr.get('boxed', False)
1146 ifcond = QAPISchemaIfCond(expr.get('if'))
1147 features = self._make_features(expr.get('features'), info)
1148 if isinstance(data, OrderedDict):
1149 data = self._make_implicit_object_type(
1150 name, info, ifcond,
1151 'arg', self._make_members(data, info))
1152 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, features,
1153 data, boxed))
1155 def _def_exprs(self, exprs):
1156 for expr_elem in exprs:
1157 expr = expr_elem['expr']
1158 info = expr_elem['info']
1159 doc = expr_elem.get('doc')
1160 if 'enum' in expr:
1161 self._def_enum_type(expr, info, doc)
1162 elif 'struct' in expr:
1163 self._def_struct_type(expr, info, doc)
1164 elif 'union' in expr:
1165 self._def_union_type(expr, info, doc)
1166 elif 'alternate' in expr:
1167 self._def_alternate_type(expr, info, doc)
1168 elif 'command' in expr:
1169 self._def_command(expr, info, doc)
1170 elif 'event' in expr:
1171 self._def_event(expr, info, doc)
1172 elif 'include' in expr:
1173 self._def_include(expr, info, doc)
1174 else:
1175 assert False
1177 def check(self):
1178 for ent in self._entity_list:
1179 ent.check(self)
1180 ent.connect_doc()
1181 ent.check_doc()
1182 for ent in self._entity_list:
1183 ent.set_module(self)
1185 def visit(self, visitor):
1186 visitor.visit_begin(self)
1187 for mod in self._module_dict.values():
1188 mod.visit(visitor)
1189 visitor.visit_end()