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