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, members, prefix): 113 pass 114 115 def visit_array_type(self, name, info, ifcond, element_type): 116 pass 117 118 def visit_object_type(self, name, info, ifcond, base, members, variants, 119 features): 120 pass 121 122 def visit_object_type_flat(self, name, info, ifcond, members, variants, 123 features): 124 pass 125 126 def visit_alternate_type(self, name, info, ifcond, variants): 127 pass 128 129 def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, 130 success_response, boxed, allow_oob, allow_preconfig, 131 features): 132 pass 133 134 def visit_event(self, name, info, ifcond, arg_type, boxed): 135 pass 136 137 138class QAPISchemaModule: 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, members, prefix): 238 super().__init__(name, info, doc, ifcond) 239 for m in members: 240 assert isinstance(m, QAPISchemaEnumMember) 241 m.set_defined_in(name) 242 assert prefix is None or isinstance(prefix, str) 243 self.members = members 244 self.prefix = prefix 245 246 def check(self, schema): 247 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(self.name, self.info, self.ifcond, 275 self.members, self.prefix) 276 277 278class QAPISchemaArrayType(QAPISchemaType): 279 meta = 'array' 280 281 def __init__(self, name, info, element_type): 282 super().__init__(name, info, None, None) 283 assert isinstance(element_type, str) 284 self._element_type_name = element_type 285 self.element_type = None 286 287 def check(self, schema): 288 super().check(schema) 289 self.element_type = schema.resolve_type( 290 self._element_type_name, self.info, 291 self.info and self.info.defn_meta) 292 assert not isinstance(self.element_type, QAPISchemaArrayType) 293 294 def set_module(self, schema): 295 self._set_module(schema, self.element_type.info) 296 297 @property 298 def ifcond(self): 299 assert self._checked 300 return self.element_type.ifcond 301 302 def is_implicit(self): 303 return True 304 305 def c_type(self): 306 return c_name(self.name) + pointer_suffix 307 308 def json_type(self): 309 return 'array' 310 311 def doc_type(self): 312 elt_doc_type = self.element_type.doc_type() 313 if not elt_doc_type: 314 return None 315 return 'array of ' + elt_doc_type 316 317 def visit(self, visitor): 318 super().visit(visitor) 319 visitor.visit_array_type(self.name, self.info, self.ifcond, 320 self.element_type) 321 322 def describe(self): 323 assert self.meta 324 return "%s type ['%s']" % (self.meta, self._element_type_name) 325 326 327class QAPISchemaObjectType(QAPISchemaType): 328 def __init__(self, name, info, doc, ifcond, 329 base, local_members, variants, features): 330 # struct has local_members, optional base, and no variants 331 # flat union has base, variants, and no local_members 332 # simple union has local_members, variants, and no base 333 super().__init__(name, info, doc, ifcond, features) 334 self.meta = 'union' if variants else 'struct' 335 assert base is None or isinstance(base, str) 336 for m in local_members: 337 assert isinstance(m, QAPISchemaObjectTypeMember) 338 m.set_defined_in(name) 339 if variants is not None: 340 assert isinstance(variants, QAPISchemaObjectTypeVariants) 341 variants.set_defined_in(name) 342 self._base_name = base 343 self.base = None 344 self.local_members = local_members 345 self.variants = variants 346 self.members = None 347 348 def check(self, schema): 349 # This calls another type T's .check() exactly when the C 350 # struct emitted by gen_object() contains that T's C struct 351 # (pointers don't count). 352 if self.members is not None: 353 # A previous .check() completed: nothing to do 354 return 355 if self._checked: 356 # Recursed: C struct contains itself 357 raise QAPISemError(self.info, 358 "object %s contains itself" % self.name) 359 360 super().check(schema) 361 assert self._checked and self.members is None 362 363 seen = OrderedDict() 364 if self._base_name: 365 self.base = schema.resolve_type(self._base_name, self.info, 366 "'base'") 367 if (not isinstance(self.base, QAPISchemaObjectType) 368 or self.base.variants): 369 raise QAPISemError( 370 self.info, 371 "'base' requires a struct type, %s isn't" 372 % self.base.describe()) 373 self.base.check(schema) 374 self.base.check_clash(self.info, seen) 375 for m in self.local_members: 376 m.check(schema) 377 m.check_clash(self.info, seen) 378 members = seen.values() 379 380 if self.variants: 381 self.variants.check(schema, seen) 382 self.variants.check_clash(self.info, seen) 383 384 self.members = members # mark completed 385 386 # Check that the members of this type do not cause duplicate JSON members, 387 # and update seen to track the members seen so far. Report any errors 388 # on behalf of info, which is not necessarily self.info 389 def check_clash(self, info, seen): 390 assert self._checked 391 assert not self.variants # not implemented 392 for m in self.members: 393 m.check_clash(info, seen) 394 395 def connect_doc(self, doc=None): 396 super().connect_doc(doc) 397 doc = doc or self.doc 398 if doc: 399 if self.base and self.base.is_implicit(): 400 self.base.connect_doc(doc) 401 for m in self.local_members: 402 doc.connect_member(m) 403 404 @property 405 def ifcond(self): 406 assert self._checked 407 if isinstance(self._ifcond, QAPISchemaType): 408 # Simple union wrapper type inherits from wrapped type; 409 # see _make_implicit_object_type() 410 return self._ifcond.ifcond 411 return self._ifcond 412 413 def is_implicit(self): 414 # See QAPISchema._make_implicit_object_type(), as well as 415 # _def_predefineds() 416 return self.name.startswith('q_') 417 418 def is_empty(self): 419 assert self.members is not None 420 return not self.members and not self.variants 421 422 def c_name(self): 423 assert self.name != 'q_empty' 424 return super().c_name() 425 426 def c_type(self): 427 assert not self.is_implicit() 428 return c_name(self.name) + pointer_suffix 429 430 def c_unboxed_type(self): 431 return c_name(self.name) 432 433 def json_type(self): 434 return 'object' 435 436 def visit(self, visitor): 437 super().visit(visitor) 438 visitor.visit_object_type(self.name, self.info, self.ifcond, 439 self.base, self.local_members, self.variants, 440 self.features) 441 visitor.visit_object_type_flat(self.name, self.info, self.ifcond, 442 self.members, self.variants, 443 self.features) 444 445 446class QAPISchemaMember: 447 """ Represents object members, enum members and features """ 448 role = 'member' 449 450 def __init__(self, name, info, ifcond=None): 451 assert isinstance(name, str) 452 self.name = name 453 self.info = info 454 self.ifcond = ifcond or [] 455 self.defined_in = None 456 457 def set_defined_in(self, name): 458 assert not self.defined_in 459 self.defined_in = name 460 461 def check_clash(self, info, seen): 462 cname = c_name(self.name) 463 if cname in seen: 464 raise QAPISemError( 465 info, 466 "%s collides with %s" 467 % (self.describe(info), seen[cname].describe(info))) 468 seen[cname] = self 469 470 def describe(self, info): 471 role = self.role 472 defined_in = self.defined_in 473 assert defined_in 474 475 if defined_in.startswith('q_obj_'): 476 # See QAPISchema._make_implicit_object_type() - reverse the 477 # mapping there to create a nice human-readable description 478 defined_in = defined_in[6:] 479 if defined_in.endswith('-arg'): 480 # Implicit type created for a command's dict 'data' 481 assert role == 'member' 482 role = 'parameter' 483 elif defined_in.endswith('-base'): 484 # Implicit type created for a flat union's dict 'base' 485 role = 'base ' + role 486 else: 487 # Implicit type created for a simple union's branch 488 assert defined_in.endswith('-wrapper') 489 # Unreachable and not implemented 490 assert False 491 elif defined_in.endswith('Kind'): 492 # See QAPISchema._make_implicit_enum_type() 493 # Implicit enum created for simple union's branches 494 assert role == 'value' 495 role = 'branch' 496 elif defined_in != info.defn_name: 497 return "%s '%s' of type '%s'" % (role, self.name, defined_in) 498 return "%s '%s'" % (role, self.name) 499 500 501class QAPISchemaEnumMember(QAPISchemaMember): 502 role = 'value' 503 504 505class QAPISchemaFeature(QAPISchemaMember): 506 role = 'feature' 507 508 509class QAPISchemaObjectTypeMember(QAPISchemaMember): 510 def __init__(self, name, info, typ, optional, ifcond=None): 511 super().__init__(name, info, ifcond) 512 assert isinstance(typ, str) 513 assert isinstance(optional, bool) 514 self._type_name = typ 515 self.type = None 516 self.optional = optional 517 518 def check(self, schema): 519 assert self.defined_in 520 self.type = schema.resolve_type(self._type_name, self.info, 521 self.describe) 522 523 524class QAPISchemaObjectTypeVariants: 525 def __init__(self, tag_name, info, tag_member, variants): 526 # Flat unions pass tag_name but not tag_member. 527 # Simple unions and alternates pass tag_member but not tag_name. 528 # After check(), tag_member is always set, and tag_name remains 529 # a reliable witness of being used by a flat union. 530 assert bool(tag_member) != bool(tag_name) 531 assert (isinstance(tag_name, str) or 532 isinstance(tag_member, QAPISchemaObjectTypeMember)) 533 for v in variants: 534 assert isinstance(v, QAPISchemaObjectTypeVariant) 535 self._tag_name = tag_name 536 self.info = info 537 self.tag_member = tag_member 538 self.variants = variants 539 540 def set_defined_in(self, name): 541 for v in self.variants: 542 v.set_defined_in(name) 543 544 def check(self, schema, seen): 545 if not self.tag_member: # flat union 546 self.tag_member = seen.get(c_name(self._tag_name)) 547 base = "'base'" 548 # Pointing to the base type when not implicit would be 549 # nice, but we don't know it here 550 if not self.tag_member or self._tag_name != self.tag_member.name: 551 raise QAPISemError( 552 self.info, 553 "discriminator '%s' is not a member of %s" 554 % (self._tag_name, base)) 555 # Here we do: 556 base_type = schema.lookup_type(self.tag_member.defined_in) 557 assert base_type 558 if not base_type.is_implicit(): 559 base = "base type '%s'" % self.tag_member.defined_in 560 if not isinstance(self.tag_member.type, QAPISchemaEnumType): 561 raise QAPISemError( 562 self.info, 563 "discriminator member '%s' of %s must be of enum type" 564 % (self._tag_name, base)) 565 if self.tag_member.optional: 566 raise QAPISemError( 567 self.info, 568 "discriminator member '%s' of %s must not be optional" 569 % (self._tag_name, base)) 570 if self.tag_member.ifcond: 571 raise QAPISemError( 572 self.info, 573 "discriminator member '%s' of %s must not be conditional" 574 % (self._tag_name, base)) 575 else: # simple union 576 assert isinstance(self.tag_member.type, QAPISchemaEnumType) 577 assert not self.tag_member.optional 578 assert self.tag_member.ifcond == [] 579 if self._tag_name: # flat union 580 # branches that are not explicitly covered get an empty type 581 cases = {v.name for v in self.variants} 582 for m in self.tag_member.type.members: 583 if m.name not in cases: 584 v = QAPISchemaObjectTypeVariant(m.name, self.info, 585 'q_empty', m.ifcond) 586 v.set_defined_in(self.tag_member.defined_in) 587 self.variants.append(v) 588 if not self.variants: 589 raise QAPISemError(self.info, "union has no branches") 590 for v in self.variants: 591 v.check(schema) 592 # Union names must match enum values; alternate names are 593 # checked separately. Use 'seen' to tell the two apart. 594 if seen: 595 if v.name not in self.tag_member.type.member_names(): 596 raise QAPISemError( 597 self.info, 598 "branch '%s' is not a value of %s" 599 % (v.name, self.tag_member.type.describe())) 600 if (not isinstance(v.type, QAPISchemaObjectType) 601 or v.type.variants): 602 raise QAPISemError( 603 self.info, 604 "%s cannot use %s" 605 % (v.describe(self.info), v.type.describe())) 606 v.type.check(schema) 607 608 def check_clash(self, info, seen): 609 for v in self.variants: 610 # Reset seen map for each variant, since qapi names from one 611 # branch do not affect another branch 612 v.type.check_clash(info, dict(seen)) 613 614 615class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): 616 role = 'branch' 617 618 def __init__(self, name, info, typ, ifcond=None): 619 super().__init__(name, info, typ, False, ifcond) 620 621 622class QAPISchemaAlternateType(QAPISchemaType): 623 meta = 'alternate' 624 625 def __init__(self, name, info, doc, ifcond, variants): 626 super().__init__(name, info, doc, ifcond) 627 assert isinstance(variants, QAPISchemaObjectTypeVariants) 628 assert variants.tag_member 629 variants.set_defined_in(name) 630 variants.tag_member.set_defined_in(self.name) 631 self.variants = variants 632 633 def check(self, schema): 634 super().check(schema) 635 self.variants.tag_member.check(schema) 636 # Not calling self.variants.check_clash(), because there's nothing 637 # to clash with 638 self.variants.check(schema, {}) 639 # Alternate branch names have no relation to the tag enum values; 640 # so we have to check for potential name collisions ourselves. 641 seen = {} 642 types_seen = {} 643 for v in self.variants.variants: 644 v.check_clash(self.info, seen) 645 qtype = v.type.alternate_qtype() 646 if not qtype: 647 raise QAPISemError( 648 self.info, 649 "%s cannot use %s" 650 % (v.describe(self.info), v.type.describe())) 651 conflicting = set([qtype]) 652 if qtype == 'QTYPE_QSTRING': 653 if isinstance(v.type, QAPISchemaEnumType): 654 for m in v.type.members: 655 if m.name in ['on', 'off']: 656 conflicting.add('QTYPE_QBOOL') 657 if re.match(r'[-+0-9.]', m.name): 658 # lazy, could be tightened 659 conflicting.add('QTYPE_QNUM') 660 else: 661 conflicting.add('QTYPE_QNUM') 662 conflicting.add('QTYPE_QBOOL') 663 for qt in conflicting: 664 if qt in types_seen: 665 raise QAPISemError( 666 self.info, 667 "%s can't be distinguished from '%s'" 668 % (v.describe(self.info), types_seen[qt])) 669 types_seen[qt] = v.name 670 671 def connect_doc(self, doc=None): 672 super().connect_doc(doc) 673 doc = doc or self.doc 674 if doc: 675 for v in self.variants.variants: 676 doc.connect_member(v) 677 678 def c_type(self): 679 return c_name(self.name) + pointer_suffix 680 681 def json_type(self): 682 return 'value' 683 684 def visit(self, visitor): 685 super().visit(visitor) 686 visitor.visit_alternate_type(self.name, self.info, self.ifcond, 687 self.variants) 688 689 690class QAPISchemaCommand(QAPISchemaEntity): 691 meta = 'command' 692 693 def __init__(self, name, info, doc, ifcond, arg_type, ret_type, 694 gen, success_response, boxed, allow_oob, allow_preconfig, 695 features): 696 super().__init__(name, info, doc, ifcond, features) 697 assert not arg_type or isinstance(arg_type, str) 698 assert not ret_type or isinstance(ret_type, str) 699 self._arg_type_name = arg_type 700 self.arg_type = None 701 self._ret_type_name = ret_type 702 self.ret_type = None 703 self.gen = gen 704 self.success_response = success_response 705 self.boxed = boxed 706 self.allow_oob = allow_oob 707 self.allow_preconfig = allow_preconfig 708 709 def check(self, schema): 710 super().check(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 typ = self.ret_type 729 if isinstance(typ, QAPISchemaArrayType): 730 typ = self.ret_type.element_type 731 assert typ 732 if not isinstance(typ, QAPISchemaObjectType): 733 raise QAPISemError( 734 self.info, 735 "command's 'returns' cannot take %s" 736 % self.ret_type.describe()) 737 738 def connect_doc(self, doc=None): 739 super().connect_doc(doc) 740 doc = doc or self.doc 741 if doc: 742 if self.arg_type and self.arg_type.is_implicit(): 743 self.arg_type.connect_doc(doc) 744 745 def visit(self, visitor): 746 super().visit(visitor) 747 visitor.visit_command(self.name, self.info, self.ifcond, 748 self.arg_type, self.ret_type, 749 self.gen, self.success_response, 750 self.boxed, self.allow_oob, 751 self.allow_preconfig, 752 self.features) 753 754 755class QAPISchemaEvent(QAPISchemaEntity): 756 meta = 'event' 757 758 def __init__(self, name, info, doc, ifcond, arg_type, boxed): 759 super().__init__(name, info, doc, ifcond) 760 assert not arg_type or isinstance(arg_type, str) 761 self._arg_type_name = arg_type 762 self.arg_type = None 763 self.boxed = boxed 764 765 def check(self, schema): 766 super().check(schema) 767 if self._arg_type_name: 768 self.arg_type = schema.resolve_type( 769 self._arg_type_name, self.info, "event's 'data'") 770 if not isinstance(self.arg_type, QAPISchemaObjectType): 771 raise QAPISemError( 772 self.info, 773 "event's 'data' cannot take %s" 774 % self.arg_type.describe()) 775 if self.arg_type.variants and not self.boxed: 776 raise QAPISemError( 777 self.info, 778 "event's 'data' can take %s only with 'boxed': true" 779 % self.arg_type.describe()) 780 781 def connect_doc(self, doc=None): 782 super().connect_doc(doc) 783 doc = doc or self.doc 784 if doc: 785 if self.arg_type and self.arg_type.is_implicit(): 786 self.arg_type.connect_doc(doc) 787 788 def visit(self, visitor): 789 super().visit(visitor) 790 visitor.visit_event(self.name, self.info, self.ifcond, 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, []) 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, 905 qtype_values, 'QTYPE')) 906 907 def _make_features(self, features, info): 908 return [QAPISchemaFeature(f['name'], info, f.get('if')) 909 for f in features] 910 911 def _make_enum_members(self, values, info): 912 return [QAPISchemaEnumMember(v['name'], info, v.get('if')) 913 for v in values] 914 915 def _make_implicit_enum_type(self, name, info, ifcond, values): 916 # See also QAPISchemaObjectTypeMember.describe() 917 name = name + 'Kind' # reserved by check_defn_name_str() 918 self._def_entity(QAPISchemaEnumType( 919 name, info, None, ifcond, self._make_enum_members(values, info), 920 None)) 921 return name 922 923 def _make_array_type(self, element_type, info): 924 name = element_type + 'List' # reserved by check_defn_name_str() 925 if not self.lookup_type(name): 926 self._def_entity(QAPISchemaArrayType(name, info, element_type)) 927 return name 928 929 def _make_implicit_object_type(self, name, info, ifcond, role, members): 930 if not members: 931 return None 932 # See also QAPISchemaObjectTypeMember.describe() 933 name = 'q_obj_%s-%s' % (name, role) 934 typ = self.lookup_entity(name, QAPISchemaObjectType) 935 if typ: 936 # The implicit object type has multiple users. This can 937 # happen only for simple unions' implicit wrapper types. 938 # Its ifcond should be the disjunction of its user's 939 # ifconds. Not implemented. Instead, we always pass the 940 # wrapped type's ifcond, which is trivially the same for all 941 # users. It's also necessary for the wrapper to compile. 942 # But it's not tight: the disjunction need not imply it. We 943 # may end up compiling useless wrapper types. 944 # TODO kill simple unions or implement the disjunction 945 assert (ifcond or []) == typ._ifcond # pylint: disable=protected-access 946 else: 947 self._def_entity(QAPISchemaObjectType(name, info, None, ifcond, 948 None, members, None, [])) 949 return name 950 951 def _def_enum_type(self, expr, info, doc): 952 name = expr['enum'] 953 data = expr['data'] 954 prefix = expr.get('prefix') 955 ifcond = expr.get('if') 956 self._def_entity(QAPISchemaEnumType( 957 name, info, doc, ifcond, 958 self._make_enum_members(data, info), prefix)) 959 960 def _make_member(self, name, typ, ifcond, info): 961 optional = False 962 if name.startswith('*'): 963 name = name[1:] 964 optional = True 965 if isinstance(typ, list): 966 assert len(typ) == 1 967 typ = self._make_array_type(typ[0], info) 968 return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond) 969 970 def _make_members(self, data, info): 971 return [self._make_member(key, value['type'], value.get('if'), info) 972 for (key, value) in data.items()] 973 974 def _def_struct_type(self, expr, info, doc): 975 name = expr['struct'] 976 base = expr.get('base') 977 data = expr['data'] 978 ifcond = expr.get('if') 979 features = expr.get('features', []) 980 self._def_entity(QAPISchemaObjectType( 981 name, info, doc, ifcond, base, 982 self._make_members(data, info), 983 None, 984 self._make_features(features, info))) 985 986 def _make_variant(self, case, typ, ifcond, info): 987 return QAPISchemaObjectTypeVariant(case, info, typ, ifcond) 988 989 def _make_simple_variant(self, case, typ, ifcond, info): 990 if isinstance(typ, list): 991 assert len(typ) == 1 992 typ = self._make_array_type(typ[0], info) 993 typ = self._make_implicit_object_type( 994 typ, info, self.lookup_type(typ), 995 'wrapper', [self._make_member('data', typ, None, info)]) 996 return QAPISchemaObjectTypeVariant(case, info, typ, ifcond) 997 998 def _def_union_type(self, expr, info, doc): 999 name = expr['union'] 1000 data = expr['data'] 1001 base = expr.get('base') 1002 ifcond = expr.get('if') 1003 tag_name = expr.get('discriminator') 1004 tag_member = None 1005 if isinstance(base, dict): 1006 base = self._make_implicit_object_type( 1007 name, info, ifcond, 1008 'base', self._make_members(base, info)) 1009 if tag_name: 1010 variants = [self._make_variant(key, value['type'], 1011 value.get('if'), info) 1012 for (key, value) in data.items()] 1013 members = [] 1014 else: 1015 variants = [self._make_simple_variant(key, value['type'], 1016 value.get('if'), info) 1017 for (key, value) in data.items()] 1018 enum = [{'name': v.name, 'if': v.ifcond} for v in variants] 1019 typ = self._make_implicit_enum_type(name, info, ifcond, enum) 1020 tag_member = QAPISchemaObjectTypeMember('type', info, typ, False) 1021 members = [tag_member] 1022 self._def_entity( 1023 QAPISchemaObjectType(name, info, doc, ifcond, base, members, 1024 QAPISchemaObjectTypeVariants( 1025 tag_name, info, tag_member, variants), 1026 [])) 1027 1028 def _def_alternate_type(self, expr, info, doc): 1029 name = expr['alternate'] 1030 data = expr['data'] 1031 ifcond = expr.get('if') 1032 variants = [self._make_variant(key, value['type'], value.get('if'), 1033 info) 1034 for (key, value) in data.items()] 1035 tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False) 1036 self._def_entity( 1037 QAPISchemaAlternateType(name, info, doc, ifcond, 1038 QAPISchemaObjectTypeVariants( 1039 None, info, tag_member, variants))) 1040 1041 def _def_command(self, expr, info, doc): 1042 name = expr['command'] 1043 data = expr.get('data') 1044 rets = expr.get('returns') 1045 gen = expr.get('gen', True) 1046 success_response = expr.get('success-response', True) 1047 boxed = expr.get('boxed', False) 1048 allow_oob = expr.get('allow-oob', False) 1049 allow_preconfig = expr.get('allow-preconfig', False) 1050 ifcond = expr.get('if') 1051 features = expr.get('features', []) 1052 if isinstance(data, OrderedDict): 1053 data = self._make_implicit_object_type( 1054 name, info, ifcond, 'arg', self._make_members(data, info)) 1055 if isinstance(rets, list): 1056 assert len(rets) == 1 1057 rets = self._make_array_type(rets[0], info) 1058 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets, 1059 gen, success_response, 1060 boxed, allow_oob, allow_preconfig, 1061 self._make_features(features, info))) 1062 1063 def _def_event(self, expr, info, doc): 1064 name = expr['event'] 1065 data = expr.get('data') 1066 boxed = expr.get('boxed', False) 1067 ifcond = expr.get('if') 1068 if isinstance(data, OrderedDict): 1069 data = self._make_implicit_object_type( 1070 name, info, ifcond, 'arg', self._make_members(data, info)) 1071 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed)) 1072 1073 def _def_exprs(self, exprs): 1074 for expr_elem in exprs: 1075 expr = expr_elem['expr'] 1076 info = expr_elem['info'] 1077 doc = expr_elem.get('doc') 1078 if 'enum' in expr: 1079 self._def_enum_type(expr, info, doc) 1080 elif 'struct' in expr: 1081 self._def_struct_type(expr, info, doc) 1082 elif 'union' in expr: 1083 self._def_union_type(expr, info, doc) 1084 elif 'alternate' in expr: 1085 self._def_alternate_type(expr, info, doc) 1086 elif 'command' in expr: 1087 self._def_command(expr, info, doc) 1088 elif 'event' in expr: 1089 self._def_event(expr, info, doc) 1090 elif 'include' in expr: 1091 self._def_include(expr, info, doc) 1092 else: 1093 assert False 1094 1095 def check(self): 1096 for ent in self._entity_list: 1097 ent.check(self) 1098 ent.connect_doc() 1099 ent.check_doc() 1100 for ent in self._entity_list: 1101 ent.set_module(self) 1102 1103 def visit(self, visitor): 1104 visitor.visit_begin(self) 1105 for mod in self._module_dict.values(): 1106 mod.visit(visitor) 1107 visitor.visit_end() 1108