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