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