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 15from contextlib import contextmanager 16import copy 17import errno 18import os 19import re 20import string 21import sys 22from collections 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# 59# Parsing the schema into expressions 60# 61 62class QAPISourceInfo(object): 63 def __init__(self, fname, line, parent): 64 self.fname = fname 65 self.line = line 66 self.parent = parent 67 68 def next_line(self): 69 info = copy.copy(self) 70 info.line += 1 71 return info 72 73 def loc(self): 74 return '%s:%d' % (self.fname, self.line) 75 76 def include_path(self): 77 ret = '' 78 parent = self.parent 79 while parent: 80 ret = 'In file included from %s:\n' % parent.loc() + ret 81 parent = parent.parent 82 return ret 83 84 def __str__(self): 85 return self.include_path() + self.loc() 86 87 88class QAPIError(Exception): 89 def __init__(self, info, col, msg): 90 Exception.__init__(self) 91 self.info = info 92 self.col = col 93 self.msg = msg 94 95 def __str__(self): 96 loc = str(self.info) 97 if self.col is not None: 98 assert self.info.line is not None 99 loc += ':%s' % self.col 100 return loc + ': ' + self.msg 101 102 103class QAPIParseError(QAPIError): 104 def __init__(self, parser, msg): 105 col = 1 106 for ch in parser.src[parser.line_pos:parser.pos]: 107 if ch == '\t': 108 col = (col + 7) % 8 + 1 109 else: 110 col += 1 111 QAPIError.__init__(self, parser.info, col, msg) 112 113 114class QAPISemError(QAPIError): 115 def __init__(self, info, msg): 116 QAPIError.__init__(self, info, None, msg) 117 118 119class QAPIDoc(object): 120 """ 121 A documentation comment block, either definition or free-form 122 123 Definition documentation blocks consist of 124 125 * a body section: one line naming the definition, followed by an 126 overview (any number of lines) 127 128 * argument sections: a description of each argument (for commands 129 and events) or member (for structs, unions and alternates) 130 131 * features sections: a description of each feature flag 132 133 * additional (non-argument) sections, possibly tagged 134 135 Free-form documentation blocks consist only of a body section. 136 """ 137 138 class Section(object): 139 def __init__(self, name=None): 140 # optional section name (argument/member or section name) 141 self.name = name 142 # the list of lines for this section 143 self.text = '' 144 145 def append(self, line): 146 self.text += line.rstrip() + '\n' 147 148 class ArgSection(Section): 149 def __init__(self, name): 150 QAPIDoc.Section.__init__(self, name) 151 self.member = None 152 153 def connect(self, member): 154 self.member = member 155 156 def __init__(self, parser, info): 157 # self._parser is used to report errors with QAPIParseError. The 158 # resulting error position depends on the state of the parser. 159 # It happens to be the beginning of the comment. More or less 160 # servicable, but action at a distance. 161 self._parser = parser 162 self.info = info 163 self.symbol = None 164 self.body = QAPIDoc.Section() 165 # dict mapping parameter name to ArgSection 166 self.args = OrderedDict() 167 self.features = OrderedDict() 168 # a list of Section 169 self.sections = [] 170 # the current section 171 self._section = self.body 172 self._append_line = self._append_body_line 173 174 def has_section(self, name): 175 """Return True if we have a section with this name.""" 176 for i in self.sections: 177 if i.name == name: 178 return True 179 return False 180 181 def append(self, line): 182 """ 183 Parse a comment line and add it to the documentation. 184 185 The way that the line is dealt with depends on which part of 186 the documentation we're parsing right now: 187 * The body section: ._append_line is ._append_body_line 188 * An argument section: ._append_line is ._append_args_line 189 * A features section: ._append_line is ._append_features_line 190 * An additional section: ._append_line is ._append_various_line 191 """ 192 line = line[1:] 193 if not line: 194 self._append_freeform(line) 195 return 196 197 if line[0] != ' ': 198 raise QAPIParseError(self._parser, "Missing space after #") 199 line = line[1:] 200 self._append_line(line) 201 202 def end_comment(self): 203 self._end_section() 204 205 @staticmethod 206 def _is_section_tag(name): 207 return name in ('Returns:', 'Since:', 208 # those are often singular or plural 209 'Note:', 'Notes:', 210 'Example:', 'Examples:', 211 'TODO:') 212 213 def _append_body_line(self, line): 214 """ 215 Process a line of documentation text in the body section. 216 217 If this a symbol line and it is the section's first line, this 218 is a definition documentation block for that symbol. 219 220 If it's a definition documentation block, another symbol line 221 begins the argument section for the argument named by it, and 222 a section tag begins an additional section. Start that 223 section and append the line to it. 224 225 Else, append the line to the current section. 226 """ 227 name = line.split(' ', 1)[0] 228 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't 229 # recognized, and get silently treated as ordinary text 230 if not self.symbol and not self.body.text and line.startswith('@'): 231 if not line.endswith(':'): 232 raise QAPIParseError(self._parser, "Line should end with ':'") 233 self.symbol = line[1:-1] 234 # FIXME invalid names other than the empty string aren't flagged 235 if not self.symbol: 236 raise QAPIParseError(self._parser, "Invalid name") 237 elif self.symbol: 238 # This is a definition documentation block 239 if name.startswith('@') and name.endswith(':'): 240 self._append_line = self._append_args_line 241 self._append_args_line(line) 242 elif line == 'Features:': 243 self._append_line = self._append_features_line 244 elif self._is_section_tag(name): 245 self._append_line = self._append_various_line 246 self._append_various_line(line) 247 else: 248 self._append_freeform(line.strip()) 249 else: 250 # This is a free-form documentation block 251 self._append_freeform(line.strip()) 252 253 def _append_args_line(self, line): 254 """ 255 Process a line of documentation text in an argument section. 256 257 A symbol line begins the next argument section, a section tag 258 section or a non-indented line after a blank line begins an 259 additional section. Start that section and append the line to 260 it. 261 262 Else, append the line to the current section. 263 264 """ 265 name = line.split(' ', 1)[0] 266 267 if name.startswith('@') and name.endswith(':'): 268 line = line[len(name)+1:] 269 self._start_args_section(name[1:-1]) 270 elif self._is_section_tag(name): 271 self._append_line = self._append_various_line 272 self._append_various_line(line) 273 return 274 elif (self._section.text.endswith('\n\n') 275 and line and not line[0].isspace()): 276 if line == 'Features:': 277 self._append_line = self._append_features_line 278 else: 279 self._start_section() 280 self._append_line = self._append_various_line 281 self._append_various_line(line) 282 return 283 284 self._append_freeform(line.strip()) 285 286 def _append_features_line(self, line): 287 name = line.split(' ', 1)[0] 288 289 if name.startswith('@') and name.endswith(':'): 290 line = line[len(name)+1:] 291 self._start_features_section(name[1:-1]) 292 elif self._is_section_tag(name): 293 self._append_line = self._append_various_line 294 self._append_various_line(line) 295 return 296 elif (self._section.text.endswith('\n\n') 297 and line and not line[0].isspace()): 298 self._start_section() 299 self._append_line = self._append_various_line 300 self._append_various_line(line) 301 return 302 303 self._append_freeform(line.strip()) 304 305 def _append_various_line(self, line): 306 """ 307 Process a line of documentation text in an additional section. 308 309 A symbol line is an error. 310 311 A section tag begins an additional section. Start that 312 section and append the line to it. 313 314 Else, append the line to the current section. 315 """ 316 name = line.split(' ', 1)[0] 317 318 if name.startswith('@') and name.endswith(':'): 319 raise QAPIParseError(self._parser, 320 "'%s' can't follow '%s' section" 321 % (name, self.sections[0].name)) 322 elif self._is_section_tag(name): 323 line = line[len(name)+1:] 324 self._start_section(name[:-1]) 325 326 if (not self._section.name or 327 not self._section.name.startswith('Example')): 328 line = line.strip() 329 330 self._append_freeform(line) 331 332 def _start_symbol_section(self, symbols_dict, name): 333 # FIXME invalid names other than the empty string aren't flagged 334 if not name: 335 raise QAPIParseError(self._parser, "Invalid parameter name") 336 if name in symbols_dict: 337 raise QAPIParseError(self._parser, 338 "'%s' parameter name duplicated" % name) 339 assert not self.sections 340 self._end_section() 341 self._section = QAPIDoc.ArgSection(name) 342 symbols_dict[name] = self._section 343 344 def _start_args_section(self, name): 345 self._start_symbol_section(self.args, name) 346 347 def _start_features_section(self, name): 348 self._start_symbol_section(self.features, name) 349 350 def _start_section(self, name=None): 351 if name in ('Returns', 'Since') and self.has_section(name): 352 raise QAPIParseError(self._parser, 353 "Duplicated '%s' section" % name) 354 self._end_section() 355 self._section = QAPIDoc.Section(name) 356 self.sections.append(self._section) 357 358 def _end_section(self): 359 if self._section: 360 text = self._section.text = self._section.text.strip() 361 if self._section.name and (not text or text.isspace()): 362 raise QAPIParseError(self._parser, "Empty doc section '%s'" 363 % self._section.name) 364 self._section = None 365 366 def _append_freeform(self, line): 367 match = re.match(r'(@\S+:)', line) 368 if match: 369 raise QAPIParseError(self._parser, 370 "'%s' not allowed in free-form documentation" 371 % match.group(1)) 372 self._section.append(line) 373 374 def connect_member(self, member): 375 if member.name not in self.args: 376 # Undocumented TODO outlaw 377 self.args[member.name] = QAPIDoc.ArgSection(member.name) 378 self.args[member.name].connect(member) 379 380 def check_expr(self, expr): 381 if self.has_section('Returns') and 'command' not in expr: 382 raise QAPISemError(self.info, 383 "'Returns:' is only valid for commands") 384 385 def check(self): 386 bogus = [name for name, section in self.args.items() 387 if not section.member] 388 if bogus: 389 raise QAPISemError( 390 self.info, 391 "The following documented members are not in " 392 "the declaration: %s" % ", ".join(bogus)) 393 394 395class QAPISchemaParser(object): 396 397 def __init__(self, fp, previously_included=[], incl_info=None): 398 self.fname = fp.name 399 previously_included.append(os.path.abspath(fp.name)) 400 self.src = fp.read() 401 if self.src == '' or self.src[-1] != '\n': 402 self.src += '\n' 403 self.cursor = 0 404 self.info = QAPISourceInfo(self.fname, 1, incl_info) 405 self.line_pos = 0 406 self.exprs = [] 407 self.docs = [] 408 self.accept() 409 cur_doc = None 410 411 while self.tok is not None: 412 info = self.info 413 if self.tok == '#': 414 self.reject_expr_doc(cur_doc) 415 cur_doc = self.get_doc(info) 416 self.docs.append(cur_doc) 417 continue 418 419 expr = self.get_expr(False) 420 if 'include' in expr: 421 self.reject_expr_doc(cur_doc) 422 if len(expr) != 1: 423 raise QAPISemError(info, "Invalid 'include' directive") 424 include = expr['include'] 425 if not isinstance(include, str): 426 raise QAPISemError(info, 427 "Value of 'include' must be a string") 428 incl_fname = os.path.join(os.path.dirname(self.fname), 429 include) 430 self.exprs.append({'expr': {'include': incl_fname}, 431 'info': info}) 432 exprs_include = self._include(include, info, incl_fname, 433 previously_included) 434 if exprs_include: 435 self.exprs.extend(exprs_include.exprs) 436 self.docs.extend(exprs_include.docs) 437 elif "pragma" in expr: 438 self.reject_expr_doc(cur_doc) 439 if len(expr) != 1: 440 raise QAPISemError(info, "Invalid 'pragma' directive") 441 pragma = expr['pragma'] 442 if not isinstance(pragma, dict): 443 raise QAPISemError( 444 info, "Value of 'pragma' must be an object") 445 for name, value in pragma.items(): 446 self._pragma(name, value, info) 447 else: 448 expr_elem = {'expr': expr, 449 'info': info} 450 if cur_doc: 451 if not cur_doc.symbol: 452 raise QAPISemError( 453 cur_doc.info, "Definition documentation required") 454 expr_elem['doc'] = cur_doc 455 self.exprs.append(expr_elem) 456 cur_doc = None 457 self.reject_expr_doc(cur_doc) 458 459 @staticmethod 460 def reject_expr_doc(doc): 461 if doc and doc.symbol: 462 raise QAPISemError( 463 doc.info, 464 "Documentation for '%s' is not followed by the definition" 465 % doc.symbol) 466 467 def _include(self, include, info, incl_fname, previously_included): 468 incl_abs_fname = os.path.abspath(incl_fname) 469 # catch inclusion cycle 470 inf = info 471 while inf: 472 if incl_abs_fname == os.path.abspath(inf.fname): 473 raise QAPISemError(info, "Inclusion loop for %s" % include) 474 inf = inf.parent 475 476 # skip multiple include of the same file 477 if incl_abs_fname in previously_included: 478 return None 479 480 try: 481 if sys.version_info[0] >= 3: 482 fobj = open(incl_fname, 'r', encoding='utf-8') 483 else: 484 fobj = open(incl_fname, 'r') 485 except IOError as e: 486 raise QAPISemError(info, "%s: %s" % (e.strerror, incl_fname)) 487 return QAPISchemaParser(fobj, previously_included, info) 488 489 def _pragma(self, name, value, info): 490 global doc_required, returns_whitelist, name_case_whitelist 491 if name == 'doc-required': 492 if not isinstance(value, bool): 493 raise QAPISemError(info, 494 "Pragma 'doc-required' must be boolean") 495 doc_required = value 496 elif name == 'returns-whitelist': 497 if (not isinstance(value, list) 498 or any([not isinstance(elt, str) for elt in value])): 499 raise QAPISemError(info, 500 "Pragma returns-whitelist must be" 501 " a list of strings") 502 returns_whitelist = value 503 elif name == 'name-case-whitelist': 504 if (not isinstance(value, list) 505 or any([not isinstance(elt, str) for elt in value])): 506 raise QAPISemError(info, 507 "Pragma name-case-whitelist must be" 508 " a list of strings") 509 name_case_whitelist = value 510 else: 511 raise QAPISemError(info, "Unknown pragma '%s'" % name) 512 513 def accept(self, skip_comment=True): 514 while True: 515 self.tok = self.src[self.cursor] 516 self.pos = self.cursor 517 self.cursor += 1 518 self.val = None 519 520 if self.tok == '#': 521 if self.src[self.cursor] == '#': 522 # Start of doc comment 523 skip_comment = False 524 self.cursor = self.src.find('\n', self.cursor) 525 if not skip_comment: 526 self.val = self.src[self.pos:self.cursor] 527 return 528 elif self.tok in '{}:,[]': 529 return 530 elif self.tok == "'": 531 # Note: we accept only printable ASCII 532 string = '' 533 esc = False 534 while True: 535 ch = self.src[self.cursor] 536 self.cursor += 1 537 if ch == '\n': 538 raise QAPIParseError(self, "Missing terminating \"'\"") 539 if esc: 540 # Note: we recognize only \\ because we have 541 # no use for funny characters in strings 542 if ch != '\\': 543 raise QAPIParseError(self, 544 "Unknown escape \\%s" % ch) 545 esc = False 546 elif ch == '\\': 547 esc = True 548 continue 549 elif ch == "'": 550 self.val = string 551 return 552 if ord(ch) < 32 or ord(ch) >= 127: 553 raise QAPIParseError( 554 self, "Funny character in string") 555 string += ch 556 elif self.src.startswith('true', self.pos): 557 self.val = True 558 self.cursor += 3 559 return 560 elif self.src.startswith('false', self.pos): 561 self.val = False 562 self.cursor += 4 563 return 564 elif self.tok == '\n': 565 if self.cursor == len(self.src): 566 self.tok = None 567 return 568 self.info = self.info.next_line() 569 self.line_pos = self.cursor 570 elif not self.tok.isspace(): 571 # Show up to next structural, whitespace or quote 572 # character 573 match = re.match('[^[\\]{}:,\\s\'"]+', 574 self.src[self.cursor-1:]) 575 raise QAPIParseError(self, "Stray '%s'" % match.group(0)) 576 577 def get_members(self): 578 expr = OrderedDict() 579 if self.tok == '}': 580 self.accept() 581 return expr 582 if self.tok != "'": 583 raise QAPIParseError(self, "Expected string or '}'") 584 while True: 585 key = self.val 586 self.accept() 587 if self.tok != ':': 588 raise QAPIParseError(self, "Expected ':'") 589 self.accept() 590 if key in expr: 591 raise QAPIParseError(self, "Duplicate key '%s'" % key) 592 expr[key] = self.get_expr(True) 593 if self.tok == '}': 594 self.accept() 595 return expr 596 if self.tok != ',': 597 raise QAPIParseError(self, "Expected ',' or '}'") 598 self.accept() 599 if self.tok != "'": 600 raise QAPIParseError(self, "Expected string") 601 602 def get_values(self): 603 expr = [] 604 if self.tok == ']': 605 self.accept() 606 return expr 607 if self.tok not in "{['tfn": 608 raise QAPIParseError( 609 self, "Expected '{', '[', ']', string, boolean or 'null'") 610 while True: 611 expr.append(self.get_expr(True)) 612 if self.tok == ']': 613 self.accept() 614 return expr 615 if self.tok != ',': 616 raise QAPIParseError(self, "Expected ',' or ']'") 617 self.accept() 618 619 def get_expr(self, nested): 620 if self.tok != '{' and not nested: 621 raise QAPIParseError(self, "Expected '{'") 622 if self.tok == '{': 623 self.accept() 624 expr = self.get_members() 625 elif self.tok == '[': 626 self.accept() 627 expr = self.get_values() 628 elif self.tok in "'tfn": 629 expr = self.val 630 self.accept() 631 else: 632 raise QAPIParseError( 633 self, "Expected '{', '[', string, boolean or 'null'") 634 return expr 635 636 def get_doc(self, info): 637 if self.val != '##': 638 raise QAPIParseError(self, "Junk after '##' at start of " 639 "documentation comment") 640 641 doc = QAPIDoc(self, info) 642 self.accept(False) 643 while self.tok == '#': 644 if self.val.startswith('##'): 645 # End of doc comment 646 if self.val != '##': 647 raise QAPIParseError(self, "Junk after '##' at end of " 648 "documentation comment") 649 doc.end_comment() 650 self.accept() 651 return doc 652 else: 653 doc.append(self.val) 654 self.accept(False) 655 656 raise QAPIParseError(self, "Documentation comment must end with '##'") 657 658 659# 660# Semantic analysis of schema expressions 661# TODO fold into QAPISchema 662# TODO catching name collisions in generated code would be nice 663# 664 665 666def find_base_members(base): 667 if isinstance(base, dict): 668 return base 669 base_struct_define = struct_types.get(base) 670 if not base_struct_define: 671 return None 672 return base_struct_define['data'] 673 674 675# Return the qtype of an alternate branch, or None on error. 676def find_alternate_member_qtype(qapi_type): 677 if qapi_type in builtin_types: 678 return builtin_types[qapi_type] 679 elif qapi_type in struct_types: 680 return 'QTYPE_QDICT' 681 elif qapi_type in enum_types: 682 return 'QTYPE_QSTRING' 683 elif qapi_type in union_types: 684 return 'QTYPE_QDICT' 685 return None 686 687 688# Names must be letters, numbers, -, and _. They must start with letter, 689# except for downstream extensions which must start with __RFQDN_. 690# Dots are only valid in the downstream extension prefix. 691valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?' 692 '[a-zA-Z][a-zA-Z0-9_-]*$') 693 694 695def check_name(info, source, name, allow_optional=False, 696 enum_member=False): 697 global valid_name 698 membername = name 699 700 if not isinstance(name, str): 701 raise QAPISemError(info, "%s requires a string name" % source) 702 if name.startswith('*'): 703 membername = name[1:] 704 if not allow_optional: 705 raise QAPISemError(info, "%s does not allow optional name '%s'" 706 % (source, name)) 707 # Enum members can start with a digit, because the generated C 708 # code always prefixes it with the enum name 709 if enum_member and membername[0].isdigit(): 710 membername = 'D' + membername 711 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty' 712 # and 'q_obj_*' implicit type names. 713 if not valid_name.match(membername) or \ 714 c_name(membername, False).startswith('q_'): 715 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name)) 716 717 718def add_name(name, info, meta): 719 global all_names 720 check_name(info, "'%s'" % meta, name) 721 # FIXME should reject names that differ only in '_' vs. '.' 722 # vs. '-', because they're liable to clash in generated C. 723 if name in all_names: 724 raise QAPISemError(info, "%s '%s' is already defined" 725 % (all_names[name], name)) 726 if name.endswith('Kind') or name.endswith('List'): 727 raise QAPISemError(info, "%s '%s' should not end in '%s'" 728 % (meta, name, name[-4:])) 729 all_names[name] = meta 730 731 732def check_if(expr, info): 733 734 def check_if_str(ifcond, info): 735 if not isinstance(ifcond, str): 736 raise QAPISemError( 737 info, "'if' condition must be a string or a list of strings") 738 if ifcond.strip() == '': 739 raise QAPISemError(info, "'if' condition '%s' makes no sense" 740 % ifcond) 741 742 ifcond = expr.get('if') 743 if ifcond is None: 744 return 745 if isinstance(ifcond, list): 746 if ifcond == []: 747 raise QAPISemError(info, "'if' condition [] is useless") 748 for elt in ifcond: 749 check_if_str(elt, info) 750 else: 751 check_if_str(ifcond, info) 752 753 754def check_type(info, source, value, 755 allow_array=False, allow_dict=False, allow_metas=[]): 756 global all_names 757 758 if value is None: 759 return 760 761 # Check if array type for value is okay 762 if isinstance(value, list): 763 if not allow_array: 764 raise QAPISemError(info, "%s cannot be an array" % source) 765 if len(value) != 1 or not isinstance(value[0], str): 766 raise QAPISemError(info, 767 "%s: array type must contain single type name" % 768 source) 769 value = value[0] 770 771 # Check if type name for value is okay 772 if isinstance(value, str): 773 if value not in all_names: 774 raise QAPISemError(info, "%s uses unknown type '%s'" 775 % (source, value)) 776 if not all_names[value] in allow_metas: 777 raise QAPISemError(info, "%s cannot use %s type '%s'" % 778 (source, all_names[value], value)) 779 return 780 781 if not allow_dict: 782 raise QAPISemError(info, "%s should be a type name" % source) 783 784 if not isinstance(value, OrderedDict): 785 raise QAPISemError(info, 786 "%s should be an object or type name" % source) 787 788 # value is a dictionary, check that each member is okay 789 for (key, arg) in value.items(): 790 check_name(info, "Member of %s" % source, key, 791 allow_optional=True) 792 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'): 793 raise QAPISemError(info, "Member of %s uses reserved name '%s'" 794 % (source, key)) 795 # Todo: allow dictionaries to represent default values of 796 # an optional argument. 797 check_known_keys(info, "member '%s' of %s" % (key, source), 798 arg, ['type'], ['if']) 799 check_if(arg, info) 800 normalize_if(arg) 801 check_type(info, "Member '%s' of %s" % (key, source), 802 arg['type'], allow_array=True, 803 allow_metas=['built-in', 'union', 'alternate', 'struct', 804 'enum']) 805 806 807def check_command(expr, info): 808 name = expr['command'] 809 boxed = expr.get('boxed', False) 810 811 args_meta = ['struct'] 812 if boxed: 813 args_meta += ['union'] 814 check_type(info, "'data' for command '%s'" % name, 815 expr.get('data'), allow_dict=not boxed, 816 allow_metas=args_meta) 817 returns_meta = ['union', 'struct'] 818 if name in returns_whitelist: 819 returns_meta += ['built-in', 'alternate', 'enum'] 820 check_type(info, "'returns' for command '%s'" % name, 821 expr.get('returns'), allow_array=True, 822 allow_metas=returns_meta) 823 824 825def check_event(expr, info): 826 name = expr['event'] 827 boxed = expr.get('boxed', False) 828 829 meta = ['struct'] 830 if boxed: 831 meta += ['union'] 832 check_type(info, "'data' for event '%s'" % name, 833 expr.get('data'), allow_dict=not boxed, 834 allow_metas=meta) 835 836 837def enum_get_names(expr): 838 return [e['name'] for e in expr['data']] 839 840 841def check_union(expr, info): 842 name = expr['union'] 843 base = expr.get('base') 844 discriminator = expr.get('discriminator') 845 members = expr['data'] 846 847 # Two types of unions, determined by discriminator. 848 849 # With no discriminator it is a simple union. 850 if discriminator is None: 851 enum_values = members.keys() 852 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum'] 853 if base is not None: 854 raise QAPISemError(info, "Simple union '%s' must not have a base" % 855 name) 856 857 # Else, it's a flat union. 858 else: 859 # The object must have a string or dictionary 'base'. 860 check_type(info, "'base' for union '%s'" % name, 861 base, allow_dict=True, 862 allow_metas=['struct']) 863 if not base: 864 raise QAPISemError(info, "Flat union '%s' must have a base" 865 % name) 866 base_members = find_base_members(base) 867 assert base_members is not None 868 869 # The value of member 'discriminator' must name a non-optional 870 # member of the base struct. 871 check_name(info, "Discriminator of flat union '%s'" % name, 872 discriminator) 873 discriminator_value = base_members.get(discriminator) 874 if not discriminator_value: 875 raise QAPISemError(info, 876 "Discriminator '%s' is not a member of 'base'" 877 % discriminator) 878 if discriminator_value.get('if'): 879 raise QAPISemError( 880 info, 881 "The discriminator '%s' for union %s must not be conditional" 882 % (discriminator, name)) 883 enum_define = enum_types.get(discriminator_value['type']) 884 # Do not allow string discriminator 885 if not enum_define: 886 raise QAPISemError(info, 887 "Discriminator '%s' must be of enumeration " 888 "type" % discriminator) 889 enum_values = enum_get_names(enum_define) 890 allow_metas = ['struct'] 891 892 if (len(enum_values) == 0): 893 raise QAPISemError(info, "Union '%s' has no branches" % name) 894 895 for (key, value) in members.items(): 896 check_name(info, "Member of union '%s'" % name, key) 897 898 check_known_keys(info, "member '%s' of union '%s'" % (key, name), 899 value, ['type'], ['if']) 900 check_if(value, info) 901 normalize_if(value) 902 # Each value must name a known type 903 check_type(info, "Member '%s' of union '%s'" % (key, name), 904 value['type'], 905 allow_array=not base, allow_metas=allow_metas) 906 907 # If the discriminator names an enum type, then all members 908 # of 'data' must also be members of the enum type. 909 if discriminator is not None: 910 if key not in enum_values: 911 raise QAPISemError(info, 912 "Discriminator value '%s' is not found in " 913 "enum '%s'" 914 % (key, enum_define['enum'])) 915 916 917def check_alternate(expr, info): 918 name = expr['alternate'] 919 members = expr['data'] 920 types_seen = {} 921 922 if len(members) == 0: 923 raise QAPISemError(info, 924 "Alternate '%s' cannot have empty 'data'" % name) 925 for (key, value) in members.items(): 926 check_name(info, "Member of alternate '%s'" % name, key) 927 check_known_keys(info, 928 "member '%s' of alternate '%s'" % (key, name), 929 value, ['type'], ['if']) 930 check_if(value, info) 931 normalize_if(value) 932 typ = value['type'] 933 934 # Ensure alternates have no type conflicts. 935 check_type(info, "Member '%s' of alternate '%s'" % (key, name), typ, 936 allow_metas=['built-in', 'union', 'struct', 'enum']) 937 qtype = find_alternate_member_qtype(typ) 938 if not qtype: 939 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use " 940 "type '%s'" % (name, key, typ)) 941 conflicting = set([qtype]) 942 if qtype == 'QTYPE_QSTRING': 943 enum_expr = enum_types.get(typ) 944 if enum_expr: 945 for v in enum_get_names(enum_expr): 946 if v in ['on', 'off']: 947 conflicting.add('QTYPE_QBOOL') 948 if re.match(r'[-+0-9.]', v): # lazy, could be tightened 949 conflicting.add('QTYPE_QNUM') 950 else: 951 conflicting.add('QTYPE_QNUM') 952 conflicting.add('QTYPE_QBOOL') 953 for qt in conflicting: 954 if qt in types_seen: 955 raise QAPISemError(info, "Alternate '%s' member '%s' can't " 956 "be distinguished from member '%s'" 957 % (name, key, types_seen[qt])) 958 types_seen[qt] = key 959 960 961def check_enum(expr, info): 962 name = expr['enum'] 963 members = expr['data'] 964 prefix = expr.get('prefix') 965 966 if not isinstance(members, list): 967 raise QAPISemError(info, 968 "Enum '%s' requires an array for 'data'" % name) 969 if prefix is not None and not isinstance(prefix, str): 970 raise QAPISemError(info, 971 "Enum '%s' requires a string for 'prefix'" % name) 972 973 for member in members: 974 check_known_keys(info, "member of enum '%s'" % name, member, 975 ['name'], ['if']) 976 check_if(member, info) 977 normalize_if(member) 978 check_name(info, "Member of enum '%s'" % name, member['name'], 979 enum_member=True) 980 981 982def check_struct(expr, info): 983 name = expr['struct'] 984 members = expr['data'] 985 features = expr.get('features') 986 987 check_type(info, "'data' for struct '%s'" % name, members, 988 allow_dict=True) 989 check_type(info, "'base' for struct '%s'" % name, expr.get('base'), 990 allow_metas=['struct']) 991 992 if features: 993 if not isinstance(features, list): 994 raise QAPISemError(info, 995 "Struct '%s' requires an array for 'features'" % 996 name) 997 for f in features: 998 assert isinstance(f, dict) 999 check_known_keys(info, "feature of struct %s" % name, f, 1000 ['name'], ['if']) 1001 1002 check_if(f, info) 1003 normalize_if(f) 1004 check_name(info, "Feature of struct %s" % name, f['name']) 1005 1006 1007def check_known_keys(info, source, value, required, optional): 1008 1009 def pprint(elems): 1010 return ', '.join("'" + e + "'" for e in sorted(elems)) 1011 1012 missing = set(required) - set(value) 1013 if missing: 1014 raise QAPISemError(info, "Key%s %s %s missing from %s" 1015 % ('s' if len(missing) > 1 else '', pprint(missing), 1016 'are' if len(missing) > 1 else 'is', source)) 1017 allowed = set(required + optional) 1018 unknown = set(value) - allowed 1019 if unknown: 1020 raise QAPISemError(info, "Unknown key%s %s in %s\nValid keys are %s." 1021 % ('s' if len(unknown) > 1 else '', pprint(unknown), 1022 source, pprint(allowed))) 1023 1024 1025def check_keys(expr, info, meta, required, optional=[]): 1026 name = expr[meta] 1027 if not isinstance(name, str): 1028 raise QAPISemError(info, "'%s' key must have a string value" % meta) 1029 required = required + [meta] 1030 source = "%s '%s'" % (meta, name) 1031 check_known_keys(info, source, expr, required, optional) 1032 for (key, value) in expr.items(): 1033 if key in ['gen', 'success-response'] and value is not False: 1034 raise QAPISemError(info, 1035 "'%s' of %s '%s' should only use false value" 1036 % (key, meta, name)) 1037 if (key in ['boxed', 'allow-oob', 'allow-preconfig'] 1038 and value is not True): 1039 raise QAPISemError(info, 1040 "'%s' of %s '%s' should only use true value" 1041 % (key, meta, name)) 1042 if key == 'if': 1043 check_if(expr, info) 1044 1045 1046def normalize_enum(expr): 1047 if isinstance(expr['data'], list): 1048 expr['data'] = [m if isinstance(m, dict) else {'name': m} 1049 for m in expr['data']] 1050 1051 1052def normalize_members(members): 1053 if isinstance(members, OrderedDict): 1054 for key, arg in members.items(): 1055 if isinstance(arg, dict): 1056 continue 1057 members[key] = {'type': arg} 1058 1059 1060def normalize_features(features): 1061 if isinstance(features, list): 1062 features[:] = [f if isinstance(f, dict) else {'name': f} 1063 for f in features] 1064 1065 1066def normalize_if(expr): 1067 ifcond = expr.get('if') 1068 if isinstance(ifcond, str): 1069 expr['if'] = [ifcond] 1070 1071 1072def check_exprs(exprs): 1073 global all_names 1074 1075 # Populate name table with names of built-in types 1076 for builtin in builtin_types.keys(): 1077 all_names[builtin] = 'built-in' 1078 1079 # Learn the types and check for valid expression keys 1080 for expr_elem in exprs: 1081 expr = expr_elem['expr'] 1082 info = expr_elem['info'] 1083 doc = expr_elem.get('doc') 1084 1085 if 'include' in expr: 1086 continue 1087 1088 if not doc and doc_required: 1089 raise QAPISemError(info, 1090 "Definition missing documentation comment") 1091 1092 if 'enum' in expr: 1093 meta = 'enum' 1094 check_keys(expr, info, 'enum', ['data'], ['if', 'prefix']) 1095 normalize_enum(expr) 1096 enum_types[expr[meta]] = expr 1097 elif 'union' in expr: 1098 meta = 'union' 1099 check_keys(expr, info, 'union', ['data'], 1100 ['base', 'discriminator', 'if']) 1101 normalize_members(expr.get('base')) 1102 normalize_members(expr['data']) 1103 union_types[expr[meta]] = expr 1104 elif 'alternate' in expr: 1105 meta = 'alternate' 1106 check_keys(expr, info, 'alternate', ['data'], ['if']) 1107 normalize_members(expr['data']) 1108 elif 'struct' in expr: 1109 meta = 'struct' 1110 check_keys(expr, info, 'struct', ['data'], 1111 ['base', 'if', 'features']) 1112 normalize_members(expr['data']) 1113 normalize_features(expr.get('features')) 1114 struct_types[expr[meta]] = expr 1115 elif 'command' in expr: 1116 meta = 'command' 1117 check_keys(expr, info, 'command', [], 1118 ['data', 'returns', 'gen', 'success-response', 1119 'boxed', 'allow-oob', 'allow-preconfig', 'if']) 1120 normalize_members(expr.get('data')) 1121 elif 'event' in expr: 1122 meta = 'event' 1123 check_keys(expr, info, 'event', [], ['data', 'boxed', 'if']) 1124 normalize_members(expr.get('data')) 1125 else: 1126 raise QAPISemError(info, "Expression is missing metatype") 1127 normalize_if(expr) 1128 name = expr[meta] 1129 add_name(name, info, meta) 1130 if doc and doc.symbol != name: 1131 raise QAPISemError(info, "Definition of '%s' follows documentation" 1132 " for '%s'" % (name, doc.symbol)) 1133 1134 # Validate that exprs make sense 1135 for expr_elem in exprs: 1136 expr = expr_elem['expr'] 1137 info = expr_elem['info'] 1138 doc = expr_elem.get('doc') 1139 1140 if 'include' in expr: 1141 continue 1142 if 'enum' in expr: 1143 check_enum(expr, info) 1144 elif 'union' in expr: 1145 check_union(expr, info) 1146 elif 'alternate' in expr: 1147 check_alternate(expr, info) 1148 elif 'struct' in expr: 1149 check_struct(expr, info) 1150 elif 'command' in expr: 1151 check_command(expr, info) 1152 elif 'event' in expr: 1153 check_event(expr, info) 1154 else: 1155 assert False, 'unexpected meta type' 1156 1157 if doc: 1158 doc.check_expr(expr) 1159 1160 return exprs 1161 1162 1163# 1164# Schema compiler frontend 1165# 1166 1167class QAPISchemaEntity(object): 1168 def __init__(self, name, info, doc, ifcond=None): 1169 assert name is None or isinstance(name, str) 1170 self.name = name 1171 self._module = None 1172 # For explicitly defined entities, info points to the (explicit) 1173 # definition. For builtins (and their arrays), info is None. 1174 # For implicitly defined entities, info points to a place that 1175 # triggered the implicit definition (there may be more than one 1176 # such place). 1177 self.info = info 1178 self.doc = doc 1179 self._ifcond = ifcond or [] 1180 self._checked = False 1181 1182 def c_name(self): 1183 return c_name(self.name) 1184 1185 def check(self, schema): 1186 assert not self._checked 1187 if self.info: 1188 self._module = os.path.relpath(self.info.fname, 1189 os.path.dirname(schema.fname)) 1190 self._checked = True 1191 1192 @property 1193 def ifcond(self): 1194 assert self._checked 1195 return self._ifcond 1196 1197 @property 1198 def module(self): 1199 assert self._checked 1200 return self._module 1201 1202 def is_implicit(self): 1203 return not self.info 1204 1205 def visit(self, visitor): 1206 assert self._checked 1207 1208 1209class QAPISchemaVisitor(object): 1210 def visit_begin(self, schema): 1211 pass 1212 1213 def visit_end(self): 1214 pass 1215 1216 def visit_module(self, fname): 1217 pass 1218 1219 def visit_needed(self, entity): 1220 # Default to visiting everything 1221 return True 1222 1223 def visit_include(self, fname, info): 1224 pass 1225 1226 def visit_builtin_type(self, name, info, json_type): 1227 pass 1228 1229 def visit_enum_type(self, name, info, ifcond, members, prefix): 1230 pass 1231 1232 def visit_array_type(self, name, info, ifcond, element_type): 1233 pass 1234 1235 def visit_object_type(self, name, info, ifcond, base, members, variants, 1236 features): 1237 pass 1238 1239 def visit_object_type_flat(self, name, info, ifcond, members, variants, 1240 features): 1241 pass 1242 1243 def visit_alternate_type(self, name, info, ifcond, variants): 1244 pass 1245 1246 def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, 1247 success_response, boxed, allow_oob, allow_preconfig): 1248 pass 1249 1250 def visit_event(self, name, info, ifcond, arg_type, boxed): 1251 pass 1252 1253 1254class QAPISchemaInclude(QAPISchemaEntity): 1255 1256 def __init__(self, fname, info): 1257 QAPISchemaEntity.__init__(self, None, info, None) 1258 self.fname = fname 1259 1260 def visit(self, visitor): 1261 QAPISchemaEntity.visit(self, visitor) 1262 visitor.visit_include(self.fname, self.info) 1263 1264 1265class QAPISchemaType(QAPISchemaEntity): 1266 # Return the C type for common use. 1267 # For the types we commonly box, this is a pointer type. 1268 def c_type(self): 1269 pass 1270 1271 # Return the C type to be used in a parameter list. 1272 def c_param_type(self): 1273 return self.c_type() 1274 1275 # Return the C type to be used where we suppress boxing. 1276 def c_unboxed_type(self): 1277 return self.c_type() 1278 1279 def json_type(self): 1280 pass 1281 1282 def alternate_qtype(self): 1283 json2qtype = { 1284 'null': 'QTYPE_QNULL', 1285 'string': 'QTYPE_QSTRING', 1286 'number': 'QTYPE_QNUM', 1287 'int': 'QTYPE_QNUM', 1288 'boolean': 'QTYPE_QBOOL', 1289 'object': 'QTYPE_QDICT' 1290 } 1291 return json2qtype.get(self.json_type()) 1292 1293 def doc_type(self): 1294 if self.is_implicit(): 1295 return None 1296 return self.name 1297 1298 1299class QAPISchemaBuiltinType(QAPISchemaType): 1300 def __init__(self, name, json_type, c_type): 1301 QAPISchemaType.__init__(self, name, None, None) 1302 assert not c_type or isinstance(c_type, str) 1303 assert json_type in ('string', 'number', 'int', 'boolean', 'null', 1304 'value') 1305 self._json_type_name = json_type 1306 self._c_type_name = c_type 1307 1308 def c_name(self): 1309 return self.name 1310 1311 def c_type(self): 1312 return self._c_type_name 1313 1314 def c_param_type(self): 1315 if self.name == 'str': 1316 return 'const ' + self._c_type_name 1317 return self._c_type_name 1318 1319 def json_type(self): 1320 return self._json_type_name 1321 1322 def doc_type(self): 1323 return self.json_type() 1324 1325 def visit(self, visitor): 1326 QAPISchemaType.visit(self, visitor) 1327 visitor.visit_builtin_type(self.name, self.info, self.json_type()) 1328 1329 1330class QAPISchemaEnumType(QAPISchemaType): 1331 def __init__(self, name, info, doc, ifcond, members, prefix): 1332 QAPISchemaType.__init__(self, name, info, doc, ifcond) 1333 for m in members: 1334 assert isinstance(m, QAPISchemaEnumMember) 1335 m.set_defined_in(name) 1336 assert prefix is None or isinstance(prefix, str) 1337 self.members = members 1338 self.prefix = prefix 1339 1340 def check(self, schema): 1341 QAPISchemaType.check(self, schema) 1342 seen = {} 1343 for m in self.members: 1344 m.check_clash(self.info, seen) 1345 if self.doc: 1346 self.doc.connect_member(m) 1347 1348 def is_implicit(self): 1349 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds() 1350 return self.name.endswith('Kind') or self.name == 'QType' 1351 1352 def c_type(self): 1353 return c_name(self.name) 1354 1355 def member_names(self): 1356 return [m.name for m in self.members] 1357 1358 def json_type(self): 1359 return 'string' 1360 1361 def visit(self, visitor): 1362 QAPISchemaType.visit(self, visitor) 1363 visitor.visit_enum_type(self.name, self.info, self.ifcond, 1364 self.members, self.prefix) 1365 1366 1367class QAPISchemaArrayType(QAPISchemaType): 1368 def __init__(self, name, info, element_type): 1369 QAPISchemaType.__init__(self, name, info, None, None) 1370 assert isinstance(element_type, str) 1371 self._element_type_name = element_type 1372 self.element_type = None 1373 1374 def check(self, schema): 1375 QAPISchemaType.check(self, schema) 1376 self.element_type = schema.lookup_type(self._element_type_name) 1377 assert self.element_type 1378 assert not isinstance(self.element_type, QAPISchemaArrayType) 1379 1380 @property 1381 def ifcond(self): 1382 assert self._checked 1383 return self.element_type.ifcond 1384 1385 @property 1386 def module(self): 1387 assert self._checked 1388 return self.element_type.module 1389 1390 def is_implicit(self): 1391 return True 1392 1393 def c_type(self): 1394 return c_name(self.name) + pointer_suffix 1395 1396 def json_type(self): 1397 return 'array' 1398 1399 def doc_type(self): 1400 elt_doc_type = self.element_type.doc_type() 1401 if not elt_doc_type: 1402 return None 1403 return 'array of ' + elt_doc_type 1404 1405 def visit(self, visitor): 1406 QAPISchemaType.visit(self, visitor) 1407 visitor.visit_array_type(self.name, self.info, self.ifcond, 1408 self.element_type) 1409 1410 1411class QAPISchemaObjectType(QAPISchemaType): 1412 def __init__(self, name, info, doc, ifcond, 1413 base, local_members, variants, features): 1414 # struct has local_members, optional base, and no variants 1415 # flat union has base, variants, and no local_members 1416 # simple union has local_members, variants, and no base 1417 QAPISchemaType.__init__(self, name, info, doc, ifcond) 1418 assert base is None or isinstance(base, str) 1419 for m in local_members: 1420 assert isinstance(m, QAPISchemaObjectTypeMember) 1421 m.set_defined_in(name) 1422 if variants is not None: 1423 assert isinstance(variants, QAPISchemaObjectTypeVariants) 1424 variants.set_defined_in(name) 1425 for f in features: 1426 assert isinstance(f, QAPISchemaFeature) 1427 f.set_defined_in(name) 1428 self._base_name = base 1429 self.base = None 1430 self.local_members = local_members 1431 self.variants = variants 1432 self.members = None 1433 self.features = features 1434 1435 def check(self, schema): 1436 # This calls another type T's .check() exactly when the C 1437 # struct emitted by gen_object() contains that T's C struct 1438 # (pointers don't count). 1439 if self.members is not None: 1440 # A previous .check() completed: nothing to do 1441 return 1442 if self._checked: 1443 # Recursed: C struct contains itself 1444 raise QAPISemError(self.info, 1445 "Object %s contains itself" % self.name) 1446 1447 QAPISchemaType.check(self, schema) 1448 assert self._checked and self.members is None 1449 1450 seen = OrderedDict() 1451 if self._base_name: 1452 self.base = schema.lookup_type(self._base_name) 1453 assert isinstance(self.base, QAPISchemaObjectType) 1454 self.base.check(schema) 1455 self.base.check_clash(self.info, seen) 1456 for m in self.local_members: 1457 m.check(schema) 1458 m.check_clash(self.info, seen) 1459 if self.doc: 1460 self.doc.connect_member(m) 1461 members = seen.values() 1462 1463 if self.variants: 1464 self.variants.check(schema, seen) 1465 assert self.variants.tag_member in members 1466 self.variants.check_clash(self.info, seen) 1467 1468 # Features are in a name space separate from members 1469 seen = {} 1470 for f in self.features: 1471 f.check_clash(self.info, seen) 1472 1473 if self.doc: 1474 self.doc.check() 1475 1476 self.members = members # mark completed 1477 1478 # Check that the members of this type do not cause duplicate JSON members, 1479 # and update seen to track the members seen so far. Report any errors 1480 # on behalf of info, which is not necessarily self.info 1481 def check_clash(self, info, seen): 1482 assert self._checked 1483 assert not self.variants # not implemented 1484 for m in self.members: 1485 m.check_clash(info, seen) 1486 1487 @property 1488 def ifcond(self): 1489 assert self._checked 1490 if isinstance(self._ifcond, QAPISchemaType): 1491 # Simple union wrapper type inherits from wrapped type; 1492 # see _make_implicit_object_type() 1493 return self._ifcond.ifcond 1494 return self._ifcond 1495 1496 def is_implicit(self): 1497 # See QAPISchema._make_implicit_object_type(), as well as 1498 # _def_predefineds() 1499 return self.name.startswith('q_') 1500 1501 def is_empty(self): 1502 assert self.members is not None 1503 return not self.members and not self.variants 1504 1505 def c_name(self): 1506 assert self.name != 'q_empty' 1507 return QAPISchemaType.c_name(self) 1508 1509 def c_type(self): 1510 assert not self.is_implicit() 1511 return c_name(self.name) + pointer_suffix 1512 1513 def c_unboxed_type(self): 1514 return c_name(self.name) 1515 1516 def json_type(self): 1517 return 'object' 1518 1519 def visit(self, visitor): 1520 QAPISchemaType.visit(self, visitor) 1521 visitor.visit_object_type(self.name, self.info, self.ifcond, 1522 self.base, self.local_members, self.variants, 1523 self.features) 1524 visitor.visit_object_type_flat(self.name, self.info, self.ifcond, 1525 self.members, self.variants, 1526 self.features) 1527 1528 1529class QAPISchemaMember(object): 1530 """ Represents object members, enum members and features """ 1531 role = 'member' 1532 1533 def __init__(self, name, ifcond=None): 1534 assert isinstance(name, str) 1535 self.name = name 1536 self.ifcond = ifcond or [] 1537 self.defined_in = None 1538 1539 def set_defined_in(self, name): 1540 assert not self.defined_in 1541 self.defined_in = name 1542 1543 def check_clash(self, info, seen): 1544 cname = c_name(self.name) 1545 if (cname.lower() != cname 1546 and self.defined_in not in name_case_whitelist): 1547 raise QAPISemError(info, 1548 "%s should not use uppercase" % self.describe()) 1549 if cname in seen: 1550 raise QAPISemError(info, "%s collides with %s" % 1551 (self.describe(), seen[cname].describe())) 1552 seen[cname] = self 1553 1554 def _pretty_defined_in(self): 1555 defined_in = self.defined_in 1556 if defined_in.startswith('q_obj_'): 1557 # See QAPISchema._make_implicit_object_type() - reverse the 1558 # mapping there to create a nice human-readable description 1559 defined_in = defined_in[6:] 1560 if defined_in.endswith('-arg'): 1561 return '(parameter of %s)' % defined_in[:-4] 1562 elif defined_in.endswith('-base'): 1563 return '(base of %s)' % defined_in[:-5] 1564 else: 1565 assert defined_in.endswith('-wrapper') 1566 # Unreachable and not implemented 1567 assert False 1568 if defined_in.endswith('Kind'): 1569 # See QAPISchema._make_implicit_enum_type() 1570 return '(branch of %s)' % defined_in[:-4] 1571 return '(%s of %s)' % (self.role, defined_in) 1572 1573 def describe(self): 1574 return "'%s' %s" % (self.name, self._pretty_defined_in()) 1575 1576 1577class QAPISchemaEnumMember(QAPISchemaMember): 1578 role = 'value' 1579 1580 1581class QAPISchemaFeature(QAPISchemaMember): 1582 role = 'feature' 1583 1584 1585class QAPISchemaObjectTypeMember(QAPISchemaMember): 1586 def __init__(self, name, typ, optional, ifcond=None): 1587 QAPISchemaMember.__init__(self, name, ifcond) 1588 assert isinstance(typ, str) 1589 assert isinstance(optional, bool) 1590 self._type_name = typ 1591 self.type = None 1592 self.optional = optional 1593 1594 def check(self, schema): 1595 assert self.defined_in 1596 self.type = schema.lookup_type(self._type_name) 1597 assert self.type 1598 1599 1600class QAPISchemaObjectTypeVariants(object): 1601 def __init__(self, tag_name, tag_member, variants): 1602 # Flat unions pass tag_name but not tag_member. 1603 # Simple unions and alternates pass tag_member but not tag_name. 1604 # After check(), tag_member is always set, and tag_name remains 1605 # a reliable witness of being used by a flat union. 1606 assert bool(tag_member) != bool(tag_name) 1607 assert (isinstance(tag_name, str) or 1608 isinstance(tag_member, QAPISchemaObjectTypeMember)) 1609 for v in variants: 1610 assert isinstance(v, QAPISchemaObjectTypeVariant) 1611 self._tag_name = tag_name 1612 self.tag_member = tag_member 1613 self.variants = variants 1614 1615 def set_defined_in(self, name): 1616 for v in self.variants: 1617 v.set_defined_in(name) 1618 1619 def check(self, schema, seen): 1620 if not self.tag_member: # flat union 1621 self.tag_member = seen[c_name(self._tag_name)] 1622 assert self._tag_name == self.tag_member.name 1623 assert isinstance(self.tag_member.type, QAPISchemaEnumType) 1624 assert not self.tag_member.optional 1625 assert self.tag_member.ifcond == [] 1626 if self._tag_name: # flat union 1627 # branches that are not explicitly covered get an empty type 1628 cases = set([v.name for v in self.variants]) 1629 for m in self.tag_member.type.members: 1630 if m.name not in cases: 1631 v = QAPISchemaObjectTypeVariant(m.name, 'q_empty', 1632 m.ifcond) 1633 v.set_defined_in(self.tag_member.defined_in) 1634 self.variants.append(v) 1635 assert self.variants 1636 for v in self.variants: 1637 v.check(schema) 1638 # Union names must match enum values; alternate names are 1639 # checked separately. Use 'seen' to tell the two apart. 1640 if seen: 1641 assert v.name in self.tag_member.type.member_names() 1642 assert (isinstance(v.type, QAPISchemaObjectType) 1643 and not v.type.variants) 1644 v.type.check(schema) 1645 1646 def check_clash(self, info, seen): 1647 for v in self.variants: 1648 # Reset seen map for each variant, since qapi names from one 1649 # branch do not affect another branch 1650 v.type.check_clash(info, dict(seen)) 1651 1652 1653class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): 1654 role = 'branch' 1655 1656 def __init__(self, name, typ, ifcond=None): 1657 QAPISchemaObjectTypeMember.__init__(self, name, typ, False, ifcond) 1658 1659 1660class QAPISchemaAlternateType(QAPISchemaType): 1661 def __init__(self, name, info, doc, ifcond, variants): 1662 QAPISchemaType.__init__(self, name, info, doc, ifcond) 1663 assert isinstance(variants, QAPISchemaObjectTypeVariants) 1664 assert variants.tag_member 1665 variants.set_defined_in(name) 1666 variants.tag_member.set_defined_in(self.name) 1667 self.variants = variants 1668 1669 def check(self, schema): 1670 QAPISchemaType.check(self, schema) 1671 self.variants.tag_member.check(schema) 1672 # Not calling self.variants.check_clash(), because there's nothing 1673 # to clash with 1674 self.variants.check(schema, {}) 1675 # Alternate branch names have no relation to the tag enum values; 1676 # so we have to check for potential name collisions ourselves. 1677 seen = {} 1678 for v in self.variants.variants: 1679 v.check_clash(self.info, seen) 1680 # TODO check conflicting qtypes 1681 if self.doc: 1682 self.doc.connect_member(v) 1683 if self.doc: 1684 self.doc.check() 1685 1686 def c_type(self): 1687 return c_name(self.name) + pointer_suffix 1688 1689 def json_type(self): 1690 return 'value' 1691 1692 def visit(self, visitor): 1693 QAPISchemaType.visit(self, visitor) 1694 visitor.visit_alternate_type(self.name, self.info, self.ifcond, 1695 self.variants) 1696 1697 1698class QAPISchemaCommand(QAPISchemaEntity): 1699 def __init__(self, name, info, doc, ifcond, arg_type, ret_type, 1700 gen, success_response, boxed, allow_oob, allow_preconfig): 1701 QAPISchemaEntity.__init__(self, name, info, doc, ifcond) 1702 assert not arg_type or isinstance(arg_type, str) 1703 assert not ret_type or isinstance(ret_type, str) 1704 self._arg_type_name = arg_type 1705 self.arg_type = None 1706 self._ret_type_name = ret_type 1707 self.ret_type = None 1708 self.gen = gen 1709 self.success_response = success_response 1710 self.boxed = boxed 1711 self.allow_oob = allow_oob 1712 self.allow_preconfig = allow_preconfig 1713 1714 def check(self, schema): 1715 QAPISchemaEntity.check(self, schema) 1716 if self._arg_type_name: 1717 self.arg_type = schema.lookup_type(self._arg_type_name) 1718 assert isinstance(self.arg_type, QAPISchemaObjectType) 1719 assert not self.arg_type.variants or self.boxed 1720 elif self.boxed: 1721 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'") 1722 if self._ret_type_name: 1723 self.ret_type = schema.lookup_type(self._ret_type_name) 1724 assert isinstance(self.ret_type, QAPISchemaType) 1725 1726 def visit(self, visitor): 1727 QAPISchemaEntity.visit(self, visitor) 1728 visitor.visit_command(self.name, self.info, self.ifcond, 1729 self.arg_type, self.ret_type, 1730 self.gen, self.success_response, 1731 self.boxed, self.allow_oob, 1732 self.allow_preconfig) 1733 1734 1735class QAPISchemaEvent(QAPISchemaEntity): 1736 def __init__(self, name, info, doc, ifcond, arg_type, boxed): 1737 QAPISchemaEntity.__init__(self, name, info, doc, ifcond) 1738 assert not arg_type or isinstance(arg_type, str) 1739 self._arg_type_name = arg_type 1740 self.arg_type = None 1741 self.boxed = boxed 1742 1743 def check(self, schema): 1744 QAPISchemaEntity.check(self, schema) 1745 if self._arg_type_name: 1746 self.arg_type = schema.lookup_type(self._arg_type_name) 1747 assert isinstance(self.arg_type, QAPISchemaObjectType) 1748 assert not self.arg_type.variants or self.boxed 1749 elif self.boxed: 1750 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'") 1751 1752 def visit(self, visitor): 1753 QAPISchemaEntity.visit(self, visitor) 1754 visitor.visit_event(self.name, self.info, self.ifcond, 1755 self.arg_type, self.boxed) 1756 1757 1758class QAPISchema(object): 1759 def __init__(self, fname): 1760 self.fname = fname 1761 if sys.version_info[0] >= 3: 1762 f = open(fname, 'r', encoding='utf-8') 1763 else: 1764 f = open(fname, 'r') 1765 parser = QAPISchemaParser(f) 1766 exprs = check_exprs(parser.exprs) 1767 self.docs = parser.docs 1768 self._entity_list = [] 1769 self._entity_dict = {} 1770 self._predefining = True 1771 self._def_predefineds() 1772 self._predefining = False 1773 self._def_exprs(exprs) 1774 self.check() 1775 1776 def _def_entity(self, ent): 1777 # Only the predefined types are allowed to not have info 1778 assert ent.info or self._predefining 1779 assert ent.name is None or ent.name not in self._entity_dict 1780 self._entity_list.append(ent) 1781 if ent.name is not None: 1782 self._entity_dict[ent.name] = ent 1783 1784 def lookup_entity(self, name, typ=None): 1785 ent = self._entity_dict.get(name) 1786 if typ and not isinstance(ent, typ): 1787 return None 1788 return ent 1789 1790 def lookup_type(self, name): 1791 return self.lookup_entity(name, QAPISchemaType) 1792 1793 def _def_include(self, expr, info, doc): 1794 include = expr['include'] 1795 assert doc is None 1796 main_info = info 1797 while main_info.parent: 1798 main_info = main_info.parent 1799 fname = os.path.relpath(include, os.path.dirname(main_info.fname)) 1800 self._def_entity(QAPISchemaInclude(fname, info)) 1801 1802 def _def_builtin_type(self, name, json_type, c_type): 1803 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type)) 1804 # Instantiating only the arrays that are actually used would 1805 # be nice, but we can't as long as their generated code 1806 # (qapi-builtin-types.[ch]) may be shared by some other 1807 # schema. 1808 self._make_array_type(name, None) 1809 1810 def _def_predefineds(self): 1811 for t in [('str', 'string', 'char' + pointer_suffix), 1812 ('number', 'number', 'double'), 1813 ('int', 'int', 'int64_t'), 1814 ('int8', 'int', 'int8_t'), 1815 ('int16', 'int', 'int16_t'), 1816 ('int32', 'int', 'int32_t'), 1817 ('int64', 'int', 'int64_t'), 1818 ('uint8', 'int', 'uint8_t'), 1819 ('uint16', 'int', 'uint16_t'), 1820 ('uint32', 'int', 'uint32_t'), 1821 ('uint64', 'int', 'uint64_t'), 1822 ('size', 'int', 'uint64_t'), 1823 ('bool', 'boolean', 'bool'), 1824 ('any', 'value', 'QObject' + pointer_suffix), 1825 ('null', 'null', 'QNull' + pointer_suffix)]: 1826 self._def_builtin_type(*t) 1827 self.the_empty_object_type = QAPISchemaObjectType( 1828 'q_empty', None, None, None, None, [], None, []) 1829 self._def_entity(self.the_empty_object_type) 1830 1831 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 1832 'qbool'] 1833 qtype_values = self._make_enum_members([{'name': n} for n in qtypes]) 1834 1835 self._def_entity(QAPISchemaEnumType('QType', None, None, None, 1836 qtype_values, 'QTYPE')) 1837 1838 def _make_features(self, features): 1839 return [QAPISchemaFeature(f['name'], f.get('if')) for f in features] 1840 1841 def _make_enum_members(self, values): 1842 return [QAPISchemaEnumMember(v['name'], v.get('if')) 1843 for v in values] 1844 1845 def _make_implicit_enum_type(self, name, info, ifcond, values): 1846 # See also QAPISchemaObjectTypeMember._pretty_defined_in() 1847 name = name + 'Kind' # Use namespace reserved by add_name() 1848 self._def_entity(QAPISchemaEnumType( 1849 name, info, None, ifcond, self._make_enum_members(values), None)) 1850 return name 1851 1852 def _make_array_type(self, element_type, info): 1853 name = element_type + 'List' # Use namespace reserved by add_name() 1854 if not self.lookup_type(name): 1855 self._def_entity(QAPISchemaArrayType(name, info, element_type)) 1856 return name 1857 1858 def _make_implicit_object_type(self, name, info, doc, ifcond, 1859 role, members): 1860 if not members: 1861 return None 1862 # See also QAPISchemaObjectTypeMember._pretty_defined_in() 1863 name = 'q_obj_%s-%s' % (name, role) 1864 typ = self.lookup_entity(name, QAPISchemaObjectType) 1865 if typ: 1866 # The implicit object type has multiple users. This can 1867 # happen only for simple unions' implicit wrapper types. 1868 # Its ifcond should be the disjunction of its user's 1869 # ifconds. Not implemented. Instead, we always pass the 1870 # wrapped type's ifcond, which is trivially the same for all 1871 # users. It's also necessary for the wrapper to compile. 1872 # But it's not tight: the disjunction need not imply it. We 1873 # may end up compiling useless wrapper types. 1874 # TODO kill simple unions or implement the disjunction 1875 assert ifcond == typ._ifcond # pylint: disable=protected-access 1876 else: 1877 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, 1878 None, members, None, [])) 1879 return name 1880 1881 def _def_enum_type(self, expr, info, doc): 1882 name = expr['enum'] 1883 data = expr['data'] 1884 prefix = expr.get('prefix') 1885 ifcond = expr.get('if') 1886 self._def_entity(QAPISchemaEnumType( 1887 name, info, doc, ifcond, 1888 self._make_enum_members(data), prefix)) 1889 1890 def _make_member(self, name, typ, ifcond, info): 1891 optional = False 1892 if name.startswith('*'): 1893 name = name[1:] 1894 optional = True 1895 if isinstance(typ, list): 1896 assert len(typ) == 1 1897 typ = self._make_array_type(typ[0], info) 1898 return QAPISchemaObjectTypeMember(name, typ, optional, ifcond) 1899 1900 def _make_members(self, data, info): 1901 return [self._make_member(key, value['type'], value.get('if'), info) 1902 for (key, value) in data.items()] 1903 1904 def _def_struct_type(self, expr, info, doc): 1905 name = expr['struct'] 1906 base = expr.get('base') 1907 data = expr['data'] 1908 ifcond = expr.get('if') 1909 features = expr.get('features', []) 1910 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base, 1911 self._make_members(data, info), 1912 None, 1913 self._make_features(features))) 1914 1915 def _make_variant(self, case, typ, ifcond): 1916 return QAPISchemaObjectTypeVariant(case, typ, ifcond) 1917 1918 def _make_simple_variant(self, case, typ, ifcond, info): 1919 if isinstance(typ, list): 1920 assert len(typ) == 1 1921 typ = self._make_array_type(typ[0], info) 1922 typ = self._make_implicit_object_type( 1923 typ, info, None, self.lookup_type(typ), 1924 'wrapper', [self._make_member('data', typ, None, info)]) 1925 return QAPISchemaObjectTypeVariant(case, typ, ifcond) 1926 1927 def _def_union_type(self, expr, info, doc): 1928 name = expr['union'] 1929 data = expr['data'] 1930 base = expr.get('base') 1931 ifcond = expr.get('if') 1932 tag_name = expr.get('discriminator') 1933 tag_member = None 1934 if isinstance(base, dict): 1935 base = self._make_implicit_object_type( 1936 name, info, doc, ifcond, 1937 'base', self._make_members(base, info)) 1938 if tag_name: 1939 variants = [self._make_variant(key, value['type'], value.get('if')) 1940 for (key, value) in data.items()] 1941 members = [] 1942 else: 1943 variants = [self._make_simple_variant(key, value['type'], 1944 value.get('if'), info) 1945 for (key, value) in data.items()] 1946 enum = [{'name': v.name, 'if': v.ifcond} for v in variants] 1947 typ = self._make_implicit_enum_type(name, info, ifcond, enum) 1948 tag_member = QAPISchemaObjectTypeMember('type', typ, False) 1949 members = [tag_member] 1950 self._def_entity( 1951 QAPISchemaObjectType(name, info, doc, ifcond, base, members, 1952 QAPISchemaObjectTypeVariants(tag_name, 1953 tag_member, 1954 variants), [])) 1955 1956 def _def_alternate_type(self, expr, info, doc): 1957 name = expr['alternate'] 1958 data = expr['data'] 1959 ifcond = expr.get('if') 1960 variants = [self._make_variant(key, value['type'], value.get('if')) 1961 for (key, value) in data.items()] 1962 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False) 1963 self._def_entity( 1964 QAPISchemaAlternateType(name, info, doc, ifcond, 1965 QAPISchemaObjectTypeVariants(None, 1966 tag_member, 1967 variants))) 1968 1969 def _def_command(self, expr, info, doc): 1970 name = expr['command'] 1971 data = expr.get('data') 1972 rets = expr.get('returns') 1973 gen = expr.get('gen', True) 1974 success_response = expr.get('success-response', True) 1975 boxed = expr.get('boxed', False) 1976 allow_oob = expr.get('allow-oob', False) 1977 allow_preconfig = expr.get('allow-preconfig', False) 1978 ifcond = expr.get('if') 1979 if isinstance(data, OrderedDict): 1980 data = self._make_implicit_object_type( 1981 name, info, doc, ifcond, 'arg', self._make_members(data, info)) 1982 if isinstance(rets, list): 1983 assert len(rets) == 1 1984 rets = self._make_array_type(rets[0], info) 1985 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets, 1986 gen, success_response, 1987 boxed, allow_oob, allow_preconfig)) 1988 1989 def _def_event(self, expr, info, doc): 1990 name = expr['event'] 1991 data = expr.get('data') 1992 boxed = expr.get('boxed', False) 1993 ifcond = expr.get('if') 1994 if isinstance(data, OrderedDict): 1995 data = self._make_implicit_object_type( 1996 name, info, doc, ifcond, 'arg', self._make_members(data, info)) 1997 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed)) 1998 1999 def _def_exprs(self, exprs): 2000 for expr_elem in exprs: 2001 expr = expr_elem['expr'] 2002 info = expr_elem['info'] 2003 doc = expr_elem.get('doc') 2004 if 'enum' in expr: 2005 self._def_enum_type(expr, info, doc) 2006 elif 'struct' in expr: 2007 self._def_struct_type(expr, info, doc) 2008 elif 'union' in expr: 2009 self._def_union_type(expr, info, doc) 2010 elif 'alternate' in expr: 2011 self._def_alternate_type(expr, info, doc) 2012 elif 'command' in expr: 2013 self._def_command(expr, info, doc) 2014 elif 'event' in expr: 2015 self._def_event(expr, info, doc) 2016 elif 'include' in expr: 2017 self._def_include(expr, info, doc) 2018 else: 2019 assert False 2020 2021 def check(self): 2022 for ent in self._entity_list: 2023 ent.check(self) 2024 2025 def visit(self, visitor): 2026 visitor.visit_begin(self) 2027 module = None 2028 visitor.visit_module(module) 2029 for entity in self._entity_list: 2030 if visitor.visit_needed(entity): 2031 if entity.module != module: 2032 module = entity.module 2033 visitor.visit_module(module) 2034 entity.visit(visitor) 2035 visitor.visit_end() 2036 2037 2038# 2039# Code generation helpers 2040# 2041 2042def camel_case(name): 2043 new_name = '' 2044 first = True 2045 for ch in name: 2046 if ch in ['_', '-']: 2047 first = True 2048 elif first: 2049 new_name += ch.upper() 2050 first = False 2051 else: 2052 new_name += ch.lower() 2053 return new_name 2054 2055 2056# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1 2057# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2 2058# ENUM24_Name -> ENUM24_NAME 2059def camel_to_upper(value): 2060 c_fun_str = c_name(value, False) 2061 if value.isupper(): 2062 return c_fun_str 2063 2064 new_name = '' 2065 length = len(c_fun_str) 2066 for i in range(length): 2067 c = c_fun_str[i] 2068 # When c is upper and no '_' appears before, do more checks 2069 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_': 2070 if i < length - 1 and c_fun_str[i + 1].islower(): 2071 new_name += '_' 2072 elif c_fun_str[i - 1].isdigit(): 2073 new_name += '_' 2074 new_name += c 2075 return new_name.lstrip('_').upper() 2076 2077 2078def c_enum_const(type_name, const_name, prefix=None): 2079 if prefix is not None: 2080 type_name = prefix 2081 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper() 2082 2083 2084if hasattr(str, 'maketrans'): 2085 c_name_trans = str.maketrans('.-', '__') 2086else: 2087 c_name_trans = string.maketrans('.-', '__') 2088 2089 2090# Map @name to a valid C identifier. 2091# If @protect, avoid returning certain ticklish identifiers (like 2092# C keywords) by prepending 'q_'. 2093# 2094# Used for converting 'name' from a 'name':'type' qapi definition 2095# into a generated struct member, as well as converting type names 2096# into substrings of a generated C function name. 2097# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo' 2098# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int' 2099def c_name(name, protect=True): 2100 # ANSI X3J11/88-090, 3.1.1 2101 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue', 2102 'default', 'do', 'double', 'else', 'enum', 'extern', 2103 'float', 'for', 'goto', 'if', 'int', 'long', 'register', 2104 'return', 'short', 'signed', 'sizeof', 'static', 2105 'struct', 'switch', 'typedef', 'union', 'unsigned', 2106 'void', 'volatile', 'while']) 2107 # ISO/IEC 9899:1999, 6.4.1 2108 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary']) 2109 # ISO/IEC 9899:2011, 6.4.1 2110 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic', 2111 '_Noreturn', '_Static_assert', '_Thread_local']) 2112 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html 2113 # excluding _.* 2114 gcc_words = set(['asm', 'typeof']) 2115 # C++ ISO/IEC 14882:2003 2.11 2116 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete', 2117 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable', 2118 'namespace', 'new', 'operator', 'private', 'protected', 2119 'public', 'reinterpret_cast', 'static_cast', 'template', 2120 'this', 'throw', 'true', 'try', 'typeid', 'typename', 2121 'using', 'virtual', 'wchar_t', 2122 # alternative representations 2123 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', 2124 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq']) 2125 # namespace pollution: 2126 polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386']) 2127 name = name.translate(c_name_trans) 2128 if protect and (name in c89_words | c99_words | c11_words | gcc_words 2129 | cpp_words | polluted_words): 2130 return 'q_' + name 2131 return name 2132 2133 2134eatspace = '\033EATSPACE.' 2135pointer_suffix = ' *' + eatspace 2136 2137 2138def genindent(count): 2139 ret = '' 2140 for _ in range(count): 2141 ret += ' ' 2142 return ret 2143 2144 2145indent_level = 0 2146 2147 2148def push_indent(indent_amount=4): 2149 global indent_level 2150 indent_level += indent_amount 2151 2152 2153def pop_indent(indent_amount=4): 2154 global indent_level 2155 indent_level -= indent_amount 2156 2157 2158# Generate @code with @kwds interpolated. 2159# Obey indent_level, and strip eatspace. 2160def cgen(code, **kwds): 2161 raw = code % kwds 2162 if indent_level: 2163 indent = genindent(indent_level) 2164 # re.subn() lacks flags support before Python 2.7, use re.compile() 2165 raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE), 2166 indent, raw) 2167 raw = raw[0] 2168 return re.sub(re.escape(eatspace) + r' *', '', raw) 2169 2170 2171def mcgen(code, **kwds): 2172 if code[0] == '\n': 2173 code = code[1:] 2174 return cgen(code, **kwds) 2175 2176 2177def c_fname(filename): 2178 return re.sub(r'[^A-Za-z0-9_]', '_', filename) 2179 2180 2181def guardstart(name): 2182 return mcgen(''' 2183#ifndef %(name)s 2184#define %(name)s 2185 2186''', 2187 name=c_fname(name).upper()) 2188 2189 2190def guardend(name): 2191 return mcgen(''' 2192 2193#endif /* %(name)s */ 2194''', 2195 name=c_fname(name).upper()) 2196 2197 2198def gen_if(ifcond): 2199 ret = '' 2200 for ifc in ifcond: 2201 ret += mcgen(''' 2202#if %(cond)s 2203''', cond=ifc) 2204 return ret 2205 2206 2207def gen_endif(ifcond): 2208 ret = '' 2209 for ifc in reversed(ifcond): 2210 ret += mcgen(''' 2211#endif /* %(cond)s */ 2212''', cond=ifc) 2213 return ret 2214 2215 2216def _wrap_ifcond(ifcond, before, after): 2217 if before == after: 2218 return after # suppress empty #if ... #endif 2219 2220 assert after.startswith(before) 2221 out = before 2222 added = after[len(before):] 2223 if added[0] == '\n': 2224 out += '\n' 2225 added = added[1:] 2226 out += gen_if(ifcond) 2227 out += added 2228 out += gen_endif(ifcond) 2229 return out 2230 2231 2232def gen_enum_lookup(name, members, prefix=None): 2233 ret = mcgen(''' 2234 2235const QEnumLookup %(c_name)s_lookup = { 2236 .array = (const char *const[]) { 2237''', 2238 c_name=c_name(name)) 2239 for m in members: 2240 ret += gen_if(m.ifcond) 2241 index = c_enum_const(name, m.name, prefix) 2242 ret += mcgen(''' 2243 [%(index)s] = "%(name)s", 2244''', 2245 index=index, name=m.name) 2246 ret += gen_endif(m.ifcond) 2247 2248 ret += mcgen(''' 2249 }, 2250 .size = %(max_index)s 2251}; 2252''', 2253 max_index=c_enum_const(name, '_MAX', prefix)) 2254 return ret 2255 2256 2257def gen_enum(name, members, prefix=None): 2258 # append automatically generated _MAX value 2259 enum_members = members + [QAPISchemaEnumMember('_MAX')] 2260 2261 ret = mcgen(''' 2262 2263typedef enum %(c_name)s { 2264''', 2265 c_name=c_name(name)) 2266 2267 for m in enum_members: 2268 ret += gen_if(m.ifcond) 2269 ret += mcgen(''' 2270 %(c_enum)s, 2271''', 2272 c_enum=c_enum_const(name, m.name, prefix)) 2273 ret += gen_endif(m.ifcond) 2274 2275 ret += mcgen(''' 2276} %(c_name)s; 2277''', 2278 c_name=c_name(name)) 2279 2280 ret += mcgen(''' 2281 2282#define %(c_name)s_str(val) \\ 2283 qapi_enum_lookup(&%(c_name)s_lookup, (val)) 2284 2285extern const QEnumLookup %(c_name)s_lookup; 2286''', 2287 c_name=c_name(name)) 2288 return ret 2289 2290 2291def build_params(arg_type, boxed, extra=None): 2292 ret = '' 2293 sep = '' 2294 if boxed: 2295 assert arg_type 2296 ret += '%s arg' % arg_type.c_param_type() 2297 sep = ', ' 2298 elif arg_type: 2299 assert not arg_type.variants 2300 for memb in arg_type.members: 2301 ret += sep 2302 sep = ', ' 2303 if memb.optional: 2304 ret += 'bool has_%s, ' % c_name(memb.name) 2305 ret += '%s %s' % (memb.type.c_param_type(), 2306 c_name(memb.name)) 2307 if extra: 2308 ret += sep + extra 2309 return ret if ret else 'void' 2310 2311 2312# 2313# Accumulate and write output 2314# 2315 2316class QAPIGen(object): 2317 2318 def __init__(self, fname): 2319 self.fname = fname 2320 self._preamble = '' 2321 self._body = '' 2322 2323 def preamble_add(self, text): 2324 self._preamble += text 2325 2326 def add(self, text): 2327 self._body += text 2328 2329 def get_content(self): 2330 return self._top() + self._preamble + self._body + self._bottom() 2331 2332 def _top(self): 2333 return '' 2334 2335 def _bottom(self): 2336 return '' 2337 2338 def write(self, output_dir): 2339 pathname = os.path.join(output_dir, self.fname) 2340 dir = os.path.dirname(pathname) 2341 if dir: 2342 try: 2343 os.makedirs(dir) 2344 except os.error as e: 2345 if e.errno != errno.EEXIST: 2346 raise 2347 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666) 2348 if sys.version_info[0] >= 3: 2349 f = open(fd, 'r+', encoding='utf-8') 2350 else: 2351 f = os.fdopen(fd, 'r+') 2352 text = self.get_content() 2353 oldtext = f.read(len(text) + 1) 2354 if text != oldtext: 2355 f.seek(0) 2356 f.truncate(0) 2357 f.write(text) 2358 f.close() 2359 2360 2361@contextmanager 2362def ifcontext(ifcond, *args): 2363 """A 'with' statement context manager to wrap with start_if()/end_if() 2364 2365 *args: any number of QAPIGenCCode 2366 2367 Example:: 2368 2369 with ifcontext(ifcond, self._genh, self._genc): 2370 modify self._genh and self._genc ... 2371 2372 Is equivalent to calling:: 2373 2374 self._genh.start_if(ifcond) 2375 self._genc.start_if(ifcond) 2376 modify self._genh and self._genc ... 2377 self._genh.end_if() 2378 self._genc.end_if() 2379 """ 2380 for arg in args: 2381 arg.start_if(ifcond) 2382 yield 2383 for arg in args: 2384 arg.end_if() 2385 2386 2387class QAPIGenCCode(QAPIGen): 2388 2389 def __init__(self, fname): 2390 QAPIGen.__init__(self, fname) 2391 self._start_if = None 2392 2393 def start_if(self, ifcond): 2394 assert self._start_if is None 2395 self._start_if = (ifcond, self._body, self._preamble) 2396 2397 def end_if(self): 2398 assert self._start_if 2399 self._wrap_ifcond() 2400 self._start_if = None 2401 2402 def _wrap_ifcond(self): 2403 self._body = _wrap_ifcond(self._start_if[0], 2404 self._start_if[1], self._body) 2405 self._preamble = _wrap_ifcond(self._start_if[0], 2406 self._start_if[2], self._preamble) 2407 2408 def get_content(self): 2409 assert self._start_if is None 2410 return QAPIGen.get_content(self) 2411 2412 2413class QAPIGenC(QAPIGenCCode): 2414 2415 def __init__(self, fname, blurb, pydoc): 2416 QAPIGenCCode.__init__(self, fname) 2417 self._blurb = blurb 2418 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc, 2419 re.MULTILINE)) 2420 2421 def _top(self): 2422 return mcgen(''' 2423/* AUTOMATICALLY GENERATED, DO NOT MODIFY */ 2424 2425/* 2426%(blurb)s 2427 * 2428 * %(copyright)s 2429 * 2430 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 2431 * See the COPYING.LIB file in the top-level directory. 2432 */ 2433 2434''', 2435 blurb=self._blurb, copyright=self._copyright) 2436 2437 def _bottom(self): 2438 return mcgen(''' 2439 2440/* Dummy declaration to prevent empty .o file */ 2441char qapi_dummy_%(name)s; 2442''', 2443 name=c_fname(self.fname)) 2444 2445 2446class QAPIGenH(QAPIGenC): 2447 2448 def _top(self): 2449 return QAPIGenC._top(self) + guardstart(self.fname) 2450 2451 def _bottom(self): 2452 return guardend(self.fname) 2453 2454 2455class QAPIGenDoc(QAPIGen): 2456 2457 def _top(self): 2458 return (QAPIGen._top(self) 2459 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n') 2460 2461 2462class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor): 2463 2464 def __init__(self, prefix, what, blurb, pydoc): 2465 self._prefix = prefix 2466 self._what = what 2467 self._genc = QAPIGenC(self._prefix + self._what + '.c', 2468 blurb, pydoc) 2469 self._genh = QAPIGenH(self._prefix + self._what + '.h', 2470 blurb, pydoc) 2471 2472 def write(self, output_dir): 2473 self._genc.write(output_dir) 2474 self._genh.write(output_dir) 2475 2476 2477class QAPISchemaModularCVisitor(QAPISchemaVisitor): 2478 2479 def __init__(self, prefix, what, blurb, pydoc): 2480 self._prefix = prefix 2481 self._what = what 2482 self._blurb = blurb 2483 self._pydoc = pydoc 2484 self._genc = None 2485 self._genh = None 2486 self._module = {} 2487 self._main_module = None 2488 2489 @staticmethod 2490 def _is_user_module(name): 2491 return name and not name.startswith('./') 2492 2493 @staticmethod 2494 def _is_builtin_module(name): 2495 return not name 2496 2497 def _module_dirname(self, what, name): 2498 if self._is_user_module(name): 2499 return os.path.dirname(name) 2500 return '' 2501 2502 def _module_basename(self, what, name): 2503 ret = '' if self._is_builtin_module(name) else self._prefix 2504 if self._is_user_module(name): 2505 basename = os.path.basename(name) 2506 ret += what 2507 if name != self._main_module: 2508 ret += '-' + os.path.splitext(basename)[0] 2509 else: 2510 name = name[2:] if name else 'builtin' 2511 ret += re.sub(r'-', '-' + name + '-', what) 2512 return ret 2513 2514 def _module_filename(self, what, name): 2515 return os.path.join(self._module_dirname(what, name), 2516 self._module_basename(what, name)) 2517 2518 def _add_module(self, name, blurb): 2519 basename = self._module_filename(self._what, name) 2520 genc = QAPIGenC(basename + '.c', blurb, self._pydoc) 2521 genh = QAPIGenH(basename + '.h', blurb, self._pydoc) 2522 self._module[name] = (genc, genh) 2523 self._set_module(name) 2524 2525 def _add_user_module(self, name, blurb): 2526 assert self._is_user_module(name) 2527 if self._main_module is None: 2528 self._main_module = name 2529 self._add_module(name, blurb) 2530 2531 def _add_system_module(self, name, blurb): 2532 self._add_module(name and './' + name, blurb) 2533 2534 def _set_module(self, name): 2535 self._genc, self._genh = self._module[name] 2536 2537 def write(self, output_dir, opt_builtins=False): 2538 for name in self._module: 2539 if self._is_builtin_module(name) and not opt_builtins: 2540 continue 2541 (genc, genh) = self._module[name] 2542 genc.write(output_dir) 2543 genh.write(output_dir) 2544 2545 def _begin_user_module(self, name): 2546 pass 2547 2548 def visit_module(self, name): 2549 if name in self._module: 2550 self._set_module(name) 2551 elif self._is_builtin_module(name): 2552 # The built-in module has not been created. No code may 2553 # be generated. 2554 self._genc = None 2555 self._genh = None 2556 else: 2557 self._add_user_module(name, self._blurb) 2558 self._begin_user_module(name) 2559 2560 def visit_include(self, name, info): 2561 relname = os.path.relpath(self._module_filename(self._what, name), 2562 os.path.dirname(self._genh.fname)) 2563 self._genh.preamble_add(mcgen(''' 2564#include "%(relname)s.h" 2565''', 2566 relname=relname)) 2567