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