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