]> git.proxmox.com Git - mirror_qemu.git/blob - scripts/qapi/schema.py
igb: Add Function Level Reset to PF and VF
[mirror_qemu.git] / scripts / qapi / schema.py
1 # -*- coding: utf-8 -*-
2 #
3 # QAPI schema internal representation
4 #
5 # Copyright (c) 2015-2019 Red Hat Inc.
6 #
7 # Authors:
8 # Markus Armbruster <armbru@redhat.com>
9 # Eric Blake <eblake@redhat.com>
10 # Marc-André Lureau <marcandre.lureau@redhat.com>
11 #
12 # This work is licensed under the terms of the GNU GPL, version 2.
13 # See the COPYING file in the top-level directory.
14
15 # TODO catching name collisions in generated code would be nice
16
17 from collections import OrderedDict
18 import os
19 import re
20 from typing import List, Optional
21
22 from .common import (
23 POINTER_SUFFIX,
24 c_name,
25 cgen_ifcond,
26 docgen_ifcond,
27 gen_endif,
28 gen_if,
29 )
30 from .error import QAPIError, QAPISemError, QAPISourceError
31 from .expr import check_exprs
32 from .parser import QAPIExpression, QAPISchemaParser
33
34
35 class QAPISchemaIfCond:
36 def __init__(self, ifcond=None):
37 self.ifcond = ifcond
38
39 def _cgen(self):
40 return cgen_ifcond(self.ifcond)
41
42 def gen_if(self):
43 return gen_if(self._cgen())
44
45 def gen_endif(self):
46 return gen_endif(self._cgen())
47
48 def docgen(self):
49 return docgen_ifcond(self.ifcond)
50
51 def is_present(self):
52 return bool(self.ifcond)
53
54
55 class QAPISchemaEntity:
56 meta: Optional[str] = None
57
58 def __init__(self, name: str, info, doc, ifcond=None, features=None):
59 assert name is None or isinstance(name, str)
60 for f in features or []:
61 assert isinstance(f, QAPISchemaFeature)
62 f.set_defined_in(name)
63 self.name = name
64 self._module = None
65 # For explicitly defined entities, info points to the (explicit)
66 # definition. For builtins (and their arrays), info is None.
67 # For implicitly defined entities, info points to a place that
68 # triggered the implicit definition (there may be more than one
69 # such place).
70 self.info = info
71 self.doc = doc
72 self._ifcond = ifcond or QAPISchemaIfCond()
73 self.features = features or []
74 self._checked = False
75
76 def __repr__(self):
77 if self.name is None:
78 return "<%s at 0x%x>" % (type(self).__name__, id(self))
79 return "<%s:%s at 0x%x>" % type(self).__name__, self.name, id(self)
80
81 def c_name(self):
82 return c_name(self.name)
83
84 def check(self, schema):
85 assert not self._checked
86 seen = {}
87 for f in self.features:
88 f.check_clash(self.info, seen)
89 self._checked = True
90
91 def connect_doc(self, doc=None):
92 doc = doc or self.doc
93 if doc:
94 for f in self.features:
95 doc.connect_feature(f)
96
97 def check_doc(self):
98 if self.doc:
99 self.doc.check()
100
101 def _set_module(self, schema, info):
102 assert self._checked
103 fname = info.fname if info else QAPISchemaModule.BUILTIN_MODULE_NAME
104 self._module = schema.module_by_fname(fname)
105 self._module.add_entity(self)
106
107 def set_module(self, schema):
108 self._set_module(schema, self.info)
109
110 @property
111 def ifcond(self):
112 assert self._checked
113 return self._ifcond
114
115 def is_implicit(self):
116 return not self.info
117
118 def visit(self, visitor):
119 assert self._checked
120
121 def describe(self):
122 assert self.meta
123 return "%s '%s'" % (self.meta, self.name)
124
125
126 class QAPISchemaVisitor:
127 def visit_begin(self, schema):
128 pass
129
130 def visit_end(self):
131 pass
132
133 def visit_module(self, name):
134 pass
135
136 def visit_needed(self, entity):
137 # Default to visiting everything
138 return True
139
140 def visit_include(self, name, info):
141 pass
142
143 def visit_builtin_type(self, name, info, json_type):
144 pass
145
146 def visit_enum_type(self, name, info, ifcond, features, members, prefix):
147 pass
148
149 def visit_array_type(self, name, info, ifcond, element_type):
150 pass
151
152 def visit_object_type(self, name, info, ifcond, features,
153 base, members, variants):
154 pass
155
156 def visit_object_type_flat(self, name, info, ifcond, features,
157 members, variants):
158 pass
159
160 def visit_alternate_type(self, name, info, ifcond, features, variants):
161 pass
162
163 def visit_command(self, name, info, ifcond, features,
164 arg_type, ret_type, gen, success_response, boxed,
165 allow_oob, allow_preconfig, coroutine):
166 pass
167
168 def visit_event(self, name, info, ifcond, features, arg_type, boxed):
169 pass
170
171
172 class QAPISchemaModule:
173
174 BUILTIN_MODULE_NAME = './builtin'
175
176 def __init__(self, name):
177 self.name = name
178 self._entity_list = []
179
180 @staticmethod
181 def is_system_module(name: str) -> bool:
182 """
183 System modules are internally defined modules.
184
185 Their names start with the "./" prefix.
186 """
187 return name.startswith('./')
188
189 @classmethod
190 def is_user_module(cls, name: str) -> bool:
191 """
192 User modules are those defined by the user in qapi JSON files.
193
194 They do not start with the "./" prefix.
195 """
196 return not cls.is_system_module(name)
197
198 @classmethod
199 def is_builtin_module(cls, name: str) -> bool:
200 """
201 The built-in module is a single System module for the built-in types.
202
203 It is always "./builtin".
204 """
205 return name == cls.BUILTIN_MODULE_NAME
206
207 def add_entity(self, ent):
208 self._entity_list.append(ent)
209
210 def visit(self, visitor):
211 visitor.visit_module(self.name)
212 for entity in self._entity_list:
213 if visitor.visit_needed(entity):
214 entity.visit(visitor)
215
216
217 class QAPISchemaInclude(QAPISchemaEntity):
218 def __init__(self, sub_module, info):
219 super().__init__(None, info, None)
220 self._sub_module = sub_module
221
222 def visit(self, visitor):
223 super().visit(visitor)
224 visitor.visit_include(self._sub_module.name, self.info)
225
226
227 class QAPISchemaType(QAPISchemaEntity):
228 # Return the C type for common use.
229 # For the types we commonly box, this is a pointer type.
230 def c_type(self):
231 pass
232
233 # Return the C type to be used in a parameter list.
234 def c_param_type(self):
235 return self.c_type()
236
237 # Return the C type to be used where we suppress boxing.
238 def c_unboxed_type(self):
239 return self.c_type()
240
241 def json_type(self):
242 pass
243
244 def alternate_qtype(self):
245 json2qtype = {
246 'null': 'QTYPE_QNULL',
247 'string': 'QTYPE_QSTRING',
248 'number': 'QTYPE_QNUM',
249 'int': 'QTYPE_QNUM',
250 'boolean': 'QTYPE_QBOOL',
251 'array': 'QTYPE_QLIST',
252 'object': 'QTYPE_QDICT'
253 }
254 return json2qtype.get(self.json_type())
255
256 def doc_type(self):
257 if self.is_implicit():
258 return None
259 return self.name
260
261 def need_has_if_optional(self):
262 # When FOO is a pointer, has_FOO == !!FOO, i.e. has_FOO is redundant.
263 # Except for arrays; see QAPISchemaArrayType.need_has_if_optional().
264 return not self.c_type().endswith(POINTER_SUFFIX)
265
266 def check(self, schema):
267 super().check(schema)
268 for feat in self.features:
269 if feat.is_special():
270 raise QAPISemError(
271 self.info,
272 f"feature '{feat.name}' is not supported for types")
273
274 def describe(self):
275 assert self.meta
276 return "%s type '%s'" % (self.meta, self.name)
277
278
279 class QAPISchemaBuiltinType(QAPISchemaType):
280 meta = 'built-in'
281
282 def __init__(self, name, json_type, c_type):
283 super().__init__(name, None, None)
284 assert not c_type or isinstance(c_type, str)
285 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
286 'value')
287 self._json_type_name = json_type
288 self._c_type_name = c_type
289
290 def c_name(self):
291 return self.name
292
293 def c_type(self):
294 return self._c_type_name
295
296 def c_param_type(self):
297 if self.name == 'str':
298 return 'const ' + self._c_type_name
299 return self._c_type_name
300
301 def json_type(self):
302 return self._json_type_name
303
304 def doc_type(self):
305 return self.json_type()
306
307 def visit(self, visitor):
308 super().visit(visitor)
309 visitor.visit_builtin_type(self.name, self.info, self.json_type())
310
311
312 class QAPISchemaEnumType(QAPISchemaType):
313 meta = 'enum'
314
315 def __init__(self, name, info, doc, ifcond, features, members, prefix):
316 super().__init__(name, info, doc, ifcond, features)
317 for m in members:
318 assert isinstance(m, QAPISchemaEnumMember)
319 m.set_defined_in(name)
320 assert prefix is None or isinstance(prefix, str)
321 self.members = members
322 self.prefix = prefix
323
324 def check(self, schema):
325 super().check(schema)
326 seen = {}
327 for m in self.members:
328 m.check_clash(self.info, seen)
329
330 def connect_doc(self, doc=None):
331 super().connect_doc(doc)
332 doc = doc or self.doc
333 for m in self.members:
334 m.connect_doc(doc)
335
336 def is_implicit(self):
337 # See QAPISchema._def_predefineds()
338 return self.name == 'QType'
339
340 def c_type(self):
341 return c_name(self.name)
342
343 def member_names(self):
344 return [m.name for m in self.members]
345
346 def json_type(self):
347 return 'string'
348
349 def visit(self, visitor):
350 super().visit(visitor)
351 visitor.visit_enum_type(
352 self.name, self.info, self.ifcond, self.features,
353 self.members, self.prefix)
354
355
356 class QAPISchemaArrayType(QAPISchemaType):
357 meta = 'array'
358
359 def __init__(self, name, info, element_type):
360 super().__init__(name, info, None)
361 assert isinstance(element_type, str)
362 self._element_type_name = element_type
363 self.element_type = None
364
365 def need_has_if_optional(self):
366 # When FOO is an array, we still need has_FOO to distinguish
367 # absent (!has_FOO) from present and empty (has_FOO && !FOO).
368 return True
369
370 def check(self, schema):
371 super().check(schema)
372 self.element_type = schema.resolve_type(
373 self._element_type_name, self.info,
374 self.info and self.info.defn_meta)
375 assert not isinstance(self.element_type, QAPISchemaArrayType)
376
377 def set_module(self, schema):
378 self._set_module(schema, self.element_type.info)
379
380 @property
381 def ifcond(self):
382 assert self._checked
383 return self.element_type.ifcond
384
385 def is_implicit(self):
386 return True
387
388 def c_type(self):
389 return c_name(self.name) + POINTER_SUFFIX
390
391 def json_type(self):
392 return 'array'
393
394 def doc_type(self):
395 elt_doc_type = self.element_type.doc_type()
396 if not elt_doc_type:
397 return None
398 return 'array of ' + elt_doc_type
399
400 def visit(self, visitor):
401 super().visit(visitor)
402 visitor.visit_array_type(self.name, self.info, self.ifcond,
403 self.element_type)
404
405 def describe(self):
406 assert self.meta
407 return "%s type ['%s']" % (self.meta, self._element_type_name)
408
409
410 class QAPISchemaObjectType(QAPISchemaType):
411 def __init__(self, name, info, doc, ifcond, features,
412 base, local_members, variants):
413 # struct has local_members, optional base, and no variants
414 # union has base, variants, and no local_members
415 super().__init__(name, info, doc, ifcond, features)
416 self.meta = 'union' if variants else 'struct'
417 assert base is None or isinstance(base, str)
418 for m in local_members:
419 assert isinstance(m, QAPISchemaObjectTypeMember)
420 m.set_defined_in(name)
421 if variants is not None:
422 assert isinstance(variants, QAPISchemaVariants)
423 variants.set_defined_in(name)
424 self._base_name = base
425 self.base = None
426 self.local_members = local_members
427 self.variants = variants
428 self.members = None
429
430 def check(self, schema):
431 # This calls another type T's .check() exactly when the C
432 # struct emitted by gen_object() contains that T's C struct
433 # (pointers don't count).
434 if self.members is not None:
435 # A previous .check() completed: nothing to do
436 return
437 if self._checked:
438 # Recursed: C struct contains itself
439 raise QAPISemError(self.info,
440 "object %s contains itself" % self.name)
441
442 super().check(schema)
443 assert self._checked and self.members is None
444
445 seen = OrderedDict()
446 if self._base_name:
447 self.base = schema.resolve_type(self._base_name, self.info,
448 "'base'")
449 if (not isinstance(self.base, QAPISchemaObjectType)
450 or self.base.variants):
451 raise QAPISemError(
452 self.info,
453 "'base' requires a struct type, %s isn't"
454 % self.base.describe())
455 self.base.check(schema)
456 self.base.check_clash(self.info, seen)
457 for m in self.local_members:
458 m.check(schema)
459 m.check_clash(self.info, seen)
460 members = seen.values()
461
462 if self.variants:
463 self.variants.check(schema, seen)
464 self.variants.check_clash(self.info, seen)
465
466 self.members = members # mark completed
467
468 # Check that the members of this type do not cause duplicate JSON members,
469 # and update seen to track the members seen so far. Report any errors
470 # on behalf of info, which is not necessarily self.info
471 def check_clash(self, info, seen):
472 assert self._checked
473 for m in self.members:
474 m.check_clash(info, seen)
475 if self.variants:
476 self.variants.check_clash(info, seen)
477
478 def connect_doc(self, doc=None):
479 super().connect_doc(doc)
480 doc = doc or self.doc
481 if self.base and self.base.is_implicit():
482 self.base.connect_doc(doc)
483 for m in self.local_members:
484 m.connect_doc(doc)
485
486 def is_implicit(self):
487 # See QAPISchema._make_implicit_object_type(), as well as
488 # _def_predefineds()
489 return self.name.startswith('q_')
490
491 def is_empty(self):
492 assert self.members is not None
493 return not self.members and not self.variants
494
495 def has_conditional_members(self):
496 assert self.members is not None
497 return any(m.ifcond.is_present() for m in self.members)
498
499 def c_name(self):
500 assert self.name != 'q_empty'
501 return super().c_name()
502
503 def c_type(self):
504 assert not self.is_implicit()
505 return c_name(self.name) + POINTER_SUFFIX
506
507 def c_unboxed_type(self):
508 return c_name(self.name)
509
510 def json_type(self):
511 return 'object'
512
513 def visit(self, visitor):
514 super().visit(visitor)
515 visitor.visit_object_type(
516 self.name, self.info, self.ifcond, self.features,
517 self.base, self.local_members, self.variants)
518 visitor.visit_object_type_flat(
519 self.name, self.info, self.ifcond, self.features,
520 self.members, self.variants)
521
522
523 class QAPISchemaAlternateType(QAPISchemaType):
524 meta = 'alternate'
525
526 def __init__(self, name, info, doc, ifcond, features, variants):
527 super().__init__(name, info, doc, ifcond, features)
528 assert isinstance(variants, QAPISchemaVariants)
529 assert variants.tag_member
530 variants.set_defined_in(name)
531 variants.tag_member.set_defined_in(self.name)
532 self.variants = variants
533
534 def check(self, schema):
535 super().check(schema)
536 self.variants.tag_member.check(schema)
537 # Not calling self.variants.check_clash(), because there's nothing
538 # to clash with
539 self.variants.check(schema, {})
540 # Alternate branch names have no relation to the tag enum values;
541 # so we have to check for potential name collisions ourselves.
542 seen = {}
543 types_seen = {}
544 for v in self.variants.variants:
545 v.check_clash(self.info, seen)
546 qtype = v.type.alternate_qtype()
547 if not qtype:
548 raise QAPISemError(
549 self.info,
550 "%s cannot use %s"
551 % (v.describe(self.info), v.type.describe()))
552 conflicting = set([qtype])
553 if qtype == 'QTYPE_QSTRING':
554 if isinstance(v.type, QAPISchemaEnumType):
555 for m in v.type.members:
556 if m.name in ['on', 'off']:
557 conflicting.add('QTYPE_QBOOL')
558 if re.match(r'[-+0-9.]', m.name):
559 # lazy, could be tightened
560 conflicting.add('QTYPE_QNUM')
561 else:
562 conflicting.add('QTYPE_QNUM')
563 conflicting.add('QTYPE_QBOOL')
564 for qt in conflicting:
565 if qt in types_seen:
566 raise QAPISemError(
567 self.info,
568 "%s can't be distinguished from '%s'"
569 % (v.describe(self.info), types_seen[qt]))
570 types_seen[qt] = v.name
571
572 def connect_doc(self, doc=None):
573 super().connect_doc(doc)
574 doc = doc or self.doc
575 for v in self.variants.variants:
576 v.connect_doc(doc)
577
578 def c_type(self):
579 return c_name(self.name) + POINTER_SUFFIX
580
581 def json_type(self):
582 return 'value'
583
584 def visit(self, visitor):
585 super().visit(visitor)
586 visitor.visit_alternate_type(
587 self.name, self.info, self.ifcond, self.features, self.variants)
588
589
590 class QAPISchemaVariants:
591 def __init__(self, tag_name, info, tag_member, variants):
592 # Unions pass tag_name but not tag_member.
593 # Alternates pass tag_member but not tag_name.
594 # After check(), tag_member is always set.
595 assert bool(tag_member) != bool(tag_name)
596 assert (isinstance(tag_name, str) or
597 isinstance(tag_member, QAPISchemaObjectTypeMember))
598 for v in variants:
599 assert isinstance(v, QAPISchemaVariant)
600 self._tag_name = tag_name
601 self.info = info
602 self.tag_member = tag_member
603 self.variants = variants
604
605 def set_defined_in(self, name):
606 for v in self.variants:
607 v.set_defined_in(name)
608
609 def check(self, schema, seen):
610 if self._tag_name: # union
611 self.tag_member = seen.get(c_name(self._tag_name))
612 base = "'base'"
613 # Pointing to the base type when not implicit would be
614 # nice, but we don't know it here
615 if not self.tag_member or self._tag_name != self.tag_member.name:
616 raise QAPISemError(
617 self.info,
618 "discriminator '%s' is not a member of %s"
619 % (self._tag_name, base))
620 # Here we do:
621 base_type = schema.lookup_type(self.tag_member.defined_in)
622 assert base_type
623 if not base_type.is_implicit():
624 base = "base type '%s'" % self.tag_member.defined_in
625 if not isinstance(self.tag_member.type, QAPISchemaEnumType):
626 raise QAPISemError(
627 self.info,
628 "discriminator member '%s' of %s must be of enum type"
629 % (self._tag_name, base))
630 if self.tag_member.optional:
631 raise QAPISemError(
632 self.info,
633 "discriminator member '%s' of %s must not be optional"
634 % (self._tag_name, base))
635 if self.tag_member.ifcond.is_present():
636 raise QAPISemError(
637 self.info,
638 "discriminator member '%s' of %s must not be conditional"
639 % (self._tag_name, base))
640 else: # alternate
641 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
642 assert not self.tag_member.optional
643 assert not self.tag_member.ifcond.is_present()
644 if self._tag_name: # union
645 # branches that are not explicitly covered get an empty type
646 cases = {v.name for v in self.variants}
647 for m in self.tag_member.type.members:
648 if m.name not in cases:
649 v = QAPISchemaVariant(m.name, self.info,
650 'q_empty', m.ifcond)
651 v.set_defined_in(self.tag_member.defined_in)
652 self.variants.append(v)
653 if not self.variants:
654 raise QAPISemError(self.info, "union has no branches")
655 for v in self.variants:
656 v.check(schema)
657 # Union names must match enum values; alternate names are
658 # checked separately. Use 'seen' to tell the two apart.
659 if seen:
660 if v.name not in self.tag_member.type.member_names():
661 raise QAPISemError(
662 self.info,
663 "branch '%s' is not a value of %s"
664 % (v.name, self.tag_member.type.describe()))
665 if not isinstance(v.type, QAPISchemaObjectType):
666 raise QAPISemError(
667 self.info,
668 "%s cannot use %s"
669 % (v.describe(self.info), v.type.describe()))
670 v.type.check(schema)
671
672 def check_clash(self, info, seen):
673 for v in self.variants:
674 # Reset seen map for each variant, since qapi names from one
675 # branch do not affect another branch
676 v.type.check_clash(info, dict(seen))
677
678
679 class QAPISchemaMember:
680 """ Represents object members, enum members and features """
681 role = 'member'
682
683 def __init__(self, name, info, ifcond=None):
684 assert isinstance(name, str)
685 self.name = name
686 self.info = info
687 self.ifcond = ifcond or QAPISchemaIfCond()
688 self.defined_in = None
689
690 def set_defined_in(self, name):
691 assert not self.defined_in
692 self.defined_in = name
693
694 def check_clash(self, info, seen):
695 cname = c_name(self.name)
696 if cname in seen:
697 raise QAPISemError(
698 info,
699 "%s collides with %s"
700 % (self.describe(info), seen[cname].describe(info)))
701 seen[cname] = self
702
703 def connect_doc(self, doc):
704 if doc:
705 doc.connect_member(self)
706
707 def describe(self, info):
708 role = self.role
709 meta = 'type'
710 defined_in = self.defined_in
711 assert defined_in
712
713 if defined_in.startswith('q_obj_'):
714 # See QAPISchema._make_implicit_object_type() - reverse the
715 # mapping there to create a nice human-readable description
716 defined_in = defined_in[6:]
717 if defined_in.endswith('-arg'):
718 # Implicit type created for a command's dict 'data'
719 assert role == 'member'
720 role = 'parameter'
721 meta = 'command'
722 defined_in = defined_in[:-4]
723 elif defined_in.endswith('-base'):
724 # Implicit type created for a union's dict 'base'
725 role = 'base ' + role
726 defined_in = defined_in[:-5]
727 else:
728 assert False
729
730 if defined_in != info.defn_name:
731 return "%s '%s' of %s '%s'" % (role, self.name, meta, defined_in)
732 return "%s '%s'" % (role, self.name)
733
734
735 class QAPISchemaEnumMember(QAPISchemaMember):
736 role = 'value'
737
738 def __init__(self, name, info, ifcond=None, features=None):
739 super().__init__(name, info, ifcond)
740 for f in features or []:
741 assert isinstance(f, QAPISchemaFeature)
742 f.set_defined_in(name)
743 self.features = features or []
744
745 def connect_doc(self, doc):
746 super().connect_doc(doc)
747 if doc:
748 for f in self.features:
749 doc.connect_feature(f)
750
751
752 class QAPISchemaFeature(QAPISchemaMember):
753 role = 'feature'
754
755 def is_special(self):
756 return self.name in ('deprecated', 'unstable')
757
758
759 class QAPISchemaObjectTypeMember(QAPISchemaMember):
760 def __init__(self, name, info, typ, optional, ifcond=None, features=None):
761 super().__init__(name, info, ifcond)
762 assert isinstance(typ, str)
763 assert isinstance(optional, bool)
764 for f in features or []:
765 assert isinstance(f, QAPISchemaFeature)
766 f.set_defined_in(name)
767 self._type_name = typ
768 self.type = None
769 self.optional = optional
770 self.features = features or []
771
772 def need_has(self):
773 assert self.type
774 return self.optional and self.type.need_has_if_optional()
775
776 def check(self, schema):
777 assert self.defined_in
778 self.type = schema.resolve_type(self._type_name, self.info,
779 self.describe)
780 seen = {}
781 for f in self.features:
782 f.check_clash(self.info, seen)
783
784 def connect_doc(self, doc):
785 super().connect_doc(doc)
786 if doc:
787 for f in self.features:
788 doc.connect_feature(f)
789
790
791 class QAPISchemaVariant(QAPISchemaObjectTypeMember):
792 role = 'branch'
793
794 def __init__(self, name, info, typ, ifcond=None):
795 super().__init__(name, info, typ, False, ifcond)
796
797
798 class QAPISchemaCommand(QAPISchemaEntity):
799 meta = 'command'
800
801 def __init__(self, name, info, doc, ifcond, features,
802 arg_type, ret_type,
803 gen, success_response, boxed, allow_oob, allow_preconfig,
804 coroutine):
805 super().__init__(name, info, doc, ifcond, features)
806 assert not arg_type or isinstance(arg_type, str)
807 assert not ret_type or isinstance(ret_type, str)
808 self._arg_type_name = arg_type
809 self.arg_type = None
810 self._ret_type_name = ret_type
811 self.ret_type = None
812 self.gen = gen
813 self.success_response = success_response
814 self.boxed = boxed
815 self.allow_oob = allow_oob
816 self.allow_preconfig = allow_preconfig
817 self.coroutine = coroutine
818
819 def check(self, schema):
820 super().check(schema)
821 if self._arg_type_name:
822 self.arg_type = schema.resolve_type(
823 self._arg_type_name, self.info, "command's 'data'")
824 if not isinstance(self.arg_type, QAPISchemaObjectType):
825 raise QAPISemError(
826 self.info,
827 "command's 'data' cannot take %s"
828 % self.arg_type.describe())
829 if self.arg_type.variants and not self.boxed:
830 raise QAPISemError(
831 self.info,
832 "command's 'data' can take %s only with 'boxed': true"
833 % self.arg_type.describe())
834 self.arg_type.check(schema)
835 if self.arg_type.has_conditional_members() and not self.boxed:
836 raise QAPISemError(
837 self.info,
838 "conditional command arguments require 'boxed': true")
839 if self._ret_type_name:
840 self.ret_type = schema.resolve_type(
841 self._ret_type_name, self.info, "command's 'returns'")
842 if self.name not in self.info.pragma.command_returns_exceptions:
843 typ = self.ret_type
844 if isinstance(typ, QAPISchemaArrayType):
845 typ = self.ret_type.element_type
846 assert typ
847 if not isinstance(typ, QAPISchemaObjectType):
848 raise QAPISemError(
849 self.info,
850 "command's 'returns' cannot take %s"
851 % self.ret_type.describe())
852
853 def connect_doc(self, doc=None):
854 super().connect_doc(doc)
855 doc = doc or self.doc
856 if doc:
857 if self.arg_type and self.arg_type.is_implicit():
858 self.arg_type.connect_doc(doc)
859
860 def visit(self, visitor):
861 super().visit(visitor)
862 visitor.visit_command(
863 self.name, self.info, self.ifcond, self.features,
864 self.arg_type, self.ret_type, self.gen, self.success_response,
865 self.boxed, self.allow_oob, self.allow_preconfig,
866 self.coroutine)
867
868
869 class QAPISchemaEvent(QAPISchemaEntity):
870 meta = 'event'
871
872 def __init__(self, name, info, doc, ifcond, features, arg_type, boxed):
873 super().__init__(name, info, doc, ifcond, features)
874 assert not arg_type or isinstance(arg_type, str)
875 self._arg_type_name = arg_type
876 self.arg_type = None
877 self.boxed = boxed
878
879 def check(self, schema):
880 super().check(schema)
881 if self._arg_type_name:
882 self.arg_type = schema.resolve_type(
883 self._arg_type_name, self.info, "event's 'data'")
884 if not isinstance(self.arg_type, QAPISchemaObjectType):
885 raise QAPISemError(
886 self.info,
887 "event's 'data' cannot take %s"
888 % self.arg_type.describe())
889 if self.arg_type.variants and not self.boxed:
890 raise QAPISemError(
891 self.info,
892 "event's 'data' can take %s only with 'boxed': true"
893 % self.arg_type.describe())
894 self.arg_type.check(schema)
895 if self.arg_type.has_conditional_members() and not self.boxed:
896 raise QAPISemError(
897 self.info,
898 "conditional event arguments require 'boxed': true")
899
900 def connect_doc(self, doc=None):
901 super().connect_doc(doc)
902 doc = doc or self.doc
903 if doc:
904 if self.arg_type and self.arg_type.is_implicit():
905 self.arg_type.connect_doc(doc)
906
907 def visit(self, visitor):
908 super().visit(visitor)
909 visitor.visit_event(
910 self.name, self.info, self.ifcond, self.features,
911 self.arg_type, self.boxed)
912
913
914 class QAPISchema:
915 def __init__(self, fname):
916 self.fname = fname
917
918 try:
919 parser = QAPISchemaParser(fname)
920 except OSError as err:
921 raise QAPIError(
922 f"can't read schema file '{fname}': {err.strerror}"
923 ) from err
924
925 exprs = check_exprs(parser.exprs)
926 self.docs = parser.docs
927 self._entity_list = []
928 self._entity_dict = {}
929 self._module_dict = OrderedDict()
930 self._schema_dir = os.path.dirname(fname)
931 self._make_module(QAPISchemaModule.BUILTIN_MODULE_NAME)
932 self._make_module(fname)
933 self._predefining = True
934 self._def_predefineds()
935 self._predefining = False
936 self._def_exprs(exprs)
937 self.check()
938
939 def _def_entity(self, ent):
940 # Only the predefined types are allowed to not have info
941 assert ent.info or self._predefining
942 self._entity_list.append(ent)
943 if ent.name is None:
944 return
945 # TODO reject names that differ only in '_' vs. '.' vs. '-',
946 # because they're liable to clash in generated C.
947 other_ent = self._entity_dict.get(ent.name)
948 if other_ent:
949 if other_ent.info:
950 where = QAPISourceError(other_ent.info, "previous definition")
951 raise QAPISemError(
952 ent.info,
953 "'%s' is already defined\n%s" % (ent.name, where))
954 raise QAPISemError(
955 ent.info, "%s is already defined" % other_ent.describe())
956 self._entity_dict[ent.name] = ent
957
958 def lookup_entity(self, name, typ=None):
959 ent = self._entity_dict.get(name)
960 if typ and not isinstance(ent, typ):
961 return None
962 return ent
963
964 def lookup_type(self, name):
965 return self.lookup_entity(name, QAPISchemaType)
966
967 def resolve_type(self, name, info, what):
968 typ = self.lookup_type(name)
969 if not typ:
970 if callable(what):
971 what = what(info)
972 raise QAPISemError(
973 info, "%s uses unknown type '%s'" % (what, name))
974 return typ
975
976 def _module_name(self, fname: str) -> str:
977 if QAPISchemaModule.is_system_module(fname):
978 return fname
979 return os.path.relpath(fname, self._schema_dir)
980
981 def _make_module(self, fname):
982 name = self._module_name(fname)
983 if name not in self._module_dict:
984 self._module_dict[name] = QAPISchemaModule(name)
985 return self._module_dict[name]
986
987 def module_by_fname(self, fname):
988 name = self._module_name(fname)
989 return self._module_dict[name]
990
991 def _def_include(self, expr: QAPIExpression):
992 include = expr['include']
993 assert expr.doc is None
994 self._def_entity(
995 QAPISchemaInclude(self._make_module(include), expr.info))
996
997 def _def_builtin_type(self, name, json_type, c_type):
998 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
999 # Instantiating only the arrays that are actually used would
1000 # be nice, but we can't as long as their generated code
1001 # (qapi-builtin-types.[ch]) may be shared by some other
1002 # schema.
1003 self._make_array_type(name, None)
1004
1005 def _def_predefineds(self):
1006 for t in [('str', 'string', 'char' + POINTER_SUFFIX),
1007 ('number', 'number', 'double'),
1008 ('int', 'int', 'int64_t'),
1009 ('int8', 'int', 'int8_t'),
1010 ('int16', 'int', 'int16_t'),
1011 ('int32', 'int', 'int32_t'),
1012 ('int64', 'int', 'int64_t'),
1013 ('uint8', 'int', 'uint8_t'),
1014 ('uint16', 'int', 'uint16_t'),
1015 ('uint32', 'int', 'uint32_t'),
1016 ('uint64', 'int', 'uint64_t'),
1017 ('size', 'int', 'uint64_t'),
1018 ('bool', 'boolean', 'bool'),
1019 ('any', 'value', 'QObject' + POINTER_SUFFIX),
1020 ('null', 'null', 'QNull' + POINTER_SUFFIX)]:
1021 self._def_builtin_type(*t)
1022 self.the_empty_object_type = QAPISchemaObjectType(
1023 'q_empty', None, None, None, None, None, [], None)
1024 self._def_entity(self.the_empty_object_type)
1025
1026 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1027 'qbool']
1028 qtype_values = self._make_enum_members(
1029 [{'name': n} for n in qtypes], None)
1030
1031 self._def_entity(QAPISchemaEnumType('QType', None, None, None, None,
1032 qtype_values, 'QTYPE'))
1033
1034 def _make_features(self, features, info):
1035 if features is None:
1036 return []
1037 return [QAPISchemaFeature(f['name'], info,
1038 QAPISchemaIfCond(f.get('if')))
1039 for f in features]
1040
1041 def _make_enum_member(self, name, ifcond, features, info):
1042 return QAPISchemaEnumMember(name, info,
1043 QAPISchemaIfCond(ifcond),
1044 self._make_features(features, info))
1045
1046 def _make_enum_members(self, values, info):
1047 return [self._make_enum_member(v['name'], v.get('if'),
1048 v.get('features'), info)
1049 for v in values]
1050
1051 def _make_array_type(self, element_type, info):
1052 name = element_type + 'List' # reserved by check_defn_name_str()
1053 if not self.lookup_type(name):
1054 self._def_entity(QAPISchemaArrayType(name, info, element_type))
1055 return name
1056
1057 def _make_implicit_object_type(self, name, info, ifcond, role, members):
1058 if not members:
1059 return None
1060 # See also QAPISchemaObjectTypeMember.describe()
1061 name = 'q_obj_%s-%s' % (name, role)
1062 typ = self.lookup_entity(name, QAPISchemaObjectType)
1063 if typ:
1064 # The implicit object type has multiple users. This can
1065 # only be a duplicate definition, which will be flagged
1066 # later.
1067 pass
1068 else:
1069 self._def_entity(QAPISchemaObjectType(
1070 name, info, None, ifcond, None, None, members, None))
1071 return name
1072
1073 def _def_enum_type(self, expr: QAPIExpression):
1074 name = expr['enum']
1075 data = expr['data']
1076 prefix = expr.get('prefix')
1077 ifcond = QAPISchemaIfCond(expr.get('if'))
1078 info = expr.info
1079 features = self._make_features(expr.get('features'), info)
1080 self._def_entity(QAPISchemaEnumType(
1081 name, info, expr.doc, ifcond, features,
1082 self._make_enum_members(data, info), prefix))
1083
1084 def _make_member(self, name, typ, ifcond, features, info):
1085 optional = False
1086 if name.startswith('*'):
1087 name = name[1:]
1088 optional = True
1089 if isinstance(typ, list):
1090 assert len(typ) == 1
1091 typ = self._make_array_type(typ[0], info)
1092 return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond,
1093 self._make_features(features, info))
1094
1095 def _make_members(self, data, info):
1096 return [self._make_member(key, value['type'],
1097 QAPISchemaIfCond(value.get('if')),
1098 value.get('features'), info)
1099 for (key, value) in data.items()]
1100
1101 def _def_struct_type(self, expr: QAPIExpression):
1102 name = expr['struct']
1103 base = expr.get('base')
1104 data = expr['data']
1105 info = expr.info
1106 ifcond = QAPISchemaIfCond(expr.get('if'))
1107 features = self._make_features(expr.get('features'), info)
1108 self._def_entity(QAPISchemaObjectType(
1109 name, info, expr.doc, ifcond, features, base,
1110 self._make_members(data, info),
1111 None))
1112
1113 def _make_variant(self, case, typ, ifcond, info):
1114 if isinstance(typ, list):
1115 assert len(typ) == 1
1116 typ = self._make_array_type(typ[0], info)
1117 return QAPISchemaVariant(case, info, typ, ifcond)
1118
1119 def _def_union_type(self, expr: QAPIExpression):
1120 name = expr['union']
1121 base = expr['base']
1122 tag_name = expr['discriminator']
1123 data = expr['data']
1124 assert isinstance(data, dict)
1125 info = expr.info
1126 ifcond = QAPISchemaIfCond(expr.get('if'))
1127 features = self._make_features(expr.get('features'), info)
1128 if isinstance(base, dict):
1129 base = self._make_implicit_object_type(
1130 name, info, ifcond,
1131 'base', self._make_members(base, info))
1132 variants = [
1133 self._make_variant(key, value['type'],
1134 QAPISchemaIfCond(value.get('if')),
1135 info)
1136 for (key, value) in data.items()]
1137 members: List[QAPISchemaObjectTypeMember] = []
1138 self._def_entity(
1139 QAPISchemaObjectType(name, info, expr.doc, ifcond, features,
1140 base, members,
1141 QAPISchemaVariants(
1142 tag_name, info, None, variants)))
1143
1144 def _def_alternate_type(self, expr: QAPIExpression):
1145 name = expr['alternate']
1146 data = expr['data']
1147 assert isinstance(data, dict)
1148 ifcond = QAPISchemaIfCond(expr.get('if'))
1149 info = expr.info
1150 features = self._make_features(expr.get('features'), info)
1151 variants = [
1152 self._make_variant(key, value['type'],
1153 QAPISchemaIfCond(value.get('if')),
1154 info)
1155 for (key, value) in data.items()]
1156 tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
1157 self._def_entity(
1158 QAPISchemaAlternateType(
1159 name, info, expr.doc, ifcond, features,
1160 QAPISchemaVariants(None, info, tag_member, variants)))
1161
1162 def _def_command(self, expr: QAPIExpression):
1163 name = expr['command']
1164 data = expr.get('data')
1165 rets = expr.get('returns')
1166 gen = expr.get('gen', True)
1167 success_response = expr.get('success-response', True)
1168 boxed = expr.get('boxed', False)
1169 allow_oob = expr.get('allow-oob', False)
1170 allow_preconfig = expr.get('allow-preconfig', False)
1171 coroutine = expr.get('coroutine', False)
1172 ifcond = QAPISchemaIfCond(expr.get('if'))
1173 info = expr.info
1174 features = self._make_features(expr.get('features'), info)
1175 if isinstance(data, OrderedDict):
1176 data = self._make_implicit_object_type(
1177 name, info, ifcond,
1178 'arg', self._make_members(data, info))
1179 if isinstance(rets, list):
1180 assert len(rets) == 1
1181 rets = self._make_array_type(rets[0], info)
1182 self._def_entity(QAPISchemaCommand(name, info, expr.doc, ifcond,
1183 features, data, rets,
1184 gen, success_response,
1185 boxed, allow_oob, allow_preconfig,
1186 coroutine))
1187
1188 def _def_event(self, expr: QAPIExpression):
1189 name = expr['event']
1190 data = expr.get('data')
1191 boxed = expr.get('boxed', False)
1192 ifcond = QAPISchemaIfCond(expr.get('if'))
1193 info = expr.info
1194 features = self._make_features(expr.get('features'), info)
1195 if isinstance(data, OrderedDict):
1196 data = self._make_implicit_object_type(
1197 name, info, ifcond,
1198 'arg', self._make_members(data, info))
1199 self._def_entity(QAPISchemaEvent(name, info, expr.doc, ifcond,
1200 features, data, boxed))
1201
1202 def _def_exprs(self, exprs):
1203 for expr in exprs:
1204 if 'enum' in expr:
1205 self._def_enum_type(expr)
1206 elif 'struct' in expr:
1207 self._def_struct_type(expr)
1208 elif 'union' in expr:
1209 self._def_union_type(expr)
1210 elif 'alternate' in expr:
1211 self._def_alternate_type(expr)
1212 elif 'command' in expr:
1213 self._def_command(expr)
1214 elif 'event' in expr:
1215 self._def_event(expr)
1216 elif 'include' in expr:
1217 self._def_include(expr)
1218 else:
1219 assert False
1220
1221 def check(self):
1222 for ent in self._entity_list:
1223 ent.check(self)
1224 ent.connect_doc()
1225 ent.check_doc()
1226 for ent in self._entity_list:
1227 ent.set_module(self)
1228
1229 def visit(self, visitor):
1230 visitor.visit_begin(self)
1231 for mod in self._module_dict.values():
1232 mod.visit(visitor)
1233 visitor.visit_end()