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