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