cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

schema.py (40682B)


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