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