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