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