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' or 876 key == 'allow-preconfig') and value is not True: 877 raise QAPISemError(info, 878 "'%s' of %s '%s' should only use true value" 879 % (key, meta, name)) 880 for key in required: 881 if key not in expr: 882 raise QAPISemError(info, "Key '%s' is missing from %s '%s'" 883 % (key, meta, name)) 884 885 886def check_exprs(exprs): 887 global all_names 888 889 # Populate name table with names of built-in types 890 for builtin in builtin_types.keys(): 891 all_names[builtin] = 'built-in' 892 893 # Learn the types and check for valid expression keys 894 for expr_elem in exprs: 895 expr = expr_elem['expr'] 896 info = expr_elem['info'] 897 doc = expr_elem.get('doc') 898 899 if 'include' in expr: 900 continue 901 902 if not doc and doc_required: 903 raise QAPISemError(info, 904 "Expression missing documentation comment") 905 906 if 'enum' in expr: 907 meta = 'enum' 908 check_keys(expr_elem, 'enum', ['data'], ['prefix']) 909 enum_types[expr[meta]] = expr 910 elif 'union' in expr: 911 meta = 'union' 912 check_keys(expr_elem, 'union', ['data'], 913 ['base', 'discriminator']) 914 union_types[expr[meta]] = expr 915 elif 'alternate' in expr: 916 meta = 'alternate' 917 check_keys(expr_elem, 'alternate', ['data']) 918 elif 'struct' in expr: 919 meta = 'struct' 920 check_keys(expr_elem, 'struct', ['data'], ['base']) 921 struct_types[expr[meta]] = expr 922 elif 'command' in expr: 923 meta = 'command' 924 check_keys(expr_elem, 'command', [], 925 ['data', 'returns', 'gen', 'success-response', 926 'boxed', 'allow-oob', 'allow-preconfig']) 927 elif 'event' in expr: 928 meta = 'event' 929 check_keys(expr_elem, 'event', [], ['data', 'boxed']) 930 else: 931 raise QAPISemError(expr_elem['info'], 932 "Expression is missing metatype") 933 name = expr[meta] 934 add_name(name, info, meta) 935 if doc and doc.symbol != name: 936 raise QAPISemError(info, "Definition of '%s' follows documentation" 937 " for '%s'" % (name, doc.symbol)) 938 939 # Try again for hidden UnionKind enum 940 for expr_elem in exprs: 941 expr = expr_elem['expr'] 942 943 if 'include' in expr: 944 continue 945 if 'union' in expr and not discriminator_find_enum_define(expr): 946 name = '%sKind' % expr['union'] 947 elif 'alternate' in expr: 948 name = '%sKind' % expr['alternate'] 949 else: 950 continue 951 enum_types[name] = {'enum': name} 952 add_name(name, info, 'enum', implicit=True) 953 954 # Validate that exprs make sense 955 for expr_elem in exprs: 956 expr = expr_elem['expr'] 957 info = expr_elem['info'] 958 doc = expr_elem.get('doc') 959 960 if 'include' in expr: 961 continue 962 if 'enum' in expr: 963 check_enum(expr, info) 964 elif 'union' in expr: 965 check_union(expr, info) 966 elif 'alternate' in expr: 967 check_alternate(expr, info) 968 elif 'struct' in expr: 969 check_struct(expr, info) 970 elif 'command' in expr: 971 check_command(expr, info) 972 elif 'event' in expr: 973 check_event(expr, info) 974 else: 975 assert False, 'unexpected meta type' 976 977 if doc: 978 doc.check_expr(expr) 979 980 return exprs 981 982 983# 984# Schema compiler frontend 985# 986 987class QAPISchemaEntity(object): 988 def __init__(self, name, info, doc): 989 assert name is None or isinstance(name, str) 990 self.name = name 991 self.module = None 992 # For explicitly defined entities, info points to the (explicit) 993 # definition. For builtins (and their arrays), info is None. 994 # For implicitly defined entities, info points to a place that 995 # triggered the implicit definition (there may be more than one 996 # such place). 997 self.info = info 998 self.doc = doc 999 1000 def c_name(self): 1001 return c_name(self.name) 1002 1003 def check(self, schema): 1004 pass 1005 1006 def is_implicit(self): 1007 return not self.info 1008 1009 def visit(self, visitor): 1010 pass 1011 1012 1013class QAPISchemaVisitor(object): 1014 def visit_begin(self, schema): 1015 pass 1016 1017 def visit_end(self): 1018 pass 1019 1020 def visit_module(self, fname): 1021 pass 1022 1023 def visit_needed(self, entity): 1024 # Default to visiting everything 1025 return True 1026 1027 def visit_include(self, fname, info): 1028 pass 1029 1030 def visit_builtin_type(self, name, info, json_type): 1031 pass 1032 1033 def visit_enum_type(self, name, info, values, prefix): 1034 pass 1035 1036 def visit_array_type(self, name, info, element_type): 1037 pass 1038 1039 def visit_object_type(self, name, info, base, members, variants): 1040 pass 1041 1042 def visit_object_type_flat(self, name, info, members, variants): 1043 pass 1044 1045 def visit_alternate_type(self, name, info, variants): 1046 pass 1047 1048 def visit_command(self, name, info, arg_type, ret_type, gen, 1049 success_response, boxed, allow_oob, allow_preconfig): 1050 pass 1051 1052 def visit_event(self, name, info, arg_type, boxed): 1053 pass 1054 1055 1056class QAPISchemaInclude(QAPISchemaEntity): 1057 1058 def __init__(self, fname, info): 1059 QAPISchemaEntity.__init__(self, None, info, None) 1060 self.fname = fname 1061 1062 def visit(self, visitor): 1063 visitor.visit_include(self.fname, self.info) 1064 1065 1066class QAPISchemaType(QAPISchemaEntity): 1067 # Return the C type for common use. 1068 # For the types we commonly box, this is a pointer type. 1069 def c_type(self): 1070 pass 1071 1072 # Return the C type to be used in a parameter list. 1073 def c_param_type(self): 1074 return self.c_type() 1075 1076 # Return the C type to be used where we suppress boxing. 1077 def c_unboxed_type(self): 1078 return self.c_type() 1079 1080 def json_type(self): 1081 pass 1082 1083 def alternate_qtype(self): 1084 json2qtype = { 1085 'null': 'QTYPE_QNULL', 1086 'string': 'QTYPE_QSTRING', 1087 'number': 'QTYPE_QNUM', 1088 'int': 'QTYPE_QNUM', 1089 'boolean': 'QTYPE_QBOOL', 1090 'object': 'QTYPE_QDICT' 1091 } 1092 return json2qtype.get(self.json_type()) 1093 1094 def doc_type(self): 1095 if self.is_implicit(): 1096 return None 1097 return self.name 1098 1099 1100class QAPISchemaBuiltinType(QAPISchemaType): 1101 def __init__(self, name, json_type, c_type): 1102 QAPISchemaType.__init__(self, name, None, None) 1103 assert not c_type or isinstance(c_type, str) 1104 assert json_type in ('string', 'number', 'int', 'boolean', 'null', 1105 'value') 1106 self._json_type_name = json_type 1107 self._c_type_name = c_type 1108 1109 def c_name(self): 1110 return self.name 1111 1112 def c_type(self): 1113 return self._c_type_name 1114 1115 def c_param_type(self): 1116 if self.name == 'str': 1117 return 'const ' + self._c_type_name 1118 return self._c_type_name 1119 1120 def json_type(self): 1121 return self._json_type_name 1122 1123 def doc_type(self): 1124 return self.json_type() 1125 1126 def visit(self, visitor): 1127 visitor.visit_builtin_type(self.name, self.info, self.json_type()) 1128 1129 1130class QAPISchemaEnumType(QAPISchemaType): 1131 def __init__(self, name, info, doc, values, prefix): 1132 QAPISchemaType.__init__(self, name, info, doc) 1133 for v in values: 1134 assert isinstance(v, QAPISchemaMember) 1135 v.set_owner(name) 1136 assert prefix is None or isinstance(prefix, str) 1137 self.values = values 1138 self.prefix = prefix 1139 1140 def check(self, schema): 1141 seen = {} 1142 for v in self.values: 1143 v.check_clash(self.info, seen) 1144 if self.doc: 1145 self.doc.connect_member(v) 1146 1147 def is_implicit(self): 1148 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds() 1149 return self.name.endswith('Kind') or self.name == 'QType' 1150 1151 def c_type(self): 1152 return c_name(self.name) 1153 1154 def member_names(self): 1155 return [v.name for v in self.values] 1156 1157 def json_type(self): 1158 return 'string' 1159 1160 def visit(self, visitor): 1161 visitor.visit_enum_type(self.name, self.info, 1162 self.member_names(), self.prefix) 1163 1164 1165class QAPISchemaArrayType(QAPISchemaType): 1166 def __init__(self, name, info, element_type): 1167 QAPISchemaType.__init__(self, name, info, None) 1168 assert isinstance(element_type, str) 1169 self._element_type_name = element_type 1170 self.element_type = None 1171 1172 def check(self, schema): 1173 self.element_type = schema.lookup_type(self._element_type_name) 1174 assert self.element_type 1175 1176 def is_implicit(self): 1177 return True 1178 1179 def c_type(self): 1180 return c_name(self.name) + pointer_suffix 1181 1182 def json_type(self): 1183 return 'array' 1184 1185 def doc_type(self): 1186 elt_doc_type = self.element_type.doc_type() 1187 if not elt_doc_type: 1188 return None 1189 return 'array of ' + elt_doc_type 1190 1191 def visit(self, visitor): 1192 visitor.visit_array_type(self.name, self.info, self.element_type) 1193 1194 1195class QAPISchemaObjectType(QAPISchemaType): 1196 def __init__(self, name, info, doc, base, local_members, variants): 1197 # struct has local_members, optional base, and no variants 1198 # flat union has base, variants, and no local_members 1199 # simple union has local_members, variants, and no base 1200 QAPISchemaType.__init__(self, name, info, doc) 1201 assert base is None or isinstance(base, str) 1202 for m in local_members: 1203 assert isinstance(m, QAPISchemaObjectTypeMember) 1204 m.set_owner(name) 1205 if variants is not None: 1206 assert isinstance(variants, QAPISchemaObjectTypeVariants) 1207 variants.set_owner(name) 1208 self._base_name = base 1209 self.base = None 1210 self.local_members = local_members 1211 self.variants = variants 1212 self.members = None 1213 1214 def check(self, schema): 1215 if self.members is False: # check for cycles 1216 raise QAPISemError(self.info, 1217 "Object %s contains itself" % self.name) 1218 if self.members: 1219 return 1220 self.members = False # mark as being checked 1221 seen = OrderedDict() 1222 if self._base_name: 1223 self.base = schema.lookup_type(self._base_name) 1224 assert isinstance(self.base, QAPISchemaObjectType) 1225 self.base.check(schema) 1226 self.base.check_clash(self.info, seen) 1227 for m in self.local_members: 1228 m.check(schema) 1229 m.check_clash(self.info, seen) 1230 if self.doc: 1231 self.doc.connect_member(m) 1232 self.members = seen.values() 1233 if self.variants: 1234 self.variants.check(schema, seen) 1235 assert self.variants.tag_member in self.members 1236 self.variants.check_clash(self.info, seen) 1237 if self.doc: 1238 self.doc.check() 1239 1240 # Check that the members of this type do not cause duplicate JSON members, 1241 # and update seen to track the members seen so far. Report any errors 1242 # on behalf of info, which is not necessarily self.info 1243 def check_clash(self, info, seen): 1244 assert not self.variants # not implemented 1245 for m in self.members: 1246 m.check_clash(info, seen) 1247 1248 def is_implicit(self): 1249 # See QAPISchema._make_implicit_object_type(), as well as 1250 # _def_predefineds() 1251 return self.name.startswith('q_') 1252 1253 def is_empty(self): 1254 assert self.members is not None 1255 return not self.members and not self.variants 1256 1257 def c_name(self): 1258 assert self.name != 'q_empty' 1259 return QAPISchemaType.c_name(self) 1260 1261 def c_type(self): 1262 assert not self.is_implicit() 1263 return c_name(self.name) + pointer_suffix 1264 1265 def c_unboxed_type(self): 1266 return c_name(self.name) 1267 1268 def json_type(self): 1269 return 'object' 1270 1271 def visit(self, visitor): 1272 visitor.visit_object_type(self.name, self.info, 1273 self.base, self.local_members, self.variants) 1274 visitor.visit_object_type_flat(self.name, self.info, 1275 self.members, self.variants) 1276 1277 1278class QAPISchemaMember(object): 1279 role = 'member' 1280 1281 def __init__(self, name): 1282 assert isinstance(name, str) 1283 self.name = name 1284 self.owner = None 1285 1286 def set_owner(self, name): 1287 assert not self.owner 1288 self.owner = name 1289 1290 def check_clash(self, info, seen): 1291 cname = c_name(self.name) 1292 if cname.lower() != cname and self.owner not in name_case_whitelist: 1293 raise QAPISemError(info, 1294 "%s should not use uppercase" % self.describe()) 1295 if cname in seen: 1296 raise QAPISemError(info, "%s collides with %s" % 1297 (self.describe(), seen[cname].describe())) 1298 seen[cname] = self 1299 1300 def _pretty_owner(self): 1301 owner = self.owner 1302 if owner.startswith('q_obj_'): 1303 # See QAPISchema._make_implicit_object_type() - reverse the 1304 # mapping there to create a nice human-readable description 1305 owner = owner[6:] 1306 if owner.endswith('-arg'): 1307 return '(parameter of %s)' % owner[:-4] 1308 elif owner.endswith('-base'): 1309 return '(base of %s)' % owner[:-5] 1310 else: 1311 assert owner.endswith('-wrapper') 1312 # Unreachable and not implemented 1313 assert False 1314 if owner.endswith('Kind'): 1315 # See QAPISchema._make_implicit_enum_type() 1316 return '(branch of %s)' % owner[:-4] 1317 return '(%s of %s)' % (self.role, owner) 1318 1319 def describe(self): 1320 return "'%s' %s" % (self.name, self._pretty_owner()) 1321 1322 1323class QAPISchemaObjectTypeMember(QAPISchemaMember): 1324 def __init__(self, name, typ, optional): 1325 QAPISchemaMember.__init__(self, name) 1326 assert isinstance(typ, str) 1327 assert isinstance(optional, bool) 1328 self._type_name = typ 1329 self.type = None 1330 self.optional = optional 1331 1332 def check(self, schema): 1333 assert self.owner 1334 self.type = schema.lookup_type(self._type_name) 1335 assert self.type 1336 1337 1338class QAPISchemaObjectTypeVariants(object): 1339 def __init__(self, tag_name, tag_member, variants): 1340 # Flat unions pass tag_name but not tag_member. 1341 # Simple unions and alternates pass tag_member but not tag_name. 1342 # After check(), tag_member is always set, and tag_name remains 1343 # a reliable witness of being used by a flat union. 1344 assert bool(tag_member) != bool(tag_name) 1345 assert (isinstance(tag_name, str) or 1346 isinstance(tag_member, QAPISchemaObjectTypeMember)) 1347 assert len(variants) > 0 1348 for v in variants: 1349 assert isinstance(v, QAPISchemaObjectTypeVariant) 1350 self._tag_name = tag_name 1351 self.tag_member = tag_member 1352 self.variants = variants 1353 1354 def set_owner(self, name): 1355 for v in self.variants: 1356 v.set_owner(name) 1357 1358 def check(self, schema, seen): 1359 if not self.tag_member: # flat union 1360 self.tag_member = seen[c_name(self._tag_name)] 1361 assert self._tag_name == self.tag_member.name 1362 assert isinstance(self.tag_member.type, QAPISchemaEnumType) 1363 for v in self.variants: 1364 v.check(schema) 1365 # Union names must match enum values; alternate names are 1366 # checked separately. Use 'seen' to tell the two apart. 1367 if seen: 1368 assert v.name in self.tag_member.type.member_names() 1369 assert isinstance(v.type, QAPISchemaObjectType) 1370 v.type.check(schema) 1371 1372 def check_clash(self, info, seen): 1373 for v in self.variants: 1374 # Reset seen map for each variant, since qapi names from one 1375 # branch do not affect another branch 1376 assert isinstance(v.type, QAPISchemaObjectType) 1377 v.type.check_clash(info, dict(seen)) 1378 1379 1380class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): 1381 role = 'branch' 1382 1383 def __init__(self, name, typ): 1384 QAPISchemaObjectTypeMember.__init__(self, name, typ, False) 1385 1386 1387class QAPISchemaAlternateType(QAPISchemaType): 1388 def __init__(self, name, info, doc, variants): 1389 QAPISchemaType.__init__(self, name, info, doc) 1390 assert isinstance(variants, QAPISchemaObjectTypeVariants) 1391 assert variants.tag_member 1392 variants.set_owner(name) 1393 variants.tag_member.set_owner(self.name) 1394 self.variants = variants 1395 1396 def check(self, schema): 1397 self.variants.tag_member.check(schema) 1398 # Not calling self.variants.check_clash(), because there's nothing 1399 # to clash with 1400 self.variants.check(schema, {}) 1401 # Alternate branch names have no relation to the tag enum values; 1402 # so we have to check for potential name collisions ourselves. 1403 seen = {} 1404 for v in self.variants.variants: 1405 v.check_clash(self.info, seen) 1406 if self.doc: 1407 self.doc.connect_member(v) 1408 if self.doc: 1409 self.doc.check() 1410 1411 def c_type(self): 1412 return c_name(self.name) + pointer_suffix 1413 1414 def json_type(self): 1415 return 'value' 1416 1417 def visit(self, visitor): 1418 visitor.visit_alternate_type(self.name, self.info, self.variants) 1419 1420 def is_empty(self): 1421 return False 1422 1423 1424class QAPISchemaCommand(QAPISchemaEntity): 1425 def __init__(self, name, info, doc, arg_type, ret_type, 1426 gen, success_response, boxed, allow_oob, allow_preconfig): 1427 QAPISchemaEntity.__init__(self, name, info, doc) 1428 assert not arg_type or isinstance(arg_type, str) 1429 assert not ret_type or isinstance(ret_type, str) 1430 self._arg_type_name = arg_type 1431 self.arg_type = None 1432 self._ret_type_name = ret_type 1433 self.ret_type = None 1434 self.gen = gen 1435 self.success_response = success_response 1436 self.boxed = boxed 1437 self.allow_oob = allow_oob 1438 self.allow_preconfig = allow_preconfig 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 if self._ret_type_name: 1456 self.ret_type = schema.lookup_type(self._ret_type_name) 1457 assert isinstance(self.ret_type, QAPISchemaType) 1458 1459 def visit(self, visitor): 1460 visitor.visit_command(self.name, self.info, 1461 self.arg_type, self.ret_type, 1462 self.gen, self.success_response, 1463 self.boxed, self.allow_oob, 1464 self.allow_preconfig) 1465 1466 1467class QAPISchemaEvent(QAPISchemaEntity): 1468 def __init__(self, name, info, doc, arg_type, boxed): 1469 QAPISchemaEntity.__init__(self, name, info, doc) 1470 assert not arg_type or isinstance(arg_type, str) 1471 self._arg_type_name = arg_type 1472 self.arg_type = None 1473 self.boxed = boxed 1474 1475 def check(self, schema): 1476 if self._arg_type_name: 1477 self.arg_type = schema.lookup_type(self._arg_type_name) 1478 assert (isinstance(self.arg_type, QAPISchemaObjectType) or 1479 isinstance(self.arg_type, QAPISchemaAlternateType)) 1480 self.arg_type.check(schema) 1481 if self.boxed: 1482 if self.arg_type.is_empty(): 1483 raise QAPISemError(self.info, 1484 "Cannot use 'boxed' with empty type") 1485 else: 1486 assert not isinstance(self.arg_type, QAPISchemaAlternateType) 1487 assert not self.arg_type.variants 1488 elif self.boxed: 1489 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'") 1490 1491 def visit(self, visitor): 1492 visitor.visit_event(self.name, self.info, self.arg_type, self.boxed) 1493 1494 1495class QAPISchema(object): 1496 def __init__(self, fname): 1497 self._fname = fname 1498 parser = QAPISchemaParser(open(fname, 'r')) 1499 exprs = check_exprs(parser.exprs) 1500 self.docs = parser.docs 1501 self._entity_list = [] 1502 self._entity_dict = {} 1503 self._predefining = True 1504 self._def_predefineds() 1505 self._predefining = False 1506 self._def_exprs(exprs) 1507 self.check() 1508 1509 def _def_entity(self, ent): 1510 # Only the predefined types are allowed to not have info 1511 assert ent.info or self._predefining 1512 assert ent.name is None or ent.name not in self._entity_dict 1513 self._entity_list.append(ent) 1514 if ent.name is not None: 1515 self._entity_dict[ent.name] = ent 1516 if ent.info: 1517 ent.module = os.path.relpath(ent.info['file'], 1518 os.path.dirname(self._fname)) 1519 1520 def lookup_entity(self, name, typ=None): 1521 ent = self._entity_dict.get(name) 1522 if typ and not isinstance(ent, typ): 1523 return None 1524 return ent 1525 1526 def lookup_type(self, name): 1527 return self.lookup_entity(name, QAPISchemaType) 1528 1529 def _def_include(self, expr, info, doc): 1530 include = expr['include'] 1531 assert doc is None 1532 main_info = info 1533 while main_info['parent']: 1534 main_info = main_info['parent'] 1535 fname = os.path.relpath(include, os.path.dirname(main_info['file'])) 1536 self._def_entity(QAPISchemaInclude(fname, info)) 1537 1538 def _def_builtin_type(self, name, json_type, c_type): 1539 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type)) 1540 # Instantiating only the arrays that are actually used would 1541 # be nice, but we can't as long as their generated code 1542 # (qapi-builtin-types.[ch]) may be shared by some other 1543 # schema. 1544 self._make_array_type(name, None) 1545 1546 def _def_predefineds(self): 1547 for t in [('str', 'string', 'char' + pointer_suffix), 1548 ('number', 'number', 'double'), 1549 ('int', 'int', 'int64_t'), 1550 ('int8', 'int', 'int8_t'), 1551 ('int16', 'int', 'int16_t'), 1552 ('int32', 'int', 'int32_t'), 1553 ('int64', 'int', 'int64_t'), 1554 ('uint8', 'int', 'uint8_t'), 1555 ('uint16', 'int', 'uint16_t'), 1556 ('uint32', 'int', 'uint32_t'), 1557 ('uint64', 'int', 'uint64_t'), 1558 ('size', 'int', 'uint64_t'), 1559 ('bool', 'boolean', 'bool'), 1560 ('any', 'value', 'QObject' + pointer_suffix), 1561 ('null', 'null', 'QNull' + pointer_suffix)]: 1562 self._def_builtin_type(*t) 1563 self.the_empty_object_type = QAPISchemaObjectType( 1564 'q_empty', None, None, None, [], None) 1565 self._def_entity(self.the_empty_object_type) 1566 qtype_values = self._make_enum_members(['none', 'qnull', 'qnum', 1567 'qstring', 'qdict', 'qlist', 1568 'qbool']) 1569 self._def_entity(QAPISchemaEnumType('QType', None, None, 1570 qtype_values, 'QTYPE')) 1571 1572 def _make_enum_members(self, values): 1573 return [QAPISchemaMember(v) for v in values] 1574 1575 def _make_implicit_enum_type(self, name, info, values): 1576 # See also QAPISchemaObjectTypeMember._pretty_owner() 1577 name = name + 'Kind' # Use namespace reserved by add_name() 1578 self._def_entity(QAPISchemaEnumType( 1579 name, info, None, self._make_enum_members(values), None)) 1580 return name 1581 1582 def _make_array_type(self, element_type, info): 1583 name = element_type + 'List' # Use namespace reserved by add_name() 1584 if not self.lookup_type(name): 1585 self._def_entity(QAPISchemaArrayType(name, info, element_type)) 1586 return name 1587 1588 def _make_implicit_object_type(self, name, info, doc, role, members): 1589 if not members: 1590 return None 1591 # See also QAPISchemaObjectTypeMember._pretty_owner() 1592 name = 'q_obj_%s-%s' % (name, role) 1593 if not self.lookup_entity(name, QAPISchemaObjectType): 1594 self._def_entity(QAPISchemaObjectType(name, info, doc, None, 1595 members, None)) 1596 return name 1597 1598 def _def_enum_type(self, expr, info, doc): 1599 name = expr['enum'] 1600 data = expr['data'] 1601 prefix = expr.get('prefix') 1602 self._def_entity(QAPISchemaEnumType( 1603 name, info, doc, self._make_enum_members(data), prefix)) 1604 1605 def _make_member(self, name, typ, info): 1606 optional = False 1607 if name.startswith('*'): 1608 name = name[1:] 1609 optional = True 1610 if isinstance(typ, list): 1611 assert len(typ) == 1 1612 typ = self._make_array_type(typ[0], info) 1613 return QAPISchemaObjectTypeMember(name, typ, optional) 1614 1615 def _make_members(self, data, info): 1616 return [self._make_member(key, value, info) 1617 for (key, value) in data.items()] 1618 1619 def _def_struct_type(self, expr, info, doc): 1620 name = expr['struct'] 1621 base = expr.get('base') 1622 data = expr['data'] 1623 self._def_entity(QAPISchemaObjectType(name, info, doc, base, 1624 self._make_members(data, info), 1625 None)) 1626 1627 def _make_variant(self, case, typ): 1628 return QAPISchemaObjectTypeVariant(case, typ) 1629 1630 def _make_simple_variant(self, case, typ, info): 1631 if isinstance(typ, list): 1632 assert len(typ) == 1 1633 typ = self._make_array_type(typ[0], info) 1634 typ = self._make_implicit_object_type( 1635 typ, info, None, 'wrapper', [self._make_member('data', typ, info)]) 1636 return QAPISchemaObjectTypeVariant(case, typ) 1637 1638 def _def_union_type(self, expr, info, doc): 1639 name = expr['union'] 1640 data = expr['data'] 1641 base = expr.get('base') 1642 tag_name = expr.get('discriminator') 1643 tag_member = None 1644 if isinstance(base, dict): 1645 base = (self._make_implicit_object_type( 1646 name, info, doc, 'base', self._make_members(base, info))) 1647 if tag_name: 1648 variants = [self._make_variant(key, value) 1649 for (key, value) in data.items()] 1650 members = [] 1651 else: 1652 variants = [self._make_simple_variant(key, value, info) 1653 for (key, value) in data.items()] 1654 typ = self._make_implicit_enum_type(name, info, 1655 [v.name for v in variants]) 1656 tag_member = QAPISchemaObjectTypeMember('type', typ, False) 1657 members = [tag_member] 1658 self._def_entity( 1659 QAPISchemaObjectType(name, info, doc, base, members, 1660 QAPISchemaObjectTypeVariants(tag_name, 1661 tag_member, 1662 variants))) 1663 1664 def _def_alternate_type(self, expr, info, doc): 1665 name = expr['alternate'] 1666 data = expr['data'] 1667 variants = [self._make_variant(key, value) 1668 for (key, value) in data.items()] 1669 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False) 1670 self._def_entity( 1671 QAPISchemaAlternateType(name, info, doc, 1672 QAPISchemaObjectTypeVariants(None, 1673 tag_member, 1674 variants))) 1675 1676 def _def_command(self, expr, info, doc): 1677 name = expr['command'] 1678 data = expr.get('data') 1679 rets = expr.get('returns') 1680 gen = expr.get('gen', True) 1681 success_response = expr.get('success-response', True) 1682 boxed = expr.get('boxed', False) 1683 allow_oob = expr.get('allow-oob', False) 1684 allow_preconfig = expr.get('allow-preconfig', False) 1685 if isinstance(data, OrderedDict): 1686 data = self._make_implicit_object_type( 1687 name, info, doc, 'arg', self._make_members(data, info)) 1688 if isinstance(rets, list): 1689 assert len(rets) == 1 1690 rets = self._make_array_type(rets[0], info) 1691 self._def_entity(QAPISchemaCommand(name, info, doc, data, rets, 1692 gen, success_response, 1693 boxed, allow_oob, allow_preconfig)) 1694 1695 def _def_event(self, expr, info, doc): 1696 name = expr['event'] 1697 data = expr.get('data') 1698 boxed = expr.get('boxed', False) 1699 if isinstance(data, OrderedDict): 1700 data = self._make_implicit_object_type( 1701 name, info, doc, 'arg', self._make_members(data, info)) 1702 self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed)) 1703 1704 def _def_exprs(self, exprs): 1705 for expr_elem in exprs: 1706 expr = expr_elem['expr'] 1707 info = expr_elem['info'] 1708 doc = expr_elem.get('doc') 1709 if 'enum' in expr: 1710 self._def_enum_type(expr, info, doc) 1711 elif 'struct' in expr: 1712 self._def_struct_type(expr, info, doc) 1713 elif 'union' in expr: 1714 self._def_union_type(expr, info, doc) 1715 elif 'alternate' in expr: 1716 self._def_alternate_type(expr, info, doc) 1717 elif 'command' in expr: 1718 self._def_command(expr, info, doc) 1719 elif 'event' in expr: 1720 self._def_event(expr, info, doc) 1721 elif 'include' in expr: 1722 self._def_include(expr, info, doc) 1723 else: 1724 assert False 1725 1726 def check(self): 1727 for ent in self._entity_list: 1728 ent.check(self) 1729 1730 def visit(self, visitor): 1731 visitor.visit_begin(self) 1732 module = None 1733 for entity in self._entity_list: 1734 if visitor.visit_needed(entity): 1735 if entity.module != module: 1736 module = entity.module 1737 visitor.visit_module(module) 1738 entity.visit(visitor) 1739 visitor.visit_end() 1740 1741 1742# 1743# Code generation helpers 1744# 1745 1746def camel_case(name): 1747 new_name = '' 1748 first = True 1749 for ch in name: 1750 if ch in ['_', '-']: 1751 first = True 1752 elif first: 1753 new_name += ch.upper() 1754 first = False 1755 else: 1756 new_name += ch.lower() 1757 return new_name 1758 1759 1760# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1 1761# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2 1762# ENUM24_Name -> ENUM24_NAME 1763def camel_to_upper(value): 1764 c_fun_str = c_name(value, False) 1765 if value.isupper(): 1766 return c_fun_str 1767 1768 new_name = '' 1769 l = len(c_fun_str) 1770 for i in range(l): 1771 c = c_fun_str[i] 1772 # When c is upper and no '_' appears before, do more checks 1773 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_': 1774 if i < l - 1 and c_fun_str[i + 1].islower(): 1775 new_name += '_' 1776 elif c_fun_str[i - 1].isdigit(): 1777 new_name += '_' 1778 new_name += c 1779 return new_name.lstrip('_').upper() 1780 1781 1782def c_enum_const(type_name, const_name, prefix=None): 1783 if prefix is not None: 1784 type_name = prefix 1785 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper() 1786 1787if hasattr(str, 'maketrans'): 1788 c_name_trans = str.maketrans('.-', '__') 1789else: 1790 c_name_trans = string.maketrans('.-', '__') 1791 1792 1793# Map @name to a valid C identifier. 1794# If @protect, avoid returning certain ticklish identifiers (like 1795# C keywords) by prepending 'q_'. 1796# 1797# Used for converting 'name' from a 'name':'type' qapi definition 1798# into a generated struct member, as well as converting type names 1799# into substrings of a generated C function name. 1800# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo' 1801# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int' 1802def c_name(name, protect=True): 1803 # ANSI X3J11/88-090, 3.1.1 1804 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue', 1805 'default', 'do', 'double', 'else', 'enum', 'extern', 1806 'float', 'for', 'goto', 'if', 'int', 'long', 'register', 1807 'return', 'short', 'signed', 'sizeof', 'static', 1808 'struct', 'switch', 'typedef', 'union', 'unsigned', 1809 'void', 'volatile', 'while']) 1810 # ISO/IEC 9899:1999, 6.4.1 1811 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary']) 1812 # ISO/IEC 9899:2011, 6.4.1 1813 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic', 1814 '_Noreturn', '_Static_assert', '_Thread_local']) 1815 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html 1816 # excluding _.* 1817 gcc_words = set(['asm', 'typeof']) 1818 # C++ ISO/IEC 14882:2003 2.11 1819 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete', 1820 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable', 1821 'namespace', 'new', 'operator', 'private', 'protected', 1822 'public', 'reinterpret_cast', 'static_cast', 'template', 1823 'this', 'throw', 'true', 'try', 'typeid', 'typename', 1824 'using', 'virtual', 'wchar_t', 1825 # alternative representations 1826 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', 1827 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq']) 1828 # namespace pollution: 1829 polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386']) 1830 name = name.translate(c_name_trans) 1831 if protect and (name in c89_words | c99_words | c11_words | gcc_words 1832 | cpp_words | polluted_words): 1833 return 'q_' + name 1834 return name 1835 1836eatspace = '\033EATSPACE.' 1837pointer_suffix = ' *' + eatspace 1838 1839 1840def genindent(count): 1841 ret = '' 1842 for _ in range(count): 1843 ret += ' ' 1844 return ret 1845 1846indent_level = 0 1847 1848 1849def push_indent(indent_amount=4): 1850 global indent_level 1851 indent_level += indent_amount 1852 1853 1854def pop_indent(indent_amount=4): 1855 global indent_level 1856 indent_level -= indent_amount 1857 1858 1859# Generate @code with @kwds interpolated. 1860# Obey indent_level, and strip eatspace. 1861def cgen(code, **kwds): 1862 raw = code % kwds 1863 if indent_level: 1864 indent = genindent(indent_level) 1865 # re.subn() lacks flags support before Python 2.7, use re.compile() 1866 raw = re.subn(re.compile(r'^.', re.MULTILINE), 1867 indent + r'\g<0>', raw) 1868 raw = raw[0] 1869 return re.sub(re.escape(eatspace) + r' *', '', raw) 1870 1871 1872def mcgen(code, **kwds): 1873 if code[0] == '\n': 1874 code = code[1:] 1875 return cgen(code, **kwds) 1876 1877 1878def guardname(filename): 1879 return re.sub(r'[^A-Za-z0-9_]', '_', filename).upper() 1880 1881 1882def guardstart(name): 1883 return mcgen(''' 1884#ifndef %(name)s 1885#define %(name)s 1886 1887''', 1888 name=guardname(name)) 1889 1890 1891def guardend(name): 1892 return mcgen(''' 1893 1894#endif /* %(name)s */ 1895''', 1896 name=guardname(name)) 1897 1898 1899def gen_enum_lookup(name, values, prefix=None): 1900 ret = mcgen(''' 1901 1902const QEnumLookup %(c_name)s_lookup = { 1903 .array = (const char *const[]) { 1904''', 1905 c_name=c_name(name)) 1906 for value in values: 1907 index = c_enum_const(name, value, prefix) 1908 ret += mcgen(''' 1909 [%(index)s] = "%(value)s", 1910''', 1911 index=index, value=value) 1912 1913 ret += mcgen(''' 1914 }, 1915 .size = %(max_index)s 1916}; 1917''', 1918 max_index=c_enum_const(name, '_MAX', prefix)) 1919 return ret 1920 1921 1922def gen_enum(name, values, prefix=None): 1923 # append automatically generated _MAX value 1924 enum_values = values + ['_MAX'] 1925 1926 ret = mcgen(''' 1927 1928typedef enum %(c_name)s { 1929''', 1930 c_name=c_name(name)) 1931 1932 i = 0 1933 for value in enum_values: 1934 ret += mcgen(''' 1935 %(c_enum)s = %(i)d, 1936''', 1937 c_enum=c_enum_const(name, value, prefix), 1938 i=i) 1939 i += 1 1940 1941 ret += mcgen(''' 1942} %(c_name)s; 1943''', 1944 c_name=c_name(name)) 1945 1946 ret += mcgen(''' 1947 1948#define %(c_name)s_str(val) \\ 1949 qapi_enum_lookup(&%(c_name)s_lookup, (val)) 1950 1951extern const QEnumLookup %(c_name)s_lookup; 1952''', 1953 c_name=c_name(name)) 1954 return ret 1955 1956 1957def build_params(arg_type, boxed, extra): 1958 if not arg_type: 1959 assert not boxed 1960 return extra 1961 ret = '' 1962 sep = '' 1963 if boxed: 1964 ret += '%s arg' % arg_type.c_param_type() 1965 sep = ', ' 1966 else: 1967 assert not arg_type.variants 1968 for memb in arg_type.members: 1969 ret += sep 1970 sep = ', ' 1971 if memb.optional: 1972 ret += 'bool has_%s, ' % c_name(memb.name) 1973 ret += '%s %s' % (memb.type.c_param_type(), 1974 c_name(memb.name)) 1975 if extra: 1976 ret += sep + extra 1977 return ret 1978 1979 1980# 1981# Accumulate and write output 1982# 1983 1984class QAPIGen(object): 1985 1986 def __init__(self): 1987 self._preamble = '' 1988 self._body = '' 1989 1990 def preamble_add(self, text): 1991 self._preamble += text 1992 1993 def add(self, text): 1994 self._body += text 1995 1996 def _top(self, fname): 1997 return '' 1998 1999 def _bottom(self, fname): 2000 return '' 2001 2002 def write(self, output_dir, fname): 2003 pathname = os.path.join(output_dir, fname) 2004 dir = os.path.dirname(pathname) 2005 if dir: 2006 try: 2007 os.makedirs(dir) 2008 except os.error as e: 2009 if e.errno != errno.EEXIST: 2010 raise 2011 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666) 2012 f = os.fdopen(fd, 'r+') 2013 text = (self._top(fname) + self._preamble + self._body 2014 + self._bottom(fname)) 2015 oldtext = f.read(len(text) + 1) 2016 if text != oldtext: 2017 f.seek(0) 2018 f.truncate(0) 2019 f.write(text) 2020 f.close() 2021 2022 2023class QAPIGenC(QAPIGen): 2024 2025 def __init__(self, blurb, pydoc): 2026 QAPIGen.__init__(self) 2027 self._blurb = blurb 2028 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc, 2029 re.MULTILINE)) 2030 2031 def _top(self, fname): 2032 return mcgen(''' 2033/* AUTOMATICALLY GENERATED, DO NOT MODIFY */ 2034 2035/* 2036%(blurb)s 2037 * 2038 * %(copyright)s 2039 * 2040 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 2041 * See the COPYING.LIB file in the top-level directory. 2042 */ 2043 2044''', 2045 blurb=self._blurb, copyright=self._copyright) 2046 2047 def _bottom(self, fname): 2048 return mcgen(''' 2049/* Dummy declaration to prevent empty .o file */ 2050char dummy_%(name)s; 2051''', 2052 name=c_name(fname)) 2053 2054 2055class QAPIGenH(QAPIGenC): 2056 2057 def _top(self, fname): 2058 return QAPIGenC._top(self, fname) + guardstart(fname) 2059 2060 def _bottom(self, fname): 2061 return guardend(fname) 2062 2063 2064class QAPIGenDoc(QAPIGen): 2065 2066 def _top(self, fname): 2067 return (QAPIGen._top(self, fname) 2068 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n') 2069 2070 2071class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor): 2072 2073 def __init__(self, prefix, what, blurb, pydoc): 2074 self._prefix = prefix 2075 self._what = what 2076 self._genc = QAPIGenC(blurb, pydoc) 2077 self._genh = QAPIGenH(blurb, pydoc) 2078 2079 def write(self, output_dir): 2080 self._genc.write(output_dir, self._prefix + self._what + '.c') 2081 self._genh.write(output_dir, self._prefix + self._what + '.h') 2082 2083 2084class QAPISchemaModularCVisitor(QAPISchemaVisitor): 2085 2086 def __init__(self, prefix, what, blurb, pydoc): 2087 self._prefix = prefix 2088 self._what = what 2089 self._blurb = blurb 2090 self._pydoc = pydoc 2091 self._module = {} 2092 self._main_module = None 2093 2094 def _module_basename(self, what, name): 2095 if name is None: 2096 return re.sub(r'-', '-builtin-', what) 2097 basename = os.path.join(os.path.dirname(name), 2098 self._prefix + what) 2099 if name == self._main_module: 2100 return basename 2101 return basename + '-' + os.path.splitext(os.path.basename(name))[0] 2102 2103 def _add_module(self, name, blurb): 2104 if self._main_module is None and name is not None: 2105 self._main_module = name 2106 genc = QAPIGenC(blurb, self._pydoc) 2107 genh = QAPIGenH(blurb, self._pydoc) 2108 self._module[name] = (genc, genh) 2109 self._set_module(name) 2110 2111 def _set_module(self, name): 2112 self._genc, self._genh = self._module[name] 2113 2114 def write(self, output_dir, opt_builtins=False): 2115 for name in self._module: 2116 if name is None and not opt_builtins: 2117 continue 2118 basename = self._module_basename(self._what, name) 2119 (genc, genh) = self._module[name] 2120 genc.write(output_dir, basename + '.c') 2121 genh.write(output_dir, basename + '.h') 2122 2123 def _begin_module(self, name): 2124 pass 2125 2126 def visit_module(self, name): 2127 if name in self._module: 2128 self._set_module(name) 2129 return 2130 self._add_module(name, self._blurb) 2131 self._begin_module(name) 2132 2133 def visit_include(self, name, info): 2134 basename = self._module_basename(self._what, name) 2135 self._genh.preamble_add(mcgen(''' 2136#include "%(basename)s.h" 2137''', 2138 basename=basename)) 2139