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