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