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