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