1# -*- coding: utf-8 -*- 2# 3# QAPI schema internal representation 4# 5# Copyright (c) 2015-2019 Red Hat Inc. 6# 7# Authors: 8# Markus Armbruster <armbru@redhat.com> 9# Eric Blake <eblake@redhat.com> 10# Marc-André Lureau <marcandre.lureau@redhat.com> 11# 12# This work is licensed under the terms of the GNU GPL, version 2. 13# See the COPYING file in the top-level directory. 14 15# pylint: disable=too-many-lines 16 17# TODO catching name collisions in generated code would be nice 18 19from __future__ import annotations 20 21from abc import ABC, abstractmethod 22from collections import OrderedDict 23import os 24import re 25from typing import ( 26 Any, 27 Callable, 28 Dict, 29 List, 30 Optional, 31 Union, 32 cast, 33) 34 35from .common import ( 36 POINTER_SUFFIX, 37 c_name, 38 cgen_ifcond, 39 docgen_ifcond, 40 gen_endif, 41 gen_if, 42) 43from .error import QAPIError, QAPISemError, QAPISourceError 44from .expr import check_exprs 45from .parser import QAPIDoc, QAPIExpression, QAPISchemaParser 46from .source import QAPISourceInfo 47 48 49class QAPISchemaIfCond: 50 def __init__( 51 self, 52 ifcond: Optional[Union[str, Dict[str, object]]] = None, 53 ) -> None: 54 self.ifcond = ifcond 55 56 def _cgen(self) -> str: 57 return cgen_ifcond(self.ifcond) 58 59 def gen_if(self) -> str: 60 return gen_if(self._cgen()) 61 62 def gen_endif(self) -> str: 63 return gen_endif(self._cgen()) 64 65 def docgen(self) -> str: 66 return docgen_ifcond(self.ifcond) 67 68 def is_present(self) -> bool: 69 return bool(self.ifcond) 70 71 72class QAPISchemaEntity: 73 """ 74 A schema entity. 75 76 This is either a directive, such as include, or a definition. 77 The latter uses sub-class `QAPISchemaDefinition`. 78 """ 79 def __init__(self, info: Optional[QAPISourceInfo]): 80 self._module: Optional[QAPISchemaModule] = None 81 # For explicitly defined entities, info points to the (explicit) 82 # definition. For builtins (and their arrays), info is None. 83 # For implicitly defined entities, info points to a place that 84 # triggered the implicit definition (there may be more than one 85 # such place). 86 self.info = info 87 self._checked = False 88 89 def __repr__(self) -> str: 90 return "<%s at 0x%x>" % (type(self).__name__, id(self)) 91 92 def check(self, schema: QAPISchema) -> None: 93 # pylint: disable=unused-argument 94 self._checked = True 95 96 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: 97 pass 98 99 def _set_module( 100 self, schema: QAPISchema, info: Optional[QAPISourceInfo] 101 ) -> None: 102 assert self._checked 103 fname = info.fname if info else QAPISchemaModule.BUILTIN_MODULE_NAME 104 self._module = schema.module_by_fname(fname) 105 self._module.add_entity(self) 106 107 def set_module(self, schema: QAPISchema) -> None: 108 self._set_module(schema, self.info) 109 110 def visit(self, visitor: QAPISchemaVisitor) -> None: 111 # pylint: disable=unused-argument 112 assert self._checked 113 114 115class QAPISchemaDefinition(QAPISchemaEntity): 116 meta: str 117 118 def __init__( 119 self, 120 name: str, 121 info: Optional[QAPISourceInfo], 122 doc: Optional[QAPIDoc], 123 ifcond: Optional[QAPISchemaIfCond] = None, 124 features: Optional[List[QAPISchemaFeature]] = None, 125 ): 126 super().__init__(info) 127 for f in features or []: 128 f.set_defined_in(name) 129 self.name = name 130 self.doc = doc 131 self._ifcond = ifcond or QAPISchemaIfCond() 132 self.features = features or [] 133 134 def __repr__(self) -> str: 135 return "<%s:%s at 0x%x>" % (type(self).__name__, self.name, 136 id(self)) 137 138 def c_name(self) -> str: 139 return c_name(self.name) 140 141 def check(self, schema: QAPISchema) -> None: 142 assert not self._checked 143 super().check(schema) 144 seen: Dict[str, QAPISchemaMember] = {} 145 for f in self.features: 146 f.check_clash(self.info, seen) 147 148 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: 149 super().connect_doc(doc) 150 doc = doc or self.doc 151 if doc: 152 for f in self.features: 153 doc.connect_feature(f) 154 155 @property 156 def ifcond(self) -> QAPISchemaIfCond: 157 assert self._checked 158 return self._ifcond 159 160 def is_implicit(self) -> bool: 161 return not self.info 162 163 def describe(self) -> str: 164 return "%s '%s'" % (self.meta, self.name) 165 166 167class QAPISchemaVisitor: 168 def visit_begin(self, schema: QAPISchema) -> None: 169 pass 170 171 def visit_end(self) -> None: 172 pass 173 174 def visit_module(self, name: str) -> None: 175 pass 176 177 def visit_needed(self, entity: QAPISchemaEntity) -> bool: 178 # pylint: disable=unused-argument 179 # Default to visiting everything 180 return True 181 182 def visit_include(self, name: str, info: Optional[QAPISourceInfo]) -> None: 183 pass 184 185 def visit_builtin_type( 186 self, name: str, info: Optional[QAPISourceInfo], json_type: str 187 ) -> None: 188 pass 189 190 def visit_enum_type( 191 self, 192 name: str, 193 info: Optional[QAPISourceInfo], 194 ifcond: QAPISchemaIfCond, 195 features: List[QAPISchemaFeature], 196 members: List[QAPISchemaEnumMember], 197 prefix: Optional[str], 198 ) -> None: 199 pass 200 201 def visit_array_type( 202 self, 203 name: str, 204 info: Optional[QAPISourceInfo], 205 ifcond: QAPISchemaIfCond, 206 element_type: QAPISchemaType, 207 ) -> None: 208 pass 209 210 def visit_object_type( 211 self, 212 name: str, 213 info: Optional[QAPISourceInfo], 214 ifcond: QAPISchemaIfCond, 215 features: List[QAPISchemaFeature], 216 base: Optional[QAPISchemaObjectType], 217 members: List[QAPISchemaObjectTypeMember], 218 variants: Optional[QAPISchemaVariants], 219 ) -> None: 220 pass 221 222 def visit_object_type_flat( 223 self, 224 name: str, 225 info: Optional[QAPISourceInfo], 226 ifcond: QAPISchemaIfCond, 227 features: List[QAPISchemaFeature], 228 members: List[QAPISchemaObjectTypeMember], 229 variants: Optional[QAPISchemaVariants], 230 ) -> None: 231 pass 232 233 def visit_alternate_type( 234 self, 235 name: str, 236 info: Optional[QAPISourceInfo], 237 ifcond: QAPISchemaIfCond, 238 features: List[QAPISchemaFeature], 239 variants: QAPISchemaVariants, 240 ) -> None: 241 pass 242 243 def visit_command( 244 self, 245 name: str, 246 info: Optional[QAPISourceInfo], 247 ifcond: QAPISchemaIfCond, 248 features: List[QAPISchemaFeature], 249 arg_type: Optional[QAPISchemaObjectType], 250 ret_type: Optional[QAPISchemaType], 251 gen: bool, 252 success_response: bool, 253 boxed: bool, 254 allow_oob: bool, 255 allow_preconfig: bool, 256 coroutine: bool, 257 ) -> None: 258 pass 259 260 def visit_event( 261 self, 262 name: str, 263 info: Optional[QAPISourceInfo], 264 ifcond: QAPISchemaIfCond, 265 features: List[QAPISchemaFeature], 266 arg_type: Optional[QAPISchemaObjectType], 267 boxed: bool, 268 ) -> None: 269 pass 270 271 272class QAPISchemaModule: 273 274 BUILTIN_MODULE_NAME = './builtin' 275 276 def __init__(self, name: str): 277 self.name = name 278 self._entity_list: List[QAPISchemaEntity] = [] 279 280 @staticmethod 281 def is_system_module(name: str) -> bool: 282 """ 283 System modules are internally defined modules. 284 285 Their names start with the "./" prefix. 286 """ 287 return name.startswith('./') 288 289 @classmethod 290 def is_user_module(cls, name: str) -> bool: 291 """ 292 User modules are those defined by the user in qapi JSON files. 293 294 They do not start with the "./" prefix. 295 """ 296 return not cls.is_system_module(name) 297 298 @classmethod 299 def is_builtin_module(cls, name: str) -> bool: 300 """ 301 The built-in module is a single System module for the built-in types. 302 303 It is always "./builtin". 304 """ 305 return name == cls.BUILTIN_MODULE_NAME 306 307 def add_entity(self, ent: QAPISchemaEntity) -> None: 308 self._entity_list.append(ent) 309 310 def visit(self, visitor: QAPISchemaVisitor) -> None: 311 visitor.visit_module(self.name) 312 for entity in self._entity_list: 313 if visitor.visit_needed(entity): 314 entity.visit(visitor) 315 316 317class QAPISchemaInclude(QAPISchemaEntity): 318 def __init__(self, sub_module: QAPISchemaModule, info: QAPISourceInfo): 319 super().__init__(info) 320 self._sub_module = sub_module 321 322 def visit(self, visitor: QAPISchemaVisitor) -> None: 323 super().visit(visitor) 324 visitor.visit_include(self._sub_module.name, self.info) 325 326 327class QAPISchemaType(QAPISchemaDefinition, ABC): 328 # Return the C type for common use. 329 # For the types we commonly box, this is a pointer type. 330 @abstractmethod 331 def c_type(self) -> str: 332 pass 333 334 # Return the C type to be used in a parameter list. 335 def c_param_type(self) -> str: 336 return self.c_type() 337 338 # Return the C type to be used where we suppress boxing. 339 def c_unboxed_type(self) -> str: 340 return self.c_type() 341 342 @abstractmethod 343 def json_type(self) -> str: 344 pass 345 346 def alternate_qtype(self) -> Optional[str]: 347 json2qtype = { 348 'null': 'QTYPE_QNULL', 349 'string': 'QTYPE_QSTRING', 350 'number': 'QTYPE_QNUM', 351 'int': 'QTYPE_QNUM', 352 'boolean': 'QTYPE_QBOOL', 353 'array': 'QTYPE_QLIST', 354 'object': 'QTYPE_QDICT' 355 } 356 return json2qtype.get(self.json_type()) 357 358 def doc_type(self) -> Optional[str]: 359 if self.is_implicit(): 360 return None 361 return self.name 362 363 def need_has_if_optional(self) -> bool: 364 # When FOO is a pointer, has_FOO == !!FOO, i.e. has_FOO is redundant. 365 # Except for arrays; see QAPISchemaArrayType.need_has_if_optional(). 366 return not self.c_type().endswith(POINTER_SUFFIX) 367 368 def check(self, schema: QAPISchema) -> None: 369 super().check(schema) 370 for feat in self.features: 371 if feat.is_special(): 372 raise QAPISemError( 373 self.info, 374 f"feature '{feat.name}' is not supported for types") 375 376 def describe(self) -> str: 377 return "%s type '%s'" % (self.meta, self.name) 378 379 380class QAPISchemaBuiltinType(QAPISchemaType): 381 meta = 'built-in' 382 383 def __init__(self, name: str, json_type: str, c_type: str): 384 super().__init__(name, None, None) 385 assert json_type in ('string', 'number', 'int', 'boolean', 'null', 386 'value') 387 self._json_type_name = json_type 388 self._c_type_name = c_type 389 390 def c_name(self) -> str: 391 return self.name 392 393 def c_type(self) -> str: 394 return self._c_type_name 395 396 def c_param_type(self) -> str: 397 if self.name == 'str': 398 return 'const ' + self._c_type_name 399 return self._c_type_name 400 401 def json_type(self) -> str: 402 return self._json_type_name 403 404 def doc_type(self) -> str: 405 return self.json_type() 406 407 def visit(self, visitor: QAPISchemaVisitor) -> None: 408 super().visit(visitor) 409 visitor.visit_builtin_type(self.name, self.info, self.json_type()) 410 411 412class QAPISchemaEnumType(QAPISchemaType): 413 meta = 'enum' 414 415 def __init__( 416 self, 417 name: str, 418 info: Optional[QAPISourceInfo], 419 doc: Optional[QAPIDoc], 420 ifcond: Optional[QAPISchemaIfCond], 421 features: Optional[List[QAPISchemaFeature]], 422 members: List[QAPISchemaEnumMember], 423 prefix: Optional[str], 424 ): 425 super().__init__(name, info, doc, ifcond, features) 426 for m in members: 427 m.set_defined_in(name) 428 self.members = members 429 self.prefix = prefix 430 431 def check(self, schema: QAPISchema) -> None: 432 super().check(schema) 433 seen: Dict[str, QAPISchemaMember] = {} 434 for m in self.members: 435 m.check_clash(self.info, seen) 436 437 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: 438 super().connect_doc(doc) 439 doc = doc or self.doc 440 for m in self.members: 441 m.connect_doc(doc) 442 443 def is_implicit(self) -> bool: 444 # See QAPISchema._def_predefineds() 445 return self.name == 'QType' 446 447 def c_type(self) -> str: 448 return c_name(self.name) 449 450 def member_names(self) -> List[str]: 451 return [m.name for m in self.members] 452 453 def json_type(self) -> str: 454 return 'string' 455 456 def visit(self, visitor: QAPISchemaVisitor) -> None: 457 super().visit(visitor) 458 visitor.visit_enum_type( 459 self.name, self.info, self.ifcond, self.features, 460 self.members, self.prefix) 461 462 463class QAPISchemaArrayType(QAPISchemaType): 464 meta = 'array' 465 466 def __init__( 467 self, name: str, info: Optional[QAPISourceInfo], element_type: str 468 ): 469 super().__init__(name, info, None) 470 self._element_type_name = element_type 471 self.element_type: QAPISchemaType 472 473 def need_has_if_optional(self) -> bool: 474 # When FOO is an array, we still need has_FOO to distinguish 475 # absent (!has_FOO) from present and empty (has_FOO && !FOO). 476 return True 477 478 def check(self, schema: QAPISchema) -> None: 479 super().check(schema) 480 self.element_type = schema.resolve_type( 481 self._element_type_name, self.info, 482 self.info.defn_meta if self.info else None) 483 assert not isinstance(self.element_type, QAPISchemaArrayType) 484 485 def set_module(self, schema: QAPISchema) -> None: 486 self._set_module(schema, self.element_type.info) 487 488 @property 489 def ifcond(self) -> QAPISchemaIfCond: 490 assert self._checked 491 return self.element_type.ifcond 492 493 def is_implicit(self) -> bool: 494 return True 495 496 def c_type(self) -> str: 497 return c_name(self.name) + POINTER_SUFFIX 498 499 def json_type(self) -> str: 500 return 'array' 501 502 def doc_type(self) -> Optional[str]: 503 elt_doc_type = self.element_type.doc_type() 504 if not elt_doc_type: 505 return None 506 return 'array of ' + elt_doc_type 507 508 def visit(self, visitor: QAPISchemaVisitor) -> None: 509 super().visit(visitor) 510 visitor.visit_array_type(self.name, self.info, self.ifcond, 511 self.element_type) 512 513 def describe(self) -> str: 514 return "%s type ['%s']" % (self.meta, self._element_type_name) 515 516 517class QAPISchemaObjectType(QAPISchemaType): 518 def __init__( 519 self, 520 name: str, 521 info: Optional[QAPISourceInfo], 522 doc: Optional[QAPIDoc], 523 ifcond: Optional[QAPISchemaIfCond], 524 features: Optional[List[QAPISchemaFeature]], 525 base: Optional[str], 526 local_members: List[QAPISchemaObjectTypeMember], 527 variants: Optional[QAPISchemaVariants], 528 ): 529 # struct has local_members, optional base, and no variants 530 # union has base, variants, and no local_members 531 super().__init__(name, info, doc, ifcond, features) 532 self.meta = 'union' if variants else 'struct' 533 for m in local_members: 534 m.set_defined_in(name) 535 if variants is not None: 536 variants.set_defined_in(name) 537 self._base_name = base 538 self.base = None 539 self.local_members = local_members 540 self.variants = variants 541 self.members: List[QAPISchemaObjectTypeMember] 542 self._check_complete = False 543 544 def check(self, schema: QAPISchema) -> None: 545 # This calls another type T's .check() exactly when the C 546 # struct emitted by gen_object() contains that T's C struct 547 # (pointers don't count). 548 if self._check_complete: 549 # A previous .check() completed: nothing to do 550 return 551 if self._checked: 552 # Recursed: C struct contains itself 553 raise QAPISemError(self.info, 554 "object %s contains itself" % self.name) 555 556 super().check(schema) 557 assert self._checked and not self._check_complete 558 559 seen = OrderedDict() 560 if self._base_name: 561 self.base = schema.resolve_type(self._base_name, self.info, 562 "'base'") 563 if (not isinstance(self.base, QAPISchemaObjectType) 564 or self.base.variants): 565 raise QAPISemError( 566 self.info, 567 "'base' requires a struct type, %s isn't" 568 % self.base.describe()) 569 self.base.check(schema) 570 self.base.check_clash(self.info, seen) 571 for m in self.local_members: 572 m.check(schema) 573 m.check_clash(self.info, seen) 574 575 # self.check_clash() works in terms of the supertype, but 576 # self.members is declared List[QAPISchemaObjectTypeMember]. 577 # Cast down to the subtype. 578 members = cast(List[QAPISchemaObjectTypeMember], list(seen.values())) 579 580 if self.variants: 581 self.variants.check(schema, seen) 582 self.variants.check_clash(self.info, seen) 583 584 self.members = members 585 self._check_complete = True # mark completed 586 587 # Check that the members of this type do not cause duplicate JSON members, 588 # and update seen to track the members seen so far. Report any errors 589 # on behalf of info, which is not necessarily self.info 590 def check_clash( 591 self, 592 info: Optional[QAPISourceInfo], 593 seen: Dict[str, QAPISchemaMember], 594 ) -> None: 595 assert self._checked 596 for m in self.members: 597 m.check_clash(info, seen) 598 if self.variants: 599 self.variants.check_clash(info, seen) 600 601 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: 602 super().connect_doc(doc) 603 doc = doc or self.doc 604 if self.base and self.base.is_implicit(): 605 self.base.connect_doc(doc) 606 for m in self.local_members: 607 m.connect_doc(doc) 608 609 def is_implicit(self) -> bool: 610 # See QAPISchema._make_implicit_object_type(), as well as 611 # _def_predefineds() 612 return self.name.startswith('q_') 613 614 def is_empty(self) -> bool: 615 return not self.members and not self.variants 616 617 def has_conditional_members(self) -> bool: 618 return any(m.ifcond.is_present() for m in self.members) 619 620 def c_name(self) -> str: 621 assert self.name != 'q_empty' 622 return super().c_name() 623 624 def c_type(self) -> str: 625 assert not self.is_implicit() 626 return c_name(self.name) + POINTER_SUFFIX 627 628 def c_unboxed_type(self) -> str: 629 return c_name(self.name) 630 631 def json_type(self) -> str: 632 return 'object' 633 634 def visit(self, visitor: QAPISchemaVisitor) -> None: 635 super().visit(visitor) 636 visitor.visit_object_type( 637 self.name, self.info, self.ifcond, self.features, 638 self.base, self.local_members, self.variants) 639 visitor.visit_object_type_flat( 640 self.name, self.info, self.ifcond, self.features, 641 self.members, self.variants) 642 643 644class QAPISchemaAlternateType(QAPISchemaType): 645 meta = 'alternate' 646 647 def __init__( 648 self, 649 name: str, 650 info: QAPISourceInfo, 651 doc: Optional[QAPIDoc], 652 ifcond: Optional[QAPISchemaIfCond], 653 features: List[QAPISchemaFeature], 654 variants: QAPISchemaVariants, 655 ): 656 super().__init__(name, info, doc, ifcond, features) 657 assert variants.tag_member 658 variants.set_defined_in(name) 659 variants.tag_member.set_defined_in(self.name) 660 self.variants = variants 661 662 def check(self, schema: QAPISchema) -> None: 663 super().check(schema) 664 self.variants.tag_member.check(schema) 665 # Not calling self.variants.check_clash(), because there's nothing 666 # to clash with 667 self.variants.check(schema, {}) 668 # Alternate branch names have no relation to the tag enum values; 669 # so we have to check for potential name collisions ourselves. 670 seen: Dict[str, QAPISchemaMember] = {} 671 types_seen: Dict[str, str] = {} 672 for v in self.variants.variants: 673 v.check_clash(self.info, seen) 674 qtype = v.type.alternate_qtype() 675 if not qtype: 676 raise QAPISemError( 677 self.info, 678 "%s cannot use %s" 679 % (v.describe(self.info), v.type.describe())) 680 conflicting = set([qtype]) 681 if qtype == 'QTYPE_QSTRING': 682 if isinstance(v.type, QAPISchemaEnumType): 683 for m in v.type.members: 684 if m.name in ['on', 'off']: 685 conflicting.add('QTYPE_QBOOL') 686 if re.match(r'[-+0-9.]', m.name): 687 # lazy, could be tightened 688 conflicting.add('QTYPE_QNUM') 689 else: 690 conflicting.add('QTYPE_QNUM') 691 conflicting.add('QTYPE_QBOOL') 692 for qt in conflicting: 693 if qt in types_seen: 694 raise QAPISemError( 695 self.info, 696 "%s can't be distinguished from '%s'" 697 % (v.describe(self.info), types_seen[qt])) 698 types_seen[qt] = v.name 699 700 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: 701 super().connect_doc(doc) 702 doc = doc or self.doc 703 for v in self.variants.variants: 704 v.connect_doc(doc) 705 706 def c_type(self) -> str: 707 return c_name(self.name) + POINTER_SUFFIX 708 709 def json_type(self) -> str: 710 return 'value' 711 712 def visit(self, visitor: QAPISchemaVisitor) -> None: 713 super().visit(visitor) 714 visitor.visit_alternate_type( 715 self.name, self.info, self.ifcond, self.features, self.variants) 716 717 718class QAPISchemaVariants: 719 def __init__( 720 self, 721 tag_name: Optional[str], 722 info: QAPISourceInfo, 723 tag_member: Optional[QAPISchemaObjectTypeMember], 724 variants: List[QAPISchemaVariant], 725 ): 726 # Unions pass tag_name but not tag_member. 727 # Alternates pass tag_member but not tag_name. 728 # After check(), tag_member is always set. 729 assert bool(tag_member) != bool(tag_name) 730 assert (isinstance(tag_name, str) or 731 isinstance(tag_member, QAPISchemaObjectTypeMember)) 732 self._tag_name = tag_name 733 self.info = info 734 self._tag_member = tag_member 735 self.variants = variants 736 737 @property 738 def tag_member(self) -> QAPISchemaObjectTypeMember: 739 if self._tag_member is None: 740 raise RuntimeError( 741 "QAPISchemaVariants has no tag_member property until " 742 "after check() has been run." 743 ) 744 return self._tag_member 745 746 def set_defined_in(self, name: str) -> None: 747 for v in self.variants: 748 v.set_defined_in(name) 749 750 def check( 751 self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember] 752 ) -> None: 753 if self._tag_name: # union 754 # We need to narrow the member type: 755 tmp = seen.get(c_name(self._tag_name)) 756 assert tmp is None or isinstance(tmp, QAPISchemaObjectTypeMember) 757 self._tag_member = tmp 758 759 base = "'base'" 760 # Pointing to the base type when not implicit would be 761 # nice, but we don't know it here 762 if not self._tag_member or self._tag_name != self._tag_member.name: 763 raise QAPISemError( 764 self.info, 765 "discriminator '%s' is not a member of %s" 766 % (self._tag_name, base)) 767 # Here we do: 768 assert self.tag_member.defined_in 769 base_type = schema.lookup_type(self.tag_member.defined_in) 770 assert base_type 771 if not base_type.is_implicit(): 772 base = "base type '%s'" % self.tag_member.defined_in 773 if not isinstance(self.tag_member.type, QAPISchemaEnumType): 774 raise QAPISemError( 775 self.info, 776 "discriminator member '%s' of %s must be of enum type" 777 % (self._tag_name, base)) 778 if self.tag_member.optional: 779 raise QAPISemError( 780 self.info, 781 "discriminator member '%s' of %s must not be optional" 782 % (self._tag_name, base)) 783 if self.tag_member.ifcond.is_present(): 784 raise QAPISemError( 785 self.info, 786 "discriminator member '%s' of %s must not be conditional" 787 % (self._tag_name, base)) 788 else: # alternate 789 assert self._tag_member 790 assert isinstance(self.tag_member.type, QAPISchemaEnumType) 791 assert not self.tag_member.optional 792 assert not self.tag_member.ifcond.is_present() 793 if self._tag_name: # union 794 # branches that are not explicitly covered get an empty type 795 assert self.tag_member.defined_in 796 cases = {v.name for v in self.variants} 797 for m in self.tag_member.type.members: 798 if m.name not in cases: 799 v = QAPISchemaVariant(m.name, self.info, 800 'q_empty', m.ifcond) 801 v.set_defined_in(self.tag_member.defined_in) 802 self.variants.append(v) 803 if not self.variants: 804 raise QAPISemError(self.info, "union has no branches") 805 for v in self.variants: 806 v.check(schema) 807 # Union names must match enum values; alternate names are 808 # checked separately. Use 'seen' to tell the two apart. 809 if seen: 810 if v.name not in self.tag_member.type.member_names(): 811 raise QAPISemError( 812 self.info, 813 "branch '%s' is not a value of %s" 814 % (v.name, self.tag_member.type.describe())) 815 if not isinstance(v.type, QAPISchemaObjectType): 816 raise QAPISemError( 817 self.info, 818 "%s cannot use %s" 819 % (v.describe(self.info), v.type.describe())) 820 v.type.check(schema) 821 822 def check_clash( 823 self, 824 info: Optional[QAPISourceInfo], 825 seen: Dict[str, QAPISchemaMember], 826 ) -> None: 827 for v in self.variants: 828 # Reset seen map for each variant, since qapi names from one 829 # branch do not affect another branch. 830 # 831 # v.type's typing is enforced in check() above. 832 assert isinstance(v.type, QAPISchemaObjectType) 833 v.type.check_clash(info, dict(seen)) 834 835 836class QAPISchemaMember: 837 """ Represents object members, enum members and features """ 838 role = 'member' 839 840 def __init__( 841 self, 842 name: str, 843 info: Optional[QAPISourceInfo], 844 ifcond: Optional[QAPISchemaIfCond] = None, 845 ): 846 self.name = name 847 self.info = info 848 self.ifcond = ifcond or QAPISchemaIfCond() 849 self.defined_in: Optional[str] = None 850 851 def set_defined_in(self, name: str) -> None: 852 assert not self.defined_in 853 self.defined_in = name 854 855 def check_clash( 856 self, 857 info: Optional[QAPISourceInfo], 858 seen: Dict[str, QAPISchemaMember], 859 ) -> None: 860 cname = c_name(self.name) 861 if cname in seen: 862 raise QAPISemError( 863 info, 864 "%s collides with %s" 865 % (self.describe(info), seen[cname].describe(info))) 866 seen[cname] = self 867 868 def connect_doc(self, doc: Optional[QAPIDoc]) -> None: 869 if doc: 870 doc.connect_member(self) 871 872 def describe(self, info: Optional[QAPISourceInfo]) -> str: 873 role = self.role 874 meta = 'type' 875 defined_in = self.defined_in 876 assert defined_in 877 878 if defined_in.startswith('q_obj_'): 879 # See QAPISchema._make_implicit_object_type() - reverse the 880 # mapping there to create a nice human-readable description 881 defined_in = defined_in[6:] 882 if defined_in.endswith('-arg'): 883 # Implicit type created for a command's dict 'data' 884 assert role == 'member' 885 role = 'parameter' 886 meta = 'command' 887 defined_in = defined_in[:-4] 888 elif defined_in.endswith('-base'): 889 # Implicit type created for a union's dict 'base' 890 role = 'base ' + role 891 defined_in = defined_in[:-5] 892 else: 893 assert False 894 895 assert info is not None 896 if defined_in != info.defn_name: 897 return "%s '%s' of %s '%s'" % (role, self.name, meta, defined_in) 898 return "%s '%s'" % (role, self.name) 899 900 901class QAPISchemaEnumMember(QAPISchemaMember): 902 role = 'value' 903 904 def __init__( 905 self, 906 name: str, 907 info: Optional[QAPISourceInfo], 908 ifcond: Optional[QAPISchemaIfCond] = None, 909 features: Optional[List[QAPISchemaFeature]] = None, 910 ): 911 super().__init__(name, info, ifcond) 912 for f in features or []: 913 f.set_defined_in(name) 914 self.features = features or [] 915 916 def connect_doc(self, doc: Optional[QAPIDoc]) -> None: 917 super().connect_doc(doc) 918 if doc: 919 for f in self.features: 920 doc.connect_feature(f) 921 922 923class QAPISchemaFeature(QAPISchemaMember): 924 role = 'feature' 925 926 def is_special(self) -> bool: 927 return self.name in ('deprecated', 'unstable') 928 929 930class QAPISchemaObjectTypeMember(QAPISchemaMember): 931 def __init__( 932 self, 933 name: str, 934 info: QAPISourceInfo, 935 typ: str, 936 optional: bool, 937 ifcond: Optional[QAPISchemaIfCond] = None, 938 features: Optional[List[QAPISchemaFeature]] = None, 939 ): 940 super().__init__(name, info, ifcond) 941 for f in features or []: 942 f.set_defined_in(name) 943 self._type_name = typ 944 self.type: QAPISchemaType # set during check() 945 self.optional = optional 946 self.features = features or [] 947 948 def need_has(self) -> bool: 949 return self.optional and self.type.need_has_if_optional() 950 951 def check(self, schema: QAPISchema) -> None: 952 assert self.defined_in 953 self.type = schema.resolve_type(self._type_name, self.info, 954 self.describe) 955 seen: Dict[str, QAPISchemaMember] = {} 956 for f in self.features: 957 f.check_clash(self.info, seen) 958 959 def connect_doc(self, doc: Optional[QAPIDoc]) -> None: 960 super().connect_doc(doc) 961 if doc: 962 for f in self.features: 963 doc.connect_feature(f) 964 965 966class QAPISchemaVariant(QAPISchemaObjectTypeMember): 967 role = 'branch' 968 969 def __init__( 970 self, 971 name: str, 972 info: QAPISourceInfo, 973 typ: str, 974 ifcond: QAPISchemaIfCond, 975 ): 976 super().__init__(name, info, typ, False, ifcond) 977 978 979class QAPISchemaCommand(QAPISchemaDefinition): 980 meta = 'command' 981 982 def __init__( 983 self, 984 name: str, 985 info: QAPISourceInfo, 986 doc: Optional[QAPIDoc], 987 ifcond: QAPISchemaIfCond, 988 features: List[QAPISchemaFeature], 989 arg_type: Optional[str], 990 ret_type: Optional[str], 991 gen: bool, 992 success_response: bool, 993 boxed: bool, 994 allow_oob: bool, 995 allow_preconfig: bool, 996 coroutine: bool, 997 ): 998 super().__init__(name, info, doc, ifcond, features) 999 self._arg_type_name = arg_type 1000 self.arg_type: Optional[QAPISchemaObjectType] = None 1001 self._ret_type_name = ret_type 1002 self.ret_type: Optional[QAPISchemaType] = None 1003 self.gen = gen 1004 self.success_response = success_response 1005 self.boxed = boxed 1006 self.allow_oob = allow_oob 1007 self.allow_preconfig = allow_preconfig 1008 self.coroutine = coroutine 1009 1010 def check(self, schema: QAPISchema) -> None: 1011 assert self.info is not None 1012 super().check(schema) 1013 if self._arg_type_name: 1014 arg_type = schema.resolve_type( 1015 self._arg_type_name, self.info, "command's 'data'") 1016 if not isinstance(arg_type, QAPISchemaObjectType): 1017 raise QAPISemError( 1018 self.info, 1019 "command's 'data' cannot take %s" 1020 % arg_type.describe()) 1021 self.arg_type = arg_type 1022 if self.arg_type.variants and not self.boxed: 1023 raise QAPISemError( 1024 self.info, 1025 "command's 'data' can take %s only with 'boxed': true" 1026 % self.arg_type.describe()) 1027 self.arg_type.check(schema) 1028 if self.arg_type.has_conditional_members() and not self.boxed: 1029 raise QAPISemError( 1030 self.info, 1031 "conditional command arguments require 'boxed': true") 1032 if self._ret_type_name: 1033 self.ret_type = schema.resolve_type( 1034 self._ret_type_name, self.info, "command's 'returns'") 1035 if self.name not in self.info.pragma.command_returns_exceptions: 1036 typ = self.ret_type 1037 if isinstance(typ, QAPISchemaArrayType): 1038 typ = typ.element_type 1039 if not isinstance(typ, QAPISchemaObjectType): 1040 raise QAPISemError( 1041 self.info, 1042 "command's 'returns' cannot take %s" 1043 % self.ret_type.describe()) 1044 1045 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: 1046 super().connect_doc(doc) 1047 doc = doc or self.doc 1048 if doc: 1049 if self.arg_type and self.arg_type.is_implicit(): 1050 self.arg_type.connect_doc(doc) 1051 1052 def visit(self, visitor: QAPISchemaVisitor) -> None: 1053 super().visit(visitor) 1054 visitor.visit_command( 1055 self.name, self.info, self.ifcond, self.features, 1056 self.arg_type, self.ret_type, self.gen, self.success_response, 1057 self.boxed, self.allow_oob, self.allow_preconfig, 1058 self.coroutine) 1059 1060 1061class QAPISchemaEvent(QAPISchemaDefinition): 1062 meta = 'event' 1063 1064 def __init__( 1065 self, 1066 name: str, 1067 info: QAPISourceInfo, 1068 doc: Optional[QAPIDoc], 1069 ifcond: QAPISchemaIfCond, 1070 features: List[QAPISchemaFeature], 1071 arg_type: Optional[str], 1072 boxed: bool, 1073 ): 1074 super().__init__(name, info, doc, ifcond, features) 1075 self._arg_type_name = arg_type 1076 self.arg_type: Optional[QAPISchemaObjectType] = None 1077 self.boxed = boxed 1078 1079 def check(self, schema: QAPISchema) -> None: 1080 super().check(schema) 1081 if self._arg_type_name: 1082 typ = schema.resolve_type( 1083 self._arg_type_name, self.info, "event's 'data'") 1084 if not isinstance(typ, QAPISchemaObjectType): 1085 raise QAPISemError( 1086 self.info, 1087 "event's 'data' cannot take %s" 1088 % typ.describe()) 1089 self.arg_type = typ 1090 if self.arg_type.variants and not self.boxed: 1091 raise QAPISemError( 1092 self.info, 1093 "event's 'data' can take %s only with 'boxed': true" 1094 % self.arg_type.describe()) 1095 self.arg_type.check(schema) 1096 if self.arg_type.has_conditional_members() and not self.boxed: 1097 raise QAPISemError( 1098 self.info, 1099 "conditional event arguments require 'boxed': true") 1100 1101 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: 1102 super().connect_doc(doc) 1103 doc = doc or self.doc 1104 if doc: 1105 if self.arg_type and self.arg_type.is_implicit(): 1106 self.arg_type.connect_doc(doc) 1107 1108 def visit(self, visitor: QAPISchemaVisitor) -> None: 1109 super().visit(visitor) 1110 visitor.visit_event( 1111 self.name, self.info, self.ifcond, self.features, 1112 self.arg_type, self.boxed) 1113 1114 1115class QAPISchema: 1116 def __init__(self, fname: str): 1117 self.fname = fname 1118 1119 try: 1120 parser = QAPISchemaParser(fname) 1121 except OSError as err: 1122 raise QAPIError( 1123 f"can't read schema file '{fname}': {err.strerror}" 1124 ) from err 1125 1126 exprs = check_exprs(parser.exprs) 1127 self.docs = parser.docs 1128 self._entity_list: List[QAPISchemaEntity] = [] 1129 self._entity_dict: Dict[str, QAPISchemaDefinition] = {} 1130 self._module_dict: Dict[str, QAPISchemaModule] = OrderedDict() 1131 self._schema_dir = os.path.dirname(fname) 1132 self._make_module(QAPISchemaModule.BUILTIN_MODULE_NAME) 1133 self._make_module(fname) 1134 self._predefining = True 1135 self._def_predefineds() 1136 self._predefining = False 1137 self._def_exprs(exprs) 1138 self.check() 1139 1140 def _def_entity(self, ent: QAPISchemaEntity) -> None: 1141 self._entity_list.append(ent) 1142 1143 def _def_definition(self, defn: QAPISchemaDefinition) -> None: 1144 # Only the predefined types are allowed to not have info 1145 assert defn.info or self._predefining 1146 self._def_entity(defn) 1147 # TODO reject names that differ only in '_' vs. '.' vs. '-', 1148 # because they're liable to clash in generated C. 1149 other_defn = self._entity_dict.get(defn.name) 1150 if other_defn: 1151 if other_defn.info: 1152 where = QAPISourceError(other_defn.info, "previous definition") 1153 raise QAPISemError( 1154 defn.info, 1155 "'%s' is already defined\n%s" % (defn.name, where)) 1156 raise QAPISemError( 1157 defn.info, "%s is already defined" % other_defn.describe()) 1158 self._entity_dict[defn.name] = defn 1159 1160 def lookup_entity(self,name: str) -> Optional[QAPISchemaEntity]: 1161 return self._entity_dict.get(name) 1162 1163 def lookup_type(self, name: str) -> Optional[QAPISchemaType]: 1164 typ = self.lookup_entity(name) 1165 if isinstance(typ, QAPISchemaType): 1166 return typ 1167 return None 1168 1169 def resolve_type( 1170 self, 1171 name: str, 1172 info: Optional[QAPISourceInfo], 1173 what: Union[None, str, Callable[[QAPISourceInfo], str]], 1174 ) -> QAPISchemaType: 1175 typ = self.lookup_type(name) 1176 if not typ: 1177 assert info and what # built-in types must not fail lookup 1178 if callable(what): 1179 what = what(info) 1180 raise QAPISemError( 1181 info, "%s uses unknown type '%s'" % (what, name)) 1182 return typ 1183 1184 def _module_name(self, fname: str) -> str: 1185 if QAPISchemaModule.is_system_module(fname): 1186 return fname 1187 return os.path.relpath(fname, self._schema_dir) 1188 1189 def _make_module(self, fname: str) -> QAPISchemaModule: 1190 name = self._module_name(fname) 1191 if name not in self._module_dict: 1192 self._module_dict[name] = QAPISchemaModule(name) 1193 return self._module_dict[name] 1194 1195 def module_by_fname(self, fname: str) -> QAPISchemaModule: 1196 name = self._module_name(fname) 1197 return self._module_dict[name] 1198 1199 def _def_include(self, expr: QAPIExpression) -> None: 1200 include = expr['include'] 1201 assert expr.doc is None 1202 self._def_entity( 1203 QAPISchemaInclude(self._make_module(include), expr.info)) 1204 1205 def _def_builtin_type( 1206 self, name: str, json_type: str, c_type: str 1207 ) -> None: 1208 self._def_definition(QAPISchemaBuiltinType(name, json_type, c_type)) 1209 # Instantiating only the arrays that are actually used would 1210 # be nice, but we can't as long as their generated code 1211 # (qapi-builtin-types.[ch]) may be shared by some other 1212 # schema. 1213 self._make_array_type(name, None) 1214 1215 def _def_predefineds(self) -> None: 1216 for t in [('str', 'string', 'char' + POINTER_SUFFIX), 1217 ('number', 'number', 'double'), 1218 ('int', 'int', 'int64_t'), 1219 ('int8', 'int', 'int8_t'), 1220 ('int16', 'int', 'int16_t'), 1221 ('int32', 'int', 'int32_t'), 1222 ('int64', 'int', 'int64_t'), 1223 ('uint8', 'int', 'uint8_t'), 1224 ('uint16', 'int', 'uint16_t'), 1225 ('uint32', 'int', 'uint32_t'), 1226 ('uint64', 'int', 'uint64_t'), 1227 ('size', 'int', 'uint64_t'), 1228 ('bool', 'boolean', 'bool'), 1229 ('any', 'value', 'QObject' + POINTER_SUFFIX), 1230 ('null', 'null', 'QNull' + POINTER_SUFFIX)]: 1231 self._def_builtin_type(*t) 1232 self.the_empty_object_type = QAPISchemaObjectType( 1233 'q_empty', None, None, None, None, None, [], None) 1234 self._def_definition(self.the_empty_object_type) 1235 1236 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 1237 'qbool'] 1238 qtype_values = self._make_enum_members( 1239 [{'name': n} for n in qtypes], None) 1240 1241 self._def_definition(QAPISchemaEnumType( 1242 'QType', None, None, None, None, qtype_values, 'QTYPE')) 1243 1244 def _make_features( 1245 self, 1246 features: Optional[List[Dict[str, Any]]], 1247 info: Optional[QAPISourceInfo], 1248 ) -> List[QAPISchemaFeature]: 1249 if features is None: 1250 return [] 1251 return [QAPISchemaFeature(f['name'], info, 1252 QAPISchemaIfCond(f.get('if'))) 1253 for f in features] 1254 1255 def _make_enum_member( 1256 self, 1257 name: str, 1258 ifcond: Optional[Union[str, Dict[str, Any]]], 1259 features: Optional[List[Dict[str, Any]]], 1260 info: Optional[QAPISourceInfo], 1261 ) -> QAPISchemaEnumMember: 1262 return QAPISchemaEnumMember(name, info, 1263 QAPISchemaIfCond(ifcond), 1264 self._make_features(features, info)) 1265 1266 def _make_enum_members( 1267 self, values: List[Dict[str, Any]], info: Optional[QAPISourceInfo] 1268 ) -> List[QAPISchemaEnumMember]: 1269 return [self._make_enum_member(v['name'], v.get('if'), 1270 v.get('features'), info) 1271 for v in values] 1272 1273 def _make_array_type( 1274 self, element_type: str, info: Optional[QAPISourceInfo] 1275 ) -> str: 1276 name = element_type + 'List' # reserved by check_defn_name_str() 1277 if not self.lookup_type(name): 1278 self._def_definition(QAPISchemaArrayType( 1279 name, info, element_type)) 1280 return name 1281 1282 def _make_implicit_object_type( 1283 self, 1284 name: str, 1285 info: QAPISourceInfo, 1286 ifcond: QAPISchemaIfCond, 1287 role: str, 1288 members: List[QAPISchemaObjectTypeMember], 1289 ) -> Optional[str]: 1290 if not members: 1291 return None 1292 # See also QAPISchemaObjectTypeMember.describe() 1293 name = 'q_obj_%s-%s' % (name, role) 1294 typ = self.lookup_entity(name) 1295 if typ: 1296 assert(isinstance(typ, QAPISchemaObjectType)) 1297 # The implicit object type has multiple users. This can 1298 # only be a duplicate definition, which will be flagged 1299 # later. 1300 pass 1301 else: 1302 self._def_definition(QAPISchemaObjectType( 1303 name, info, None, ifcond, None, None, members, None)) 1304 return name 1305 1306 def _def_enum_type(self, expr: QAPIExpression) -> None: 1307 name = expr['enum'] 1308 data = expr['data'] 1309 prefix = expr.get('prefix') 1310 ifcond = QAPISchemaIfCond(expr.get('if')) 1311 info = expr.info 1312 features = self._make_features(expr.get('features'), info) 1313 self._def_definition(QAPISchemaEnumType( 1314 name, info, expr.doc, ifcond, features, 1315 self._make_enum_members(data, info), prefix)) 1316 1317 def _make_member( 1318 self, 1319 name: str, 1320 typ: Union[List[str], str], 1321 ifcond: QAPISchemaIfCond, 1322 features: Optional[List[Dict[str, Any]]], 1323 info: QAPISourceInfo, 1324 ) -> QAPISchemaObjectTypeMember: 1325 optional = False 1326 if name.startswith('*'): 1327 name = name[1:] 1328 optional = True 1329 if isinstance(typ, list): 1330 assert len(typ) == 1 1331 typ = self._make_array_type(typ[0], info) 1332 return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond, 1333 self._make_features(features, info)) 1334 1335 def _make_members( 1336 self, 1337 data: Dict[str, Any], 1338 info: QAPISourceInfo, 1339 ) -> List[QAPISchemaObjectTypeMember]: 1340 return [self._make_member(key, value['type'], 1341 QAPISchemaIfCond(value.get('if')), 1342 value.get('features'), info) 1343 for (key, value) in data.items()] 1344 1345 def _def_struct_type(self, expr: QAPIExpression) -> None: 1346 name = expr['struct'] 1347 base = expr.get('base') 1348 data = expr['data'] 1349 info = expr.info 1350 ifcond = QAPISchemaIfCond(expr.get('if')) 1351 features = self._make_features(expr.get('features'), info) 1352 self._def_definition(QAPISchemaObjectType( 1353 name, info, expr.doc, ifcond, features, base, 1354 self._make_members(data, info), 1355 None)) 1356 1357 def _make_variant( 1358 self, 1359 case: str, 1360 typ: str, 1361 ifcond: QAPISchemaIfCond, 1362 info: QAPISourceInfo, 1363 ) -> QAPISchemaVariant: 1364 if isinstance(typ, list): 1365 assert len(typ) == 1 1366 typ = self._make_array_type(typ[0], info) 1367 return QAPISchemaVariant(case, info, typ, ifcond) 1368 1369 def _def_union_type(self, expr: QAPIExpression) -> None: 1370 name = expr['union'] 1371 base = expr['base'] 1372 tag_name = expr['discriminator'] 1373 data = expr['data'] 1374 assert isinstance(data, dict) 1375 info = expr.info 1376 ifcond = QAPISchemaIfCond(expr.get('if')) 1377 features = self._make_features(expr.get('features'), info) 1378 if isinstance(base, dict): 1379 base = self._make_implicit_object_type( 1380 name, info, ifcond, 1381 'base', self._make_members(base, info)) 1382 variants = [ 1383 self._make_variant(key, value['type'], 1384 QAPISchemaIfCond(value.get('if')), 1385 info) 1386 for (key, value) in data.items()] 1387 members: List[QAPISchemaObjectTypeMember] = [] 1388 self._def_definition( 1389 QAPISchemaObjectType(name, info, expr.doc, ifcond, features, 1390 base, members, 1391 QAPISchemaVariants( 1392 tag_name, info, None, variants))) 1393 1394 def _def_alternate_type(self, expr: QAPIExpression) -> None: 1395 name = expr['alternate'] 1396 data = expr['data'] 1397 assert isinstance(data, dict) 1398 ifcond = QAPISchemaIfCond(expr.get('if')) 1399 info = expr.info 1400 features = self._make_features(expr.get('features'), info) 1401 variants = [ 1402 self._make_variant(key, value['type'], 1403 QAPISchemaIfCond(value.get('if')), 1404 info) 1405 for (key, value) in data.items()] 1406 tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False) 1407 self._def_definition( 1408 QAPISchemaAlternateType( 1409 name, info, expr.doc, ifcond, features, 1410 QAPISchemaVariants(None, info, tag_member, variants))) 1411 1412 def _def_command(self, expr: QAPIExpression) -> None: 1413 name = expr['command'] 1414 data = expr.get('data') 1415 rets = expr.get('returns') 1416 gen = expr.get('gen', True) 1417 success_response = expr.get('success-response', True) 1418 boxed = expr.get('boxed', False) 1419 allow_oob = expr.get('allow-oob', False) 1420 allow_preconfig = expr.get('allow-preconfig', False) 1421 coroutine = expr.get('coroutine', False) 1422 ifcond = QAPISchemaIfCond(expr.get('if')) 1423 info = expr.info 1424 features = self._make_features(expr.get('features'), info) 1425 if isinstance(data, OrderedDict): 1426 data = self._make_implicit_object_type( 1427 name, info, ifcond, 1428 'arg', self._make_members(data, info)) 1429 if isinstance(rets, list): 1430 assert len(rets) == 1 1431 rets = self._make_array_type(rets[0], info) 1432 self._def_definition( 1433 QAPISchemaCommand(name, info, expr.doc, ifcond, features, data, 1434 rets, gen, success_response, boxed, allow_oob, 1435 allow_preconfig, coroutine)) 1436 1437 def _def_event(self, expr: QAPIExpression) -> None: 1438 name = expr['event'] 1439 data = expr.get('data') 1440 boxed = expr.get('boxed', False) 1441 ifcond = QAPISchemaIfCond(expr.get('if')) 1442 info = expr.info 1443 features = self._make_features(expr.get('features'), info) 1444 if isinstance(data, OrderedDict): 1445 data = self._make_implicit_object_type( 1446 name, info, ifcond, 1447 'arg', self._make_members(data, info)) 1448 self._def_definition(QAPISchemaEvent(name, info, expr.doc, ifcond, 1449 features, data, boxed)) 1450 1451 def _def_exprs(self, exprs: List[QAPIExpression]) -> None: 1452 for expr in exprs: 1453 if 'enum' in expr: 1454 self._def_enum_type(expr) 1455 elif 'struct' in expr: 1456 self._def_struct_type(expr) 1457 elif 'union' in expr: 1458 self._def_union_type(expr) 1459 elif 'alternate' in expr: 1460 self._def_alternate_type(expr) 1461 elif 'command' in expr: 1462 self._def_command(expr) 1463 elif 'event' in expr: 1464 self._def_event(expr) 1465 elif 'include' in expr: 1466 self._def_include(expr) 1467 else: 1468 assert False 1469 1470 def check(self) -> None: 1471 for ent in self._entity_list: 1472 ent.check(self) 1473 ent.connect_doc() 1474 for ent in self._entity_list: 1475 ent.set_module(self) 1476 for doc in self.docs: 1477 doc.check() 1478 1479 def visit(self, visitor: QAPISchemaVisitor) -> None: 1480 visitor.visit_begin(self) 1481 for mod in self._module_dict.values(): 1482 mod.visit(visitor) 1483 visitor.visit_end() 1484