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