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