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