xref: /qemu/scripts/qapi/introspect.py (revision 5444dedf)
1fb0bc835SMarkus Armbruster"""
2fb0bc835SMarkus ArmbrusterQAPI introspection generator
3fb0bc835SMarkus Armbruster
4fb0bc835SMarkus ArmbrusterCopyright (C) 2015-2018 Red Hat, Inc.
5fb0bc835SMarkus Armbruster
6fb0bc835SMarkus ArmbrusterAuthors:
7fb0bc835SMarkus Armbruster Markus Armbruster <armbru@redhat.com>
8fb0bc835SMarkus Armbruster
9fb0bc835SMarkus ArmbrusterThis work is licensed under the terms of the GNU GPL, version 2.
10fb0bc835SMarkus ArmbrusterSee the COPYING file in the top-level directory.
11fb0bc835SMarkus Armbruster"""
12fb0bc835SMarkus Armbruster
139db27346SJohn Snowfrom typing import (
149db27346SJohn Snow    Any,
159db27346SJohn Snow    Dict,
164f7f97a7SJohn Snow    Generic,
174f7f97a7SJohn Snow    Iterable,
189db27346SJohn Snow    List,
199db27346SJohn Snow    Optional,
2082b52f6bSJohn Snow    Sequence,
214f7f97a7SJohn Snow    Tuple,
224f7f97a7SJohn Snow    TypeVar,
239db27346SJohn Snow    Union,
249db27346SJohn Snow)
255f50cedeSJohn Snow
265af8263dSJohn Snowfrom .common import (
275af8263dSJohn Snow    c_name,
285af8263dSJohn Snow    gen_endif,
295af8263dSJohn Snow    gen_if,
305af8263dSJohn Snow    mcgen,
315af8263dSJohn Snow)
327137a960SJohn Snowfrom .gen import QAPISchemaMonolithicCVisitor
3367fea575SJohn Snowfrom .schema import (
3482b52f6bSJohn Snow    QAPISchema,
3567fea575SJohn Snow    QAPISchemaArrayType,
3667fea575SJohn Snow    QAPISchemaBuiltinType,
3782b52f6bSJohn Snow    QAPISchemaEntity,
3882b52f6bSJohn Snow    QAPISchemaEnumMember,
3982b52f6bSJohn Snow    QAPISchemaFeature,
4082b52f6bSJohn Snow    QAPISchemaObjectType,
4182b52f6bSJohn Snow    QAPISchemaObjectTypeMember,
4267fea575SJohn Snow    QAPISchemaType,
4382b52f6bSJohn Snow    QAPISchemaVariant,
4482b52f6bSJohn Snow    QAPISchemaVariants,
4567fea575SJohn Snow)
4682b52f6bSJohn Snowfrom .source import QAPISourceInfo
47fb0bc835SMarkus Armbruster
48fb0bc835SMarkus Armbruster
499db27346SJohn Snow# This module constructs a tree data structure that is used to
509db27346SJohn Snow# generate the introspection information for QEMU. It is shaped
519db27346SJohn Snow# like a JSON value.
529db27346SJohn Snow#
539db27346SJohn Snow# A complexity over JSON is that our values may or may not be annotated.
549db27346SJohn Snow#
559db27346SJohn Snow# Un-annotated values may be:
569db27346SJohn Snow#     Scalar: str, bool, None.
579db27346SJohn Snow#     Non-scalar: List, Dict
589db27346SJohn Snow# _value = Union[str, bool, None, Dict[str, JSONValue], List[JSONValue]]
599db27346SJohn Snow#
609db27346SJohn Snow# With optional annotations, the type of all values is:
619db27346SJohn Snow# JSONValue = Union[_Value, Annotated[_Value]]
629db27346SJohn Snow#
639db27346SJohn Snow# Sadly, mypy does not support recursive types; so the _Stub alias is used to
649db27346SJohn Snow# mark the imprecision in the type model where we'd otherwise use JSONValue.
659db27346SJohn Snow_Stub = Any
669db27346SJohn Snow_Scalar = Union[str, bool, None]
679db27346SJohn Snow_NonScalar = Union[Dict[str, _Stub], List[_Stub]]
689db27346SJohn Snow_Value = Union[_Scalar, _NonScalar]
694f7f97a7SJohn SnowJSONValue = Union[_Value, 'Annotated[_Value]']
709db27346SJohn Snow
7182b52f6bSJohn Snow# These types are based on structures defined in QEMU's schema, so we
7282b52f6bSJohn Snow# lack precise types for them here. Python 3.6 does not offer
7382b52f6bSJohn Snow# TypedDict constructs, so they are broadly typed here as simple
7482b52f6bSJohn Snow# Python Dicts.
7582b52f6bSJohn SnowSchemaInfo = Dict[str, object]
7682b52f6bSJohn SnowSchemaInfoObject = Dict[str, object]
7782b52f6bSJohn SnowSchemaInfoObjectVariant = Dict[str, object]
7882b52f6bSJohn SnowSchemaInfoObjectMember = Dict[str, object]
7982b52f6bSJohn SnowSchemaInfoCommand = Dict[str, object]
8082b52f6bSJohn Snow
819db27346SJohn Snow
824f7f97a7SJohn Snow_ValueT = TypeVar('_ValueT', bound=_Value)
834f7f97a7SJohn Snow
844f7f97a7SJohn Snow
854f7f97a7SJohn Snowclass Annotated(Generic[_ValueT]):
864f7f97a7SJohn Snow    """
874f7f97a7SJohn Snow    Annotated generally contains a SchemaInfo-like type (as a dict),
884f7f97a7SJohn Snow    But it also used to wrap comments/ifconds around scalar leaf values,
894f7f97a7SJohn Snow    for the benefit of features and enums.
904f7f97a7SJohn Snow    """
914f7f97a7SJohn Snow    # TODO: Remove after Python 3.7 adds @dataclass:
924f7f97a7SJohn Snow    # pylint: disable=too-few-public-methods
934f7f97a7SJohn Snow    def __init__(self, value: _ValueT, ifcond: Iterable[str],
944f7f97a7SJohn Snow                 comment: Optional[str] = None):
954f7f97a7SJohn Snow        self.value = value
964f7f97a7SJohn Snow        self.comment: Optional[str] = comment
974f7f97a7SJohn Snow        self.ifcond: Tuple[str, ...] = tuple(ifcond)
9824cfd6adSMarkus Armbruster
9924cfd6adSMarkus Armbruster
10082b52f6bSJohn Snowdef _tree_to_qlit(obj: JSONValue,
10182b52f6bSJohn Snow                  level: int = 0,
10282b52f6bSJohn Snow                  dict_value: bool = False) -> str:
103*5444dedfSJohn Snow    """
104*5444dedfSJohn Snow    Convert the type tree into a QLIT C string, recursively.
105*5444dedfSJohn Snow
106*5444dedfSJohn Snow    :param obj: The value to convert.
107*5444dedfSJohn Snow                This value may not be Annotated when dict_value is True.
108*5444dedfSJohn Snow    :param level: The indentation level for this particular value.
109*5444dedfSJohn Snow    :param dict_value: True when the value being processed belongs to a
110*5444dedfSJohn Snow                       dict key; which suppresses the output indent.
111*5444dedfSJohn Snow    """
1127d0f982bSMarc-André Lureau
11382b52f6bSJohn Snow    def indent(level: int) -> str:
1147d0f982bSMarc-André Lureau        return level * 4 * ' '
1157d0f982bSMarc-André Lureau
1164f7f97a7SJohn Snow    if isinstance(obj, Annotated):
11705556960SJohn Snow        # NB: _tree_to_qlit is called recursively on the values of a
11805556960SJohn Snow        # key:value pair; those values can't be decorated with
11905556960SJohn Snow        # comments or conditionals.
12005556960SJohn Snow        msg = "dict values cannot have attached comments or if-conditionals."
12105556960SJohn Snow        assert not dict_value, msg
12205556960SJohn Snow
1238c643361SEric Blake        ret = ''
1244f7f97a7SJohn Snow        if obj.comment:
125c0e8d9f3SJohn Snow            ret += indent(level) + f"/* {obj.comment} */\n"
1264f7f97a7SJohn Snow        if obj.ifcond:
1274f7f97a7SJohn Snow            ret += gen_if(obj.ifcond)
1284f7f97a7SJohn Snow        ret += _tree_to_qlit(obj.value, level)
1294f7f97a7SJohn Snow        if obj.ifcond:
1304f7f97a7SJohn Snow            ret += '\n' + gen_endif(obj.ifcond)
131d626b6c1SMarc-André Lureau        return ret
132d626b6c1SMarc-André Lureau
1337d0f982bSMarc-André Lureau    ret = ''
13405556960SJohn Snow    if not dict_value:
1357d0f982bSMarc-André Lureau        ret += indent(level)
136c0e8d9f3SJohn Snow
137c0e8d9f3SJohn Snow    # Scalars:
138fb0bc835SMarkus Armbruster    if obj is None:
1397d0f982bSMarc-André Lureau        ret += 'QLIT_QNULL'
140fb0bc835SMarkus Armbruster    elif isinstance(obj, str):
141c0e8d9f3SJohn Snow        ret += f"QLIT_QSTR({to_c_string(obj)})"
142c0e8d9f3SJohn Snow    elif isinstance(obj, bool):
143c0e8d9f3SJohn Snow        ret += f"QLIT_QBOOL({str(obj).lower()})"
144c0e8d9f3SJohn Snow
145c0e8d9f3SJohn Snow    # Non-scalars:
146fb0bc835SMarkus Armbruster    elif isinstance(obj, list):
1477d0f982bSMarc-André Lureau        ret += 'QLIT_QLIST(((QLitObject[]) {\n'
148c0e8d9f3SJohn Snow        for value in obj:
149c0e8d9f3SJohn Snow            ret += _tree_to_qlit(value, level + 1).strip('\n') + '\n'
150c0e8d9f3SJohn Snow        ret += indent(level + 1) + '{}\n'
1517d0f982bSMarc-André Lureau        ret += indent(level) + '}))'
152fb0bc835SMarkus Armbruster    elif isinstance(obj, dict):
1537d0f982bSMarc-André Lureau        ret += 'QLIT_QDICT(((QLitDictEntry[]) {\n'
154c0e8d9f3SJohn Snow        for key, value in sorted(obj.items()):
155c0e8d9f3SJohn Snow            ret += indent(level + 1) + "{{ {:s}, {:s} }},\n".format(
156c0e8d9f3SJohn Snow                to_c_string(key),
157c0e8d9f3SJohn Snow                _tree_to_qlit(value, level + 1, dict_value=True)
158c0e8d9f3SJohn Snow            )
159c0e8d9f3SJohn Snow        ret += indent(level + 1) + '{}\n'
1607d0f982bSMarc-André Lureau        ret += indent(level) + '}))'
161fb0bc835SMarkus Armbruster    else:
1622a6c161bSJohn Snow        raise NotImplementedError(
1632a6c161bSJohn Snow            f"type '{type(obj).__name__}' not implemented"
1642a6c161bSJohn Snow        )
165c0e8d9f3SJohn Snow
16640bb1376SMarc-André Lureau    if level > 0:
16740bb1376SMarc-André Lureau        ret += ','
168fb0bc835SMarkus Armbruster    return ret
169fb0bc835SMarkus Armbruster
170fb0bc835SMarkus Armbruster
17182b52f6bSJohn Snowdef to_c_string(string: str) -> str:
172fb0bc835SMarkus Armbruster    return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"'
173fb0bc835SMarkus Armbruster
174fb0bc835SMarkus Armbruster
17571b3f045SMarkus Armbrusterclass QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor):
176fb0bc835SMarkus Armbruster
17782b52f6bSJohn Snow    def __init__(self, prefix: str, unmask: bool):
1782cae67bcSMarkus Armbruster        super().__init__(
1792cae67bcSMarkus Armbruster            prefix, 'qapi-introspect',
18071b3f045SMarkus Armbruster            ' * QAPI/QMP schema introspection', __doc__)
18171b3f045SMarkus Armbruster        self._unmask = unmask
18282b52f6bSJohn Snow        self._schema: Optional[QAPISchema] = None
18382b52f6bSJohn Snow        self._trees: List[Annotated[SchemaInfo]] = []
18482b52f6bSJohn Snow        self._used_types: List[QAPISchemaType] = []
18582b52f6bSJohn Snow        self._name_map: Dict[str, str] = {}
18671b3f045SMarkus Armbruster        self._genc.add(mcgen('''
18771b3f045SMarkus Armbruster#include "qemu/osdep.h"
188eb815e24SMarkus Armbruster#include "%(prefix)sqapi-introspect.h"
18971b3f045SMarkus Armbruster
19071b3f045SMarkus Armbruster''',
19171b3f045SMarkus Armbruster                             prefix=prefix))
19271b3f045SMarkus Armbruster
19382b52f6bSJohn Snow    def visit_begin(self, schema: QAPISchema) -> None:
19471b3f045SMarkus Armbruster        self._schema = schema
195fb0bc835SMarkus Armbruster
19682b52f6bSJohn Snow    def visit_end(self) -> None:
197fb0bc835SMarkus Armbruster        # visit the types that are actually used
198fb0bc835SMarkus Armbruster        for typ in self._used_types:
199fb0bc835SMarkus Armbruster            typ.visit(self)
200fb0bc835SMarkus Armbruster        # generate C
2017d0f982bSMarc-André Lureau        name = c_name(self._prefix, protect=False) + 'qmp_schema_qlit'
20271b3f045SMarkus Armbruster        self._genh.add(mcgen('''
2037d0f982bSMarc-André Lureau#include "qapi/qmp/qlit.h"
2047d0f982bSMarc-André Lureau
2057d0f982bSMarc-André Lureauextern const QLitObject %(c_name)s;
206fb0bc835SMarkus Armbruster''',
20771b3f045SMarkus Armbruster                             c_name=c_name(name)))
20871b3f045SMarkus Armbruster        self._genc.add(mcgen('''
2097d0f982bSMarc-André Lureauconst QLitObject %(c_name)s = %(c_string)s;
210fb0bc835SMarkus Armbruster''',
211fb0bc835SMarkus Armbruster                             c_name=c_name(name),
2122e8a843dSMarkus Armbruster                             c_string=_tree_to_qlit(self._trees)))
213fb0bc835SMarkus Armbruster        self._schema = None
2142e8a843dSMarkus Armbruster        self._trees = []
21571b3f045SMarkus Armbruster        self._used_types = []
21671b3f045SMarkus Armbruster        self._name_map = {}
217fb0bc835SMarkus Armbruster
21882b52f6bSJohn Snow    def visit_needed(self, entity: QAPISchemaEntity) -> bool:
219fb0bc835SMarkus Armbruster        # Ignore types on first pass; visit_end() will pick up used types
220fb0bc835SMarkus Armbruster        return not isinstance(entity, QAPISchemaType)
221fb0bc835SMarkus Armbruster
22282b52f6bSJohn Snow    def _name(self, name: str) -> str:
223fb0bc835SMarkus Armbruster        if self._unmask:
224fb0bc835SMarkus Armbruster            return name
225fb0bc835SMarkus Armbruster        if name not in self._name_map:
226fb0bc835SMarkus Armbruster            self._name_map[name] = '%d' % len(self._name_map)
227fb0bc835SMarkus Armbruster        return self._name_map[name]
228fb0bc835SMarkus Armbruster
22982b52f6bSJohn Snow    def _use_type(self, typ: QAPISchemaType) -> str:
2306b67bcacSJohn Snow        assert self._schema is not None
2316b67bcacSJohn Snow
232fb0bc835SMarkus Armbruster        # Map the various integer types to plain int
233fb0bc835SMarkus Armbruster        if typ.json_type() == 'int':
234fb0bc835SMarkus Armbruster            typ = self._schema.lookup_type('int')
235fb0bc835SMarkus Armbruster        elif (isinstance(typ, QAPISchemaArrayType) and
236fb0bc835SMarkus Armbruster              typ.element_type.json_type() == 'int'):
237fb0bc835SMarkus Armbruster            typ = self._schema.lookup_type('intList')
238fb0bc835SMarkus Armbruster        # Add type to work queue if new
239fb0bc835SMarkus Armbruster        if typ not in self._used_types:
240fb0bc835SMarkus Armbruster            self._used_types.append(typ)
241fb0bc835SMarkus Armbruster        # Clients should examine commands and events, not types.  Hide
2421aa806ccSEric Blake        # type names as integers to reduce the temptation.  Also, it
2431aa806ccSEric Blake        # saves a few characters on the wire.
244fb0bc835SMarkus Armbruster        if isinstance(typ, QAPISchemaBuiltinType):
245fb0bc835SMarkus Armbruster            return typ.name
246fb0bc835SMarkus Armbruster        if isinstance(typ, QAPISchemaArrayType):
247fb0bc835SMarkus Armbruster            return '[' + self._use_type(typ.element_type) + ']'
248fb0bc835SMarkus Armbruster        return self._name(typ.name)
249fb0bc835SMarkus Armbruster
25084bece7dSJohn Snow    @staticmethod
25182b52f6bSJohn Snow    def _gen_features(features: List[QAPISchemaFeature]
25282b52f6bSJohn Snow                      ) -> List[Annotated[str]]:
2534f7f97a7SJohn Snow        return [Annotated(f.name, f.ifcond) for f in features]
25484bece7dSJohn Snow
25582b52f6bSJohn Snow    def _gen_tree(self, name: str, mtype: str, obj: Dict[str, object],
25682b52f6bSJohn Snow                  ifcond: Sequence[str],
25782b52f6bSJohn Snow                  features: Optional[List[QAPISchemaFeature]]) -> None:
258*5444dedfSJohn Snow        """
259*5444dedfSJohn Snow        Build and append a SchemaInfo object to self._trees.
260*5444dedfSJohn Snow
261*5444dedfSJohn Snow        :param name: The SchemaInfo's name.
262*5444dedfSJohn Snow        :param mtype: The SchemaInfo's meta-type.
263*5444dedfSJohn Snow        :param obj: Additional SchemaInfo members, as appropriate for
264*5444dedfSJohn Snow                    the meta-type.
265*5444dedfSJohn Snow        :param ifcond: Conditionals to apply to the SchemaInfo.
266*5444dedfSJohn Snow        :param features: The SchemaInfo's features.
267*5444dedfSJohn Snow                         Will be omitted from the output if empty.
268*5444dedfSJohn Snow        """
2695f50cedeSJohn Snow        comment: Optional[str] = None
270fb0bc835SMarkus Armbruster        if mtype not in ('command', 'event', 'builtin', 'array'):
2718c643361SEric Blake            if not self._unmask:
2728c643361SEric Blake                # Output a comment to make it easy to map masked names
2738c643361SEric Blake                # back to the source when reading the generated output.
2745f50cedeSJohn Snow                comment = f'"{self._name(name)}" = {name}'
275fb0bc835SMarkus Armbruster            name = self._name(name)
276fb0bc835SMarkus Armbruster        obj['name'] = name
277fb0bc835SMarkus Armbruster        obj['meta-type'] = mtype
27884bece7dSJohn Snow        if features:
27984bece7dSJohn Snow            obj['features'] = self._gen_features(features)
2804f7f97a7SJohn Snow        self._trees.append(Annotated(obj, ifcond, comment))
281fb0bc835SMarkus Armbruster
28282b52f6bSJohn Snow    def _gen_member(self, member: QAPISchemaObjectTypeMember
28382b52f6bSJohn Snow                    ) -> Annotated[SchemaInfoObjectMember]:
28482b52f6bSJohn Snow        obj: SchemaInfoObjectMember = {
28582b52f6bSJohn Snow            'name': member.name,
28682b52f6bSJohn Snow            'type': self._use_type(member.type)
28782b52f6bSJohn Snow        }
288fb0bc835SMarkus Armbruster        if member.optional:
28924cfd6adSMarkus Armbruster            obj['default'] = None
29084bece7dSJohn Snow        if member.features:
29184bece7dSJohn Snow            obj['features'] = self._gen_features(member.features)
2924f7f97a7SJohn Snow        return Annotated(obj, member.ifcond)
293fb0bc835SMarkus Armbruster
29482b52f6bSJohn Snow    def _gen_variant(self, variant: QAPISchemaVariant
29582b52f6bSJohn Snow                     ) -> Annotated[SchemaInfoObjectVariant]:
29682b52f6bSJohn Snow        obj: SchemaInfoObjectVariant = {
29782b52f6bSJohn Snow            'case': variant.name,
29882b52f6bSJohn Snow            'type': self._use_type(variant.type)
29982b52f6bSJohn Snow        }
3004f7f97a7SJohn Snow        return Annotated(obj, variant.ifcond)
301fb0bc835SMarkus Armbruster
30282b52f6bSJohn Snow    def visit_builtin_type(self, name: str, info: Optional[QAPISourceInfo],
30382b52f6bSJohn Snow                           json_type: str) -> None:
3042e8a843dSMarkus Armbruster        self._gen_tree(name, 'builtin', {'json-type': json_type}, [], None)
305fb0bc835SMarkus Armbruster
30682b52f6bSJohn Snow    def visit_enum_type(self, name: str, info: Optional[QAPISourceInfo],
30782b52f6bSJohn Snow                        ifcond: Sequence[str],
30882b52f6bSJohn Snow                        features: List[QAPISchemaFeature],
30982b52f6bSJohn Snow                        members: List[QAPISchemaEnumMember],
31082b52f6bSJohn Snow                        prefix: Optional[str]) -> None:
3114f7f97a7SJohn Snow        self._gen_tree(
3124f7f97a7SJohn Snow            name, 'enum',
3134f7f97a7SJohn Snow            {'values': [Annotated(m.name, m.ifcond) for m in members]},
3144f7f97a7SJohn Snow            ifcond, features
3154f7f97a7SJohn Snow        )
316fb0bc835SMarkus Armbruster
31782b52f6bSJohn Snow    def visit_array_type(self, name: str, info: Optional[QAPISourceInfo],
31882b52f6bSJohn Snow                         ifcond: Sequence[str],
31982b52f6bSJohn Snow                         element_type: QAPISchemaType) -> None:
320fb0bc835SMarkus Armbruster        element = self._use_type(element_type)
3212e8a843dSMarkus Armbruster        self._gen_tree('[' + element + ']', 'array', {'element-type': element},
322013b4efcSMarkus Armbruster                       ifcond, None)
323fb0bc835SMarkus Armbruster
32482b52f6bSJohn Snow    def visit_object_type_flat(self, name: str, info: Optional[QAPISourceInfo],
32582b52f6bSJohn Snow                               ifcond: Sequence[str],
32682b52f6bSJohn Snow                               features: List[QAPISchemaFeature],
32782b52f6bSJohn Snow                               members: List[QAPISchemaObjectTypeMember],
32882b52f6bSJohn Snow                               variants: Optional[QAPISchemaVariants]) -> None:
32982b52f6bSJohn Snow        obj: SchemaInfoObject = {
33082b52f6bSJohn Snow            'members': [self._gen_member(m) for m in members]
33182b52f6bSJohn Snow        }
332fb0bc835SMarkus Armbruster        if variants:
333cf5db214SJohn Snow            obj['tag'] = variants.tag_member.name
334cf5db214SJohn Snow            obj['variants'] = [self._gen_variant(v) for v in variants.variants]
3352e8a843dSMarkus Armbruster        self._gen_tree(name, 'object', obj, ifcond, features)
336fb0bc835SMarkus Armbruster
33782b52f6bSJohn Snow    def visit_alternate_type(self, name: str, info: Optional[QAPISourceInfo],
33882b52f6bSJohn Snow                             ifcond: Sequence[str],
33982b52f6bSJohn Snow                             features: List[QAPISchemaFeature],
34082b52f6bSJohn Snow                             variants: QAPISchemaVariants) -> None:
3414f7f97a7SJohn Snow        self._gen_tree(
3424f7f97a7SJohn Snow            name, 'alternate',
3434f7f97a7SJohn Snow            {'members': [Annotated({'type': self._use_type(m.type)},
3444f7f97a7SJohn Snow                                   m.ifcond)
345013b4efcSMarkus Armbruster                         for m in variants.variants]},
3464f7f97a7SJohn Snow            ifcond, features
3474f7f97a7SJohn Snow        )
348fb0bc835SMarkus Armbruster
34982b52f6bSJohn Snow    def visit_command(self, name: str, info: Optional[QAPISourceInfo],
35082b52f6bSJohn Snow                      ifcond: Sequence[str],
35182b52f6bSJohn Snow                      features: List[QAPISchemaFeature],
35282b52f6bSJohn Snow                      arg_type: Optional[QAPISchemaObjectType],
35382b52f6bSJohn Snow                      ret_type: Optional[QAPISchemaType], gen: bool,
35482b52f6bSJohn Snow                      success_response: bool, boxed: bool, allow_oob: bool,
35582b52f6bSJohn Snow                      allow_preconfig: bool, coroutine: bool) -> None:
3566b67bcacSJohn Snow        assert self._schema is not None
3576b67bcacSJohn Snow
358fb0bc835SMarkus Armbruster        arg_type = arg_type or self._schema.the_empty_object_type
359fb0bc835SMarkus Armbruster        ret_type = ret_type or self._schema.the_empty_object_type
36082b52f6bSJohn Snow        obj: SchemaInfoCommand = {
36182b52f6bSJohn Snow            'arg-type': self._use_type(arg_type),
36282b52f6bSJohn Snow            'ret-type': self._use_type(ret_type)
36382b52f6bSJohn Snow        }
36425b1ef31SMarkus Armbruster        if allow_oob:
36525b1ef31SMarkus Armbruster            obj['allow-oob'] = allow_oob
3662e8a843dSMarkus Armbruster        self._gen_tree(name, 'command', obj, ifcond, features)
36723394b4cSPeter Krempa
36882b52f6bSJohn Snow    def visit_event(self, name: str, info: Optional[QAPISourceInfo],
36982b52f6bSJohn Snow                    ifcond: Sequence[str], features: List[QAPISchemaFeature],
37082b52f6bSJohn Snow                    arg_type: Optional[QAPISchemaObjectType],
37182b52f6bSJohn Snow                    boxed: bool) -> None:
3726b67bcacSJohn Snow        assert self._schema is not None
37382b52f6bSJohn Snow
374fb0bc835SMarkus Armbruster        arg_type = arg_type or self._schema.the_empty_object_type
3752e8a843dSMarkus Armbruster        self._gen_tree(name, 'event', {'arg-type': self._use_type(arg_type)},
376013b4efcSMarkus Armbruster                       ifcond, features)
377fb0bc835SMarkus Armbruster
378fb0bc835SMarkus Armbruster
37982b52f6bSJohn Snowdef gen_introspect(schema: QAPISchema, output_dir: str, prefix: str,
38082b52f6bSJohn Snow                   opt_unmask: bool) -> None:
381fb0bc835SMarkus Armbruster    vis = QAPISchemaGenIntrospectVisitor(prefix, opt_unmask)
382fb0bc835SMarkus Armbruster    schema.visit(vis)
38371b3f045SMarkus Armbruster    vis.write(output_dir)
384