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