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