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