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