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