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