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