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