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