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