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