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): 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 super().__init__(name, info, doc, ifcond, features) 718 assert not arg_type or isinstance(arg_type, str) 719 assert not ret_type or isinstance(ret_type, str) 720 self._arg_type_name = arg_type 721 self.arg_type = None 722 self._ret_type_name = ret_type 723 self.ret_type = None 724 self.gen = gen 725 self.success_response = success_response 726 self.boxed = boxed 727 self.allow_oob = allow_oob 728 self.allow_preconfig = allow_preconfig 729 730 def check(self, schema): 731 super().check(schema) 732 if self._arg_type_name: 733 self.arg_type = schema.resolve_type( 734 self._arg_type_name, self.info, "command's 'data'") 735 if not isinstance(self.arg_type, QAPISchemaObjectType): 736 raise QAPISemError( 737 self.info, 738 "command's 'data' cannot take %s" 739 % self.arg_type.describe()) 740 if self.arg_type.variants and not self.boxed: 741 raise QAPISemError( 742 self.info, 743 "command's 'data' can take %s only with 'boxed': true" 744 % self.arg_type.describe()) 745 if self._ret_type_name: 746 self.ret_type = schema.resolve_type( 747 self._ret_type_name, self.info, "command's 'returns'") 748 if self.name not in self.info.pragma.returns_whitelist: 749 typ = self.ret_type 750 if isinstance(typ, QAPISchemaArrayType): 751 typ = self.ret_type.element_type 752 assert typ 753 if not isinstance(typ, QAPISchemaObjectType): 754 raise QAPISemError( 755 self.info, 756 "command's 'returns' cannot take %s" 757 % self.ret_type.describe()) 758 759 def connect_doc(self, doc=None): 760 super().connect_doc(doc) 761 doc = doc or self.doc 762 if doc: 763 if self.arg_type and self.arg_type.is_implicit(): 764 self.arg_type.connect_doc(doc) 765 766 def visit(self, visitor): 767 super().visit(visitor) 768 visitor.visit_command( 769 self.name, self.info, self.ifcond, self.features, 770 self.arg_type, self.ret_type, self.gen, self.success_response, 771 self.boxed, self.allow_oob, self.allow_preconfig) 772 773 774class QAPISchemaEvent(QAPISchemaEntity): 775 meta = 'event' 776 777 def __init__(self, name, info, doc, ifcond, features, arg_type, boxed): 778 super().__init__(name, info, doc, ifcond, features) 779 assert not arg_type or isinstance(arg_type, str) 780 self._arg_type_name = arg_type 781 self.arg_type = None 782 self.boxed = boxed 783 784 def check(self, schema): 785 super().check(schema) 786 if self._arg_type_name: 787 self.arg_type = schema.resolve_type( 788 self._arg_type_name, self.info, "event's 'data'") 789 if not isinstance(self.arg_type, QAPISchemaObjectType): 790 raise QAPISemError( 791 self.info, 792 "event's 'data' cannot take %s" 793 % self.arg_type.describe()) 794 if self.arg_type.variants and not self.boxed: 795 raise QAPISemError( 796 self.info, 797 "event's 'data' can take %s only with 'boxed': true" 798 % self.arg_type.describe()) 799 800 def connect_doc(self, doc=None): 801 super().connect_doc(doc) 802 doc = doc or self.doc 803 if doc: 804 if self.arg_type and self.arg_type.is_implicit(): 805 self.arg_type.connect_doc(doc) 806 807 def visit(self, visitor): 808 super().visit(visitor) 809 visitor.visit_event( 810 self.name, self.info, self.ifcond, self.features, 811 self.arg_type, self.boxed) 812 813 814class QAPISchema: 815 def __init__(self, fname): 816 self.fname = fname 817 parser = QAPISchemaParser(fname) 818 exprs = check_exprs(parser.exprs) 819 self.docs = parser.docs 820 self._entity_list = [] 821 self._entity_dict = {} 822 self._module_dict = OrderedDict() 823 self._schema_dir = os.path.dirname(fname) 824 self._make_module(None) # built-ins 825 self._make_module(fname) 826 self._predefining = True 827 self._def_predefineds() 828 self._predefining = False 829 self._def_exprs(exprs) 830 self.check() 831 832 def _def_entity(self, ent): 833 # Only the predefined types are allowed to not have info 834 assert ent.info or self._predefining 835 self._entity_list.append(ent) 836 if ent.name is None: 837 return 838 # TODO reject names that differ only in '_' vs. '.' vs. '-', 839 # because they're liable to clash in generated C. 840 other_ent = self._entity_dict.get(ent.name) 841 if other_ent: 842 if other_ent.info: 843 where = QAPIError(other_ent.info, None, "previous definition") 844 raise QAPISemError( 845 ent.info, 846 "'%s' is already defined\n%s" % (ent.name, where)) 847 raise QAPISemError( 848 ent.info, "%s is already defined" % other_ent.describe()) 849 self._entity_dict[ent.name] = ent 850 851 def lookup_entity(self, name, typ=None): 852 ent = self._entity_dict.get(name) 853 if typ and not isinstance(ent, typ): 854 return None 855 return ent 856 857 def lookup_type(self, name): 858 return self.lookup_entity(name, QAPISchemaType) 859 860 def resolve_type(self, name, info, what): 861 typ = self.lookup_type(name) 862 if not typ: 863 if callable(what): 864 what = what(info) 865 raise QAPISemError( 866 info, "%s uses unknown type '%s'" % (what, name)) 867 return typ 868 869 def _module_name(self, fname): 870 if fname is None: 871 return None 872 return os.path.relpath(fname, self._schema_dir) 873 874 def _make_module(self, fname): 875 name = self._module_name(fname) 876 if name not in self._module_dict: 877 self._module_dict[name] = QAPISchemaModule(name) 878 return self._module_dict[name] 879 880 def module_by_fname(self, fname): 881 name = self._module_name(fname) 882 assert name in self._module_dict 883 return self._module_dict[name] 884 885 def _def_include(self, expr, info, doc): 886 include = expr['include'] 887 assert doc is None 888 self._def_entity(QAPISchemaInclude(self._make_module(include), info)) 889 890 def _def_builtin_type(self, name, json_type, c_type): 891 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type)) 892 # Instantiating only the arrays that are actually used would 893 # be nice, but we can't as long as their generated code 894 # (qapi-builtin-types.[ch]) may be shared by some other 895 # schema. 896 self._make_array_type(name, None) 897 898 def _def_predefineds(self): 899 for t in [('str', 'string', 'char' + pointer_suffix), 900 ('number', 'number', 'double'), 901 ('int', 'int', 'int64_t'), 902 ('int8', 'int', 'int8_t'), 903 ('int16', 'int', 'int16_t'), 904 ('int32', 'int', 'int32_t'), 905 ('int64', 'int', 'int64_t'), 906 ('uint8', 'int', 'uint8_t'), 907 ('uint16', 'int', 'uint16_t'), 908 ('uint32', 'int', 'uint32_t'), 909 ('uint64', 'int', 'uint64_t'), 910 ('size', 'int', 'uint64_t'), 911 ('bool', 'boolean', 'bool'), 912 ('any', 'value', 'QObject' + pointer_suffix), 913 ('null', 'null', 'QNull' + pointer_suffix)]: 914 self._def_builtin_type(*t) 915 self.the_empty_object_type = QAPISchemaObjectType( 916 'q_empty', None, None, None, None, None, [], None) 917 self._def_entity(self.the_empty_object_type) 918 919 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 920 'qbool'] 921 qtype_values = self._make_enum_members( 922 [{'name': n} for n in qtypes], None) 923 924 self._def_entity(QAPISchemaEnumType('QType', None, None, None, None, 925 qtype_values, 'QTYPE')) 926 927 def _make_features(self, features, info): 928 if features is None: 929 return [] 930 return [QAPISchemaFeature(f['name'], info, f.get('if')) 931 for f in features] 932 933 def _make_enum_members(self, values, info): 934 return [QAPISchemaEnumMember(v['name'], info, v.get('if')) 935 for v in values] 936 937 def _make_implicit_enum_type(self, name, info, ifcond, values): 938 # See also QAPISchemaObjectTypeMember.describe() 939 name = name + 'Kind' # reserved by check_defn_name_str() 940 self._def_entity(QAPISchemaEnumType( 941 name, info, None, ifcond, None, 942 self._make_enum_members(values, info), 943 None)) 944 return name 945 946 def _make_array_type(self, element_type, info): 947 name = element_type + 'List' # reserved by check_defn_name_str() 948 if not self.lookup_type(name): 949 self._def_entity(QAPISchemaArrayType(name, info, element_type)) 950 return name 951 952 def _make_implicit_object_type(self, name, info, ifcond, role, members): 953 if not members: 954 return None 955 # See also QAPISchemaObjectTypeMember.describe() 956 name = 'q_obj_%s-%s' % (name, role) 957 typ = self.lookup_entity(name, QAPISchemaObjectType) 958 if typ: 959 # The implicit object type has multiple users. This can 960 # happen only for simple unions' implicit wrapper types. 961 # Its ifcond should be the disjunction of its user's 962 # ifconds. Not implemented. Instead, we always pass the 963 # wrapped type's ifcond, which is trivially the same for all 964 # users. It's also necessary for the wrapper to compile. 965 # But it's not tight: the disjunction need not imply it. We 966 # may end up compiling useless wrapper types. 967 # TODO kill simple unions or implement the disjunction 968 assert (ifcond or []) == typ._ifcond # pylint: disable=protected-access 969 else: 970 self._def_entity(QAPISchemaObjectType( 971 name, info, None, ifcond, None, None, members, None)) 972 return name 973 974 def _def_enum_type(self, expr, info, doc): 975 name = expr['enum'] 976 data = expr['data'] 977 prefix = expr.get('prefix') 978 ifcond = expr.get('if') 979 features = self._make_features(expr.get('features'), info) 980 self._def_entity(QAPISchemaEnumType( 981 name, info, doc, ifcond, features, 982 self._make_enum_members(data, info), prefix)) 983 984 def _make_member(self, name, typ, ifcond, features, info): 985 optional = False 986 if name.startswith('*'): 987 name = name[1:] 988 optional = True 989 if isinstance(typ, list): 990 assert len(typ) == 1 991 typ = self._make_array_type(typ[0], info) 992 return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond, 993 self._make_features(features, info)) 994 995 def _make_members(self, data, info): 996 return [self._make_member(key, value['type'], value.get('if'), 997 value.get('features'), info) 998 for (key, value) in data.items()] 999 1000 def _def_struct_type(self, expr, info, doc): 1001 name = expr['struct'] 1002 base = expr.get('base') 1003 data = expr['data'] 1004 ifcond = expr.get('if') 1005 features = self._make_features(expr.get('features'), info) 1006 self._def_entity(QAPISchemaObjectType( 1007 name, info, doc, ifcond, features, base, 1008 self._make_members(data, info), 1009 None)) 1010 1011 def _make_variant(self, case, typ, ifcond, info): 1012 return QAPISchemaVariant(case, info, typ, ifcond) 1013 1014 def _make_simple_variant(self, case, typ, ifcond, info): 1015 if isinstance(typ, list): 1016 assert len(typ) == 1 1017 typ = self._make_array_type(typ[0], info) 1018 typ = self._make_implicit_object_type( 1019 typ, info, self.lookup_type(typ), 1020 'wrapper', [self._make_member('data', typ, None, None, info)]) 1021 return QAPISchemaVariant(case, info, typ, ifcond) 1022 1023 def _def_union_type(self, expr, info, doc): 1024 name = expr['union'] 1025 data = expr['data'] 1026 base = expr.get('base') 1027 ifcond = expr.get('if') 1028 features = self._make_features(expr.get('features'), info) 1029 tag_name = expr.get('discriminator') 1030 tag_member = None 1031 if isinstance(base, dict): 1032 base = self._make_implicit_object_type( 1033 name, info, ifcond, 1034 'base', self._make_members(base, info)) 1035 if tag_name: 1036 variants = [self._make_variant(key, value['type'], 1037 value.get('if'), info) 1038 for (key, value) in data.items()] 1039 members = [] 1040 else: 1041 variants = [self._make_simple_variant(key, value['type'], 1042 value.get('if'), info) 1043 for (key, value) in data.items()] 1044 enum = [{'name': v.name, 'if': v.ifcond} for v in variants] 1045 typ = self._make_implicit_enum_type(name, info, ifcond, enum) 1046 tag_member = QAPISchemaObjectTypeMember('type', info, typ, False) 1047 members = [tag_member] 1048 self._def_entity( 1049 QAPISchemaObjectType(name, info, doc, ifcond, features, 1050 base, members, 1051 QAPISchemaVariants( 1052 tag_name, info, tag_member, variants))) 1053 1054 def _def_alternate_type(self, expr, info, doc): 1055 name = expr['alternate'] 1056 data = expr['data'] 1057 ifcond = expr.get('if') 1058 features = self._make_features(expr.get('features'), info) 1059 variants = [self._make_variant(key, value['type'], value.get('if'), 1060 info) 1061 for (key, value) in data.items()] 1062 tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False) 1063 self._def_entity( 1064 QAPISchemaAlternateType(name, info, doc, ifcond, features, 1065 QAPISchemaVariants( 1066 None, info, tag_member, variants))) 1067 1068 def _def_command(self, expr, info, doc): 1069 name = expr['command'] 1070 data = expr.get('data') 1071 rets = expr.get('returns') 1072 gen = expr.get('gen', True) 1073 success_response = expr.get('success-response', True) 1074 boxed = expr.get('boxed', False) 1075 allow_oob = expr.get('allow-oob', False) 1076 allow_preconfig = expr.get('allow-preconfig', False) 1077 ifcond = expr.get('if') 1078 features = self._make_features(expr.get('features'), info) 1079 if isinstance(data, OrderedDict): 1080 data = self._make_implicit_object_type( 1081 name, info, ifcond, 1082 'arg', self._make_members(data, info)) 1083 if isinstance(rets, list): 1084 assert len(rets) == 1 1085 rets = self._make_array_type(rets[0], info) 1086 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, features, 1087 data, rets, 1088 gen, success_response, 1089 boxed, allow_oob, allow_preconfig)) 1090 1091 def _def_event(self, expr, info, doc): 1092 name = expr['event'] 1093 data = expr.get('data') 1094 boxed = expr.get('boxed', False) 1095 ifcond = expr.get('if') 1096 features = self._make_features(expr.get('features'), info) 1097 if isinstance(data, OrderedDict): 1098 data = self._make_implicit_object_type( 1099 name, info, ifcond, 1100 'arg', self._make_members(data, info)) 1101 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, features, 1102 data, boxed)) 1103 1104 def _def_exprs(self, exprs): 1105 for expr_elem in exprs: 1106 expr = expr_elem['expr'] 1107 info = expr_elem['info'] 1108 doc = expr_elem.get('doc') 1109 if 'enum' in expr: 1110 self._def_enum_type(expr, info, doc) 1111 elif 'struct' in expr: 1112 self._def_struct_type(expr, info, doc) 1113 elif 'union' in expr: 1114 self._def_union_type(expr, info, doc) 1115 elif 'alternate' in expr: 1116 self._def_alternate_type(expr, info, doc) 1117 elif 'command' in expr: 1118 self._def_command(expr, info, doc) 1119 elif 'event' in expr: 1120 self._def_event(expr, info, doc) 1121 elif 'include' in expr: 1122 self._def_include(expr, info, doc) 1123 else: 1124 assert False 1125 1126 def check(self): 1127 for ent in self._entity_list: 1128 ent.check(self) 1129 ent.connect_doc() 1130 ent.check_doc() 1131 for ent in self._entity_list: 1132 ent.set_module(self) 1133 1134 def visit(self, visitor): 1135 visitor.visit_begin(self) 1136 for mod in self._module_dict.values(): 1137 mod.visit(visitor) 1138 visitor.visit_end() 1139