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