xref: /qemu/scripts/qapi/introspect.py (revision cf26906c)
1fb0bc835SMarkus Armbruster"""
2fb0bc835SMarkus ArmbrusterQAPI introspection generator
3fb0bc835SMarkus Armbruster
4*cf26906cSJohn SnowCopyright (C) 2015-2021 Red Hat, Inc.
5fb0bc835SMarkus Armbruster
6fb0bc835SMarkus ArmbrusterAuthors:
7fb0bc835SMarkus Armbruster Markus Armbruster <armbru@redhat.com>
8*cf26906cSJohn 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,
184f7f97a7SJohn Snow    Iterable,
199db27346SJohn Snow    List,
209db27346SJohn Snow    Optional,
2182b52f6bSJohn Snow    Sequence,
224f7f97a7SJohn Snow    Tuple,
234f7f97a7SJohn Snow    TypeVar,
249db27346SJohn Snow    Union,
259db27346SJohn Snow)
265f50cedeSJohn Snow
275af8263dSJohn Snowfrom .common import (
285af8263dSJohn Snow    c_name,
295af8263dSJohn Snow    gen_endif,
305af8263dSJohn Snow    gen_if,
315af8263dSJohn Snow    mcgen,
325af8263dSJohn Snow)
337137a960SJohn Snowfrom .gen import QAPISchemaMonolithicCVisitor
3467fea575SJohn Snowfrom .schema import (
3582b52f6bSJohn Snow    QAPISchema,
3667fea575SJohn Snow    QAPISchemaArrayType,
3767fea575SJohn Snow    QAPISchemaBuiltinType,
3882b52f6bSJohn Snow    QAPISchemaEntity,
3982b52f6bSJohn Snow    QAPISchemaEnumMember,
4082b52f6bSJohn Snow    QAPISchemaFeature,
4182b52f6bSJohn Snow    QAPISchemaObjectType,
4282b52f6bSJohn Snow    QAPISchemaObjectTypeMember,
4367fea575SJohn Snow    QAPISchemaType,
4482b52f6bSJohn Snow    QAPISchemaVariant,
4582b52f6bSJohn Snow    QAPISchemaVariants,
4667fea575SJohn Snow)
4782b52f6bSJohn Snowfrom .source import QAPISourceInfo
48fb0bc835SMarkus Armbruster
49fb0bc835SMarkus Armbruster
509db27346SJohn Snow# This module constructs a tree data structure that is used to
519db27346SJohn Snow# generate the introspection information for QEMU. It is shaped
529db27346SJohn Snow# like a JSON value.
539db27346SJohn Snow#
549db27346SJohn Snow# A complexity over JSON is that our values may or may not be annotated.
559db27346SJohn Snow#
569db27346SJohn Snow# Un-annotated values may be:
579db27346SJohn Snow#     Scalar: str, bool, None.
589db27346SJohn Snow#     Non-scalar: List, Dict
599db27346SJohn Snow# _value = Union[str, bool, None, Dict[str, JSONValue], List[JSONValue]]
609db27346SJohn Snow#
619db27346SJohn Snow# With optional annotations, the type of all values is:
629db27346SJohn Snow# JSONValue = Union[_Value, Annotated[_Value]]
639db27346SJohn Snow#
649db27346SJohn Snow# Sadly, mypy does not support recursive types; so the _Stub alias is used to
659db27346SJohn Snow# mark the imprecision in the type model where we'd otherwise use JSONValue.
669db27346SJohn Snow_Stub = Any
679db27346SJohn Snow_Scalar = Union[str, bool, None]
689db27346SJohn Snow_NonScalar = Union[Dict[str, _Stub], List[_Stub]]
699db27346SJohn Snow_Value = Union[_Scalar, _NonScalar]
704f7f97a7SJohn SnowJSONValue = Union[_Value, 'Annotated[_Value]']
719db27346SJohn Snow
7282b52f6bSJohn Snow# These types are based on structures defined in QEMU's schema, so we
7382b52f6bSJohn Snow# lack precise types for them here. Python 3.6 does not offer
7482b52f6bSJohn Snow# TypedDict constructs, so they are broadly typed here as simple
7582b52f6bSJohn Snow# Python Dicts.
7682b52f6bSJohn SnowSchemaInfo = Dict[str, object]
7782b52f6bSJohn SnowSchemaInfoObject = Dict[str, object]
7882b52f6bSJohn SnowSchemaInfoObjectVariant = Dict[str, object]
7982b52f6bSJohn SnowSchemaInfoObjectMember = Dict[str, object]
8082b52f6bSJohn SnowSchemaInfoCommand = Dict[str, object]
8182b52f6bSJohn Snow
829db27346SJohn Snow
834f7f97a7SJohn Snow_ValueT = TypeVar('_ValueT', bound=_Value)
844f7f97a7SJohn Snow
854f7f97a7SJohn Snow
864f7f97a7SJohn Snowclass Annotated(Generic[_ValueT]):
874f7f97a7SJohn Snow    """
884f7f97a7SJohn Snow    Annotated generally contains a SchemaInfo-like type (as a dict),
894f7f97a7SJohn Snow    But it also used to wrap comments/ifconds around scalar leaf values,
904f7f97a7SJohn Snow    for the benefit of features and enums.
914f7f97a7SJohn Snow    """
924f7f97a7SJohn Snow    # TODO: Remove after Python 3.7 adds @dataclass:
934f7f97a7SJohn Snow    # pylint: disable=too-few-public-methods
944f7f97a7SJohn Snow    def __init__(self, value: _ValueT, ifcond: Iterable[str],
954f7f97a7SJohn Snow                 comment: Optional[str] = None):
964f7f97a7SJohn Snow        self.value = value
974f7f97a7SJohn Snow        self.comment: Optional[str] = comment
984f7f97a7SJohn Snow        self.ifcond: Tuple[str, ...] = tuple(ifcond)
9924cfd6adSMarkus Armbruster
10024cfd6adSMarkus Armbruster
10182b52f6bSJohn Snowdef _tree_to_qlit(obj: JSONValue,
10282b52f6bSJohn Snow                  level: int = 0,
10382b52f6bSJohn Snow                  dict_value: bool = False) -> str:
1045444dedfSJohn Snow    """
1055444dedfSJohn Snow    Convert the type tree into a QLIT C string, recursively.
1065444dedfSJohn Snow
1075444dedfSJohn Snow    :param obj: The value to convert.
1085444dedfSJohn Snow                This value may not be Annotated when dict_value is True.
1095444dedfSJohn Snow    :param level: The indentation level for this particular value.
1105444dedfSJohn Snow    :param dict_value: True when the value being processed belongs to a
1115444dedfSJohn Snow                       dict key; which suppresses the output indent.
1125444dedfSJohn Snow    """
1137d0f982bSMarc-André Lureau
11482b52f6bSJohn Snow    def indent(level: int) -> str:
1157d0f982bSMarc-André Lureau        return level * 4 * ' '
1167d0f982bSMarc-André Lureau
1174f7f97a7SJohn Snow    if isinstance(obj, Annotated):
11805556960SJohn Snow        # NB: _tree_to_qlit is called recursively on the values of a
11905556960SJohn Snow        # key:value pair; those values can't be decorated with
12005556960SJohn Snow        # comments or conditionals.
12105556960SJohn Snow        msg = "dict values cannot have attached comments or if-conditionals."
12205556960SJohn Snow        assert not dict_value, msg
12305556960SJohn Snow
1248c643361SEric Blake        ret = ''
1254f7f97a7SJohn Snow        if obj.comment:
126c0e8d9f3SJohn Snow            ret += indent(level) + f"/* {obj.comment} */\n"
1274f7f97a7SJohn Snow        if obj.ifcond:
1284f7f97a7SJohn Snow            ret += gen_if(obj.ifcond)
1294f7f97a7SJohn Snow        ret += _tree_to_qlit(obj.value, level)
1304f7f97a7SJohn Snow        if obj.ifcond:
1314f7f97a7SJohn Snow            ret += '\n' + gen_endif(obj.ifcond)
132d626b6c1SMarc-André Lureau        return ret
133d626b6c1SMarc-André Lureau
1347d0f982bSMarc-André Lureau    ret = ''
13505556960SJohn Snow    if not dict_value:
1367d0f982bSMarc-André Lureau        ret += indent(level)
137c0e8d9f3SJohn Snow
138c0e8d9f3SJohn Snow    # Scalars:
139fb0bc835SMarkus Armbruster    if obj is None:
1407d0f982bSMarc-André Lureau        ret += 'QLIT_QNULL'
141fb0bc835SMarkus Armbruster    elif isinstance(obj, str):
142c0e8d9f3SJohn Snow        ret += f"QLIT_QSTR({to_c_string(obj)})"
143c0e8d9f3SJohn Snow    elif isinstance(obj, bool):
144c0e8d9f3SJohn Snow        ret += f"QLIT_QBOOL({str(obj).lower()})"
145c0e8d9f3SJohn Snow
146c0e8d9f3SJohn Snow    # Non-scalars:
147fb0bc835SMarkus Armbruster    elif isinstance(obj, list):
1487d0f982bSMarc-André Lureau        ret += 'QLIT_QLIST(((QLitObject[]) {\n'
149c0e8d9f3SJohn Snow        for value in obj:
150c0e8d9f3SJohn Snow            ret += _tree_to_qlit(value, level + 1).strip('\n') + '\n'
151c0e8d9f3SJohn Snow        ret += indent(level + 1) + '{}\n'
1527d0f982bSMarc-André Lureau        ret += indent(level) + '}))'
153fb0bc835SMarkus Armbruster    elif isinstance(obj, dict):
1547d0f982bSMarc-André Lureau        ret += 'QLIT_QDICT(((QLitDictEntry[]) {\n'
155c0e8d9f3SJohn Snow        for key, value in sorted(obj.items()):
156c0e8d9f3SJohn Snow            ret += indent(level + 1) + "{{ {:s}, {:s} }},\n".format(
157c0e8d9f3SJohn Snow                to_c_string(key),
158c0e8d9f3SJohn Snow                _tree_to_qlit(value, level + 1, dict_value=True)
159c0e8d9f3SJohn Snow            )
160c0e8d9f3SJohn Snow        ret += indent(level + 1) + '{}\n'
1617d0f982bSMarc-André Lureau        ret += indent(level) + '}))'
162fb0bc835SMarkus Armbruster    else:
1632a6c161bSJohn Snow        raise NotImplementedError(
1642a6c161bSJohn Snow            f"type '{type(obj).__name__}' not implemented"
1652a6c161bSJohn Snow        )
166c0e8d9f3SJohn Snow
16740bb1376SMarc-André Lureau    if level > 0:
16840bb1376SMarc-André Lureau        ret += ','
169fb0bc835SMarkus Armbruster    return ret
170fb0bc835SMarkus Armbruster
171fb0bc835SMarkus Armbruster
17282b52f6bSJohn Snowdef to_c_string(string: str) -> str:
173fb0bc835SMarkus Armbruster    return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"'
174fb0bc835SMarkus Armbruster
175fb0bc835SMarkus Armbruster
17671b3f045SMarkus Armbrusterclass QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor):
177fb0bc835SMarkus Armbruster
17882b52f6bSJohn Snow    def __init__(self, prefix: str, unmask: bool):
1792cae67bcSMarkus Armbruster        super().__init__(
1802cae67bcSMarkus Armbruster            prefix, 'qapi-introspect',
18171b3f045SMarkus Armbruster            ' * QAPI/QMP schema introspection', __doc__)
18271b3f045SMarkus Armbruster        self._unmask = unmask
18382b52f6bSJohn Snow        self._schema: Optional[QAPISchema] = None
18482b52f6bSJohn Snow        self._trees: List[Annotated[SchemaInfo]] = []
18582b52f6bSJohn Snow        self._used_types: List[QAPISchemaType] = []
18682b52f6bSJohn Snow        self._name_map: Dict[str, str] = {}
18771b3f045SMarkus Armbruster        self._genc.add(mcgen('''
18871b3f045SMarkus Armbruster#include "qemu/osdep.h"
189eb815e24SMarkus Armbruster#include "%(prefix)sqapi-introspect.h"
19071b3f045SMarkus Armbruster
19171b3f045SMarkus Armbruster''',
19271b3f045SMarkus Armbruster                             prefix=prefix))
19371b3f045SMarkus Armbruster
19482b52f6bSJohn Snow    def visit_begin(self, schema: QAPISchema) -> None:
19571b3f045SMarkus Armbruster        self._schema = schema
196fb0bc835SMarkus Armbruster
19782b52f6bSJohn Snow    def visit_end(self) -> None:
198fb0bc835SMarkus Armbruster        # visit the types that are actually used
199fb0bc835SMarkus Armbruster        for typ in self._used_types:
200fb0bc835SMarkus Armbruster            typ.visit(self)
201fb0bc835SMarkus Armbruster        # generate C
2027d0f982bSMarc-André Lureau        name = c_name(self._prefix, protect=False) + 'qmp_schema_qlit'
20371b3f045SMarkus Armbruster        self._genh.add(mcgen('''
2047d0f982bSMarc-André Lureau#include "qapi/qmp/qlit.h"
2057d0f982bSMarc-André Lureau
2067d0f982bSMarc-André Lureauextern const QLitObject %(c_name)s;
207fb0bc835SMarkus Armbruster''',
20871b3f045SMarkus Armbruster                             c_name=c_name(name)))
20971b3f045SMarkus Armbruster        self._genc.add(mcgen('''
2107d0f982bSMarc-André Lureauconst QLitObject %(c_name)s = %(c_string)s;
211fb0bc835SMarkus Armbruster''',
212fb0bc835SMarkus Armbruster                             c_name=c_name(name),
2132e8a843dSMarkus Armbruster                             c_string=_tree_to_qlit(self._trees)))
214fb0bc835SMarkus Armbruster        self._schema = None
2152e8a843dSMarkus Armbruster        self._trees = []
21671b3f045SMarkus Armbruster        self._used_types = []
21771b3f045SMarkus Armbruster        self._name_map = {}
218fb0bc835SMarkus Armbruster
21982b52f6bSJohn Snow    def visit_needed(self, entity: QAPISchemaEntity) -> bool:
220fb0bc835SMarkus Armbruster        # Ignore types on first pass; visit_end() will pick up used types
221fb0bc835SMarkus Armbruster        return not isinstance(entity, QAPISchemaType)
222fb0bc835SMarkus Armbruster
22382b52f6bSJohn Snow    def _name(self, name: str) -> str:
224fb0bc835SMarkus Armbruster        if self._unmask:
225fb0bc835SMarkus Armbruster            return name
226fb0bc835SMarkus Armbruster        if name not in self._name_map:
227fb0bc835SMarkus Armbruster            self._name_map[name] = '%d' % len(self._name_map)
228fb0bc835SMarkus Armbruster        return self._name_map[name]
229fb0bc835SMarkus Armbruster
23082b52f6bSJohn Snow    def _use_type(self, typ: QAPISchemaType) -> str:
2316b67bcacSJohn Snow        assert self._schema is not None
2326b67bcacSJohn Snow
233fb0bc835SMarkus Armbruster        # Map the various integer types to plain int
234fb0bc835SMarkus Armbruster        if typ.json_type() == 'int':
235fb0bc835SMarkus Armbruster            typ = self._schema.lookup_type('int')
236fb0bc835SMarkus Armbruster        elif (isinstance(typ, QAPISchemaArrayType) and
237fb0bc835SMarkus Armbruster              typ.element_type.json_type() == 'int'):
238fb0bc835SMarkus Armbruster            typ = self._schema.lookup_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
25282b52f6bSJohn Snow    def _gen_features(features: List[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],
25782b52f6bSJohn Snow                  ifcond: Sequence[str],
25882b52f6bSJohn Snow                  features: Optional[List[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
28382b52f6bSJohn Snow    def _gen_member(self, member: QAPISchemaObjectTypeMember
28482b52f6bSJohn Snow                    ) -> Annotated[SchemaInfoObjectMember]:
28582b52f6bSJohn Snow        obj: SchemaInfoObjectMember = {
28682b52f6bSJohn Snow            'name': member.name,
28782b52f6bSJohn Snow            'type': self._use_type(member.type)
28882b52f6bSJohn Snow        }
289fb0bc835SMarkus Armbruster        if member.optional:
29024cfd6adSMarkus Armbruster            obj['default'] = None
29184bece7dSJohn Snow        if member.features:
29284bece7dSJohn Snow            obj['features'] = self._gen_features(member.features)
2934f7f97a7SJohn Snow        return Annotated(obj, member.ifcond)
294fb0bc835SMarkus Armbruster
29582b52f6bSJohn Snow    def _gen_variant(self, variant: QAPISchemaVariant
29682b52f6bSJohn Snow                     ) -> Annotated[SchemaInfoObjectVariant]:
29782b52f6bSJohn Snow        obj: SchemaInfoObjectVariant = {
29882b52f6bSJohn Snow            'case': variant.name,
29982b52f6bSJohn Snow            'type': self._use_type(variant.type)
30082b52f6bSJohn Snow        }
3014f7f97a7SJohn Snow        return Annotated(obj, variant.ifcond)
302fb0bc835SMarkus Armbruster
30382b52f6bSJohn Snow    def visit_builtin_type(self, name: str, info: Optional[QAPISourceInfo],
30482b52f6bSJohn Snow                           json_type: str) -> None:
3052e8a843dSMarkus Armbruster        self._gen_tree(name, 'builtin', {'json-type': json_type}, [], None)
306fb0bc835SMarkus Armbruster
30782b52f6bSJohn Snow    def visit_enum_type(self, name: str, info: Optional[QAPISourceInfo],
30882b52f6bSJohn Snow                        ifcond: Sequence[str],
30982b52f6bSJohn Snow                        features: List[QAPISchemaFeature],
31082b52f6bSJohn Snow                        members: List[QAPISchemaEnumMember],
31182b52f6bSJohn Snow                        prefix: Optional[str]) -> None:
3124f7f97a7SJohn Snow        self._gen_tree(
3134f7f97a7SJohn Snow            name, 'enum',
3144f7f97a7SJohn Snow            {'values': [Annotated(m.name, m.ifcond) for m in members]},
3154f7f97a7SJohn Snow            ifcond, features
3164f7f97a7SJohn Snow        )
317fb0bc835SMarkus Armbruster
31882b52f6bSJohn Snow    def visit_array_type(self, name: str, info: Optional[QAPISourceInfo],
31982b52f6bSJohn Snow                         ifcond: Sequence[str],
32082b52f6bSJohn Snow                         element_type: QAPISchemaType) -> None:
321fb0bc835SMarkus Armbruster        element = self._use_type(element_type)
3222e8a843dSMarkus Armbruster        self._gen_tree('[' + element + ']', 'array', {'element-type': element},
323013b4efcSMarkus Armbruster                       ifcond, None)
324fb0bc835SMarkus Armbruster
32582b52f6bSJohn Snow    def visit_object_type_flat(self, name: str, info: Optional[QAPISourceInfo],
32682b52f6bSJohn Snow                               ifcond: Sequence[str],
32782b52f6bSJohn Snow                               features: List[QAPISchemaFeature],
32882b52f6bSJohn Snow                               members: List[QAPISchemaObjectTypeMember],
32982b52f6bSJohn Snow                               variants: Optional[QAPISchemaVariants]) -> None:
33082b52f6bSJohn Snow        obj: SchemaInfoObject = {
33182b52f6bSJohn Snow            'members': [self._gen_member(m) for m in members]
33282b52f6bSJohn Snow        }
333fb0bc835SMarkus Armbruster        if variants:
334cf5db214SJohn Snow            obj['tag'] = variants.tag_member.name
335cf5db214SJohn Snow            obj['variants'] = [self._gen_variant(v) for v in variants.variants]
3362e8a843dSMarkus Armbruster        self._gen_tree(name, 'object', obj, ifcond, features)
337fb0bc835SMarkus Armbruster
33882b52f6bSJohn Snow    def visit_alternate_type(self, name: str, info: Optional[QAPISourceInfo],
33982b52f6bSJohn Snow                             ifcond: Sequence[str],
34082b52f6bSJohn Snow                             features: List[QAPISchemaFeature],
34182b52f6bSJohn Snow                             variants: QAPISchemaVariants) -> None:
3424f7f97a7SJohn Snow        self._gen_tree(
3434f7f97a7SJohn Snow            name, 'alternate',
3444f7f97a7SJohn Snow            {'members': [Annotated({'type': self._use_type(m.type)},
3454f7f97a7SJohn Snow                                   m.ifcond)
346013b4efcSMarkus Armbruster                         for m in variants.variants]},
3474f7f97a7SJohn Snow            ifcond, features
3484f7f97a7SJohn Snow        )
349fb0bc835SMarkus Armbruster
35082b52f6bSJohn Snow    def visit_command(self, name: str, info: Optional[QAPISourceInfo],
35182b52f6bSJohn Snow                      ifcond: Sequence[str],
35282b52f6bSJohn Snow                      features: List[QAPISchemaFeature],
35382b52f6bSJohn Snow                      arg_type: Optional[QAPISchemaObjectType],
35482b52f6bSJohn Snow                      ret_type: Optional[QAPISchemaType], gen: bool,
35582b52f6bSJohn Snow                      success_response: bool, boxed: bool, allow_oob: bool,
35682b52f6bSJohn Snow                      allow_preconfig: bool, coroutine: bool) -> None:
3576b67bcacSJohn Snow        assert self._schema is not None
3586b67bcacSJohn Snow
359fb0bc835SMarkus Armbruster        arg_type = arg_type or self._schema.the_empty_object_type
360fb0bc835SMarkus Armbruster        ret_type = ret_type or self._schema.the_empty_object_type
36182b52f6bSJohn Snow        obj: SchemaInfoCommand = {
36282b52f6bSJohn Snow            'arg-type': self._use_type(arg_type),
36382b52f6bSJohn Snow            'ret-type': self._use_type(ret_type)
36482b52f6bSJohn Snow        }
36525b1ef31SMarkus Armbruster        if allow_oob:
36625b1ef31SMarkus Armbruster            obj['allow-oob'] = allow_oob
3672e8a843dSMarkus Armbruster        self._gen_tree(name, 'command', obj, ifcond, features)
36823394b4cSPeter Krempa
36982b52f6bSJohn Snow    def visit_event(self, name: str, info: Optional[QAPISourceInfo],
37082b52f6bSJohn Snow                    ifcond: Sequence[str], features: List[QAPISchemaFeature],
37182b52f6bSJohn Snow                    arg_type: Optional[QAPISchemaObjectType],
37282b52f6bSJohn Snow                    boxed: bool) -> None:
3736b67bcacSJohn Snow        assert self._schema is not None
37482b52f6bSJohn Snow
375fb0bc835SMarkus Armbruster        arg_type = arg_type or self._schema.the_empty_object_type
3762e8a843dSMarkus Armbruster        self._gen_tree(name, 'event', {'arg-type': self._use_type(arg_type)},
377013b4efcSMarkus Armbruster                       ifcond, features)
378fb0bc835SMarkus Armbruster
379fb0bc835SMarkus Armbruster
38082b52f6bSJohn Snowdef gen_introspect(schema: QAPISchema, output_dir: str, prefix: str,
38182b52f6bSJohn Snow                   opt_unmask: bool) -> None:
382fb0bc835SMarkus Armbruster    vis = QAPISchemaGenIntrospectVisitor(prefix, opt_unmask)
383fb0bc835SMarkus Armbruster    schema.visit(vis)
38471b3f045SMarkus Armbruster    vis.write(output_dir)
385