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