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