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