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