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( 1579 info, 1580 "%s collides with %s" 1581 % (self.describe(info), seen[cname].describe(info))) 1582 seen[cname] = self 1583 1584 def describe(self, info): 1585 role = self.role 1586 defined_in = self.defined_in 1587 assert defined_in 1588 1589 if defined_in.startswith('q_obj_'): 1590 # See QAPISchema._make_implicit_object_type() - reverse the 1591 # mapping there to create a nice human-readable description 1592 defined_in = defined_in[6:] 1593 if defined_in.endswith('-arg'): 1594 # Implicit type created for a command's dict 'data' 1595 assert role == 'member' 1596 role = 'parameter' 1597 elif defined_in.endswith('-base'): 1598 # Implicit type created for a flat union's dict 'base' 1599 role = 'base ' + role 1600 else: 1601 # Implicit type created for a simple union's branch 1602 assert defined_in.endswith('-wrapper') 1603 # Unreachable and not implemented 1604 assert False 1605 elif defined_in.endswith('Kind'): 1606 # See QAPISchema._make_implicit_enum_type() 1607 # Implicit enum created for simple union's branches 1608 assert role == 'value' 1609 role = 'branch' 1610 elif defined_in != info.defn_name: 1611 return "%s '%s' of type '%s'" % (role, self.name, defined_in) 1612 return "%s '%s'" % (role, self.name) 1613 1614 1615class QAPISchemaEnumMember(QAPISchemaMember): 1616 role = 'value' 1617 1618 1619class QAPISchemaFeature(QAPISchemaMember): 1620 role = 'feature' 1621 1622 1623class QAPISchemaObjectTypeMember(QAPISchemaMember): 1624 def __init__(self, name, typ, optional, ifcond=None): 1625 QAPISchemaMember.__init__(self, name, ifcond) 1626 assert isinstance(typ, str) 1627 assert isinstance(optional, bool) 1628 self._type_name = typ 1629 self.type = None 1630 self.optional = optional 1631 1632 def check(self, schema): 1633 assert self.defined_in 1634 self.type = schema.lookup_type(self._type_name) 1635 assert self.type 1636 1637 1638class QAPISchemaObjectTypeVariants(object): 1639 def __init__(self, tag_name, tag_member, variants): 1640 # Flat unions pass tag_name but not tag_member. 1641 # Simple unions and alternates pass tag_member but not tag_name. 1642 # After check(), tag_member is always set, and tag_name remains 1643 # a reliable witness of being used by a flat union. 1644 assert bool(tag_member) != bool(tag_name) 1645 assert (isinstance(tag_name, str) or 1646 isinstance(tag_member, QAPISchemaObjectTypeMember)) 1647 for v in variants: 1648 assert isinstance(v, QAPISchemaObjectTypeVariant) 1649 self._tag_name = tag_name 1650 self.tag_member = tag_member 1651 self.variants = variants 1652 1653 def set_defined_in(self, name): 1654 for v in self.variants: 1655 v.set_defined_in(name) 1656 1657 def check(self, schema, seen): 1658 if not self.tag_member: # flat union 1659 self.tag_member = seen[c_name(self._tag_name)] 1660 assert self._tag_name == self.tag_member.name 1661 assert isinstance(self.tag_member.type, QAPISchemaEnumType) 1662 assert not self.tag_member.optional 1663 assert self.tag_member.ifcond == [] 1664 if self._tag_name: # flat union 1665 # branches that are not explicitly covered get an empty type 1666 cases = set([v.name for v in self.variants]) 1667 for m in self.tag_member.type.members: 1668 if m.name not in cases: 1669 v = QAPISchemaObjectTypeVariant(m.name, 'q_empty', 1670 m.ifcond) 1671 v.set_defined_in(self.tag_member.defined_in) 1672 self.variants.append(v) 1673 assert self.variants 1674 for v in self.variants: 1675 v.check(schema) 1676 # Union names must match enum values; alternate names are 1677 # checked separately. Use 'seen' to tell the two apart. 1678 if seen: 1679 assert v.name in self.tag_member.type.member_names() 1680 assert (isinstance(v.type, QAPISchemaObjectType) 1681 and not v.type.variants) 1682 v.type.check(schema) 1683 1684 def check_clash(self, info, seen): 1685 for v in self.variants: 1686 # Reset seen map for each variant, since qapi names from one 1687 # branch do not affect another branch 1688 v.type.check_clash(info, dict(seen)) 1689 1690 1691class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): 1692 role = 'branch' 1693 1694 def __init__(self, name, typ, ifcond=None): 1695 QAPISchemaObjectTypeMember.__init__(self, name, typ, False, ifcond) 1696 1697 1698class QAPISchemaAlternateType(QAPISchemaType): 1699 def __init__(self, name, info, doc, ifcond, variants): 1700 QAPISchemaType.__init__(self, name, info, doc, ifcond) 1701 assert isinstance(variants, QAPISchemaObjectTypeVariants) 1702 assert variants.tag_member 1703 variants.set_defined_in(name) 1704 variants.tag_member.set_defined_in(self.name) 1705 self.variants = variants 1706 1707 def check(self, schema): 1708 QAPISchemaType.check(self, schema) 1709 self.variants.tag_member.check(schema) 1710 # Not calling self.variants.check_clash(), because there's nothing 1711 # to clash with 1712 self.variants.check(schema, {}) 1713 # Alternate branch names have no relation to the tag enum values; 1714 # so we have to check for potential name collisions ourselves. 1715 seen = {} 1716 for v in self.variants.variants: 1717 v.check_clash(self.info, seen) 1718 # TODO check conflicting qtypes 1719 if self.doc: 1720 self.doc.connect_member(v) 1721 if self.doc: 1722 self.doc.check() 1723 1724 def c_type(self): 1725 return c_name(self.name) + pointer_suffix 1726 1727 def json_type(self): 1728 return 'value' 1729 1730 def visit(self, visitor): 1731 QAPISchemaType.visit(self, visitor) 1732 visitor.visit_alternate_type(self.name, self.info, self.ifcond, 1733 self.variants) 1734 1735 1736class QAPISchemaCommand(QAPISchemaEntity): 1737 def __init__(self, name, info, doc, ifcond, arg_type, ret_type, 1738 gen, success_response, boxed, allow_oob, allow_preconfig): 1739 QAPISchemaEntity.__init__(self, name, info, doc, ifcond) 1740 assert not arg_type or isinstance(arg_type, str) 1741 assert not ret_type or isinstance(ret_type, str) 1742 self._arg_type_name = arg_type 1743 self.arg_type = None 1744 self._ret_type_name = ret_type 1745 self.ret_type = None 1746 self.gen = gen 1747 self.success_response = success_response 1748 self.boxed = boxed 1749 self.allow_oob = allow_oob 1750 self.allow_preconfig = allow_preconfig 1751 1752 def check(self, schema): 1753 QAPISchemaEntity.check(self, schema) 1754 if self._arg_type_name: 1755 self.arg_type = schema.lookup_type(self._arg_type_name) 1756 assert isinstance(self.arg_type, QAPISchemaObjectType) 1757 assert not self.arg_type.variants or self.boxed 1758 elif self.boxed: 1759 raise QAPISemError(self.info, "use of 'boxed' requires 'data'") 1760 if self._ret_type_name: 1761 self.ret_type = schema.lookup_type(self._ret_type_name) 1762 assert isinstance(self.ret_type, QAPISchemaType) 1763 1764 def visit(self, visitor): 1765 QAPISchemaEntity.visit(self, visitor) 1766 visitor.visit_command(self.name, self.info, self.ifcond, 1767 self.arg_type, self.ret_type, 1768 self.gen, self.success_response, 1769 self.boxed, self.allow_oob, 1770 self.allow_preconfig) 1771 1772 1773class QAPISchemaEvent(QAPISchemaEntity): 1774 def __init__(self, name, info, doc, ifcond, arg_type, boxed): 1775 QAPISchemaEntity.__init__(self, name, info, doc, ifcond) 1776 assert not arg_type or isinstance(arg_type, str) 1777 self._arg_type_name = arg_type 1778 self.arg_type = None 1779 self.boxed = boxed 1780 1781 def check(self, schema): 1782 QAPISchemaEntity.check(self, schema) 1783 if self._arg_type_name: 1784 self.arg_type = schema.lookup_type(self._arg_type_name) 1785 assert isinstance(self.arg_type, QAPISchemaObjectType) 1786 assert not self.arg_type.variants or self.boxed 1787 elif self.boxed: 1788 raise QAPISemError(self.info, "use of 'boxed' requires 'data'") 1789 1790 def visit(self, visitor): 1791 QAPISchemaEntity.visit(self, visitor) 1792 visitor.visit_event(self.name, self.info, self.ifcond, 1793 self.arg_type, self.boxed) 1794 1795 1796class QAPISchema(object): 1797 def __init__(self, fname): 1798 self.fname = fname 1799 if sys.version_info[0] >= 3: 1800 f = open(fname, 'r', encoding='utf-8') 1801 else: 1802 f = open(fname, 'r') 1803 parser = QAPISchemaParser(f) 1804 exprs = check_exprs(parser.exprs) 1805 self.docs = parser.docs 1806 self._entity_list = [] 1807 self._entity_dict = {} 1808 self._predefining = True 1809 self._def_predefineds() 1810 self._predefining = False 1811 self._def_exprs(exprs) 1812 self.check() 1813 1814 def _def_entity(self, ent): 1815 # Only the predefined types are allowed to not have info 1816 assert ent.info or self._predefining 1817 assert ent.name is None or ent.name not in self._entity_dict 1818 self._entity_list.append(ent) 1819 if ent.name is not None: 1820 self._entity_dict[ent.name] = ent 1821 1822 def lookup_entity(self, name, typ=None): 1823 ent = self._entity_dict.get(name) 1824 if typ and not isinstance(ent, typ): 1825 return None 1826 return ent 1827 1828 def lookup_type(self, name): 1829 return self.lookup_entity(name, QAPISchemaType) 1830 1831 def _def_include(self, expr, info, doc): 1832 include = expr['include'] 1833 assert doc is None 1834 main_info = info 1835 while main_info.parent: 1836 main_info = main_info.parent 1837 fname = os.path.relpath(include, os.path.dirname(main_info.fname)) 1838 self._def_entity(QAPISchemaInclude(fname, info)) 1839 1840 def _def_builtin_type(self, name, json_type, c_type): 1841 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type)) 1842 # Instantiating only the arrays that are actually used would 1843 # be nice, but we can't as long as their generated code 1844 # (qapi-builtin-types.[ch]) may be shared by some other 1845 # schema. 1846 self._make_array_type(name, None) 1847 1848 def _def_predefineds(self): 1849 for t in [('str', 'string', 'char' + pointer_suffix), 1850 ('number', 'number', 'double'), 1851 ('int', 'int', 'int64_t'), 1852 ('int8', 'int', 'int8_t'), 1853 ('int16', 'int', 'int16_t'), 1854 ('int32', 'int', 'int32_t'), 1855 ('int64', 'int', 'int64_t'), 1856 ('uint8', 'int', 'uint8_t'), 1857 ('uint16', 'int', 'uint16_t'), 1858 ('uint32', 'int', 'uint32_t'), 1859 ('uint64', 'int', 'uint64_t'), 1860 ('size', 'int', 'uint64_t'), 1861 ('bool', 'boolean', 'bool'), 1862 ('any', 'value', 'QObject' + pointer_suffix), 1863 ('null', 'null', 'QNull' + pointer_suffix)]: 1864 self._def_builtin_type(*t) 1865 self.the_empty_object_type = QAPISchemaObjectType( 1866 'q_empty', None, None, None, None, [], None, []) 1867 self._def_entity(self.the_empty_object_type) 1868 1869 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 1870 'qbool'] 1871 qtype_values = self._make_enum_members([{'name': n} for n in qtypes]) 1872 1873 self._def_entity(QAPISchemaEnumType('QType', None, None, None, 1874 qtype_values, 'QTYPE')) 1875 1876 def _make_features(self, features): 1877 return [QAPISchemaFeature(f['name'], f.get('if')) for f in features] 1878 1879 def _make_enum_members(self, values): 1880 return [QAPISchemaEnumMember(v['name'], v.get('if')) 1881 for v in values] 1882 1883 def _make_implicit_enum_type(self, name, info, ifcond, values): 1884 # See also QAPISchemaObjectTypeMember.describe() 1885 name = name + 'Kind' # Use namespace reserved by add_name() 1886 self._def_entity(QAPISchemaEnumType( 1887 name, info, None, ifcond, self._make_enum_members(values), None)) 1888 return name 1889 1890 def _make_array_type(self, element_type, info): 1891 name = element_type + 'List' # Use namespace reserved by add_name() 1892 if not self.lookup_type(name): 1893 self._def_entity(QAPISchemaArrayType(name, info, element_type)) 1894 return name 1895 1896 def _make_implicit_object_type(self, name, info, doc, ifcond, 1897 role, members): 1898 if not members: 1899 return None 1900 # See also QAPISchemaObjectTypeMember.describe() 1901 name = 'q_obj_%s-%s' % (name, role) 1902 typ = self.lookup_entity(name, QAPISchemaObjectType) 1903 if typ: 1904 # The implicit object type has multiple users. This can 1905 # happen only for simple unions' implicit wrapper types. 1906 # Its ifcond should be the disjunction of its user's 1907 # ifconds. Not implemented. Instead, we always pass the 1908 # wrapped type's ifcond, which is trivially the same for all 1909 # users. It's also necessary for the wrapper to compile. 1910 # But it's not tight: the disjunction need not imply it. We 1911 # may end up compiling useless wrapper types. 1912 # TODO kill simple unions or implement the disjunction 1913 assert ifcond == typ._ifcond # pylint: disable=protected-access 1914 else: 1915 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, 1916 None, members, None, [])) 1917 return name 1918 1919 def _def_enum_type(self, expr, info, doc): 1920 name = expr['enum'] 1921 data = expr['data'] 1922 prefix = expr.get('prefix') 1923 ifcond = expr.get('if') 1924 self._def_entity(QAPISchemaEnumType( 1925 name, info, doc, ifcond, 1926 self._make_enum_members(data), prefix)) 1927 1928 def _make_member(self, name, typ, ifcond, info): 1929 optional = False 1930 if name.startswith('*'): 1931 name = name[1:] 1932 optional = True 1933 if isinstance(typ, list): 1934 assert len(typ) == 1 1935 typ = self._make_array_type(typ[0], info) 1936 return QAPISchemaObjectTypeMember(name, typ, optional, ifcond) 1937 1938 def _make_members(self, data, info): 1939 return [self._make_member(key, value['type'], value.get('if'), info) 1940 for (key, value) in data.items()] 1941 1942 def _def_struct_type(self, expr, info, doc): 1943 name = expr['struct'] 1944 base = expr.get('base') 1945 data = expr['data'] 1946 ifcond = expr.get('if') 1947 features = expr.get('features', []) 1948 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base, 1949 self._make_members(data, info), 1950 None, 1951 self._make_features(features))) 1952 1953 def _make_variant(self, case, typ, ifcond): 1954 return QAPISchemaObjectTypeVariant(case, typ, ifcond) 1955 1956 def _make_simple_variant(self, case, typ, ifcond, info): 1957 if isinstance(typ, list): 1958 assert len(typ) == 1 1959 typ = self._make_array_type(typ[0], info) 1960 typ = self._make_implicit_object_type( 1961 typ, info, None, self.lookup_type(typ), 1962 'wrapper', [self._make_member('data', typ, None, info)]) 1963 return QAPISchemaObjectTypeVariant(case, typ, ifcond) 1964 1965 def _def_union_type(self, expr, info, doc): 1966 name = expr['union'] 1967 data = expr['data'] 1968 base = expr.get('base') 1969 ifcond = expr.get('if') 1970 tag_name = expr.get('discriminator') 1971 tag_member = None 1972 if isinstance(base, dict): 1973 base = self._make_implicit_object_type( 1974 name, info, doc, ifcond, 1975 'base', self._make_members(base, info)) 1976 if tag_name: 1977 variants = [self._make_variant(key, value['type'], value.get('if')) 1978 for (key, value) in data.items()] 1979 members = [] 1980 else: 1981 variants = [self._make_simple_variant(key, value['type'], 1982 value.get('if'), info) 1983 for (key, value) in data.items()] 1984 enum = [{'name': v.name, 'if': v.ifcond} for v in variants] 1985 typ = self._make_implicit_enum_type(name, info, ifcond, enum) 1986 tag_member = QAPISchemaObjectTypeMember('type', typ, False) 1987 members = [tag_member] 1988 self._def_entity( 1989 QAPISchemaObjectType(name, info, doc, ifcond, base, members, 1990 QAPISchemaObjectTypeVariants(tag_name, 1991 tag_member, 1992 variants), [])) 1993 1994 def _def_alternate_type(self, expr, info, doc): 1995 name = expr['alternate'] 1996 data = expr['data'] 1997 ifcond = expr.get('if') 1998 variants = [self._make_variant(key, value['type'], value.get('if')) 1999 for (key, value) in data.items()] 2000 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False) 2001 self._def_entity( 2002 QAPISchemaAlternateType(name, info, doc, ifcond, 2003 QAPISchemaObjectTypeVariants(None, 2004 tag_member, 2005 variants))) 2006 2007 def _def_command(self, expr, info, doc): 2008 name = expr['command'] 2009 data = expr.get('data') 2010 rets = expr.get('returns') 2011 gen = expr.get('gen', True) 2012 success_response = expr.get('success-response', True) 2013 boxed = expr.get('boxed', False) 2014 allow_oob = expr.get('allow-oob', False) 2015 allow_preconfig = expr.get('allow-preconfig', False) 2016 ifcond = expr.get('if') 2017 if isinstance(data, OrderedDict): 2018 data = self._make_implicit_object_type( 2019 name, info, doc, ifcond, 'arg', self._make_members(data, info)) 2020 if isinstance(rets, list): 2021 assert len(rets) == 1 2022 rets = self._make_array_type(rets[0], info) 2023 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets, 2024 gen, success_response, 2025 boxed, allow_oob, allow_preconfig)) 2026 2027 def _def_event(self, expr, info, doc): 2028 name = expr['event'] 2029 data = expr.get('data') 2030 boxed = expr.get('boxed', False) 2031 ifcond = expr.get('if') 2032 if isinstance(data, OrderedDict): 2033 data = self._make_implicit_object_type( 2034 name, info, doc, ifcond, 'arg', self._make_members(data, info)) 2035 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed)) 2036 2037 def _def_exprs(self, exprs): 2038 for expr_elem in exprs: 2039 expr = expr_elem['expr'] 2040 info = expr_elem['info'] 2041 doc = expr_elem.get('doc') 2042 if 'enum' in expr: 2043 self._def_enum_type(expr, info, doc) 2044 elif 'struct' in expr: 2045 self._def_struct_type(expr, info, doc) 2046 elif 'union' in expr: 2047 self._def_union_type(expr, info, doc) 2048 elif 'alternate' in expr: 2049 self._def_alternate_type(expr, info, doc) 2050 elif 'command' in expr: 2051 self._def_command(expr, info, doc) 2052 elif 'event' in expr: 2053 self._def_event(expr, info, doc) 2054 elif 'include' in expr: 2055 self._def_include(expr, info, doc) 2056 else: 2057 assert False 2058 2059 def check(self): 2060 for ent in self._entity_list: 2061 ent.check(self) 2062 2063 def visit(self, visitor): 2064 visitor.visit_begin(self) 2065 module = None 2066 visitor.visit_module(module) 2067 for entity in self._entity_list: 2068 if visitor.visit_needed(entity): 2069 if entity.module != module: 2070 module = entity.module 2071 visitor.visit_module(module) 2072 entity.visit(visitor) 2073 visitor.visit_end() 2074 2075 2076# 2077# Code generation helpers 2078# 2079 2080def camel_case(name): 2081 new_name = '' 2082 first = True 2083 for ch in name: 2084 if ch in ['_', '-']: 2085 first = True 2086 elif first: 2087 new_name += ch.upper() 2088 first = False 2089 else: 2090 new_name += ch.lower() 2091 return new_name 2092 2093 2094# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1 2095# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2 2096# ENUM24_Name -> ENUM24_NAME 2097def camel_to_upper(value): 2098 c_fun_str = c_name(value, False) 2099 if value.isupper(): 2100 return c_fun_str 2101 2102 new_name = '' 2103 length = len(c_fun_str) 2104 for i in range(length): 2105 c = c_fun_str[i] 2106 # When c is upper and no '_' appears before, do more checks 2107 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_': 2108 if i < length - 1 and c_fun_str[i + 1].islower(): 2109 new_name += '_' 2110 elif c_fun_str[i - 1].isdigit(): 2111 new_name += '_' 2112 new_name += c 2113 return new_name.lstrip('_').upper() 2114 2115 2116def c_enum_const(type_name, const_name, prefix=None): 2117 if prefix is not None: 2118 type_name = prefix 2119 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper() 2120 2121 2122if hasattr(str, 'maketrans'): 2123 c_name_trans = str.maketrans('.-', '__') 2124else: 2125 c_name_trans = string.maketrans('.-', '__') 2126 2127 2128# Map @name to a valid C identifier. 2129# If @protect, avoid returning certain ticklish identifiers (like 2130# C keywords) by prepending 'q_'. 2131# 2132# Used for converting 'name' from a 'name':'type' qapi definition 2133# into a generated struct member, as well as converting type names 2134# into substrings of a generated C function name. 2135# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo' 2136# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int' 2137def c_name(name, protect=True): 2138 # ANSI X3J11/88-090, 3.1.1 2139 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue', 2140 'default', 'do', 'double', 'else', 'enum', 'extern', 2141 'float', 'for', 'goto', 'if', 'int', 'long', 'register', 2142 'return', 'short', 'signed', 'sizeof', 'static', 2143 'struct', 'switch', 'typedef', 'union', 'unsigned', 2144 'void', 'volatile', 'while']) 2145 # ISO/IEC 9899:1999, 6.4.1 2146 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary']) 2147 # ISO/IEC 9899:2011, 6.4.1 2148 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic', 2149 '_Noreturn', '_Static_assert', '_Thread_local']) 2150 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html 2151 # excluding _.* 2152 gcc_words = set(['asm', 'typeof']) 2153 # C++ ISO/IEC 14882:2003 2.11 2154 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete', 2155 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable', 2156 'namespace', 'new', 'operator', 'private', 'protected', 2157 'public', 'reinterpret_cast', 'static_cast', 'template', 2158 'this', 'throw', 'true', 'try', 'typeid', 'typename', 2159 'using', 'virtual', 'wchar_t', 2160 # alternative representations 2161 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', 2162 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq']) 2163 # namespace pollution: 2164 polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386']) 2165 name = name.translate(c_name_trans) 2166 if protect and (name in c89_words | c99_words | c11_words | gcc_words 2167 | cpp_words | polluted_words): 2168 return 'q_' + name 2169 return name 2170 2171 2172eatspace = '\033EATSPACE.' 2173pointer_suffix = ' *' + eatspace 2174 2175 2176def genindent(count): 2177 ret = '' 2178 for _ in range(count): 2179 ret += ' ' 2180 return ret 2181 2182 2183indent_level = 0 2184 2185 2186def push_indent(indent_amount=4): 2187 global indent_level 2188 indent_level += indent_amount 2189 2190 2191def pop_indent(indent_amount=4): 2192 global indent_level 2193 indent_level -= indent_amount 2194 2195 2196# Generate @code with @kwds interpolated. 2197# Obey indent_level, and strip eatspace. 2198def cgen(code, **kwds): 2199 raw = code % kwds 2200 if indent_level: 2201 indent = genindent(indent_level) 2202 # re.subn() lacks flags support before Python 2.7, use re.compile() 2203 raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE), 2204 indent, raw) 2205 raw = raw[0] 2206 return re.sub(re.escape(eatspace) + r' *', '', raw) 2207 2208 2209def mcgen(code, **kwds): 2210 if code[0] == '\n': 2211 code = code[1:] 2212 return cgen(code, **kwds) 2213 2214 2215def c_fname(filename): 2216 return re.sub(r'[^A-Za-z0-9_]', '_', filename) 2217 2218 2219def guardstart(name): 2220 return mcgen(''' 2221#ifndef %(name)s 2222#define %(name)s 2223 2224''', 2225 name=c_fname(name).upper()) 2226 2227 2228def guardend(name): 2229 return mcgen(''' 2230 2231#endif /* %(name)s */ 2232''', 2233 name=c_fname(name).upper()) 2234 2235 2236def gen_if(ifcond): 2237 ret = '' 2238 for ifc in ifcond: 2239 ret += mcgen(''' 2240#if %(cond)s 2241''', cond=ifc) 2242 return ret 2243 2244 2245def gen_endif(ifcond): 2246 ret = '' 2247 for ifc in reversed(ifcond): 2248 ret += mcgen(''' 2249#endif /* %(cond)s */ 2250''', cond=ifc) 2251 return ret 2252 2253 2254def _wrap_ifcond(ifcond, before, after): 2255 if before == after: 2256 return after # suppress empty #if ... #endif 2257 2258 assert after.startswith(before) 2259 out = before 2260 added = after[len(before):] 2261 if added[0] == '\n': 2262 out += '\n' 2263 added = added[1:] 2264 out += gen_if(ifcond) 2265 out += added 2266 out += gen_endif(ifcond) 2267 return out 2268 2269 2270def gen_enum_lookup(name, members, prefix=None): 2271 ret = mcgen(''' 2272 2273const QEnumLookup %(c_name)s_lookup = { 2274 .array = (const char *const[]) { 2275''', 2276 c_name=c_name(name)) 2277 for m in members: 2278 ret += gen_if(m.ifcond) 2279 index = c_enum_const(name, m.name, prefix) 2280 ret += mcgen(''' 2281 [%(index)s] = "%(name)s", 2282''', 2283 index=index, name=m.name) 2284 ret += gen_endif(m.ifcond) 2285 2286 ret += mcgen(''' 2287 }, 2288 .size = %(max_index)s 2289}; 2290''', 2291 max_index=c_enum_const(name, '_MAX', prefix)) 2292 return ret 2293 2294 2295def gen_enum(name, members, prefix=None): 2296 # append automatically generated _MAX value 2297 enum_members = members + [QAPISchemaEnumMember('_MAX')] 2298 2299 ret = mcgen(''' 2300 2301typedef enum %(c_name)s { 2302''', 2303 c_name=c_name(name)) 2304 2305 for m in enum_members: 2306 ret += gen_if(m.ifcond) 2307 ret += mcgen(''' 2308 %(c_enum)s, 2309''', 2310 c_enum=c_enum_const(name, m.name, prefix)) 2311 ret += gen_endif(m.ifcond) 2312 2313 ret += mcgen(''' 2314} %(c_name)s; 2315''', 2316 c_name=c_name(name)) 2317 2318 ret += mcgen(''' 2319 2320#define %(c_name)s_str(val) \\ 2321 qapi_enum_lookup(&%(c_name)s_lookup, (val)) 2322 2323extern const QEnumLookup %(c_name)s_lookup; 2324''', 2325 c_name=c_name(name)) 2326 return ret 2327 2328 2329def build_params(arg_type, boxed, extra=None): 2330 ret = '' 2331 sep = '' 2332 if boxed: 2333 assert arg_type 2334 ret += '%s arg' % arg_type.c_param_type() 2335 sep = ', ' 2336 elif arg_type: 2337 assert not arg_type.variants 2338 for memb in arg_type.members: 2339 ret += sep 2340 sep = ', ' 2341 if memb.optional: 2342 ret += 'bool has_%s, ' % c_name(memb.name) 2343 ret += '%s %s' % (memb.type.c_param_type(), 2344 c_name(memb.name)) 2345 if extra: 2346 ret += sep + extra 2347 return ret if ret else 'void' 2348 2349 2350# 2351# Accumulate and write output 2352# 2353 2354class QAPIGen(object): 2355 2356 def __init__(self, fname): 2357 self.fname = fname 2358 self._preamble = '' 2359 self._body = '' 2360 2361 def preamble_add(self, text): 2362 self._preamble += text 2363 2364 def add(self, text): 2365 self._body += text 2366 2367 def get_content(self): 2368 return self._top() + self._preamble + self._body + self._bottom() 2369 2370 def _top(self): 2371 return '' 2372 2373 def _bottom(self): 2374 return '' 2375 2376 def write(self, output_dir): 2377 pathname = os.path.join(output_dir, self.fname) 2378 dir = os.path.dirname(pathname) 2379 if dir: 2380 try: 2381 os.makedirs(dir) 2382 except os.error as e: 2383 if e.errno != errno.EEXIST: 2384 raise 2385 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666) 2386 if sys.version_info[0] >= 3: 2387 f = open(fd, 'r+', encoding='utf-8') 2388 else: 2389 f = os.fdopen(fd, 'r+') 2390 text = self.get_content() 2391 oldtext = f.read(len(text) + 1) 2392 if text != oldtext: 2393 f.seek(0) 2394 f.truncate(0) 2395 f.write(text) 2396 f.close() 2397 2398 2399@contextmanager 2400def ifcontext(ifcond, *args): 2401 """A 'with' statement context manager to wrap with start_if()/end_if() 2402 2403 *args: any number of QAPIGenCCode 2404 2405 Example:: 2406 2407 with ifcontext(ifcond, self._genh, self._genc): 2408 modify self._genh and self._genc ... 2409 2410 Is equivalent to calling:: 2411 2412 self._genh.start_if(ifcond) 2413 self._genc.start_if(ifcond) 2414 modify self._genh and self._genc ... 2415 self._genh.end_if() 2416 self._genc.end_if() 2417 """ 2418 for arg in args: 2419 arg.start_if(ifcond) 2420 yield 2421 for arg in args: 2422 arg.end_if() 2423 2424 2425class QAPIGenCCode(QAPIGen): 2426 2427 def __init__(self, fname): 2428 QAPIGen.__init__(self, fname) 2429 self._start_if = None 2430 2431 def start_if(self, ifcond): 2432 assert self._start_if is None 2433 self._start_if = (ifcond, self._body, self._preamble) 2434 2435 def end_if(self): 2436 assert self._start_if 2437 self._wrap_ifcond() 2438 self._start_if = None 2439 2440 def _wrap_ifcond(self): 2441 self._body = _wrap_ifcond(self._start_if[0], 2442 self._start_if[1], self._body) 2443 self._preamble = _wrap_ifcond(self._start_if[0], 2444 self._start_if[2], self._preamble) 2445 2446 def get_content(self): 2447 assert self._start_if is None 2448 return QAPIGen.get_content(self) 2449 2450 2451class QAPIGenC(QAPIGenCCode): 2452 2453 def __init__(self, fname, blurb, pydoc): 2454 QAPIGenCCode.__init__(self, fname) 2455 self._blurb = blurb 2456 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc, 2457 re.MULTILINE)) 2458 2459 def _top(self): 2460 return mcgen(''' 2461/* AUTOMATICALLY GENERATED, DO NOT MODIFY */ 2462 2463/* 2464%(blurb)s 2465 * 2466 * %(copyright)s 2467 * 2468 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 2469 * See the COPYING.LIB file in the top-level directory. 2470 */ 2471 2472''', 2473 blurb=self._blurb, copyright=self._copyright) 2474 2475 def _bottom(self): 2476 return mcgen(''' 2477 2478/* Dummy declaration to prevent empty .o file */ 2479char qapi_dummy_%(name)s; 2480''', 2481 name=c_fname(self.fname)) 2482 2483 2484class QAPIGenH(QAPIGenC): 2485 2486 def _top(self): 2487 return QAPIGenC._top(self) + guardstart(self.fname) 2488 2489 def _bottom(self): 2490 return guardend(self.fname) 2491 2492 2493class QAPIGenDoc(QAPIGen): 2494 2495 def _top(self): 2496 return (QAPIGen._top(self) 2497 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n') 2498 2499 2500class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor): 2501 2502 def __init__(self, prefix, what, blurb, pydoc): 2503 self._prefix = prefix 2504 self._what = what 2505 self._genc = QAPIGenC(self._prefix + self._what + '.c', 2506 blurb, pydoc) 2507 self._genh = QAPIGenH(self._prefix + self._what + '.h', 2508 blurb, pydoc) 2509 2510 def write(self, output_dir): 2511 self._genc.write(output_dir) 2512 self._genh.write(output_dir) 2513 2514 2515class QAPISchemaModularCVisitor(QAPISchemaVisitor): 2516 2517 def __init__(self, prefix, what, blurb, pydoc): 2518 self._prefix = prefix 2519 self._what = what 2520 self._blurb = blurb 2521 self._pydoc = pydoc 2522 self._genc = None 2523 self._genh = None 2524 self._module = {} 2525 self._main_module = None 2526 2527 @staticmethod 2528 def _is_user_module(name): 2529 return name and not name.startswith('./') 2530 2531 @staticmethod 2532 def _is_builtin_module(name): 2533 return not name 2534 2535 def _module_dirname(self, what, name): 2536 if self._is_user_module(name): 2537 return os.path.dirname(name) 2538 return '' 2539 2540 def _module_basename(self, what, name): 2541 ret = '' if self._is_builtin_module(name) else self._prefix 2542 if self._is_user_module(name): 2543 basename = os.path.basename(name) 2544 ret += what 2545 if name != self._main_module: 2546 ret += '-' + os.path.splitext(basename)[0] 2547 else: 2548 name = name[2:] if name else 'builtin' 2549 ret += re.sub(r'-', '-' + name + '-', what) 2550 return ret 2551 2552 def _module_filename(self, what, name): 2553 return os.path.join(self._module_dirname(what, name), 2554 self._module_basename(what, name)) 2555 2556 def _add_module(self, name, blurb): 2557 basename = self._module_filename(self._what, name) 2558 genc = QAPIGenC(basename + '.c', blurb, self._pydoc) 2559 genh = QAPIGenH(basename + '.h', blurb, self._pydoc) 2560 self._module[name] = (genc, genh) 2561 self._set_module(name) 2562 2563 def _add_user_module(self, name, blurb): 2564 assert self._is_user_module(name) 2565 if self._main_module is None: 2566 self._main_module = name 2567 self._add_module(name, blurb) 2568 2569 def _add_system_module(self, name, blurb): 2570 self._add_module(name and './' + name, blurb) 2571 2572 def _set_module(self, name): 2573 self._genc, self._genh = self._module[name] 2574 2575 def write(self, output_dir, opt_builtins=False): 2576 for name in self._module: 2577 if self._is_builtin_module(name) and not opt_builtins: 2578 continue 2579 (genc, genh) = self._module[name] 2580 genc.write(output_dir) 2581 genh.write(output_dir) 2582 2583 def _begin_user_module(self, name): 2584 pass 2585 2586 def visit_module(self, name): 2587 if name in self._module: 2588 self._set_module(name) 2589 elif self._is_builtin_module(name): 2590 # The built-in module has not been created. No code may 2591 # be generated. 2592 self._genc = None 2593 self._genh = None 2594 else: 2595 self._add_user_module(name, self._blurb) 2596 self._begin_user_module(name) 2597 2598 def visit_include(self, name, info): 2599 relname = os.path.relpath(self._module_filename(self._what, name), 2600 os.path.dirname(self._genh.fname)) 2601 self._genh.preamble_add(mcgen(''' 2602#include "%(relname)s.h" 2603''', 2604 relname=relname)) 2605