xref: /qemu/scripts/qapi/introspect.py (revision 41d0ad1d)
1fb0bc835SMarkus Armbruster"""
2fb0bc835SMarkus ArmbrusterQAPI introspection generator
3fb0bc835SMarkus Armbruster
4cf26906cSJohn SnowCopyright (C) 2015-2021 Red Hat, Inc.
5fb0bc835SMarkus Armbruster
6fb0bc835SMarkus ArmbrusterAuthors:
7fb0bc835SMarkus Armbruster Markus Armbruster <armbru@redhat.com>
8cf26906cSJohn Snow John Snow <jsnow@redhat.com>
9fb0bc835SMarkus Armbruster
10fb0bc835SMarkus ArmbrusterThis work is licensed under the terms of the GNU GPL, version 2.
11fb0bc835SMarkus ArmbrusterSee the COPYING file in the top-level directory.
12fb0bc835SMarkus Armbruster"""
13fb0bc835SMarkus Armbruster
149db27346SJohn Snowfrom typing import (
159db27346SJohn Snow    Any,
169db27346SJohn Snow    Dict,
174f7f97a7SJohn Snow    Generic,
189db27346SJohn Snow    List,
199db27346SJohn Snow    Optional,
2082b52f6bSJohn Snow    Sequence,
214f7f97a7SJohn Snow    TypeVar,
229db27346SJohn Snow    Union,
239db27346SJohn Snow)
245f50cedeSJohn Snow
251889e57aSMarkus Armbrusterfrom .common import c_name, mcgen
267137a960SJohn Snowfrom .gen import QAPISchemaMonolithicCVisitor
2767fea575SJohn Snowfrom .schema import (
2882b52f6bSJohn Snow    QAPISchema,
291d067e39SMarkus Armbruster    QAPISchemaAlternatives,
301d067e39SMarkus Armbruster    QAPISchemaBranches,
3167fea575SJohn Snow    QAPISchemaArrayType,
3267fea575SJohn Snow    QAPISchemaBuiltinType,
3382b52f6bSJohn Snow    QAPISchemaEntity,
3482b52f6bSJohn Snow    QAPISchemaEnumMember,
3582b52f6bSJohn Snow    QAPISchemaFeature,
36f17539c8SMarc-André Lureau    QAPISchemaIfCond,
3782b52f6bSJohn Snow    QAPISchemaObjectType,
3882b52f6bSJohn Snow    QAPISchemaObjectTypeMember,
3967fea575SJohn Snow    QAPISchemaType,
4082b52f6bSJohn Snow    QAPISchemaVariant,
4167fea575SJohn Snow)
4282b52f6bSJohn Snowfrom .source import QAPISourceInfo
43fb0bc835SMarkus Armbruster
44fb0bc835SMarkus Armbruster
459db27346SJohn Snow# This module constructs a tree data structure that is used to
469db27346SJohn Snow# generate the introspection information for QEMU. It is shaped
479db27346SJohn Snow# like a JSON value.
489db27346SJohn Snow#
499db27346SJohn Snow# A complexity over JSON is that our values may or may not be annotated.
509db27346SJohn Snow#
519db27346SJohn Snow# Un-annotated values may be:
529db27346SJohn Snow#     Scalar: str, bool, None.
539db27346SJohn Snow#     Non-scalar: List, Dict
549db27346SJohn Snow# _value = Union[str, bool, None, Dict[str, JSONValue], List[JSONValue]]
559db27346SJohn Snow#
569db27346SJohn Snow# With optional annotations, the type of all values is:
579db27346SJohn Snow# JSONValue = Union[_Value, Annotated[_Value]]
589db27346SJohn Snow#
599db27346SJohn Snow# Sadly, mypy does not support recursive types; so the _Stub alias is used to
609db27346SJohn Snow# mark the imprecision in the type model where we'd otherwise use JSONValue.
619db27346SJohn Snow_Stub = Any
629db27346SJohn Snow_Scalar = Union[str, bool, None]
639db27346SJohn Snow_NonScalar = Union[Dict[str, _Stub], List[_Stub]]
649db27346SJohn Snow_Value = Union[_Scalar, _NonScalar]
654f7f97a7SJohn SnowJSONValue = Union[_Value, 'Annotated[_Value]']
669db27346SJohn Snow
6782b52f6bSJohn Snow# These types are based on structures defined in QEMU's schema, so we
6882b52f6bSJohn Snow# lack precise types for them here. Python 3.6 does not offer
6982b52f6bSJohn Snow# TypedDict constructs, so they are broadly typed here as simple
7082b52f6bSJohn Snow# Python Dicts.
7182b52f6bSJohn SnowSchemaInfo = Dict[str, object]
7275ecee72SMarkus ArmbrusterSchemaInfoEnumMember = Dict[str, object]
7382b52f6bSJohn SnowSchemaInfoObject = Dict[str, object]
7482b52f6bSJohn SnowSchemaInfoObjectVariant = Dict[str, object]
7582b52f6bSJohn SnowSchemaInfoObjectMember = Dict[str, object]
7682b52f6bSJohn SnowSchemaInfoCommand = Dict[str, object]
7782b52f6bSJohn Snow
789db27346SJohn Snow
794f7f97a7SJohn Snow_ValueT = TypeVar('_ValueT', bound=_Value)
804f7f97a7SJohn Snow
814f7f97a7SJohn Snow
824f7f97a7SJohn Snowclass Annotated(Generic[_ValueT]):
834f7f97a7SJohn Snow    """
844f7f97a7SJohn Snow    Annotated generally contains a SchemaInfo-like type (as a dict),
854f7f97a7SJohn Snow    But it also used to wrap comments/ifconds around scalar leaf values,
864f7f97a7SJohn Snow    for the benefit of features and enums.
874f7f97a7SJohn Snow    """
884f7f97a7SJohn Snow    # TODO: Remove after Python 3.7 adds @dataclass:
894f7f97a7SJohn Snow    # pylint: disable=too-few-public-methods
90f17539c8SMarc-André Lureau    def __init__(self, value: _ValueT, ifcond: QAPISchemaIfCond,
914f7f97a7SJohn Snow                 comment: Optional[str] = None):
924f7f97a7SJohn Snow        self.value = value
934f7f97a7SJohn Snow        self.comment: Optional[str] = comment
94f17539c8SMarc-André Lureau        self.ifcond = ifcond
9524cfd6adSMarkus Armbruster
9624cfd6adSMarkus Armbruster
9782b52f6bSJohn Snowdef _tree_to_qlit(obj: JSONValue,
9882b52f6bSJohn Snow                  level: int = 0,
9982b52f6bSJohn Snow                  dict_value: bool = False) -> str:
1005444dedfSJohn Snow    """
1015444dedfSJohn Snow    Convert the type tree into a QLIT C string, recursively.
1025444dedfSJohn Snow
1035444dedfSJohn Snow    :param obj: The value to convert.
1045444dedfSJohn Snow                This value may not be Annotated when dict_value is True.
1055444dedfSJohn Snow    :param level: The indentation level for this particular value.
1065444dedfSJohn Snow    :param dict_value: True when the value being processed belongs to a
1075444dedfSJohn Snow                       dict key; which suppresses the output indent.
1085444dedfSJohn Snow    """
1097d0f982bSMarc-André Lureau
11082b52f6bSJohn Snow    def indent(level: int) -> str:
1117d0f982bSMarc-André Lureau        return level * 4 * ' '
1127d0f982bSMarc-André Lureau
1134f7f97a7SJohn Snow    if isinstance(obj, Annotated):
11405556960SJohn Snow        # NB: _tree_to_qlit is called recursively on the values of a
11505556960SJohn Snow        # key:value pair; those values can't be decorated with
11605556960SJohn Snow        # comments or conditionals.
11705556960SJohn Snow        msg = "dict values cannot have attached comments or if-conditionals."
11805556960SJohn Snow        assert not dict_value, msg
11905556960SJohn Snow
1208c643361SEric Blake        ret = ''
1214f7f97a7SJohn Snow        if obj.comment:
122c0e8d9f3SJohn Snow            ret += indent(level) + f"/* {obj.comment} */\n"
12333aa3267SMarc-André Lureau        if obj.ifcond.is_present():
1241889e57aSMarkus Armbruster            ret += obj.ifcond.gen_if()
1254f7f97a7SJohn Snow        ret += _tree_to_qlit(obj.value, level)
12633aa3267SMarc-André Lureau        if obj.ifcond.is_present():
1271889e57aSMarkus Armbruster            ret += '\n' + obj.ifcond.gen_endif()
128d626b6c1SMarc-André Lureau        return ret
129d626b6c1SMarc-André Lureau
1307d0f982bSMarc-André Lureau    ret = ''
13105556960SJohn Snow    if not dict_value:
1327d0f982bSMarc-André Lureau        ret += indent(level)
133c0e8d9f3SJohn Snow
134c0e8d9f3SJohn Snow    # Scalars:
135fb0bc835SMarkus Armbruster    if obj is None:
1367d0f982bSMarc-André Lureau        ret += 'QLIT_QNULL'
137fb0bc835SMarkus Armbruster    elif isinstance(obj, str):
138c0e8d9f3SJohn Snow        ret += f"QLIT_QSTR({to_c_string(obj)})"
139c0e8d9f3SJohn Snow    elif isinstance(obj, bool):
140c0e8d9f3SJohn Snow        ret += f"QLIT_QBOOL({str(obj).lower()})"
141c0e8d9f3SJohn Snow
142c0e8d9f3SJohn Snow    # Non-scalars:
143fb0bc835SMarkus Armbruster    elif isinstance(obj, list):
1447d0f982bSMarc-André Lureau        ret += 'QLIT_QLIST(((QLitObject[]) {\n'
145c0e8d9f3SJohn Snow        for value in obj:
146c0e8d9f3SJohn Snow            ret += _tree_to_qlit(value, level + 1).strip('\n') + '\n'
147c0e8d9f3SJohn Snow        ret += indent(level + 1) + '{}\n'
1487d0f982bSMarc-André Lureau        ret += indent(level) + '}))'
149fb0bc835SMarkus Armbruster    elif isinstance(obj, dict):
1507d0f982bSMarc-André Lureau        ret += 'QLIT_QDICT(((QLitDictEntry[]) {\n'
151c0e8d9f3SJohn Snow        for key, value in sorted(obj.items()):
152c0e8d9f3SJohn Snow            ret += indent(level + 1) + "{{ {:s}, {:s} }},\n".format(
153c0e8d9f3SJohn Snow                to_c_string(key),
154c0e8d9f3SJohn Snow                _tree_to_qlit(value, level + 1, dict_value=True)
155c0e8d9f3SJohn Snow            )
156c0e8d9f3SJohn Snow        ret += indent(level + 1) + '{}\n'
1577d0f982bSMarc-André Lureau        ret += indent(level) + '}))'
158fb0bc835SMarkus Armbruster    else:
1592a6c161bSJohn Snow        raise NotImplementedError(
1602a6c161bSJohn Snow            f"type '{type(obj).__name__}' not implemented"
1612a6c161bSJohn Snow        )
162c0e8d9f3SJohn Snow
16340bb1376SMarc-André Lureau    if level > 0:
16440bb1376SMarc-André Lureau        ret += ','
165fb0bc835SMarkus Armbruster    return ret
166fb0bc835SMarkus Armbruster
167fb0bc835SMarkus Armbruster
16882b52f6bSJohn Snowdef to_c_string(string: str) -> str:
169fb0bc835SMarkus Armbruster    return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"'
170fb0bc835SMarkus Armbruster
171fb0bc835SMarkus Armbruster
17271b3f045SMarkus Armbrusterclass QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor):
173fb0bc835SMarkus Armbruster
17482b52f6bSJohn Snow    def __init__(self, prefix: str, unmask: bool):
1752cae67bcSMarkus Armbruster        super().__init__(
1762cae67bcSMarkus Armbruster            prefix, 'qapi-introspect',
17771b3f045SMarkus Armbruster            ' * QAPI/QMP schema introspection', __doc__)
17871b3f045SMarkus Armbruster        self._unmask = unmask
17982b52f6bSJohn Snow        self._schema: Optional[QAPISchema] = None
18082b52f6bSJohn Snow        self._trees: List[Annotated[SchemaInfo]] = []
18182b52f6bSJohn Snow        self._used_types: List[QAPISchemaType] = []
18282b52f6bSJohn Snow        self._name_map: Dict[str, str] = {}
18371b3f045SMarkus Armbruster        self._genc.add(mcgen('''
18471b3f045SMarkus Armbruster#include "qemu/osdep.h"
185eb815e24SMarkus Armbruster#include "%(prefix)sqapi-introspect.h"
18671b3f045SMarkus Armbruster
18771b3f045SMarkus Armbruster''',
18871b3f045SMarkus Armbruster                             prefix=prefix))
18971b3f045SMarkus Armbruster
19082b52f6bSJohn Snow    def visit_begin(self, schema: QAPISchema) -> None:
19171b3f045SMarkus Armbruster        self._schema = schema
192fb0bc835SMarkus Armbruster
19382b52f6bSJohn Snow    def visit_end(self) -> None:
194fb0bc835SMarkus Armbruster        # visit the types that are actually used
195fb0bc835SMarkus Armbruster        for typ in self._used_types:
196fb0bc835SMarkus Armbruster            typ.visit(self)
197fb0bc835SMarkus Armbruster        # generate C
1987d0f982bSMarc-André Lureau        name = c_name(self._prefix, protect=False) + 'qmp_schema_qlit'
19971b3f045SMarkus Armbruster        self._genh.add(mcgen('''
2007d0f982bSMarc-André Lureau#include "qapi/qmp/qlit.h"
2017d0f982bSMarc-André Lureau
2027d0f982bSMarc-André Lureauextern const QLitObject %(c_name)s;
203fb0bc835SMarkus Armbruster''',
20471b3f045SMarkus Armbruster                             c_name=c_name(name)))
20571b3f045SMarkus Armbruster        self._genc.add(mcgen('''
2067d0f982bSMarc-André Lureauconst QLitObject %(c_name)s = %(c_string)s;
207fb0bc835SMarkus Armbruster''',
208fb0bc835SMarkus Armbruster                             c_name=c_name(name),
2092e8a843dSMarkus Armbruster                             c_string=_tree_to_qlit(self._trees)))
210fb0bc835SMarkus Armbruster        self._schema = None
2112e8a843dSMarkus Armbruster        self._trees = []
21271b3f045SMarkus Armbruster        self._used_types = []
21371b3f045SMarkus Armbruster        self._name_map = {}
214fb0bc835SMarkus Armbruster
21582b52f6bSJohn Snow    def visit_needed(self, entity: QAPISchemaEntity) -> bool:
216fb0bc835SMarkus Armbruster        # Ignore types on first pass; visit_end() will pick up used types
217fb0bc835SMarkus Armbruster        return not isinstance(entity, QAPISchemaType)
218fb0bc835SMarkus Armbruster
21982b52f6bSJohn Snow    def _name(self, name: str) -> str:
220fb0bc835SMarkus Armbruster        if self._unmask:
221fb0bc835SMarkus Armbruster            return name
222fb0bc835SMarkus Armbruster        if name not in self._name_map:
223fb0bc835SMarkus Armbruster            self._name_map[name] = '%d' % len(self._name_map)
224fb0bc835SMarkus Armbruster        return self._name_map[name]
225fb0bc835SMarkus Armbruster
22682b52f6bSJohn Snow    def _use_type(self, typ: QAPISchemaType) -> str:
2276b67bcacSJohn Snow        assert self._schema is not None
2286b67bcacSJohn Snow
229fb0bc835SMarkus Armbruster        # Map the various integer types to plain int
230fb0bc835SMarkus Armbruster        if typ.json_type() == 'int':
2317191400aSMarkus Armbruster            type_int = self._schema.lookup_type('int')
2327191400aSMarkus Armbruster            assert type_int
2337191400aSMarkus Armbruster            typ = type_int
234fb0bc835SMarkus Armbruster        elif (isinstance(typ, QAPISchemaArrayType) and
235fb0bc835SMarkus Armbruster              typ.element_type.json_type() == 'int'):
2367191400aSMarkus Armbruster            type_intList = self._schema.lookup_type('intList')
2377191400aSMarkus Armbruster            assert type_intList
2387191400aSMarkus Armbruster            typ = type_intList
239fb0bc835SMarkus Armbruster        # Add type to work queue if new
240fb0bc835SMarkus Armbruster        if typ not in self._used_types:
241fb0bc835SMarkus Armbruster            self._used_types.append(typ)
242fb0bc835SMarkus Armbruster        # Clients should examine commands and events, not types.  Hide
2431aa806ccSEric Blake        # type names as integers to reduce the temptation.  Also, it
2441aa806ccSEric Blake        # saves a few characters on the wire.
245fb0bc835SMarkus Armbruster        if isinstance(typ, QAPISchemaBuiltinType):
246fb0bc835SMarkus Armbruster            return typ.name
247fb0bc835SMarkus Armbruster        if isinstance(typ, QAPISchemaArrayType):
248fb0bc835SMarkus Armbruster            return '[' + self._use_type(typ.element_type) + ']'
249fb0bc835SMarkus Armbruster        return self._name(typ.name)
250fb0bc835SMarkus Armbruster
25184bece7dSJohn Snow    @staticmethod
252cea53c31SJohn Snow    def _gen_features(features: Sequence[QAPISchemaFeature]
25382b52f6bSJohn Snow                      ) -> List[Annotated[str]]:
2544f7f97a7SJohn Snow        return [Annotated(f.name, f.ifcond) for f in features]
25584bece7dSJohn Snow
25682b52f6bSJohn Snow    def _gen_tree(self, name: str, mtype: str, obj: Dict[str, object],
257f17539c8SMarc-André Lureau                  ifcond: QAPISchemaIfCond = QAPISchemaIfCond(),
258cea53c31SJohn Snow                  features: Sequence[QAPISchemaFeature] = ()) -> None:
2595444dedfSJohn Snow        """
2605444dedfSJohn Snow        Build and append a SchemaInfo object to self._trees.
2615444dedfSJohn Snow
2625444dedfSJohn Snow        :param name: The SchemaInfo's name.
2635444dedfSJohn Snow        :param mtype: The SchemaInfo's meta-type.
2645444dedfSJohn Snow        :param obj: Additional SchemaInfo members, as appropriate for
2655444dedfSJohn Snow                    the meta-type.
2665444dedfSJohn Snow        :param ifcond: Conditionals to apply to the SchemaInfo.
2675444dedfSJohn Snow        :param features: The SchemaInfo's features.
2685444dedfSJohn Snow                         Will be omitted from the output if empty.
2695444dedfSJohn Snow        """
2705f50cedeSJohn Snow        comment: Optional[str] = None
271fb0bc835SMarkus Armbruster        if mtype not in ('command', 'event', 'builtin', 'array'):
2728c643361SEric Blake            if not self._unmask:
2738c643361SEric Blake                # Output a comment to make it easy to map masked names
2748c643361SEric Blake                # back to the source when reading the generated output.
2755f50cedeSJohn Snow                comment = f'"{self._name(name)}" = {name}'
276fb0bc835SMarkus Armbruster            name = self._name(name)
277fb0bc835SMarkus Armbruster        obj['name'] = name
278fb0bc835SMarkus Armbruster        obj['meta-type'] = mtype
27984bece7dSJohn Snow        if features:
28084bece7dSJohn Snow            obj['features'] = self._gen_features(features)
2814f7f97a7SJohn Snow        self._trees.append(Annotated(obj, ifcond, comment))
282fb0bc835SMarkus Armbruster
283b6c18755SMarkus Armbruster    def _gen_enum_member(self, member: QAPISchemaEnumMember
28475ecee72SMarkus Armbruster                         ) -> Annotated[SchemaInfoEnumMember]:
28575ecee72SMarkus Armbruster        obj: SchemaInfoEnumMember = {
28675ecee72SMarkus Armbruster            'name': member.name,
28775ecee72SMarkus Armbruster        }
288b6c18755SMarkus Armbruster        if member.features:
289b6c18755SMarkus Armbruster            obj['features'] = self._gen_features(member.features)
29075ecee72SMarkus Armbruster        return Annotated(obj, member.ifcond)
29175ecee72SMarkus Armbruster
29275ecee72SMarkus Armbruster    def _gen_object_member(self, member: QAPISchemaObjectTypeMember
29382b52f6bSJohn Snow                           ) -> Annotated[SchemaInfoObjectMember]:
29482b52f6bSJohn Snow        obj: SchemaInfoObjectMember = {
29582b52f6bSJohn Snow            'name': member.name,
29682b52f6bSJohn Snow            'type': self._use_type(member.type)
29782b52f6bSJohn Snow        }
298fb0bc835SMarkus Armbruster        if member.optional:
29924cfd6adSMarkus Armbruster            obj['default'] = None
30084bece7dSJohn Snow        if member.features:
30184bece7dSJohn Snow            obj['features'] = self._gen_features(member.features)
3024f7f97a7SJohn Snow        return Annotated(obj, member.ifcond)
303fb0bc835SMarkus Armbruster
30482b52f6bSJohn Snow    def _gen_variant(self, variant: QAPISchemaVariant
30582b52f6bSJohn Snow                     ) -> Annotated[SchemaInfoObjectVariant]:
30682b52f6bSJohn Snow        obj: SchemaInfoObjectVariant = {
30782b52f6bSJohn Snow            'case': variant.name,
30882b52f6bSJohn Snow            'type': self._use_type(variant.type)
30982b52f6bSJohn Snow        }
3104f7f97a7SJohn Snow        return Annotated(obj, variant.ifcond)
311fb0bc835SMarkus Armbruster
31282b52f6bSJohn Snow    def visit_builtin_type(self, name: str, info: Optional[QAPISourceInfo],
31382b52f6bSJohn Snow                           json_type: str) -> None:
3149b77d946SJohn Snow        self._gen_tree(name, 'builtin', {'json-type': json_type})
315fb0bc835SMarkus Armbruster
31682b52f6bSJohn Snow    def visit_enum_type(self, name: str, info: Optional[QAPISourceInfo],
317f17539c8SMarc-André Lureau                        ifcond: QAPISchemaIfCond,
31882b52f6bSJohn Snow                        features: List[QAPISchemaFeature],
31982b52f6bSJohn Snow                        members: List[QAPISchemaEnumMember],
32082b52f6bSJohn Snow                        prefix: Optional[str]) -> None:
3214f7f97a7SJohn Snow        self._gen_tree(
3224f7f97a7SJohn Snow            name, 'enum',
32375ecee72SMarkus Armbruster            {'members': [self._gen_enum_member(m) for m in members],
32475ecee72SMarkus Armbruster             'values': [Annotated(m.name, m.ifcond) for m in members]},
3254f7f97a7SJohn Snow            ifcond, features
3264f7f97a7SJohn Snow        )
327fb0bc835SMarkus Armbruster
32882b52f6bSJohn Snow    def visit_array_type(self, name: str, info: Optional[QAPISourceInfo],
329f17539c8SMarc-André Lureau                         ifcond: QAPISchemaIfCond,
33082b52f6bSJohn Snow                         element_type: QAPISchemaType) -> None:
331fb0bc835SMarkus Armbruster        element = self._use_type(element_type)
3322e8a843dSMarkus Armbruster        self._gen_tree('[' + element + ']', 'array', {'element-type': element},
333cea53c31SJohn Snow                       ifcond)
334fb0bc835SMarkus Armbruster
33582b52f6bSJohn Snow    def visit_object_type_flat(self, name: str, info: Optional[QAPISourceInfo],
336f17539c8SMarc-André Lureau                               ifcond: QAPISchemaIfCond,
33782b52f6bSJohn Snow                               features: List[QAPISchemaFeature],
33882b52f6bSJohn Snow                               members: List[QAPISchemaObjectTypeMember],
339d1da8af8SMarkus Armbruster                               branches: Optional[QAPISchemaBranches]) -> None:
34082b52f6bSJohn Snow        obj: SchemaInfoObject = {
34175ecee72SMarkus Armbruster            'members': [self._gen_object_member(m) for m in members]
34282b52f6bSJohn Snow        }
343d1da8af8SMarkus Armbruster        if branches:
344d1da8af8SMarkus Armbruster            obj['tag'] = branches.tag_member.name
345d1da8af8SMarkus Armbruster            obj['variants'] = [self._gen_variant(v) for v in branches.variants]
3462e8a843dSMarkus Armbruster        self._gen_tree(name, 'object', obj, ifcond, features)
347fb0bc835SMarkus Armbruster
34882b52f6bSJohn Snow    def visit_alternate_type(self, name: str, info: Optional[QAPISourceInfo],
349f17539c8SMarc-André Lureau                             ifcond: QAPISchemaIfCond,
35082b52f6bSJohn Snow                             features: List[QAPISchemaFeature],
35141d0ad1dSMarkus Armbruster                             alternatives: QAPISchemaAlternatives) -> None:
3524f7f97a7SJohn Snow        self._gen_tree(
3534f7f97a7SJohn Snow            name, 'alternate',
3544f7f97a7SJohn Snow            {'members': [Annotated({'type': self._use_type(m.type)},
3554f7f97a7SJohn Snow                                   m.ifcond)
35641d0ad1dSMarkus Armbruster                         for m in alternatives.variants]},
3574f7f97a7SJohn Snow            ifcond, features
3584f7f97a7SJohn Snow        )
359fb0bc835SMarkus Armbruster
36082b52f6bSJohn Snow    def visit_command(self, name: str, info: Optional[QAPISourceInfo],
361f17539c8SMarc-André Lureau                      ifcond: QAPISchemaIfCond,
36282b52f6bSJohn Snow                      features: List[QAPISchemaFeature],
36382b52f6bSJohn Snow                      arg_type: Optional[QAPISchemaObjectType],
36482b52f6bSJohn Snow                      ret_type: Optional[QAPISchemaType], gen: bool,
36582b52f6bSJohn Snow                      success_response: bool, boxed: bool, allow_oob: bool,
36682b52f6bSJohn Snow                      allow_preconfig: bool, coroutine: bool) -> None:
3676b67bcacSJohn Snow        assert self._schema is not None
3686b67bcacSJohn Snow
369fb0bc835SMarkus Armbruster        arg_type = arg_type or self._schema.the_empty_object_type
370fb0bc835SMarkus Armbruster        ret_type = ret_type or self._schema.the_empty_object_type
37182b52f6bSJohn Snow        obj: SchemaInfoCommand = {
37282b52f6bSJohn Snow            'arg-type': self._use_type(arg_type),
37382b52f6bSJohn Snow            'ret-type': self._use_type(ret_type)
37482b52f6bSJohn Snow        }
37525b1ef31SMarkus Armbruster        if allow_oob:
37625b1ef31SMarkus Armbruster            obj['allow-oob'] = allow_oob
3772e8a843dSMarkus Armbruster        self._gen_tree(name, 'command', obj, ifcond, features)
37823394b4cSPeter Krempa
37982b52f6bSJohn Snow    def visit_event(self, name: str, info: Optional[QAPISourceInfo],
380f17539c8SMarc-André Lureau                    ifcond: QAPISchemaIfCond,
381f17539c8SMarc-André Lureau                    features: List[QAPISchemaFeature],
38282b52f6bSJohn Snow                    arg_type: Optional[QAPISchemaObjectType],
38382b52f6bSJohn Snow                    boxed: bool) -> None:
3846b67bcacSJohn Snow        assert self._schema is not None
38582b52f6bSJohn Snow
386fb0bc835SMarkus Armbruster        arg_type = arg_type or self._schema.the_empty_object_type
3872e8a843dSMarkus Armbruster        self._gen_tree(name, 'event', {'arg-type': self._use_type(arg_type)},
388013b4efcSMarkus Armbruster                       ifcond, features)
389fb0bc835SMarkus Armbruster
390fb0bc835SMarkus Armbruster
39182b52f6bSJohn Snowdef gen_introspect(schema: QAPISchema, output_dir: str, prefix: str,
39282b52f6bSJohn Snow                   opt_unmask: bool) -> None:
393fb0bc835SMarkus Armbruster    vis = QAPISchemaGenIntrospectVisitor(prefix, opt_unmask)
394fb0bc835SMarkus Armbruster    schema.visit(vis)
39571b3f045SMarkus Armbruster    vis.write(output_dir)
396