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