hw/timer/cmsdk-apb-timer: Add Clock input
[qemu/ar7.git] / scripts / qapi / schema.py
blob720449feee4d861431b8cdaddd36bf06e6b9f20f
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, 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 self._module = schema.module_by_fname(info and info.fname)
72 self._module.add_entity(self)
74 def set_module(self, schema):
75 self._set_module(schema, self.info)
77 @property
78 def ifcond(self):
79 assert self._checked
80 return self._ifcond
82 def is_implicit(self):
83 return not self.info
85 def visit(self, visitor):
86 assert self._checked
88 def describe(self):
89 assert self.meta
90 return "%s '%s'" % (self.meta, self.name)
93 class QAPISchemaVisitor:
94 def visit_begin(self, schema):
95 pass
97 def visit_end(self):
98 pass
100 def visit_module(self, name):
101 pass
103 def visit_needed(self, entity):
104 # Default to visiting everything
105 return True
107 def visit_include(self, name, info):
108 pass
110 def visit_builtin_type(self, name, info, json_type):
111 pass
113 def visit_enum_type(self, name, info, ifcond, features, members, prefix):
114 pass
116 def visit_array_type(self, name, info, ifcond, element_type):
117 pass
119 def visit_object_type(self, name, info, ifcond, features,
120 base, members, variants):
121 pass
123 def visit_object_type_flat(self, name, info, ifcond, features,
124 members, variants):
125 pass
127 def visit_alternate_type(self, name, info, ifcond, features, variants):
128 pass
130 def visit_command(self, name, info, ifcond, features,
131 arg_type, ret_type, gen, success_response, boxed,
132 allow_oob, allow_preconfig, coroutine):
133 pass
135 def visit_event(self, name, info, ifcond, features, arg_type, boxed):
136 pass
139 class QAPISchemaModule:
140 def __init__(self, name):
141 self.name = name
142 self._entity_list = []
144 def add_entity(self, ent):
145 self._entity_list.append(ent)
147 def visit(self, visitor):
148 visitor.visit_module(self.name)
149 for entity in self._entity_list:
150 if visitor.visit_needed(entity):
151 entity.visit(visitor)
154 class QAPISchemaInclude(QAPISchemaEntity):
155 def __init__(self, sub_module, info):
156 super().__init__(None, info, None)
157 self._sub_module = sub_module
159 def visit(self, visitor):
160 super().visit(visitor)
161 visitor.visit_include(self._sub_module.name, self.info)
164 class QAPISchemaType(QAPISchemaEntity):
165 # Return the C type for common use.
166 # For the types we commonly box, this is a pointer type.
167 def c_type(self):
168 pass
170 # Return the C type to be used in a parameter list.
171 def c_param_type(self):
172 return self.c_type()
174 # Return the C type to be used where we suppress boxing.
175 def c_unboxed_type(self):
176 return self.c_type()
178 def json_type(self):
179 pass
181 def alternate_qtype(self):
182 json2qtype = {
183 'null': 'QTYPE_QNULL',
184 'string': 'QTYPE_QSTRING',
185 'number': 'QTYPE_QNUM',
186 'int': 'QTYPE_QNUM',
187 'boolean': 'QTYPE_QBOOL',
188 'object': 'QTYPE_QDICT'
190 return json2qtype.get(self.json_type())
192 def doc_type(self):
193 if self.is_implicit():
194 return None
195 return self.name
197 def check(self, schema):
198 QAPISchemaEntity.check(self, schema)
199 if 'deprecated' in [f.name for f in self.features]:
200 raise QAPISemError(
201 self.info, "feature 'deprecated' is not supported for types")
203 def describe(self):
204 assert self.meta
205 return "%s type '%s'" % (self.meta, self.name)
208 class QAPISchemaBuiltinType(QAPISchemaType):
209 meta = 'built-in'
211 def __init__(self, name, json_type, c_type):
212 super().__init__(name, None, None)
213 assert not c_type or isinstance(c_type, str)
214 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
215 'value')
216 self._json_type_name = json_type
217 self._c_type_name = c_type
219 def c_name(self):
220 return self.name
222 def c_type(self):
223 return self._c_type_name
225 def c_param_type(self):
226 if self.name == 'str':
227 return 'const ' + self._c_type_name
228 return self._c_type_name
230 def json_type(self):
231 return self._json_type_name
233 def doc_type(self):
234 return self.json_type()
236 def visit(self, visitor):
237 super().visit(visitor)
238 visitor.visit_builtin_type(self.name, self.info, self.json_type())
241 class QAPISchemaEnumType(QAPISchemaType):
242 meta = 'enum'
244 def __init__(self, name, info, doc, ifcond, features, members, prefix):
245 super().__init__(name, info, doc, ifcond, features)
246 for m in members:
247 assert isinstance(m, QAPISchemaEnumMember)
248 m.set_defined_in(name)
249 assert prefix is None or isinstance(prefix, str)
250 self.members = members
251 self.prefix = prefix
253 def check(self, schema):
254 super().check(schema)
255 seen = {}
256 for m in self.members:
257 m.check_clash(self.info, seen)
259 def connect_doc(self, doc=None):
260 super().connect_doc(doc)
261 doc = doc or self.doc
262 for m in self.members:
263 m.connect_doc(doc)
265 def is_implicit(self):
266 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
267 return self.name.endswith('Kind') or self.name == 'QType'
269 def c_type(self):
270 return c_name(self.name)
272 def member_names(self):
273 return [m.name for m in self.members]
275 def json_type(self):
276 return 'string'
278 def visit(self, visitor):
279 super().visit(visitor)
280 visitor.visit_enum_type(
281 self.name, self.info, self.ifcond, self.features,
282 self.members, self.prefix)
285 class QAPISchemaArrayType(QAPISchemaType):
286 meta = 'array'
288 def __init__(self, name, info, element_type):
289 super().__init__(name, info, None)
290 assert isinstance(element_type, str)
291 self._element_type_name = element_type
292 self.element_type = None
294 def check(self, schema):
295 super().check(schema)
296 self.element_type = schema.resolve_type(
297 self._element_type_name, self.info,
298 self.info and self.info.defn_meta)
299 assert not isinstance(self.element_type, QAPISchemaArrayType)
301 def set_module(self, schema):
302 self._set_module(schema, self.element_type.info)
304 @property
305 def ifcond(self):
306 assert self._checked
307 return self.element_type.ifcond
309 def is_implicit(self):
310 return True
312 def c_type(self):
313 return c_name(self.name) + POINTER_SUFFIX
315 def json_type(self):
316 return 'array'
318 def doc_type(self):
319 elt_doc_type = self.element_type.doc_type()
320 if not elt_doc_type:
321 return None
322 return 'array of ' + elt_doc_type
324 def visit(self, visitor):
325 super().visit(visitor)
326 visitor.visit_array_type(self.name, self.info, self.ifcond,
327 self.element_type)
329 def describe(self):
330 assert self.meta
331 return "%s type ['%s']" % (self.meta, self._element_type_name)
334 class QAPISchemaObjectType(QAPISchemaType):
335 def __init__(self, name, info, doc, ifcond, features,
336 base, local_members, variants):
337 # struct has local_members, optional base, and no variants
338 # flat union has base, variants, and no local_members
339 # simple union has local_members, variants, and no base
340 super().__init__(name, info, doc, ifcond, features)
341 self.meta = 'union' if variants else 'struct'
342 assert base is None or isinstance(base, str)
343 for m in local_members:
344 assert isinstance(m, QAPISchemaObjectTypeMember)
345 m.set_defined_in(name)
346 if variants is not None:
347 assert isinstance(variants, QAPISchemaVariants)
348 variants.set_defined_in(name)
349 self._base_name = base
350 self.base = None
351 self.local_members = local_members
352 self.variants = variants
353 self.members = None
355 def check(self, schema):
356 # This calls another type T's .check() exactly when the C
357 # struct emitted by gen_object() contains that T's C struct
358 # (pointers don't count).
359 if self.members is not None:
360 # A previous .check() completed: nothing to do
361 return
362 if self._checked:
363 # Recursed: C struct contains itself
364 raise QAPISemError(self.info,
365 "object %s contains itself" % self.name)
367 super().check(schema)
368 assert self._checked and self.members is None
370 seen = OrderedDict()
371 if self._base_name:
372 self.base = schema.resolve_type(self._base_name, self.info,
373 "'base'")
374 if (not isinstance(self.base, QAPISchemaObjectType)
375 or self.base.variants):
376 raise QAPISemError(
377 self.info,
378 "'base' requires a struct type, %s isn't"
379 % self.base.describe())
380 self.base.check(schema)
381 self.base.check_clash(self.info, seen)
382 for m in self.local_members:
383 m.check(schema)
384 m.check_clash(self.info, seen)
385 members = seen.values()
387 if self.variants:
388 self.variants.check(schema, seen)
389 self.variants.check_clash(self.info, seen)
391 self.members = members # mark completed
393 # Check that the members of this type do not cause duplicate JSON members,
394 # and update seen to track the members seen so far. Report any errors
395 # on behalf of info, which is not necessarily self.info
396 def check_clash(self, info, seen):
397 assert self._checked
398 assert not self.variants # not implemented
399 for m in self.members:
400 m.check_clash(info, seen)
402 def connect_doc(self, doc=None):
403 super().connect_doc(doc)
404 doc = doc or self.doc
405 if self.base and self.base.is_implicit():
406 self.base.connect_doc(doc)
407 for m in self.local_members:
408 m.connect_doc(doc)
410 @property
411 def ifcond(self):
412 assert self._checked
413 if isinstance(self._ifcond, QAPISchemaType):
414 # Simple union wrapper type inherits from wrapped type;
415 # see _make_implicit_object_type()
416 return self._ifcond.ifcond
417 return self._ifcond
419 def is_implicit(self):
420 # See QAPISchema._make_implicit_object_type(), as well as
421 # _def_predefineds()
422 return self.name.startswith('q_')
424 def is_empty(self):
425 assert self.members is not None
426 return not self.members and not self.variants
428 def c_name(self):
429 assert self.name != 'q_empty'
430 return super().c_name()
432 def c_type(self):
433 assert not self.is_implicit()
434 return c_name(self.name) + POINTER_SUFFIX
436 def c_unboxed_type(self):
437 return c_name(self.name)
439 def json_type(self):
440 return 'object'
442 def visit(self, visitor):
443 super().visit(visitor)
444 visitor.visit_object_type(
445 self.name, self.info, self.ifcond, self.features,
446 self.base, self.local_members, self.variants)
447 visitor.visit_object_type_flat(
448 self.name, self.info, self.ifcond, self.features,
449 self.members, self.variants)
452 class QAPISchemaAlternateType(QAPISchemaType):
453 meta = 'alternate'
455 def __init__(self, name, info, doc, ifcond, features, variants):
456 super().__init__(name, info, doc, ifcond, features)
457 assert isinstance(variants, QAPISchemaVariants)
458 assert variants.tag_member
459 variants.set_defined_in(name)
460 variants.tag_member.set_defined_in(self.name)
461 self.variants = variants
463 def check(self, schema):
464 super().check(schema)
465 self.variants.tag_member.check(schema)
466 # Not calling self.variants.check_clash(), because there's nothing
467 # to clash with
468 self.variants.check(schema, {})
469 # Alternate branch names have no relation to the tag enum values;
470 # so we have to check for potential name collisions ourselves.
471 seen = {}
472 types_seen = {}
473 for v in self.variants.variants:
474 v.check_clash(self.info, seen)
475 qtype = v.type.alternate_qtype()
476 if not qtype:
477 raise QAPISemError(
478 self.info,
479 "%s cannot use %s"
480 % (v.describe(self.info), v.type.describe()))
481 conflicting = set([qtype])
482 if qtype == 'QTYPE_QSTRING':
483 if isinstance(v.type, QAPISchemaEnumType):
484 for m in v.type.members:
485 if m.name in ['on', 'off']:
486 conflicting.add('QTYPE_QBOOL')
487 if re.match(r'[-+0-9.]', m.name):
488 # lazy, could be tightened
489 conflicting.add('QTYPE_QNUM')
490 else:
491 conflicting.add('QTYPE_QNUM')
492 conflicting.add('QTYPE_QBOOL')
493 for qt in conflicting:
494 if qt in types_seen:
495 raise QAPISemError(
496 self.info,
497 "%s can't be distinguished from '%s'"
498 % (v.describe(self.info), types_seen[qt]))
499 types_seen[qt] = v.name
501 def connect_doc(self, doc=None):
502 super().connect_doc(doc)
503 doc = doc or self.doc
504 for v in self.variants.variants:
505 v.connect_doc(doc)
507 def c_type(self):
508 return c_name(self.name) + POINTER_SUFFIX
510 def json_type(self):
511 return 'value'
513 def visit(self, visitor):
514 super().visit(visitor)
515 visitor.visit_alternate_type(
516 self.name, self.info, self.ifcond, self.features, self.variants)
519 class QAPISchemaVariants:
520 def __init__(self, tag_name, info, tag_member, variants):
521 # Flat unions pass tag_name but not tag_member.
522 # Simple unions and alternates pass tag_member but not tag_name.
523 # After check(), tag_member is always set, and tag_name remains
524 # a reliable witness of being used by a flat union.
525 assert bool(tag_member) != bool(tag_name)
526 assert (isinstance(tag_name, str) or
527 isinstance(tag_member, QAPISchemaObjectTypeMember))
528 for v in variants:
529 assert isinstance(v, QAPISchemaVariant)
530 self._tag_name = tag_name
531 self.info = info
532 self.tag_member = tag_member
533 self.variants = variants
535 def set_defined_in(self, name):
536 for v in self.variants:
537 v.set_defined_in(name)
539 def check(self, schema, seen):
540 if not self.tag_member: # flat union
541 self.tag_member = seen.get(c_name(self._tag_name))
542 base = "'base'"
543 # Pointing to the base type when not implicit would be
544 # nice, but we don't know it here
545 if not self.tag_member or self._tag_name != self.tag_member.name:
546 raise QAPISemError(
547 self.info,
548 "discriminator '%s' is not a member of %s"
549 % (self._tag_name, base))
550 # Here we do:
551 base_type = schema.lookup_type(self.tag_member.defined_in)
552 assert base_type
553 if not base_type.is_implicit():
554 base = "base type '%s'" % self.tag_member.defined_in
555 if not isinstance(self.tag_member.type, QAPISchemaEnumType):
556 raise QAPISemError(
557 self.info,
558 "discriminator member '%s' of %s must be of enum type"
559 % (self._tag_name, base))
560 if self.tag_member.optional:
561 raise QAPISemError(
562 self.info,
563 "discriminator member '%s' of %s must not be optional"
564 % (self._tag_name, base))
565 if self.tag_member.ifcond:
566 raise QAPISemError(
567 self.info,
568 "discriminator member '%s' of %s must not be conditional"
569 % (self._tag_name, base))
570 else: # simple union
571 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
572 assert not self.tag_member.optional
573 assert self.tag_member.ifcond == []
574 if self._tag_name: # flat union
575 # branches that are not explicitly covered get an empty type
576 cases = {v.name for v in self.variants}
577 for m in self.tag_member.type.members:
578 if m.name not in cases:
579 v = QAPISchemaVariant(m.name, self.info,
580 'q_empty', m.ifcond)
581 v.set_defined_in(self.tag_member.defined_in)
582 self.variants.append(v)
583 if not self.variants:
584 raise QAPISemError(self.info, "union has no branches")
585 for v in self.variants:
586 v.check(schema)
587 # Union names must match enum values; alternate names are
588 # checked separately. Use 'seen' to tell the two apart.
589 if seen:
590 if v.name not in self.tag_member.type.member_names():
591 raise QAPISemError(
592 self.info,
593 "branch '%s' is not a value of %s"
594 % (v.name, self.tag_member.type.describe()))
595 if (not isinstance(v.type, QAPISchemaObjectType)
596 or v.type.variants):
597 raise QAPISemError(
598 self.info,
599 "%s cannot use %s"
600 % (v.describe(self.info), v.type.describe()))
601 v.type.check(schema)
603 def check_clash(self, info, seen):
604 for v in self.variants:
605 # Reset seen map for each variant, since qapi names from one
606 # branch do not affect another branch
607 v.type.check_clash(info, dict(seen))
610 class QAPISchemaMember:
611 """ Represents object members, enum members and features """
612 role = 'member'
614 def __init__(self, name, info, ifcond=None):
615 assert isinstance(name, str)
616 self.name = name
617 self.info = info
618 self.ifcond = ifcond or []
619 self.defined_in = None
621 def set_defined_in(self, name):
622 assert not self.defined_in
623 self.defined_in = name
625 def check_clash(self, info, seen):
626 cname = c_name(self.name)
627 if cname in seen:
628 raise QAPISemError(
629 info,
630 "%s collides with %s"
631 % (self.describe(info), seen[cname].describe(info)))
632 seen[cname] = self
634 def connect_doc(self, doc):
635 if doc:
636 doc.connect_member(self)
638 def describe(self, info):
639 role = self.role
640 defined_in = self.defined_in
641 assert defined_in
643 if defined_in.startswith('q_obj_'):
644 # See QAPISchema._make_implicit_object_type() - reverse the
645 # mapping there to create a nice human-readable description
646 defined_in = defined_in[6:]
647 if defined_in.endswith('-arg'):
648 # Implicit type created for a command's dict 'data'
649 assert role == 'member'
650 role = 'parameter'
651 elif defined_in.endswith('-base'):
652 # Implicit type created for a flat union's dict 'base'
653 role = 'base ' + role
654 else:
655 # Implicit type created for a simple union's branch
656 assert defined_in.endswith('-wrapper')
657 # Unreachable and not implemented
658 assert False
659 elif defined_in.endswith('Kind'):
660 # See QAPISchema._make_implicit_enum_type()
661 # Implicit enum created for simple union's branches
662 assert role == 'value'
663 role = 'branch'
664 elif defined_in != info.defn_name:
665 return "%s '%s' of type '%s'" % (role, self.name, defined_in)
666 return "%s '%s'" % (role, self.name)
669 class QAPISchemaEnumMember(QAPISchemaMember):
670 role = 'value'
673 class QAPISchemaFeature(QAPISchemaMember):
674 role = 'feature'
677 class QAPISchemaObjectTypeMember(QAPISchemaMember):
678 def __init__(self, name, info, typ, optional, ifcond=None, features=None):
679 super().__init__(name, info, ifcond)
680 assert isinstance(typ, str)
681 assert isinstance(optional, bool)
682 for f in features or []:
683 assert isinstance(f, QAPISchemaFeature)
684 f.set_defined_in(name)
685 self._type_name = typ
686 self.type = None
687 self.optional = optional
688 self.features = features or []
690 def check(self, schema):
691 assert self.defined_in
692 self.type = schema.resolve_type(self._type_name, self.info,
693 self.describe)
694 seen = {}
695 for f in self.features:
696 f.check_clash(self.info, seen)
698 def connect_doc(self, doc):
699 super().connect_doc(doc)
700 if doc:
701 for f in self.features:
702 doc.connect_feature(f)
705 class QAPISchemaVariant(QAPISchemaObjectTypeMember):
706 role = 'branch'
708 def __init__(self, name, info, typ, ifcond=None):
709 super().__init__(name, info, typ, False, ifcond)
712 class QAPISchemaCommand(QAPISchemaEntity):
713 meta = 'command'
715 def __init__(self, name, info, doc, ifcond, features,
716 arg_type, ret_type,
717 gen, success_response, boxed, allow_oob, allow_preconfig,
718 coroutine):
719 super().__init__(name, info, doc, ifcond, features)
720 assert not arg_type or isinstance(arg_type, str)
721 assert not ret_type or isinstance(ret_type, str)
722 self._arg_type_name = arg_type
723 self.arg_type = None
724 self._ret_type_name = ret_type
725 self.ret_type = None
726 self.gen = gen
727 self.success_response = success_response
728 self.boxed = boxed
729 self.allow_oob = allow_oob
730 self.allow_preconfig = allow_preconfig
731 self.coroutine = coroutine
733 def check(self, schema):
734 super().check(schema)
735 if self._arg_type_name:
736 self.arg_type = schema.resolve_type(
737 self._arg_type_name, self.info, "command's 'data'")
738 if not isinstance(self.arg_type, QAPISchemaObjectType):
739 raise QAPISemError(
740 self.info,
741 "command's 'data' cannot take %s"
742 % self.arg_type.describe())
743 if self.arg_type.variants and not self.boxed:
744 raise QAPISemError(
745 self.info,
746 "command's 'data' can take %s only with 'boxed': true"
747 % self.arg_type.describe())
748 if self._ret_type_name:
749 self.ret_type = schema.resolve_type(
750 self._ret_type_name, self.info, "command's 'returns'")
751 if self.name not in self.info.pragma.returns_whitelist:
752 typ = self.ret_type
753 if isinstance(typ, QAPISchemaArrayType):
754 typ = self.ret_type.element_type
755 assert typ
756 if not isinstance(typ, QAPISchemaObjectType):
757 raise QAPISemError(
758 self.info,
759 "command's 'returns' cannot take %s"
760 % self.ret_type.describe())
762 def connect_doc(self, doc=None):
763 super().connect_doc(doc)
764 doc = doc or self.doc
765 if doc:
766 if self.arg_type and self.arg_type.is_implicit():
767 self.arg_type.connect_doc(doc)
769 def visit(self, visitor):
770 super().visit(visitor)
771 visitor.visit_command(
772 self.name, self.info, self.ifcond, self.features,
773 self.arg_type, self.ret_type, self.gen, self.success_response,
774 self.boxed, self.allow_oob, self.allow_preconfig,
775 self.coroutine)
778 class QAPISchemaEvent(QAPISchemaEntity):
779 meta = 'event'
781 def __init__(self, name, info, doc, ifcond, features, arg_type, boxed):
782 super().__init__(name, info, doc, ifcond, features)
783 assert not arg_type or isinstance(arg_type, str)
784 self._arg_type_name = arg_type
785 self.arg_type = None
786 self.boxed = boxed
788 def check(self, schema):
789 super().check(schema)
790 if self._arg_type_name:
791 self.arg_type = schema.resolve_type(
792 self._arg_type_name, self.info, "event's 'data'")
793 if not isinstance(self.arg_type, QAPISchemaObjectType):
794 raise QAPISemError(
795 self.info,
796 "event's 'data' cannot take %s"
797 % self.arg_type.describe())
798 if self.arg_type.variants and not self.boxed:
799 raise QAPISemError(
800 self.info,
801 "event's 'data' can take %s only with 'boxed': true"
802 % self.arg_type.describe())
804 def connect_doc(self, doc=None):
805 super().connect_doc(doc)
806 doc = doc or self.doc
807 if doc:
808 if self.arg_type and self.arg_type.is_implicit():
809 self.arg_type.connect_doc(doc)
811 def visit(self, visitor):
812 super().visit(visitor)
813 visitor.visit_event(
814 self.name, self.info, self.ifcond, self.features,
815 self.arg_type, self.boxed)
818 class QAPISchema:
819 def __init__(self, fname):
820 self.fname = fname
821 parser = QAPISchemaParser(fname)
822 exprs = check_exprs(parser.exprs)
823 self.docs = parser.docs
824 self._entity_list = []
825 self._entity_dict = {}
826 self._module_dict = OrderedDict()
827 self._schema_dir = os.path.dirname(fname)
828 self._make_module(None) # built-ins
829 self._make_module(fname)
830 self._predefining = True
831 self._def_predefineds()
832 self._predefining = False
833 self._def_exprs(exprs)
834 self.check()
836 def _def_entity(self, ent):
837 # Only the predefined types are allowed to not have info
838 assert ent.info or self._predefining
839 self._entity_list.append(ent)
840 if ent.name is None:
841 return
842 # TODO reject names that differ only in '_' vs. '.' vs. '-',
843 # because they're liable to clash in generated C.
844 other_ent = self._entity_dict.get(ent.name)
845 if other_ent:
846 if other_ent.info:
847 where = QAPIError(other_ent.info, None, "previous definition")
848 raise QAPISemError(
849 ent.info,
850 "'%s' is already defined\n%s" % (ent.name, where))
851 raise QAPISemError(
852 ent.info, "%s is already defined" % other_ent.describe())
853 self._entity_dict[ent.name] = ent
855 def lookup_entity(self, name, typ=None):
856 ent = self._entity_dict.get(name)
857 if typ and not isinstance(ent, typ):
858 return None
859 return ent
861 def lookup_type(self, name):
862 return self.lookup_entity(name, QAPISchemaType)
864 def resolve_type(self, name, info, what):
865 typ = self.lookup_type(name)
866 if not typ:
867 if callable(what):
868 what = what(info)
869 raise QAPISemError(
870 info, "%s uses unknown type '%s'" % (what, name))
871 return typ
873 def _module_name(self, fname):
874 if fname is None:
875 return None
876 return os.path.relpath(fname, self._schema_dir)
878 def _make_module(self, fname):
879 name = self._module_name(fname)
880 if name not in self._module_dict:
881 self._module_dict[name] = QAPISchemaModule(name)
882 return self._module_dict[name]
884 def module_by_fname(self, fname):
885 name = self._module_name(fname)
886 assert name in self._module_dict
887 return self._module_dict[name]
889 def _def_include(self, expr, info, doc):
890 include = expr['include']
891 assert doc is None
892 self._def_entity(QAPISchemaInclude(self._make_module(include), info))
894 def _def_builtin_type(self, name, json_type, c_type):
895 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
896 # Instantiating only the arrays that are actually used would
897 # be nice, but we can't as long as their generated code
898 # (qapi-builtin-types.[ch]) may be shared by some other
899 # schema.
900 self._make_array_type(name, None)
902 def _def_predefineds(self):
903 for t in [('str', 'string', 'char' + POINTER_SUFFIX),
904 ('number', 'number', 'double'),
905 ('int', 'int', 'int64_t'),
906 ('int8', 'int', 'int8_t'),
907 ('int16', 'int', 'int16_t'),
908 ('int32', 'int', 'int32_t'),
909 ('int64', 'int', 'int64_t'),
910 ('uint8', 'int', 'uint8_t'),
911 ('uint16', 'int', 'uint16_t'),
912 ('uint32', 'int', 'uint32_t'),
913 ('uint64', 'int', 'uint64_t'),
914 ('size', 'int', 'uint64_t'),
915 ('bool', 'boolean', 'bool'),
916 ('any', 'value', 'QObject' + POINTER_SUFFIX),
917 ('null', 'null', 'QNull' + POINTER_SUFFIX)]:
918 self._def_builtin_type(*t)
919 self.the_empty_object_type = QAPISchemaObjectType(
920 'q_empty', None, None, None, None, None, [], None)
921 self._def_entity(self.the_empty_object_type)
923 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
924 'qbool']
925 qtype_values = self._make_enum_members(
926 [{'name': n} for n in qtypes], None)
928 self._def_entity(QAPISchemaEnumType('QType', None, None, None, None,
929 qtype_values, 'QTYPE'))
931 def _make_features(self, features, info):
932 if features is None:
933 return []
934 return [QAPISchemaFeature(f['name'], info, f.get('if'))
935 for f in features]
937 def _make_enum_members(self, values, info):
938 return [QAPISchemaEnumMember(v['name'], info, v.get('if'))
939 for v in values]
941 def _make_implicit_enum_type(self, name, info, ifcond, values):
942 # See also QAPISchemaObjectTypeMember.describe()
943 name = name + 'Kind' # reserved by check_defn_name_str()
944 self._def_entity(QAPISchemaEnumType(
945 name, info, None, ifcond, None,
946 self._make_enum_members(values, info),
947 None))
948 return name
950 def _make_array_type(self, element_type, info):
951 name = element_type + 'List' # reserved by check_defn_name_str()
952 if not self.lookup_type(name):
953 self._def_entity(QAPISchemaArrayType(name, info, element_type))
954 return name
956 def _make_implicit_object_type(self, name, info, ifcond, role, members):
957 if not members:
958 return None
959 # See also QAPISchemaObjectTypeMember.describe()
960 name = 'q_obj_%s-%s' % (name, role)
961 typ = self.lookup_entity(name, QAPISchemaObjectType)
962 if typ:
963 # The implicit object type has multiple users. This can
964 # happen only for simple unions' implicit wrapper types.
965 # Its ifcond should be the disjunction of its user's
966 # ifconds. Not implemented. Instead, we always pass the
967 # wrapped type's ifcond, which is trivially the same for all
968 # users. It's also necessary for the wrapper to compile.
969 # But it's not tight: the disjunction need not imply it. We
970 # may end up compiling useless wrapper types.
971 # TODO kill simple unions or implement the disjunction
973 # pylint: disable=protected-access
974 assert (ifcond or []) == typ._ifcond
975 else:
976 self._def_entity(QAPISchemaObjectType(
977 name, info, None, ifcond, None, None, members, None))
978 return name
980 def _def_enum_type(self, expr, info, doc):
981 name = expr['enum']
982 data = expr['data']
983 prefix = expr.get('prefix')
984 ifcond = expr.get('if')
985 features = self._make_features(expr.get('features'), info)
986 self._def_entity(QAPISchemaEnumType(
987 name, info, doc, ifcond, features,
988 self._make_enum_members(data, info), prefix))
990 def _make_member(self, name, typ, ifcond, features, info):
991 optional = False
992 if name.startswith('*'):
993 name = name[1:]
994 optional = True
995 if isinstance(typ, list):
996 assert len(typ) == 1
997 typ = self._make_array_type(typ[0], info)
998 return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond,
999 self._make_features(features, info))
1001 def _make_members(self, data, info):
1002 return [self._make_member(key, value['type'], value.get('if'),
1003 value.get('features'), info)
1004 for (key, value) in data.items()]
1006 def _def_struct_type(self, expr, info, doc):
1007 name = expr['struct']
1008 base = expr.get('base')
1009 data = expr['data']
1010 ifcond = expr.get('if')
1011 features = self._make_features(expr.get('features'), info)
1012 self._def_entity(QAPISchemaObjectType(
1013 name, info, doc, ifcond, features, base,
1014 self._make_members(data, info),
1015 None))
1017 def _make_variant(self, case, typ, ifcond, info):
1018 return QAPISchemaVariant(case, info, typ, ifcond)
1020 def _make_simple_variant(self, case, typ, ifcond, info):
1021 if isinstance(typ, list):
1022 assert len(typ) == 1
1023 typ = self._make_array_type(typ[0], info)
1024 typ = self._make_implicit_object_type(
1025 typ, info, self.lookup_type(typ),
1026 'wrapper', [self._make_member('data', typ, None, None, info)])
1027 return QAPISchemaVariant(case, info, typ, ifcond)
1029 def _def_union_type(self, expr, info, doc):
1030 name = expr['union']
1031 data = expr['data']
1032 base = expr.get('base')
1033 ifcond = expr.get('if')
1034 features = self._make_features(expr.get('features'), info)
1035 tag_name = expr.get('discriminator')
1036 tag_member = None
1037 if isinstance(base, dict):
1038 base = self._make_implicit_object_type(
1039 name, info, ifcond,
1040 'base', self._make_members(base, info))
1041 if tag_name:
1042 variants = [self._make_variant(key, value['type'],
1043 value.get('if'), info)
1044 for (key, value) in data.items()]
1045 members = []
1046 else:
1047 variants = [self._make_simple_variant(key, value['type'],
1048 value.get('if'), info)
1049 for (key, value) in data.items()]
1050 enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
1051 typ = self._make_implicit_enum_type(name, info, ifcond, enum)
1052 tag_member = QAPISchemaObjectTypeMember('type', info, typ, False)
1053 members = [tag_member]
1054 self._def_entity(
1055 QAPISchemaObjectType(name, info, doc, ifcond, features,
1056 base, members,
1057 QAPISchemaVariants(
1058 tag_name, info, tag_member, variants)))
1060 def _def_alternate_type(self, expr, info, doc):
1061 name = expr['alternate']
1062 data = expr['data']
1063 ifcond = expr.get('if')
1064 features = self._make_features(expr.get('features'), info)
1065 variants = [self._make_variant(key, value['type'], value.get('if'),
1066 info)
1067 for (key, value) in data.items()]
1068 tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
1069 self._def_entity(
1070 QAPISchemaAlternateType(name, info, doc, ifcond, features,
1071 QAPISchemaVariants(
1072 None, info, tag_member, variants)))
1074 def _def_command(self, expr, info, doc):
1075 name = expr['command']
1076 data = expr.get('data')
1077 rets = expr.get('returns')
1078 gen = expr.get('gen', True)
1079 success_response = expr.get('success-response', True)
1080 boxed = expr.get('boxed', False)
1081 allow_oob = expr.get('allow-oob', False)
1082 allow_preconfig = expr.get('allow-preconfig', False)
1083 coroutine = expr.get('coroutine', False)
1084 ifcond = expr.get('if')
1085 features = self._make_features(expr.get('features'), info)
1086 if isinstance(data, OrderedDict):
1087 data = self._make_implicit_object_type(
1088 name, info, ifcond,
1089 'arg', self._make_members(data, info))
1090 if isinstance(rets, list):
1091 assert len(rets) == 1
1092 rets = self._make_array_type(rets[0], info)
1093 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, features,
1094 data, rets,
1095 gen, success_response,
1096 boxed, allow_oob, allow_preconfig,
1097 coroutine))
1099 def _def_event(self, expr, info, doc):
1100 name = expr['event']
1101 data = expr.get('data')
1102 boxed = expr.get('boxed', False)
1103 ifcond = expr.get('if')
1104 features = self._make_features(expr.get('features'), info)
1105 if isinstance(data, OrderedDict):
1106 data = self._make_implicit_object_type(
1107 name, info, ifcond,
1108 'arg', self._make_members(data, info))
1109 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, features,
1110 data, boxed))
1112 def _def_exprs(self, exprs):
1113 for expr_elem in exprs:
1114 expr = expr_elem['expr']
1115 info = expr_elem['info']
1116 doc = expr_elem.get('doc')
1117 if 'enum' in expr:
1118 self._def_enum_type(expr, info, doc)
1119 elif 'struct' in expr:
1120 self._def_struct_type(expr, info, doc)
1121 elif 'union' in expr:
1122 self._def_union_type(expr, info, doc)
1123 elif 'alternate' in expr:
1124 self._def_alternate_type(expr, info, doc)
1125 elif 'command' in expr:
1126 self._def_command(expr, info, doc)
1127 elif 'event' in expr:
1128 self._def_event(expr, info, doc)
1129 elif 'include' in expr:
1130 self._def_include(expr, info, doc)
1131 else:
1132 assert False
1134 def check(self):
1135 for ent in self._entity_list:
1136 ent.check(self)
1137 ent.connect_doc()
1138 ent.check_doc()
1139 for ent in self._entity_list:
1140 ent.set_module(self)
1142 def visit(self, visitor):
1143 visitor.visit_begin(self)
1144 for mod in self._module_dict.values():
1145 mod.visit(visitor)
1146 visitor.visit_end()