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