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