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