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