qapi/error: Repurpose QAPIError as an abstract base exception class
[qemu/ar7.git] / scripts / qapi / schema.py
blobc277fcacc5353aa7ba272c2037b7bb0681e18d44
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 QAPISemError, QAPISourceError
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.command_returns_exceptions:
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 = QAPISourceError(other_ent.info, None,
879 "previous definition")
880 raise QAPISemError(
881 ent.info,
882 "'%s' is already defined\n%s" % (ent.name, where))
883 raise QAPISemError(
884 ent.info, "%s is already defined" % other_ent.describe())
885 self._entity_dict[ent.name] = ent
887 def lookup_entity(self, name, typ=None):
888 ent = self._entity_dict.get(name)
889 if typ and not isinstance(ent, typ):
890 return None
891 return ent
893 def lookup_type(self, name):
894 return self.lookup_entity(name, QAPISchemaType)
896 def resolve_type(self, name, info, what):
897 typ = self.lookup_type(name)
898 if not typ:
899 if callable(what):
900 what = what(info)
901 raise QAPISemError(
902 info, "%s uses unknown type '%s'" % (what, name))
903 return typ
905 def _module_name(self, fname: str) -> str:
906 if QAPISchemaModule.is_system_module(fname):
907 return fname
908 return os.path.relpath(fname, self._schema_dir)
910 def _make_module(self, fname):
911 name = self._module_name(fname)
912 if name not in self._module_dict:
913 self._module_dict[name] = QAPISchemaModule(name)
914 return self._module_dict[name]
916 def module_by_fname(self, fname):
917 name = self._module_name(fname)
918 return self._module_dict[name]
920 def _def_include(self, expr, info, doc):
921 include = expr['include']
922 assert doc is None
923 self._def_entity(QAPISchemaInclude(self._make_module(include), info))
925 def _def_builtin_type(self, name, json_type, c_type):
926 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
927 # Instantiating only the arrays that are actually used would
928 # be nice, but we can't as long as their generated code
929 # (qapi-builtin-types.[ch]) may be shared by some other
930 # schema.
931 self._make_array_type(name, None)
933 def _def_predefineds(self):
934 for t in [('str', 'string', 'char' + POINTER_SUFFIX),
935 ('number', 'number', 'double'),
936 ('int', 'int', 'int64_t'),
937 ('int8', 'int', 'int8_t'),
938 ('int16', 'int', 'int16_t'),
939 ('int32', 'int', 'int32_t'),
940 ('int64', 'int', 'int64_t'),
941 ('uint8', 'int', 'uint8_t'),
942 ('uint16', 'int', 'uint16_t'),
943 ('uint32', 'int', 'uint32_t'),
944 ('uint64', 'int', 'uint64_t'),
945 ('size', 'int', 'uint64_t'),
946 ('bool', 'boolean', 'bool'),
947 ('any', 'value', 'QObject' + POINTER_SUFFIX),
948 ('null', 'null', 'QNull' + POINTER_SUFFIX)]:
949 self._def_builtin_type(*t)
950 self.the_empty_object_type = QAPISchemaObjectType(
951 'q_empty', None, None, None, None, None, [], None)
952 self._def_entity(self.the_empty_object_type)
954 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
955 'qbool']
956 qtype_values = self._make_enum_members(
957 [{'name': n} for n in qtypes], None)
959 self._def_entity(QAPISchemaEnumType('QType', None, None, None, None,
960 qtype_values, 'QTYPE'))
962 def _make_features(self, features, info):
963 if features is None:
964 return []
965 return [QAPISchemaFeature(f['name'], info, f.get('if'))
966 for f in features]
968 def _make_enum_members(self, values, info):
969 return [QAPISchemaEnumMember(v['name'], info, v.get('if'))
970 for v in values]
972 def _make_implicit_enum_type(self, name, info, ifcond, values):
973 # See also QAPISchemaObjectTypeMember.describe()
974 name = name + 'Kind' # reserved by check_defn_name_str()
975 self._def_entity(QAPISchemaEnumType(
976 name, info, None, ifcond, None,
977 self._make_enum_members(values, info),
978 None))
979 return name
981 def _make_array_type(self, element_type, info):
982 name = element_type + 'List' # reserved by check_defn_name_str()
983 if not self.lookup_type(name):
984 self._def_entity(QAPISchemaArrayType(name, info, element_type))
985 return name
987 def _make_implicit_object_type(self, name, info, ifcond, role, members):
988 if not members:
989 return None
990 # See also QAPISchemaObjectTypeMember.describe()
991 name = 'q_obj_%s-%s' % (name, role)
992 typ = self.lookup_entity(name, QAPISchemaObjectType)
993 if typ:
994 # The implicit object type has multiple users. This can
995 # happen only for simple unions' implicit wrapper types.
996 # Its ifcond should be the disjunction of its user's
997 # ifconds. Not implemented. Instead, we always pass the
998 # wrapped type's ifcond, which is trivially the same for all
999 # users. It's also necessary for the wrapper to compile.
1000 # But it's not tight: the disjunction need not imply it. We
1001 # may end up compiling useless wrapper types.
1002 # TODO kill simple unions or implement the disjunction
1004 # pylint: disable=protected-access
1005 assert (ifcond or []) == typ._ifcond
1006 else:
1007 self._def_entity(QAPISchemaObjectType(
1008 name, info, None, ifcond, None, None, members, None))
1009 return name
1011 def _def_enum_type(self, expr, info, doc):
1012 name = expr['enum']
1013 data = expr['data']
1014 prefix = expr.get('prefix')
1015 ifcond = expr.get('if')
1016 features = self._make_features(expr.get('features'), info)
1017 self._def_entity(QAPISchemaEnumType(
1018 name, info, doc, ifcond, features,
1019 self._make_enum_members(data, info), prefix))
1021 def _make_member(self, name, typ, ifcond, features, info):
1022 optional = False
1023 if name.startswith('*'):
1024 name = name[1:]
1025 optional = True
1026 if isinstance(typ, list):
1027 assert len(typ) == 1
1028 typ = self._make_array_type(typ[0], info)
1029 return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond,
1030 self._make_features(features, info))
1032 def _make_members(self, data, info):
1033 return [self._make_member(key, value['type'], value.get('if'),
1034 value.get('features'), info)
1035 for (key, value) in data.items()]
1037 def _def_struct_type(self, expr, info, doc):
1038 name = expr['struct']
1039 base = expr.get('base')
1040 data = expr['data']
1041 ifcond = expr.get('if')
1042 features = self._make_features(expr.get('features'), info)
1043 self._def_entity(QAPISchemaObjectType(
1044 name, info, doc, ifcond, features, base,
1045 self._make_members(data, info),
1046 None))
1048 def _make_variant(self, case, typ, ifcond, info):
1049 return QAPISchemaVariant(case, info, typ, ifcond)
1051 def _make_simple_variant(self, case, typ, ifcond, info):
1052 if isinstance(typ, list):
1053 assert len(typ) == 1
1054 typ = self._make_array_type(typ[0], info)
1055 typ = self._make_implicit_object_type(
1056 typ, info, self.lookup_type(typ),
1057 'wrapper', [self._make_member('data', typ, None, None, info)])
1058 return QAPISchemaVariant(case, info, typ, ifcond)
1060 def _def_union_type(self, expr, info, doc):
1061 name = expr['union']
1062 data = expr['data']
1063 base = expr.get('base')
1064 ifcond = expr.get('if')
1065 features = self._make_features(expr.get('features'), info)
1066 tag_name = expr.get('discriminator')
1067 tag_member = None
1068 if isinstance(base, dict):
1069 base = self._make_implicit_object_type(
1070 name, info, ifcond,
1071 'base', self._make_members(base, info))
1072 if tag_name:
1073 variants = [self._make_variant(key, value['type'],
1074 value.get('if'), info)
1075 for (key, value) in data.items()]
1076 members = []
1077 else:
1078 variants = [self._make_simple_variant(key, value['type'],
1079 value.get('if'), info)
1080 for (key, value) in data.items()]
1081 enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
1082 typ = self._make_implicit_enum_type(name, info, ifcond, enum)
1083 tag_member = QAPISchemaObjectTypeMember('type', info, typ, False)
1084 members = [tag_member]
1085 self._def_entity(
1086 QAPISchemaObjectType(name, info, doc, ifcond, features,
1087 base, members,
1088 QAPISchemaVariants(
1089 tag_name, info, tag_member, variants)))
1091 def _def_alternate_type(self, expr, info, doc):
1092 name = expr['alternate']
1093 data = expr['data']
1094 ifcond = expr.get('if')
1095 features = self._make_features(expr.get('features'), info)
1096 variants = [self._make_variant(key, value['type'], value.get('if'),
1097 info)
1098 for (key, value) in data.items()]
1099 tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
1100 self._def_entity(
1101 QAPISchemaAlternateType(name, info, doc, ifcond, features,
1102 QAPISchemaVariants(
1103 None, info, tag_member, variants)))
1105 def _def_command(self, expr, info, doc):
1106 name = expr['command']
1107 data = expr.get('data')
1108 rets = expr.get('returns')
1109 gen = expr.get('gen', True)
1110 success_response = expr.get('success-response', True)
1111 boxed = expr.get('boxed', False)
1112 allow_oob = expr.get('allow-oob', False)
1113 allow_preconfig = expr.get('allow-preconfig', False)
1114 coroutine = expr.get('coroutine', False)
1115 ifcond = expr.get('if')
1116 features = self._make_features(expr.get('features'), info)
1117 if isinstance(data, OrderedDict):
1118 data = self._make_implicit_object_type(
1119 name, info, ifcond,
1120 'arg', self._make_members(data, info))
1121 if isinstance(rets, list):
1122 assert len(rets) == 1
1123 rets = self._make_array_type(rets[0], info)
1124 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, features,
1125 data, rets,
1126 gen, success_response,
1127 boxed, allow_oob, allow_preconfig,
1128 coroutine))
1130 def _def_event(self, expr, info, doc):
1131 name = expr['event']
1132 data = expr.get('data')
1133 boxed = expr.get('boxed', False)
1134 ifcond = expr.get('if')
1135 features = self._make_features(expr.get('features'), info)
1136 if isinstance(data, OrderedDict):
1137 data = self._make_implicit_object_type(
1138 name, info, ifcond,
1139 'arg', self._make_members(data, info))
1140 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, features,
1141 data, boxed))
1143 def _def_exprs(self, exprs):
1144 for expr_elem in exprs:
1145 expr = expr_elem['expr']
1146 info = expr_elem['info']
1147 doc = expr_elem.get('doc')
1148 if 'enum' in expr:
1149 self._def_enum_type(expr, info, doc)
1150 elif 'struct' in expr:
1151 self._def_struct_type(expr, info, doc)
1152 elif 'union' in expr:
1153 self._def_union_type(expr, info, doc)
1154 elif 'alternate' in expr:
1155 self._def_alternate_type(expr, info, doc)
1156 elif 'command' in expr:
1157 self._def_command(expr, info, doc)
1158 elif 'event' in expr:
1159 self._def_event(expr, info, doc)
1160 elif 'include' in expr:
1161 self._def_include(expr, info, doc)
1162 else:
1163 assert False
1165 def check(self):
1166 for ent in self._entity_list:
1167 ent.check(self)
1168 ent.connect_doc()
1169 ent.check_doc()
1170 for ent in self._entity_list:
1171 ent.set_module(self)
1173 def visit(self, visitor):
1174 visitor.visit_begin(self)
1175 for mod in self._module_dict.values():
1176 mod.visit(visitor)
1177 visitor.visit_end()