xref: /openbmc/qemu/scripts/qapi/common.py (revision 56d2df5e65d873ca0e9841f7bb7676cab759f8da)
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 copy
17import errno
18import os
19import re
20import string
21import sys
22from collections import OrderedDict
23
24# Are documentation comments required?
25doc_required = False
26
27# Whitelist of commands allowed to return a non-dictionary
28returns_whitelist = []
29
30# Whitelist of entities allowed to violate case conventions
31name_case_whitelist = []
32
33
34#
35# Parsing the schema into expressions
36#
37
38class QAPISourceInfo(object):
39    def __init__(self, fname, line, parent):
40        self.fname = fname
41        self.line = line
42        self.parent = parent
43        self.defn_meta = None
44        self.defn_name = None
45
46    def set_defn(self, meta, name):
47        self.defn_meta = meta
48        self.defn_name = name
49
50    def next_line(self):
51        info = copy.copy(self)
52        info.line += 1
53        return info
54
55    def loc(self):
56        return '%s:%d' % (self.fname, self.line)
57
58    def in_defn(self):
59        if self.defn_name:
60            return "%s: In %s '%s':\n" % (self.fname,
61                                          self.defn_meta, self.defn_name)
62        return ''
63
64    def include_path(self):
65        ret = ''
66        parent = self.parent
67        while parent:
68            ret = 'In file included from %s:\n' % parent.loc() + ret
69            parent = parent.parent
70        return ret
71
72    def __str__(self):
73        return self.include_path() + self.in_defn() + self.loc()
74
75
76class QAPIError(Exception):
77    def __init__(self, info, col, msg):
78        Exception.__init__(self)
79        self.info = info
80        self.col = col
81        self.msg = msg
82
83    def __str__(self):
84        loc = str(self.info)
85        if self.col is not None:
86            assert self.info.line is not None
87            loc += ':%s' % self.col
88        return loc + ': ' + self.msg
89
90
91class QAPIParseError(QAPIError):
92    def __init__(self, parser, msg):
93        col = 1
94        for ch in parser.src[parser.line_pos:parser.pos]:
95            if ch == '\t':
96                col = (col + 7) % 8 + 1
97            else:
98                col += 1
99        QAPIError.__init__(self, parser.info, col, msg)
100
101
102class QAPISemError(QAPIError):
103    def __init__(self, info, msg):
104        QAPIError.__init__(self, info, None, msg)
105
106
107class QAPIDoc(object):
108    """
109    A documentation comment block, either definition or free-form
110
111    Definition documentation blocks consist of
112
113    * a body section: one line naming the definition, followed by an
114      overview (any number of lines)
115
116    * argument sections: a description of each argument (for commands
117      and events) or member (for structs, unions and alternates)
118
119    * features sections: a description of each feature flag
120
121    * additional (non-argument) sections, possibly tagged
122
123    Free-form documentation blocks consist only of a body section.
124    """
125
126    class Section(object):
127        def __init__(self, name=None):
128            # optional section name (argument/member or section name)
129            self.name = name
130            # the list of lines for this section
131            self.text = ''
132
133        def append(self, line):
134            self.text += line.rstrip() + '\n'
135
136    class ArgSection(Section):
137        def __init__(self, name):
138            QAPIDoc.Section.__init__(self, name)
139            self.member = None
140
141        def connect(self, member):
142            self.member = member
143
144    def __init__(self, parser, info):
145        # self._parser is used to report errors with QAPIParseError.  The
146        # resulting error position depends on the state of the parser.
147        # It happens to be the beginning of the comment.  More or less
148        # servicable, but action at a distance.
149        self._parser = parser
150        self.info = info
151        self.symbol = None
152        self.body = QAPIDoc.Section()
153        # dict mapping parameter name to ArgSection
154        self.args = OrderedDict()
155        self.features = OrderedDict()
156        # a list of Section
157        self.sections = []
158        # the current section
159        self._section = self.body
160        self._append_line = self._append_body_line
161
162    def has_section(self, name):
163        """Return True if we have a section with this name."""
164        for i in self.sections:
165            if i.name == name:
166                return True
167        return False
168
169    def append(self, line):
170        """
171        Parse a comment line and add it to the documentation.
172
173        The way that the line is dealt with depends on which part of
174        the documentation we're parsing right now:
175        * The body section: ._append_line is ._append_body_line
176        * An argument section: ._append_line is ._append_args_line
177        * A features section: ._append_line is ._append_features_line
178        * An additional section: ._append_line is ._append_various_line
179        """
180        line = line[1:]
181        if not line:
182            self._append_freeform(line)
183            return
184
185        if line[0] != ' ':
186            raise QAPIParseError(self._parser, "missing space after #")
187        line = line[1:]
188        self._append_line(line)
189
190    def end_comment(self):
191        self._end_section()
192
193    @staticmethod
194    def _is_section_tag(name):
195        return name in ('Returns:', 'Since:',
196                        # those are often singular or plural
197                        'Note:', 'Notes:',
198                        'Example:', 'Examples:',
199                        'TODO:')
200
201    def _append_body_line(self, line):
202        """
203        Process a line of documentation text in the body section.
204
205        If this a symbol line and it is the section's first line, this
206        is a definition documentation block for that symbol.
207
208        If it's a definition documentation block, another symbol line
209        begins the argument section for the argument named by it, and
210        a section tag begins an additional section.  Start that
211        section and append the line to it.
212
213        Else, append the line to the current section.
214        """
215        name = line.split(' ', 1)[0]
216        # FIXME not nice: things like '#  @foo:' and '# @foo: ' aren't
217        # recognized, and get silently treated as ordinary text
218        if not self.symbol and not self.body.text and line.startswith('@'):
219            if not line.endswith(':'):
220                raise QAPIParseError(self._parser, "line should end with ':'")
221            self.symbol = line[1:-1]
222            # FIXME invalid names other than the empty string aren't flagged
223            if not self.symbol:
224                raise QAPIParseError(self._parser, "invalid name")
225        elif self.symbol:
226            # This is a definition documentation block
227            if name.startswith('@') and name.endswith(':'):
228                self._append_line = self._append_args_line
229                self._append_args_line(line)
230            elif line == 'Features:':
231                self._append_line = self._append_features_line
232            elif self._is_section_tag(name):
233                self._append_line = self._append_various_line
234                self._append_various_line(line)
235            else:
236                self._append_freeform(line.strip())
237        else:
238            # This is a free-form documentation block
239            self._append_freeform(line.strip())
240
241    def _append_args_line(self, line):
242        """
243        Process a line of documentation text in an argument section.
244
245        A symbol line begins the next argument section, a section tag
246        section or a non-indented line after a blank line begins an
247        additional section.  Start that section and append the line to
248        it.
249
250        Else, append the line to the current section.
251
252        """
253        name = line.split(' ', 1)[0]
254
255        if name.startswith('@') and name.endswith(':'):
256            line = line[len(name)+1:]
257            self._start_args_section(name[1:-1])
258        elif self._is_section_tag(name):
259            self._append_line = self._append_various_line
260            self._append_various_line(line)
261            return
262        elif (self._section.text.endswith('\n\n')
263              and line and not line[0].isspace()):
264            if line == 'Features:':
265                self._append_line = self._append_features_line
266            else:
267                self._start_section()
268                self._append_line = self._append_various_line
269                self._append_various_line(line)
270            return
271
272        self._append_freeform(line.strip())
273
274    def _append_features_line(self, line):
275        name = line.split(' ', 1)[0]
276
277        if name.startswith('@') and name.endswith(':'):
278            line = line[len(name)+1:]
279            self._start_features_section(name[1:-1])
280        elif self._is_section_tag(name):
281            self._append_line = self._append_various_line
282            self._append_various_line(line)
283            return
284        elif (self._section.text.endswith('\n\n')
285              and line and not line[0].isspace()):
286            self._start_section()
287            self._append_line = self._append_various_line
288            self._append_various_line(line)
289            return
290
291        self._append_freeform(line.strip())
292
293    def _append_various_line(self, line):
294        """
295        Process a line of documentation text in an additional section.
296
297        A symbol line is an error.
298
299        A section tag begins an additional section.  Start that
300        section and append the line to it.
301
302        Else, append the line to the current section.
303        """
304        name = line.split(' ', 1)[0]
305
306        if name.startswith('@') and name.endswith(':'):
307            raise QAPIParseError(self._parser,
308                                 "'%s' can't follow '%s' section"
309                                 % (name, self.sections[0].name))
310        elif self._is_section_tag(name):
311            line = line[len(name)+1:]
312            self._start_section(name[:-1])
313
314        if (not self._section.name or
315                not self._section.name.startswith('Example')):
316            line = line.strip()
317
318        self._append_freeform(line)
319
320    def _start_symbol_section(self, symbols_dict, name):
321        # FIXME invalid names other than the empty string aren't flagged
322        if not name:
323            raise QAPIParseError(self._parser, "invalid parameter name")
324        if name in symbols_dict:
325            raise QAPIParseError(self._parser,
326                                 "'%s' parameter name duplicated" % name)
327        assert not self.sections
328        self._end_section()
329        self._section = QAPIDoc.ArgSection(name)
330        symbols_dict[name] = self._section
331
332    def _start_args_section(self, name):
333        self._start_symbol_section(self.args, name)
334
335    def _start_features_section(self, name):
336        self._start_symbol_section(self.features, name)
337
338    def _start_section(self, name=None):
339        if name in ('Returns', 'Since') and self.has_section(name):
340            raise QAPIParseError(self._parser,
341                                 "duplicated '%s' section" % name)
342        self._end_section()
343        self._section = QAPIDoc.Section(name)
344        self.sections.append(self._section)
345
346    def _end_section(self):
347        if self._section:
348            text = self._section.text = self._section.text.strip()
349            if self._section.name and (not text or text.isspace()):
350                raise QAPIParseError(
351                    self._parser,
352                    "empty doc section '%s'" % self._section.name)
353            self._section = None
354
355    def _append_freeform(self, line):
356        match = re.match(r'(@\S+:)', line)
357        if match:
358            raise QAPIParseError(self._parser,
359                                 "'%s' not allowed in free-form documentation"
360                                 % match.group(1))
361        self._section.append(line)
362
363    def connect_member(self, member):
364        if member.name not in self.args:
365            # Undocumented TODO outlaw
366            self.args[member.name] = QAPIDoc.ArgSection(member.name)
367        self.args[member.name].connect(member)
368
369    def check_expr(self, expr):
370        if self.has_section('Returns') and 'command' not in expr:
371            raise QAPISemError(self.info,
372                               "'Returns:' is only valid for commands")
373
374    def check(self):
375        bogus = [name for name, section in self.args.items()
376                 if not section.member]
377        if bogus:
378            raise QAPISemError(
379                self.info,
380                "the following documented members are not in "
381                "the declaration: %s" % ", ".join(bogus))
382
383
384class QAPISchemaParser(object):
385
386    def __init__(self, fp, previously_included=[], incl_info=None):
387        self.fname = fp.name
388        previously_included.append(os.path.abspath(fp.name))
389        self.src = fp.read()
390        if self.src == '' or self.src[-1] != '\n':
391            self.src += '\n'
392        self.cursor = 0
393        self.info = QAPISourceInfo(self.fname, 1, incl_info)
394        self.line_pos = 0
395        self.exprs = []
396        self.docs = []
397        self.accept()
398        cur_doc = None
399
400        while self.tok is not None:
401            info = self.info
402            if self.tok == '#':
403                self.reject_expr_doc(cur_doc)
404                cur_doc = self.get_doc(info)
405                self.docs.append(cur_doc)
406                continue
407
408            expr = self.get_expr(False)
409            if 'include' in expr:
410                self.reject_expr_doc(cur_doc)
411                if len(expr) != 1:
412                    raise QAPISemError(info, "invalid 'include' directive")
413                include = expr['include']
414                if not isinstance(include, str):
415                    raise QAPISemError(info,
416                                       "value of 'include' must be a string")
417                incl_fname = os.path.join(os.path.dirname(self.fname),
418                                          include)
419                self.exprs.append({'expr': {'include': incl_fname},
420                                   'info': info})
421                exprs_include = self._include(include, info, incl_fname,
422                                              previously_included)
423                if exprs_include:
424                    self.exprs.extend(exprs_include.exprs)
425                    self.docs.extend(exprs_include.docs)
426            elif "pragma" in expr:
427                self.reject_expr_doc(cur_doc)
428                if len(expr) != 1:
429                    raise QAPISemError(info, "invalid 'pragma' directive")
430                pragma = expr['pragma']
431                if not isinstance(pragma, dict):
432                    raise QAPISemError(
433                        info, "value of 'pragma' must be an object")
434                for name, value in pragma.items():
435                    self._pragma(name, value, info)
436            else:
437                expr_elem = {'expr': expr,
438                             'info': info}
439                if cur_doc:
440                    if not cur_doc.symbol:
441                        raise QAPISemError(
442                            cur_doc.info, "definition documentation required")
443                    expr_elem['doc'] = cur_doc
444                self.exprs.append(expr_elem)
445            cur_doc = None
446        self.reject_expr_doc(cur_doc)
447
448    @staticmethod
449    def reject_expr_doc(doc):
450        if doc and doc.symbol:
451            raise QAPISemError(
452                doc.info,
453                "documentation for '%s' is not followed by the definition"
454                % doc.symbol)
455
456    def _include(self, include, info, incl_fname, previously_included):
457        incl_abs_fname = os.path.abspath(incl_fname)
458        # catch inclusion cycle
459        inf = info
460        while inf:
461            if incl_abs_fname == os.path.abspath(inf.fname):
462                raise QAPISemError(info, "inclusion loop for %s" % include)
463            inf = inf.parent
464
465        # skip multiple include of the same file
466        if incl_abs_fname in previously_included:
467            return None
468
469        try:
470            if sys.version_info[0] >= 3:
471                fobj = open(incl_fname, 'r', encoding='utf-8')
472            else:
473                fobj = open(incl_fname, 'r')
474        except IOError as e:
475            raise QAPISemError(info, "%s: %s" % (e.strerror, incl_fname))
476        return QAPISchemaParser(fobj, previously_included, info)
477
478    def _pragma(self, name, value, info):
479        global doc_required, returns_whitelist, name_case_whitelist
480        if name == 'doc-required':
481            if not isinstance(value, bool):
482                raise QAPISemError(info,
483                                   "pragma 'doc-required' must be boolean")
484            doc_required = value
485        elif name == 'returns-whitelist':
486            if (not isinstance(value, list)
487                    or any([not isinstance(elt, str) for elt in value])):
488                raise QAPISemError(
489                    info,
490                    "pragma returns-whitelist must be a list of strings")
491            returns_whitelist = value
492        elif name == 'name-case-whitelist':
493            if (not isinstance(value, list)
494                    or any([not isinstance(elt, str) for elt in value])):
495                raise QAPISemError(
496                    info,
497                    "pragma name-case-whitelist must be a list of strings")
498            name_case_whitelist = value
499        else:
500            raise QAPISemError(info, "unknown pragma '%s'" % name)
501
502    def accept(self, skip_comment=True):
503        while True:
504            self.tok = self.src[self.cursor]
505            self.pos = self.cursor
506            self.cursor += 1
507            self.val = None
508
509            if self.tok == '#':
510                if self.src[self.cursor] == '#':
511                    # Start of doc comment
512                    skip_comment = False
513                self.cursor = self.src.find('\n', self.cursor)
514                if not skip_comment:
515                    self.val = self.src[self.pos:self.cursor]
516                    return
517            elif self.tok in '{}:,[]':
518                return
519            elif self.tok == "'":
520                # Note: we accept only printable ASCII
521                string = ''
522                esc = False
523                while True:
524                    ch = self.src[self.cursor]
525                    self.cursor += 1
526                    if ch == '\n':
527                        raise QAPIParseError(self, "missing terminating \"'\"")
528                    if esc:
529                        # Note: we recognize only \\ because we have
530                        # no use for funny characters in strings
531                        if ch != '\\':
532                            raise QAPIParseError(self,
533                                                 "unknown escape \\%s" % ch)
534                        esc = False
535                    elif ch == '\\':
536                        esc = True
537                        continue
538                    elif ch == "'":
539                        self.val = string
540                        return
541                    if ord(ch) < 32 or ord(ch) >= 127:
542                        raise QAPIParseError(
543                            self, "funny character in string")
544                    string += ch
545            elif self.src.startswith('true', self.pos):
546                self.val = True
547                self.cursor += 3
548                return
549            elif self.src.startswith('false', self.pos):
550                self.val = False
551                self.cursor += 4
552                return
553            elif self.tok == '\n':
554                if self.cursor == len(self.src):
555                    self.tok = None
556                    return
557                self.info = self.info.next_line()
558                self.line_pos = self.cursor
559            elif not self.tok.isspace():
560                # Show up to next structural, whitespace or quote
561                # character
562                match = re.match('[^[\\]{}:,\\s\'"]+',
563                                 self.src[self.cursor-1:])
564                raise QAPIParseError(self, "stray '%s'" % match.group(0))
565
566    def get_members(self):
567        expr = OrderedDict()
568        if self.tok == '}':
569            self.accept()
570            return expr
571        if self.tok != "'":
572            raise QAPIParseError(self, "expected string or '}'")
573        while True:
574            key = self.val
575            self.accept()
576            if self.tok != ':':
577                raise QAPIParseError(self, "expected ':'")
578            self.accept()
579            if key in expr:
580                raise QAPIParseError(self, "duplicate key '%s'" % key)
581            expr[key] = self.get_expr(True)
582            if self.tok == '}':
583                self.accept()
584                return expr
585            if self.tok != ',':
586                raise QAPIParseError(self, "expected ',' or '}'")
587            self.accept()
588            if self.tok != "'":
589                raise QAPIParseError(self, "expected string")
590
591    def get_values(self):
592        expr = []
593        if self.tok == ']':
594            self.accept()
595            return expr
596        if self.tok not in "{['tfn":
597            raise QAPIParseError(
598                self, "expected '{', '[', ']', string, boolean or 'null'")
599        while True:
600            expr.append(self.get_expr(True))
601            if self.tok == ']':
602                self.accept()
603                return expr
604            if self.tok != ',':
605                raise QAPIParseError(self, "expected ',' or ']'")
606            self.accept()
607
608    def get_expr(self, nested):
609        if self.tok != '{' and not nested:
610            raise QAPIParseError(self, "expected '{'")
611        if self.tok == '{':
612            self.accept()
613            expr = self.get_members()
614        elif self.tok == '[':
615            self.accept()
616            expr = self.get_values()
617        elif self.tok in "'tfn":
618            expr = self.val
619            self.accept()
620        else:
621            raise QAPIParseError(
622                self, "expected '{', '[', string, boolean or 'null'")
623        return expr
624
625    def get_doc(self, info):
626        if self.val != '##':
627            raise QAPIParseError(
628                self, "junk after '##' at start of documentation comment")
629
630        doc = QAPIDoc(self, info)
631        self.accept(False)
632        while self.tok == '#':
633            if self.val.startswith('##'):
634                # End of doc comment
635                if self.val != '##':
636                    raise QAPIParseError(
637                        self,
638                        "junk after '##' at end of documentation comment")
639                doc.end_comment()
640                self.accept()
641                return doc
642            else:
643                doc.append(self.val)
644            self.accept(False)
645
646        raise QAPIParseError(self, "documentation comment must end with '##'")
647
648
649#
650# Check (context-free) schema expression structure
651#
652
653# Names must be letters, numbers, -, and _.  They must start with letter,
654# except for downstream extensions which must start with __RFQDN_.
655# Dots are only valid in the downstream extension prefix.
656valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
657                        '[a-zA-Z][a-zA-Z0-9_-]*$')
658
659
660def check_name_is_str(name, info, source):
661    if not isinstance(name, str):
662        raise QAPISemError(info, "%s requires a string name" % source)
663
664
665def check_name_str(name, info, source,
666                   allow_optional=False, enum_member=False,
667                   permit_upper=False):
668    global valid_name
669    membername = name
670
671    if allow_optional and name.startswith('*'):
672        membername = name[1:]
673    # Enum members can start with a digit, because the generated C
674    # code always prefixes it with the enum name
675    if enum_member and membername[0].isdigit():
676        membername = 'D' + membername
677    # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
678    # and 'q_obj_*' implicit type names.
679    if not valid_name.match(membername) or \
680       c_name(membername, False).startswith('q_'):
681        raise QAPISemError(info, "%s has an invalid name" % source)
682    if not permit_upper and name.lower() != name:
683        raise QAPISemError(
684            info, "%s uses uppercase in name" % source)
685    assert not membername.startswith('*')
686
687
688def check_defn_name_str(name, info, meta):
689    check_name_str(name, info, meta, permit_upper=True)
690    if name.endswith('Kind') or name.endswith('List'):
691        raise QAPISemError(
692            info, "%s name should not end in '%s'" % (meta, name[-4:]))
693
694
695def check_if(expr, info, source):
696
697    def check_if_str(ifcond, info):
698        if not isinstance(ifcond, str):
699            raise QAPISemError(
700                info,
701                "'if' condition of %s must be a string or a list of strings"
702                % source)
703        if ifcond.strip() == '':
704            raise QAPISemError(
705                info,
706                "'if' condition '%s' of %s makes no sense"
707                % (ifcond, source))
708
709    ifcond = expr.get('if')
710    if ifcond is None:
711        return
712    if isinstance(ifcond, list):
713        if ifcond == []:
714            raise QAPISemError(
715                info, "'if' condition [] of %s is useless" % source)
716        for elt in ifcond:
717            check_if_str(elt, info)
718    else:
719        check_if_str(ifcond, info)
720
721
722def check_type(value, info, source,
723               allow_array=False, allow_dict=False):
724    if value is None:
725        return
726
727    # Array type
728    if isinstance(value, list):
729        if not allow_array:
730            raise QAPISemError(info, "%s cannot be an array" % source)
731        if len(value) != 1 or not isinstance(value[0], str):
732            raise QAPISemError(info,
733                               "%s: array type must contain single type name" %
734                               source)
735        return
736
737    # Type name
738    if isinstance(value, str):
739        return
740
741    # Anonymous type
742
743    if not allow_dict:
744        raise QAPISemError(info, "%s should be a type name" % source)
745
746    if not isinstance(value, OrderedDict):
747        raise QAPISemError(info,
748                           "%s should be an object or type name" % source)
749
750    permit_upper = allow_dict in name_case_whitelist
751
752    # value is a dictionary, check that each member is okay
753    for (key, arg) in value.items():
754        key_source = "%s member '%s'" % (source, key)
755        check_name_str(key, info, key_source,
756                       allow_optional=True, permit_upper=permit_upper)
757        if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
758            raise QAPISemError(info, "%s uses reserved name" % key_source)
759        check_keys(arg, info, key_source, ['type'], ['if'])
760        check_if(arg, info, key_source)
761        normalize_if(arg)
762        check_type(arg['type'], info, key_source, allow_array=True)
763
764
765def check_command(expr, info):
766    args = expr.get('data')
767    rets = expr.get('returns')
768    boxed = expr.get('boxed', False)
769
770    if boxed and args is None:
771        raise QAPISemError(info, "'boxed': true requires 'data'")
772    check_type(args, info, "'data'", allow_dict=not boxed)
773    check_type(rets, info, "'returns'", allow_array=True)
774
775
776def check_event(expr, info):
777    args = expr.get('data')
778    boxed = expr.get('boxed', False)
779
780    if boxed and args is None:
781        raise QAPISemError(info, "'boxed': true requires 'data'")
782    check_type(args, info, "'data'", allow_dict=not boxed)
783
784
785def check_union(expr, info):
786    name = expr['union']
787    base = expr.get('base')
788    discriminator = expr.get('discriminator')
789    members = expr['data']
790
791    if discriminator is None:   # simple union
792        if base is not None:
793            raise QAPISemError(info, "'base' requires 'discriminator'")
794    else:                       # flat union
795        check_type(base, info, "'base'", allow_dict=name)
796        if not base:
797            raise QAPISemError(info, "'discriminator' requires 'base'")
798        check_name_is_str(discriminator, info, "'discriminator'")
799
800    for (key, value) in members.items():
801        source = "'data' member '%s'" % key
802        check_name_str(key, info, source)
803        check_keys(value, info, source, ['type'], ['if'])
804        check_if(value, info, source)
805        normalize_if(value)
806        check_type(value['type'], info, source, allow_array=not base)
807
808
809def check_alternate(expr, info):
810    members = expr['data']
811
812    if len(members) == 0:
813        raise QAPISemError(info, "'data' must not be empty")
814    for (key, value) in members.items():
815        source = "'data' member '%s'" % key
816        check_name_str(key, info, source)
817        check_keys(value, info, source, ['type'], ['if'])
818        check_if(value, info, source)
819        normalize_if(value)
820        check_type(value['type'], info, source)
821
822
823def check_enum(expr, info):
824    name = expr['enum']
825    members = expr['data']
826    prefix = expr.get('prefix')
827
828    if not isinstance(members, list):
829        raise QAPISemError(info, "'data' must be an array")
830    if prefix is not None and not isinstance(prefix, str):
831        raise QAPISemError(info, "'prefix' must be a string")
832
833    permit_upper = name in name_case_whitelist
834
835    for member in members:
836        source = "'data' member"
837        check_keys(member, info, source, ['name'], ['if'])
838        check_name_is_str(member['name'], info, source)
839        source = "%s '%s'" % (source, member['name'])
840        check_name_str(member['name'], info, source,
841                       enum_member=True, permit_upper=permit_upper)
842        check_if(member, info, source)
843        normalize_if(member)
844
845
846def check_struct(expr, info):
847    name = expr['struct']
848    members = expr['data']
849    features = expr.get('features')
850
851    check_type(members, info, "'data'", allow_dict=name)
852    check_type(expr.get('base'), info, "'base'")
853
854    if features:
855        if not isinstance(features, list):
856            raise QAPISemError(info, "'features' must be an array")
857        for f in features:
858            source = "'features' member"
859            assert isinstance(f, dict)
860            check_keys(f, info, source, ['name'], ['if'])
861            check_name_is_str(f['name'], info, source)
862            source = "%s '%s'" % (source, f['name'])
863            check_name_str(f['name'], info, source)
864            check_if(f, info, source)
865            normalize_if(f)
866
867
868def check_keys(value, info, source, required, optional):
869
870    def pprint(elems):
871        return ', '.join("'" + e + "'" for e in sorted(elems))
872
873    missing = set(required) - set(value)
874    if missing:
875        raise QAPISemError(
876            info,
877            "%s misses key%s %s"
878            % (source, 's' if len(missing) > 1 else '',
879               pprint(missing)))
880    allowed = set(required + optional)
881    unknown = set(value) - allowed
882    if unknown:
883        raise QAPISemError(
884            info,
885            "%s has unknown key%s %s\nValid keys are %s."
886            % (source, 's' if len(unknown) > 1 else '',
887               pprint(unknown), pprint(allowed)))
888
889
890def check_flags(expr, info):
891    for key in ['gen', 'success-response']:
892        if key in expr and expr[key] is not False:
893            raise QAPISemError(
894                info, "flag '%s' may only use false value" % key)
895    for key in ['boxed', 'allow-oob', 'allow-preconfig']:
896        if key in expr and expr[key] is not True:
897            raise QAPISemError(
898                info, "flag '%s' may only use true value" % key)
899
900
901def normalize_enum(expr):
902    if isinstance(expr['data'], list):
903        expr['data'] = [m if isinstance(m, dict) else {'name': m}
904                        for m in expr['data']]
905
906
907def normalize_members(members):
908    if isinstance(members, OrderedDict):
909        for key, arg in members.items():
910            if isinstance(arg, dict):
911                continue
912            members[key] = {'type': arg}
913
914
915def normalize_features(features):
916    if isinstance(features, list):
917        features[:] = [f if isinstance(f, dict) else {'name': f}
918                       for f in features]
919
920
921def normalize_if(expr):
922    ifcond = expr.get('if')
923    if isinstance(ifcond, str):
924        expr['if'] = [ifcond]
925
926
927def check_exprs(exprs):
928    for expr_elem in exprs:
929        expr = expr_elem['expr']
930        info = expr_elem['info']
931        doc = expr_elem.get('doc')
932
933        if 'include' in expr:
934            continue
935
936        if 'enum' in expr:
937            meta = 'enum'
938        elif 'union' in expr:
939            meta = 'union'
940        elif 'alternate' in expr:
941            meta = 'alternate'
942        elif 'struct' in expr:
943            meta = 'struct'
944        elif 'command' in expr:
945            meta = 'command'
946        elif 'event' in expr:
947            meta = 'event'
948        else:
949            raise QAPISemError(info, "expression is missing metatype")
950
951        name = expr[meta]
952        check_name_is_str(name, info, "'%s'" % meta)
953        info.set_defn(meta, name)
954        check_defn_name_str(name, info, meta)
955
956        if doc:
957            if doc.symbol != name:
958                raise QAPISemError(
959                    info, "documentation comment is for '%s'" % doc.symbol)
960            doc.check_expr(expr)
961        elif doc_required:
962            raise QAPISemError(info,
963                               "documentation comment required")
964
965        if meta == 'enum':
966            check_keys(expr, info, meta,
967                       ['enum', 'data'], ['if', 'prefix'])
968            normalize_enum(expr)
969            check_enum(expr, info)
970        elif meta == 'union':
971            check_keys(expr, info, meta,
972                       ['union', 'data'],
973                       ['base', 'discriminator', 'if'])
974            normalize_members(expr.get('base'))
975            normalize_members(expr['data'])
976            check_union(expr, info)
977        elif meta == 'alternate':
978            check_keys(expr, info, meta,
979                       ['alternate', 'data'], ['if'])
980            normalize_members(expr['data'])
981            check_alternate(expr, info)
982        elif meta == 'struct':
983            check_keys(expr, info, meta,
984                       ['struct', 'data'], ['base', 'if', 'features'])
985            normalize_members(expr['data'])
986            normalize_features(expr.get('features'))
987            check_struct(expr, info)
988        elif meta == 'command':
989            check_keys(expr, info, meta,
990                       ['command'],
991                       ['data', 'returns', 'boxed', 'if',
992                        'gen', 'success-response', 'allow-oob',
993                        'allow-preconfig'])
994            normalize_members(expr.get('data'))
995            check_command(expr, info)
996        elif meta == 'event':
997            check_keys(expr, info, meta,
998                       ['event'], ['data', 'boxed', 'if'])
999            normalize_members(expr.get('data'))
1000            check_event(expr, info)
1001        else:
1002            assert False, 'unexpected meta type'
1003
1004        normalize_if(expr)
1005        check_if(expr, info, meta)
1006        check_flags(expr, info)
1007
1008    return exprs
1009
1010
1011#
1012# Schema compiler frontend
1013# TODO catching name collisions in generated code would be nice
1014#
1015
1016class QAPISchemaEntity(object):
1017    meta = None
1018
1019    def __init__(self, name, info, doc, ifcond=None):
1020        assert name is None or isinstance(name, str)
1021        self.name = name
1022        self._module = None
1023        # For explicitly defined entities, info points to the (explicit)
1024        # definition.  For builtins (and their arrays), info is None.
1025        # For implicitly defined entities, info points to a place that
1026        # triggered the implicit definition (there may be more than one
1027        # such place).
1028        self.info = info
1029        self.doc = doc
1030        self._ifcond = ifcond or []
1031        self._checked = False
1032
1033    def c_name(self):
1034        return c_name(self.name)
1035
1036    def check(self, schema):
1037        assert not self._checked
1038        if self.info:
1039            self._module = os.path.relpath(self.info.fname,
1040                                           os.path.dirname(schema.fname))
1041        self._checked = True
1042
1043    @property
1044    def ifcond(self):
1045        assert self._checked
1046        return self._ifcond
1047
1048    @property
1049    def module(self):
1050        assert self._checked
1051        return self._module
1052
1053    def is_implicit(self):
1054        return not self.info
1055
1056    def visit(self, visitor):
1057        assert self._checked
1058
1059    def describe(self):
1060        assert self.meta
1061        return "%s '%s'" % (self.meta, self.name)
1062
1063
1064class QAPISchemaVisitor(object):
1065    def visit_begin(self, schema):
1066        pass
1067
1068    def visit_end(self):
1069        pass
1070
1071    def visit_module(self, fname):
1072        pass
1073
1074    def visit_needed(self, entity):
1075        # Default to visiting everything
1076        return True
1077
1078    def visit_include(self, fname, info):
1079        pass
1080
1081    def visit_builtin_type(self, name, info, json_type):
1082        pass
1083
1084    def visit_enum_type(self, name, info, ifcond, members, prefix):
1085        pass
1086
1087    def visit_array_type(self, name, info, ifcond, element_type):
1088        pass
1089
1090    def visit_object_type(self, name, info, ifcond, base, members, variants,
1091                          features):
1092        pass
1093
1094    def visit_object_type_flat(self, name, info, ifcond, members, variants,
1095                               features):
1096        pass
1097
1098    def visit_alternate_type(self, name, info, ifcond, variants):
1099        pass
1100
1101    def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
1102                      success_response, boxed, allow_oob, allow_preconfig):
1103        pass
1104
1105    def visit_event(self, name, info, ifcond, arg_type, boxed):
1106        pass
1107
1108
1109class QAPISchemaInclude(QAPISchemaEntity):
1110
1111    def __init__(self, fname, info):
1112        QAPISchemaEntity.__init__(self, None, info, None)
1113        self.fname = fname
1114
1115    def visit(self, visitor):
1116        QAPISchemaEntity.visit(self, visitor)
1117        visitor.visit_include(self.fname, self.info)
1118
1119
1120class QAPISchemaType(QAPISchemaEntity):
1121    # Return the C type for common use.
1122    # For the types we commonly box, this is a pointer type.
1123    def c_type(self):
1124        pass
1125
1126    # Return the C type to be used in a parameter list.
1127    def c_param_type(self):
1128        return self.c_type()
1129
1130    # Return the C type to be used where we suppress boxing.
1131    def c_unboxed_type(self):
1132        return self.c_type()
1133
1134    def json_type(self):
1135        pass
1136
1137    def alternate_qtype(self):
1138        json2qtype = {
1139            'null':    'QTYPE_QNULL',
1140            'string':  'QTYPE_QSTRING',
1141            'number':  'QTYPE_QNUM',
1142            'int':     'QTYPE_QNUM',
1143            'boolean': 'QTYPE_QBOOL',
1144            'object':  'QTYPE_QDICT'
1145        }
1146        return json2qtype.get(self.json_type())
1147
1148    def doc_type(self):
1149        if self.is_implicit():
1150            return None
1151        return self.name
1152
1153    def describe(self):
1154        assert self.meta
1155        return "%s type '%s'" % (self.meta, self.name)
1156
1157
1158class QAPISchemaBuiltinType(QAPISchemaType):
1159    meta = 'built-in'
1160
1161    def __init__(self, name, json_type, c_type):
1162        QAPISchemaType.__init__(self, name, None, None)
1163        assert not c_type or isinstance(c_type, str)
1164        assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1165                             'value')
1166        self._json_type_name = json_type
1167        self._c_type_name = c_type
1168
1169    def c_name(self):
1170        return self.name
1171
1172    def c_type(self):
1173        return self._c_type_name
1174
1175    def c_param_type(self):
1176        if self.name == 'str':
1177            return 'const ' + self._c_type_name
1178        return self._c_type_name
1179
1180    def json_type(self):
1181        return self._json_type_name
1182
1183    def doc_type(self):
1184        return self.json_type()
1185
1186    def visit(self, visitor):
1187        QAPISchemaType.visit(self, visitor)
1188        visitor.visit_builtin_type(self.name, self.info, self.json_type())
1189
1190
1191class QAPISchemaEnumType(QAPISchemaType):
1192    meta = 'enum'
1193
1194    def __init__(self, name, info, doc, ifcond, members, prefix):
1195        QAPISchemaType.__init__(self, name, info, doc, ifcond)
1196        for m in members:
1197            assert isinstance(m, QAPISchemaEnumMember)
1198            m.set_defined_in(name)
1199        assert prefix is None or isinstance(prefix, str)
1200        self.members = members
1201        self.prefix = prefix
1202
1203    def check(self, schema):
1204        QAPISchemaType.check(self, schema)
1205        seen = {}
1206        for m in self.members:
1207            m.check_clash(self.info, seen)
1208            if self.doc:
1209                self.doc.connect_member(m)
1210
1211    def is_implicit(self):
1212        # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1213        return self.name.endswith('Kind') or self.name == 'QType'
1214
1215    def c_type(self):
1216        return c_name(self.name)
1217
1218    def member_names(self):
1219        return [m.name for m in self.members]
1220
1221    def json_type(self):
1222        return 'string'
1223
1224    def visit(self, visitor):
1225        QAPISchemaType.visit(self, visitor)
1226        visitor.visit_enum_type(self.name, self.info, self.ifcond,
1227                                self.members, self.prefix)
1228
1229
1230class QAPISchemaArrayType(QAPISchemaType):
1231    meta = 'array'
1232
1233    def __init__(self, name, info, element_type):
1234        QAPISchemaType.__init__(self, name, info, None, None)
1235        assert isinstance(element_type, str)
1236        self._element_type_name = element_type
1237        self.element_type = None
1238
1239    def check(self, schema):
1240        QAPISchemaType.check(self, schema)
1241        self.element_type = schema.resolve_type(
1242            self._element_type_name, self.info,
1243            self.info and self.info.defn_meta)
1244        assert not isinstance(self.element_type, QAPISchemaArrayType)
1245
1246    @property
1247    def ifcond(self):
1248        assert self._checked
1249        return self.element_type.ifcond
1250
1251    @property
1252    def module(self):
1253        assert self._checked
1254        return self.element_type.module
1255
1256    def is_implicit(self):
1257        return True
1258
1259    def c_type(self):
1260        return c_name(self.name) + pointer_suffix
1261
1262    def json_type(self):
1263        return 'array'
1264
1265    def doc_type(self):
1266        elt_doc_type = self.element_type.doc_type()
1267        if not elt_doc_type:
1268            return None
1269        return 'array of ' + elt_doc_type
1270
1271    def visit(self, visitor):
1272        QAPISchemaType.visit(self, visitor)
1273        visitor.visit_array_type(self.name, self.info, self.ifcond,
1274                                 self.element_type)
1275
1276    def describe(self):
1277        assert self.meta
1278        return "%s type ['%s']" % (self.meta, self._element_type_name)
1279
1280
1281class QAPISchemaObjectType(QAPISchemaType):
1282    def __init__(self, name, info, doc, ifcond,
1283                 base, local_members, variants, features):
1284        # struct has local_members, optional base, and no variants
1285        # flat union has base, variants, and no local_members
1286        # simple union has local_members, variants, and no base
1287        QAPISchemaType.__init__(self, name, info, doc, ifcond)
1288        self.meta = 'union' if variants else 'struct'
1289        assert base is None or isinstance(base, str)
1290        for m in local_members:
1291            assert isinstance(m, QAPISchemaObjectTypeMember)
1292            m.set_defined_in(name)
1293        if variants is not None:
1294            assert isinstance(variants, QAPISchemaObjectTypeVariants)
1295            variants.set_defined_in(name)
1296        for f in features:
1297            assert isinstance(f, QAPISchemaFeature)
1298            f.set_defined_in(name)
1299        self._base_name = base
1300        self.base = None
1301        self.local_members = local_members
1302        self.variants = variants
1303        self.members = None
1304        self.features = features
1305
1306    def check(self, schema):
1307        # This calls another type T's .check() exactly when the C
1308        # struct emitted by gen_object() contains that T's C struct
1309        # (pointers don't count).
1310        if self.members is not None:
1311            # A previous .check() completed: nothing to do
1312            return
1313        if self._checked:
1314            # Recursed: C struct contains itself
1315            raise QAPISemError(self.info,
1316                               "object %s contains itself" % self.name)
1317
1318        QAPISchemaType.check(self, schema)
1319        assert self._checked and self.members is None
1320
1321        seen = OrderedDict()
1322        if self._base_name:
1323            self.base = schema.resolve_type(self._base_name, self.info,
1324                                            "'base'")
1325            if (not isinstance(self.base, QAPISchemaObjectType)
1326                    or self.base.variants):
1327                raise QAPISemError(
1328                    self.info,
1329                    "'base' requires a struct type, %s isn't"
1330                    % self.base.describe())
1331            self.base.check(schema)
1332            self.base.check_clash(self.info, seen)
1333        for m in self.local_members:
1334            m.check(schema)
1335            m.check_clash(self.info, seen)
1336            if self.doc:
1337                self.doc.connect_member(m)
1338        members = seen.values()
1339
1340        if self.variants:
1341            self.variants.check(schema, seen)
1342            self.variants.check_clash(self.info, seen)
1343
1344        # Features are in a name space separate from members
1345        seen = {}
1346        for f in self.features:
1347            f.check_clash(self.info, seen)
1348
1349        if self.doc:
1350            self.doc.check()
1351
1352        self.members = members  # mark completed
1353
1354    # Check that the members of this type do not cause duplicate JSON members,
1355    # and update seen to track the members seen so far. Report any errors
1356    # on behalf of info, which is not necessarily self.info
1357    def check_clash(self, info, seen):
1358        assert self._checked
1359        assert not self.variants       # not implemented
1360        for m in self.members:
1361            m.check_clash(info, seen)
1362
1363    @property
1364    def ifcond(self):
1365        assert self._checked
1366        if isinstance(self._ifcond, QAPISchemaType):
1367            # Simple union wrapper type inherits from wrapped type;
1368            # see _make_implicit_object_type()
1369            return self._ifcond.ifcond
1370        return self._ifcond
1371
1372    def is_implicit(self):
1373        # See QAPISchema._make_implicit_object_type(), as well as
1374        # _def_predefineds()
1375        return self.name.startswith('q_')
1376
1377    def is_empty(self):
1378        assert self.members is not None
1379        return not self.members and not self.variants
1380
1381    def c_name(self):
1382        assert self.name != 'q_empty'
1383        return QAPISchemaType.c_name(self)
1384
1385    def c_type(self):
1386        assert not self.is_implicit()
1387        return c_name(self.name) + pointer_suffix
1388
1389    def c_unboxed_type(self):
1390        return c_name(self.name)
1391
1392    def json_type(self):
1393        return 'object'
1394
1395    def visit(self, visitor):
1396        QAPISchemaType.visit(self, visitor)
1397        visitor.visit_object_type(self.name, self.info, self.ifcond,
1398                                  self.base, self.local_members, self.variants,
1399                                  self.features)
1400        visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
1401                                       self.members, self.variants,
1402                                       self.features)
1403
1404
1405class QAPISchemaMember(object):
1406    """ Represents object members, enum members and features """
1407    role = 'member'
1408
1409    def __init__(self, name, info, ifcond=None):
1410        assert isinstance(name, str)
1411        self.name = name
1412        self.info = info
1413        self.ifcond = ifcond or []
1414        self.defined_in = None
1415
1416    def set_defined_in(self, name):
1417        assert not self.defined_in
1418        self.defined_in = name
1419
1420    def check_clash(self, info, seen):
1421        cname = c_name(self.name)
1422        if cname in seen:
1423            raise QAPISemError(
1424                info,
1425                "%s collides with %s"
1426                % (self.describe(info), seen[cname].describe(info)))
1427        seen[cname] = self
1428
1429    def describe(self, info):
1430        role = self.role
1431        defined_in = self.defined_in
1432        assert defined_in
1433
1434        if defined_in.startswith('q_obj_'):
1435            # See QAPISchema._make_implicit_object_type() - reverse the
1436            # mapping there to create a nice human-readable description
1437            defined_in = defined_in[6:]
1438            if defined_in.endswith('-arg'):
1439                # Implicit type created for a command's dict 'data'
1440                assert role == 'member'
1441                role = 'parameter'
1442            elif defined_in.endswith('-base'):
1443                # Implicit type created for a flat union's dict 'base'
1444                role = 'base ' + role
1445            else:
1446                # Implicit type created for a simple union's branch
1447                assert defined_in.endswith('-wrapper')
1448                # Unreachable and not implemented
1449                assert False
1450        elif defined_in.endswith('Kind'):
1451            # See QAPISchema._make_implicit_enum_type()
1452            # Implicit enum created for simple union's branches
1453            assert role == 'value'
1454            role = 'branch'
1455        elif defined_in != info.defn_name:
1456            return "%s '%s' of type '%s'" % (role, self.name, defined_in)
1457        return "%s '%s'" % (role, self.name)
1458
1459
1460class QAPISchemaEnumMember(QAPISchemaMember):
1461    role = 'value'
1462
1463
1464class QAPISchemaFeature(QAPISchemaMember):
1465    role = 'feature'
1466
1467
1468class QAPISchemaObjectTypeMember(QAPISchemaMember):
1469    def __init__(self, name, info, typ, optional, ifcond=None):
1470        QAPISchemaMember.__init__(self, name, info, ifcond)
1471        assert isinstance(typ, str)
1472        assert isinstance(optional, bool)
1473        self._type_name = typ
1474        self.type = None
1475        self.optional = optional
1476
1477    def check(self, schema):
1478        assert self.defined_in
1479        self.type = schema.resolve_type(self._type_name, self.info,
1480                                        self.describe)
1481
1482
1483class QAPISchemaObjectTypeVariants(object):
1484    def __init__(self, tag_name, info, tag_member, variants):
1485        # Flat unions pass tag_name but not tag_member.
1486        # Simple unions and alternates pass tag_member but not tag_name.
1487        # After check(), tag_member is always set, and tag_name remains
1488        # a reliable witness of being used by a flat union.
1489        assert bool(tag_member) != bool(tag_name)
1490        assert (isinstance(tag_name, str) or
1491                isinstance(tag_member, QAPISchemaObjectTypeMember))
1492        for v in variants:
1493            assert isinstance(v, QAPISchemaObjectTypeVariant)
1494        self._tag_name = tag_name
1495        self.info = info
1496        self.tag_member = tag_member
1497        self.variants = variants
1498
1499    def set_defined_in(self, name):
1500        for v in self.variants:
1501            v.set_defined_in(name)
1502
1503    def check(self, schema, seen):
1504        if not self.tag_member: # flat union
1505            self.tag_member = seen.get(c_name(self._tag_name))
1506            base = "'base'"
1507            # Pointing to the base type when not implicit would be
1508            # nice, but we don't know it here
1509            if not self.tag_member or self._tag_name != self.tag_member.name:
1510                raise QAPISemError(
1511                    self.info,
1512                    "discriminator '%s' is not a member of %s"
1513                    % (self._tag_name, base))
1514            # Here we do:
1515            base_type = schema.lookup_type(self.tag_member.defined_in)
1516            assert base_type
1517            if not base_type.is_implicit():
1518                base = "base type '%s'" % self.tag_member.defined_in
1519            if not isinstance(self.tag_member.type, QAPISchemaEnumType):
1520                raise QAPISemError(
1521                    self.info,
1522                    "discriminator member '%s' of %s must be of enum type"
1523                    % (self._tag_name, base))
1524            if self.tag_member.optional:
1525                raise QAPISemError(
1526                    self.info,
1527                    "discriminator member '%s' of %s must not be optional"
1528                    % (self._tag_name, base))
1529            if self.tag_member.ifcond:
1530                raise QAPISemError(
1531                    self.info,
1532                    "discriminator member '%s' of %s must not be conditional"
1533                    % (self._tag_name, base))
1534        else:                   # simple union
1535            assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1536            assert not self.tag_member.optional
1537            assert self.tag_member.ifcond == []
1538        if self._tag_name:    # flat union
1539            # branches that are not explicitly covered get an empty type
1540            cases = set([v.name for v in self.variants])
1541            for m in self.tag_member.type.members:
1542                if m.name not in cases:
1543                    v = QAPISchemaObjectTypeVariant(m.name, self.info,
1544                                                    'q_empty', m.ifcond)
1545                    v.set_defined_in(self.tag_member.defined_in)
1546                    self.variants.append(v)
1547        if not self.variants:
1548            raise QAPISemError(self.info, "union has no branches")
1549        for v in self.variants:
1550            v.check(schema)
1551            # Union names must match enum values; alternate names are
1552            # checked separately. Use 'seen' to tell the two apart.
1553            if seen:
1554                if v.name not in self.tag_member.type.member_names():
1555                    raise QAPISemError(
1556                        self.info,
1557                        "branch '%s' is not a value of %s"
1558                        % (v.name, self.tag_member.type.describe()))
1559                if (not isinstance(v.type, QAPISchemaObjectType)
1560                        or v.type.variants):
1561                    raise QAPISemError(
1562                        self.info,
1563                        "%s cannot use %s"
1564                        % (v.describe(self.info), v.type.describe()))
1565                v.type.check(schema)
1566
1567    def check_clash(self, info, seen):
1568        for v in self.variants:
1569            # Reset seen map for each variant, since qapi names from one
1570            # branch do not affect another branch
1571            v.type.check_clash(info, dict(seen))
1572
1573
1574class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1575    role = 'branch'
1576
1577    def __init__(self, name, info, typ, ifcond=None):
1578        QAPISchemaObjectTypeMember.__init__(self, name, info, typ,
1579                                            False, ifcond)
1580
1581
1582class QAPISchemaAlternateType(QAPISchemaType):
1583    meta = 'alternate'
1584
1585    def __init__(self, name, info, doc, ifcond, variants):
1586        QAPISchemaType.__init__(self, name, info, doc, ifcond)
1587        assert isinstance(variants, QAPISchemaObjectTypeVariants)
1588        assert variants.tag_member
1589        variants.set_defined_in(name)
1590        variants.tag_member.set_defined_in(self.name)
1591        self.variants = variants
1592
1593    def check(self, schema):
1594        QAPISchemaType.check(self, schema)
1595        self.variants.tag_member.check(schema)
1596        # Not calling self.variants.check_clash(), because there's nothing
1597        # to clash with
1598        self.variants.check(schema, {})
1599        # Alternate branch names have no relation to the tag enum values;
1600        # so we have to check for potential name collisions ourselves.
1601        seen = {}
1602        types_seen = {}
1603        for v in self.variants.variants:
1604            v.check_clash(self.info, seen)
1605            qtype = v.type.alternate_qtype()
1606            if not qtype:
1607                raise QAPISemError(
1608                    self.info,
1609                    "%s cannot use %s"
1610                    % (v.describe(self.info), v.type.describe()))
1611            conflicting = set([qtype])
1612            if qtype == 'QTYPE_QSTRING':
1613                if isinstance(v.type, QAPISchemaEnumType):
1614                    for m in v.type.members:
1615                        if m.name in ['on', 'off']:
1616                            conflicting.add('QTYPE_QBOOL')
1617                        if re.match(r'[-+0-9.]', m.name):
1618                            # lazy, could be tightened
1619                            conflicting.add('QTYPE_QNUM')
1620                else:
1621                    conflicting.add('QTYPE_QNUM')
1622                    conflicting.add('QTYPE_QBOOL')
1623            for qt in conflicting:
1624                if qt in types_seen:
1625                    raise QAPISemError(
1626                        self.info,
1627                        "%s can't be distinguished from '%s'"
1628                        % (v.describe(self.info), types_seen[qt]))
1629                types_seen[qt] = v.name
1630            if self.doc:
1631                self.doc.connect_member(v)
1632        if self.doc:
1633            self.doc.check()
1634
1635    def c_type(self):
1636        return c_name(self.name) + pointer_suffix
1637
1638    def json_type(self):
1639        return 'value'
1640
1641    def visit(self, visitor):
1642        QAPISchemaType.visit(self, visitor)
1643        visitor.visit_alternate_type(self.name, self.info, self.ifcond,
1644                                     self.variants)
1645
1646
1647class QAPISchemaCommand(QAPISchemaEntity):
1648    meta = 'command'
1649
1650    def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
1651                 gen, success_response, boxed, allow_oob, allow_preconfig):
1652        QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1653        assert not arg_type or isinstance(arg_type, str)
1654        assert not ret_type or isinstance(ret_type, str)
1655        self._arg_type_name = arg_type
1656        self.arg_type = None
1657        self._ret_type_name = ret_type
1658        self.ret_type = None
1659        self.gen = gen
1660        self.success_response = success_response
1661        self.boxed = boxed
1662        self.allow_oob = allow_oob
1663        self.allow_preconfig = allow_preconfig
1664
1665    def check(self, schema):
1666        QAPISchemaEntity.check(self, schema)
1667        if self._arg_type_name:
1668            self.arg_type = schema.resolve_type(
1669                self._arg_type_name, self.info, "command's 'data'")
1670            if not isinstance(self.arg_type, QAPISchemaObjectType):
1671                raise QAPISemError(
1672                    self.info,
1673                    "command's 'data' cannot take %s"
1674                    % self.arg_type.describe())
1675            if self.arg_type.variants and not self.boxed:
1676                raise QAPISemError(
1677                    self.info,
1678                    "command's 'data' can take %s only with 'boxed': true"
1679                    % self.arg_type.describe())
1680        if self._ret_type_name:
1681            self.ret_type = schema.resolve_type(
1682                self._ret_type_name, self.info, "command's 'returns'")
1683            if self.name not in returns_whitelist:
1684                if not (isinstance(self.ret_type, QAPISchemaObjectType)
1685                        or (isinstance(self.ret_type, QAPISchemaArrayType)
1686                            and isinstance(self.ret_type.element_type,
1687                                           QAPISchemaObjectType))):
1688                    raise QAPISemError(
1689                        self.info,
1690                        "command's 'returns' cannot take %s"
1691                        % self.ret_type.describe())
1692
1693    def visit(self, visitor):
1694        QAPISchemaEntity.visit(self, visitor)
1695        visitor.visit_command(self.name, self.info, self.ifcond,
1696                              self.arg_type, self.ret_type,
1697                              self.gen, self.success_response,
1698                              self.boxed, self.allow_oob,
1699                              self.allow_preconfig)
1700
1701
1702class QAPISchemaEvent(QAPISchemaEntity):
1703    meta = 'event'
1704
1705    def __init__(self, name, info, doc, ifcond, arg_type, boxed):
1706        QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
1707        assert not arg_type or isinstance(arg_type, str)
1708        self._arg_type_name = arg_type
1709        self.arg_type = None
1710        self.boxed = boxed
1711
1712    def check(self, schema):
1713        QAPISchemaEntity.check(self, schema)
1714        if self._arg_type_name:
1715            self.arg_type = schema.resolve_type(
1716                self._arg_type_name, self.info, "event's 'data'")
1717            if not isinstance(self.arg_type, QAPISchemaObjectType):
1718                raise QAPISemError(
1719                    self.info,
1720                    "event's 'data' cannot take %s"
1721                    % self.arg_type.describe())
1722            if self.arg_type.variants and not self.boxed:
1723                raise QAPISemError(
1724                    self.info,
1725                    "event's 'data' can take %s only with 'boxed': true"
1726                    % self.arg_type.describe())
1727
1728    def visit(self, visitor):
1729        QAPISchemaEntity.visit(self, visitor)
1730        visitor.visit_event(self.name, self.info, self.ifcond,
1731                            self.arg_type, self.boxed)
1732
1733
1734class QAPISchema(object):
1735    def __init__(self, fname):
1736        self.fname = fname
1737        if sys.version_info[0] >= 3:
1738            f = open(fname, 'r', encoding='utf-8')
1739        else:
1740            f = open(fname, 'r')
1741        parser = QAPISchemaParser(f)
1742        exprs = check_exprs(parser.exprs)
1743        self.docs = parser.docs
1744        self._entity_list = []
1745        self._entity_dict = {}
1746        self._predefining = True
1747        self._def_predefineds()
1748        self._predefining = False
1749        self._def_exprs(exprs)
1750        self.check()
1751
1752    def _def_entity(self, ent):
1753        # Only the predefined types are allowed to not have info
1754        assert ent.info or self._predefining
1755        self._entity_list.append(ent)
1756        if ent.name is None:
1757            return
1758        # TODO reject names that differ only in '_' vs. '.'  vs. '-',
1759        # because they're liable to clash in generated C.
1760        other_ent = self._entity_dict.get(ent.name)
1761        if other_ent:
1762            if other_ent.info:
1763                where = QAPIError(other_ent.info, None, "previous definition")
1764                raise QAPISemError(
1765                    ent.info,
1766                    "'%s' is already defined\n%s" % (ent.name, where))
1767            raise QAPISemError(
1768                ent.info, "%s is already defined" % other_ent.describe())
1769        self._entity_dict[ent.name] = ent
1770
1771    def lookup_entity(self, name, typ=None):
1772        ent = self._entity_dict.get(name)
1773        if typ and not isinstance(ent, typ):
1774            return None
1775        return ent
1776
1777    def lookup_type(self, name):
1778        return self.lookup_entity(name, QAPISchemaType)
1779
1780    def resolve_type(self, name, info, what):
1781        typ = self.lookup_type(name)
1782        if not typ:
1783            if callable(what):
1784                what = what(info)
1785            raise QAPISemError(
1786                info, "%s uses unknown type '%s'" % (what, name))
1787        return typ
1788
1789    def _def_include(self, expr, info, doc):
1790        include = expr['include']
1791        assert doc is None
1792        main_info = info
1793        while main_info.parent:
1794            main_info = main_info.parent
1795        fname = os.path.relpath(include, os.path.dirname(main_info.fname))
1796        self._def_entity(QAPISchemaInclude(fname, info))
1797
1798    def _def_builtin_type(self, name, json_type, c_type):
1799        self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1800        # Instantiating only the arrays that are actually used would
1801        # be nice, but we can't as long as their generated code
1802        # (qapi-builtin-types.[ch]) may be shared by some other
1803        # schema.
1804        self._make_array_type(name, None)
1805
1806    def _def_predefineds(self):
1807        for t in [('str',    'string',  'char' + pointer_suffix),
1808                  ('number', 'number',  'double'),
1809                  ('int',    'int',     'int64_t'),
1810                  ('int8',   'int',     'int8_t'),
1811                  ('int16',  'int',     'int16_t'),
1812                  ('int32',  'int',     'int32_t'),
1813                  ('int64',  'int',     'int64_t'),
1814                  ('uint8',  'int',     'uint8_t'),
1815                  ('uint16', 'int',     'uint16_t'),
1816                  ('uint32', 'int',     'uint32_t'),
1817                  ('uint64', 'int',     'uint64_t'),
1818                  ('size',   'int',     'uint64_t'),
1819                  ('bool',   'boolean', 'bool'),
1820                  ('any',    'value',   'QObject' + pointer_suffix),
1821                  ('null',   'null',    'QNull' + pointer_suffix)]:
1822            self._def_builtin_type(*t)
1823        self.the_empty_object_type = QAPISchemaObjectType(
1824            'q_empty', None, None, None, None, [], None, [])
1825        self._def_entity(self.the_empty_object_type)
1826
1827        qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist',
1828                  'qbool']
1829        qtype_values = self._make_enum_members(
1830            [{'name': n} for n in qtypes], None)
1831
1832        self._def_entity(QAPISchemaEnumType('QType', None, None, None,
1833                                            qtype_values, 'QTYPE'))
1834
1835    def _make_features(self, features, info):
1836        return [QAPISchemaFeature(f['name'], info, f.get('if'))
1837                for f in features]
1838
1839    def _make_enum_members(self, values, info):
1840        return [QAPISchemaEnumMember(v['name'], info, v.get('if'))
1841                for v in values]
1842
1843    def _make_implicit_enum_type(self, name, info, ifcond, values):
1844        # See also QAPISchemaObjectTypeMember.describe()
1845        name = name + 'Kind'    # reserved by check_defn_name_str()
1846        self._def_entity(QAPISchemaEnumType(
1847            name, info, None, ifcond, self._make_enum_members(values, info),
1848            None))
1849        return name
1850
1851    def _make_array_type(self, element_type, info):
1852        name = element_type + 'List'    # reserved by check_defn_name_str()
1853        if not self.lookup_type(name):
1854            self._def_entity(QAPISchemaArrayType(name, info, element_type))
1855        return name
1856
1857    def _make_implicit_object_type(self, name, info, doc, ifcond,
1858                                   role, members):
1859        if not members:
1860            return None
1861        # See also QAPISchemaObjectTypeMember.describe()
1862        name = 'q_obj_%s-%s' % (name, role)
1863        typ = self.lookup_entity(name, QAPISchemaObjectType)
1864        if typ:
1865            # The implicit object type has multiple users.  This can
1866            # happen only for simple unions' implicit wrapper types.
1867            # Its ifcond should be the disjunction of its user's
1868            # ifconds.  Not implemented.  Instead, we always pass the
1869            # wrapped type's ifcond, which is trivially the same for all
1870            # users.  It's also necessary for the wrapper to compile.
1871            # But it's not tight: the disjunction need not imply it.  We
1872            # may end up compiling useless wrapper types.
1873            # TODO kill simple unions or implement the disjunction
1874            assert (ifcond or []) == typ._ifcond # pylint: disable=protected-access
1875        else:
1876            self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
1877                                                  None, members, None, []))
1878        return name
1879
1880    def _def_enum_type(self, expr, info, doc):
1881        name = expr['enum']
1882        data = expr['data']
1883        prefix = expr.get('prefix')
1884        ifcond = expr.get('if')
1885        self._def_entity(QAPISchemaEnumType(
1886            name, info, doc, ifcond,
1887            self._make_enum_members(data, info), prefix))
1888
1889    def _make_member(self, name, typ, ifcond, info):
1890        optional = False
1891        if name.startswith('*'):
1892            name = name[1:]
1893            optional = True
1894        if isinstance(typ, list):
1895            assert len(typ) == 1
1896            typ = self._make_array_type(typ[0], info)
1897        return QAPISchemaObjectTypeMember(name, info, typ, optional, ifcond)
1898
1899    def _make_members(self, data, info):
1900        return [self._make_member(key, value['type'], value.get('if'), info)
1901                for (key, value) in data.items()]
1902
1903    def _def_struct_type(self, expr, info, doc):
1904        name = expr['struct']
1905        base = expr.get('base')
1906        data = expr['data']
1907        ifcond = expr.get('if')
1908        features = expr.get('features', [])
1909        self._def_entity(QAPISchemaObjectType(
1910            name, info, doc, ifcond, base,
1911            self._make_members(data, info),
1912            None,
1913            self._make_features(features, info)))
1914
1915    def _make_variant(self, case, typ, ifcond, info):
1916        return QAPISchemaObjectTypeVariant(case, info, typ, ifcond)
1917
1918    def _make_simple_variant(self, case, typ, ifcond, info):
1919        if isinstance(typ, list):
1920            assert len(typ) == 1
1921            typ = self._make_array_type(typ[0], info)
1922        typ = self._make_implicit_object_type(
1923            typ, info, None, self.lookup_type(typ),
1924            'wrapper', [self._make_member('data', typ, None, info)])
1925        return QAPISchemaObjectTypeVariant(case, info, typ, ifcond)
1926
1927    def _def_union_type(self, expr, info, doc):
1928        name = expr['union']
1929        data = expr['data']
1930        base = expr.get('base')
1931        ifcond = expr.get('if')
1932        tag_name = expr.get('discriminator')
1933        tag_member = None
1934        if isinstance(base, dict):
1935            base = self._make_implicit_object_type(
1936                name, info, doc, ifcond,
1937                'base', self._make_members(base, info))
1938        if tag_name:
1939            variants = [self._make_variant(key, value['type'],
1940                                           value.get('if'), info)
1941                        for (key, value) in data.items()]
1942            members = []
1943        else:
1944            variants = [self._make_simple_variant(key, value['type'],
1945                                                  value.get('if'), info)
1946                        for (key, value) in data.items()]
1947            enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
1948            typ = self._make_implicit_enum_type(name, info, ifcond, enum)
1949            tag_member = QAPISchemaObjectTypeMember('type', info, typ, False)
1950            members = [tag_member]
1951        self._def_entity(
1952            QAPISchemaObjectType(name, info, doc, ifcond, base, members,
1953                                 QAPISchemaObjectTypeVariants(
1954                                     tag_name, info, tag_member, variants),
1955                                 []))
1956
1957    def _def_alternate_type(self, expr, info, doc):
1958        name = expr['alternate']
1959        data = expr['data']
1960        ifcond = expr.get('if')
1961        variants = [self._make_variant(key, value['type'], value.get('if'),
1962                                       info)
1963                    for (key, value) in data.items()]
1964        tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
1965        self._def_entity(
1966            QAPISchemaAlternateType(name, info, doc, ifcond,
1967                                    QAPISchemaObjectTypeVariants(
1968                                        None, info, tag_member, variants)))
1969
1970    def _def_command(self, expr, info, doc):
1971        name = expr['command']
1972        data = expr.get('data')
1973        rets = expr.get('returns')
1974        gen = expr.get('gen', True)
1975        success_response = expr.get('success-response', True)
1976        boxed = expr.get('boxed', False)
1977        allow_oob = expr.get('allow-oob', False)
1978        allow_preconfig = expr.get('allow-preconfig', False)
1979        ifcond = expr.get('if')
1980        if isinstance(data, OrderedDict):
1981            data = self._make_implicit_object_type(
1982                name, info, doc, ifcond, 'arg', self._make_members(data, info))
1983        if isinstance(rets, list):
1984            assert len(rets) == 1
1985            rets = self._make_array_type(rets[0], info)
1986        self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
1987                                           gen, success_response,
1988                                           boxed, allow_oob, allow_preconfig))
1989
1990    def _def_event(self, expr, info, doc):
1991        name = expr['event']
1992        data = expr.get('data')
1993        boxed = expr.get('boxed', False)
1994        ifcond = expr.get('if')
1995        if isinstance(data, OrderedDict):
1996            data = self._make_implicit_object_type(
1997                name, info, doc, ifcond, 'arg', self._make_members(data, info))
1998        self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
1999
2000    def _def_exprs(self, exprs):
2001        for expr_elem in exprs:
2002            expr = expr_elem['expr']
2003            info = expr_elem['info']
2004            doc = expr_elem.get('doc')
2005            if 'enum' in expr:
2006                self._def_enum_type(expr, info, doc)
2007            elif 'struct' in expr:
2008                self._def_struct_type(expr, info, doc)
2009            elif 'union' in expr:
2010                self._def_union_type(expr, info, doc)
2011            elif 'alternate' in expr:
2012                self._def_alternate_type(expr, info, doc)
2013            elif 'command' in expr:
2014                self._def_command(expr, info, doc)
2015            elif 'event' in expr:
2016                self._def_event(expr, info, doc)
2017            elif 'include' in expr:
2018                self._def_include(expr, info, doc)
2019            else:
2020                assert False
2021
2022    def check(self):
2023        for ent in self._entity_list:
2024            ent.check(self)
2025
2026    def visit(self, visitor):
2027        visitor.visit_begin(self)
2028        module = None
2029        visitor.visit_module(module)
2030        for entity in self._entity_list:
2031            if visitor.visit_needed(entity):
2032                if entity.module != module:
2033                    module = entity.module
2034                    visitor.visit_module(module)
2035                entity.visit(visitor)
2036        visitor.visit_end()
2037
2038
2039#
2040# Code generation helpers
2041#
2042
2043def camel_case(name):
2044    new_name = ''
2045    first = True
2046    for ch in name:
2047        if ch in ['_', '-']:
2048            first = True
2049        elif first:
2050            new_name += ch.upper()
2051            first = False
2052        else:
2053            new_name += ch.lower()
2054    return new_name
2055
2056
2057# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
2058# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
2059# ENUM24_Name -> ENUM24_NAME
2060def camel_to_upper(value):
2061    c_fun_str = c_name(value, False)
2062    if value.isupper():
2063        return c_fun_str
2064
2065    new_name = ''
2066    length = len(c_fun_str)
2067    for i in range(length):
2068        c = c_fun_str[i]
2069        # When c is upper and no '_' appears before, do more checks
2070        if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
2071            if i < length - 1 and c_fun_str[i + 1].islower():
2072                new_name += '_'
2073            elif c_fun_str[i - 1].isdigit():
2074                new_name += '_'
2075        new_name += c
2076    return new_name.lstrip('_').upper()
2077
2078
2079def c_enum_const(type_name, const_name, prefix=None):
2080    if prefix is not None:
2081        type_name = prefix
2082    return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
2083
2084
2085if hasattr(str, 'maketrans'):
2086    c_name_trans = str.maketrans('.-', '__')
2087else:
2088    c_name_trans = string.maketrans('.-', '__')
2089
2090
2091# Map @name to a valid C identifier.
2092# If @protect, avoid returning certain ticklish identifiers (like
2093# C keywords) by prepending 'q_'.
2094#
2095# Used for converting 'name' from a 'name':'type' qapi definition
2096# into a generated struct member, as well as converting type names
2097# into substrings of a generated C function name.
2098# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
2099# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
2100def c_name(name, protect=True):
2101    # ANSI X3J11/88-090, 3.1.1
2102    c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
2103                     'default', 'do', 'double', 'else', 'enum', 'extern',
2104                     'float', 'for', 'goto', 'if', 'int', 'long', 'register',
2105                     'return', 'short', 'signed', 'sizeof', 'static',
2106                     'struct', 'switch', 'typedef', 'union', 'unsigned',
2107                     'void', 'volatile', 'while'])
2108    # ISO/IEC 9899:1999, 6.4.1
2109    c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
2110    # ISO/IEC 9899:2011, 6.4.1
2111    c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
2112                     '_Noreturn', '_Static_assert', '_Thread_local'])
2113    # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
2114    # excluding _.*
2115    gcc_words = set(['asm', 'typeof'])
2116    # C++ ISO/IEC 14882:2003 2.11
2117    cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
2118                     'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
2119                     'namespace', 'new', 'operator', 'private', 'protected',
2120                     'public', 'reinterpret_cast', 'static_cast', 'template',
2121                     'this', 'throw', 'true', 'try', 'typeid', 'typename',
2122                     'using', 'virtual', 'wchar_t',
2123                     # alternative representations
2124                     'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
2125                     'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
2126    # namespace pollution:
2127    polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
2128    name = name.translate(c_name_trans)
2129    if protect and (name in c89_words | c99_words | c11_words | gcc_words
2130                    | cpp_words | polluted_words):
2131        return 'q_' + name
2132    return name
2133
2134
2135eatspace = '\033EATSPACE.'
2136pointer_suffix = ' *' + eatspace
2137
2138
2139def genindent(count):
2140    ret = ''
2141    for _ in range(count):
2142        ret += ' '
2143    return ret
2144
2145
2146indent_level = 0
2147
2148
2149def push_indent(indent_amount=4):
2150    global indent_level
2151    indent_level += indent_amount
2152
2153
2154def pop_indent(indent_amount=4):
2155    global indent_level
2156    indent_level -= indent_amount
2157
2158
2159# Generate @code with @kwds interpolated.
2160# Obey indent_level, and strip eatspace.
2161def cgen(code, **kwds):
2162    raw = code % kwds
2163    if indent_level:
2164        indent = genindent(indent_level)
2165        # re.subn() lacks flags support before Python 2.7, use re.compile()
2166        raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
2167                      indent, raw)
2168        raw = raw[0]
2169    return re.sub(re.escape(eatspace) + r' *', '', raw)
2170
2171
2172def mcgen(code, **kwds):
2173    if code[0] == '\n':
2174        code = code[1:]
2175    return cgen(code, **kwds)
2176
2177
2178def c_fname(filename):
2179    return re.sub(r'[^A-Za-z0-9_]', '_', filename)
2180
2181
2182def guardstart(name):
2183    return mcgen('''
2184#ifndef %(name)s
2185#define %(name)s
2186
2187''',
2188                 name=c_fname(name).upper())
2189
2190
2191def guardend(name):
2192    return mcgen('''
2193
2194#endif /* %(name)s */
2195''',
2196                 name=c_fname(name).upper())
2197
2198
2199def gen_if(ifcond):
2200    ret = ''
2201    for ifc in ifcond:
2202        ret += mcgen('''
2203#if %(cond)s
2204''', cond=ifc)
2205    return ret
2206
2207
2208def gen_endif(ifcond):
2209    ret = ''
2210    for ifc in reversed(ifcond):
2211        ret += mcgen('''
2212#endif /* %(cond)s */
2213''', cond=ifc)
2214    return ret
2215
2216
2217def _wrap_ifcond(ifcond, before, after):
2218    if before == after:
2219        return after   # suppress empty #if ... #endif
2220
2221    assert after.startswith(before)
2222    out = before
2223    added = after[len(before):]
2224    if added[0] == '\n':
2225        out += '\n'
2226        added = added[1:]
2227    out += gen_if(ifcond)
2228    out += added
2229    out += gen_endif(ifcond)
2230    return out
2231
2232
2233def gen_enum_lookup(name, members, prefix=None):
2234    ret = mcgen('''
2235
2236const QEnumLookup %(c_name)s_lookup = {
2237    .array = (const char *const[]) {
2238''',
2239                c_name=c_name(name))
2240    for m in members:
2241        ret += gen_if(m.ifcond)
2242        index = c_enum_const(name, m.name, prefix)
2243        ret += mcgen('''
2244        [%(index)s] = "%(name)s",
2245''',
2246                     index=index, name=m.name)
2247        ret += gen_endif(m.ifcond)
2248
2249    ret += mcgen('''
2250    },
2251    .size = %(max_index)s
2252};
2253''',
2254                 max_index=c_enum_const(name, '_MAX', prefix))
2255    return ret
2256
2257
2258def gen_enum(name, members, prefix=None):
2259    # append automatically generated _MAX value
2260    enum_members = members + [QAPISchemaEnumMember('_MAX', None)]
2261
2262    ret = mcgen('''
2263
2264typedef enum %(c_name)s {
2265''',
2266                c_name=c_name(name))
2267
2268    for m in enum_members:
2269        ret += gen_if(m.ifcond)
2270        ret += mcgen('''
2271    %(c_enum)s,
2272''',
2273                     c_enum=c_enum_const(name, m.name, prefix))
2274        ret += gen_endif(m.ifcond)
2275
2276    ret += mcgen('''
2277} %(c_name)s;
2278''',
2279                 c_name=c_name(name))
2280
2281    ret += mcgen('''
2282
2283#define %(c_name)s_str(val) \\
2284    qapi_enum_lookup(&%(c_name)s_lookup, (val))
2285
2286extern const QEnumLookup %(c_name)s_lookup;
2287''',
2288                 c_name=c_name(name))
2289    return ret
2290
2291
2292def build_params(arg_type, boxed, extra=None):
2293    ret = ''
2294    sep = ''
2295    if boxed:
2296        assert arg_type
2297        ret += '%s arg' % arg_type.c_param_type()
2298        sep = ', '
2299    elif arg_type:
2300        assert not arg_type.variants
2301        for memb in arg_type.members:
2302            ret += sep
2303            sep = ', '
2304            if memb.optional:
2305                ret += 'bool has_%s, ' % c_name(memb.name)
2306            ret += '%s %s' % (memb.type.c_param_type(),
2307                              c_name(memb.name))
2308    if extra:
2309        ret += sep + extra
2310    return ret if ret else 'void'
2311
2312
2313#
2314# Accumulate and write output
2315#
2316
2317class QAPIGen(object):
2318
2319    def __init__(self, fname):
2320        self.fname = fname
2321        self._preamble = ''
2322        self._body = ''
2323
2324    def preamble_add(self, text):
2325        self._preamble += text
2326
2327    def add(self, text):
2328        self._body += text
2329
2330    def get_content(self):
2331        return self._top() + self._preamble + self._body + self._bottom()
2332
2333    def _top(self):
2334        return ''
2335
2336    def _bottom(self):
2337        return ''
2338
2339    def write(self, output_dir):
2340        pathname = os.path.join(output_dir, self.fname)
2341        dir = os.path.dirname(pathname)
2342        if dir:
2343            try:
2344                os.makedirs(dir)
2345            except os.error as e:
2346                if e.errno != errno.EEXIST:
2347                    raise
2348        fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
2349        if sys.version_info[0] >= 3:
2350            f = open(fd, 'r+', encoding='utf-8')
2351        else:
2352            f = os.fdopen(fd, 'r+')
2353        text = self.get_content()
2354        oldtext = f.read(len(text) + 1)
2355        if text != oldtext:
2356            f.seek(0)
2357            f.truncate(0)
2358            f.write(text)
2359        f.close()
2360
2361
2362@contextmanager
2363def ifcontext(ifcond, *args):
2364    """A 'with' statement context manager to wrap with start_if()/end_if()
2365
2366    *args: any number of QAPIGenCCode
2367
2368    Example::
2369
2370        with ifcontext(ifcond, self._genh, self._genc):
2371            modify self._genh and self._genc ...
2372
2373    Is equivalent to calling::
2374
2375        self._genh.start_if(ifcond)
2376        self._genc.start_if(ifcond)
2377        modify self._genh and self._genc ...
2378        self._genh.end_if()
2379        self._genc.end_if()
2380    """
2381    for arg in args:
2382        arg.start_if(ifcond)
2383    yield
2384    for arg in args:
2385        arg.end_if()
2386
2387
2388class QAPIGenCCode(QAPIGen):
2389
2390    def __init__(self, fname):
2391        QAPIGen.__init__(self, fname)
2392        self._start_if = None
2393
2394    def start_if(self, ifcond):
2395        assert self._start_if is None
2396        self._start_if = (ifcond, self._body, self._preamble)
2397
2398    def end_if(self):
2399        assert self._start_if
2400        self._wrap_ifcond()
2401        self._start_if = None
2402
2403    def _wrap_ifcond(self):
2404        self._body = _wrap_ifcond(self._start_if[0],
2405                                  self._start_if[1], self._body)
2406        self._preamble = _wrap_ifcond(self._start_if[0],
2407                                      self._start_if[2], self._preamble)
2408
2409    def get_content(self):
2410        assert self._start_if is None
2411        return QAPIGen.get_content(self)
2412
2413
2414class QAPIGenC(QAPIGenCCode):
2415
2416    def __init__(self, fname, blurb, pydoc):
2417        QAPIGenCCode.__init__(self, fname)
2418        self._blurb = blurb
2419        self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
2420                                                  re.MULTILINE))
2421
2422    def _top(self):
2423        return mcgen('''
2424/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2425
2426/*
2427%(blurb)s
2428 *
2429 * %(copyright)s
2430 *
2431 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2432 * See the COPYING.LIB file in the top-level directory.
2433 */
2434
2435''',
2436                     blurb=self._blurb, copyright=self._copyright)
2437
2438    def _bottom(self):
2439        return mcgen('''
2440
2441/* Dummy declaration to prevent empty .o file */
2442char qapi_dummy_%(name)s;
2443''',
2444                     name=c_fname(self.fname))
2445
2446
2447class QAPIGenH(QAPIGenC):
2448
2449    def _top(self):
2450        return QAPIGenC._top(self) + guardstart(self.fname)
2451
2452    def _bottom(self):
2453        return guardend(self.fname)
2454
2455
2456class QAPIGenDoc(QAPIGen):
2457
2458    def _top(self):
2459        return (QAPIGen._top(self)
2460                + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2461
2462
2463class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
2464
2465    def __init__(self, prefix, what, blurb, pydoc):
2466        self._prefix = prefix
2467        self._what = what
2468        self._genc = QAPIGenC(self._prefix + self._what + '.c',
2469                              blurb, pydoc)
2470        self._genh = QAPIGenH(self._prefix + self._what + '.h',
2471                              blurb, pydoc)
2472
2473    def write(self, output_dir):
2474        self._genc.write(output_dir)
2475        self._genh.write(output_dir)
2476
2477
2478class QAPISchemaModularCVisitor(QAPISchemaVisitor):
2479
2480    def __init__(self, prefix, what, blurb, pydoc):
2481        self._prefix = prefix
2482        self._what = what
2483        self._blurb = blurb
2484        self._pydoc = pydoc
2485        self._genc = None
2486        self._genh = None
2487        self._module = {}
2488        self._main_module = None
2489
2490    @staticmethod
2491    def _is_user_module(name):
2492        return name and not name.startswith('./')
2493
2494    @staticmethod
2495    def _is_builtin_module(name):
2496        return not name
2497
2498    def _module_dirname(self, what, name):
2499        if self._is_user_module(name):
2500            return os.path.dirname(name)
2501        return ''
2502
2503    def _module_basename(self, what, name):
2504        ret = '' if self._is_builtin_module(name) else self._prefix
2505        if self._is_user_module(name):
2506            basename = os.path.basename(name)
2507            ret += what
2508            if name != self._main_module:
2509                ret += '-' + os.path.splitext(basename)[0]
2510        else:
2511            name = name[2:] if name else 'builtin'
2512            ret += re.sub(r'-', '-' + name + '-', what)
2513        return ret
2514
2515    def _module_filename(self, what, name):
2516        return os.path.join(self._module_dirname(what, name),
2517                            self._module_basename(what, name))
2518
2519    def _add_module(self, name, blurb):
2520        basename = self._module_filename(self._what, name)
2521        genc = QAPIGenC(basename + '.c', blurb, self._pydoc)
2522        genh = QAPIGenH(basename + '.h', blurb, self._pydoc)
2523        self._module[name] = (genc, genh)
2524        self._set_module(name)
2525
2526    def _add_user_module(self, name, blurb):
2527        assert self._is_user_module(name)
2528        if self._main_module is None:
2529            self._main_module = name
2530        self._add_module(name, blurb)
2531
2532    def _add_system_module(self, name, blurb):
2533        self._add_module(name and './' + name, blurb)
2534
2535    def _set_module(self, name):
2536        self._genc, self._genh = self._module[name]
2537
2538    def write(self, output_dir, opt_builtins=False):
2539        for name in self._module:
2540            if self._is_builtin_module(name) and not opt_builtins:
2541                continue
2542            (genc, genh) = self._module[name]
2543            genc.write(output_dir)
2544            genh.write(output_dir)
2545
2546    def _begin_user_module(self, name):
2547        pass
2548
2549    def visit_module(self, name):
2550        if name in self._module:
2551            self._set_module(name)
2552        elif self._is_builtin_module(name):
2553            # The built-in module has not been created.  No code may
2554            # be generated.
2555            self._genc = None
2556            self._genh = None
2557        else:
2558            self._add_user_module(name, self._blurb)
2559            self._begin_user_module(name)
2560
2561    def visit_include(self, name, info):
2562        relname = os.path.relpath(self._module_filename(self._what, name),
2563                                  os.path.dirname(self._genh.fname))
2564        self._genh.preamble_add(mcgen('''
2565#include "%(relname)s.h"
2566''',
2567                                      relname=relname))
2568