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