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