hw/arm/virt: Add missing 5.0 options call to 4.2 options
[qemu/ar7.git] / scripts / qapi / schema.py
blob5100110fa2dee17f1777cb6ae795c6ac621003d2
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 import os
18 import re
19 from collections import OrderedDict
21 from qapi.common import c_name, pointer_suffix
22 from qapi.error import QAPIError, QAPIParseError, QAPISemError
23 from qapi.expr import check_exprs
24 from qapi.parser import QAPISchemaParser
27 class QAPISchemaEntity(object):
28 meta = None
30 def __init__(self, name, info, doc, ifcond=None, features=None):
31 assert name is None or isinstance(name, str)
32 for f in features or []:
33 assert isinstance(f, QAPISchemaFeature)
34 f.set_defined_in(name)
35 self.name = name
36 self._module = None
37 # For explicitly defined entities, info points to the (explicit)
38 # definition. For builtins (and their arrays), info is None.
39 # For implicitly defined entities, info points to a place that
40 # triggered the implicit definition (there may be more than one
41 # such place).
42 self.info = info
43 self.doc = doc
44 self._ifcond = ifcond or []
45 self.features = features or []
46 self._checked = False
48 def c_name(self):
49 return c_name(self.name)
51 def check(self, schema):
52 assert not self._checked
53 seen = {}
54 for f in self.features:
55 f.check_clash(self.info, seen)
56 if self.doc:
57 self.doc.connect_feature(f)
59 self._checked = True
61 def connect_doc(self, doc=None):
62 pass
64 def check_doc(self):
65 if self.doc:
66 self.doc.check()
68 def _set_module(self, schema, info):
69 assert self._checked
70 self._module = schema.module_by_fname(info and info.fname)
71 self._module.add_entity(self)
73 def set_module(self, schema):
74 self._set_module(schema, self.info)
76 @property
77 def ifcond(self):
78 assert self._checked
79 return self._ifcond
81 def is_implicit(self):
82 return not self.info
84 def visit(self, visitor):
85 assert self._checked
87 def describe(self):
88 assert self.meta
89 return "%s '%s'" % (self.meta, self.name)
92 class QAPISchemaVisitor(object):
93 def visit_begin(self, schema):
94 pass
96 def visit_end(self):
97 pass
99 def visit_module(self, fname):
100 pass
102 def visit_needed(self, entity):
103 # Default to visiting everything
104 return True
106 def visit_include(self, fname, info):
107 pass
109 def visit_builtin_type(self, name, info, json_type):
110 pass
112 def visit_enum_type(self, name, info, ifcond, members, prefix):
113 pass
115 def visit_array_type(self, name, info, ifcond, element_type):
116 pass
118 def visit_object_type(self, name, info, ifcond, base, members, variants,
119 features):
120 pass
122 def visit_object_type_flat(self, name, info, ifcond, members, variants,
123 features):
124 pass
126 def visit_alternate_type(self, name, info, ifcond, variants):
127 pass
129 def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
130 success_response, boxed, allow_oob, allow_preconfig,
131 features):
132 pass
134 def visit_event(self, name, info, ifcond, arg_type, boxed):
135 pass
138 class QAPISchemaModule(object):
139 def __init__(self, name):
140 self.name = name
141 self._entity_list = []
143 def add_entity(self, ent):
144 self._entity_list.append(ent)
146 def visit(self, visitor):
147 visitor.visit_module(self.name)
148 for entity in self._entity_list:
149 if visitor.visit_needed(entity):
150 entity.visit(visitor)
153 class QAPISchemaInclude(QAPISchemaEntity):
154 def __init__(self, sub_module, info):
155 QAPISchemaEntity.__init__(self, None, info, None)
156 self._sub_module = sub_module
158 def visit(self, visitor):
159 QAPISchemaEntity.visit(self, visitor)
160 visitor.visit_include(self._sub_module.name, self.info)
163 class QAPISchemaType(QAPISchemaEntity):
164 # Return the C type for common use.
165 # For the types we commonly box, this is a pointer type.
166 def c_type(self):
167 pass
169 # Return the C type to be used in a parameter list.
170 def c_param_type(self):
171 return self.c_type()
173 # Return the C type to be used where we suppress boxing.
174 def c_unboxed_type(self):
175 return self.c_type()
177 def json_type(self):
178 pass
180 def alternate_qtype(self):
181 json2qtype = {
182 'null': 'QTYPE_QNULL',
183 'string': 'QTYPE_QSTRING',
184 'number': 'QTYPE_QNUM',
185 'int': 'QTYPE_QNUM',
186 'boolean': 'QTYPE_QBOOL',
187 'object': 'QTYPE_QDICT'
189 return json2qtype.get(self.json_type())
191 def doc_type(self):
192 if self.is_implicit():
193 return None
194 return self.name
196 def describe(self):
197 assert self.meta
198 return "%s type '%s'" % (self.meta, self.name)
201 class QAPISchemaBuiltinType(QAPISchemaType):
202 meta = 'built-in'
204 def __init__(self, name, json_type, c_type):
205 QAPISchemaType.__init__(self, name, None, None)
206 assert not c_type or isinstance(c_type, str)
207 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
208 'value')
209 self._json_type_name = json_type
210 self._c_type_name = c_type
212 def c_name(self):
213 return self.name
215 def c_type(self):
216 return self._c_type_name
218 def c_param_type(self):
219 if self.name == 'str':
220 return 'const ' + self._c_type_name
221 return self._c_type_name
223 def json_type(self):
224 return self._json_type_name
226 def doc_type(self):
227 return self.json_type()
229 def visit(self, visitor):
230 QAPISchemaType.visit(self, visitor)
231 visitor.visit_builtin_type(self.name, self.info, self.json_type())
234 class QAPISchemaEnumType(QAPISchemaType):
235 meta = 'enum'
237 def __init__(self, name, info, doc, ifcond, members, prefix):
238 QAPISchemaType.__init__(self, name, info, doc, ifcond)
239 for m in members:
240 assert isinstance(m, QAPISchemaEnumMember)
241 m.set_defined_in(name)
242 assert prefix is None or isinstance(prefix, str)
243 self.members = members
244 self.prefix = prefix
246 def check(self, schema):
247 QAPISchemaType.check(self, schema)
248 seen = {}
249 for m in self.members:
250 m.check_clash(self.info, seen)
252 def connect_doc(self, doc=None):
253 doc = doc or self.doc
254 if doc:
255 for m in self.members:
256 doc.connect_member(m)
258 def is_implicit(self):
259 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
260 return self.name.endswith('Kind') or self.name == 'QType'
262 def c_type(self):
263 return c_name(self.name)
265 def member_names(self):
266 return [m.name for m in self.members]
268 def json_type(self):
269 return 'string'
271 def visit(self, visitor):
272 QAPISchemaType.visit(self, visitor)
273 visitor.visit_enum_type(self.name, self.info, self.ifcond,
274 self.members, self.prefix)
277 class QAPISchemaArrayType(QAPISchemaType):
278 meta = 'array'
280 def __init__(self, name, info, element_type):
281 QAPISchemaType.__init__(self, name, info, None, None)
282 assert isinstance(element_type, str)
283 self._element_type_name = element_type
284 self.element_type = None
286 def check(self, schema):
287 QAPISchemaType.check(self, schema)
288 self.element_type = schema.resolve_type(
289 self._element_type_name, self.info,
290 self.info and self.info.defn_meta)
291 assert not isinstance(self.element_type, QAPISchemaArrayType)
293 def set_module(self, schema):
294 self._set_module(schema, self.element_type.info)
296 @property
297 def ifcond(self):
298 assert self._checked
299 return self.element_type.ifcond
301 def is_implicit(self):
302 return True
304 def c_type(self):
305 return c_name(self.name) + pointer_suffix
307 def json_type(self):
308 return 'array'
310 def doc_type(self):
311 elt_doc_type = self.element_type.doc_type()
312 if not elt_doc_type:
313 return None
314 return 'array of ' + elt_doc_type
316 def visit(self, visitor):
317 QAPISchemaType.visit(self, visitor)
318 visitor.visit_array_type(self.name, self.info, self.ifcond,
319 self.element_type)
321 def describe(self):
322 assert self.meta
323 return "%s type ['%s']" % (self.meta, self._element_type_name)
326 class QAPISchemaObjectType(QAPISchemaType):
327 def __init__(self, name, info, doc, ifcond,
328 base, local_members, variants, features):
329 # struct has local_members, optional base, and no variants
330 # flat union has base, variants, and no local_members
331 # simple union has local_members, variants, and no base
332 QAPISchemaType.__init__(self, name, info, doc, ifcond, features)
333 self.meta = 'union' if variants else 'struct'
334 assert base is None or isinstance(base, str)
335 for m in local_members:
336 assert isinstance(m, QAPISchemaObjectTypeMember)
337 m.set_defined_in(name)
338 if variants is not None:
339 assert isinstance(variants, QAPISchemaObjectTypeVariants)
340 variants.set_defined_in(name)
341 self._base_name = base
342 self.base = None
343 self.local_members = local_members
344 self.variants = variants
345 self.members = None
347 def check(self, schema):
348 # This calls another type T's .check() exactly when the C
349 # struct emitted by gen_object() contains that T's C struct
350 # (pointers don't count).
351 if self.members is not None:
352 # A previous .check() completed: nothing to do
353 return
354 if self._checked:
355 # Recursed: C struct contains itself
356 raise QAPISemError(self.info,
357 "object %s contains itself" % self.name)
359 QAPISchemaType.check(self, schema)
360 assert self._checked and self.members is None
362 seen = OrderedDict()
363 if self._base_name:
364 self.base = schema.resolve_type(self._base_name, self.info,
365 "'base'")
366 if (not isinstance(self.base, QAPISchemaObjectType)
367 or self.base.variants):
368 raise QAPISemError(
369 self.info,
370 "'base' requires a struct type, %s isn't"
371 % self.base.describe())
372 self.base.check(schema)
373 self.base.check_clash(self.info, seen)
374 for m in self.local_members:
375 m.check(schema)
376 m.check_clash(self.info, seen)
377 members = seen.values()
379 if self.variants:
380 self.variants.check(schema, seen)
381 self.variants.check_clash(self.info, seen)
383 self.members = members # mark completed
385 # Check that the members of this type do not cause duplicate JSON members,
386 # and update seen to track the members seen so far. Report any errors
387 # on behalf of info, which is not necessarily self.info
388 def check_clash(self, info, seen):
389 assert self._checked
390 assert not self.variants # not implemented
391 for m in self.members:
392 m.check_clash(info, seen)
394 def connect_doc(self, doc=None):
395 doc = doc or self.doc
396 if doc:
397 if self.base and self.base.is_implicit():
398 self.base.connect_doc(doc)
399 for m in self.local_members:
400 doc.connect_member(m)
402 @property
403 def ifcond(self):
404 assert self._checked
405 if isinstance(self._ifcond, QAPISchemaType):
406 # Simple union wrapper type inherits from wrapped type;
407 # see _make_implicit_object_type()
408 return self._ifcond.ifcond
409 return self._ifcond
411 def is_implicit(self):
412 # See QAPISchema._make_implicit_object_type(), as well as
413 # _def_predefineds()
414 return self.name.startswith('q_')
416 def is_empty(self):
417 assert self.members is not None
418 return not self.members and not self.variants
420 def c_name(self):
421 assert self.name != 'q_empty'
422 return QAPISchemaType.c_name(self)
424 def c_type(self):
425 assert not self.is_implicit()
426 return c_name(self.name) + pointer_suffix
428 def c_unboxed_type(self):
429 return c_name(self.name)
431 def json_type(self):
432 return 'object'
434 def visit(self, visitor):
435 QAPISchemaType.visit(self, visitor)
436 visitor.visit_object_type(self.name, self.info, self.ifcond,
437 self.base, self.local_members, self.variants,
438 self.features)
439 visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
440 self.members, self.variants,
441 self.features)
444 class QAPISchemaMember(object):
445 """ Represents object members, enum members and features """
446 role = 'member'
448 def __init__(self, name, info, ifcond=None):
449 assert isinstance(name, str)
450 self.name = name
451 self.info = info
452 self.ifcond = ifcond or []
453 self.defined_in = None
455 def set_defined_in(self, name):
456 assert not self.defined_in
457 self.defined_in = name
459 def check_clash(self, info, seen):
460 cname = c_name(self.name)
461 if cname in seen:
462 raise QAPISemError(
463 info,
464 "%s collides with %s"
465 % (self.describe(info), seen[cname].describe(info)))
466 seen[cname] = self
468 def describe(self, info):
469 role = self.role
470 defined_in = self.defined_in
471 assert defined_in
473 if defined_in.startswith('q_obj_'):
474 # See QAPISchema._make_implicit_object_type() - reverse the
475 # mapping there to create a nice human-readable description
476 defined_in = defined_in[6:]
477 if defined_in.endswith('-arg'):
478 # Implicit type created for a command's dict 'data'
479 assert role == 'member'
480 role = 'parameter'
481 elif defined_in.endswith('-base'):
482 # Implicit type created for a flat union's dict 'base'
483 role = 'base ' + role
484 else:
485 # Implicit type created for a simple union's branch
486 assert defined_in.endswith('-wrapper')
487 # Unreachable and not implemented
488 assert False
489 elif defined_in.endswith('Kind'):
490 # See QAPISchema._make_implicit_enum_type()
491 # Implicit enum created for simple union's branches
492 assert role == 'value'
493 role = 'branch'
494 elif defined_in != info.defn_name:
495 return "%s '%s' of type '%s'" % (role, self.name, defined_in)
496 return "%s '%s'" % (role, self.name)
499 class QAPISchemaEnumMember(QAPISchemaMember):
500 role = 'value'
503 class QAPISchemaFeature(QAPISchemaMember):
504 role = 'feature'
507 class QAPISchemaObjectTypeMember(QAPISchemaMember):
508 def __init__(self, name, info, typ, optional, ifcond=None):
509 QAPISchemaMember.__init__(self, name, info, ifcond)
510 assert isinstance(typ, str)
511 assert isinstance(optional, bool)
512 self._type_name = typ
513 self.type = None
514 self.optional = optional
516 def check(self, schema):
517 assert self.defined_in
518 self.type = schema.resolve_type(self._type_name, self.info,
519 self.describe)
522 class QAPISchemaObjectTypeVariants(object):
523 def __init__(self, tag_name, info, tag_member, variants):
524 # Flat unions pass tag_name but not tag_member.
525 # Simple unions and alternates pass tag_member but not tag_name.
526 # After check(), tag_member is always set, and tag_name remains
527 # a reliable witness of being used by a flat union.
528 assert bool(tag_member) != bool(tag_name)
529 assert (isinstance(tag_name, str) or
530 isinstance(tag_member, QAPISchemaObjectTypeMember))
531 for v in variants:
532 assert isinstance(v, QAPISchemaObjectTypeVariant)
533 self._tag_name = tag_name
534 self.info = info
535 self.tag_member = tag_member
536 self.variants = variants
538 def set_defined_in(self, name):
539 for v in self.variants:
540 v.set_defined_in(name)
542 def check(self, schema, seen):
543 if not self.tag_member: # flat union
544 self.tag_member = seen.get(c_name(self._tag_name))
545 base = "'base'"
546 # Pointing to the base type when not implicit would be
547 # nice, but we don't know it here
548 if not self.tag_member or self._tag_name != self.tag_member.name:
549 raise QAPISemError(
550 self.info,
551 "discriminator '%s' is not a member of %s"
552 % (self._tag_name, base))
553 # Here we do:
554 base_type = schema.lookup_type(self.tag_member.defined_in)
555 assert base_type
556 if not base_type.is_implicit():
557 base = "base type '%s'" % self.tag_member.defined_in
558 if not isinstance(self.tag_member.type, QAPISchemaEnumType):
559 raise QAPISemError(
560 self.info,
561 "discriminator member '%s' of %s must be of enum type"
562 % (self._tag_name, base))
563 if self.tag_member.optional:
564 raise QAPISemError(
565 self.info,
566 "discriminator member '%s' of %s must not be optional"
567 % (self._tag_name, base))
568 if self.tag_member.ifcond:
569 raise QAPISemError(
570 self.info,
571 "discriminator member '%s' of %s must not be conditional"
572 % (self._tag_name, base))
573 else: # simple union
574 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
575 assert not self.tag_member.optional
576 assert self.tag_member.ifcond == []
577 if self._tag_name: # flat union
578 # branches that are not explicitly covered get an empty type
579 cases = set([v.name for v in self.variants])
580 for m in self.tag_member.type.members:
581 if m.name not in cases:
582 v = QAPISchemaObjectTypeVariant(m.name, self.info,
583 'q_empty', m.ifcond)
584 v.set_defined_in(self.tag_member.defined_in)
585 self.variants.append(v)
586 if not self.variants:
587 raise QAPISemError(self.info, "union has no branches")
588 for v in self.variants:
589 v.check(schema)
590 # Union names must match enum values; alternate names are
591 # checked separately. Use 'seen' to tell the two apart.
592 if seen:
593 if v.name not in self.tag_member.type.member_names():
594 raise QAPISemError(
595 self.info,
596 "branch '%s' is not a value of %s"
597 % (v.name, self.tag_member.type.describe()))
598 if (not isinstance(v.type, QAPISchemaObjectType)
599 or v.type.variants):
600 raise QAPISemError(
601 self.info,
602 "%s cannot use %s"
603 % (v.describe(self.info), v.type.describe()))
604 v.type.check(schema)
606 def check_clash(self, info, seen):
607 for v in self.variants:
608 # Reset seen map for each variant, since qapi names from one
609 # branch do not affect another branch
610 v.type.check_clash(info, dict(seen))
613 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
614 role = 'branch'
616 def __init__(self, name, info, typ, ifcond=None):
617 QAPISchemaObjectTypeMember.__init__(self, name, info, typ,
618 False, ifcond)
621 class QAPISchemaAlternateType(QAPISchemaType):
622 meta = 'alternate'
624 def __init__(self, name, info, doc, ifcond, variants):
625 QAPISchemaType.__init__(self, name, info, doc, ifcond)
626 assert isinstance(variants, QAPISchemaObjectTypeVariants)
627 assert variants.tag_member
628 variants.set_defined_in(name)
629 variants.tag_member.set_defined_in(self.name)
630 self.variants = variants
632 def check(self, schema):
633 QAPISchemaType.check(self, schema)
634 self.variants.tag_member.check(schema)
635 # Not calling self.variants.check_clash(), because there's nothing
636 # to clash with
637 self.variants.check(schema, {})
638 # Alternate branch names have no relation to the tag enum values;
639 # so we have to check for potential name collisions ourselves.
640 seen = {}
641 types_seen = {}
642 for v in self.variants.variants:
643 v.check_clash(self.info, seen)
644 qtype = v.type.alternate_qtype()
645 if not qtype:
646 raise QAPISemError(
647 self.info,
648 "%s cannot use %s"
649 % (v.describe(self.info), v.type.describe()))
650 conflicting = set([qtype])
651 if qtype == 'QTYPE_QSTRING':
652 if isinstance(v.type, QAPISchemaEnumType):
653 for m in v.type.members:
654 if m.name in ['on', 'off']:
655 conflicting.add('QTYPE_QBOOL')
656 if re.match(r'[-+0-9.]', m.name):
657 # lazy, could be tightened
658 conflicting.add('QTYPE_QNUM')
659 else:
660 conflicting.add('QTYPE_QNUM')
661 conflicting.add('QTYPE_QBOOL')
662 for qt in conflicting:
663 if qt in types_seen:
664 raise QAPISemError(
665 self.info,
666 "%s can't be distinguished from '%s'"
667 % (v.describe(self.info), types_seen[qt]))
668 types_seen[qt] = v.name
670 def connect_doc(self, doc=None):
671 doc = doc or self.doc
672 if doc:
673 for v in self.variants.variants:
674 doc.connect_member(v)
676 def c_type(self):
677 return c_name(self.name) + pointer_suffix
679 def json_type(self):
680 return 'value'
682 def visit(self, visitor):
683 QAPISchemaType.visit(self, visitor)
684 visitor.visit_alternate_type(self.name, self.info, self.ifcond,
685 self.variants)
688 class QAPISchemaCommand(QAPISchemaEntity):
689 meta = 'command'
691 def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
692 gen, success_response, boxed, allow_oob, allow_preconfig,
693 features):
694 QAPISchemaEntity.__init__(self, name, info, doc, ifcond, features)
695 assert not arg_type or isinstance(arg_type, str)
696 assert not ret_type or isinstance(ret_type, str)
697 self._arg_type_name = arg_type
698 self.arg_type = None
699 self._ret_type_name = ret_type
700 self.ret_type = None
701 self.gen = gen
702 self.success_response = success_response
703 self.boxed = boxed
704 self.allow_oob = allow_oob
705 self.allow_preconfig = allow_preconfig
707 def check(self, schema):
708 QAPISchemaEntity.check(self, schema)
709 if self._arg_type_name:
710 self.arg_type = schema.resolve_type(
711 self._arg_type_name, self.info, "command's 'data'")
712 if not isinstance(self.arg_type, QAPISchemaObjectType):
713 raise QAPISemError(
714 self.info,
715 "command's 'data' cannot take %s"
716 % self.arg_type.describe())
717 if self.arg_type.variants and not self.boxed:
718 raise QAPISemError(
719 self.info,
720 "command's 'data' can take %s only with 'boxed': true"
721 % self.arg_type.describe())
722 if self._ret_type_name:
723 self.ret_type = schema.resolve_type(
724 self._ret_type_name, self.info, "command's 'returns'")
725 if self.name not in self.info.pragma.returns_whitelist:
726 typ = self.ret_type
727 if isinstance(typ, QAPISchemaArrayType):
728 typ = self.ret_type.element_type
729 assert typ
730 if not isinstance(typ, QAPISchemaObjectType):
731 raise QAPISemError(
732 self.info,
733 "command's 'returns' cannot take %s"
734 % self.ret_type.describe())
736 def connect_doc(self, doc=None):
737 doc = doc or self.doc
738 if doc:
739 if self.arg_type and self.arg_type.is_implicit():
740 self.arg_type.connect_doc(doc)
742 def visit(self, visitor):
743 QAPISchemaEntity.visit(self, visitor)
744 visitor.visit_command(self.name, self.info, self.ifcond,
745 self.arg_type, self.ret_type,
746 self.gen, self.success_response,
747 self.boxed, self.allow_oob,
748 self.allow_preconfig,
749 self.features)
752 class QAPISchemaEvent(QAPISchemaEntity):
753 meta = 'event'
755 def __init__(self, name, info, doc, ifcond, arg_type, boxed):
756 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
757 assert not arg_type or isinstance(arg_type, str)
758 self._arg_type_name = arg_type
759 self.arg_type = None
760 self.boxed = boxed
762 def check(self, schema):
763 QAPISchemaEntity.check(self, schema)
764 if self._arg_type_name:
765 self.arg_type = schema.resolve_type(
766 self._arg_type_name, self.info, "event's 'data'")
767 if not isinstance(self.arg_type, QAPISchemaObjectType):
768 raise QAPISemError(
769 self.info,
770 "event's 'data' cannot take %s"
771 % self.arg_type.describe())
772 if self.arg_type.variants and not self.boxed:
773 raise QAPISemError(
774 self.info,
775 "event's 'data' can take %s only with 'boxed': true"
776 % self.arg_type.describe())
778 def connect_doc(self, doc=None):
779 doc = doc or self.doc
780 if doc:
781 if self.arg_type and self.arg_type.is_implicit():
782 self.arg_type.connect_doc(doc)
784 def visit(self, visitor):
785 QAPISchemaEntity.visit(self, visitor)
786 visitor.visit_event(self.name, self.info, self.ifcond,
787 self.arg_type, self.boxed)
790 class QAPISchema(object):
791 def __init__(self, fname):
792 self.fname = fname
793 parser = QAPISchemaParser(fname)
794 exprs = check_exprs(parser.exprs)
795 self.docs = parser.docs
796 self._entity_list = []
797 self._entity_dict = {}
798 self._module_dict = OrderedDict()
799 self._schema_dir = os.path.dirname(fname)
800 self._make_module(None) # built-ins
801 self._make_module(fname)
802 self._predefining = True
803 self._def_predefineds()
804 self._predefining = False
805 self._def_exprs(exprs)
806 self.check()
808 def _def_entity(self, ent):
809 # Only the predefined types are allowed to not have info
810 assert ent.info or self._predefining
811 self._entity_list.append(ent)
812 if ent.name is None:
813 return
814 # TODO reject names that differ only in '_' vs. '.' vs. '-',
815 # because they're liable to clash in generated C.
816 other_ent = self._entity_dict.get(ent.name)
817 if other_ent:
818 if other_ent.info:
819 where = QAPIError(other_ent.info, None, "previous definition")
820 raise QAPISemError(
821 ent.info,
822 "'%s' is already defined\n%s" % (ent.name, where))
823 raise QAPISemError(
824 ent.info, "%s is already defined" % other_ent.describe())
825 self._entity_dict[ent.name] = ent
827 def lookup_entity(self, name, typ=None):
828 ent = self._entity_dict.get(name)
829 if typ and not isinstance(ent, typ):
830 return None
831 return ent
833 def lookup_type(self, name):
834 return self.lookup_entity(name, QAPISchemaType)
836 def resolve_type(self, name, info, what):
837 typ = self.lookup_type(name)
838 if not typ:
839 if callable(what):
840 what = what(info)
841 raise QAPISemError(
842 info, "%s uses unknown type '%s'" % (what, name))
843 return typ
845 def _module_name(self, fname):
846 if fname is None:
847 return None
848 return os.path.relpath(fname, self._schema_dir)
850 def _make_module(self, fname):
851 name = self._module_name(fname)
852 if not name in self._module_dict:
853 self._module_dict[name] = QAPISchemaModule(name)
854 return self._module_dict[name]
856 def module_by_fname(self, fname):
857 name = self._module_name(fname)
858 assert name in self._module_dict
859 return self._module_dict[name]
861 def _def_include(self, expr, info, doc):
862 include = expr['include']
863 assert doc is None
864 self._def_entity(QAPISchemaInclude(self._make_module(include), info))
866 def _def_builtin_type(self, name, json_type, c_type):
867 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
868 # Instantiating only the arrays that are actually used would
869 # be nice, but we can't as long as their generated code
870 # (qapi-builtin-types.[ch]) may be shared by some other
871 # schema.
872 self._make_array_type(name, None)
874 def _def_predefineds(self):
875 for t in [('str', 'string', 'char' + pointer_suffix),
876 ('number', 'number', 'double'),
877 ('int', 'int', 'int64_t'),
878 ('int8', 'int', 'int8_t'),
879 ('int16', 'int', 'int16_t'),
880 ('int32', 'int', 'int32_t'),
881 ('int64', 'int', 'int64_t'),
882 ('uint8', 'int', 'uint8_t'),
883 ('uint16', 'int', 'uint16_t'),
884 ('uint32', 'int', 'uint32_t'),
885 ('uint64', 'int', 'uint64_t'),
886 ('size', 'int', 'uint64_t'),
887 ('bool', 'boolean', 'bool'),
888 ('any', 'value', 'QObject' + pointer_suffix),
889 ('null', 'null', 'QNull' + pointer_suffix)]:
890 self._def_builtin_type(*t)
891 self.the_empty_object_type = QAPISchemaObjectType(
892 'q_empty', None, None, None, None, [], None, [])
893 self._def_entity(self.the_empty_object_type)
895 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
896 'qbool']
897 qtype_values = self._make_enum_members(
898 [{'name': n} for n in qtypes], None)
900 self._def_entity(QAPISchemaEnumType('QType', None, None, None,
901 qtype_values, 'QTYPE'))
903 def _make_features(self, features, info):
904 return [QAPISchemaFeature(f['name'], info, f.get('if'))
905 for f in features]
907 def _make_enum_members(self, values, info):
908 return [QAPISchemaEnumMember(v['name'], info, v.get('if'))
909 for v in values]
911 def _make_implicit_enum_type(self, name, info, ifcond, values):
912 # See also QAPISchemaObjectTypeMember.describe()
913 name = name + 'Kind' # reserved by check_defn_name_str()
914 self._def_entity(QAPISchemaEnumType(
915 name, info, None, ifcond, self._make_enum_members(values, info),
916 None))
917 return name
919 def _make_array_type(self, element_type, info):
920 name = element_type + 'List' # reserved by check_defn_name_str()
921 if not self.lookup_type(name):
922 self._def_entity(QAPISchemaArrayType(name, info, element_type))
923 return name
925 def _make_implicit_object_type(self, name, info, ifcond, role, members):
926 if not members:
927 return None
928 # See also QAPISchemaObjectTypeMember.describe()
929 name = 'q_obj_%s-%s' % (name, role)
930 typ = self.lookup_entity(name, QAPISchemaObjectType)
931 if typ:
932 # The implicit object type has multiple users. This can
933 # happen only for simple unions' implicit wrapper types.
934 # Its ifcond should be the disjunction of its user's
935 # ifconds. Not implemented. Instead, we always pass the
936 # wrapped type's ifcond, which is trivially the same for all
937 # users. It's also necessary for the wrapper to compile.
938 # But it's not tight: the disjunction need not imply it. We
939 # may end up compiling useless wrapper types.
940 # TODO kill simple unions or implement the disjunction
941 assert (ifcond or []) == typ._ifcond # pylint: disable=protected-access
942 else:
943 self._def_entity(QAPISchemaObjectType(name, info, None, ifcond,
944 None, members, None, []))
945 return name
947 def _def_enum_type(self, expr, info, doc):
948 name = expr['enum']
949 data = expr['data']
950 prefix = expr.get('prefix')
951 ifcond = expr.get('if')
952 self._def_entity(QAPISchemaEnumType(
953 name, info, doc, ifcond,
954 self._make_enum_members(data, info), prefix))
956 def _make_member(self, name, typ, ifcond, info):
957 optional = False
958 if name.startswith('*'):
959 name = name[1:]
960 optional = True
961 if isinstance(typ, list):
962 assert len(typ) == 1
963 typ = self._make_array_type(typ[0], info)
964 return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond)
966 def _make_members(self, data, info):
967 return [self._make_member(key, value['type'], value.get('if'), info)
968 for (key, value) in data.items()]
970 def _def_struct_type(self, expr, info, doc):
971 name = expr['struct']
972 base = expr.get('base')
973 data = expr['data']
974 ifcond = expr.get('if')
975 features = expr.get('features', [])
976 self._def_entity(QAPISchemaObjectType(
977 name, info, doc, ifcond, base,
978 self._make_members(data, info),
979 None,
980 self._make_features(features, info)))
982 def _make_variant(self, case, typ, ifcond, info):
983 return QAPISchemaObjectTypeVariant(case, info, typ, ifcond)
985 def _make_simple_variant(self, case, typ, ifcond, info):
986 if isinstance(typ, list):
987 assert len(typ) == 1
988 typ = self._make_array_type(typ[0], info)
989 typ = self._make_implicit_object_type(
990 typ, info, self.lookup_type(typ),
991 'wrapper', [self._make_member('data', typ, None, info)])
992 return QAPISchemaObjectTypeVariant(case, info, typ, ifcond)
994 def _def_union_type(self, expr, info, doc):
995 name = expr['union']
996 data = expr['data']
997 base = expr.get('base')
998 ifcond = expr.get('if')
999 tag_name = expr.get('discriminator')
1000 tag_member = None
1001 if isinstance(base, dict):
1002 base = self._make_implicit_object_type(
1003 name, info, ifcond,
1004 'base', self._make_members(base, info))
1005 if tag_name:
1006 variants = [self._make_variant(key, value['type'],
1007 value.get('if'), info)
1008 for (key, value) in data.items()]
1009 members = []
1010 else:
1011 variants = [self._make_simple_variant(key, value['type'],
1012 value.get('if'), info)
1013 for (key, value) in data.items()]
1014 enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
1015 typ = self._make_implicit_enum_type(name, info, ifcond, enum)
1016 tag_member = QAPISchemaObjectTypeMember('type', info, typ, False)
1017 members = [tag_member]
1018 self._def_entity(
1019 QAPISchemaObjectType(name, info, doc, ifcond, base, members,
1020 QAPISchemaObjectTypeVariants(
1021 tag_name, info, tag_member, variants),
1022 []))
1024 def _def_alternate_type(self, expr, info, doc):
1025 name = expr['alternate']
1026 data = expr['data']
1027 ifcond = expr.get('if')
1028 variants = [self._make_variant(key, value['type'], value.get('if'),
1029 info)
1030 for (key, value) in data.items()]
1031 tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
1032 self._def_entity(
1033 QAPISchemaAlternateType(name, info, doc, ifcond,
1034 QAPISchemaObjectTypeVariants(
1035 None, info, tag_member, variants)))
1037 def _def_command(self, expr, info, doc):
1038 name = expr['command']
1039 data = expr.get('data')
1040 rets = expr.get('returns')
1041 gen = expr.get('gen', True)
1042 success_response = expr.get('success-response', True)
1043 boxed = expr.get('boxed', False)
1044 allow_oob = expr.get('allow-oob', False)
1045 allow_preconfig = expr.get('allow-preconfig', False)
1046 ifcond = expr.get('if')
1047 features = expr.get('features', [])
1048 if isinstance(data, OrderedDict):
1049 data = self._make_implicit_object_type(
1050 name, info, ifcond, 'arg', self._make_members(data, info))
1051 if isinstance(rets, list):
1052 assert len(rets) == 1
1053 rets = self._make_array_type(rets[0], info)
1054 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
1055 gen, success_response,
1056 boxed, allow_oob, allow_preconfig,
1057 self._make_features(features, info)))
1059 def _def_event(self, expr, info, doc):
1060 name = expr['event']
1061 data = expr.get('data')
1062 boxed = expr.get('boxed', False)
1063 ifcond = expr.get('if')
1064 if isinstance(data, OrderedDict):
1065 data = self._make_implicit_object_type(
1066 name, info, ifcond, 'arg', self._make_members(data, info))
1067 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
1069 def _def_exprs(self, exprs):
1070 for expr_elem in exprs:
1071 expr = expr_elem['expr']
1072 info = expr_elem['info']
1073 doc = expr_elem.get('doc')
1074 if 'enum' in expr:
1075 self._def_enum_type(expr, info, doc)
1076 elif 'struct' in expr:
1077 self._def_struct_type(expr, info, doc)
1078 elif 'union' in expr:
1079 self._def_union_type(expr, info, doc)
1080 elif 'alternate' in expr:
1081 self._def_alternate_type(expr, info, doc)
1082 elif 'command' in expr:
1083 self._def_command(expr, info, doc)
1084 elif 'event' in expr:
1085 self._def_event(expr, info, doc)
1086 elif 'include' in expr:
1087 self._def_include(expr, info, doc)
1088 else:
1089 assert False
1091 def check(self):
1092 for ent in self._entity_list:
1093 ent.check(self)
1094 ent.connect_doc()
1095 ent.check_doc()
1096 for ent in self._entity_list:
1097 ent.set_module(self)
1099 def visit(self, visitor):
1100 visitor.visit_begin(self)
1101 module = None
1102 for mod in self._module_dict.values():
1103 mod.visit(visitor)
1104 visitor.visit_end()