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