tcg: add early clober modifier in atomic16_cmpxchg on aarch64
[qemu/ar7.git] / scripts / qapi / common.py
blobc89edc0cb0df52603b967eb4567a9cf8bf7fbb0a
2 # QAPI helper library
4 # Copyright IBM, Corp. 2011
5 # Copyright (c) 2013-2018 Red Hat Inc.
7 # Authors:
8 # Anthony Liguori <aliguori@us.ibm.com>
9 # Markus Armbruster <armbru@redhat.com>
11 # This work is licensed under the terms of the GNU GPL, version 2.
12 # See the COPYING file in the top-level directory.
14 from __future__ import print_function
15 from contextlib import contextmanager
16 import errno
17 import os
18 import re
19 import string
20 import sys
21 from collections import OrderedDict
23 builtin_types = {
24 'null': 'QTYPE_QNULL',
25 'str': 'QTYPE_QSTRING',
26 'int': 'QTYPE_QNUM',
27 'number': 'QTYPE_QNUM',
28 'bool': 'QTYPE_QBOOL',
29 'int8': 'QTYPE_QNUM',
30 'int16': 'QTYPE_QNUM',
31 'int32': 'QTYPE_QNUM',
32 'int64': 'QTYPE_QNUM',
33 'uint8': 'QTYPE_QNUM',
34 'uint16': 'QTYPE_QNUM',
35 'uint32': 'QTYPE_QNUM',
36 'uint64': 'QTYPE_QNUM',
37 'size': 'QTYPE_QNUM',
38 'any': None, # any QType possible, actually
39 'QType': 'QTYPE_QSTRING',
42 # Are documentation comments required?
43 doc_required = False
45 # Whitelist of commands allowed to return a non-dictionary
46 returns_whitelist = []
48 # Whitelist of entities allowed to violate case conventions
49 name_case_whitelist = []
51 enum_types = {}
52 struct_types = {}
53 union_types = {}
54 all_names = {}
57 # Parsing the schema into expressions
61 def error_path(parent):
62 res = ''
63 while parent:
64 res = ('In file included from %s:%d:\n' % (parent['file'],
65 parent['line'])) + res
66 parent = parent['parent']
67 return res
70 class QAPIError(Exception):
71 def __init__(self, fname, line, col, incl_info, msg):
72 Exception.__init__(self)
73 self.fname = fname
74 self.line = line
75 self.col = col
76 self.info = incl_info
77 self.msg = msg
79 def __str__(self):
80 loc = '%s:%d' % (self.fname, self.line)
81 if self.col is not None:
82 loc += ':%s' % self.col
83 return error_path(self.info) + '%s: %s' % (loc, self.msg)
86 class QAPIParseError(QAPIError):
87 def __init__(self, parser, msg):
88 col = 1
89 for ch in parser.src[parser.line_pos:parser.pos]:
90 if ch == '\t':
91 col = (col + 7) % 8 + 1
92 else:
93 col += 1
94 QAPIError.__init__(self, parser.fname, parser.line, col,
95 parser.incl_info, msg)
98 class QAPISemError(QAPIError):
99 def __init__(self, info, msg):
100 QAPIError.__init__(self, info['file'], info['line'], None,
101 info['parent'], msg)
104 class QAPIDoc(object):
105 class Section(object):
106 def __init__(self, name=None):
107 # optional section name (argument/member or section name)
108 self.name = name
109 # the list of lines for this section
110 self.text = ''
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)
118 self.member = None
120 def connect(self, member):
121 self.member = 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
129 self.info = info
130 self.symbol = None
131 self.body = QAPIDoc.Section()
132 # dict mapping parameter name to ArgSection
133 self.args = OrderedDict()
134 # a list of Section
135 self.sections = []
136 # the current section
137 self._section = self.body
139 def has_section(self, name):
140 """Return True if we have a section with this name."""
141 for i in self.sections:
142 if i.name == name:
143 return True
144 return False
146 def append(self, line):
147 """Parse a comment line and add it to the documentation."""
148 line = line[1:]
149 if not line:
150 self._append_freeform(line)
151 return
153 if line[0] != ' ':
154 raise QAPIParseError(self._parser, "Missing space after #")
155 line = line[1:]
157 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
158 # recognized, and get silently treated as ordinary text
159 if self.symbol:
160 self._append_symbol_line(line)
161 elif not self.body.text and line.startswith('@'):
162 if not line.endswith(':'):
163 raise QAPIParseError(self._parser, "Line should end with :")
164 self.symbol = line[1:-1]
165 # FIXME invalid names other than the empty string aren't flagged
166 if not self.symbol:
167 raise QAPIParseError(self._parser, "Invalid name")
168 else:
169 self._append_freeform(line)
171 def end_comment(self):
172 self._end_section()
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
182 'Note:', 'Notes:',
183 'Example:', 'Examples:',
184 'TODO:'):
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
192 if not name:
193 raise QAPIParseError(self._parser, "Invalid parameter name")
194 if name in self.args:
195 raise QAPIParseError(self._parser,
196 "'%s' parameter name duplicated" % name)
197 if self.sections:
198 raise QAPIParseError(self._parser,
199 "'@%s:' can't follow '%s' section"
200 % (name, self.sections[0].name))
201 self._end_section()
202 self._section = QAPIDoc.ArgSection(name)
203 self.args[name] = self._section
205 def _start_section(self, name=None):
206 if name in ('Returns', 'Since') and self.has_section(name):
207 raise QAPIParseError(self._parser,
208 "Duplicated '%s' section" % name)
209 self._end_section()
210 self._section = QAPIDoc.Section(name)
211 self.sections.append(self._section)
213 def _end_section(self):
214 if self._section:
215 text = self._section.text = self._section.text.strip()
216 if self._section.name and (not text or text.isspace()):
217 raise QAPIParseError(self._parser, "Empty doc section '%s'"
218 % self._section.name)
219 self._section = None
221 def _append_freeform(self, line):
222 in_arg = isinstance(self._section, QAPIDoc.ArgSection)
223 if (in_arg and self._section.text.endswith('\n\n')
224 and line and not line[0].isspace()):
225 self._start_section()
226 if (in_arg or not self._section.name
227 or not self._section.name.startswith('Example')):
228 line = line.strip()
229 match = re.match(r'(@\S+:)', line)
230 if match:
231 raise QAPIParseError(self._parser,
232 "'%s' not allowed in free-form documentation"
233 % match.group(1))
234 self._section.append(line)
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")
247 def check(self):
248 bogus = [name for name, section in self.args.items()
249 if not section.member]
250 if bogus:
251 raise QAPISemError(
252 self.info,
253 "The following documented members are not in "
254 "the declaration: %s" % ", ".join(bogus))
257 class QAPISchemaParser(object):
259 def __init__(self, fp, previously_included=[], incl_info=None):
260 self.fname = fp.name
261 previously_included.append(os.path.abspath(fp.name))
262 self.incl_info = incl_info
263 self.src = fp.read()
264 if self.src == '' or self.src[-1] != '\n':
265 self.src += '\n'
266 self.cursor = 0
267 self.line = 1
268 self.line_pos = 0
269 self.exprs = []
270 self.docs = []
271 self.accept()
272 cur_doc = None
274 while self.tok is not None:
275 info = {'file': self.fname, 'line': self.line,
276 'parent': self.incl_info}
277 if self.tok == '#':
278 self.reject_expr_doc(cur_doc)
279 cur_doc = self.get_doc(info)
280 self.docs.append(cur_doc)
281 continue
283 expr = self.get_expr(False)
284 if 'include' in expr:
285 self.reject_expr_doc(cur_doc)
286 if len(expr) != 1:
287 raise QAPISemError(info, "Invalid 'include' directive")
288 include = expr['include']
289 if not isinstance(include, str):
290 raise QAPISemError(info,
291 "Value of 'include' must be a string")
292 incl_fname = os.path.join(os.path.dirname(self.fname),
293 include)
294 self.exprs.append({'expr': {'include': incl_fname},
295 'info': info})
296 exprs_include = self._include(include, info, incl_fname,
297 previously_included)
298 if exprs_include:
299 self.exprs.extend(exprs_include.exprs)
300 self.docs.extend(exprs_include.docs)
301 elif "pragma" in expr:
302 self.reject_expr_doc(cur_doc)
303 if len(expr) != 1:
304 raise QAPISemError(info, "Invalid 'pragma' directive")
305 pragma = expr['pragma']
306 if not isinstance(pragma, dict):
307 raise QAPISemError(
308 info, "Value of 'pragma' must be a dictionary")
309 for name, value in pragma.items():
310 self._pragma(name, value, info)
311 else:
312 expr_elem = {'expr': expr,
313 'info': info}
314 if cur_doc:
315 if not cur_doc.symbol:
316 raise QAPISemError(
317 cur_doc.info, "Expression documentation required")
318 expr_elem['doc'] = cur_doc
319 self.exprs.append(expr_elem)
320 cur_doc = None
321 self.reject_expr_doc(cur_doc)
323 @staticmethod
324 def reject_expr_doc(doc):
325 if doc and doc.symbol:
326 raise QAPISemError(
327 doc.info,
328 "Documentation for '%s' is not followed by the definition"
329 % doc.symbol)
331 def _include(self, include, info, incl_fname, previously_included):
332 incl_abs_fname = os.path.abspath(incl_fname)
333 # catch inclusion cycle
334 inf = info
335 while inf:
336 if incl_abs_fname == os.path.abspath(inf['file']):
337 raise QAPISemError(info, "Inclusion loop for %s" % include)
338 inf = inf['parent']
340 # skip multiple include of the same file
341 if incl_abs_fname in previously_included:
342 return None
344 try:
345 if sys.version_info[0] >= 3:
346 fobj = open(incl_fname, 'r', encoding='utf-8')
347 else:
348 fobj = open(incl_fname, 'r')
349 except IOError as e:
350 raise QAPISemError(info, '%s: %s' % (e.strerror, incl_fname))
351 return QAPISchemaParser(fobj, previously_included, info)
353 def _pragma(self, name, value, info):
354 global doc_required, returns_whitelist, name_case_whitelist
355 if name == 'doc-required':
356 if not isinstance(value, bool):
357 raise QAPISemError(info,
358 "Pragma 'doc-required' must be boolean")
359 doc_required = value
360 elif name == 'returns-whitelist':
361 if (not isinstance(value, list)
362 or any([not isinstance(elt, str) for elt in value])):
363 raise QAPISemError(info,
364 "Pragma returns-whitelist must be"
365 " a list of strings")
366 returns_whitelist = value
367 elif name == 'name-case-whitelist':
368 if (not isinstance(value, list)
369 or any([not isinstance(elt, str) for elt in value])):
370 raise QAPISemError(info,
371 "Pragma name-case-whitelist must be"
372 " a list of strings")
373 name_case_whitelist = value
374 else:
375 raise QAPISemError(info, "Unknown pragma '%s'" % name)
377 def accept(self, skip_comment=True):
378 while True:
379 self.tok = self.src[self.cursor]
380 self.pos = self.cursor
381 self.cursor += 1
382 self.val = None
384 if self.tok == '#':
385 if self.src[self.cursor] == '#':
386 # Start of doc comment
387 skip_comment = False
388 self.cursor = self.src.find('\n', self.cursor)
389 if not skip_comment:
390 self.val = self.src[self.pos:self.cursor]
391 return
392 elif self.tok in '{}:,[]':
393 return
394 elif self.tok == "'":
395 string = ''
396 esc = False
397 while True:
398 ch = self.src[self.cursor]
399 self.cursor += 1
400 if ch == '\n':
401 raise QAPIParseError(self, 'Missing terminating "\'"')
402 if esc:
403 if ch == 'b':
404 string += '\b'
405 elif ch == 'f':
406 string += '\f'
407 elif ch == 'n':
408 string += '\n'
409 elif ch == 'r':
410 string += '\r'
411 elif ch == 't':
412 string += '\t'
413 elif ch == 'u':
414 value = 0
415 for _ in range(0, 4):
416 ch = self.src[self.cursor]
417 self.cursor += 1
418 if ch not in '0123456789abcdefABCDEF':
419 raise QAPIParseError(self,
420 '\\u escape needs 4 '
421 'hex digits')
422 value = (value << 4) + int(ch, 16)
423 # If Python 2 and 3 didn't disagree so much on
424 # how to handle Unicode, then we could allow
425 # Unicode string defaults. But most of QAPI is
426 # ASCII-only, so we aren't losing much for now.
427 if not value or value > 0x7f:
428 raise QAPIParseError(self,
429 'For now, \\u escape '
430 'only supports non-zero '
431 'values up to \\u007f')
432 string += chr(value)
433 elif ch in '\\/\'"':
434 string += ch
435 else:
436 raise QAPIParseError(self,
437 "Unknown escape \\%s" % ch)
438 esc = False
439 elif ch == '\\':
440 esc = True
441 elif ch == "'":
442 self.val = string
443 return
444 else:
445 string += ch
446 elif self.src.startswith('true', self.pos):
447 self.val = True
448 self.cursor += 3
449 return
450 elif self.src.startswith('false', self.pos):
451 self.val = False
452 self.cursor += 4
453 return
454 elif self.src.startswith('null', self.pos):
455 self.val = None
456 self.cursor += 3
457 return
458 elif self.tok == '\n':
459 if self.cursor == len(self.src):
460 self.tok = None
461 return
462 self.line += 1
463 self.line_pos = self.cursor
464 elif not self.tok.isspace():
465 raise QAPIParseError(self, 'Stray "%s"' % self.tok)
467 def get_members(self):
468 expr = OrderedDict()
469 if self.tok == '}':
470 self.accept()
471 return expr
472 if self.tok != "'":
473 raise QAPIParseError(self, 'Expected string or "}"')
474 while True:
475 key = self.val
476 self.accept()
477 if self.tok != ':':
478 raise QAPIParseError(self, 'Expected ":"')
479 self.accept()
480 if key in expr:
481 raise QAPIParseError(self, 'Duplicate key "%s"' % key)
482 expr[key] = self.get_expr(True)
483 if self.tok == '}':
484 self.accept()
485 return expr
486 if self.tok != ',':
487 raise QAPIParseError(self, 'Expected "," or "}"')
488 self.accept()
489 if self.tok != "'":
490 raise QAPIParseError(self, 'Expected string')
492 def get_values(self):
493 expr = []
494 if self.tok == ']':
495 self.accept()
496 return expr
497 if self.tok not in "{['tfn":
498 raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
499 'boolean or "null"')
500 while True:
501 expr.append(self.get_expr(True))
502 if self.tok == ']':
503 self.accept()
504 return expr
505 if self.tok != ',':
506 raise QAPIParseError(self, 'Expected "," or "]"')
507 self.accept()
509 def get_expr(self, nested):
510 if self.tok != '{' and not nested:
511 raise QAPIParseError(self, 'Expected "{"')
512 if self.tok == '{':
513 self.accept()
514 expr = self.get_members()
515 elif self.tok == '[':
516 self.accept()
517 expr = self.get_values()
518 elif self.tok in "'tfn":
519 expr = self.val
520 self.accept()
521 else:
522 raise QAPIParseError(self, 'Expected "{", "[", string, '
523 'boolean or "null"')
524 return expr
526 def get_doc(self, info):
527 if self.val != '##':
528 raise QAPIParseError(self, "Junk after '##' at start of "
529 "documentation comment")
531 doc = QAPIDoc(self, info)
532 self.accept(False)
533 while self.tok == '#':
534 if self.val.startswith('##'):
535 # End of doc comment
536 if self.val != '##':
537 raise QAPIParseError(self, "Junk after '##' at end of "
538 "documentation comment")
539 doc.end_comment()
540 self.accept()
541 return doc
542 else:
543 doc.append(self.val)
544 self.accept(False)
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):
558 return base
559 base_struct_define = struct_types.get(base)
560 if not base_struct_define:
561 return None
562 return base_struct_define['data']
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:
570 return 'QTYPE_QDICT'
571 elif qapi_type in enum_types:
572 return 'QTYPE_QSTRING'
573 elif qapi_type in union_types:
574 return 'QTYPE_QDICT'
575 return None
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):
585 return None
587 base_members = find_base_members(base)
588 if not base_members:
589 return None
591 discriminator_value = base_members.get(discriminator)
592 if not discriminator_value:
593 return None
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,
606 enum_member=False):
607 global valid_name
608 membername = name
610 if not isinstance(name, str):
611 raise QAPISemError(info, "%s requires a string name" % source)
612 if name.startswith('*'):
613 membername = name[1:]
614 if not allow_optional:
615 raise QAPISemError(info, "%s does not allow optional name '%s'"
616 % (source, name))
617 # Enum members can start with a digit, because the generated C
618 # code always prefixes it with the enum name
619 if enum_member and membername[0].isdigit():
620 membername = 'D' + membername
621 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
622 # and 'q_obj_*' implicit type names.
623 if not valid_name.match(membername) or \
624 c_name(membername, False).startswith('q_'):
625 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
628 def add_name(name, info, meta, implicit=False):
629 global all_names
630 check_name(info, "'%s'" % meta, name)
631 # FIXME should reject names that differ only in '_' vs. '.'
632 # vs. '-', because they're liable to clash in generated C.
633 if name in all_names:
634 raise QAPISemError(info, "%s '%s' is already defined"
635 % (all_names[name], name))
636 if not implicit and (name.endswith('Kind') or name.endswith('List')):
637 raise QAPISemError(info, "%s '%s' should not end in '%s'"
638 % (meta, name, name[-4:]))
639 all_names[name] = meta
642 def check_if(expr, info):
644 def check_if_str(ifcond, info):
645 if not isinstance(ifcond, str):
646 raise QAPISemError(
647 info, "'if' condition must be a string or a list of strings")
648 if ifcond == '':
649 raise QAPISemError(info, "'if' condition '' makes no sense")
651 ifcond = expr.get('if')
652 if ifcond is None:
653 return
654 if isinstance(ifcond, list):
655 if ifcond == []:
656 raise QAPISemError(info, "'if' condition [] is useless")
657 for elt in ifcond:
658 check_if_str(elt, info)
659 else:
660 check_if_str(ifcond, info)
663 def check_type(info, source, value, allow_array=False,
664 allow_dict=False, allow_optional=False,
665 allow_metas=[]):
666 global all_names
668 if value is None:
669 return
671 # Check if array type for value is okay
672 if isinstance(value, list):
673 if not allow_array:
674 raise QAPISemError(info, "%s cannot be an array" % source)
675 if len(value) != 1 or not isinstance(value[0], str):
676 raise QAPISemError(info,
677 "%s: array type must contain single type name" %
678 source)
679 value = value[0]
681 # Check if type name for value is okay
682 if isinstance(value, str):
683 if value not in all_names:
684 raise QAPISemError(info, "%s uses unknown type '%s'"
685 % (source, value))
686 if not all_names[value] in allow_metas:
687 raise QAPISemError(info, "%s cannot use %s type '%s'" %
688 (source, all_names[value], value))
689 return
691 if not allow_dict:
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'"
704 % (source, key))
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',
712 'enum'])
715 def check_command(expr, info):
716 name = expr['command']
717 boxed = expr.get('boxed', False)
719 args_meta = ['struct']
720 if boxed:
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):
734 name = expr['event']
735 boxed = expr.get('boxed', False)
737 meta = ['struct']
738 if boxed:
739 meta += ['union', 'alternate']
740 check_type(info, "'data' for event '%s'" % name,
741 expr.get('data'), allow_dict=not boxed, allow_optional=True,
742 allow_metas=meta)
745 def enum_get_names(expr):
746 return [e['name'] for e in expr['data']]
749 def check_union(expr, info):
750 name = expr['union']
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:
759 enum_define = None
760 allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
761 if base is not None:
762 raise QAPISemError(info, "Simple union '%s' must not have a base" %
763 name)
765 # Else, it's a flat union.
766 else:
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'])
771 if not base:
772 raise QAPISemError(info, "Flat union '%s' must have a base"
773 % name)
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,
780 discriminator)
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 "
785 "struct '%s'"
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
794 if not enum_define:
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),
809 value['type'],
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.
814 if enum_define:
815 if key not in enum_get_names(enum_define):
816 raise QAPISemError(info,
817 "Discriminator value '%s' is not found in "
818 "enum '%s'"
819 % (key, enum_define['enum']))
822 def check_alternate(expr, info):
823 name = expr['alternate']
824 members = expr['data']
825 types_seen = {}
827 # Check every branch; require at least two branches
828 if len(members) < 2:
829 raise QAPISemError(info,
830 "Alternate '%s' should have at least two branches "
831 "in 'data'" % name)
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'])
837 typ = value['type']
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)
843 if not qtype:
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)
849 if enum_expr:
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')
855 else:
856 conflicting.add('QTYPE_QNUM')
857 conflicting.add('QTYPE_QBOOL')
858 for qt in conflicting:
859 if qt in types_seen:
860 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
861 "be distinguished from member '%s'"
862 % (name, key, types_seen[qt]))
863 types_seen[qt] = key
866 def check_enum(expr, info):
867 name = expr['enum']
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'],
883 enum_member=True)
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):
898 def pprint(elems):
899 return ', '.join("'" + e + "'" for e in sorted(elems))
901 missing = set(required) - set(keys)
902 if missing:
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
908 if unknown:
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']
917 name = expr[meta]
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"
927 % (key, meta, name))
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"
932 % (key, meta, name))
933 if key == 'if':
934 check_if(expr, info)
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):
947 continue
948 members[key] = {'type': arg}
951 def check_exprs(exprs):
952 global all_names
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:
965 continue
967 if not doc and doc_required:
968 raise QAPISemError(info,
969 "Expression missing documentation comment")
971 if 'enum' in expr:
972 meta = 'enum'
973 check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix'])
974 normalize_enum(expr)
975 enum_types[expr[meta]] = expr
976 elif 'union' in expr:
977 meta = 'union'
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:
984 meta = 'alternate'
985 check_keys(expr_elem, 'alternate', ['data'], ['if'])
986 normalize_members(expr['data'])
987 elif 'struct' in expr:
988 meta = 'struct'
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:
993 meta = 'command'
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:
999 meta = 'event'
1000 check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if'])
1001 normalize_members(expr.get('data'))
1002 else:
1003 raise QAPISemError(expr_elem['info'],
1004 "Expression is missing metatype")
1005 name = expr[meta]
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:
1016 continue
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']
1021 else:
1022 continue
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:
1033 continue
1034 if 'enum' 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)
1046 else:
1047 assert False, 'unexpected meta type'
1049 if doc:
1050 doc.check_expr(expr)
1052 return exprs
1056 # Schema compiler frontend
1059 def listify_cond(ifcond):
1060 if not ifcond:
1061 return []
1062 if not isinstance(ifcond, list):
1063 return [ifcond]
1064 return ifcond
1067 class QAPISchemaEntity(object):
1068 def __init__(self, name, info, doc, ifcond=None):
1069 assert name is None or isinstance(name, str)
1070 self.name = name
1071 self.module = None
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
1076 # such place).
1077 self.info = info
1078 self.doc = doc
1079 self._ifcond = ifcond # self.ifcond is set only after .check()
1081 def c_name(self):
1082 return c_name(self.name)
1084 def check(self, schema):
1085 if isinstance(self._ifcond, QAPISchemaType):
1086 # inherit the condition from a type
1087 typ = self._ifcond
1088 typ.check(schema)
1089 self.ifcond = typ.ifcond
1090 else:
1091 self.ifcond = listify_cond(self._ifcond)
1093 def is_implicit(self):
1094 return not self.info
1096 def visit(self, visitor):
1097 pass
1100 class QAPISchemaVisitor(object):
1101 def visit_begin(self, schema):
1102 pass
1104 def visit_end(self):
1105 pass
1107 def visit_module(self, fname):
1108 pass
1110 def visit_needed(self, entity):
1111 # Default to visiting everything
1112 return True
1114 def visit_include(self, fname, info):
1115 pass
1117 def visit_builtin_type(self, name, info, json_type):
1118 pass
1120 def visit_enum_type(self, name, info, ifcond, members, prefix):
1121 pass
1123 def visit_array_type(self, name, info, ifcond, element_type):
1124 pass
1126 def visit_object_type(self, name, info, ifcond, base, members, variants):
1127 pass
1129 def visit_object_type_flat(self, name, info, ifcond, members, variants):
1130 pass
1132 def visit_alternate_type(self, name, info, ifcond, variants):
1133 pass
1135 def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
1136 success_response, boxed, allow_oob, allow_preconfig):
1137 pass
1139 def visit_event(self, name, info, ifcond, arg_type, boxed):
1140 pass
1143 class QAPISchemaInclude(QAPISchemaEntity):
1145 def __init__(self, fname, info):
1146 QAPISchemaEntity.__init__(self, None, info, None)
1147 self.fname = fname
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.
1156 def c_type(self):
1157 pass
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):
1168 pass
1170 def alternate_qtype(self):
1171 json2qtype = {
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())
1181 def doc_type(self):
1182 if self.is_implicit():
1183 return None
1184 return self.name
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',
1192 'value')
1193 self._json_type_name = json_type
1194 self._c_type_name = c_type
1196 def c_name(self):
1197 return self.name
1199 def c_type(self):
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
1210 def doc_type(self):
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)
1220 for m in members:
1221 assert isinstance(m, QAPISchemaMember)
1222 m.set_owner(name)
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)
1229 seen = {}
1230 for m in self.members:
1231 m.check_clash(self.info, seen)
1232 if self.doc:
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'
1239 def c_type(self):
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):
1246 return 'string'
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):
1268 return True
1270 def c_type(self):
1271 return c_name(self.name) + pointer_suffix
1273 def json_type(self):
1274 return 'array'
1276 def doc_type(self):
1277 elt_doc_type = self.element_type.doc_type()
1278 if not elt_doc_type:
1279 return None
1280 return 'array of ' + elt_doc_type
1282 def visit(self, visitor):
1283 visitor.visit_array_type(self.name, self.info, self.ifcond,
1284 self.element_type)
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)
1297 m.set_owner(name)
1298 if variants is not None:
1299 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1300 variants.set_owner(name)
1301 self._base_name = base
1302 self.base = None
1303 self.local_members = local_members
1304 self.variants = variants
1305 self.members = None
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)
1312 if self.members:
1313 return
1314 self.members = False # mark as being checked
1315 seen = OrderedDict()
1316 if self._base_name:
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:
1322 m.check(schema)
1323 m.check_clash(self.info, seen)
1324 if self.doc:
1325 self.doc.connect_member(m)
1326 self.members = seen.values()
1327 if self.variants:
1328 self.variants.check(schema, seen)
1329 assert self.variants.tag_member in self.members
1330 self.variants.check_clash(self.info, seen)
1331 if self.doc:
1332 self.doc.check()
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_')
1347 def is_empty(self):
1348 assert self.members is not None
1349 return not self.members and not self.variants
1351 def c_name(self):
1352 assert self.name != 'q_empty'
1353 return QAPISchemaType.c_name(self)
1355 def c_type(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):
1363 return 'object'
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):
1373 role = 'member'
1375 def __init__(self, name, ifcond=None):
1376 assert isinstance(name, str)
1377 self.name = name
1378 self.ifcond = listify_cond(ifcond)
1379 self.owner = None
1381 def set_owner(self, name):
1382 assert not self.owner
1383 self.owner = name
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())
1390 if cname in seen:
1391 raise QAPISemError(info, "%s collides with %s" %
1392 (self.describe(), seen[cname].describe()))
1393 seen[cname] = self
1395 def _pretty_owner(self):
1396 owner = self.owner
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
1400 owner = owner[6:]
1401 if owner.endswith('-arg'):
1402 return '(parameter of %s)' % owner[:-4]
1403 elif owner.endswith('-base'):
1404 return '(base of %s)' % owner[:-5]
1405 else:
1406 assert owner.endswith('-wrapper')
1407 # Unreachable and not implemented
1408 assert False
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)
1414 def describe(self):
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
1424 self.type = None
1425 self.optional = optional
1427 def check(self, schema):
1428 assert self.owner
1429 self.type = schema.lookup_type(self._type_name)
1430 assert self.type
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
1443 for v in variants:
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:
1451 v.set_owner(name)
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 m.ifcond)
1465 v.set_owner(self.tag_member.owner)
1466 self.variants.append(v)
1467 for v in self.variants:
1468 v.check(schema)
1469 # Union names must match enum values; alternate names are
1470 # checked separately. Use 'seen' to tell the two apart.
1471 if seen:
1472 assert v.name in self.tag_member.type.member_names()
1473 assert isinstance(v.type, QAPISchemaObjectType)
1474 v.type.check(schema)
1476 def check_clash(self, info, seen):
1477 for v in self.variants:
1478 # Reset seen map for each variant, since qapi names from one
1479 # branch do not affect another branch
1480 assert isinstance(v.type, QAPISchemaObjectType)
1481 v.type.check_clash(info, dict(seen))
1484 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1485 role = 'branch'
1487 def __init__(self, name, typ, ifcond=None):
1488 QAPISchemaObjectTypeMember.__init__(self, name, typ, False, ifcond)
1491 class QAPISchemaAlternateType(QAPISchemaType):
1492 def __init__(self, name, info, doc, ifcond, variants):
1493 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1494 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1495 assert variants.tag_member
1496 variants.set_owner(name)
1497 variants.tag_member.set_owner(self.name)
1498 self.variants = variants
1500 def check(self, schema):
1501 QAPISchemaType.check(self, schema)
1502 self.variants.tag_member.check(schema)
1503 # Not calling self.variants.check_clash(), because there's nothing
1504 # to clash with
1505 self.variants.check(schema, {})
1506 # Alternate branch names have no relation to the tag enum values;
1507 # so we have to check for potential name collisions ourselves.
1508 seen = {}
1509 for v in self.variants.variants:
1510 v.check_clash(self.info, seen)
1511 if self.doc:
1512 self.doc.connect_member(v)
1513 if self.doc:
1514 self.doc.check()
1516 def c_type(self):
1517 return c_name(self.name) + pointer_suffix
1519 def json_type(self):
1520 return 'value'
1522 def visit(self, visitor):
1523 visitor.visit_alternate_type(self.name, self.info, self.ifcond,
1524 self.variants)
1526 def is_empty(self):
1527 return False
1530 class QAPISchemaCommand(QAPISchemaEntity):
1531 def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
1532 gen, success_response, boxed, allow_oob, allow_preconfig):
1533 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1534 assert not arg_type or isinstance(arg_type, str)
1535 assert not ret_type or isinstance(ret_type, str)
1536 self._arg_type_name = arg_type
1537 self.arg_type = None
1538 self._ret_type_name = ret_type
1539 self.ret_type = None
1540 self.gen = gen
1541 self.success_response = success_response
1542 self.boxed = boxed
1543 self.allow_oob = allow_oob
1544 self.allow_preconfig = allow_preconfig
1546 def check(self, schema):
1547 QAPISchemaEntity.check(self, schema)
1548 if self._arg_type_name:
1549 self.arg_type = schema.lookup_type(self._arg_type_name)
1550 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1551 isinstance(self.arg_type, QAPISchemaAlternateType))
1552 self.arg_type.check(schema)
1553 if self.boxed:
1554 if self.arg_type.is_empty():
1555 raise QAPISemError(self.info,
1556 "Cannot use 'boxed' with empty type")
1557 else:
1558 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1559 assert not self.arg_type.variants
1560 elif self.boxed:
1561 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1562 if self._ret_type_name:
1563 self.ret_type = schema.lookup_type(self._ret_type_name)
1564 assert isinstance(self.ret_type, QAPISchemaType)
1566 def visit(self, visitor):
1567 visitor.visit_command(self.name, self.info, self.ifcond,
1568 self.arg_type, self.ret_type,
1569 self.gen, self.success_response,
1570 self.boxed, self.allow_oob,
1571 self.allow_preconfig)
1574 class QAPISchemaEvent(QAPISchemaEntity):
1575 def __init__(self, name, info, doc, ifcond, arg_type, boxed):
1576 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1577 assert not arg_type or isinstance(arg_type, str)
1578 self._arg_type_name = arg_type
1579 self.arg_type = None
1580 self.boxed = boxed
1582 def check(self, schema):
1583 QAPISchemaEntity.check(self, schema)
1584 if self._arg_type_name:
1585 self.arg_type = schema.lookup_type(self._arg_type_name)
1586 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1587 isinstance(self.arg_type, QAPISchemaAlternateType))
1588 self.arg_type.check(schema)
1589 if self.boxed:
1590 if self.arg_type.is_empty():
1591 raise QAPISemError(self.info,
1592 "Cannot use 'boxed' with empty type")
1593 else:
1594 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1595 assert not self.arg_type.variants
1596 elif self.boxed:
1597 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1599 def visit(self, visitor):
1600 visitor.visit_event(self.name, self.info, self.ifcond,
1601 self.arg_type, self.boxed)
1604 class QAPISchema(object):
1605 def __init__(self, fname):
1606 self._fname = fname
1607 if sys.version_info[0] >= 3:
1608 f = open(fname, 'r', encoding='utf-8')
1609 else:
1610 f = open(fname, 'r')
1611 parser = QAPISchemaParser(f)
1612 exprs = check_exprs(parser.exprs)
1613 self.docs = parser.docs
1614 self._entity_list = []
1615 self._entity_dict = {}
1616 self._predefining = True
1617 self._def_predefineds()
1618 self._predefining = False
1619 self._def_exprs(exprs)
1620 self.check()
1622 def _def_entity(self, ent):
1623 # Only the predefined types are allowed to not have info
1624 assert ent.info or self._predefining
1625 assert ent.name is None or ent.name not in self._entity_dict
1626 self._entity_list.append(ent)
1627 if ent.name is not None:
1628 self._entity_dict[ent.name] = ent
1629 if ent.info:
1630 ent.module = os.path.relpath(ent.info['file'],
1631 os.path.dirname(self._fname))
1633 def lookup_entity(self, name, typ=None):
1634 ent = self._entity_dict.get(name)
1635 if typ and not isinstance(ent, typ):
1636 return None
1637 return ent
1639 def lookup_type(self, name):
1640 return self.lookup_entity(name, QAPISchemaType)
1642 def _def_include(self, expr, info, doc):
1643 include = expr['include']
1644 assert doc is None
1645 main_info = info
1646 while main_info['parent']:
1647 main_info = main_info['parent']
1648 fname = os.path.relpath(include, os.path.dirname(main_info['file']))
1649 self._def_entity(QAPISchemaInclude(fname, info))
1651 def _def_builtin_type(self, name, json_type, c_type):
1652 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1653 # Instantiating only the arrays that are actually used would
1654 # be nice, but we can't as long as their generated code
1655 # (qapi-builtin-types.[ch]) may be shared by some other
1656 # schema.
1657 self._make_array_type(name, None)
1659 def _def_predefineds(self):
1660 for t in [('str', 'string', 'char' + pointer_suffix),
1661 ('number', 'number', 'double'),
1662 ('int', 'int', 'int64_t'),
1663 ('int8', 'int', 'int8_t'),
1664 ('int16', 'int', 'int16_t'),
1665 ('int32', 'int', 'int32_t'),
1666 ('int64', 'int', 'int64_t'),
1667 ('uint8', 'int', 'uint8_t'),
1668 ('uint16', 'int', 'uint16_t'),
1669 ('uint32', 'int', 'uint32_t'),
1670 ('uint64', 'int', 'uint64_t'),
1671 ('size', 'int', 'uint64_t'),
1672 ('bool', 'boolean', 'bool'),
1673 ('any', 'value', 'QObject' + pointer_suffix),
1674 ('null', 'null', 'QNull' + pointer_suffix)]:
1675 self._def_builtin_type(*t)
1676 self.the_empty_object_type = QAPISchemaObjectType(
1677 'q_empty', None, None, None, None, [], None)
1678 self._def_entity(self.the_empty_object_type)
1680 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1681 'qbool']
1682 qtype_values = self._make_enum_members([{'name': n} for n in qtypes])
1684 self._def_entity(QAPISchemaEnumType('QType', None, None, None,
1685 qtype_values, 'QTYPE'))
1687 def _make_enum_members(self, values):
1688 return [QAPISchemaMember(v['name'], v.get('if')) for v in values]
1690 def _make_implicit_enum_type(self, name, info, ifcond, values):
1691 # See also QAPISchemaObjectTypeMember._pretty_owner()
1692 name = name + 'Kind' # Use namespace reserved by add_name()
1693 self._def_entity(QAPISchemaEnumType(
1694 name, info, None, ifcond, self._make_enum_members(values), None))
1695 return name
1697 def _make_array_type(self, element_type, info):
1698 name = element_type + 'List' # Use namespace reserved by add_name()
1699 if not self.lookup_type(name):
1700 self._def_entity(QAPISchemaArrayType(name, info, element_type))
1701 return name
1703 def _make_implicit_object_type(self, name, info, doc, ifcond,
1704 role, members):
1705 if not members:
1706 return None
1707 # See also QAPISchemaObjectTypeMember._pretty_owner()
1708 name = 'q_obj_%s-%s' % (name, role)
1709 typ = self.lookup_entity(name, QAPISchemaObjectType)
1710 if typ:
1711 # The implicit object type has multiple users. This can
1712 # happen only for simple unions' implicit wrapper types.
1713 # Its ifcond should be the disjunction of its user's
1714 # ifconds. Not implemented. Instead, we always pass the
1715 # wrapped type's ifcond, which is trivially the same for all
1716 # users. It's also necessary for the wrapper to compile.
1717 # But it's not tight: the disjunction need not imply it. We
1718 # may end up compiling useless wrapper types.
1719 # TODO kill simple unions or implement the disjunction
1720 assert ifcond == typ._ifcond # pylint: disable=protected-access
1721 else:
1722 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
1723 None, members, None))
1724 return name
1726 def _def_enum_type(self, expr, info, doc):
1727 name = expr['enum']
1728 data = expr['data']
1729 prefix = expr.get('prefix')
1730 ifcond = expr.get('if')
1731 self._def_entity(QAPISchemaEnumType(
1732 name, info, doc, ifcond,
1733 self._make_enum_members(data), prefix))
1735 def _make_member(self, name, typ, ifcond, info):
1736 optional = False
1737 if name.startswith('*'):
1738 name = name[1:]
1739 optional = True
1740 if isinstance(typ, list):
1741 assert len(typ) == 1
1742 typ = self._make_array_type(typ[0], info)
1743 return QAPISchemaObjectTypeMember(name, typ, optional, ifcond)
1745 def _make_members(self, data, info):
1746 return [self._make_member(key, value['type'], value.get('if'), info)
1747 for (key, value) in data.items()]
1749 def _def_struct_type(self, expr, info, doc):
1750 name = expr['struct']
1751 base = expr.get('base')
1752 data = expr['data']
1753 ifcond = expr.get('if')
1754 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
1755 self._make_members(data, info),
1756 None))
1758 def _make_variant(self, case, typ, ifcond):
1759 return QAPISchemaObjectTypeVariant(case, typ, ifcond)
1761 def _make_simple_variant(self, case, typ, ifcond, info):
1762 if isinstance(typ, list):
1763 assert len(typ) == 1
1764 typ = self._make_array_type(typ[0], info)
1765 typ = self._make_implicit_object_type(
1766 typ, info, None, self.lookup_type(typ),
1767 'wrapper', [self._make_member('data', typ, None, info)])
1768 return QAPISchemaObjectTypeVariant(case, typ, ifcond)
1770 def _def_union_type(self, expr, info, doc):
1771 name = expr['union']
1772 data = expr['data']
1773 base = expr.get('base')
1774 ifcond = expr.get('if')
1775 tag_name = expr.get('discriminator')
1776 tag_member = None
1777 if isinstance(base, dict):
1778 base = self._make_implicit_object_type(
1779 name, info, doc, ifcond,
1780 'base', self._make_members(base, info))
1781 if tag_name:
1782 variants = [self._make_variant(key, value['type'], value.get('if'))
1783 for (key, value) in data.items()]
1784 members = []
1785 else:
1786 variants = [self._make_simple_variant(key, value['type'],
1787 value.get('if'), info)
1788 for (key, value) in data.items()]
1789 enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
1790 typ = self._make_implicit_enum_type(name, info, ifcond, enum)
1791 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1792 members = [tag_member]
1793 self._def_entity(
1794 QAPISchemaObjectType(name, info, doc, ifcond, base, members,
1795 QAPISchemaObjectTypeVariants(tag_name,
1796 tag_member,
1797 variants)))
1799 def _def_alternate_type(self, expr, info, doc):
1800 name = expr['alternate']
1801 data = expr['data']
1802 ifcond = expr.get('if')
1803 variants = [self._make_variant(key, value['type'], value.get('if'))
1804 for (key, value) in data.items()]
1805 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
1806 self._def_entity(
1807 QAPISchemaAlternateType(name, info, doc, ifcond,
1808 QAPISchemaObjectTypeVariants(None,
1809 tag_member,
1810 variants)))
1812 def _def_command(self, expr, info, doc):
1813 name = expr['command']
1814 data = expr.get('data')
1815 rets = expr.get('returns')
1816 gen = expr.get('gen', True)
1817 success_response = expr.get('success-response', True)
1818 boxed = expr.get('boxed', False)
1819 allow_oob = expr.get('allow-oob', False)
1820 allow_preconfig = expr.get('allow-preconfig', False)
1821 ifcond = expr.get('if')
1822 if isinstance(data, OrderedDict):
1823 data = self._make_implicit_object_type(
1824 name, info, doc, ifcond, 'arg', self._make_members(data, info))
1825 if isinstance(rets, list):
1826 assert len(rets) == 1
1827 rets = self._make_array_type(rets[0], info)
1828 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
1829 gen, success_response,
1830 boxed, allow_oob, allow_preconfig))
1832 def _def_event(self, expr, info, doc):
1833 name = expr['event']
1834 data = expr.get('data')
1835 boxed = expr.get('boxed', False)
1836 ifcond = expr.get('if')
1837 if isinstance(data, OrderedDict):
1838 data = self._make_implicit_object_type(
1839 name, info, doc, ifcond, 'arg', self._make_members(data, info))
1840 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
1842 def _def_exprs(self, exprs):
1843 for expr_elem in exprs:
1844 expr = expr_elem['expr']
1845 info = expr_elem['info']
1846 doc = expr_elem.get('doc')
1847 if 'enum' in expr:
1848 self._def_enum_type(expr, info, doc)
1849 elif 'struct' in expr:
1850 self._def_struct_type(expr, info, doc)
1851 elif 'union' in expr:
1852 self._def_union_type(expr, info, doc)
1853 elif 'alternate' in expr:
1854 self._def_alternate_type(expr, info, doc)
1855 elif 'command' in expr:
1856 self._def_command(expr, info, doc)
1857 elif 'event' in expr:
1858 self._def_event(expr, info, doc)
1859 elif 'include' in expr:
1860 self._def_include(expr, info, doc)
1861 else:
1862 assert False
1864 def check(self):
1865 for ent in self._entity_list:
1866 ent.check(self)
1868 def visit(self, visitor):
1869 visitor.visit_begin(self)
1870 module = None
1871 for entity in self._entity_list:
1872 if visitor.visit_needed(entity):
1873 if entity.module != module:
1874 module = entity.module
1875 visitor.visit_module(module)
1876 entity.visit(visitor)
1877 visitor.visit_end()
1881 # Code generation helpers
1884 def camel_case(name):
1885 new_name = ''
1886 first = True
1887 for ch in name:
1888 if ch in ['_', '-']:
1889 first = True
1890 elif first:
1891 new_name += ch.upper()
1892 first = False
1893 else:
1894 new_name += ch.lower()
1895 return new_name
1898 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1899 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1900 # ENUM24_Name -> ENUM24_NAME
1901 def camel_to_upper(value):
1902 c_fun_str = c_name(value, False)
1903 if value.isupper():
1904 return c_fun_str
1906 new_name = ''
1907 length = len(c_fun_str)
1908 for i in range(length):
1909 c = c_fun_str[i]
1910 # When c is upper and no '_' appears before, do more checks
1911 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
1912 if i < length - 1 and c_fun_str[i + 1].islower():
1913 new_name += '_'
1914 elif c_fun_str[i - 1].isdigit():
1915 new_name += '_'
1916 new_name += c
1917 return new_name.lstrip('_').upper()
1920 def c_enum_const(type_name, const_name, prefix=None):
1921 if prefix is not None:
1922 type_name = prefix
1923 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
1926 if hasattr(str, 'maketrans'):
1927 c_name_trans = str.maketrans('.-', '__')
1928 else:
1929 c_name_trans = string.maketrans('.-', '__')
1932 # Map @name to a valid C identifier.
1933 # If @protect, avoid returning certain ticklish identifiers (like
1934 # C keywords) by prepending 'q_'.
1936 # Used for converting 'name' from a 'name':'type' qapi definition
1937 # into a generated struct member, as well as converting type names
1938 # into substrings of a generated C function name.
1939 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1940 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1941 def c_name(name, protect=True):
1942 # ANSI X3J11/88-090, 3.1.1
1943 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
1944 'default', 'do', 'double', 'else', 'enum', 'extern',
1945 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1946 'return', 'short', 'signed', 'sizeof', 'static',
1947 'struct', 'switch', 'typedef', 'union', 'unsigned',
1948 'void', 'volatile', 'while'])
1949 # ISO/IEC 9899:1999, 6.4.1
1950 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1951 # ISO/IEC 9899:2011, 6.4.1
1952 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1953 '_Noreturn', '_Static_assert', '_Thread_local'])
1954 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1955 # excluding _.*
1956 gcc_words = set(['asm', 'typeof'])
1957 # C++ ISO/IEC 14882:2003 2.11
1958 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1959 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1960 'namespace', 'new', 'operator', 'private', 'protected',
1961 'public', 'reinterpret_cast', 'static_cast', 'template',
1962 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1963 'using', 'virtual', 'wchar_t',
1964 # alternative representations
1965 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1966 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1967 # namespace pollution:
1968 polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
1969 name = name.translate(c_name_trans)
1970 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1971 | cpp_words | polluted_words):
1972 return 'q_' + name
1973 return name
1976 eatspace = '\033EATSPACE.'
1977 pointer_suffix = ' *' + eatspace
1980 def genindent(count):
1981 ret = ''
1982 for _ in range(count):
1983 ret += ' '
1984 return ret
1987 indent_level = 0
1990 def push_indent(indent_amount=4):
1991 global indent_level
1992 indent_level += indent_amount
1995 def pop_indent(indent_amount=4):
1996 global indent_level
1997 indent_level -= indent_amount
2000 # Generate @code with @kwds interpolated.
2001 # Obey indent_level, and strip eatspace.
2002 def cgen(code, **kwds):
2003 raw = code % kwds
2004 if indent_level:
2005 indent = genindent(indent_level)
2006 # re.subn() lacks flags support before Python 2.7, use re.compile()
2007 raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
2008 indent, raw)
2009 raw = raw[0]
2010 return re.sub(re.escape(eatspace) + r' *', '', raw)
2013 def mcgen(code, **kwds):
2014 if code[0] == '\n':
2015 code = code[1:]
2016 return cgen(code, **kwds)
2019 def guardname(filename):
2020 return re.sub(r'[^A-Za-z0-9_]', '_', filename).upper()
2023 def guardstart(name):
2024 return mcgen('''
2025 #ifndef %(name)s
2026 #define %(name)s
2028 ''',
2029 name=guardname(name))
2032 def guardend(name):
2033 return mcgen('''
2035 #endif /* %(name)s */
2036 ''',
2037 name=guardname(name))
2040 def gen_if(ifcond):
2041 ret = ''
2042 for ifc in ifcond:
2043 ret += mcgen('''
2044 #if %(cond)s
2045 ''', cond=ifc)
2046 return ret
2049 def gen_endif(ifcond):
2050 ret = ''
2051 for ifc in reversed(ifcond):
2052 ret += mcgen('''
2053 #endif /* %(cond)s */
2054 ''', cond=ifc)
2055 return ret
2058 def _wrap_ifcond(ifcond, before, after):
2059 if before == after:
2060 return after # suppress empty #if ... #endif
2062 assert after.startswith(before)
2063 out = before
2064 added = after[len(before):]
2065 if added[0] == '\n':
2066 out += '\n'
2067 added = added[1:]
2068 out += gen_if(ifcond)
2069 out += added
2070 out += gen_endif(ifcond)
2071 return out
2074 def gen_enum_lookup(name, members, prefix=None):
2075 ret = mcgen('''
2077 const QEnumLookup %(c_name)s_lookup = {
2078 .array = (const char *const[]) {
2079 ''',
2080 c_name=c_name(name))
2081 for m in members:
2082 ret += gen_if(m.ifcond)
2083 index = c_enum_const(name, m.name, prefix)
2084 ret += mcgen('''
2085 [%(index)s] = "%(name)s",
2086 ''',
2087 index=index, name=m.name)
2088 ret += gen_endif(m.ifcond)
2090 ret += mcgen('''
2092 .size = %(max_index)s
2094 ''',
2095 max_index=c_enum_const(name, '_MAX', prefix))
2096 return ret
2099 def gen_enum(name, members, prefix=None):
2100 # append automatically generated _MAX value
2101 enum_members = members + [QAPISchemaMember('_MAX')]
2103 ret = mcgen('''
2105 typedef enum %(c_name)s {
2106 ''',
2107 c_name=c_name(name))
2109 for m in enum_members:
2110 ret += gen_if(m.ifcond)
2111 ret += mcgen('''
2112 %(c_enum)s,
2113 ''',
2114 c_enum=c_enum_const(name, m.name, prefix))
2115 ret += gen_endif(m.ifcond)
2117 ret += mcgen('''
2118 } %(c_name)s;
2119 ''',
2120 c_name=c_name(name))
2122 ret += mcgen('''
2124 #define %(c_name)s_str(val) \\
2125 qapi_enum_lookup(&%(c_name)s_lookup, (val))
2127 extern const QEnumLookup %(c_name)s_lookup;
2128 ''',
2129 c_name=c_name(name))
2130 return ret
2133 def build_params(arg_type, boxed, extra=None):
2134 ret = ''
2135 sep = ''
2136 if boxed:
2137 assert arg_type
2138 ret += '%s arg' % arg_type.c_param_type()
2139 sep = ', '
2140 elif arg_type:
2141 assert not arg_type.variants
2142 for memb in arg_type.members:
2143 ret += sep
2144 sep = ', '
2145 if memb.optional:
2146 ret += 'bool has_%s, ' % c_name(memb.name)
2147 ret += '%s %s' % (memb.type.c_param_type(),
2148 c_name(memb.name))
2149 if extra:
2150 ret += sep + extra
2151 return ret if ret else 'void'
2155 # Accumulate and write output
2158 class QAPIGen(object):
2160 def __init__(self):
2161 self._preamble = ''
2162 self._body = ''
2164 def preamble_add(self, text):
2165 self._preamble += text
2167 def add(self, text):
2168 self._body += text
2170 def get_content(self, fname=None):
2171 return (self._top(fname) + self._preamble + self._body
2172 + self._bottom(fname))
2174 def _top(self, fname):
2175 return ''
2177 def _bottom(self, fname):
2178 return ''
2180 def write(self, output_dir, fname):
2181 pathname = os.path.join(output_dir, fname)
2182 dir = os.path.dirname(pathname)
2183 if dir:
2184 try:
2185 os.makedirs(dir)
2186 except os.error as e:
2187 if e.errno != errno.EEXIST:
2188 raise
2189 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
2190 if sys.version_info[0] >= 3:
2191 f = open(fd, 'r+', encoding='utf-8')
2192 else:
2193 f = os.fdopen(fd, 'r+')
2194 text = self.get_content(fname)
2195 oldtext = f.read(len(text) + 1)
2196 if text != oldtext:
2197 f.seek(0)
2198 f.truncate(0)
2199 f.write(text)
2200 f.close()
2203 @contextmanager
2204 def ifcontext(ifcond, *args):
2205 """A 'with' statement context manager to wrap with start_if()/end_if()
2207 *args: any number of QAPIGenCCode
2209 Example::
2211 with ifcontext(ifcond, self._genh, self._genc):
2212 modify self._genh and self._genc ...
2214 Is equivalent to calling::
2216 self._genh.start_if(ifcond)
2217 self._genc.start_if(ifcond)
2218 modify self._genh and self._genc ...
2219 self._genh.end_if()
2220 self._genc.end_if()
2222 for arg in args:
2223 arg.start_if(ifcond)
2224 yield
2225 for arg in args:
2226 arg.end_if()
2229 class QAPIGenCCode(QAPIGen):
2231 def __init__(self):
2232 QAPIGen.__init__(self)
2233 self._start_if = None
2235 def start_if(self, ifcond):
2236 assert self._start_if is None
2237 self._start_if = (ifcond, self._body, self._preamble)
2239 def end_if(self):
2240 assert self._start_if
2241 self._wrap_ifcond()
2242 self._start_if = None
2244 def _wrap_ifcond(self):
2245 self._body = _wrap_ifcond(self._start_if[0],
2246 self._start_if[1], self._body)
2247 self._preamble = _wrap_ifcond(self._start_if[0],
2248 self._start_if[2], self._preamble)
2250 def get_content(self, fname=None):
2251 assert self._start_if is None
2252 return QAPIGen.get_content(self, fname)
2255 class QAPIGenC(QAPIGenCCode):
2257 def __init__(self, blurb, pydoc):
2258 QAPIGenCCode.__init__(self)
2259 self._blurb = blurb
2260 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
2261 re.MULTILINE))
2263 def _top(self, fname):
2264 return mcgen('''
2265 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2268 %(blurb)s
2270 * %(copyright)s
2272 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2273 * See the COPYING.LIB file in the top-level directory.
2276 ''',
2277 blurb=self._blurb, copyright=self._copyright)
2279 def _bottom(self, fname):
2280 return mcgen('''
2282 /* Dummy declaration to prevent empty .o file */
2283 char dummy_%(name)s;
2284 ''',
2285 name=c_name(fname))
2288 class QAPIGenH(QAPIGenC):
2290 def _top(self, fname):
2291 return QAPIGenC._top(self, fname) + guardstart(fname)
2293 def _bottom(self, fname):
2294 return guardend(fname)
2297 class QAPIGenDoc(QAPIGen):
2299 def _top(self, fname):
2300 return (QAPIGen._top(self, fname)
2301 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2304 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
2306 def __init__(self, prefix, what, blurb, pydoc):
2307 self._prefix = prefix
2308 self._what = what
2309 self._genc = QAPIGenC(blurb, pydoc)
2310 self._genh = QAPIGenH(blurb, pydoc)
2312 def write(self, output_dir):
2313 self._genc.write(output_dir, self._prefix + self._what + '.c')
2314 self._genh.write(output_dir, self._prefix + self._what + '.h')
2317 class QAPISchemaModularCVisitor(QAPISchemaVisitor):
2319 def __init__(self, prefix, what, blurb, pydoc):
2320 self._prefix = prefix
2321 self._what = what
2322 self._blurb = blurb
2323 self._pydoc = pydoc
2324 self._module = {}
2325 self._main_module = None
2327 def _module_basename(self, what, name):
2328 if name is None:
2329 return re.sub(r'-', '-builtin-', what)
2330 basename = os.path.join(os.path.dirname(name),
2331 self._prefix + what)
2332 if name == self._main_module:
2333 return basename
2334 return basename + '-' + os.path.splitext(os.path.basename(name))[0]
2336 def _add_module(self, name, blurb):
2337 if self._main_module is None and name is not None:
2338 self._main_module = name
2339 genc = QAPIGenC(blurb, self._pydoc)
2340 genh = QAPIGenH(blurb, self._pydoc)
2341 self._module[name] = (genc, genh)
2342 self._set_module(name)
2344 def _set_module(self, name):
2345 self._genc, self._genh = self._module[name]
2347 def write(self, output_dir, opt_builtins=False):
2348 for name in self._module:
2349 if name is None and not opt_builtins:
2350 continue
2351 basename = self._module_basename(self._what, name)
2352 (genc, genh) = self._module[name]
2353 genc.write(output_dir, basename + '.c')
2354 genh.write(output_dir, basename + '.h')
2356 def _begin_module(self, name):
2357 pass
2359 def visit_module(self, name):
2360 if name in self._module:
2361 self._set_module(name)
2362 return
2363 self._add_module(name, self._blurb)
2364 self._begin_module(name)
2366 def visit_include(self, name, info):
2367 basename = self._module_basename(self._what, name)
2368 self._genh.preamble_add(mcgen('''
2369 #include "%(basename)s.h"
2370 ''',
2371 basename=basename))