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 expression or free-form 107 108 Expression documentation blocks consist of 109 110 * a body section: one line naming the expression, 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 an expression documentation block for that symbol. 204 205 If it's an expression 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 an expression 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 a dictionary") 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, "Expression 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.src.startswith('null', self.pos): 552 self.val = None 553 self.cursor += 3 554 return 555 elif self.tok == '\n': 556 if self.cursor == len(self.src): 557 self.tok = None 558 return 559 self.line += 1 560 self.line_pos = self.cursor 561 elif not self.tok.isspace(): 562 raise QAPIParseError(self, 'Stray "%s"' % self.tok) 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(self, 'Expected "{", "[", "]", string, ' 596 '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(self, 'Expected "{", "[", string, ' 620 '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# Return the discriminator enum define if discriminator is specified as an 676# enum type, otherwise return None. 677def discriminator_find_enum_define(expr): 678 base = expr.get('base') 679 discriminator = expr.get('discriminator') 680 681 if not (discriminator and base): 682 return None 683 684 base_members = find_base_members(base) 685 if not base_members: 686 return None 687 688 discriminator_value = base_members.get(discriminator) 689 if not discriminator_value: 690 return None 691 692 return enum_types.get(discriminator_value['type']) 693 694 695# Names must be letters, numbers, -, and _. They must start with letter, 696# except for downstream extensions which must start with __RFQDN_. 697# Dots are only valid in the downstream extension prefix. 698valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?' 699 '[a-zA-Z][a-zA-Z0-9_-]*$') 700 701 702def check_name(info, source, name, allow_optional=False, 703 enum_member=False): 704 global valid_name 705 membername = name 706 707 if not isinstance(name, str): 708 raise QAPISemError(info, "%s requires a string name" % source) 709 if name.startswith('*'): 710 membername = name[1:] 711 if not allow_optional: 712 raise QAPISemError(info, "%s does not allow optional name '%s'" 713 % (source, name)) 714 # Enum members can start with a digit, because the generated C 715 # code always prefixes it with the enum name 716 if enum_member and membername[0].isdigit(): 717 membername = 'D' + membername 718 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty' 719 # and 'q_obj_*' implicit type names. 720 if not valid_name.match(membername) or \ 721 c_name(membername, False).startswith('q_'): 722 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name)) 723 724 725def add_name(name, info, meta, implicit=False): 726 global all_names 727 check_name(info, "'%s'" % meta, name) 728 # FIXME should reject names that differ only in '_' vs. '.' 729 # vs. '-', because they're liable to clash in generated C. 730 if name in all_names: 731 raise QAPISemError(info, "%s '%s' is already defined" 732 % (all_names[name], name)) 733 if not implicit and (name.endswith('Kind') or name.endswith('List')): 734 raise QAPISemError(info, "%s '%s' should not end in '%s'" 735 % (meta, name, name[-4:])) 736 all_names[name] = meta 737 738 739def check_if(expr, info): 740 741 def check_if_str(ifcond, info): 742 if not isinstance(ifcond, str): 743 raise QAPISemError( 744 info, "'if' condition must be a string or a list of strings") 745 if ifcond == '': 746 raise QAPISemError(info, "'if' condition '' makes no sense") 747 748 ifcond = expr.get('if') 749 if ifcond is None: 750 return 751 if isinstance(ifcond, list): 752 if ifcond == []: 753 raise QAPISemError(info, "'if' condition [] is useless") 754 for elt in ifcond: 755 check_if_str(elt, info) 756 else: 757 check_if_str(ifcond, info) 758 759 760def check_type(info, source, value, 761 allow_array=False, allow_dict=False, allow_metas=[]): 762 global all_names 763 764 if value is None: 765 return 766 767 # Check if array type for value is okay 768 if isinstance(value, list): 769 if not allow_array: 770 raise QAPISemError(info, "%s cannot be an array" % source) 771 if len(value) != 1 or not isinstance(value[0], str): 772 raise QAPISemError(info, 773 "%s: array type must contain single type name" % 774 source) 775 value = value[0] 776 777 # Check if type name for value is okay 778 if isinstance(value, str): 779 if value not in all_names: 780 raise QAPISemError(info, "%s uses unknown type '%s'" 781 % (source, value)) 782 if not all_names[value] in allow_metas: 783 raise QAPISemError(info, "%s cannot use %s type '%s'" % 784 (source, all_names[value], value)) 785 return 786 787 if not allow_dict: 788 raise QAPISemError(info, "%s should be a type name" % source) 789 790 if not isinstance(value, OrderedDict): 791 raise QAPISemError(info, 792 "%s should be a dictionary or type name" % source) 793 794 # value is a dictionary, check that each member is okay 795 for (key, arg) in value.items(): 796 check_name(info, "Member of %s" % source, key, 797 allow_optional=True) 798 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'): 799 raise QAPISemError(info, "Member of %s uses reserved name '%s'" 800 % (source, key)) 801 # Todo: allow dictionaries to represent default values of 802 # an optional argument. 803 check_known_keys(info, "member '%s' of %s" % (key, source), 804 arg, ['type'], ['if']) 805 check_type(info, "Member '%s' of %s" % (key, source), 806 arg['type'], allow_array=True, 807 allow_metas=['built-in', 'union', 'alternate', 'struct', 808 'enum']) 809 810 811def check_command(expr, info): 812 name = expr['command'] 813 boxed = expr.get('boxed', False) 814 815 args_meta = ['struct'] 816 if boxed: 817 args_meta += ['union'] 818 check_type(info, "'data' for command '%s'" % name, 819 expr.get('data'), allow_dict=not boxed, 820 allow_metas=args_meta) 821 returns_meta = ['union', 'struct'] 822 if name in returns_whitelist: 823 returns_meta += ['built-in', 'alternate', 'enum'] 824 check_type(info, "'returns' for command '%s'" % name, 825 expr.get('returns'), allow_array=True, 826 allow_metas=returns_meta) 827 828 829def check_event(expr, info): 830 name = expr['event'] 831 boxed = expr.get('boxed', False) 832 833 meta = ['struct'] 834 if boxed: 835 meta += ['union'] 836 check_type(info, "'data' for event '%s'" % name, 837 expr.get('data'), allow_dict=not boxed, 838 allow_metas=meta) 839 840 841def enum_get_names(expr): 842 return [e['name'] for e in expr['data']] 843 844 845def check_union(expr, info): 846 name = expr['union'] 847 base = expr.get('base') 848 discriminator = expr.get('discriminator') 849 members = expr['data'] 850 851 # Two types of unions, determined by discriminator. 852 853 # With no discriminator it is a simple union. 854 if discriminator is None: 855 enum_define = None 856 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum'] 857 if base is not None: 858 raise QAPISemError(info, "Simple union '%s' must not have a base" % 859 name) 860 861 # Else, it's a flat union. 862 else: 863 # The object must have a string or dictionary 'base'. 864 check_type(info, "'base' for union '%s'" % name, 865 base, allow_dict=True, 866 allow_metas=['struct']) 867 if not base: 868 raise QAPISemError(info, "Flat union '%s' must have a base" 869 % name) 870 base_members = find_base_members(base) 871 assert base_members is not None 872 873 # The value of member 'discriminator' must name a non-optional 874 # member of the base struct. 875 check_name(info, "Discriminator of flat union '%s'" % name, 876 discriminator) 877 discriminator_value = base_members.get(discriminator) 878 if not discriminator_value: 879 raise QAPISemError(info, 880 "Discriminator '%s' is not a member of base " 881 "struct '%s'" 882 % (discriminator, base)) 883 if discriminator_value.get('if'): 884 raise QAPISemError(info, 'The discriminator %s.%s for union %s ' 885 'must not be conditional' % 886 (base, discriminator, name)) 887 enum_define = enum_types.get(discriminator_value['type']) 888 allow_metas = ['struct'] 889 # Do not allow string discriminator 890 if not enum_define: 891 raise QAPISemError(info, 892 "Discriminator '%s' must be of enumeration " 893 "type" % discriminator) 894 895 # Check every branch; don't allow an empty union 896 if len(members) == 0: 897 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name) 898 for (key, value) in members.items(): 899 check_name(info, "Member of union '%s'" % name, key) 900 901 check_known_keys(info, "member '%s' of union '%s'" % (key, name), 902 value, ['type'], ['if']) 903 # Each value must name a known type 904 check_type(info, "Member '%s' of union '%s'" % (key, name), 905 value['type'], 906 allow_array=not base, allow_metas=allow_metas) 907 908 # If the discriminator names an enum type, then all members 909 # of 'data' must also be members of the enum type. 910 if enum_define: 911 if key not in enum_get_names(enum_define): 912 raise QAPISemError(info, 913 "Discriminator value '%s' is not found in " 914 "enum '%s'" 915 % (key, enum_define['enum'])) 916 917 918def check_alternate(expr, info): 919 name = expr['alternate'] 920 members = expr['data'] 921 types_seen = {} 922 923 # Check every branch; require at least two branches 924 if len(members) < 2: 925 raise QAPISemError(info, 926 "Alternate '%s' should have at least two branches " 927 "in 'data'" % name) 928 for (key, value) in members.items(): 929 check_name(info, "Member of alternate '%s'" % name, key) 930 check_known_keys(info, 931 "member '%s' of alternate '%s'" % (key, name), 932 value, ['type'], ['if']) 933 typ = value['type'] 934 935 # Ensure alternates have no type conflicts. 936 check_type(info, "Member '%s' of alternate '%s'" % (key, name), typ, 937 allow_metas=['built-in', 'union', 'struct', 'enum']) 938 qtype = find_alternate_member_qtype(typ) 939 if not qtype: 940 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use " 941 "type '%s'" % (name, key, typ)) 942 conflicting = set([qtype]) 943 if qtype == 'QTYPE_QSTRING': 944 enum_expr = enum_types.get(typ) 945 if enum_expr: 946 for v in enum_get_names(enum_expr): 947 if v in ['on', 'off']: 948 conflicting.add('QTYPE_QBOOL') 949 if re.match(r'[-+0-9.]', v): # lazy, could be tightened 950 conflicting.add('QTYPE_QNUM') 951 else: 952 conflicting.add('QTYPE_QNUM') 953 conflicting.add('QTYPE_QBOOL') 954 for qt in conflicting: 955 if qt in types_seen: 956 raise QAPISemError(info, "Alternate '%s' member '%s' can't " 957 "be distinguished from member '%s'" 958 % (name, key, types_seen[qt])) 959 types_seen[qt] = key 960 961 962def check_enum(expr, info): 963 name = expr['enum'] 964 members = expr['data'] 965 prefix = expr.get('prefix') 966 967 if not isinstance(members, list): 968 raise QAPISemError(info, 969 "Enum '%s' requires an array for 'data'" % name) 970 if prefix is not None and not isinstance(prefix, str): 971 raise QAPISemError(info, 972 "Enum '%s' requires a string for 'prefix'" % name) 973 974 for member in members: 975 source = "dictionary member of enum '%s'" % name 976 check_known_keys(info, source, member, ['name'], ['if']) 977 check_if(member, info) 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 check_name(info, "Feature of struct %s" % name, f['name']) 1004 1005 1006def check_known_keys(info, source, keys, required, optional): 1007 1008 def pprint(elems): 1009 return ', '.join("'" + e + "'" for e in sorted(elems)) 1010 1011 missing = set(required) - set(keys) 1012 if missing: 1013 raise QAPISemError(info, "Key%s %s %s missing from %s" 1014 % ('s' if len(missing) > 1 else '', pprint(missing), 1015 'are' if len(missing) > 1 else 'is', source)) 1016 allowed = set(required + optional) 1017 unknown = set(keys) - allowed 1018 if unknown: 1019 raise QAPISemError(info, "Unknown key%s %s in %s\nValid keys are %s." 1020 % ('s' if len(unknown) > 1 else '', pprint(unknown), 1021 source, pprint(allowed))) 1022 1023 1024def check_keys(expr_elem, meta, required, optional=[]): 1025 expr = expr_elem['expr'] 1026 info = expr_elem['info'] 1027 name = expr[meta] 1028 if not isinstance(name, str): 1029 raise QAPISemError(info, "'%s' key must have a string value" % meta) 1030 required = required + [meta] 1031 source = "%s '%s'" % (meta, name) 1032 check_known_keys(info, source, expr.keys(), required, optional) 1033 for (key, value) in expr.items(): 1034 if key in ['gen', 'success-response'] and value is not False: 1035 raise QAPISemError(info, 1036 "'%s' of %s '%s' should only use false value" 1037 % (key, meta, name)) 1038 if (key in ['boxed', 'allow-oob', 'allow-preconfig'] 1039 and value is not True): 1040 raise QAPISemError(info, 1041 "'%s' of %s '%s' should only use true value" 1042 % (key, meta, name)) 1043 if key == 'if': 1044 check_if(expr, info) 1045 1046 1047def normalize_enum(expr): 1048 if isinstance(expr['data'], list): 1049 expr['data'] = [m if isinstance(m, dict) else {'name': m} 1050 for m in expr['data']] 1051 1052 1053def normalize_members(members): 1054 if isinstance(members, OrderedDict): 1055 for key, arg in members.items(): 1056 if isinstance(arg, dict): 1057 continue 1058 members[key] = {'type': arg} 1059 1060 1061def normalize_features(features): 1062 if isinstance(features, list): 1063 features[:] = [f if isinstance(f, dict) else {'name': f} 1064 for f in features] 1065 1066 1067def check_exprs(exprs): 1068 global all_names 1069 1070 # Populate name table with names of built-in types 1071 for builtin in builtin_types.keys(): 1072 all_names[builtin] = 'built-in' 1073 1074 # Learn the types and check for valid expression keys 1075 for expr_elem in exprs: 1076 expr = expr_elem['expr'] 1077 info = expr_elem['info'] 1078 doc = expr_elem.get('doc') 1079 1080 if 'include' in expr: 1081 continue 1082 1083 if not doc and doc_required: 1084 raise QAPISemError(info, 1085 "Expression missing documentation comment") 1086 1087 if 'enum' in expr: 1088 meta = 'enum' 1089 check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix']) 1090 normalize_enum(expr) 1091 enum_types[expr[meta]] = expr 1092 elif 'union' in expr: 1093 meta = 'union' 1094 check_keys(expr_elem, 'union', ['data'], 1095 ['base', 'discriminator', 'if']) 1096 normalize_members(expr.get('base')) 1097 normalize_members(expr['data']) 1098 union_types[expr[meta]] = expr 1099 elif 'alternate' in expr: 1100 meta = 'alternate' 1101 check_keys(expr_elem, 'alternate', ['data'], ['if']) 1102 normalize_members(expr['data']) 1103 elif 'struct' in expr: 1104 meta = 'struct' 1105 check_keys(expr_elem, 'struct', ['data'], 1106 ['base', 'if', 'features']) 1107 normalize_members(expr['data']) 1108 normalize_features(expr.get('features')) 1109 struct_types[expr[meta]] = expr 1110 elif 'command' in expr: 1111 meta = 'command' 1112 check_keys(expr_elem, 'command', [], 1113 ['data', 'returns', 'gen', 'success-response', 1114 'boxed', 'allow-oob', 'allow-preconfig', 'if']) 1115 normalize_members(expr.get('data')) 1116 elif 'event' in expr: 1117 meta = 'event' 1118 check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if']) 1119 normalize_members(expr.get('data')) 1120 else: 1121 raise QAPISemError(expr_elem['info'], 1122 "Expression is missing metatype") 1123 name = expr[meta] 1124 add_name(name, info, meta) 1125 if doc and doc.symbol != name: 1126 raise QAPISemError(info, "Definition of '%s' follows documentation" 1127 " for '%s'" % (name, doc.symbol)) 1128 1129 # Try again for hidden UnionKind enum 1130 for expr_elem in exprs: 1131 expr = expr_elem['expr'] 1132 1133 if 'include' in expr: 1134 continue 1135 if 'union' in expr and not discriminator_find_enum_define(expr): 1136 name = '%sKind' % expr['union'] 1137 elif 'alternate' in expr: 1138 name = '%sKind' % expr['alternate'] 1139 else: 1140 continue 1141 enum_types[name] = {'enum': name} 1142 add_name(name, info, 'enum', implicit=True) 1143 1144 # Validate that exprs make sense 1145 for expr_elem in exprs: 1146 expr = expr_elem['expr'] 1147 info = expr_elem['info'] 1148 doc = expr_elem.get('doc') 1149 1150 if 'include' in expr: 1151 continue 1152 if 'enum' in expr: 1153 check_enum(expr, info) 1154 elif 'union' in expr: 1155 check_union(expr, info) 1156 elif 'alternate' in expr: 1157 check_alternate(expr, info) 1158 elif 'struct' in expr: 1159 check_struct(expr, info) 1160 elif 'command' in expr: 1161 check_command(expr, info) 1162 elif 'event' in expr: 1163 check_event(expr, info) 1164 else: 1165 assert False, 'unexpected meta type' 1166 1167 if doc: 1168 doc.check_expr(expr) 1169 1170 return exprs 1171 1172 1173# 1174# Schema compiler frontend 1175# 1176 1177def listify_cond(ifcond): 1178 if not ifcond: 1179 return [] 1180 if not isinstance(ifcond, list): 1181 return [ifcond] 1182 return ifcond 1183 1184 1185class QAPISchemaEntity(object): 1186 def __init__(self, name, info, doc, ifcond=None): 1187 assert name is None or isinstance(name, str) 1188 self.name = name 1189 self.module = None 1190 # For explicitly defined entities, info points to the (explicit) 1191 # definition. For builtins (and their arrays), info is None. 1192 # For implicitly defined entities, info points to a place that 1193 # triggered the implicit definition (there may be more than one 1194 # such place). 1195 self.info = info 1196 self.doc = doc 1197 self._ifcond = ifcond # self.ifcond is set only after .check() 1198 1199 def c_name(self): 1200 return c_name(self.name) 1201 1202 def check(self, schema): 1203 if isinstance(self._ifcond, QAPISchemaType): 1204 # inherit the condition from a type 1205 typ = self._ifcond 1206 typ.check(schema) 1207 self.ifcond = typ.ifcond 1208 else: 1209 self.ifcond = listify_cond(self._ifcond) 1210 if self.info: 1211 self.module = os.path.relpath(self.info['file'], 1212 os.path.dirname(schema.fname)) 1213 1214 def is_implicit(self): 1215 return not self.info 1216 1217 def visit(self, visitor): 1218 pass 1219 1220 1221class QAPISchemaVisitor(object): 1222 def visit_begin(self, schema): 1223 pass 1224 1225 def visit_end(self): 1226 pass 1227 1228 def visit_module(self, fname): 1229 pass 1230 1231 def visit_needed(self, entity): 1232 # Default to visiting everything 1233 return True 1234 1235 def visit_include(self, fname, info): 1236 pass 1237 1238 def visit_builtin_type(self, name, info, json_type): 1239 pass 1240 1241 def visit_enum_type(self, name, info, ifcond, members, prefix): 1242 pass 1243 1244 def visit_array_type(self, name, info, ifcond, element_type): 1245 pass 1246 1247 def visit_object_type(self, name, info, ifcond, base, members, variants, 1248 features): 1249 pass 1250 1251 def visit_object_type_flat(self, name, info, ifcond, members, variants, 1252 features): 1253 pass 1254 1255 def visit_alternate_type(self, name, info, ifcond, variants): 1256 pass 1257 1258 def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, 1259 success_response, boxed, allow_oob, allow_preconfig): 1260 pass 1261 1262 def visit_event(self, name, info, ifcond, arg_type, boxed): 1263 pass 1264 1265 1266class QAPISchemaInclude(QAPISchemaEntity): 1267 1268 def __init__(self, fname, info): 1269 QAPISchemaEntity.__init__(self, None, info, None) 1270 self.fname = fname 1271 1272 def visit(self, visitor): 1273 visitor.visit_include(self.fname, self.info) 1274 1275 1276class QAPISchemaType(QAPISchemaEntity): 1277 # Return the C type for common use. 1278 # For the types we commonly box, this is a pointer type. 1279 def c_type(self): 1280 pass 1281 1282 # Return the C type to be used in a parameter list. 1283 def c_param_type(self): 1284 return self.c_type() 1285 1286 # Return the C type to be used where we suppress boxing. 1287 def c_unboxed_type(self): 1288 return self.c_type() 1289 1290 def json_type(self): 1291 pass 1292 1293 def alternate_qtype(self): 1294 json2qtype = { 1295 'null': 'QTYPE_QNULL', 1296 'string': 'QTYPE_QSTRING', 1297 'number': 'QTYPE_QNUM', 1298 'int': 'QTYPE_QNUM', 1299 'boolean': 'QTYPE_QBOOL', 1300 'object': 'QTYPE_QDICT' 1301 } 1302 return json2qtype.get(self.json_type()) 1303 1304 def doc_type(self): 1305 if self.is_implicit(): 1306 return None 1307 return self.name 1308 1309 1310class QAPISchemaBuiltinType(QAPISchemaType): 1311 def __init__(self, name, json_type, c_type): 1312 QAPISchemaType.__init__(self, name, None, None) 1313 assert not c_type or isinstance(c_type, str) 1314 assert json_type in ('string', 'number', 'int', 'boolean', 'null', 1315 'value') 1316 self._json_type_name = json_type 1317 self._c_type_name = c_type 1318 1319 def c_name(self): 1320 return self.name 1321 1322 def c_type(self): 1323 return self._c_type_name 1324 1325 def c_param_type(self): 1326 if self.name == 'str': 1327 return 'const ' + self._c_type_name 1328 return self._c_type_name 1329 1330 def json_type(self): 1331 return self._json_type_name 1332 1333 def doc_type(self): 1334 return self.json_type() 1335 1336 def visit(self, visitor): 1337 visitor.visit_builtin_type(self.name, self.info, self.json_type()) 1338 1339 1340class QAPISchemaEnumType(QAPISchemaType): 1341 def __init__(self, name, info, doc, ifcond, members, prefix): 1342 QAPISchemaType.__init__(self, name, info, doc, ifcond) 1343 for m in members: 1344 assert isinstance(m, QAPISchemaMember) 1345 m.set_owner(name) 1346 assert prefix is None or isinstance(prefix, str) 1347 self.members = members 1348 self.prefix = prefix 1349 1350 def check(self, schema): 1351 QAPISchemaType.check(self, schema) 1352 seen = {} 1353 for m in self.members: 1354 m.check_clash(self.info, seen) 1355 if self.doc: 1356 self.doc.connect_member(m) 1357 1358 def is_implicit(self): 1359 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds() 1360 return self.name.endswith('Kind') or self.name == 'QType' 1361 1362 def c_type(self): 1363 return c_name(self.name) 1364 1365 def member_names(self): 1366 return [m.name for m in self.members] 1367 1368 def json_type(self): 1369 return 'string' 1370 1371 def visit(self, visitor): 1372 visitor.visit_enum_type(self.name, self.info, self.ifcond, 1373 self.members, self.prefix) 1374 1375 1376class QAPISchemaArrayType(QAPISchemaType): 1377 def __init__(self, name, info, element_type): 1378 QAPISchemaType.__init__(self, name, info, None, None) 1379 assert isinstance(element_type, str) 1380 self._element_type_name = element_type 1381 self.element_type = None 1382 1383 def check(self, schema): 1384 QAPISchemaType.check(self, schema) 1385 self.element_type = schema.lookup_type(self._element_type_name) 1386 assert self.element_type 1387 self.element_type.check(schema) 1388 self.module = self.element_type.module 1389 self.ifcond = self.element_type.ifcond 1390 1391 def is_implicit(self): 1392 return True 1393 1394 def c_type(self): 1395 return c_name(self.name) + pointer_suffix 1396 1397 def json_type(self): 1398 return 'array' 1399 1400 def doc_type(self): 1401 elt_doc_type = self.element_type.doc_type() 1402 if not elt_doc_type: 1403 return None 1404 return 'array of ' + elt_doc_type 1405 1406 def 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_owner(name) 1422 if variants is not None: 1423 assert isinstance(variants, QAPISchemaObjectTypeVariants) 1424 variants.set_owner(name) 1425 for f in features: 1426 assert isinstance(f, QAPISchemaFeature) 1427 f.set_owner(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 QAPISchemaType.check(self, schema) 1437 if self.members is False: # check for cycles 1438 raise QAPISemError(self.info, 1439 "Object %s contains itself" % self.name) 1440 if self.members: 1441 return 1442 self.members = False # mark as being checked 1443 seen = OrderedDict() 1444 if self._base_name: 1445 self.base = schema.lookup_type(self._base_name) 1446 assert isinstance(self.base, QAPISchemaObjectType) 1447 self.base.check(schema) 1448 self.base.check_clash(self.info, seen) 1449 for m in self.local_members: 1450 m.check(schema) 1451 m.check_clash(self.info, seen) 1452 if self.doc: 1453 self.doc.connect_member(m) 1454 self.members = seen.values() 1455 if self.variants: 1456 self.variants.check(schema, seen) 1457 assert self.variants.tag_member in self.members 1458 self.variants.check_clash(self.info, seen) 1459 1460 # Features are in a name space separate from members 1461 seen = {} 1462 for f in self.features: 1463 f.check_clash(self.info, seen) 1464 1465 if self.doc: 1466 self.doc.check() 1467 1468 # Check that the members of this type do not cause duplicate JSON members, 1469 # and update seen to track the members seen so far. Report any errors 1470 # on behalf of info, which is not necessarily self.info 1471 def check_clash(self, info, seen): 1472 assert not self.variants # not implemented 1473 for m in self.members: 1474 m.check_clash(info, seen) 1475 1476 def is_implicit(self): 1477 # See QAPISchema._make_implicit_object_type(), as well as 1478 # _def_predefineds() 1479 return self.name.startswith('q_') 1480 1481 def is_empty(self): 1482 assert self.members is not None 1483 return not self.members and not self.variants 1484 1485 def c_name(self): 1486 assert self.name != 'q_empty' 1487 return QAPISchemaType.c_name(self) 1488 1489 def c_type(self): 1490 assert not self.is_implicit() 1491 return c_name(self.name) + pointer_suffix 1492 1493 def c_unboxed_type(self): 1494 return c_name(self.name) 1495 1496 def json_type(self): 1497 return 'object' 1498 1499 def visit(self, visitor): 1500 visitor.visit_object_type(self.name, self.info, self.ifcond, 1501 self.base, self.local_members, self.variants, 1502 self.features) 1503 visitor.visit_object_type_flat(self.name, self.info, self.ifcond, 1504 self.members, self.variants, 1505 self.features) 1506 1507 1508class QAPISchemaMember(object): 1509 """ Represents object members, enum members and features """ 1510 role = 'member' 1511 1512 def __init__(self, name, ifcond=None): 1513 assert isinstance(name, str) 1514 self.name = name 1515 self.ifcond = listify_cond(ifcond) 1516 self.owner = None 1517 1518 def set_owner(self, name): 1519 assert not self.owner 1520 self.owner = name 1521 1522 def check_clash(self, info, seen): 1523 cname = c_name(self.name) 1524 if cname.lower() != cname and self.owner not in name_case_whitelist: 1525 raise QAPISemError(info, 1526 "%s should not use uppercase" % self.describe()) 1527 if cname in seen: 1528 raise QAPISemError(info, "%s collides with %s" % 1529 (self.describe(), seen[cname].describe())) 1530 seen[cname] = self 1531 1532 def _pretty_owner(self): 1533 owner = self.owner 1534 if owner.startswith('q_obj_'): 1535 # See QAPISchema._make_implicit_object_type() - reverse the 1536 # mapping there to create a nice human-readable description 1537 owner = owner[6:] 1538 if owner.endswith('-arg'): 1539 return '(parameter of %s)' % owner[:-4] 1540 elif owner.endswith('-base'): 1541 return '(base of %s)' % owner[:-5] 1542 else: 1543 assert owner.endswith('-wrapper') 1544 # Unreachable and not implemented 1545 assert False 1546 if owner.endswith('Kind'): 1547 # See QAPISchema._make_implicit_enum_type() 1548 return '(branch of %s)' % owner[:-4] 1549 return '(%s of %s)' % (self.role, owner) 1550 1551 def describe(self): 1552 return "'%s' %s" % (self.name, self._pretty_owner()) 1553 1554 1555class QAPISchemaFeature(QAPISchemaMember): 1556 role = 'feature' 1557 1558 1559class QAPISchemaObjectTypeMember(QAPISchemaMember): 1560 def __init__(self, name, typ, optional, ifcond=None): 1561 QAPISchemaMember.__init__(self, name, ifcond) 1562 assert isinstance(typ, str) 1563 assert isinstance(optional, bool) 1564 self._type_name = typ 1565 self.type = None 1566 self.optional = optional 1567 1568 def check(self, schema): 1569 assert self.owner 1570 self.type = schema.lookup_type(self._type_name) 1571 assert self.type 1572 1573 1574class QAPISchemaObjectTypeVariants(object): 1575 def __init__(self, tag_name, tag_member, variants): 1576 # Flat unions pass tag_name but not tag_member. 1577 # Simple unions and alternates pass tag_member but not tag_name. 1578 # After check(), tag_member is always set, and tag_name remains 1579 # a reliable witness of being used by a flat union. 1580 assert bool(tag_member) != bool(tag_name) 1581 assert (isinstance(tag_name, str) or 1582 isinstance(tag_member, QAPISchemaObjectTypeMember)) 1583 assert len(variants) > 0 1584 for v in variants: 1585 assert isinstance(v, QAPISchemaObjectTypeVariant) 1586 self._tag_name = tag_name 1587 self.tag_member = tag_member 1588 self.variants = variants 1589 1590 def set_owner(self, name): 1591 for v in self.variants: 1592 v.set_owner(name) 1593 1594 def check(self, schema, seen): 1595 if not self.tag_member: # flat union 1596 self.tag_member = seen[c_name(self._tag_name)] 1597 assert self._tag_name == self.tag_member.name 1598 assert isinstance(self.tag_member.type, QAPISchemaEnumType) 1599 if self._tag_name: # flat union 1600 # branches that are not explicitly covered get an empty type 1601 cases = set([v.name for v in self.variants]) 1602 for m in self.tag_member.type.members: 1603 if m.name not in cases: 1604 v = QAPISchemaObjectTypeVariant(m.name, 'q_empty', 1605 m.ifcond) 1606 v.set_owner(self.tag_member.owner) 1607 self.variants.append(v) 1608 for v in self.variants: 1609 v.check(schema) 1610 # Union names must match enum values; alternate names are 1611 # checked separately. Use 'seen' to tell the two apart. 1612 if seen: 1613 assert v.name in self.tag_member.type.member_names() 1614 assert isinstance(v.type, QAPISchemaObjectType) 1615 v.type.check(schema) 1616 1617 def check_clash(self, info, seen): 1618 for v in self.variants: 1619 # Reset seen map for each variant, since qapi names from one 1620 # branch do not affect another branch 1621 assert isinstance(v.type, QAPISchemaObjectType) 1622 v.type.check_clash(info, dict(seen)) 1623 1624 1625class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): 1626 role = 'branch' 1627 1628 def __init__(self, name, typ, ifcond=None): 1629 QAPISchemaObjectTypeMember.__init__(self, name, typ, False, ifcond) 1630 1631 1632class QAPISchemaAlternateType(QAPISchemaType): 1633 def __init__(self, name, info, doc, ifcond, variants): 1634 QAPISchemaType.__init__(self, name, info, doc, ifcond) 1635 assert isinstance(variants, QAPISchemaObjectTypeVariants) 1636 assert variants.tag_member 1637 variants.set_owner(name) 1638 variants.tag_member.set_owner(self.name) 1639 self.variants = variants 1640 1641 def check(self, schema): 1642 QAPISchemaType.check(self, schema) 1643 self.variants.tag_member.check(schema) 1644 # Not calling self.variants.check_clash(), because there's nothing 1645 # to clash with 1646 self.variants.check(schema, {}) 1647 # Alternate branch names have no relation to the tag enum values; 1648 # so we have to check for potential name collisions ourselves. 1649 seen = {} 1650 for v in self.variants.variants: 1651 v.check_clash(self.info, seen) 1652 if self.doc: 1653 self.doc.connect_member(v) 1654 if self.doc: 1655 self.doc.check() 1656 1657 def c_type(self): 1658 return c_name(self.name) + pointer_suffix 1659 1660 def json_type(self): 1661 return 'value' 1662 1663 def visit(self, visitor): 1664 visitor.visit_alternate_type(self.name, self.info, self.ifcond, 1665 self.variants) 1666 1667 1668class QAPISchemaCommand(QAPISchemaEntity): 1669 def __init__(self, name, info, doc, ifcond, arg_type, ret_type, 1670 gen, success_response, boxed, allow_oob, allow_preconfig): 1671 QAPISchemaEntity.__init__(self, name, info, doc, ifcond) 1672 assert not arg_type or isinstance(arg_type, str) 1673 assert not ret_type or isinstance(ret_type, str) 1674 self._arg_type_name = arg_type 1675 self.arg_type = None 1676 self._ret_type_name = ret_type 1677 self.ret_type = None 1678 self.gen = gen 1679 self.success_response = success_response 1680 self.boxed = boxed 1681 self.allow_oob = allow_oob 1682 self.allow_preconfig = allow_preconfig 1683 1684 def check(self, schema): 1685 QAPISchemaEntity.check(self, schema) 1686 if self._arg_type_name: 1687 self.arg_type = schema.lookup_type(self._arg_type_name) 1688 assert isinstance(self.arg_type, QAPISchemaObjectType) 1689 self.arg_type.check(schema) 1690 if self.boxed: 1691 if self.arg_type.is_empty(): 1692 raise QAPISemError(self.info, 1693 "Cannot use 'boxed' with empty type") 1694 else: 1695 assert not self.arg_type.variants 1696 elif self.boxed: 1697 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'") 1698 if self._ret_type_name: 1699 self.ret_type = schema.lookup_type(self._ret_type_name) 1700 assert isinstance(self.ret_type, QAPISchemaType) 1701 1702 def visit(self, visitor): 1703 visitor.visit_command(self.name, self.info, self.ifcond, 1704 self.arg_type, self.ret_type, 1705 self.gen, self.success_response, 1706 self.boxed, self.allow_oob, 1707 self.allow_preconfig) 1708 1709 1710class QAPISchemaEvent(QAPISchemaEntity): 1711 def __init__(self, name, info, doc, ifcond, arg_type, boxed): 1712 QAPISchemaEntity.__init__(self, name, info, doc, ifcond) 1713 assert not arg_type or isinstance(arg_type, str) 1714 self._arg_type_name = arg_type 1715 self.arg_type = None 1716 self.boxed = boxed 1717 1718 def check(self, schema): 1719 QAPISchemaEntity.check(self, schema) 1720 if self._arg_type_name: 1721 self.arg_type = schema.lookup_type(self._arg_type_name) 1722 assert isinstance(self.arg_type, QAPISchemaObjectType) 1723 self.arg_type.check(schema) 1724 if self.boxed: 1725 if self.arg_type.is_empty(): 1726 raise QAPISemError(self.info, 1727 "Cannot use 'boxed' with empty type") 1728 else: 1729 assert not self.arg_type.variants 1730 elif self.boxed: 1731 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'") 1732 1733 def visit(self, visitor): 1734 visitor.visit_event(self.name, self.info, self.ifcond, 1735 self.arg_type, self.boxed) 1736 1737 1738class QAPISchema(object): 1739 def __init__(self, fname): 1740 self.fname = fname 1741 if sys.version_info[0] >= 3: 1742 f = open(fname, 'r', encoding='utf-8') 1743 else: 1744 f = open(fname, 'r') 1745 parser = QAPISchemaParser(f) 1746 exprs = check_exprs(parser.exprs) 1747 self.docs = parser.docs 1748 self._entity_list = [] 1749 self._entity_dict = {} 1750 self._predefining = True 1751 self._def_predefineds() 1752 self._predefining = False 1753 self._def_exprs(exprs) 1754 self.check() 1755 1756 def _def_entity(self, ent): 1757 # Only the predefined types are allowed to not have info 1758 assert ent.info or self._predefining 1759 assert ent.name is None or ent.name not in self._entity_dict 1760 self._entity_list.append(ent) 1761 if ent.name is not None: 1762 self._entity_dict[ent.name] = ent 1763 1764 def lookup_entity(self, name, typ=None): 1765 ent = self._entity_dict.get(name) 1766 if typ and not isinstance(ent, typ): 1767 return None 1768 return ent 1769 1770 def lookup_type(self, name): 1771 return self.lookup_entity(name, QAPISchemaType) 1772 1773 def _def_include(self, expr, info, doc): 1774 include = expr['include'] 1775 assert doc is None 1776 main_info = info 1777 while main_info['parent']: 1778 main_info = main_info['parent'] 1779 fname = os.path.relpath(include, os.path.dirname(main_info['file'])) 1780 self._def_entity(QAPISchemaInclude(fname, info)) 1781 1782 def _def_builtin_type(self, name, json_type, c_type): 1783 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type)) 1784 # Instantiating only the arrays that are actually used would 1785 # be nice, but we can't as long as their generated code 1786 # (qapi-builtin-types.[ch]) may be shared by some other 1787 # schema. 1788 self._make_array_type(name, None) 1789 1790 def _def_predefineds(self): 1791 for t in [('str', 'string', 'char' + pointer_suffix), 1792 ('number', 'number', 'double'), 1793 ('int', 'int', 'int64_t'), 1794 ('int8', 'int', 'int8_t'), 1795 ('int16', 'int', 'int16_t'), 1796 ('int32', 'int', 'int32_t'), 1797 ('int64', 'int', 'int64_t'), 1798 ('uint8', 'int', 'uint8_t'), 1799 ('uint16', 'int', 'uint16_t'), 1800 ('uint32', 'int', 'uint32_t'), 1801 ('uint64', 'int', 'uint64_t'), 1802 ('size', 'int', 'uint64_t'), 1803 ('bool', 'boolean', 'bool'), 1804 ('any', 'value', 'QObject' + pointer_suffix), 1805 ('null', 'null', 'QNull' + pointer_suffix)]: 1806 self._def_builtin_type(*t) 1807 self.the_empty_object_type = QAPISchemaObjectType( 1808 'q_empty', None, None, None, None, [], None, []) 1809 self._def_entity(self.the_empty_object_type) 1810 1811 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 1812 'qbool'] 1813 qtype_values = self._make_enum_members([{'name': n} for n in qtypes]) 1814 1815 self._def_entity(QAPISchemaEnumType('QType', None, None, None, 1816 qtype_values, 'QTYPE')) 1817 1818 def _make_features(self, features): 1819 return [QAPISchemaFeature(f['name'], f.get('if')) for f in features] 1820 1821 def _make_enum_members(self, values): 1822 return [QAPISchemaMember(v['name'], v.get('if')) for v in values] 1823 1824 def _make_implicit_enum_type(self, name, info, ifcond, values): 1825 # See also QAPISchemaObjectTypeMember._pretty_owner() 1826 name = name + 'Kind' # Use namespace reserved by add_name() 1827 self._def_entity(QAPISchemaEnumType( 1828 name, info, None, ifcond, self._make_enum_members(values), None)) 1829 return name 1830 1831 def _make_array_type(self, element_type, info): 1832 name = element_type + 'List' # Use namespace reserved by add_name() 1833 if not self.lookup_type(name): 1834 self._def_entity(QAPISchemaArrayType(name, info, element_type)) 1835 return name 1836 1837 def _make_implicit_object_type(self, name, info, doc, ifcond, 1838 role, members): 1839 if not members: 1840 return None 1841 # See also QAPISchemaObjectTypeMember._pretty_owner() 1842 name = 'q_obj_%s-%s' % (name, role) 1843 typ = self.lookup_entity(name, QAPISchemaObjectType) 1844 if typ: 1845 # The implicit object type has multiple users. This can 1846 # happen only for simple unions' implicit wrapper types. 1847 # Its ifcond should be the disjunction of its user's 1848 # ifconds. Not implemented. Instead, we always pass the 1849 # wrapped type's ifcond, which is trivially the same for all 1850 # users. It's also necessary for the wrapper to compile. 1851 # But it's not tight: the disjunction need not imply it. We 1852 # may end up compiling useless wrapper types. 1853 # TODO kill simple unions or implement the disjunction 1854 assert ifcond == typ._ifcond # pylint: disable=protected-access 1855 else: 1856 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, 1857 None, members, None, [])) 1858 return name 1859 1860 def _def_enum_type(self, expr, info, doc): 1861 name = expr['enum'] 1862 data = expr['data'] 1863 prefix = expr.get('prefix') 1864 ifcond = expr.get('if') 1865 self._def_entity(QAPISchemaEnumType( 1866 name, info, doc, ifcond, 1867 self._make_enum_members(data), prefix)) 1868 1869 def _make_member(self, name, typ, ifcond, info): 1870 optional = False 1871 if name.startswith('*'): 1872 name = name[1:] 1873 optional = True 1874 if isinstance(typ, list): 1875 assert len(typ) == 1 1876 typ = self._make_array_type(typ[0], info) 1877 return QAPISchemaObjectTypeMember(name, typ, optional, ifcond) 1878 1879 def _make_members(self, data, info): 1880 return [self._make_member(key, value['type'], value.get('if'), info) 1881 for (key, value) in data.items()] 1882 1883 def _def_struct_type(self, expr, info, doc): 1884 name = expr['struct'] 1885 base = expr.get('base') 1886 data = expr['data'] 1887 ifcond = expr.get('if') 1888 features = expr.get('features', []) 1889 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base, 1890 self._make_members(data, info), 1891 None, 1892 self._make_features(features))) 1893 1894 def _make_variant(self, case, typ, ifcond): 1895 return QAPISchemaObjectTypeVariant(case, typ, ifcond) 1896 1897 def _make_simple_variant(self, case, typ, ifcond, info): 1898 if isinstance(typ, list): 1899 assert len(typ) == 1 1900 typ = self._make_array_type(typ[0], info) 1901 typ = self._make_implicit_object_type( 1902 typ, info, None, self.lookup_type(typ), 1903 'wrapper', [self._make_member('data', typ, None, info)]) 1904 return QAPISchemaObjectTypeVariant(case, typ, ifcond) 1905 1906 def _def_union_type(self, expr, info, doc): 1907 name = expr['union'] 1908 data = expr['data'] 1909 base = expr.get('base') 1910 ifcond = expr.get('if') 1911 tag_name = expr.get('discriminator') 1912 tag_member = None 1913 if isinstance(base, dict): 1914 base = self._make_implicit_object_type( 1915 name, info, doc, ifcond, 1916 'base', self._make_members(base, info)) 1917 if tag_name: 1918 variants = [self._make_variant(key, value['type'], value.get('if')) 1919 for (key, value) in data.items()] 1920 members = [] 1921 else: 1922 variants = [self._make_simple_variant(key, value['type'], 1923 value.get('if'), info) 1924 for (key, value) in data.items()] 1925 enum = [{'name': v.name, 'if': v.ifcond} for v in variants] 1926 typ = self._make_implicit_enum_type(name, info, ifcond, enum) 1927 tag_member = QAPISchemaObjectTypeMember('type', typ, False) 1928 members = [tag_member] 1929 self._def_entity( 1930 QAPISchemaObjectType(name, info, doc, ifcond, base, members, 1931 QAPISchemaObjectTypeVariants(tag_name, 1932 tag_member, 1933 variants), [])) 1934 1935 def _def_alternate_type(self, expr, info, doc): 1936 name = expr['alternate'] 1937 data = expr['data'] 1938 ifcond = expr.get('if') 1939 variants = [self._make_variant(key, value['type'], value.get('if')) 1940 for (key, value) in data.items()] 1941 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False) 1942 self._def_entity( 1943 QAPISchemaAlternateType(name, info, doc, ifcond, 1944 QAPISchemaObjectTypeVariants(None, 1945 tag_member, 1946 variants))) 1947 1948 def _def_command(self, expr, info, doc): 1949 name = expr['command'] 1950 data = expr.get('data') 1951 rets = expr.get('returns') 1952 gen = expr.get('gen', True) 1953 success_response = expr.get('success-response', True) 1954 boxed = expr.get('boxed', False) 1955 allow_oob = expr.get('allow-oob', False) 1956 allow_preconfig = expr.get('allow-preconfig', False) 1957 ifcond = expr.get('if') 1958 if isinstance(data, OrderedDict): 1959 data = self._make_implicit_object_type( 1960 name, info, doc, ifcond, 'arg', self._make_members(data, info)) 1961 if isinstance(rets, list): 1962 assert len(rets) == 1 1963 rets = self._make_array_type(rets[0], info) 1964 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets, 1965 gen, success_response, 1966 boxed, allow_oob, allow_preconfig)) 1967 1968 def _def_event(self, expr, info, doc): 1969 name = expr['event'] 1970 data = expr.get('data') 1971 boxed = expr.get('boxed', False) 1972 ifcond = expr.get('if') 1973 if isinstance(data, OrderedDict): 1974 data = self._make_implicit_object_type( 1975 name, info, doc, ifcond, 'arg', self._make_members(data, info)) 1976 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed)) 1977 1978 def _def_exprs(self, exprs): 1979 for expr_elem in exprs: 1980 expr = expr_elem['expr'] 1981 info = expr_elem['info'] 1982 doc = expr_elem.get('doc') 1983 if 'enum' in expr: 1984 self._def_enum_type(expr, info, doc) 1985 elif 'struct' in expr: 1986 self._def_struct_type(expr, info, doc) 1987 elif 'union' in expr: 1988 self._def_union_type(expr, info, doc) 1989 elif 'alternate' in expr: 1990 self._def_alternate_type(expr, info, doc) 1991 elif 'command' in expr: 1992 self._def_command(expr, info, doc) 1993 elif 'event' in expr: 1994 self._def_event(expr, info, doc) 1995 elif 'include' in expr: 1996 self._def_include(expr, info, doc) 1997 else: 1998 assert False 1999 2000 def check(self): 2001 for ent in self._entity_list: 2002 ent.check(self) 2003 2004 def visit(self, visitor): 2005 visitor.visit_begin(self) 2006 module = None 2007 visitor.visit_module(module) 2008 for entity in self._entity_list: 2009 if visitor.visit_needed(entity): 2010 if entity.module != module: 2011 module = entity.module 2012 visitor.visit_module(module) 2013 entity.visit(visitor) 2014 visitor.visit_end() 2015 2016 2017# 2018# Code generation helpers 2019# 2020 2021def camel_case(name): 2022 new_name = '' 2023 first = True 2024 for ch in name: 2025 if ch in ['_', '-']: 2026 first = True 2027 elif first: 2028 new_name += ch.upper() 2029 first = False 2030 else: 2031 new_name += ch.lower() 2032 return new_name 2033 2034 2035# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1 2036# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2 2037# ENUM24_Name -> ENUM24_NAME 2038def camel_to_upper(value): 2039 c_fun_str = c_name(value, False) 2040 if value.isupper(): 2041 return c_fun_str 2042 2043 new_name = '' 2044 length = len(c_fun_str) 2045 for i in range(length): 2046 c = c_fun_str[i] 2047 # When c is upper and no '_' appears before, do more checks 2048 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_': 2049 if i < length - 1 and c_fun_str[i + 1].islower(): 2050 new_name += '_' 2051 elif c_fun_str[i - 1].isdigit(): 2052 new_name += '_' 2053 new_name += c 2054 return new_name.lstrip('_').upper() 2055 2056 2057def c_enum_const(type_name, const_name, prefix=None): 2058 if prefix is not None: 2059 type_name = prefix 2060 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper() 2061 2062 2063if hasattr(str, 'maketrans'): 2064 c_name_trans = str.maketrans('.-', '__') 2065else: 2066 c_name_trans = string.maketrans('.-', '__') 2067 2068 2069# Map @name to a valid C identifier. 2070# If @protect, avoid returning certain ticklish identifiers (like 2071# C keywords) by prepending 'q_'. 2072# 2073# Used for converting 'name' from a 'name':'type' qapi definition 2074# into a generated struct member, as well as converting type names 2075# into substrings of a generated C function name. 2076# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo' 2077# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int' 2078def c_name(name, protect=True): 2079 # ANSI X3J11/88-090, 3.1.1 2080 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue', 2081 'default', 'do', 'double', 'else', 'enum', 'extern', 2082 'float', 'for', 'goto', 'if', 'int', 'long', 'register', 2083 'return', 'short', 'signed', 'sizeof', 'static', 2084 'struct', 'switch', 'typedef', 'union', 'unsigned', 2085 'void', 'volatile', 'while']) 2086 # ISO/IEC 9899:1999, 6.4.1 2087 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary']) 2088 # ISO/IEC 9899:2011, 6.4.1 2089 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic', 2090 '_Noreturn', '_Static_assert', '_Thread_local']) 2091 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html 2092 # excluding _.* 2093 gcc_words = set(['asm', 'typeof']) 2094 # C++ ISO/IEC 14882:2003 2.11 2095 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete', 2096 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable', 2097 'namespace', 'new', 'operator', 'private', 'protected', 2098 'public', 'reinterpret_cast', 'static_cast', 'template', 2099 'this', 'throw', 'true', 'try', 'typeid', 'typename', 2100 'using', 'virtual', 'wchar_t', 2101 # alternative representations 2102 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', 2103 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq']) 2104 # namespace pollution: 2105 polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386']) 2106 name = name.translate(c_name_trans) 2107 if protect and (name in c89_words | c99_words | c11_words | gcc_words 2108 | cpp_words | polluted_words): 2109 return 'q_' + name 2110 return name 2111 2112 2113eatspace = '\033EATSPACE.' 2114pointer_suffix = ' *' + eatspace 2115 2116 2117def genindent(count): 2118 ret = '' 2119 for _ in range(count): 2120 ret += ' ' 2121 return ret 2122 2123 2124indent_level = 0 2125 2126 2127def push_indent(indent_amount=4): 2128 global indent_level 2129 indent_level += indent_amount 2130 2131 2132def pop_indent(indent_amount=4): 2133 global indent_level 2134 indent_level -= indent_amount 2135 2136 2137# Generate @code with @kwds interpolated. 2138# Obey indent_level, and strip eatspace. 2139def cgen(code, **kwds): 2140 raw = code % kwds 2141 if indent_level: 2142 indent = genindent(indent_level) 2143 # re.subn() lacks flags support before Python 2.7, use re.compile() 2144 raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE), 2145 indent, raw) 2146 raw = raw[0] 2147 return re.sub(re.escape(eatspace) + r' *', '', raw) 2148 2149 2150def mcgen(code, **kwds): 2151 if code[0] == '\n': 2152 code = code[1:] 2153 return cgen(code, **kwds) 2154 2155 2156def c_fname(filename): 2157 return re.sub(r'[^A-Za-z0-9_]', '_', filename) 2158 2159 2160def guardstart(name): 2161 return mcgen(''' 2162#ifndef %(name)s 2163#define %(name)s 2164 2165''', 2166 name=c_fname(name).upper()) 2167 2168 2169def guardend(name): 2170 return mcgen(''' 2171 2172#endif /* %(name)s */ 2173''', 2174 name=c_fname(name).upper()) 2175 2176 2177def gen_if(ifcond): 2178 ret = '' 2179 for ifc in ifcond: 2180 ret += mcgen(''' 2181#if %(cond)s 2182''', cond=ifc) 2183 return ret 2184 2185 2186def gen_endif(ifcond): 2187 ret = '' 2188 for ifc in reversed(ifcond): 2189 ret += mcgen(''' 2190#endif /* %(cond)s */ 2191''', cond=ifc) 2192 return ret 2193 2194 2195def _wrap_ifcond(ifcond, before, after): 2196 if before == after: 2197 return after # suppress empty #if ... #endif 2198 2199 assert after.startswith(before) 2200 out = before 2201 added = after[len(before):] 2202 if added[0] == '\n': 2203 out += '\n' 2204 added = added[1:] 2205 out += gen_if(ifcond) 2206 out += added 2207 out += gen_endif(ifcond) 2208 return out 2209 2210 2211def gen_enum_lookup(name, members, prefix=None): 2212 ret = mcgen(''' 2213 2214const QEnumLookup %(c_name)s_lookup = { 2215 .array = (const char *const[]) { 2216''', 2217 c_name=c_name(name)) 2218 for m in members: 2219 ret += gen_if(m.ifcond) 2220 index = c_enum_const(name, m.name, prefix) 2221 ret += mcgen(''' 2222 [%(index)s] = "%(name)s", 2223''', 2224 index=index, name=m.name) 2225 ret += gen_endif(m.ifcond) 2226 2227 ret += mcgen(''' 2228 }, 2229 .size = %(max_index)s 2230}; 2231''', 2232 max_index=c_enum_const(name, '_MAX', prefix)) 2233 return ret 2234 2235 2236def gen_enum(name, members, prefix=None): 2237 # append automatically generated _MAX value 2238 enum_members = members + [QAPISchemaMember('_MAX')] 2239 2240 ret = mcgen(''' 2241 2242typedef enum %(c_name)s { 2243''', 2244 c_name=c_name(name)) 2245 2246 for m in enum_members: 2247 ret += gen_if(m.ifcond) 2248 ret += mcgen(''' 2249 %(c_enum)s, 2250''', 2251 c_enum=c_enum_const(name, m.name, prefix)) 2252 ret += gen_endif(m.ifcond) 2253 2254 ret += mcgen(''' 2255} %(c_name)s; 2256''', 2257 c_name=c_name(name)) 2258 2259 ret += mcgen(''' 2260 2261#define %(c_name)s_str(val) \\ 2262 qapi_enum_lookup(&%(c_name)s_lookup, (val)) 2263 2264extern const QEnumLookup %(c_name)s_lookup; 2265''', 2266 c_name=c_name(name)) 2267 return ret 2268 2269 2270def build_params(arg_type, boxed, extra=None): 2271 ret = '' 2272 sep = '' 2273 if boxed: 2274 assert arg_type 2275 ret += '%s arg' % arg_type.c_param_type() 2276 sep = ', ' 2277 elif arg_type: 2278 assert not arg_type.variants 2279 for memb in arg_type.members: 2280 ret += sep 2281 sep = ', ' 2282 if memb.optional: 2283 ret += 'bool has_%s, ' % c_name(memb.name) 2284 ret += '%s %s' % (memb.type.c_param_type(), 2285 c_name(memb.name)) 2286 if extra: 2287 ret += sep + extra 2288 return ret if ret else 'void' 2289 2290 2291# 2292# Accumulate and write output 2293# 2294 2295class QAPIGen(object): 2296 2297 def __init__(self, fname): 2298 self.fname = fname 2299 self._preamble = '' 2300 self._body = '' 2301 2302 def preamble_add(self, text): 2303 self._preamble += text 2304 2305 def add(self, text): 2306 self._body += text 2307 2308 def get_content(self): 2309 return self._top() + self._preamble + self._body + self._bottom() 2310 2311 def _top(self): 2312 return '' 2313 2314 def _bottom(self): 2315 return '' 2316 2317 def write(self, output_dir): 2318 pathname = os.path.join(output_dir, self.fname) 2319 dir = os.path.dirname(pathname) 2320 if dir: 2321 try: 2322 os.makedirs(dir) 2323 except os.error as e: 2324 if e.errno != errno.EEXIST: 2325 raise 2326 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666) 2327 if sys.version_info[0] >= 3: 2328 f = open(fd, 'r+', encoding='utf-8') 2329 else: 2330 f = os.fdopen(fd, 'r+') 2331 text = self.get_content() 2332 oldtext = f.read(len(text) + 1) 2333 if text != oldtext: 2334 f.seek(0) 2335 f.truncate(0) 2336 f.write(text) 2337 f.close() 2338 2339 2340@contextmanager 2341def ifcontext(ifcond, *args): 2342 """A 'with' statement context manager to wrap with start_if()/end_if() 2343 2344 *args: any number of QAPIGenCCode 2345 2346 Example:: 2347 2348 with ifcontext(ifcond, self._genh, self._genc): 2349 modify self._genh and self._genc ... 2350 2351 Is equivalent to calling:: 2352 2353 self._genh.start_if(ifcond) 2354 self._genc.start_if(ifcond) 2355 modify self._genh and self._genc ... 2356 self._genh.end_if() 2357 self._genc.end_if() 2358 """ 2359 for arg in args: 2360 arg.start_if(ifcond) 2361 yield 2362 for arg in args: 2363 arg.end_if() 2364 2365 2366class QAPIGenCCode(QAPIGen): 2367 2368 def __init__(self, fname): 2369 QAPIGen.__init__(self, fname) 2370 self._start_if = None 2371 2372 def start_if(self, ifcond): 2373 assert self._start_if is None 2374 self._start_if = (ifcond, self._body, self._preamble) 2375 2376 def end_if(self): 2377 assert self._start_if 2378 self._wrap_ifcond() 2379 self._start_if = None 2380 2381 def _wrap_ifcond(self): 2382 self._body = _wrap_ifcond(self._start_if[0], 2383 self._start_if[1], self._body) 2384 self._preamble = _wrap_ifcond(self._start_if[0], 2385 self._start_if[2], self._preamble) 2386 2387 def get_content(self): 2388 assert self._start_if is None 2389 return QAPIGen.get_content(self) 2390 2391 2392class QAPIGenC(QAPIGenCCode): 2393 2394 def __init__(self, fname, blurb, pydoc): 2395 QAPIGenCCode.__init__(self, fname) 2396 self._blurb = blurb 2397 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc, 2398 re.MULTILINE)) 2399 2400 def _top(self): 2401 return mcgen(''' 2402/* AUTOMATICALLY GENERATED, DO NOT MODIFY */ 2403 2404/* 2405%(blurb)s 2406 * 2407 * %(copyright)s 2408 * 2409 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 2410 * See the COPYING.LIB file in the top-level directory. 2411 */ 2412 2413''', 2414 blurb=self._blurb, copyright=self._copyright) 2415 2416 def _bottom(self): 2417 return mcgen(''' 2418 2419/* Dummy declaration to prevent empty .o file */ 2420char qapi_dummy_%(name)s; 2421''', 2422 name=c_fname(self.fname)) 2423 2424 2425class QAPIGenH(QAPIGenC): 2426 2427 def _top(self): 2428 return QAPIGenC._top(self) + guardstart(self.fname) 2429 2430 def _bottom(self): 2431 return guardend(self.fname) 2432 2433 2434class QAPIGenDoc(QAPIGen): 2435 2436 def _top(self): 2437 return (QAPIGen._top(self) 2438 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n') 2439 2440 2441class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor): 2442 2443 def __init__(self, prefix, what, blurb, pydoc): 2444 self._prefix = prefix 2445 self._what = what 2446 self._genc = QAPIGenC(self._prefix + self._what + '.c', 2447 blurb, pydoc) 2448 self._genh = QAPIGenH(self._prefix + self._what + '.h', 2449 blurb, pydoc) 2450 2451 def write(self, output_dir): 2452 self._genc.write(output_dir) 2453 self._genh.write(output_dir) 2454 2455 2456class QAPISchemaModularCVisitor(QAPISchemaVisitor): 2457 2458 def __init__(self, prefix, what, blurb, pydoc): 2459 self._prefix = prefix 2460 self._what = what 2461 self._blurb = blurb 2462 self._pydoc = pydoc 2463 self._genc = None 2464 self._genh = None 2465 self._module = {} 2466 self._main_module = None 2467 2468 @staticmethod 2469 def _is_user_module(name): 2470 return name and not name.startswith('./') 2471 2472 @staticmethod 2473 def _is_builtin_module(name): 2474 return not name 2475 2476 def _module_dirname(self, what, name): 2477 if self._is_user_module(name): 2478 return os.path.dirname(name) 2479 return '' 2480 2481 def _module_basename(self, what, name): 2482 ret = '' if self._is_builtin_module(name) else self._prefix 2483 if self._is_user_module(name): 2484 basename = os.path.basename(name) 2485 ret += what 2486 if name != self._main_module: 2487 ret += '-' + os.path.splitext(basename)[0] 2488 else: 2489 name = name[2:] if name else 'builtin' 2490 ret += re.sub(r'-', '-' + name + '-', what) 2491 return ret 2492 2493 def _module_filename(self, what, name): 2494 return os.path.join(self._module_dirname(what, name), 2495 self._module_basename(what, name)) 2496 2497 def _add_module(self, name, blurb): 2498 basename = self._module_filename(self._what, name) 2499 genc = QAPIGenC(basename + '.c', blurb, self._pydoc) 2500 genh = QAPIGenH(basename + '.h', blurb, self._pydoc) 2501 self._module[name] = (genc, genh) 2502 self._set_module(name) 2503 2504 def _add_user_module(self, name, blurb): 2505 assert self._is_user_module(name) 2506 if self._main_module is None: 2507 self._main_module = name 2508 self._add_module(name, blurb) 2509 2510 def _add_system_module(self, name, blurb): 2511 self._add_module(name and './' + name, blurb) 2512 2513 def _set_module(self, name): 2514 self._genc, self._genh = self._module[name] 2515 2516 def write(self, output_dir, opt_builtins=False): 2517 for name in self._module: 2518 if self._is_builtin_module(name) and not opt_builtins: 2519 continue 2520 (genc, genh) = self._module[name] 2521 genc.write(output_dir) 2522 genh.write(output_dir) 2523 2524 def _begin_user_module(self, name): 2525 pass 2526 2527 def visit_module(self, name): 2528 if name in self._module: 2529 self._set_module(name) 2530 elif self._is_builtin_module(name): 2531 # The built-in module has not been created. No code may 2532 # be generated. 2533 self._genc = None 2534 self._genh = None 2535 else: 2536 self._add_user_module(name, self._blurb) 2537 self._begin_user_module(name) 2538 2539 def visit_include(self, name, info): 2540 relname = os.path.relpath(self._module_filename(self._what, name), 2541 os.path.dirname(self._genh.fname)) 2542 self._genh.preamble_add(mcgen(''' 2543#include "%(relname)s.h" 2544''', 2545 relname=relname)) 2546