xref: /qemu/scripts/qapi/introspect.py (revision b6c18755)
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,
2967fea575SJohn Snow    QAPISchemaArrayType,
3067fea575SJohn Snow    QAPISchemaBuiltinType,
3182b52f6bSJohn Snow    QAPISchemaEntity,
3282b52f6bSJohn Snow    QAPISchemaEnumMember,
3382b52f6bSJohn Snow    QAPISchemaFeature,
34f17539c8SMarc-André Lureau    QAPISchemaIfCond,
3582b52f6bSJohn Snow    QAPISchemaObjectType,
3682b52f6bSJohn Snow    QAPISchemaObjectTypeMember,
3767fea575SJohn Snow    QAPISchemaType,
3882b52f6bSJohn Snow    QAPISchemaVariant,
3982b52f6bSJohn Snow    QAPISchemaVariants,
4067fea575SJohn Snow)
4182b52f6bSJohn Snowfrom .source import QAPISourceInfo
42fb0bc835SMarkus Armbruster
43fb0bc835SMarkus Armbruster
449db27346SJohn Snow# This module constructs a tree data structure that is used to
459db27346SJohn Snow# generate the introspection information for QEMU. It is shaped
469db27346SJohn Snow# like a JSON value.
479db27346SJohn Snow#
489db27346SJohn Snow# A complexity over JSON is that our values may or may not be annotated.
499db27346SJohn Snow#
509db27346SJohn Snow# Un-annotated values may be:
519db27346SJohn Snow#     Scalar: str, bool, None.
529db27346SJohn Snow#     Non-scalar: List, Dict
539db27346SJohn Snow# _value = Union[str, bool, None, Dict[str, JSONValue], List[JSONValue]]
549db27346SJohn Snow#
559db27346SJohn Snow# With optional annotations, the type of all values is:
569db27346SJohn Snow# JSONValue = Union[_Value, Annotated[_Value]]
579db27346SJohn Snow#
589db27346SJohn Snow# Sadly, mypy does not support recursive types; so the _Stub alias is used to
599db27346SJohn Snow# mark the imprecision in the type model where we'd otherwise use JSONValue.
609db27346SJohn Snow_Stub = Any
619db27346SJohn Snow_Scalar = Union[str, bool, None]
629db27346SJohn Snow_NonScalar = Union[Dict[str, _Stub], List[_Stub]]
639db27346SJohn Snow_Value = Union[_Scalar, _NonScalar]
644f7f97a7SJohn SnowJSONValue = Union[_Value, 'Annotated[_Value]']
659db27346SJohn Snow
6682b52f6bSJohn Snow# These types are based on structures defined in QEMU's schema, so we
6782b52f6bSJohn Snow# lack precise types for them here. Python 3.6 does not offer
6882b52f6bSJohn Snow# TypedDict constructs, so they are broadly typed here as simple
6982b52f6bSJohn Snow# Python Dicts.
7082b52f6bSJohn SnowSchemaInfo = Dict[str, object]
7175ecee72SMarkus ArmbrusterSchemaInfoEnumMember = Dict[str, object]
7282b52f6bSJohn SnowSchemaInfoObject = Dict[str, object]
7382b52f6bSJohn SnowSchemaInfoObjectVariant = Dict[str, object]
7482b52f6bSJohn SnowSchemaInfoObjectMember = Dict[str, object]
7582b52f6bSJohn SnowSchemaInfoCommand = Dict[str, object]
7682b52f6bSJohn Snow
779db27346SJohn Snow
784f7f97a7SJohn Snow_ValueT = TypeVar('_ValueT', bound=_Value)
794f7f97a7SJohn Snow
804f7f97a7SJohn Snow
814f7f97a7SJohn Snowclass Annotated(Generic[_ValueT]):
824f7f97a7SJohn Snow    """
834f7f97a7SJohn Snow    Annotated generally contains a SchemaInfo-like type (as a dict),
844f7f97a7SJohn Snow    But it also used to wrap comments/ifconds around scalar leaf values,
854f7f97a7SJohn Snow    for the benefit of features and enums.
864f7f97a7SJohn Snow    """
874f7f97a7SJohn Snow    # TODO: Remove after Python 3.7 adds @dataclass:
884f7f97a7SJohn Snow    # pylint: disable=too-few-public-methods
89f17539c8SMarc-André Lureau    def __init__(self, value: _ValueT, ifcond: QAPISchemaIfCond,
904f7f97a7SJohn Snow                 comment: Optional[str] = None):
914f7f97a7SJohn Snow        self.value = value
924f7f97a7SJohn Snow        self.comment: Optional[str] = comment
93f17539c8SMarc-André Lureau        self.ifcond = ifcond
9424cfd6adSMarkus Armbruster
9524cfd6adSMarkus Armbruster
9682b52f6bSJohn Snowdef _tree_to_qlit(obj: JSONValue,
9782b52f6bSJohn Snow                  level: int = 0,
9882b52f6bSJohn Snow                  dict_value: bool = False) -> str:
995444dedfSJohn Snow    """
1005444dedfSJohn Snow    Convert the type tree into a QLIT C string, recursively.
1015444dedfSJohn Snow
1025444dedfSJohn Snow    :param obj: The value to convert.
1035444dedfSJohn Snow                This value may not be Annotated when dict_value is True.
1045444dedfSJohn Snow    :param level: The indentation level for this particular value.
1055444dedfSJohn Snow    :param dict_value: True when the value being processed belongs to a
1065444dedfSJohn Snow                       dict key; which suppresses the output indent.
1075444dedfSJohn Snow    """
1087d0f982bSMarc-André Lureau
10982b52f6bSJohn Snow    def indent(level: int) -> str:
1107d0f982bSMarc-André Lureau        return level * 4 * ' '
1117d0f982bSMarc-André Lureau
1124f7f97a7SJohn Snow    if isinstance(obj, Annotated):
11305556960SJohn Snow        # NB: _tree_to_qlit is called recursively on the values of a
11405556960SJohn Snow        # key:value pair; those values can't be decorated with
11505556960SJohn Snow        # comments or conditionals.
11605556960SJohn Snow        msg = "dict values cannot have attached comments or if-conditionals."
11705556960SJohn Snow        assert not dict_value, msg
11805556960SJohn Snow
1198c643361SEric Blake        ret = ''
1204f7f97a7SJohn Snow        if obj.comment:
121c0e8d9f3SJohn Snow            ret += indent(level) + f"/* {obj.comment} */\n"
12233aa3267SMarc-André Lureau        if obj.ifcond.is_present():
1231889e57aSMarkus Armbruster            ret += obj.ifcond.gen_if()
1244f7f97a7SJohn Snow        ret += _tree_to_qlit(obj.value, level)
12533aa3267SMarc-André Lureau        if obj.ifcond.is_present():
1261889e57aSMarkus Armbruster            ret += '\n' + obj.ifcond.gen_endif()
127d626b6c1SMarc-André Lureau        return ret
128d626b6c1SMarc-André Lureau
1297d0f982bSMarc-André Lureau    ret = ''
13005556960SJohn Snow    if not dict_value:
1317d0f982bSMarc-André Lureau        ret += indent(level)
132c0e8d9f3SJohn Snow
133c0e8d9f3SJohn Snow    # Scalars:
134fb0bc835SMarkus Armbruster    if obj is None:
1357d0f982bSMarc-André Lureau        ret += 'QLIT_QNULL'
136fb0bc835SMarkus Armbruster    elif isinstance(obj, str):
137c0e8d9f3SJohn Snow        ret += f"QLIT_QSTR({to_c_string(obj)})"
138c0e8d9f3SJohn Snow    elif isinstance(obj, bool):
139c0e8d9f3SJohn Snow        ret += f"QLIT_QBOOL({str(obj).lower()})"
140c0e8d9f3SJohn Snow
141c0e8d9f3SJohn Snow    # Non-scalars:
142fb0bc835SMarkus Armbruster    elif isinstance(obj, list):
1437d0f982bSMarc-André Lureau        ret += 'QLIT_QLIST(((QLitObject[]) {\n'
144c0e8d9f3SJohn Snow        for value in obj:
145c0e8d9f3SJohn Snow            ret += _tree_to_qlit(value, level + 1).strip('\n') + '\n'
146c0e8d9f3SJohn Snow        ret += indent(level + 1) + '{}\n'
1477d0f982bSMarc-André Lureau        ret += indent(level) + '}))'
148fb0bc835SMarkus Armbruster    elif isinstance(obj, dict):
1497d0f982bSMarc-André Lureau        ret += 'QLIT_QDICT(((QLitDictEntry[]) {\n'
150c0e8d9f3SJohn Snow        for key, value in sorted(obj.items()):
151c0e8d9f3SJohn Snow            ret += indent(level + 1) + "{{ {:s}, {:s} }},\n".format(
152c0e8d9f3SJohn Snow                to_c_string(key),
153c0e8d9f3SJohn Snow                _tree_to_qlit(value, level + 1, dict_value=True)
154c0e8d9f3SJohn Snow            )
155c0e8d9f3SJohn Snow        ret += indent(level + 1) + '{}\n'
1567d0f982bSMarc-André Lureau        ret += indent(level) + '}))'
157fb0bc835SMarkus Armbruster    else:
1582a6c161bSJohn Snow        raise NotImplementedError(
1592a6c161bSJohn Snow            f"type '{type(obj).__name__}' not implemented"
1602a6c161bSJohn Snow        )
161c0e8d9f3SJohn Snow
16240bb1376SMarc-André Lureau    if level > 0:
16340bb1376SMarc-André Lureau        ret += ','
164fb0bc835SMarkus Armbruster    return ret
165fb0bc835SMarkus Armbruster
166fb0bc835SMarkus Armbruster
16782b52f6bSJohn Snowdef to_c_string(string: str) -> str:
168fb0bc835SMarkus Armbruster    return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"'
169fb0bc835SMarkus Armbruster
170fb0bc835SMarkus Armbruster
17171b3f045SMarkus Armbrusterclass QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor):
172fb0bc835SMarkus Armbruster
17382b52f6bSJohn Snow    def __init__(self, prefix: str, unmask: bool):
1742cae67bcSMarkus Armbruster        super().__init__(
1752cae67bcSMarkus Armbruster            prefix, 'qapi-introspect',
17671b3f045SMarkus Armbruster            ' * QAPI/QMP schema introspection', __doc__)
17771b3f045SMarkus Armbruster        self._unmask = unmask
17882b52f6bSJohn Snow        self._schema: Optional[QAPISchema] = None
17982b52f6bSJohn Snow        self._trees: List[Annotated[SchemaInfo]] = []
18082b52f6bSJohn Snow        self._used_types: List[QAPISchemaType] = []
18182b52f6bSJohn Snow        self._name_map: Dict[str, str] = {}
18271b3f045SMarkus Armbruster        self._genc.add(mcgen('''
18371b3f045SMarkus Armbruster#include "qemu/osdep.h"
184eb815e24SMarkus Armbruster#include "%(prefix)sqapi-introspect.h"
18571b3f045SMarkus Armbruster
18671b3f045SMarkus Armbruster''',
18771b3f045SMarkus Armbruster                             prefix=prefix))
18871b3f045SMarkus Armbruster
18982b52f6bSJohn Snow    def visit_begin(self, schema: QAPISchema) -> None:
19071b3f045SMarkus Armbruster        self._schema = schema
191fb0bc835SMarkus Armbruster
19282b52f6bSJohn Snow    def visit_end(self) -> None:
193fb0bc835SMarkus Armbruster        # visit the types that are actually used
194fb0bc835SMarkus Armbruster        for typ in self._used_types:
195fb0bc835SMarkus Armbruster            typ.visit(self)
196fb0bc835SMarkus Armbruster        # generate C
1977d0f982bSMarc-André Lureau        name = c_name(self._prefix, protect=False) + 'qmp_schema_qlit'
19871b3f045SMarkus Armbruster        self._genh.add(mcgen('''
1997d0f982bSMarc-André Lureau#include "qapi/qmp/qlit.h"
2007d0f982bSMarc-André Lureau
2017d0f982bSMarc-André Lureauextern const QLitObject %(c_name)s;
202fb0bc835SMarkus Armbruster''',
20371b3f045SMarkus Armbruster                             c_name=c_name(name)))
20471b3f045SMarkus Armbruster        self._genc.add(mcgen('''
2057d0f982bSMarc-André Lureauconst QLitObject %(c_name)s = %(c_string)s;
206fb0bc835SMarkus Armbruster''',
207fb0bc835SMarkus Armbruster                             c_name=c_name(name),
2082e8a843dSMarkus Armbruster                             c_string=_tree_to_qlit(self._trees)))
209fb0bc835SMarkus Armbruster        self._schema = None
2102e8a843dSMarkus Armbruster        self._trees = []
21171b3f045SMarkus Armbruster        self._used_types = []
21271b3f045SMarkus Armbruster        self._name_map = {}
213fb0bc835SMarkus Armbruster
21482b52f6bSJohn Snow    def visit_needed(self, entity: QAPISchemaEntity) -> bool:
215fb0bc835SMarkus Armbruster        # Ignore types on first pass; visit_end() will pick up used types
216fb0bc835SMarkus Armbruster        return not isinstance(entity, QAPISchemaType)
217fb0bc835SMarkus Armbruster
21882b52f6bSJohn Snow    def _name(self, name: str) -> str:
219fb0bc835SMarkus Armbruster        if self._unmask:
220fb0bc835SMarkus Armbruster            return name
221fb0bc835SMarkus Armbruster        if name not in self._name_map:
222fb0bc835SMarkus Armbruster            self._name_map[name] = '%d' % len(self._name_map)
223fb0bc835SMarkus Armbruster        return self._name_map[name]
224fb0bc835SMarkus Armbruster
22582b52f6bSJohn Snow    def _use_type(self, typ: QAPISchemaType) -> str:
2266b67bcacSJohn Snow        assert self._schema is not None
2276b67bcacSJohn Snow
228fb0bc835SMarkus Armbruster        # Map the various integer types to plain int
229fb0bc835SMarkus Armbruster        if typ.json_type() == 'int':
230fb0bc835SMarkus Armbruster            typ = self._schema.lookup_type('int')
231fb0bc835SMarkus Armbruster        elif (isinstance(typ, QAPISchemaArrayType) and
232fb0bc835SMarkus Armbruster              typ.element_type.json_type() == 'int'):
233fb0bc835SMarkus Armbruster            typ = self._schema.lookup_type('intList')
234fb0bc835SMarkus Armbruster        # Add type to work queue if new
235fb0bc835SMarkus Armbruster        if typ not in self._used_types:
236fb0bc835SMarkus Armbruster            self._used_types.append(typ)
237fb0bc835SMarkus Armbruster        # Clients should examine commands and events, not types.  Hide
2381aa806ccSEric Blake        # type names as integers to reduce the temptation.  Also, it
2391aa806ccSEric Blake        # saves a few characters on the wire.
240fb0bc835SMarkus Armbruster        if isinstance(typ, QAPISchemaBuiltinType):
241fb0bc835SMarkus Armbruster            return typ.name
242fb0bc835SMarkus Armbruster        if isinstance(typ, QAPISchemaArrayType):
243fb0bc835SMarkus Armbruster            return '[' + self._use_type(typ.element_type) + ']'
244fb0bc835SMarkus Armbruster        return self._name(typ.name)
245fb0bc835SMarkus Armbruster
24684bece7dSJohn Snow    @staticmethod
247cea53c31SJohn Snow    def _gen_features(features: Sequence[QAPISchemaFeature]
24882b52f6bSJohn Snow                      ) -> List[Annotated[str]]:
2494f7f97a7SJohn Snow        return [Annotated(f.name, f.ifcond) for f in features]
25084bece7dSJohn Snow
25182b52f6bSJohn Snow    def _gen_tree(self, name: str, mtype: str, obj: Dict[str, object],
252f17539c8SMarc-André Lureau                  ifcond: QAPISchemaIfCond = QAPISchemaIfCond(),
253cea53c31SJohn Snow                  features: Sequence[QAPISchemaFeature] = ()) -> None:
2545444dedfSJohn Snow        """
2555444dedfSJohn Snow        Build and append a SchemaInfo object to self._trees.
2565444dedfSJohn Snow
2575444dedfSJohn Snow        :param name: The SchemaInfo's name.
2585444dedfSJohn Snow        :param mtype: The SchemaInfo's meta-type.
2595444dedfSJohn Snow        :param obj: Additional SchemaInfo members, as appropriate for
2605444dedfSJohn Snow                    the meta-type.
2615444dedfSJohn Snow        :param ifcond: Conditionals to apply to the SchemaInfo.
2625444dedfSJohn Snow        :param features: The SchemaInfo's features.
2635444dedfSJohn Snow                         Will be omitted from the output if empty.
2645444dedfSJohn Snow        """
2655f50cedeSJohn Snow        comment: Optional[str] = None
266fb0bc835SMarkus Armbruster        if mtype not in ('command', 'event', 'builtin', 'array'):
2678c643361SEric Blake            if not self._unmask:
2688c643361SEric Blake                # Output a comment to make it easy to map masked names
2698c643361SEric Blake                # back to the source when reading the generated output.
2705f50cedeSJohn Snow                comment = f'"{self._name(name)}" = {name}'
271fb0bc835SMarkus Armbruster            name = self._name(name)
272fb0bc835SMarkus Armbruster        obj['name'] = name
273fb0bc835SMarkus Armbruster        obj['meta-type'] = mtype
27484bece7dSJohn Snow        if features:
27584bece7dSJohn Snow            obj['features'] = self._gen_features(features)
2764f7f97a7SJohn Snow        self._trees.append(Annotated(obj, ifcond, comment))
277fb0bc835SMarkus Armbruster
278*b6c18755SMarkus Armbruster    def _gen_enum_member(self, member: QAPISchemaEnumMember
27975ecee72SMarkus Armbruster                         ) -> Annotated[SchemaInfoEnumMember]:
28075ecee72SMarkus Armbruster        obj: SchemaInfoEnumMember = {
28175ecee72SMarkus Armbruster            'name': member.name,
28275ecee72SMarkus Armbruster        }
283*b6c18755SMarkus Armbruster        if member.features:
284*b6c18755SMarkus Armbruster            obj['features'] = self._gen_features(member.features)
28575ecee72SMarkus Armbruster        return Annotated(obj, member.ifcond)
28675ecee72SMarkus Armbruster
28775ecee72SMarkus Armbruster    def _gen_object_member(self, member: QAPISchemaObjectTypeMember
28882b52f6bSJohn Snow                           ) -> Annotated[SchemaInfoObjectMember]:
28982b52f6bSJohn Snow        obj: SchemaInfoObjectMember = {
29082b52f6bSJohn Snow            'name': member.name,
29182b52f6bSJohn Snow            'type': self._use_type(member.type)
29282b52f6bSJohn Snow        }
293fb0bc835SMarkus Armbruster        if member.optional:
29424cfd6adSMarkus Armbruster            obj['default'] = None
29584bece7dSJohn Snow        if member.features:
29684bece7dSJohn Snow            obj['features'] = self._gen_features(member.features)
2974f7f97a7SJohn Snow        return Annotated(obj, member.ifcond)
298fb0bc835SMarkus Armbruster
29982b52f6bSJohn Snow    def _gen_variant(self, variant: QAPISchemaVariant
30082b52f6bSJohn Snow                     ) -> Annotated[SchemaInfoObjectVariant]:
30182b52f6bSJohn Snow        obj: SchemaInfoObjectVariant = {
30282b52f6bSJohn Snow            'case': variant.name,
30382b52f6bSJohn Snow            'type': self._use_type(variant.type)
30482b52f6bSJohn Snow        }
3054f7f97a7SJohn Snow        return Annotated(obj, variant.ifcond)
306fb0bc835SMarkus Armbruster
30782b52f6bSJohn Snow    def visit_builtin_type(self, name: str, info: Optional[QAPISourceInfo],
30882b52f6bSJohn Snow                           json_type: str) -> None:
3099b77d946SJohn Snow        self._gen_tree(name, 'builtin', {'json-type': json_type})
310fb0bc835SMarkus Armbruster
31182b52f6bSJohn Snow    def visit_enum_type(self, name: str, info: Optional[QAPISourceInfo],
312f17539c8SMarc-André Lureau                        ifcond: QAPISchemaIfCond,
31382b52f6bSJohn Snow                        features: List[QAPISchemaFeature],
31482b52f6bSJohn Snow                        members: List[QAPISchemaEnumMember],
31582b52f6bSJohn Snow                        prefix: Optional[str]) -> None:
3164f7f97a7SJohn Snow        self._gen_tree(
3174f7f97a7SJohn Snow            name, 'enum',
31875ecee72SMarkus Armbruster            {'members': [self._gen_enum_member(m) for m in members],
31975ecee72SMarkus Armbruster             'values': [Annotated(m.name, m.ifcond) for m in members]},
3204f7f97a7SJohn Snow            ifcond, features
3214f7f97a7SJohn Snow        )
322fb0bc835SMarkus Armbruster
32382b52f6bSJohn Snow    def visit_array_type(self, name: str, info: Optional[QAPISourceInfo],
324f17539c8SMarc-André Lureau                         ifcond: QAPISchemaIfCond,
32582b52f6bSJohn Snow                         element_type: QAPISchemaType) -> None:
326fb0bc835SMarkus Armbruster        element = self._use_type(element_type)
3272e8a843dSMarkus Armbruster        self._gen_tree('[' + element + ']', 'array', {'element-type': element},
328cea53c31SJohn Snow                       ifcond)
329fb0bc835SMarkus Armbruster
33082b52f6bSJohn Snow    def visit_object_type_flat(self, name: str, info: Optional[QAPISourceInfo],
331f17539c8SMarc-André Lureau                               ifcond: QAPISchemaIfCond,
33282b52f6bSJohn Snow                               features: List[QAPISchemaFeature],
33382b52f6bSJohn Snow                               members: List[QAPISchemaObjectTypeMember],
33482b52f6bSJohn Snow                               variants: Optional[QAPISchemaVariants]) -> None:
33582b52f6bSJohn Snow        obj: SchemaInfoObject = {
33675ecee72SMarkus Armbruster            'members': [self._gen_object_member(m) for m in members]
33782b52f6bSJohn Snow        }
338fb0bc835SMarkus Armbruster        if variants:
339cf5db214SJohn Snow            obj['tag'] = variants.tag_member.name
340cf5db214SJohn Snow            obj['variants'] = [self._gen_variant(v) for v in variants.variants]
3412e8a843dSMarkus Armbruster        self._gen_tree(name, 'object', obj, ifcond, features)
342fb0bc835SMarkus Armbruster
34382b52f6bSJohn Snow    def visit_alternate_type(self, name: str, info: Optional[QAPISourceInfo],
344f17539c8SMarc-André Lureau                             ifcond: QAPISchemaIfCond,
34582b52f6bSJohn Snow                             features: List[QAPISchemaFeature],
34682b52f6bSJohn Snow                             variants: QAPISchemaVariants) -> None:
3474f7f97a7SJohn Snow        self._gen_tree(
3484f7f97a7SJohn Snow            name, 'alternate',
3494f7f97a7SJohn Snow            {'members': [Annotated({'type': self._use_type(m.type)},
3504f7f97a7SJohn Snow                                   m.ifcond)
351013b4efcSMarkus Armbruster                         for m in variants.variants]},
3524f7f97a7SJohn Snow            ifcond, features
3534f7f97a7SJohn Snow        )
354fb0bc835SMarkus Armbruster
35582b52f6bSJohn Snow    def visit_command(self, name: str, info: Optional[QAPISourceInfo],
356f17539c8SMarc-André Lureau                      ifcond: QAPISchemaIfCond,
35782b52f6bSJohn Snow                      features: List[QAPISchemaFeature],
35882b52f6bSJohn Snow                      arg_type: Optional[QAPISchemaObjectType],
35982b52f6bSJohn Snow                      ret_type: Optional[QAPISchemaType], gen: bool,
36082b52f6bSJohn Snow                      success_response: bool, boxed: bool, allow_oob: bool,
36182b52f6bSJohn Snow                      allow_preconfig: bool, coroutine: bool) -> None:
3626b67bcacSJohn Snow        assert self._schema is not None
3636b67bcacSJohn Snow
364fb0bc835SMarkus Armbruster        arg_type = arg_type or self._schema.the_empty_object_type
365fb0bc835SMarkus Armbruster        ret_type = ret_type or self._schema.the_empty_object_type
36682b52f6bSJohn Snow        obj: SchemaInfoCommand = {
36782b52f6bSJohn Snow            'arg-type': self._use_type(arg_type),
36882b52f6bSJohn Snow            'ret-type': self._use_type(ret_type)
36982b52f6bSJohn Snow        }
37025b1ef31SMarkus Armbruster        if allow_oob:
37125b1ef31SMarkus Armbruster            obj['allow-oob'] = allow_oob
3722e8a843dSMarkus Armbruster        self._gen_tree(name, 'command', obj, ifcond, features)
37323394b4cSPeter Krempa
37482b52f6bSJohn Snow    def visit_event(self, name: str, info: Optional[QAPISourceInfo],
375f17539c8SMarc-André Lureau                    ifcond: QAPISchemaIfCond,
376f17539c8SMarc-André Lureau                    features: List[QAPISchemaFeature],
37782b52f6bSJohn Snow                    arg_type: Optional[QAPISchemaObjectType],
37882b52f6bSJohn Snow                    boxed: bool) -> None:
3796b67bcacSJohn Snow        assert self._schema is not None
38082b52f6bSJohn Snow
381fb0bc835SMarkus Armbruster        arg_type = arg_type or self._schema.the_empty_object_type
3822e8a843dSMarkus Armbruster        self._gen_tree(name, 'event', {'arg-type': self._use_type(arg_type)},
383013b4efcSMarkus Armbruster                       ifcond, features)
384fb0bc835SMarkus Armbruster
385fb0bc835SMarkus Armbruster
38682b52f6bSJohn Snowdef gen_introspect(schema: QAPISchema, output_dir: str, prefix: str,
38782b52f6bSJohn Snow                   opt_unmask: bool) -> None:
388fb0bc835SMarkus Armbruster    vis = QAPISchemaGenIntrospectVisitor(prefix, opt_unmask)
389fb0bc835SMarkus Armbruster    schema.visit(vis)
39071b3f045SMarkus Armbruster    vis.write(output_dir)
391