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