4 # Copyright IBM, Corp. 2011
5 # Copyright (c) 2013-2018 Red Hat Inc.
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
21 from collections import OrderedDict
24 'null': 'QTYPE_QNULL',
25 'str': 'QTYPE_QSTRING',
27 'number': 'QTYPE_QNUM',
28 'bool': 'QTYPE_QBOOL',
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',
38 'any': None, # any QType possible, actually
39 'QType': 'QTYPE_QSTRING',
42 # Are documentation comments required?
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 = []
57 # Parsing the schema into expressions
61 def error_path(parent):
64 res = ('In file included from %s:%d:\n' % (parent['file'],
65 parent['line'])) + res
66 parent = parent['parent']
70 class QAPIError(Exception):
71 def __init__(self, fname, line, col, incl_info, msg):
72 Exception.__init__(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):
89 for ch in parser.src[parser.line_pos:parser.pos]:
91 col = (col + 7) % 8 + 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,
104 class QAPIDoc(object):
105 class Section(object):
106 def __init__(self, name=None):
107 # optional section name (argument/member or section name)
109 # the list of lines for this section
112 def append(self, line):
113 self.text += line.rstrip() + '\n'
115 class ArgSection(Section):
116 def __init__(self, name):
117 QAPIDoc.Section.__init__(self, name)
120 def connect(self, member):
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
131 self.body = QAPIDoc.Section()
132 # dict mapping parameter name to ArgSection
133 self.args = OrderedDict()
136 # the current section
137 self._section = self.body
139 def has_section(self, name):
140 """Return True if we have a section with this name."""
141 for i in self.sections:
146 def append(self, line):
147 """Parse a comment line and add it to the documentation."""
150 self._append_freeform(line)
154 raise QAPIParseError(self._parser, "Missing space after #")
157 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
158 # recognized, and get silently treated as ordinary text
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
167 raise QAPIParseError(self._parser, "Invalid name")
169 self._append_freeform(line)
171 def end_comment(self):
174 def _append_symbol_line(self, line):
175 name = line.split(' ', 1)[0]
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
183 'Example:', 'Examples:',
185 line = line[len(name)+1:]
186 self._start_section(name[:-1])
188 self._append_freeform(line)
190 def _start_args_section(self, name):
191 # FIXME invalid names other than the empty string aren't flagged
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)
198 raise QAPIParseError(self._parser,
199 "'@%s:' can't follow '%s' section"
200 % (name, self.sections[0].name))
202 self._section = QAPIDoc.ArgSection(name)
203 self.args[name] = self._section
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)
210 self._section = QAPIDoc.Section(name)
211 self.sections.append(self._section)
213 def _end_section(self):
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)
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')):
229 match = re.match(r'(@\S+:)', line)
231 raise QAPIParseError(self._parser,
232 "'%s' not allowed in free-form documentation"
234 self._section.append(line)
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)
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")
248 bogus = [name for name, section in self.args.items()
249 if not section.member]
253 "The following documented members are not in "
254 "the declaration: %s" % ", ".join(bogus))
257 class QAPISchemaParser(object):
259 def __init__(self, fp, previously_included=[], incl_info=None):
261 previously_included.append(os.path.abspath(fp.name))
262 self.incl_info = incl_info
264 if self.src == '' or self.src[-1] != '\n':
274 while self.tok is not None:
275 info = {'file': self.fname, 'line': self.line,
276 'parent': self.incl_info}
278 self.reject_expr_doc(cur_doc)
279 cur_doc = self.get_doc(info)
280 self.docs.append(cur_doc)
283 expr = self.get_expr(False)
284 if 'include' in expr:
285 self.reject_expr_doc(cur_doc)
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),
294 self.exprs.append({'expr': {'include': incl_fname},
296 exprs_include = self._include(include, info, incl_fname,
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)
304 raise QAPISemError(info, "Invalid 'pragma' directive")
305 pragma = expr['pragma']
306 if not isinstance(pragma, dict):
308 info, "Value of 'pragma' must be a dictionary")
309 for name, value in pragma.items():
310 self._pragma(name, value, info)
312 expr_elem = {'expr': expr,
315 if not cur_doc.symbol:
317 cur_doc.info, "Expression documentation required")
318 expr_elem['doc'] = cur_doc
319 self.exprs.append(expr_elem)
321 self.reject_expr_doc(cur_doc)
324 def reject_expr_doc(doc):
325 if doc and doc.symbol:
328 "Documentation for '%s' is not followed by the definition"
331 def _include(self, include, info, incl_fname, previously_included):
332 incl_abs_fname = os.path.abspath(incl_fname)
333 # catch inclusion cycle
336 if incl_abs_fname == os.path.abspath(inf['file']):
337 raise QAPISemError(info, "Inclusion loop for %s" % include)
340 # skip multiple include of the same file
341 if incl_abs_fname in previously_included:
345 if sys.version_info[0] >= 3:
346 fobj = open(incl_fname, 'r', encoding='utf-8')
348 fobj = open(incl_fname, 'r')
350 raise QAPISemError(info, '%s: %s' % (e.strerror, incl_fname))
351 return QAPISchemaParser(fobj, previously_included, info)
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")
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
375 raise QAPISemError(info, "Unknown pragma '%s'" % name)
377 def accept(self, skip_comment=True):
379 self.tok = self.src[self.cursor]
380 self.pos = self.cursor
385 if self.src[self.cursor] == '#':
386 # Start of doc comment
388 self.cursor = self.src.find('\n', self.cursor)
390 self.val = self.src[self.pos:self.cursor]
392 elif self.tok in '{}:,[]':
394 elif self.tok == "'":
398 ch = self.src[self.cursor]
401 raise QAPIParseError(self, 'Missing terminating "\'"')
415 for _ in range(0, 4):
416 ch = self.src[self.cursor]
418 if ch not in '0123456789abcdefABCDEF':
419 raise QAPIParseError(self,
420 '\\u escape needs 4 '
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')
436 raise QAPIParseError(self,
437 "Unknown escape \\%s" % ch)
446 elif self.src.startswith('true', self.pos):
450 elif self.src.startswith('false', self.pos):
454 elif self.src.startswith('null', self.pos):
458 elif self.tok == '\n':
459 if self.cursor == len(self.src):
463 self.line_pos = self.cursor
464 elif not self.tok.isspace():
465 raise QAPIParseError(self, 'Stray "%s"' % self.tok)
467 def get_members(self):
473 raise QAPIParseError(self, 'Expected string or "}"')
478 raise QAPIParseError(self, 'Expected ":"')
481 raise QAPIParseError(self, 'Duplicate key "%s"' % key)
482 expr[key] = self.get_expr(True)
487 raise QAPIParseError(self, 'Expected "," or "}"')
490 raise QAPIParseError(self, 'Expected string')
492 def get_values(self):
497 if self.tok not in "{['tfn":
498 raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
501 expr.append(self.get_expr(True))
506 raise QAPIParseError(self, 'Expected "," or "]"')
509 def get_expr(self, nested):
510 if self.tok != '{' and not nested:
511 raise QAPIParseError(self, 'Expected "{"')
514 expr = self.get_members()
515 elif self.tok == '[':
517 expr = self.get_values()
518 elif self.tok in "'tfn":
522 raise QAPIParseError(self, 'Expected "{", "[", string, '
526 def get_doc(self, info):
528 raise QAPIParseError(self, "Junk after '##' at start of "
529 "documentation comment")
531 doc = QAPIDoc(self, info)
533 while self.tok == '#':
534 if self.val.startswith('##'):
537 raise QAPIParseError(self, "Junk after '##' at end of "
538 "documentation comment")
546 raise QAPIParseError(self, "Documentation comment must end with '##'")
550 # Semantic analysis of schema expressions
551 # TODO fold into QAPISchema
552 # TODO catching name collisions in generated code would be nice
556 def find_base_members(base):
557 if isinstance(base, dict):
559 base_struct_define = struct_types.get(base)
560 if not base_struct_define:
562 return base_struct_define['data']
565 # Return the qtype of an alternate branch, or None on error.
566 def 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:
571 elif qapi_type in enum_types:
572 return 'QTYPE_QSTRING'
573 elif qapi_type in union_types:
578 # Return the discriminator enum define if discriminator is specified as an
579 # enum type, otherwise return None.
580 def discriminator_find_enum_define(expr):
581 base = expr.get('base')
582 discriminator = expr.get('discriminator')
584 if not (discriminator and base):
587 base_members = find_base_members(base)
591 discriminator_value = base_members.get(discriminator)
592 if not discriminator_value:
595 return enum_types.get(discriminator_value['type'])
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.
601 valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
602 '[a-zA-Z][a-zA-Z0-9_-]*$')
605 def check_name(info, source, name, allow_optional=False,
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'"
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))
628 def add_name(name, info, meta, implicit=False):
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
642 def check_if(expr, info):
644 def check_if_str(ifcond, info):
645 if not isinstance(ifcond, str):
647 info, "'if' condition must be a string or a list of strings")
649 raise QAPISemError(info, "'if' condition '' makes no sense")
651 ifcond = expr.get('if')
654 if isinstance(ifcond, list):
656 raise QAPISemError(info, "'if' condition [] is useless")
658 check_if_str(elt, info)
660 check_if_str(ifcond, info)
663 def check_type(info, source, value, allow_array=False,
664 allow_dict=False, allow_optional=False,
671 # Check if array type for value is okay
672 if isinstance(value, list):
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" %
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'"
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))
692 raise QAPISemError(info, "%s should be a type name" % source)
694 if not isinstance(value, OrderedDict):
695 raise QAPISemError(info,
696 "%s should be a dictionary or type name" % source)
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'"
705 # Todo: allow dictionaries to represent default values of
706 # an optional argument.
707 check_known_keys(info, "member '%s' of %s" % (key, source),
708 arg, ['type'], ['if'])
709 check_type(info, "Member '%s' of %s" % (key, source),
710 arg['type'], allow_array=True,
711 allow_metas=['built-in', 'union', 'alternate', 'struct',
715 def check_command(expr, info):
716 name = expr['command']
717 boxed = expr.get('boxed', False)
719 args_meta = ['struct']
721 args_meta += ['union', 'alternate']
722 check_type(info, "'data' for command '%s'" % name,
723 expr.get('data'), allow_dict=not boxed, allow_optional=True,
724 allow_metas=args_meta)
725 returns_meta = ['union', 'struct']
726 if name in returns_whitelist:
727 returns_meta += ['built-in', 'alternate', 'enum']
728 check_type(info, "'returns' for command '%s'" % name,
729 expr.get('returns'), allow_array=True,
730 allow_optional=True, allow_metas=returns_meta)
733 def check_event(expr, info):
735 boxed = expr.get('boxed', False)
739 meta += ['union', 'alternate']
740 check_type(info, "'data' for event '%s'" % name,
741 expr.get('data'), allow_dict=not boxed, allow_optional=True,
745 def enum_get_names(expr):
746 return [e['name'] for e in expr['data']]
749 def check_union(expr, info):
751 base = expr.get('base')
752 discriminator = expr.get('discriminator')
753 members = expr['data']
755 # Two types of unions, determined by discriminator.
757 # With no discriminator it is a simple union.
758 if discriminator is None:
760 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
762 raise QAPISemError(info, "Simple union '%s' must not have a base" %
765 # Else, it's a flat union.
767 # The object must have a string or dictionary 'base'.
768 check_type(info, "'base' for union '%s'" % name,
769 base, allow_dict=True, allow_optional=True,
770 allow_metas=['struct'])
772 raise QAPISemError(info, "Flat union '%s' must have a base"
774 base_members = find_base_members(base)
775 assert base_members is not None
777 # The value of member 'discriminator' must name a non-optional
778 # member of the base struct.
779 check_name(info, "Discriminator of flat union '%s'" % name,
781 discriminator_value = base_members.get(discriminator)
782 if not discriminator_value:
783 raise QAPISemError(info,
784 "Discriminator '%s' is not a member of base "
786 % (discriminator, base))
787 if discriminator_value.get('if'):
788 raise QAPISemError(info, 'The discriminator %s.%s for union %s '
789 'must not be conditional' %
790 (base, discriminator, name))
791 enum_define = enum_types.get(discriminator_value['type'])
792 allow_metas = ['struct']
793 # Do not allow string discriminator
795 raise QAPISemError(info,
796 "Discriminator '%s' must be of enumeration "
797 "type" % discriminator)
799 # Check every branch; don't allow an empty union
800 if len(members) == 0:
801 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
802 for (key, value) in members.items():
803 check_name(info, "Member of union '%s'" % name, key)
805 check_known_keys(info, "member '%s' of union '%s'" % (key, name),
806 value, ['type'], ['if'])
807 # Each value must name a known type
808 check_type(info, "Member '%s' of union '%s'" % (key, name),
810 allow_array=not base, allow_metas=allow_metas)
812 # If the discriminator names an enum type, then all members
813 # of 'data' must also be members of the enum type.
815 if key not in enum_get_names(enum_define):
816 raise QAPISemError(info,
817 "Discriminator value '%s' is not found in "
819 % (key, enum_define['enum']))
822 def check_alternate(expr, info):
823 name = expr['alternate']
824 members = expr['data']
827 # Check every branch; require at least two branches
829 raise QAPISemError(info,
830 "Alternate '%s' should have at least two branches "
832 for (key, value) in members.items():
833 check_name(info, "Member of alternate '%s'" % name, key)
834 check_known_keys(info,
835 "member '%s' of alternate '%s'" % (key, name),
836 value, ['type'], ['if'])
839 # Ensure alternates have no type conflicts.
840 check_type(info, "Member '%s' of alternate '%s'" % (key, name), typ,
841 allow_metas=['built-in', 'union', 'struct', 'enum'])
842 qtype = find_alternate_member_qtype(typ)
844 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
845 "type '%s'" % (name, key, typ))
846 conflicting = set([qtype])
847 if qtype == 'QTYPE_QSTRING':
848 enum_expr = enum_types.get(typ)
850 for v in enum_get_names(enum_expr):
851 if v in ['on', 'off']:
852 conflicting.add('QTYPE_QBOOL')
853 if re.match(r'[-+0-9.]', v): # lazy, could be tightened
854 conflicting.add('QTYPE_QNUM')
856 conflicting.add('QTYPE_QNUM')
857 conflicting.add('QTYPE_QBOOL')
858 for qt in conflicting:
860 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
861 "be distinguished from member '%s'"
862 % (name, key, types_seen[qt]))
866 def check_enum(expr, info):
868 members = expr['data']
869 prefix = expr.get('prefix')
871 if not isinstance(members, list):
872 raise QAPISemError(info,
873 "Enum '%s' requires an array for 'data'" % name)
874 if prefix is not None and not isinstance(prefix, str):
875 raise QAPISemError(info,
876 "Enum '%s' requires a string for 'prefix'" % name)
878 for member in members:
879 source = "dictionary member of enum '%s'" % name
880 check_known_keys(info, source, member, ['name'], ['if'])
881 check_if(member, info)
882 check_name(info, "Member of enum '%s'" % name, member['name'],
886 def check_struct(expr, info):
887 name = expr['struct']
888 members = expr['data']
890 check_type(info, "'data' for struct '%s'" % name, members,
891 allow_dict=True, allow_optional=True)
892 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
893 allow_metas=['struct'])
896 def check_known_keys(info, source, keys, required, optional):
899 return ', '.join("'" + e + "'" for e in sorted(elems))
901 missing = set(required) - set(keys)
903 raise QAPISemError(info, "Key%s %s %s missing from %s"
904 % ('s' if len(missing) > 1 else '', pprint(missing),
905 'are' if len(missing) > 1 else 'is', source))
906 allowed = set(required + optional)
907 unknown = set(keys) - allowed
909 raise QAPISemError(info, "Unknown key%s %s in %s\nValid keys are %s."
910 % ('s' if len(unknown) > 1 else '', pprint(unknown),
911 source, pprint(allowed)))
914 def check_keys(expr_elem, meta, required, optional=[]):
915 expr = expr_elem['expr']
916 info = expr_elem['info']
918 if not isinstance(name, str):
919 raise QAPISemError(info, "'%s' key must have a string value" % meta)
920 required = required + [meta]
921 source = "%s '%s'" % (meta, name)
922 check_known_keys(info, source, expr.keys(), required, optional)
923 for (key, value) in expr.items():
924 if key in ['gen', 'success-response'] and value is not False:
925 raise QAPISemError(info,
926 "'%s' of %s '%s' should only use false value"
928 if (key in ['boxed', 'allow-oob', 'allow-preconfig']
929 and value is not True):
930 raise QAPISemError(info,
931 "'%s' of %s '%s' should only use true value"
937 def normalize_enum(expr):
938 if isinstance(expr['data'], list):
939 expr['data'] = [m if isinstance(m, dict) else {'name': m}
940 for m in expr['data']]
943 def normalize_members(members):
944 if isinstance(members, OrderedDict):
945 for key, arg in members.items():
946 if isinstance(arg, dict):
948 members[key] = {'type': arg}
951 def check_exprs(exprs):
954 # Populate name table with names of built-in types
955 for builtin in builtin_types.keys():
956 all_names[builtin] = 'built-in'
958 # Learn the types and check for valid expression keys
959 for expr_elem in exprs:
960 expr = expr_elem['expr']
961 info = expr_elem['info']
962 doc = expr_elem.get('doc')
964 if 'include' in expr:
967 if not doc and doc_required:
968 raise QAPISemError(info,
969 "Expression missing documentation comment")
973 check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix'])
975 enum_types[expr[meta]] = expr
976 elif 'union' in expr:
978 check_keys(expr_elem, 'union', ['data'],
979 ['base', 'discriminator', 'if'])
980 normalize_members(expr.get('base'))
981 normalize_members(expr['data'])
982 union_types[expr[meta]] = expr
983 elif 'alternate' in expr:
985 check_keys(expr_elem, 'alternate', ['data'], ['if'])
986 normalize_members(expr['data'])
987 elif 'struct' in expr:
989 check_keys(expr_elem, 'struct', ['data'], ['base', 'if'])
990 normalize_members(expr['data'])
991 struct_types[expr[meta]] = expr
992 elif 'command' in expr:
994 check_keys(expr_elem, 'command', [],
995 ['data', 'returns', 'gen', 'success-response',
996 'boxed', 'allow-oob', 'allow-preconfig', 'if'])
997 normalize_members(expr.get('data'))
998 elif 'event' in expr:
1000 check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if'])
1001 normalize_members(expr.get('data'))
1003 raise QAPISemError(expr_elem['info'],
1004 "Expression is missing metatype")
1006 add_name(name, info, meta)
1007 if doc and doc.symbol != name:
1008 raise QAPISemError(info, "Definition of '%s' follows documentation"
1009 " for '%s'" % (name, doc.symbol))
1011 # Try again for hidden UnionKind enum
1012 for expr_elem in exprs:
1013 expr = expr_elem['expr']
1015 if 'include' in expr:
1017 if 'union' in expr and not discriminator_find_enum_define(expr):
1018 name = '%sKind' % expr['union']
1019 elif 'alternate' in expr:
1020 name = '%sKind' % expr['alternate']
1023 enum_types[name] = {'enum': name}
1024 add_name(name, info, 'enum', implicit=True)
1026 # Validate that exprs make sense
1027 for expr_elem in exprs:
1028 expr = expr_elem['expr']
1029 info = expr_elem['info']
1030 doc = expr_elem.get('doc')
1032 if 'include' in expr:
1035 check_enum(expr, info)
1036 elif 'union' in expr:
1037 check_union(expr, info)
1038 elif 'alternate' in expr:
1039 check_alternate(expr, info)
1040 elif 'struct' in expr:
1041 check_struct(expr, info)
1042 elif 'command' in expr:
1043 check_command(expr, info)
1044 elif 'event' in expr:
1045 check_event(expr, info)
1047 assert False, 'unexpected meta type'
1050 doc.check_expr(expr)
1056 # Schema compiler frontend
1059 def listify_cond(ifcond):
1062 if not isinstance(ifcond, list):
1067 class QAPISchemaEntity(object):
1068 def __init__(self, name, info, doc, ifcond=None):
1069 assert name is None or isinstance(name, str)
1072 # For explicitly defined entities, info points to the (explicit)
1073 # definition. For builtins (and their arrays), info is None.
1074 # For implicitly defined entities, info points to a place that
1075 # triggered the implicit definition (there may be more than one
1079 self._ifcond = ifcond # self.ifcond is set only after .check()
1082 return c_name(self.name)
1084 def check(self, schema):
1085 if isinstance(self._ifcond, QAPISchemaType):
1086 # inherit the condition from a type
1089 self.ifcond = typ.ifcond
1091 self.ifcond = listify_cond(self._ifcond)
1093 def is_implicit(self):
1094 return not self.info
1096 def visit(self, visitor):
1100 class QAPISchemaVisitor(object):
1101 def visit_begin(self, schema):
1104 def visit_end(self):
1107 def visit_module(self, fname):
1110 def visit_needed(self, entity):
1111 # Default to visiting everything
1114 def visit_include(self, fname, info):
1117 def visit_builtin_type(self, name, info, json_type):
1120 def visit_enum_type(self, name, info, ifcond, members, prefix):
1123 def visit_array_type(self, name, info, ifcond, element_type):
1126 def visit_object_type(self, name, info, ifcond, base, members, variants):
1129 def visit_object_type_flat(self, name, info, ifcond, members, variants):
1132 def visit_alternate_type(self, name, info, ifcond, variants):
1135 def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
1136 success_response, boxed, allow_oob, allow_preconfig):
1139 def visit_event(self, name, info, ifcond, arg_type, boxed):
1143 class QAPISchemaInclude(QAPISchemaEntity):
1145 def __init__(self, fname, info):
1146 QAPISchemaEntity.__init__(self, None, info, None)
1149 def visit(self, visitor):
1150 visitor.visit_include(self.fname, self.info)
1153 class QAPISchemaType(QAPISchemaEntity):
1154 # Return the C type for common use.
1155 # For the types we commonly box, this is a pointer type.
1159 # Return the C type to be used in a parameter list.
1160 def c_param_type(self):
1161 return self.c_type()
1163 # Return the C type to be used where we suppress boxing.
1164 def c_unboxed_type(self):
1165 return self.c_type()
1167 def json_type(self):
1170 def alternate_qtype(self):
1172 'null': 'QTYPE_QNULL',
1173 'string': 'QTYPE_QSTRING',
1174 'number': 'QTYPE_QNUM',
1175 'int': 'QTYPE_QNUM',
1176 'boolean': 'QTYPE_QBOOL',
1177 'object': 'QTYPE_QDICT'
1179 return json2qtype.get(self.json_type())
1182 if self.is_implicit():
1187 class QAPISchemaBuiltinType(QAPISchemaType):
1188 def __init__(self, name, json_type, c_type):
1189 QAPISchemaType.__init__(self, name, None, None)
1190 assert not c_type or isinstance(c_type, str)
1191 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1193 self._json_type_name = json_type
1194 self._c_type_name = c_type
1200 return self._c_type_name
1202 def c_param_type(self):
1203 if self.name == 'str':
1204 return 'const ' + self._c_type_name
1205 return self._c_type_name
1207 def json_type(self):
1208 return self._json_type_name
1211 return self.json_type()
1213 def visit(self, visitor):
1214 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1217 class QAPISchemaEnumType(QAPISchemaType):
1218 def __init__(self, name, info, doc, ifcond, members, prefix):
1219 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1221 assert isinstance(m, QAPISchemaMember)
1223 assert prefix is None or isinstance(prefix, str)
1224 self.members = members
1225 self.prefix = prefix
1227 def check(self, schema):
1228 QAPISchemaType.check(self, schema)
1230 for m in self.members:
1231 m.check_clash(self.info, seen)
1233 self.doc.connect_member(m)
1235 def is_implicit(self):
1236 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1237 return self.name.endswith('Kind') or self.name == 'QType'
1240 return c_name(self.name)
1242 def member_names(self):
1243 return [m.name for m in self.members]
1245 def json_type(self):
1248 def visit(self, visitor):
1249 visitor.visit_enum_type(self.name, self.info, self.ifcond,
1250 self.members, self.prefix)
1253 class QAPISchemaArrayType(QAPISchemaType):
1254 def __init__(self, name, info, element_type):
1255 QAPISchemaType.__init__(self, name, info, None, None)
1256 assert isinstance(element_type, str)
1257 self._element_type_name = element_type
1258 self.element_type = None
1260 def check(self, schema):
1261 QAPISchemaType.check(self, schema)
1262 self.element_type = schema.lookup_type(self._element_type_name)
1263 assert self.element_type
1264 self.element_type.check(schema)
1265 self.ifcond = self.element_type.ifcond
1267 def is_implicit(self):
1271 return c_name(self.name) + pointer_suffix
1273 def json_type(self):
1277 elt_doc_type = self.element_type.doc_type()
1278 if not elt_doc_type:
1280 return 'array of ' + elt_doc_type
1282 def visit(self, visitor):
1283 visitor.visit_array_type(self.name, self.info, self.ifcond,
1287 class QAPISchemaObjectType(QAPISchemaType):
1288 def __init__(self, name, info, doc, ifcond,
1289 base, local_members, variants):
1290 # struct has local_members, optional base, and no variants
1291 # flat union has base, variants, and no local_members
1292 # simple union has local_members, variants, and no base
1293 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1294 assert base is None or isinstance(base, str)
1295 for m in local_members:
1296 assert isinstance(m, QAPISchemaObjectTypeMember)
1298 if variants is not None:
1299 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1300 variants.set_owner(name)
1301 self._base_name = base
1303 self.local_members = local_members
1304 self.variants = variants
1307 def check(self, schema):
1308 QAPISchemaType.check(self, schema)
1309 if self.members is False: # check for cycles
1310 raise QAPISemError(self.info,
1311 "Object %s contains itself" % self.name)
1314 self.members = False # mark as being checked
1315 seen = OrderedDict()
1317 self.base = schema.lookup_type(self._base_name)
1318 assert isinstance(self.base, QAPISchemaObjectType)
1319 self.base.check(schema)
1320 self.base.check_clash(self.info, seen)
1321 for m in self.local_members:
1323 m.check_clash(self.info, seen)
1325 self.doc.connect_member(m)
1326 self.members = seen.values()
1328 self.variants.check(schema, seen)
1329 assert self.variants.tag_member in self.members
1330 self.variants.check_clash(self.info, seen)
1334 # Check that the members of this type do not cause duplicate JSON members,
1335 # and update seen to track the members seen so far. Report any errors
1336 # on behalf of info, which is not necessarily self.info
1337 def check_clash(self, info, seen):
1338 assert not self.variants # not implemented
1339 for m in self.members:
1340 m.check_clash(info, seen)
1342 def is_implicit(self):
1343 # See QAPISchema._make_implicit_object_type(), as well as
1344 # _def_predefineds()
1345 return self.name.startswith('q_')
1348 assert self.members is not None
1349 return not self.members and not self.variants
1352 assert self.name != 'q_empty'
1353 return QAPISchemaType.c_name(self)
1356 assert not self.is_implicit()
1357 return c_name(self.name) + pointer_suffix
1359 def c_unboxed_type(self):
1360 return c_name(self.name)
1362 def json_type(self):
1365 def visit(self, visitor):
1366 visitor.visit_object_type(self.name, self.info, self.ifcond,
1367 self.base, self.local_members, self.variants)
1368 visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
1369 self.members, self.variants)
1372 class QAPISchemaMember(object):
1375 def __init__(self, name, ifcond=None):
1376 assert isinstance(name, str)
1378 self.ifcond = listify_cond(ifcond)
1381 def set_owner(self, name):
1382 assert not self.owner
1385 def check_clash(self, info, seen):
1386 cname = c_name(self.name)
1387 if cname.lower() != cname and self.owner not in name_case_whitelist:
1388 raise QAPISemError(info,
1389 "%s should not use uppercase" % self.describe())
1391 raise QAPISemError(info, "%s collides with %s" %
1392 (self.describe(), seen[cname].describe()))
1395 def _pretty_owner(self):
1397 if owner.startswith('q_obj_'):
1398 # See QAPISchema._make_implicit_object_type() - reverse the
1399 # mapping there to create a nice human-readable description
1401 if owner.endswith('-arg'):
1402 return '(parameter of %s)' % owner[:-4]
1403 elif owner.endswith('-base'):
1404 return '(base of %s)' % owner[:-5]
1406 assert owner.endswith('-wrapper')
1407 # Unreachable and not implemented
1409 if owner.endswith('Kind'):
1410 # See QAPISchema._make_implicit_enum_type()
1411 return '(branch of %s)' % owner[:-4]
1412 return '(%s of %s)' % (self.role, owner)
1415 return "'%s' %s" % (self.name, self._pretty_owner())
1418 class QAPISchemaObjectTypeMember(QAPISchemaMember):
1419 def __init__(self, name, typ, optional, ifcond=None):
1420 QAPISchemaMember.__init__(self, name, ifcond)
1421 assert isinstance(typ, str)
1422 assert isinstance(optional, bool)
1423 self._type_name = typ
1425 self.optional = optional
1427 def check(self, schema):
1429 self.type = schema.lookup_type(self._type_name)
1433 class QAPISchemaObjectTypeVariants(object):
1434 def __init__(self, tag_name, tag_member, variants):
1435 # Flat unions pass tag_name but not tag_member.
1436 # Simple unions and alternates pass tag_member but not tag_name.
1437 # After check(), tag_member is always set, and tag_name remains
1438 # a reliable witness of being used by a flat union.
1439 assert bool(tag_member) != bool(tag_name)
1440 assert (isinstance(tag_name, str) or
1441 isinstance(tag_member, QAPISchemaObjectTypeMember))
1442 assert len(variants) > 0
1444 assert isinstance(v, QAPISchemaObjectTypeVariant)
1445 self._tag_name = tag_name
1446 self.tag_member = tag_member
1447 self.variants = variants
1449 def set_owner(self, name):
1450 for v in self.variants:
1453 def check(self, schema, seen):
1454 if not self.tag_member: # flat union
1455 self.tag_member = seen[c_name(self._tag_name)]
1456 assert self._tag_name == self.tag_member.name
1457 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1458 if self._tag_name: # flat union
1459 # branches that are not explicitly covered get an empty type
1460 cases = set([v.name for v in self.variants])
1461 for m in self.tag_member.type.members:
1462 if m.name not in cases:
1463 v = QAPISchemaObjectTypeVariant(m.name, 'q_empty')
1464 v.set_owner(self.tag_member.owner)
1465 self.variants.append(v)
1466 for v in self.variants:
1468 # Union names must match enum values; alternate names are
1469 # checked separately. Use 'seen' to tell the two apart.
1471 assert v.name in self.tag_member.type.member_names()
1472 assert isinstance(v.type, QAPISchemaObjectType)
1473 v.type.check(schema)
1475 def check_clash(self, info, seen):
1476 for v in self.variants:
1477 # Reset seen map for each variant, since qapi names from one
1478 # branch do not affect another branch
1479 assert isinstance(v.type, QAPISchemaObjectType)
1480 v.type.check_clash(info, dict(seen))
1483 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1486 def __init__(self, name, typ, ifcond=None):
1487 QAPISchemaObjectTypeMember.__init__(self, name, typ, False, ifcond)
1490 class QAPISchemaAlternateType(QAPISchemaType):
1491 def __init__(self, name, info, doc, ifcond, variants):
1492 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1493 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1494 assert variants.tag_member
1495 variants.set_owner(name)
1496 variants.tag_member.set_owner(self.name)
1497 self.variants = variants
1499 def check(self, schema):
1500 QAPISchemaType.check(self, schema)
1501 self.variants.tag_member.check(schema)
1502 # Not calling self.variants.check_clash(), because there's nothing
1504 self.variants.check(schema, {})
1505 # Alternate branch names have no relation to the tag enum values;
1506 # so we have to check for potential name collisions ourselves.
1508 for v in self.variants.variants:
1509 v.check_clash(self.info, seen)
1511 self.doc.connect_member(v)
1516 return c_name(self.name) + pointer_suffix
1518 def json_type(self):
1521 def visit(self, visitor):
1522 visitor.visit_alternate_type(self.name, self.info, self.ifcond,
1529 class QAPISchemaCommand(QAPISchemaEntity):
1530 def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
1531 gen, success_response, boxed, allow_oob, allow_preconfig):
1532 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1533 assert not arg_type or isinstance(arg_type, str)
1534 assert not ret_type or isinstance(ret_type, str)
1535 self._arg_type_name = arg_type
1536 self.arg_type = None
1537 self._ret_type_name = ret_type
1538 self.ret_type = None
1540 self.success_response = success_response
1542 self.allow_oob = allow_oob
1543 self.allow_preconfig = allow_preconfig
1545 def check(self, schema):
1546 QAPISchemaEntity.check(self, schema)
1547 if self._arg_type_name:
1548 self.arg_type = schema.lookup_type(self._arg_type_name)
1549 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1550 isinstance(self.arg_type, QAPISchemaAlternateType))
1551 self.arg_type.check(schema)
1553 if self.arg_type.is_empty():
1554 raise QAPISemError(self.info,
1555 "Cannot use 'boxed' with empty type")
1557 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1558 assert not self.arg_type.variants
1560 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1561 if self._ret_type_name:
1562 self.ret_type = schema.lookup_type(self._ret_type_name)
1563 assert isinstance(self.ret_type, QAPISchemaType)
1565 def visit(self, visitor):
1566 visitor.visit_command(self.name, self.info, self.ifcond,
1567 self.arg_type, self.ret_type,
1568 self.gen, self.success_response,
1569 self.boxed, self.allow_oob,
1570 self.allow_preconfig)
1573 class QAPISchemaEvent(QAPISchemaEntity):
1574 def __init__(self, name, info, doc, ifcond, arg_type, boxed):
1575 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1576 assert not arg_type or isinstance(arg_type, str)
1577 self._arg_type_name = arg_type
1578 self.arg_type = None
1581 def check(self, schema):
1582 QAPISchemaEntity.check(self, schema)
1583 if self._arg_type_name:
1584 self.arg_type = schema.lookup_type(self._arg_type_name)
1585 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1586 isinstance(self.arg_type, QAPISchemaAlternateType))
1587 self.arg_type.check(schema)
1589 if self.arg_type.is_empty():
1590 raise QAPISemError(self.info,
1591 "Cannot use 'boxed' with empty type")
1593 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1594 assert not self.arg_type.variants
1596 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1598 def visit(self, visitor):
1599 visitor.visit_event(self.name, self.info, self.ifcond,
1600 self.arg_type, self.boxed)
1603 class QAPISchema(object):
1604 def __init__(self, fname):
1606 if sys.version_info[0] >= 3:
1607 f = open(fname, 'r', encoding='utf-8')
1609 f = open(fname, 'r')
1610 parser = QAPISchemaParser(f)
1611 exprs = check_exprs(parser.exprs)
1612 self.docs = parser.docs
1613 self._entity_list = []
1614 self._entity_dict = {}
1615 self._predefining = True
1616 self._def_predefineds()
1617 self._predefining = False
1618 self._def_exprs(exprs)
1621 def _def_entity(self, ent):
1622 # Only the predefined types are allowed to not have info
1623 assert ent.info or self._predefining
1624 assert ent.name is None or ent.name not in self._entity_dict
1625 self._entity_list.append(ent)
1626 if ent.name is not None:
1627 self._entity_dict[ent.name] = ent
1629 ent.module = os.path.relpath(ent.info['file'],
1630 os.path.dirname(self._fname))
1632 def lookup_entity(self, name, typ=None):
1633 ent = self._entity_dict.get(name)
1634 if typ and not isinstance(ent, typ):
1638 def lookup_type(self, name):
1639 return self.lookup_entity(name, QAPISchemaType)
1641 def _def_include(self, expr, info, doc):
1642 include = expr['include']
1645 while main_info['parent']:
1646 main_info = main_info['parent']
1647 fname = os.path.relpath(include, os.path.dirname(main_info['file']))
1648 self._def_entity(QAPISchemaInclude(fname, info))
1650 def _def_builtin_type(self, name, json_type, c_type):
1651 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1652 # Instantiating only the arrays that are actually used would
1653 # be nice, but we can't as long as their generated code
1654 # (qapi-builtin-types.[ch]) may be shared by some other
1656 self._make_array_type(name, None)
1658 def _def_predefineds(self):
1659 for t in [('str', 'string', 'char' + pointer_suffix),
1660 ('number', 'number', 'double'),
1661 ('int', 'int', 'int64_t'),
1662 ('int8', 'int', 'int8_t'),
1663 ('int16', 'int', 'int16_t'),
1664 ('int32', 'int', 'int32_t'),
1665 ('int64', 'int', 'int64_t'),
1666 ('uint8', 'int', 'uint8_t'),
1667 ('uint16', 'int', 'uint16_t'),
1668 ('uint32', 'int', 'uint32_t'),
1669 ('uint64', 'int', 'uint64_t'),
1670 ('size', 'int', 'uint64_t'),
1671 ('bool', 'boolean', 'bool'),
1672 ('any', 'value', 'QObject' + pointer_suffix),
1673 ('null', 'null', 'QNull' + pointer_suffix)]:
1674 self._def_builtin_type(*t)
1675 self.the_empty_object_type = QAPISchemaObjectType(
1676 'q_empty', None, None, None, None, [], None)
1677 self._def_entity(self.the_empty_object_type)
1679 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1681 qtype_values = self._make_enum_members([{'name': n} for n in qtypes])
1683 self._def_entity(QAPISchemaEnumType('QType', None, None, None,
1684 qtype_values, 'QTYPE'))
1686 def _make_enum_members(self, values):
1687 return [QAPISchemaMember(v['name'], v.get('if')) for v in values]
1689 def _make_implicit_enum_type(self, name, info, ifcond, values):
1690 # See also QAPISchemaObjectTypeMember._pretty_owner()
1691 name = name + 'Kind' # Use namespace reserved by add_name()
1692 self._def_entity(QAPISchemaEnumType(
1693 name, info, None, ifcond, self._make_enum_members(values), None))
1696 def _make_array_type(self, element_type, info):
1697 name = element_type + 'List' # Use namespace reserved by add_name()
1698 if not self.lookup_type(name):
1699 self._def_entity(QAPISchemaArrayType(name, info, element_type))
1702 def _make_implicit_object_type(self, name, info, doc, ifcond,
1706 # See also QAPISchemaObjectTypeMember._pretty_owner()
1707 name = 'q_obj_%s-%s' % (name, role)
1708 typ = self.lookup_entity(name, QAPISchemaObjectType)
1710 # The implicit object type has multiple users. This can
1711 # happen only for simple unions' implicit wrapper types.
1712 # Its ifcond should be the disjunction of its user's
1713 # ifconds. Not implemented. Instead, we always pass the
1714 # wrapped type's ifcond, which is trivially the same for all
1715 # users. It's also necessary for the wrapper to compile.
1716 # But it's not tight: the disjunction need not imply it. We
1717 # may end up compiling useless wrapper types.
1718 # TODO kill simple unions or implement the disjunction
1719 assert ifcond == typ._ifcond # pylint: disable=protected-access
1721 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
1722 None, members, None))
1725 def _def_enum_type(self, expr, info, doc):
1728 prefix = expr.get('prefix')
1729 ifcond = expr.get('if')
1730 self._def_entity(QAPISchemaEnumType(
1731 name, info, doc, ifcond,
1732 self._make_enum_members(data), prefix))
1734 def _make_member(self, name, typ, ifcond, info):
1736 if name.startswith('*'):
1739 if isinstance(typ, list):
1740 assert len(typ) == 1
1741 typ = self._make_array_type(typ[0], info)
1742 return QAPISchemaObjectTypeMember(name, typ, optional, ifcond)
1744 def _make_members(self, data, info):
1745 return [self._make_member(key, value['type'], value.get('if'), info)
1746 for (key, value) in data.items()]
1748 def _def_struct_type(self, expr, info, doc):
1749 name = expr['struct']
1750 base = expr.get('base')
1752 ifcond = expr.get('if')
1753 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
1754 self._make_members(data, info),
1757 def _make_variant(self, case, typ, ifcond):
1758 return QAPISchemaObjectTypeVariant(case, typ, ifcond)
1760 def _make_simple_variant(self, case, typ, ifcond, info):
1761 if isinstance(typ, list):
1762 assert len(typ) == 1
1763 typ = self._make_array_type(typ[0], info)
1764 typ = self._make_implicit_object_type(
1765 typ, info, None, self.lookup_type(typ),
1766 'wrapper', [self._make_member('data', typ, None, info)])
1767 return QAPISchemaObjectTypeVariant(case, typ, ifcond)
1769 def _def_union_type(self, expr, info, doc):
1770 name = expr['union']
1772 base = expr.get('base')
1773 ifcond = expr.get('if')
1774 tag_name = expr.get('discriminator')
1776 if isinstance(base, dict):
1777 base = self._make_implicit_object_type(
1778 name, info, doc, ifcond,
1779 'base', self._make_members(base, info))
1781 variants = [self._make_variant(key, value['type'], value.get('if'))
1782 for (key, value) in data.items()]
1785 variants = [self._make_simple_variant(key, value['type'],
1786 value.get('if'), info)
1787 for (key, value) in data.items()]
1788 enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
1789 typ = self._make_implicit_enum_type(name, info, ifcond, enum)
1790 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1791 members = [tag_member]
1793 QAPISchemaObjectType(name, info, doc, ifcond, base, members,
1794 QAPISchemaObjectTypeVariants(tag_name,
1798 def _def_alternate_type(self, expr, info, doc):
1799 name = expr['alternate']
1801 ifcond = expr.get('if')
1802 variants = [self._make_variant(key, value['type'], value.get('if'))
1803 for (key, value) in data.items()]
1804 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
1806 QAPISchemaAlternateType(name, info, doc, ifcond,
1807 QAPISchemaObjectTypeVariants(None,
1811 def _def_command(self, expr, info, doc):
1812 name = expr['command']
1813 data = expr.get('data')
1814 rets = expr.get('returns')
1815 gen = expr.get('gen', True)
1816 success_response = expr.get('success-response', True)
1817 boxed = expr.get('boxed', False)
1818 allow_oob = expr.get('allow-oob', False)
1819 allow_preconfig = expr.get('allow-preconfig', False)
1820 ifcond = expr.get('if')
1821 if isinstance(data, OrderedDict):
1822 data = self._make_implicit_object_type(
1823 name, info, doc, ifcond, 'arg', self._make_members(data, info))
1824 if isinstance(rets, list):
1825 assert len(rets) == 1
1826 rets = self._make_array_type(rets[0], info)
1827 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
1828 gen, success_response,
1829 boxed, allow_oob, allow_preconfig))
1831 def _def_event(self, expr, info, doc):
1832 name = expr['event']
1833 data = expr.get('data')
1834 boxed = expr.get('boxed', False)
1835 ifcond = expr.get('if')
1836 if isinstance(data, OrderedDict):
1837 data = self._make_implicit_object_type(
1838 name, info, doc, ifcond, 'arg', self._make_members(data, info))
1839 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
1841 def _def_exprs(self, exprs):
1842 for expr_elem in exprs:
1843 expr = expr_elem['expr']
1844 info = expr_elem['info']
1845 doc = expr_elem.get('doc')
1847 self._def_enum_type(expr, info, doc)
1848 elif 'struct' in expr:
1849 self._def_struct_type(expr, info, doc)
1850 elif 'union' in expr:
1851 self._def_union_type(expr, info, doc)
1852 elif 'alternate' in expr:
1853 self._def_alternate_type(expr, info, doc)
1854 elif 'command' in expr:
1855 self._def_command(expr, info, doc)
1856 elif 'event' in expr:
1857 self._def_event(expr, info, doc)
1858 elif 'include' in expr:
1859 self._def_include(expr, info, doc)
1864 for ent in self._entity_list:
1867 def visit(self, visitor):
1868 visitor.visit_begin(self)
1870 for entity in self._entity_list:
1871 if visitor.visit_needed(entity):
1872 if entity.module != module:
1873 module = entity.module
1874 visitor.visit_module(module)
1875 entity.visit(visitor)
1880 # Code generation helpers
1883 def camel_case(name):
1887 if ch in ['_', '-']:
1890 new_name += ch.upper()
1893 new_name += ch.lower()
1897 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1898 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1899 # ENUM24_Name -> ENUM24_NAME
1900 def camel_to_upper(value):
1901 c_fun_str = c_name(value, False)
1906 length = len(c_fun_str)
1907 for i in range(length):
1909 # When c is upper and no '_' appears before, do more checks
1910 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
1911 if i < length - 1 and c_fun_str[i + 1].islower():
1913 elif c_fun_str[i - 1].isdigit():
1916 return new_name.lstrip('_').upper()
1919 def c_enum_const(type_name, const_name, prefix=None):
1920 if prefix is not None:
1922 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
1925 if hasattr(str, 'maketrans'):
1926 c_name_trans = str.maketrans('.-', '__')
1928 c_name_trans = string.maketrans('.-', '__')
1931 # Map @name to a valid C identifier.
1932 # If @protect, avoid returning certain ticklish identifiers (like
1933 # C keywords) by prepending 'q_'.
1935 # Used for converting 'name' from a 'name':'type' qapi definition
1936 # into a generated struct member, as well as converting type names
1937 # into substrings of a generated C function name.
1938 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1939 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1940 def c_name(name, protect=True):
1941 # ANSI X3J11/88-090, 3.1.1
1942 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
1943 'default', 'do', 'double', 'else', 'enum', 'extern',
1944 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1945 'return', 'short', 'signed', 'sizeof', 'static',
1946 'struct', 'switch', 'typedef', 'union', 'unsigned',
1947 'void', 'volatile', 'while'])
1948 # ISO/IEC 9899:1999, 6.4.1
1949 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1950 # ISO/IEC 9899:2011, 6.4.1
1951 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1952 '_Noreturn', '_Static_assert', '_Thread_local'])
1953 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1955 gcc_words = set(['asm', 'typeof'])
1956 # C++ ISO/IEC 14882:2003 2.11
1957 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1958 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1959 'namespace', 'new', 'operator', 'private', 'protected',
1960 'public', 'reinterpret_cast', 'static_cast', 'template',
1961 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1962 'using', 'virtual', 'wchar_t',
1963 # alternative representations
1964 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1965 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1966 # namespace pollution:
1967 polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
1968 name = name.translate(c_name_trans)
1969 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1970 | cpp_words | polluted_words):
1975 eatspace = '\033EATSPACE.'
1976 pointer_suffix = ' *' + eatspace
1979 def genindent(count):
1981 for _ in range(count):
1989 def push_indent(indent_amount=4):
1991 indent_level += indent_amount
1994 def pop_indent(indent_amount=4):
1996 indent_level -= indent_amount
1999 # Generate @code with @kwds interpolated.
2000 # Obey indent_level, and strip eatspace.
2001 def cgen(code, **kwds):
2004 indent = genindent(indent_level)
2005 # re.subn() lacks flags support before Python 2.7, use re.compile()
2006 raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
2009 return re.sub(re.escape(eatspace) + r' *', '', raw)
2012 def mcgen(code, **kwds):
2015 return cgen(code, **kwds)
2018 def guardname(filename):
2019 return re.sub(r'[^A-Za-z0-9_]', '_', filename).upper()
2022 def guardstart(name):
2028 name=guardname(name))
2034 #endif /* %(name)s */
2036 name=guardname(name))
2048 def gen_endif(ifcond):
2050 for ifc in reversed(ifcond):
2052 #endif /* %(cond)s */
2057 def _wrap_ifcond(ifcond, before, after):
2059 return after # suppress empty #if ... #endif
2061 assert after.startswith(before)
2063 added = after[len(before):]
2064 if added[0] == '\n':
2067 out += gen_if(ifcond)
2069 out += gen_endif(ifcond)
2073 def gen_enum_lookup(name, members, prefix=None):
2076 const QEnumLookup %(c_name)s_lookup = {
2077 .array = (const char *const[]) {
2079 c_name=c_name(name))
2081 ret += gen_if(m.ifcond)
2082 index = c_enum_const(name, m.name, prefix)
2084 [%(index)s] = "%(name)s",
2086 index=index, name=m.name)
2087 ret += gen_endif(m.ifcond)
2091 .size = %(max_index)s
2094 max_index=c_enum_const(name, '_MAX', prefix))
2098 def gen_enum(name, members, prefix=None):
2099 # append automatically generated _MAX value
2100 enum_members = members + [QAPISchemaMember('_MAX')]
2104 typedef enum %(c_name)s {
2106 c_name=c_name(name))
2108 for m in enum_members:
2109 ret += gen_if(m.ifcond)
2113 c_enum=c_enum_const(name, m.name, prefix))
2114 ret += gen_endif(m.ifcond)
2119 c_name=c_name(name))
2123 #define %(c_name)s_str(val) \\
2124 qapi_enum_lookup(&%(c_name)s_lookup, (val))
2126 extern const QEnumLookup %(c_name)s_lookup;
2128 c_name=c_name(name))
2132 def build_params(arg_type, boxed, extra=None):
2137 ret += '%s arg' % arg_type.c_param_type()
2140 assert not arg_type.variants
2141 for memb in arg_type.members:
2145 ret += 'bool has_%s, ' % c_name(memb.name)
2146 ret += '%s %s' % (memb.type.c_param_type(),
2150 return ret if ret else 'void'
2154 # Accumulate and write output
2157 class QAPIGen(object):
2163 def preamble_add(self, text):
2164 self._preamble += text
2166 def add(self, text):
2169 def get_content(self, fname=None):
2170 return (self._top(fname) + self._preamble + self._body
2171 + self._bottom(fname))
2173 def _top(self, fname):
2176 def _bottom(self, fname):
2179 def write(self, output_dir, fname):
2180 pathname = os.path.join(output_dir, fname)
2181 dir = os.path.dirname(pathname)
2185 except os.error as e:
2186 if e.errno != errno.EEXIST:
2188 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
2189 if sys.version_info[0] >= 3:
2190 f = open(fd, 'r+', encoding='utf-8')
2192 f = os.fdopen(fd, 'r+')
2193 text = self.get_content(fname)
2194 oldtext = f.read(len(text) + 1)
2203 def ifcontext(ifcond, *args):
2204 """A 'with' statement context manager to wrap with start_if()/end_if()
2206 *args: any number of QAPIGenCCode
2210 with ifcontext(ifcond, self._genh, self._genc):
2211 modify self._genh and self._genc ...
2213 Is equivalent to calling::
2215 self._genh.start_if(ifcond)
2216 self._genc.start_if(ifcond)
2217 modify self._genh and self._genc ...
2222 arg.start_if(ifcond)
2228 class QAPIGenCCode(QAPIGen):
2231 QAPIGen.__init__(self)
2232 self._start_if = None
2234 def start_if(self, ifcond):
2235 assert self._start_if is None
2236 self._start_if = (ifcond, self._body, self._preamble)
2239 assert self._start_if
2241 self._start_if = None
2243 def _wrap_ifcond(self):
2244 self._body = _wrap_ifcond(self._start_if[0],
2245 self._start_if[1], self._body)
2246 self._preamble = _wrap_ifcond(self._start_if[0],
2247 self._start_if[2], self._preamble)
2249 def get_content(self, fname=None):
2250 assert self._start_if is None
2251 return QAPIGen.get_content(self, fname)
2254 class QAPIGenC(QAPIGenCCode):
2256 def __init__(self, blurb, pydoc):
2257 QAPIGenCCode.__init__(self)
2259 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
2262 def _top(self, fname):
2264 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2271 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2272 * See the COPYING.LIB file in the top-level directory.
2276 blurb=self._blurb, copyright=self._copyright)
2278 def _bottom(self, fname):
2281 /* Dummy declaration to prevent empty .o file */
2282 char dummy_%(name)s;
2287 class QAPIGenH(QAPIGenC):
2289 def _top(self, fname):
2290 return QAPIGenC._top(self, fname) + guardstart(fname)
2292 def _bottom(self, fname):
2293 return guardend(fname)
2296 class QAPIGenDoc(QAPIGen):
2298 def _top(self, fname):
2299 return (QAPIGen._top(self, fname)
2300 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2303 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
2305 def __init__(self, prefix, what, blurb, pydoc):
2306 self._prefix = prefix
2308 self._genc = QAPIGenC(blurb, pydoc)
2309 self._genh = QAPIGenH(blurb, pydoc)
2311 def write(self, output_dir):
2312 self._genc.write(output_dir, self._prefix + self._what + '.c')
2313 self._genh.write(output_dir, self._prefix + self._what + '.h')
2316 class QAPISchemaModularCVisitor(QAPISchemaVisitor):
2318 def __init__(self, prefix, what, blurb, pydoc):
2319 self._prefix = prefix
2324 self._main_module = None
2326 def _module_basename(self, what, name):
2328 return re.sub(r'-', '-builtin-', what)
2329 basename = os.path.join(os.path.dirname(name),
2330 self._prefix + what)
2331 if name == self._main_module:
2333 return basename + '-' + os.path.splitext(os.path.basename(name))[0]
2335 def _add_module(self, name, blurb):
2336 if self._main_module is None and name is not None:
2337 self._main_module = name
2338 genc = QAPIGenC(blurb, self._pydoc)
2339 genh = QAPIGenH(blurb, self._pydoc)
2340 self._module[name] = (genc, genh)
2341 self._set_module(name)
2343 def _set_module(self, name):
2344 self._genc, self._genh = self._module[name]
2346 def write(self, output_dir, opt_builtins=False):
2347 for name in self._module:
2348 if name is None and not opt_builtins:
2350 basename = self._module_basename(self._what, name)
2351 (genc, genh) = self._module[name]
2352 genc.write(output_dir, basename + '.c')
2353 genh.write(output_dir, basename + '.h')
2355 def _begin_module(self, name):
2358 def visit_module(self, name):
2359 if name in self._module:
2360 self._set_module(name)
2362 self._add_module(name, self._blurb)
2363 self._begin_module(name)
2365 def visit_include(self, name, info):
2366 basename = self._module_basename(self._what, name)
2367 self._genh.preamble_add(mcgen('''
2368 #include "%(basename)s.h"