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 def check( 734 self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember] 735 ) -> None: 736 for v in self.variants: 737 v.check(schema) 738 739 740class QAPISchemaBranches(QAPISchemaVariants): 741 def __init__(self, 742 info: QAPISourceInfo, 743 variants: List[QAPISchemaVariant], 744 tag_name: str): 745 super().__init__(info, variants) 746 self._tag_name = tag_name 747 748 def check( 749 self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember] 750 ) -> None: 751 # We need to narrow the member type: 752 tag_member = seen.get(c_name(self._tag_name)) 753 assert (tag_member is None 754 or isinstance(tag_member, QAPISchemaObjectTypeMember)) 755 756 base = "'base'" 757 # Pointing to the base type when not implicit would be 758 # nice, but we don't know it here 759 if not tag_member or self._tag_name != tag_member.name: 760 raise QAPISemError( 761 self.info, 762 "discriminator '%s' is not a member of %s" 763 % (self._tag_name, base)) 764 self.tag_member = tag_member 765 # Here we do: 766 assert tag_member.defined_in 767 base_type = schema.lookup_type(tag_member.defined_in) 768 assert base_type 769 if not base_type.is_implicit(): 770 base = "base type '%s'" % tag_member.defined_in 771 if not isinstance(tag_member.type, QAPISchemaEnumType): 772 raise QAPISemError( 773 self.info, 774 "discriminator member '%s' of %s must be of enum type" 775 % (self._tag_name, base)) 776 if tag_member.optional: 777 raise QAPISemError( 778 self.info, 779 "discriminator member '%s' of %s must not be optional" 780 % (self._tag_name, base)) 781 if tag_member.ifcond.is_present(): 782 raise QAPISemError( 783 self.info, 784 "discriminator member '%s' of %s must not be conditional" 785 % (self._tag_name, base)) 786 # branches that are not explicitly covered get an empty type 787 assert tag_member.defined_in 788 cases = {v.name for v in self.variants} 789 for m in tag_member.type.members: 790 if m.name not in cases: 791 v = QAPISchemaVariant(m.name, self.info, 792 'q_empty', m.ifcond) 793 v.set_defined_in(tag_member.defined_in) 794 self.variants.append(v) 795 if not self.variants: 796 raise QAPISemError(self.info, "union has no branches") 797 for v in self.variants: 798 v.check(schema) 799 # Union names must match enum values; alternate names are 800 # checked separately. Use 'seen' to tell the two apart. 801 if seen: 802 if v.name not in tag_member.type.member_names(): 803 raise QAPISemError( 804 self.info, 805 "branch '%s' is not a value of %s" 806 % (v.name, tag_member.type.describe())) 807 if not isinstance(v.type, QAPISchemaObjectType): 808 raise QAPISemError( 809 self.info, 810 "%s cannot use %s" 811 % (v.describe(self.info), v.type.describe())) 812 v.type.check(schema) 813 814 def check_clash( 815 self, 816 info: Optional[QAPISourceInfo], 817 seen: Dict[str, QAPISchemaMember], 818 ) -> None: 819 for v in self.variants: 820 # Reset seen map for each variant, since qapi names from one 821 # branch do not affect another branch. 822 # 823 # v.type's typing is enforced in check() above. 824 assert isinstance(v.type, QAPISchemaObjectType) 825 v.type.check_clash(info, dict(seen)) 826 827 828class QAPISchemaAlternatives(QAPISchemaVariants): 829 def __init__(self, 830 info: QAPISourceInfo, 831 variants: List[QAPISchemaVariant], 832 tag_member: QAPISchemaObjectTypeMember): 833 super().__init__(info, variants) 834 self.tag_member = tag_member 835 836 def check( 837 self, schema: QAPISchema, seen: Dict[str, QAPISchemaMember] 838 ) -> None: 839 super().check(schema, seen) 840 assert isinstance(self.tag_member.type, QAPISchemaEnumType) 841 assert not self.tag_member.optional 842 assert not self.tag_member.ifcond.is_present() 843 844 845class QAPISchemaMember: 846 """ Represents object members, enum members and features """ 847 role = 'member' 848 849 def __init__( 850 self, 851 name: str, 852 info: Optional[QAPISourceInfo], 853 ifcond: Optional[QAPISchemaIfCond] = None, 854 ): 855 self.name = name 856 self.info = info 857 self.ifcond = ifcond or QAPISchemaIfCond() 858 self.defined_in: Optional[str] = None 859 860 def set_defined_in(self, name: str) -> None: 861 assert not self.defined_in 862 self.defined_in = name 863 864 def check_clash( 865 self, 866 info: Optional[QAPISourceInfo], 867 seen: Dict[str, QAPISchemaMember], 868 ) -> None: 869 cname = c_name(self.name) 870 if cname in seen: 871 raise QAPISemError( 872 info, 873 "%s collides with %s" 874 % (self.describe(info), seen[cname].describe(info))) 875 seen[cname] = self 876 877 def connect_doc(self, doc: Optional[QAPIDoc]) -> None: 878 if doc: 879 doc.connect_member(self) 880 881 def describe(self, info: Optional[QAPISourceInfo]) -> str: 882 role = self.role 883 meta = 'type' 884 defined_in = self.defined_in 885 assert defined_in 886 887 if defined_in.startswith('q_obj_'): 888 # See QAPISchema._make_implicit_object_type() - reverse the 889 # mapping there to create a nice human-readable description 890 defined_in = defined_in[6:] 891 if defined_in.endswith('-arg'): 892 # Implicit type created for a command's dict 'data' 893 assert role == 'member' 894 role = 'parameter' 895 meta = 'command' 896 defined_in = defined_in[:-4] 897 elif defined_in.endswith('-base'): 898 # Implicit type created for a union's dict 'base' 899 role = 'base ' + role 900 defined_in = defined_in[:-5] 901 else: 902 assert False 903 904 assert info is not None 905 if defined_in != info.defn_name: 906 return "%s '%s' of %s '%s'" % (role, self.name, meta, defined_in) 907 return "%s '%s'" % (role, self.name) 908 909 910class QAPISchemaEnumMember(QAPISchemaMember): 911 role = 'value' 912 913 def __init__( 914 self, 915 name: str, 916 info: Optional[QAPISourceInfo], 917 ifcond: Optional[QAPISchemaIfCond] = None, 918 features: Optional[List[QAPISchemaFeature]] = None, 919 ): 920 super().__init__(name, info, ifcond) 921 for f in features or []: 922 f.set_defined_in(name) 923 self.features = features or [] 924 925 def connect_doc(self, doc: Optional[QAPIDoc]) -> None: 926 super().connect_doc(doc) 927 if doc: 928 for f in self.features: 929 doc.connect_feature(f) 930 931 932class QAPISchemaFeature(QAPISchemaMember): 933 role = 'feature' 934 935 def is_special(self) -> bool: 936 return self.name in ('deprecated', 'unstable') 937 938 939class QAPISchemaObjectTypeMember(QAPISchemaMember): 940 def __init__( 941 self, 942 name: str, 943 info: QAPISourceInfo, 944 typ: str, 945 optional: bool, 946 ifcond: Optional[QAPISchemaIfCond] = None, 947 features: Optional[List[QAPISchemaFeature]] = None, 948 ): 949 super().__init__(name, info, ifcond) 950 for f in features or []: 951 f.set_defined_in(name) 952 self._type_name = typ 953 self.type: QAPISchemaType # set during check() 954 self.optional = optional 955 self.features = features or [] 956 957 def need_has(self) -> bool: 958 return self.optional and self.type.need_has_if_optional() 959 960 def check(self, schema: QAPISchema) -> None: 961 assert self.defined_in 962 self.type = schema.resolve_type(self._type_name, self.info, 963 self.describe) 964 seen: Dict[str, QAPISchemaMember] = {} 965 for f in self.features: 966 f.check_clash(self.info, seen) 967 968 def connect_doc(self, doc: Optional[QAPIDoc]) -> None: 969 super().connect_doc(doc) 970 if doc: 971 for f in self.features: 972 doc.connect_feature(f) 973 974 975class QAPISchemaVariant(QAPISchemaObjectTypeMember): 976 role = 'branch' 977 978 def __init__( 979 self, 980 name: str, 981 info: QAPISourceInfo, 982 typ: str, 983 ifcond: QAPISchemaIfCond, 984 ): 985 super().__init__(name, info, typ, False, ifcond) 986 987 988class QAPISchemaCommand(QAPISchemaDefinition): 989 meta = 'command' 990 991 def __init__( 992 self, 993 name: str, 994 info: QAPISourceInfo, 995 doc: Optional[QAPIDoc], 996 ifcond: QAPISchemaIfCond, 997 features: List[QAPISchemaFeature], 998 arg_type: Optional[str], 999 ret_type: Optional[str], 1000 gen: bool, 1001 success_response: bool, 1002 boxed: bool, 1003 allow_oob: bool, 1004 allow_preconfig: bool, 1005 coroutine: bool, 1006 ): 1007 super().__init__(name, info, doc, ifcond, features) 1008 self._arg_type_name = arg_type 1009 self.arg_type: Optional[QAPISchemaObjectType] = None 1010 self._ret_type_name = ret_type 1011 self.ret_type: Optional[QAPISchemaType] = None 1012 self.gen = gen 1013 self.success_response = success_response 1014 self.boxed = boxed 1015 self.allow_oob = allow_oob 1016 self.allow_preconfig = allow_preconfig 1017 self.coroutine = coroutine 1018 1019 def check(self, schema: QAPISchema) -> None: 1020 assert self.info is not None 1021 super().check(schema) 1022 if self._arg_type_name: 1023 arg_type = schema.resolve_type( 1024 self._arg_type_name, self.info, "command's 'data'") 1025 if not isinstance(arg_type, QAPISchemaObjectType): 1026 raise QAPISemError( 1027 self.info, 1028 "command's 'data' cannot take %s" 1029 % arg_type.describe()) 1030 self.arg_type = arg_type 1031 if self.arg_type.branches and not self.boxed: 1032 raise QAPISemError( 1033 self.info, 1034 "command's 'data' can take %s only with 'boxed': true" 1035 % self.arg_type.describe()) 1036 self.arg_type.check(schema) 1037 if self.arg_type.has_conditional_members() and not self.boxed: 1038 raise QAPISemError( 1039 self.info, 1040 "conditional command arguments require 'boxed': true") 1041 if self._ret_type_name: 1042 self.ret_type = schema.resolve_type( 1043 self._ret_type_name, self.info, "command's 'returns'") 1044 if self.name not in self.info.pragma.command_returns_exceptions: 1045 typ = self.ret_type 1046 if isinstance(typ, QAPISchemaArrayType): 1047 typ = typ.element_type 1048 if not isinstance(typ, QAPISchemaObjectType): 1049 raise QAPISemError( 1050 self.info, 1051 "command's 'returns' cannot take %s" 1052 % self.ret_type.describe()) 1053 1054 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: 1055 super().connect_doc(doc) 1056 doc = doc or self.doc 1057 if doc: 1058 if self.arg_type and self.arg_type.is_implicit(): 1059 self.arg_type.connect_doc(doc) 1060 1061 def visit(self, visitor: QAPISchemaVisitor) -> None: 1062 super().visit(visitor) 1063 visitor.visit_command( 1064 self.name, self.info, self.ifcond, self.features, 1065 self.arg_type, self.ret_type, self.gen, self.success_response, 1066 self.boxed, self.allow_oob, self.allow_preconfig, 1067 self.coroutine) 1068 1069 1070class QAPISchemaEvent(QAPISchemaDefinition): 1071 meta = 'event' 1072 1073 def __init__( 1074 self, 1075 name: str, 1076 info: QAPISourceInfo, 1077 doc: Optional[QAPIDoc], 1078 ifcond: QAPISchemaIfCond, 1079 features: List[QAPISchemaFeature], 1080 arg_type: Optional[str], 1081 boxed: bool, 1082 ): 1083 super().__init__(name, info, doc, ifcond, features) 1084 self._arg_type_name = arg_type 1085 self.arg_type: Optional[QAPISchemaObjectType] = None 1086 self.boxed = boxed 1087 1088 def check(self, schema: QAPISchema) -> None: 1089 super().check(schema) 1090 if self._arg_type_name: 1091 typ = schema.resolve_type( 1092 self._arg_type_name, self.info, "event's 'data'") 1093 if not isinstance(typ, QAPISchemaObjectType): 1094 raise QAPISemError( 1095 self.info, 1096 "event's 'data' cannot take %s" 1097 % typ.describe()) 1098 self.arg_type = typ 1099 if self.arg_type.branches and not self.boxed: 1100 raise QAPISemError( 1101 self.info, 1102 "event's 'data' can take %s only with 'boxed': true" 1103 % self.arg_type.describe()) 1104 self.arg_type.check(schema) 1105 if self.arg_type.has_conditional_members() and not self.boxed: 1106 raise QAPISemError( 1107 self.info, 1108 "conditional event arguments require 'boxed': true") 1109 1110 def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: 1111 super().connect_doc(doc) 1112 doc = doc or self.doc 1113 if doc: 1114 if self.arg_type and self.arg_type.is_implicit(): 1115 self.arg_type.connect_doc(doc) 1116 1117 def visit(self, visitor: QAPISchemaVisitor) -> None: 1118 super().visit(visitor) 1119 visitor.visit_event( 1120 self.name, self.info, self.ifcond, self.features, 1121 self.arg_type, self.boxed) 1122 1123 1124class QAPISchema: 1125 def __init__(self, fname: str): 1126 self.fname = fname 1127 1128 try: 1129 parser = QAPISchemaParser(fname) 1130 except OSError as err: 1131 raise QAPIError( 1132 f"can't read schema file '{fname}': {err.strerror}" 1133 ) from err 1134 1135 exprs = check_exprs(parser.exprs) 1136 self.docs = parser.docs 1137 self._entity_list: List[QAPISchemaEntity] = [] 1138 self._entity_dict: Dict[str, QAPISchemaDefinition] = {} 1139 self._module_dict: Dict[str, QAPISchemaModule] = OrderedDict() 1140 self._schema_dir = os.path.dirname(fname) 1141 self._make_module(QAPISchemaModule.BUILTIN_MODULE_NAME) 1142 self._make_module(fname) 1143 self._predefining = True 1144 self._def_predefineds() 1145 self._predefining = False 1146 self._def_exprs(exprs) 1147 self.check() 1148 1149 def _def_entity(self, ent: QAPISchemaEntity) -> None: 1150 self._entity_list.append(ent) 1151 1152 def _def_definition(self, defn: QAPISchemaDefinition) -> None: 1153 # Only the predefined types are allowed to not have info 1154 assert defn.info or self._predefining 1155 self._def_entity(defn) 1156 # TODO reject names that differ only in '_' vs. '.' vs. '-', 1157 # because they're liable to clash in generated C. 1158 other_defn = self._entity_dict.get(defn.name) 1159 if other_defn: 1160 if other_defn.info: 1161 where = QAPISourceError(other_defn.info, "previous definition") 1162 raise QAPISemError( 1163 defn.info, 1164 "'%s' is already defined\n%s" % (defn.name, where)) 1165 raise QAPISemError( 1166 defn.info, "%s is already defined" % other_defn.describe()) 1167 self._entity_dict[defn.name] = defn 1168 1169 def lookup_entity(self,name: str) -> Optional[QAPISchemaEntity]: 1170 return self._entity_dict.get(name) 1171 1172 def lookup_type(self, name: str) -> Optional[QAPISchemaType]: 1173 typ = self.lookup_entity(name) 1174 if isinstance(typ, QAPISchemaType): 1175 return typ 1176 return None 1177 1178 def resolve_type( 1179 self, 1180 name: str, 1181 info: Optional[QAPISourceInfo], 1182 what: Union[None, str, Callable[[QAPISourceInfo], str]], 1183 ) -> QAPISchemaType: 1184 typ = self.lookup_type(name) 1185 if not typ: 1186 assert info and what # built-in types must not fail lookup 1187 if callable(what): 1188 what = what(info) 1189 raise QAPISemError( 1190 info, "%s uses unknown type '%s'" % (what, name)) 1191 return typ 1192 1193 def _module_name(self, fname: str) -> str: 1194 if QAPISchemaModule.is_system_module(fname): 1195 return fname 1196 return os.path.relpath(fname, self._schema_dir) 1197 1198 def _make_module(self, fname: str) -> QAPISchemaModule: 1199 name = self._module_name(fname) 1200 if name not in self._module_dict: 1201 self._module_dict[name] = QAPISchemaModule(name) 1202 return self._module_dict[name] 1203 1204 def module_by_fname(self, fname: str) -> QAPISchemaModule: 1205 name = self._module_name(fname) 1206 return self._module_dict[name] 1207 1208 def _def_include(self, expr: QAPIExpression) -> None: 1209 include = expr['include'] 1210 assert expr.doc is None 1211 self._def_entity( 1212 QAPISchemaInclude(self._make_module(include), expr.info)) 1213 1214 def _def_builtin_type( 1215 self, name: str, json_type: str, c_type: str 1216 ) -> None: 1217 self._def_definition(QAPISchemaBuiltinType(name, json_type, c_type)) 1218 # Instantiating only the arrays that are actually used would 1219 # be nice, but we can't as long as their generated code 1220 # (qapi-builtin-types.[ch]) may be shared by some other 1221 # schema. 1222 self._make_array_type(name, None) 1223 1224 def _def_predefineds(self) -> None: 1225 for t in [('str', 'string', 'char' + POINTER_SUFFIX), 1226 ('number', 'number', 'double'), 1227 ('int', 'int', 'int64_t'), 1228 ('int8', 'int', 'int8_t'), 1229 ('int16', 'int', 'int16_t'), 1230 ('int32', 'int', 'int32_t'), 1231 ('int64', 'int', 'int64_t'), 1232 ('uint8', 'int', 'uint8_t'), 1233 ('uint16', 'int', 'uint16_t'), 1234 ('uint32', 'int', 'uint32_t'), 1235 ('uint64', 'int', 'uint64_t'), 1236 ('size', 'int', 'uint64_t'), 1237 ('bool', 'boolean', 'bool'), 1238 ('any', 'value', 'QObject' + POINTER_SUFFIX), 1239 ('null', 'null', 'QNull' + POINTER_SUFFIX)]: 1240 self._def_builtin_type(*t) 1241 self.the_empty_object_type = QAPISchemaObjectType( 1242 'q_empty', None, None, None, None, None, [], None) 1243 self._def_definition(self.the_empty_object_type) 1244 1245 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 1246 'qbool'] 1247 qtype_values = self._make_enum_members( 1248 [{'name': n} for n in qtypes], None) 1249 1250 self._def_definition(QAPISchemaEnumType( 1251 'QType', None, None, None, None, qtype_values, 'QTYPE')) 1252 1253 def _make_features( 1254 self, 1255 features: Optional[List[Dict[str, Any]]], 1256 info: Optional[QAPISourceInfo], 1257 ) -> List[QAPISchemaFeature]: 1258 if features is None: 1259 return [] 1260 return [QAPISchemaFeature(f['name'], info, 1261 QAPISchemaIfCond(f.get('if'))) 1262 for f in features] 1263 1264 def _make_enum_member( 1265 self, 1266 name: str, 1267 ifcond: Optional[Union[str, Dict[str, Any]]], 1268 features: Optional[List[Dict[str, Any]]], 1269 info: Optional[QAPISourceInfo], 1270 ) -> QAPISchemaEnumMember: 1271 return QAPISchemaEnumMember(name, info, 1272 QAPISchemaIfCond(ifcond), 1273 self._make_features(features, info)) 1274 1275 def _make_enum_members( 1276 self, values: List[Dict[str, Any]], info: Optional[QAPISourceInfo] 1277 ) -> List[QAPISchemaEnumMember]: 1278 return [self._make_enum_member(v['name'], v.get('if'), 1279 v.get('features'), info) 1280 for v in values] 1281 1282 def _make_array_type( 1283 self, element_type: str, info: Optional[QAPISourceInfo] 1284 ) -> str: 1285 name = element_type + 'List' # reserved by check_defn_name_str() 1286 if not self.lookup_type(name): 1287 self._def_definition(QAPISchemaArrayType( 1288 name, info, element_type)) 1289 return name 1290 1291 def _make_implicit_object_type( 1292 self, 1293 name: str, 1294 info: QAPISourceInfo, 1295 ifcond: QAPISchemaIfCond, 1296 role: str, 1297 members: List[QAPISchemaObjectTypeMember], 1298 ) -> Optional[str]: 1299 if not members: 1300 return None 1301 # See also QAPISchemaObjectTypeMember.describe() 1302 name = 'q_obj_%s-%s' % (name, role) 1303 typ = self.lookup_entity(name) 1304 if typ: 1305 assert(isinstance(typ, QAPISchemaObjectType)) 1306 # The implicit object type has multiple users. This can 1307 # only be a duplicate definition, which will be flagged 1308 # later. 1309 pass 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