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