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