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