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