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 255af8263dSJohn Snowfrom .common import ( 265af8263dSJohn Snow c_name, 275af8263dSJohn Snow gen_endif, 285af8263dSJohn Snow gen_if, 295af8263dSJohn Snow mcgen, 305af8263dSJohn Snow) 317137a960SJohn Snowfrom .gen import QAPISchemaMonolithicCVisitor 3267fea575SJohn Snowfrom .schema import ( 3382b52f6bSJohn Snow QAPISchema, 3467fea575SJohn Snow QAPISchemaArrayType, 3567fea575SJohn Snow QAPISchemaBuiltinType, 3682b52f6bSJohn Snow QAPISchemaEntity, 3782b52f6bSJohn Snow QAPISchemaEnumMember, 3882b52f6bSJohn Snow QAPISchemaFeature, 39*f17539c8SMarc-André Lureau QAPISchemaIfCond, 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 93*f17539c8SMarc-André Lureau def __init__(self, value: _ValueT, ifcond: QAPISchemaIfCond, 944f7f97a7SJohn Snow comment: Optional[str] = None): 954f7f97a7SJohn Snow self.value = value 964f7f97a7SJohn Snow self.comment: Optional[str] = comment 97*f17539c8SMarc-André Lureau self.ifcond = ifcond 9824cfd6adSMarkus Armbruster 9924cfd6adSMarkus Armbruster 10082b52f6bSJohn Snowdef _tree_to_qlit(obj: JSONValue, 10182b52f6bSJohn Snow level: int = 0, 10282b52f6bSJohn Snow dict_value: bool = False) -> str: 1035444dedfSJohn Snow """ 1045444dedfSJohn Snow Convert the type tree into a QLIT C string, recursively. 1055444dedfSJohn Snow 1065444dedfSJohn Snow :param obj: The value to convert. 1075444dedfSJohn Snow This value may not be Annotated when dict_value is True. 1085444dedfSJohn Snow :param level: The indentation level for this particular value. 1095444dedfSJohn Snow :param dict_value: True when the value being processed belongs to a 1105444dedfSJohn Snow dict key; which suppresses the output indent. 1115444dedfSJohn 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" 126*f17539c8SMarc-André Lureau if obj.ifcond.ifcond: 127*f17539c8SMarc-André Lureau ret += gen_if(obj.ifcond.ifcond) 1284f7f97a7SJohn Snow ret += _tree_to_qlit(obj.value, level) 129*f17539c8SMarc-André Lureau if obj.ifcond.ifcond: 130*f17539c8SMarc-André Lureau ret += '\n' + gen_endif(obj.ifcond.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 251cea53c31SJohn Snow def _gen_features(features: Sequence[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], 256*f17539c8SMarc-André Lureau ifcond: QAPISchemaIfCond = QAPISchemaIfCond(), 257cea53c31SJohn Snow features: Sequence[QAPISchemaFeature] = ()) -> None: 2585444dedfSJohn Snow """ 2595444dedfSJohn Snow Build and append a SchemaInfo object to self._trees. 2605444dedfSJohn Snow 2615444dedfSJohn Snow :param name: The SchemaInfo's name. 2625444dedfSJohn Snow :param mtype: The SchemaInfo's meta-type. 2635444dedfSJohn Snow :param obj: Additional SchemaInfo members, as appropriate for 2645444dedfSJohn Snow the meta-type. 2655444dedfSJohn Snow :param ifcond: Conditionals to apply to the SchemaInfo. 2665444dedfSJohn Snow :param features: The SchemaInfo's features. 2675444dedfSJohn Snow Will be omitted from the output if empty. 2685444dedfSJohn 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: 3049b77d946SJohn Snow self._gen_tree(name, 'builtin', {'json-type': json_type}) 305fb0bc835SMarkus Armbruster 30682b52f6bSJohn Snow def visit_enum_type(self, name: str, info: Optional[QAPISourceInfo], 307*f17539c8SMarc-André Lureau ifcond: QAPISchemaIfCond, 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], 318*f17539c8SMarc-André Lureau ifcond: QAPISchemaIfCond, 31982b52f6bSJohn Snow element_type: QAPISchemaType) -> None: 320fb0bc835SMarkus Armbruster element = self._use_type(element_type) 3212e8a843dSMarkus Armbruster self._gen_tree('[' + element + ']', 'array', {'element-type': element}, 322cea53c31SJohn Snow ifcond) 323fb0bc835SMarkus Armbruster 32482b52f6bSJohn Snow def visit_object_type_flat(self, name: str, info: Optional[QAPISourceInfo], 325*f17539c8SMarc-André Lureau ifcond: QAPISchemaIfCond, 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], 338*f17539c8SMarc-André Lureau ifcond: QAPISchemaIfCond, 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], 350*f17539c8SMarc-André Lureau ifcond: QAPISchemaIfCond, 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], 369*f17539c8SMarc-André Lureau ifcond: QAPISchemaIfCond, 370*f17539c8SMarc-André Lureau 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