xref: /openbmc/qemu/scripts/qapi/common.py (revision 563bd35d87f7bcab644381319d6247e8aee8a4f4)
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_type = base_members.get(discriminator)
592    if not discriminator_type:
593        return None
594
595    return enum_types.get(discriminator_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_type(info, "Member '%s' of %s" % (key, source), arg,
708                   allow_array=True,
709                   allow_metas=['built-in', 'union', 'alternate', 'struct',
710                                'enum'])
711
712
713def check_command(expr, info):
714    name = expr['command']
715    boxed = expr.get('boxed', False)
716
717    args_meta = ['struct']
718    if boxed:
719        args_meta += ['union', 'alternate']
720    check_type(info, "'data' for command '%s'" % name,
721               expr.get('data'), allow_dict=not boxed, allow_optional=True,
722               allow_metas=args_meta)
723    returns_meta = ['union', 'struct']
724    if name in returns_whitelist:
725        returns_meta += ['built-in', 'alternate', 'enum']
726    check_type(info, "'returns' for command '%s'" % name,
727               expr.get('returns'), allow_array=True,
728               allow_optional=True, allow_metas=returns_meta)
729
730
731def check_event(expr, info):
732    name = expr['event']
733    boxed = expr.get('boxed', False)
734
735    meta = ['struct']
736    if boxed:
737        meta += ['union', 'alternate']
738    check_type(info, "'data' for event '%s'" % name,
739               expr.get('data'), allow_dict=not boxed, allow_optional=True,
740               allow_metas=meta)
741
742
743def check_union(expr, info):
744    name = expr['union']
745    base = expr.get('base')
746    discriminator = expr.get('discriminator')
747    members = expr['data']
748
749    # Two types of unions, determined by discriminator.
750
751    # With no discriminator it is a simple union.
752    if discriminator is None:
753        enum_define = None
754        allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
755        if base is not None:
756            raise QAPISemError(info, "Simple union '%s' must not have a base" %
757                               name)
758
759    # Else, it's a flat union.
760    else:
761        # The object must have a string or dictionary 'base'.
762        check_type(info, "'base' for union '%s'" % name,
763                   base, allow_dict=True, allow_optional=True,
764                   allow_metas=['struct'])
765        if not base:
766            raise QAPISemError(info, "Flat union '%s' must have a base"
767                               % name)
768        base_members = find_base_members(base)
769        assert base_members is not None
770
771        # The value of member 'discriminator' must name a non-optional
772        # member of the base struct.
773        check_name(info, "Discriminator of flat union '%s'" % name,
774                   discriminator)
775        discriminator_type = base_members.get(discriminator)
776        if not discriminator_type:
777            raise QAPISemError(info,
778                               "Discriminator '%s' is not a member of base "
779                               "struct '%s'"
780                               % (discriminator, base))
781        enum_define = enum_types.get(discriminator_type)
782        allow_metas = ['struct']
783        # Do not allow string discriminator
784        if not enum_define:
785            raise QAPISemError(info,
786                               "Discriminator '%s' must be of enumeration "
787                               "type" % discriminator)
788
789    # Check every branch; don't allow an empty union
790    if len(members) == 0:
791        raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
792    for (key, value) in members.items():
793        check_name(info, "Member of union '%s'" % name, key)
794
795        # Each value must name a known type
796        check_type(info, "Member '%s' of union '%s'" % (key, name),
797                   value, allow_array=not base, allow_metas=allow_metas)
798
799        # If the discriminator names an enum type, then all members
800        # of 'data' must also be members of the enum type.
801        if enum_define:
802            if key not in enum_define['data']:
803                raise QAPISemError(info,
804                                   "Discriminator value '%s' is not found in "
805                                   "enum '%s'"
806                                   % (key, enum_define['enum']))
807
808
809def check_alternate(expr, info):
810    name = expr['alternate']
811    members = expr['data']
812    types_seen = {}
813
814    # Check every branch; require at least two branches
815    if len(members) < 2:
816        raise QAPISemError(info,
817                           "Alternate '%s' should have at least two branches "
818                           "in 'data'" % name)
819    for (key, value) in members.items():
820        check_name(info, "Member of alternate '%s'" % name, key)
821
822        # Ensure alternates have no type conflicts.
823        check_type(info, "Member '%s' of alternate '%s'" % (key, name),
824                   value,
825                   allow_metas=['built-in', 'union', 'struct', 'enum'])
826        qtype = find_alternate_member_qtype(value)
827        if not qtype:
828            raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
829                               "type '%s'" % (name, key, value))
830        conflicting = set([qtype])
831        if qtype == 'QTYPE_QSTRING':
832            enum_expr = enum_types.get(value)
833            if enum_expr:
834                for v in enum_expr['data']:
835                    if v in ['on', 'off']:
836                        conflicting.add('QTYPE_QBOOL')
837                    if re.match(r'[-+0-9.]', v): # lazy, could be tightened
838                        conflicting.add('QTYPE_QNUM')
839            else:
840                conflicting.add('QTYPE_QNUM')
841                conflicting.add('QTYPE_QBOOL')
842        for qt in conflicting:
843            if qt in types_seen:
844                raise QAPISemError(info, "Alternate '%s' member '%s' can't "
845                                   "be distinguished from member '%s'"
846                                   % (name, key, types_seen[qt]))
847            types_seen[qt] = key
848
849
850def check_enum(expr, info):
851    name = expr['enum']
852    members = expr.get('data')
853    prefix = expr.get('prefix')
854
855    if not isinstance(members, list):
856        raise QAPISemError(info,
857                           "Enum '%s' requires an array for 'data'" % name)
858    if prefix is not None and not isinstance(prefix, str):
859        raise QAPISemError(info,
860                           "Enum '%s' requires a string for 'prefix'" % name)
861    for member in members:
862        check_name(info, "Member of enum '%s'" % name, member,
863                   enum_member=True)
864
865
866def check_struct(expr, info):
867    name = expr['struct']
868    members = expr['data']
869
870    check_type(info, "'data' for struct '%s'" % name, members,
871               allow_dict=True, allow_optional=True)
872    check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
873               allow_metas=['struct'])
874
875
876def check_known_keys(info, source, keys, required, optional):
877    for key in keys:
878        if key not in required and key not in optional:
879            raise QAPISemError(info, "Unknown key '%s' in %s" % (key, source))
880
881    for key in required:
882        if key not in keys:
883            raise QAPISemError(info, "Key '%s' is missing from %s"
884                               % (key, source))
885
886
887def check_keys(expr_elem, meta, required, optional=[]):
888    expr = expr_elem['expr']
889    info = expr_elem['info']
890    name = expr[meta]
891    if not isinstance(name, str):
892        raise QAPISemError(info, "'%s' key must have a string value" % meta)
893    required = required + [meta]
894    source = "%s '%s'" % (meta, name)
895    check_known_keys(info, source, expr.keys(), required, optional)
896    for (key, value) in expr.items():
897        if key in ['gen', 'success-response'] and value is not False:
898            raise QAPISemError(info,
899                               "'%s' of %s '%s' should only use false value"
900                               % (key, meta, name))
901        if (key in ['boxed', 'allow-oob', 'allow-preconfig']
902                and value is not True):
903            raise QAPISemError(info,
904                               "'%s' of %s '%s' should only use true value"
905                               % (key, meta, name))
906        if key == 'if':
907            check_if(expr, info)
908
909
910def check_exprs(exprs):
911    global all_names
912
913    # Populate name table with names of built-in types
914    for builtin in builtin_types.keys():
915        all_names[builtin] = 'built-in'
916
917    # Learn the types and check for valid expression keys
918    for expr_elem in exprs:
919        expr = expr_elem['expr']
920        info = expr_elem['info']
921        doc = expr_elem.get('doc')
922
923        if 'include' in expr:
924            continue
925
926        if not doc and doc_required:
927            raise QAPISemError(info,
928                               "Expression missing documentation comment")
929
930        if 'enum' in expr:
931            meta = 'enum'
932            check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix'])
933            enum_types[expr[meta]] = expr
934        elif 'union' in expr:
935            meta = 'union'
936            check_keys(expr_elem, 'union', ['data'],
937                       ['base', 'discriminator', 'if'])
938            union_types[expr[meta]] = expr
939        elif 'alternate' in expr:
940            meta = 'alternate'
941            check_keys(expr_elem, 'alternate', ['data'], ['if'])
942        elif 'struct' in expr:
943            meta = 'struct'
944            check_keys(expr_elem, 'struct', ['data'], ['base', 'if'])
945            struct_types[expr[meta]] = expr
946        elif 'command' in expr:
947            meta = 'command'
948            check_keys(expr_elem, 'command', [],
949                       ['data', 'returns', 'gen', 'success-response',
950                        'boxed', 'allow-oob', 'allow-preconfig', 'if'])
951        elif 'event' in expr:
952            meta = 'event'
953            check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if'])
954        else:
955            raise QAPISemError(expr_elem['info'],
956                               "Expression is missing metatype")
957        name = expr[meta]
958        add_name(name, info, meta)
959        if doc and doc.symbol != name:
960            raise QAPISemError(info, "Definition of '%s' follows documentation"
961                               " for '%s'" % (name, doc.symbol))
962
963    # Try again for hidden UnionKind enum
964    for expr_elem in exprs:
965        expr = expr_elem['expr']
966
967        if 'include' in expr:
968            continue
969        if 'union' in expr and not discriminator_find_enum_define(expr):
970            name = '%sKind' % expr['union']
971        elif 'alternate' in expr:
972            name = '%sKind' % expr['alternate']
973        else:
974            continue
975        enum_types[name] = {'enum': name}
976        add_name(name, info, 'enum', implicit=True)
977
978    # Validate that exprs make sense
979    for expr_elem in exprs:
980        expr = expr_elem['expr']
981        info = expr_elem['info']
982        doc = expr_elem.get('doc')
983
984        if 'include' in expr:
985            continue
986        if 'enum' in expr:
987            check_enum(expr, info)
988        elif 'union' in expr:
989            check_union(expr, info)
990        elif 'alternate' in expr:
991            check_alternate(expr, info)
992        elif 'struct' in expr:
993            check_struct(expr, info)
994        elif 'command' in expr:
995            check_command(expr, info)
996        elif 'event' in expr:
997            check_event(expr, info)
998        else:
999            assert False, 'unexpected meta type'
1000
1001        if doc:
1002            doc.check_expr(expr)
1003
1004    return exprs
1005
1006
1007#
1008# Schema compiler frontend
1009#
1010
1011def listify_cond(ifcond):
1012    if not ifcond:
1013        return []
1014    if not isinstance(ifcond, list):
1015        return [ifcond]
1016    return ifcond
1017
1018
1019class QAPISchemaEntity(object):
1020    def __init__(self, name, info, doc, ifcond=None):
1021        assert name is None or isinstance(name, str)
1022        self.name = name
1023        self.module = None
1024        # For explicitly defined entities, info points to the (explicit)
1025        # definition.  For builtins (and their arrays), info is None.
1026        # For implicitly defined entities, info points to a place that
1027        # triggered the implicit definition (there may be more than one
1028        # such place).
1029        self.info = info
1030        self.doc = doc
1031        self._ifcond = ifcond  # self.ifcond is set only after .check()
1032
1033    def c_name(self):
1034        return c_name(self.name)
1035
1036    def check(self, schema):
1037        if isinstance(self._ifcond, QAPISchemaType):
1038            # inherit the condition from a type
1039            typ = self._ifcond
1040            typ.check(schema)
1041            self.ifcond = typ.ifcond
1042        else:
1043            self.ifcond = listify_cond(self._ifcond)
1044
1045    def is_implicit(self):
1046        return not self.info
1047
1048    def visit(self, visitor):
1049        pass
1050
1051
1052class QAPISchemaVisitor(object):
1053    def visit_begin(self, schema):
1054        pass
1055
1056    def visit_end(self):
1057        pass
1058
1059    def visit_module(self, fname):
1060        pass
1061
1062    def visit_needed(self, entity):
1063        # Default to visiting everything
1064        return True
1065
1066    def visit_include(self, fname, info):
1067        pass
1068
1069    def visit_builtin_type(self, name, info, json_type):
1070        pass
1071
1072    def visit_enum_type(self, name, info, ifcond, members, prefix):
1073        pass
1074
1075    def visit_array_type(self, name, info, ifcond, element_type):
1076        pass
1077
1078    def visit_object_type(self, name, info, ifcond, base, members, variants):
1079        pass
1080
1081    def visit_object_type_flat(self, name, info, ifcond, members, variants):
1082        pass
1083
1084    def visit_alternate_type(self, name, info, ifcond, variants):
1085        pass
1086
1087    def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
1088                      success_response, boxed, allow_oob, allow_preconfig):
1089        pass
1090
1091    def visit_event(self, name, info, ifcond, arg_type, boxed):
1092        pass
1093
1094
1095class QAPISchemaInclude(QAPISchemaEntity):
1096
1097    def __init__(self, fname, info):
1098        QAPISchemaEntity.__init__(self, None, info, None)
1099        self.fname = fname
1100
1101    def visit(self, visitor):
1102        visitor.visit_include(self.fname, self.info)
1103
1104
1105class QAPISchemaType(QAPISchemaEntity):
1106    # Return the C type for common use.
1107    # For the types we commonly box, this is a pointer type.
1108    def c_type(self):
1109        pass
1110
1111    # Return the C type to be used in a parameter list.
1112    def c_param_type(self):
1113        return self.c_type()
1114
1115    # Return the C type to be used where we suppress boxing.
1116    def c_unboxed_type(self):
1117        return self.c_type()
1118
1119    def json_type(self):
1120        pass
1121
1122    def alternate_qtype(self):
1123        json2qtype = {
1124            'null':    'QTYPE_QNULL',
1125            'string':  'QTYPE_QSTRING',
1126            'number':  'QTYPE_QNUM',
1127            'int':     'QTYPE_QNUM',
1128            'boolean': 'QTYPE_QBOOL',
1129            'object':  'QTYPE_QDICT'
1130        }
1131        return json2qtype.get(self.json_type())
1132
1133    def doc_type(self):
1134        if self.is_implicit():
1135            return None
1136        return self.name
1137
1138
1139class QAPISchemaBuiltinType(QAPISchemaType):
1140    def __init__(self, name, json_type, c_type):
1141        QAPISchemaType.__init__(self, name, None, None)
1142        assert not c_type or isinstance(c_type, str)
1143        assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1144                             'value')
1145        self._json_type_name = json_type
1146        self._c_type_name = c_type
1147
1148    def c_name(self):
1149        return self.name
1150
1151    def c_type(self):
1152        return self._c_type_name
1153
1154    def c_param_type(self):
1155        if self.name == 'str':
1156            return 'const ' + self._c_type_name
1157        return self._c_type_name
1158
1159    def json_type(self):
1160        return self._json_type_name
1161
1162    def doc_type(self):
1163        return self.json_type()
1164
1165    def visit(self, visitor):
1166        visitor.visit_builtin_type(self.name, self.info, self.json_type())
1167
1168
1169class QAPISchemaEnumType(QAPISchemaType):
1170    def __init__(self, name, info, doc, ifcond, members, prefix):
1171        QAPISchemaType.__init__(self, name, info, doc, ifcond)
1172        for m in members:
1173            assert isinstance(m, QAPISchemaMember)
1174            m.set_owner(name)
1175        assert prefix is None or isinstance(prefix, str)
1176        self.members = members
1177        self.prefix = prefix
1178
1179    def check(self, schema):
1180        QAPISchemaType.check(self, schema)
1181        seen = {}
1182        for m in self.members:
1183            m.check_clash(self.info, seen)
1184            if self.doc:
1185                self.doc.connect_member(m)
1186
1187    def is_implicit(self):
1188        # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1189        return self.name.endswith('Kind') or self.name == 'QType'
1190
1191    def c_type(self):
1192        return c_name(self.name)
1193
1194    def member_names(self):
1195        return [m.name for m in self.members]
1196
1197    def json_type(self):
1198        return 'string'
1199
1200    def visit(self, visitor):
1201        visitor.visit_enum_type(self.name, self.info, self.ifcond,
1202                                self.members, self.prefix)
1203
1204
1205class QAPISchemaArrayType(QAPISchemaType):
1206    def __init__(self, name, info, element_type):
1207        QAPISchemaType.__init__(self, name, info, None, None)
1208        assert isinstance(element_type, str)
1209        self._element_type_name = element_type
1210        self.element_type = None
1211
1212    def check(self, schema):
1213        QAPISchemaType.check(self, schema)
1214        self.element_type = schema.lookup_type(self._element_type_name)
1215        assert self.element_type
1216        self.element_type.check(schema)
1217        self.ifcond = self.element_type.ifcond
1218
1219    def is_implicit(self):
1220        return True
1221
1222    def c_type(self):
1223        return c_name(self.name) + pointer_suffix
1224
1225    def json_type(self):
1226        return 'array'
1227
1228    def doc_type(self):
1229        elt_doc_type = self.element_type.doc_type()
1230        if not elt_doc_type:
1231            return None
1232        return 'array of ' + elt_doc_type
1233
1234    def visit(self, visitor):
1235        visitor.visit_array_type(self.name, self.info, self.ifcond,
1236                                 self.element_type)
1237
1238
1239class QAPISchemaObjectType(QAPISchemaType):
1240    def __init__(self, name, info, doc, ifcond,
1241                 base, local_members, variants):
1242        # struct has local_members, optional base, and no variants
1243        # flat union has base, variants, and no local_members
1244        # simple union has local_members, variants, and no base
1245        QAPISchemaType.__init__(self, name, info, doc, ifcond)
1246        assert base is None or isinstance(base, str)
1247        for m in local_members:
1248            assert isinstance(m, QAPISchemaObjectTypeMember)
1249            m.set_owner(name)
1250        if variants is not None:
1251            assert isinstance(variants, QAPISchemaObjectTypeVariants)
1252            variants.set_owner(name)
1253        self._base_name = base
1254        self.base = None
1255        self.local_members = local_members
1256        self.variants = variants
1257        self.members = None
1258
1259    def check(self, schema):
1260        QAPISchemaType.check(self, schema)
1261        if self.members is False:               # check for cycles
1262            raise QAPISemError(self.info,
1263                               "Object %s contains itself" % self.name)
1264        if self.members:
1265            return
1266        self.members = False                    # mark as being checked
1267        seen = OrderedDict()
1268        if self._base_name:
1269            self.base = schema.lookup_type(self._base_name)
1270            assert isinstance(self.base, QAPISchemaObjectType)
1271            self.base.check(schema)
1272            self.base.check_clash(self.info, seen)
1273        for m in self.local_members:
1274            m.check(schema)
1275            m.check_clash(self.info, seen)
1276            if self.doc:
1277                self.doc.connect_member(m)
1278        self.members = seen.values()
1279        if self.variants:
1280            self.variants.check(schema, seen)
1281            assert self.variants.tag_member in self.members
1282            self.variants.check_clash(self.info, seen)
1283        if self.doc:
1284            self.doc.check()
1285
1286    # Check that the members of this type do not cause duplicate JSON members,
1287    # and update seen to track the members seen so far. Report any errors
1288    # on behalf of info, which is not necessarily self.info
1289    def check_clash(self, info, seen):
1290        assert not self.variants       # not implemented
1291        for m in self.members:
1292            m.check_clash(info, seen)
1293
1294    def is_implicit(self):
1295        # See QAPISchema._make_implicit_object_type(), as well as
1296        # _def_predefineds()
1297        return self.name.startswith('q_')
1298
1299    def is_empty(self):
1300        assert self.members is not None
1301        return not self.members and not self.variants
1302
1303    def c_name(self):
1304        assert self.name != 'q_empty'
1305        return QAPISchemaType.c_name(self)
1306
1307    def c_type(self):
1308        assert not self.is_implicit()
1309        return c_name(self.name) + pointer_suffix
1310
1311    def c_unboxed_type(self):
1312        return c_name(self.name)
1313
1314    def json_type(self):
1315        return 'object'
1316
1317    def visit(self, visitor):
1318        visitor.visit_object_type(self.name, self.info, self.ifcond,
1319                                  self.base, self.local_members, self.variants)
1320        visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
1321                                       self.members, self.variants)
1322
1323
1324class QAPISchemaMember(object):
1325    role = 'member'
1326
1327    def __init__(self, name):
1328        assert isinstance(name, str)
1329        self.name = name
1330        self.owner = None
1331
1332    def set_owner(self, name):
1333        assert not self.owner
1334        self.owner = name
1335
1336    def check_clash(self, info, seen):
1337        cname = c_name(self.name)
1338        if cname.lower() != cname and self.owner not in name_case_whitelist:
1339            raise QAPISemError(info,
1340                               "%s should not use uppercase" % self.describe())
1341        if cname in seen:
1342            raise QAPISemError(info, "%s collides with %s" %
1343                               (self.describe(), seen[cname].describe()))
1344        seen[cname] = self
1345
1346    def _pretty_owner(self):
1347        owner = self.owner
1348        if owner.startswith('q_obj_'):
1349            # See QAPISchema._make_implicit_object_type() - reverse the
1350            # mapping there to create a nice human-readable description
1351            owner = owner[6:]
1352            if owner.endswith('-arg'):
1353                return '(parameter of %s)' % owner[:-4]
1354            elif owner.endswith('-base'):
1355                return '(base of %s)' % owner[:-5]
1356            else:
1357                assert owner.endswith('-wrapper')
1358                # Unreachable and not implemented
1359                assert False
1360        if owner.endswith('Kind'):
1361            # See QAPISchema._make_implicit_enum_type()
1362            return '(branch of %s)' % owner[:-4]
1363        return '(%s of %s)' % (self.role, owner)
1364
1365    def describe(self):
1366        return "'%s' %s" % (self.name, self._pretty_owner())
1367
1368
1369class QAPISchemaObjectTypeMember(QAPISchemaMember):
1370    def __init__(self, name, typ, optional):
1371        QAPISchemaMember.__init__(self, name)
1372        assert isinstance(typ, str)
1373        assert isinstance(optional, bool)
1374        self._type_name = typ
1375        self.type = None
1376        self.optional = optional
1377
1378    def check(self, schema):
1379        assert self.owner
1380        self.type = schema.lookup_type(self._type_name)
1381        assert self.type
1382
1383
1384class QAPISchemaObjectTypeVariants(object):
1385    def __init__(self, tag_name, tag_member, variants):
1386        # Flat unions pass tag_name but not tag_member.
1387        # Simple unions and alternates pass tag_member but not tag_name.
1388        # After check(), tag_member is always set, and tag_name remains
1389        # a reliable witness of being used by a flat union.
1390        assert bool(tag_member) != bool(tag_name)
1391        assert (isinstance(tag_name, str) or
1392                isinstance(tag_member, QAPISchemaObjectTypeMember))
1393        assert len(variants) > 0
1394        for v in variants:
1395            assert isinstance(v, QAPISchemaObjectTypeVariant)
1396        self._tag_name = tag_name
1397        self.tag_member = tag_member
1398        self.variants = variants
1399
1400    def set_owner(self, name):
1401        for v in self.variants:
1402            v.set_owner(name)
1403
1404    def check(self, schema, seen):
1405        if not self.tag_member:    # flat union
1406            self.tag_member = seen[c_name(self._tag_name)]
1407            assert self._tag_name == self.tag_member.name
1408        assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1409        if self._tag_name:    # flat union
1410            # branches that are not explicitly covered get an empty type
1411            cases = set([v.name for v in self.variants])
1412            for m in self.tag_member.type.members:
1413                if m.name not in cases:
1414                    v = QAPISchemaObjectTypeVariant(m.name, 'q_empty')
1415                    v.set_owner(self.tag_member.owner)
1416                    self.variants.append(v)
1417        for v in self.variants:
1418            v.check(schema)
1419            # Union names must match enum values; alternate names are
1420            # checked separately. Use 'seen' to tell the two apart.
1421            if seen:
1422                assert v.name in self.tag_member.type.member_names()
1423                assert isinstance(v.type, QAPISchemaObjectType)
1424                v.type.check(schema)
1425
1426    def check_clash(self, info, seen):
1427        for v in self.variants:
1428            # Reset seen map for each variant, since qapi names from one
1429            # branch do not affect another branch
1430            assert isinstance(v.type, QAPISchemaObjectType)
1431            v.type.check_clash(info, dict(seen))
1432
1433
1434class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1435    role = 'branch'
1436
1437    def __init__(self, name, typ):
1438        QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1439
1440
1441class QAPISchemaAlternateType(QAPISchemaType):
1442    def __init__(self, name, info, doc, ifcond, variants):
1443        QAPISchemaType.__init__(self, name, info, doc, ifcond)
1444        assert isinstance(variants, QAPISchemaObjectTypeVariants)
1445        assert variants.tag_member
1446        variants.set_owner(name)
1447        variants.tag_member.set_owner(self.name)
1448        self.variants = variants
1449
1450    def check(self, schema):
1451        QAPISchemaType.check(self, schema)
1452        self.variants.tag_member.check(schema)
1453        # Not calling self.variants.check_clash(), because there's nothing
1454        # to clash with
1455        self.variants.check(schema, {})
1456        # Alternate branch names have no relation to the tag enum values;
1457        # so we have to check for potential name collisions ourselves.
1458        seen = {}
1459        for v in self.variants.variants:
1460            v.check_clash(self.info, seen)
1461            if self.doc:
1462                self.doc.connect_member(v)
1463        if self.doc:
1464            self.doc.check()
1465
1466    def c_type(self):
1467        return c_name(self.name) + pointer_suffix
1468
1469    def json_type(self):
1470        return 'value'
1471
1472    def visit(self, visitor):
1473        visitor.visit_alternate_type(self.name, self.info, self.ifcond,
1474                                     self.variants)
1475
1476    def is_empty(self):
1477        return False
1478
1479
1480class QAPISchemaCommand(QAPISchemaEntity):
1481    def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
1482                 gen, success_response, boxed, allow_oob, allow_preconfig):
1483        QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1484        assert not arg_type or isinstance(arg_type, str)
1485        assert not ret_type or isinstance(ret_type, str)
1486        self._arg_type_name = arg_type
1487        self.arg_type = None
1488        self._ret_type_name = ret_type
1489        self.ret_type = None
1490        self.gen = gen
1491        self.success_response = success_response
1492        self.boxed = boxed
1493        self.allow_oob = allow_oob
1494        self.allow_preconfig = allow_preconfig
1495
1496    def check(self, schema):
1497        QAPISchemaEntity.check(self, schema)
1498        if self._arg_type_name:
1499            self.arg_type = schema.lookup_type(self._arg_type_name)
1500            assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1501                    isinstance(self.arg_type, QAPISchemaAlternateType))
1502            self.arg_type.check(schema)
1503            if self.boxed:
1504                if self.arg_type.is_empty():
1505                    raise QAPISemError(self.info,
1506                                       "Cannot use 'boxed' with empty type")
1507            else:
1508                assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1509                assert not self.arg_type.variants
1510        elif self.boxed:
1511            raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1512        if self._ret_type_name:
1513            self.ret_type = schema.lookup_type(self._ret_type_name)
1514            assert isinstance(self.ret_type, QAPISchemaType)
1515
1516    def visit(self, visitor):
1517        visitor.visit_command(self.name, self.info, self.ifcond,
1518                              self.arg_type, self.ret_type,
1519                              self.gen, self.success_response,
1520                              self.boxed, self.allow_oob,
1521                              self.allow_preconfig)
1522
1523
1524class QAPISchemaEvent(QAPISchemaEntity):
1525    def __init__(self, name, info, doc, ifcond, arg_type, boxed):
1526        QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1527        assert not arg_type or isinstance(arg_type, str)
1528        self._arg_type_name = arg_type
1529        self.arg_type = None
1530        self.boxed = boxed
1531
1532    def check(self, schema):
1533        QAPISchemaEntity.check(self, schema)
1534        if self._arg_type_name:
1535            self.arg_type = schema.lookup_type(self._arg_type_name)
1536            assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1537                    isinstance(self.arg_type, QAPISchemaAlternateType))
1538            self.arg_type.check(schema)
1539            if self.boxed:
1540                if self.arg_type.is_empty():
1541                    raise QAPISemError(self.info,
1542                                       "Cannot use 'boxed' with empty type")
1543            else:
1544                assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1545                assert not self.arg_type.variants
1546        elif self.boxed:
1547            raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1548
1549    def visit(self, visitor):
1550        visitor.visit_event(self.name, self.info, self.ifcond,
1551                            self.arg_type, self.boxed)
1552
1553
1554class QAPISchema(object):
1555    def __init__(self, fname):
1556        self._fname = fname
1557        if sys.version_info[0] >= 3:
1558            f = open(fname, 'r', encoding='utf-8')
1559        else:
1560            f = open(fname, 'r')
1561        parser = QAPISchemaParser(f)
1562        exprs = check_exprs(parser.exprs)
1563        self.docs = parser.docs
1564        self._entity_list = []
1565        self._entity_dict = {}
1566        self._predefining = True
1567        self._def_predefineds()
1568        self._predefining = False
1569        self._def_exprs(exprs)
1570        self.check()
1571
1572    def _def_entity(self, ent):
1573        # Only the predefined types are allowed to not have info
1574        assert ent.info or self._predefining
1575        assert ent.name is None or ent.name not in self._entity_dict
1576        self._entity_list.append(ent)
1577        if ent.name is not None:
1578            self._entity_dict[ent.name] = ent
1579        if ent.info:
1580            ent.module = os.path.relpath(ent.info['file'],
1581                                         os.path.dirname(self._fname))
1582
1583    def lookup_entity(self, name, typ=None):
1584        ent = self._entity_dict.get(name)
1585        if typ and not isinstance(ent, typ):
1586            return None
1587        return ent
1588
1589    def lookup_type(self, name):
1590        return self.lookup_entity(name, QAPISchemaType)
1591
1592    def _def_include(self, expr, info, doc):
1593        include = expr['include']
1594        assert doc is None
1595        main_info = info
1596        while main_info['parent']:
1597            main_info = main_info['parent']
1598        fname = os.path.relpath(include, os.path.dirname(main_info['file']))
1599        self._def_entity(QAPISchemaInclude(fname, info))
1600
1601    def _def_builtin_type(self, name, json_type, c_type):
1602        self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1603        # Instantiating only the arrays that are actually used would
1604        # be nice, but we can't as long as their generated code
1605        # (qapi-builtin-types.[ch]) may be shared by some other
1606        # schema.
1607        self._make_array_type(name, None)
1608
1609    def _def_predefineds(self):
1610        for t in [('str',    'string',  'char' + pointer_suffix),
1611                  ('number', 'number',  'double'),
1612                  ('int',    'int',     'int64_t'),
1613                  ('int8',   'int',     'int8_t'),
1614                  ('int16',  'int',     'int16_t'),
1615                  ('int32',  'int',     'int32_t'),
1616                  ('int64',  'int',     'int64_t'),
1617                  ('uint8',  'int',     'uint8_t'),
1618                  ('uint16', 'int',     'uint16_t'),
1619                  ('uint32', 'int',     'uint32_t'),
1620                  ('uint64', 'int',     'uint64_t'),
1621                  ('size',   'int',     'uint64_t'),
1622                  ('bool',   'boolean', 'bool'),
1623                  ('any',    'value',   'QObject' + pointer_suffix),
1624                  ('null',   'null',    'QNull' + pointer_suffix)]:
1625            self._def_builtin_type(*t)
1626        self.the_empty_object_type = QAPISchemaObjectType(
1627            'q_empty', None, None, None, None, [], None)
1628        self._def_entity(self.the_empty_object_type)
1629        qtype_values = self._make_enum_members(['none', 'qnull', 'qnum',
1630                                                'qstring', 'qdict', 'qlist',
1631                                                'qbool'])
1632        self._def_entity(QAPISchemaEnumType('QType', None, None, None,
1633                                            qtype_values, 'QTYPE'))
1634
1635    def _make_enum_members(self, values):
1636        return [QAPISchemaMember(v) for v in values]
1637
1638    def _make_implicit_enum_type(self, name, info, ifcond, values):
1639        # See also QAPISchemaObjectTypeMember._pretty_owner()
1640        name = name + 'Kind'   # Use namespace reserved by add_name()
1641        self._def_entity(QAPISchemaEnumType(
1642            name, info, None, ifcond, self._make_enum_members(values), None))
1643        return name
1644
1645    def _make_array_type(self, element_type, info):
1646        name = element_type + 'List'   # Use namespace reserved by add_name()
1647        if not self.lookup_type(name):
1648            self._def_entity(QAPISchemaArrayType(name, info, element_type))
1649        return name
1650
1651    def _make_implicit_object_type(self, name, info, doc, ifcond,
1652                                   role, members):
1653        if not members:
1654            return None
1655        # See also QAPISchemaObjectTypeMember._pretty_owner()
1656        name = 'q_obj_%s-%s' % (name, role)
1657        typ = self.lookup_entity(name, QAPISchemaObjectType)
1658        if typ:
1659            # The implicit object type has multiple users.  This can
1660            # happen only for simple unions' implicit wrapper types.
1661            # Its ifcond should be the disjunction of its user's
1662            # ifconds.  Not implemented.  Instead, we always pass the
1663            # wrapped type's ifcond, which is trivially the same for all
1664            # users.  It's also necessary for the wrapper to compile.
1665            # But it's not tight: the disjunction need not imply it.  We
1666            # may end up compiling useless wrapper types.
1667            # TODO kill simple unions or implement the disjunction
1668            assert ifcond == typ._ifcond # pylint: disable=protected-access
1669        else:
1670            self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
1671                                                  None, members, None))
1672        return name
1673
1674    def _def_enum_type(self, expr, info, doc):
1675        name = expr['enum']
1676        data = expr['data']
1677        prefix = expr.get('prefix')
1678        ifcond = expr.get('if')
1679        self._def_entity(QAPISchemaEnumType(
1680            name, info, doc, ifcond,
1681            self._make_enum_members(data), prefix))
1682
1683    def _make_member(self, name, typ, info):
1684        optional = False
1685        if name.startswith('*'):
1686            name = name[1:]
1687            optional = True
1688        if isinstance(typ, list):
1689            assert len(typ) == 1
1690            typ = self._make_array_type(typ[0], info)
1691        return QAPISchemaObjectTypeMember(name, typ, optional)
1692
1693    def _make_members(self, data, info):
1694        return [self._make_member(key, value, info)
1695                for (key, value) in data.items()]
1696
1697    def _def_struct_type(self, expr, info, doc):
1698        name = expr['struct']
1699        base = expr.get('base')
1700        data = expr['data']
1701        ifcond = expr.get('if')
1702        self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
1703                                              self._make_members(data, info),
1704                                              None))
1705
1706    def _make_variant(self, case, typ):
1707        return QAPISchemaObjectTypeVariant(case, typ)
1708
1709    def _make_simple_variant(self, case, typ, info):
1710        if isinstance(typ, list):
1711            assert len(typ) == 1
1712            typ = self._make_array_type(typ[0], info)
1713        typ = self._make_implicit_object_type(
1714            typ, info, None, self.lookup_type(typ),
1715            'wrapper', [self._make_member('data', typ, info)])
1716        return QAPISchemaObjectTypeVariant(case, typ)
1717
1718    def _def_union_type(self, expr, info, doc):
1719        name = expr['union']
1720        data = expr['data']
1721        base = expr.get('base')
1722        ifcond = expr.get('if')
1723        tag_name = expr.get('discriminator')
1724        tag_member = None
1725        if isinstance(base, dict):
1726            base = self._make_implicit_object_type(
1727                name, info, doc, ifcond,
1728                'base', self._make_members(base, info))
1729        if tag_name:
1730            variants = [self._make_variant(key, value)
1731                        for (key, value) in data.items()]
1732            members = []
1733        else:
1734            variants = [self._make_simple_variant(key, value, info)
1735                        for (key, value) in data.items()]
1736            typ = self._make_implicit_enum_type(name, info, ifcond,
1737                                                [v.name for v in variants])
1738            tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1739            members = [tag_member]
1740        self._def_entity(
1741            QAPISchemaObjectType(name, info, doc, ifcond, base, members,
1742                                 QAPISchemaObjectTypeVariants(tag_name,
1743                                                              tag_member,
1744                                                              variants)))
1745
1746    def _def_alternate_type(self, expr, info, doc):
1747        name = expr['alternate']
1748        data = expr['data']
1749        ifcond = expr.get('if')
1750        variants = [self._make_variant(key, value)
1751                    for (key, value) in data.items()]
1752        tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
1753        self._def_entity(
1754            QAPISchemaAlternateType(name, info, doc, ifcond,
1755                                    QAPISchemaObjectTypeVariants(None,
1756                                                                 tag_member,
1757                                                                 variants)))
1758
1759    def _def_command(self, expr, info, doc):
1760        name = expr['command']
1761        data = expr.get('data')
1762        rets = expr.get('returns')
1763        gen = expr.get('gen', True)
1764        success_response = expr.get('success-response', True)
1765        boxed = expr.get('boxed', False)
1766        allow_oob = expr.get('allow-oob', False)
1767        allow_preconfig = expr.get('allow-preconfig', False)
1768        ifcond = expr.get('if')
1769        if isinstance(data, OrderedDict):
1770            data = self._make_implicit_object_type(
1771                name, info, doc, ifcond, 'arg', self._make_members(data, info))
1772        if isinstance(rets, list):
1773            assert len(rets) == 1
1774            rets = self._make_array_type(rets[0], info)
1775        self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
1776                                           gen, success_response,
1777                                           boxed, allow_oob, allow_preconfig))
1778
1779    def _def_event(self, expr, info, doc):
1780        name = expr['event']
1781        data = expr.get('data')
1782        boxed = expr.get('boxed', False)
1783        ifcond = expr.get('if')
1784        if isinstance(data, OrderedDict):
1785            data = self._make_implicit_object_type(
1786                name, info, doc, ifcond, 'arg', self._make_members(data, info))
1787        self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
1788
1789    def _def_exprs(self, exprs):
1790        for expr_elem in exprs:
1791            expr = expr_elem['expr']
1792            info = expr_elem['info']
1793            doc = expr_elem.get('doc')
1794            if 'enum' in expr:
1795                self._def_enum_type(expr, info, doc)
1796            elif 'struct' in expr:
1797                self._def_struct_type(expr, info, doc)
1798            elif 'union' in expr:
1799                self._def_union_type(expr, info, doc)
1800            elif 'alternate' in expr:
1801                self._def_alternate_type(expr, info, doc)
1802            elif 'command' in expr:
1803                self._def_command(expr, info, doc)
1804            elif 'event' in expr:
1805                self._def_event(expr, info, doc)
1806            elif 'include' in expr:
1807                self._def_include(expr, info, doc)
1808            else:
1809                assert False
1810
1811    def check(self):
1812        for ent in self._entity_list:
1813            ent.check(self)
1814
1815    def visit(self, visitor):
1816        visitor.visit_begin(self)
1817        module = None
1818        for entity in self._entity_list:
1819            if visitor.visit_needed(entity):
1820                if entity.module != module:
1821                    module = entity.module
1822                    visitor.visit_module(module)
1823                entity.visit(visitor)
1824        visitor.visit_end()
1825
1826
1827#
1828# Code generation helpers
1829#
1830
1831def camel_case(name):
1832    new_name = ''
1833    first = True
1834    for ch in name:
1835        if ch in ['_', '-']:
1836            first = True
1837        elif first:
1838            new_name += ch.upper()
1839            first = False
1840        else:
1841            new_name += ch.lower()
1842    return new_name
1843
1844
1845# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1846# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1847# ENUM24_Name -> ENUM24_NAME
1848def camel_to_upper(value):
1849    c_fun_str = c_name(value, False)
1850    if value.isupper():
1851        return c_fun_str
1852
1853    new_name = ''
1854    length = len(c_fun_str)
1855    for i in range(length):
1856        c = c_fun_str[i]
1857        # When c is upper and no '_' appears before, do more checks
1858        if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
1859            if i < length - 1 and c_fun_str[i + 1].islower():
1860                new_name += '_'
1861            elif c_fun_str[i - 1].isdigit():
1862                new_name += '_'
1863        new_name += c
1864    return new_name.lstrip('_').upper()
1865
1866
1867def c_enum_const(type_name, const_name, prefix=None):
1868    if prefix is not None:
1869        type_name = prefix
1870    return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
1871
1872
1873if hasattr(str, 'maketrans'):
1874    c_name_trans = str.maketrans('.-', '__')
1875else:
1876    c_name_trans = string.maketrans('.-', '__')
1877
1878
1879# Map @name to a valid C identifier.
1880# If @protect, avoid returning certain ticklish identifiers (like
1881# C keywords) by prepending 'q_'.
1882#
1883# Used for converting 'name' from a 'name':'type' qapi definition
1884# into a generated struct member, as well as converting type names
1885# into substrings of a generated C function name.
1886# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1887# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1888def c_name(name, protect=True):
1889    # ANSI X3J11/88-090, 3.1.1
1890    c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
1891                     'default', 'do', 'double', 'else', 'enum', 'extern',
1892                     'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1893                     'return', 'short', 'signed', 'sizeof', 'static',
1894                     'struct', 'switch', 'typedef', 'union', 'unsigned',
1895                     'void', 'volatile', 'while'])
1896    # ISO/IEC 9899:1999, 6.4.1
1897    c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1898    # ISO/IEC 9899:2011, 6.4.1
1899    c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1900                     '_Noreturn', '_Static_assert', '_Thread_local'])
1901    # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1902    # excluding _.*
1903    gcc_words = set(['asm', 'typeof'])
1904    # C++ ISO/IEC 14882:2003 2.11
1905    cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1906                     'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1907                     'namespace', 'new', 'operator', 'private', 'protected',
1908                     'public', 'reinterpret_cast', 'static_cast', 'template',
1909                     'this', 'throw', 'true', 'try', 'typeid', 'typename',
1910                     'using', 'virtual', 'wchar_t',
1911                     # alternative representations
1912                     'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1913                     'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1914    # namespace pollution:
1915    polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
1916    name = name.translate(c_name_trans)
1917    if protect and (name in c89_words | c99_words | c11_words | gcc_words
1918                    | cpp_words | polluted_words):
1919        return 'q_' + name
1920    return name
1921
1922
1923eatspace = '\033EATSPACE.'
1924pointer_suffix = ' *' + eatspace
1925
1926
1927def genindent(count):
1928    ret = ''
1929    for _ in range(count):
1930        ret += ' '
1931    return ret
1932
1933
1934indent_level = 0
1935
1936
1937def push_indent(indent_amount=4):
1938    global indent_level
1939    indent_level += indent_amount
1940
1941
1942def pop_indent(indent_amount=4):
1943    global indent_level
1944    indent_level -= indent_amount
1945
1946
1947# Generate @code with @kwds interpolated.
1948# Obey indent_level, and strip eatspace.
1949def cgen(code, **kwds):
1950    raw = code % kwds
1951    if indent_level:
1952        indent = genindent(indent_level)
1953        # re.subn() lacks flags support before Python 2.7, use re.compile()
1954        raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
1955                      indent, raw)
1956        raw = raw[0]
1957    return re.sub(re.escape(eatspace) + r' *', '', raw)
1958
1959
1960def mcgen(code, **kwds):
1961    if code[0] == '\n':
1962        code = code[1:]
1963    return cgen(code, **kwds)
1964
1965
1966def guardname(filename):
1967    return re.sub(r'[^A-Za-z0-9_]', '_', filename).upper()
1968
1969
1970def guardstart(name):
1971    return mcgen('''
1972#ifndef %(name)s
1973#define %(name)s
1974
1975''',
1976                 name=guardname(name))
1977
1978
1979def guardend(name):
1980    return mcgen('''
1981
1982#endif /* %(name)s */
1983''',
1984                 name=guardname(name))
1985
1986
1987def gen_if(ifcond):
1988    ret = ''
1989    for ifc in ifcond:
1990        ret += mcgen('''
1991#if %(cond)s
1992''', cond=ifc)
1993    return ret
1994
1995
1996def gen_endif(ifcond):
1997    ret = ''
1998    for ifc in reversed(ifcond):
1999        ret += mcgen('''
2000#endif /* %(cond)s */
2001''', cond=ifc)
2002    return ret
2003
2004
2005def _wrap_ifcond(ifcond, before, after):
2006    if before == after:
2007        return after   # suppress empty #if ... #endif
2008
2009    assert after.startswith(before)
2010    out = before
2011    added = after[len(before):]
2012    if added[0] == '\n':
2013        out += '\n'
2014        added = added[1:]
2015    out += gen_if(ifcond)
2016    out += added
2017    out += gen_endif(ifcond)
2018    return out
2019
2020
2021def gen_enum_lookup(name, members, prefix=None):
2022    ret = mcgen('''
2023
2024const QEnumLookup %(c_name)s_lookup = {
2025    .array = (const char *const[]) {
2026''',
2027                c_name=c_name(name))
2028    for m in members:
2029        index = c_enum_const(name, m.name, prefix)
2030        ret += mcgen('''
2031        [%(index)s] = "%(name)s",
2032''',
2033                     index=index, name=m.name)
2034
2035    ret += mcgen('''
2036    },
2037    .size = %(max_index)s
2038};
2039''',
2040                 max_index=c_enum_const(name, '_MAX', prefix))
2041    return ret
2042
2043
2044def gen_enum(name, members, prefix=None):
2045    # append automatically generated _MAX value
2046    enum_members = members + [QAPISchemaMember('_MAX')]
2047
2048    ret = mcgen('''
2049
2050typedef enum %(c_name)s {
2051''',
2052                c_name=c_name(name))
2053
2054    for m in enum_members:
2055        ret += mcgen('''
2056    %(c_enum)s,
2057''',
2058                     c_enum=c_enum_const(name, m.name, prefix))
2059
2060    ret += mcgen('''
2061} %(c_name)s;
2062''',
2063                 c_name=c_name(name))
2064
2065    ret += mcgen('''
2066
2067#define %(c_name)s_str(val) \\
2068    qapi_enum_lookup(&%(c_name)s_lookup, (val))
2069
2070extern const QEnumLookup %(c_name)s_lookup;
2071''',
2072                 c_name=c_name(name))
2073    return ret
2074
2075
2076def build_params(arg_type, boxed, extra=None):
2077    ret = ''
2078    sep = ''
2079    if boxed:
2080        assert arg_type
2081        ret += '%s arg' % arg_type.c_param_type()
2082        sep = ', '
2083    elif arg_type:
2084        assert not arg_type.variants
2085        for memb in arg_type.members:
2086            ret += sep
2087            sep = ', '
2088            if memb.optional:
2089                ret += 'bool has_%s, ' % c_name(memb.name)
2090            ret += '%s %s' % (memb.type.c_param_type(),
2091                              c_name(memb.name))
2092    if extra:
2093        ret += sep + extra
2094    return ret if ret else 'void'
2095
2096
2097#
2098# Accumulate and write output
2099#
2100
2101class QAPIGen(object):
2102
2103    def __init__(self):
2104        self._preamble = ''
2105        self._body = ''
2106
2107    def preamble_add(self, text):
2108        self._preamble += text
2109
2110    def add(self, text):
2111        self._body += text
2112
2113    def get_content(self, fname=None):
2114        return (self._top(fname) + self._preamble + self._body
2115                + self._bottom(fname))
2116
2117    def _top(self, fname):
2118        return ''
2119
2120    def _bottom(self, fname):
2121        return ''
2122
2123    def write(self, output_dir, fname):
2124        pathname = os.path.join(output_dir, fname)
2125        dir = os.path.dirname(pathname)
2126        if dir:
2127            try:
2128                os.makedirs(dir)
2129            except os.error as e:
2130                if e.errno != errno.EEXIST:
2131                    raise
2132        fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
2133        if sys.version_info[0] >= 3:
2134            f = open(fd, 'r+', encoding='utf-8')
2135        else:
2136            f = os.fdopen(fd, 'r+')
2137        text = self.get_content(fname)
2138        oldtext = f.read(len(text) + 1)
2139        if text != oldtext:
2140            f.seek(0)
2141            f.truncate(0)
2142            f.write(text)
2143        f.close()
2144
2145
2146@contextmanager
2147def ifcontext(ifcond, *args):
2148    """A 'with' statement context manager to wrap with start_if()/end_if()
2149
2150    *args: any number of QAPIGenCCode
2151
2152    Example::
2153
2154        with ifcontext(ifcond, self._genh, self._genc):
2155            modify self._genh and self._genc ...
2156
2157    Is equivalent to calling::
2158
2159        self._genh.start_if(ifcond)
2160        self._genc.start_if(ifcond)
2161        modify self._genh and self._genc ...
2162        self._genh.end_if()
2163        self._genc.end_if()
2164    """
2165    for arg in args:
2166        arg.start_if(ifcond)
2167    yield
2168    for arg in args:
2169        arg.end_if()
2170
2171
2172class QAPIGenCCode(QAPIGen):
2173
2174    def __init__(self):
2175        QAPIGen.__init__(self)
2176        self._start_if = None
2177
2178    def start_if(self, ifcond):
2179        assert self._start_if is None
2180        self._start_if = (ifcond, self._body, self._preamble)
2181
2182    def end_if(self):
2183        assert self._start_if
2184        self._wrap_ifcond()
2185        self._start_if = None
2186
2187    def _wrap_ifcond(self):
2188        self._body = _wrap_ifcond(self._start_if[0],
2189                                  self._start_if[1], self._body)
2190        self._preamble = _wrap_ifcond(self._start_if[0],
2191                                      self._start_if[2], self._preamble)
2192
2193    def get_content(self, fname=None):
2194        assert self._start_if is None
2195        return QAPIGen.get_content(self, fname)
2196
2197
2198class QAPIGenC(QAPIGenCCode):
2199
2200    def __init__(self, blurb, pydoc):
2201        QAPIGenCCode.__init__(self)
2202        self._blurb = blurb
2203        self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
2204                                                  re.MULTILINE))
2205
2206    def _top(self, fname):
2207        return mcgen('''
2208/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2209
2210/*
2211%(blurb)s
2212 *
2213 * %(copyright)s
2214 *
2215 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2216 * See the COPYING.LIB file in the top-level directory.
2217 */
2218
2219''',
2220                     blurb=self._blurb, copyright=self._copyright)
2221
2222    def _bottom(self, fname):
2223        return mcgen('''
2224
2225/* Dummy declaration to prevent empty .o file */
2226char dummy_%(name)s;
2227''',
2228                     name=c_name(fname))
2229
2230
2231class QAPIGenH(QAPIGenC):
2232
2233    def _top(self, fname):
2234        return QAPIGenC._top(self, fname) + guardstart(fname)
2235
2236    def _bottom(self, fname):
2237        return guardend(fname)
2238
2239
2240class QAPIGenDoc(QAPIGen):
2241
2242    def _top(self, fname):
2243        return (QAPIGen._top(self, fname)
2244                + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2245
2246
2247class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
2248
2249    def __init__(self, prefix, what, blurb, pydoc):
2250        self._prefix = prefix
2251        self._what = what
2252        self._genc = QAPIGenC(blurb, pydoc)
2253        self._genh = QAPIGenH(blurb, pydoc)
2254
2255    def write(self, output_dir):
2256        self._genc.write(output_dir, self._prefix + self._what + '.c')
2257        self._genh.write(output_dir, self._prefix + self._what + '.h')
2258
2259
2260class QAPISchemaModularCVisitor(QAPISchemaVisitor):
2261
2262    def __init__(self, prefix, what, blurb, pydoc):
2263        self._prefix = prefix
2264        self._what = what
2265        self._blurb = blurb
2266        self._pydoc = pydoc
2267        self._module = {}
2268        self._main_module = None
2269
2270    def _module_basename(self, what, name):
2271        if name is None:
2272            return re.sub(r'-', '-builtin-', what)
2273        basename = os.path.join(os.path.dirname(name),
2274                                self._prefix + what)
2275        if name == self._main_module:
2276            return basename
2277        return basename + '-' + os.path.splitext(os.path.basename(name))[0]
2278
2279    def _add_module(self, name, blurb):
2280        if self._main_module is None and name is not None:
2281            self._main_module = name
2282        genc = QAPIGenC(blurb, self._pydoc)
2283        genh = QAPIGenH(blurb, self._pydoc)
2284        self._module[name] = (genc, genh)
2285        self._set_module(name)
2286
2287    def _set_module(self, name):
2288        self._genc, self._genh = self._module[name]
2289
2290    def write(self, output_dir, opt_builtins=False):
2291        for name in self._module:
2292            if name is None and not opt_builtins:
2293                continue
2294            basename = self._module_basename(self._what, name)
2295            (genc, genh) = self._module[name]
2296            genc.write(output_dir, basename + '.c')
2297            genh.write(output_dir, basename + '.h')
2298
2299    def _begin_module(self, name):
2300        pass
2301
2302    def visit_module(self, name):
2303        if name in self._module:
2304            self._set_module(name)
2305            return
2306        self._add_module(name, self._blurb)
2307        self._begin_module(name)
2308
2309    def visit_include(self, name, info):
2310        basename = self._module_basename(self._what, name)
2311        self._genh.preamble_add(mcgen('''
2312#include "%(basename)s.h"
2313''',
2314                                      basename=basename))
2315