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