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