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