4 # Copyright IBM, Corp. 2011
5 # Copyright (c) 2013-2018 Red Hat Inc.
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
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 self
.module
= os
.path
.relpath(self
.info
['file'],
1094 os
.path
.dirname(schema
.fname
))
1096 def is_implicit(self
):
1097 return not self
.info
1099 def visit(self
, visitor
):
1103 class QAPISchemaVisitor(object):
1104 def visit_begin(self
, schema
):
1107 def visit_end(self
):
1110 def visit_module(self
, fname
):
1113 def visit_needed(self
, entity
):
1114 # Default to visiting everything
1117 def visit_include(self
, fname
, info
):
1120 def visit_builtin_type(self
, name
, info
, json_type
):
1123 def visit_enum_type(self
, name
, info
, ifcond
, members
, prefix
):
1126 def visit_array_type(self
, name
, info
, ifcond
, element_type
):
1129 def visit_object_type(self
, name
, info
, ifcond
, base
, members
, variants
):
1132 def visit_object_type_flat(self
, name
, info
, ifcond
, members
, variants
):
1135 def visit_alternate_type(self
, name
, info
, ifcond
, variants
):
1138 def visit_command(self
, name
, info
, ifcond
, arg_type
, ret_type
, gen
,
1139 success_response
, boxed
, allow_oob
, allow_preconfig
):
1142 def visit_event(self
, name
, info
, ifcond
, arg_type
, boxed
):
1146 class QAPISchemaInclude(QAPISchemaEntity
):
1148 def __init__(self
, fname
, info
):
1149 QAPISchemaEntity
.__init
__(self
, None, info
, None)
1152 def visit(self
, visitor
):
1153 visitor
.visit_include(self
.fname
, self
.info
)
1156 class QAPISchemaType(QAPISchemaEntity
):
1157 # Return the C type for common use.
1158 # For the types we commonly box, this is a pointer type.
1162 # Return the C type to be used in a parameter list.
1163 def c_param_type(self
):
1164 return self
.c_type()
1166 # Return the C type to be used where we suppress boxing.
1167 def c_unboxed_type(self
):
1168 return self
.c_type()
1170 def json_type(self
):
1173 def alternate_qtype(self
):
1175 'null': 'QTYPE_QNULL',
1176 'string': 'QTYPE_QSTRING',
1177 'number': 'QTYPE_QNUM',
1178 'int': 'QTYPE_QNUM',
1179 'boolean': 'QTYPE_QBOOL',
1180 'object': 'QTYPE_QDICT'
1182 return json2qtype
.get(self
.json_type())
1185 if self
.is_implicit():
1190 class QAPISchemaBuiltinType(QAPISchemaType
):
1191 def __init__(self
, name
, json_type
, c_type
):
1192 QAPISchemaType
.__init
__(self
, name
, None, None)
1193 assert not c_type
or isinstance(c_type
, str)
1194 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
1196 self
._json
_type
_name
= json_type
1197 self
._c
_type
_name
= c_type
1203 return self
._c
_type
_name
1205 def c_param_type(self
):
1206 if self
.name
== 'str':
1207 return 'const ' + self
._c
_type
_name
1208 return self
._c
_type
_name
1210 def json_type(self
):
1211 return self
._json
_type
_name
1214 return self
.json_type()
1216 def visit(self
, visitor
):
1217 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
1220 class QAPISchemaEnumType(QAPISchemaType
):
1221 def __init__(self
, name
, info
, doc
, ifcond
, members
, prefix
):
1222 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1224 assert isinstance(m
, QAPISchemaMember
)
1226 assert prefix
is None or isinstance(prefix
, str)
1227 self
.members
= members
1228 self
.prefix
= prefix
1230 def check(self
, schema
):
1231 QAPISchemaType
.check(self
, schema
)
1233 for m
in self
.members
:
1234 m
.check_clash(self
.info
, seen
)
1236 self
.doc
.connect_member(m
)
1238 def is_implicit(self
):
1239 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1240 return self
.name
.endswith('Kind') or self
.name
== 'QType'
1243 return c_name(self
.name
)
1245 def member_names(self
):
1246 return [m
.name
for m
in self
.members
]
1248 def json_type(self
):
1251 def visit(self
, visitor
):
1252 visitor
.visit_enum_type(self
.name
, self
.info
, self
.ifcond
,
1253 self
.members
, self
.prefix
)
1256 class QAPISchemaArrayType(QAPISchemaType
):
1257 def __init__(self
, name
, info
, element_type
):
1258 QAPISchemaType
.__init
__(self
, name
, info
, None, None)
1259 assert isinstance(element_type
, str)
1260 self
._element
_type
_name
= element_type
1261 self
.element_type
= None
1263 def check(self
, schema
):
1264 QAPISchemaType
.check(self
, schema
)
1265 self
.element_type
= schema
.lookup_type(self
._element
_type
_name
)
1266 assert self
.element_type
1267 self
.element_type
.check(schema
)
1268 self
.module
= self
.element_type
.module
1269 self
.ifcond
= self
.element_type
.ifcond
1271 def is_implicit(self
):
1275 return c_name(self
.name
) + pointer_suffix
1277 def json_type(self
):
1281 elt_doc_type
= self
.element_type
.doc_type()
1282 if not elt_doc_type
:
1284 return 'array of ' + elt_doc_type
1286 def visit(self
, visitor
):
1287 visitor
.visit_array_type(self
.name
, self
.info
, self
.ifcond
,
1291 class QAPISchemaObjectType(QAPISchemaType
):
1292 def __init__(self
, name
, info
, doc
, ifcond
,
1293 base
, local_members
, variants
):
1294 # struct has local_members, optional base, and no variants
1295 # flat union has base, variants, and no local_members
1296 # simple union has local_members, variants, and no base
1297 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1298 assert base
is None or isinstance(base
, str)
1299 for m
in local_members
:
1300 assert isinstance(m
, QAPISchemaObjectTypeMember
)
1302 if variants
is not None:
1303 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1304 variants
.set_owner(name
)
1305 self
._base
_name
= base
1307 self
.local_members
= local_members
1308 self
.variants
= variants
1311 def check(self
, schema
):
1312 QAPISchemaType
.check(self
, schema
)
1313 if self
.members
is False: # check for cycles
1314 raise QAPISemError(self
.info
,
1315 "Object %s contains itself" % self
.name
)
1318 self
.members
= False # mark as being checked
1319 seen
= OrderedDict()
1321 self
.base
= schema
.lookup_type(self
._base
_name
)
1322 assert isinstance(self
.base
, QAPISchemaObjectType
)
1323 self
.base
.check(schema
)
1324 self
.base
.check_clash(self
.info
, seen
)
1325 for m
in self
.local_members
:
1327 m
.check_clash(self
.info
, seen
)
1329 self
.doc
.connect_member(m
)
1330 self
.members
= seen
.values()
1332 self
.variants
.check(schema
, seen
)
1333 assert self
.variants
.tag_member
in self
.members
1334 self
.variants
.check_clash(self
.info
, seen
)
1338 # Check that the members of this type do not cause duplicate JSON members,
1339 # and update seen to track the members seen so far. Report any errors
1340 # on behalf of info, which is not necessarily self.info
1341 def check_clash(self
, info
, seen
):
1342 assert not self
.variants
# not implemented
1343 for m
in self
.members
:
1344 m
.check_clash(info
, seen
)
1346 def is_implicit(self
):
1347 # See QAPISchema._make_implicit_object_type(), as well as
1348 # _def_predefineds()
1349 return self
.name
.startswith('q_')
1352 assert self
.members
is not None
1353 return not self
.members
and not self
.variants
1356 assert self
.name
!= 'q_empty'
1357 return QAPISchemaType
.c_name(self
)
1360 assert not self
.is_implicit()
1361 return c_name(self
.name
) + pointer_suffix
1363 def c_unboxed_type(self
):
1364 return c_name(self
.name
)
1366 def json_type(self
):
1369 def visit(self
, visitor
):
1370 visitor
.visit_object_type(self
.name
, self
.info
, self
.ifcond
,
1371 self
.base
, self
.local_members
, self
.variants
)
1372 visitor
.visit_object_type_flat(self
.name
, self
.info
, self
.ifcond
,
1373 self
.members
, self
.variants
)
1376 class QAPISchemaMember(object):
1379 def __init__(self
, name
, ifcond
=None):
1380 assert isinstance(name
, str)
1382 self
.ifcond
= listify_cond(ifcond
)
1385 def set_owner(self
, name
):
1386 assert not self
.owner
1389 def check_clash(self
, info
, seen
):
1390 cname
= c_name(self
.name
)
1391 if cname
.lower() != cname
and self
.owner
not in name_case_whitelist
:
1392 raise QAPISemError(info
,
1393 "%s should not use uppercase" % self
.describe())
1395 raise QAPISemError(info
, "%s collides with %s" %
1396 (self
.describe(), seen
[cname
].describe()))
1399 def _pretty_owner(self
):
1401 if owner
.startswith('q_obj_'):
1402 # See QAPISchema._make_implicit_object_type() - reverse the
1403 # mapping there to create a nice human-readable description
1405 if owner
.endswith('-arg'):
1406 return '(parameter of %s)' % owner
[:-4]
1407 elif owner
.endswith('-base'):
1408 return '(base of %s)' % owner
[:-5]
1410 assert owner
.endswith('-wrapper')
1411 # Unreachable and not implemented
1413 if owner
.endswith('Kind'):
1414 # See QAPISchema._make_implicit_enum_type()
1415 return '(branch of %s)' % owner
[:-4]
1416 return '(%s of %s)' % (self
.role
, owner
)
1419 return "'%s' %s" % (self
.name
, self
._pretty
_owner
())
1422 class QAPISchemaObjectTypeMember(QAPISchemaMember
):
1423 def __init__(self
, name
, typ
, optional
, ifcond
=None):
1424 QAPISchemaMember
.__init
__(self
, name
, ifcond
)
1425 assert isinstance(typ
, str)
1426 assert isinstance(optional
, bool)
1427 self
._type
_name
= typ
1429 self
.optional
= optional
1431 def check(self
, schema
):
1433 self
.type = schema
.lookup_type(self
._type
_name
)
1437 class QAPISchemaObjectTypeVariants(object):
1438 def __init__(self
, tag_name
, tag_member
, variants
):
1439 # Flat unions pass tag_name but not tag_member.
1440 # Simple unions and alternates pass tag_member but not tag_name.
1441 # After check(), tag_member is always set, and tag_name remains
1442 # a reliable witness of being used by a flat union.
1443 assert bool(tag_member
) != bool(tag_name
)
1444 assert (isinstance(tag_name
, str) or
1445 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
1446 assert len(variants
) > 0
1448 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
1449 self
._tag
_name
= tag_name
1450 self
.tag_member
= tag_member
1451 self
.variants
= variants
1453 def set_owner(self
, name
):
1454 for v
in self
.variants
:
1457 def check(self
, schema
, seen
):
1458 if not self
.tag_member
: # flat union
1459 self
.tag_member
= seen
[c_name(self
._tag
_name
)]
1460 assert self
._tag
_name
== self
.tag_member
.name
1461 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
1462 if self
._tag
_name
: # flat union
1463 # branches that are not explicitly covered get an empty type
1464 cases
= set([v
.name
for v
in self
.variants
])
1465 for m
in self
.tag_member
.type.members
:
1466 if m
.name
not in cases
:
1467 v
= QAPISchemaObjectTypeVariant(m
.name
, 'q_empty',
1469 v
.set_owner(self
.tag_member
.owner
)
1470 self
.variants
.append(v
)
1471 for v
in self
.variants
:
1473 # Union names must match enum values; alternate names are
1474 # checked separately. Use 'seen' to tell the two apart.
1476 assert v
.name
in self
.tag_member
.type.member_names()
1477 assert isinstance(v
.type, QAPISchemaObjectType
)
1478 v
.type.check(schema
)
1480 def check_clash(self
, info
, seen
):
1481 for v
in self
.variants
:
1482 # Reset seen map for each variant, since qapi names from one
1483 # branch do not affect another branch
1484 assert isinstance(v
.type, QAPISchemaObjectType
)
1485 v
.type.check_clash(info
, dict(seen
))
1488 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
1491 def __init__(self
, name
, typ
, ifcond
=None):
1492 QAPISchemaObjectTypeMember
.__init
__(self
, name
, typ
, False, ifcond
)
1495 class QAPISchemaAlternateType(QAPISchemaType
):
1496 def __init__(self
, name
, info
, doc
, ifcond
, variants
):
1497 QAPISchemaType
.__init
__(self
, name
, info
, doc
, ifcond
)
1498 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1499 assert variants
.tag_member
1500 variants
.set_owner(name
)
1501 variants
.tag_member
.set_owner(self
.name
)
1502 self
.variants
= variants
1504 def check(self
, schema
):
1505 QAPISchemaType
.check(self
, schema
)
1506 self
.variants
.tag_member
.check(schema
)
1507 # Not calling self.variants.check_clash(), because there's nothing
1509 self
.variants
.check(schema
, {})
1510 # Alternate branch names have no relation to the tag enum values;
1511 # so we have to check for potential name collisions ourselves.
1513 for v
in self
.variants
.variants
:
1514 v
.check_clash(self
.info
, seen
)
1516 self
.doc
.connect_member(v
)
1521 return c_name(self
.name
) + pointer_suffix
1523 def json_type(self
):
1526 def visit(self
, visitor
):
1527 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.ifcond
,
1534 class QAPISchemaCommand(QAPISchemaEntity
):
1535 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, ret_type
,
1536 gen
, success_response
, boxed
, allow_oob
, allow_preconfig
):
1537 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
1538 assert not arg_type
or isinstance(arg_type
, str)
1539 assert not ret_type
or isinstance(ret_type
, str)
1540 self
._arg
_type
_name
= arg_type
1541 self
.arg_type
= None
1542 self
._ret
_type
_name
= ret_type
1543 self
.ret_type
= None
1545 self
.success_response
= success_response
1547 self
.allow_oob
= allow_oob
1548 self
.allow_preconfig
= allow_preconfig
1550 def check(self
, schema
):
1551 QAPISchemaEntity
.check(self
, schema
)
1552 if self
._arg
_type
_name
:
1553 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1554 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1555 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1556 self
.arg_type
.check(schema
)
1558 if self
.arg_type
.is_empty():
1559 raise QAPISemError(self
.info
,
1560 "Cannot use 'boxed' with empty type")
1562 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1563 assert not self
.arg_type
.variants
1565 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1566 if self
._ret
_type
_name
:
1567 self
.ret_type
= schema
.lookup_type(self
._ret
_type
_name
)
1568 assert isinstance(self
.ret_type
, QAPISchemaType
)
1570 def visit(self
, visitor
):
1571 visitor
.visit_command(self
.name
, self
.info
, self
.ifcond
,
1572 self
.arg_type
, self
.ret_type
,
1573 self
.gen
, self
.success_response
,
1574 self
.boxed
, self
.allow_oob
,
1575 self
.allow_preconfig
)
1578 class QAPISchemaEvent(QAPISchemaEntity
):
1579 def __init__(self
, name
, info
, doc
, ifcond
, arg_type
, boxed
):
1580 QAPISchemaEntity
.__init
__(self
, name
, info
, doc
, ifcond
)
1581 assert not arg_type
or isinstance(arg_type
, str)
1582 self
._arg
_type
_name
= arg_type
1583 self
.arg_type
= None
1586 def check(self
, schema
):
1587 QAPISchemaEntity
.check(self
, schema
)
1588 if self
._arg
_type
_name
:
1589 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1590 assert (isinstance(self
.arg_type
, QAPISchemaObjectType
) or
1591 isinstance(self
.arg_type
, QAPISchemaAlternateType
))
1592 self
.arg_type
.check(schema
)
1594 if self
.arg_type
.is_empty():
1595 raise QAPISemError(self
.info
,
1596 "Cannot use 'boxed' with empty type")
1598 assert not isinstance(self
.arg_type
, QAPISchemaAlternateType
)
1599 assert not self
.arg_type
.variants
1601 raise QAPISemError(self
.info
, "Use of 'boxed' requires 'data'")
1603 def visit(self
, visitor
):
1604 visitor
.visit_event(self
.name
, self
.info
, self
.ifcond
,
1605 self
.arg_type
, self
.boxed
)
1608 class QAPISchema(object):
1609 def __init__(self
, fname
):
1611 if sys
.version_info
[0] >= 3:
1612 f
= open(fname
, 'r', encoding
='utf-8')
1614 f
= open(fname
, 'r')
1615 parser
= QAPISchemaParser(f
)
1616 exprs
= check_exprs(parser
.exprs
)
1617 self
.docs
= parser
.docs
1618 self
._entity
_list
= []
1619 self
._entity
_dict
= {}
1620 self
._predefining
= True
1621 self
._def
_predefineds
()
1622 self
._predefining
= False
1623 self
._def
_exprs
(exprs
)
1626 def _def_entity(self
, ent
):
1627 # Only the predefined types are allowed to not have info
1628 assert ent
.info
or self
._predefining
1629 assert ent
.name
is None or ent
.name
not in self
._entity
_dict
1630 self
._entity
_list
.append(ent
)
1631 if ent
.name
is not None:
1632 self
._entity
_dict
[ent
.name
] = ent
1634 def lookup_entity(self
, name
, typ
=None):
1635 ent
= self
._entity
_dict
.get(name
)
1636 if typ
and not isinstance(ent
, typ
):
1640 def lookup_type(self
, name
):
1641 return self
.lookup_entity(name
, QAPISchemaType
)
1643 def _def_include(self
, expr
, info
, doc
):
1644 include
= expr
['include']
1647 while main_info
['parent']:
1648 main_info
= main_info
['parent']
1649 fname
= os
.path
.relpath(include
, os
.path
.dirname(main_info
['file']))
1650 self
._def
_entity
(QAPISchemaInclude(fname
, info
))
1652 def _def_builtin_type(self
, name
, json_type
, c_type
):
1653 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
, c_type
))
1654 # Instantiating only the arrays that are actually used would
1655 # be nice, but we can't as long as their generated code
1656 # (qapi-builtin-types.[ch]) may be shared by some other
1658 self
._make
_array
_type
(name
, None)
1660 def _def_predefineds(self
):
1661 for t
in [('str', 'string', 'char' + pointer_suffix
),
1662 ('number', 'number', 'double'),
1663 ('int', 'int', 'int64_t'),
1664 ('int8', 'int', 'int8_t'),
1665 ('int16', 'int', 'int16_t'),
1666 ('int32', 'int', 'int32_t'),
1667 ('int64', 'int', 'int64_t'),
1668 ('uint8', 'int', 'uint8_t'),
1669 ('uint16', 'int', 'uint16_t'),
1670 ('uint32', 'int', 'uint32_t'),
1671 ('uint64', 'int', 'uint64_t'),
1672 ('size', 'int', 'uint64_t'),
1673 ('bool', 'boolean', 'bool'),
1674 ('any', 'value', 'QObject' + pointer_suffix
),
1675 ('null', 'null', 'QNull' + pointer_suffix
)]:
1676 self
._def
_builtin
_type
(*t
)
1677 self
.the_empty_object_type
= QAPISchemaObjectType(
1678 'q_empty', None, None, None, None, [], None)
1679 self
._def
_entity
(self
.the_empty_object_type
)
1681 qtypes
= ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1683 qtype_values
= self
._make
_enum
_members
([{'name': n
} for n
in qtypes
])
1685 self
._def
_entity
(QAPISchemaEnumType('QType', None, None, None,
1686 qtype_values
, 'QTYPE'))
1688 def _make_enum_members(self
, values
):
1689 return [QAPISchemaMember(v
['name'], v
.get('if')) for v
in values
]
1691 def _make_implicit_enum_type(self
, name
, info
, ifcond
, values
):
1692 # See also QAPISchemaObjectTypeMember._pretty_owner()
1693 name
= name
+ 'Kind' # Use namespace reserved by add_name()
1694 self
._def
_entity
(QAPISchemaEnumType(
1695 name
, info
, None, ifcond
, self
._make
_enum
_members
(values
), None))
1698 def _make_array_type(self
, element_type
, info
):
1699 name
= element_type
+ 'List' # Use namespace reserved by add_name()
1700 if not self
.lookup_type(name
):
1701 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1704 def _make_implicit_object_type(self
, name
, info
, doc
, ifcond
,
1708 # See also QAPISchemaObjectTypeMember._pretty_owner()
1709 name
= 'q_obj_%s-%s' % (name
, role
)
1710 typ
= self
.lookup_entity(name
, QAPISchemaObjectType
)
1712 # The implicit object type has multiple users. This can
1713 # happen only for simple unions' implicit wrapper types.
1714 # Its ifcond should be the disjunction of its user's
1715 # ifconds. Not implemented. Instead, we always pass the
1716 # wrapped type's ifcond, which is trivially the same for all
1717 # users. It's also necessary for the wrapper to compile.
1718 # But it's not tight: the disjunction need not imply it. We
1719 # may end up compiling useless wrapper types.
1720 # TODO kill simple unions or implement the disjunction
1721 assert ifcond
== typ
._ifcond
# pylint: disable=protected-access
1723 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, ifcond
,
1724 None, members
, None))
1727 def _def_enum_type(self
, expr
, info
, doc
):
1730 prefix
= expr
.get('prefix')
1731 ifcond
= expr
.get('if')
1732 self
._def
_entity
(QAPISchemaEnumType(
1733 name
, info
, doc
, ifcond
,
1734 self
._make
_enum
_members
(data
), prefix
))
1736 def _make_member(self
, name
, typ
, ifcond
, info
):
1738 if name
.startswith('*'):
1741 if isinstance(typ
, list):
1742 assert len(typ
) == 1
1743 typ
= self
._make
_array
_type
(typ
[0], info
)
1744 return QAPISchemaObjectTypeMember(name
, typ
, optional
, ifcond
)
1746 def _make_members(self
, data
, info
):
1747 return [self
._make
_member
(key
, value
['type'], value
.get('if'), info
)
1748 for (key
, value
) in data
.items()]
1750 def _def_struct_type(self
, expr
, info
, doc
):
1751 name
= expr
['struct']
1752 base
= expr
.get('base')
1754 ifcond
= expr
.get('if')
1755 self
._def
_entity
(QAPISchemaObjectType(name
, info
, doc
, ifcond
, base
,
1756 self
._make
_members
(data
, info
),
1759 def _make_variant(self
, case
, typ
, ifcond
):
1760 return QAPISchemaObjectTypeVariant(case
, typ
, ifcond
)
1762 def _make_simple_variant(self
, case
, typ
, ifcond
, info
):
1763 if isinstance(typ
, list):
1764 assert len(typ
) == 1
1765 typ
= self
._make
_array
_type
(typ
[0], info
)
1766 typ
= self
._make
_implicit
_object
_type
(
1767 typ
, info
, None, self
.lookup_type(typ
),
1768 'wrapper', [self
._make
_member
('data', typ
, None, info
)])
1769 return QAPISchemaObjectTypeVariant(case
, typ
, ifcond
)
1771 def _def_union_type(self
, expr
, info
, doc
):
1772 name
= expr
['union']
1774 base
= expr
.get('base')
1775 ifcond
= expr
.get('if')
1776 tag_name
= expr
.get('discriminator')
1778 if isinstance(base
, dict):
1779 base
= self
._make
_implicit
_object
_type
(
1780 name
, info
, doc
, ifcond
,
1781 'base', self
._make
_members
(base
, info
))
1783 variants
= [self
._make
_variant
(key
, value
['type'], value
.get('if'))
1784 for (key
, value
) in data
.items()]
1787 variants
= [self
._make
_simple
_variant
(key
, value
['type'],
1788 value
.get('if'), info
)
1789 for (key
, value
) in data
.items()]
1790 enum
= [{'name': v
.name
, 'if': v
.ifcond
} for v
in variants
]
1791 typ
= self
._make
_implicit
_enum
_type
(name
, info
, ifcond
, enum
)
1792 tag_member
= QAPISchemaObjectTypeMember('type', typ
, False)
1793 members
= [tag_member
]
1795 QAPISchemaObjectType(name
, info
, doc
, ifcond
, base
, members
,
1796 QAPISchemaObjectTypeVariants(tag_name
,
1800 def _def_alternate_type(self
, expr
, info
, doc
):
1801 name
= expr
['alternate']
1803 ifcond
= expr
.get('if')
1804 variants
= [self
._make
_variant
(key
, value
['type'], value
.get('if'))
1805 for (key
, value
) in data
.items()]
1806 tag_member
= QAPISchemaObjectTypeMember('type', 'QType', False)
1808 QAPISchemaAlternateType(name
, info
, doc
, ifcond
,
1809 QAPISchemaObjectTypeVariants(None,
1813 def _def_command(self
, expr
, info
, doc
):
1814 name
= expr
['command']
1815 data
= expr
.get('data')
1816 rets
= expr
.get('returns')
1817 gen
= expr
.get('gen', True)
1818 success_response
= expr
.get('success-response', True)
1819 boxed
= expr
.get('boxed', False)
1820 allow_oob
= expr
.get('allow-oob', False)
1821 allow_preconfig
= expr
.get('allow-preconfig', False)
1822 ifcond
= expr
.get('if')
1823 if isinstance(data
, OrderedDict
):
1824 data
= self
._make
_implicit
_object
_type
(
1825 name
, info
, doc
, ifcond
, 'arg', self
._make
_members
(data
, info
))
1826 if isinstance(rets
, list):
1827 assert len(rets
) == 1
1828 rets
= self
._make
_array
_type
(rets
[0], info
)
1829 self
._def
_entity
(QAPISchemaCommand(name
, info
, doc
, ifcond
, data
, rets
,
1830 gen
, success_response
,
1831 boxed
, allow_oob
, allow_preconfig
))
1833 def _def_event(self
, expr
, info
, doc
):
1834 name
= expr
['event']
1835 data
= expr
.get('data')
1836 boxed
= expr
.get('boxed', False)
1837 ifcond
= expr
.get('if')
1838 if isinstance(data
, OrderedDict
):
1839 data
= self
._make
_implicit
_object
_type
(
1840 name
, info
, doc
, ifcond
, 'arg', self
._make
_members
(data
, info
))
1841 self
._def
_entity
(QAPISchemaEvent(name
, info
, doc
, ifcond
, data
, boxed
))
1843 def _def_exprs(self
, exprs
):
1844 for expr_elem
in exprs
:
1845 expr
= expr_elem
['expr']
1846 info
= expr_elem
['info']
1847 doc
= expr_elem
.get('doc')
1849 self
._def
_enum
_type
(expr
, info
, doc
)
1850 elif 'struct' in expr
:
1851 self
._def
_struct
_type
(expr
, info
, doc
)
1852 elif 'union' in expr
:
1853 self
._def
_union
_type
(expr
, info
, doc
)
1854 elif 'alternate' in expr
:
1855 self
._def
_alternate
_type
(expr
, info
, doc
)
1856 elif 'command' in expr
:
1857 self
._def
_command
(expr
, info
, doc
)
1858 elif 'event' in expr
:
1859 self
._def
_event
(expr
, info
, doc
)
1860 elif 'include' in expr
:
1861 self
._def
_include
(expr
, info
, doc
)
1866 for ent
in self
._entity
_list
:
1869 def visit(self
, visitor
):
1870 visitor
.visit_begin(self
)
1872 visitor
.visit_module(module
)
1873 for entity
in self
._entity
_list
:
1874 if visitor
.visit_needed(entity
):
1875 if entity
.module
!= module
:
1876 module
= entity
.module
1877 visitor
.visit_module(module
)
1878 entity
.visit(visitor
)
1883 # Code generation helpers
1886 def camel_case(name
):
1890 if ch
in ['_', '-']:
1893 new_name
+= ch
.upper()
1896 new_name
+= ch
.lower()
1900 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1901 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1902 # ENUM24_Name -> ENUM24_NAME
1903 def camel_to_upper(value
):
1904 c_fun_str
= c_name(value
, False)
1909 length
= len(c_fun_str
)
1910 for i
in range(length
):
1912 # When c is upper and no '_' appears before, do more checks
1913 if c
.isupper() and (i
> 0) and c_fun_str
[i
- 1] != '_':
1914 if i
< length
- 1 and c_fun_str
[i
+ 1].islower():
1916 elif c_fun_str
[i
- 1].isdigit():
1919 return new_name
.lstrip('_').upper()
1922 def c_enum_const(type_name
, const_name
, prefix
=None):
1923 if prefix
is not None:
1925 return camel_to_upper(type_name
) + '_' + c_name(const_name
, False).upper()
1928 if hasattr(str, 'maketrans'):
1929 c_name_trans
= str.maketrans('.-', '__')
1931 c_name_trans
= string
.maketrans('.-', '__')
1934 # Map @name to a valid C identifier.
1935 # If @protect, avoid returning certain ticklish identifiers (like
1936 # C keywords) by prepending 'q_'.
1938 # Used for converting 'name' from a 'name':'type' qapi definition
1939 # into a generated struct member, as well as converting type names
1940 # into substrings of a generated C function name.
1941 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1942 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1943 def c_name(name
, protect
=True):
1944 # ANSI X3J11/88-090, 3.1.1
1945 c89_words
= set(['auto', 'break', 'case', 'char', 'const', 'continue',
1946 'default', 'do', 'double', 'else', 'enum', 'extern',
1947 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1948 'return', 'short', 'signed', 'sizeof', 'static',
1949 'struct', 'switch', 'typedef', 'union', 'unsigned',
1950 'void', 'volatile', 'while'])
1951 # ISO/IEC 9899:1999, 6.4.1
1952 c99_words
= set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1953 # ISO/IEC 9899:2011, 6.4.1
1954 c11_words
= set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1955 '_Noreturn', '_Static_assert', '_Thread_local'])
1956 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1958 gcc_words
= set(['asm', 'typeof'])
1959 # C++ ISO/IEC 14882:2003 2.11
1960 cpp_words
= set(['bool', 'catch', 'class', 'const_cast', 'delete',
1961 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1962 'namespace', 'new', 'operator', 'private', 'protected',
1963 'public', 'reinterpret_cast', 'static_cast', 'template',
1964 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1965 'using', 'virtual', 'wchar_t',
1966 # alternative representations
1967 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1968 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1969 # namespace pollution:
1970 polluted_words
= set(['unix', 'errno', 'mips', 'sparc', 'i386'])
1971 name
= name
.translate(c_name_trans
)
1972 if protect
and (name
in c89_words | c99_words | c11_words | gcc_words
1973 | cpp_words | polluted_words
):
1978 eatspace
= '\033EATSPACE.'
1979 pointer_suffix
= ' *' + eatspace
1982 def genindent(count
):
1984 for _
in range(count
):
1992 def push_indent(indent_amount
=4):
1994 indent_level
+= indent_amount
1997 def pop_indent(indent_amount
=4):
1999 indent_level
-= indent_amount
2002 # Generate @code with @kwds interpolated.
2003 # Obey indent_level, and strip eatspace.
2004 def cgen(code
, **kwds
):
2007 indent
= genindent(indent_level
)
2008 # re.subn() lacks flags support before Python 2.7, use re.compile()
2009 raw
= re
.subn(re
.compile(r
'^(?!(#|$))', re
.MULTILINE
),
2012 return re
.sub(re
.escape(eatspace
) + r
' *', '', raw
)
2015 def mcgen(code
, **kwds
):
2018 return cgen(code
, **kwds
)
2021 def c_fname(filename
):
2022 return re
.sub(r
'[^A-Za-z0-9_]', '_', filename
)
2025 def guardstart(name
):
2031 name
=c_fname(name
).upper())
2037 #endif /* %(name)s */
2039 name
=c_fname(name
).upper())
2051 def gen_endif(ifcond
):
2053 for ifc
in reversed(ifcond
):
2055 #endif /* %(cond)s */
2060 def _wrap_ifcond(ifcond
, before
, after
):
2062 return after
# suppress empty #if ... #endif
2064 assert after
.startswith(before
)
2066 added
= after
[len(before
):]
2067 if added
[0] == '\n':
2070 out
+= gen_if(ifcond
)
2072 out
+= gen_endif(ifcond
)
2076 def gen_enum_lookup(name
, members
, prefix
=None):
2079 const QEnumLookup %(c_name)s_lookup = {
2080 .array = (const char *const[]) {
2082 c_name
=c_name(name
))
2084 ret
+= gen_if(m
.ifcond
)
2085 index
= c_enum_const(name
, m
.name
, prefix
)
2087 [%(index)s] = "%(name)s",
2089 index
=index
, name
=m
.name
)
2090 ret
+= gen_endif(m
.ifcond
)
2094 .size = %(max_index)s
2097 max_index
=c_enum_const(name
, '_MAX', prefix
))
2101 def gen_enum(name
, members
, prefix
=None):
2102 # append automatically generated _MAX value
2103 enum_members
= members
+ [QAPISchemaMember('_MAX')]
2107 typedef enum %(c_name)s {
2109 c_name
=c_name(name
))
2111 for m
in enum_members
:
2112 ret
+= gen_if(m
.ifcond
)
2116 c_enum
=c_enum_const(name
, m
.name
, prefix
))
2117 ret
+= gen_endif(m
.ifcond
)
2122 c_name
=c_name(name
))
2126 #define %(c_name)s_str(val) \\
2127 qapi_enum_lookup(&%(c_name)s_lookup, (val))
2129 extern const QEnumLookup %(c_name)s_lookup;
2131 c_name
=c_name(name
))
2135 def build_params(arg_type
, boxed
, extra
=None):
2140 ret
+= '%s arg' % arg_type
.c_param_type()
2143 assert not arg_type
.variants
2144 for memb
in arg_type
.members
:
2148 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
2149 ret
+= '%s %s' % (memb
.type.c_param_type(),
2153 return ret
if ret
else 'void'
2157 # Accumulate and write output
2160 class QAPIGen(object):
2162 def __init__(self
, fname
):
2167 def preamble_add(self
, text
):
2168 self
._preamble
+= text
2170 def add(self
, text
):
2173 def get_content(self
):
2174 return self
._top
() + self
._preamble
+ self
._body
+ self
._bottom
()
2182 def write(self
, output_dir
):
2183 pathname
= os
.path
.join(output_dir
, self
.fname
)
2184 dir = os
.path
.dirname(pathname
)
2188 except os
.error
as e
:
2189 if e
.errno
!= errno
.EEXIST
:
2191 fd
= os
.open(pathname
, os
.O_RDWR | os
.O_CREAT
, 0o666)
2192 if sys
.version_info
[0] >= 3:
2193 f
= open(fd
, 'r+', encoding
='utf-8')
2195 f
= os
.fdopen(fd
, 'r+')
2196 text
= self
.get_content()
2197 oldtext
= f
.read(len(text
) + 1)
2206 def ifcontext(ifcond
, *args
):
2207 """A 'with' statement context manager to wrap with start_if()/end_if()
2209 *args: any number of QAPIGenCCode
2213 with ifcontext(ifcond, self._genh, self._genc):
2214 modify self._genh and self._genc ...
2216 Is equivalent to calling::
2218 self._genh.start_if(ifcond)
2219 self._genc.start_if(ifcond)
2220 modify self._genh and self._genc ...
2225 arg
.start_if(ifcond
)
2231 class QAPIGenCCode(QAPIGen
):
2233 def __init__(self
, fname
):
2234 QAPIGen
.__init
__(self
, fname
)
2235 self
._start
_if
= None
2237 def start_if(self
, ifcond
):
2238 assert self
._start
_if
is None
2239 self
._start
_if
= (ifcond
, self
._body
, self
._preamble
)
2242 assert self
._start
_if
2244 self
._start
_if
= None
2246 def _wrap_ifcond(self
):
2247 self
._body
= _wrap_ifcond(self
._start
_if
[0],
2248 self
._start
_if
[1], self
._body
)
2249 self
._preamble
= _wrap_ifcond(self
._start
_if
[0],
2250 self
._start
_if
[2], self
._preamble
)
2252 def get_content(self
):
2253 assert self
._start
_if
is None
2254 return QAPIGen
.get_content(self
)
2257 class QAPIGenC(QAPIGenCCode
):
2259 def __init__(self
, fname
, blurb
, pydoc
):
2260 QAPIGenCCode
.__init
__(self
, fname
)
2262 self
._copyright
= '\n * '.join(re
.findall(r
'^Copyright .*', pydoc
,
2267 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2274 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2275 * See the COPYING.LIB file in the top-level directory.
2279 blurb
=self
._blurb
, copyright
=self
._copyright
)
2284 /* Dummy declaration to prevent empty .o file */
2285 char qapi_dummy_%(name)s;
2287 name
=c_fname(self
.fname
))
2290 class QAPIGenH(QAPIGenC
):
2293 return QAPIGenC
._top
(self
) + guardstart(self
.fname
)
2296 return guardend(self
.fname
)
2299 class QAPIGenDoc(QAPIGen
):
2302 return (QAPIGen
._top
(self
)
2303 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2306 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor
):
2308 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2309 self
._prefix
= prefix
2311 self
._genc
= QAPIGenC(self
._prefix
+ self
._what
+ '.c',
2313 self
._genh
= QAPIGenH(self
._prefix
+ self
._what
+ '.h',
2316 def write(self
, output_dir
):
2317 self
._genc
.write(output_dir
)
2318 self
._genh
.write(output_dir
)
2321 class QAPISchemaModularCVisitor(QAPISchemaVisitor
):
2323 def __init__(self
, prefix
, what
, blurb
, pydoc
):
2324 self
._prefix
= prefix
2331 self
._main
_module
= None
2334 def _is_user_module(name
):
2335 return name
and not name
.startswith('./')
2338 def _is_builtin_module(name
):
2341 def _module_dirname(self
, what
, name
):
2342 if self
._is
_user
_module
(name
):
2343 return os
.path
.dirname(name
)
2346 def _module_basename(self
, what
, name
):
2347 ret
= '' if self
._is
_builtin
_module
(name
) else self
._prefix
2348 if self
._is
_user
_module
(name
):
2349 basename
= os
.path
.basename(name
)
2351 if name
!= self
._main
_module
:
2352 ret
+= '-' + os
.path
.splitext(basename
)[0]
2354 name
= name
[2:] if name
else 'builtin'
2355 ret
+= re
.sub(r
'-', '-' + name
+ '-', what
)
2358 def _module_filename(self
, what
, name
):
2359 return os
.path
.join(self
._module
_dirname
(what
, name
),
2360 self
._module
_basename
(what
, name
))
2362 def _add_module(self
, name
, blurb
):
2363 basename
= self
._module
_filename
(self
._what
, name
)
2364 genc
= QAPIGenC(basename
+ '.c', blurb
, self
._pydoc
)
2365 genh
= QAPIGenH(basename
+ '.h', blurb
, self
._pydoc
)
2366 self
._module
[name
] = (genc
, genh
)
2367 self
._set
_module
(name
)
2369 def _add_user_module(self
, name
, blurb
):
2370 assert self
._is
_user
_module
(name
)
2371 if self
._main
_module
is None:
2372 self
._main
_module
= name
2373 self
._add
_module
(name
, blurb
)
2375 def _add_system_module(self
, name
, blurb
):
2376 self
._add
_module
(name
and './' + name
, blurb
)
2378 def _set_module(self
, name
):
2379 self
._genc
, self
._genh
= self
._module
[name
]
2381 def write(self
, output_dir
, opt_builtins
=False):
2382 for name
in self
._module
:
2383 if self
._is
_builtin
_module
(name
) and not opt_builtins
:
2385 (genc
, genh
) = self
._module
[name
]
2386 genc
.write(output_dir
)
2387 genh
.write(output_dir
)
2389 def _begin_user_module(self
, name
):
2392 def visit_module(self
, name
):
2393 if name
in self
._module
:
2394 self
._set
_module
(name
)
2395 elif self
._is
_builtin
_module
(name
):
2396 # The built-in module has not been created. No code may
2401 self
._add
_user
_module
(name
, self
._blurb
)
2402 self
._begin
_user
_module
(name
)
2404 def visit_include(self
, name
, info
):
2405 relname
= os
.path
.relpath(self
._module
_filename
(self
._what
, name
),
2406 os
.path
.dirname(self
._genh
.fname
))
2407 self
._genh
.preamble_add(mcgen('''
2408 #include "%(relname)s.h"