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