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