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 20 21from .common import POINTER_SUFFIX, c_name 22from .error import QAPIError, QAPISemError 23from .expr import check_exprs 24from .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 972 # pylint: disable=protected-access 973 assert (ifcond or []) == typ._ifcond 974 else: 975 self._def_entity(QAPISchemaObjectType( 976 name, info, None, ifcond, None, None, members, None)) 977 return name 978 979 def _def_enum_type(self, expr, info, doc): 980 name = expr['enum'] 981 data = expr['data'] 982 prefix = expr.get('prefix') 983 ifcond = expr.get('if') 984 features = self._make_features(expr.get('features'), info) 985 self._def_entity(QAPISchemaEnumType( 986 name, info, doc, ifcond, features, 987 self._make_enum_members(data, info), prefix)) 988 989 def _make_member(self, name, typ, ifcond, features, info): 990 optional = False 991 if name.startswith('*'): 992 name = name[1:] 993 optional = True 994 if isinstance(typ, list): 995 assert len(typ) == 1 996 typ = self._make_array_type(typ[0], info) 997 return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond, 998 self._make_features(features, info)) 999 1000 def _make_members(self, data, info): 1001 return [self._make_member(key, value['type'], value.get('if'), 1002 value.get('features'), info) 1003 for (key, value) in data.items()] 1004 1005 def _def_struct_type(self, expr, info, doc): 1006 name = expr['struct'] 1007 base = expr.get('base') 1008 data = expr['data'] 1009 ifcond = expr.get('if') 1010 features = self._make_features(expr.get('features'), info) 1011 self._def_entity(QAPISchemaObjectType( 1012 name, info, doc, ifcond, features, base, 1013 self._make_members(data, info), 1014 None)) 1015 1016 def _make_variant(self, case, typ, ifcond, info): 1017 return QAPISchemaVariant(case, info, typ, ifcond) 1018 1019 def _make_simple_variant(self, case, typ, ifcond, info): 1020 if isinstance(typ, list): 1021 assert len(typ) == 1 1022 typ = self._make_array_type(typ[0], info) 1023 typ = self._make_implicit_object_type( 1024 typ, info, self.lookup_type(typ), 1025 'wrapper', [self._make_member('data', typ, None, None, info)]) 1026 return QAPISchemaVariant(case, info, typ, ifcond) 1027 1028 def _def_union_type(self, expr, info, doc): 1029 name = expr['union'] 1030 data = expr['data'] 1031 base = expr.get('base') 1032 ifcond = expr.get('if') 1033 features = self._make_features(expr.get('features'), info) 1034 tag_name = expr.get('discriminator') 1035 tag_member = None 1036 if isinstance(base, dict): 1037 base = self._make_implicit_object_type( 1038 name, info, ifcond, 1039 'base', self._make_members(base, info)) 1040 if tag_name: 1041 variants = [self._make_variant(key, value['type'], 1042 value.get('if'), info) 1043 for (key, value) in data.items()] 1044 members = [] 1045 else: 1046 variants = [self._make_simple_variant(key, value['type'], 1047 value.get('if'), info) 1048 for (key, value) in data.items()] 1049 enum = [{'name': v.name, 'if': v.ifcond} for v in variants] 1050 typ = self._make_implicit_enum_type(name, info, ifcond, enum) 1051 tag_member = QAPISchemaObjectTypeMember('type', info, typ, False) 1052 members = [tag_member] 1053 self._def_entity( 1054 QAPISchemaObjectType(name, info, doc, ifcond, features, 1055 base, members, 1056 QAPISchemaVariants( 1057 tag_name, info, tag_member, variants))) 1058 1059 def _def_alternate_type(self, expr, info, doc): 1060 name = expr['alternate'] 1061 data = expr['data'] 1062 ifcond = expr.get('if') 1063 features = self._make_features(expr.get('features'), info) 1064 variants = [self._make_variant(key, value['type'], value.get('if'), 1065 info) 1066 for (key, value) in data.items()] 1067 tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False) 1068 self._def_entity( 1069 QAPISchemaAlternateType(name, info, doc, ifcond, features, 1070 QAPISchemaVariants( 1071 None, info, tag_member, variants))) 1072 1073 def _def_command(self, expr, info, doc): 1074 name = expr['command'] 1075 data = expr.get('data') 1076 rets = expr.get('returns') 1077 gen = expr.get('gen', True) 1078 success_response = expr.get('success-response', True) 1079 boxed = expr.get('boxed', False) 1080 allow_oob = expr.get('allow-oob', False) 1081 allow_preconfig = expr.get('allow-preconfig', False) 1082 coroutine = expr.get('coroutine', False) 1083 ifcond = expr.get('if') 1084 features = self._make_features(expr.get('features'), info) 1085 if isinstance(data, OrderedDict): 1086 data = self._make_implicit_object_type( 1087 name, info, ifcond, 1088 'arg', self._make_members(data, info)) 1089 if isinstance(rets, list): 1090 assert len(rets) == 1 1091 rets = self._make_array_type(rets[0], info) 1092 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, features, 1093 data, rets, 1094 gen, success_response, 1095 boxed, allow_oob, allow_preconfig, 1096 coroutine)) 1097 1098 def _def_event(self, expr, info, doc): 1099 name = expr['event'] 1100 data = expr.get('data') 1101 boxed = expr.get('boxed', False) 1102 ifcond = expr.get('if') 1103 features = self._make_features(expr.get('features'), info) 1104 if isinstance(data, OrderedDict): 1105 data = self._make_implicit_object_type( 1106 name, info, ifcond, 1107 'arg', self._make_members(data, info)) 1108 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, features, 1109 data, boxed)) 1110 1111 def _def_exprs(self, exprs): 1112 for expr_elem in exprs: 1113 expr = expr_elem['expr'] 1114 info = expr_elem['info'] 1115 doc = expr_elem.get('doc') 1116 if 'enum' in expr: 1117 self._def_enum_type(expr, info, doc) 1118 elif 'struct' in expr: 1119 self._def_struct_type(expr, info, doc) 1120 elif 'union' in expr: 1121 self._def_union_type(expr, info, doc) 1122 elif 'alternate' in expr: 1123 self._def_alternate_type(expr, info, doc) 1124 elif 'command' in expr: 1125 self._def_command(expr, info, doc) 1126 elif 'event' in expr: 1127 self._def_event(expr, info, doc) 1128 elif 'include' in expr: 1129 self._def_include(expr, info, doc) 1130 else: 1131 assert False 1132 1133 def check(self): 1134 for ent in self._entity_list: 1135 ent.check(self) 1136 ent.connect_doc() 1137 ent.check_doc() 1138 for ent in self._entity_list: 1139 ent.set_module(self) 1140 1141 def visit(self, visitor): 1142 visitor.visit_begin(self) 1143 for mod in self._module_dict.values(): 1144 mod.visit(visitor) 1145 visitor.visit_end() 1146