xref: /openbmc/qemu/scripts/qapi/common.py (revision b22e86585b296b254209cdc7011fcc74dd08717d)
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']
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']
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
1694class QAPISchemaCommand(QAPISchemaEntity):
1695    def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
1696                 gen, success_response, boxed, allow_oob, allow_preconfig):
1697        QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1698        assert not arg_type or isinstance(arg_type, str)
1699        assert not ret_type or isinstance(ret_type, str)
1700        self._arg_type_name = arg_type
1701        self.arg_type = None
1702        self._ret_type_name = ret_type
1703        self.ret_type = None
1704        self.gen = gen
1705        self.success_response = success_response
1706        self.boxed = boxed
1707        self.allow_oob = allow_oob
1708        self.allow_preconfig = allow_preconfig
1709
1710    def check(self, schema):
1711        QAPISchemaEntity.check(self, schema)
1712        if self._arg_type_name:
1713            self.arg_type = schema.lookup_type(self._arg_type_name)
1714            assert isinstance(self.arg_type, QAPISchemaObjectType)
1715            self.arg_type.check(schema)
1716            if self.boxed:
1717                if self.arg_type.is_empty():
1718                    raise QAPISemError(self.info,
1719                                       "Cannot use 'boxed' with empty type")
1720            else:
1721                assert not self.arg_type.variants
1722        elif self.boxed:
1723            raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1724        if self._ret_type_name:
1725            self.ret_type = schema.lookup_type(self._ret_type_name)
1726            assert isinstance(self.ret_type, QAPISchemaType)
1727
1728    def visit(self, visitor):
1729        visitor.visit_command(self.name, self.info, self.ifcond,
1730                              self.arg_type, self.ret_type,
1731                              self.gen, self.success_response,
1732                              self.boxed, self.allow_oob,
1733                              self.allow_preconfig)
1734
1735
1736class QAPISchemaEvent(QAPISchemaEntity):
1737    def __init__(self, name, info, doc, ifcond, arg_type, boxed):
1738        QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1739        assert not arg_type or isinstance(arg_type, str)
1740        self._arg_type_name = arg_type
1741        self.arg_type = None
1742        self.boxed = boxed
1743
1744    def check(self, schema):
1745        QAPISchemaEntity.check(self, schema)
1746        if self._arg_type_name:
1747            self.arg_type = schema.lookup_type(self._arg_type_name)
1748            assert isinstance(self.arg_type, QAPISchemaObjectType)
1749            self.arg_type.check(schema)
1750            if self.boxed:
1751                if self.arg_type.is_empty():
1752                    raise QAPISemError(self.info,
1753                                       "Cannot use 'boxed' with empty type")
1754            else:
1755                assert not self.arg_type.variants
1756        elif self.boxed:
1757            raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1758
1759    def visit(self, visitor):
1760        visitor.visit_event(self.name, self.info, self.ifcond,
1761                            self.arg_type, self.boxed)
1762
1763
1764class QAPISchema(object):
1765    def __init__(self, fname):
1766        self.fname = fname
1767        if sys.version_info[0] >= 3:
1768            f = open(fname, 'r', encoding='utf-8')
1769        else:
1770            f = open(fname, 'r')
1771        parser = QAPISchemaParser(f)
1772        exprs = check_exprs(parser.exprs)
1773        self.docs = parser.docs
1774        self._entity_list = []
1775        self._entity_dict = {}
1776        self._predefining = True
1777        self._def_predefineds()
1778        self._predefining = False
1779        self._def_exprs(exprs)
1780        self.check()
1781
1782    def _def_entity(self, ent):
1783        # Only the predefined types are allowed to not have info
1784        assert ent.info or self._predefining
1785        assert ent.name is None or ent.name not in self._entity_dict
1786        self._entity_list.append(ent)
1787        if ent.name is not None:
1788            self._entity_dict[ent.name] = ent
1789
1790    def lookup_entity(self, name, typ=None):
1791        ent = self._entity_dict.get(name)
1792        if typ and not isinstance(ent, typ):
1793            return None
1794        return ent
1795
1796    def lookup_type(self, name):
1797        return self.lookup_entity(name, QAPISchemaType)
1798
1799    def _def_include(self, expr, info, doc):
1800        include = expr['include']
1801        assert doc is None
1802        main_info = info
1803        while main_info['parent']:
1804            main_info = main_info['parent']
1805        fname = os.path.relpath(include, os.path.dirname(main_info['file']))
1806        self._def_entity(QAPISchemaInclude(fname, info))
1807
1808    def _def_builtin_type(self, name, json_type, c_type):
1809        self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1810        # Instantiating only the arrays that are actually used would
1811        # be nice, but we can't as long as their generated code
1812        # (qapi-builtin-types.[ch]) may be shared by some other
1813        # schema.
1814        self._make_array_type(name, None)
1815
1816    def _def_predefineds(self):
1817        for t in [('str',    'string',  'char' + pointer_suffix),
1818                  ('number', 'number',  'double'),
1819                  ('int',    'int',     'int64_t'),
1820                  ('int8',   'int',     'int8_t'),
1821                  ('int16',  'int',     'int16_t'),
1822                  ('int32',  'int',     'int32_t'),
1823                  ('int64',  'int',     'int64_t'),
1824                  ('uint8',  'int',     'uint8_t'),
1825                  ('uint16', 'int',     'uint16_t'),
1826                  ('uint32', 'int',     'uint32_t'),
1827                  ('uint64', 'int',     'uint64_t'),
1828                  ('size',   'int',     'uint64_t'),
1829                  ('bool',   'boolean', 'bool'),
1830                  ('any',    'value',   'QObject' + pointer_suffix),
1831                  ('null',   'null',    'QNull' + pointer_suffix)]:
1832            self._def_builtin_type(*t)
1833        self.the_empty_object_type = QAPISchemaObjectType(
1834            'q_empty', None, None, None, None, [], None, [])
1835        self._def_entity(self.the_empty_object_type)
1836
1837        qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1838                  'qbool']
1839        qtype_values = self._make_enum_members([{'name': n} for n in qtypes])
1840
1841        self._def_entity(QAPISchemaEnumType('QType', None, None, None,
1842                                            qtype_values, 'QTYPE'))
1843
1844    def _make_features(self, features):
1845        return [QAPISchemaFeature(f['name'], f.get('if')) for f in features]
1846
1847    def _make_enum_members(self, values):
1848        return [QAPISchemaMember(v['name'], v.get('if')) for v in values]
1849
1850    def _make_implicit_enum_type(self, name, info, ifcond, values):
1851        # See also QAPISchemaObjectTypeMember._pretty_owner()
1852        name = name + 'Kind'   # Use namespace reserved by add_name()
1853        self._def_entity(QAPISchemaEnumType(
1854            name, info, None, ifcond, self._make_enum_members(values), None))
1855        return name
1856
1857    def _make_array_type(self, element_type, info):
1858        name = element_type + 'List'   # Use namespace reserved by add_name()
1859        if not self.lookup_type(name):
1860            self._def_entity(QAPISchemaArrayType(name, info, element_type))
1861        return name
1862
1863    def _make_implicit_object_type(self, name, info, doc, ifcond,
1864                                   role, members):
1865        if not members:
1866            return None
1867        # See also QAPISchemaObjectTypeMember._pretty_owner()
1868        name = 'q_obj_%s-%s' % (name, role)
1869        typ = self.lookup_entity(name, QAPISchemaObjectType)
1870        if typ:
1871            # The implicit object type has multiple users.  This can
1872            # happen only for simple unions' implicit wrapper types.
1873            # Its ifcond should be the disjunction of its user's
1874            # ifconds.  Not implemented.  Instead, we always pass the
1875            # wrapped type's ifcond, which is trivially the same for all
1876            # users.  It's also necessary for the wrapper to compile.
1877            # But it's not tight: the disjunction need not imply it.  We
1878            # may end up compiling useless wrapper types.
1879            # TODO kill simple unions or implement the disjunction
1880            assert ifcond == typ._ifcond # pylint: disable=protected-access
1881        else:
1882            self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
1883                                                  None, members, None, []))
1884        return name
1885
1886    def _def_enum_type(self, expr, info, doc):
1887        name = expr['enum']
1888        data = expr['data']
1889        prefix = expr.get('prefix')
1890        ifcond = expr.get('if')
1891        self._def_entity(QAPISchemaEnumType(
1892            name, info, doc, ifcond,
1893            self._make_enum_members(data), prefix))
1894
1895    def _make_member(self, name, typ, ifcond, info):
1896        optional = False
1897        if name.startswith('*'):
1898            name = name[1:]
1899            optional = True
1900        if isinstance(typ, list):
1901            assert len(typ) == 1
1902            typ = self._make_array_type(typ[0], info)
1903        return QAPISchemaObjectTypeMember(name, typ, optional, ifcond)
1904
1905    def _make_members(self, data, info):
1906        return [self._make_member(key, value['type'], value.get('if'), info)
1907                for (key, value) in data.items()]
1908
1909    def _def_struct_type(self, expr, info, doc):
1910        name = expr['struct']
1911        base = expr.get('base')
1912        data = expr['data']
1913        ifcond = expr.get('if')
1914        features = expr.get('features', [])
1915        self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
1916                                              self._make_members(data, info),
1917                                              None,
1918                                              self._make_features(features)))
1919
1920    def _make_variant(self, case, typ, ifcond):
1921        return QAPISchemaObjectTypeVariant(case, typ, ifcond)
1922
1923    def _make_simple_variant(self, case, typ, ifcond, info):
1924        if isinstance(typ, list):
1925            assert len(typ) == 1
1926            typ = self._make_array_type(typ[0], info)
1927        typ = self._make_implicit_object_type(
1928            typ, info, None, self.lookup_type(typ),
1929            'wrapper', [self._make_member('data', typ, None, info)])
1930        return QAPISchemaObjectTypeVariant(case, typ, ifcond)
1931
1932    def _def_union_type(self, expr, info, doc):
1933        name = expr['union']
1934        data = expr['data']
1935        base = expr.get('base')
1936        ifcond = expr.get('if')
1937        tag_name = expr.get('discriminator')
1938        tag_member = None
1939        if isinstance(base, dict):
1940            base = self._make_implicit_object_type(
1941                name, info, doc, ifcond,
1942                'base', self._make_members(base, info))
1943        if tag_name:
1944            variants = [self._make_variant(key, value['type'], value.get('if'))
1945                        for (key, value) in data.items()]
1946            members = []
1947        else:
1948            variants = [self._make_simple_variant(key, value['type'],
1949                                                  value.get('if'), info)
1950                        for (key, value) in data.items()]
1951            enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
1952            typ = self._make_implicit_enum_type(name, info, ifcond, enum)
1953            tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1954            members = [tag_member]
1955        self._def_entity(
1956            QAPISchemaObjectType(name, info, doc, ifcond, base, members,
1957                                 QAPISchemaObjectTypeVariants(tag_name,
1958                                                              tag_member,
1959                                                              variants), []))
1960
1961    def _def_alternate_type(self, expr, info, doc):
1962        name = expr['alternate']
1963        data = expr['data']
1964        ifcond = expr.get('if')
1965        variants = [self._make_variant(key, value['type'], value.get('if'))
1966                    for (key, value) in data.items()]
1967        tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
1968        self._def_entity(
1969            QAPISchemaAlternateType(name, info, doc, ifcond,
1970                                    QAPISchemaObjectTypeVariants(None,
1971                                                                 tag_member,
1972                                                                 variants)))
1973
1974    def _def_command(self, expr, info, doc):
1975        name = expr['command']
1976        data = expr.get('data')
1977        rets = expr.get('returns')
1978        gen = expr.get('gen', True)
1979        success_response = expr.get('success-response', True)
1980        boxed = expr.get('boxed', False)
1981        allow_oob = expr.get('allow-oob', False)
1982        allow_preconfig = expr.get('allow-preconfig', False)
1983        ifcond = expr.get('if')
1984        if isinstance(data, OrderedDict):
1985            data = self._make_implicit_object_type(
1986                name, info, doc, ifcond, 'arg', self._make_members(data, info))
1987        if isinstance(rets, list):
1988            assert len(rets) == 1
1989            rets = self._make_array_type(rets[0], info)
1990        self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
1991                                           gen, success_response,
1992                                           boxed, allow_oob, allow_preconfig))
1993
1994    def _def_event(self, expr, info, doc):
1995        name = expr['event']
1996        data = expr.get('data')
1997        boxed = expr.get('boxed', False)
1998        ifcond = expr.get('if')
1999        if isinstance(data, OrderedDict):
2000            data = self._make_implicit_object_type(
2001                name, info, doc, ifcond, 'arg', self._make_members(data, info))
2002        self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
2003
2004    def _def_exprs(self, exprs):
2005        for expr_elem in exprs:
2006            expr = expr_elem['expr']
2007            info = expr_elem['info']
2008            doc = expr_elem.get('doc')
2009            if 'enum' in expr:
2010                self._def_enum_type(expr, info, doc)
2011            elif 'struct' in expr:
2012                self._def_struct_type(expr, info, doc)
2013            elif 'union' in expr:
2014                self._def_union_type(expr, info, doc)
2015            elif 'alternate' in expr:
2016                self._def_alternate_type(expr, info, doc)
2017            elif 'command' in expr:
2018                self._def_command(expr, info, doc)
2019            elif 'event' in expr:
2020                self._def_event(expr, info, doc)
2021            elif 'include' in expr:
2022                self._def_include(expr, info, doc)
2023            else:
2024                assert False
2025
2026    def check(self):
2027        for ent in self._entity_list:
2028            ent.check(self)
2029
2030    def visit(self, visitor):
2031        visitor.visit_begin(self)
2032        module = None
2033        visitor.visit_module(module)
2034        for entity in self._entity_list:
2035            if visitor.visit_needed(entity):
2036                if entity.module != module:
2037                    module = entity.module
2038                    visitor.visit_module(module)
2039                entity.visit(visitor)
2040        visitor.visit_end()
2041
2042
2043#
2044# Code generation helpers
2045#
2046
2047def camel_case(name):
2048    new_name = ''
2049    first = True
2050    for ch in name:
2051        if ch in ['_', '-']:
2052            first = True
2053        elif first:
2054            new_name += ch.upper()
2055            first = False
2056        else:
2057            new_name += ch.lower()
2058    return new_name
2059
2060
2061# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
2062# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
2063# ENUM24_Name -> ENUM24_NAME
2064def camel_to_upper(value):
2065    c_fun_str = c_name(value, False)
2066    if value.isupper():
2067        return c_fun_str
2068
2069    new_name = ''
2070    length = len(c_fun_str)
2071    for i in range(length):
2072        c = c_fun_str[i]
2073        # When c is upper and no '_' appears before, do more checks
2074        if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
2075            if i < length - 1 and c_fun_str[i + 1].islower():
2076                new_name += '_'
2077            elif c_fun_str[i - 1].isdigit():
2078                new_name += '_'
2079        new_name += c
2080    return new_name.lstrip('_').upper()
2081
2082
2083def c_enum_const(type_name, const_name, prefix=None):
2084    if prefix is not None:
2085        type_name = prefix
2086    return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
2087
2088
2089if hasattr(str, 'maketrans'):
2090    c_name_trans = str.maketrans('.-', '__')
2091else:
2092    c_name_trans = string.maketrans('.-', '__')
2093
2094
2095# Map @name to a valid C identifier.
2096# If @protect, avoid returning certain ticklish identifiers (like
2097# C keywords) by prepending 'q_'.
2098#
2099# Used for converting 'name' from a 'name':'type' qapi definition
2100# into a generated struct member, as well as converting type names
2101# into substrings of a generated C function name.
2102# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
2103# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
2104def c_name(name, protect=True):
2105    # ANSI X3J11/88-090, 3.1.1
2106    c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
2107                     'default', 'do', 'double', 'else', 'enum', 'extern',
2108                     'float', 'for', 'goto', 'if', 'int', 'long', 'register',
2109                     'return', 'short', 'signed', 'sizeof', 'static',
2110                     'struct', 'switch', 'typedef', 'union', 'unsigned',
2111                     'void', 'volatile', 'while'])
2112    # ISO/IEC 9899:1999, 6.4.1
2113    c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
2114    # ISO/IEC 9899:2011, 6.4.1
2115    c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
2116                     '_Noreturn', '_Static_assert', '_Thread_local'])
2117    # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
2118    # excluding _.*
2119    gcc_words = set(['asm', 'typeof'])
2120    # C++ ISO/IEC 14882:2003 2.11
2121    cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
2122                     'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
2123                     'namespace', 'new', 'operator', 'private', 'protected',
2124                     'public', 'reinterpret_cast', 'static_cast', 'template',
2125                     'this', 'throw', 'true', 'try', 'typeid', 'typename',
2126                     'using', 'virtual', 'wchar_t',
2127                     # alternative representations
2128                     'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
2129                     'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
2130    # namespace pollution:
2131    polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
2132    name = name.translate(c_name_trans)
2133    if protect and (name in c89_words | c99_words | c11_words | gcc_words
2134                    | cpp_words | polluted_words):
2135        return 'q_' + name
2136    return name
2137
2138
2139eatspace = '\033EATSPACE.'
2140pointer_suffix = ' *' + eatspace
2141
2142
2143def genindent(count):
2144    ret = ''
2145    for _ in range(count):
2146        ret += ' '
2147    return ret
2148
2149
2150indent_level = 0
2151
2152
2153def push_indent(indent_amount=4):
2154    global indent_level
2155    indent_level += indent_amount
2156
2157
2158def pop_indent(indent_amount=4):
2159    global indent_level
2160    indent_level -= indent_amount
2161
2162
2163# Generate @code with @kwds interpolated.
2164# Obey indent_level, and strip eatspace.
2165def cgen(code, **kwds):
2166    raw = code % kwds
2167    if indent_level:
2168        indent = genindent(indent_level)
2169        # re.subn() lacks flags support before Python 2.7, use re.compile()
2170        raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
2171                      indent, raw)
2172        raw = raw[0]
2173    return re.sub(re.escape(eatspace) + r' *', '', raw)
2174
2175
2176def mcgen(code, **kwds):
2177    if code[0] == '\n':
2178        code = code[1:]
2179    return cgen(code, **kwds)
2180
2181
2182def c_fname(filename):
2183    return re.sub(r'[^A-Za-z0-9_]', '_', filename)
2184
2185
2186def guardstart(name):
2187    return mcgen('''
2188#ifndef %(name)s
2189#define %(name)s
2190
2191''',
2192                 name=c_fname(name).upper())
2193
2194
2195def guardend(name):
2196    return mcgen('''
2197
2198#endif /* %(name)s */
2199''',
2200                 name=c_fname(name).upper())
2201
2202
2203def gen_if(ifcond):
2204    ret = ''
2205    for ifc in ifcond:
2206        ret += mcgen('''
2207#if %(cond)s
2208''', cond=ifc)
2209    return ret
2210
2211
2212def gen_endif(ifcond):
2213    ret = ''
2214    for ifc in reversed(ifcond):
2215        ret += mcgen('''
2216#endif /* %(cond)s */
2217''', cond=ifc)
2218    return ret
2219
2220
2221def _wrap_ifcond(ifcond, before, after):
2222    if before == after:
2223        return after   # suppress empty #if ... #endif
2224
2225    assert after.startswith(before)
2226    out = before
2227    added = after[len(before):]
2228    if added[0] == '\n':
2229        out += '\n'
2230        added = added[1:]
2231    out += gen_if(ifcond)
2232    out += added
2233    out += gen_endif(ifcond)
2234    return out
2235
2236
2237def gen_enum_lookup(name, members, prefix=None):
2238    ret = mcgen('''
2239
2240const QEnumLookup %(c_name)s_lookup = {
2241    .array = (const char *const[]) {
2242''',
2243                c_name=c_name(name))
2244    for m in members:
2245        ret += gen_if(m.ifcond)
2246        index = c_enum_const(name, m.name, prefix)
2247        ret += mcgen('''
2248        [%(index)s] = "%(name)s",
2249''',
2250                     index=index, name=m.name)
2251        ret += gen_endif(m.ifcond)
2252
2253    ret += mcgen('''
2254    },
2255    .size = %(max_index)s
2256};
2257''',
2258                 max_index=c_enum_const(name, '_MAX', prefix))
2259    return ret
2260
2261
2262def gen_enum(name, members, prefix=None):
2263    # append automatically generated _MAX value
2264    enum_members = members + [QAPISchemaMember('_MAX')]
2265
2266    ret = mcgen('''
2267
2268typedef enum %(c_name)s {
2269''',
2270                c_name=c_name(name))
2271
2272    for m in enum_members:
2273        ret += gen_if(m.ifcond)
2274        ret += mcgen('''
2275    %(c_enum)s,
2276''',
2277                     c_enum=c_enum_const(name, m.name, prefix))
2278        ret += gen_endif(m.ifcond)
2279
2280    ret += mcgen('''
2281} %(c_name)s;
2282''',
2283                 c_name=c_name(name))
2284
2285    ret += mcgen('''
2286
2287#define %(c_name)s_str(val) \\
2288    qapi_enum_lookup(&%(c_name)s_lookup, (val))
2289
2290extern const QEnumLookup %(c_name)s_lookup;
2291''',
2292                 c_name=c_name(name))
2293    return ret
2294
2295
2296def build_params(arg_type, boxed, extra=None):
2297    ret = ''
2298    sep = ''
2299    if boxed:
2300        assert arg_type
2301        ret += '%s arg' % arg_type.c_param_type()
2302        sep = ', '
2303    elif arg_type:
2304        assert not arg_type.variants
2305        for memb in arg_type.members:
2306            ret += sep
2307            sep = ', '
2308            if memb.optional:
2309                ret += 'bool has_%s, ' % c_name(memb.name)
2310            ret += '%s %s' % (memb.type.c_param_type(),
2311                              c_name(memb.name))
2312    if extra:
2313        ret += sep + extra
2314    return ret if ret else 'void'
2315
2316
2317#
2318# Accumulate and write output
2319#
2320
2321class QAPIGen(object):
2322
2323    def __init__(self, fname):
2324        self.fname = fname
2325        self._preamble = ''
2326        self._body = ''
2327
2328    def preamble_add(self, text):
2329        self._preamble += text
2330
2331    def add(self, text):
2332        self._body += text
2333
2334    def get_content(self):
2335        return self._top() + self._preamble + self._body + self._bottom()
2336
2337    def _top(self):
2338        return ''
2339
2340    def _bottom(self):
2341        return ''
2342
2343    def write(self, output_dir):
2344        pathname = os.path.join(output_dir, self.fname)
2345        dir = os.path.dirname(pathname)
2346        if dir:
2347            try:
2348                os.makedirs(dir)
2349            except os.error as e:
2350                if e.errno != errno.EEXIST:
2351                    raise
2352        fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
2353        if sys.version_info[0] >= 3:
2354            f = open(fd, 'r+', encoding='utf-8')
2355        else:
2356            f = os.fdopen(fd, 'r+')
2357        text = self.get_content()
2358        oldtext = f.read(len(text) + 1)
2359        if text != oldtext:
2360            f.seek(0)
2361            f.truncate(0)
2362            f.write(text)
2363        f.close()
2364
2365
2366@contextmanager
2367def ifcontext(ifcond, *args):
2368    """A 'with' statement context manager to wrap with start_if()/end_if()
2369
2370    *args: any number of QAPIGenCCode
2371
2372    Example::
2373
2374        with ifcontext(ifcond, self._genh, self._genc):
2375            modify self._genh and self._genc ...
2376
2377    Is equivalent to calling::
2378
2379        self._genh.start_if(ifcond)
2380        self._genc.start_if(ifcond)
2381        modify self._genh and self._genc ...
2382        self._genh.end_if()
2383        self._genc.end_if()
2384    """
2385    for arg in args:
2386        arg.start_if(ifcond)
2387    yield
2388    for arg in args:
2389        arg.end_if()
2390
2391
2392class QAPIGenCCode(QAPIGen):
2393
2394    def __init__(self, fname):
2395        QAPIGen.__init__(self, fname)
2396        self._start_if = None
2397
2398    def start_if(self, ifcond):
2399        assert self._start_if is None
2400        self._start_if = (ifcond, self._body, self._preamble)
2401
2402    def end_if(self):
2403        assert self._start_if
2404        self._wrap_ifcond()
2405        self._start_if = None
2406
2407    def _wrap_ifcond(self):
2408        self._body = _wrap_ifcond(self._start_if[0],
2409                                  self._start_if[1], self._body)
2410        self._preamble = _wrap_ifcond(self._start_if[0],
2411                                      self._start_if[2], self._preamble)
2412
2413    def get_content(self):
2414        assert self._start_if is None
2415        return QAPIGen.get_content(self)
2416
2417
2418class QAPIGenC(QAPIGenCCode):
2419
2420    def __init__(self, fname, blurb, pydoc):
2421        QAPIGenCCode.__init__(self, fname)
2422        self._blurb = blurb
2423        self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
2424                                                  re.MULTILINE))
2425
2426    def _top(self):
2427        return mcgen('''
2428/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2429
2430/*
2431%(blurb)s
2432 *
2433 * %(copyright)s
2434 *
2435 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2436 * See the COPYING.LIB file in the top-level directory.
2437 */
2438
2439''',
2440                     blurb=self._blurb, copyright=self._copyright)
2441
2442    def _bottom(self):
2443        return mcgen('''
2444
2445/* Dummy declaration to prevent empty .o file */
2446char qapi_dummy_%(name)s;
2447''',
2448                     name=c_fname(self.fname))
2449
2450
2451class QAPIGenH(QAPIGenC):
2452
2453    def _top(self):
2454        return QAPIGenC._top(self) + guardstart(self.fname)
2455
2456    def _bottom(self):
2457        return guardend(self.fname)
2458
2459
2460class QAPIGenDoc(QAPIGen):
2461
2462    def _top(self):
2463        return (QAPIGen._top(self)
2464                + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2465
2466
2467class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
2468
2469    def __init__(self, prefix, what, blurb, pydoc):
2470        self._prefix = prefix
2471        self._what = what
2472        self._genc = QAPIGenC(self._prefix + self._what + '.c',
2473                              blurb, pydoc)
2474        self._genh = QAPIGenH(self._prefix + self._what + '.h',
2475                              blurb, pydoc)
2476
2477    def write(self, output_dir):
2478        self._genc.write(output_dir)
2479        self._genh.write(output_dir)
2480
2481
2482class QAPISchemaModularCVisitor(QAPISchemaVisitor):
2483
2484    def __init__(self, prefix, what, blurb, pydoc):
2485        self._prefix = prefix
2486        self._what = what
2487        self._blurb = blurb
2488        self._pydoc = pydoc
2489        self._genc = None
2490        self._genh = None
2491        self._module = {}
2492        self._main_module = None
2493
2494    @staticmethod
2495    def _is_user_module(name):
2496        return name and not name.startswith('./')
2497
2498    @staticmethod
2499    def _is_builtin_module(name):
2500        return not name
2501
2502    def _module_dirname(self, what, name):
2503        if self._is_user_module(name):
2504            return os.path.dirname(name)
2505        return ''
2506
2507    def _module_basename(self, what, name):
2508        ret = '' if self._is_builtin_module(name) else self._prefix
2509        if self._is_user_module(name):
2510            basename = os.path.basename(name)
2511            ret += what
2512            if name != self._main_module:
2513                ret += '-' + os.path.splitext(basename)[0]
2514        else:
2515            name = name[2:] if name else 'builtin'
2516            ret += re.sub(r'-', '-' + name + '-', what)
2517        return ret
2518
2519    def _module_filename(self, what, name):
2520        return os.path.join(self._module_dirname(what, name),
2521                            self._module_basename(what, name))
2522
2523    def _add_module(self, name, blurb):
2524        basename = self._module_filename(self._what, name)
2525        genc = QAPIGenC(basename + '.c', blurb, self._pydoc)
2526        genh = QAPIGenH(basename + '.h', blurb, self._pydoc)
2527        self._module[name] = (genc, genh)
2528        self._set_module(name)
2529
2530    def _add_user_module(self, name, blurb):
2531        assert self._is_user_module(name)
2532        if self._main_module is None:
2533            self._main_module = name
2534        self._add_module(name, blurb)
2535
2536    def _add_system_module(self, name, blurb):
2537        self._add_module(name and './' + name, blurb)
2538
2539    def _set_module(self, name):
2540        self._genc, self._genh = self._module[name]
2541
2542    def write(self, output_dir, opt_builtins=False):
2543        for name in self._module:
2544            if self._is_builtin_module(name) and not opt_builtins:
2545                continue
2546            (genc, genh) = self._module[name]
2547            genc.write(output_dir)
2548            genh.write(output_dir)
2549
2550    def _begin_user_module(self, name):
2551        pass
2552
2553    def visit_module(self, name):
2554        if name in self._module:
2555            self._set_module(name)
2556        elif self._is_builtin_module(name):
2557            # The built-in module has not been created.  No code may
2558            # be generated.
2559            self._genc = None
2560            self._genh = None
2561        else:
2562            self._add_user_module(name, self._blurb)
2563            self._begin_user_module(name)
2564
2565    def visit_include(self, name, info):
2566        relname = os.path.relpath(self._module_filename(self._what, name),
2567                                  os.path.dirname(self._genh.fname))
2568        self._genh.preamble_add(mcgen('''
2569#include "%(relname)s.h"
2570''',
2571                                      relname=relname))
2572