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