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 branches: Optional[QAPISchemaBranches], 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 branches: Optional[QAPISchemaBranches], 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 alternatives: QAPISchemaAlternatives, 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 branches: Optional[QAPISchemaBranches], 528 ): 529 # struct has local_members, optional base, and no branches 530 # union has base, branches, and no local_members 531 super().__init__(name, info, doc, ifcond, features) 532 self.meta = 'union' if branches else 'struct' 533 for m in local_members: 534 m.set_defined_in(name) 535 if branches is not None: 536 branches.set_defined_in(name) 537 self._base_name = base 538 self.base = None 539 self.local_members = local_members 540 self.branches = branches 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.branches): 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.branches: 581 self.branches.check(schema, seen) 582 self.branches.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.branches: 599 self.branches.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.branches 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.branches) 639 visitor.visit_object_type_flat( 640 self.name, self.info, self.ifcond, self.features, 641 self.members, self.branches) 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 alternatives: QAPISchemaAlternatives, 655 ): 656 super().__init__(name, info, doc, ifcond, features) 657 assert alternatives.tag_member 658 alternatives.set_defined_in(name) 659 alternatives.tag_member.set_defined_in(self.name) 660 self.alternatives = alternatives 661 662 def check(self, schema: QAPISchema) -> None: 663 super().check(schema) 664 self.alternatives.tag_member.check(schema) 665 # Not calling self.alternatives.check_clash(), because there's 666 # nothing to clash with 667 self.alternatives.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.alternatives.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.alternatives.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, 716 self.alternatives) 717 718 719class QAPISchemaVariants: 720 def __init__( 721 self, 722 info: QAPISourceInfo, 723 variants: List[QAPISchemaVariant], 724 ): 725 self.info = info 726 self.tag_member: QAPISchemaObjectTypeMember 727 self.variants = variants 728 729 def set_defined_in(self, name: str) -> None: 730 for v in self.variants: 731 v.set_defined_in(name) 732 733 # pylint: disable=unused-argument 734 def check( 735 self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember] 736 ) -> None: 737 for v in self.variants: 738 v.check(schema) 739 740 741class QAPISchemaBranches(QAPISchemaVariants): 742 def __init__(self, 743 info: QAPISourceInfo, 744 variants: List[QAPISchemaVariant], 745 tag_name: str): 746 super().__init__(info, variants) 747 self._tag_name = tag_name 748 749 def check( 750 self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember] 751 ) -> None: 752 # We need to narrow the member type: 753 tag_member = seen.get(c_name(self._tag_name)) 754 assert (tag_member is None 755 or isinstance(tag_member, QAPISchemaObjectTypeMember)) 756 757 base = "'base'" 758 # Pointing to the base type when not implicit would be 759 # nice, but we don't know it here 760 if not tag_member or self._tag_name != tag_member.name: 761 raise QAPISemError( 762 self.info, 763 "discriminator '%s' is not a member of %s" 764 % (self._tag_name, base)) 765 self.tag_member = tag_member 766 # Here we do: 767 assert tag_member.defined_in 768 base_type = schema.lookup_type(tag_member.defined_in) 769 assert base_type 770 if not base_type.is_implicit(): 771 base = "base type '%s'" % tag_member.defined_in 772 if not isinstance(tag_member.type, QAPISchemaEnumType): 773 raise QAPISemError( 774 self.info, 775 "discriminator member '%s' of %s must be of enum type" 776 % (self._tag_name, base)) 777 if tag_member.optional: 778 raise QAPISemError( 779 self.info, 780 "discriminator member '%s' of %s must not be optional" 781 % (self._tag_name, base)) 782 if tag_member.ifcond.is_present(): 783 raise QAPISemError( 784 self.info, 785 "discriminator member '%s' of %s must not be conditional" 786 % (self._tag_name, base)) 787 # branches that are not explicitly covered get an empty type 788 assert tag_member.defined_in 789 cases = {v.name for v in self.variants} 790 for m in tag_member.type.members: 791 if m.name not in cases: 792 v = QAPISchemaVariant(m.name, self.info, 793 'q_empty', m.ifcond) 794 v.set_defined_in(tag_member.defined_in) 795 self.variants.append(v) 796 if not self.variants: 797 raise QAPISemError(self.info, "union has no branches") 798 for v in self.variants: 799 v.check(schema) 800 # Union names must match enum values; alternate names are 801 # checked separately. Use 'seen' to tell the two apart. 802 if seen: 803 if v.name not in tag_member.type.member_names(): 804 raise QAPISemError( 805 self.info, 806 "branch '%s' is not a value of %s" 807 % (v.name, tag_member.type.describe())) 808 if not isinstance(v.type, QAPISchemaObjectType): 809 raise QAPISemError( 810 self.info, 811 "%s cannot use %s" 812 % (v.describe(self.info), v.type.describe())) 813 v.type.check(schema) 814 815 def check_clash( 816 self, 817 info: Optional[QAPISourceInfo], 818 seen: Dict[str, QAPISchemaMember], 819 ) -> None: 820 for v in self.variants: 821 # Reset seen map for each variant, since qapi names from one 822 # branch do not affect another branch. 823 # 824 # v.type's typing is enforced in check() above. 825 assert isinstance(v.type, QAPISchemaObjectType) 826 v.type.check_clash(info, dict(seen)) 827 828 829class QAPISchemaAlternatives(QAPISchemaVariants): 830 def __init__(self, 831 info: QAPISourceInfo, 832 variants: List[QAPISchemaVariant], 833 tag_member: QAPISchemaObjectTypeMember): 834 super().__init__(info, variants) 835 self.tag_member = tag_member 836 837 def check( 838 self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember] 839 ) -> None: 840 super().check(schema, seen) 841 assert isinstance(self.tag_member.type, QAPISchemaEnumType) 842 assert not self.tag_member.optional 843 assert not self.tag_member.ifcond.is_present() 844 845 846class QAPISchemaMember: 847 """ Represents object members, enum members and features """ 848 role = 'member' 849 850 def __init__( 851 self, 852 name: str, 853 info: Optional[QAPISourceInfo], 854 ifcond: Optional[QAPISchemaIfCond] = None, 855 ): 856 self.name = name 857 self.info = info 858 self.ifcond = ifcond or QAPISchemaIfCond() 859 self.defined_in: Optional[str] = None 860 861 def set_defined_in(self, name: str) -> None: 862 assert not self.defined_in 863 self.defined_in = name 864 865 def check_clash( 866 self, 867 info: Optional[QAPISourceInfo], 868 seen: Dict[str, QAPISchemaMember], 869 ) -> None: 870 cname = c_name(self.name) 871 if cname in seen: 872 raise QAPISemError( 873 info, 874 "%s collides with %s" 875 % (self.describe(info), seen[cname].describe(info))) 876 seen[cname] = self 877 878 def connect_doc(self, doc: Optional[QAPIDoc]) -> None: 879 if doc: 880 doc.connect_member(self) 881 882 def describe(self, info: Optional[QAPISourceInfo]) -> str: 883 role = self.role 884 meta = 'type' 885 defined_in = self.defined_in 886 assert defined_in 887 888 if defined_in.startswith('q_obj_'): 889 # See QAPISchema._make_implicit_object_type() - reverse the 890 # mapping there to create a nice human-readable description 891 defined_in = defined_in[6:] 892 if defined_in.endswith('-arg'): 893 # Implicit type created for a command's dict 'data' 894 assert role == 'member' 895 role = 'parameter' 896 meta = 'command' 897 defined_in = defined_in[:-4] 898 elif defined_in.endswith('-base'): 899 # Implicit type created for a union's dict 'base' 900 role = 'base ' + role 901 defined_in = defined_in[:-5] 902 else: 903 assert False 904 905 assert info is not None 906 if defined_in != info.defn_name: 907 return "%s '%s' of %s '%s'" % (role, self.name, meta, defined_in) 908 return "%s '%s'" % (role, self.name) 909 910 911class QAPISchemaEnumMember(QAPISchemaMember): 912 role = 'value' 913 914 def __init__( 915 self, 916 name: str, 917 info: Optional[QAPISourceInfo], 918 ifcond: Optional[QAPISchemaIfCond] = None, 919 features: Optional[List[QAPISchemaFeature]] = None, 920 ): 921 super().__init__(name, info, ifcond) 922 for f in features or []: 923 f.set_defined_in(name) 924 self.features = features or [] 925 926 def connect_doc(self, doc: Optional[QAPIDoc]) -> None: 927 super().connect_doc(doc) 928 if doc: 929 for f in self.features: 930 doc.connect_feature(f) 931 932 933class QAPISchemaFeature(QAPISchemaMember): 934 role = 'feature' 935 936 def is_special(self) -> bool: 937 return self.name in ('deprecated', 'unstable') 938 939 940class QAPISchemaObjectTypeMember(QAPISchemaMember): 941 def __init__( 942 self, 943 name: str, 944 info: QAPISourceInfo, 945 typ: str, 946 optional: bool, 947 ifcond: Optional[QAPISchemaIfCond] = None, 948 features: Optional[List[QAPISchemaFeature]] = None, 949 ): 950 super().__init__(name, info, ifcond) 951 for f in features or []: 952 f.set_defined_in(name) 953 self._type_name = typ 954 self.type: QAPISchemaType # set during check() 955 self.optional = optional 956 self.features = features or [] 957 958 def need_has(self) -> bool: 959 return self.optional and self.type.need_has_if_optional() 960 961 def check(self, schema: QAPISchema) -> None: 962 assert self.defined_in 963 self.type = schema.resolve_type(self._type_name, self.info, 964 self.describe) 965 seen: Dict[str, QAPISchemaMember] = {} 966 for f in self.features: 967 f.check_clash(self.info, seen) 968 969 def connect_doc(self, doc: Optional[QAPIDoc]) -> None: 970 super().connect_doc(doc) 971 if doc: 972 for f in self.features: 973 doc.connect_feature(f) 974 975 976class QAPISchemaVariant(QAPISchemaObjectTypeMember): 977 role = 'branch' 978 979 def __init__( 980 self, 981 name: str, 982 info: QAPISourceInfo, 983 typ: str, 984 ifcond: QAPISchemaIfCond, 985 ): 986 super().__init__(name, info, typ, False, ifcond) 987 988 989class QAPISchemaCommand(QAPISchemaDefinition): 990 meta = 'command' 991 992 def __init__( 993 self, 994 name: str, 995 info: QAPISourceInfo, 996 doc: Optional[QAPIDoc], 997 ifcond: QAPISchemaIfCond, 998 features: List[QAPISchemaFeature], 999 arg_type: Optional[str], 1000 ret_type: Optional[str], 1001 gen: bool, 1002 success_response: bool, 1003 boxed: bool, 1004 allow_oob: bool, 1005 allow_preconfig: bool, 1006 coroutine: bool, 1007 ): 1008 super().__init__(name, info, doc, ifcond, features) 1009 self._arg_type_name = arg_type 1010 self.arg_type: Optional[QAPISchemaObjectType] = None 1011 self._ret_type_name = ret_type 1012 self.ret_type: Optional[QAPISchemaType] = None 1013 self.gen = gen 1014 self.success_response = success_response 1015 self.boxed = boxed 1016 self.allow_oob = allow_oob 1017 self.allow_preconfig = allow_preconfig 1018 self.coroutine = coroutine 1019 1020 def check(self, schema: QAPISchema) -> None: 1021 assert self.info is not None 1022 super().check(schema) 1023 if self._arg_type_name: 1024 arg_type = schema.resolve_type( 1025 self._arg_type_name, self.info, "command's 'data'") 1026 if not isinstance(arg_type, QAPISchemaObjectType): 1027 raise QAPISemError( 1028 self.info, 1029 "command's 'data' cannot take %s" 1030 % arg_type.describe()) 1031 self.arg_type = arg_type 1032 if self.arg_type.branches and not self.boxed: 1033 raise QAPISemError( 1034 self.info, 1035 "command's 'data' can take %s only with 'boxed': true" 1036 % self.arg_type.describe()) 1037 self.arg_type.check(schema) 1038 if self.arg_type.has_conditional_members() and not self.boxed: 1039 raise QAPISemError( 1040 self.info, 1041 "conditional command arguments require 'boxed': true") 1042 if self._ret_type_name: 1043 self.ret_type = schema.resolve_type( 1044 self._ret_type_name, self.info, "command's 'returns'") 1045 if self.name not in self.info.pragma.command_returns_exceptions: 1046 typ = self.ret_type 1047 if isinstance(typ, QAPISchemaArrayType): 1048 typ = typ.element_type 1049 if not isinstance(typ, QAPISchemaObjectType): 1050 raise QAPISemError( 1051 self.info, 1052 "command's 'returns' cannot take %s" 1053 % self.ret_type.describe()) 1054 1055 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: 1056 super().connect_doc(doc) 1057 doc = doc or self.doc 1058 if doc: 1059 if self.arg_type and self.arg_type.is_implicit(): 1060 self.arg_type.connect_doc(doc) 1061 1062 def visit(self, visitor: QAPISchemaVisitor) -> None: 1063 super().visit(visitor) 1064 visitor.visit_command( 1065 self.name, self.info, self.ifcond, self.features, 1066 self.arg_type, self.ret_type, self.gen, self.success_response, 1067 self.boxed, self.allow_oob, self.allow_preconfig, 1068 self.coroutine) 1069 1070 1071class QAPISchemaEvent(QAPISchemaDefinition): 1072 meta = 'event' 1073 1074 def __init__( 1075 self, 1076 name: str, 1077 info: QAPISourceInfo, 1078 doc: Optional[QAPIDoc], 1079 ifcond: QAPISchemaIfCond, 1080 features: List[QAPISchemaFeature], 1081 arg_type: Optional[str], 1082 boxed: bool, 1083 ): 1084 super().__init__(name, info, doc, ifcond, features) 1085 self._arg_type_name = arg_type 1086 self.arg_type: Optional[QAPISchemaObjectType] = None 1087 self.boxed = boxed 1088 1089 def check(self, schema: QAPISchema) -> None: 1090 super().check(schema) 1091 if self._arg_type_name: 1092 typ = schema.resolve_type( 1093 self._arg_type_name, self.info, "event's 'data'") 1094 if not isinstance(typ, QAPISchemaObjectType): 1095 raise QAPISemError( 1096 self.info, 1097 "event's 'data' cannot take %s" 1098 % typ.describe()) 1099 self.arg_type = typ 1100 if self.arg_type.branches and not self.boxed: 1101 raise QAPISemError( 1102 self.info, 1103 "event's 'data' can take %s only with 'boxed': true" 1104 % self.arg_type.describe()) 1105 self.arg_type.check(schema) 1106 if self.arg_type.has_conditional_members() and not self.boxed: 1107 raise QAPISemError( 1108 self.info, 1109 "conditional event arguments require 'boxed': true") 1110 1111 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: 1112 super().connect_doc(doc) 1113 doc = doc or self.doc 1114 if doc: 1115 if self.arg_type and self.arg_type.is_implicit(): 1116 self.arg_type.connect_doc(doc) 1117 1118 def visit(self, visitor: QAPISchemaVisitor) -> None: 1119 super().visit(visitor) 1120 visitor.visit_event( 1121 self.name, self.info, self.ifcond, self.features, 1122 self.arg_type, self.boxed) 1123 1124 1125class QAPISchema: 1126 def __init__(self, fname: str): 1127 self.fname = fname 1128 1129 try: 1130 parser = QAPISchemaParser(fname) 1131 except OSError as err: 1132 raise QAPIError( 1133 f"can't read schema file '{fname}': {err.strerror}" 1134 ) from err 1135 1136 exprs = check_exprs(parser.exprs) 1137 self.docs = parser.docs 1138 self._entity_list: List[QAPISchemaEntity] = [] 1139 self._entity_dict: Dict[str, QAPISchemaDefinition] = {} 1140 self._module_dict: Dict[str, QAPISchemaModule] = OrderedDict() 1141 self._schema_dir = os.path.dirname(fname) 1142 self._make_module(QAPISchemaModule.BUILTIN_MODULE_NAME) 1143 self._make_module(fname) 1144 self._predefining = True 1145 self._def_predefineds() 1146 self._predefining = False 1147 self._def_exprs(exprs) 1148 self.check() 1149 1150 def _def_entity(self, ent: QAPISchemaEntity) -> None: 1151 self._entity_list.append(ent) 1152 1153 def _def_definition(self, defn: QAPISchemaDefinition) -> None: 1154 # Only the predefined types are allowed to not have info 1155 assert defn.info or self._predefining 1156 self._def_entity(defn) 1157 # TODO reject names that differ only in '_' vs. '.' vs. '-', 1158 # because they're liable to clash in generated C. 1159 other_defn = self._entity_dict.get(defn.name) 1160 if other_defn: 1161 if other_defn.info: 1162 where = QAPISourceError(other_defn.info, "previous definition") 1163 raise QAPISemError( 1164 defn.info, 1165 "'%s' is already defined\n%s" % (defn.name, where)) 1166 raise QAPISemError( 1167 defn.info, "%s is already defined" % other_defn.describe()) 1168 self._entity_dict[defn.name] = defn 1169 1170 def lookup_entity(self, name: str) -> Optional[QAPISchemaEntity]: 1171 return self._entity_dict.get(name) 1172 1173 def lookup_type(self, name: str) -> Optional[QAPISchemaType]: 1174 typ = self.lookup_entity(name) 1175 if isinstance(typ, QAPISchemaType): 1176 return typ 1177 return None 1178 1179 def resolve_type( 1180 self, 1181 name: str, 1182 info: Optional[QAPISourceInfo], 1183 what: Union[None, str, Callable[[QAPISourceInfo], str]], 1184 ) -> QAPISchemaType: 1185 typ = self.lookup_type(name) 1186 if not typ: 1187 assert info and what # built-in types must not fail lookup 1188 if callable(what): 1189 what = what(info) 1190 raise QAPISemError( 1191 info, "%s uses unknown type '%s'" % (what, name)) 1192 return typ 1193 1194 def _module_name(self, fname: str) -> str: 1195 if QAPISchemaModule.is_system_module(fname): 1196 return fname 1197 return os.path.relpath(fname, self._schema_dir) 1198 1199 def _make_module(self, fname: str) -> QAPISchemaModule: 1200 name = self._module_name(fname) 1201 if name not in self._module_dict: 1202 self._module_dict[name] = QAPISchemaModule(name) 1203 return self._module_dict[name] 1204 1205 def module_by_fname(self, fname: str) -> QAPISchemaModule: 1206 name = self._module_name(fname) 1207 return self._module_dict[name] 1208 1209 def _def_include(self, expr: QAPIExpression) -> None: 1210 include = expr['include'] 1211 assert expr.doc is None 1212 self._def_entity( 1213 QAPISchemaInclude(self._make_module(include), expr.info)) 1214 1215 def _def_builtin_type( 1216 self, name: str, json_type: str, c_type: str 1217 ) -> None: 1218 self._def_definition(QAPISchemaBuiltinType(name, json_type, c_type)) 1219 # Instantiating only the arrays that are actually used would 1220 # be nice, but we can't as long as their generated code 1221 # (qapi-builtin-types.[ch]) may be shared by some other 1222 # schema. 1223 self._make_array_type(name, None) 1224 1225 def _def_predefineds(self) -> None: 1226 for t in [('str', 'string', 'char' + POINTER_SUFFIX), 1227 ('number', 'number', 'double'), 1228 ('int', 'int', 'int64_t'), 1229 ('int8', 'int', 'int8_t'), 1230 ('int16', 'int', 'int16_t'), 1231 ('int32', 'int', 'int32_t'), 1232 ('int64', 'int', 'int64_t'), 1233 ('uint8', 'int', 'uint8_t'), 1234 ('uint16', 'int', 'uint16_t'), 1235 ('uint32', 'int', 'uint32_t'), 1236 ('uint64', 'int', 'uint64_t'), 1237 ('size', 'int', 'uint64_t'), 1238 ('bool', 'boolean', 'bool'), 1239 ('any', 'value', 'QObject' + POINTER_SUFFIX), 1240 ('null', 'null', 'QNull' + POINTER_SUFFIX)]: 1241 self._def_builtin_type(*t) 1242 self.the_empty_object_type = QAPISchemaObjectType( 1243 'q_empty', None, None, None, None, None, [], None) 1244 self._def_definition(self.the_empty_object_type) 1245 1246 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 1247 'qbool'] 1248 qtype_values = self._make_enum_members( 1249 [{'name': n} for n in qtypes], None) 1250 1251 self._def_definition(QAPISchemaEnumType( 1252 'QType', None, None, None, None, qtype_values, 'QTYPE')) 1253 1254 def _make_features( 1255 self, 1256 features: Optional[List[Dict[str, Any]]], 1257 info: Optional[QAPISourceInfo], 1258 ) -> List[QAPISchemaFeature]: 1259 if features is None: 1260 return [] 1261 return [QAPISchemaFeature(f['name'], info, 1262 QAPISchemaIfCond(f.get('if'))) 1263 for f in features] 1264 1265 def _make_enum_member( 1266 self, 1267 name: str, 1268 ifcond: Optional[Union[str, Dict[str, Any]]], 1269 features: Optional[List[Dict[str, Any]]], 1270 info: Optional[QAPISourceInfo], 1271 ) -> QAPISchemaEnumMember: 1272 return QAPISchemaEnumMember(name, info, 1273 QAPISchemaIfCond(ifcond), 1274 self._make_features(features, info)) 1275 1276 def _make_enum_members( 1277 self, values: List[Dict[str, Any]], info: Optional[QAPISourceInfo] 1278 ) -> List[QAPISchemaEnumMember]: 1279 return [self._make_enum_member(v['name'], v.get('if'), 1280 v.get('features'), info) 1281 for v in values] 1282 1283 def _make_array_type( 1284 self, element_type: str, info: Optional[QAPISourceInfo] 1285 ) -> str: 1286 name = element_type + 'List' # reserved by check_defn_name_str() 1287 if not self.lookup_type(name): 1288 self._def_definition(QAPISchemaArrayType( 1289 name, info, element_type)) 1290 return name 1291 1292 def _make_implicit_object_type( 1293 self, 1294 name: str, 1295 info: QAPISourceInfo, 1296 ifcond: QAPISchemaIfCond, 1297 role: str, 1298 members: List[QAPISchemaObjectTypeMember], 1299 ) -> Optional[str]: 1300 if not members: 1301 return None 1302 # See also QAPISchemaObjectTypeMember.describe() 1303 name = 'q_obj_%s-%s' % (name, role) 1304 typ = self.lookup_entity(name) 1305 if typ: 1306 assert isinstance(typ, QAPISchemaObjectType) 1307 # The implicit object type has multiple users. This can 1308 # only be a duplicate definition, which will be flagged 1309 # later. 1310 else: 1311 self._def_definition(QAPISchemaObjectType( 1312 name, info, None, ifcond, None, None, members, None)) 1313 return name 1314 1315 def _def_enum_type(self, expr: QAPIExpression) -> None: 1316 name = expr['enum'] 1317 data = expr['data'] 1318 prefix = expr.get('prefix') 1319 ifcond = QAPISchemaIfCond(expr.get('if')) 1320 info = expr.info 1321 features = self._make_features(expr.get('features'), info) 1322 self._def_definition(QAPISchemaEnumType( 1323 name, info, expr.doc, ifcond, features, 1324 self._make_enum_members(data, info), prefix)) 1325 1326 def _make_member( 1327 self, 1328 name: str, 1329 typ: Union[List[str], str], 1330 ifcond: QAPISchemaIfCond, 1331 features: Optional[List[Dict[str, Any]]], 1332 info: QAPISourceInfo, 1333 ) -> QAPISchemaObjectTypeMember: 1334 optional = False 1335 if name.startswith('*'): 1336 name = name[1:] 1337 optional = True 1338 if isinstance(typ, list): 1339 assert len(typ) == 1 1340 typ = self._make_array_type(typ[0], info) 1341 return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond, 1342 self._make_features(features, info)) 1343 1344 def _make_members( 1345 self, 1346 data: Dict[str, Any], 1347 info: QAPISourceInfo, 1348 ) -> List[QAPISchemaObjectTypeMember]: 1349 return [self._make_member(key, value['type'], 1350 QAPISchemaIfCond(value.get('if')), 1351 value.get('features'), info) 1352 for (key, value) in data.items()] 1353 1354 def _def_struct_type(self, expr: QAPIExpression) -> None: 1355 name = expr['struct'] 1356 base = expr.get('base') 1357 data = expr['data'] 1358 info = expr.info 1359 ifcond = QAPISchemaIfCond(expr.get('if')) 1360 features = self._make_features(expr.get('features'), info) 1361 self._def_definition(QAPISchemaObjectType( 1362 name, info, expr.doc, ifcond, features, base, 1363 self._make_members(data, info), 1364 None)) 1365 1366 def _make_variant( 1367 self, 1368 case: str, 1369 typ: str, 1370 ifcond: QAPISchemaIfCond, 1371 info: QAPISourceInfo, 1372 ) -> QAPISchemaVariant: 1373 if isinstance(typ, list): 1374 assert len(typ) == 1 1375 typ = self._make_array_type(typ[0], info) 1376 return QAPISchemaVariant(case, info, typ, ifcond) 1377 1378 def _def_union_type(self, expr: QAPIExpression) -> None: 1379 name = expr['union'] 1380 base = expr['base'] 1381 tag_name = expr['discriminator'] 1382 data = expr['data'] 1383 assert isinstance(data, dict) 1384 info = expr.info 1385 ifcond = QAPISchemaIfCond(expr.get('if')) 1386 features = self._make_features(expr.get('features'), info) 1387 if isinstance(base, dict): 1388 base = self._make_implicit_object_type( 1389 name, info, ifcond, 1390 'base', self._make_members(base, info)) 1391 variants = [ 1392 self._make_variant(key, value['type'], 1393 QAPISchemaIfCond(value.get('if')), 1394 info) 1395 for (key, value) in data.items()] 1396 members: List[QAPISchemaObjectTypeMember] = [] 1397 self._def_definition( 1398 QAPISchemaObjectType(name, info, expr.doc, ifcond, features, 1399 base, members, 1400 QAPISchemaBranches( 1401 info, variants, tag_name))) 1402 1403 def _def_alternate_type(self, expr: QAPIExpression) -> None: 1404 name = expr['alternate'] 1405 data = expr['data'] 1406 assert isinstance(data, dict) 1407 ifcond = QAPISchemaIfCond(expr.get('if')) 1408 info = expr.info 1409 features = self._make_features(expr.get('features'), info) 1410 variants = [ 1411 self._make_variant(key, value['type'], 1412 QAPISchemaIfCond(value.get('if')), 1413 info) 1414 for (key, value) in data.items()] 1415 tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False) 1416 self._def_definition( 1417 QAPISchemaAlternateType( 1418 name, info, expr.doc, ifcond, features, 1419 QAPISchemaAlternatives(info, variants, tag_member))) 1420 1421 def _def_command(self, expr: QAPIExpression) -> None: 1422 name = expr['command'] 1423 data = expr.get('data') 1424 rets = expr.get('returns') 1425 gen = expr.get('gen', True) 1426 success_response = expr.get('success-response', True) 1427 boxed = expr.get('boxed', False) 1428 allow_oob = expr.get('allow-oob', False) 1429 allow_preconfig = expr.get('allow-preconfig', False) 1430 coroutine = expr.get('coroutine', False) 1431 ifcond = QAPISchemaIfCond(expr.get('if')) 1432 info = expr.info 1433 features = self._make_features(expr.get('features'), info) 1434 if isinstance(data, OrderedDict): 1435 data = self._make_implicit_object_type( 1436 name, info, ifcond, 1437 'arg', self._make_members(data, info)) 1438 if isinstance(rets, list): 1439 assert len(rets) == 1 1440 rets = self._make_array_type(rets[0], info) 1441 self._def_definition( 1442 QAPISchemaCommand(name, info, expr.doc, ifcond, features, data, 1443 rets, gen, success_response, boxed, allow_oob, 1444 allow_preconfig, coroutine)) 1445 1446 def _def_event(self, expr: QAPIExpression) -> None: 1447 name = expr['event'] 1448 data = expr.get('data') 1449 boxed = expr.get('boxed', False) 1450 ifcond = QAPISchemaIfCond(expr.get('if')) 1451 info = expr.info 1452 features = self._make_features(expr.get('features'), info) 1453 if isinstance(data, OrderedDict): 1454 data = self._make_implicit_object_type( 1455 name, info, ifcond, 1456 'arg', self._make_members(data, info)) 1457 self._def_definition(QAPISchemaEvent(name, info, expr.doc, ifcond, 1458 features, data, boxed)) 1459 1460 def _def_exprs(self, exprs: List[QAPIExpression]) -> None: 1461 for expr in exprs: 1462 if 'enum' in expr: 1463 self._def_enum_type(expr) 1464 elif 'struct' in expr: 1465 self._def_struct_type(expr) 1466 elif 'union' in expr: 1467 self._def_union_type(expr) 1468 elif 'alternate' in expr: 1469 self._def_alternate_type(expr) 1470 elif 'command' in expr: 1471 self._def_command(expr) 1472 elif 'event' in expr: 1473 self._def_event(expr) 1474 elif 'include' in expr: 1475 self._def_include(expr) 1476 else: 1477 assert False 1478 1479 def check(self) -> None: 1480 for ent in self._entity_list: 1481 ent.check(self) 1482 ent.connect_doc() 1483 for ent in self._entity_list: 1484 ent.set_module(self) 1485 for doc in self.docs: 1486 doc.check() 1487 1488 def visit(self, visitor: QAPISchemaVisitor) -> None: 1489 visitor.visit_begin(self) 1490 for mod in self._module_dict.values(): 1491 mod.visit(visitor) 1492 visitor.visit_end() 1493