]> git.proxmox.com Git - mirror_qemu.git/blob - scripts/qapi/common.py
cdfe2bf2a54be9dec4cf9303900ed3caa0ca61b0
[mirror_qemu.git] / scripts / qapi / common.py
1 #
2 # QAPI helper library
3 #
4 # Copyright IBM, Corp. 2011
5 # Copyright (c) 2013-2018 Red Hat Inc.
6 #
7 # Authors:
8 # Anthony Liguori <aliguori@us.ibm.com>
9 # Markus Armbruster <armbru@redhat.com>
10 #
11 # This work is licensed under the terms of the GNU GPL, version 2.
12 # See the COPYING file in the top-level directory.
13
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
22
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',
40 }
41
42 # Are documentation comments required?
43 doc_required = False
44
45 # Whitelist of commands allowed to return a non-dictionary
46 returns_whitelist = []
47
48 # Whitelist of entities allowed to violate case conventions
49 name_case_whitelist = []
50
51 enum_types = {}
52 struct_types = {}
53 union_types = {}
54 all_names = {}
55
56 #
57 # Parsing the schema into expressions
58 #
59
60
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
68
69
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
78
79 def __str__(self):
80 loc = '%s:%d' % (self.fname, self.line)
81 if self.col is not None:
82 loc += ':%s' % self.col
83 return error_path(self.info) + '%s: %s' % (loc, self.msg)
84
85
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)
96
97
98 class QAPISemError(QAPIError):
99 def __init__(self, info, msg):
100 QAPIError.__init__(self, info['file'], info['line'], None,
101 info['parent'], msg)
102
103
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 = ''
111
112 def append(self, line):
113 self.text += line.rstrip() + '\n'
114
115 class ArgSection(Section):
116 def __init__(self, name):
117 QAPIDoc.Section.__init__(self, name)
118 self.member = None
119
120 def connect(self, member):
121 self.member = member
122
123 def __init__(self, parser, info):
124 # self._parser is used to report errors with QAPIParseError. The
125 # resulting error position depends on the state of the parser.
126 # It happens to be the beginning of the comment. More or less
127 # servicable, but action at a distance.
128 self._parser = parser
129 self.info = info
130 self.symbol = None
131 self.body = QAPIDoc.Section()
132 # dict mapping parameter name to ArgSection
133 self.args = OrderedDict()
134 # a list of Section
135 self.sections = []
136 # the current section
137 self._section = self.body
138
139 def has_section(self, name):
140 """Return True if we have a section with this name."""
141 for i in self.sections:
142 if i.name == name:
143 return True
144 return False
145
146 def append(self, line):
147 """Parse a comment line and add it to the documentation."""
148 line = line[1:]
149 if not line:
150 self._append_freeform(line)
151 return
152
153 if line[0] != ' ':
154 raise QAPIParseError(self._parser, "Missing space after #")
155 line = line[1:]
156
157 # FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
158 # recognized, and get silently treated as ordinary text
159 if self.symbol:
160 self._append_symbol_line(line)
161 elif not self.body.text and line.startswith('@'):
162 if not line.endswith(':'):
163 raise QAPIParseError(self._parser, "Line should end with :")
164 self.symbol = line[1:-1]
165 # FIXME invalid names other than the empty string aren't flagged
166 if not self.symbol:
167 raise QAPIParseError(self._parser, "Invalid name")
168 else:
169 self._append_freeform(line)
170
171 def end_comment(self):
172 self._end_section()
173
174 def _append_symbol_line(self, line):
175 name = line.split(' ', 1)[0]
176
177 if name.startswith('@') and name.endswith(':'):
178 line = line[len(name)+1:]
179 self._start_args_section(name[1:-1])
180 elif name in ('Returns:', 'Since:',
181 # those are often singular or plural
182 'Note:', 'Notes:',
183 'Example:', 'Examples:',
184 'TODO:'):
185 line = line[len(name)+1:]
186 self._start_section(name[:-1])
187
188 self._append_freeform(line)
189
190 def _start_args_section(self, name):
191 # FIXME invalid names other than the empty string aren't flagged
192 if not name:
193 raise QAPIParseError(self._parser, "Invalid parameter name")
194 if name in self.args:
195 raise QAPIParseError(self._parser,
196 "'%s' parameter name duplicated" % name)
197 if self.sections:
198 raise QAPIParseError(self._parser,
199 "'@%s:' can't follow '%s' section"
200 % (name, self.sections[0].name))
201 self._end_section()
202 self._section = QAPIDoc.ArgSection(name)
203 self.args[name] = self._section
204
205 def _start_section(self, name=None):
206 if name in ('Returns', 'Since') and self.has_section(name):
207 raise QAPIParseError(self._parser,
208 "Duplicated '%s' section" % name)
209 self._end_section()
210 self._section = QAPIDoc.Section(name)
211 self.sections.append(self._section)
212
213 def _end_section(self):
214 if self._section:
215 text = self._section.text = self._section.text.strip()
216 if self._section.name and (not text or text.isspace()):
217 raise QAPIParseError(self._parser, "Empty doc section '%s'"
218 % self._section.name)
219 self._section = None
220
221 def _append_freeform(self, line):
222 in_arg = isinstance(self._section, QAPIDoc.ArgSection)
223 if (in_arg and self._section.text.endswith('\n\n')
224 and line and not line[0].isspace()):
225 self._start_section()
226 if (in_arg or not self._section.name
227 or not self._section.name.startswith('Example')):
228 line = line.strip()
229 match = re.match(r'(@\S+:)', line)
230 if match:
231 raise QAPIParseError(self._parser,
232 "'%s' not allowed in free-form documentation"
233 % match.group(1))
234 self._section.append(line)
235
236 def connect_member(self, member):
237 if member.name not in self.args:
238 # Undocumented TODO outlaw
239 self.args[member.name] = QAPIDoc.ArgSection(member.name)
240 self.args[member.name].connect(member)
241
242 def check_expr(self, expr):
243 if self.has_section('Returns') and 'command' not in expr:
244 raise QAPISemError(self.info,
245 "'Returns:' is only valid for commands")
246
247 def check(self):
248 bogus = [name for name, section in self.args.items()
249 if not section.member]
250 if bogus:
251 raise QAPISemError(
252 self.info,
253 "The following documented members are not in "
254 "the declaration: %s" % ", ".join(bogus))
255
256
257 class QAPISchemaParser(object):
258
259 def __init__(self, fp, previously_included=[], incl_info=None):
260 self.fname = fp.name
261 previously_included.append(os.path.abspath(fp.name))
262 self.incl_info = incl_info
263 self.src = fp.read()
264 if self.src == '' or self.src[-1] != '\n':
265 self.src += '\n'
266 self.cursor = 0
267 self.line = 1
268 self.line_pos = 0
269 self.exprs = []
270 self.docs = []
271 self.accept()
272 cur_doc = None
273
274 while self.tok is not None:
275 info = {'file': self.fname, 'line': self.line,
276 'parent': self.incl_info}
277 if self.tok == '#':
278 self.reject_expr_doc(cur_doc)
279 cur_doc = self.get_doc(info)
280 self.docs.append(cur_doc)
281 continue
282
283 expr = self.get_expr(False)
284 if 'include' in expr:
285 self.reject_expr_doc(cur_doc)
286 if len(expr) != 1:
287 raise QAPISemError(info, "Invalid 'include' directive")
288 include = expr['include']
289 if not isinstance(include, str):
290 raise QAPISemError(info,
291 "Value of 'include' must be a string")
292 incl_fname = os.path.join(os.path.dirname(self.fname),
293 include)
294 self.exprs.append({'expr': {'include': incl_fname},
295 'info': info})
296 exprs_include = self._include(include, info, incl_fname,
297 previously_included)
298 if exprs_include:
299 self.exprs.extend(exprs_include.exprs)
300 self.docs.extend(exprs_include.docs)
301 elif "pragma" in expr:
302 self.reject_expr_doc(cur_doc)
303 if len(expr) != 1:
304 raise QAPISemError(info, "Invalid 'pragma' directive")
305 pragma = expr['pragma']
306 if not isinstance(pragma, dict):
307 raise QAPISemError(
308 info, "Value of 'pragma' must be a dictionary")
309 for name, value in pragma.items():
310 self._pragma(name, value, info)
311 else:
312 expr_elem = {'expr': expr,
313 'info': info}
314 if cur_doc:
315 if not cur_doc.symbol:
316 raise QAPISemError(
317 cur_doc.info, "Expression documentation required")
318 expr_elem['doc'] = cur_doc
319 self.exprs.append(expr_elem)
320 cur_doc = None
321 self.reject_expr_doc(cur_doc)
322
323 @staticmethod
324 def reject_expr_doc(doc):
325 if doc and doc.symbol:
326 raise QAPISemError(
327 doc.info,
328 "Documentation for '%s' is not followed by the definition"
329 % doc.symbol)
330
331 def _include(self, include, info, incl_fname, previously_included):
332 incl_abs_fname = os.path.abspath(incl_fname)
333 # catch inclusion cycle
334 inf = info
335 while inf:
336 if incl_abs_fname == os.path.abspath(inf['file']):
337 raise QAPISemError(info, "Inclusion loop for %s" % include)
338 inf = inf['parent']
339
340 # skip multiple include of the same file
341 if incl_abs_fname in previously_included:
342 return None
343
344 try:
345 if sys.version_info[0] >= 3:
346 fobj = open(incl_fname, 'r', encoding='utf-8')
347 else:
348 fobj = open(incl_fname, 'r')
349 except IOError as e:
350 raise QAPISemError(info, '%s: %s' % (e.strerror, incl_fname))
351 return QAPISchemaParser(fobj, previously_included, info)
352
353 def _pragma(self, name, value, info):
354 global doc_required, returns_whitelist, name_case_whitelist
355 if name == 'doc-required':
356 if not isinstance(value, bool):
357 raise QAPISemError(info,
358 "Pragma 'doc-required' must be boolean")
359 doc_required = value
360 elif name == 'returns-whitelist':
361 if (not isinstance(value, list)
362 or any([not isinstance(elt, str) for elt in value])):
363 raise QAPISemError(info,
364 "Pragma returns-whitelist must be"
365 " a list of strings")
366 returns_whitelist = value
367 elif name == 'name-case-whitelist':
368 if (not isinstance(value, list)
369 or any([not isinstance(elt, str) for elt in value])):
370 raise QAPISemError(info,
371 "Pragma name-case-whitelist must be"
372 " a list of strings")
373 name_case_whitelist = value
374 else:
375 raise QAPISemError(info, "Unknown pragma '%s'" % name)
376
377 def accept(self, skip_comment=True):
378 while True:
379 self.tok = self.src[self.cursor]
380 self.pos = self.cursor
381 self.cursor += 1
382 self.val = None
383
384 if self.tok == '#':
385 if self.src[self.cursor] == '#':
386 # Start of doc comment
387 skip_comment = False
388 self.cursor = self.src.find('\n', self.cursor)
389 if not skip_comment:
390 self.val = self.src[self.pos:self.cursor]
391 return
392 elif self.tok in '{}:,[]':
393 return
394 elif self.tok == "'":
395 string = ''
396 esc = False
397 while True:
398 ch = self.src[self.cursor]
399 self.cursor += 1
400 if ch == '\n':
401 raise QAPIParseError(self, 'Missing terminating "\'"')
402 if esc:
403 if ch == 'b':
404 string += '\b'
405 elif ch == 'f':
406 string += '\f'
407 elif ch == 'n':
408 string += '\n'
409 elif ch == 'r':
410 string += '\r'
411 elif ch == 't':
412 string += '\t'
413 elif ch == 'u':
414 value = 0
415 for _ in range(0, 4):
416 ch = self.src[self.cursor]
417 self.cursor += 1
418 if ch not in '0123456789abcdefABCDEF':
419 raise QAPIParseError(self,
420 '\\u escape needs 4 '
421 'hex digits')
422 value = (value << 4) + int(ch, 16)
423 # If Python 2 and 3 didn't disagree so much on
424 # how to handle Unicode, then we could allow
425 # Unicode string defaults. But most of QAPI is
426 # ASCII-only, so we aren't losing much for now.
427 if not value or value > 0x7f:
428 raise QAPIParseError(self,
429 'For now, \\u escape '
430 'only supports non-zero '
431 'values up to \\u007f')
432 string += chr(value)
433 elif ch in '\\/\'"':
434 string += ch
435 else:
436 raise QAPIParseError(self,
437 "Unknown escape \\%s" % ch)
438 esc = False
439 elif ch == '\\':
440 esc = True
441 elif ch == "'":
442 self.val = string
443 return
444 else:
445 string += ch
446 elif self.src.startswith('true', self.pos):
447 self.val = True
448 self.cursor += 3
449 return
450 elif self.src.startswith('false', self.pos):
451 self.val = False
452 self.cursor += 4
453 return
454 elif self.src.startswith('null', self.pos):
455 self.val = None
456 self.cursor += 3
457 return
458 elif self.tok == '\n':
459 if self.cursor == len(self.src):
460 self.tok = None
461 return
462 self.line += 1
463 self.line_pos = self.cursor
464 elif not self.tok.isspace():
465 raise QAPIParseError(self, 'Stray "%s"' % self.tok)
466
467 def get_members(self):
468 expr = OrderedDict()
469 if self.tok == '}':
470 self.accept()
471 return expr
472 if self.tok != "'":
473 raise QAPIParseError(self, 'Expected string or "}"')
474 while True:
475 key = self.val
476 self.accept()
477 if self.tok != ':':
478 raise QAPIParseError(self, 'Expected ":"')
479 self.accept()
480 if key in expr:
481 raise QAPIParseError(self, 'Duplicate key "%s"' % key)
482 expr[key] = self.get_expr(True)
483 if self.tok == '}':
484 self.accept()
485 return expr
486 if self.tok != ',':
487 raise QAPIParseError(self, 'Expected "," or "}"')
488 self.accept()
489 if self.tok != "'":
490 raise QAPIParseError(self, 'Expected string')
491
492 def get_values(self):
493 expr = []
494 if self.tok == ']':
495 self.accept()
496 return expr
497 if self.tok not in "{['tfn":
498 raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
499 'boolean or "null"')
500 while True:
501 expr.append(self.get_expr(True))
502 if self.tok == ']':
503 self.accept()
504 return expr
505 if self.tok != ',':
506 raise QAPIParseError(self, 'Expected "," or "]"')
507 self.accept()
508
509 def get_expr(self, nested):
510 if self.tok != '{' and not nested:
511 raise QAPIParseError(self, 'Expected "{"')
512 if self.tok == '{':
513 self.accept()
514 expr = self.get_members()
515 elif self.tok == '[':
516 self.accept()
517 expr = self.get_values()
518 elif self.tok in "'tfn":
519 expr = self.val
520 self.accept()
521 else:
522 raise QAPIParseError(self, 'Expected "{", "[", string, '
523 'boolean or "null"')
524 return expr
525
526 def get_doc(self, info):
527 if self.val != '##':
528 raise QAPIParseError(self, "Junk after '##' at start of "
529 "documentation comment")
530
531 doc = QAPIDoc(self, info)
532 self.accept(False)
533 while self.tok == '#':
534 if self.val.startswith('##'):
535 # End of doc comment
536 if self.val != '##':
537 raise QAPIParseError(self, "Junk after '##' at end of "
538 "documentation comment")
539 doc.end_comment()
540 self.accept()
541 return doc
542 else:
543 doc.append(self.val)
544 self.accept(False)
545
546 raise QAPIParseError(self, "Documentation comment must end with '##'")
547
548
549 #
550 # Semantic analysis of schema expressions
551 # TODO fold into QAPISchema
552 # TODO catching name collisions in generated code would be nice
553 #
554
555
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']
563
564
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
576
577
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')
583
584 if not (discriminator and base):
585 return None
586
587 base_members = find_base_members(base)
588 if not base_members:
589 return None
590
591 discriminator_value = base_members.get(discriminator)
592 if not discriminator_value:
593 return None
594
595 return enum_types.get(discriminator_value['type'])
596
597
598 # Names must be letters, numbers, -, and _. They must start with letter,
599 # except for downstream extensions which must start with __RFQDN_.
600 # Dots are only valid in the downstream extension prefix.
601 valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
602 '[a-zA-Z][a-zA-Z0-9_-]*$')
603
604
605 def check_name(info, source, name, allow_optional=False,
606 enum_member=False):
607 global valid_name
608 membername = name
609
610 if not isinstance(name, str):
611 raise QAPISemError(info, "%s requires a string name" % source)
612 if name.startswith('*'):
613 membername = name[1:]
614 if not allow_optional:
615 raise QAPISemError(info, "%s does not allow optional name '%s'"
616 % (source, name))
617 # Enum members can start with a digit, because the generated C
618 # code always prefixes it with the enum name
619 if enum_member and membername[0].isdigit():
620 membername = 'D' + membername
621 # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
622 # and 'q_obj_*' implicit type names.
623 if not valid_name.match(membername) or \
624 c_name(membername, False).startswith('q_'):
625 raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
626
627
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
640
641
642 def check_if(expr, info):
643
644 def check_if_str(ifcond, info):
645 if not isinstance(ifcond, str):
646 raise QAPISemError(
647 info, "'if' condition must be a string or a list of strings")
648 if ifcond == '':
649 raise QAPISemError(info, "'if' condition '' makes no sense")
650
651 ifcond = expr.get('if')
652 if ifcond is None:
653 return
654 if isinstance(ifcond, list):
655 if ifcond == []:
656 raise QAPISemError(info, "'if' condition [] is useless")
657 for elt in ifcond:
658 check_if_str(elt, info)
659 else:
660 check_if_str(ifcond, info)
661
662
663 def check_type(info, source, value, allow_array=False,
664 allow_dict=False, allow_optional=False,
665 allow_metas=[]):
666 global all_names
667
668 if value is None:
669 return
670
671 # Check if array type for value is okay
672 if isinstance(value, list):
673 if not allow_array:
674 raise QAPISemError(info, "%s cannot be an array" % source)
675 if len(value) != 1 or not isinstance(value[0], str):
676 raise QAPISemError(info,
677 "%s: array type must contain single type name" %
678 source)
679 value = value[0]
680
681 # Check if type name for value is okay
682 if isinstance(value, str):
683 if value not in all_names:
684 raise QAPISemError(info, "%s uses unknown type '%s'"
685 % (source, value))
686 if not all_names[value] in allow_metas:
687 raise QAPISemError(info, "%s cannot use %s type '%s'" %
688 (source, all_names[value], value))
689 return
690
691 if not allow_dict:
692 raise QAPISemError(info, "%s should be a type name" % source)
693
694 if not isinstance(value, OrderedDict):
695 raise QAPISemError(info,
696 "%s should be a dictionary or type name" % source)
697
698 # value is a dictionary, check that each member is okay
699 for (key, arg) in value.items():
700 check_name(info, "Member of %s" % source, key,
701 allow_optional=allow_optional)
702 if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
703 raise QAPISemError(info, "Member of %s uses reserved name '%s'"
704 % (source, key))
705 # Todo: allow dictionaries to represent default values of
706 # an optional argument.
707 check_known_keys(info, "member '%s' of %s" % (key, source),
708 arg, ['type'], [])
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'])
713
714
715 def check_command(expr, info):
716 name = expr['command']
717 boxed = expr.get('boxed', False)
718
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)
731
732
733 def check_event(expr, info):
734 name = expr['event']
735 boxed = expr.get('boxed', False)
736
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)
743
744
745 def enum_get_names(expr):
746 return [e['name'] for e in expr['data']]
747
748
749 def check_union(expr, info):
750 name = expr['union']
751 base = expr.get('base')
752 discriminator = expr.get('discriminator')
753 members = expr['data']
754
755 # Two types of unions, determined by discriminator.
756
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)
764
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
776
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 enum_define = enum_types.get(discriminator_value['type'])
788 allow_metas = ['struct']
789 # Do not allow string discriminator
790 if not enum_define:
791 raise QAPISemError(info,
792 "Discriminator '%s' must be of enumeration "
793 "type" % discriminator)
794
795 # Check every branch; don't allow an empty union
796 if len(members) == 0:
797 raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
798 for (key, value) in members.items():
799 check_name(info, "Member of union '%s'" % name, key)
800
801 check_known_keys(info, "member '%s' of union '%s'" % (key, name),
802 value, ['type'], [])
803 # Each value must name a known type
804 check_type(info, "Member '%s' of union '%s'" % (key, name),
805 value['type'],
806 allow_array=not base, allow_metas=allow_metas)
807
808 # If the discriminator names an enum type, then all members
809 # of 'data' must also be members of the enum type.
810 if enum_define:
811 if key not in enum_get_names(enum_define):
812 raise QAPISemError(info,
813 "Discriminator value '%s' is not found in "
814 "enum '%s'"
815 % (key, enum_define['enum']))
816
817
818 def check_alternate(expr, info):
819 name = expr['alternate']
820 members = expr['data']
821 types_seen = {}
822
823 # Check every branch; require at least two branches
824 if len(members) < 2:
825 raise QAPISemError(info,
826 "Alternate '%s' should have at least two branches "
827 "in 'data'" % name)
828 for (key, value) in members.items():
829 check_name(info, "Member of alternate '%s'" % name, key)
830 check_known_keys(info,
831 "member '%s' of alternate '%s'" % (key, name),
832 value, ['type'], [])
833 typ = value['type']
834
835 # Ensure alternates have no type conflicts.
836 check_type(info, "Member '%s' of alternate '%s'" % (key, name), typ,
837 allow_metas=['built-in', 'union', 'struct', 'enum'])
838 qtype = find_alternate_member_qtype(typ)
839 if not qtype:
840 raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
841 "type '%s'" % (name, key, typ))
842 conflicting = set([qtype])
843 if qtype == 'QTYPE_QSTRING':
844 enum_expr = enum_types.get(typ)
845 if enum_expr:
846 for v in enum_get_names(enum_expr):
847 if v in ['on', 'off']:
848 conflicting.add('QTYPE_QBOOL')
849 if re.match(r'[-+0-9.]', v): # lazy, could be tightened
850 conflicting.add('QTYPE_QNUM')
851 else:
852 conflicting.add('QTYPE_QNUM')
853 conflicting.add('QTYPE_QBOOL')
854 for qt in conflicting:
855 if qt in types_seen:
856 raise QAPISemError(info, "Alternate '%s' member '%s' can't "
857 "be distinguished from member '%s'"
858 % (name, key, types_seen[qt]))
859 types_seen[qt] = key
860
861
862 def check_enum(expr, info):
863 name = expr['enum']
864 members = expr['data']
865 prefix = expr.get('prefix')
866
867 if not isinstance(members, list):
868 raise QAPISemError(info,
869 "Enum '%s' requires an array for 'data'" % name)
870 if prefix is not None and not isinstance(prefix, str):
871 raise QAPISemError(info,
872 "Enum '%s' requires a string for 'prefix'" % name)
873
874 for member in members:
875 source = "dictionary member of enum '%s'" % name
876 check_known_keys(info, source, member, ['name'], ['if'])
877 check_if(member, info)
878 check_name(info, "Member of enum '%s'" % name, member['name'],
879 enum_member=True)
880
881
882 def check_struct(expr, info):
883 name = expr['struct']
884 members = expr['data']
885
886 check_type(info, "'data' for struct '%s'" % name, members,
887 allow_dict=True, allow_optional=True)
888 check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
889 allow_metas=['struct'])
890
891
892 def check_known_keys(info, source, keys, required, optional):
893
894 def pprint(elems):
895 return ', '.join("'" + e + "'" for e in sorted(elems))
896
897 missing = set(required) - set(keys)
898 if missing:
899 raise QAPISemError(info, "Key%s %s %s missing from %s"
900 % ('s' if len(missing) > 1 else '', pprint(missing),
901 'are' if len(missing) > 1 else 'is', source))
902 allowed = set(required + optional)
903 unknown = set(keys) - allowed
904 if unknown:
905 raise QAPISemError(info, "Unknown key%s %s in %s\nValid keys are %s."
906 % ('s' if len(unknown) > 1 else '', pprint(unknown),
907 source, pprint(allowed)))
908
909
910 def check_keys(expr_elem, meta, required, optional=[]):
911 expr = expr_elem['expr']
912 info = expr_elem['info']
913 name = expr[meta]
914 if not isinstance(name, str):
915 raise QAPISemError(info, "'%s' key must have a string value" % meta)
916 required = required + [meta]
917 source = "%s '%s'" % (meta, name)
918 check_known_keys(info, source, expr.keys(), required, optional)
919 for (key, value) in expr.items():
920 if key in ['gen', 'success-response'] and value is not False:
921 raise QAPISemError(info,
922 "'%s' of %s '%s' should only use false value"
923 % (key, meta, name))
924 if (key in ['boxed', 'allow-oob', 'allow-preconfig']
925 and value is not True):
926 raise QAPISemError(info,
927 "'%s' of %s '%s' should only use true value"
928 % (key, meta, name))
929 if key == 'if':
930 check_if(expr, info)
931
932
933 def normalize_enum(expr):
934 if isinstance(expr['data'], list):
935 expr['data'] = [m if isinstance(m, dict) else {'name': m}
936 for m in expr['data']]
937
938
939 def normalize_members(members):
940 if isinstance(members, OrderedDict):
941 for key, arg in members.items():
942 if isinstance(arg, dict):
943 continue
944 members[key] = {'type': arg}
945
946
947 def check_exprs(exprs):
948 global all_names
949
950 # Populate name table with names of built-in types
951 for builtin in builtin_types.keys():
952 all_names[builtin] = 'built-in'
953
954 # Learn the types and check for valid expression keys
955 for expr_elem in exprs:
956 expr = expr_elem['expr']
957 info = expr_elem['info']
958 doc = expr_elem.get('doc')
959
960 if 'include' in expr:
961 continue
962
963 if not doc and doc_required:
964 raise QAPISemError(info,
965 "Expression missing documentation comment")
966
967 if 'enum' in expr:
968 meta = 'enum'
969 check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix'])
970 normalize_enum(expr)
971 enum_types[expr[meta]] = expr
972 elif 'union' in expr:
973 meta = 'union'
974 check_keys(expr_elem, 'union', ['data'],
975 ['base', 'discriminator', 'if'])
976 normalize_members(expr.get('base'))
977 normalize_members(expr['data'])
978 union_types[expr[meta]] = expr
979 elif 'alternate' in expr:
980 meta = 'alternate'
981 check_keys(expr_elem, 'alternate', ['data'], ['if'])
982 normalize_members(expr['data'])
983 elif 'struct' in expr:
984 meta = 'struct'
985 check_keys(expr_elem, 'struct', ['data'], ['base', 'if'])
986 normalize_members(expr['data'])
987 struct_types[expr[meta]] = expr
988 elif 'command' in expr:
989 meta = 'command'
990 check_keys(expr_elem, 'command', [],
991 ['data', 'returns', 'gen', 'success-response',
992 'boxed', 'allow-oob', 'allow-preconfig', 'if'])
993 normalize_members(expr.get('data'))
994 elif 'event' in expr:
995 meta = 'event'
996 check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if'])
997 normalize_members(expr.get('data'))
998 else:
999 raise QAPISemError(expr_elem['info'],
1000 "Expression is missing metatype")
1001 name = expr[meta]
1002 add_name(name, info, meta)
1003 if doc and doc.symbol != name:
1004 raise QAPISemError(info, "Definition of '%s' follows documentation"
1005 " for '%s'" % (name, doc.symbol))
1006
1007 # Try again for hidden UnionKind enum
1008 for expr_elem in exprs:
1009 expr = expr_elem['expr']
1010
1011 if 'include' in expr:
1012 continue
1013 if 'union' in expr and not discriminator_find_enum_define(expr):
1014 name = '%sKind' % expr['union']
1015 elif 'alternate' in expr:
1016 name = '%sKind' % expr['alternate']
1017 else:
1018 continue
1019 enum_types[name] = {'enum': name}
1020 add_name(name, info, 'enum', implicit=True)
1021
1022 # Validate that exprs make sense
1023 for expr_elem in exprs:
1024 expr = expr_elem['expr']
1025 info = expr_elem['info']
1026 doc = expr_elem.get('doc')
1027
1028 if 'include' in expr:
1029 continue
1030 if 'enum' in expr:
1031 check_enum(expr, info)
1032 elif 'union' in expr:
1033 check_union(expr, info)
1034 elif 'alternate' in expr:
1035 check_alternate(expr, info)
1036 elif 'struct' in expr:
1037 check_struct(expr, info)
1038 elif 'command' in expr:
1039 check_command(expr, info)
1040 elif 'event' in expr:
1041 check_event(expr, info)
1042 else:
1043 assert False, 'unexpected meta type'
1044
1045 if doc:
1046 doc.check_expr(expr)
1047
1048 return exprs
1049
1050
1051 #
1052 # Schema compiler frontend
1053 #
1054
1055 def listify_cond(ifcond):
1056 if not ifcond:
1057 return []
1058 if not isinstance(ifcond, list):
1059 return [ifcond]
1060 return ifcond
1061
1062
1063 class QAPISchemaEntity(object):
1064 def __init__(self, name, info, doc, ifcond=None):
1065 assert name is None or isinstance(name, str)
1066 self.name = name
1067 self.module = None
1068 # For explicitly defined entities, info points to the (explicit)
1069 # definition. For builtins (and their arrays), info is None.
1070 # For implicitly defined entities, info points to a place that
1071 # triggered the implicit definition (there may be more than one
1072 # such place).
1073 self.info = info
1074 self.doc = doc
1075 self._ifcond = ifcond # self.ifcond is set only after .check()
1076
1077 def c_name(self):
1078 return c_name(self.name)
1079
1080 def check(self, schema):
1081 if isinstance(self._ifcond, QAPISchemaType):
1082 # inherit the condition from a type
1083 typ = self._ifcond
1084 typ.check(schema)
1085 self.ifcond = typ.ifcond
1086 else:
1087 self.ifcond = listify_cond(self._ifcond)
1088
1089 def is_implicit(self):
1090 return not self.info
1091
1092 def visit(self, visitor):
1093 pass
1094
1095
1096 class QAPISchemaVisitor(object):
1097 def visit_begin(self, schema):
1098 pass
1099
1100 def visit_end(self):
1101 pass
1102
1103 def visit_module(self, fname):
1104 pass
1105
1106 def visit_needed(self, entity):
1107 # Default to visiting everything
1108 return True
1109
1110 def visit_include(self, fname, info):
1111 pass
1112
1113 def visit_builtin_type(self, name, info, json_type):
1114 pass
1115
1116 def visit_enum_type(self, name, info, ifcond, members, prefix):
1117 pass
1118
1119 def visit_array_type(self, name, info, ifcond, element_type):
1120 pass
1121
1122 def visit_object_type(self, name, info, ifcond, base, members, variants):
1123 pass
1124
1125 def visit_object_type_flat(self, name, info, ifcond, members, variants):
1126 pass
1127
1128 def visit_alternate_type(self, name, info, ifcond, variants):
1129 pass
1130
1131 def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
1132 success_response, boxed, allow_oob, allow_preconfig):
1133 pass
1134
1135 def visit_event(self, name, info, ifcond, arg_type, boxed):
1136 pass
1137
1138
1139 class QAPISchemaInclude(QAPISchemaEntity):
1140
1141 def __init__(self, fname, info):
1142 QAPISchemaEntity.__init__(self, None, info, None)
1143 self.fname = fname
1144
1145 def visit(self, visitor):
1146 visitor.visit_include(self.fname, self.info)
1147
1148
1149 class QAPISchemaType(QAPISchemaEntity):
1150 # Return the C type for common use.
1151 # For the types we commonly box, this is a pointer type.
1152 def c_type(self):
1153 pass
1154
1155 # Return the C type to be used in a parameter list.
1156 def c_param_type(self):
1157 return self.c_type()
1158
1159 # Return the C type to be used where we suppress boxing.
1160 def c_unboxed_type(self):
1161 return self.c_type()
1162
1163 def json_type(self):
1164 pass
1165
1166 def alternate_qtype(self):
1167 json2qtype = {
1168 'null': 'QTYPE_QNULL',
1169 'string': 'QTYPE_QSTRING',
1170 'number': 'QTYPE_QNUM',
1171 'int': 'QTYPE_QNUM',
1172 'boolean': 'QTYPE_QBOOL',
1173 'object': 'QTYPE_QDICT'
1174 }
1175 return json2qtype.get(self.json_type())
1176
1177 def doc_type(self):
1178 if self.is_implicit():
1179 return None
1180 return self.name
1181
1182
1183 class QAPISchemaBuiltinType(QAPISchemaType):
1184 def __init__(self, name, json_type, c_type):
1185 QAPISchemaType.__init__(self, name, None, None)
1186 assert not c_type or isinstance(c_type, str)
1187 assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1188 'value')
1189 self._json_type_name = json_type
1190 self._c_type_name = c_type
1191
1192 def c_name(self):
1193 return self.name
1194
1195 def c_type(self):
1196 return self._c_type_name
1197
1198 def c_param_type(self):
1199 if self.name == 'str':
1200 return 'const ' + self._c_type_name
1201 return self._c_type_name
1202
1203 def json_type(self):
1204 return self._json_type_name
1205
1206 def doc_type(self):
1207 return self.json_type()
1208
1209 def visit(self, visitor):
1210 visitor.visit_builtin_type(self.name, self.info, self.json_type())
1211
1212
1213 class QAPISchemaEnumType(QAPISchemaType):
1214 def __init__(self, name, info, doc, ifcond, members, prefix):
1215 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1216 for m in members:
1217 assert isinstance(m, QAPISchemaMember)
1218 m.set_owner(name)
1219 assert prefix is None or isinstance(prefix, str)
1220 self.members = members
1221 self.prefix = prefix
1222
1223 def check(self, schema):
1224 QAPISchemaType.check(self, schema)
1225 seen = {}
1226 for m in self.members:
1227 m.check_clash(self.info, seen)
1228 if self.doc:
1229 self.doc.connect_member(m)
1230
1231 def is_implicit(self):
1232 # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1233 return self.name.endswith('Kind') or self.name == 'QType'
1234
1235 def c_type(self):
1236 return c_name(self.name)
1237
1238 def member_names(self):
1239 return [m.name for m in self.members]
1240
1241 def json_type(self):
1242 return 'string'
1243
1244 def visit(self, visitor):
1245 visitor.visit_enum_type(self.name, self.info, self.ifcond,
1246 self.members, self.prefix)
1247
1248
1249 class QAPISchemaArrayType(QAPISchemaType):
1250 def __init__(self, name, info, element_type):
1251 QAPISchemaType.__init__(self, name, info, None, None)
1252 assert isinstance(element_type, str)
1253 self._element_type_name = element_type
1254 self.element_type = None
1255
1256 def check(self, schema):
1257 QAPISchemaType.check(self, schema)
1258 self.element_type = schema.lookup_type(self._element_type_name)
1259 assert self.element_type
1260 self.element_type.check(schema)
1261 self.ifcond = self.element_type.ifcond
1262
1263 def is_implicit(self):
1264 return True
1265
1266 def c_type(self):
1267 return c_name(self.name) + pointer_suffix
1268
1269 def json_type(self):
1270 return 'array'
1271
1272 def doc_type(self):
1273 elt_doc_type = self.element_type.doc_type()
1274 if not elt_doc_type:
1275 return None
1276 return 'array of ' + elt_doc_type
1277
1278 def visit(self, visitor):
1279 visitor.visit_array_type(self.name, self.info, self.ifcond,
1280 self.element_type)
1281
1282
1283 class QAPISchemaObjectType(QAPISchemaType):
1284 def __init__(self, name, info, doc, ifcond,
1285 base, local_members, variants):
1286 # struct has local_members, optional base, and no variants
1287 # flat union has base, variants, and no local_members
1288 # simple union has local_members, variants, and no base
1289 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1290 assert base is None or isinstance(base, str)
1291 for m in local_members:
1292 assert isinstance(m, QAPISchemaObjectTypeMember)
1293 m.set_owner(name)
1294 if variants is not None:
1295 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1296 variants.set_owner(name)
1297 self._base_name = base
1298 self.base = None
1299 self.local_members = local_members
1300 self.variants = variants
1301 self.members = None
1302
1303 def check(self, schema):
1304 QAPISchemaType.check(self, schema)
1305 if self.members is False: # check for cycles
1306 raise QAPISemError(self.info,
1307 "Object %s contains itself" % self.name)
1308 if self.members:
1309 return
1310 self.members = False # mark as being checked
1311 seen = OrderedDict()
1312 if self._base_name:
1313 self.base = schema.lookup_type(self._base_name)
1314 assert isinstance(self.base, QAPISchemaObjectType)
1315 self.base.check(schema)
1316 self.base.check_clash(self.info, seen)
1317 for m in self.local_members:
1318 m.check(schema)
1319 m.check_clash(self.info, seen)
1320 if self.doc:
1321 self.doc.connect_member(m)
1322 self.members = seen.values()
1323 if self.variants:
1324 self.variants.check(schema, seen)
1325 assert self.variants.tag_member in self.members
1326 self.variants.check_clash(self.info, seen)
1327 if self.doc:
1328 self.doc.check()
1329
1330 # Check that the members of this type do not cause duplicate JSON members,
1331 # and update seen to track the members seen so far. Report any errors
1332 # on behalf of info, which is not necessarily self.info
1333 def check_clash(self, info, seen):
1334 assert not self.variants # not implemented
1335 for m in self.members:
1336 m.check_clash(info, seen)
1337
1338 def is_implicit(self):
1339 # See QAPISchema._make_implicit_object_type(), as well as
1340 # _def_predefineds()
1341 return self.name.startswith('q_')
1342
1343 def is_empty(self):
1344 assert self.members is not None
1345 return not self.members and not self.variants
1346
1347 def c_name(self):
1348 assert self.name != 'q_empty'
1349 return QAPISchemaType.c_name(self)
1350
1351 def c_type(self):
1352 assert not self.is_implicit()
1353 return c_name(self.name) + pointer_suffix
1354
1355 def c_unboxed_type(self):
1356 return c_name(self.name)
1357
1358 def json_type(self):
1359 return 'object'
1360
1361 def visit(self, visitor):
1362 visitor.visit_object_type(self.name, self.info, self.ifcond,
1363 self.base, self.local_members, self.variants)
1364 visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
1365 self.members, self.variants)
1366
1367
1368 class QAPISchemaMember(object):
1369 role = 'member'
1370
1371 def __init__(self, name, ifcond=None):
1372 assert isinstance(name, str)
1373 self.name = name
1374 self.ifcond = listify_cond(ifcond)
1375 self.owner = None
1376
1377 def set_owner(self, name):
1378 assert not self.owner
1379 self.owner = name
1380
1381 def check_clash(self, info, seen):
1382 cname = c_name(self.name)
1383 if cname.lower() != cname and self.owner not in name_case_whitelist:
1384 raise QAPISemError(info,
1385 "%s should not use uppercase" % self.describe())
1386 if cname in seen:
1387 raise QAPISemError(info, "%s collides with %s" %
1388 (self.describe(), seen[cname].describe()))
1389 seen[cname] = self
1390
1391 def _pretty_owner(self):
1392 owner = self.owner
1393 if owner.startswith('q_obj_'):
1394 # See QAPISchema._make_implicit_object_type() - reverse the
1395 # mapping there to create a nice human-readable description
1396 owner = owner[6:]
1397 if owner.endswith('-arg'):
1398 return '(parameter of %s)' % owner[:-4]
1399 elif owner.endswith('-base'):
1400 return '(base of %s)' % owner[:-5]
1401 else:
1402 assert owner.endswith('-wrapper')
1403 # Unreachable and not implemented
1404 assert False
1405 if owner.endswith('Kind'):
1406 # See QAPISchema._make_implicit_enum_type()
1407 return '(branch of %s)' % owner[:-4]
1408 return '(%s of %s)' % (self.role, owner)
1409
1410 def describe(self):
1411 return "'%s' %s" % (self.name, self._pretty_owner())
1412
1413
1414 class QAPISchemaObjectTypeMember(QAPISchemaMember):
1415 def __init__(self, name, typ, optional):
1416 QAPISchemaMember.__init__(self, name)
1417 assert isinstance(typ, str)
1418 assert isinstance(optional, bool)
1419 self._type_name = typ
1420 self.type = None
1421 self.optional = optional
1422
1423 def check(self, schema):
1424 assert self.owner
1425 self.type = schema.lookup_type(self._type_name)
1426 assert self.type
1427
1428
1429 class QAPISchemaObjectTypeVariants(object):
1430 def __init__(self, tag_name, tag_member, variants):
1431 # Flat unions pass tag_name but not tag_member.
1432 # Simple unions and alternates pass tag_member but not tag_name.
1433 # After check(), tag_member is always set, and tag_name remains
1434 # a reliable witness of being used by a flat union.
1435 assert bool(tag_member) != bool(tag_name)
1436 assert (isinstance(tag_name, str) or
1437 isinstance(tag_member, QAPISchemaObjectTypeMember))
1438 assert len(variants) > 0
1439 for v in variants:
1440 assert isinstance(v, QAPISchemaObjectTypeVariant)
1441 self._tag_name = tag_name
1442 self.tag_member = tag_member
1443 self.variants = variants
1444
1445 def set_owner(self, name):
1446 for v in self.variants:
1447 v.set_owner(name)
1448
1449 def check(self, schema, seen):
1450 if not self.tag_member: # flat union
1451 self.tag_member = seen[c_name(self._tag_name)]
1452 assert self._tag_name == self.tag_member.name
1453 assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1454 if self._tag_name: # flat union
1455 # branches that are not explicitly covered get an empty type
1456 cases = set([v.name for v in self.variants])
1457 for m in self.tag_member.type.members:
1458 if m.name not in cases:
1459 v = QAPISchemaObjectTypeVariant(m.name, 'q_empty')
1460 v.set_owner(self.tag_member.owner)
1461 self.variants.append(v)
1462 for v in self.variants:
1463 v.check(schema)
1464 # Union names must match enum values; alternate names are
1465 # checked separately. Use 'seen' to tell the two apart.
1466 if seen:
1467 assert v.name in self.tag_member.type.member_names()
1468 assert isinstance(v.type, QAPISchemaObjectType)
1469 v.type.check(schema)
1470
1471 def check_clash(self, info, seen):
1472 for v in self.variants:
1473 # Reset seen map for each variant, since qapi names from one
1474 # branch do not affect another branch
1475 assert isinstance(v.type, QAPISchemaObjectType)
1476 v.type.check_clash(info, dict(seen))
1477
1478
1479 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1480 role = 'branch'
1481
1482 def __init__(self, name, typ):
1483 QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1484
1485
1486 class QAPISchemaAlternateType(QAPISchemaType):
1487 def __init__(self, name, info, doc, ifcond, variants):
1488 QAPISchemaType.__init__(self, name, info, doc, ifcond)
1489 assert isinstance(variants, QAPISchemaObjectTypeVariants)
1490 assert variants.tag_member
1491 variants.set_owner(name)
1492 variants.tag_member.set_owner(self.name)
1493 self.variants = variants
1494
1495 def check(self, schema):
1496 QAPISchemaType.check(self, schema)
1497 self.variants.tag_member.check(schema)
1498 # Not calling self.variants.check_clash(), because there's nothing
1499 # to clash with
1500 self.variants.check(schema, {})
1501 # Alternate branch names have no relation to the tag enum values;
1502 # so we have to check for potential name collisions ourselves.
1503 seen = {}
1504 for v in self.variants.variants:
1505 v.check_clash(self.info, seen)
1506 if self.doc:
1507 self.doc.connect_member(v)
1508 if self.doc:
1509 self.doc.check()
1510
1511 def c_type(self):
1512 return c_name(self.name) + pointer_suffix
1513
1514 def json_type(self):
1515 return 'value'
1516
1517 def visit(self, visitor):
1518 visitor.visit_alternate_type(self.name, self.info, self.ifcond,
1519 self.variants)
1520
1521 def is_empty(self):
1522 return False
1523
1524
1525 class QAPISchemaCommand(QAPISchemaEntity):
1526 def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
1527 gen, success_response, boxed, allow_oob, allow_preconfig):
1528 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1529 assert not arg_type or isinstance(arg_type, str)
1530 assert not ret_type or isinstance(ret_type, str)
1531 self._arg_type_name = arg_type
1532 self.arg_type = None
1533 self._ret_type_name = ret_type
1534 self.ret_type = None
1535 self.gen = gen
1536 self.success_response = success_response
1537 self.boxed = boxed
1538 self.allow_oob = allow_oob
1539 self.allow_preconfig = allow_preconfig
1540
1541 def check(self, schema):
1542 QAPISchemaEntity.check(self, schema)
1543 if self._arg_type_name:
1544 self.arg_type = schema.lookup_type(self._arg_type_name)
1545 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1546 isinstance(self.arg_type, QAPISchemaAlternateType))
1547 self.arg_type.check(schema)
1548 if self.boxed:
1549 if self.arg_type.is_empty():
1550 raise QAPISemError(self.info,
1551 "Cannot use 'boxed' with empty type")
1552 else:
1553 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1554 assert not self.arg_type.variants
1555 elif self.boxed:
1556 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1557 if self._ret_type_name:
1558 self.ret_type = schema.lookup_type(self._ret_type_name)
1559 assert isinstance(self.ret_type, QAPISchemaType)
1560
1561 def visit(self, visitor):
1562 visitor.visit_command(self.name, self.info, self.ifcond,
1563 self.arg_type, self.ret_type,
1564 self.gen, self.success_response,
1565 self.boxed, self.allow_oob,
1566 self.allow_preconfig)
1567
1568
1569 class QAPISchemaEvent(QAPISchemaEntity):
1570 def __init__(self, name, info, doc, ifcond, arg_type, boxed):
1571 QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1572 assert not arg_type or isinstance(arg_type, str)
1573 self._arg_type_name = arg_type
1574 self.arg_type = None
1575 self.boxed = boxed
1576
1577 def check(self, schema):
1578 QAPISchemaEntity.check(self, schema)
1579 if self._arg_type_name:
1580 self.arg_type = schema.lookup_type(self._arg_type_name)
1581 assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1582 isinstance(self.arg_type, QAPISchemaAlternateType))
1583 self.arg_type.check(schema)
1584 if self.boxed:
1585 if self.arg_type.is_empty():
1586 raise QAPISemError(self.info,
1587 "Cannot use 'boxed' with empty type")
1588 else:
1589 assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1590 assert not self.arg_type.variants
1591 elif self.boxed:
1592 raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1593
1594 def visit(self, visitor):
1595 visitor.visit_event(self.name, self.info, self.ifcond,
1596 self.arg_type, self.boxed)
1597
1598
1599 class QAPISchema(object):
1600 def __init__(self, fname):
1601 self._fname = fname
1602 if sys.version_info[0] >= 3:
1603 f = open(fname, 'r', encoding='utf-8')
1604 else:
1605 f = open(fname, 'r')
1606 parser = QAPISchemaParser(f)
1607 exprs = check_exprs(parser.exprs)
1608 self.docs = parser.docs
1609 self._entity_list = []
1610 self._entity_dict = {}
1611 self._predefining = True
1612 self._def_predefineds()
1613 self._predefining = False
1614 self._def_exprs(exprs)
1615 self.check()
1616
1617 def _def_entity(self, ent):
1618 # Only the predefined types are allowed to not have info
1619 assert ent.info or self._predefining
1620 assert ent.name is None or ent.name not in self._entity_dict
1621 self._entity_list.append(ent)
1622 if ent.name is not None:
1623 self._entity_dict[ent.name] = ent
1624 if ent.info:
1625 ent.module = os.path.relpath(ent.info['file'],
1626 os.path.dirname(self._fname))
1627
1628 def lookup_entity(self, name, typ=None):
1629 ent = self._entity_dict.get(name)
1630 if typ and not isinstance(ent, typ):
1631 return None
1632 return ent
1633
1634 def lookup_type(self, name):
1635 return self.lookup_entity(name, QAPISchemaType)
1636
1637 def _def_include(self, expr, info, doc):
1638 include = expr['include']
1639 assert doc is None
1640 main_info = info
1641 while main_info['parent']:
1642 main_info = main_info['parent']
1643 fname = os.path.relpath(include, os.path.dirname(main_info['file']))
1644 self._def_entity(QAPISchemaInclude(fname, info))
1645
1646 def _def_builtin_type(self, name, json_type, c_type):
1647 self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1648 # Instantiating only the arrays that are actually used would
1649 # be nice, but we can't as long as their generated code
1650 # (qapi-builtin-types.[ch]) may be shared by some other
1651 # schema.
1652 self._make_array_type(name, None)
1653
1654 def _def_predefineds(self):
1655 for t in [('str', 'string', 'char' + pointer_suffix),
1656 ('number', 'number', 'double'),
1657 ('int', 'int', 'int64_t'),
1658 ('int8', 'int', 'int8_t'),
1659 ('int16', 'int', 'int16_t'),
1660 ('int32', 'int', 'int32_t'),
1661 ('int64', 'int', 'int64_t'),
1662 ('uint8', 'int', 'uint8_t'),
1663 ('uint16', 'int', 'uint16_t'),
1664 ('uint32', 'int', 'uint32_t'),
1665 ('uint64', 'int', 'uint64_t'),
1666 ('size', 'int', 'uint64_t'),
1667 ('bool', 'boolean', 'bool'),
1668 ('any', 'value', 'QObject' + pointer_suffix),
1669 ('null', 'null', 'QNull' + pointer_suffix)]:
1670 self._def_builtin_type(*t)
1671 self.the_empty_object_type = QAPISchemaObjectType(
1672 'q_empty', None, None, None, None, [], None)
1673 self._def_entity(self.the_empty_object_type)
1674
1675 qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1676 'qbool']
1677 qtype_values = self._make_enum_members([{'name': n} for n in qtypes])
1678
1679 self._def_entity(QAPISchemaEnumType('QType', None, None, None,
1680 qtype_values, 'QTYPE'))
1681
1682 def _make_enum_members(self, values):
1683 return [QAPISchemaMember(v['name'], v.get('if')) for v in values]
1684
1685 def _make_implicit_enum_type(self, name, info, ifcond, values):
1686 # See also QAPISchemaObjectTypeMember._pretty_owner()
1687 name = name + 'Kind' # Use namespace reserved by add_name()
1688 self._def_entity(QAPISchemaEnumType(
1689 name, info, None, ifcond, self._make_enum_members(values), None))
1690 return name
1691
1692 def _make_array_type(self, element_type, info):
1693 name = element_type + 'List' # Use namespace reserved by add_name()
1694 if not self.lookup_type(name):
1695 self._def_entity(QAPISchemaArrayType(name, info, element_type))
1696 return name
1697
1698 def _make_implicit_object_type(self, name, info, doc, ifcond,
1699 role, members):
1700 if not members:
1701 return None
1702 # See also QAPISchemaObjectTypeMember._pretty_owner()
1703 name = 'q_obj_%s-%s' % (name, role)
1704 typ = self.lookup_entity(name, QAPISchemaObjectType)
1705 if typ:
1706 # The implicit object type has multiple users. This can
1707 # happen only for simple unions' implicit wrapper types.
1708 # Its ifcond should be the disjunction of its user's
1709 # ifconds. Not implemented. Instead, we always pass the
1710 # wrapped type's ifcond, which is trivially the same for all
1711 # users. It's also necessary for the wrapper to compile.
1712 # But it's not tight: the disjunction need not imply it. We
1713 # may end up compiling useless wrapper types.
1714 # TODO kill simple unions or implement the disjunction
1715 assert ifcond == typ._ifcond # pylint: disable=protected-access
1716 else:
1717 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
1718 None, members, None))
1719 return name
1720
1721 def _def_enum_type(self, expr, info, doc):
1722 name = expr['enum']
1723 data = expr['data']
1724 prefix = expr.get('prefix')
1725 ifcond = expr.get('if')
1726 self._def_entity(QAPISchemaEnumType(
1727 name, info, doc, ifcond,
1728 self._make_enum_members(data), prefix))
1729
1730 def _make_member(self, name, typ, info):
1731 optional = False
1732 if name.startswith('*'):
1733 name = name[1:]
1734 optional = True
1735 if isinstance(typ, list):
1736 assert len(typ) == 1
1737 typ = self._make_array_type(typ[0], info)
1738 return QAPISchemaObjectTypeMember(name, typ, optional)
1739
1740 def _make_members(self, data, info):
1741 return [self._make_member(key, value['type'], info)
1742 for (key, value) in data.items()]
1743
1744 def _def_struct_type(self, expr, info, doc):
1745 name = expr['struct']
1746 base = expr.get('base')
1747 data = expr['data']
1748 ifcond = expr.get('if')
1749 self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
1750 self._make_members(data, info),
1751 None))
1752
1753 def _make_variant(self, case, typ):
1754 return QAPISchemaObjectTypeVariant(case, typ)
1755
1756 def _make_simple_variant(self, case, typ, info):
1757 if isinstance(typ, list):
1758 assert len(typ) == 1
1759 typ = self._make_array_type(typ[0], info)
1760 typ = self._make_implicit_object_type(
1761 typ, info, None, self.lookup_type(typ),
1762 'wrapper', [self._make_member('data', typ, info)])
1763 return QAPISchemaObjectTypeVariant(case, typ)
1764
1765 def _def_union_type(self, expr, info, doc):
1766 name = expr['union']
1767 data = expr['data']
1768 base = expr.get('base')
1769 ifcond = expr.get('if')
1770 tag_name = expr.get('discriminator')
1771 tag_member = None
1772 if isinstance(base, dict):
1773 base = self._make_implicit_object_type(
1774 name, info, doc, ifcond,
1775 'base', self._make_members(base, info))
1776 if tag_name:
1777 variants = [self._make_variant(key, value['type'])
1778 for (key, value) in data.items()]
1779 members = []
1780 else:
1781 variants = [self._make_simple_variant(key, value['type'], info)
1782 for (key, value) in data.items()]
1783 enum = [{'name': v.name} for v in variants]
1784 typ = self._make_implicit_enum_type(name, info, ifcond, enum)
1785 tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1786 members = [tag_member]
1787 self._def_entity(
1788 QAPISchemaObjectType(name, info, doc, ifcond, base, members,
1789 QAPISchemaObjectTypeVariants(tag_name,
1790 tag_member,
1791 variants)))
1792
1793 def _def_alternate_type(self, expr, info, doc):
1794 name = expr['alternate']
1795 data = expr['data']
1796 ifcond = expr.get('if')
1797 variants = [self._make_variant(key, value['type'])
1798 for (key, value) in data.items()]
1799 tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
1800 self._def_entity(
1801 QAPISchemaAlternateType(name, info, doc, ifcond,
1802 QAPISchemaObjectTypeVariants(None,
1803 tag_member,
1804 variants)))
1805
1806 def _def_command(self, expr, info, doc):
1807 name = expr['command']
1808 data = expr.get('data')
1809 rets = expr.get('returns')
1810 gen = expr.get('gen', True)
1811 success_response = expr.get('success-response', True)
1812 boxed = expr.get('boxed', False)
1813 allow_oob = expr.get('allow-oob', False)
1814 allow_preconfig = expr.get('allow-preconfig', False)
1815 ifcond = expr.get('if')
1816 if isinstance(data, OrderedDict):
1817 data = self._make_implicit_object_type(
1818 name, info, doc, ifcond, 'arg', self._make_members(data, info))
1819 if isinstance(rets, list):
1820 assert len(rets) == 1
1821 rets = self._make_array_type(rets[0], info)
1822 self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
1823 gen, success_response,
1824 boxed, allow_oob, allow_preconfig))
1825
1826 def _def_event(self, expr, info, doc):
1827 name = expr['event']
1828 data = expr.get('data')
1829 boxed = expr.get('boxed', False)
1830 ifcond = expr.get('if')
1831 if isinstance(data, OrderedDict):
1832 data = self._make_implicit_object_type(
1833 name, info, doc, ifcond, 'arg', self._make_members(data, info))
1834 self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
1835
1836 def _def_exprs(self, exprs):
1837 for expr_elem in exprs:
1838 expr = expr_elem['expr']
1839 info = expr_elem['info']
1840 doc = expr_elem.get('doc')
1841 if 'enum' in expr:
1842 self._def_enum_type(expr, info, doc)
1843 elif 'struct' in expr:
1844 self._def_struct_type(expr, info, doc)
1845 elif 'union' in expr:
1846 self._def_union_type(expr, info, doc)
1847 elif 'alternate' in expr:
1848 self._def_alternate_type(expr, info, doc)
1849 elif 'command' in expr:
1850 self._def_command(expr, info, doc)
1851 elif 'event' in expr:
1852 self._def_event(expr, info, doc)
1853 elif 'include' in expr:
1854 self._def_include(expr, info, doc)
1855 else:
1856 assert False
1857
1858 def check(self):
1859 for ent in self._entity_list:
1860 ent.check(self)
1861
1862 def visit(self, visitor):
1863 visitor.visit_begin(self)
1864 module = None
1865 for entity in self._entity_list:
1866 if visitor.visit_needed(entity):
1867 if entity.module != module:
1868 module = entity.module
1869 visitor.visit_module(module)
1870 entity.visit(visitor)
1871 visitor.visit_end()
1872
1873
1874 #
1875 # Code generation helpers
1876 #
1877
1878 def camel_case(name):
1879 new_name = ''
1880 first = True
1881 for ch in name:
1882 if ch in ['_', '-']:
1883 first = True
1884 elif first:
1885 new_name += ch.upper()
1886 first = False
1887 else:
1888 new_name += ch.lower()
1889 return new_name
1890
1891
1892 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1893 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1894 # ENUM24_Name -> ENUM24_NAME
1895 def camel_to_upper(value):
1896 c_fun_str = c_name(value, False)
1897 if value.isupper():
1898 return c_fun_str
1899
1900 new_name = ''
1901 length = len(c_fun_str)
1902 for i in range(length):
1903 c = c_fun_str[i]
1904 # When c is upper and no '_' appears before, do more checks
1905 if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
1906 if i < length - 1 and c_fun_str[i + 1].islower():
1907 new_name += '_'
1908 elif c_fun_str[i - 1].isdigit():
1909 new_name += '_'
1910 new_name += c
1911 return new_name.lstrip('_').upper()
1912
1913
1914 def c_enum_const(type_name, const_name, prefix=None):
1915 if prefix is not None:
1916 type_name = prefix
1917 return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
1918
1919
1920 if hasattr(str, 'maketrans'):
1921 c_name_trans = str.maketrans('.-', '__')
1922 else:
1923 c_name_trans = string.maketrans('.-', '__')
1924
1925
1926 # Map @name to a valid C identifier.
1927 # If @protect, avoid returning certain ticklish identifiers (like
1928 # C keywords) by prepending 'q_'.
1929 #
1930 # Used for converting 'name' from a 'name':'type' qapi definition
1931 # into a generated struct member, as well as converting type names
1932 # into substrings of a generated C function name.
1933 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1934 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1935 def c_name(name, protect=True):
1936 # ANSI X3J11/88-090, 3.1.1
1937 c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
1938 'default', 'do', 'double', 'else', 'enum', 'extern',
1939 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1940 'return', 'short', 'signed', 'sizeof', 'static',
1941 'struct', 'switch', 'typedef', 'union', 'unsigned',
1942 'void', 'volatile', 'while'])
1943 # ISO/IEC 9899:1999, 6.4.1
1944 c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1945 # ISO/IEC 9899:2011, 6.4.1
1946 c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1947 '_Noreturn', '_Static_assert', '_Thread_local'])
1948 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1949 # excluding _.*
1950 gcc_words = set(['asm', 'typeof'])
1951 # C++ ISO/IEC 14882:2003 2.11
1952 cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1953 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1954 'namespace', 'new', 'operator', 'private', 'protected',
1955 'public', 'reinterpret_cast', 'static_cast', 'template',
1956 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1957 'using', 'virtual', 'wchar_t',
1958 # alternative representations
1959 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1960 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1961 # namespace pollution:
1962 polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
1963 name = name.translate(c_name_trans)
1964 if protect and (name in c89_words | c99_words | c11_words | gcc_words
1965 | cpp_words | polluted_words):
1966 return 'q_' + name
1967 return name
1968
1969
1970 eatspace = '\033EATSPACE.'
1971 pointer_suffix = ' *' + eatspace
1972
1973
1974 def genindent(count):
1975 ret = ''
1976 for _ in range(count):
1977 ret += ' '
1978 return ret
1979
1980
1981 indent_level = 0
1982
1983
1984 def push_indent(indent_amount=4):
1985 global indent_level
1986 indent_level += indent_amount
1987
1988
1989 def pop_indent(indent_amount=4):
1990 global indent_level
1991 indent_level -= indent_amount
1992
1993
1994 # Generate @code with @kwds interpolated.
1995 # Obey indent_level, and strip eatspace.
1996 def cgen(code, **kwds):
1997 raw = code % kwds
1998 if indent_level:
1999 indent = genindent(indent_level)
2000 # re.subn() lacks flags support before Python 2.7, use re.compile()
2001 raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
2002 indent, raw)
2003 raw = raw[0]
2004 return re.sub(re.escape(eatspace) + r' *', '', raw)
2005
2006
2007 def mcgen(code, **kwds):
2008 if code[0] == '\n':
2009 code = code[1:]
2010 return cgen(code, **kwds)
2011
2012
2013 def guardname(filename):
2014 return re.sub(r'[^A-Za-z0-9_]', '_', filename).upper()
2015
2016
2017 def guardstart(name):
2018 return mcgen('''
2019 #ifndef %(name)s
2020 #define %(name)s
2021
2022 ''',
2023 name=guardname(name))
2024
2025
2026 def guardend(name):
2027 return mcgen('''
2028
2029 #endif /* %(name)s */
2030 ''',
2031 name=guardname(name))
2032
2033
2034 def gen_if(ifcond):
2035 ret = ''
2036 for ifc in ifcond:
2037 ret += mcgen('''
2038 #if %(cond)s
2039 ''', cond=ifc)
2040 return ret
2041
2042
2043 def gen_endif(ifcond):
2044 ret = ''
2045 for ifc in reversed(ifcond):
2046 ret += mcgen('''
2047 #endif /* %(cond)s */
2048 ''', cond=ifc)
2049 return ret
2050
2051
2052 def _wrap_ifcond(ifcond, before, after):
2053 if before == after:
2054 return after # suppress empty #if ... #endif
2055
2056 assert after.startswith(before)
2057 out = before
2058 added = after[len(before):]
2059 if added[0] == '\n':
2060 out += '\n'
2061 added = added[1:]
2062 out += gen_if(ifcond)
2063 out += added
2064 out += gen_endif(ifcond)
2065 return out
2066
2067
2068 def gen_enum_lookup(name, members, prefix=None):
2069 ret = mcgen('''
2070
2071 const QEnumLookup %(c_name)s_lookup = {
2072 .array = (const char *const[]) {
2073 ''',
2074 c_name=c_name(name))
2075 for m in members:
2076 index = c_enum_const(name, m.name, prefix)
2077 ret += mcgen('''
2078 [%(index)s] = "%(name)s",
2079 ''',
2080 index=index, name=m.name)
2081
2082 ret += mcgen('''
2083 },
2084 .size = %(max_index)s
2085 };
2086 ''',
2087 max_index=c_enum_const(name, '_MAX', prefix))
2088 return ret
2089
2090
2091 def gen_enum(name, members, prefix=None):
2092 # append automatically generated _MAX value
2093 enum_members = members + [QAPISchemaMember('_MAX')]
2094
2095 ret = mcgen('''
2096
2097 typedef enum %(c_name)s {
2098 ''',
2099 c_name=c_name(name))
2100
2101 for m in enum_members:
2102 ret += mcgen('''
2103 %(c_enum)s,
2104 ''',
2105 c_enum=c_enum_const(name, m.name, prefix))
2106
2107 ret += mcgen('''
2108 } %(c_name)s;
2109 ''',
2110 c_name=c_name(name))
2111
2112 ret += mcgen('''
2113
2114 #define %(c_name)s_str(val) \\
2115 qapi_enum_lookup(&%(c_name)s_lookup, (val))
2116
2117 extern const QEnumLookup %(c_name)s_lookup;
2118 ''',
2119 c_name=c_name(name))
2120 return ret
2121
2122
2123 def build_params(arg_type, boxed, extra=None):
2124 ret = ''
2125 sep = ''
2126 if boxed:
2127 assert arg_type
2128 ret += '%s arg' % arg_type.c_param_type()
2129 sep = ', '
2130 elif arg_type:
2131 assert not arg_type.variants
2132 for memb in arg_type.members:
2133 ret += sep
2134 sep = ', '
2135 if memb.optional:
2136 ret += 'bool has_%s, ' % c_name(memb.name)
2137 ret += '%s %s' % (memb.type.c_param_type(),
2138 c_name(memb.name))
2139 if extra:
2140 ret += sep + extra
2141 return ret if ret else 'void'
2142
2143
2144 #
2145 # Accumulate and write output
2146 #
2147
2148 class QAPIGen(object):
2149
2150 def __init__(self):
2151 self._preamble = ''
2152 self._body = ''
2153
2154 def preamble_add(self, text):
2155 self._preamble += text
2156
2157 def add(self, text):
2158 self._body += text
2159
2160 def get_content(self, fname=None):
2161 return (self._top(fname) + self._preamble + self._body
2162 + self._bottom(fname))
2163
2164 def _top(self, fname):
2165 return ''
2166
2167 def _bottom(self, fname):
2168 return ''
2169
2170 def write(self, output_dir, fname):
2171 pathname = os.path.join(output_dir, fname)
2172 dir = os.path.dirname(pathname)
2173 if dir:
2174 try:
2175 os.makedirs(dir)
2176 except os.error as e:
2177 if e.errno != errno.EEXIST:
2178 raise
2179 fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
2180 if sys.version_info[0] >= 3:
2181 f = open(fd, 'r+', encoding='utf-8')
2182 else:
2183 f = os.fdopen(fd, 'r+')
2184 text = self.get_content(fname)
2185 oldtext = f.read(len(text) + 1)
2186 if text != oldtext:
2187 f.seek(0)
2188 f.truncate(0)
2189 f.write(text)
2190 f.close()
2191
2192
2193 @contextmanager
2194 def ifcontext(ifcond, *args):
2195 """A 'with' statement context manager to wrap with start_if()/end_if()
2196
2197 *args: any number of QAPIGenCCode
2198
2199 Example::
2200
2201 with ifcontext(ifcond, self._genh, self._genc):
2202 modify self._genh and self._genc ...
2203
2204 Is equivalent to calling::
2205
2206 self._genh.start_if(ifcond)
2207 self._genc.start_if(ifcond)
2208 modify self._genh and self._genc ...
2209 self._genh.end_if()
2210 self._genc.end_if()
2211 """
2212 for arg in args:
2213 arg.start_if(ifcond)
2214 yield
2215 for arg in args:
2216 arg.end_if()
2217
2218
2219 class QAPIGenCCode(QAPIGen):
2220
2221 def __init__(self):
2222 QAPIGen.__init__(self)
2223 self._start_if = None
2224
2225 def start_if(self, ifcond):
2226 assert self._start_if is None
2227 self._start_if = (ifcond, self._body, self._preamble)
2228
2229 def end_if(self):
2230 assert self._start_if
2231 self._wrap_ifcond()
2232 self._start_if = None
2233
2234 def _wrap_ifcond(self):
2235 self._body = _wrap_ifcond(self._start_if[0],
2236 self._start_if[1], self._body)
2237 self._preamble = _wrap_ifcond(self._start_if[0],
2238 self._start_if[2], self._preamble)
2239
2240 def get_content(self, fname=None):
2241 assert self._start_if is None
2242 return QAPIGen.get_content(self, fname)
2243
2244
2245 class QAPIGenC(QAPIGenCCode):
2246
2247 def __init__(self, blurb, pydoc):
2248 QAPIGenCCode.__init__(self)
2249 self._blurb = blurb
2250 self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
2251 re.MULTILINE))
2252
2253 def _top(self, fname):
2254 return mcgen('''
2255 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2256
2257 /*
2258 %(blurb)s
2259 *
2260 * %(copyright)s
2261 *
2262 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2263 * See the COPYING.LIB file in the top-level directory.
2264 */
2265
2266 ''',
2267 blurb=self._blurb, copyright=self._copyright)
2268
2269 def _bottom(self, fname):
2270 return mcgen('''
2271
2272 /* Dummy declaration to prevent empty .o file */
2273 char dummy_%(name)s;
2274 ''',
2275 name=c_name(fname))
2276
2277
2278 class QAPIGenH(QAPIGenC):
2279
2280 def _top(self, fname):
2281 return QAPIGenC._top(self, fname) + guardstart(fname)
2282
2283 def _bottom(self, fname):
2284 return guardend(fname)
2285
2286
2287 class QAPIGenDoc(QAPIGen):
2288
2289 def _top(self, fname):
2290 return (QAPIGen._top(self, fname)
2291 + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2292
2293
2294 class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
2295
2296 def __init__(self, prefix, what, blurb, pydoc):
2297 self._prefix = prefix
2298 self._what = what
2299 self._genc = QAPIGenC(blurb, pydoc)
2300 self._genh = QAPIGenH(blurb, pydoc)
2301
2302 def write(self, output_dir):
2303 self._genc.write(output_dir, self._prefix + self._what + '.c')
2304 self._genh.write(output_dir, self._prefix + self._what + '.h')
2305
2306
2307 class QAPISchemaModularCVisitor(QAPISchemaVisitor):
2308
2309 def __init__(self, prefix, what, blurb, pydoc):
2310 self._prefix = prefix
2311 self._what = what
2312 self._blurb = blurb
2313 self._pydoc = pydoc
2314 self._module = {}
2315 self._main_module = None
2316
2317 def _module_basename(self, what, name):
2318 if name is None:
2319 return re.sub(r'-', '-builtin-', what)
2320 basename = os.path.join(os.path.dirname(name),
2321 self._prefix + what)
2322 if name == self._main_module:
2323 return basename
2324 return basename + '-' + os.path.splitext(os.path.basename(name))[0]
2325
2326 def _add_module(self, name, blurb):
2327 if self._main_module is None and name is not None:
2328 self._main_module = name
2329 genc = QAPIGenC(blurb, self._pydoc)
2330 genh = QAPIGenH(blurb, self._pydoc)
2331 self._module[name] = (genc, genh)
2332 self._set_module(name)
2333
2334 def _set_module(self, name):
2335 self._genc, self._genh = self._module[name]
2336
2337 def write(self, output_dir, opt_builtins=False):
2338 for name in self._module:
2339 if name is None and not opt_builtins:
2340 continue
2341 basename = self._module_basename(self._what, name)
2342 (genc, genh) = self._module[name]
2343 genc.write(output_dir, basename + '.c')
2344 genh.write(output_dir, basename + '.h')
2345
2346 def _begin_module(self, name):
2347 pass
2348
2349 def visit_module(self, name):
2350 if name in self._module:
2351 self._set_module(name)
2352 return
2353 self._add_module(name, self._blurb)
2354 self._begin_module(name)
2355
2356 def visit_include(self, name, info):
2357 basename = self._module_basename(self._what, name)
2358 self._genh.preamble_add(mcgen('''
2359 #include "%(basename)s.h"
2360 ''',
2361 basename=basename))