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