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