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