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