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