xref: /openbmc/qemu/scripts/qapi/common.py (revision 97f0249474d19c1d60fb9d934c8bc08625a619ca)
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
15import errno
16import os
17import re
18import string
19try:
20    from collections import OrderedDict
21except:
22    from ordereddict 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# Parsing the schema into expressions
59#
60
61
62def error_path(parent):
63    res = ''
64    while parent:
65        res = ('In file included from %s:%d:\n' % (parent['file'],
66                                                   parent['line'])) + res
67        parent = parent['parent']
68    return res
69
70
71class QAPIError(Exception):
72    def __init__(self, fname, line, col, incl_info, msg):
73        Exception.__init__(self)
74        self.fname = fname
75        self.line = line
76        self.col = col
77        self.info = incl_info
78        self.msg = msg
79
80    def __str__(self):
81        loc = '%s:%d' % (self.fname, self.line)
82        if self.col is not None:
83            loc += ':%s' % self.col
84        return error_path(self.info) + '%s: %s' % (loc, self.msg)
85
86
87class QAPIParseError(QAPIError):
88    def __init__(self, parser, msg):
89        col = 1
90        for ch in parser.src[parser.line_pos:parser.pos]:
91            if ch == '\t':
92                col = (col + 7) % 8 + 1
93            else:
94                col += 1
95        QAPIError.__init__(self, parser.fname, parser.line, col,
96                           parser.incl_info, msg)
97
98
99class QAPISemError(QAPIError):
100    def __init__(self, info, msg):
101        QAPIError.__init__(self, info['file'], info['line'], None,
102                           info['parent'], msg)
103
104
105class QAPIDoc(object):
106    class Section(object):
107        def __init__(self, name=None):
108            # optional section name (argument/member or section name)
109            self.name = name
110            # the list of lines for this section
111            self.text = ''
112
113        def append(self, line):
114            self.text += line.rstrip() + '\n'
115
116    class ArgSection(Section):
117        def __init__(self, name):
118            QAPIDoc.Section.__init__(self, name)
119            self.member = None
120
121        def connect(self, member):
122            self.member = member
123
124    def __init__(self, parser, info):
125        # self._parser is used to report errors with QAPIParseError.  The
126        # resulting error position depends on the state of the parser.
127        # It happens to be the beginning of the comment.  More or less
128        # servicable, but action at a distance.
129        self._parser = parser
130        self.info = info
131        self.symbol = None
132        self.body = QAPIDoc.Section()
133        # dict mapping parameter name to ArgSection
134        self.args = OrderedDict()
135        # a list of Section
136        self.sections = []
137        # the current section
138        self._section = self.body
139
140    def has_section(self, name):
141        """Return True if we have a section with this name."""
142        for i in self.sections:
143            if i.name == name:
144                return True
145        return False
146
147    def append(self, line):
148        """Parse a comment line and add it to the documentation."""
149        line = line[1:]
150        if not line:
151            self._append_freeform(line)
152            return
153
154        if line[0] != ' ':
155            raise QAPIParseError(self._parser, "Missing space after #")
156        line = line[1:]
157
158        # FIXME not nice: things like '#  @foo:' and '# @foo: ' aren't
159        # recognized, and get silently treated as ordinary text
160        if self.symbol:
161            self._append_symbol_line(line)
162        elif not self.body.text and line.startswith('@'):
163            if not line.endswith(':'):
164                raise QAPIParseError(self._parser, "Line should end with :")
165            self.symbol = line[1:-1]
166            # FIXME invalid names other than the empty string aren't flagged
167            if not self.symbol:
168                raise QAPIParseError(self._parser, "Invalid name")
169        else:
170            self._append_freeform(line)
171
172    def end_comment(self):
173        self._end_section()
174
175    def _append_symbol_line(self, line):
176        name = line.split(' ', 1)[0]
177
178        if name.startswith('@') and name.endswith(':'):
179            line = line[len(name)+1:]
180            self._start_args_section(name[1:-1])
181        elif name in ('Returns:', 'Since:',
182                      # those are often singular or plural
183                      'Note:', 'Notes:',
184                      'Example:', 'Examples:',
185                      'TODO:'):
186            line = line[len(name)+1:]
187            self._start_section(name[:-1])
188
189        self._append_freeform(line)
190
191    def _start_args_section(self, name):
192        # FIXME invalid names other than the empty string aren't flagged
193        if not name:
194            raise QAPIParseError(self._parser, "Invalid parameter name")
195        if name in self.args:
196            raise QAPIParseError(self._parser,
197                                 "'%s' parameter name duplicated" % name)
198        if self.sections:
199            raise QAPIParseError(self._parser,
200                                 "'@%s:' can't follow '%s' section"
201                                 % (name, self.sections[0].name))
202        self._end_section()
203        self._section = QAPIDoc.ArgSection(name)
204        self.args[name] = self._section
205
206    def _start_section(self, name=None):
207        if name in ('Returns', 'Since') and self.has_section(name):
208            raise QAPIParseError(self._parser,
209                                 "Duplicated '%s' section" % name)
210        self._end_section()
211        self._section = QAPIDoc.Section(name)
212        self.sections.append(self._section)
213
214    def _end_section(self):
215        if self._section:
216            text = self._section.text = self._section.text.strip()
217            if self._section.name and (not text or text.isspace()):
218                raise QAPIParseError(self._parser, "Empty doc section '%s'"
219                                     % self._section.name)
220            self._section = None
221
222    def _append_freeform(self, line):
223        in_arg = isinstance(self._section, QAPIDoc.ArgSection)
224        if (in_arg and self._section.text.endswith('\n\n')
225                and line and not line[0].isspace()):
226            self._start_section()
227        if (in_arg or not self._section.name
228                or not self._section.name.startswith('Example')):
229            line = line.strip()
230        match = re.match(r'(@\S+:)', line)
231        if match:
232            raise QAPIParseError(self._parser,
233                                 "'%s' not allowed in free-form documentation"
234                                 % match.group(1))
235        self._section.append(line)
236
237    def connect_member(self, member):
238        if member.name not in self.args:
239            # Undocumented TODO outlaw
240            self.args[member.name] = QAPIDoc.ArgSection(member.name)
241        self.args[member.name].connect(member)
242
243    def check_expr(self, expr):
244        if self.has_section('Returns') and 'command' not in expr:
245            raise QAPISemError(self.info,
246                               "'Returns:' is only valid for commands")
247
248    def check(self):
249        bogus = [name for name, section in self.args.items()
250                 if not section.member]
251        if bogus:
252            raise QAPISemError(
253                self.info,
254                "The following documented members are not in "
255                "the declaration: %s" % ", ".join(bogus))
256
257
258class QAPISchemaParser(object):
259
260    def __init__(self, fp, previously_included=[], incl_info=None):
261        self.fname = fp.name
262        previously_included.append(os.path.abspath(fp.name))
263        self.incl_info = incl_info
264        self.src = fp.read()
265        if self.src == '' or self.src[-1] != '\n':
266            self.src += '\n'
267        self.cursor = 0
268        self.line = 1
269        self.line_pos = 0
270        self.exprs = []
271        self.docs = []
272        self.accept()
273        cur_doc = None
274
275        while self.tok is not None:
276            info = {'file': self.fname, 'line': self.line,
277                    'parent': self.incl_info}
278            if self.tok == '#':
279                self.reject_expr_doc(cur_doc)
280                cur_doc = self.get_doc(info)
281                self.docs.append(cur_doc)
282                continue
283
284            expr = self.get_expr(False)
285            if 'include' in expr:
286                self.reject_expr_doc(cur_doc)
287                if len(expr) != 1:
288                    raise QAPISemError(info, "Invalid 'include' directive")
289                include = expr['include']
290                if not isinstance(include, str):
291                    raise QAPISemError(info,
292                                       "Value of 'include' must be a string")
293                incl_fname = os.path.join(os.path.dirname(self.fname),
294                                          include)
295                self.exprs.append({'expr': {'include': incl_fname},
296                                   'info': info})
297                exprs_include = self._include(include, info, incl_fname,
298                                              previously_included)
299                if exprs_include:
300                    self.exprs.extend(exprs_include.exprs)
301                    self.docs.extend(exprs_include.docs)
302            elif "pragma" in expr:
303                self.reject_expr_doc(cur_doc)
304                if len(expr) != 1:
305                    raise QAPISemError(info, "Invalid 'pragma' directive")
306                pragma = expr['pragma']
307                if not isinstance(pragma, dict):
308                    raise QAPISemError(
309                        info, "Value of 'pragma' must be a dictionary")
310                for name, value in pragma.items():
311                    self._pragma(name, value, info)
312            else:
313                expr_elem = {'expr': expr,
314                             'info': info}
315                if cur_doc:
316                    if not cur_doc.symbol:
317                        raise QAPISemError(
318                            cur_doc.info, "Expression documentation required")
319                    expr_elem['doc'] = cur_doc
320                self.exprs.append(expr_elem)
321            cur_doc = None
322        self.reject_expr_doc(cur_doc)
323
324    @staticmethod
325    def reject_expr_doc(doc):
326        if doc and doc.symbol:
327            raise QAPISemError(
328                doc.info,
329                "Documentation for '%s' is not followed by the definition"
330                % doc.symbol)
331
332    def _include(self, include, info, incl_fname, previously_included):
333        incl_abs_fname = os.path.abspath(incl_fname)
334        # catch inclusion cycle
335        inf = info
336        while inf:
337            if incl_abs_fname == os.path.abspath(inf['file']):
338                raise QAPISemError(info, "Inclusion loop for %s" % include)
339            inf = inf['parent']
340
341        # skip multiple include of the same file
342        if incl_abs_fname in previously_included:
343            return None
344
345        try:
346            fobj = open(incl_fname, 'r')
347        except IOError as e:
348            raise QAPISemError(info, '%s: %s' % (e.strerror, incl_fname))
349        return QAPISchemaParser(fobj, previously_included, info)
350
351    def _pragma(self, name, value, info):
352        global doc_required, returns_whitelist, name_case_whitelist
353        if name == 'doc-required':
354            if not isinstance(value, bool):
355                raise QAPISemError(info,
356                                   "Pragma 'doc-required' must be boolean")
357            doc_required = value
358        elif name == 'returns-whitelist':
359            if (not isinstance(value, list)
360                    or any([not isinstance(elt, str) for elt in value])):
361                raise QAPISemError(info,
362                                   "Pragma returns-whitelist must be"
363                                   " a list of strings")
364            returns_whitelist = value
365        elif name == 'name-case-whitelist':
366            if (not isinstance(value, list)
367                    or any([not isinstance(elt, str) for elt in value])):
368                raise QAPISemError(info,
369                                   "Pragma name-case-whitelist must be"
370                                   " a list of strings")
371            name_case_whitelist = value
372        else:
373            raise QAPISemError(info, "Unknown pragma '%s'" % name)
374
375    def accept(self, skip_comment=True):
376        while True:
377            self.tok = self.src[self.cursor]
378            self.pos = self.cursor
379            self.cursor += 1
380            self.val = None
381
382            if self.tok == '#':
383                if self.src[self.cursor] == '#':
384                    # Start of doc comment
385                    skip_comment = False
386                self.cursor = self.src.find('\n', self.cursor)
387                if not skip_comment:
388                    self.val = self.src[self.pos:self.cursor]
389                    return
390            elif self.tok in '{}:,[]':
391                return
392            elif self.tok == "'":
393                string = ''
394                esc = False
395                while True:
396                    ch = self.src[self.cursor]
397                    self.cursor += 1
398                    if ch == '\n':
399                        raise QAPIParseError(self, 'Missing terminating "\'"')
400                    if esc:
401                        if ch == 'b':
402                            string += '\b'
403                        elif ch == 'f':
404                            string += '\f'
405                        elif ch == 'n':
406                            string += '\n'
407                        elif ch == 'r':
408                            string += '\r'
409                        elif ch == 't':
410                            string += '\t'
411                        elif ch == 'u':
412                            value = 0
413                            for _ in range(0, 4):
414                                ch = self.src[self.cursor]
415                                self.cursor += 1
416                                if ch not in '0123456789abcdefABCDEF':
417                                    raise QAPIParseError(self,
418                                                         '\\u escape needs 4 '
419                                                         'hex digits')
420                                value = (value << 4) + int(ch, 16)
421                            # If Python 2 and 3 didn't disagree so much on
422                            # how to handle Unicode, then we could allow
423                            # Unicode string defaults.  But most of QAPI is
424                            # ASCII-only, so we aren't losing much for now.
425                            if not value or value > 0x7f:
426                                raise QAPIParseError(self,
427                                                     'For now, \\u escape '
428                                                     'only supports non-zero '
429                                                     'values up to \\u007f')
430                            string += chr(value)
431                        elif ch in '\\/\'"':
432                            string += ch
433                        else:
434                            raise QAPIParseError(self,
435                                                 "Unknown escape \\%s" % ch)
436                        esc = False
437                    elif ch == '\\':
438                        esc = True
439                    elif ch == "'":
440                        self.val = string
441                        return
442                    else:
443                        string += ch
444            elif self.src.startswith('true', self.pos):
445                self.val = True
446                self.cursor += 3
447                return
448            elif self.src.startswith('false', self.pos):
449                self.val = False
450                self.cursor += 4
451                return
452            elif self.src.startswith('null', self.pos):
453                self.val = None
454                self.cursor += 3
455                return
456            elif self.tok == '\n':
457                if self.cursor == len(self.src):
458                    self.tok = None
459                    return
460                self.line += 1
461                self.line_pos = self.cursor
462            elif not self.tok.isspace():
463                raise QAPIParseError(self, 'Stray "%s"' % self.tok)
464
465    def get_members(self):
466        expr = OrderedDict()
467        if self.tok == '}':
468            self.accept()
469            return expr
470        if self.tok != "'":
471            raise QAPIParseError(self, 'Expected string or "}"')
472        while True:
473            key = self.val
474            self.accept()
475            if self.tok != ':':
476                raise QAPIParseError(self, 'Expected ":"')
477            self.accept()
478            if key in expr:
479                raise QAPIParseError(self, 'Duplicate key "%s"' % key)
480            expr[key] = self.get_expr(True)
481            if self.tok == '}':
482                self.accept()
483                return expr
484            if self.tok != ',':
485                raise QAPIParseError(self, 'Expected "," or "}"')
486            self.accept()
487            if self.tok != "'":
488                raise QAPIParseError(self, 'Expected string')
489
490    def get_values(self):
491        expr = []
492        if self.tok == ']':
493            self.accept()
494            return expr
495        if self.tok not in "{['tfn":
496            raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
497                                 'boolean or "null"')
498        while True:
499            expr.append(self.get_expr(True))
500            if self.tok == ']':
501                self.accept()
502                return expr
503            if self.tok != ',':
504                raise QAPIParseError(self, 'Expected "," or "]"')
505            self.accept()
506
507    def get_expr(self, nested):
508        if self.tok != '{' and not nested:
509            raise QAPIParseError(self, 'Expected "{"')
510        if self.tok == '{':
511            self.accept()
512            expr = self.get_members()
513        elif self.tok == '[':
514            self.accept()
515            expr = self.get_values()
516        elif self.tok in "'tfn":
517            expr = self.val
518            self.accept()
519        else:
520            raise QAPIParseError(self, 'Expected "{", "[", string, '
521                                 'boolean or "null"')
522        return expr
523
524    def get_doc(self, info):
525        if self.val != '##':
526            raise QAPIParseError(self, "Junk after '##' at start of "
527                                 "documentation comment")
528
529        doc = QAPIDoc(self, info)
530        self.accept(False)
531        while self.tok == '#':
532            if self.val.startswith('##'):
533                # End of doc comment
534                if self.val != '##':
535                    raise QAPIParseError(self, "Junk after '##' at end of "
536                                         "documentation comment")
537                doc.end_comment()
538                self.accept()
539                return doc
540            else:
541                doc.append(self.val)
542            self.accept(False)
543
544        raise QAPIParseError(self, "Documentation comment must end with '##'")
545
546
547#
548# Semantic analysis of schema expressions
549# TODO fold into QAPISchema
550# TODO catching name collisions in generated code would be nice
551#
552
553
554def find_base_members(base):
555    if isinstance(base, dict):
556        return base
557    base_struct_define = struct_types.get(base)
558    if not base_struct_define:
559        return None
560    return base_struct_define['data']
561
562
563# Return the qtype of an alternate branch, or None on error.
564def find_alternate_member_qtype(qapi_type):
565    if qapi_type in builtin_types:
566        return builtin_types[qapi_type]
567    elif qapi_type in struct_types:
568        return 'QTYPE_QDICT'
569    elif qapi_type in enum_types:
570        return 'QTYPE_QSTRING'
571    elif qapi_type in union_types:
572        return 'QTYPE_QDICT'
573    return None
574
575
576# Return the discriminator enum define if discriminator is specified as an
577# enum type, otherwise return None.
578def discriminator_find_enum_define(expr):
579    base = expr.get('base')
580    discriminator = expr.get('discriminator')
581
582    if not (discriminator and base):
583        return None
584
585    base_members = find_base_members(base)
586    if not base_members:
587        return None
588
589    discriminator_type = base_members.get(discriminator)
590    if not discriminator_type:
591        return None
592
593    return enum_types.get(discriminator_type)
594
595
596# Names must be letters, numbers, -, and _.  They must start with letter,
597# except for downstream extensions which must start with __RFQDN_.
598# Dots are only valid in the downstream extension prefix.
599valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
600                        '[a-zA-Z][a-zA-Z0-9_-]*$')
601
602
603def check_name(info, source, name, allow_optional=False,
604               enum_member=False):
605    global valid_name
606    membername = name
607
608    if not isinstance(name, str):
609        raise QAPISemError(info, "%s requires a string name" % source)
610    if name.startswith('*'):
611        membername = name[1:]
612        if not allow_optional:
613            raise QAPISemError(info, "%s does not allow optional name '%s'"
614                               % (source, name))
615    # Enum members can start with a digit, because the generated C
616    # code always prefixes it with the enum name
617    if enum_member and membername[0].isdigit():
618        membername = 'D' + membername
619    # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
620    # and 'q_obj_*' implicit type names.
621    if not valid_name.match(membername) or \
622       c_name(membername, False).startswith('q_'):
623        raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
624
625
626def add_name(name, info, meta, implicit=False):
627    global all_names
628    check_name(info, "'%s'" % meta, name)
629    # FIXME should reject names that differ only in '_' vs. '.'
630    # vs. '-', because they're liable to clash in generated C.
631    if name in all_names:
632        raise QAPISemError(info, "%s '%s' is already defined"
633                           % (all_names[name], name))
634    if not implicit and (name.endswith('Kind') or name.endswith('List')):
635        raise QAPISemError(info, "%s '%s' should not end in '%s'"
636                           % (meta, name, name[-4:]))
637    all_names[name] = meta
638
639
640def check_type(info, source, value, allow_array=False,
641               allow_dict=False, allow_optional=False,
642               allow_metas=[]):
643    global all_names
644
645    if value is None:
646        return
647
648    # Check if array type for value is okay
649    if isinstance(value, list):
650        if not allow_array:
651            raise QAPISemError(info, "%s cannot be an array" % source)
652        if len(value) != 1 or not isinstance(value[0], str):
653            raise QAPISemError(info,
654                               "%s: array type must contain single type name" %
655                               source)
656        value = value[0]
657
658    # Check if type name for value is okay
659    if isinstance(value, str):
660        if value not in all_names:
661            raise QAPISemError(info, "%s uses unknown type '%s'"
662                               % (source, value))
663        if not all_names[value] in allow_metas:
664            raise QAPISemError(info, "%s cannot use %s type '%s'" %
665                               (source, all_names[value], value))
666        return
667
668    if not allow_dict:
669        raise QAPISemError(info, "%s should be a type name" % source)
670
671    if not isinstance(value, OrderedDict):
672        raise QAPISemError(info,
673                           "%s should be a dictionary or type name" % source)
674
675    # value is a dictionary, check that each member is okay
676    for (key, arg) in value.items():
677        check_name(info, "Member of %s" % source, key,
678                   allow_optional=allow_optional)
679        if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
680            raise QAPISemError(info, "Member of %s uses reserved name '%s'"
681                               % (source, key))
682        # Todo: allow dictionaries to represent default values of
683        # an optional argument.
684        check_type(info, "Member '%s' of %s" % (key, source), arg,
685                   allow_array=True,
686                   allow_metas=['built-in', 'union', 'alternate', 'struct',
687                                'enum'])
688
689
690def check_command(expr, info):
691    name = expr['command']
692    boxed = expr.get('boxed', False)
693
694    args_meta = ['struct']
695    if boxed:
696        args_meta += ['union', 'alternate']
697    check_type(info, "'data' for command '%s'" % name,
698               expr.get('data'), allow_dict=not boxed, allow_optional=True,
699               allow_metas=args_meta)
700    returns_meta = ['union', 'struct']
701    if name in returns_whitelist:
702        returns_meta += ['built-in', 'alternate', 'enum']
703    check_type(info, "'returns' for command '%s'" % name,
704               expr.get('returns'), allow_array=True,
705               allow_optional=True, allow_metas=returns_meta)
706
707
708def check_event(expr, info):
709    name = expr['event']
710    boxed = expr.get('boxed', False)
711
712    meta = ['struct']
713    if boxed:
714        meta += ['union', 'alternate']
715    check_type(info, "'data' for event '%s'" % name,
716               expr.get('data'), allow_dict=not boxed, allow_optional=True,
717               allow_metas=meta)
718
719
720def check_union(expr, info):
721    name = expr['union']
722    base = expr.get('base')
723    discriminator = expr.get('discriminator')
724    members = expr['data']
725
726    # Two types of unions, determined by discriminator.
727
728    # With no discriminator it is a simple union.
729    if discriminator is None:
730        enum_define = None
731        allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
732        if base is not None:
733            raise QAPISemError(info, "Simple union '%s' must not have a base" %
734                               name)
735
736    # Else, it's a flat union.
737    else:
738        # The object must have a string or dictionary 'base'.
739        check_type(info, "'base' for union '%s'" % name,
740                   base, allow_dict=True, allow_optional=True,
741                   allow_metas=['struct'])
742        if not base:
743            raise QAPISemError(info, "Flat union '%s' must have a base"
744                               % name)
745        base_members = find_base_members(base)
746        assert base_members is not None
747
748        # The value of member 'discriminator' must name a non-optional
749        # member of the base struct.
750        check_name(info, "Discriminator of flat union '%s'" % name,
751                   discriminator)
752        discriminator_type = base_members.get(discriminator)
753        if not discriminator_type:
754            raise QAPISemError(info,
755                               "Discriminator '%s' is not a member of base "
756                               "struct '%s'"
757                               % (discriminator, base))
758        enum_define = enum_types.get(discriminator_type)
759        allow_metas = ['struct']
760        # Do not allow string discriminator
761        if not enum_define:
762            raise QAPISemError(info,
763                               "Discriminator '%s' must be of enumeration "
764                               "type" % discriminator)
765
766    # Check every branch; don't allow an empty union
767    if len(members) == 0:
768        raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
769    for (key, value) in members.items():
770        check_name(info, "Member of union '%s'" % name, key)
771
772        # Each value must name a known type
773        check_type(info, "Member '%s' of union '%s'" % (key, name),
774                   value, allow_array=not base, allow_metas=allow_metas)
775
776        # If the discriminator names an enum type, then all members
777        # of 'data' must also be members of the enum type.
778        if enum_define:
779            if key not in enum_define['data']:
780                raise QAPISemError(info,
781                                   "Discriminator value '%s' is not found in "
782                                   "enum '%s'"
783                                   % (key, enum_define['enum']))
784
785    # If discriminator is user-defined, ensure all values are covered
786    if enum_define:
787        for value in enum_define['data']:
788            if value not in members.keys():
789                raise QAPISemError(info, "Union '%s' data missing '%s' branch"
790                                   % (name, value))
791
792
793def check_alternate(expr, info):
794    name = expr['alternate']
795    members = expr['data']
796    types_seen = {}
797
798    # Check every branch; require at least two branches
799    if len(members) < 2:
800        raise QAPISemError(info,
801                           "Alternate '%s' should have at least two branches "
802                           "in 'data'" % name)
803    for (key, value) in members.items():
804        check_name(info, "Member of alternate '%s'" % name, key)
805
806        # Ensure alternates have no type conflicts.
807        check_type(info, "Member '%s' of alternate '%s'" % (key, name),
808                   value,
809                   allow_metas=['built-in', 'union', 'struct', 'enum'])
810        qtype = find_alternate_member_qtype(value)
811        if not qtype:
812            raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
813                               "type '%s'" % (name, key, value))
814        conflicting = set([qtype])
815        if qtype == 'QTYPE_QSTRING':
816            enum_expr = enum_types.get(value)
817            if enum_expr:
818                for v in enum_expr['data']:
819                    if v in ['on', 'off']:
820                        conflicting.add('QTYPE_QBOOL')
821                    if re.match(r'[-+0-9.]', v): # lazy, could be tightened
822                        conflicting.add('QTYPE_QNUM')
823            else:
824                conflicting.add('QTYPE_QNUM')
825                conflicting.add('QTYPE_QBOOL')
826        for qt in conflicting:
827            if qt in types_seen:
828                raise QAPISemError(info, "Alternate '%s' member '%s' can't "
829                                   "be distinguished from member '%s'"
830                                   % (name, key, types_seen[qt]))
831            types_seen[qt] = key
832
833
834def check_enum(expr, info):
835    name = expr['enum']
836    members = expr.get('data')
837    prefix = expr.get('prefix')
838
839    if not isinstance(members, list):
840        raise QAPISemError(info,
841                           "Enum '%s' requires an array for 'data'" % name)
842    if prefix is not None and not isinstance(prefix, str):
843        raise QAPISemError(info,
844                           "Enum '%s' requires a string for 'prefix'" % name)
845    for member in members:
846        check_name(info, "Member of enum '%s'" % name, member,
847                   enum_member=True)
848
849
850def check_struct(expr, info):
851    name = expr['struct']
852    members = expr['data']
853
854    check_type(info, "'data' for struct '%s'" % name, members,
855               allow_dict=True, allow_optional=True)
856    check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
857               allow_metas=['struct'])
858
859
860def check_keys(expr_elem, meta, required, optional=[]):
861    expr = expr_elem['expr']
862    info = expr_elem['info']
863    name = expr[meta]
864    if not isinstance(name, str):
865        raise QAPISemError(info, "'%s' key must have a string value" % meta)
866    required = required + [meta]
867    for (key, value) in expr.items():
868        if key not in required and key not in optional:
869            raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
870                               % (key, meta, name))
871        if (key == 'gen' or key == 'success-response') and value is not False:
872            raise QAPISemError(info,
873                               "'%s' of %s '%s' should only use false value"
874                               % (key, meta, name))
875        if key == 'boxed' and value is not True:
876            raise QAPISemError(info,
877                               "'%s' of %s '%s' should only use true value"
878                               % (key, meta, name))
879    for key in required:
880        if key not in expr:
881            raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
882                               % (key, meta, name))
883
884
885def check_exprs(exprs):
886    global all_names
887
888    # Populate name table with names of built-in types
889    for builtin in builtin_types.keys():
890        all_names[builtin] = 'built-in'
891
892    # Learn the types and check for valid expression keys
893    for expr_elem in exprs:
894        expr = expr_elem['expr']
895        info = expr_elem['info']
896        doc = expr_elem.get('doc')
897
898        if 'include' in expr:
899            continue
900
901        if not doc and doc_required:
902            raise QAPISemError(info,
903                               "Expression missing documentation comment")
904
905        if 'enum' in expr:
906            meta = 'enum'
907            check_keys(expr_elem, 'enum', ['data'], ['prefix'])
908            enum_types[expr[meta]] = expr
909        elif 'union' in expr:
910            meta = 'union'
911            check_keys(expr_elem, 'union', ['data'],
912                       ['base', 'discriminator'])
913            union_types[expr[meta]] = expr
914        elif 'alternate' in expr:
915            meta = 'alternate'
916            check_keys(expr_elem, 'alternate', ['data'])
917        elif 'struct' in expr:
918            meta = 'struct'
919            check_keys(expr_elem, 'struct', ['data'], ['base'])
920            struct_types[expr[meta]] = expr
921        elif 'command' in expr:
922            meta = 'command'
923            check_keys(expr_elem, 'command', [],
924                       ['data', 'returns', 'gen', 'success-response', 'boxed'])
925        elif 'event' in expr:
926            meta = 'event'
927            check_keys(expr_elem, 'event', [], ['data', 'boxed'])
928        else:
929            raise QAPISemError(expr_elem['info'],
930                               "Expression is missing metatype")
931        name = expr[meta]
932        add_name(name, info, meta)
933        if doc and doc.symbol != name:
934            raise QAPISemError(info, "Definition of '%s' follows documentation"
935                               " for '%s'" % (name, doc.symbol))
936
937    # Try again for hidden UnionKind enum
938    for expr_elem in exprs:
939        expr = expr_elem['expr']
940
941        if 'include' in expr:
942            continue
943        if 'union' in expr and not discriminator_find_enum_define(expr):
944            name = '%sKind' % expr['union']
945        elif 'alternate' in expr:
946            name = '%sKind' % expr['alternate']
947        else:
948            continue
949        enum_types[name] = {'enum': name}
950        add_name(name, info, 'enum', implicit=True)
951
952    # Validate that exprs make sense
953    for expr_elem in exprs:
954        expr = expr_elem['expr']
955        info = expr_elem['info']
956        doc = expr_elem.get('doc')
957
958        if 'include' in expr:
959            continue
960        if 'enum' in expr:
961            check_enum(expr, info)
962        elif 'union' in expr:
963            check_union(expr, info)
964        elif 'alternate' in expr:
965            check_alternate(expr, info)
966        elif 'struct' in expr:
967            check_struct(expr, info)
968        elif 'command' in expr:
969            check_command(expr, info)
970        elif 'event' in expr:
971            check_event(expr, info)
972        else:
973            assert False, 'unexpected meta type'
974
975        if doc:
976            doc.check_expr(expr)
977
978    return exprs
979
980
981#
982# Schema compiler frontend
983#
984
985class QAPISchemaEntity(object):
986    def __init__(self, name, info, doc):
987        assert isinstance(name, str)
988        self.name = name
989        # For explicitly defined entities, info points to the (explicit)
990        # definition.  For builtins (and their arrays), info is None.
991        # For implicitly defined entities, info points to a place that
992        # triggered the implicit definition (there may be more than one
993        # such place).
994        self.info = info
995        self.doc = doc
996
997    def c_name(self):
998        return c_name(self.name)
999
1000    def check(self, schema):
1001        pass
1002
1003    def is_implicit(self):
1004        return not self.info
1005
1006    def visit(self, visitor):
1007        pass
1008
1009
1010class QAPISchemaVisitor(object):
1011    def visit_begin(self, schema):
1012        pass
1013
1014    def visit_end(self):
1015        pass
1016
1017    def visit_needed(self, entity):
1018        # Default to visiting everything
1019        return True
1020
1021    def visit_builtin_type(self, name, info, json_type):
1022        pass
1023
1024    def visit_enum_type(self, name, info, values, prefix):
1025        pass
1026
1027    def visit_array_type(self, name, info, element_type):
1028        pass
1029
1030    def visit_object_type(self, name, info, base, members, variants):
1031        pass
1032
1033    def visit_object_type_flat(self, name, info, members, variants):
1034        pass
1035
1036    def visit_alternate_type(self, name, info, variants):
1037        pass
1038
1039    def visit_command(self, name, info, arg_type, ret_type,
1040                      gen, success_response, boxed):
1041        pass
1042
1043    def visit_event(self, name, info, arg_type, boxed):
1044        pass
1045
1046
1047class QAPISchemaType(QAPISchemaEntity):
1048    # Return the C type for common use.
1049    # For the types we commonly box, this is a pointer type.
1050    def c_type(self):
1051        pass
1052
1053    # Return the C type to be used in a parameter list.
1054    def c_param_type(self):
1055        return self.c_type()
1056
1057    # Return the C type to be used where we suppress boxing.
1058    def c_unboxed_type(self):
1059        return self.c_type()
1060
1061    def json_type(self):
1062        pass
1063
1064    def alternate_qtype(self):
1065        json2qtype = {
1066            'null':    'QTYPE_QNULL',
1067            'string':  'QTYPE_QSTRING',
1068            'number':  'QTYPE_QNUM',
1069            'int':     'QTYPE_QNUM',
1070            'boolean': 'QTYPE_QBOOL',
1071            'object':  'QTYPE_QDICT'
1072        }
1073        return json2qtype.get(self.json_type())
1074
1075    def doc_type(self):
1076        if self.is_implicit():
1077            return None
1078        return self.name
1079
1080
1081class QAPISchemaBuiltinType(QAPISchemaType):
1082    def __init__(self, name, json_type, c_type):
1083        QAPISchemaType.__init__(self, name, None, None)
1084        assert not c_type or isinstance(c_type, str)
1085        assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1086                             'value')
1087        self._json_type_name = json_type
1088        self._c_type_name = c_type
1089
1090    def c_name(self):
1091        return self.name
1092
1093    def c_type(self):
1094        return self._c_type_name
1095
1096    def c_param_type(self):
1097        if self.name == 'str':
1098            return 'const ' + self._c_type_name
1099        return self._c_type_name
1100
1101    def json_type(self):
1102        return self._json_type_name
1103
1104    def doc_type(self):
1105        return self.json_type()
1106
1107    def visit(self, visitor):
1108        visitor.visit_builtin_type(self.name, self.info, self.json_type())
1109
1110
1111class QAPISchemaEnumType(QAPISchemaType):
1112    def __init__(self, name, info, doc, values, prefix):
1113        QAPISchemaType.__init__(self, name, info, doc)
1114        for v in values:
1115            assert isinstance(v, QAPISchemaMember)
1116            v.set_owner(name)
1117        assert prefix is None or isinstance(prefix, str)
1118        self.values = values
1119        self.prefix = prefix
1120
1121    def check(self, schema):
1122        seen = {}
1123        for v in self.values:
1124            v.check_clash(self.info, seen)
1125            if self.doc:
1126                self.doc.connect_member(v)
1127
1128    def is_implicit(self):
1129        # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1130        return self.name.endswith('Kind') or self.name == 'QType'
1131
1132    def c_type(self):
1133        return c_name(self.name)
1134
1135    def member_names(self):
1136        return [v.name for v in self.values]
1137
1138    def json_type(self):
1139        return 'string'
1140
1141    def visit(self, visitor):
1142        visitor.visit_enum_type(self.name, self.info,
1143                                self.member_names(), self.prefix)
1144
1145
1146class QAPISchemaArrayType(QAPISchemaType):
1147    def __init__(self, name, info, element_type):
1148        QAPISchemaType.__init__(self, name, info, None)
1149        assert isinstance(element_type, str)
1150        self._element_type_name = element_type
1151        self.element_type = None
1152
1153    def check(self, schema):
1154        self.element_type = schema.lookup_type(self._element_type_name)
1155        assert self.element_type
1156
1157    def is_implicit(self):
1158        return True
1159
1160    def c_type(self):
1161        return c_name(self.name) + pointer_suffix
1162
1163    def json_type(self):
1164        return 'array'
1165
1166    def doc_type(self):
1167        elt_doc_type = self.element_type.doc_type()
1168        if not elt_doc_type:
1169            return None
1170        return 'array of ' + elt_doc_type
1171
1172    def visit(self, visitor):
1173        visitor.visit_array_type(self.name, self.info, self.element_type)
1174
1175
1176class QAPISchemaObjectType(QAPISchemaType):
1177    def __init__(self, name, info, doc, base, local_members, variants):
1178        # struct has local_members, optional base, and no variants
1179        # flat union has base, variants, and no local_members
1180        # simple union has local_members, variants, and no base
1181        QAPISchemaType.__init__(self, name, info, doc)
1182        assert base is None or isinstance(base, str)
1183        for m in local_members:
1184            assert isinstance(m, QAPISchemaObjectTypeMember)
1185            m.set_owner(name)
1186        if variants is not None:
1187            assert isinstance(variants, QAPISchemaObjectTypeVariants)
1188            variants.set_owner(name)
1189        self._base_name = base
1190        self.base = None
1191        self.local_members = local_members
1192        self.variants = variants
1193        self.members = None
1194
1195    def check(self, schema):
1196        if self.members is False:               # check for cycles
1197            raise QAPISemError(self.info,
1198                               "Object %s contains itself" % self.name)
1199        if self.members:
1200            return
1201        self.members = False                    # mark as being checked
1202        seen = OrderedDict()
1203        if self._base_name:
1204            self.base = schema.lookup_type(self._base_name)
1205            assert isinstance(self.base, QAPISchemaObjectType)
1206            self.base.check(schema)
1207            self.base.check_clash(self.info, seen)
1208        for m in self.local_members:
1209            m.check(schema)
1210            m.check_clash(self.info, seen)
1211            if self.doc:
1212                self.doc.connect_member(m)
1213        self.members = seen.values()
1214        if self.variants:
1215            self.variants.check(schema, seen)
1216            assert self.variants.tag_member in self.members
1217            self.variants.check_clash(self.info, seen)
1218        if self.doc:
1219            self.doc.check()
1220
1221    # Check that the members of this type do not cause duplicate JSON members,
1222    # and update seen to track the members seen so far. Report any errors
1223    # on behalf of info, which is not necessarily self.info
1224    def check_clash(self, info, seen):
1225        assert not self.variants       # not implemented
1226        for m in self.members:
1227            m.check_clash(info, seen)
1228
1229    def is_implicit(self):
1230        # See QAPISchema._make_implicit_object_type(), as well as
1231        # _def_predefineds()
1232        return self.name.startswith('q_')
1233
1234    def is_empty(self):
1235        assert self.members is not None
1236        return not self.members and not self.variants
1237
1238    def c_name(self):
1239        assert self.name != 'q_empty'
1240        return QAPISchemaType.c_name(self)
1241
1242    def c_type(self):
1243        assert not self.is_implicit()
1244        return c_name(self.name) + pointer_suffix
1245
1246    def c_unboxed_type(self):
1247        return c_name(self.name)
1248
1249    def json_type(self):
1250        return 'object'
1251
1252    def visit(self, visitor):
1253        visitor.visit_object_type(self.name, self.info,
1254                                  self.base, self.local_members, self.variants)
1255        visitor.visit_object_type_flat(self.name, self.info,
1256                                       self.members, self.variants)
1257
1258
1259class QAPISchemaMember(object):
1260    role = 'member'
1261
1262    def __init__(self, name):
1263        assert isinstance(name, str)
1264        self.name = name
1265        self.owner = None
1266
1267    def set_owner(self, name):
1268        assert not self.owner
1269        self.owner = name
1270
1271    def check_clash(self, info, seen):
1272        cname = c_name(self.name)
1273        if cname.lower() != cname and self.owner not in name_case_whitelist:
1274            raise QAPISemError(info,
1275                               "%s should not use uppercase" % self.describe())
1276        if cname in seen:
1277            raise QAPISemError(info, "%s collides with %s" %
1278                               (self.describe(), seen[cname].describe()))
1279        seen[cname] = self
1280
1281    def _pretty_owner(self):
1282        owner = self.owner
1283        if owner.startswith('q_obj_'):
1284            # See QAPISchema._make_implicit_object_type() - reverse the
1285            # mapping there to create a nice human-readable description
1286            owner = owner[6:]
1287            if owner.endswith('-arg'):
1288                return '(parameter of %s)' % owner[:-4]
1289            elif owner.endswith('-base'):
1290                return '(base of %s)' % owner[:-5]
1291            else:
1292                assert owner.endswith('-wrapper')
1293                # Unreachable and not implemented
1294                assert False
1295        if owner.endswith('Kind'):
1296            # See QAPISchema._make_implicit_enum_type()
1297            return '(branch of %s)' % owner[:-4]
1298        return '(%s of %s)' % (self.role, owner)
1299
1300    def describe(self):
1301        return "'%s' %s" % (self.name, self._pretty_owner())
1302
1303
1304class QAPISchemaObjectTypeMember(QAPISchemaMember):
1305    def __init__(self, name, typ, optional):
1306        QAPISchemaMember.__init__(self, name)
1307        assert isinstance(typ, str)
1308        assert isinstance(optional, bool)
1309        self._type_name = typ
1310        self.type = None
1311        self.optional = optional
1312
1313    def check(self, schema):
1314        assert self.owner
1315        self.type = schema.lookup_type(self._type_name)
1316        assert self.type
1317
1318
1319class QAPISchemaObjectTypeVariants(object):
1320    def __init__(self, tag_name, tag_member, variants):
1321        # Flat unions pass tag_name but not tag_member.
1322        # Simple unions and alternates pass tag_member but not tag_name.
1323        # After check(), tag_member is always set, and tag_name remains
1324        # a reliable witness of being used by a flat union.
1325        assert bool(tag_member) != bool(tag_name)
1326        assert (isinstance(tag_name, str) or
1327                isinstance(tag_member, QAPISchemaObjectTypeMember))
1328        assert len(variants) > 0
1329        for v in variants:
1330            assert isinstance(v, QAPISchemaObjectTypeVariant)
1331        self._tag_name = tag_name
1332        self.tag_member = tag_member
1333        self.variants = variants
1334
1335    def set_owner(self, name):
1336        for v in self.variants:
1337            v.set_owner(name)
1338
1339    def check(self, schema, seen):
1340        if not self.tag_member:    # flat union
1341            self.tag_member = seen[c_name(self._tag_name)]
1342            assert self._tag_name == self.tag_member.name
1343        assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1344        for v in self.variants:
1345            v.check(schema)
1346            # Union names must match enum values; alternate names are
1347            # checked separately. Use 'seen' to tell the two apart.
1348            if seen:
1349                assert v.name in self.tag_member.type.member_names()
1350                assert isinstance(v.type, QAPISchemaObjectType)
1351                v.type.check(schema)
1352
1353    def check_clash(self, info, seen):
1354        for v in self.variants:
1355            # Reset seen map for each variant, since qapi names from one
1356            # branch do not affect another branch
1357            assert isinstance(v.type, QAPISchemaObjectType)
1358            v.type.check_clash(info, dict(seen))
1359
1360
1361class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1362    role = 'branch'
1363
1364    def __init__(self, name, typ):
1365        QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1366
1367
1368class QAPISchemaAlternateType(QAPISchemaType):
1369    def __init__(self, name, info, doc, variants):
1370        QAPISchemaType.__init__(self, name, info, doc)
1371        assert isinstance(variants, QAPISchemaObjectTypeVariants)
1372        assert variants.tag_member
1373        variants.set_owner(name)
1374        variants.tag_member.set_owner(self.name)
1375        self.variants = variants
1376
1377    def check(self, schema):
1378        self.variants.tag_member.check(schema)
1379        # Not calling self.variants.check_clash(), because there's nothing
1380        # to clash with
1381        self.variants.check(schema, {})
1382        # Alternate branch names have no relation to the tag enum values;
1383        # so we have to check for potential name collisions ourselves.
1384        seen = {}
1385        for v in self.variants.variants:
1386            v.check_clash(self.info, seen)
1387            if self.doc:
1388                self.doc.connect_member(v)
1389        if self.doc:
1390            self.doc.check()
1391
1392    def c_type(self):
1393        return c_name(self.name) + pointer_suffix
1394
1395    def json_type(self):
1396        return 'value'
1397
1398    def visit(self, visitor):
1399        visitor.visit_alternate_type(self.name, self.info, self.variants)
1400
1401    def is_empty(self):
1402        return False
1403
1404
1405class QAPISchemaCommand(QAPISchemaEntity):
1406    def __init__(self, name, info, doc, arg_type, ret_type,
1407                 gen, success_response, boxed):
1408        QAPISchemaEntity.__init__(self, name, info, doc)
1409        assert not arg_type or isinstance(arg_type, str)
1410        assert not ret_type or isinstance(ret_type, str)
1411        self._arg_type_name = arg_type
1412        self.arg_type = None
1413        self._ret_type_name = ret_type
1414        self.ret_type = None
1415        self.gen = gen
1416        self.success_response = success_response
1417        self.boxed = boxed
1418
1419    def check(self, schema):
1420        if self._arg_type_name:
1421            self.arg_type = schema.lookup_type(self._arg_type_name)
1422            assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1423                    isinstance(self.arg_type, QAPISchemaAlternateType))
1424            self.arg_type.check(schema)
1425            if self.boxed:
1426                if self.arg_type.is_empty():
1427                    raise QAPISemError(self.info,
1428                                       "Cannot use 'boxed' with empty type")
1429            else:
1430                assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1431                assert not self.arg_type.variants
1432        elif self.boxed:
1433            raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1434        if self._ret_type_name:
1435            self.ret_type = schema.lookup_type(self._ret_type_name)
1436            assert isinstance(self.ret_type, QAPISchemaType)
1437
1438    def visit(self, visitor):
1439        visitor.visit_command(self.name, self.info,
1440                              self.arg_type, self.ret_type,
1441                              self.gen, self.success_response, self.boxed)
1442
1443
1444class QAPISchemaEvent(QAPISchemaEntity):
1445    def __init__(self, name, info, doc, arg_type, boxed):
1446        QAPISchemaEntity.__init__(self, name, info, doc)
1447        assert not arg_type or isinstance(arg_type, str)
1448        self._arg_type_name = arg_type
1449        self.arg_type = None
1450        self.boxed = boxed
1451
1452    def check(self, schema):
1453        if self._arg_type_name:
1454            self.arg_type = schema.lookup_type(self._arg_type_name)
1455            assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1456                    isinstance(self.arg_type, QAPISchemaAlternateType))
1457            self.arg_type.check(schema)
1458            if self.boxed:
1459                if self.arg_type.is_empty():
1460                    raise QAPISemError(self.info,
1461                                       "Cannot use 'boxed' with empty type")
1462            else:
1463                assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1464                assert not self.arg_type.variants
1465        elif self.boxed:
1466            raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1467
1468    def visit(self, visitor):
1469        visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
1470
1471
1472class QAPISchema(object):
1473    def __init__(self, fname):
1474        parser = QAPISchemaParser(open(fname, 'r'))
1475        exprs = check_exprs(parser.exprs)
1476        self.docs = parser.docs
1477        self._entity_dict = {}
1478        self._predefining = True
1479        self._def_predefineds()
1480        self._predefining = False
1481        self._def_exprs(exprs)
1482        self.check()
1483
1484    def _def_entity(self, ent):
1485        # Only the predefined types are allowed to not have info
1486        assert ent.info or self._predefining
1487        assert ent.name not in self._entity_dict
1488        self._entity_dict[ent.name] = ent
1489
1490    def lookup_entity(self, name, typ=None):
1491        ent = self._entity_dict.get(name)
1492        if typ and not isinstance(ent, typ):
1493            return None
1494        return ent
1495
1496    def lookup_type(self, name):
1497        return self.lookup_entity(name, QAPISchemaType)
1498
1499    def _def_builtin_type(self, name, json_type, c_type):
1500        self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1501        # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1502        # qapi-types.h from a single .c, all arrays of builtins must be
1503        # declared in the first file whether or not they are used.  Nicer
1504        # would be to use lazy instantiation, while figuring out how to
1505        # avoid compilation issues with multiple qapi-types.h.
1506        self._make_array_type(name, None)
1507
1508    def _def_predefineds(self):
1509        for t in [('str',    'string',  'char' + pointer_suffix),
1510                  ('number', 'number',  'double'),
1511                  ('int',    'int',     'int64_t'),
1512                  ('int8',   'int',     'int8_t'),
1513                  ('int16',  'int',     'int16_t'),
1514                  ('int32',  'int',     'int32_t'),
1515                  ('int64',  'int',     'int64_t'),
1516                  ('uint8',  'int',     'uint8_t'),
1517                  ('uint16', 'int',     'uint16_t'),
1518                  ('uint32', 'int',     'uint32_t'),
1519                  ('uint64', 'int',     'uint64_t'),
1520                  ('size',   'int',     'uint64_t'),
1521                  ('bool',   'boolean', 'bool'),
1522                  ('any',    'value',   'QObject' + pointer_suffix),
1523                  ('null',   'null',    'QNull' + pointer_suffix)]:
1524            self._def_builtin_type(*t)
1525        self.the_empty_object_type = QAPISchemaObjectType(
1526            'q_empty', None, None, None, [], None)
1527        self._def_entity(self.the_empty_object_type)
1528        qtype_values = self._make_enum_members(['none', 'qnull', 'qnum',
1529                                                'qstring', 'qdict', 'qlist',
1530                                                'qbool'])
1531        self._def_entity(QAPISchemaEnumType('QType', None, None,
1532                                            qtype_values, 'QTYPE'))
1533
1534    def _make_enum_members(self, values):
1535        return [QAPISchemaMember(v) for v in values]
1536
1537    def _make_implicit_enum_type(self, name, info, values):
1538        # See also QAPISchemaObjectTypeMember._pretty_owner()
1539        name = name + 'Kind'   # Use namespace reserved by add_name()
1540        self._def_entity(QAPISchemaEnumType(
1541            name, info, None, self._make_enum_members(values), None))
1542        return name
1543
1544    def _make_array_type(self, element_type, info):
1545        name = element_type + 'List'   # Use namespace reserved by add_name()
1546        if not self.lookup_type(name):
1547            self._def_entity(QAPISchemaArrayType(name, info, element_type))
1548        return name
1549
1550    def _make_implicit_object_type(self, name, info, doc, role, members):
1551        if not members:
1552            return None
1553        # See also QAPISchemaObjectTypeMember._pretty_owner()
1554        name = 'q_obj_%s-%s' % (name, role)
1555        if not self.lookup_entity(name, QAPISchemaObjectType):
1556            self._def_entity(QAPISchemaObjectType(name, info, doc, None,
1557                                                  members, None))
1558        return name
1559
1560    def _def_enum_type(self, expr, info, doc):
1561        name = expr['enum']
1562        data = expr['data']
1563        prefix = expr.get('prefix')
1564        self._def_entity(QAPISchemaEnumType(
1565            name, info, doc, self._make_enum_members(data), prefix))
1566
1567    def _make_member(self, name, typ, info):
1568        optional = False
1569        if name.startswith('*'):
1570            name = name[1:]
1571            optional = True
1572        if isinstance(typ, list):
1573            assert len(typ) == 1
1574            typ = self._make_array_type(typ[0], info)
1575        return QAPISchemaObjectTypeMember(name, typ, optional)
1576
1577    def _make_members(self, data, info):
1578        return [self._make_member(key, value, info)
1579                for (key, value) in data.items()]
1580
1581    def _def_struct_type(self, expr, info, doc):
1582        name = expr['struct']
1583        base = expr.get('base')
1584        data = expr['data']
1585        self._def_entity(QAPISchemaObjectType(name, info, doc, base,
1586                                              self._make_members(data, info),
1587                                              None))
1588
1589    def _make_variant(self, case, typ):
1590        return QAPISchemaObjectTypeVariant(case, typ)
1591
1592    def _make_simple_variant(self, case, typ, info):
1593        if isinstance(typ, list):
1594            assert len(typ) == 1
1595            typ = self._make_array_type(typ[0], info)
1596        typ = self._make_implicit_object_type(
1597            typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
1598        return QAPISchemaObjectTypeVariant(case, typ)
1599
1600    def _def_union_type(self, expr, info, doc):
1601        name = expr['union']
1602        data = expr['data']
1603        base = expr.get('base')
1604        tag_name = expr.get('discriminator')
1605        tag_member = None
1606        if isinstance(base, dict):
1607            base = (self._make_implicit_object_type(
1608                name, info, doc, 'base', self._make_members(base, info)))
1609        if tag_name:
1610            variants = [self._make_variant(key, value)
1611                        for (key, value) in data.items()]
1612            members = []
1613        else:
1614            variants = [self._make_simple_variant(key, value, info)
1615                        for (key, value) in data.items()]
1616            typ = self._make_implicit_enum_type(name, info,
1617                                                [v.name for v in variants])
1618            tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1619            members = [tag_member]
1620        self._def_entity(
1621            QAPISchemaObjectType(name, info, doc, base, members,
1622                                 QAPISchemaObjectTypeVariants(tag_name,
1623                                                              tag_member,
1624                                                              variants)))
1625
1626    def _def_alternate_type(self, expr, info, doc):
1627        name = expr['alternate']
1628        data = expr['data']
1629        variants = [self._make_variant(key, value)
1630                    for (key, value) in data.items()]
1631        tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
1632        self._def_entity(
1633            QAPISchemaAlternateType(name, info, doc,
1634                                    QAPISchemaObjectTypeVariants(None,
1635                                                                 tag_member,
1636                                                                 variants)))
1637
1638    def _def_command(self, expr, info, doc):
1639        name = expr['command']
1640        data = expr.get('data')
1641        rets = expr.get('returns')
1642        gen = expr.get('gen', True)
1643        success_response = expr.get('success-response', True)
1644        boxed = expr.get('boxed', False)
1645        if isinstance(data, OrderedDict):
1646            data = self._make_implicit_object_type(
1647                name, info, doc, 'arg', self._make_members(data, info))
1648        if isinstance(rets, list):
1649            assert len(rets) == 1
1650            rets = self._make_array_type(rets[0], info)
1651        self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
1652                                           gen, success_response, boxed))
1653
1654    def _def_event(self, expr, info, doc):
1655        name = expr['event']
1656        data = expr.get('data')
1657        boxed = expr.get('boxed', False)
1658        if isinstance(data, OrderedDict):
1659            data = self._make_implicit_object_type(
1660                name, info, doc, 'arg', self._make_members(data, info))
1661        self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
1662
1663    def _def_exprs(self, exprs):
1664        for expr_elem in exprs:
1665            expr = expr_elem['expr']
1666            info = expr_elem['info']
1667            doc = expr_elem.get('doc')
1668            if 'enum' in expr:
1669                self._def_enum_type(expr, info, doc)
1670            elif 'struct' in expr:
1671                self._def_struct_type(expr, info, doc)
1672            elif 'union' in expr:
1673                self._def_union_type(expr, info, doc)
1674            elif 'alternate' in expr:
1675                self._def_alternate_type(expr, info, doc)
1676            elif 'command' in expr:
1677                self._def_command(expr, info, doc)
1678            elif 'event' in expr:
1679                self._def_event(expr, info, doc)
1680            elif 'include' in expr:
1681                pass
1682            else:
1683                assert False
1684
1685    def check(self):
1686        for (name, ent) in sorted(self._entity_dict.items()):
1687            ent.check(self)
1688
1689    def visit(self, visitor):
1690        visitor.visit_begin(self)
1691        for (name, entity) in sorted(self._entity_dict.items()):
1692            if visitor.visit_needed(entity):
1693                entity.visit(visitor)
1694        visitor.visit_end()
1695
1696
1697#
1698# Code generation helpers
1699#
1700
1701def camel_case(name):
1702    new_name = ''
1703    first = True
1704    for ch in name:
1705        if ch in ['_', '-']:
1706            first = True
1707        elif first:
1708            new_name += ch.upper()
1709            first = False
1710        else:
1711            new_name += ch.lower()
1712    return new_name
1713
1714
1715# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1716# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1717# ENUM24_Name -> ENUM24_NAME
1718def camel_to_upper(value):
1719    c_fun_str = c_name(value, False)
1720    if value.isupper():
1721        return c_fun_str
1722
1723    new_name = ''
1724    l = len(c_fun_str)
1725    for i in range(l):
1726        c = c_fun_str[i]
1727        # When c is upper and no '_' appears before, do more checks
1728        if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
1729            if i < l - 1 and c_fun_str[i + 1].islower():
1730                new_name += '_'
1731            elif c_fun_str[i - 1].isdigit():
1732                new_name += '_'
1733        new_name += c
1734    return new_name.lstrip('_').upper()
1735
1736
1737def c_enum_const(type_name, const_name, prefix=None):
1738    if prefix is not None:
1739        type_name = prefix
1740    return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
1741
1742if hasattr(str, 'maketrans'):
1743    c_name_trans = str.maketrans('.-', '__')
1744else:
1745    c_name_trans = string.maketrans('.-', '__')
1746
1747
1748# Map @name to a valid C identifier.
1749# If @protect, avoid returning certain ticklish identifiers (like
1750# C keywords) by prepending 'q_'.
1751#
1752# Used for converting 'name' from a 'name':'type' qapi definition
1753# into a generated struct member, as well as converting type names
1754# into substrings of a generated C function name.
1755# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1756# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1757def c_name(name, protect=True):
1758    # ANSI X3J11/88-090, 3.1.1
1759    c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
1760                     'default', 'do', 'double', 'else', 'enum', 'extern',
1761                     'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1762                     'return', 'short', 'signed', 'sizeof', 'static',
1763                     'struct', 'switch', 'typedef', 'union', 'unsigned',
1764                     'void', 'volatile', 'while'])
1765    # ISO/IEC 9899:1999, 6.4.1
1766    c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1767    # ISO/IEC 9899:2011, 6.4.1
1768    c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1769                     '_Noreturn', '_Static_assert', '_Thread_local'])
1770    # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1771    # excluding _.*
1772    gcc_words = set(['asm', 'typeof'])
1773    # C++ ISO/IEC 14882:2003 2.11
1774    cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1775                     'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1776                     'namespace', 'new', 'operator', 'private', 'protected',
1777                     'public', 'reinterpret_cast', 'static_cast', 'template',
1778                     'this', 'throw', 'true', 'try', 'typeid', 'typename',
1779                     'using', 'virtual', 'wchar_t',
1780                     # alternative representations
1781                     'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1782                     'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1783    # namespace pollution:
1784    polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
1785    name = name.translate(c_name_trans)
1786    if protect and (name in c89_words | c99_words | c11_words | gcc_words
1787                    | cpp_words | polluted_words):
1788        return 'q_' + name
1789    return name
1790
1791eatspace = '\033EATSPACE.'
1792pointer_suffix = ' *' + eatspace
1793
1794
1795def genindent(count):
1796    ret = ''
1797    for _ in range(count):
1798        ret += ' '
1799    return ret
1800
1801indent_level = 0
1802
1803
1804def push_indent(indent_amount=4):
1805    global indent_level
1806    indent_level += indent_amount
1807
1808
1809def pop_indent(indent_amount=4):
1810    global indent_level
1811    indent_level -= indent_amount
1812
1813
1814# Generate @code with @kwds interpolated.
1815# Obey indent_level, and strip eatspace.
1816def cgen(code, **kwds):
1817    raw = code % kwds
1818    if indent_level:
1819        indent = genindent(indent_level)
1820        # re.subn() lacks flags support before Python 2.7, use re.compile()
1821        raw = re.subn(re.compile(r'^.', re.MULTILINE),
1822                      indent + r'\g<0>', raw)
1823        raw = raw[0]
1824    return re.sub(re.escape(eatspace) + r' *', '', raw)
1825
1826
1827def mcgen(code, **kwds):
1828    if code[0] == '\n':
1829        code = code[1:]
1830    return cgen(code, **kwds)
1831
1832
1833def guardname(filename):
1834    return c_name(filename, protect=False).upper()
1835
1836
1837def guardstart(name):
1838    return mcgen('''
1839#ifndef %(name)s
1840#define %(name)s
1841
1842''',
1843                 name=guardname(name))
1844
1845
1846def guardend(name):
1847    return mcgen('''
1848
1849#endif /* %(name)s */
1850''',
1851                 name=guardname(name))
1852
1853
1854def gen_enum_lookup(name, values, prefix=None):
1855    ret = mcgen('''
1856
1857const QEnumLookup %(c_name)s_lookup = {
1858    .array = (const char *const[]) {
1859''',
1860                c_name=c_name(name))
1861    for value in values:
1862        index = c_enum_const(name, value, prefix)
1863        ret += mcgen('''
1864        [%(index)s] = "%(value)s",
1865''',
1866                     index=index, value=value)
1867
1868    ret += mcgen('''
1869    },
1870    .size = %(max_index)s
1871};
1872''',
1873                 max_index=c_enum_const(name, '_MAX', prefix))
1874    return ret
1875
1876
1877def gen_enum(name, values, prefix=None):
1878    # append automatically generated _MAX value
1879    enum_values = values + ['_MAX']
1880
1881    ret = mcgen('''
1882
1883typedef enum %(c_name)s {
1884''',
1885                c_name=c_name(name))
1886
1887    i = 0
1888    for value in enum_values:
1889        ret += mcgen('''
1890    %(c_enum)s = %(i)d,
1891''',
1892                     c_enum=c_enum_const(name, value, prefix),
1893                     i=i)
1894        i += 1
1895
1896    ret += mcgen('''
1897} %(c_name)s;
1898''',
1899                 c_name=c_name(name))
1900
1901    ret += mcgen('''
1902
1903#define %(c_name)s_str(val) \\
1904    qapi_enum_lookup(&%(c_name)s_lookup, (val))
1905
1906extern const QEnumLookup %(c_name)s_lookup;
1907''',
1908                 c_name=c_name(name))
1909    return ret
1910
1911
1912def build_params(arg_type, boxed, extra):
1913    if not arg_type:
1914        assert not boxed
1915        return extra
1916    ret = ''
1917    sep = ''
1918    if boxed:
1919        ret += '%s arg' % arg_type.c_param_type()
1920        sep = ', '
1921    else:
1922        assert not arg_type.variants
1923        for memb in arg_type.members:
1924            ret += sep
1925            sep = ', '
1926            if memb.optional:
1927                ret += 'bool has_%s, ' % c_name(memb.name)
1928            ret += '%s %s' % (memb.type.c_param_type(),
1929                              c_name(memb.name))
1930    if extra:
1931        ret += sep + extra
1932    return ret
1933
1934
1935#
1936# Accumulate and write output
1937#
1938
1939class QAPIGen(object):
1940
1941    def __init__(self):
1942        self._preamble = ''
1943        self._body = ''
1944
1945    def preamble_add(self, text):
1946        self._preamble += text
1947
1948    def add(self, text):
1949        self._body += text
1950
1951    def _top(self, fname):
1952        return ''
1953
1954    def _bottom(self, fname):
1955        return ''
1956
1957    def write(self, output_dir, fname):
1958        if output_dir:
1959            try:
1960                os.makedirs(output_dir)
1961            except os.error as e:
1962                if e.errno != errno.EEXIST:
1963                    raise
1964        fd = os.open(os.path.join(output_dir, fname),
1965                     os.O_RDWR | os.O_CREAT, 0o666)
1966        f = os.fdopen(fd, 'r+')
1967        text = (self._top(fname) + self._preamble + self._body
1968                + self._bottom(fname))
1969        oldtext = f.read(len(text) + 1)
1970        if text != oldtext:
1971            f.seek(0)
1972            f.truncate(0)
1973            f.write(text)
1974        f.close()
1975
1976
1977class QAPIGenC(QAPIGen):
1978
1979    def __init__(self, blurb, pydoc):
1980        QAPIGen.__init__(self)
1981        self._blurb = blurb
1982        self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
1983                                                  re.MULTILINE))
1984
1985    def _top(self, fname):
1986        return mcgen('''
1987/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
1988
1989/*
1990%(blurb)s
1991 *
1992 * %(copyright)s
1993 *
1994 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
1995 * See the COPYING.LIB file in the top-level directory.
1996 */
1997
1998''',
1999                     blurb=self._blurb, copyright=self._copyright)
2000
2001
2002class QAPIGenH(QAPIGenC):
2003
2004    def _top(self, fname):
2005        return QAPIGenC._top(self, fname) + guardstart(fname)
2006
2007    def _bottom(self, fname):
2008        return guardend(fname)
2009
2010
2011class QAPIGenDoc(QAPIGen):
2012
2013    def _top(self, fname):
2014        return (QAPIGen._top(self, fname)
2015                + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2016