xref: /openbmc/qemu/scripts/qapi/common.py (revision 71b3f0459c460c9e16a47372ccddbfa6e2c7aadf)
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 name is None or isinstance(name, str)
988        self.name = name
989        self.module = None
990        # For explicitly defined entities, info points to the (explicit)
991        # definition.  For builtins (and their arrays), info is None.
992        # For implicitly defined entities, info points to a place that
993        # triggered the implicit definition (there may be more than one
994        # such place).
995        self.info = info
996        self.doc = doc
997
998    def c_name(self):
999        return c_name(self.name)
1000
1001    def check(self, schema):
1002        pass
1003
1004    def is_implicit(self):
1005        return not self.info
1006
1007    def visit(self, visitor):
1008        pass
1009
1010
1011class QAPISchemaVisitor(object):
1012    def visit_begin(self, schema):
1013        pass
1014
1015    def visit_end(self):
1016        pass
1017
1018    def visit_module(self, fname):
1019        pass
1020
1021    def visit_needed(self, entity):
1022        # Default to visiting everything
1023        return True
1024
1025    def visit_include(self, fname, info):
1026        pass
1027
1028    def visit_builtin_type(self, name, info, json_type):
1029        pass
1030
1031    def visit_enum_type(self, name, info, values, prefix):
1032        pass
1033
1034    def visit_array_type(self, name, info, element_type):
1035        pass
1036
1037    def visit_object_type(self, name, info, base, members, variants):
1038        pass
1039
1040    def visit_object_type_flat(self, name, info, members, variants):
1041        pass
1042
1043    def visit_alternate_type(self, name, info, variants):
1044        pass
1045
1046    def visit_command(self, name, info, arg_type, ret_type,
1047                      gen, success_response, boxed):
1048        pass
1049
1050    def visit_event(self, name, info, arg_type, boxed):
1051        pass
1052
1053
1054class QAPISchemaInclude(QAPISchemaEntity):
1055
1056    def __init__(self, fname, info):
1057        QAPISchemaEntity.__init__(self, None, info, None)
1058        self.fname = fname
1059
1060    def visit(self, visitor):
1061        visitor.visit_include(self.fname, self.info)
1062
1063
1064class QAPISchemaType(QAPISchemaEntity):
1065    # Return the C type for common use.
1066    # For the types we commonly box, this is a pointer type.
1067    def c_type(self):
1068        pass
1069
1070    # Return the C type to be used in a parameter list.
1071    def c_param_type(self):
1072        return self.c_type()
1073
1074    # Return the C type to be used where we suppress boxing.
1075    def c_unboxed_type(self):
1076        return self.c_type()
1077
1078    def json_type(self):
1079        pass
1080
1081    def alternate_qtype(self):
1082        json2qtype = {
1083            'null':    'QTYPE_QNULL',
1084            'string':  'QTYPE_QSTRING',
1085            'number':  'QTYPE_QNUM',
1086            'int':     'QTYPE_QNUM',
1087            'boolean': 'QTYPE_QBOOL',
1088            'object':  'QTYPE_QDICT'
1089        }
1090        return json2qtype.get(self.json_type())
1091
1092    def doc_type(self):
1093        if self.is_implicit():
1094            return None
1095        return self.name
1096
1097
1098class QAPISchemaBuiltinType(QAPISchemaType):
1099    def __init__(self, name, json_type, c_type):
1100        QAPISchemaType.__init__(self, name, None, None)
1101        assert not c_type or isinstance(c_type, str)
1102        assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1103                             'value')
1104        self._json_type_name = json_type
1105        self._c_type_name = c_type
1106
1107    def c_name(self):
1108        return self.name
1109
1110    def c_type(self):
1111        return self._c_type_name
1112
1113    def c_param_type(self):
1114        if self.name == 'str':
1115            return 'const ' + self._c_type_name
1116        return self._c_type_name
1117
1118    def json_type(self):
1119        return self._json_type_name
1120
1121    def doc_type(self):
1122        return self.json_type()
1123
1124    def visit(self, visitor):
1125        visitor.visit_builtin_type(self.name, self.info, self.json_type())
1126
1127
1128class QAPISchemaEnumType(QAPISchemaType):
1129    def __init__(self, name, info, doc, values, prefix):
1130        QAPISchemaType.__init__(self, name, info, doc)
1131        for v in values:
1132            assert isinstance(v, QAPISchemaMember)
1133            v.set_owner(name)
1134        assert prefix is None or isinstance(prefix, str)
1135        self.values = values
1136        self.prefix = prefix
1137
1138    def check(self, schema):
1139        seen = {}
1140        for v in self.values:
1141            v.check_clash(self.info, seen)
1142            if self.doc:
1143                self.doc.connect_member(v)
1144
1145    def is_implicit(self):
1146        # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1147        return self.name.endswith('Kind') or self.name == 'QType'
1148
1149    def c_type(self):
1150        return c_name(self.name)
1151
1152    def member_names(self):
1153        return [v.name for v in self.values]
1154
1155    def json_type(self):
1156        return 'string'
1157
1158    def visit(self, visitor):
1159        visitor.visit_enum_type(self.name, self.info,
1160                                self.member_names(), self.prefix)
1161
1162
1163class QAPISchemaArrayType(QAPISchemaType):
1164    def __init__(self, name, info, element_type):
1165        QAPISchemaType.__init__(self, name, info, None)
1166        assert isinstance(element_type, str)
1167        self._element_type_name = element_type
1168        self.element_type = None
1169
1170    def check(self, schema):
1171        self.element_type = schema.lookup_type(self._element_type_name)
1172        assert self.element_type
1173
1174    def is_implicit(self):
1175        return True
1176
1177    def c_type(self):
1178        return c_name(self.name) + pointer_suffix
1179
1180    def json_type(self):
1181        return 'array'
1182
1183    def doc_type(self):
1184        elt_doc_type = self.element_type.doc_type()
1185        if not elt_doc_type:
1186            return None
1187        return 'array of ' + elt_doc_type
1188
1189    def visit(self, visitor):
1190        visitor.visit_array_type(self.name, self.info, self.element_type)
1191
1192
1193class QAPISchemaObjectType(QAPISchemaType):
1194    def __init__(self, name, info, doc, base, local_members, variants):
1195        # struct has local_members, optional base, and no variants
1196        # flat union has base, variants, and no local_members
1197        # simple union has local_members, variants, and no base
1198        QAPISchemaType.__init__(self, name, info, doc)
1199        assert base is None or isinstance(base, str)
1200        for m in local_members:
1201            assert isinstance(m, QAPISchemaObjectTypeMember)
1202            m.set_owner(name)
1203        if variants is not None:
1204            assert isinstance(variants, QAPISchemaObjectTypeVariants)
1205            variants.set_owner(name)
1206        self._base_name = base
1207        self.base = None
1208        self.local_members = local_members
1209        self.variants = variants
1210        self.members = None
1211
1212    def check(self, schema):
1213        if self.members is False:               # check for cycles
1214            raise QAPISemError(self.info,
1215                               "Object %s contains itself" % self.name)
1216        if self.members:
1217            return
1218        self.members = False                    # mark as being checked
1219        seen = OrderedDict()
1220        if self._base_name:
1221            self.base = schema.lookup_type(self._base_name)
1222            assert isinstance(self.base, QAPISchemaObjectType)
1223            self.base.check(schema)
1224            self.base.check_clash(self.info, seen)
1225        for m in self.local_members:
1226            m.check(schema)
1227            m.check_clash(self.info, seen)
1228            if self.doc:
1229                self.doc.connect_member(m)
1230        self.members = seen.values()
1231        if self.variants:
1232            self.variants.check(schema, seen)
1233            assert self.variants.tag_member in self.members
1234            self.variants.check_clash(self.info, seen)
1235        if self.doc:
1236            self.doc.check()
1237
1238    # Check that the members of this type do not cause duplicate JSON members,
1239    # and update seen to track the members seen so far. Report any errors
1240    # on behalf of info, which is not necessarily self.info
1241    def check_clash(self, info, seen):
1242        assert not self.variants       # not implemented
1243        for m in self.members:
1244            m.check_clash(info, seen)
1245
1246    def is_implicit(self):
1247        # See QAPISchema._make_implicit_object_type(), as well as
1248        # _def_predefineds()
1249        return self.name.startswith('q_')
1250
1251    def is_empty(self):
1252        assert self.members is not None
1253        return not self.members and not self.variants
1254
1255    def c_name(self):
1256        assert self.name != 'q_empty'
1257        return QAPISchemaType.c_name(self)
1258
1259    def c_type(self):
1260        assert not self.is_implicit()
1261        return c_name(self.name) + pointer_suffix
1262
1263    def c_unboxed_type(self):
1264        return c_name(self.name)
1265
1266    def json_type(self):
1267        return 'object'
1268
1269    def visit(self, visitor):
1270        visitor.visit_object_type(self.name, self.info,
1271                                  self.base, self.local_members, self.variants)
1272        visitor.visit_object_type_flat(self.name, self.info,
1273                                       self.members, self.variants)
1274
1275
1276class QAPISchemaMember(object):
1277    role = 'member'
1278
1279    def __init__(self, name):
1280        assert isinstance(name, str)
1281        self.name = name
1282        self.owner = None
1283
1284    def set_owner(self, name):
1285        assert not self.owner
1286        self.owner = name
1287
1288    def check_clash(self, info, seen):
1289        cname = c_name(self.name)
1290        if cname.lower() != cname and self.owner not in name_case_whitelist:
1291            raise QAPISemError(info,
1292                               "%s should not use uppercase" % self.describe())
1293        if cname in seen:
1294            raise QAPISemError(info, "%s collides with %s" %
1295                               (self.describe(), seen[cname].describe()))
1296        seen[cname] = self
1297
1298    def _pretty_owner(self):
1299        owner = self.owner
1300        if owner.startswith('q_obj_'):
1301            # See QAPISchema._make_implicit_object_type() - reverse the
1302            # mapping there to create a nice human-readable description
1303            owner = owner[6:]
1304            if owner.endswith('-arg'):
1305                return '(parameter of %s)' % owner[:-4]
1306            elif owner.endswith('-base'):
1307                return '(base of %s)' % owner[:-5]
1308            else:
1309                assert owner.endswith('-wrapper')
1310                # Unreachable and not implemented
1311                assert False
1312        if owner.endswith('Kind'):
1313            # See QAPISchema._make_implicit_enum_type()
1314            return '(branch of %s)' % owner[:-4]
1315        return '(%s of %s)' % (self.role, owner)
1316
1317    def describe(self):
1318        return "'%s' %s" % (self.name, self._pretty_owner())
1319
1320
1321class QAPISchemaObjectTypeMember(QAPISchemaMember):
1322    def __init__(self, name, typ, optional):
1323        QAPISchemaMember.__init__(self, name)
1324        assert isinstance(typ, str)
1325        assert isinstance(optional, bool)
1326        self._type_name = typ
1327        self.type = None
1328        self.optional = optional
1329
1330    def check(self, schema):
1331        assert self.owner
1332        self.type = schema.lookup_type(self._type_name)
1333        assert self.type
1334
1335
1336class QAPISchemaObjectTypeVariants(object):
1337    def __init__(self, tag_name, tag_member, variants):
1338        # Flat unions pass tag_name but not tag_member.
1339        # Simple unions and alternates pass tag_member but not tag_name.
1340        # After check(), tag_member is always set, and tag_name remains
1341        # a reliable witness of being used by a flat union.
1342        assert bool(tag_member) != bool(tag_name)
1343        assert (isinstance(tag_name, str) or
1344                isinstance(tag_member, QAPISchemaObjectTypeMember))
1345        assert len(variants) > 0
1346        for v in variants:
1347            assert isinstance(v, QAPISchemaObjectTypeVariant)
1348        self._tag_name = tag_name
1349        self.tag_member = tag_member
1350        self.variants = variants
1351
1352    def set_owner(self, name):
1353        for v in self.variants:
1354            v.set_owner(name)
1355
1356    def check(self, schema, seen):
1357        if not self.tag_member:    # flat union
1358            self.tag_member = seen[c_name(self._tag_name)]
1359            assert self._tag_name == self.tag_member.name
1360        assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1361        for v in self.variants:
1362            v.check(schema)
1363            # Union names must match enum values; alternate names are
1364            # checked separately. Use 'seen' to tell the two apart.
1365            if seen:
1366                assert v.name in self.tag_member.type.member_names()
1367                assert isinstance(v.type, QAPISchemaObjectType)
1368                v.type.check(schema)
1369
1370    def check_clash(self, info, seen):
1371        for v in self.variants:
1372            # Reset seen map for each variant, since qapi names from one
1373            # branch do not affect another branch
1374            assert isinstance(v.type, QAPISchemaObjectType)
1375            v.type.check_clash(info, dict(seen))
1376
1377
1378class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1379    role = 'branch'
1380
1381    def __init__(self, name, typ):
1382        QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1383
1384
1385class QAPISchemaAlternateType(QAPISchemaType):
1386    def __init__(self, name, info, doc, variants):
1387        QAPISchemaType.__init__(self, name, info, doc)
1388        assert isinstance(variants, QAPISchemaObjectTypeVariants)
1389        assert variants.tag_member
1390        variants.set_owner(name)
1391        variants.tag_member.set_owner(self.name)
1392        self.variants = variants
1393
1394    def check(self, schema):
1395        self.variants.tag_member.check(schema)
1396        # Not calling self.variants.check_clash(), because there's nothing
1397        # to clash with
1398        self.variants.check(schema, {})
1399        # Alternate branch names have no relation to the tag enum values;
1400        # so we have to check for potential name collisions ourselves.
1401        seen = {}
1402        for v in self.variants.variants:
1403            v.check_clash(self.info, seen)
1404            if self.doc:
1405                self.doc.connect_member(v)
1406        if self.doc:
1407            self.doc.check()
1408
1409    def c_type(self):
1410        return c_name(self.name) + pointer_suffix
1411
1412    def json_type(self):
1413        return 'value'
1414
1415    def visit(self, visitor):
1416        visitor.visit_alternate_type(self.name, self.info, self.variants)
1417
1418    def is_empty(self):
1419        return False
1420
1421
1422class QAPISchemaCommand(QAPISchemaEntity):
1423    def __init__(self, name, info, doc, arg_type, ret_type,
1424                 gen, success_response, boxed):
1425        QAPISchemaEntity.__init__(self, name, info, doc)
1426        assert not arg_type or isinstance(arg_type, str)
1427        assert not ret_type or isinstance(ret_type, str)
1428        self._arg_type_name = arg_type
1429        self.arg_type = None
1430        self._ret_type_name = ret_type
1431        self.ret_type = None
1432        self.gen = gen
1433        self.success_response = success_response
1434        self.boxed = boxed
1435
1436    def check(self, schema):
1437        if self._arg_type_name:
1438            self.arg_type = schema.lookup_type(self._arg_type_name)
1439            assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1440                    isinstance(self.arg_type, QAPISchemaAlternateType))
1441            self.arg_type.check(schema)
1442            if self.boxed:
1443                if self.arg_type.is_empty():
1444                    raise QAPISemError(self.info,
1445                                       "Cannot use 'boxed' with empty type")
1446            else:
1447                assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1448                assert not self.arg_type.variants
1449        elif self.boxed:
1450            raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1451        if self._ret_type_name:
1452            self.ret_type = schema.lookup_type(self._ret_type_name)
1453            assert isinstance(self.ret_type, QAPISchemaType)
1454
1455    def visit(self, visitor):
1456        visitor.visit_command(self.name, self.info,
1457                              self.arg_type, self.ret_type,
1458                              self.gen, self.success_response, self.boxed)
1459
1460
1461class QAPISchemaEvent(QAPISchemaEntity):
1462    def __init__(self, name, info, doc, arg_type, boxed):
1463        QAPISchemaEntity.__init__(self, name, info, doc)
1464        assert not arg_type or isinstance(arg_type, str)
1465        self._arg_type_name = arg_type
1466        self.arg_type = None
1467        self.boxed = boxed
1468
1469    def check(self, schema):
1470        if self._arg_type_name:
1471            self.arg_type = schema.lookup_type(self._arg_type_name)
1472            assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1473                    isinstance(self.arg_type, QAPISchemaAlternateType))
1474            self.arg_type.check(schema)
1475            if self.boxed:
1476                if self.arg_type.is_empty():
1477                    raise QAPISemError(self.info,
1478                                       "Cannot use 'boxed' with empty type")
1479            else:
1480                assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1481                assert not self.arg_type.variants
1482        elif self.boxed:
1483            raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1484
1485    def visit(self, visitor):
1486        visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
1487
1488
1489class QAPISchema(object):
1490    def __init__(self, fname):
1491        self._fname = fname
1492        parser = QAPISchemaParser(open(fname, 'r'))
1493        exprs = check_exprs(parser.exprs)
1494        self.docs = parser.docs
1495        self._entity_list = []
1496        self._entity_dict = {}
1497        self._predefining = True
1498        self._def_predefineds()
1499        self._predefining = False
1500        self._def_exprs(exprs)
1501        self.check()
1502
1503    def _def_entity(self, ent):
1504        # Only the predefined types are allowed to not have info
1505        assert ent.info or self._predefining
1506        assert ent.name is None or ent.name not in self._entity_dict
1507        self._entity_list.append(ent)
1508        if ent.name is not None:
1509            self._entity_dict[ent.name] = ent
1510        if ent.info:
1511            ent.module = os.path.relpath(ent.info['file'],
1512                                         os.path.dirname(self._fname))
1513
1514    def lookup_entity(self, name, typ=None):
1515        ent = self._entity_dict.get(name)
1516        if typ and not isinstance(ent, typ):
1517            return None
1518        return ent
1519
1520    def lookup_type(self, name):
1521        return self.lookup_entity(name, QAPISchemaType)
1522
1523    def _def_include(self, expr, info, doc):
1524        include = expr['include']
1525        assert doc is None
1526        main_info = info
1527        while main_info['parent']:
1528            main_info = main_info['parent']
1529        fname = os.path.relpath(include, os.path.dirname(main_info['file']))
1530        self._def_entity(QAPISchemaInclude(fname, info))
1531
1532    def _def_builtin_type(self, name, json_type, c_type):
1533        self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1534        # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1535        # qapi-types.h from a single .c, all arrays of builtins must be
1536        # declared in the first file whether or not they are used.  Nicer
1537        # would be to use lazy instantiation, while figuring out how to
1538        # avoid compilation issues with multiple qapi-types.h.
1539        self._make_array_type(name, None)
1540
1541    def _def_predefineds(self):
1542        for t in [('str',    'string',  'char' + pointer_suffix),
1543                  ('number', 'number',  'double'),
1544                  ('int',    'int',     'int64_t'),
1545                  ('int8',   'int',     'int8_t'),
1546                  ('int16',  'int',     'int16_t'),
1547                  ('int32',  'int',     'int32_t'),
1548                  ('int64',  'int',     'int64_t'),
1549                  ('uint8',  'int',     'uint8_t'),
1550                  ('uint16', 'int',     'uint16_t'),
1551                  ('uint32', 'int',     'uint32_t'),
1552                  ('uint64', 'int',     'uint64_t'),
1553                  ('size',   'int',     'uint64_t'),
1554                  ('bool',   'boolean', 'bool'),
1555                  ('any',    'value',   'QObject' + pointer_suffix),
1556                  ('null',   'null',    'QNull' + pointer_suffix)]:
1557            self._def_builtin_type(*t)
1558        self.the_empty_object_type = QAPISchemaObjectType(
1559            'q_empty', None, None, None, [], None)
1560        self._def_entity(self.the_empty_object_type)
1561        qtype_values = self._make_enum_members(['none', 'qnull', 'qnum',
1562                                                'qstring', 'qdict', 'qlist',
1563                                                'qbool'])
1564        self._def_entity(QAPISchemaEnumType('QType', None, None,
1565                                            qtype_values, 'QTYPE'))
1566
1567    def _make_enum_members(self, values):
1568        return [QAPISchemaMember(v) for v in values]
1569
1570    def _make_implicit_enum_type(self, name, info, values):
1571        # See also QAPISchemaObjectTypeMember._pretty_owner()
1572        name = name + 'Kind'   # Use namespace reserved by add_name()
1573        self._def_entity(QAPISchemaEnumType(
1574            name, info, None, self._make_enum_members(values), None))
1575        return name
1576
1577    def _make_array_type(self, element_type, info):
1578        name = element_type + 'List'   # Use namespace reserved by add_name()
1579        if not self.lookup_type(name):
1580            self._def_entity(QAPISchemaArrayType(name, info, element_type))
1581        return name
1582
1583    def _make_implicit_object_type(self, name, info, doc, role, members):
1584        if not members:
1585            return None
1586        # See also QAPISchemaObjectTypeMember._pretty_owner()
1587        name = 'q_obj_%s-%s' % (name, role)
1588        if not self.lookup_entity(name, QAPISchemaObjectType):
1589            self._def_entity(QAPISchemaObjectType(name, info, doc, None,
1590                                                  members, None))
1591        return name
1592
1593    def _def_enum_type(self, expr, info, doc):
1594        name = expr['enum']
1595        data = expr['data']
1596        prefix = expr.get('prefix')
1597        self._def_entity(QAPISchemaEnumType(
1598            name, info, doc, self._make_enum_members(data), prefix))
1599
1600    def _make_member(self, name, typ, info):
1601        optional = False
1602        if name.startswith('*'):
1603            name = name[1:]
1604            optional = True
1605        if isinstance(typ, list):
1606            assert len(typ) == 1
1607            typ = self._make_array_type(typ[0], info)
1608        return QAPISchemaObjectTypeMember(name, typ, optional)
1609
1610    def _make_members(self, data, info):
1611        return [self._make_member(key, value, info)
1612                for (key, value) in data.items()]
1613
1614    def _def_struct_type(self, expr, info, doc):
1615        name = expr['struct']
1616        base = expr.get('base')
1617        data = expr['data']
1618        self._def_entity(QAPISchemaObjectType(name, info, doc, base,
1619                                              self._make_members(data, info),
1620                                              None))
1621
1622    def _make_variant(self, case, typ):
1623        return QAPISchemaObjectTypeVariant(case, typ)
1624
1625    def _make_simple_variant(self, case, typ, info):
1626        if isinstance(typ, list):
1627            assert len(typ) == 1
1628            typ = self._make_array_type(typ[0], info)
1629        typ = self._make_implicit_object_type(
1630            typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
1631        return QAPISchemaObjectTypeVariant(case, typ)
1632
1633    def _def_union_type(self, expr, info, doc):
1634        name = expr['union']
1635        data = expr['data']
1636        base = expr.get('base')
1637        tag_name = expr.get('discriminator')
1638        tag_member = None
1639        if isinstance(base, dict):
1640            base = (self._make_implicit_object_type(
1641                name, info, doc, 'base', self._make_members(base, info)))
1642        if tag_name:
1643            variants = [self._make_variant(key, value)
1644                        for (key, value) in data.items()]
1645            members = []
1646        else:
1647            variants = [self._make_simple_variant(key, value, info)
1648                        for (key, value) in data.items()]
1649            typ = self._make_implicit_enum_type(name, info,
1650                                                [v.name for v in variants])
1651            tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1652            members = [tag_member]
1653        self._def_entity(
1654            QAPISchemaObjectType(name, info, doc, base, members,
1655                                 QAPISchemaObjectTypeVariants(tag_name,
1656                                                              tag_member,
1657                                                              variants)))
1658
1659    def _def_alternate_type(self, expr, info, doc):
1660        name = expr['alternate']
1661        data = expr['data']
1662        variants = [self._make_variant(key, value)
1663                    for (key, value) in data.items()]
1664        tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
1665        self._def_entity(
1666            QAPISchemaAlternateType(name, info, doc,
1667                                    QAPISchemaObjectTypeVariants(None,
1668                                                                 tag_member,
1669                                                                 variants)))
1670
1671    def _def_command(self, expr, info, doc):
1672        name = expr['command']
1673        data = expr.get('data')
1674        rets = expr.get('returns')
1675        gen = expr.get('gen', True)
1676        success_response = expr.get('success-response', True)
1677        boxed = expr.get('boxed', False)
1678        if isinstance(data, OrderedDict):
1679            data = self._make_implicit_object_type(
1680                name, info, doc, 'arg', self._make_members(data, info))
1681        if isinstance(rets, list):
1682            assert len(rets) == 1
1683            rets = self._make_array_type(rets[0], info)
1684        self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
1685                                           gen, success_response, boxed))
1686
1687    def _def_event(self, expr, info, doc):
1688        name = expr['event']
1689        data = expr.get('data')
1690        boxed = expr.get('boxed', False)
1691        if isinstance(data, OrderedDict):
1692            data = self._make_implicit_object_type(
1693                name, info, doc, 'arg', self._make_members(data, info))
1694        self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
1695
1696    def _def_exprs(self, exprs):
1697        for expr_elem in exprs:
1698            expr = expr_elem['expr']
1699            info = expr_elem['info']
1700            doc = expr_elem.get('doc')
1701            if 'enum' in expr:
1702                self._def_enum_type(expr, info, doc)
1703            elif 'struct' in expr:
1704                self._def_struct_type(expr, info, doc)
1705            elif 'union' in expr:
1706                self._def_union_type(expr, info, doc)
1707            elif 'alternate' in expr:
1708                self._def_alternate_type(expr, info, doc)
1709            elif 'command' in expr:
1710                self._def_command(expr, info, doc)
1711            elif 'event' in expr:
1712                self._def_event(expr, info, doc)
1713            elif 'include' in expr:
1714                self._def_include(expr, info, doc)
1715            else:
1716                assert False
1717
1718    def check(self):
1719        for ent in self._entity_list:
1720            ent.check(self)
1721
1722    def visit(self, visitor):
1723        visitor.visit_begin(self)
1724        module = None
1725        for entity in self._entity_list:
1726            if visitor.visit_needed(entity):
1727                if entity.module != module:
1728                    module = entity.module
1729                    visitor.visit_module(module)
1730                entity.visit(visitor)
1731        visitor.visit_end()
1732
1733
1734#
1735# Code generation helpers
1736#
1737
1738def camel_case(name):
1739    new_name = ''
1740    first = True
1741    for ch in name:
1742        if ch in ['_', '-']:
1743            first = True
1744        elif first:
1745            new_name += ch.upper()
1746            first = False
1747        else:
1748            new_name += ch.lower()
1749    return new_name
1750
1751
1752# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1753# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1754# ENUM24_Name -> ENUM24_NAME
1755def camel_to_upper(value):
1756    c_fun_str = c_name(value, False)
1757    if value.isupper():
1758        return c_fun_str
1759
1760    new_name = ''
1761    l = len(c_fun_str)
1762    for i in range(l):
1763        c = c_fun_str[i]
1764        # When c is upper and no '_' appears before, do more checks
1765        if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
1766            if i < l - 1 and c_fun_str[i + 1].islower():
1767                new_name += '_'
1768            elif c_fun_str[i - 1].isdigit():
1769                new_name += '_'
1770        new_name += c
1771    return new_name.lstrip('_').upper()
1772
1773
1774def c_enum_const(type_name, const_name, prefix=None):
1775    if prefix is not None:
1776        type_name = prefix
1777    return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
1778
1779if hasattr(str, 'maketrans'):
1780    c_name_trans = str.maketrans('.-', '__')
1781else:
1782    c_name_trans = string.maketrans('.-', '__')
1783
1784
1785# Map @name to a valid C identifier.
1786# If @protect, avoid returning certain ticklish identifiers (like
1787# C keywords) by prepending 'q_'.
1788#
1789# Used for converting 'name' from a 'name':'type' qapi definition
1790# into a generated struct member, as well as converting type names
1791# into substrings of a generated C function name.
1792# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1793# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1794def c_name(name, protect=True):
1795    # ANSI X3J11/88-090, 3.1.1
1796    c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
1797                     'default', 'do', 'double', 'else', 'enum', 'extern',
1798                     'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1799                     'return', 'short', 'signed', 'sizeof', 'static',
1800                     'struct', 'switch', 'typedef', 'union', 'unsigned',
1801                     'void', 'volatile', 'while'])
1802    # ISO/IEC 9899:1999, 6.4.1
1803    c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1804    # ISO/IEC 9899:2011, 6.4.1
1805    c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1806                     '_Noreturn', '_Static_assert', '_Thread_local'])
1807    # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1808    # excluding _.*
1809    gcc_words = set(['asm', 'typeof'])
1810    # C++ ISO/IEC 14882:2003 2.11
1811    cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1812                     'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1813                     'namespace', 'new', 'operator', 'private', 'protected',
1814                     'public', 'reinterpret_cast', 'static_cast', 'template',
1815                     'this', 'throw', 'true', 'try', 'typeid', 'typename',
1816                     'using', 'virtual', 'wchar_t',
1817                     # alternative representations
1818                     'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1819                     'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1820    # namespace pollution:
1821    polluted_words = set(['unix', 'errno', 'mips', 'sparc'])
1822    name = name.translate(c_name_trans)
1823    if protect and (name in c89_words | c99_words | c11_words | gcc_words
1824                    | cpp_words | polluted_words):
1825        return 'q_' + name
1826    return name
1827
1828eatspace = '\033EATSPACE.'
1829pointer_suffix = ' *' + eatspace
1830
1831
1832def genindent(count):
1833    ret = ''
1834    for _ in range(count):
1835        ret += ' '
1836    return ret
1837
1838indent_level = 0
1839
1840
1841def push_indent(indent_amount=4):
1842    global indent_level
1843    indent_level += indent_amount
1844
1845
1846def pop_indent(indent_amount=4):
1847    global indent_level
1848    indent_level -= indent_amount
1849
1850
1851# Generate @code with @kwds interpolated.
1852# Obey indent_level, and strip eatspace.
1853def cgen(code, **kwds):
1854    raw = code % kwds
1855    if indent_level:
1856        indent = genindent(indent_level)
1857        # re.subn() lacks flags support before Python 2.7, use re.compile()
1858        raw = re.subn(re.compile(r'^.', re.MULTILINE),
1859                      indent + r'\g<0>', raw)
1860        raw = raw[0]
1861    return re.sub(re.escape(eatspace) + r' *', '', raw)
1862
1863
1864def mcgen(code, **kwds):
1865    if code[0] == '\n':
1866        code = code[1:]
1867    return cgen(code, **kwds)
1868
1869
1870def guardname(filename):
1871    return c_name(filename, protect=False).upper()
1872
1873
1874def guardstart(name):
1875    return mcgen('''
1876#ifndef %(name)s
1877#define %(name)s
1878
1879''',
1880                 name=guardname(name))
1881
1882
1883def guardend(name):
1884    return mcgen('''
1885
1886#endif /* %(name)s */
1887''',
1888                 name=guardname(name))
1889
1890
1891def gen_enum_lookup(name, values, prefix=None):
1892    ret = mcgen('''
1893
1894const QEnumLookup %(c_name)s_lookup = {
1895    .array = (const char *const[]) {
1896''',
1897                c_name=c_name(name))
1898    for value in values:
1899        index = c_enum_const(name, value, prefix)
1900        ret += mcgen('''
1901        [%(index)s] = "%(value)s",
1902''',
1903                     index=index, value=value)
1904
1905    ret += mcgen('''
1906    },
1907    .size = %(max_index)s
1908};
1909''',
1910                 max_index=c_enum_const(name, '_MAX', prefix))
1911    return ret
1912
1913
1914def gen_enum(name, values, prefix=None):
1915    # append automatically generated _MAX value
1916    enum_values = values + ['_MAX']
1917
1918    ret = mcgen('''
1919
1920typedef enum %(c_name)s {
1921''',
1922                c_name=c_name(name))
1923
1924    i = 0
1925    for value in enum_values:
1926        ret += mcgen('''
1927    %(c_enum)s = %(i)d,
1928''',
1929                     c_enum=c_enum_const(name, value, prefix),
1930                     i=i)
1931        i += 1
1932
1933    ret += mcgen('''
1934} %(c_name)s;
1935''',
1936                 c_name=c_name(name))
1937
1938    ret += mcgen('''
1939
1940#define %(c_name)s_str(val) \\
1941    qapi_enum_lookup(&%(c_name)s_lookup, (val))
1942
1943extern const QEnumLookup %(c_name)s_lookup;
1944''',
1945                 c_name=c_name(name))
1946    return ret
1947
1948
1949def build_params(arg_type, boxed, extra):
1950    if not arg_type:
1951        assert not boxed
1952        return extra
1953    ret = ''
1954    sep = ''
1955    if boxed:
1956        ret += '%s arg' % arg_type.c_param_type()
1957        sep = ', '
1958    else:
1959        assert not arg_type.variants
1960        for memb in arg_type.members:
1961            ret += sep
1962            sep = ', '
1963            if memb.optional:
1964                ret += 'bool has_%s, ' % c_name(memb.name)
1965            ret += '%s %s' % (memb.type.c_param_type(),
1966                              c_name(memb.name))
1967    if extra:
1968        ret += sep + extra
1969    return ret
1970
1971
1972#
1973# Accumulate and write output
1974#
1975
1976class QAPIGen(object):
1977
1978    def __init__(self):
1979        self._preamble = ''
1980        self._body = ''
1981
1982    def preamble_add(self, text):
1983        self._preamble += text
1984
1985    def add(self, text):
1986        self._body += text
1987
1988    def _top(self, fname):
1989        return ''
1990
1991    def _bottom(self, fname):
1992        return ''
1993
1994    def write(self, output_dir, fname):
1995        if output_dir:
1996            try:
1997                os.makedirs(output_dir)
1998            except os.error as e:
1999                if e.errno != errno.EEXIST:
2000                    raise
2001        fd = os.open(os.path.join(output_dir, fname),
2002                     os.O_RDWR | os.O_CREAT, 0o666)
2003        f = os.fdopen(fd, 'r+')
2004        text = (self._top(fname) + self._preamble + self._body
2005                + self._bottom(fname))
2006        oldtext = f.read(len(text) + 1)
2007        if text != oldtext:
2008            f.seek(0)
2009            f.truncate(0)
2010            f.write(text)
2011        f.close()
2012
2013
2014class QAPIGenC(QAPIGen):
2015
2016    def __init__(self, blurb, pydoc):
2017        QAPIGen.__init__(self)
2018        self._blurb = blurb
2019        self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
2020                                                  re.MULTILINE))
2021
2022    def _top(self, fname):
2023        return mcgen('''
2024/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2025
2026/*
2027%(blurb)s
2028 *
2029 * %(copyright)s
2030 *
2031 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2032 * See the COPYING.LIB file in the top-level directory.
2033 */
2034
2035''',
2036                     blurb=self._blurb, copyright=self._copyright)
2037
2038
2039class QAPIGenH(QAPIGenC):
2040
2041    def _top(self, fname):
2042        return QAPIGenC._top(self, fname) + guardstart(fname)
2043
2044    def _bottom(self, fname):
2045        return guardend(fname)
2046
2047
2048class QAPIGenDoc(QAPIGen):
2049
2050    def _top(self, fname):
2051        return (QAPIGen._top(self, fname)
2052                + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2053
2054
2055class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
2056
2057    def __init__(self, prefix, what, blurb, pydoc):
2058        self._prefix = prefix
2059        self._what = what
2060        self._genc = QAPIGenC(blurb, pydoc)
2061        self._genh = QAPIGenH(blurb, pydoc)
2062
2063    def write(self, output_dir):
2064        self._genc.write(output_dir, self._prefix + self._what + '.c')
2065        self._genh.write(output_dir, self._prefix + self._what + '.h')
2066