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