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