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