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