1# 2# QAPI helper library 3# 4# Copyright IBM, Corp. 2011 5# Copyright (c) 2013-2018 Red Hat Inc. 6# 7# Authors: 8# Anthony Liguori <aliguori@us.ibm.com> 9# Markus Armbruster <armbru@redhat.com> 10# 11# This work is licensed under the terms of the GNU GPL, version 2. 12# See the COPYING file in the top-level directory. 13 14from __future__ import print_function 15import errno 16import os 17import re 18import string 19try: 20 from collections import OrderedDict 21except: 22 from ordereddict import OrderedDict 23 24builtin_types = { 25 'null': 'QTYPE_QNULL', 26 'str': 'QTYPE_QSTRING', 27 'int': 'QTYPE_QNUM', 28 'number': 'QTYPE_QNUM', 29 'bool': 'QTYPE_QBOOL', 30 'int8': 'QTYPE_QNUM', 31 'int16': 'QTYPE_QNUM', 32 'int32': 'QTYPE_QNUM', 33 'int64': 'QTYPE_QNUM', 34 'uint8': 'QTYPE_QNUM', 35 'uint16': 'QTYPE_QNUM', 36 'uint32': 'QTYPE_QNUM', 37 'uint64': 'QTYPE_QNUM', 38 'size': 'QTYPE_QNUM', 39 'any': None, # any QType possible, actually 40 'QType': 'QTYPE_QSTRING', 41} 42 43# Are documentation comments required? 44doc_required = False 45 46# Whitelist of commands allowed to return a non-dictionary 47returns_whitelist = [] 48 49# Whitelist of entities allowed to violate case conventions 50name_case_whitelist = [] 51 52enum_types = {} 53struct_types = {} 54union_types = {} 55all_names = {} 56 57# 58# Parsing the schema into expressions 59# 60 61 62def error_path(parent): 63 res = '' 64 while parent: 65 res = ('In file included from %s:%d:\n' % (parent['file'], 66 parent['line'])) + res 67 parent = parent['parent'] 68 return res 69 70 71class QAPIError(Exception): 72 def __init__(self, fname, line, col, incl_info, msg): 73 Exception.__init__(self) 74 self.fname = fname 75 self.line = line 76 self.col = col 77 self.info = incl_info 78 self.msg = msg 79 80 def __str__(self): 81 loc = '%s:%d' % (self.fname, self.line) 82 if self.col is not None: 83 loc += ':%s' % self.col 84 return error_path(self.info) + '%s: %s' % (loc, self.msg) 85 86 87class QAPIParseError(QAPIError): 88 def __init__(self, parser, msg): 89 col = 1 90 for ch in parser.src[parser.line_pos:parser.pos]: 91 if ch == '\t': 92 col = (col + 7) % 8 + 1 93 else: 94 col += 1 95 QAPIError.__init__(self, parser.fname, parser.line, col, 96 parser.incl_info, msg) 97 98 99class QAPISemError(QAPIError): 100 def __init__(self, info, msg): 101 QAPIError.__init__(self, info['file'], info['line'], None, 102 info['parent'], msg) 103 104 105class QAPIDoc(object): 106 class Section(object): 107 def __init__(self, name=None): 108 # optional section name (argument/member or section name) 109 self.name = name 110 # the list of lines for this section 111 self.text = '' 112 113 def append(self, line): 114 self.text += line.rstrip() + '\n' 115 116 class ArgSection(Section): 117 def __init__(self, name): 118 QAPIDoc.Section.__init__(self, name) 119 self.member = None 120 121 def connect(self, member): 122 self.member = member 123 124 def __init__(self, parser, info): 125 # self._parser is used to report errors with QAPIParseError. The 126 # resulting error position depends on the state of the parser. 127 # It happens to be the beginning of the comment. More or less 128 # servicable, but action at a distance. 129 self._parser = parser 130 self.info = info 131 self.symbol = None 132 self.body = QAPIDoc.Section() 133 # dict mapping parameter name to ArgSection 134 self.args = OrderedDict() 135 # a list of Section 136 self.sections = [] 137 # the current section 138 self._section = self.body 139 140 def has_section(self, name): 141 """Return True if we have a section with this name.""" 142 for i in self.sections: 143 if i.name == name: 144 return True 145 return False 146 147 def append(self, line): 148 """Parse a comment line and add it to the documentation.""" 149 line = line[1:] 150 if not line: 151 self._append_freeform(line) 152 return 153 154 if line[0] != ' ': 155 raise QAPIParseError(self._parser, "Missing space after #") 156 line = line[1:] 157 158 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't 159 # recognized, and get silently treated as ordinary text 160 if self.symbol: 161 self._append_symbol_line(line) 162 elif not self.body.text and line.startswith('@'): 163 if not line.endswith(':'): 164 raise QAPIParseError(self._parser, "Line should end with :") 165 self.symbol = line[1:-1] 166 # FIXME invalid names other than the empty string aren't flagged 167 if not self.symbol: 168 raise QAPIParseError(self._parser, "Invalid name") 169 else: 170 self._append_freeform(line) 171 172 def end_comment(self): 173 self._end_section() 174 175 def _append_symbol_line(self, line): 176 name = line.split(' ', 1)[0] 177 178 if name.startswith('@') and name.endswith(':'): 179 line = line[len(name)+1:] 180 self._start_args_section(name[1:-1]) 181 elif name in ('Returns:', 'Since:', 182 # those are often singular or plural 183 'Note:', 'Notes:', 184 'Example:', 'Examples:', 185 'TODO:'): 186 line = line[len(name)+1:] 187 self._start_section(name[:-1]) 188 189 self._append_freeform(line) 190 191 def _start_args_section(self, name): 192 # FIXME invalid names other than the empty string aren't flagged 193 if not name: 194 raise QAPIParseError(self._parser, "Invalid parameter name") 195 if name in self.args: 196 raise QAPIParseError(self._parser, 197 "'%s' parameter name duplicated" % name) 198 if self.sections: 199 raise QAPIParseError(self._parser, 200 "'@%s:' can't follow '%s' section" 201 % (name, self.sections[0].name)) 202 self._end_section() 203 self._section = QAPIDoc.ArgSection(name) 204 self.args[name] = self._section 205 206 def _start_section(self, name=None): 207 if name in ('Returns', 'Since') and self.has_section(name): 208 raise QAPIParseError(self._parser, 209 "Duplicated '%s' section" % name) 210 self._end_section() 211 self._section = QAPIDoc.Section(name) 212 self.sections.append(self._section) 213 214 def _end_section(self): 215 if self._section: 216 text = self._section.text = self._section.text.strip() 217 if self._section.name and (not text or text.isspace()): 218 raise QAPIParseError(self._parser, "Empty doc section '%s'" 219 % self._section.name) 220 self._section = None 221 222 def _append_freeform(self, line): 223 in_arg = isinstance(self._section, QAPIDoc.ArgSection) 224 if (in_arg and self._section.text.endswith('\n\n') 225 and line and not line[0].isspace()): 226 self._start_section() 227 if (in_arg or not self._section.name 228 or not self._section.name.startswith('Example')): 229 line = line.strip() 230 match = re.match(r'(@\S+:)', line) 231 if match: 232 raise QAPIParseError(self._parser, 233 "'%s' not allowed in free-form documentation" 234 % match.group(1)) 235 self._section.append(line) 236 237 def connect_member(self, member): 238 if member.name not in self.args: 239 # Undocumented TODO outlaw 240 self.args[member.name] = QAPIDoc.ArgSection(member.name) 241 self.args[member.name].connect(member) 242 243 def check_expr(self, expr): 244 if self.has_section('Returns') and 'command' not in expr: 245 raise QAPISemError(self.info, 246 "'Returns:' is only valid for commands") 247 248 def check(self): 249 bogus = [name for name, section in self.args.items() 250 if not section.member] 251 if bogus: 252 raise QAPISemError( 253 self.info, 254 "The following documented members are not in " 255 "the declaration: %s" % ", ".join(bogus)) 256 257 258class QAPISchemaParser(object): 259 260 def __init__(self, fp, previously_included=[], incl_info=None): 261 self.fname = fp.name 262 previously_included.append(os.path.abspath(fp.name)) 263 self.incl_info = incl_info 264 self.src = fp.read() 265 if self.src == '' or self.src[-1] != '\n': 266 self.src += '\n' 267 self.cursor = 0 268 self.line = 1 269 self.line_pos = 0 270 self.exprs = [] 271 self.docs = [] 272 self.accept() 273 cur_doc = None 274 275 while self.tok is not None: 276 info = {'file': self.fname, 'line': self.line, 277 'parent': self.incl_info} 278 if self.tok == '#': 279 self.reject_expr_doc(cur_doc) 280 cur_doc = self.get_doc(info) 281 self.docs.append(cur_doc) 282 continue 283 284 expr = self.get_expr(False) 285 if 'include' in expr: 286 self.reject_expr_doc(cur_doc) 287 if len(expr) != 1: 288 raise QAPISemError(info, "Invalid 'include' directive") 289 include = expr['include'] 290 if not isinstance(include, str): 291 raise QAPISemError(info, 292 "Value of 'include' must be a string") 293 self._include(include, info, os.path.dirname(self.fname), 294 previously_included) 295 elif "pragma" in expr: 296 self.reject_expr_doc(cur_doc) 297 if len(expr) != 1: 298 raise QAPISemError(info, "Invalid 'pragma' directive") 299 pragma = expr['pragma'] 300 if not isinstance(pragma, dict): 301 raise QAPISemError( 302 info, "Value of 'pragma' must be a dictionary") 303 for name, value in pragma.items(): 304 self._pragma(name, value, info) 305 else: 306 expr_elem = {'expr': expr, 307 'info': info} 308 if cur_doc: 309 if not cur_doc.symbol: 310 raise QAPISemError( 311 cur_doc.info, "Expression documentation required") 312 expr_elem['doc'] = cur_doc 313 self.exprs.append(expr_elem) 314 cur_doc = None 315 self.reject_expr_doc(cur_doc) 316 317 @staticmethod 318 def reject_expr_doc(doc): 319 if doc and doc.symbol: 320 raise QAPISemError( 321 doc.info, 322 "Documentation for '%s' is not followed by the definition" 323 % doc.symbol) 324 325 def _include(self, include, info, base_dir, previously_included): 326 incl_fname = os.path.join(base_dir, include) 327 incl_abs_fname = os.path.abspath(incl_fname) 328 # catch inclusion cycle 329 inf = info 330 while inf: 331 if incl_abs_fname == os.path.abspath(inf['file']): 332 raise QAPISemError(info, "Inclusion loop for %s" % include) 333 inf = inf['parent'] 334 335 # skip multiple include of the same file 336 if incl_abs_fname in previously_included: 337 return 338 try: 339 fobj = open(incl_fname, 'r') 340 except IOError as e: 341 raise QAPISemError(info, '%s: %s' % (e.strerror, incl_fname)) 342 exprs_include = QAPISchemaParser(fobj, previously_included, info) 343 self.exprs.extend(exprs_include.exprs) 344 self.docs.extend(exprs_include.docs) 345 346 def _pragma(self, name, value, info): 347 global doc_required, returns_whitelist, name_case_whitelist 348 if name == 'doc-required': 349 if not isinstance(value, bool): 350 raise QAPISemError(info, 351 "Pragma 'doc-required' must be boolean") 352 doc_required = value 353 elif name == 'returns-whitelist': 354 if (not isinstance(value, list) 355 or any([not isinstance(elt, str) for elt in value])): 356 raise QAPISemError(info, 357 "Pragma returns-whitelist must be" 358 " a list of strings") 359 returns_whitelist = value 360 elif name == 'name-case-whitelist': 361 if (not isinstance(value, list) 362 or any([not isinstance(elt, str) for elt in value])): 363 raise QAPISemError(info, 364 "Pragma name-case-whitelist must be" 365 " a list of strings") 366 name_case_whitelist = value 367 else: 368 raise QAPISemError(info, "Unknown pragma '%s'" % name) 369 370 def accept(self, skip_comment=True): 371 while True: 372 self.tok = self.src[self.cursor] 373 self.pos = self.cursor 374 self.cursor += 1 375 self.val = None 376 377 if self.tok == '#': 378 if self.src[self.cursor] == '#': 379 # Start of doc comment 380 skip_comment = False 381 self.cursor = self.src.find('\n', self.cursor) 382 if not skip_comment: 383 self.val = self.src[self.pos:self.cursor] 384 return 385 elif self.tok in '{}:,[]': 386 return 387 elif self.tok == "'": 388 string = '' 389 esc = False 390 while True: 391 ch = self.src[self.cursor] 392 self.cursor += 1 393 if ch == '\n': 394 raise QAPIParseError(self, 'Missing terminating "\'"') 395 if esc: 396 if ch == 'b': 397 string += '\b' 398 elif ch == 'f': 399 string += '\f' 400 elif ch == 'n': 401 string += '\n' 402 elif ch == 'r': 403 string += '\r' 404 elif ch == 't': 405 string += '\t' 406 elif ch == 'u': 407 value = 0 408 for _ in range(0, 4): 409 ch = self.src[self.cursor] 410 self.cursor += 1 411 if ch not in '0123456789abcdefABCDEF': 412 raise QAPIParseError(self, 413 '\\u escape needs 4 ' 414 'hex digits') 415 value = (value << 4) + int(ch, 16) 416 # If Python 2 and 3 didn't disagree so much on 417 # how to handle Unicode, then we could allow 418 # Unicode string defaults. But most of QAPI is 419 # ASCII-only, so we aren't losing much for now. 420 if not value or value > 0x7f: 421 raise QAPIParseError(self, 422 'For now, \\u escape ' 423 'only supports non-zero ' 424 'values up to \\u007f') 425 string += chr(value) 426 elif ch in '\\/\'"': 427 string += ch 428 else: 429 raise QAPIParseError(self, 430 "Unknown escape \\%s" % ch) 431 esc = False 432 elif ch == '\\': 433 esc = True 434 elif ch == "'": 435 self.val = string 436 return 437 else: 438 string += ch 439 elif self.src.startswith('true', self.pos): 440 self.val = True 441 self.cursor += 3 442 return 443 elif self.src.startswith('false', self.pos): 444 self.val = False 445 self.cursor += 4 446 return 447 elif self.src.startswith('null', self.pos): 448 self.val = None 449 self.cursor += 3 450 return 451 elif self.tok == '\n': 452 if self.cursor == len(self.src): 453 self.tok = None 454 return 455 self.line += 1 456 self.line_pos = self.cursor 457 elif not self.tok.isspace(): 458 raise QAPIParseError(self, 'Stray "%s"' % self.tok) 459 460 def get_members(self): 461 expr = OrderedDict() 462 if self.tok == '}': 463 self.accept() 464 return expr 465 if self.tok != "'": 466 raise QAPIParseError(self, 'Expected string or "}"') 467 while True: 468 key = self.val 469 self.accept() 470 if self.tok != ':': 471 raise QAPIParseError(self, 'Expected ":"') 472 self.accept() 473 if key in expr: 474 raise QAPIParseError(self, 'Duplicate key "%s"' % key) 475 expr[key] = self.get_expr(True) 476 if self.tok == '}': 477 self.accept() 478 return expr 479 if self.tok != ',': 480 raise QAPIParseError(self, 'Expected "," or "}"') 481 self.accept() 482 if self.tok != "'": 483 raise QAPIParseError(self, 'Expected string') 484 485 def get_values(self): 486 expr = [] 487 if self.tok == ']': 488 self.accept() 489 return expr 490 if self.tok not in "{['tfn": 491 raise QAPIParseError(self, 'Expected "{", "[", "]", string, ' 492 'boolean or "null"') 493 while True: 494 expr.append(self.get_expr(True)) 495 if self.tok == ']': 496 self.accept() 497 return expr 498 if self.tok != ',': 499 raise QAPIParseError(self, 'Expected "," or "]"') 500 self.accept() 501 502 def get_expr(self, nested): 503 if self.tok != '{' and not nested: 504 raise QAPIParseError(self, 'Expected "{"') 505 if self.tok == '{': 506 self.accept() 507 expr = self.get_members() 508 elif self.tok == '[': 509 self.accept() 510 expr = self.get_values() 511 elif self.tok in "'tfn": 512 expr = self.val 513 self.accept() 514 else: 515 raise QAPIParseError(self, 'Expected "{", "[", string, ' 516 'boolean or "null"') 517 return expr 518 519 def get_doc(self, info): 520 if self.val != '##': 521 raise QAPIParseError(self, "Junk after '##' at start of " 522 "documentation comment") 523 524 doc = QAPIDoc(self, info) 525 self.accept(False) 526 while self.tok == '#': 527 if self.val.startswith('##'): 528 # End of doc comment 529 if self.val != '##': 530 raise QAPIParseError(self, "Junk after '##' at end of " 531 "documentation comment") 532 doc.end_comment() 533 self.accept() 534 return doc 535 else: 536 doc.append(self.val) 537 self.accept(False) 538 539 raise QAPIParseError(self, "Documentation comment must end with '##'") 540 541 542# 543# Semantic analysis of schema expressions 544# TODO fold into QAPISchema 545# TODO catching name collisions in generated code would be nice 546# 547 548 549def find_base_members(base): 550 if isinstance(base, dict): 551 return base 552 base_struct_define = struct_types.get(base) 553 if not base_struct_define: 554 return None 555 return base_struct_define['data'] 556 557 558# Return the qtype of an alternate branch, or None on error. 559def find_alternate_member_qtype(qapi_type): 560 if qapi_type in builtin_types: 561 return builtin_types[qapi_type] 562 elif qapi_type in struct_types: 563 return 'QTYPE_QDICT' 564 elif qapi_type in enum_types: 565 return 'QTYPE_QSTRING' 566 elif qapi_type in union_types: 567 return 'QTYPE_QDICT' 568 return None 569 570 571# Return the discriminator enum define if discriminator is specified as an 572# enum type, otherwise return None. 573def discriminator_find_enum_define(expr): 574 base = expr.get('base') 575 discriminator = expr.get('discriminator') 576 577 if not (discriminator and base): 578 return None 579 580 base_members = find_base_members(base) 581 if not base_members: 582 return None 583 584 discriminator_type = base_members.get(discriminator) 585 if not discriminator_type: 586 return None 587 588 return enum_types.get(discriminator_type) 589 590 591# Names must be letters, numbers, -, and _. They must start with letter, 592# except for downstream extensions which must start with __RFQDN_. 593# Dots are only valid in the downstream extension prefix. 594valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?' 595 '[a-zA-Z][a-zA-Z0-9_-]*$') 596 597 598def check_name(info, source, name, allow_optional=False, 599 enum_member=False): 600 global valid_name 601 membername = name 602 603 if not isinstance(name, str): 604 raise QAPISemError(info, "%s requires a string name" % source) 605 if name.startswith('*'): 606 membername = name[1:] 607 if not allow_optional: 608 raise QAPISemError(info, "%s does not allow optional name '%s'" 609 % (source, name)) 610 # Enum members can start with a digit, because the generated C 611 # code always prefixes it with the enum name 612 if enum_member and membername[0].isdigit(): 613 membername = 'D' + membername 614 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty' 615 # and 'q_obj_*' implicit type names. 616 if not valid_name.match(membername) or \ 617 c_name(membername, False).startswith('q_'): 618 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name)) 619 620 621def add_name(name, info, meta, implicit=False): 622 global all_names 623 check_name(info, "'%s'" % meta, name) 624 # FIXME should reject names that differ only in '_' vs. '.' 625 # vs. '-', because they're liable to clash in generated C. 626 if name in all_names: 627 raise QAPISemError(info, "%s '%s' is already defined" 628 % (all_names[name], name)) 629 if not implicit and (name.endswith('Kind') or name.endswith('List')): 630 raise QAPISemError(info, "%s '%s' should not end in '%s'" 631 % (meta, name, name[-4:])) 632 all_names[name] = meta 633 634 635def check_type(info, source, value, allow_array=False, 636 allow_dict=False, allow_optional=False, 637 allow_metas=[]): 638 global all_names 639 640 if value is None: 641 return 642 643 # Check if array type for value is okay 644 if isinstance(value, list): 645 if not allow_array: 646 raise QAPISemError(info, "%s cannot be an array" % source) 647 if len(value) != 1 or not isinstance(value[0], str): 648 raise QAPISemError(info, 649 "%s: array type must contain single type name" % 650 source) 651 value = value[0] 652 653 # Check if type name for value is okay 654 if isinstance(value, str): 655 if value not in all_names: 656 raise QAPISemError(info, "%s uses unknown type '%s'" 657 % (source, value)) 658 if not all_names[value] in allow_metas: 659 raise QAPISemError(info, "%s cannot use %s type '%s'" % 660 (source, all_names[value], value)) 661 return 662 663 if not allow_dict: 664 raise QAPISemError(info, "%s should be a type name" % source) 665 666 if not isinstance(value, OrderedDict): 667 raise QAPISemError(info, 668 "%s should be a dictionary or type name" % source) 669 670 # value is a dictionary, check that each member is okay 671 for (key, arg) in value.items(): 672 check_name(info, "Member of %s" % source, key, 673 allow_optional=allow_optional) 674 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'): 675 raise QAPISemError(info, "Member of %s uses reserved name '%s'" 676 % (source, key)) 677 # Todo: allow dictionaries to represent default values of 678 # an optional argument. 679 check_type(info, "Member '%s' of %s" % (key, source), arg, 680 allow_array=True, 681 allow_metas=['built-in', 'union', 'alternate', 'struct', 682 'enum']) 683 684 685def check_command(expr, info): 686 name = expr['command'] 687 boxed = expr.get('boxed', False) 688 689 args_meta = ['struct'] 690 if boxed: 691 args_meta += ['union', 'alternate'] 692 check_type(info, "'data' for command '%s'" % name, 693 expr.get('data'), allow_dict=not boxed, allow_optional=True, 694 allow_metas=args_meta) 695 returns_meta = ['union', 'struct'] 696 if name in returns_whitelist: 697 returns_meta += ['built-in', 'alternate', 'enum'] 698 check_type(info, "'returns' for command '%s'" % name, 699 expr.get('returns'), allow_array=True, 700 allow_optional=True, allow_metas=returns_meta) 701 702 703def check_event(expr, info): 704 name = expr['event'] 705 boxed = expr.get('boxed', False) 706 707 meta = ['struct'] 708 if boxed: 709 meta += ['union', 'alternate'] 710 check_type(info, "'data' for event '%s'" % name, 711 expr.get('data'), allow_dict=not boxed, allow_optional=True, 712 allow_metas=meta) 713 714 715def check_union(expr, info): 716 name = expr['union'] 717 base = expr.get('base') 718 discriminator = expr.get('discriminator') 719 members = expr['data'] 720 721 # Two types of unions, determined by discriminator. 722 723 # With no discriminator it is a simple union. 724 if discriminator is None: 725 enum_define = None 726 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum'] 727 if base is not None: 728 raise QAPISemError(info, "Simple union '%s' must not have a base" % 729 name) 730 731 # Else, it's a flat union. 732 else: 733 # The object must have a string or dictionary 'base'. 734 check_type(info, "'base' for union '%s'" % name, 735 base, allow_dict=True, allow_optional=True, 736 allow_metas=['struct']) 737 if not base: 738 raise QAPISemError(info, "Flat union '%s' must have a base" 739 % name) 740 base_members = find_base_members(base) 741 assert base_members is not None 742 743 # The value of member 'discriminator' must name a non-optional 744 # member of the base struct. 745 check_name(info, "Discriminator of flat union '%s'" % name, 746 discriminator) 747 discriminator_type = base_members.get(discriminator) 748 if not discriminator_type: 749 raise QAPISemError(info, 750 "Discriminator '%s' is not a member of base " 751 "struct '%s'" 752 % (discriminator, base)) 753 enum_define = enum_types.get(discriminator_type) 754 allow_metas = ['struct'] 755 # Do not allow string discriminator 756 if not enum_define: 757 raise QAPISemError(info, 758 "Discriminator '%s' must be of enumeration " 759 "type" % discriminator) 760 761 # Check every branch; don't allow an empty union 762 if len(members) == 0: 763 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name) 764 for (key, value) in members.items(): 765 check_name(info, "Member of union '%s'" % name, key) 766 767 # Each value must name a known type 768 check_type(info, "Member '%s' of union '%s'" % (key, name), 769 value, allow_array=not base, allow_metas=allow_metas) 770 771 # If the discriminator names an enum type, then all members 772 # of 'data' must also be members of the enum type. 773 if enum_define: 774 if key not in enum_define['data']: 775 raise QAPISemError(info, 776 "Discriminator value '%s' is not found in " 777 "enum '%s'" 778 % (key, enum_define['enum'])) 779 780 # If discriminator is user-defined, ensure all values are covered 781 if enum_define: 782 for value in enum_define['data']: 783 if value not in members.keys(): 784 raise QAPISemError(info, "Union '%s' data missing '%s' branch" 785 % (name, value)) 786 787 788def check_alternate(expr, info): 789 name = expr['alternate'] 790 members = expr['data'] 791 types_seen = {} 792 793 # Check every branch; require at least two branches 794 if len(members) < 2: 795 raise QAPISemError(info, 796 "Alternate '%s' should have at least two branches " 797 "in 'data'" % name) 798 for (key, value) in members.items(): 799 check_name(info, "Member of alternate '%s'" % name, key) 800 801 # Ensure alternates have no type conflicts. 802 check_type(info, "Member '%s' of alternate '%s'" % (key, name), 803 value, 804 allow_metas=['built-in', 'union', 'struct', 'enum']) 805 qtype = find_alternate_member_qtype(value) 806 if not qtype: 807 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use " 808 "type '%s'" % (name, key, value)) 809 conflicting = set([qtype]) 810 if qtype == 'QTYPE_QSTRING': 811 enum_expr = enum_types.get(value) 812 if enum_expr: 813 for v in enum_expr['data']: 814 if v in ['on', 'off']: 815 conflicting.add('QTYPE_QBOOL') 816 if re.match(r'[-+0-9.]', v): # lazy, could be tightened 817 conflicting.add('QTYPE_QNUM') 818 else: 819 conflicting.add('QTYPE_QNUM') 820 conflicting.add('QTYPE_QBOOL') 821 for qt in conflicting: 822 if qt in types_seen: 823 raise QAPISemError(info, "Alternate '%s' member '%s' can't " 824 "be distinguished from member '%s'" 825 % (name, key, types_seen[qt])) 826 types_seen[qt] = key 827 828 829def check_enum(expr, info): 830 name = expr['enum'] 831 members = expr.get('data') 832 prefix = expr.get('prefix') 833 834 if not isinstance(members, list): 835 raise QAPISemError(info, 836 "Enum '%s' requires an array for 'data'" % name) 837 if prefix is not None and not isinstance(prefix, str): 838 raise QAPISemError(info, 839 "Enum '%s' requires a string for 'prefix'" % name) 840 for member in members: 841 check_name(info, "Member of enum '%s'" % name, member, 842 enum_member=True) 843 844 845def check_struct(expr, info): 846 name = expr['struct'] 847 members = expr['data'] 848 849 check_type(info, "'data' for struct '%s'" % name, members, 850 allow_dict=True, allow_optional=True) 851 check_type(info, "'base' for struct '%s'" % name, expr.get('base'), 852 allow_metas=['struct']) 853 854 855def check_keys(expr_elem, meta, required, optional=[]): 856 expr = expr_elem['expr'] 857 info = expr_elem['info'] 858 name = expr[meta] 859 if not isinstance(name, str): 860 raise QAPISemError(info, "'%s' key must have a string value" % meta) 861 required = required + [meta] 862 for (key, value) in expr.items(): 863 if key not in required and key not in optional: 864 raise QAPISemError(info, "Unknown key '%s' in %s '%s'" 865 % (key, meta, name)) 866 if (key == 'gen' or key == 'success-response') and value is not False: 867 raise QAPISemError(info, 868 "'%s' of %s '%s' should only use false value" 869 % (key, meta, name)) 870 if key == 'boxed' and value is not True: 871 raise QAPISemError(info, 872 "'%s' of %s '%s' should only use true value" 873 % (key, meta, name)) 874 for key in required: 875 if key not in expr: 876 raise QAPISemError(info, "Key '%s' is missing from %s '%s'" 877 % (key, meta, name)) 878 879 880def check_exprs(exprs): 881 global all_names 882 883 # Populate name table with names of built-in types 884 for builtin in builtin_types.keys(): 885 all_names[builtin] = 'built-in' 886 887 # Learn the types and check for valid expression keys 888 for expr_elem in exprs: 889 expr = expr_elem['expr'] 890 info = expr_elem['info'] 891 doc = expr_elem.get('doc') 892 893 if not doc and doc_required: 894 raise QAPISemError(info, 895 "Expression missing documentation comment") 896 897 if 'enum' in expr: 898 meta = 'enum' 899 check_keys(expr_elem, 'enum', ['data'], ['prefix']) 900 enum_types[expr[meta]] = expr 901 elif 'union' in expr: 902 meta = 'union' 903 check_keys(expr_elem, 'union', ['data'], 904 ['base', 'discriminator']) 905 union_types[expr[meta]] = expr 906 elif 'alternate' in expr: 907 meta = 'alternate' 908 check_keys(expr_elem, 'alternate', ['data']) 909 elif 'struct' in expr: 910 meta = 'struct' 911 check_keys(expr_elem, 'struct', ['data'], ['base']) 912 struct_types[expr[meta]] = expr 913 elif 'command' in expr: 914 meta = 'command' 915 check_keys(expr_elem, 'command', [], 916 ['data', 'returns', 'gen', 'success-response', 'boxed']) 917 elif 'event' in expr: 918 meta = 'event' 919 check_keys(expr_elem, 'event', [], ['data', 'boxed']) 920 else: 921 raise QAPISemError(expr_elem['info'], 922 "Expression is missing metatype") 923 name = expr[meta] 924 add_name(name, info, meta) 925 if doc and doc.symbol != name: 926 raise QAPISemError(info, "Definition of '%s' follows documentation" 927 " for '%s'" % (name, doc.symbol)) 928 929 # Try again for hidden UnionKind enum 930 for expr_elem in exprs: 931 expr = expr_elem['expr'] 932 if 'union' in expr and not discriminator_find_enum_define(expr): 933 name = '%sKind' % expr['union'] 934 elif 'alternate' in expr: 935 name = '%sKind' % expr['alternate'] 936 else: 937 continue 938 enum_types[name] = {'enum': name} 939 add_name(name, info, 'enum', implicit=True) 940 941 # Validate that exprs make sense 942 for expr_elem in exprs: 943 expr = expr_elem['expr'] 944 info = expr_elem['info'] 945 doc = expr_elem.get('doc') 946 947 if 'enum' in expr: 948 check_enum(expr, info) 949 elif 'union' in expr: 950 check_union(expr, info) 951 elif 'alternate' in expr: 952 check_alternate(expr, info) 953 elif 'struct' in expr: 954 check_struct(expr, info) 955 elif 'command' in expr: 956 check_command(expr, info) 957 elif 'event' in expr: 958 check_event(expr, info) 959 else: 960 assert False, 'unexpected meta type' 961 962 if doc: 963 doc.check_expr(expr) 964 965 return exprs 966 967 968# 969# Schema compiler frontend 970# 971 972class QAPISchemaEntity(object): 973 def __init__(self, name, info, doc): 974 assert isinstance(name, str) 975 self.name = name 976 # For explicitly defined entities, info points to the (explicit) 977 # definition. For builtins (and their arrays), info is None. 978 # For implicitly defined entities, info points to a place that 979 # triggered the implicit definition (there may be more than one 980 # such place). 981 self.info = info 982 self.doc = doc 983 984 def c_name(self): 985 return c_name(self.name) 986 987 def check(self, schema): 988 pass 989 990 def is_implicit(self): 991 return not self.info 992 993 def visit(self, visitor): 994 pass 995 996 997class QAPISchemaVisitor(object): 998 def visit_begin(self, schema): 999 pass 1000 1001 def visit_end(self): 1002 pass 1003 1004 def visit_needed(self, entity): 1005 # Default to visiting everything 1006 return True 1007 1008 def visit_builtin_type(self, name, info, json_type): 1009 pass 1010 1011 def visit_enum_type(self, name, info, values, prefix): 1012 pass 1013 1014 def visit_array_type(self, name, info, element_type): 1015 pass 1016 1017 def visit_object_type(self, name, info, base, members, variants): 1018 pass 1019 1020 def visit_object_type_flat(self, name, info, members, variants): 1021 pass 1022 1023 def visit_alternate_type(self, name, info, variants): 1024 pass 1025 1026 def visit_command(self, name, info, arg_type, ret_type, 1027 gen, success_response, boxed): 1028 pass 1029 1030 def visit_event(self, name, info, arg_type, boxed): 1031 pass 1032 1033 1034class QAPISchemaType(QAPISchemaEntity): 1035 # Return the C type for common use. 1036 # For the types we commonly box, this is a pointer type. 1037 def c_type(self): 1038 pass 1039 1040 # Return the C type to be used in a parameter list. 1041 def c_param_type(self): 1042 return self.c_type() 1043 1044 # Return the C type to be used where we suppress boxing. 1045 def c_unboxed_type(self): 1046 return self.c_type() 1047 1048 def json_type(self): 1049 pass 1050 1051 def alternate_qtype(self): 1052 json2qtype = { 1053 'null': 'QTYPE_QNULL', 1054 'string': 'QTYPE_QSTRING', 1055 'number': 'QTYPE_QNUM', 1056 'int': 'QTYPE_QNUM', 1057 'boolean': 'QTYPE_QBOOL', 1058 'object': 'QTYPE_QDICT' 1059 } 1060 return json2qtype.get(self.json_type()) 1061 1062 def doc_type(self): 1063 if self.is_implicit(): 1064 return None 1065 return self.name 1066 1067 1068class QAPISchemaBuiltinType(QAPISchemaType): 1069 def __init__(self, name, json_type, c_type): 1070 QAPISchemaType.__init__(self, name, None, None) 1071 assert not c_type or isinstance(c_type, str) 1072 assert json_type in ('string', 'number', 'int', 'boolean', 'null', 1073 'value') 1074 self._json_type_name = json_type 1075 self._c_type_name = c_type 1076 1077 def c_name(self): 1078 return self.name 1079 1080 def c_type(self): 1081 return self._c_type_name 1082 1083 def c_param_type(self): 1084 if self.name == 'str': 1085 return 'const ' + self._c_type_name 1086 return self._c_type_name 1087 1088 def json_type(self): 1089 return self._json_type_name 1090 1091 def doc_type(self): 1092 return self.json_type() 1093 1094 def visit(self, visitor): 1095 visitor.visit_builtin_type(self.name, self.info, self.json_type()) 1096 1097 1098class QAPISchemaEnumType(QAPISchemaType): 1099 def __init__(self, name, info, doc, values, prefix): 1100 QAPISchemaType.__init__(self, name, info, doc) 1101 for v in values: 1102 assert isinstance(v, QAPISchemaMember) 1103 v.set_owner(name) 1104 assert prefix is None or isinstance(prefix, str) 1105 self.values = values 1106 self.prefix = prefix 1107 1108 def check(self, schema): 1109 seen = {} 1110 for v in self.values: 1111 v.check_clash(self.info, seen) 1112 if self.doc: 1113 self.doc.connect_member(v) 1114 1115 def is_implicit(self): 1116 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds() 1117 return self.name.endswith('Kind') or self.name == 'QType' 1118 1119 def c_type(self): 1120 return c_name(self.name) 1121 1122 def member_names(self): 1123 return [v.name for v in self.values] 1124 1125 def json_type(self): 1126 return 'string' 1127 1128 def visit(self, visitor): 1129 visitor.visit_enum_type(self.name, self.info, 1130 self.member_names(), self.prefix) 1131 1132 1133class QAPISchemaArrayType(QAPISchemaType): 1134 def __init__(self, name, info, element_type): 1135 QAPISchemaType.__init__(self, name, info, None) 1136 assert isinstance(element_type, str) 1137 self._element_type_name = element_type 1138 self.element_type = None 1139 1140 def check(self, schema): 1141 self.element_type = schema.lookup_type(self._element_type_name) 1142 assert self.element_type 1143 1144 def is_implicit(self): 1145 return True 1146 1147 def c_type(self): 1148 return c_name(self.name) + pointer_suffix 1149 1150 def json_type(self): 1151 return 'array' 1152 1153 def doc_type(self): 1154 elt_doc_type = self.element_type.doc_type() 1155 if not elt_doc_type: 1156 return None 1157 return 'array of ' + elt_doc_type 1158 1159 def visit(self, visitor): 1160 visitor.visit_array_type(self.name, self.info, self.element_type) 1161 1162 1163class QAPISchemaObjectType(QAPISchemaType): 1164 def __init__(self, name, info, doc, base, local_members, variants): 1165 # struct has local_members, optional base, and no variants 1166 # flat union has base, variants, and no local_members 1167 # simple union has local_members, variants, and no base 1168 QAPISchemaType.__init__(self, name, info, doc) 1169 assert base is None or isinstance(base, str) 1170 for m in local_members: 1171 assert isinstance(m, QAPISchemaObjectTypeMember) 1172 m.set_owner(name) 1173 if variants is not None: 1174 assert isinstance(variants, QAPISchemaObjectTypeVariants) 1175 variants.set_owner(name) 1176 self._base_name = base 1177 self.base = None 1178 self.local_members = local_members 1179 self.variants = variants 1180 self.members = None 1181 1182 def check(self, schema): 1183 if self.members is False: # check for cycles 1184 raise QAPISemError(self.info, 1185 "Object %s contains itself" % self.name) 1186 if self.members: 1187 return 1188 self.members = False # mark as being checked 1189 seen = OrderedDict() 1190 if self._base_name: 1191 self.base = schema.lookup_type(self._base_name) 1192 assert isinstance(self.base, QAPISchemaObjectType) 1193 self.base.check(schema) 1194 self.base.check_clash(self.info, seen) 1195 for m in self.local_members: 1196 m.check(schema) 1197 m.check_clash(self.info, seen) 1198 if self.doc: 1199 self.doc.connect_member(m) 1200 self.members = seen.values() 1201 if self.variants: 1202 self.variants.check(schema, seen) 1203 assert self.variants.tag_member in self.members 1204 self.variants.check_clash(self.info, seen) 1205 if self.doc: 1206 self.doc.check() 1207 1208 # Check that the members of this type do not cause duplicate JSON members, 1209 # and update seen to track the members seen so far. Report any errors 1210 # on behalf of info, which is not necessarily self.info 1211 def check_clash(self, info, seen): 1212 assert not self.variants # not implemented 1213 for m in self.members: 1214 m.check_clash(info, seen) 1215 1216 def is_implicit(self): 1217 # See QAPISchema._make_implicit_object_type(), as well as 1218 # _def_predefineds() 1219 return self.name.startswith('q_') 1220 1221 def is_empty(self): 1222 assert self.members is not None 1223 return not self.members and not self.variants 1224 1225 def c_name(self): 1226 assert self.name != 'q_empty' 1227 return QAPISchemaType.c_name(self) 1228 1229 def c_type(self): 1230 assert not self.is_implicit() 1231 return c_name(self.name) + pointer_suffix 1232 1233 def c_unboxed_type(self): 1234 return c_name(self.name) 1235 1236 def json_type(self): 1237 return 'object' 1238 1239 def visit(self, visitor): 1240 visitor.visit_object_type(self.name, self.info, 1241 self.base, self.local_members, self.variants) 1242 visitor.visit_object_type_flat(self.name, self.info, 1243 self.members, self.variants) 1244 1245 1246class QAPISchemaMember(object): 1247 role = 'member' 1248 1249 def __init__(self, name): 1250 assert isinstance(name, str) 1251 self.name = name 1252 self.owner = None 1253 1254 def set_owner(self, name): 1255 assert not self.owner 1256 self.owner = name 1257 1258 def check_clash(self, info, seen): 1259 cname = c_name(self.name) 1260 if cname.lower() != cname and self.owner not in name_case_whitelist: 1261 raise QAPISemError(info, 1262 "%s should not use uppercase" % self.describe()) 1263 if cname in seen: 1264 raise QAPISemError(info, "%s collides with %s" % 1265 (self.describe(), seen[cname].describe())) 1266 seen[cname] = self 1267 1268 def _pretty_owner(self): 1269 owner = self.owner 1270 if owner.startswith('q_obj_'): 1271 # See QAPISchema._make_implicit_object_type() - reverse the 1272 # mapping there to create a nice human-readable description 1273 owner = owner[6:] 1274 if owner.endswith('-arg'): 1275 return '(parameter of %s)' % owner[:-4] 1276 elif owner.endswith('-base'): 1277 return '(base of %s)' % owner[:-5] 1278 else: 1279 assert owner.endswith('-wrapper') 1280 # Unreachable and not implemented 1281 assert False 1282 if owner.endswith('Kind'): 1283 # See QAPISchema._make_implicit_enum_type() 1284 return '(branch of %s)' % owner[:-4] 1285 return '(%s of %s)' % (self.role, owner) 1286 1287 def describe(self): 1288 return "'%s' %s" % (self.name, self._pretty_owner()) 1289 1290 1291class QAPISchemaObjectTypeMember(QAPISchemaMember): 1292 def __init__(self, name, typ, optional): 1293 QAPISchemaMember.__init__(self, name) 1294 assert isinstance(typ, str) 1295 assert isinstance(optional, bool) 1296 self._type_name = typ 1297 self.type = None 1298 self.optional = optional 1299 1300 def check(self, schema): 1301 assert self.owner 1302 self.type = schema.lookup_type(self._type_name) 1303 assert self.type 1304 1305 1306class QAPISchemaObjectTypeVariants(object): 1307 def __init__(self, tag_name, tag_member, variants): 1308 # Flat unions pass tag_name but not tag_member. 1309 # Simple unions and alternates pass tag_member but not tag_name. 1310 # After check(), tag_member is always set, and tag_name remains 1311 # a reliable witness of being used by a flat union. 1312 assert bool(tag_member) != bool(tag_name) 1313 assert (isinstance(tag_name, str) or 1314 isinstance(tag_member, QAPISchemaObjectTypeMember)) 1315 assert len(variants) > 0 1316 for v in variants: 1317 assert isinstance(v, QAPISchemaObjectTypeVariant) 1318 self._tag_name = tag_name 1319 self.tag_member = tag_member 1320 self.variants = variants 1321 1322 def set_owner(self, name): 1323 for v in self.variants: 1324 v.set_owner(name) 1325 1326 def check(self, schema, seen): 1327 if not self.tag_member: # flat union 1328 self.tag_member = seen[c_name(self._tag_name)] 1329 assert self._tag_name == self.tag_member.name 1330 assert isinstance(self.tag_member.type, QAPISchemaEnumType) 1331 for v in self.variants: 1332 v.check(schema) 1333 # Union names must match enum values; alternate names are 1334 # checked separately. Use 'seen' to tell the two apart. 1335 if seen: 1336 assert v.name in self.tag_member.type.member_names() 1337 assert isinstance(v.type, QAPISchemaObjectType) 1338 v.type.check(schema) 1339 1340 def check_clash(self, info, seen): 1341 for v in self.variants: 1342 # Reset seen map for each variant, since qapi names from one 1343 # branch do not affect another branch 1344 assert isinstance(v.type, QAPISchemaObjectType) 1345 v.type.check_clash(info, dict(seen)) 1346 1347 1348class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): 1349 role = 'branch' 1350 1351 def __init__(self, name, typ): 1352 QAPISchemaObjectTypeMember.__init__(self, name, typ, False) 1353 1354 1355class QAPISchemaAlternateType(QAPISchemaType): 1356 def __init__(self, name, info, doc, variants): 1357 QAPISchemaType.__init__(self, name, info, doc) 1358 assert isinstance(variants, QAPISchemaObjectTypeVariants) 1359 assert variants.tag_member 1360 variants.set_owner(name) 1361 variants.tag_member.set_owner(self.name) 1362 self.variants = variants 1363 1364 def check(self, schema): 1365 self.variants.tag_member.check(schema) 1366 # Not calling self.variants.check_clash(), because there's nothing 1367 # to clash with 1368 self.variants.check(schema, {}) 1369 # Alternate branch names have no relation to the tag enum values; 1370 # so we have to check for potential name collisions ourselves. 1371 seen = {} 1372 for v in self.variants.variants: 1373 v.check_clash(self.info, seen) 1374 if self.doc: 1375 self.doc.connect_member(v) 1376 if self.doc: 1377 self.doc.check() 1378 1379 def c_type(self): 1380 return c_name(self.name) + pointer_suffix 1381 1382 def json_type(self): 1383 return 'value' 1384 1385 def visit(self, visitor): 1386 visitor.visit_alternate_type(self.name, self.info, self.variants) 1387 1388 def is_empty(self): 1389 return False 1390 1391 1392class QAPISchemaCommand(QAPISchemaEntity): 1393 def __init__(self, name, info, doc, arg_type, ret_type, 1394 gen, success_response, boxed): 1395 QAPISchemaEntity.__init__(self, name, info, doc) 1396 assert not arg_type or isinstance(arg_type, str) 1397 assert not ret_type or isinstance(ret_type, str) 1398 self._arg_type_name = arg_type 1399 self.arg_type = None 1400 self._ret_type_name = ret_type 1401 self.ret_type = None 1402 self.gen = gen 1403 self.success_response = success_response 1404 self.boxed = boxed 1405 1406 def check(self, schema): 1407 if self._arg_type_name: 1408 self.arg_type = schema.lookup_type(self._arg_type_name) 1409 assert (isinstance(self.arg_type, QAPISchemaObjectType) or 1410 isinstance(self.arg_type, QAPISchemaAlternateType)) 1411 self.arg_type.check(schema) 1412 if self.boxed: 1413 if self.arg_type.is_empty(): 1414 raise QAPISemError(self.info, 1415 "Cannot use 'boxed' with empty type") 1416 else: 1417 assert not isinstance(self.arg_type, QAPISchemaAlternateType) 1418 assert not self.arg_type.variants 1419 elif self.boxed: 1420 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'") 1421 if self._ret_type_name: 1422 self.ret_type = schema.lookup_type(self._ret_type_name) 1423 assert isinstance(self.ret_type, QAPISchemaType) 1424 1425 def visit(self, visitor): 1426 visitor.visit_command(self.name, self.info, 1427 self.arg_type, self.ret_type, 1428 self.gen, self.success_response, self.boxed) 1429 1430 1431class QAPISchemaEvent(QAPISchemaEntity): 1432 def __init__(self, name, info, doc, arg_type, boxed): 1433 QAPISchemaEntity.__init__(self, name, info, doc) 1434 assert not arg_type or isinstance(arg_type, str) 1435 self._arg_type_name = arg_type 1436 self.arg_type = None 1437 self.boxed = boxed 1438 1439 def check(self, schema): 1440 if self._arg_type_name: 1441 self.arg_type = schema.lookup_type(self._arg_type_name) 1442 assert (isinstance(self.arg_type, QAPISchemaObjectType) or 1443 isinstance(self.arg_type, QAPISchemaAlternateType)) 1444 self.arg_type.check(schema) 1445 if self.boxed: 1446 if self.arg_type.is_empty(): 1447 raise QAPISemError(self.info, 1448 "Cannot use 'boxed' with empty type") 1449 else: 1450 assert not isinstance(self.arg_type, QAPISchemaAlternateType) 1451 assert not self.arg_type.variants 1452 elif self.boxed: 1453 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'") 1454 1455 def visit(self, visitor): 1456 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed) 1457 1458 1459class QAPISchema(object): 1460 def __init__(self, fname): 1461 parser = QAPISchemaParser(open(fname, 'r')) 1462 exprs = check_exprs(parser.exprs) 1463 self.docs = parser.docs 1464 self._entity_dict = {} 1465 self._predefining = True 1466 self._def_predefineds() 1467 self._predefining = False 1468 self._def_exprs(exprs) 1469 self.check() 1470 1471 def _def_entity(self, ent): 1472 # Only the predefined types are allowed to not have info 1473 assert ent.info or self._predefining 1474 assert ent.name not in self._entity_dict 1475 self._entity_dict[ent.name] = ent 1476 1477 def lookup_entity(self, name, typ=None): 1478 ent = self._entity_dict.get(name) 1479 if typ and not isinstance(ent, typ): 1480 return None 1481 return ent 1482 1483 def lookup_type(self, name): 1484 return self.lookup_entity(name, QAPISchemaType) 1485 1486 def _def_builtin_type(self, name, json_type, c_type): 1487 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type)) 1488 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple 1489 # qapi-types.h from a single .c, all arrays of builtins must be 1490 # declared in the first file whether or not they are used. Nicer 1491 # would be to use lazy instantiation, while figuring out how to 1492 # avoid compilation issues with multiple qapi-types.h. 1493 self._make_array_type(name, None) 1494 1495 def _def_predefineds(self): 1496 for t in [('str', 'string', 'char' + pointer_suffix), 1497 ('number', 'number', 'double'), 1498 ('int', 'int', 'int64_t'), 1499 ('int8', 'int', 'int8_t'), 1500 ('int16', 'int', 'int16_t'), 1501 ('int32', 'int', 'int32_t'), 1502 ('int64', 'int', 'int64_t'), 1503 ('uint8', 'int', 'uint8_t'), 1504 ('uint16', 'int', 'uint16_t'), 1505 ('uint32', 'int', 'uint32_t'), 1506 ('uint64', 'int', 'uint64_t'), 1507 ('size', 'int', 'uint64_t'), 1508 ('bool', 'boolean', 'bool'), 1509 ('any', 'value', 'QObject' + pointer_suffix), 1510 ('null', 'null', 'QNull' + pointer_suffix)]: 1511 self._def_builtin_type(*t) 1512 self.the_empty_object_type = QAPISchemaObjectType( 1513 'q_empty', None, None, None, [], None) 1514 self._def_entity(self.the_empty_object_type) 1515 qtype_values = self._make_enum_members(['none', 'qnull', 'qnum', 1516 'qstring', 'qdict', 'qlist', 1517 'qbool']) 1518 self._def_entity(QAPISchemaEnumType('QType', None, None, 1519 qtype_values, 'QTYPE')) 1520 1521 def _make_enum_members(self, values): 1522 return [QAPISchemaMember(v) for v in values] 1523 1524 def _make_implicit_enum_type(self, name, info, values): 1525 # See also QAPISchemaObjectTypeMember._pretty_owner() 1526 name = name + 'Kind' # Use namespace reserved by add_name() 1527 self._def_entity(QAPISchemaEnumType( 1528 name, info, None, self._make_enum_members(values), None)) 1529 return name 1530 1531 def _make_array_type(self, element_type, info): 1532 name = element_type + 'List' # Use namespace reserved by add_name() 1533 if not self.lookup_type(name): 1534 self._def_entity(QAPISchemaArrayType(name, info, element_type)) 1535 return name 1536 1537 def _make_implicit_object_type(self, name, info, doc, role, members): 1538 if not members: 1539 return None 1540 # See also QAPISchemaObjectTypeMember._pretty_owner() 1541 name = 'q_obj_%s-%s' % (name, role) 1542 if not self.lookup_entity(name, QAPISchemaObjectType): 1543 self._def_entity(QAPISchemaObjectType(name, info, doc, None, 1544 members, None)) 1545 return name 1546 1547 def _def_enum_type(self, expr, info, doc): 1548 name = expr['enum'] 1549 data = expr['data'] 1550 prefix = expr.get('prefix') 1551 self._def_entity(QAPISchemaEnumType( 1552 name, info, doc, self._make_enum_members(data), prefix)) 1553 1554 def _make_member(self, name, typ, info): 1555 optional = False 1556 if name.startswith('*'): 1557 name = name[1:] 1558 optional = True 1559 if isinstance(typ, list): 1560 assert len(typ) == 1 1561 typ = self._make_array_type(typ[0], info) 1562 return QAPISchemaObjectTypeMember(name, typ, optional) 1563 1564 def _make_members(self, data, info): 1565 return [self._make_member(key, value, info) 1566 for (key, value) in data.items()] 1567 1568 def _def_struct_type(self, expr, info, doc): 1569 name = expr['struct'] 1570 base = expr.get('base') 1571 data = expr['data'] 1572 self._def_entity(QAPISchemaObjectType(name, info, doc, base, 1573 self._make_members(data, info), 1574 None)) 1575 1576 def _make_variant(self, case, typ): 1577 return QAPISchemaObjectTypeVariant(case, typ) 1578 1579 def _make_simple_variant(self, case, typ, info): 1580 if isinstance(typ, list): 1581 assert len(typ) == 1 1582 typ = self._make_array_type(typ[0], info) 1583 typ = self._make_implicit_object_type( 1584 typ, info, None, 'wrapper', [self._make_member('data', typ, info)]) 1585 return QAPISchemaObjectTypeVariant(case, typ) 1586 1587 def _def_union_type(self, expr, info, doc): 1588 name = expr['union'] 1589 data = expr['data'] 1590 base = expr.get('base') 1591 tag_name = expr.get('discriminator') 1592 tag_member = None 1593 if isinstance(base, dict): 1594 base = (self._make_implicit_object_type( 1595 name, info, doc, 'base', self._make_members(base, info))) 1596 if tag_name: 1597 variants = [self._make_variant(key, value) 1598 for (key, value) in data.items()] 1599 members = [] 1600 else: 1601 variants = [self._make_simple_variant(key, value, info) 1602 for (key, value) in data.items()] 1603 typ = self._make_implicit_enum_type(name, info, 1604 [v.name for v in variants]) 1605 tag_member = QAPISchemaObjectTypeMember('type', typ, False) 1606 members = [tag_member] 1607 self._def_entity( 1608 QAPISchemaObjectType(name, info, doc, base, members, 1609 QAPISchemaObjectTypeVariants(tag_name, 1610 tag_member, 1611 variants))) 1612 1613 def _def_alternate_type(self, expr, info, doc): 1614 name = expr['alternate'] 1615 data = expr['data'] 1616 variants = [self._make_variant(key, value) 1617 for (key, value) in data.items()] 1618 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False) 1619 self._def_entity( 1620 QAPISchemaAlternateType(name, info, doc, 1621 QAPISchemaObjectTypeVariants(None, 1622 tag_member, 1623 variants))) 1624 1625 def _def_command(self, expr, info, doc): 1626 name = expr['command'] 1627 data = expr.get('data') 1628 rets = expr.get('returns') 1629 gen = expr.get('gen', True) 1630 success_response = expr.get('success-response', True) 1631 boxed = expr.get('boxed', False) 1632 if isinstance(data, OrderedDict): 1633 data = self._make_implicit_object_type( 1634 name, info, doc, 'arg', self._make_members(data, info)) 1635 if isinstance(rets, list): 1636 assert len(rets) == 1 1637 rets = self._make_array_type(rets[0], info) 1638 self._def_entity(QAPISchemaCommand(name, info, doc, data, rets, 1639 gen, success_response, boxed)) 1640 1641 def _def_event(self, expr, info, doc): 1642 name = expr['event'] 1643 data = expr.get('data') 1644 boxed = expr.get('boxed', False) 1645 if isinstance(data, OrderedDict): 1646 data = self._make_implicit_object_type( 1647 name, info, doc, 'arg', self._make_members(data, info)) 1648 self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed)) 1649 1650 def _def_exprs(self, exprs): 1651 for expr_elem in exprs: 1652 expr = expr_elem['expr'] 1653 info = expr_elem['info'] 1654 doc = expr_elem.get('doc') 1655 if 'enum' in expr: 1656 self._def_enum_type(expr, info, doc) 1657 elif 'struct' in expr: 1658 self._def_struct_type(expr, info, doc) 1659 elif 'union' in expr: 1660 self._def_union_type(expr, info, doc) 1661 elif 'alternate' in expr: 1662 self._def_alternate_type(expr, info, doc) 1663 elif 'command' in expr: 1664 self._def_command(expr, info, doc) 1665 elif 'event' in expr: 1666 self._def_event(expr, info, doc) 1667 else: 1668 assert False 1669 1670 def check(self): 1671 for (name, ent) in sorted(self._entity_dict.items()): 1672 ent.check(self) 1673 1674 def visit(self, visitor): 1675 visitor.visit_begin(self) 1676 for (name, entity) in sorted(self._entity_dict.items()): 1677 if visitor.visit_needed(entity): 1678 entity.visit(visitor) 1679 visitor.visit_end() 1680 1681 1682# 1683# Code generation helpers 1684# 1685 1686def camel_case(name): 1687 new_name = '' 1688 first = True 1689 for ch in name: 1690 if ch in ['_', '-']: 1691 first = True 1692 elif first: 1693 new_name += ch.upper() 1694 first = False 1695 else: 1696 new_name += ch.lower() 1697 return new_name 1698 1699 1700# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1 1701# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2 1702# ENUM24_Name -> ENUM24_NAME 1703def camel_to_upper(value): 1704 c_fun_str = c_name(value, False) 1705 if value.isupper(): 1706 return c_fun_str 1707 1708 new_name = '' 1709 l = len(c_fun_str) 1710 for i in range(l): 1711 c = c_fun_str[i] 1712 # When c is upper and no '_' appears before, do more checks 1713 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_': 1714 if i < l - 1 and c_fun_str[i + 1].islower(): 1715 new_name += '_' 1716 elif c_fun_str[i - 1].isdigit(): 1717 new_name += '_' 1718 new_name += c 1719 return new_name.lstrip('_').upper() 1720 1721 1722def c_enum_const(type_name, const_name, prefix=None): 1723 if prefix is not None: 1724 type_name = prefix 1725 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper() 1726 1727if hasattr(str, 'maketrans'): 1728 c_name_trans = str.maketrans('.-', '__') 1729else: 1730 c_name_trans = string.maketrans('.-', '__') 1731 1732 1733# Map @name to a valid C identifier. 1734# If @protect, avoid returning certain ticklish identifiers (like 1735# C keywords) by prepending 'q_'. 1736# 1737# Used for converting 'name' from a 'name':'type' qapi definition 1738# into a generated struct member, as well as converting type names 1739# into substrings of a generated C function name. 1740# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo' 1741# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int' 1742def c_name(name, protect=True): 1743 # ANSI X3J11/88-090, 3.1.1 1744 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue', 1745 'default', 'do', 'double', 'else', 'enum', 'extern', 1746 'float', 'for', 'goto', 'if', 'int', 'long', 'register', 1747 'return', 'short', 'signed', 'sizeof', 'static', 1748 'struct', 'switch', 'typedef', 'union', 'unsigned', 1749 'void', 'volatile', 'while']) 1750 # ISO/IEC 9899:1999, 6.4.1 1751 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary']) 1752 # ISO/IEC 9899:2011, 6.4.1 1753 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic', 1754 '_Noreturn', '_Static_assert', '_Thread_local']) 1755 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html 1756 # excluding _.* 1757 gcc_words = set(['asm', 'typeof']) 1758 # C++ ISO/IEC 14882:2003 2.11 1759 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete', 1760 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable', 1761 'namespace', 'new', 'operator', 'private', 'protected', 1762 'public', 'reinterpret_cast', 'static_cast', 'template', 1763 'this', 'throw', 'true', 'try', 'typeid', 'typename', 1764 'using', 'virtual', 'wchar_t', 1765 # alternative representations 1766 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', 1767 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq']) 1768 # namespace pollution: 1769 polluted_words = set(['unix', 'errno', 'mips', 'sparc']) 1770 name = name.translate(c_name_trans) 1771 if protect and (name in c89_words | c99_words | c11_words | gcc_words 1772 | cpp_words | polluted_words): 1773 return 'q_' + name 1774 return name 1775 1776eatspace = '\033EATSPACE.' 1777pointer_suffix = ' *' + eatspace 1778 1779 1780def genindent(count): 1781 ret = '' 1782 for _ in range(count): 1783 ret += ' ' 1784 return ret 1785 1786indent_level = 0 1787 1788 1789def push_indent(indent_amount=4): 1790 global indent_level 1791 indent_level += indent_amount 1792 1793 1794def pop_indent(indent_amount=4): 1795 global indent_level 1796 indent_level -= indent_amount 1797 1798 1799# Generate @code with @kwds interpolated. 1800# Obey indent_level, and strip eatspace. 1801def cgen(code, **kwds): 1802 raw = code % kwds 1803 if indent_level: 1804 indent = genindent(indent_level) 1805 # re.subn() lacks flags support before Python 2.7, use re.compile() 1806 raw = re.subn(re.compile(r'^.', re.MULTILINE), 1807 indent + r'\g<0>', raw) 1808 raw = raw[0] 1809 return re.sub(re.escape(eatspace) + r' *', '', raw) 1810 1811 1812def mcgen(code, **kwds): 1813 if code[0] == '\n': 1814 code = code[1:] 1815 return cgen(code, **kwds) 1816 1817 1818def guardname(filename): 1819 return c_name(filename, protect=False).upper() 1820 1821 1822def guardstart(name): 1823 return mcgen(''' 1824#ifndef %(name)s 1825#define %(name)s 1826 1827''', 1828 name=guardname(name)) 1829 1830 1831def guardend(name): 1832 return mcgen(''' 1833 1834#endif /* %(name)s */ 1835''', 1836 name=guardname(name)) 1837 1838 1839def gen_enum_lookup(name, values, prefix=None): 1840 ret = mcgen(''' 1841 1842const QEnumLookup %(c_name)s_lookup = { 1843 .array = (const char *const[]) { 1844''', 1845 c_name=c_name(name)) 1846 for value in values: 1847 index = c_enum_const(name, value, prefix) 1848 ret += mcgen(''' 1849 [%(index)s] = "%(value)s", 1850''', 1851 index=index, value=value) 1852 1853 ret += mcgen(''' 1854 }, 1855 .size = %(max_index)s 1856}; 1857''', 1858 max_index=c_enum_const(name, '_MAX', prefix)) 1859 return ret 1860 1861 1862def gen_enum(name, values, prefix=None): 1863 # append automatically generated _MAX value 1864 enum_values = values + ['_MAX'] 1865 1866 ret = mcgen(''' 1867 1868typedef enum %(c_name)s { 1869''', 1870 c_name=c_name(name)) 1871 1872 i = 0 1873 for value in enum_values: 1874 ret += mcgen(''' 1875 %(c_enum)s = %(i)d, 1876''', 1877 c_enum=c_enum_const(name, value, prefix), 1878 i=i) 1879 i += 1 1880 1881 ret += mcgen(''' 1882} %(c_name)s; 1883''', 1884 c_name=c_name(name)) 1885 1886 ret += mcgen(''' 1887 1888#define %(c_name)s_str(val) \\ 1889 qapi_enum_lookup(&%(c_name)s_lookup, (val)) 1890 1891extern const QEnumLookup %(c_name)s_lookup; 1892''', 1893 c_name=c_name(name)) 1894 return ret 1895 1896 1897def build_params(arg_type, boxed, extra): 1898 if not arg_type: 1899 assert not boxed 1900 return extra 1901 ret = '' 1902 sep = '' 1903 if boxed: 1904 ret += '%s arg' % arg_type.c_param_type() 1905 sep = ', ' 1906 else: 1907 assert not arg_type.variants 1908 for memb in arg_type.members: 1909 ret += sep 1910 sep = ', ' 1911 if memb.optional: 1912 ret += 'bool has_%s, ' % c_name(memb.name) 1913 ret += '%s %s' % (memb.type.c_param_type(), 1914 c_name(memb.name)) 1915 if extra: 1916 ret += sep + extra 1917 return ret 1918 1919 1920# 1921# Accumulate and write output 1922# 1923 1924class QAPIGen(object): 1925 1926 def __init__(self): 1927 self._preamble = '' 1928 self._body = '' 1929 1930 def preamble_add(self, text): 1931 self._preamble += text 1932 1933 def add(self, text): 1934 self._body += text 1935 1936 def _top(self, fname): 1937 return '' 1938 1939 def _bottom(self, fname): 1940 return '' 1941 1942 def write(self, output_dir, fname): 1943 if output_dir: 1944 try: 1945 os.makedirs(output_dir) 1946 except os.error as e: 1947 if e.errno != errno.EEXIST: 1948 raise 1949 fd = os.open(os.path.join(output_dir, fname), 1950 os.O_RDWR | os.O_CREAT, 0o666) 1951 f = os.fdopen(fd, 'r+') 1952 text = (self._top(fname) + self._preamble + self._body 1953 + self._bottom(fname)) 1954 oldtext = f.read(len(text) + 1) 1955 if text != oldtext: 1956 f.seek(0) 1957 f.truncate(0) 1958 f.write(text) 1959 f.close() 1960 1961 1962class QAPIGenC(QAPIGen): 1963 1964 def __init__(self, blurb, pydoc): 1965 QAPIGen.__init__(self) 1966 self._blurb = blurb 1967 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc, 1968 re.MULTILINE)) 1969 1970 def _top(self, fname): 1971 return mcgen(''' 1972/* AUTOMATICALLY GENERATED, DO NOT MODIFY */ 1973 1974/* 1975%(blurb)s 1976 * 1977 * %(copyright)s 1978 * 1979 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 1980 * See the COPYING.LIB file in the top-level directory. 1981 */ 1982 1983''', 1984 blurb=self._blurb, copyright=self._copyright) 1985 1986 1987class QAPIGenH(QAPIGenC): 1988 1989 def _top(self, fname): 1990 return QAPIGenC._top(self, fname) + guardstart(fname) 1991 1992 def _bottom(self, fname): 1993 return guardend(fname) 1994 1995 1996class QAPIGenDoc(QAPIGen): 1997 1998 def _top(self, fname): 1999 return (QAPIGen._top(self, fname) 2000 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n') 2001