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