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