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