storage-daemon: include current command line option in the errors
[qemu/ar7.git] / scripts / qapi / schema.py
blobff16578f6de90ceeb3fbf39a4bb9c9873b21f30a
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 POINTER_SUFFIX, c_name
23 from .error import QAPIError, QAPISemError
24 from .expr import check_exprs
25 from .parser import QAPISchemaParser
28 class QAPISchemaEntity:
29 meta: Optional[str] = None
31 def __init__(self, name: str, info, doc, ifcond=None, features=None):
32 assert name is None or isinstance(name, str)
33 for f in features or []:
34 assert isinstance(f, QAPISchemaFeature)
35 f.set_defined_in(name)
36 self.name = name
37 self._module = None
38 # For explicitly defined entities, info points to the (explicit)
39 # definition. For builtins (and their arrays), info is None.
40 # For implicitly defined entities, info points to a place that
41 # triggered the implicit definition (there may be more than one
42 # such place).
43 self.info = info
44 self.doc = doc
45 self._ifcond = ifcond or []
46 self.features = features or []
47 self._checked = False
49 def c_name(self):
50 return c_name(self.name)
52 def check(self, schema):
53 assert not self._checked
54 seen = {}
55 for f in self.features:
56 f.check_clash(self.info, seen)
57 self._checked = True
59 def connect_doc(self, doc=None):
60 doc = doc or self.doc
61 if doc:
62 for f in self.features:
63 doc.connect_feature(f)
65 def check_doc(self):
66 if self.doc:
67 self.doc.check()
69 def _set_module(self, schema, info):
70 assert self._checked
71 fname = info.fname if info else QAPISchemaModule.BUILTIN_MODULE_NAME
72 self._module = schema.module_by_fname(fname)
73 self._module.add_entity(self)
75 def set_module(self, schema):
76 self._set_module(schema, self.info)
78 @property
79 def ifcond(self):
80 assert self._checked
81 return self._ifcond
83 def is_implicit(self):
84 return not self.info
86 def visit(self, visitor):
87 assert self._checked
89 def describe(self):
90 assert self.meta
91 return "%s '%s'" % (self.meta, self.name)
94 class QAPISchemaVisitor:
95 def visit_begin(self, schema):
96 pass
98 def visit_end(self):
99 pass
101 def visit_module(self, name):
102 pass
104 def visit_needed(self, entity):
105 # Default to visiting everything
106 return True
108 def visit_include(self, name, info):
109 pass
111 def visit_builtin_type(self, name, info, json_type):
112 pass
114 def visit_enum_type(self, name, info, ifcond, features, members, prefix):
115 pass
117 def visit_array_type(self, name, info, ifcond, element_type):
118 pass
120 def visit_object_type(self, name, info, ifcond, features,
121 base, members, variants):
122 pass
124 def visit_object_type_flat(self, name, info, ifcond, features,
125 members, variants):
126 pass
128 def visit_alternate_type(self, name, info, ifcond, features, variants):
129 pass
131 def visit_command(self, name, info, ifcond, features,
132 arg_type, ret_type, gen, success_response, boxed,
133 allow_oob, allow_preconfig, coroutine):
134 pass
136 def visit_event(self, name, info, ifcond, features, arg_type, boxed):
137 pass
140 class QAPISchemaModule:
142 BUILTIN_MODULE_NAME = './builtin'
144 def __init__(self, name):
145 self.name = name
146 self._entity_list = []
148 @staticmethod
149 def is_system_module(name: str) -> bool:
151 System modules are internally defined modules.
153 Their names start with the "./" prefix.
155 return name.startswith('./')
157 @classmethod
158 def is_user_module(cls, name: str) -> bool:
160 User modules are those defined by the user in qapi JSON files.
162 They do not start with the "./" prefix.
164 return not cls.is_system_module(name)
166 @classmethod
167 def is_builtin_module(cls, name: str) -> bool:
169 The built-in module is a single System module for the built-in types.
171 It is always "./builtin".
173 return name == cls.BUILTIN_MODULE_NAME
175 def add_entity(self, ent):
176 self._entity_list.append(ent)
178 def visit(self, visitor):
179 visitor.visit_module(self.name)
180 for entity in self._entity_list:
181 if visitor.visit_needed(entity):
182 entity.visit(visitor)
185 class QAPISchemaInclude(QAPISchemaEntity):
186 def __init__(self, sub_module, info):
187 super().__init__(None, info, None)
188 self._sub_module = sub_module
190 def visit(self, visitor):
191 super().visit(visitor)
192 visitor.visit_include(self._sub_module.name, self.info)
195 class QAPISchemaType(QAPISchemaEntity):
196 # Return the C type for common use.
197 # For the types we commonly box, this is a pointer type.
198 def c_type(self):
199 pass
201 # Return the C type to be used in a parameter list.
202 def c_param_type(self):
203 return self.c_type()
205 # Return the C type to be used where we suppress boxing.
206 def c_unboxed_type(self):
207 return self.c_type()
209 def json_type(self):
210 pass
212 def alternate_qtype(self):
213 json2qtype = {
214 'null': 'QTYPE_QNULL',
215 'string': 'QTYPE_QSTRING',
216 'number': 'QTYPE_QNUM',
217 'int': 'QTYPE_QNUM',
218 'boolean': 'QTYPE_QBOOL',
219 'object': 'QTYPE_QDICT'
221 return json2qtype.get(self.json_type())
223 def doc_type(self):
224 if self.is_implicit():
225 return None
226 return self.name
228 def check(self, schema):
229 QAPISchemaEntity.check(self, schema)
230 if 'deprecated' in [f.name for f in self.features]:
231 raise QAPISemError(
232 self.info, "feature 'deprecated' is not supported for types")
234 def describe(self):
235 assert self.meta
236 return "%s type '%s'" % (self.meta, self.name)
239 class QAPISchemaBuiltinType(QAPISchemaType):
240 meta = 'built-in'
242 def __init__(self, name, json_type, c_type):
243 super().__init__(name, None, None)
244 assert not c_type or isinstance(c_type, str)
245 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
246 'value')
247 self._json_type_name = json_type
248 self._c_type_name = c_type
250 def c_name(self):
251 return self.name
253 def c_type(self):
254 return self._c_type_name
256 def c_param_type(self):
257 if self.name == 'str':
258 return 'const ' + self._c_type_name
259 return self._c_type_name
261 def json_type(self):
262 return self._json_type_name
264 def doc_type(self):
265 return self.json_type()
267 def visit(self, visitor):
268 super().visit(visitor)
269 visitor.visit_builtin_type(self.name, self.info, self.json_type())
272 class QAPISchemaEnumType(QAPISchemaType):
273 meta = 'enum'
275 def __init__(self, name, info, doc, ifcond, features, members, prefix):
276 super().__init__(name, info, doc, ifcond, features)
277 for m in members:
278 assert isinstance(m, QAPISchemaEnumMember)
279 m.set_defined_in(name)
280 assert prefix is None or isinstance(prefix, str)
281 self.members = members
282 self.prefix = prefix
284 def check(self, schema):
285 super().check(schema)
286 seen = {}
287 for m in self.members:
288 m.check_clash(self.info, seen)
290 def connect_doc(self, doc=None):
291 super().connect_doc(doc)
292 doc = doc or self.doc
293 for m in self.members:
294 m.connect_doc(doc)
296 def is_implicit(self):
297 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
298 return self.name.endswith('Kind') or self.name == 'QType'
300 def c_type(self):
301 return c_name(self.name)
303 def member_names(self):
304 return [m.name for m in self.members]
306 def json_type(self):
307 return 'string'
309 def visit(self, visitor):
310 super().visit(visitor)
311 visitor.visit_enum_type(
312 self.name, self.info, self.ifcond, self.features,
313 self.members, self.prefix)
316 class QAPISchemaArrayType(QAPISchemaType):
317 meta = 'array'
319 def __init__(self, name, info, element_type):
320 super().__init__(name, info, None)
321 assert isinstance(element_type, str)
322 self._element_type_name = element_type
323 self.element_type = None
325 def check(self, schema):
326 super().check(schema)
327 self.element_type = schema.resolve_type(
328 self._element_type_name, self.info,
329 self.info and self.info.defn_meta)
330 assert not isinstance(self.element_type, QAPISchemaArrayType)
332 def set_module(self, schema):
333 self._set_module(schema, self.element_type.info)
335 @property
336 def ifcond(self):
337 assert self._checked
338 return self.element_type.ifcond
340 def is_implicit(self):
341 return True
343 def c_type(self):
344 return c_name(self.name) + POINTER_SUFFIX
346 def json_type(self):
347 return 'array'
349 def doc_type(self):
350 elt_doc_type = self.element_type.doc_type()
351 if not elt_doc_type:
352 return None
353 return 'array of ' + elt_doc_type
355 def visit(self, visitor):
356 super().visit(visitor)
357 visitor.visit_array_type(self.name, self.info, self.ifcond,
358 self.element_type)
360 def describe(self):
361 assert self.meta
362 return "%s type ['%s']" % (self.meta, self._element_type_name)
365 class QAPISchemaObjectType(QAPISchemaType):
366 def __init__(self, name, info, doc, ifcond, features,
367 base, local_members, variants):
368 # struct has local_members, optional base, and no variants
369 # flat union has base, variants, and no local_members
370 # simple union has local_members, variants, and no base
371 super().__init__(name, info, doc, ifcond, features)
372 self.meta = 'union' if variants else 'struct'
373 assert base is None or isinstance(base, str)
374 for m in local_members:
375 assert isinstance(m, QAPISchemaObjectTypeMember)
376 m.set_defined_in(name)
377 if variants is not None:
378 assert isinstance(variants, QAPISchemaVariants)
379 variants.set_defined_in(name)
380 self._base_name = base
381 self.base = None
382 self.local_members = local_members
383 self.variants = variants
384 self.members = None
386 def check(self, schema):
387 # This calls another type T's .check() exactly when the C
388 # struct emitted by gen_object() contains that T's C struct
389 # (pointers don't count).
390 if self.members is not None:
391 # A previous .check() completed: nothing to do
392 return
393 if self._checked:
394 # Recursed: C struct contains itself
395 raise QAPISemError(self.info,
396 "object %s contains itself" % self.name)
398 super().check(schema)
399 assert self._checked and self.members is None
401 seen = OrderedDict()
402 if self._base_name:
403 self.base = schema.resolve_type(self._base_name, self.info,
404 "'base'")
405 if (not isinstance(self.base, QAPISchemaObjectType)
406 or self.base.variants):
407 raise QAPISemError(
408 self.info,
409 "'base' requires a struct type, %s isn't"
410 % self.base.describe())
411 self.base.check(schema)
412 self.base.check_clash(self.info, seen)
413 for m in self.local_members:
414 m.check(schema)
415 m.check_clash(self.info, seen)
416 members = seen.values()
418 if self.variants:
419 self.variants.check(schema, seen)
420 self.variants.check_clash(self.info, seen)
422 self.members = members # mark completed
424 # Check that the members of this type do not cause duplicate JSON members,
425 # and update seen to track the members seen so far. Report any errors
426 # on behalf of info, which is not necessarily self.info
427 def check_clash(self, info, seen):
428 assert self._checked
429 assert not self.variants # not implemented
430 for m in self.members:
431 m.check_clash(info, seen)
433 def connect_doc(self, doc=None):
434 super().connect_doc(doc)
435 doc = doc or self.doc
436 if self.base and self.base.is_implicit():
437 self.base.connect_doc(doc)
438 for m in self.local_members:
439 m.connect_doc(doc)
441 @property
442 def ifcond(self):
443 assert self._checked
444 if isinstance(self._ifcond, QAPISchemaType):
445 # Simple union wrapper type inherits from wrapped type;
446 # see _make_implicit_object_type()
447 return self._ifcond.ifcond
448 return self._ifcond
450 def is_implicit(self):
451 # See QAPISchema._make_implicit_object_type(), as well as
452 # _def_predefineds()
453 return self.name.startswith('q_')
455 def is_empty(self):
456 assert self.members is not None
457 return not self.members and not self.variants
459 def c_name(self):
460 assert self.name != 'q_empty'
461 return super().c_name()
463 def c_type(self):
464 assert not self.is_implicit()
465 return c_name(self.name) + POINTER_SUFFIX
467 def c_unboxed_type(self):
468 return c_name(self.name)
470 def json_type(self):
471 return 'object'
473 def visit(self, visitor):
474 super().visit(visitor)
475 visitor.visit_object_type(
476 self.name, self.info, self.ifcond, self.features,
477 self.base, self.local_members, self.variants)
478 visitor.visit_object_type_flat(
479 self.name, self.info, self.ifcond, self.features,
480 self.members, self.variants)
483 class QAPISchemaAlternateType(QAPISchemaType):
484 meta = 'alternate'
486 def __init__(self, name, info, doc, ifcond, features, variants):
487 super().__init__(name, info, doc, ifcond, features)
488 assert isinstance(variants, QAPISchemaVariants)
489 assert variants.tag_member
490 variants.set_defined_in(name)
491 variants.tag_member.set_defined_in(self.name)
492 self.variants = variants
494 def check(self, schema):
495 super().check(schema)
496 self.variants.tag_member.check(schema)
497 # Not calling self.variants.check_clash(), because there's nothing
498 # to clash with
499 self.variants.check(schema, {})
500 # Alternate branch names have no relation to the tag enum values;
501 # so we have to check for potential name collisions ourselves.
502 seen = {}
503 types_seen = {}
504 for v in self.variants.variants:
505 v.check_clash(self.info, seen)
506 qtype = v.type.alternate_qtype()
507 if not qtype:
508 raise QAPISemError(
509 self.info,
510 "%s cannot use %s"
511 % (v.describe(self.info), v.type.describe()))
512 conflicting = set([qtype])
513 if qtype == 'QTYPE_QSTRING':
514 if isinstance(v.type, QAPISchemaEnumType):
515 for m in v.type.members:
516 if m.name in ['on', 'off']:
517 conflicting.add('QTYPE_QBOOL')
518 if re.match(r'[-+0-9.]', m.name):
519 # lazy, could be tightened
520 conflicting.add('QTYPE_QNUM')
521 else:
522 conflicting.add('QTYPE_QNUM')
523 conflicting.add('QTYPE_QBOOL')
524 for qt in conflicting:
525 if qt in types_seen:
526 raise QAPISemError(
527 self.info,
528 "%s can't be distinguished from '%s'"
529 % (v.describe(self.info), types_seen[qt]))
530 types_seen[qt] = v.name
532 def connect_doc(self, doc=None):
533 super().connect_doc(doc)
534 doc = doc or self.doc
535 for v in self.variants.variants:
536 v.connect_doc(doc)
538 def c_type(self):
539 return c_name(self.name) + POINTER_SUFFIX
541 def json_type(self):
542 return 'value'
544 def visit(self, visitor):
545 super().visit(visitor)
546 visitor.visit_alternate_type(
547 self.name, self.info, self.ifcond, self.features, self.variants)
550 class QAPISchemaVariants:
551 def __init__(self, tag_name, info, tag_member, variants):
552 # Flat unions pass tag_name but not tag_member.
553 # Simple unions and alternates pass tag_member but not tag_name.
554 # After check(), tag_member is always set, and tag_name remains
555 # a reliable witness of being used by a flat union.
556 assert bool(tag_member) != bool(tag_name)
557 assert (isinstance(tag_name, str) or
558 isinstance(tag_member, QAPISchemaObjectTypeMember))
559 for v in variants:
560 assert isinstance(v, QAPISchemaVariant)
561 self._tag_name = tag_name
562 self.info = info
563 self.tag_member = tag_member
564 self.variants = variants
566 def set_defined_in(self, name):
567 for v in self.variants:
568 v.set_defined_in(name)
570 def check(self, schema, seen):
571 if not self.tag_member: # flat union
572 self.tag_member = seen.get(c_name(self._tag_name))
573 base = "'base'"
574 # Pointing to the base type when not implicit would be
575 # nice, but we don't know it here
576 if not self.tag_member or self._tag_name != self.tag_member.name:
577 raise QAPISemError(
578 self.info,
579 "discriminator '%s' is not a member of %s"
580 % (self._tag_name, base))
581 # Here we do:
582 base_type = schema.lookup_type(self.tag_member.defined_in)
583 assert base_type
584 if not base_type.is_implicit():
585 base = "base type '%s'" % self.tag_member.defined_in
586 if not isinstance(self.tag_member.type, QAPISchemaEnumType):
587 raise QAPISemError(
588 self.info,
589 "discriminator member '%s' of %s must be of enum type"
590 % (self._tag_name, base))
591 if self.tag_member.optional:
592 raise QAPISemError(
593 self.info,
594 "discriminator member '%s' of %s must not be optional"
595 % (self._tag_name, base))
596 if self.tag_member.ifcond:
597 raise QAPISemError(
598 self.info,
599 "discriminator member '%s' of %s must not be conditional"
600 % (self._tag_name, base))
601 else: # simple union
602 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
603 assert not self.tag_member.optional
604 assert self.tag_member.ifcond == []
605 if self._tag_name: # flat union
606 # branches that are not explicitly covered get an empty type
607 cases = {v.name for v in self.variants}
608 for m in self.tag_member.type.members:
609 if m.name not in cases:
610 v = QAPISchemaVariant(m.name, self.info,
611 'q_empty', m.ifcond)
612 v.set_defined_in(self.tag_member.defined_in)
613 self.variants.append(v)
614 if not self.variants:
615 raise QAPISemError(self.info, "union has no branches")
616 for v in self.variants:
617 v.check(schema)
618 # Union names must match enum values; alternate names are
619 # checked separately. Use 'seen' to tell the two apart.
620 if seen:
621 if v.name not in self.tag_member.type.member_names():
622 raise QAPISemError(
623 self.info,
624 "branch '%s' is not a value of %s"
625 % (v.name, self.tag_member.type.describe()))
626 if (not isinstance(v.type, QAPISchemaObjectType)
627 or v.type.variants):
628 raise QAPISemError(
629 self.info,
630 "%s cannot use %s"
631 % (v.describe(self.info), v.type.describe()))
632 v.type.check(schema)
634 def check_clash(self, info, seen):
635 for v in self.variants:
636 # Reset seen map for each variant, since qapi names from one
637 # branch do not affect another branch
638 v.type.check_clash(info, dict(seen))
641 class QAPISchemaMember:
642 """ Represents object members, enum members and features """
643 role = 'member'
645 def __init__(self, name, info, ifcond=None):
646 assert isinstance(name, str)
647 self.name = name
648 self.info = info
649 self.ifcond = ifcond or []
650 self.defined_in = None
652 def set_defined_in(self, name):
653 assert not self.defined_in
654 self.defined_in = name
656 def check_clash(self, info, seen):
657 cname = c_name(self.name)
658 if cname in seen:
659 raise QAPISemError(
660 info,
661 "%s collides with %s"
662 % (self.describe(info), seen[cname].describe(info)))
663 seen[cname] = self
665 def connect_doc(self, doc):
666 if doc:
667 doc.connect_member(self)
669 def describe(self, info):
670 role = self.role
671 defined_in = self.defined_in
672 assert defined_in
674 if defined_in.startswith('q_obj_'):
675 # See QAPISchema._make_implicit_object_type() - reverse the
676 # mapping there to create a nice human-readable description
677 defined_in = defined_in[6:]
678 if defined_in.endswith('-arg'):
679 # Implicit type created for a command's dict 'data'
680 assert role == 'member'
681 role = 'parameter'
682 elif defined_in.endswith('-base'):
683 # Implicit type created for a flat union's dict 'base'
684 role = 'base ' + role
685 else:
686 # Implicit type created for a simple union's branch
687 assert defined_in.endswith('-wrapper')
688 # Unreachable and not implemented
689 assert False
690 elif defined_in.endswith('Kind'):
691 # See QAPISchema._make_implicit_enum_type()
692 # Implicit enum created for simple union's branches
693 assert role == 'value'
694 role = 'branch'
695 elif defined_in != info.defn_name:
696 return "%s '%s' of type '%s'" % (role, self.name, defined_in)
697 return "%s '%s'" % (role, self.name)
700 class QAPISchemaEnumMember(QAPISchemaMember):
701 role = 'value'
704 class QAPISchemaFeature(QAPISchemaMember):
705 role = 'feature'
708 class QAPISchemaObjectTypeMember(QAPISchemaMember):
709 def __init__(self, name, info, typ, optional, ifcond=None, features=None):
710 super().__init__(name, info, ifcond)
711 assert isinstance(typ, str)
712 assert isinstance(optional, bool)
713 for f in features or []:
714 assert isinstance(f, QAPISchemaFeature)
715 f.set_defined_in(name)
716 self._type_name = typ
717 self.type = None
718 self.optional = optional
719 self.features = features or []
721 def check(self, schema):
722 assert self.defined_in
723 self.type = schema.resolve_type(self._type_name, self.info,
724 self.describe)
725 seen = {}
726 for f in self.features:
727 f.check_clash(self.info, seen)
729 def connect_doc(self, doc):
730 super().connect_doc(doc)
731 if doc:
732 for f in self.features:
733 doc.connect_feature(f)
736 class QAPISchemaVariant(QAPISchemaObjectTypeMember):
737 role = 'branch'
739 def __init__(self, name, info, typ, ifcond=None):
740 super().__init__(name, info, typ, False, ifcond)
743 class QAPISchemaCommand(QAPISchemaEntity):
744 meta = 'command'
746 def __init__(self, name, info, doc, ifcond, features,
747 arg_type, ret_type,
748 gen, success_response, boxed, allow_oob, allow_preconfig,
749 coroutine):
750 super().__init__(name, info, doc, ifcond, features)
751 assert not arg_type or isinstance(arg_type, str)
752 assert not ret_type or isinstance(ret_type, str)
753 self._arg_type_name = arg_type
754 self.arg_type = None
755 self._ret_type_name = ret_type
756 self.ret_type = None
757 self.gen = gen
758 self.success_response = success_response
759 self.boxed = boxed
760 self.allow_oob = allow_oob
761 self.allow_preconfig = allow_preconfig
762 self.coroutine = coroutine
764 def check(self, schema):
765 super().check(schema)
766 if self._arg_type_name:
767 self.arg_type = schema.resolve_type(
768 self._arg_type_name, self.info, "command's 'data'")
769 if not isinstance(self.arg_type, QAPISchemaObjectType):
770 raise QAPISemError(
771 self.info,
772 "command's 'data' cannot take %s"
773 % self.arg_type.describe())
774 if self.arg_type.variants and not self.boxed:
775 raise QAPISemError(
776 self.info,
777 "command's 'data' can take %s only with 'boxed': true"
778 % self.arg_type.describe())
779 if self._ret_type_name:
780 self.ret_type = schema.resolve_type(
781 self._ret_type_name, self.info, "command's 'returns'")
782 if self.name not in self.info.pragma.returns_whitelist:
783 typ = self.ret_type
784 if isinstance(typ, QAPISchemaArrayType):
785 typ = self.ret_type.element_type
786 assert typ
787 if not isinstance(typ, QAPISchemaObjectType):
788 raise QAPISemError(
789 self.info,
790 "command's 'returns' cannot take %s"
791 % self.ret_type.describe())
793 def connect_doc(self, doc=None):
794 super().connect_doc(doc)
795 doc = doc or self.doc
796 if doc:
797 if self.arg_type and self.arg_type.is_implicit():
798 self.arg_type.connect_doc(doc)
800 def visit(self, visitor):
801 super().visit(visitor)
802 visitor.visit_command(
803 self.name, self.info, self.ifcond, self.features,
804 self.arg_type, self.ret_type, self.gen, self.success_response,
805 self.boxed, self.allow_oob, self.allow_preconfig,
806 self.coroutine)
809 class QAPISchemaEvent(QAPISchemaEntity):
810 meta = 'event'
812 def __init__(self, name, info, doc, ifcond, features, arg_type, boxed):
813 super().__init__(name, info, doc, ifcond, features)
814 assert not arg_type or isinstance(arg_type, str)
815 self._arg_type_name = arg_type
816 self.arg_type = None
817 self.boxed = boxed
819 def check(self, schema):
820 super().check(schema)
821 if self._arg_type_name:
822 self.arg_type = schema.resolve_type(
823 self._arg_type_name, self.info, "event's 'data'")
824 if not isinstance(self.arg_type, QAPISchemaObjectType):
825 raise QAPISemError(
826 self.info,
827 "event's 'data' cannot take %s"
828 % self.arg_type.describe())
829 if self.arg_type.variants and not self.boxed:
830 raise QAPISemError(
831 self.info,
832 "event's 'data' can take %s only with 'boxed': true"
833 % self.arg_type.describe())
835 def connect_doc(self, doc=None):
836 super().connect_doc(doc)
837 doc = doc or self.doc
838 if doc:
839 if self.arg_type and self.arg_type.is_implicit():
840 self.arg_type.connect_doc(doc)
842 def visit(self, visitor):
843 super().visit(visitor)
844 visitor.visit_event(
845 self.name, self.info, self.ifcond, self.features,
846 self.arg_type, self.boxed)
849 class QAPISchema:
850 def __init__(self, fname):
851 self.fname = fname
852 parser = QAPISchemaParser(fname)
853 exprs = check_exprs(parser.exprs)
854 self.docs = parser.docs
855 self._entity_list = []
856 self._entity_dict = {}
857 self._module_dict = OrderedDict()
858 self._schema_dir = os.path.dirname(fname)
859 self._make_module(QAPISchemaModule.BUILTIN_MODULE_NAME)
860 self._make_module(fname)
861 self._predefining = True
862 self._def_predefineds()
863 self._predefining = False
864 self._def_exprs(exprs)
865 self.check()
867 def _def_entity(self, ent):
868 # Only the predefined types are allowed to not have info
869 assert ent.info or self._predefining
870 self._entity_list.append(ent)
871 if ent.name is None:
872 return
873 # TODO reject names that differ only in '_' vs. '.' vs. '-',
874 # because they're liable to clash in generated C.
875 other_ent = self._entity_dict.get(ent.name)
876 if other_ent:
877 if other_ent.info:
878 where = QAPIError(other_ent.info, None, "previous definition")
879 raise QAPISemError(
880 ent.info,
881 "'%s' is already defined\n%s" % (ent.name, where))
882 raise QAPISemError(
883 ent.info, "%s is already defined" % other_ent.describe())
884 self._entity_dict[ent.name] = ent
886 def lookup_entity(self, name, typ=None):
887 ent = self._entity_dict.get(name)
888 if typ and not isinstance(ent, typ):
889 return None
890 return ent
892 def lookup_type(self, name):
893 return self.lookup_entity(name, QAPISchemaType)
895 def resolve_type(self, name, info, what):
896 typ = self.lookup_type(name)
897 if not typ:
898 if callable(what):
899 what = what(info)
900 raise QAPISemError(
901 info, "%s uses unknown type '%s'" % (what, name))
902 return typ
904 def _module_name(self, fname: str) -> str:
905 if QAPISchemaModule.is_system_module(fname):
906 return fname
907 return os.path.relpath(fname, self._schema_dir)
909 def _make_module(self, fname):
910 name = self._module_name(fname)
911 if name not in self._module_dict:
912 self._module_dict[name] = QAPISchemaModule(name)
913 return self._module_dict[name]
915 def module_by_fname(self, fname):
916 name = self._module_name(fname)
917 return self._module_dict[name]
919 def _def_include(self, expr, info, doc):
920 include = expr['include']
921 assert doc is None
922 self._def_entity(QAPISchemaInclude(self._make_module(include), info))
924 def _def_builtin_type(self, name, json_type, c_type):
925 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
926 # Instantiating only the arrays that are actually used would
927 # be nice, but we can't as long as their generated code
928 # (qapi-builtin-types.[ch]) may be shared by some other
929 # schema.
930 self._make_array_type(name, None)
932 def _def_predefineds(self):
933 for t in [('str', 'string', 'char' + POINTER_SUFFIX),
934 ('number', 'number', 'double'),
935 ('int', 'int', 'int64_t'),
936 ('int8', 'int', 'int8_t'),
937 ('int16', 'int', 'int16_t'),
938 ('int32', 'int', 'int32_t'),
939 ('int64', 'int', 'int64_t'),
940 ('uint8', 'int', 'uint8_t'),
941 ('uint16', 'int', 'uint16_t'),
942 ('uint32', 'int', 'uint32_t'),
943 ('uint64', 'int', 'uint64_t'),
944 ('size', 'int', 'uint64_t'),
945 ('bool', 'boolean', 'bool'),
946 ('any', 'value', 'QObject' + POINTER_SUFFIX),
947 ('null', 'null', 'QNull' + POINTER_SUFFIX)]:
948 self._def_builtin_type(*t)
949 self.the_empty_object_type = QAPISchemaObjectType(
950 'q_empty', None, None, None, None, None, [], None)
951 self._def_entity(self.the_empty_object_type)
953 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
954 'qbool']
955 qtype_values = self._make_enum_members(
956 [{'name': n} for n in qtypes], None)
958 self._def_entity(QAPISchemaEnumType('QType', None, None, None, None,
959 qtype_values, 'QTYPE'))
961 def _make_features(self, features, info):
962 if features is None:
963 return []
964 return [QAPISchemaFeature(f['name'], info, f.get('if'))
965 for f in features]
967 def _make_enum_members(self, values, info):
968 return [QAPISchemaEnumMember(v['name'], info, v.get('if'))
969 for v in values]
971 def _make_implicit_enum_type(self, name, info, ifcond, values):
972 # See also QAPISchemaObjectTypeMember.describe()
973 name = name + 'Kind' # reserved by check_defn_name_str()
974 self._def_entity(QAPISchemaEnumType(
975 name, info, None, ifcond, None,
976 self._make_enum_members(values, info),
977 None))
978 return name
980 def _make_array_type(self, element_type, info):
981 name = element_type + 'List' # reserved by check_defn_name_str()
982 if not self.lookup_type(name):
983 self._def_entity(QAPISchemaArrayType(name, info, element_type))
984 return name
986 def _make_implicit_object_type(self, name, info, ifcond, role, members):
987 if not members:
988 return None
989 # See also QAPISchemaObjectTypeMember.describe()
990 name = 'q_obj_%s-%s' % (name, role)
991 typ = self.lookup_entity(name, QAPISchemaObjectType)
992 if typ:
993 # The implicit object type has multiple users. This can
994 # happen only for simple unions' implicit wrapper types.
995 # Its ifcond should be the disjunction of its user's
996 # ifconds. Not implemented. Instead, we always pass the
997 # wrapped type's ifcond, which is trivially the same for all
998 # users. It's also necessary for the wrapper to compile.
999 # But it's not tight: the disjunction need not imply it. We
1000 # may end up compiling useless wrapper types.
1001 # TODO kill simple unions or implement the disjunction
1003 # pylint: disable=protected-access
1004 assert (ifcond or []) == typ._ifcond
1005 else:
1006 self._def_entity(QAPISchemaObjectType(
1007 name, info, None, ifcond, None, None, members, None))
1008 return name
1010 def _def_enum_type(self, expr, info, doc):
1011 name = expr['enum']
1012 data = expr['data']
1013 prefix = expr.get('prefix')
1014 ifcond = expr.get('if')
1015 features = self._make_features(expr.get('features'), info)
1016 self._def_entity(QAPISchemaEnumType(
1017 name, info, doc, ifcond, features,
1018 self._make_enum_members(data, info), prefix))
1020 def _make_member(self, name, typ, ifcond, features, info):
1021 optional = False
1022 if name.startswith('*'):
1023 name = name[1:]
1024 optional = True
1025 if isinstance(typ, list):
1026 assert len(typ) == 1
1027 typ = self._make_array_type(typ[0], info)
1028 return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond,
1029 self._make_features(features, info))
1031 def _make_members(self, data, info):
1032 return [self._make_member(key, value['type'], value.get('if'),
1033 value.get('features'), info)
1034 for (key, value) in data.items()]
1036 def _def_struct_type(self, expr, info, doc):
1037 name = expr['struct']
1038 base = expr.get('base')
1039 data = expr['data']
1040 ifcond = expr.get('if')
1041 features = self._make_features(expr.get('features'), info)
1042 self._def_entity(QAPISchemaObjectType(
1043 name, info, doc, ifcond, features, base,
1044 self._make_members(data, info),
1045 None))
1047 def _make_variant(self, case, typ, ifcond, info):
1048 return QAPISchemaVariant(case, info, typ, ifcond)
1050 def _make_simple_variant(self, case, typ, ifcond, info):
1051 if isinstance(typ, list):
1052 assert len(typ) == 1
1053 typ = self._make_array_type(typ[0], info)
1054 typ = self._make_implicit_object_type(
1055 typ, info, self.lookup_type(typ),
1056 'wrapper', [self._make_member('data', typ, None, None, info)])
1057 return QAPISchemaVariant(case, info, typ, ifcond)
1059 def _def_union_type(self, expr, info, doc):
1060 name = expr['union']
1061 data = expr['data']
1062 base = expr.get('base')
1063 ifcond = expr.get('if')
1064 features = self._make_features(expr.get('features'), info)
1065 tag_name = expr.get('discriminator')
1066 tag_member = None
1067 if isinstance(base, dict):
1068 base = self._make_implicit_object_type(
1069 name, info, ifcond,
1070 'base', self._make_members(base, info))
1071 if tag_name:
1072 variants = [self._make_variant(key, value['type'],
1073 value.get('if'), info)
1074 for (key, value) in data.items()]
1075 members = []
1076 else:
1077 variants = [self._make_simple_variant(key, value['type'],
1078 value.get('if'), info)
1079 for (key, value) in data.items()]
1080 enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
1081 typ = self._make_implicit_enum_type(name, info, ifcond, enum)
1082 tag_member = QAPISchemaObjectTypeMember('type', info, typ, False)
1083 members = [tag_member]
1084 self._def_entity(
1085 QAPISchemaObjectType(name, info, doc, ifcond, features,
1086 base, members,
1087 QAPISchemaVariants(
1088 tag_name, info, tag_member, variants)))
1090 def _def_alternate_type(self, expr, info, doc):
1091 name = expr['alternate']
1092 data = expr['data']
1093 ifcond = expr.get('if')
1094 features = self._make_features(expr.get('features'), info)
1095 variants = [self._make_variant(key, value['type'], value.get('if'),
1096 info)
1097 for (key, value) in data.items()]
1098 tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
1099 self._def_entity(
1100 QAPISchemaAlternateType(name, info, doc, ifcond, features,
1101 QAPISchemaVariants(
1102 None, info, tag_member, variants)))
1104 def _def_command(self, expr, info, doc):
1105 name = expr['command']
1106 data = expr.get('data')
1107 rets = expr.get('returns')
1108 gen = expr.get('gen', True)
1109 success_response = expr.get('success-response', True)
1110 boxed = expr.get('boxed', False)
1111 allow_oob = expr.get('allow-oob', False)
1112 allow_preconfig = expr.get('allow-preconfig', False)
1113 coroutine = expr.get('coroutine', False)
1114 ifcond = expr.get('if')
1115 features = self._make_features(expr.get('features'), info)
1116 if isinstance(data, OrderedDict):
1117 data = self._make_implicit_object_type(
1118 name, info, ifcond,
1119 'arg', self._make_members(data, info))
1120 if isinstance(rets, list):
1121 assert len(rets) == 1
1122 rets = self._make_array_type(rets[0], info)
1123 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, features,
1124 data, rets,
1125 gen, success_response,
1126 boxed, allow_oob, allow_preconfig,
1127 coroutine))
1129 def _def_event(self, expr, info, doc):
1130 name = expr['event']
1131 data = expr.get('data')
1132 boxed = expr.get('boxed', False)
1133 ifcond = expr.get('if')
1134 features = self._make_features(expr.get('features'), info)
1135 if isinstance(data, OrderedDict):
1136 data = self._make_implicit_object_type(
1137 name, info, ifcond,
1138 'arg', self._make_members(data, info))
1139 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, features,
1140 data, boxed))
1142 def _def_exprs(self, exprs):
1143 for expr_elem in exprs:
1144 expr = expr_elem['expr']
1145 info = expr_elem['info']
1146 doc = expr_elem.get('doc')
1147 if 'enum' in expr:
1148 self._def_enum_type(expr, info, doc)
1149 elif 'struct' in expr:
1150 self._def_struct_type(expr, info, doc)
1151 elif 'union' in expr:
1152 self._def_union_type(expr, info, doc)
1153 elif 'alternate' in expr:
1154 self._def_alternate_type(expr, info, doc)
1155 elif 'command' in expr:
1156 self._def_command(expr, info, doc)
1157 elif 'event' in expr:
1158 self._def_event(expr, info, doc)
1159 elif 'include' in expr:
1160 self._def_include(expr, info, doc)
1161 else:
1162 assert False
1164 def check(self):
1165 for ent in self._entity_list:
1166 ent.check(self)
1167 ent.connect_doc()
1168 ent.check_doc()
1169 for ent in self._entity_list:
1170 ent.set_module(self)
1172 def visit(self, visitor):
1173 visitor.visit_begin(self)
1174 for mod in self._module_dict.values():
1175 mod.visit(visitor)
1176 visitor.visit_end()