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