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