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