qapi: use './builtin' as the built-in module name
[qemu/ar7.git] / scripts / qapi / schema.py
blob14cf9da784277a57d60204f4c9c35d9951c09a2d
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 fname = info.fname if info else './builtin'
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:
141 def __init__(self, name):
142 self.name = name
143 self._entity_list = []
145 @staticmethod
146 def is_system_module(name: str) -> bool:
148 System modules are internally defined modules.
150 Their names start with the "./" prefix.
152 return name.startswith('./')
154 @classmethod
155 def is_user_module(cls, name: str) -> bool:
157 User modules are those defined by the user in qapi JSON files.
159 They do not start with the "./" prefix.
161 return not cls.is_system_module(name)
163 @staticmethod
164 def is_builtin_module(name: str) -> bool:
166 The built-in module is a single System module for the built-in types.
168 It is always "./builtin".
170 return name == './builtin'
172 def add_entity(self, ent):
173 self._entity_list.append(ent)
175 def visit(self, visitor):
176 visitor.visit_module(self.name)
177 for entity in self._entity_list:
178 if visitor.visit_needed(entity):
179 entity.visit(visitor)
182 class QAPISchemaInclude(QAPISchemaEntity):
183 def __init__(self, sub_module, info):
184 super().__init__(None, info, None)
185 self._sub_module = sub_module
187 def visit(self, visitor):
188 super().visit(visitor)
189 visitor.visit_include(self._sub_module.name, self.info)
192 class QAPISchemaType(QAPISchemaEntity):
193 # Return the C type for common use.
194 # For the types we commonly box, this is a pointer type.
195 def c_type(self):
196 pass
198 # Return the C type to be used in a parameter list.
199 def c_param_type(self):
200 return self.c_type()
202 # Return the C type to be used where we suppress boxing.
203 def c_unboxed_type(self):
204 return self.c_type()
206 def json_type(self):
207 pass
209 def alternate_qtype(self):
210 json2qtype = {
211 'null': 'QTYPE_QNULL',
212 'string': 'QTYPE_QSTRING',
213 'number': 'QTYPE_QNUM',
214 'int': 'QTYPE_QNUM',
215 'boolean': 'QTYPE_QBOOL',
216 'object': 'QTYPE_QDICT'
218 return json2qtype.get(self.json_type())
220 def doc_type(self):
221 if self.is_implicit():
222 return None
223 return self.name
225 def check(self, schema):
226 QAPISchemaEntity.check(self, schema)
227 if 'deprecated' in [f.name for f in self.features]:
228 raise QAPISemError(
229 self.info, "feature 'deprecated' is not supported for types")
231 def describe(self):
232 assert self.meta
233 return "%s type '%s'" % (self.meta, self.name)
236 class QAPISchemaBuiltinType(QAPISchemaType):
237 meta = 'built-in'
239 def __init__(self, name, json_type, c_type):
240 super().__init__(name, None, None)
241 assert not c_type or isinstance(c_type, str)
242 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
243 'value')
244 self._json_type_name = json_type
245 self._c_type_name = c_type
247 def c_name(self):
248 return self.name
250 def c_type(self):
251 return self._c_type_name
253 def c_param_type(self):
254 if self.name == 'str':
255 return 'const ' + self._c_type_name
256 return self._c_type_name
258 def json_type(self):
259 return self._json_type_name
261 def doc_type(self):
262 return self.json_type()
264 def visit(self, visitor):
265 super().visit(visitor)
266 visitor.visit_builtin_type(self.name, self.info, self.json_type())
269 class QAPISchemaEnumType(QAPISchemaType):
270 meta = 'enum'
272 def __init__(self, name, info, doc, ifcond, features, members, prefix):
273 super().__init__(name, info, doc, ifcond, features)
274 for m in members:
275 assert isinstance(m, QAPISchemaEnumMember)
276 m.set_defined_in(name)
277 assert prefix is None or isinstance(prefix, str)
278 self.members = members
279 self.prefix = prefix
281 def check(self, schema):
282 super().check(schema)
283 seen = {}
284 for m in self.members:
285 m.check_clash(self.info, seen)
287 def connect_doc(self, doc=None):
288 super().connect_doc(doc)
289 doc = doc or self.doc
290 for m in self.members:
291 m.connect_doc(doc)
293 def is_implicit(self):
294 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
295 return self.name.endswith('Kind') or self.name == 'QType'
297 def c_type(self):
298 return c_name(self.name)
300 def member_names(self):
301 return [m.name for m in self.members]
303 def json_type(self):
304 return 'string'
306 def visit(self, visitor):
307 super().visit(visitor)
308 visitor.visit_enum_type(
309 self.name, self.info, self.ifcond, self.features,
310 self.members, self.prefix)
313 class QAPISchemaArrayType(QAPISchemaType):
314 meta = 'array'
316 def __init__(self, name, info, element_type):
317 super().__init__(name, info, None)
318 assert isinstance(element_type, str)
319 self._element_type_name = element_type
320 self.element_type = None
322 def check(self, schema):
323 super().check(schema)
324 self.element_type = schema.resolve_type(
325 self._element_type_name, self.info,
326 self.info and self.info.defn_meta)
327 assert not isinstance(self.element_type, QAPISchemaArrayType)
329 def set_module(self, schema):
330 self._set_module(schema, self.element_type.info)
332 @property
333 def ifcond(self):
334 assert self._checked
335 return self.element_type.ifcond
337 def is_implicit(self):
338 return True
340 def c_type(self):
341 return c_name(self.name) + POINTER_SUFFIX
343 def json_type(self):
344 return 'array'
346 def doc_type(self):
347 elt_doc_type = self.element_type.doc_type()
348 if not elt_doc_type:
349 return None
350 return 'array of ' + elt_doc_type
352 def visit(self, visitor):
353 super().visit(visitor)
354 visitor.visit_array_type(self.name, self.info, self.ifcond,
355 self.element_type)
357 def describe(self):
358 assert self.meta
359 return "%s type ['%s']" % (self.meta, self._element_type_name)
362 class QAPISchemaObjectType(QAPISchemaType):
363 def __init__(self, name, info, doc, ifcond, features,
364 base, local_members, variants):
365 # struct has local_members, optional base, and no variants
366 # flat union has base, variants, and no local_members
367 # simple union has local_members, variants, and no base
368 super().__init__(name, info, doc, ifcond, features)
369 self.meta = 'union' if variants else 'struct'
370 assert base is None or isinstance(base, str)
371 for m in local_members:
372 assert isinstance(m, QAPISchemaObjectTypeMember)
373 m.set_defined_in(name)
374 if variants is not None:
375 assert isinstance(variants, QAPISchemaVariants)
376 variants.set_defined_in(name)
377 self._base_name = base
378 self.base = None
379 self.local_members = local_members
380 self.variants = variants
381 self.members = None
383 def check(self, schema):
384 # This calls another type T's .check() exactly when the C
385 # struct emitted by gen_object() contains that T's C struct
386 # (pointers don't count).
387 if self.members is not None:
388 # A previous .check() completed: nothing to do
389 return
390 if self._checked:
391 # Recursed: C struct contains itself
392 raise QAPISemError(self.info,
393 "object %s contains itself" % self.name)
395 super().check(schema)
396 assert self._checked and self.members is None
398 seen = OrderedDict()
399 if self._base_name:
400 self.base = schema.resolve_type(self._base_name, self.info,
401 "'base'")
402 if (not isinstance(self.base, QAPISchemaObjectType)
403 or self.base.variants):
404 raise QAPISemError(
405 self.info,
406 "'base' requires a struct type, %s isn't"
407 % self.base.describe())
408 self.base.check(schema)
409 self.base.check_clash(self.info, seen)
410 for m in self.local_members:
411 m.check(schema)
412 m.check_clash(self.info, seen)
413 members = seen.values()
415 if self.variants:
416 self.variants.check(schema, seen)
417 self.variants.check_clash(self.info, seen)
419 self.members = members # mark completed
421 # Check that the members of this type do not cause duplicate JSON members,
422 # and update seen to track the members seen so far. Report any errors
423 # on behalf of info, which is not necessarily self.info
424 def check_clash(self, info, seen):
425 assert self._checked
426 assert not self.variants # not implemented
427 for m in self.members:
428 m.check_clash(info, seen)
430 def connect_doc(self, doc=None):
431 super().connect_doc(doc)
432 doc = doc or self.doc
433 if self.base and self.base.is_implicit():
434 self.base.connect_doc(doc)
435 for m in self.local_members:
436 m.connect_doc(doc)
438 @property
439 def ifcond(self):
440 assert self._checked
441 if isinstance(self._ifcond, QAPISchemaType):
442 # Simple union wrapper type inherits from wrapped type;
443 # see _make_implicit_object_type()
444 return self._ifcond.ifcond
445 return self._ifcond
447 def is_implicit(self):
448 # See QAPISchema._make_implicit_object_type(), as well as
449 # _def_predefineds()
450 return self.name.startswith('q_')
452 def is_empty(self):
453 assert self.members is not None
454 return not self.members and not self.variants
456 def c_name(self):
457 assert self.name != 'q_empty'
458 return super().c_name()
460 def c_type(self):
461 assert not self.is_implicit()
462 return c_name(self.name) + POINTER_SUFFIX
464 def c_unboxed_type(self):
465 return c_name(self.name)
467 def json_type(self):
468 return 'object'
470 def visit(self, visitor):
471 super().visit(visitor)
472 visitor.visit_object_type(
473 self.name, self.info, self.ifcond, self.features,
474 self.base, self.local_members, self.variants)
475 visitor.visit_object_type_flat(
476 self.name, self.info, self.ifcond, self.features,
477 self.members, self.variants)
480 class QAPISchemaAlternateType(QAPISchemaType):
481 meta = 'alternate'
483 def __init__(self, name, info, doc, ifcond, features, variants):
484 super().__init__(name, info, doc, ifcond, features)
485 assert isinstance(variants, QAPISchemaVariants)
486 assert variants.tag_member
487 variants.set_defined_in(name)
488 variants.tag_member.set_defined_in(self.name)
489 self.variants = variants
491 def check(self, schema):
492 super().check(schema)
493 self.variants.tag_member.check(schema)
494 # Not calling self.variants.check_clash(), because there's nothing
495 # to clash with
496 self.variants.check(schema, {})
497 # Alternate branch names have no relation to the tag enum values;
498 # so we have to check for potential name collisions ourselves.
499 seen = {}
500 types_seen = {}
501 for v in self.variants.variants:
502 v.check_clash(self.info, seen)
503 qtype = v.type.alternate_qtype()
504 if not qtype:
505 raise QAPISemError(
506 self.info,
507 "%s cannot use %s"
508 % (v.describe(self.info), v.type.describe()))
509 conflicting = set([qtype])
510 if qtype == 'QTYPE_QSTRING':
511 if isinstance(v.type, QAPISchemaEnumType):
512 for m in v.type.members:
513 if m.name in ['on', 'off']:
514 conflicting.add('QTYPE_QBOOL')
515 if re.match(r'[-+0-9.]', m.name):
516 # lazy, could be tightened
517 conflicting.add('QTYPE_QNUM')
518 else:
519 conflicting.add('QTYPE_QNUM')
520 conflicting.add('QTYPE_QBOOL')
521 for qt in conflicting:
522 if qt in types_seen:
523 raise QAPISemError(
524 self.info,
525 "%s can't be distinguished from '%s'"
526 % (v.describe(self.info), types_seen[qt]))
527 types_seen[qt] = v.name
529 def connect_doc(self, doc=None):
530 super().connect_doc(doc)
531 doc = doc or self.doc
532 for v in self.variants.variants:
533 v.connect_doc(doc)
535 def c_type(self):
536 return c_name(self.name) + POINTER_SUFFIX
538 def json_type(self):
539 return 'value'
541 def visit(self, visitor):
542 super().visit(visitor)
543 visitor.visit_alternate_type(
544 self.name, self.info, self.ifcond, self.features, self.variants)
547 class QAPISchemaVariants:
548 def __init__(self, tag_name, info, tag_member, variants):
549 # Flat unions pass tag_name but not tag_member.
550 # Simple unions and alternates pass tag_member but not tag_name.
551 # After check(), tag_member is always set, and tag_name remains
552 # a reliable witness of being used by a flat union.
553 assert bool(tag_member) != bool(tag_name)
554 assert (isinstance(tag_name, str) or
555 isinstance(tag_member, QAPISchemaObjectTypeMember))
556 for v in variants:
557 assert isinstance(v, QAPISchemaVariant)
558 self._tag_name = tag_name
559 self.info = info
560 self.tag_member = tag_member
561 self.variants = variants
563 def set_defined_in(self, name):
564 for v in self.variants:
565 v.set_defined_in(name)
567 def check(self, schema, seen):
568 if not self.tag_member: # flat union
569 self.tag_member = seen.get(c_name(self._tag_name))
570 base = "'base'"
571 # Pointing to the base type when not implicit would be
572 # nice, but we don't know it here
573 if not self.tag_member or self._tag_name != self.tag_member.name:
574 raise QAPISemError(
575 self.info,
576 "discriminator '%s' is not a member of %s"
577 % (self._tag_name, base))
578 # Here we do:
579 base_type = schema.lookup_type(self.tag_member.defined_in)
580 assert base_type
581 if not base_type.is_implicit():
582 base = "base type '%s'" % self.tag_member.defined_in
583 if not isinstance(self.tag_member.type, QAPISchemaEnumType):
584 raise QAPISemError(
585 self.info,
586 "discriminator member '%s' of %s must be of enum type"
587 % (self._tag_name, base))
588 if self.tag_member.optional:
589 raise QAPISemError(
590 self.info,
591 "discriminator member '%s' of %s must not be optional"
592 % (self._tag_name, base))
593 if self.tag_member.ifcond:
594 raise QAPISemError(
595 self.info,
596 "discriminator member '%s' of %s must not be conditional"
597 % (self._tag_name, base))
598 else: # simple union
599 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
600 assert not self.tag_member.optional
601 assert self.tag_member.ifcond == []
602 if self._tag_name: # flat union
603 # branches that are not explicitly covered get an empty type
604 cases = {v.name for v in self.variants}
605 for m in self.tag_member.type.members:
606 if m.name not in cases:
607 v = QAPISchemaVariant(m.name, self.info,
608 'q_empty', m.ifcond)
609 v.set_defined_in(self.tag_member.defined_in)
610 self.variants.append(v)
611 if not self.variants:
612 raise QAPISemError(self.info, "union has no branches")
613 for v in self.variants:
614 v.check(schema)
615 # Union names must match enum values; alternate names are
616 # checked separately. Use 'seen' to tell the two apart.
617 if seen:
618 if v.name not in self.tag_member.type.member_names():
619 raise QAPISemError(
620 self.info,
621 "branch '%s' is not a value of %s"
622 % (v.name, self.tag_member.type.describe()))
623 if (not isinstance(v.type, QAPISchemaObjectType)
624 or v.type.variants):
625 raise QAPISemError(
626 self.info,
627 "%s cannot use %s"
628 % (v.describe(self.info), v.type.describe()))
629 v.type.check(schema)
631 def check_clash(self, info, seen):
632 for v in self.variants:
633 # Reset seen map for each variant, since qapi names from one
634 # branch do not affect another branch
635 v.type.check_clash(info, dict(seen))
638 class QAPISchemaMember:
639 """ Represents object members, enum members and features """
640 role = 'member'
642 def __init__(self, name, info, ifcond=None):
643 assert isinstance(name, str)
644 self.name = name
645 self.info = info
646 self.ifcond = ifcond or []
647 self.defined_in = None
649 def set_defined_in(self, name):
650 assert not self.defined_in
651 self.defined_in = name
653 def check_clash(self, info, seen):
654 cname = c_name(self.name)
655 if cname in seen:
656 raise QAPISemError(
657 info,
658 "%s collides with %s"
659 % (self.describe(info), seen[cname].describe(info)))
660 seen[cname] = self
662 def connect_doc(self, doc):
663 if doc:
664 doc.connect_member(self)
666 def describe(self, info):
667 role = self.role
668 defined_in = self.defined_in
669 assert defined_in
671 if defined_in.startswith('q_obj_'):
672 # See QAPISchema._make_implicit_object_type() - reverse the
673 # mapping there to create a nice human-readable description
674 defined_in = defined_in[6:]
675 if defined_in.endswith('-arg'):
676 # Implicit type created for a command's dict 'data'
677 assert role == 'member'
678 role = 'parameter'
679 elif defined_in.endswith('-base'):
680 # Implicit type created for a flat union's dict 'base'
681 role = 'base ' + role
682 else:
683 # Implicit type created for a simple union's branch
684 assert defined_in.endswith('-wrapper')
685 # Unreachable and not implemented
686 assert False
687 elif defined_in.endswith('Kind'):
688 # See QAPISchema._make_implicit_enum_type()
689 # Implicit enum created for simple union's branches
690 assert role == 'value'
691 role = 'branch'
692 elif defined_in != info.defn_name:
693 return "%s '%s' of type '%s'" % (role, self.name, defined_in)
694 return "%s '%s'" % (role, self.name)
697 class QAPISchemaEnumMember(QAPISchemaMember):
698 role = 'value'
701 class QAPISchemaFeature(QAPISchemaMember):
702 role = 'feature'
705 class QAPISchemaObjectTypeMember(QAPISchemaMember):
706 def __init__(self, name, info, typ, optional, ifcond=None, features=None):
707 super().__init__(name, info, ifcond)
708 assert isinstance(typ, str)
709 assert isinstance(optional, bool)
710 for f in features or []:
711 assert isinstance(f, QAPISchemaFeature)
712 f.set_defined_in(name)
713 self._type_name = typ
714 self.type = None
715 self.optional = optional
716 self.features = features or []
718 def check(self, schema):
719 assert self.defined_in
720 self.type = schema.resolve_type(self._type_name, self.info,
721 self.describe)
722 seen = {}
723 for f in self.features:
724 f.check_clash(self.info, seen)
726 def connect_doc(self, doc):
727 super().connect_doc(doc)
728 if doc:
729 for f in self.features:
730 doc.connect_feature(f)
733 class QAPISchemaVariant(QAPISchemaObjectTypeMember):
734 role = 'branch'
736 def __init__(self, name, info, typ, ifcond=None):
737 super().__init__(name, info, typ, False, ifcond)
740 class QAPISchemaCommand(QAPISchemaEntity):
741 meta = 'command'
743 def __init__(self, name, info, doc, ifcond, features,
744 arg_type, ret_type,
745 gen, success_response, boxed, allow_oob, allow_preconfig,
746 coroutine):
747 super().__init__(name, info, doc, ifcond, features)
748 assert not arg_type or isinstance(arg_type, str)
749 assert not ret_type or isinstance(ret_type, str)
750 self._arg_type_name = arg_type
751 self.arg_type = None
752 self._ret_type_name = ret_type
753 self.ret_type = None
754 self.gen = gen
755 self.success_response = success_response
756 self.boxed = boxed
757 self.allow_oob = allow_oob
758 self.allow_preconfig = allow_preconfig
759 self.coroutine = coroutine
761 def check(self, schema):
762 super().check(schema)
763 if self._arg_type_name:
764 self.arg_type = schema.resolve_type(
765 self._arg_type_name, self.info, "command's 'data'")
766 if not isinstance(self.arg_type, QAPISchemaObjectType):
767 raise QAPISemError(
768 self.info,
769 "command's 'data' cannot take %s"
770 % self.arg_type.describe())
771 if self.arg_type.variants and not self.boxed:
772 raise QAPISemError(
773 self.info,
774 "command's 'data' can take %s only with 'boxed': true"
775 % self.arg_type.describe())
776 if self._ret_type_name:
777 self.ret_type = schema.resolve_type(
778 self._ret_type_name, self.info, "command's 'returns'")
779 if self.name not in self.info.pragma.returns_whitelist:
780 typ = self.ret_type
781 if isinstance(typ, QAPISchemaArrayType):
782 typ = self.ret_type.element_type
783 assert typ
784 if not isinstance(typ, QAPISchemaObjectType):
785 raise QAPISemError(
786 self.info,
787 "command's 'returns' cannot take %s"
788 % self.ret_type.describe())
790 def connect_doc(self, doc=None):
791 super().connect_doc(doc)
792 doc = doc or self.doc
793 if doc:
794 if self.arg_type and self.arg_type.is_implicit():
795 self.arg_type.connect_doc(doc)
797 def visit(self, visitor):
798 super().visit(visitor)
799 visitor.visit_command(
800 self.name, self.info, self.ifcond, self.features,
801 self.arg_type, self.ret_type, self.gen, self.success_response,
802 self.boxed, self.allow_oob, self.allow_preconfig,
803 self.coroutine)
806 class QAPISchemaEvent(QAPISchemaEntity):
807 meta = 'event'
809 def __init__(self, name, info, doc, ifcond, features, arg_type, boxed):
810 super().__init__(name, info, doc, ifcond, features)
811 assert not arg_type or isinstance(arg_type, str)
812 self._arg_type_name = arg_type
813 self.arg_type = None
814 self.boxed = boxed
816 def check(self, schema):
817 super().check(schema)
818 if self._arg_type_name:
819 self.arg_type = schema.resolve_type(
820 self._arg_type_name, self.info, "event's 'data'")
821 if not isinstance(self.arg_type, QAPISchemaObjectType):
822 raise QAPISemError(
823 self.info,
824 "event's 'data' cannot take %s"
825 % self.arg_type.describe())
826 if self.arg_type.variants and not self.boxed:
827 raise QAPISemError(
828 self.info,
829 "event's 'data' can take %s only with 'boxed': true"
830 % self.arg_type.describe())
832 def connect_doc(self, doc=None):
833 super().connect_doc(doc)
834 doc = doc or self.doc
835 if doc:
836 if self.arg_type and self.arg_type.is_implicit():
837 self.arg_type.connect_doc(doc)
839 def visit(self, visitor):
840 super().visit(visitor)
841 visitor.visit_event(
842 self.name, self.info, self.ifcond, self.features,
843 self.arg_type, self.boxed)
846 class QAPISchema:
847 def __init__(self, fname):
848 self.fname = fname
849 parser = QAPISchemaParser(fname)
850 exprs = check_exprs(parser.exprs)
851 self.docs = parser.docs
852 self._entity_list = []
853 self._entity_dict = {}
854 self._module_dict = OrderedDict()
855 self._schema_dir = os.path.dirname(fname)
856 self._make_module('./builtin')
857 self._make_module(fname)
858 self._predefining = True
859 self._def_predefineds()
860 self._predefining = False
861 self._def_exprs(exprs)
862 self.check()
864 def _def_entity(self, ent):
865 # Only the predefined types are allowed to not have info
866 assert ent.info or self._predefining
867 self._entity_list.append(ent)
868 if ent.name is None:
869 return
870 # TODO reject names that differ only in '_' vs. '.' vs. '-',
871 # because they're liable to clash in generated C.
872 other_ent = self._entity_dict.get(ent.name)
873 if other_ent:
874 if other_ent.info:
875 where = QAPIError(other_ent.info, None, "previous definition")
876 raise QAPISemError(
877 ent.info,
878 "'%s' is already defined\n%s" % (ent.name, where))
879 raise QAPISemError(
880 ent.info, "%s is already defined" % other_ent.describe())
881 self._entity_dict[ent.name] = ent
883 def lookup_entity(self, name, typ=None):
884 ent = self._entity_dict.get(name)
885 if typ and not isinstance(ent, typ):
886 return None
887 return ent
889 def lookup_type(self, name):
890 return self.lookup_entity(name, QAPISchemaType)
892 def resolve_type(self, name, info, what):
893 typ = self.lookup_type(name)
894 if not typ:
895 if callable(what):
896 what = what(info)
897 raise QAPISemError(
898 info, "%s uses unknown type '%s'" % (what, name))
899 return typ
901 def _module_name(self, fname: str) -> str:
902 if QAPISchemaModule.is_system_module(fname):
903 return fname
904 return os.path.relpath(fname, self._schema_dir)
906 def _make_module(self, fname):
907 name = self._module_name(fname)
908 if name not in self._module_dict:
909 self._module_dict[name] = QAPISchemaModule(name)
910 return self._module_dict[name]
912 def module_by_fname(self, fname):
913 name = self._module_name(fname)
914 return self._module_dict[name]
916 def _def_include(self, expr, info, doc):
917 include = expr['include']
918 assert doc is None
919 self._def_entity(QAPISchemaInclude(self._make_module(include), info))
921 def _def_builtin_type(self, name, json_type, c_type):
922 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
923 # Instantiating only the arrays that are actually used would
924 # be nice, but we can't as long as their generated code
925 # (qapi-builtin-types.[ch]) may be shared by some other
926 # schema.
927 self._make_array_type(name, None)
929 def _def_predefineds(self):
930 for t in [('str', 'string', 'char' + POINTER_SUFFIX),
931 ('number', 'number', 'double'),
932 ('int', 'int', 'int64_t'),
933 ('int8', 'int', 'int8_t'),
934 ('int16', 'int', 'int16_t'),
935 ('int32', 'int', 'int32_t'),
936 ('int64', 'int', 'int64_t'),
937 ('uint8', 'int', 'uint8_t'),
938 ('uint16', 'int', 'uint16_t'),
939 ('uint32', 'int', 'uint32_t'),
940 ('uint64', 'int', 'uint64_t'),
941 ('size', 'int', 'uint64_t'),
942 ('bool', 'boolean', 'bool'),
943 ('any', 'value', 'QObject' + POINTER_SUFFIX),
944 ('null', 'null', 'QNull' + POINTER_SUFFIX)]:
945 self._def_builtin_type(*t)
946 self.the_empty_object_type = QAPISchemaObjectType(
947 'q_empty', None, None, None, None, None, [], None)
948 self._def_entity(self.the_empty_object_type)
950 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
951 'qbool']
952 qtype_values = self._make_enum_members(
953 [{'name': n} for n in qtypes], None)
955 self._def_entity(QAPISchemaEnumType('QType', None, None, None, None,
956 qtype_values, 'QTYPE'))
958 def _make_features(self, features, info):
959 if features is None:
960 return []
961 return [QAPISchemaFeature(f['name'], info, f.get('if'))
962 for f in features]
964 def _make_enum_members(self, values, info):
965 return [QAPISchemaEnumMember(v['name'], info, v.get('if'))
966 for v in values]
968 def _make_implicit_enum_type(self, name, info, ifcond, values):
969 # See also QAPISchemaObjectTypeMember.describe()
970 name = name + 'Kind' # reserved by check_defn_name_str()
971 self._def_entity(QAPISchemaEnumType(
972 name, info, None, ifcond, None,
973 self._make_enum_members(values, info),
974 None))
975 return name
977 def _make_array_type(self, element_type, info):
978 name = element_type + 'List' # reserved by check_defn_name_str()
979 if not self.lookup_type(name):
980 self._def_entity(QAPISchemaArrayType(name, info, element_type))
981 return name
983 def _make_implicit_object_type(self, name, info, ifcond, role, members):
984 if not members:
985 return None
986 # See also QAPISchemaObjectTypeMember.describe()
987 name = 'q_obj_%s-%s' % (name, role)
988 typ = self.lookup_entity(name, QAPISchemaObjectType)
989 if typ:
990 # The implicit object type has multiple users. This can
991 # happen only for simple unions' implicit wrapper types.
992 # Its ifcond should be the disjunction of its user's
993 # ifconds. Not implemented. Instead, we always pass the
994 # wrapped type's ifcond, which is trivially the same for all
995 # users. It's also necessary for the wrapper to compile.
996 # But it's not tight: the disjunction need not imply it. We
997 # may end up compiling useless wrapper types.
998 # TODO kill simple unions or implement the disjunction
1000 # pylint: disable=protected-access
1001 assert (ifcond or []) == typ._ifcond
1002 else:
1003 self._def_entity(QAPISchemaObjectType(
1004 name, info, None, ifcond, None, None, members, None))
1005 return name
1007 def _def_enum_type(self, expr, info, doc):
1008 name = expr['enum']
1009 data = expr['data']
1010 prefix = expr.get('prefix')
1011 ifcond = expr.get('if')
1012 features = self._make_features(expr.get('features'), info)
1013 self._def_entity(QAPISchemaEnumType(
1014 name, info, doc, ifcond, features,
1015 self._make_enum_members(data, info), prefix))
1017 def _make_member(self, name, typ, ifcond, features, info):
1018 optional = False
1019 if name.startswith('*'):
1020 name = name[1:]
1021 optional = True
1022 if isinstance(typ, list):
1023 assert len(typ) == 1
1024 typ = self._make_array_type(typ[0], info)
1025 return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond,
1026 self._make_features(features, info))
1028 def _make_members(self, data, info):
1029 return [self._make_member(key, value['type'], value.get('if'),
1030 value.get('features'), info)
1031 for (key, value) in data.items()]
1033 def _def_struct_type(self, expr, info, doc):
1034 name = expr['struct']
1035 base = expr.get('base')
1036 data = expr['data']
1037 ifcond = expr.get('if')
1038 features = self._make_features(expr.get('features'), info)
1039 self._def_entity(QAPISchemaObjectType(
1040 name, info, doc, ifcond, features, base,
1041 self._make_members(data, info),
1042 None))
1044 def _make_variant(self, case, typ, ifcond, info):
1045 return QAPISchemaVariant(case, info, typ, ifcond)
1047 def _make_simple_variant(self, case, typ, ifcond, info):
1048 if isinstance(typ, list):
1049 assert len(typ) == 1
1050 typ = self._make_array_type(typ[0], info)
1051 typ = self._make_implicit_object_type(
1052 typ, info, self.lookup_type(typ),
1053 'wrapper', [self._make_member('data', typ, None, None, info)])
1054 return QAPISchemaVariant(case, info, typ, ifcond)
1056 def _def_union_type(self, expr, info, doc):
1057 name = expr['union']
1058 data = expr['data']
1059 base = expr.get('base')
1060 ifcond = expr.get('if')
1061 features = self._make_features(expr.get('features'), info)
1062 tag_name = expr.get('discriminator')
1063 tag_member = None
1064 if isinstance(base, dict):
1065 base = self._make_implicit_object_type(
1066 name, info, ifcond,
1067 'base', self._make_members(base, info))
1068 if tag_name:
1069 variants = [self._make_variant(key, value['type'],
1070 value.get('if'), info)
1071 for (key, value) in data.items()]
1072 members = []
1073 else:
1074 variants = [self._make_simple_variant(key, value['type'],
1075 value.get('if'), info)
1076 for (key, value) in data.items()]
1077 enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
1078 typ = self._make_implicit_enum_type(name, info, ifcond, enum)
1079 tag_member = QAPISchemaObjectTypeMember('type', info, typ, False)
1080 members = [tag_member]
1081 self._def_entity(
1082 QAPISchemaObjectType(name, info, doc, ifcond, features,
1083 base, members,
1084 QAPISchemaVariants(
1085 tag_name, info, tag_member, variants)))
1087 def _def_alternate_type(self, expr, info, doc):
1088 name = expr['alternate']
1089 data = expr['data']
1090 ifcond = expr.get('if')
1091 features = self._make_features(expr.get('features'), info)
1092 variants = [self._make_variant(key, value['type'], value.get('if'),
1093 info)
1094 for (key, value) in data.items()]
1095 tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
1096 self._def_entity(
1097 QAPISchemaAlternateType(name, info, doc, ifcond, features,
1098 QAPISchemaVariants(
1099 None, info, tag_member, variants)))
1101 def _def_command(self, expr, info, doc):
1102 name = expr['command']
1103 data = expr.get('data')
1104 rets = expr.get('returns')
1105 gen = expr.get('gen', True)
1106 success_response = expr.get('success-response', True)
1107 boxed = expr.get('boxed', False)
1108 allow_oob = expr.get('allow-oob', False)
1109 allow_preconfig = expr.get('allow-preconfig', False)
1110 coroutine = expr.get('coroutine', False)
1111 ifcond = expr.get('if')
1112 features = self._make_features(expr.get('features'), info)
1113 if isinstance(data, OrderedDict):
1114 data = self._make_implicit_object_type(
1115 name, info, ifcond,
1116 'arg', self._make_members(data, info))
1117 if isinstance(rets, list):
1118 assert len(rets) == 1
1119 rets = self._make_array_type(rets[0], info)
1120 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, features,
1121 data, rets,
1122 gen, success_response,
1123 boxed, allow_oob, allow_preconfig,
1124 coroutine))
1126 def _def_event(self, expr, info, doc):
1127 name = expr['event']
1128 data = expr.get('data')
1129 boxed = expr.get('boxed', False)
1130 ifcond = expr.get('if')
1131 features = self._make_features(expr.get('features'), info)
1132 if isinstance(data, OrderedDict):
1133 data = self._make_implicit_object_type(
1134 name, info, ifcond,
1135 'arg', self._make_members(data, info))
1136 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, features,
1137 data, boxed))
1139 def _def_exprs(self, exprs):
1140 for expr_elem in exprs:
1141 expr = expr_elem['expr']
1142 info = expr_elem['info']
1143 doc = expr_elem.get('doc')
1144 if 'enum' in expr:
1145 self._def_enum_type(expr, info, doc)
1146 elif 'struct' in expr:
1147 self._def_struct_type(expr, info, doc)
1148 elif 'union' in expr:
1149 self._def_union_type(expr, info, doc)
1150 elif 'alternate' in expr:
1151 self._def_alternate_type(expr, info, doc)
1152 elif 'command' in expr:
1153 self._def_command(expr, info, doc)
1154 elif 'event' in expr:
1155 self._def_event(expr, info, doc)
1156 elif 'include' in expr:
1157 self._def_include(expr, info, doc)
1158 else:
1159 assert False
1161 def check(self):
1162 for ent in self._entity_list:
1163 ent.check(self)
1164 ent.connect_doc()
1165 ent.check_doc()
1166 for ent in self._entity_list:
1167 ent.set_module(self)
1169 def visit(self, visitor):
1170 visitor.visit_begin(self)
1171 for mod in self._module_dict.values():
1172 mod.visit(visitor)
1173 visitor.visit_end()