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