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