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