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