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